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 *******************************************************************/
16 /* GLOBALS ********************************************************************/
22 HARDWARE_PTE HalpSavedPte
;
39 USHORT HalpSavedIopmBase
;
40 PUSHORT HalpSavedIoMap
;
41 USHORT HalpSavedIoMapData
[32][2];
42 ULONG HalpSavedIoMapEntries
;
44 /* Where the protected mode stack is */
45 ULONG_PTR HalpSavedEsp
;
47 /* Where the real mode code ends */
48 extern PVOID HalpRealModeEnd
;
50 /* REAL MODE CODE AND STACK START HERE ****************************************/
54 HalpRealModeStart(VOID
)
56 /* Do the video BIOS interrupt */
57 HalpCallBiosInterrupt(VIDEO_SERVICES
, (SET_VIDEO_MODE
<< 8) | (GRAPHICS_MODE_12
));
62 /* We want the stack to be inside this function so we can map real mode */
63 HalpRealModeStack(sizeof(ULONG
), PAGE_SIZE
/ 2);
67 /* REAL MODE CODE AND STACK END HERE ******************************************/
69 /* V86 OPCODE HANDLERS ********************************************************/
73 HalpOpcodeInvalid(IN PHAL_BIOS_FRAME BiosFrame
)
75 /* Print error message */
76 DPRINT1("HAL: An invalid V86 opcode was encountered at address %x:%x\n",
77 BiosFrame
->SegCs
, BiosFrame
->Eip
);
86 HalpPushInt(IN PHAL_BIOS_FRAME BiosFrame
,
92 /* Calculate stack address (SP) */
93 Stack
= (PUSHORT
)(BiosFrame
->SsBase
+ (BiosFrame
->Esp
& 0xFFFF));
97 *Stack
= BiosFrame
->EFlags
& 0xFFFF;
101 *Stack
= BiosFrame
->SegCs
& 0xFFFF;
105 *Stack
= BiosFrame
->Eip
& 0xFFFF;
107 /* Compute new CS:IP from the IVT address for this interrupt entry */
108 Eip
= *(PULONG
)(Interrupt
* 4);
109 BiosFrame
->Eip
= Eip
& 0xFFFF;
110 BiosFrame
->SegCs
= Eip
>> 16;
112 /* Update stack address */
113 BiosFrame
->Esp
= (ULONG_PTR
)Stack
& 0xFFFF;
115 /* Update CS to linear */
116 BiosFrame
->CsBase
= BiosFrame
->SegCs
<< 4;
117 BiosFrame
->CsLimit
= 0xFFFF;
118 BiosFrame
->CsFlags
= 0;
126 HalpOpcodeINTnn(IN PHAL_BIOS_FRAME BiosFrame
)
129 PKTRAP_FRAME TrapFrame
;
131 /* Convert SS to linear */
132 BiosFrame
->SsBase
= BiosFrame
->SegSs
<< 4;
133 BiosFrame
->SsLimit
= 0xFFFF;
134 BiosFrame
->SsFlags
= 0;
136 /* Increase EIP and validate */
138 if (BiosFrame
->Eip
> BiosFrame
->CsLimit
) return FALSE
;
140 /* Read interrupt number */
141 Interrupt
= *(PUCHAR
)(BiosFrame
->CsBase
+ BiosFrame
->Eip
);
143 /* Increase EIP and push the interrupt */
145 if (HalpPushInt(BiosFrame
, Interrupt
))
147 /* Update the trap frame */
148 TrapFrame
= BiosFrame
->TrapFrame
;
149 TrapFrame
->HardwareSegSs
= BiosFrame
->SegSs
;
150 TrapFrame
->HardwareEsp
= BiosFrame
->Esp
;
151 TrapFrame
->SegCs
= BiosFrame
->SegCs
;
152 TrapFrame
->EFlags
= BiosFrame
->EFlags
;
164 HalpDispatchV86Opcode(IN PKTRAP_FRAME TrapFrame
)
167 HAL_BIOS_FRAME BiosFrame
;
169 /* Fill out the BIOS frame */
170 BiosFrame
.TrapFrame
= TrapFrame
;
171 BiosFrame
.SegSs
= TrapFrame
->HardwareSegSs
;
172 BiosFrame
.Esp
= TrapFrame
->HardwareEsp
;
173 BiosFrame
.EFlags
= TrapFrame
->EFlags
;
174 BiosFrame
.SegCs
= TrapFrame
->SegCs
;
175 BiosFrame
.Eip
= TrapFrame
->Eip
;
176 BiosFrame
.Prefix
= 0;
178 /* Convert CS to linear */
179 BiosFrame
.CsBase
= BiosFrame
.SegCs
<< 4;
180 BiosFrame
.CsLimit
= 0xFFFF;
181 BiosFrame
.CsFlags
= 0;
184 if (BiosFrame
.Eip
> BiosFrame
.CsLimit
) return FALSE
;
187 Instruction
= *(PUCHAR
)(BiosFrame
.CsBase
+ BiosFrame
.Eip
);
188 if (Instruction
!= 0xCD)
190 /* We only support INT */
191 HalpOpcodeInvalid(&BiosFrame
);
195 /* Handle the interrupt */
196 if (HalpOpcodeINTnn(&BiosFrame
))
199 TrapFrame
->Eip
= BiosFrame
.Eip
;
209 /* V86 TRAP HANDLERS **********************************************************/
214 HalpTrap0DHandler(IN PKTRAP_FRAME TrapFrame
)
217 KiEnterTrap(TrapFrame
);
219 /* Check if this is a V86 trap */
220 if (TrapFrame
->EFlags
& EFLAGS_V86_MASK
)
222 /* Dispatch the opcode and exit the trap */
223 HalpDispatchV86Opcode(TrapFrame
);
224 KiEoiHelper(TrapFrame
);
227 /* Strange, it isn't! This can happen during NMI */
228 DPRINT1("HAL: Trap0D while not in V86 mode\n");
232 KiTrap(HalpTrap0D
, 0);
238 PKTRAP_FRAME TrapFrame
;
240 /* Restore ES/DS to known good values first */
241 Ke386SetEs(KGDT_R3_DATA
| RPL_MASK
);
242 Ke386SetDs(KGDT_R3_DATA
| RPL_MASK
);
244 /* Read trap frame address */
245 TrapFrame
= (PKTRAP_FRAME
)HalpSavedEsp
;
247 /* Restore segments from the trap frame */
248 Ke386SetGs(TrapFrame
->SegGs
);
249 Ke386SetFs(TrapFrame
->SegFs
);
250 Ke386SetEs(TrapFrame
->SegEs
);
251 Ke386SetDs(TrapFrame
->SegDs
);
254 __writeeflags(TrapFrame
->EFlags
);
256 /* Exit the V86 mode trap frame */
257 KiCallReturn(TrapFrame
);
260 /* V8086 ENTER ****************************************************************/
265 HalpBiosCallHandler(IN PKTRAP_FRAME TrapFrame
)
267 /* Must be volatile so it doesn't get optimized away! */
268 volatile KTRAP_FRAME V86TrapFrame
;
269 ULONG_PTR StackOffset
, CodeOffset
;
271 /* Fill out the quick-n-dirty trap frame */
272 TrapFrame
->EFlags
= __readeflags();
273 TrapFrame
->SegGs
= Ke386GetGs();
274 TrapFrame
->SegFs
= Ke386GetFs();
275 TrapFrame
->SegEs
= Ke386GetEs();
276 TrapFrame
->SegDs
= Ke386GetDs();
278 /* Our stack (the frame) */
279 HalpSavedEsp
= (ULONG_PTR
)TrapFrame
;
281 /* Kill alignment faults */
282 __writecr0(__readcr0() & ~CR0_AM
);
284 /* Set new stack address */
285 KeGetPcr()->TSS
->Esp0
= HalpSavedEsp
- sizeof(FX_SAVE_AREA
);
287 /* Compute segmented IP and SP offsets */
288 StackOffset
= (ULONG_PTR
)&HalpRealModeEnd
- 4 - (ULONG_PTR
)HalpRealModeStart
;
289 CodeOffset
= (ULONG_PTR
)HalpRealModeStart
& 0xFFF;
291 /* Now build the V86 trap frame */
292 V86TrapFrame
.V86Es
= 0;
293 V86TrapFrame
.V86Ds
= 0;
294 V86TrapFrame
.V86Gs
= 0;
295 V86TrapFrame
.V86Fs
= 0;
296 V86TrapFrame
.HardwareSegSs
= 0x2000;
297 V86TrapFrame
.HardwareEsp
= StackOffset
+ CodeOffset
;
298 V86TrapFrame
.EFlags
= __readeflags() | EFLAGS_V86_MASK
| EFLAGS_IOPL
;
299 V86TrapFrame
.SegCs
= 0x2000;
300 V86TrapFrame
.Eip
= CodeOffset
;
302 /* Exit to V86 mode */
303 KiDirectTrapReturn((PKTRAP_FRAME
)&V86TrapFrame
);
306 KiTrampoline(HalpBiosCall
, KI_PUSH_FAKE_ERROR_CODE
| KI_NONVOLATILES_ONLY
);
308 /* FUNCTIONS ******************************************************************/
320 // Get the current TSS and its GDT entry
323 TssGdt
= &((PKIPCR
)KeGetPcr())->GDT
[Tss
/ sizeof(KGDTENTRY
)];
326 // Get the KTSS limit and check if it has IOPM space
328 TssLimit
= TssGdt
->LimitLow
| TssGdt
->HighWord
.Bits
.LimitHi
<< 16;
331 // If the KTSS doesn't have enough space this is probably an NMI or DF
333 if (TssLimit
> IOPM_SIZE
)
343 // Get the "real" TSS
345 TssGdt
= &((PKIPCR
)KeGetPcr())->GDT
[KGDT_TSS
/ sizeof(KGDTENTRY
)];
346 TssBase
= (PKTSS
)(ULONG_PTR
)(TssGdt
->BaseLow
|
347 TssGdt
->HighWord
.Bytes
.BaseMid
<< 16 |
348 TssGdt
->HighWord
.Bytes
.BaseHi
<< 24);
353 KeGetPcr()->TSS
= TssBase
;
358 TssGdt
->HighWord
.Bits
.Type
= I386_TSS
;
359 TssGdt
->HighWord
.Bits
.Pres
= 1;
360 TssGdt
->HighWord
.Bits
.Dpl
= 0;
363 // Load new TSS and return old one
365 Ke386SetTr(KGDT_TSS
);
377 // Get the original TSS
379 TssGdt
= &((PKIPCR
)KeGetPcr())->GDT
[HalpSavedTss
/ sizeof(KGDTENTRY
)];
380 TssBase
= (PKTSS
)(ULONG_PTR
)(TssGdt
->BaseLow
|
381 TssGdt
->HighWord
.Bytes
.BaseMid
<< 16 |
382 TssGdt
->HighWord
.Bytes
.BaseHi
<< 24);
387 KeGetPcr()->TSS
= TssBase
;
392 TssGdt
->HighWord
.Bits
.Type
= I386_TSS
;
393 TssGdt
->HighWord
.Bits
.Pres
= 1;
394 TssGdt
->HighWord
.Bits
.Dpl
= 0;
399 Ke386SetTr(HalpSavedTss
);
404 HalpStoreAndClearIopm(VOID
)
407 PUSHORT Entry
= HalpSavedIoMap
;
412 for (i
= j
= 0; i
< (IOPM_SIZE
) / 2; i
++)
415 // Check for non-FFFF entry
417 if (*Entry
!= 0xFFFF)
423 HalpSavedIoMapData
[j
][0] = i
;
424 HalpSavedIoMapData
[j
][1] = *Entry
;
436 while (i
++ < (IOPM_FULL_SIZE
/ 2)) *Entry
++ = 0xFFFF;
439 // Return the entries we saved
441 HalpSavedIoMapEntries
= j
;
446 HalpRestoreIopm(VOID
)
448 ULONG i
= HalpSavedIoMapEntries
;
453 RtlFillMemory(HalpSavedIoMap
, 0xFF, IOPM_FULL_SIZE
);
456 // Restore the backed up copy, and initialize it
458 while (i
--) HalpSavedIoMap
[HalpSavedIoMapData
[i
][0]] = HalpSavedIoMapData
[i
][1];
463 HalpMapRealModeMemory(VOID
)
465 PHARDWARE_PTE Pte
, V86Pte
;
469 // Get the page table directory for the lowest meg of memory
471 Pte
= HalAddressToPde(0);
472 HalpSavedPfn
= Pte
->PageFrameNumber
;
476 // Map it to the HAL reserved region and make it valid
481 Pte
->PageFrameNumber
= (HalAddressToPde(0xFFC00000))->PageFrameNumber
;
489 // Now loop the first meg of memory
491 for (i
= 0; i
< 0x100000; i
+= PAGE_SIZE
)
496 Pte
= HalAddressToPte(i
);
497 Pte
->PageFrameNumber
= i
>> PAGE_SHIFT
;
504 // Now get the entry for our real mode V86 code and the target
506 Pte
= HalAddressToPte(0x20000);
507 V86Pte
= HalAddressToPte(&HalpRealModeStart
);
511 // Map the physical address into our real-mode region
513 Pte
->PageFrameNumber
= V86Pte
->PageFrameNumber
;
516 // Keep going until we've reached the end of our region
520 } while (V86Pte
<= HalAddressToPte(&HalpRealModeEnd
));
530 HalpSwitchToRealModeTrapHandlers(VOID
)
533 // Save the current Invalid Opcode and General Protection Fault Handlers
535 HalpGpfHandler
= KeQueryInterruptHandler(13);
536 HalpBopHandler
= KeQueryInterruptHandler(6);
539 // Now set our own GPF handler to handle exceptions while in real mode
541 KeRegisterInterruptHandler(13, HalpTrap0D
);
544 // And our own invalid opcode handler to detect the BOP to get us out
546 KeRegisterInterruptHandler(6, HalpTrap06
);
551 HalpSetupRealModeIoPermissionsAndTask(VOID
)
554 // Switch to valid TSS
559 // Save a copy of the I/O Map and delete it
561 HalpSavedIoMap
= (PUSHORT
)&(KeGetPcr()->TSS
->IoMaps
[0]);
562 HalpStoreAndClearIopm();
565 // Save the IOPM and switch to the real-mode one
567 HalpSavedIopmBase
= KeGetPcr()->TSS
->IoMapBase
;
568 KeGetPcr()->TSS
->IoMapBase
= KiComputeIopmOffset(1);
571 // Save our stack pointer
573 HalpSavedEsp0
= KeGetPcr()->TSS
->Esp0
;
578 HalpRestoreTrapHandlers(VOID
)
581 // Keep dummy GPF handler in case we get an NMI during V8086
583 if (!HalpNMIInProgress
)
586 // Not an NMI -- put back the original handler
588 KeRegisterInterruptHandler(13, HalpGpfHandler
);
592 // Restore invalid opcode handler
594 KeRegisterInterruptHandler(6, HalpBopHandler
);
599 HalpRestoreIoPermissionsAndTask(VOID
)
602 // Restore the stack pointer
604 KeGetPcr()->TSS
->Esp0
= HalpSavedEsp0
;
607 // Restore the I/O Map
614 KeGetPcr()->TSS
->IoMapBase
= HalpSavedIopmBase
;
619 if (HalpSavedTss
) HalpReturnTss();
624 HalpUnmapRealModeMemory(VOID
)
630 // Loop the first meg of memory
632 for (i
= 0; i
< 0x100000; i
+= PAGE_SIZE
)
635 // Invalidate each PTE
637 Pte
= HalAddressToPte(i
);
641 Pte
->PageFrameNumber
= 0;
645 // Restore the PDE for the lowest megabyte of memory
647 Pte
= HalAddressToPde(0);
649 Pte
->PageFrameNumber
= HalpSavedPfn
;
659 HalpBiosDisplayReset(VOID
)
662 PHARDWARE_PTE IdtPte
;
663 BOOLEAN RestoreWriteProtection
= FALSE
;
666 // Disable interrupts
668 Flags
= __readeflags();
672 // Map memory available to the V8086 real-mode code
674 HalpMapRealModeMemory();
677 // On P5, the first 7 entries of the IDT are write protected to work around
678 // the cmpxchg8b lock errata. Unprotect them here so we can set our custom
679 // invalid op-code handler.
681 IdtPte
= HalAddressToPte(((PKIPCR
)KeGetPcr())->IDT
);
682 RestoreWriteProtection
= IdtPte
->Write
;
686 // Use special invalid opcode and GPF trap handlers
688 HalpSwitchToRealModeTrapHandlers();
691 // Configure the IOPM and TSS
693 HalpSetupRealModeIoPermissionsAndTask();
696 // Now jump to real mode
701 // Restore kernel trap handlers
703 HalpRestoreTrapHandlers();
706 // Restore write permission
708 IdtPte
->Write
= RestoreWriteProtection
;
711 // Restore TSS and IOPM
713 HalpRestoreIoPermissionsAndTask();
716 // Restore low memory mapping
718 HalpUnmapRealModeMemory();
721 // Restore interrupts if they were previously enabled
723 __writeeflags(Flags
);