492 lines
10 KiB
C
492 lines
10 KiB
C
/*++
|
||
|
||
Copyright (c) 1991 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
xxbiosc.c
|
||
|
||
Abstract:
|
||
|
||
This module implements the protect-mode routines necessary to make the
|
||
transition to real mode and return to protected mode.
|
||
|
||
Author:
|
||
|
||
John Vert (jvert) 29-Oct-1991
|
||
|
||
|
||
Environment:
|
||
|
||
Kernel mode only.
|
||
Probably a panic-stop, so we cannot use any system services.
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
#include "halp.h"
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma alloc_text(PAGE, HalpGetDisplayBiosInformation)
|
||
#endif // ALLOC_PRAGMA
|
||
|
||
//
|
||
// The IOPM should be mostly 0xff. However it is possible a few
|
||
// bits may be cleared. Build a table of what's not 0xff.
|
||
//
|
||
|
||
#define MAX_DIFFERENCES 10
|
||
|
||
typedef struct _IOPM_DIFF_ENTRY
|
||
{
|
||
USHORT Entry;
|
||
USHORT Value;
|
||
} IOPM_DIFF_ENTRY, *PIOPM_DIFF_ENTRY;
|
||
|
||
//
|
||
// Function definitions
|
||
//
|
||
|
||
|
||
ULONG
|
||
HalpBorrowTss(
|
||
VOID
|
||
);
|
||
|
||
VOID
|
||
HalpReturnTss(
|
||
ULONG TssSelector
|
||
);
|
||
|
||
VOID
|
||
HalpBiosCall(
|
||
VOID
|
||
);
|
||
|
||
|
||
VOID
|
||
HalpTrap06(
|
||
VOID
|
||
);
|
||
|
||
|
||
VOID
|
||
HalpTrap0D(
|
||
VOID
|
||
);
|
||
|
||
|
||
ULONG
|
||
HalpStoreAndClearIopm(
|
||
PVOID Iopm,
|
||
PIOPM_DIFF_ENTRY IopmDiffTable,
|
||
ULONG MaxIopmTableEntries
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
The primary function of this routine is to clear all the bits in the
|
||
IOPM. However, we will need to recover any of our changes later.
|
||
|
||
It is very likely that the IOPM will be all 0xff's. If there are
|
||
deviations from this, they should be minimal. So lets only store what's
|
||
different.
|
||
|
||
Arguments:
|
||
|
||
Iopm - Pointer to the IOPM to clear.
|
||
|
||
IopmDiffTable - Pointer to the table of IOPM deviations from 0xff.
|
||
|
||
MaxIopmTableEntries - The maximum number of entries in our table.
|
||
|
||
Returns:
|
||
|
||
Number of entries added to the table.
|
||
|
||
--*/
|
||
|
||
{
|
||
PUSHORT IoMap = Iopm;
|
||
ULONG IopmDiffTableEntries = 0;
|
||
ULONG i;
|
||
|
||
for (i=0; i<(IOPM_SIZE / 2); i++) {
|
||
|
||
if (*IoMap != 0xffff) {
|
||
if (IopmDiffTableEntries < MaxIopmTableEntries) {
|
||
IopmDiffTable[IopmDiffTableEntries].Entry = (USHORT) i;
|
||
IopmDiffTable[IopmDiffTableEntries].Value = *IoMap;
|
||
IopmDiffTableEntries++;
|
||
} else {
|
||
ASSERT(IopmDiffTableEntries < MaxIopmTableEntries);
|
||
}
|
||
}
|
||
*IoMap++ = 0;
|
||
}
|
||
|
||
//
|
||
// The end of the IOPM table must be followed by a string of FF's.
|
||
//
|
||
|
||
while (i < (PIOPM_SIZE / 2)) {
|
||
*IoMap++ = 0xffff;
|
||
i++;
|
||
}
|
||
|
||
return IopmDiffTableEntries;
|
||
}
|
||
|
||
|
||
VOID
|
||
HalpRestoreIopm(
|
||
PVOID Iopm,
|
||
PIOPM_DIFF_ENTRY IopmDiffTable,
|
||
ULONG IopmTableEntries
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
We expect that most IOPM's will be all FF's. So we'll reset to that
|
||
state, and then we'll apply any changes from our differences table.
|
||
|
||
Arguments:
|
||
|
||
Iopm - Pointer to the IOPM to restore.
|
||
|
||
IopmDiffTable - Pointer to the table of IOPM deviations from 0xff.
|
||
|
||
IopmTableEntries - The number of entries in our table.
|
||
|
||
Returns:
|
||
|
||
none
|
||
|
||
--*/
|
||
|
||
{
|
||
PUSHORT IoMap = Iopm;
|
||
|
||
memset(Iopm, 0xff, PIOPM_SIZE);
|
||
|
||
while (IopmTableEntries--) {
|
||
IoMap[IopmDiffTable[IopmTableEntries].Entry] =
|
||
IopmDiffTable[IopmTableEntries].Value;
|
||
}
|
||
}
|
||
|
||
|
||
VOID
|
||
HalpBiosDisplayReset(
|
||
VOID
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Calls BIOS by putting the machine into V86 mode. This involves setting up
|
||
a physical==virtual identity mapping for the first 1Mb of memory, setting
|
||
up V86-specific trap handlers, and granting I/O privilege to the V86
|
||
process by editing the IOPM bitmap in the TSS.
|
||
|
||
Environment:
|
||
|
||
Interrupts disabled.
|
||
|
||
Arguments:
|
||
|
||
None
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
HARDWARE_PTE OldPageTable;
|
||
HARDWARE_PTE_X86PAE OldPageTablePae;
|
||
ULONGLONG OldPageTablePfn;
|
||
|
||
USHORT OldIoMapBase;
|
||
ULONG OldEsp0;
|
||
PHARDWARE_PTE Pte;
|
||
PHARDWARE_PTE V86CodePte;
|
||
ULONG OldTrap0DHandler;
|
||
ULONG OldTrap06Handler;
|
||
PUCHAR IoMap;
|
||
ULONG Virtual;
|
||
KIRQL OldIrql;
|
||
ULONG OriginalTssSelector;
|
||
extern PVOID HalpRealModeStart;
|
||
extern PVOID HalpRealModeEnd;
|
||
extern volatile ULONG HalpNMIInProgress;
|
||
PHARDWARE_PTE PointerPde;
|
||
PHARDWARE_PTE IdtPte;
|
||
ULONG OldIdtWrite;
|
||
ULONG PageFrame;
|
||
ULONG PageFrameEnd;
|
||
PKPCR Pcr;
|
||
IOPM_DIFF_ENTRY IopmDiffTable[MAX_DIFFERENCES];
|
||
ULONG IopmDiffTableEntries;
|
||
|
||
//
|
||
// Interrupts are off, but V86 mode might turn them back on again.
|
||
//
|
||
OldIrql = HalpDisableAllInterrupts ();
|
||
Pcr = KeGetPcr();
|
||
|
||
//
|
||
// We need to set up an identity mapping in the first page table. First,
|
||
// we save away the old page table.
|
||
//
|
||
|
||
PointerPde = MiGetPdeAddress((PVOID)0);
|
||
OldPageTablePfn = HalpGetPageFrameNumber( PointerPde );
|
||
|
||
if (HalPaeEnabled() != FALSE) {
|
||
|
||
OldPageTablePae = *(PHARDWARE_PTE_X86PAE)PointerPde;
|
||
((PHARDWARE_PTE_X86PAE)PointerPde)->reserved1 = 0;
|
||
|
||
} else {
|
||
|
||
OldPageTable = *PointerPde;
|
||
|
||
}
|
||
|
||
//
|
||
// Now we put the HAL page table into the first slot of the page
|
||
// directory. Note that this page table is now the first and last
|
||
// entries in the page directory.
|
||
//
|
||
|
||
Pte = MiGetPdeAddress((PVOID)0);
|
||
|
||
HalpCopyPageFrameNumber( Pte,
|
||
MiGetPdeAddress( MM_HAL_RESERVED ));
|
||
|
||
Pte->Valid = 1;
|
||
Pte->Write = 1;
|
||
Pte->Owner = 1; // User-accessible
|
||
Pte->LargePage = 0;
|
||
|
||
//
|
||
// Flush TLB
|
||
//
|
||
|
||
HalpFlushTLB();
|
||
|
||
//
|
||
// Map the first 1Mb of virtual memory to the first 1Mb of physical
|
||
// memory
|
||
//
|
||
for (Virtual=0; Virtual < 0x100000; Virtual += PAGE_SIZE) {
|
||
Pte = MiGetPteAddress((PVOID)Virtual);
|
||
HalpSetPageFrameNumber( Pte, Virtual >> PAGE_SHIFT );
|
||
Pte->Valid = 1;
|
||
Pte->Write = 1;
|
||
Pte->Owner = 1; // User-accessible
|
||
}
|
||
|
||
//
|
||
// Map our code into the virtual machine
|
||
//
|
||
|
||
Pte = MiGetPteAddress((PVOID)0x20000);
|
||
PointerPde = MiGetPdeAddress(&HalpRealModeStart);
|
||
|
||
if ( PointerPde->LargePage ) {
|
||
|
||
//
|
||
// Map real mode PTEs into virtual mapping. The source PDE is
|
||
// from the indenity large pte map, so map the virtual machine PTEs
|
||
// based on the base of the large PDE frame.
|
||
//
|
||
|
||
PageFrame = MiGetPteIndex( &HalpRealModeStart );
|
||
PageFrameEnd = MiGetPteIndex( &HalpRealModeEnd );
|
||
do {
|
||
|
||
HalpSetPageFrameNumber( Pte,
|
||
HalpGetPageFrameNumber( PointerPde ) +
|
||
PageFrame );
|
||
|
||
HalpIncrementPte( &Pte );
|
||
++PageFrame;
|
||
|
||
} while (PageFrame <= PageFrameEnd);
|
||
|
||
} else {
|
||
|
||
//
|
||
// Map real mode PTEs into virtual machine PTEs, by copying the
|
||
// page frames from the source to the virtual machine PTEs.
|
||
//
|
||
|
||
V86CodePte = MiGetPteAddress(&HalpRealModeStart);
|
||
do {
|
||
HalpCopyPageFrameNumber( Pte, V86CodePte );
|
||
HalpIncrementPte( &Pte );
|
||
HalpIncrementPte( &V86CodePte );
|
||
|
||
} while ( V86CodePte <= MiGetPteAddress(&HalpRealModeEnd) );
|
||
|
||
}
|
||
|
||
//
|
||
// Verify the IDT is writable
|
||
//
|
||
|
||
Pte = MiGetPteAddress(Pcr->IDT);
|
||
PointerPde = MiGetPdeAddress(Pcr->IDT);
|
||
IdtPte = PointerPde->LargePage ? PointerPde : Pte;
|
||
|
||
OldIdtWrite = (ULONG)IdtPte->Write;
|
||
IdtPte->Write = 1;
|
||
|
||
//
|
||
// Flush TLB
|
||
//
|
||
|
||
HalpFlushTLB();
|
||
|
||
//
|
||
// We need to replace the current TRAP D handler with our own, so
|
||
// we can do instruction emulation for V86 mode
|
||
//
|
||
|
||
OldTrap0DHandler = KiReturnHandlerAddressFromIDT(0xd);
|
||
KiSetHandlerAddressToIDT(0xd, HalpTrap0D);
|
||
|
||
OldTrap06Handler = KiReturnHandlerAddressFromIDT(6);
|
||
KiSetHandlerAddressToIDT(6, HalpTrap06);
|
||
|
||
//
|
||
// Make sure current TSS has IoMap space available. If no, borrow
|
||
// Normal TSS.
|
||
//
|
||
|
||
OriginalTssSelector = HalpBorrowTss();
|
||
|
||
//
|
||
// Overwrite the first access map with zeroes, so the V86 code can
|
||
// party on all the registers.
|
||
//
|
||
IoMap = (PUCHAR)&(Pcr->TSS->IoMaps[0].IoMap);
|
||
|
||
IopmDiffTableEntries =
|
||
HalpStoreAndClearIopm(IoMap, IopmDiffTable, MAX_DIFFERENCES);
|
||
|
||
OldIoMapBase = Pcr->TSS->IoMapBase;
|
||
|
||
Pcr->TSS->IoMapBase = KiComputeIopmOffset(1);
|
||
|
||
//
|
||
// Save the current ESP0, as HalpBiosCall() trashes it.
|
||
//
|
||
OldEsp0 = Pcr->TSS->Esp0;
|
||
|
||
//
|
||
// Call the V86-mode code
|
||
//
|
||
HalpBiosCall();
|
||
|
||
//
|
||
// Restore the TRAP handlers
|
||
//
|
||
|
||
|
||
if ((HalpNMIInProgress == FALSE) ||
|
||
((*((PBOOLEAN)(*(PLONG)&KdDebuggerNotPresent)) == FALSE) &&
|
||
(**((PUCHAR *)&KdDebuggerEnabled) != FALSE))) {
|
||
|
||
// If we are here due to an NMI, the IRET performed in HalpBiosCall()
|
||
// allows a second NMI to occur. The second NMI causes a trap 0d because
|
||
// the NMI TSS is busy and proceeds to bugcheck which trashes the screen.
|
||
// Thus in this case we leave this trap 0d handler in place which will then
|
||
// just spin on a jump to self if a second NMI occurs.
|
||
|
||
KiSetHandlerAddressToIDT(0xd, OldTrap0DHandler);
|
||
}
|
||
|
||
KiSetHandlerAddressToIDT(6, OldTrap06Handler);
|
||
IdtPte->Write = OldIdtWrite;
|
||
|
||
//
|
||
// Restore Esp0 value
|
||
//
|
||
Pcr->TSS->Esp0 = OldEsp0;
|
||
|
||
//
|
||
// Restore the IoMap to its previous state.
|
||
//
|
||
|
||
HalpRestoreIopm(IoMap, IopmDiffTable, IopmDiffTableEntries);
|
||
|
||
Pcr->TSS->IoMapBase = OldIoMapBase;
|
||
|
||
//
|
||
// Return borrowed TSS if any.
|
||
//
|
||
|
||
if (OriginalTssSelector != 0) {
|
||
HalpReturnTss(OriginalTssSelector);
|
||
}
|
||
|
||
//
|
||
// Unmap the first 1Mb of virtual memory
|
||
//
|
||
for (Virtual = 0; Virtual < 0x100000; Virtual += PAGE_SIZE) {
|
||
Pte = MiGetPteAddress((PVOID)Virtual);
|
||
HalpSetPageFrameNumber( Pte, 0 );
|
||
Pte->Valid = 0;
|
||
Pte->Write = 0;
|
||
}
|
||
|
||
//
|
||
// Restore the original page table that we replaced.
|
||
//
|
||
|
||
PointerPde = MiGetPdeAddress((PVOID)0);
|
||
|
||
if (HalPaeEnabled() != FALSE) {
|
||
|
||
*(PHARDWARE_PTE_X86PAE)PointerPde = OldPageTablePae;
|
||
|
||
} else {
|
||
|
||
*PointerPde = OldPageTable;
|
||
|
||
}
|
||
|
||
HalpSetPageFrameNumber( PointerPde, OldPageTablePfn );
|
||
|
||
//
|
||
// Flush TLB
|
||
//
|
||
|
||
HalpFlushTLB();
|
||
|
||
//
|
||
// Re-enable Interrupts
|
||
//
|
||
|
||
HalpReenableInterrupts(OldIrql);
|
||
}
|
||
|
||
HAL_DISPLAY_BIOS_INFORMATION
|
||
HalpGetDisplayBiosInformation (
|
||
VOID
|
||
)
|
||
{
|
||
// this hal uses native int-10
|
||
|
||
return HalDisplayInt10Bios;
|
||
}
|