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