2 * PROJECT: ReactOS Hardware Abstraction Layer (HAL)
3 * LICENSE: BSD - See COPYING.ARM in the top level directory
4 * FILE: halx86/generic/bios.c
5 * PURPOSE: BIOS Access Routines
6 * PROGRAMMERS: ReactOS Portable Systems Group
7 * Alex Ionescu (alex.ionescu@reactos.org)
10 /* INCLUDES *******************************************************************/
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 /* Print error message */
64 DPRINT1("HAL: An invalid V86 opcode was encountered at address %x:%x\n",
65 BiosFrame
->SegCs
, BiosFrame
->Eip
);
74 HalpPushInt(IN PHAL_BIOS_FRAME BiosFrame
,
80 /* Calculate stack address (SP) */
81 Stack
= (PUSHORT
)(BiosFrame
->SsBase
+ (BiosFrame
->Esp
& 0xFFFF));
85 *Stack
= BiosFrame
->EFlags
& 0xFFFF;
89 *Stack
= BiosFrame
->SegCs
& 0xFFFF;
93 *Stack
= BiosFrame
->Eip
& 0xFFFF;
95 /* Compute new CS:IP from the IVT address for this interrupt entry */
96 Eip
= *(PULONG
)(Interrupt
* 4);
97 BiosFrame
->Eip
= Eip
& 0xFFFF;
98 BiosFrame
->SegCs
= Eip
>> 16;
100 /* Update stack address */
101 BiosFrame
->Esp
= (ULONG_PTR
)Stack
& 0xFFFF;
103 /* Update CS to linear */
104 BiosFrame
->CsBase
= BiosFrame
->SegCs
<< 4;
105 BiosFrame
->CsLimit
= 0xFFFF;
106 BiosFrame
->CsFlags
= 0;
114 HalpOpcodeINTnn(IN PHAL_BIOS_FRAME BiosFrame
)
117 PKTRAP_FRAME TrapFrame
;
119 /* Convert SS to linear */
120 BiosFrame
->SsBase
= BiosFrame
->SegSs
<< 4;
121 BiosFrame
->SsLimit
= 0xFFFF;
122 BiosFrame
->SsFlags
= 0;
124 /* Increase EIP and validate */
126 if (BiosFrame
->Eip
> BiosFrame
->CsLimit
) return FALSE
;
128 /* Read interrupt number */
129 Interrupt
= *(PUCHAR
)(BiosFrame
->CsBase
+ BiosFrame
->Eip
);
131 /* Increase EIP and push the interrupt */
133 if (HalpPushInt(BiosFrame
, Interrupt
))
135 /* Update the trap frame */
136 TrapFrame
= BiosFrame
->TrapFrame
;
137 TrapFrame
->HardwareSegSs
= BiosFrame
->SegSs
;
138 TrapFrame
->HardwareEsp
= BiosFrame
->Esp
;
139 TrapFrame
->SegCs
= BiosFrame
->SegCs
;
140 TrapFrame
->EFlags
= BiosFrame
->EFlags
;
152 HalpDispatchV86Opcode(IN PKTRAP_FRAME TrapFrame
)
155 HAL_BIOS_FRAME BiosFrame
;
157 /* Fill out the BIOS frame */
158 BiosFrame
.TrapFrame
= TrapFrame
;
159 BiosFrame
.SegSs
= TrapFrame
->HardwareSegSs
;
160 BiosFrame
.Esp
= TrapFrame
->HardwareEsp
;
161 BiosFrame
.EFlags
= TrapFrame
->EFlags
;
162 BiosFrame
.SegCs
= TrapFrame
->SegCs
;
163 BiosFrame
.Eip
= TrapFrame
->Eip
;
164 BiosFrame
.Prefix
= 0;
166 /* Convert CS to linear */
167 BiosFrame
.CsBase
= BiosFrame
.SegCs
<< 4;
168 BiosFrame
.CsLimit
= 0xFFFF;
169 BiosFrame
.CsFlags
= 0;
172 if (BiosFrame
.Eip
> BiosFrame
.CsLimit
) return FALSE
;
175 Instruction
= *(PUCHAR
)(BiosFrame
.CsBase
+ BiosFrame
.Eip
);
176 if (Instruction
!= 0xCD)
178 /* We only support INT */
179 HalpOpcodeInvalid(&BiosFrame
);
183 /* Handle the interrupt */
184 if (HalpOpcodeINTnn(&BiosFrame
))
187 TrapFrame
->Eip
= BiosFrame
.Eip
;
197 /* V86 TRAP HANDLERS **********************************************************/
202 HalpTrap0DHandler(IN PKTRAP_FRAME TrapFrame
)
205 KiEnterTrap(TrapFrame
);
207 /* Check if this is a V86 trap */
208 if (TrapFrame
->EFlags
& EFLAGS_V86_MASK
)
210 /* Dispatch the opcode and exit the trap */
211 HalpDispatchV86Opcode(TrapFrame
);
212 KiEoiHelper(TrapFrame
);
215 /* Strange, it isn't! This can happen during NMI */
216 DPRINT1("HAL: Trap0D while not in V86 mode\n");
217 KiDumpTrapFrame(TrapFrame
);
225 /* Restore ES/DS to known good values first */
226 Ke386SetEs(KGDT_R3_DATA
| RPL_MASK
);
227 Ke386SetDs(KGDT_R3_DATA
| RPL_MASK
);
228 Ke386SetFs(KGDT_R0_PCR
);
230 /* Restore the stack */
231 KeGetPcr()->TSS
->Esp0
= HalpSavedEsp0
;
233 /* Return back to where we left */
234 longjmp(HalpSavedContext
, 1);
238 /* V8086 ENTER ****************************************************************/
244 /* Must be volatile so it doesn't get optimized away! */
245 volatile KTRAP_FRAME V86TrapFrame
;
246 ULONG_PTR StackOffset
, CodeOffset
;
248 /* Save the context, check for return */
249 if (_setjmp(HalpSavedContext
))
251 /* Returned from v86 */
255 /* Kill alignment faults */
256 __writecr0(__readcr0() & ~CR0_AM
);
258 /* Set new stack address */
259 KeGetPcr()->TSS
->Esp0
= (ULONG
)&V86TrapFrame
- 0x20 - sizeof(FX_SAVE_AREA
);
261 /* Compute segmented IP and SP offsets */
262 StackOffset
= (ULONG_PTR
)&HalpRealModeEnd
- 4 - (ULONG_PTR
)HalpRealModeStart
;
263 CodeOffset
= (ULONG_PTR
)HalpRealModeStart
& 0xFFF;
265 /* Now build the V86 trap frame */
266 V86TrapFrame
.V86Es
= 0;
267 V86TrapFrame
.V86Ds
= 0;
268 V86TrapFrame
.V86Gs
= 0;
269 V86TrapFrame
.V86Fs
= 0;
270 V86TrapFrame
.HardwareSegSs
= 0x2000;
271 V86TrapFrame
.HardwareEsp
= StackOffset
+ CodeOffset
;
272 V86TrapFrame
.EFlags
= __readeflags() | EFLAGS_V86_MASK
| EFLAGS_IOPL
;
273 V86TrapFrame
.SegCs
= 0x2000;
274 V86TrapFrame
.Eip
= CodeOffset
;
276 /* Exit to V86 mode */
277 HalpExitToV86((PKTRAP_FRAME
)&V86TrapFrame
);
280 /* FUNCTIONS ******************************************************************/
292 // Get the current TSS and its GDT entry
295 TssGdt
= &((PKIPCR
)KeGetPcr())->GDT
[Tss
/ sizeof(KGDTENTRY
)];
298 // Get the KTSS limit and check if it has IOPM space
300 TssLimit
= TssGdt
->LimitLow
| TssGdt
->HighWord
.Bits
.LimitHi
<< 16;
303 // If the KTSS doesn't have enough space this is probably an NMI or DF
305 if (TssLimit
> IOPM_SIZE
)
315 // Get the "real" TSS
317 TssGdt
= &((PKIPCR
)KeGetPcr())->GDT
[KGDT_TSS
/ sizeof(KGDTENTRY
)];
318 TssBase
= (PKTSS
)(ULONG_PTR
)(TssGdt
->BaseLow
|
319 TssGdt
->HighWord
.Bytes
.BaseMid
<< 16 |
320 TssGdt
->HighWord
.Bytes
.BaseHi
<< 24);
325 KeGetPcr()->TSS
= TssBase
;
330 TssGdt
->HighWord
.Bits
.Type
= I386_TSS
;
331 TssGdt
->HighWord
.Bits
.Pres
= 1;
332 TssGdt
->HighWord
.Bits
.Dpl
= 0;
335 // Load new TSS and return old one
337 Ke386SetTr(KGDT_TSS
);
349 // Get the original TSS
351 TssGdt
= &((PKIPCR
)KeGetPcr())->GDT
[HalpSavedTss
/ sizeof(KGDTENTRY
)];
352 TssBase
= (PKTSS
)(ULONG_PTR
)(TssGdt
->BaseLow
|
353 TssGdt
->HighWord
.Bytes
.BaseMid
<< 16 |
354 TssGdt
->HighWord
.Bytes
.BaseHi
<< 24);
359 KeGetPcr()->TSS
= TssBase
;
364 TssGdt
->HighWord
.Bits
.Type
= I386_TSS
;
365 TssGdt
->HighWord
.Bits
.Pres
= 1;
366 TssGdt
->HighWord
.Bits
.Dpl
= 0;
371 Ke386SetTr(HalpSavedTss
);
376 HalpStoreAndClearIopm(VOID
)
379 PUSHORT Entry
= HalpSavedIoMap
;
384 for (i
= j
= 0; i
< (IOPM_SIZE
) / 2; i
++)
387 // Check for non-FFFF entry
389 if (*Entry
!= 0xFFFF)
395 HalpSavedIoMapData
[j
][0] = i
;
396 HalpSavedIoMapData
[j
][1] = *Entry
;
408 while (i
++ < (IOPM_FULL_SIZE
/ 2)) *Entry
++ = 0xFFFF;
411 // Return the entries we saved
413 HalpSavedIoMapEntries
= j
;
418 HalpRestoreIopm(VOID
)
420 ULONG i
= HalpSavedIoMapEntries
;
425 RtlFillMemory(HalpSavedIoMap
, 0xFF, IOPM_FULL_SIZE
);
428 // Restore the backed up copy, and initialize it
430 while (i
--) HalpSavedIoMap
[HalpSavedIoMapData
[i
][0]] = HalpSavedIoMapData
[i
][1];
435 HalpMapRealModeMemory(VOID
)
437 PHARDWARE_PTE Pte
, V86Pte
;
441 // Get the page table directory for the lowest meg of memory
443 Pte
= HalAddressToPde(0);
444 HalpSavedPfn
= Pte
->PageFrameNumber
;
448 // Map it to the HAL reserved region and make it valid
453 Pte
->PageFrameNumber
= (HalAddressToPde(0xFFC00000))->PageFrameNumber
;
461 // Now loop the first meg of memory
463 for (i
= 0; i
< 0x100000; i
+= PAGE_SIZE
)
468 Pte
= HalAddressToPte(i
);
469 Pte
->PageFrameNumber
= i
>> PAGE_SHIFT
;
476 // Now get the entry for our real mode V86 code and the target
478 Pte
= HalAddressToPte(0x20000);
479 V86Pte
= HalAddressToPte(&HalpRealModeStart
);
483 // Map the physical address into our real-mode region
485 Pte
->PageFrameNumber
= V86Pte
->PageFrameNumber
;
488 // Keep going until we've reached the end of our region
492 } while (V86Pte
<= HalAddressToPte(&HalpRealModeEnd
));
502 HalpSwitchToRealModeTrapHandlers(VOID
)
505 // Save the current Invalid Opcode and General Protection Fault Handlers
507 HalpGpfHandler
= KeQueryInterruptHandler(13);
508 HalpBopHandler
= KeQueryInterruptHandler(6);
511 // Now set our own GPF handler to handle exceptions while in real mode
513 KeRegisterInterruptHandler(13, HalpTrap0D
);
516 // And our own invalid opcode handler to detect the BOP to get us out
518 KeRegisterInterruptHandler(6, HalpTrap06
);
523 HalpSetupRealModeIoPermissionsAndTask(VOID
)
526 // Switch to valid TSS
531 // Save a copy of the I/O Map and delete it
533 HalpSavedIoMap
= (PUSHORT
)&(KeGetPcr()->TSS
->IoMaps
[0]);
534 HalpStoreAndClearIopm();
537 // Save the IOPM and switch to the real-mode one
539 HalpSavedIopmBase
= KeGetPcr()->TSS
->IoMapBase
;
540 KeGetPcr()->TSS
->IoMapBase
= KiComputeIopmOffset(1);
543 // Save our stack pointer
545 HalpSavedEsp0
= KeGetPcr()->TSS
->Esp0
;
550 HalpRestoreTrapHandlers(VOID
)
553 // Keep dummy GPF handler in case we get an NMI during V8086
555 if (!HalpNMIInProgress
)
558 // Not an NMI -- put back the original handler
560 KeRegisterInterruptHandler(13, HalpGpfHandler
);
564 // Restore invalid opcode handler
566 KeRegisterInterruptHandler(6, HalpBopHandler
);
571 HalpRestoreIoPermissionsAndTask(VOID
)
574 // Restore the stack pointer
576 KeGetPcr()->TSS
->Esp0
= HalpSavedEsp0
;
579 // Restore the I/O Map
586 KeGetPcr()->TSS
->IoMapBase
= HalpSavedIopmBase
;
591 if (HalpSavedTss
) HalpReturnTss();
596 HalpUnmapRealModeMemory(VOID
)
602 // Loop the first meg of memory
604 for (i
= 0; i
< 0x100000; i
+= PAGE_SIZE
)
607 // Invalidate each PTE
609 Pte
= HalAddressToPte(i
);
613 Pte
->PageFrameNumber
= 0;
617 // Restore the PDE for the lowest megabyte of memory
619 Pte
= HalAddressToPde(0);
621 Pte
->PageFrameNumber
= HalpSavedPfn
;
631 HalpBiosDisplayReset(VOID
)
634 PHARDWARE_PTE IdtPte
;
635 BOOLEAN RestoreWriteProtection
= FALSE
;
638 // Disable interrupts
640 Flags
= __readeflags();
644 // Map memory available to the V8086 real-mode code
646 HalpMapRealModeMemory();
649 // On P5, the first 7 entries of the IDT are write protected to work around
650 // the cmpxchg8b lock errata. Unprotect them here so we can set our custom
651 // invalid op-code handler.
653 IdtPte
= HalAddressToPte(((PKIPCR
)KeGetPcr())->IDT
);
654 RestoreWriteProtection
= IdtPte
->Write
;
658 // Use special invalid opcode and GPF trap handlers
660 HalpSwitchToRealModeTrapHandlers();
663 // Configure the IOPM and TSS
665 HalpSetupRealModeIoPermissionsAndTask();
668 // Now jump to real mode
673 // Restore kernel trap handlers
675 HalpRestoreTrapHandlers();
678 // Restore write permission
680 IdtPte
->Write
= RestoreWriteProtection
;
683 // Restore TSS and IOPM
685 HalpRestoreIoPermissionsAndTask();
688 // Restore low memory mapping
690 HalpUnmapRealModeMemory();
693 // Restore interrupts if they were previously enabled
695 __writeeflags(Flags
);