/*++ Copyright (c) 1994 Microsoft Corporation Module Name: x86bios.c Abstract: This module implements supplies the HAL interface to the 386/486 real mode emulator for the purpose of emulating BIOS calls.. Author: David N. Cutler (davec) 13-Nov-1994 Environment: Kernel mode only. Revision History: --*/ #include "nthal.h" #include "hal.h" #include "xm86.h" #include "x86new.h" // // Define the size of low memory. // #define LOW_MEMORY_SIZE 0x800 // // Define storage for low emulated memory. // UCHAR x86BiosLowMemory[LOW_MEMORY_SIZE + 3]; ULONG x86BiosScratchMemory; // // Define storage to capture the base address of I/O space, the base address // of I/O memory space, and the base address of the video frame buffer. // ULONG x86BiosFrameBuffer; ULONG x86BiosIoMemory; ULONG x86BiosIoSpace; // // Define BIOS initialized state. // BOOLEAN x86BiosInitialized = FALSE; // // Define storage for PCI BIOS initialization state. // UCHAR XmNumberPciBusses = 0; BOOLEAN XmPciBiosPresent = FALSE; PGETSETPCIBUSDATA XmGetPciData; PGETSETPCIBUSDATA XmSetPciData; ULONG x86BiosReadIoSpace ( IN XM_OPERATION_DATATYPE DataType, IN USHORT PortNumber ) /*++ Routine Description: This function reads from emulated I/O space. Arguments: DataType - Supplies the datatype for the read operation. PortNumber - Supplies the port number in I/O space to read from. Return Value: The value read from I/O space is returned as the function value. N.B. If an aligned operation is specified, then the individual bytes are read from the specified port one at a time and assembled into the specified datatype. --*/ { ULONG Result; union { PUCHAR Byte; PUSHORT Word; PULONG Long; } u; // // Compute port address and read port. // u.Long = (PULONG)(x86BiosIoSpace + PortNumber); if (DataType == BYTE_DATA) { Result = READ_PORT_UCHAR(u.Byte); } else if (DataType == LONG_DATA) { if (((ULONG)u.Long & 0x3) != 0) { Result = (READ_PORT_UCHAR(u.Byte + 0)) | (READ_PORT_UCHAR(u.Byte + 1) << 8) | (READ_PORT_UCHAR(u.Byte + 2) << 16) | (READ_PORT_UCHAR(u.Byte + 3) << 24); } else { Result = READ_PORT_ULONG(u.Long); } } else { if (((ULONG)u.Word & 0x1) != 0) { Result = (READ_PORT_UCHAR(u.Byte + 0)) | (READ_PORT_UCHAR(u.Byte + 1) << 8); } else { Result = READ_PORT_USHORT(u.Word); } } return Result; } VOID x86BiosWriteIoSpace ( IN XM_OPERATION_DATATYPE DataType, IN USHORT PortNumber, IN ULONG Value ) /*++ Routine Description: This function write to emulated I/O space. N.B. If an aligned operation is specified, then the individual bytes are written to the specified port one at a time. Arguments: DataType - Supplies the datatype for the write operation. PortNumber - Supplies the port number in I/O space to write to. Value - Supplies the value to write. Return Value: None. --*/ { union { PUCHAR Byte; PUSHORT Word; PULONG Long; } u; // // Compute port address and read port. // u.Long = (PULONG)(x86BiosIoSpace + PortNumber); if (DataType == BYTE_DATA) { WRITE_PORT_UCHAR(u.Byte, (UCHAR)Value); } else if (DataType == LONG_DATA) { if (((ULONG)u.Long & 0x3) != 0) { WRITE_PORT_UCHAR(u.Byte + 0, (UCHAR)(Value)); WRITE_PORT_UCHAR(u.Byte + 1, (UCHAR)(Value >> 8)); WRITE_PORT_UCHAR(u.Byte + 2, (UCHAR)(Value >> 16)); WRITE_PORT_UCHAR(u.Byte + 3, (UCHAR)(Value >> 24)); } else { WRITE_PORT_ULONG(u.Long, Value); } } else { if (((ULONG)u.Word & 0x1) != 0) { WRITE_PORT_UCHAR(u.Byte + 0, (UCHAR)(Value)); WRITE_PORT_UCHAR(u.Byte + 1, (UCHAR)(Value >> 8)); } else { WRITE_PORT_USHORT(u.Word, (USHORT)Value); } } return; } PVOID x86BiosTranslateAddress ( IN USHORT Segment, IN USHORT Offset ) /*++ Routine Description: This translates a segment/offset address into a memory address. Arguments: Segment - Supplies the segment register value. Offset - Supplies the offset within segment. Return Value: The memory address of the translated segment/offset pair is returned as the function value. --*/ { ULONG Value; // // Compute the logical memory address and case on high hex digit of // the resultant address. // Value = Offset + (Segment << 4); Offset = (USHORT)(Value & 0xffff); Value &= 0xf0000; switch ((Value >> 16) & 0xf) { // // Interrupt vector/stack space. // case 0x0: if (Offset > LOW_MEMORY_SIZE) { x86BiosScratchMemory = 0; return (PVOID)&x86BiosScratchMemory; } else { return (PVOID)(&x86BiosLowMemory[0] + Offset); } // // The memory range from 0x10000 to 0x9ffff reads as zero // and writes are ignored. // case 0x1: case 0x2: case 0x3: case 0x4: case 0x5: case 0x6: case 0x7: case 0x8: case 0x9: x86BiosScratchMemory = 0; return (PVOID)&x86BiosScratchMemory; // // The memory range from 0xa0000 to 0xdffff maps to I/O memory. // case 0xa: case 0xb: if (x86BiosFrameBuffer != 0) { return (PVOID)(x86BiosFrameBuffer + Offset + Value); } case 0xc: case 0xd: return (PVOID)(x86BiosIoMemory + Offset + Value); // // The memory range from 0x10000 to 0x9ffff reads as zero // and writes are ignored. // case 0xe: case 0xf: x86BiosScratchMemory = 0; return (PVOID)&x86BiosScratchMemory; } } VOID x86BiosInitializeBios ( IN PVOID BiosIoSpace, IN PVOID BiosIoMemory ) /*++ Routine Description: This function initializes x86 BIOS emulation. Arguments: BiosIoSpace - Supplies the base address of the I/O space to be used for BIOS emulation. BiosIoMemory - Supplies the base address of the I/O memory to be used for BIOS emulation. Return Value: None. --*/ { // // Initialize x86 BIOS emulation. // x86BiosInitializeBiosShadowed(BiosIoSpace, BiosIoMemory, NULL); return; } VOID x86BiosInitializeBiosShadowed ( IN PVOID BiosIoSpace, IN PVOID BiosIoMemory, IN PVOID BiosFrameBuffer ) /*++ Routine Description: This function initializes x86 BIOS emulation. Arguments: BiosIoSpace - Supplies the base address of the I/O space to be used for BIOS emulation. BiosIoMemory - Supplies the base address of the I/O memory to be used for BIOS emulation. BiosFrameBuffer - Supplies the base address of the video frame buffer to be used for bios emulation. Return Value: None. --*/ { // // Zero low memory. // memset(&x86BiosLowMemory, 0, LOW_MEMORY_SIZE); // // Save base address of I/O memory and I/O space. // x86BiosIoSpace = (ULONG)BiosIoSpace; x86BiosIoMemory = (ULONG)BiosIoMemory; x86BiosFrameBuffer = (ULONG)BiosFrameBuffer; // // Initialize the emulator and the BIOS. // XmInitializeEmulator(0, LOW_MEMORY_SIZE, x86BiosReadIoSpace, x86BiosWriteIoSpace, x86BiosTranslateAddress); x86BiosInitialized = TRUE; return; } VOID x86BiosInitializeBiosShadowedPci ( IN PVOID BiosIoSpace, IN PVOID BiosIoMemory, IN PVOID BiosFrameBuffer, IN UCHAR NumberPciBusses, IN PGETSETPCIBUSDATA GetPciData, IN PGETSETPCIBUSDATA SetPciData ) /*++ Routine Description: This function initializes x86 BIOS emulation and also sets up the emulator with BIOS shadowed and PCI functions enabled. Since the PCI specification requires BIOS shadowing, there isn't any need to provide a function that turns on the PCI functions, but doesn't shadow the BIOS. Arguments: BiosIoSpace - Supplies the base address of the I/O space to be used for BIOS emulation. BiosIoMemory - Supplies the base address of the I/O memory to be used for BIOS emulation. BiosFrameBuffer - Supplies the base address of the video frame buffer to be used for bios emulation. NumberPciBusses - Supplies the number of PCI busses in the system. GetPciData - Supplies the address of a function to read the PCI configuration space. SetPciData - Supplies the address of a function to write the PCI configuration space. Return Value: None. --*/ { // // Enable PCI BIOS support. // XmPciBiosPresent = TRUE; XmGetPciData = GetPciData; XmSetPciData = SetPciData; XmNumberPciBusses = NumberPciBusses; // // Initialize x86 BIOS emulation. // x86BiosInitializeBiosShadowed(BiosIoSpace, BiosIoMemory, BiosFrameBuffer); return; } XM_STATUS x86BiosExecuteInterrupt ( IN UCHAR Number, IN OUT PXM86_CONTEXT Context, IN PVOID BiosIoSpace OPTIONAL, IN PVOID BiosIoMemory OPTIONAL ) /*++ Routine Description: This function executes an interrupt by calling the x86 emulator. Arguments: Number - Supplies the number of the interrupt that is to be emulated. Context - Supplies a pointer to an x86 context structure. BiosIoSpace - Supplies an optional base address of the I/O space to be used for BIOS emulation. BiosIoMemory - Supplies an optional base address of the I/O memory to be used for BIOS emulation. Return Value: The emulation completion status. --*/ { // // Execute x86 interrupt. // return x86BiosExecuteInterruptShadowed(Number, Context, BiosIoSpace, BiosIoMemory, NULL); } XM_STATUS x86BiosExecuteInterruptShadowed ( IN UCHAR Number, IN OUT PXM86_CONTEXT Context, IN PVOID BiosIoSpace OPTIONAL, IN PVOID BiosIoMemory OPTIONAL, IN PVOID BiosFrameBuffer OPTIONAL ) /*++ Routine Description: This function executes an interrupt by calling the x86 emulator. Arguments: Number - Supplies the number of the interrupt that is to be emulated. Context - Supplies a pointer to an x86 context structure. BiosIoSpace - Supplies an optional base address of the I/O space to be used for BIOS emulation. BiosIoMemory - Supplies an optional base address of the I/O memory to be used for BIOS emulation. BiosFrameBuffer - Supplies an optional base address of the video frame buffer to be used for bios emulation. Return Value: The emulation completion status. --*/ { XM_STATUS Status; // // If a new base address is specified, then set the appropriate base. // if (BiosIoSpace != NULL) { x86BiosIoSpace = (ULONG)BiosIoSpace; } if (BiosIoMemory != NULL) { x86BiosIoMemory = (ULONG)BiosIoMemory; } if (BiosFrameBuffer != NULL) { x86BiosFrameBuffer = (ULONG)BiosFrameBuffer; } // // Execute the specified interrupt. // Status = XmEmulateInterrupt(Number, Context); if (Status != XM_SUCCESS) { DbgPrint("HAL: Interrupt emulation failed, status %lx\n", Status); } return Status; } XM_STATUS x86BiosExecuteInterruptShadowedPci ( IN UCHAR Number, IN OUT PXM86_CONTEXT Context, IN PVOID BiosIoSpace OPTIONAL, IN PVOID BiosIoMemory OPTIONAL, IN PVOID BiosFrameBuffer OPTIONAL, IN UCHAR NumberPciBusses, IN PGETSETPCIBUSDATA GetPciData, IN PGETSETPCIBUSDATA SetPciData ) /*++ Routine Description: This function executes an interrupt by calling the x86 emulator. Arguments: Number - Supplies the number of the interrupt that is to be emulated. Context - Supplies a pointer to an x86 context structure. BiosIoSpace - Supplies an optional base address of the I/O space to be used for BIOS emulation. BiosIoMemory - Supplies an optional base address of the I/O memory to be used for BIOS emulation. NumberPciBusses - Supplies the number of PCI busses in the system. GetPciData - Supplies the address of a function to read the PCI configuration space. SetPciData - Supplies the address of a function to write the PCI configuration space. Return Value: The emulation completion status. --*/ { // // Enable PCI BIOS support. // XmPciBiosPresent = TRUE; XmGetPciData = GetPciData; XmSetPciData = SetPciData; XmNumberPciBusses = NumberPciBusses; // // Execute x86 interrupt. // return x86BiosExecuteInterruptShadowed(Number, Context, BiosIoSpace, BiosIoMemory, BiosFrameBuffer); } XM_STATUS x86BiosInitializeAdapter( IN ULONG Adapter, IN OUT PXM86_CONTEXT Context OPTIONAL, IN PVOID BiosIoSpace OPTIONAL, IN PVOID BiosIoMemory OPTIONAL ) /*++ Routine Description: This function initializes the adapter whose BIOS starts at the specified 20-bit address. Arguments: Adpater - Supplies the 20-bit address of the BIOS for the adapter to be initialized. Return Value: The emulation completion status. --*/ { // // Initialize the specified adapter. // return x86BiosInitializeAdapterShadowed(Adapter, Context, BiosIoSpace, BiosIoMemory, NULL); } XM_STATUS x86BiosInitializeAdapterShadowed ( IN ULONG Adapter, IN OUT PXM86_CONTEXT Context OPTIONAL, IN PVOID BiosIoSpace OPTIONAL, IN PVOID BiosIoMemory OPTIONAL, IN PVOID BiosFrameBuffer OPTIONAL ) /*++ Routine Description: This function initializes the adapter whose BIOS starts at the specified 20-bit address. Arguments: Adpater - Supplies the 20-bit address of the BIOS for the adapter to be initialized. Return Value: The emulation completion status. --*/ { PUCHAR Byte; XM86_CONTEXT State; USHORT Offset; USHORT Segment; XM_STATUS Status; // // If BIOS emulation has not been initialized, then return an error. // if (x86BiosInitialized == FALSE) { return XM_EMULATOR_NOT_INITIALIZED; } // // If an emulator context is not specified, then use a default // context. // if (ARGUMENT_PRESENT(Context) == FALSE) { State.Eax = 0; State.Ecx = 0; State.Edx = 0; State.Ebx = 0; State.Ebp = 0; State.Esi = 0; State.Edi = 0; Context = &State; } // // If a new base address is specified, then set the appropriate base. // if (BiosIoSpace != NULL) { x86BiosIoSpace = (ULONG)BiosIoSpace; } if (BiosIoMemory != NULL) { x86BiosIoMemory = (ULONG)BiosIoMemory; } if (BiosFrameBuffer != NULL) { x86BiosFrameBuffer = (ULONG)BiosFrameBuffer; } // // If the specified adpater is not BIOS code, then return an error. // Segment = (USHORT)((Adapter >> 4) & 0xf000); Offset = (USHORT)(Adapter & 0xffff); Byte = (PUCHAR)x86BiosTranslateAddress(Segment, Offset); if ((*Byte++ != 0x55) || (*Byte != 0xaa)) { return XM_ILLEGAL_CODE_SEGMENT; } // // Call the BIOS code to initialize the specified adapter. // Adapter += 3; Segment = (USHORT)((Adapter >> 4) & 0xf000); Offset = (USHORT)(Adapter & 0xffff); Status = XmEmulateFarCall(Segment, Offset, Context); if (Status != XM_SUCCESS) { DbgPrint("HAL: Adapter initialization falied, status %lx\n", Status); } return Status; } XM_STATUS x86BiosInitializeAdapterShadowedPci( IN ULONG Adapter, IN OUT PXM86_CONTEXT Context OPTIONAL, IN PVOID BiosIoSpace OPTIONAL, IN PVOID BiosIoMemory OPTIONAL, IN PVOID BiosFrameBuffer OPTIONAL, IN UCHAR NumberPciBusses, IN PGETSETPCIBUSDATA GetPciData, IN PGETSETPCIBUSDATA SetPciData ) /*++ Routine Description: This function initializes the adapter whose BIOS starts at the specified 20-bit address. Arguments: Adpater - Supplies the 20-bit address of the BIOS for the adapter to be initialized. Return Value: The emulation completion status. --*/ { // // Enable PCI BIOS support. // XmPciBiosPresent = TRUE; XmGetPciData = GetPciData; XmSetPciData = SetPciData; XmNumberPciBusses = NumberPciBusses; // // Initialize the specified adapter. // return x86BiosInitializeAdapterShadowed(Adapter, Context, BiosIoSpace, BiosIoMemory, BiosFrameBuffer); }