/*++ Copyright (C) 1994,1995 Microsoft Corporation Module Name: x86bios.c Abstract: This module implements the platform specific interface between a device driver and the execution of x86 ROM bios code for the device. Environment: Kernel mode only. --*/ /* * Copyright (c) 1995 FirePower Systems, Inc. * DO NOT DISTRIBUTE without permission * * $RCSfile: x86bios.c $ * $Revision: 1.21 $ * $Date: 1996/07/02 04:58:06 $ * $Locker: $ */ #include "halp.h" #include "xm86.h" #include "x86new.h" #include "pxpcisup.h" #include "pxmemctl.h" #include "fpdebug.h" #include "pci.h" // // Define global data. // ULONG HalpX86BiosInitialized = FALSE; ULONG HalpEnableInt10Calls = FALSE; //PVOID HalpIoMemoryBase = NULL; PUCHAR HalpRomBase = NULL; UCHAR HalpVideoBus; // Used as arguments to the PCI BIOS UCHAR HalpVideoDevice; // init function. Set HalpInitX86Emulator, UCHAR HalpVideoFunction; // used by HalpInitializeX86DisplayAdapter. UCHAR HalpLastPciBus; // Set by scanning the configuration data and // used by PCI BIOS eumulation code. ULONG ROM_Length; #define BUFFER_SIZE (128*1024) UCHAR ROM_Buffer[BUFFER_SIZE]; static VOID DumpPCIConfig(PVOID ConfigBaseAddress) { USHORT VendorID, DeviceID, RevisionID; ULONG addr; ULONG TempReg32; VendorID = READ_REGISTER_USHORT(&((PCI_CONFIG)ConfigBaseAddress)->VendorID); DeviceID = READ_REGISTER_USHORT(&((PCI_CONFIG)ConfigBaseAddress)->DeviceID); RevisionID = READ_REGISTER_UCHAR(&((PCI_CONFIG)ConfigBaseAddress)->RevisionID); PRNTDISP(DbgPrint("vendorID=0x%04x deviceID=0x%04x revisionID=0x%02x\n", VendorID, DeviceID, RevisionID)); TempReg32 = READ_REGISTER_ULONG((PULONG)&((PCI_CONFIG)ConfigBaseAddress)->Command); PRNTDISP(DbgPrint("Status Command=0x%08x\n", TempReg32)); TempReg32 = READ_REGISTER_ULONG((PULONG)&((PCI_CONFIG)ConfigBaseAddress)->RevisionID); PRNTDISP(DbgPrint("Revision ID=0x%08x\n", TempReg32)); addr = READ_REGISTER_ULONG(&((PCI_CONFIG)ConfigBaseAddress)->BaseAddress1); PRNTDISP(DbgPrint("BaseAddress1=0x%08x\n", addr)); addr = READ_REGISTER_ULONG(&((PCI_CONFIG)ConfigBaseAddress)->BaseAddress2); PRNTDISP(DbgPrint("BaseAddress2=0x%08x\n", addr)); addr = READ_REGISTER_ULONG(&((PCI_CONFIG)ConfigBaseAddress)->BaseAddress3); PRNTDISP(DbgPrint("BaseAddress3=0x%08x\n", addr)); addr = READ_REGISTER_ULONG(&((PCI_CONFIG)ConfigBaseAddress)->BaseAddress4); PRNTDISP(DbgPrint("BaseAddress4=0x%08x\n", addr)); addr = READ_REGISTER_ULONG(&((PCI_CONFIG)ConfigBaseAddress)->BaseAddress5); PRNTDISP(DbgPrint("BaseAddress5=0x%08x\n", addr)); addr = READ_REGISTER_ULONG(&((PCI_CONFIG)ConfigBaseAddress)->BaseAddress6); PRNTDISP(DbgPrint("BaseAddress6=0x%08x\n", addr)); } BOOLEAN HalpInitX86Emulator( VOID) { BOOLEAN Found = FALSE; ULONG ROM_size = 0; PHYSICAL_ADDRESS PhysAddr; USHORT Cmd, VendorID, Slot; PVOID HalpVideoConfigBase; PUCHAR ROM_Ptr; ULONG i; UCHAR Class; UCHAR SubClass; USHORT DeviceID = 0; UCHAR RevisionID = 0; ULONG mapSize = 0x800000; PhysAddr.HighPart = 0x00000000; // // Scan PCI slots for video BIOS ROMs // for (Slot = 1; Slot < MAXIMUM_PCI_SLOTS; Slot++) { HalpVideoConfigBase = (PVOID) ((ULONG) HalpPciConfigBase + HalpPciConfigSlot[Slot]); // Read Vendor ID and check if slot is empty VendorID = READ_REGISTER_USHORT(&((PCI_CONFIG)HalpVideoConfigBase)->VendorID); if (VendorID == 0xFFFF) continue; // Slot is empty; go to next slot DumpPCIConfig(HalpVideoConfigBase); Class = READ_REGISTER_UCHAR(&((PCI_CONFIG)HalpVideoConfigBase)->ClassCode[2]); SubClass = READ_REGISTER_UCHAR(&((PCI_CONFIG)HalpVideoConfigBase)->ClassCode[1]); #define DISPLAY_CLASS 0x03 if ( !(Class == DISPLAY_CLASS && (SubClass == 0)) && !(Class == 0x00 && SubClass == 0x01)) continue; DeviceID = READ_REGISTER_USHORT(&((PCI_CONFIG)HalpVideoConfigBase)->DeviceID); RevisionID = READ_REGISTER_UCHAR(&((PCI_CONFIG)HalpVideoConfigBase)->RevisionID); //PRNTDISP(DbgPrint("vendorID=0x%04x deviceID=0x%04x revisionID=0x%02x\n", VendorID, DeviceID, RevisionID)); //DbgBreakPoint(); // Get size of ROM WRITE_REGISTER_ULONG(&((PCI_CONFIG)HalpVideoConfigBase)->ROMbase, 0xFFFFFFFF); ROM_size = READ_REGISTER_ULONG(&((PCI_CONFIG)HalpVideoConfigBase)->ROMbase); if ((ROM_size != 0xFFFFFFFF) && (ROM_size != 0)) { ROM_size = ~(ROM_size & 0xFFFFFFFE) + 1; PRNTDISP(DbgPrint("ROM_size=0x%08x\n", ROM_size)); ROM_size += 0xC0000; // if (ROM_size < 0xE0000) ROM_size = 0xE0000; // Map to end of option ROM space // // Set Expansion ROM Base Address & enable ROM // PhysAddr.LowPart = 0x000C0000 | 1; WRITE_REGISTER_ULONG(&((PCI_CONFIG)HalpVideoConfigBase)->ROMbase, PhysAddr.LowPart); // // Enable Memory & I/O spaces in command register // Cmd = READ_REGISTER_USHORT(&((PCI_CONFIG)HalpVideoConfigBase)->Command); WRITE_REGISTER_USHORT(&((PCI_CONFIG)HalpVideoConfigBase)->Command, Cmd | 3); PRNTDISP(DbgPrint("HalpVideoConfigBase=0x%08x Slot=%d Cmd=0x%08x ROM_size=0x%08x\n", HalpVideoConfigBase, Slot, Cmd, ROM_size)); // // Create a mapping to the PCI memory space // if (NULL == HalpIoMemoryBase) { HalpIoMemoryBase = KePhase0MapIo((PVOID)IO_MEMORY_PHYSICAL_BASE, mapSize /*ROM_size*/); if (HalpIoMemoryBase == NULL) { PRNTDISP(DbgPrint("\nCan't create mapping to PCI memory space\n")); return FALSE; } } // // Look for PCI option video ROM signature // HalpRomBase = ROM_Ptr = (PUCHAR) HalpIoMemoryBase + 0xC0000; if (*ROM_Ptr == 0x55 && *(ROM_Ptr+1) == 0xAA) { // // Copy ROM to RAM. PCI Spec says you can't execute from ROM. // Sometimes option ROM and video RAM can't co-exist. // ROM_Length = *(ROM_Ptr+2) << 9; PRNTDISP(DbgPrint("ROM_Length=0x%08x\n", ROM_Length)); if (ROM_Length <= BUFFER_SIZE) { for (i=0; iConfigurationRoot, AdapterClass, MultiFunctionAdapter, &MatchKey)) != NULL) { if (!strcmp(ConfigurationEntry->ComponentEntry.Identifier,"PCI")) { Descriptor = (PCM_PARTIAL_RESOURCE_LIST)ConfigurationEntry->ConfigurationData; PCIRegInfo = (PPCI_REGISTRY_INFO)&Descriptor->PartialDescriptors[1]; HalpLastPciBus = PCIRegInfo->NoBuses - 1; break; } MatchKey++; } #endif // // Initialize the x86 bios emulator. // x86BiosInitializeBios(HalpIoControlBase, HalpIoMemoryBase); HalpX86BiosInitialized = TRUE; // // Attempt to initialize the display adapter by executing its ROM bios // code. The standard ROM bios code address for PC video adapters is // 0xC000:0000 on the ISA bus. // State.Eax = (HalpVideoBus << 8) | (HalpVideoDevice << 3) | HalpVideoFunction; State.Ecx = 0; State.Edx = 0; State.Ebx = 0; State.Ebp = 0; State.Esi = 0; State.Edi = 0; if (x86BiosInitializeAdapter(0xc0000, &State, HalpIoControlBase, HalpIoMemoryBase) != XM_SUCCESS) { HalpEnableInt10Calls = FALSE; return FALSE; } HalpEnableInt10Calls = TRUE; return TRUE; } VOID HalpResetX86DisplayAdapter( VOID ) /*++ Routine Description: This function resets a display adapter using the x86 bios emulator. Arguments: None. Return Value: None. --*/ { XM86_CONTEXT Context; // // Initialize the x86 bios context and make the INT 10 call to initialize // the display adapter to 80x25 color text mode. // Context.Eax = 0x0003; // Function 0, Mode 3 Context.Ebx = 0; Context.Ecx = 0; Context.Edx = 0; Context.Esi = 0; Context.Edi = 0; Context.Ebp = 0; HalCallBios(0x10, &Context.Eax, &Context.Ebx, &Context.Ecx, &Context.Edx, &Context.Esi, &Context.Edi, &Context.Ebp); return; } // // This code came from ..\..\x86new\x86bios.c // #define LOW_MEMORY_SIZE 0x800 extern UCHAR x86BiosLowMemory[LOW_MEMORY_SIZE + 3]; extern ULONG x86BiosScratchMemory; extern ULONG x86BiosIoMemory; extern ULONG x86BiosIoSpace; 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: return (PVOID)(x86BiosIoMemory + Offset + Value); case 0xc: case 0xd: return (PVOID)(HalpRomBase + Offset); // // The memory range from 0x10000 to 0x9ffff reads as zero // and writes are ignored. // case 0xe: case 0xf: x86BiosScratchMemory = 0; return (PVOID)&x86BiosScratchMemory; } // NOT REACHED - NOT EXECUTED - Prevents Compiler Warning. return (PVOID)NULL; } VOID HalpCopyROMs(VOID) { ULONG i; PUCHAR ROM_Shadow; if (ROM_Buffer[0] == 0x55 && ROM_Buffer[1] == 0xAA) { //DbgPrint("HalpCopyROMs: calling ExAllocatePool.\n"); HalpRomBase = ROM_Shadow = ExAllocatePool(NonPagedPool, ROM_Length); for (i=0; i> 8)); WRITE_REGISTER_UCHAR(u.Byte + 2, (UCHAR)(Value >> 16)); WRITE_REGISTER_UCHAR(u.Byte + 3, (UCHAR)(Value >> 24)); } else { WRITE_REGISTER_ULONG(u.Long, Value); } } } else { if (((ULONG)u.Word & 0x1) != 0) { WRITE_REGISTER_UCHAR(u.Byte + 0, (UCHAR)(Value)); WRITE_REGISTER_UCHAR(u.Byte + 1, (UCHAR)(Value >> 8)); } else { WRITE_REGISTER_USHORT(u.Word, (USHORT)Value); } } return; } 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. --*/ { // // 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; // // Initialize the emulator and the BIOS. // XmInitializeEmulator(0, LOW_MEMORY_SIZE, x86BiosReadIoSpace, x86BiosWriteIoSpace, x86BiosTranslateAddress); x86BiosInitialized = TRUE; 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. 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; } // // 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 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. --*/ { 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 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; }