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 **********************************************************/
203 HalpTrap0DHandler(IN PKTRAP_FRAME TrapFrame
)
206 KiEnterTrap(TrapFrame
);
208 /* Check if this is a V86 trap */
209 if (TrapFrame
->EFlags
& EFLAGS_V86_MASK
)
211 /* Dispatch the opcode and exit the trap */
212 HalpDispatchV86Opcode(TrapFrame
);
213 KiEoiHelper(TrapFrame
);
216 /* Strange, it isn't! This can happen during NMI */
217 DPRINT1("HAL: Trap0D while not in V86 mode\n");
218 KiDumpTrapFrame(TrapFrame
);
226 /* Restore ES/DS to known good values first */
227 Ke386SetEs(KGDT_R3_DATA
| RPL_MASK
);
228 Ke386SetDs(KGDT_R3_DATA
| RPL_MASK
);
229 Ke386SetFs(KGDT_R0_PCR
);
231 /* Restore the stack */
232 KeGetPcr()->TSS
->Esp0
= HalpSavedEsp0
;
234 /* Return back to where we left */
235 longjmp(HalpSavedContext
, 1);
239 /* V8086 ENTER ****************************************************************/
245 /* Must be volatile so it doesn't get optimized away! */
246 volatile KTRAP_FRAME V86TrapFrame
;
247 ULONG_PTR StackOffset
, CodeOffset
;
249 /* Save the context, check for return */
250 if (_setjmp(HalpSavedContext
))
252 /* Returned from v86 */
256 /* Kill alignment faults */
257 __writecr0(__readcr0() & ~CR0_AM
);
259 /* Set new stack address */
260 KeGetPcr()->TSS
->Esp0
= (ULONG
)&V86TrapFrame
- 0x20 - sizeof(FX_SAVE_AREA
);
262 /* Compute segmented IP and SP offsets */
263 StackOffset
= (ULONG_PTR
)&HalpRealModeEnd
- 4 - (ULONG_PTR
)HalpRealModeStart
;
264 CodeOffset
= (ULONG_PTR
)HalpRealModeStart
& 0xFFF;
266 /* Now build the V86 trap frame */
267 V86TrapFrame
.V86Es
= 0;
268 V86TrapFrame
.V86Ds
= 0;
269 V86TrapFrame
.V86Gs
= 0;
270 V86TrapFrame
.V86Fs
= 0;
271 V86TrapFrame
.HardwareSegSs
= 0x2000;
272 V86TrapFrame
.HardwareEsp
= StackOffset
+ CodeOffset
;
273 V86TrapFrame
.EFlags
= __readeflags() | EFLAGS_V86_MASK
| EFLAGS_IOPL
;
274 V86TrapFrame
.SegCs
= 0x2000;
275 V86TrapFrame
.Eip
= CodeOffset
;
277 /* Exit to V86 mode */
278 HalpExitToV86((PKTRAP_FRAME
)&V86TrapFrame
);
282 /* FUNCTIONS ******************************************************************/
294 // Get the current TSS and its GDT entry
297 TssGdt
= &((PKIPCR
)KeGetPcr())->GDT
[Tss
/ sizeof(KGDTENTRY
)];
300 // Get the KTSS limit and check if it has IOPM space
302 TssLimit
= TssGdt
->LimitLow
| TssGdt
->HighWord
.Bits
.LimitHi
<< 16;
305 // If the KTSS doesn't have enough space this is probably an NMI or DF
307 if (TssLimit
> IOPM_SIZE
)
317 // Get the "real" TSS
319 TssGdt
= &((PKIPCR
)KeGetPcr())->GDT
[KGDT_TSS
/ sizeof(KGDTENTRY
)];
320 TssBase
= (PKTSS
)(ULONG_PTR
)(TssGdt
->BaseLow
|
321 TssGdt
->HighWord
.Bytes
.BaseMid
<< 16 |
322 TssGdt
->HighWord
.Bytes
.BaseHi
<< 24);
327 KeGetPcr()->TSS
= TssBase
;
332 TssGdt
->HighWord
.Bits
.Type
= I386_TSS
;
333 TssGdt
->HighWord
.Bits
.Pres
= 1;
334 TssGdt
->HighWord
.Bits
.Dpl
= 0;
337 // Load new TSS and return old one
339 Ke386SetTr(KGDT_TSS
);
351 // Get the original TSS
353 TssGdt
= &((PKIPCR
)KeGetPcr())->GDT
[HalpSavedTss
/ sizeof(KGDTENTRY
)];
354 TssBase
= (PKTSS
)(ULONG_PTR
)(TssGdt
->BaseLow
|
355 TssGdt
->HighWord
.Bytes
.BaseMid
<< 16 |
356 TssGdt
->HighWord
.Bytes
.BaseHi
<< 24);
361 KeGetPcr()->TSS
= TssBase
;
366 TssGdt
->HighWord
.Bits
.Type
= I386_TSS
;
367 TssGdt
->HighWord
.Bits
.Pres
= 1;
368 TssGdt
->HighWord
.Bits
.Dpl
= 0;
373 Ke386SetTr(HalpSavedTss
);
378 HalpStoreAndClearIopm(VOID
)
381 PUSHORT Entry
= HalpSavedIoMap
;
386 for (i
= j
= 0; i
< (IOPM_SIZE
) / 2; i
++)
389 // Check for non-FFFF entry
391 if (*Entry
!= 0xFFFF)
397 HalpSavedIoMapData
[j
][0] = i
;
398 HalpSavedIoMapData
[j
][1] = *Entry
;
411 while (i
++ < (IOPM_FULL_SIZE
/ 2)) *Entry
++ = 0xFFFF;
414 // Return the entries we saved
416 HalpSavedIoMapEntries
= j
;
421 HalpRestoreIopm(VOID
)
423 ULONG i
= HalpSavedIoMapEntries
;
428 RtlFillMemory(HalpSavedIoMap
, IOPM_FULL_SIZE
, 0xFF);
431 // Restore the backed up copy, and initialize it
433 while (i
--) HalpSavedIoMap
[HalpSavedIoMapData
[i
][0]] = HalpSavedIoMapData
[i
][1];
439 HalpMapRealModeMemory(VOID
)
441 PHARDWARE_PTE Pte
, V86Pte
;
445 // Get the page table directory for the lowest meg of memory
447 Pte
= HalAddressToPde(0);
448 HalpSavedPfn
= Pte
->PageFrameNumber
;
452 // Map it to the HAL reserved region and make it valid
457 Pte
->PageFrameNumber
= (HalAddressToPde(0xFFC00000))->PageFrameNumber
;
465 // Now loop the first meg of memory
467 for (i
= 0; i
< 0x100000; i
+= PAGE_SIZE
)
472 Pte
= HalAddressToPte(i
);
473 Pte
->PageFrameNumber
= i
>> PAGE_SHIFT
;
480 // Now get the entry for our real mode V86 code and the target
482 Pte
= HalAddressToPte(0x20000);
483 V86Pte
= HalAddressToPte(&HalpRealModeStart
);
487 // Map the physical address into our real-mode region
489 Pte
->PageFrameNumber
= V86Pte
->PageFrameNumber
;
492 // Keep going until we've reached the end of our region
496 } while (V86Pte
<= HalAddressToPte(&HalpRealModeEnd
));
506 HalpSwitchToRealModeTrapHandlers(VOID
)
509 // Save the current Invalid Opcode and General Protection Fault Handlers
511 HalpGpfHandler
= KeQueryInterruptHandler(13);
512 HalpBopHandler
= KeQueryInterruptHandler(6);
515 // Now set our own GPF handler to handle exceptions while in real mode
517 KeRegisterInterruptHandler(13, HalpTrap0D
);
520 // And our own invalid opcode handler to detect the BOP to get us out
522 KeRegisterInterruptHandler(6, HalpTrap06
);
528 HalpSetupRealModeIoPermissionsAndTask(VOID
)
531 // Switch to valid TSS
536 // Save a copy of the I/O Map and delete it
538 HalpSavedIoMap
= (PUSHORT
)&(KeGetPcr()->TSS
->IoMaps
[0]);
539 HalpStoreAndClearIopm();
542 // Save the IOPM and switch to the real-mode one
544 HalpSavedIopmBase
= KeGetPcr()->TSS
->IoMapBase
;
545 KeGetPcr()->TSS
->IoMapBase
= KiComputeIopmOffset(1);
548 // Save our stack pointer
550 HalpSavedEsp0
= KeGetPcr()->TSS
->Esp0
;
555 HalpRestoreTrapHandlers(VOID
)
558 // Keep dummy GPF handler in case we get an NMI during V8086
560 if (!HalpNMIInProgress
)
563 // Not an NMI -- put back the original handler
565 KeRegisterInterruptHandler(13, HalpGpfHandler
);
569 // Restore invalid opcode handler
571 KeRegisterInterruptHandler(6, HalpBopHandler
);
576 HalpRestoreIoPermissionsAndTask(VOID
)
579 // Restore the stack pointer
581 KeGetPcr()->TSS
->Esp0
= HalpSavedEsp0
;
584 // Restore the I/O Map
591 KeGetPcr()->TSS
->IoMapBase
= HalpSavedIopmBase
;
596 if (HalpSavedTss
) HalpReturnTss();
601 HalpUnmapRealModeMemory(VOID
)
607 // Loop the first meg of memory
609 for (i
= 0; i
< 0x100000; i
+= PAGE_SIZE
)
612 // Invalidate each PTE
614 Pte
= HalAddressToPte(i
);
618 Pte
->PageFrameNumber
= 0;
622 // Restore the PDE for the lowest megabyte of memory
624 Pte
= HalAddressToPde(0);
626 Pte
->PageFrameNumber
= HalpSavedPfn
;
637 HalpBiosDisplayReset(VOID
)
640 PHARDWARE_PTE IdtPte
;
641 BOOLEAN RestoreWriteProtection
= FALSE
;
644 // Disable interrupts
646 Flags
= __readeflags();
650 // Map memory available to the V8086 real-mode code
652 HalpMapRealModeMemory();
655 // On P5, the first 7 entries of the IDT are write protected to work around
656 // the cmpxchg8b lock errata. Unprotect them here so we can set our custom
657 // invalid op-code handler.
659 IdtPte
= HalAddressToPte(((PKIPCR
)KeGetPcr())->IDT
);
660 RestoreWriteProtection
= IdtPte
->Write
!= 0;
664 // Use special invalid opcode and GPF trap handlers
666 HalpSwitchToRealModeTrapHandlers();
669 // Configure the IOPM and TSS
671 HalpSetupRealModeIoPermissionsAndTask();
674 // Now jump to real mode
679 // Restore kernel trap handlers
681 HalpRestoreTrapHandlers();
684 // Restore write permission
686 IdtPte
->Write
= RestoreWriteProtection
;
689 // Restore TSS and IOPM
691 HalpRestoreIoPermissionsAndTask();
694 // Restore low memory mapping
696 HalpUnmapRealModeMemory();
699 // Restore interrupts if they were previously enabled
701 __writeeflags(Flags
);