+++ /dev/null
-/*
- * PROJECT: ReactOS Hardware Abstraction Layer (HAL)
- * LICENSE: BSD - See COPYING.ARM in the top level directory
- * FILE: hal/halx86/generic/bios.c
- * PURPOSE: BIOS Access Routines
- * PROGRAMMERS: ReactOS Portable Systems Group
- * Alex Ionescu (alex.ionescu@reactos.org)
- */
-
-/* INCLUDES *******************************************************************/
-
-#include <hal.h>
-#define NDEBUG
-#include <debug.h>
-#include <setjmp.h>
-
-void __cdecl HalpTrap0D();
-
-/* GLOBALS ********************************************************************/
-
-//
-// PTE Data
-//
-ULONG HalpSavedPfn;
-HARDWARE_PTE HalpSavedPte;
-
-//
-// IDT Data
-//
-PVOID HalpGpfHandler;
-PVOID HalpBopHandler;
-
-//
-// TSS Data
-//
-ULONG HalpSavedEsp0;
-USHORT HalpSavedTss;
-
-//
-// IOPM Data
-//
-USHORT HalpSavedIopmBase;
-PUSHORT HalpSavedIoMap;
-USHORT HalpSavedIoMapData[32][2];
-ULONG HalpSavedIoMapEntries;
-
-/* Where the protected mode stack is */
-ULONG_PTR HalpSavedEsp;
-
-/* Where the real mode code ends */
-extern PVOID HalpRealModeEnd;
-
-/* Context saved for return from v86 mode */
-jmp_buf HalpSavedContext;
-
-
-/* V86 OPCODE HANDLERS ********************************************************/
-
-BOOLEAN
-FASTCALL
-HalpOpcodeInvalid(IN PHAL_BIOS_FRAME BiosFrame)
-{
- PUCHAR Inst = (PUCHAR)(BiosFrame->CsBase + BiosFrame->Eip);
-
- /* Print error message */
- DPRINT1("HAL: An invalid V86 opcode was encountered at address %X:%X\n"
- "Opcode: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X\n",
- BiosFrame->SegCs, BiosFrame->Eip,
- Inst[0], Inst[1], Inst[2], Inst[3], Inst[4],
- Inst[5], Inst[6], Inst[7], Inst[8], Inst[9]);
-
- /* Break */
- DbgBreakPoint();
- return FALSE;
-}
-
-BOOLEAN
-FASTCALL
-HalpPushInt(IN PHAL_BIOS_FRAME BiosFrame,
- IN ULONG Interrupt)
-{
- PUSHORT Stack;
- ULONG Eip;
-
- /* Calculate stack address (SP) */
- Stack = (PUSHORT)(BiosFrame->SsBase + (BiosFrame->Esp & 0xFFFF));
-
- /* Push EFlags */
- Stack--;
- *Stack = BiosFrame->EFlags & 0xFFFF;
-
- /* Push CS */
- Stack--;
- *Stack = BiosFrame->SegCs & 0xFFFF;
-
- /* Push IP */
- Stack--;
- *Stack = BiosFrame->Eip & 0xFFFF;
-
- /* Compute new CS:IP from the IVT address for this interrupt entry */
- Eip = *(PULONG)(Interrupt * 4);
- BiosFrame->Eip = Eip & 0xFFFF;
- BiosFrame->SegCs = Eip >> 16;
-
- /* Update stack address */
- BiosFrame->Esp = (ULONG_PTR)Stack & 0xFFFF;
-
- /* Update CS to linear */
- BiosFrame->CsBase = BiosFrame->SegCs << 4;
- BiosFrame->CsLimit = 0xFFFF;
- BiosFrame->CsFlags = 0;
-
- /* We're done */
- return TRUE;
-}
-
-BOOLEAN
-FASTCALL
-HalpOpcodeINTnn(IN PHAL_BIOS_FRAME BiosFrame)
-{
- UCHAR Interrupt;
- PKTRAP_FRAME TrapFrame;
-
- /* Convert SS to linear */
- BiosFrame->SsBase = BiosFrame->SegSs << 4;
- BiosFrame->SsLimit = 0xFFFF;
- BiosFrame->SsFlags = 0;
-
- /* Increase EIP and validate */
- BiosFrame->Eip++;
- if (BiosFrame->Eip > BiosFrame->CsLimit) return FALSE;
-
- /* Read interrupt number */
- Interrupt = *(PUCHAR)(BiosFrame->CsBase + BiosFrame->Eip);
-
- /* Increase EIP and push the interrupt */
- BiosFrame->Eip++;
- if (HalpPushInt(BiosFrame, Interrupt))
- {
- /* Update the trap frame */
- TrapFrame = BiosFrame->TrapFrame;
- TrapFrame->HardwareSegSs = BiosFrame->SegSs;
- TrapFrame->HardwareEsp = BiosFrame->Esp;
- TrapFrame->SegCs = BiosFrame->SegCs;
- TrapFrame->EFlags = BiosFrame->EFlags;
-
- /* Success */
- return TRUE;
- }
-
- /* Failure */
- return FALSE;
-}
-
-BOOLEAN
-FASTCALL
-HalpDispatchV86Opcode(IN PKTRAP_FRAME TrapFrame)
-{
- UCHAR Instruction;
- HAL_BIOS_FRAME BiosFrame;
-
- /* Fill out the BIOS frame */
- BiosFrame.TrapFrame = TrapFrame;
- BiosFrame.SegSs = TrapFrame->HardwareSegSs;
- BiosFrame.Esp = TrapFrame->HardwareEsp;
- BiosFrame.EFlags = TrapFrame->EFlags;
- BiosFrame.SegCs = TrapFrame->SegCs;
- BiosFrame.Eip = TrapFrame->Eip;
- BiosFrame.Prefix = 0;
-
- /* Convert CS to linear */
- BiosFrame.CsBase = BiosFrame.SegCs << 4;
- BiosFrame.CsLimit = 0xFFFF;
- BiosFrame.CsFlags = 0;
-
- /* Validate IP */
- if (BiosFrame.Eip > BiosFrame.CsLimit) return FALSE;
-
- /* Read IP */
- Instruction = *(PUCHAR)(BiosFrame.CsBase + BiosFrame.Eip);
- if (Instruction != 0xCD)
- {
- /* We only support INT */
- HalpOpcodeInvalid(&BiosFrame);
- return FALSE;
- }
-
- /* Handle the interrupt */
- if (HalpOpcodeINTnn(&BiosFrame))
- {
- /* Update EIP */
- TrapFrame->Eip = BiosFrame.Eip;
-
- /* We're done */
- return TRUE;
- }
-
- /* Failure */
- return FALSE;
-}
-
-/* V86 TRAP HANDLERS **********************************************************/
-
-#ifndef _MINIHAL_
-DECLSPEC_NORETURN
-VOID
-FASTCALL
-HalpTrap0DHandler(IN PKTRAP_FRAME TrapFrame)
-{
- /* Enter the trap */
- KiEnterTrap(TrapFrame);
-
- /* Check if this is a V86 trap */
- if (TrapFrame->EFlags & EFLAGS_V86_MASK)
- {
- /* Dispatch the opcode and exit the trap */
- HalpDispatchV86Opcode(TrapFrame);
- KiEoiHelper(TrapFrame);
- }
-
- /* Strange, it isn't! This can happen during NMI */
- DPRINT1("HAL: Trap0D while not in V86 mode\n");
- KiDumpTrapFrame(TrapFrame);
-
- ERROR_FATAL();
- while (TRUE); /* 'noreturn' function */
-}
-
-VOID
-DECLSPEC_NORETURN
-HalpTrap06(VOID)
-{
- /* Restore ES/DS to known good values first */
- Ke386SetEs(KGDT_R3_DATA | RPL_MASK);
- Ke386SetDs(KGDT_R3_DATA | RPL_MASK);
- Ke386SetFs(KGDT_R0_PCR);
-
- /* Restore the stack */
- KeGetPcr()->TSS->Esp0 = HalpSavedEsp0;
-
- /* Return back to where we left */
- longjmp(HalpSavedContext, 1);
- UNREACHABLE;
-}
-
-/* V8086 ENTER ****************************************************************/
-
-VOID
-NTAPI
-HalpBiosCall(VOID)
-{
- /* Must be volatile so it doesn't get optimized away! */
- volatile KTRAP_FRAME V86TrapFrame;
- ULONG_PTR StackOffset, CodeOffset;
-
- /* Save the context, check for return */
- if (_setjmp(HalpSavedContext))
- {
- /* Returned from v86 */
- return;
- }
-
- /* Kill alignment faults */
- __writecr0(__readcr0() & ~CR0_AM);
-
- /* Set new stack address */
- KeGetPcr()->TSS->Esp0 = (ULONG)&V86TrapFrame - 0x20 - sizeof(FX_SAVE_AREA);
-
- /* Compute segmented IP and SP offsets */
- StackOffset = (ULONG_PTR)&HalpRealModeEnd - 4 - (ULONG_PTR)HalpRealModeStart;
- CodeOffset = (ULONG_PTR)HalpRealModeStart & 0xFFF;
-
- /* Now build the V86 trap frame */
- V86TrapFrame.V86Es = 0;
- V86TrapFrame.V86Ds = 0;
- V86TrapFrame.V86Gs = 0;
- V86TrapFrame.V86Fs = 0;
- V86TrapFrame.HardwareSegSs = 0x2000;
- V86TrapFrame.HardwareEsp = StackOffset + CodeOffset;
- V86TrapFrame.EFlags = __readeflags() | EFLAGS_V86_MASK | EFLAGS_IOPL;
- V86TrapFrame.SegCs = 0x2000;
- V86TrapFrame.Eip = CodeOffset;
-
- /* Exit to V86 mode */
- HalpExitToV86((PKTRAP_FRAME)&V86TrapFrame);
-}
-#endif
-
-/* FUNCTIONS ******************************************************************/
-
-VOID
-NTAPI
-HalpBorrowTss(VOID)
-{
- USHORT Tss;
- PKGDTENTRY TssGdt;
- ULONG_PTR TssLimit;
- PKTSS TssBase;
-
- //
- // Get the current TSS and its GDT entry
- //
- Tss = Ke386GetTr();
- TssGdt = &((PKIPCR)KeGetPcr())->GDT[Tss / sizeof(KGDTENTRY)];
-
- //
- // Get the KTSS limit and check if it has IOPM space
- //
- TssLimit = TssGdt->LimitLow | TssGdt->HighWord.Bits.LimitHi << 16;
-
- //
- // If the KTSS doesn't have enough space this is probably an NMI or DF
- //
- if (TssLimit > IOPM_SIZE)
- {
- //
- // We are good to go
- //
- HalpSavedTss = 0;
- return;
- }
-
- //
- // Get the "real" TSS
- //
- TssGdt = &((PKIPCR)KeGetPcr())->GDT[KGDT_TSS / sizeof(KGDTENTRY)];
- TssBase = (PKTSS)(ULONG_PTR)(TssGdt->BaseLow |
- TssGdt->HighWord.Bytes.BaseMid << 16 |
- TssGdt->HighWord.Bytes.BaseHi << 24);
-
- //
- // Switch to it
- //
- KeGetPcr()->TSS = TssBase;
-
- //
- // Set it up
- //
- TssGdt->HighWord.Bits.Type = I386_TSS;
- TssGdt->HighWord.Bits.Pres = 1;
- TssGdt->HighWord.Bits.Dpl = 0;
-
- //
- // Load new TSS and return old one
- //
- Ke386SetTr(KGDT_TSS);
- HalpSavedTss = Tss;
-}
-
-VOID
-NTAPI
-HalpReturnTss(VOID)
-{
- PKGDTENTRY TssGdt;
- PKTSS TssBase;
-
- //
- // Get the original TSS
- //
- TssGdt = &((PKIPCR)KeGetPcr())->GDT[HalpSavedTss / sizeof(KGDTENTRY)];
- TssBase = (PKTSS)(ULONG_PTR)(TssGdt->BaseLow |
- TssGdt->HighWord.Bytes.BaseMid << 16 |
- TssGdt->HighWord.Bytes.BaseHi << 24);
-
- //
- // Switch to it
- //
- KeGetPcr()->TSS = TssBase;
-
- //
- // Set it up
- //
- TssGdt->HighWord.Bits.Type = I386_TSS;
- TssGdt->HighWord.Bits.Pres = 1;
- TssGdt->HighWord.Bits.Dpl = 0;
-
- //
- // Load old TSS
- //
- Ke386SetTr(HalpSavedTss);
-}
-
-VOID
-NTAPI
-HalpStoreAndClearIopm(VOID)
-{
- USHORT i, j;
- PUSHORT Entry = HalpSavedIoMap;
-
- //
- // Loop the I/O Map
- //
- for (i = j = 0; i < IOPM_SIZE / sizeof(USHORT); i++)
- {
- //
- // Check for non-FFFF entry
- //
- if (*Entry != 0xFFFF)
- {
- //
- // Save it
- //
- ASSERT(j < 32);
- HalpSavedIoMapData[j][0] = i;
- HalpSavedIoMapData[j][1] = *Entry;
- j++;
- }
-
- //
- // Clear it
- //
- *Entry++ = 0;
- }
-
- //
- // Terminate it
- //
- while (i++ < IOPM_FULL_SIZE / sizeof(USHORT))
- {
- *Entry++ = 0xFFFF;
- }
-
- //
- // Return the entries we saved
- //
- HalpSavedIoMapEntries = j;
-}
-
-VOID
-NTAPI
-HalpRestoreIopm(VOID)
-{
- ULONG i = HalpSavedIoMapEntries;
-
- //
- // Set default state
- //
- RtlFillMemory(HalpSavedIoMap, IOPM_FULL_SIZE, 0xFF);
-
- //
- // Restore the backed up copy, and initialize it
- //
- while (i--) HalpSavedIoMap[HalpSavedIoMapData[i][0]] = HalpSavedIoMapData[i][1];
-}
-
-#ifndef _MINIHAL_
-VOID
-NTAPI
-HalpMapRealModeMemory(VOID)
-{
- PHARDWARE_PTE Pte, V86Pte;
- ULONG i;
-
- //
- // Get the page table directory for the lowest meg of memory
- //
- Pte = HalAddressToPde(0);
- HalpSavedPfn = Pte->PageFrameNumber;
- HalpSavedPte = *Pte;
-
- //
- // Map it to the HAL reserved region and make it valid
- //
- Pte->Valid = 1;
- Pte->Write = 1;
- Pte->Owner = 1;
- Pte->PageFrameNumber = (HalAddressToPde(0xFFC00000))->PageFrameNumber;
-
- //
- // Flush the TLB
- //
- HalpFlushTLB();
-
- //
- // Now loop the first meg of memory
- //
- for (i = 0; i < 0x100000; i += PAGE_SIZE)
- {
- //
- // Identity map it
- //
- Pte = HalAddressToPte(i);
- Pte->PageFrameNumber = i >> PAGE_SHIFT;
- Pte->Valid = 1;
- Pte->Write = 1;
- Pte->Owner = 1;
- }
-
- //
- // Now get the entry for our real mode V86 code and the target
- //
- Pte = HalAddressToPte(0x20000);
- V86Pte = HalAddressToPte(&HalpRealModeStart);
- do
- {
- //
- // Map the physical address into our real-mode region
- //
- Pte->PageFrameNumber = V86Pte->PageFrameNumber;
-
- //
- // Keep going until we've reached the end of our region
- //
- Pte++;
- V86Pte++;
- } while (V86Pte <= HalAddressToPte(&HalpRealModeEnd));
-
- //
- // Flush the TLB
- //
- HalpFlushTLB();
-}
-
-VOID
-NTAPI
-HalpSwitchToRealModeTrapHandlers(VOID)
-{
- //
- // Save the current Invalid Opcode and General Protection Fault Handlers
- //
- HalpGpfHandler = KeQueryInterruptHandler(13);
- HalpBopHandler = KeQueryInterruptHandler(6);
-
- //
- // Now set our own GPF handler to handle exceptions while in real mode
- //
- KeRegisterInterruptHandler(13, HalpTrap0D);
-
- //
- // And our own invalid opcode handler to detect the BOP to get us out
- //
- KeRegisterInterruptHandler(6, HalpTrap06);
-}
-#endif
-
-VOID
-NTAPI
-HalpSetupRealModeIoPermissionsAndTask(VOID)
-{
- //
- // Switch to valid TSS
- //
- HalpBorrowTss();
-
- //
- // Save a copy of the I/O Map and delete it
- //
- HalpSavedIoMap = (PUSHORT)KeGetPcr()->TSS->IoMaps[0].IoMap;
- HalpStoreAndClearIopm();
-
- //
- // Save the IOPM and switch to the real-mode one
- //
- HalpSavedIopmBase = KeGetPcr()->TSS->IoMapBase;
- KeGetPcr()->TSS->IoMapBase = KiComputeIopmOffset(1);
-
- //
- // Save our stack pointer
- //
- HalpSavedEsp0 = KeGetPcr()->TSS->Esp0;
-}
-
-VOID
-NTAPI
-HalpRestoreTrapHandlers(VOID)
-{
- //
- // Keep dummy GPF handler in case we get an NMI during V8086
- //
- if (!HalpNMIInProgress)
- {
- //
- // Not an NMI -- put back the original handler
- //
- KeRegisterInterruptHandler(13, HalpGpfHandler);
- }
-
- //
- // Restore invalid opcode handler
- //
- KeRegisterInterruptHandler(6, HalpBopHandler);
-}
-
-VOID
-NTAPI
-HalpRestoreIoPermissionsAndTask(VOID)
-{
- //
- // Restore the stack pointer
- //
- KeGetPcr()->TSS->Esp0 = HalpSavedEsp0;
-
- //
- // Restore the I/O Map
- //
- HalpRestoreIopm();
-
- //
- // Restore the IOPM
- //
- KeGetPcr()->TSS->IoMapBase = HalpSavedIopmBase;
-
- //
- // Restore the TSS
- //
- if (HalpSavedTss) HalpReturnTss();
-}
-
-VOID
-NTAPI
-HalpUnmapRealModeMemory(VOID)
-{
- ULONG i;
- PHARDWARE_PTE Pte;
-
- //
- // Loop the first meg of memory
- //
- for (i = 0; i < 0x100000; i += PAGE_SIZE)
- {
- //
- // Invalidate each PTE
- //
- Pte = HalAddressToPte(i);
- Pte->Valid = 0;
- Pte->Write = 0;
- Pte->Owner = 0;
- Pte->PageFrameNumber = 0;
- }
-
- //
- // Restore the PDE for the lowest megabyte of memory
- //
- Pte = HalAddressToPde(0);
- *Pte = HalpSavedPte;
- Pte->PageFrameNumber = HalpSavedPfn;
-
- //
- // Flush the TLB
- //
- HalpFlushTLB();
-}
-
-#ifndef _MINIHAL_
-BOOLEAN
-NTAPI
-HalpBiosDisplayReset(VOID)
-{
- ULONG Flags;
- PHARDWARE_PTE IdtPte;
- BOOLEAN RestoreWriteProtection = FALSE;
-
- //
- // Disable interrupts
- //
- Flags = __readeflags();
- _disable();
-
- //
- // Map memory available to the V8086 real-mode code
- //
- HalpMapRealModeMemory();
-
- //
- // On P5, the first 7 entries of the IDT are write protected to work around
- // the cmpxchg8b lock errata. Unprotect them here so we can set our custom
- // invalid op-code handler.
- //
- IdtPte = HalAddressToPte(((PKIPCR)KeGetPcr())->IDT);
- RestoreWriteProtection = IdtPte->Write != 0;
- IdtPte->Write = 1;
-
- //
- // Use special invalid opcode and GPF trap handlers
- //
- HalpSwitchToRealModeTrapHandlers();
-
- //
- // Configure the IOPM and TSS
- //
- HalpSetupRealModeIoPermissionsAndTask();
-
- //
- // Now jump to real mode
- //
- HalpBiosCall();
-
- //
- // Restore kernel trap handlers
- //
- HalpRestoreTrapHandlers();
-
- //
- // Restore write permission
- //
- IdtPte->Write = RestoreWriteProtection;
-
- //
- // Restore TSS and IOPM
- //
- HalpRestoreIoPermissionsAndTask();
-
- //
- // Restore low memory mapping
- //
- HalpUnmapRealModeMemory();
-
- //
- // Restore interrupts if they were previously enabled
- //
- __writeeflags(Flags);
- return TRUE;
-}
-#endif
-
-/* EOF */