xref: /reactos/hal/halx86/generic/bios.c (revision c2c66aff)
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[32][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             ASSERT(j < 32);
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     ULONG Flags;
650     PHARDWARE_PTE IdtPte;
651     BOOLEAN RestoreWriteProtection = FALSE;
652 
653     //
654     // Disable interrupts
655     //
656     Flags = __readeflags();
657     _disable();
658 
659     //
660     // Map memory available to the V8086 real-mode code
661     //
662     HalpMapRealModeMemory();
663 
664     //
665     // On P5, the first 7 entries of the IDT are write protected to work around
666     // the cmpxchg8b lock errata. Unprotect them here so we can set our custom
667     // invalid op-code handler.
668     //
669     IdtPte = HalAddressToPte(((PKIPCR)KeGetPcr())->IDT);
670     RestoreWriteProtection = IdtPte->Write != 0;
671     IdtPte->Write = 1;
672 
673     //
674     // Use special invalid opcode and GPF trap handlers
675     //
676     HalpSwitchToRealModeTrapHandlers();
677 
678     //
679     // Configure the IOPM and TSS
680     //
681     HalpSetupRealModeIoPermissionsAndTask();
682 
683     //
684     // Now jump to real mode
685     //
686     HalpBiosCall();
687 
688     //
689     // Restore kernel trap handlers
690     //
691     HalpRestoreTrapHandlers();
692 
693     //
694     // Restore write permission
695     //
696     IdtPte->Write = RestoreWriteProtection;
697 
698     //
699     // Restore TSS and IOPM
700     //
701     HalpRestoreIoPermissionsAndTask();
702 
703     //
704     // Restore low memory mapping
705     //
706     HalpUnmapRealModeMemory();
707 
708     //
709     // Restore interrupts if they were previously enabled
710     //
711     __writeeflags(Flags);
712     return TRUE;
713 }
714 #endif
715 
716 /* EOF */
717