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)
10 /* INCLUDES *******************************************************************/
17 void __cdecl
HalpTrap0D();
19 /* GLOBALS ********************************************************************/
25 HARDWARE_PTE HalpSavedPte
;
42 USHORT HalpSavedIopmBase
;
43 PUSHORT HalpSavedIoMap
;
44 USHORT HalpSavedIoMapData
[32][2];
45 ULONG HalpSavedIoMapEntries
;
47 /* Where the protected mode stack is */
48 ULONG_PTR HalpSavedEsp
;
50 /* Where the real mode code ends */
51 extern PVOID HalpRealModeEnd
;
53 /* Context saved for return from v86 mode */
54 jmp_buf HalpSavedContext
;
57 /* V86 OPCODE HANDLERS ********************************************************/
61 HalpOpcodeInvalid(IN PHAL_BIOS_FRAME BiosFrame
)
63 PUCHAR Inst
= (PUCHAR
)(BiosFrame
->CsBase
+ BiosFrame
->Eip
);
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",
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]);
79 HalpPushInt(IN PHAL_BIOS_FRAME BiosFrame
,
85 /* Calculate stack address (SP) */
86 Stack
= (PUSHORT
)(BiosFrame
->SsBase
+ (BiosFrame
->Esp
& 0xFFFF));
90 *Stack
= BiosFrame
->EFlags
& 0xFFFF;
94 *Stack
= BiosFrame
->SegCs
& 0xFFFF;
98 *Stack
= BiosFrame
->Eip
& 0xFFFF;
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;
105 /* Update stack address */
106 BiosFrame
->Esp
= (ULONG_PTR
)Stack
& 0xFFFF;
108 /* Update CS to linear */
109 BiosFrame
->CsBase
= BiosFrame
->SegCs
<< 4;
110 BiosFrame
->CsLimit
= 0xFFFF;
111 BiosFrame
->CsFlags
= 0;
119 HalpOpcodeINTnn(IN PHAL_BIOS_FRAME BiosFrame
)
122 PKTRAP_FRAME TrapFrame
;
124 /* Convert SS to linear */
125 BiosFrame
->SsBase
= BiosFrame
->SegSs
<< 4;
126 BiosFrame
->SsLimit
= 0xFFFF;
127 BiosFrame
->SsFlags
= 0;
129 /* Increase EIP and validate */
131 if (BiosFrame
->Eip
> BiosFrame
->CsLimit
) return FALSE
;
133 /* Read interrupt number */
134 Interrupt
= *(PUCHAR
)(BiosFrame
->CsBase
+ BiosFrame
->Eip
);
136 /* Increase EIP and push the interrupt */
138 if (HalpPushInt(BiosFrame
, Interrupt
))
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
;
157 HalpDispatchV86Opcode(IN PKTRAP_FRAME TrapFrame
)
160 HAL_BIOS_FRAME BiosFrame
;
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;
171 /* Convert CS to linear */
172 BiosFrame
.CsBase
= BiosFrame
.SegCs
<< 4;
173 BiosFrame
.CsLimit
= 0xFFFF;
174 BiosFrame
.CsFlags
= 0;
177 if (BiosFrame
.Eip
> BiosFrame
.CsLimit
) return FALSE
;
180 Instruction
= *(PUCHAR
)(BiosFrame
.CsBase
+ BiosFrame
.Eip
);
181 if (Instruction
!= 0xCD)
183 /* We only support INT */
184 HalpOpcodeInvalid(&BiosFrame
);
188 /* Handle the interrupt */
189 if (HalpOpcodeINTnn(&BiosFrame
))
192 TrapFrame
->Eip
= BiosFrame
.Eip
;
202 /* V86 TRAP HANDLERS **********************************************************/
208 HalpTrap0DHandler(IN PKTRAP_FRAME TrapFrame
)
211 KiEnterTrap(TrapFrame
);
213 /* Check if this is a V86 trap */
214 if (TrapFrame
->EFlags
& EFLAGS_V86_MASK
)
216 /* Dispatch the opcode and exit the trap */
217 HalpDispatchV86Opcode(TrapFrame
);
218 KiEoiHelper(TrapFrame
);
221 /* Strange, it isn't! This can happen during NMI */
222 DPRINT1("HAL: Trap0D while not in V86 mode\n");
223 KiDumpTrapFrame(TrapFrame
);
226 while (TRUE
); /* 'noreturn' function */
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
);
238 /* Restore the stack */
239 KeGetPcr()->TSS
->Esp0
= HalpSavedEsp0
;
241 /* Return back to where we left */
242 longjmp(HalpSavedContext
, 1);
246 /* V8086 ENTER ****************************************************************/
252 /* Must be volatile so it doesn't get optimized away! */
253 volatile KTRAP_FRAME V86TrapFrame
;
254 ULONG_PTR StackOffset
, CodeOffset
;
256 /* Save the context, check for return */
257 if (_setjmp(HalpSavedContext
))
259 /* Returned from v86 */
263 /* Kill alignment faults */
264 __writecr0(__readcr0() & ~CR0_AM
);
266 /* Set new stack address */
267 KeGetPcr()->TSS
->Esp0
= (ULONG
)&V86TrapFrame
- 0x20 - sizeof(FX_SAVE_AREA
);
269 /* Compute segmented IP and SP offsets */
270 StackOffset
= (ULONG_PTR
)&HalpRealModeEnd
- 4 - (ULONG_PTR
)HalpRealModeStart
;
271 CodeOffset
= (ULONG_PTR
)HalpRealModeStart
& 0xFFF;
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
;
284 /* Exit to V86 mode */
285 HalpExitToV86((PKTRAP_FRAME
)&V86TrapFrame
);
289 /* FUNCTIONS ******************************************************************/
301 // Get the current TSS and its GDT entry
304 TssGdt
= &((PKIPCR
)KeGetPcr())->GDT
[Tss
/ sizeof(KGDTENTRY
)];
307 // Get the KTSS limit and check if it has IOPM space
309 TssLimit
= TssGdt
->LimitLow
| TssGdt
->HighWord
.Bits
.LimitHi
<< 16;
312 // If the KTSS doesn't have enough space this is probably an NMI or DF
314 if (TssLimit
> IOPM_SIZE
)
324 // Get the "real" TSS
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);
334 KeGetPcr()->TSS
= TssBase
;
339 TssGdt
->HighWord
.Bits
.Type
= I386_TSS
;
340 TssGdt
->HighWord
.Bits
.Pres
= 1;
341 TssGdt
->HighWord
.Bits
.Dpl
= 0;
344 // Load new TSS and return old one
346 Ke386SetTr(KGDT_TSS
);
358 // Get the original TSS
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);
368 KeGetPcr()->TSS
= TssBase
;
373 TssGdt
->HighWord
.Bits
.Type
= I386_TSS
;
374 TssGdt
->HighWord
.Bits
.Pres
= 1;
375 TssGdt
->HighWord
.Bits
.Dpl
= 0;
380 Ke386SetTr(HalpSavedTss
);
385 HalpStoreAndClearIopm(VOID
)
388 PUSHORT Entry
= HalpSavedIoMap
;
393 for (i
= j
= 0; i
< (IOPM_SIZE
) / 2; i
++)
396 // Check for non-FFFF entry
398 if (*Entry
!= 0xFFFF)
404 HalpSavedIoMapData
[j
][0] = i
;
405 HalpSavedIoMapData
[j
][1] = *Entry
;
418 while (i
++ < (IOPM_FULL_SIZE
/ 2)) *Entry
++ = 0xFFFF;
421 // Return the entries we saved
423 HalpSavedIoMapEntries
= j
;
428 HalpRestoreIopm(VOID
)
430 ULONG i
= HalpSavedIoMapEntries
;
435 RtlFillMemory(HalpSavedIoMap
, IOPM_FULL_SIZE
, 0xFF);
438 // Restore the backed up copy, and initialize it
440 while (i
--) HalpSavedIoMap
[HalpSavedIoMapData
[i
][0]] = HalpSavedIoMapData
[i
][1];
446 HalpMapRealModeMemory(VOID
)
448 PHARDWARE_PTE Pte
, V86Pte
;
452 // Get the page table directory for the lowest meg of memory
454 Pte
= HalAddressToPde(0);
455 HalpSavedPfn
= Pte
->PageFrameNumber
;
459 // Map it to the HAL reserved region and make it valid
464 Pte
->PageFrameNumber
= (HalAddressToPde(0xFFC00000))->PageFrameNumber
;
472 // Now loop the first meg of memory
474 for (i
= 0; i
< 0x100000; i
+= PAGE_SIZE
)
479 Pte
= HalAddressToPte(i
);
480 Pte
->PageFrameNumber
= i
>> PAGE_SHIFT
;
487 // Now get the entry for our real mode V86 code and the target
489 Pte
= HalAddressToPte(0x20000);
490 V86Pte
= HalAddressToPte(&HalpRealModeStart
);
494 // Map the physical address into our real-mode region
496 Pte
->PageFrameNumber
= V86Pte
->PageFrameNumber
;
499 // Keep going until we've reached the end of our region
503 } while (V86Pte
<= HalAddressToPte(&HalpRealModeEnd
));
513 HalpSwitchToRealModeTrapHandlers(VOID
)
516 // Save the current Invalid Opcode and General Protection Fault Handlers
518 HalpGpfHandler
= KeQueryInterruptHandler(13);
519 HalpBopHandler
= KeQueryInterruptHandler(6);
522 // Now set our own GPF handler to handle exceptions while in real mode
524 KeRegisterInterruptHandler(13, HalpTrap0D
);
527 // And our own invalid opcode handler to detect the BOP to get us out
529 KeRegisterInterruptHandler(6, HalpTrap06
);
535 HalpSetupRealModeIoPermissionsAndTask(VOID
)
538 // Switch to valid TSS
543 // Save a copy of the I/O Map and delete it
545 HalpSavedIoMap
= (PUSHORT
)&(KeGetPcr()->TSS
->IoMaps
[0]);
546 HalpStoreAndClearIopm();
549 // Save the IOPM and switch to the real-mode one
551 HalpSavedIopmBase
= KeGetPcr()->TSS
->IoMapBase
;
552 KeGetPcr()->TSS
->IoMapBase
= KiComputeIopmOffset(1);
555 // Save our stack pointer
557 HalpSavedEsp0
= KeGetPcr()->TSS
->Esp0
;
562 HalpRestoreTrapHandlers(VOID
)
565 // Keep dummy GPF handler in case we get an NMI during V8086
567 if (!HalpNMIInProgress
)
570 // Not an NMI -- put back the original handler
572 KeRegisterInterruptHandler(13, HalpGpfHandler
);
576 // Restore invalid opcode handler
578 KeRegisterInterruptHandler(6, HalpBopHandler
);
583 HalpRestoreIoPermissionsAndTask(VOID
)
586 // Restore the stack pointer
588 KeGetPcr()->TSS
->Esp0
= HalpSavedEsp0
;
591 // Restore the I/O Map
598 KeGetPcr()->TSS
->IoMapBase
= HalpSavedIopmBase
;
603 if (HalpSavedTss
) HalpReturnTss();
608 HalpUnmapRealModeMemory(VOID
)
614 // Loop the first meg of memory
616 for (i
= 0; i
< 0x100000; i
+= PAGE_SIZE
)
619 // Invalidate each PTE
621 Pte
= HalAddressToPte(i
);
625 Pte
->PageFrameNumber
= 0;
629 // Restore the PDE for the lowest megabyte of memory
631 Pte
= HalAddressToPde(0);
633 Pte
->PageFrameNumber
= HalpSavedPfn
;
644 HalpBiosDisplayReset(VOID
)
647 PHARDWARE_PTE IdtPte
;
648 BOOLEAN RestoreWriteProtection
= FALSE
;
651 // Disable interrupts
653 Flags
= __readeflags();
657 // Map memory available to the V8086 real-mode code
659 HalpMapRealModeMemory();
662 // On P5, the first 7 entries of the IDT are write protected to work around
663 // the cmpxchg8b lock errata. Unprotect them here so we can set our custom
664 // invalid op-code handler.
666 IdtPte
= HalAddressToPte(((PKIPCR
)KeGetPcr())->IDT
);
667 RestoreWriteProtection
= IdtPte
->Write
!= 0;
671 // Use special invalid opcode and GPF trap handlers
673 HalpSwitchToRealModeTrapHandlers();
676 // Configure the IOPM and TSS
678 HalpSetupRealModeIoPermissionsAndTask();
681 // Now jump to real mode
686 // Restore kernel trap handlers
688 HalpRestoreTrapHandlers();
691 // Restore write permission
693 IdtPte
->Write
= RestoreWriteProtection
;
696 // Restore TSS and IOPM
698 HalpRestoreIoPermissionsAndTask();
701 // Restore low memory mapping
703 HalpUnmapRealModeMemory();
706 // Restore interrupts if they were previously enabled
708 __writeeflags(Flags
);