xref: /reactos/hal/halx86/generic/bios.c (revision 8a978a17)
1 /*
2  * PROJECT:         ReactOS Hardware Abstraction Layer (HAL)
3  * LICENSE:         BSD - See COPYING.ARM in the top level directory
4  * PURPOSE:         BIOS Access Routines
5  * PROGRAMMERS:     ReactOS Portable Systems Group
6  *                  Alex Ionescu (alex.ionescu@reactos.org)
7  */
8 
9 /* INCLUDES *******************************************************************/
10 
11 #include <hal.h>
12 
13 #define NDEBUG
14 #include <debug.h>
15 
16 #include <setjmp.h>
17 
18 void __cdecl HalpTrap0D();
19 
20 /* GLOBALS ********************************************************************/
21 
22 //
23 // PTE Data
24 //
25 ULONG HalpSavedPfn;
26 HARDWARE_PTE HalpSavedPte;
27 
28 //
29 // IDT Data
30 //
31 PVOID HalpGpfHandler;
32 PVOID HalpBopHandler;
33 
34 //
35 // TSS Data
36 //
37 ULONG HalpSavedEsp0;
38 USHORT HalpSavedTss;
39 
40 //
41 // IOPM Data
42 //
43 USHORT HalpSavedIopmBase;
44 PUSHORT HalpSavedIoMap;
45 USHORT HalpSavedIoMapData[IOPM_SIZE / sizeof(USHORT)][2];
46 ULONG HalpSavedIoMapEntries;
47 
48 /* Where the protected mode stack is */
49 ULONG_PTR HalpSavedEsp;
50 
51 /* Where the real mode code ends */
52 extern PVOID HalpRealModeEnd;
53 
54 /* Context saved for return from v86 mode */
55 jmp_buf HalpSavedContext;
56 
57 
58 /* V86 OPCODE HANDLERS ********************************************************/
59 
60 BOOLEAN
61 FASTCALL
62 HalpOpcodeInvalid(IN PHAL_BIOS_FRAME BiosFrame)
63 {
64     PUCHAR Inst = (PUCHAR)(BiosFrame->CsBase + BiosFrame->Eip);
65 
66     /* Print error message */
67     DPRINT1("HAL: An invalid V86 opcode was encountered at address %X:%X\n"
68             "Opcode: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X\n",
69             BiosFrame->SegCs, BiosFrame->Eip,
70             Inst[0], Inst[1], Inst[2], Inst[3], Inst[4],
71             Inst[5], Inst[6], Inst[7], Inst[8], Inst[9]);
72 
73     /* Break */
74     DbgBreakPoint();
75     return FALSE;
76 }
77 
78 BOOLEAN
79 FASTCALL
80 HalpPushInt(IN PHAL_BIOS_FRAME BiosFrame,
81             IN ULONG Interrupt)
82 {
83     PUSHORT Stack;
84     ULONG Eip;
85 
86     /* Calculate stack address (SP) */
87     Stack = (PUSHORT)(BiosFrame->SsBase + (BiosFrame->Esp & 0xFFFF));
88 
89     /* Push EFlags */
90     Stack--;
91     *Stack = BiosFrame->EFlags & 0xFFFF;
92 
93     /* Push CS */
94     Stack--;
95     *Stack = BiosFrame->SegCs & 0xFFFF;
96 
97     /* Push IP */
98     Stack--;
99     *Stack = BiosFrame->Eip & 0xFFFF;
100 
101     /* Compute new CS:IP from the IVT address for this interrupt entry */
102     Eip = *(PULONG)(Interrupt * 4);
103     BiosFrame->Eip = Eip & 0xFFFF;
104     BiosFrame->SegCs = Eip >> 16;
105 
106     /* Update stack address */
107     BiosFrame->Esp = (ULONG_PTR)Stack & 0xFFFF;
108 
109     /* Update CS to linear */
110     BiosFrame->CsBase = BiosFrame->SegCs << 4;
111     BiosFrame->CsLimit = 0xFFFF;
112     BiosFrame->CsFlags = 0;
113 
114     /* We're done */
115     return TRUE;
116 }
117 
118 BOOLEAN
119 FASTCALL
120 HalpOpcodeINTnn(IN PHAL_BIOS_FRAME BiosFrame)
121 {
122     UCHAR Interrupt;
123     PKTRAP_FRAME TrapFrame;
124 
125     /* Convert SS to linear */
126     BiosFrame->SsBase = BiosFrame->SegSs << 4;
127     BiosFrame->SsLimit = 0xFFFF;
128     BiosFrame->SsFlags = 0;
129 
130     /* Increase EIP and validate */
131     BiosFrame->Eip++;
132     if (BiosFrame->Eip > BiosFrame->CsLimit) return FALSE;
133 
134     /* Read interrupt number */
135     Interrupt = *(PUCHAR)(BiosFrame->CsBase + BiosFrame->Eip);
136 
137     /* Increase EIP and push the interrupt */
138     BiosFrame->Eip++;
139     if (HalpPushInt(BiosFrame, Interrupt))
140     {
141         /* Update the trap frame */
142         TrapFrame = BiosFrame->TrapFrame;
143         TrapFrame->HardwareSegSs = BiosFrame->SegSs;
144         TrapFrame->HardwareEsp = BiosFrame->Esp;
145         TrapFrame->SegCs = BiosFrame->SegCs;
146         TrapFrame->EFlags = BiosFrame->EFlags;
147 
148         /* Success */
149         return TRUE;
150     }
151 
152     /* Failure */
153     return FALSE;
154 }
155 
156 BOOLEAN
157 FASTCALL
158 HalpDispatchV86Opcode(IN PKTRAP_FRAME TrapFrame)
159 {
160     UCHAR Instruction;
161     HAL_BIOS_FRAME BiosFrame;
162 
163     /* Fill out the BIOS frame */
164     BiosFrame.TrapFrame = TrapFrame;
165     BiosFrame.SegSs = TrapFrame->HardwareSegSs;
166     BiosFrame.Esp = TrapFrame->HardwareEsp;
167     BiosFrame.EFlags = TrapFrame->EFlags;
168     BiosFrame.SegCs = TrapFrame->SegCs;
169     BiosFrame.Eip = TrapFrame->Eip;
170     BiosFrame.Prefix = 0;
171 
172     /* Convert CS to linear */
173     BiosFrame.CsBase = BiosFrame.SegCs << 4;
174     BiosFrame.CsLimit = 0xFFFF;
175     BiosFrame.CsFlags = 0;
176 
177     /* Validate IP */
178     if (BiosFrame.Eip > BiosFrame.CsLimit) return FALSE;
179 
180     /* Read IP */
181     Instruction = *(PUCHAR)(BiosFrame.CsBase + BiosFrame.Eip);
182     if (Instruction != 0xCD)
183     {
184         /* We only support INT */
185         HalpOpcodeInvalid(&BiosFrame);
186         return FALSE;
187     }
188 
189     /* Handle the interrupt */
190     if (HalpOpcodeINTnn(&BiosFrame))
191     {
192         /* Update EIP */
193         TrapFrame->Eip = BiosFrame.Eip;
194 
195         /* We're done */
196         return TRUE;
197     }
198 
199     /* Failure */
200     return FALSE;
201 }
202 
203 /* V86 TRAP HANDLERS **********************************************************/
204 
205 #ifndef _MINIHAL_
206 DECLSPEC_NORETURN
207 VOID
208 FASTCALL
209 HalpTrap0DHandler(IN PKTRAP_FRAME TrapFrame)
210 {
211     /* Enter the trap */
212     KiEnterTrap(TrapFrame);
213 
214     /* Check if this is a V86 trap */
215     if (TrapFrame->EFlags & EFLAGS_V86_MASK)
216     {
217         /* Dispatch the opcode and exit the trap */
218         HalpDispatchV86Opcode(TrapFrame);
219         KiEoiHelper(TrapFrame);
220     }
221 
222     /* Strange, it isn't! This can happen during NMI */
223     DPRINT1("HAL: Trap0D while not in V86 mode\n");
224     KiDumpTrapFrame(TrapFrame);
225 
226     ERROR_FATAL();
227     while (TRUE); /* 'noreturn' function */
228 }
229 
230 VOID
231 DECLSPEC_NORETURN
232 HalpTrap06(VOID)
233 {
234     /* Restore ES/DS to known good values first */
235     Ke386SetEs(KGDT_R3_DATA | RPL_MASK);
236     Ke386SetDs(KGDT_R3_DATA | RPL_MASK);
237     Ke386SetFs(KGDT_R0_PCR);
238 
239     /* Restore the stack */
240     KeGetPcr()->TSS->Esp0 = HalpSavedEsp0;
241 
242     /* Return back to where we left */
243     longjmp(HalpSavedContext, 1);
244     UNREACHABLE;
245 }
246 
247 /* V8086 ENTER ****************************************************************/
248 
249 VOID
250 NTAPI
251 HalpBiosCall(VOID)
252 {
253     /* Must be volatile so it doesn't get optimized away! */
254     volatile KTRAP_FRAME V86TrapFrame;
255     ULONG_PTR StackOffset, CodeOffset;
256 
257     /* Save the context, check for return */
258     if (_setjmp(HalpSavedContext))
259     {
260         /* Returned from v86 */
261         return;
262     }
263 
264     /* Kill alignment faults */
265     __writecr0(__readcr0() & ~CR0_AM);
266 
267     /* Set new stack address */
268     KeGetPcr()->TSS->Esp0 = (ULONG)&V86TrapFrame - 0x20 - sizeof(FX_SAVE_AREA);
269 
270     /* Compute segmented IP and SP offsets */
271     StackOffset = (ULONG_PTR)&HalpRealModeEnd - 4 - (ULONG_PTR)HalpRealModeStart;
272     CodeOffset = (ULONG_PTR)HalpRealModeStart & 0xFFF;
273 
274     /* Now build the V86 trap frame */
275     V86TrapFrame.V86Es = 0;
276     V86TrapFrame.V86Ds = 0;
277     V86TrapFrame.V86Gs = 0;
278     V86TrapFrame.V86Fs = 0;
279     V86TrapFrame.HardwareSegSs = 0x2000;
280     V86TrapFrame.HardwareEsp = StackOffset + CodeOffset;
281     V86TrapFrame.EFlags = __readeflags() | EFLAGS_V86_MASK | EFLAGS_IOPL;
282     V86TrapFrame.SegCs = 0x2000;
283     V86TrapFrame.Eip = CodeOffset;
284 
285     /* Exit to V86 mode */
286     HalpExitToV86((PKTRAP_FRAME)&V86TrapFrame);
287 }
288 #endif
289 
290 /* FUNCTIONS ******************************************************************/
291 
292 VOID
293 NTAPI
294 HalpBorrowTss(VOID)
295 {
296     USHORT Tss;
297     PKGDTENTRY TssGdt;
298     ULONG_PTR TssLimit;
299     PKTSS TssBase;
300 
301     //
302     // Get the current TSS and its GDT entry
303     //
304     Tss = Ke386GetTr();
305     TssGdt = &((PKIPCR)KeGetPcr())->GDT[Tss / sizeof(KGDTENTRY)];
306 
307     //
308     // Get the KTSS limit and check if it has IOPM space
309     //
310     TssLimit = TssGdt->LimitLow | TssGdt->HighWord.Bits.LimitHi << 16;
311 
312     //
313     // If the KTSS doesn't have enough space this is probably an NMI or DF
314     //
315     if (TssLimit > IOPM_SIZE)
316     {
317         //
318         // We are good to go
319         //
320         HalpSavedTss = 0;
321         return;
322     }
323 
324     //
325     // Get the "real" TSS
326     //
327     TssGdt = &((PKIPCR)KeGetPcr())->GDT[KGDT_TSS / sizeof(KGDTENTRY)];
328     TssBase = (PKTSS)(ULONG_PTR)(TssGdt->BaseLow |
329                                  TssGdt->HighWord.Bytes.BaseMid << 16 |
330                                  TssGdt->HighWord.Bytes.BaseHi << 24);
331 
332     //
333     // Switch to it
334     //
335     KeGetPcr()->TSS = TssBase;
336 
337     //
338     // Set it up
339     //
340     TssGdt->HighWord.Bits.Type = I386_TSS;
341     TssGdt->HighWord.Bits.Pres = 1;
342     TssGdt->HighWord.Bits.Dpl = 0;
343 
344     //
345     // Load new TSS and return old one
346     //
347     Ke386SetTr(KGDT_TSS);
348     HalpSavedTss = Tss;
349 }
350 
351 VOID
352 NTAPI
353 HalpReturnTss(VOID)
354 {
355     PKGDTENTRY TssGdt;
356     PKTSS TssBase;
357 
358     //
359     // Get the original TSS
360     //
361     TssGdt = &((PKIPCR)KeGetPcr())->GDT[HalpSavedTss / sizeof(KGDTENTRY)];
362     TssBase = (PKTSS)(ULONG_PTR)(TssGdt->BaseLow |
363                                  TssGdt->HighWord.Bytes.BaseMid << 16 |
364                                  TssGdt->HighWord.Bytes.BaseHi << 24);
365 
366     //
367     // Switch to it
368     //
369     KeGetPcr()->TSS = TssBase;
370 
371     //
372     // Set it up
373     //
374     TssGdt->HighWord.Bits.Type = I386_TSS;
375     TssGdt->HighWord.Bits.Pres = 1;
376     TssGdt->HighWord.Bits.Dpl = 0;
377 
378     //
379     // Load old TSS
380     //
381     Ke386SetTr(HalpSavedTss);
382 }
383 
384 VOID
385 NTAPI
386 HalpStoreAndClearIopm(VOID)
387 {
388     USHORT i, j;
389     PUSHORT Entry = HalpSavedIoMap;
390 
391     //
392     // Loop the I/O Map
393     //
394     for (i = j = 0; i < IOPM_SIZE / sizeof(USHORT); i++)
395     {
396         //
397         // Check for non-FFFF entry
398         //
399         if (*Entry != 0xFFFF)
400         {
401             //
402             // Save it
403             //
404             HalpSavedIoMapData[j][0] = i;
405             HalpSavedIoMapData[j][1] = *Entry;
406             j++;
407         }
408 
409         //
410         // Clear it
411         //
412         *Entry++ = 0;
413     }
414 
415     //
416     // Terminate it
417     //
418     while (i++ < IOPM_FULL_SIZE / sizeof(USHORT))
419     {
420         *Entry++ = 0xFFFF;
421     }
422 
423     //
424     // Return the entries we saved
425     //
426     HalpSavedIoMapEntries = j;
427 }
428 
429 VOID
430 NTAPI
431 HalpRestoreIopm(VOID)
432 {
433     ULONG i = HalpSavedIoMapEntries;
434 
435     //
436     // Set default state
437     //
438     RtlFillMemory(HalpSavedIoMap, IOPM_FULL_SIZE, 0xFF);
439 
440     //
441     // Restore the backed up copy, and initialize it
442     //
443     while (i--) HalpSavedIoMap[HalpSavedIoMapData[i][0]] = HalpSavedIoMapData[i][1];
444 }
445 
446 #ifndef _MINIHAL_
447 VOID
448 NTAPI
449 HalpMapRealModeMemory(VOID)
450 {
451     PHARDWARE_PTE Pte, V86Pte;
452     ULONG i;
453 
454     //
455     // Get the page table directory for the lowest meg of memory
456     //
457     Pte = HalAddressToPde(0);
458     HalpSavedPfn = Pte->PageFrameNumber;
459     HalpSavedPte = *Pte;
460 
461     //
462     // Map it to the HAL reserved region and make it valid
463     //
464     Pte->Valid = 1;
465     Pte->Write = 1;
466     Pte->Owner = 1;
467     Pte->PageFrameNumber = (HalAddressToPde(0xFFC00000))->PageFrameNumber;
468 
469     //
470     // Flush the TLB
471     //
472     HalpFlushTLB();
473 
474     //
475     // Now loop the first meg of memory
476     //
477     for (i = 0; i < 0x100000; i += PAGE_SIZE)
478     {
479         //
480         // Identity map it
481         //
482         Pte = HalAddressToPte(i);
483         Pte->PageFrameNumber = i >> PAGE_SHIFT;
484         Pte->Valid = 1;
485         Pte->Write = 1;
486         Pte->Owner = 1;
487     }
488 
489     //
490     // Now get the entry for our real mode V86 code and the target
491     //
492     Pte = HalAddressToPte(0x20000);
493     V86Pte = HalAddressToPte(&HalpRealModeStart);
494     do
495     {
496         //
497         // Map the physical address into our real-mode region
498         //
499         Pte->PageFrameNumber = V86Pte->PageFrameNumber;
500 
501         //
502         // Keep going until we've reached the end of our region
503         //
504         Pte++;
505         V86Pte++;
506     } while (V86Pte <= HalAddressToPte(&HalpRealModeEnd));
507 
508     //
509     // Flush the TLB
510     //
511     HalpFlushTLB();
512 }
513 
514 VOID
515 NTAPI
516 HalpSwitchToRealModeTrapHandlers(VOID)
517 {
518     //
519     // Save the current Invalid Opcode and General Protection Fault Handlers
520     //
521     HalpGpfHandler = KeQueryInterruptHandler(13);
522     HalpBopHandler = KeQueryInterruptHandler(6);
523 
524     //
525     // Now set our own GPF handler to handle exceptions while in real mode
526     //
527     KeRegisterInterruptHandler(13, HalpTrap0D);
528 
529     //
530     // And our own invalid opcode handler to detect the BOP to get us out
531     //
532     KeRegisterInterruptHandler(6, HalpTrap06);
533 }
534 #endif
535 
536 VOID
537 NTAPI
538 HalpSetupRealModeIoPermissionsAndTask(VOID)
539 {
540     //
541     // Switch to valid TSS
542     //
543     HalpBorrowTss();
544 
545     //
546     // Save a copy of the I/O Map and delete it
547     //
548     HalpSavedIoMap = (PUSHORT)KeGetPcr()->TSS->IoMaps[0].IoMap;
549     HalpStoreAndClearIopm();
550 
551     //
552     // Save the IOPM and switch to the real-mode one
553     //
554     HalpSavedIopmBase = KeGetPcr()->TSS->IoMapBase;
555     KeGetPcr()->TSS->IoMapBase = KiComputeIopmOffset(1);
556 
557     //
558     // Save our stack pointer
559     //
560     HalpSavedEsp0 = KeGetPcr()->TSS->Esp0;
561 }
562 
563 VOID
564 NTAPI
565 HalpRestoreTrapHandlers(VOID)
566 {
567     //
568     // Keep dummy GPF handler in case we get an NMI during V8086
569     //
570     if (!HalpNMIInProgress)
571     {
572         //
573         // Not an NMI -- put back the original handler
574         //
575         KeRegisterInterruptHandler(13, HalpGpfHandler);
576     }
577 
578     //
579     // Restore invalid opcode handler
580     //
581     KeRegisterInterruptHandler(6, HalpBopHandler);
582 }
583 
584 VOID
585 NTAPI
586 HalpRestoreIoPermissionsAndTask(VOID)
587 {
588     //
589     // Restore the stack pointer
590     //
591     KeGetPcr()->TSS->Esp0 = HalpSavedEsp0;
592 
593     //
594     // Restore the I/O Map
595     //
596     HalpRestoreIopm();
597 
598     //
599     // Restore the IOPM
600     //
601     KeGetPcr()->TSS->IoMapBase = HalpSavedIopmBase;
602 
603     //
604     // Restore the TSS
605     //
606     if (HalpSavedTss) HalpReturnTss();
607 }
608 
609 VOID
610 NTAPI
611 HalpUnmapRealModeMemory(VOID)
612 {
613     ULONG i;
614     PHARDWARE_PTE Pte;
615 
616     //
617     // Loop the first meg of memory
618     //
619     for (i = 0; i < 0x100000; i += PAGE_SIZE)
620     {
621         //
622         // Invalidate each PTE
623         //
624         Pte = HalAddressToPte(i);
625         Pte->Valid = 0;
626         Pte->Write = 0;
627         Pte->Owner = 0;
628         Pte->PageFrameNumber = 0;
629     }
630 
631     //
632     // Restore the PDE for the lowest megabyte of memory
633     //
634     Pte = HalAddressToPde(0);
635     *Pte = HalpSavedPte;
636     Pte->PageFrameNumber = HalpSavedPfn;
637 
638     //
639     // Flush the TLB
640     //
641     HalpFlushTLB();
642 }
643 
644 #ifndef _MINIHAL_
645 BOOLEAN
646 NTAPI
647 HalpBiosDisplayReset(VOID)
648 {
649 #if defined(SARCH_XBOX) || defined(SARCH_PC98)
650     /* There is no VGA BIOS on these machine types */
651     return FALSE;
652 #else
653     ULONG Flags;
654     PHARDWARE_PTE IdtPte;
655     BOOLEAN RestoreWriteProtection = FALSE;
656 
657     //
658     // Disable interrupts
659     //
660     Flags = __readeflags();
661     _disable();
662 
663     //
664     // Map memory available to the V8086 real-mode code
665     //
666     HalpMapRealModeMemory();
667 
668     //
669     // On P5, the first 7 entries of the IDT are write protected to work around
670     // the cmpxchg8b lock errata. Unprotect them here so we can set our custom
671     // invalid op-code handler.
672     //
673     IdtPte = HalAddressToPte(((PKIPCR)KeGetPcr())->IDT);
674     RestoreWriteProtection = IdtPte->Write != 0;
675     IdtPte->Write = 1;
676 
677     //
678     // Use special invalid opcode and GPF trap handlers
679     //
680     HalpSwitchToRealModeTrapHandlers();
681 
682     //
683     // Configure the IOPM and TSS
684     //
685     HalpSetupRealModeIoPermissionsAndTask();
686 
687     //
688     // Now jump to real mode
689     //
690     HalpBiosCall();
691 
692     //
693     // Restore kernel trap handlers
694     //
695     HalpRestoreTrapHandlers();
696 
697     //
698     // Restore write permission
699     //
700     IdtPte->Write = RestoreWriteProtection;
701 
702     //
703     // Restore TSS and IOPM
704     //
705     HalpRestoreIoPermissionsAndTask();
706 
707     //
708     // Restore low memory mapping
709     //
710     HalpUnmapRealModeMemory();
711 
712     //
713     // Restore interrupts if they were previously enabled
714     //
715     __writeeflags(Flags);
716     return TRUE;
717 #endif
718 }
719 #endif
720 
721 /* EOF */
722