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
HalpOpcodeInvalid(IN PHAL_BIOS_FRAME BiosFrame)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
HalpPushInt(IN PHAL_BIOS_FRAME BiosFrame,IN ULONG Interrupt)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
HalpOpcodeINTnn(IN PHAL_BIOS_FRAME BiosFrame)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
HalpDispatchV86Opcode(IN PKTRAP_FRAME TrapFrame)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 DECLSPEC_NORETURN
206 VOID
207 FASTCALL
HalpTrap0DHandler(IN PKTRAP_FRAME TrapFrame)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
HalpTrap06(VOID)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
HalpBiosCall(VOID)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
288 /* FUNCTIONS ******************************************************************/
289
290 VOID
291 NTAPI
HalpBorrowTss(VOID)292 HalpBorrowTss(VOID)
293 {
294 USHORT Tss;
295 PKGDTENTRY TssGdt;
296 ULONG_PTR TssLimit;
297 PKTSS TssBase;
298
299 //
300 // Get the current TSS and its GDT entry
301 //
302 Tss = Ke386GetTr();
303 TssGdt = &KeGetPcr()->GDT[Tss / sizeof(KGDTENTRY)];
304
305 //
306 // Get the KTSS limit and check if it has IOPM space
307 //
308 TssLimit = TssGdt->LimitLow | TssGdt->HighWord.Bits.LimitHi << 16;
309
310 //
311 // If the KTSS doesn't have enough space this is probably an NMI or DF
312 //
313 if (TssLimit > IOPM_SIZE)
314 {
315 //
316 // We are good to go
317 //
318 HalpSavedTss = 0;
319 return;
320 }
321
322 //
323 // Get the "real" TSS
324 //
325 TssGdt = &KeGetPcr()->GDT[KGDT_TSS / sizeof(KGDTENTRY)];
326 TssBase = (PKTSS)(ULONG_PTR)(TssGdt->BaseLow |
327 TssGdt->HighWord.Bytes.BaseMid << 16 |
328 TssGdt->HighWord.Bytes.BaseHi << 24);
329
330 //
331 // Switch to it
332 //
333 KeGetPcr()->TSS = TssBase;
334
335 //
336 // Set it up
337 //
338 TssGdt->HighWord.Bits.Type = I386_TSS;
339 TssGdt->HighWord.Bits.Pres = 1;
340 TssGdt->HighWord.Bits.Dpl = 0;
341
342 //
343 // Load new TSS and return old one
344 //
345 Ke386SetTr(KGDT_TSS);
346 HalpSavedTss = Tss;
347 }
348
349 VOID
350 NTAPI
HalpReturnTss(VOID)351 HalpReturnTss(VOID)
352 {
353 PKGDTENTRY TssGdt;
354 PKTSS TssBase;
355
356 //
357 // Get the original TSS
358 //
359 TssGdt = &KeGetPcr()->GDT[HalpSavedTss / sizeof(KGDTENTRY)];
360 TssBase = (PKTSS)(ULONG_PTR)(TssGdt->BaseLow |
361 TssGdt->HighWord.Bytes.BaseMid << 16 |
362 TssGdt->HighWord.Bytes.BaseHi << 24);
363
364 //
365 // Switch to it
366 //
367 KeGetPcr()->TSS = TssBase;
368
369 //
370 // Set it up
371 //
372 TssGdt->HighWord.Bits.Type = I386_TSS;
373 TssGdt->HighWord.Bits.Pres = 1;
374 TssGdt->HighWord.Bits.Dpl = 0;
375
376 //
377 // Load old TSS
378 //
379 Ke386SetTr(HalpSavedTss);
380 }
381
382 VOID
383 NTAPI
HalpStoreAndClearIopm(VOID)384 HalpStoreAndClearIopm(VOID)
385 {
386 USHORT i, j;
387 PUSHORT Entry = HalpSavedIoMap;
388
389 //
390 // Loop the I/O Map
391 //
392 for (i = j = 0; i < IOPM_SIZE / sizeof(USHORT); i++)
393 {
394 //
395 // Check for non-FFFF entry
396 //
397 if (*Entry != 0xFFFF)
398 {
399 //
400 // Save it
401 //
402 HalpSavedIoMapData[j][0] = i;
403 HalpSavedIoMapData[j][1] = *Entry;
404 j++;
405 }
406
407 //
408 // Clear it
409 //
410 *Entry++ = 0;
411 }
412
413 //
414 // Terminate it
415 //
416 while (i++ < IOPM_FULL_SIZE / sizeof(USHORT))
417 {
418 *Entry++ = 0xFFFF;
419 }
420
421 //
422 // Return the entries we saved
423 //
424 HalpSavedIoMapEntries = j;
425 }
426
427 VOID
428 NTAPI
HalpRestoreIopm(VOID)429 HalpRestoreIopm(VOID)
430 {
431 ULONG i = HalpSavedIoMapEntries;
432
433 //
434 // Set default state
435 //
436 RtlFillMemory(HalpSavedIoMap, IOPM_FULL_SIZE, 0xFF);
437
438 //
439 // Restore the backed up copy, and initialize it
440 //
441 while (i--) HalpSavedIoMap[HalpSavedIoMapData[i][0]] = HalpSavedIoMapData[i][1];
442 }
443
444 VOID
445 NTAPI
HalpMapRealModeMemory(VOID)446 HalpMapRealModeMemory(VOID)
447 {
448 PHARDWARE_PTE Pte, V86Pte;
449 ULONG i;
450
451 //
452 // Get the page table directory for the lowest meg of memory
453 //
454 Pte = HalAddressToPde(0);
455 HalpSavedPfn = Pte->PageFrameNumber;
456 HalpSavedPte = *Pte;
457
458 //
459 // Map it to the HAL reserved region and make it valid
460 //
461 Pte->Valid = 1;
462 Pte->Write = 1;
463 Pte->Owner = 1;
464 Pte->PageFrameNumber = (HalAddressToPde(0xFFC00000))->PageFrameNumber;
465
466 //
467 // Flush the TLB
468 //
469 HalpFlushTLB();
470
471 //
472 // Now loop the first meg of memory
473 //
474 for (i = 0; i < 0x100000; i += PAGE_SIZE)
475 {
476 //
477 // Identity map it
478 //
479 Pte = HalAddressToPte(i);
480 Pte->PageFrameNumber = i >> PAGE_SHIFT;
481 Pte->Valid = 1;
482 Pte->Write = 1;
483 Pte->Owner = 1;
484 }
485
486 //
487 // Now get the entry for our real mode V86 code and the target
488 //
489 Pte = HalAddressToPte(0x20000);
490 V86Pte = HalAddressToPte(&HalpRealModeStart);
491 do
492 {
493 //
494 // Map the physical address into our real-mode region
495 //
496 Pte->PageFrameNumber = V86Pte->PageFrameNumber;
497
498 //
499 // Keep going until we've reached the end of our region
500 //
501 Pte++;
502 V86Pte++;
503 } while (V86Pte <= HalAddressToPte(&HalpRealModeEnd));
504
505 //
506 // Flush the TLB
507 //
508 HalpFlushTLB();
509 }
510
511 VOID
512 NTAPI
HalpSwitchToRealModeTrapHandlers(VOID)513 HalpSwitchToRealModeTrapHandlers(VOID)
514 {
515 //
516 // Save the current Invalid Opcode and General Protection Fault Handlers
517 //
518 HalpGpfHandler = KeQueryInterruptHandler(13);
519 HalpBopHandler = KeQueryInterruptHandler(6);
520
521 //
522 // Now set our own GPF handler to handle exceptions while in real mode
523 //
524 KeRegisterInterruptHandler(13, HalpTrap0D);
525
526 //
527 // And our own invalid opcode handler to detect the BOP to get us out
528 //
529 KeRegisterInterruptHandler(6, HalpTrap06);
530 }
531
532 VOID
533 NTAPI
HalpSetupRealModeIoPermissionsAndTask(VOID)534 HalpSetupRealModeIoPermissionsAndTask(VOID)
535 {
536 //
537 // Switch to valid TSS
538 //
539 HalpBorrowTss();
540
541 //
542 // Save a copy of the I/O Map and delete it
543 //
544 HalpSavedIoMap = (PUSHORT)KeGetPcr()->TSS->IoMaps[0].IoMap;
545 HalpStoreAndClearIopm();
546
547 //
548 // Save the IOPM and switch to the real-mode one
549 //
550 HalpSavedIopmBase = KeGetPcr()->TSS->IoMapBase;
551 KeGetPcr()->TSS->IoMapBase = KiComputeIopmOffset(1);
552
553 //
554 // Save our stack pointer
555 //
556 HalpSavedEsp0 = KeGetPcr()->TSS->Esp0;
557 }
558
559 VOID
560 NTAPI
HalpRestoreTrapHandlers(VOID)561 HalpRestoreTrapHandlers(VOID)
562 {
563 //
564 // Keep dummy GPF handler in case we get an NMI during V8086
565 //
566 if (!HalpNMIInProgress)
567 {
568 //
569 // Not an NMI -- put back the original handler
570 //
571 KeRegisterInterruptHandler(13, HalpGpfHandler);
572 }
573
574 //
575 // Restore invalid opcode handler
576 //
577 KeRegisterInterruptHandler(6, HalpBopHandler);
578 }
579
580 VOID
581 NTAPI
HalpRestoreIoPermissionsAndTask(VOID)582 HalpRestoreIoPermissionsAndTask(VOID)
583 {
584 //
585 // Restore the stack pointer
586 //
587 KeGetPcr()->TSS->Esp0 = HalpSavedEsp0;
588
589 //
590 // Restore the I/O Map
591 //
592 HalpRestoreIopm();
593
594 //
595 // Restore the IOPM
596 //
597 KeGetPcr()->TSS->IoMapBase = HalpSavedIopmBase;
598
599 //
600 // Restore the TSS
601 //
602 if (HalpSavedTss) HalpReturnTss();
603 }
604
605 VOID
606 NTAPI
HalpUnmapRealModeMemory(VOID)607 HalpUnmapRealModeMemory(VOID)
608 {
609 ULONG i;
610 PHARDWARE_PTE Pte;
611
612 //
613 // Loop the first meg of memory
614 //
615 for (i = 0; i < 0x100000; i += PAGE_SIZE)
616 {
617 //
618 // Invalidate each PTE
619 //
620 Pte = HalAddressToPte(i);
621 Pte->Valid = 0;
622 Pte->Write = 0;
623 Pte->Owner = 0;
624 Pte->PageFrameNumber = 0;
625 }
626
627 //
628 // Restore the PDE for the lowest megabyte of memory
629 //
630 Pte = HalAddressToPde(0);
631 *Pte = HalpSavedPte;
632 Pte->PageFrameNumber = HalpSavedPfn;
633
634 //
635 // Flush the TLB
636 //
637 HalpFlushTLB();
638 }
639
640 BOOLEAN
641 NTAPI
HalpBiosDisplayReset(VOID)642 HalpBiosDisplayReset(VOID)
643 {
644 #if defined(SARCH_XBOX) || defined(SARCH_PC98)
645 /* There is no VGA BIOS on these machine types */
646 return FALSE;
647 #else
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(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 #endif
713 }
714
715 /* EOF */
716