/** @file Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include "LegacyBiosInterface.h" #include #define PCI_START_ADDRESS(x) (((x) + 0x7ff) & ~0x7ff) #define MAX_BRIDGE_INDEX 0x20 typedef struct { UINTN PciSegment; UINTN PciBus; UINTN PciDevice; UINTN PciFunction; UINT8 PrimaryBus; UINT8 SecondaryBus; UINT8 SubordinateBus; } BRIDGE_TABLE; #define ROM_MAX_ENTRIES 24 BRIDGE_TABLE Bridges[MAX_BRIDGE_INDEX]; UINTN SortedBridgeIndex[MAX_BRIDGE_INDEX]; UINTN NumberOfBridges; LEGACY_PNP_EXPANSION_HEADER *mBasePnpPtr; UINT16 mBbsRomSegment; UINTN mHandleCount; EFI_HANDLE mVgaHandle; BOOLEAN mIgnoreBbsUpdateFlag; BOOLEAN mVgaInstallationInProgress = FALSE; UINT32 mRomCount = 0x00; ROM_INSTANCE_ENTRY mRomEntry[ROM_MAX_ENTRIES]; EDKII_IOMMU_PROTOCOL *mIoMmu; /** Query shadowed legacy ROM parameters registered by RomShadow() previously. @param PciHandle PCI device whos ROM has been shadowed @param DiskStart DiskStart value from EFI_LEGACY_BIOS_PROTOCOL.InstallPciRom @param DiskEnd DiskEnd value from EFI_LEGACY_BIOS_PROTOCOL.InstallPciRom @param RomShadowAddress Address where ROM was shadowed @param ShadowedSize Runtime size of ROM @retval EFI_SUCCESS Query Logging successful. @retval EFI_NOT_FOUND No logged data found about PciHandle. **/ EFI_STATUS GetShadowedRomParameters ( IN EFI_HANDLE PciHandle, OUT UINT8 *DiskStart, OPTIONAL OUT UINT8 *DiskEnd, OPTIONAL OUT VOID **RomShadowAddress, OPTIONAL OUT UINTN *ShadowedSize OPTIONAL ) { EFI_STATUS Status; EFI_PCI_IO_PROTOCOL *PciIo; UINTN Index; UINTN PciSegment; UINTN PciBus; UINTN PciDevice; UINTN PciFunction; // // Get the PCI I/O Protocol on PciHandle // Status = gBS->HandleProtocol ( PciHandle, &gEfiPciIoProtocolGuid, (VOID **) &PciIo ); if (EFI_ERROR (Status)) { return Status; } // // Get the location of the PCI device // PciIo->GetLocation ( PciIo, &PciSegment, &PciBus, &PciDevice, &PciFunction ); for(Index = 0; Index < mRomCount; Index++) { if ((mRomEntry[Index].PciSegment == PciSegment) && (mRomEntry[Index].PciBus == PciBus) && (mRomEntry[Index].PciDevice == PciDevice) && (mRomEntry[Index].PciFunction == PciFunction)) { break; } } if (Index == mRomCount) { return EFI_NOT_FOUND; } if (DiskStart != NULL) { *DiskStart = mRomEntry[Index].DiskStart; } if (DiskEnd != NULL) { *DiskEnd = mRomEntry[Index].DiskEnd; } if (RomShadowAddress != NULL) { *RomShadowAddress = (VOID *)(UINTN)mRomEntry[Index].ShadowAddress; } if (ShadowedSize != NULL) { *ShadowedSize = mRomEntry[Index].ShadowedSize; } return EFI_SUCCESS; } /** Every legacy ROM that is shadowed by the Legacy BIOS driver will be registered into this API so that the policy code can know what has happend @param PciHandle PCI device whos ROM is being shadowed @param ShadowAddress Address that ROM was shadowed @param ShadowedSize Runtime size of ROM @param DiskStart DiskStart value from EFI_LEGACY_BIOS_PROTOCOL.InstallPciRom @param DiskEnd DiskEnd value from EFI_LEGACY_BIOS_PROTOCOL.InstallPciRom @retval EFI_SUCCESS Logging successful. @retval EFI_OUT_OF_RESOURCES No remaining room for registering another option ROM. **/ EFI_STATUS RomShadow ( IN EFI_HANDLE PciHandle, IN UINT32 ShadowAddress, IN UINT32 ShadowedSize, IN UINT8 DiskStart, IN UINT8 DiskEnd ) { EFI_STATUS Status; EFI_PCI_IO_PROTOCOL *PciIo; // // See if there is room to register another option ROM // if (mRomCount >= ROM_MAX_ENTRIES) { return EFI_OUT_OF_RESOURCES; } // // Get the PCI I/O Protocol on PciHandle // Status = gBS->HandleProtocol ( PciHandle, &gEfiPciIoProtocolGuid, (VOID **) &PciIo ); if (EFI_ERROR (Status)) { return Status; } // // Get the location of the PCI device // PciIo->GetLocation ( PciIo, &mRomEntry[mRomCount].PciSegment, &mRomEntry[mRomCount].PciBus, &mRomEntry[mRomCount].PciDevice, &mRomEntry[mRomCount].PciFunction ); mRomEntry[mRomCount].ShadowAddress = ShadowAddress; mRomEntry[mRomCount].ShadowedSize = ShadowedSize; mRomEntry[mRomCount].DiskStart = DiskStart; mRomEntry[mRomCount].DiskEnd = DiskEnd; mRomCount++; return EFI_SUCCESS; } /** Return EFI_SUCCESS if PciHandle has had a legacy BIOS ROM shadowed. This information represents every call to RomShadow () @param PciHandle PCI device to get status for @retval EFI_SUCCESS Legacy ROM loaded for this device @retval EFI_NOT_FOUND No Legacy ROM loaded for this device **/ EFI_STATUS IsLegacyRom ( IN EFI_HANDLE PciHandle ) { EFI_STATUS Status; EFI_PCI_IO_PROTOCOL *PciIo; UINTN Index; UINTN Segment; UINTN Bus; UINTN Device; UINTN Function; // // Get the PCI I/O Protocol on PciHandle // Status = gBS->HandleProtocol ( PciHandle, &gEfiPciIoProtocolGuid, (VOID **) &PciIo ); if (EFI_ERROR (Status)) { return Status; } // // Get the location of the PCI device // PciIo->GetLocation ( PciIo, &Segment, &Bus, &Device, &Function ); // // See if the option ROM from PciHandle has been previously posted // for (Index = 0; Index < mRomCount; Index++) { if (mRomEntry[Index].PciSegment == Segment && mRomEntry[Index].PciBus == Bus && mRomEntry[Index].PciDevice == Device && mRomEntry[Index].PciFunction == Function ) { return EFI_SUCCESS; } } return EFI_NOT_FOUND; } /** Find the PC-AT ROM Image in the raw PCI Option ROM. Also return the related information from the header. @param Csm16Revision The PCI interface version of underlying CSM16 @param VendorId Vendor ID of the PCI device @param DeviceId Device ID of the PCI device @param Rom On input pointing to beginning of the raw PCI OpROM On output pointing to the first legacy PCI OpROM @param ImageSize On input is the size of Raw PCI Rom On output is the size of the first legacy PCI ROM @param MaxRuntimeImageLength The max runtime image length only valid if OpRomRevision >= 3 @param OpRomRevision Revision of the PCI Rom @param ConfigUtilityCodeHeader Pointer to Configuration Utility Code Header @retval EFI_SUCCESS Successfully find the legacy PCI ROM @retval EFI_NOT_FOUND Failed to find the legacy PCI ROM **/ EFI_STATUS GetPciLegacyRom ( IN UINT16 Csm16Revision, IN UINT16 VendorId, IN UINT16 DeviceId, IN OUT VOID **Rom, IN OUT UINTN *ImageSize, OUT UINTN *MaxRuntimeImageLength, OPTIONAL OUT UINT8 *OpRomRevision, OPTIONAL OUT VOID **ConfigUtilityCodeHeader OPTIONAL ) { BOOLEAN Match; UINT16 *DeviceIdList; EFI_PCI_ROM_HEADER RomHeader; PCI_3_0_DATA_STRUCTURE *Pcir; VOID *BackupImage; VOID *BestImage; if (*ImageSize < sizeof (EFI_PCI_ROM_HEADER)) { return EFI_NOT_FOUND; } BestImage = NULL; BackupImage = NULL; RomHeader.Raw = *Rom; while (RomHeader.Generic->Signature == PCI_EXPANSION_ROM_HEADER_SIGNATURE) { if (RomHeader.Generic->PcirOffset == 0 || (RomHeader.Generic->PcirOffset & 3) !=0 || *ImageSize < RomHeader.Raw - (UINT8 *) *Rom + RomHeader.Generic->PcirOffset + sizeof (PCI_DATA_STRUCTURE)) { break; } Pcir = (PCI_3_0_DATA_STRUCTURE *) (RomHeader.Raw + RomHeader.Generic->PcirOffset); // // Check signature in the PCI Data Structure. // if (Pcir->Signature != PCI_DATA_STRUCTURE_SIGNATURE) { break; } if (((UINTN)RomHeader.Raw - (UINTN)*Rom) + Pcir->ImageLength * 512 > *ImageSize) { break; } if (Pcir->CodeType == PCI_CODE_TYPE_PCAT_IMAGE) { Match = FALSE; if (Pcir->VendorId == VendorId) { if (Pcir->DeviceId == DeviceId) { Match = TRUE; } else if ((Pcir->Revision >= 3) && (Pcir->DeviceListOffset != 0)) { DeviceIdList = (UINT16 *)(((UINT8 *) Pcir) + Pcir->DeviceListOffset); // // Checking the device list // while (*DeviceIdList != 0) { if (*DeviceIdList == DeviceId) { Match = TRUE; break; } DeviceIdList ++; } } } if (Match) { if (Csm16Revision >= 0x0300) { // // Case 1: CSM16 3.0 // if (Pcir->Revision >= 3) { // // case 1.1: meets OpRom 3.0 // Perfect!!! // BestImage = RomHeader.Raw; break; } else { // // case 1.2: meets OpRom 2.x // Store it and try to find the OpRom 3.0 // BackupImage = RomHeader.Raw; } } else { // // Case 2: CSM16 2.x // if (Pcir->Revision >= 3) { // // case 2.1: meets OpRom 3.0 // Store it and try to find the OpRom 2.x // BackupImage = RomHeader.Raw; } else { // // case 2.2: meets OpRom 2.x // Perfect!!! // BestImage = RomHeader.Raw; break; } } } else { DEBUG ((EFI_D_ERROR, "GetPciLegacyRom - OpRom not match (%04x-%04x)\n", (UINTN)VendorId, (UINTN)DeviceId)); } } if ((Pcir->Indicator & 0x80) == 0x80) { break; } else { RomHeader.Raw += 512 * Pcir->ImageLength; } } if (BestImage == NULL) { if (BackupImage == NULL) { return EFI_NOT_FOUND; } // // The versions of CSM16 and OpRom don't match exactly // BestImage = BackupImage; } RomHeader.Raw = BestImage; Pcir = (PCI_3_0_DATA_STRUCTURE *) (RomHeader.Raw + RomHeader.Generic->PcirOffset); *Rom = BestImage; *ImageSize = Pcir->ImageLength * 512; if (MaxRuntimeImageLength != NULL) { if (Pcir->Revision < 3) { *MaxRuntimeImageLength = 0; } else { *MaxRuntimeImageLength = Pcir->MaxRuntimeImageLength * 512; } } if (OpRomRevision != NULL) { // // Optional return PCI Data Structure revision // if (Pcir->Length >= 0x1C) { *OpRomRevision = Pcir->Revision; } else { *OpRomRevision = 0; } } if (ConfigUtilityCodeHeader != NULL) { // // Optional return ConfigUtilityCodeHeaderOffset supported by the PC-AT ROM // if ((Pcir->Revision < 3) || (Pcir->ConfigUtilityCodeHeaderOffset == 0)) { *ConfigUtilityCodeHeader = NULL; } else { *ConfigUtilityCodeHeader = RomHeader.Raw + Pcir->ConfigUtilityCodeHeaderOffset; } } return EFI_SUCCESS; } /** Build a table of bridge info for PIRQ translation. @param RoutingTable RoutingTable obtained from Platform. @param RoutingTableEntries Number of RoutingTable entries. @retval EFI_SUCCESS New Subordinate bus. @retval EFI_NOT_FOUND No more Subordinate busses. **/ EFI_STATUS CreateBridgeTable ( IN EFI_LEGACY_IRQ_ROUTING_ENTRY *RoutingTable, IN UINTN RoutingTableEntries ) { EFI_STATUS Status; UINTN HandleCount; EFI_HANDLE *HandleBuffer; UINTN BridgeIndex; UINTN Index; UINTN Index1; EFI_PCI_IO_PROTOCOL *PciIo; PCI_TYPE01 PciConfigHeader; BRIDGE_TABLE SlotBridges[MAX_BRIDGE_INDEX]; UINTN SlotBridgeIndex; BridgeIndex = 0x00; SlotBridgeIndex = 0x00; // // Assumption is table is built from low bus to high bus numbers. // Status = gBS->LocateHandleBuffer ( ByProtocol, &gEfiPciIoProtocolGuid, NULL, &HandleCount, &HandleBuffer ); if (EFI_ERROR (Status)) { return EFI_NOT_FOUND; } for (Index = 0; Index < HandleCount; Index++) { Status = gBS->HandleProtocol ( HandleBuffer[Index], &gEfiPciIoProtocolGuid, (VOID **) &PciIo ); if (EFI_ERROR (Status)) { continue; } PciIo->Pci.Read ( PciIo, EfiPciIoWidthUint32, 0, sizeof (PciConfigHeader) / sizeof (UINT32), &PciConfigHeader ); if (IS_PCI_P2P (&PciConfigHeader) && (BridgeIndex < MAX_BRIDGE_INDEX)) { PciIo->GetLocation ( PciIo, &Bridges[BridgeIndex].PciSegment, &Bridges[BridgeIndex].PciBus, &Bridges[BridgeIndex].PciDevice, &Bridges[BridgeIndex].PciFunction ); Bridges[BridgeIndex].PrimaryBus = PciConfigHeader.Bridge.PrimaryBus; Bridges[BridgeIndex].SecondaryBus = PciConfigHeader.Bridge.SecondaryBus; Bridges[BridgeIndex].SubordinateBus = PciConfigHeader.Bridge.SubordinateBus; for (Index1 = 0; Index1 < RoutingTableEntries; Index1++){ // // Test whether we have found the Bridge in the slot, must be the one that directly interfaced to the board // Once we find one, store it in the SlotBridges[] // if ((RoutingTable[Index1].Slot != 0) && (Bridges[BridgeIndex].PrimaryBus == RoutingTable[Index1].Bus) && ((Bridges[BridgeIndex].PciDevice << 3) == RoutingTable[Index1].Device)) { CopyMem (&SlotBridges[SlotBridgeIndex], &Bridges[BridgeIndex], sizeof (BRIDGE_TABLE)); SlotBridgeIndex++; break; } } ++BridgeIndex; } } // // Pack up Bridges by removing those useless ones // for (Index = 0; Index < BridgeIndex;){ for (Index1 = 0; Index1 < SlotBridgeIndex; Index1++) { if (((Bridges[Index].PciBus == SlotBridges[Index1].PrimaryBus) && (Bridges[Index].PciDevice == SlotBridges[Index1].PciDevice)) || ((Bridges[Index].PciBus >= SlotBridges[Index1].SecondaryBus) && (Bridges[Index].PciBus <= SlotBridges[Index1].SubordinateBus))) { // // We have found one that meets our criteria // Index++; break; } } // // This one doesn't meet criteria, pack it // if (Index1 >= SlotBridgeIndex) { for (Index1 = Index; BridgeIndex > 1 && Index1 < BridgeIndex - 1 ; Index1++) { CopyMem (&Bridges[Index1], &Bridges[Index1 + 1], sizeof (BRIDGE_TABLE)); } BridgeIndex--; } } NumberOfBridges = BridgeIndex; // // Sort bridges low to high by Secondary bus followed by subordinate bus // if (NumberOfBridges > 1) { Index = 0; do { SortedBridgeIndex[Index] = Index; ++Index; } while (Index < NumberOfBridges); for (Index = 0; Index < NumberOfBridges - 1; Index++) { for (Index1 = Index + 1; Index1 < NumberOfBridges; Index1++) { if (Bridges[Index].SecondaryBus > Bridges[Index1].SecondaryBus) { SortedBridgeIndex[Index] = Index1; SortedBridgeIndex[Index1] = Index; } if ((Bridges[Index].SecondaryBus == Bridges[Index1].SecondaryBus) && (Bridges[Index].SubordinateBus > Bridges[Index1].SubordinateBus) ) { SortedBridgeIndex[Index] = Index1; SortedBridgeIndex[Index1] = Index; } } } } FreePool (HandleBuffer); return EFI_SUCCESS; } /** Find base Bridge for device. @param Private Legacy BIOS Instance data @param PciBus Input = Bus of device. @param PciDevice Input = Device. @param RoutingTable The platform specific routing table @param RoutingTableEntries Number of entries in table @retval EFI_SUCCESS At base bus. @retval EFI_NOT_FOUND Behind a bridge. **/ EFI_STATUS GetBaseBus ( IN LEGACY_BIOS_INSTANCE *Private, IN UINTN PciBus, IN UINTN PciDevice, IN EFI_LEGACY_IRQ_ROUTING_ENTRY *RoutingTable, IN UINTN RoutingTableEntries ) { UINTN Index; for (Index = 0; Index < RoutingTableEntries; Index++) { if ((RoutingTable[Index].Bus == PciBus) && (RoutingTable[Index].Device == (PciDevice << 3))) { return EFI_SUCCESS; } } return EFI_NOT_FOUND; } /** Translate PIRQ through busses @param Private Legacy BIOS Instance data @param PciBus Input = Bus of device. Output = Translated Bus @param PciDevice Input = Device. Output = Translated Device @param PciFunction Input = Function. Output = Translated Function @param PirqIndex Input = Original PIRQ index. If single function device then 0, otherwise 0-3. Output = Translated Index @retval EFI_SUCCESS Pirq successfully translated. @retval EFI_NOT_FOUND The device is not behind any known bridge. **/ EFI_STATUS TranslateBusPirq ( IN LEGACY_BIOS_INSTANCE *Private, IN OUT UINTN *PciBus, IN OUT UINTN *PciDevice, IN OUT UINTN *PciFunction, IN OUT UINT8 *PirqIndex ) { /* This routine traverses the PCI busses from base slot and translates the PIRQ register to the appropriate one. Example: Bus 0, Device 1 is PCI-PCI bridge that all PCI slots reside on. Primary bus# = 0 Secondary bus # = 1 Subordinate bus # is highest bus # behind this bus Bus 1, Device 0 is Slot 0 and is not a bridge. Bus 1, Device 1 is Slot 1 and is a bridge. Slot PIRQ routing is A,B,C,D. Primary bus # = 1 Secondary bus # = 2 Subordinate bus # = 5 Bus 2, Device 6 is a bridge. It has no bridges behind it. Primary bus # = 2 Secondary bus # = 3 Subordinate bus # = 3 Bridge PIRQ routing is C,D,A,B Bus 2, Device 7 is a bridge. It has 1 bridge behind it. Primary bus # = 2 Secondary bus = 4 Device 6 takes bus 2. Subordinate bus = 5. Bridge PIRQ routing is D,A,B,C Bus 4, Device 2 is a bridge. It has no bridges behind it. Primary bus # = 4 Secondary bus # = 5 Subordinate bus = 5 Bridge PIRQ routing is B,C,D,A Bus 5, Device 1 is to be programmed. Device PIRQ routing is C,D,A,B Search busses starting from slot bus for final bus >= Secondary bus and final bus <= Suborninate bus. Assumption is bus entries increase in bus number. Starting PIRQ is A,B,C,D. Bus 2, Device 7 satisfies search criteria. Rotate (A,B,C,D) left by device 7 modulo 4 giving (D,A,B,C). Bus 4, Device 2 satisfies search criteria. Rotate (D,A,B,C) left by 2 giving (B,C,D,A). No other busses match criteria. Device to be programmed is Bus 5, Device 1. Rotate (B,C,D,A) by 1 giving C,D,A,B. Translated PIRQ is C. */ UINTN LocalBus; UINTN LocalDevice; UINTN BaseBus; UINTN BaseDevice; UINTN BaseFunction; UINT8 LocalPirqIndex; BOOLEAN BaseIndexFlag; UINTN BridgeIndex; UINTN SBridgeIndex; BaseIndexFlag = FALSE; BridgeIndex = 0x00; LocalPirqIndex = *PirqIndex; LocalBus = *PciBus; LocalDevice = *PciDevice; BaseBus = *PciBus; BaseDevice = *PciDevice; BaseFunction = *PciFunction; // // LocalPirqIndex list PIRQs in rotated fashion // = 0 A,B,C,D // = 1 B,C,D,A // = 2 C,D,A,B // = 3 D,A,B,C // for (BridgeIndex = 0; BridgeIndex < NumberOfBridges; BridgeIndex++) { SBridgeIndex = SortedBridgeIndex[BridgeIndex]; // // Check if device behind this bridge // if ((LocalBus >= Bridges[SBridgeIndex].SecondaryBus) && (LocalBus <= Bridges[SBridgeIndex].SubordinateBus)) { // // If BaseIndexFlag = FALSE then have found base bridge, i.e // bridge in slot. Save info for use by IRQ routing table. // if (!BaseIndexFlag) { BaseBus = Bridges[SBridgeIndex].PciBus; BaseDevice = Bridges[SBridgeIndex].PciDevice; BaseFunction = Bridges[SBridgeIndex].PciFunction; BaseIndexFlag = TRUE; } else { LocalPirqIndex = (UINT8) ((LocalPirqIndex + (UINT8)Bridges[SBridgeIndex].PciDevice)%4); } // // Check if at device. If not get new PCI location & PIRQ // if (Bridges[SBridgeIndex].SecondaryBus == (UINT8) LocalBus) { // // Translate PIRQ // LocalPirqIndex = (UINT8) ((LocalPirqIndex + (UINT8) (LocalDevice)) % 4); break; } } } // // In case we fail to find the Bridge just above us, this is some potential error and we want to warn the user // if(BridgeIndex >= NumberOfBridges){ DEBUG ((EFI_D_ERROR, "Cannot Find IRQ Routing for Bus %d, Device %d, Function %d\n", *PciBus, *PciDevice, *PciFunction)); } *PirqIndex = LocalPirqIndex; *PciBus = BaseBus; *PciDevice = BaseDevice; *PciFunction = BaseFunction; return EFI_SUCCESS; } /** Copy the $PIR table as required. @param Private Legacy BIOS Instance data @param RoutingTable Pointer to IRQ routing table @param RoutingTableEntries IRQ routing table entries @param PirqTable Pointer to $PIR table @param PirqTableSize Length of table **/ VOID CopyPirqTable ( IN LEGACY_BIOS_INSTANCE *Private, IN EFI_LEGACY_IRQ_ROUTING_ENTRY *RoutingTable, IN UINTN RoutingTableEntries, IN EFI_LEGACY_PIRQ_TABLE_HEADER *PirqTable, IN UINTN PirqTableSize ) { EFI_IA32_REGISTER_SET Regs; UINT32 Granularity; // // Copy $PIR table, if it exists. // if (PirqTable != NULL) { Private->LegacyRegion->UnLock ( Private->LegacyRegion, 0xE0000, 0x20000, &Granularity ); Private->InternalIrqRoutingTable = RoutingTable; Private->NumberIrqRoutingEntries = (UINT16) (RoutingTableEntries); ZeroMem (&Regs, sizeof (EFI_IA32_REGISTER_SET)); Regs.X.AX = Legacy16GetTableAddress; Regs.X.CX = (UINT16) PirqTableSize; // // Allocate at F segment according to PCI IRQ Routing Table Specification // Regs.X.BX = (UINT16) 0x1; // // 16-byte boundary alignment requirement according to // PCI IRQ Routing Table Specification // Regs.X.DX = 0x10; Private->LegacyBios.FarCall86 ( &Private->LegacyBios, Private->Legacy16CallSegment, Private->Legacy16CallOffset, &Regs, NULL, 0 ); Private->Legacy16Table->IrqRoutingTablePointer = (UINT32) (Regs.X.DS * 16 + Regs.X.BX); if (Regs.X.AX != 0) { DEBUG ((EFI_D_ERROR, "PIRQ table length insufficient - %x\n", PirqTableSize)); } else { DEBUG ((EFI_D_INFO, "PIRQ table in legacy region - %x\n", Private->Legacy16Table->IrqRoutingTablePointer)); Private->Legacy16Table->IrqRoutingTableLength = (UINT32)PirqTableSize; CopyMem ( (VOID *) (UINTN)Private->Legacy16Table->IrqRoutingTablePointer, PirqTable, PirqTableSize ); } Private->Cpu->FlushDataCache (Private->Cpu, 0xE0000, 0x20000, EfiCpuFlushTypeWriteBackInvalidate); Private->LegacyRegion->Lock ( Private->LegacyRegion, 0xE0000, 0x20000, &Granularity ); } Private->PciInterruptLine = TRUE; mHandleCount = 0; } /** Dump EFI_LEGACY_INSTALL_PCI_HANDLER structure information. @param PciHandle The pointer to EFI_LEGACY_INSTALL_PCI_HANDLER structure **/ VOID DumpPciHandle ( IN EFI_LEGACY_INSTALL_PCI_HANDLER *PciHandle ) { DEBUG ((EFI_D_INFO, "PciBus - %02x\n", (UINTN)PciHandle->PciBus)); DEBUG ((EFI_D_INFO, "PciDeviceFun - %02x\n", (UINTN)PciHandle->PciDeviceFun)); DEBUG ((EFI_D_INFO, "PciSegment - %02x\n", (UINTN)PciHandle->PciSegment)); DEBUG ((EFI_D_INFO, "PciClass - %02x\n", (UINTN)PciHandle->PciClass)); DEBUG ((EFI_D_INFO, "PciSubclass - %02x\n", (UINTN)PciHandle->PciSubclass)); DEBUG ((EFI_D_INFO, "PciInterface - %02x\n", (UINTN)PciHandle->PciInterface)); DEBUG ((EFI_D_INFO, "PrimaryIrq - %02x\n", (UINTN)PciHandle->PrimaryIrq)); DEBUG ((EFI_D_INFO, "PrimaryReserved - %02x\n", (UINTN)PciHandle->PrimaryReserved)); DEBUG ((EFI_D_INFO, "PrimaryControl - %04x\n", (UINTN)PciHandle->PrimaryControl)); DEBUG ((EFI_D_INFO, "PrimaryBase - %04x\n", (UINTN)PciHandle->PrimaryBase)); DEBUG ((EFI_D_INFO, "PrimaryBusMaster - %04x\n", (UINTN)PciHandle->PrimaryBusMaster)); DEBUG ((EFI_D_INFO, "SecondaryIrq - %02x\n", (UINTN)PciHandle->SecondaryIrq)); DEBUG ((EFI_D_INFO, "SecondaryReserved - %02x\n", (UINTN)PciHandle->SecondaryReserved)); DEBUG ((EFI_D_INFO, "SecondaryControl - %04x\n", (UINTN)PciHandle->SecondaryControl)); DEBUG ((EFI_D_INFO, "SecondaryBase - %04x\n", (UINTN)PciHandle->SecondaryBase)); DEBUG ((EFI_D_INFO, "SecondaryBusMaster - %04x\n", (UINTN)PciHandle->SecondaryBusMaster)); return; } /** Copy the $PIR table as required. @param Private Legacy BIOS Instance data @param PciIo Pointer to PCI_IO protocol @param PciIrq Pci IRQ number @param PciConfigHeader Type00 Pci configuration header **/ VOID InstallLegacyIrqHandler ( IN LEGACY_BIOS_INSTANCE *Private, IN EFI_PCI_IO_PROTOCOL *PciIo, IN UINT8 PciIrq, IN PCI_TYPE00 *PciConfigHeader ) { EFI_IA32_REGISTER_SET Regs; UINT16 LegMask; UINTN PciSegment; UINTN PciBus; UINTN PciDevice; UINTN PciFunction; EFI_LEGACY_8259_PROTOCOL *Legacy8259; UINT16 PrimaryMaster; UINT16 SecondaryMaster; UINTN TempData; UINTN RegisterAddress; UINT32 Granularity; PrimaryMaster = 0; SecondaryMaster = 0; Legacy8259 = Private->Legacy8259; // // Disable interrupt in PIC, in case shared, to prevent an // interrupt from occuring. // Legacy8259->GetMask ( Legacy8259, &LegMask, NULL, NULL, NULL ); LegMask = (UINT16) (LegMask | (UINT16) (1 << PciIrq)); Legacy8259->SetMask ( Legacy8259, &LegMask, NULL, NULL, NULL ); PciIo->GetLocation ( PciIo, &PciSegment, &PciBus, &PciDevice, &PciFunction ); Private->IntThunk->PciHandler.PciBus = (UINT8) PciBus; Private->IntThunk->PciHandler.PciDeviceFun = (UINT8) ((PciDevice << 3) + PciFunction); Private->IntThunk->PciHandler.PciSegment = (UINT8) PciSegment; Private->IntThunk->PciHandler.PciClass = PciConfigHeader->Hdr.ClassCode[2]; Private->IntThunk->PciHandler.PciSubclass = PciConfigHeader->Hdr.ClassCode[1]; Private->IntThunk->PciHandler.PciInterface = PciConfigHeader->Hdr.ClassCode[0]; // // Use native mode base address registers in two cases: // 1. Programming Interface (PI) register indicates Primary Controller is // in native mode OR // 2. PCI device Sub Class Code is not IDE // Private->IntThunk->PciHandler.PrimaryBusMaster = (UINT16)(PciConfigHeader->Device.Bar[4] & 0xfffc); if (((PciConfigHeader->Hdr.ClassCode[0] & 0x01) != 0) || (PciConfigHeader->Hdr.ClassCode[1] != PCI_CLASS_MASS_STORAGE_IDE)) { Private->IntThunk->PciHandler.PrimaryIrq = PciIrq; Private->IntThunk->PciHandler.PrimaryBase = (UINT16) (PciConfigHeader->Device.Bar[0] & 0xfffc); Private->IntThunk->PciHandler.PrimaryControl = (UINT16) ((PciConfigHeader->Device.Bar[1] & 0xfffc) + 2); } else { Private->IntThunk->PciHandler.PrimaryIrq = 14; Private->IntThunk->PciHandler.PrimaryBase = 0x1f0; Private->IntThunk->PciHandler.PrimaryControl = 0x3f6; } // // Secondary controller data // if (Private->IntThunk->PciHandler.PrimaryBusMaster != 0) { Private->IntThunk->PciHandler.SecondaryBusMaster = (UINT16) ((PciConfigHeader->Device.Bar[4] & 0xfffc) + 8); PrimaryMaster = (UINT16) (Private->IntThunk->PciHandler.PrimaryBusMaster + 2); SecondaryMaster = (UINT16) (Private->IntThunk->PciHandler.SecondaryBusMaster + 2); // // Clear pending interrupts in Bus Master registers // IoWrite16 (PrimaryMaster, 0x04); IoWrite16 (SecondaryMaster, 0x04); } // // Use native mode base address registers in two cases: // 1. Programming Interface (PI) register indicates Secondary Controller is // in native mode OR // 2. PCI device Sub Class Code is not IDE // if (((PciConfigHeader->Hdr.ClassCode[0] & 0x04) != 0) || (PciConfigHeader->Hdr.ClassCode[1] != PCI_CLASS_MASS_STORAGE_IDE)) { Private->IntThunk->PciHandler.SecondaryIrq = PciIrq; Private->IntThunk->PciHandler.SecondaryBase = (UINT16) (PciConfigHeader->Device.Bar[2] & 0xfffc); Private->IntThunk->PciHandler.SecondaryControl = (UINT16) ((PciConfigHeader->Device.Bar[3] & 0xfffc) + 2); } else { Private->IntThunk->PciHandler.SecondaryIrq = 15; Private->IntThunk->PciHandler.SecondaryBase = 0x170; Private->IntThunk->PciHandler.SecondaryControl = 0x376; } // // Clear pending interrupts in IDE Command Block Status reg before we // Thunk to CSM16 below. Don't want a pending Interrupt before we // install the handlers as wierd corruption would occur and hang system. // // // Read IDE CMD blk status reg to clear out any pending interrupts. // Do here for Primary and Secondary IDE channels // RegisterAddress = (UINT16)Private->IntThunk->PciHandler.PrimaryBase + 0x07; IoRead8 (RegisterAddress); RegisterAddress = (UINT16)Private->IntThunk->PciHandler.SecondaryBase + 0x07; IoRead8 (RegisterAddress); Private->IntThunk->PciHandler.PrimaryReserved = 0; Private->IntThunk->PciHandler.SecondaryReserved = 0; Private->LegacyRegion->UnLock ( Private->LegacyRegion, 0xE0000, 0x20000, &Granularity ); Regs.X.AX = Legacy16InstallPciHandler; TempData = (UINTN) &Private->IntThunk->PciHandler; Regs.X.ES = EFI_SEGMENT ((UINT32) TempData); Regs.X.BX = EFI_OFFSET ((UINT32) TempData); DumpPciHandle (&Private->IntThunk->PciHandler); Private->LegacyBios.FarCall86 ( &Private->LegacyBios, Private->Legacy16CallSegment, Private->Legacy16CallOffset, &Regs, NULL, 0 ); Private->Cpu->FlushDataCache (Private->Cpu, 0xE0000, 0x20000, EfiCpuFlushTypeWriteBackInvalidate); Private->LegacyRegion->Lock ( Private->LegacyRegion, 0xE0000, 0x20000, &Granularity ); } /** Program the interrupt routing register in all the PCI devices. On a PC AT system this register contains the 8259 IRQ vector that matches it's PCI interrupt. @param Private Legacy BIOS Instance data @retval EFI_SUCCESS Succeed. @retval EFI_ALREADY_STARTED All PCI devices have been processed. **/ EFI_STATUS PciProgramAllInterruptLineRegisters ( IN LEGACY_BIOS_INSTANCE *Private ) { EFI_STATUS Status; EFI_PCI_IO_PROTOCOL *PciIo; EFI_LEGACY_8259_PROTOCOL *Legacy8259; EFI_LEGACY_INTERRUPT_PROTOCOL *LegacyInterrupt; EFI_LEGACY_BIOS_PLATFORM_PROTOCOL *LegacyBiosPlatform; UINT8 InterruptPin; UINTN Index; UINTN HandleCount; EFI_HANDLE *HandleBuffer; UINTN MassStorageHandleCount; EFI_HANDLE *MassStorageHandleBuffer; UINTN MassStorageHandleIndex; UINT8 PciIrq; UINT16 Command; UINTN PciSegment; UINTN PciBus; UINTN PciDevice; UINTN PciFunction; EFI_LEGACY_IRQ_ROUTING_ENTRY *RoutingTable; UINTN RoutingTableEntries; UINT16 LegMask; UINT16 LegEdgeLevel; PCI_TYPE00 PciConfigHeader; EFI_LEGACY_PIRQ_TABLE_HEADER *PirqTable; UINTN PirqTableSize; UINTN Flags; HDD_INFO *HddInfo; UINT64 Supports; // // Note - This routine use to return immediately if Private->PciInterruptLine // was true. Routine changed since resets etc can cause not all // PciIo protocols to be registered the first time through. // New algorithm is to do the copy $PIR table on first pass and save // HandleCount on first pass. If subsequent passes LocateHandleBuffer gives // a larger handle count then proceed with body of function else return // EFI_ALREADY_STARTED. In addition check if PCI device InterruptLine != 0. // If zero then function unprogrammed else skip function. // Legacy8259 = Private->Legacy8259; LegacyInterrupt = Private->LegacyInterrupt; LegacyBiosPlatform = Private->LegacyBiosPlatform; LegacyBiosPlatform->GetRoutingTable ( Private->LegacyBiosPlatform, (VOID *) &RoutingTable, &RoutingTableEntries, (VOID *) &PirqTable, &PirqTableSize, NULL, NULL ); CreateBridgeTable (RoutingTable, RoutingTableEntries); if (!Private->PciInterruptLine) { CopyPirqTable ( Private, RoutingTable, RoutingTableEntries, PirqTable, PirqTableSize ); } Status = gBS->LocateHandleBuffer ( ByProtocol, &gEfiPciIoProtocolGuid, NULL, &HandleCount, &HandleBuffer ); if (EFI_ERROR (Status)) { return EFI_NOT_FOUND; } if (HandleCount == mHandleCount) { FreePool (HandleBuffer); return EFI_ALREADY_STARTED; } if (mHandleCount == 0x00) { mHandleCount = HandleCount; } for (Index = 0; Index < HandleCount; Index++) { // // If VGA then only do VGA to allow drives fore time to spin up // otherwise assign PCI IRQs to all potential devices. // if ((mVgaInstallationInProgress) && (HandleBuffer[Index] != mVgaHandle)) { continue; } else { // // Force code to go through all handles next time called if video. // This will catch case where HandleCount doesn't change but want // to get drive info etc. // mHandleCount = 0x00; } Status = gBS->HandleProtocol ( HandleBuffer[Index], &gEfiPciIoProtocolGuid, (VOID **) &PciIo ); ASSERT_EFI_ERROR (Status); // // Test whether the device can be enabled or not. // If it can't be enabled, then just skip it to avoid further operation. // PciIo->Pci.Read ( PciIo, EfiPciIoWidthUint32, 0, sizeof (PciConfigHeader) / sizeof (UINT32), &PciConfigHeader ); Command = PciConfigHeader.Hdr.Command; // // Note PciIo->Attributes does not program the PCI command register // Status = PciIo->Attributes ( PciIo, EfiPciIoAttributeOperationSupported, 0, &Supports ); if (!EFI_ERROR (Status)) { Supports &= (UINT64)EFI_PCI_DEVICE_ENABLE; Status = PciIo->Attributes ( PciIo, EfiPciIoAttributeOperationEnable, Supports, NULL ); } PciIo->Pci.Write (PciIo, EfiPciIoWidthUint16, 0x04, 1, &Command); if (EFI_ERROR (Status)) { continue; } InterruptPin = PciConfigHeader.Device.InterruptPin; if ((InterruptPin != 0) && (PciConfigHeader.Device.InterruptLine == PCI_INT_LINE_UNKNOWN)) { PciIo->GetLocation ( PciIo, &PciSegment, &PciBus, &PciDevice, &PciFunction ); // // Translate PIRQ index back thru busses to slot bus with InterruptPin // zero based // InterruptPin -= 1; Status = GetBaseBus ( Private, PciBus, PciDevice, RoutingTable, RoutingTableEntries ); if (Status == EFI_NOT_FOUND) { TranslateBusPirq ( Private, &PciBus, &PciDevice, &PciFunction, &InterruptPin ); } // // Translate InterruptPin(0-3) into PIRQ // Status = LegacyBiosPlatform->TranslatePirq ( LegacyBiosPlatform, PciBus, (PciDevice << 3), PciFunction, &InterruptPin, &PciIrq ); // // TranslatePirq() should never fail or we are in trouble // If it does return failure status, check your PIRQ routing table to see if some item is missing or incorrect // if (EFI_ERROR (Status)) { DEBUG ((EFI_D_ERROR, "Translate Pirq Failed - Status = %r\n ", Status)); continue; } LegacyInterrupt->WritePirq ( LegacyInterrupt, InterruptPin, PciIrq ); // // Check if device has an OPROM associated with it. // If not invoke special 16-bit function, to allow 16-bit // code to install an interrupt handler. // Status = LegacyBiosCheckPciRom ( &Private->LegacyBios, HandleBuffer[Index], NULL, NULL, &Flags ); if ((EFI_ERROR (Status)) && (PciConfigHeader.Hdr.ClassCode[2] == PCI_CLASS_MASS_STORAGE)) { // // Device has no OPROM associated with it and is a mass storage // device. It needs to have an PCI IRQ handler installed. To // correctly install the handler we need to insure device is // connected. The device may just have register itself but not // been connected. Re-read PCI config space after as it can // change // // // Get IDE Handle. If matches handle then skip ConnectController // since ConnectController may force native mode and we don't // want that for primary IDE controller // MassStorageHandleCount = 0; MassStorageHandleBuffer = NULL; LegacyBiosPlatform->GetPlatformHandle ( Private->LegacyBiosPlatform, EfiGetPlatformIdeHandle, 0, &MassStorageHandleBuffer, &MassStorageHandleCount, NULL ); HddInfo = &Private->IntThunk->EfiToLegacy16BootTable.HddInfo[0]; LegacyBiosBuildIdeData (Private, &HddInfo, 0); PciIo->Pci.Read ( PciIo, EfiPciIoWidthUint32, 0, sizeof (PciConfigHeader) / sizeof (UINT32), &PciConfigHeader ); for (MassStorageHandleIndex = 0; MassStorageHandleIndex < MassStorageHandleCount; MassStorageHandleIndex++) { if (MassStorageHandleBuffer[MassStorageHandleIndex] == HandleBuffer[Index]) { // // InstallLegacyIrqHandler according to Platform requirement // InstallLegacyIrqHandler ( Private, PciIo, PciIrq, &PciConfigHeader ); break; } } } // // Write InterruptPin and enable 8259. // PciIo->Pci.Write ( PciIo, EfiPciIoWidthUint8, 0x3c, 1, &PciIrq ); Private->IntThunk->EfiToLegacy16BootTable.PciIrqMask = (UINT16) (Private->IntThunk->EfiToLegacy16BootTable.PciIrqMask | (UINT16) (1 << PciIrq)); Legacy8259->GetMask ( Legacy8259, &LegMask, &LegEdgeLevel, NULL, NULL ); LegMask = (UINT16) (LegMask & (UINT16)~(1 << PciIrq)); LegEdgeLevel = (UINT16) (LegEdgeLevel | (UINT16) (1 << PciIrq)); Legacy8259->SetMask ( Legacy8259, &LegMask, &LegEdgeLevel, NULL, NULL ); } } FreePool (HandleBuffer); return EFI_SUCCESS; } /** Find & verify PnP Expansion header in ROM image @param Private Protocol instance pointer. @param FirstHeader 1 = Find first header, 0 = Find successive headers @param PnpPtr Input Rom start if FirstHeader =1, Current Header otherwise Output Next header, if it exists @retval EFI_SUCCESS Next Header found at BasePnpPtr @retval EFI_NOT_FOUND No more headers **/ EFI_STATUS FindNextPnpExpansionHeader ( IN LEGACY_BIOS_INSTANCE *Private, IN BOOLEAN FirstHeader, IN OUT LEGACY_PNP_EXPANSION_HEADER **PnpPtr ) { UINTN TempData; LEGACY_PNP_EXPANSION_HEADER *LocalPnpPtr; LocalPnpPtr = *PnpPtr; if (FirstHeader == FIRST_INSTANCE) { mBasePnpPtr = LocalPnpPtr; mBbsRomSegment = (UINT16) ((UINTN) mBasePnpPtr >> 4); // // Offset 0x1a gives offset to PnP expansion header for the first // instance, there after the structure gives the offset to the next // structure // LocalPnpPtr = (LEGACY_PNP_EXPANSION_HEADER *) ((UINT8 *) LocalPnpPtr + 0x1a); TempData = (*((UINT16 *) LocalPnpPtr)); } else { TempData = (UINT16) LocalPnpPtr->NextHeader; } LocalPnpPtr = (LEGACY_PNP_EXPANSION_HEADER *) (((UINT8 *) mBasePnpPtr + TempData)); // // Search for PnP table in Shadowed ROM // *PnpPtr = LocalPnpPtr; if (*(UINT32 *) LocalPnpPtr == SIGNATURE_32 ('$', 'P', 'n', 'P')) { return EFI_SUCCESS; } else { return EFI_NOT_FOUND; } } /** Update list of Bev or BCV table entries. @param Private Protocol instance pointer. @param RomStart Table of ROM start address in RAM/ROM. PciIo _ Handle to PCI IO for this device @param PciIo Instance of PCI I/O Protocol @retval EFI_SUCCESS Always should succeed. **/ EFI_STATUS UpdateBevBcvTable ( IN LEGACY_BIOS_INSTANCE *Private, IN EFI_LEGACY_EXPANSION_ROM_HEADER *RomStart, IN EFI_PCI_IO_PROTOCOL *PciIo ) { VOID *RomEnd; BBS_TABLE *BbsTable; UINTN BbsIndex; EFI_LEGACY_EXPANSION_ROM_HEADER *PciPtr; LEGACY_PNP_EXPANSION_HEADER *PnpPtr; BOOLEAN Instance; EFI_STATUS Status; UINTN Segment; UINTN Bus; UINTN Device; UINTN Function; UINT8 Class; UINT16 DeviceType; Segment = 0; Bus = 0; Device = 0; Function = 0; Class = 0; DeviceType = BBS_UNKNOWN; // // Skip floppy and 2*onboard IDE controller entries(Master/Slave per // controller). // BbsIndex = Private->IntThunk->EfiToLegacy16BootTable.NumberBbsEntries; BbsTable = (BBS_TABLE*)(UINTN) Private->IntThunk->EfiToLegacy16BootTable.BbsTable; PnpPtr = (LEGACY_PNP_EXPANSION_HEADER *) RomStart; PciPtr = (EFI_LEGACY_EXPANSION_ROM_HEADER *) RomStart; RomEnd = (VOID *) (PciPtr->Size512 * 512 + (UINTN) PciPtr); Instance = FIRST_INSTANCE; // // OPROMs like PXE may not be tied to a piece of hardware and thus // don't have a PciIo associated with them // if (PciIo != NULL) { PciIo->GetLocation ( PciIo, &Segment, &Bus, &Device, &Function ); PciIo->Pci.Read ( PciIo, EfiPciIoWidthUint8, 0x0b, 1, &Class ); if (Class == PCI_CLASS_MASS_STORAGE) { DeviceType = BBS_HARDDISK; } else { if (Class == PCI_CLASS_NETWORK) { DeviceType = BBS_EMBED_NETWORK; } } } while (TRUE) { Status = FindNextPnpExpansionHeader (Private, Instance, &PnpPtr); Instance = NOT_FIRST_INSTANCE; if (EFI_ERROR (Status)) { break; } // // There can be additional $PnP headers within the OPROM. // Example: SCSI can have one per drive. // BbsTable[BbsIndex].BootPriority = BBS_UNPRIORITIZED_ENTRY; BbsTable[BbsIndex].DeviceType = DeviceType; BbsTable[BbsIndex].Bus = (UINT32) Bus; BbsTable[BbsIndex].Device = (UINT32) Device; BbsTable[BbsIndex].Function = (UINT32) Function; BbsTable[BbsIndex].StatusFlags.OldPosition = 0; BbsTable[BbsIndex].StatusFlags.Reserved1 = 0; BbsTable[BbsIndex].StatusFlags.Enabled = 0; BbsTable[BbsIndex].StatusFlags.Failed = 0; BbsTable[BbsIndex].StatusFlags.MediaPresent = 0; BbsTable[BbsIndex].StatusFlags.Reserved2 = 0; BbsTable[BbsIndex].Class = PnpPtr->Class; BbsTable[BbsIndex].SubClass = PnpPtr->SubClass; BbsTable[BbsIndex].DescStringOffset = PnpPtr->ProductNamePointer; BbsTable[BbsIndex].DescStringSegment = mBbsRomSegment; BbsTable[BbsIndex].MfgStringOffset = PnpPtr->MfgPointer; BbsTable[BbsIndex].MfgStringSegment = mBbsRomSegment; BbsTable[BbsIndex].BootHandlerSegment = mBbsRomSegment; // // Have seen case where PXE base code have PnP expansion ROM // header but no Bcv or Bev vectors. // if (PnpPtr->Bcv != 0) { BbsTable[BbsIndex].BootHandlerOffset = PnpPtr->Bcv; ++BbsIndex; } if (PnpPtr->Bev != 0) { BbsTable[BbsIndex].BootHandlerOffset = PnpPtr->Bev; BbsTable[BbsIndex].DeviceType = BBS_BEV_DEVICE; ++BbsIndex; } if ((PnpPtr == (LEGACY_PNP_EXPANSION_HEADER *) PciPtr) || (PnpPtr > (LEGACY_PNP_EXPANSION_HEADER *) RomEnd)) { break; } } BbsTable[BbsIndex].BootPriority = BBS_IGNORE_ENTRY; Private->IntThunk->EfiToLegacy16BootTable.NumberBbsEntries = (UINT32) BbsIndex; return EFI_SUCCESS; } /** Shadow all the PCI legacy ROMs. Use data from the Legacy BIOS Protocol to chose the order. Skip any devices that have already have legacy BIOS run. @param Private Protocol instance pointer. @retval EFI_SUCCESS Succeed. @retval EFI_UNSUPPORTED Cannot get VGA device handle. **/ EFI_STATUS PciShadowRoms ( IN LEGACY_BIOS_INSTANCE *Private ) { EFI_STATUS Status; EFI_PCI_IO_PROTOCOL *PciIo; PCI_TYPE00 Pci; UINTN Index; UINTN HandleCount; EFI_HANDLE *HandleBuffer; EFI_HANDLE VgaHandle; EFI_HANDLE FirstHandle; VOID **RomStart; UINTN Flags; PCI_TYPE00 PciConfigHeader; UINT16 *Command; UINT64 Supports; // // Make the VGA device first // Status = Private->LegacyBiosPlatform->GetPlatformHandle ( Private->LegacyBiosPlatform, EfiGetPlatformVgaHandle, 0, &HandleBuffer, &HandleCount, NULL ); if (EFI_ERROR (Status)) { return EFI_UNSUPPORTED; } VgaHandle = HandleBuffer[0]; Status = gBS->LocateHandleBuffer ( ByProtocol, &gEfiPciIoProtocolGuid, NULL, &HandleCount, &HandleBuffer ); if (EFI_ERROR (Status)) { return Status; } // // Place the VGA handle as first. // for (Index = 0; Index < HandleCount; Index++) { if (HandleBuffer[Index] == VgaHandle) { FirstHandle = HandleBuffer[0]; HandleBuffer[0] = HandleBuffer[Index]; HandleBuffer[Index] = FirstHandle; break; } } // // Allocate memory to save Command WORD from each device. We do this // to restore devices to same state as EFI after switching to legacy. // Command = (UINT16 *) AllocatePool ( sizeof (UINT16) * (HandleCount + 1) ); if (NULL == Command) { FreePool (HandleBuffer); return EFI_OUT_OF_RESOURCES; } // // Disconnect all EFI devices first. This covers cases where alegacy BIOS // may control multiple PCI devices. // for (Index = 0; Index < HandleCount; Index++) { Status = gBS->HandleProtocol ( HandleBuffer[Index], &gEfiPciIoProtocolGuid, (VOID **) &PciIo ); ASSERT_EFI_ERROR (Status); // // Save command register for "connect" loop // PciIo->Pci.Read ( PciIo, EfiPciIoWidthUint32, 0, sizeof (PciConfigHeader) / sizeof (UINT32), &PciConfigHeader ); Command[Index] = PciConfigHeader.Hdr.Command; // // Skip any device that already has a legacy ROM run // Status = IsLegacyRom (HandleBuffer[Index]); if (!EFI_ERROR (Status)) { continue; } // // Stop EFI Drivers with oprom. // gBS->DisconnectController ( HandleBuffer[Index], NULL, NULL ); } // // For every device that has not had a legacy ROM started. Start a legacy ROM. // for (Index = 0; Index < HandleCount; Index++) { Status = gBS->HandleProtocol ( HandleBuffer[Index], &gEfiPciIoProtocolGuid, (VOID **) &PciIo ); ASSERT_EFI_ERROR (Status); // // Here make sure if one VGA have been shadowed, // then wil not shadowed another one. // PciIo->Pci.Read ( PciIo, EfiPciIoWidthUint32, 0, sizeof (Pci) / sizeof (UINT32), &Pci ); // // Only one Video OPROM can be given control in BIOS phase. If there are multiple Video devices, // one will work in legacy mode (OPROM will be given control) and // other Video devices will work in native mode (OS driver will handle these devices). // if (IS_PCI_DISPLAY (&Pci) && Index != 0) { continue; } // // Skip any device that already has a legacy ROM run // Status = IsLegacyRom (HandleBuffer[Index]); if (!EFI_ERROR (Status)) { continue; } // // If legacy VBIOS Oprom has not been dispatched before, install legacy VBIOS here. // if (IS_PCI_DISPLAY (&Pci) && Index == 0) { Status = LegacyBiosInstallVgaRom (Private); // // A return status of EFI_NOT_FOUND is considered valid (No EFI // driver is controlling video). // ASSERT ((Status == EFI_SUCCESS) || (Status == EFI_NOT_FOUND)); continue; } // // Install legacy ROM // Status = LegacyBiosInstallPciRom ( &Private->LegacyBios, HandleBuffer[Index], NULL, &Flags, NULL, NULL, (VOID **) &RomStart, NULL ); if (EFI_ERROR (Status)) { if (!((Status == EFI_UNSUPPORTED) && (Flags == NO_ROM))) { continue; } } // // Restore Command register so legacy has same devices enabled or disabled // as EFI. // If Flags = NO_ROM use command register as is. This covers the // following cases: // Device has no ROMs associated with it. // Device has ROM associated with it but was already // installed. // = ROM_FOUND but not VALID_LEGACY_ROM, disable it. // = ROM_FOUND and VALID_LEGACY_ROM, enable it. // if ((Flags & ROM_FOUND) == ROM_FOUND) { if ((Flags & VALID_LEGACY_ROM) == 0) { Command[Index] = 0; } else { // // For several VGAs, only one of them can be enabled. // Status = PciIo->Attributes ( PciIo, EfiPciIoAttributeOperationSupported, 0, &Supports ); if (!EFI_ERROR (Status)) { Supports &= (UINT64)EFI_PCI_DEVICE_ENABLE; Status = PciIo->Attributes ( PciIo, EfiPciIoAttributeOperationEnable, Supports, NULL ); } if (!EFI_ERROR (Status)) { Command[Index] = 0x1f; } } } PciIo->Pci.Write ( PciIo, EfiPciIoWidthUint16, 0x04, 1, &Command[Index] ); } FreePool (Command); FreePool (HandleBuffer); return EFI_SUCCESS; } /** Test to see if a legacy PCI ROM exists for this device. Optionally return the Legacy ROM instance for this PCI device. @param This Protocol instance pointer. @param PciHandle The PCI PC-AT OPROM from this devices ROM BAR will be loaded @param RomImage Return the legacy PCI ROM for this device @param RomSize Size of ROM Image @param Flags Indicates if ROM found and if PC-AT. @retval EFI_SUCCESS Legacy Option ROM available for this device @retval EFI_UNSUPPORTED Legacy Option ROM not supported. **/ EFI_STATUS EFIAPI LegacyBiosCheckPciRom ( IN EFI_LEGACY_BIOS_PROTOCOL *This, IN EFI_HANDLE PciHandle, OUT VOID **RomImage, OPTIONAL OUT UINTN *RomSize, OPTIONAL OUT UINTN *Flags ) { return LegacyBiosCheckPciRomEx ( This, PciHandle, RomImage, RomSize, NULL, Flags, NULL, NULL ); } /** Routine Description: Test to see if a legacy PCI ROM exists for this device. Optionally return the Legacy ROM instance for this PCI device. @param[in] This Protocol instance pointer. @param[in] PciHandle The PCI PC-AT OPROM from this devices ROM BAR will be loaded @param[out] RomImage Return the legacy PCI ROM for this device @param[out] RomSize Size of ROM Image @param[out] RuntimeImageLength Runtime size of ROM Image @param[out] Flags Indicates if ROM found and if PC-AT. @param[out] OpromRevision Revision of the PCI Rom @param[out] ConfigUtilityCodeHeaderPointer of Configuration Utility Code Header @return EFI_SUCCESS Legacy Option ROM available for this device @return EFI_ALREADY_STARTED This device is already managed by its Oprom @return EFI_UNSUPPORTED Legacy Option ROM not supported. **/ EFI_STATUS LegacyBiosCheckPciRomEx ( IN EFI_LEGACY_BIOS_PROTOCOL *This, IN EFI_HANDLE PciHandle, OUT VOID **RomImage, OPTIONAL OUT UINTN *RomSize, OPTIONAL OUT UINTN *RuntimeImageLength, OPTIONAL OUT UINTN *Flags, OPTIONAL OUT UINT8 *OpromRevision, OPTIONAL OUT VOID **ConfigUtilityCodeHeader OPTIONAL ) { EFI_STATUS Status; LEGACY_BIOS_INSTANCE *Private; EFI_PCI_IO_PROTOCOL *PciIo; UINTN LocalRomSize; VOID *LocalRomImage; PCI_TYPE00 PciConfigHeader; VOID *LocalConfigUtilityCodeHeader; LocalConfigUtilityCodeHeader = NULL; *Flags = NO_ROM; Status = gBS->HandleProtocol ( PciHandle, &gEfiPciIoProtocolGuid, (VOID **) &PciIo ); if (EFI_ERROR (Status)) { return EFI_UNSUPPORTED; } // // See if the option ROM for PciHandle has already been executed // Status = IsLegacyRom (PciHandle); if (!EFI_ERROR (Status)) { *Flags |= (UINTN)(ROM_FOUND | VALID_LEGACY_ROM); return EFI_SUCCESS; } // // Check for PCI ROM Bar // LocalRomSize = (UINTN) PciIo->RomSize; LocalRomImage = PciIo->RomImage; if (LocalRomSize != 0) { *Flags |= ROM_FOUND; } // // PCI specification states you should check VendorId and Device Id. // PciIo->Pci.Read ( PciIo, EfiPciIoWidthUint32, 0, sizeof (PciConfigHeader) / sizeof (UINT32), &PciConfigHeader ); Private = LEGACY_BIOS_INSTANCE_FROM_THIS (This); Status = GetPciLegacyRom ( Private->Csm16PciInterfaceVersion, PciConfigHeader.Hdr.VendorId, PciConfigHeader.Hdr.DeviceId, &LocalRomImage, &LocalRomSize, RuntimeImageLength, OpromRevision, &LocalConfigUtilityCodeHeader ); if (EFI_ERROR (Status)) { return EFI_UNSUPPORTED; } *Flags |= VALID_LEGACY_ROM; // // See if Configuration Utility Code Header valid // if (LocalConfigUtilityCodeHeader != NULL) { *Flags |= ROM_WITH_CONFIG; } if (ConfigUtilityCodeHeader != NULL) { *ConfigUtilityCodeHeader = LocalConfigUtilityCodeHeader; } if (RomImage != NULL) { *RomImage = LocalRomImage; } if (RomSize != NULL) { *RomSize = LocalRomSize; } return EFI_SUCCESS; } /** Load a legacy PC-AT OPROM on the PciHandle device. Return information about how many disks were added by the OPROM and the shadow address and size. DiskStart & DiskEnd are INT 13h drive letters. Thus 0x80 is C: @retval EFI_SUCCESS Legacy ROM loaded for this device @retval EFI_NOT_FOUND No PS2 Keyboard found **/ EFI_STATUS EnablePs2Keyboard ( VOID ) { EFI_STATUS Status; EFI_HANDLE *HandleBuffer; UINTN HandleCount; EFI_ISA_IO_PROTOCOL *IsaIo; UINTN Index; // // Get SimpleTextIn and find PS2 controller // Status = gBS->LocateHandleBuffer ( ByProtocol, &gEfiSimpleTextInProtocolGuid, NULL, &HandleCount, &HandleBuffer ); if (EFI_ERROR (Status)) { return EFI_NOT_FOUND; } for (Index = 0; Index < HandleCount; Index++) { // // Open the IO Abstraction(s) needed to perform the supported test // Status = gBS->OpenProtocol ( HandleBuffer[Index], &gEfiIsaIoProtocolGuid, (VOID **) &IsaIo, NULL, HandleBuffer[Index], EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL ); if (!EFI_ERROR (Status)) { // // Use the ISA I/O Protocol to see if Controller is the Keyboard // controller // if (IsaIo->ResourceList->Device.HID != EISA_PNP_ID (0x303) || IsaIo->ResourceList->Device.UID != 0) { Status = EFI_UNSUPPORTED; } gBS->CloseProtocol ( HandleBuffer[Index], &gEfiIsaIoProtocolGuid, NULL, HandleBuffer[Index] ); } if (!EFI_ERROR (Status)) { gBS->ConnectController (HandleBuffer[Index], NULL, NULL, FALSE); } } FreePool (HandleBuffer); return EFI_SUCCESS; } /** Load a legacy PC-AT OpROM for VGA controller. @param Private Driver private data. @retval EFI_SUCCESS Legacy ROM successfully installed for this device. @retval EFI_DEVICE_ERROR No VGA device handle found, or native EFI video driver cannot be successfully disconnected, or VGA thunk driver cannot be successfully connected. **/ EFI_STATUS LegacyBiosInstallVgaRom ( IN LEGACY_BIOS_INSTANCE *Private ) { EFI_STATUS Status; EFI_HANDLE VgaHandle; UINTN HandleCount; EFI_HANDLE *HandleBuffer; EFI_HANDLE *ConnectHandleBuffer; EFI_PCI_IO_PROTOCOL *PciIo; PCI_TYPE00 PciConfigHeader; UINT64 Supports; EFI_OPEN_PROTOCOL_INFORMATION_ENTRY *OpenInfoBuffer; UINTN EntryCount; UINTN Index; VOID *Interface; // // EfiLegacyBiosGuild attached to a device implies that there is a legacy // BIOS associated with that device. // // There are 3 cases to consider. // Case 1: No EFI driver is controlling the video. // Action: Return EFI_SUCCESS from DisconnectController, search // video thunk driver, and connect it. // Case 2: EFI driver is controlling the video and EfiLegacyBiosGuid is // not on the image handle. // Action: Disconnect EFI driver. // ConnectController for video thunk // Case 3: EFI driver is controlling the video and EfiLegacyBiosGuid is // on the image handle. // Action: Do nothing and set Private->VgaInstalled = TRUE. // Then this routine is not called any more. // // // Get the VGA device. // Status = Private->LegacyBiosPlatform->GetPlatformHandle ( Private->LegacyBiosPlatform, EfiGetPlatformVgaHandle, 0, &HandleBuffer, &HandleCount, NULL ); if (EFI_ERROR (Status)) { return EFI_DEVICE_ERROR; } VgaHandle = HandleBuffer[0]; // // Check whether video thunk driver already starts. // Status = gBS->OpenProtocolInformation ( VgaHandle, &gEfiPciIoProtocolGuid, &OpenInfoBuffer, &EntryCount ); if (EFI_ERROR (Status)) { return Status; } for (Index = 0; Index < EntryCount; Index++) { if ((OpenInfoBuffer[Index].Attributes & EFI_OPEN_PROTOCOL_BY_DRIVER) != 0) { Status = gBS->HandleProtocol ( OpenInfoBuffer[Index].AgentHandle, &gEfiLegacyBiosGuid, (VOID **) &Interface ); if (!EFI_ERROR (Status)) { // // This should be video thunk driver which is managing video device // So it need not start again // DEBUG ((EFI_D_INFO, "Video thunk driver already start! Return!\n")); Private->VgaInstalled = TRUE; return EFI_SUCCESS; } } } // // Kick off the native EFI driver // Status = gBS->DisconnectController ( VgaHandle, NULL, NULL ); if (EFI_ERROR (Status)) { if (Status != EFI_NOT_FOUND) { return EFI_DEVICE_ERROR; } else { return Status; } } // // Find all the Thunk Driver // HandleBuffer = NULL; Status = gBS->LocateHandleBuffer ( ByProtocol, &gEfiLegacyBiosGuid, NULL, &HandleCount, &HandleBuffer ); ASSERT_EFI_ERROR (Status); ConnectHandleBuffer = (EFI_HANDLE *) AllocatePool (sizeof (EFI_HANDLE) * (HandleCount + 1)); ASSERT (ConnectHandleBuffer != NULL); CopyMem ( ConnectHandleBuffer, HandleBuffer, sizeof (EFI_HANDLE) * HandleCount ); ConnectHandleBuffer[HandleCount] = NULL; FreePool (HandleBuffer); // // Enable the device and make sure VGA cycles are being forwarded to this VGA device // Status = gBS->HandleProtocol ( VgaHandle, &gEfiPciIoProtocolGuid, (VOID **) &PciIo ); ASSERT_EFI_ERROR (Status); PciIo->Pci.Read ( PciIo, EfiPciIoWidthUint32, 0, sizeof (PciConfigHeader) / sizeof (UINT32), &PciConfigHeader ); Status = PciIo->Attributes ( PciIo, EfiPciIoAttributeOperationSupported, 0, &Supports ); if (!EFI_ERROR (Status)) { Supports &= (UINT64)(EFI_PCI_DEVICE_ENABLE | EFI_PCI_IO_ATTRIBUTE_VGA_MEMORY | \ EFI_PCI_IO_ATTRIBUTE_VGA_IO | EFI_PCI_IO_ATTRIBUTE_VGA_IO_16); Status = PciIo->Attributes ( PciIo, EfiPciIoAttributeOperationEnable, Supports, NULL ); } if (Status == EFI_SUCCESS) { Private->VgaInstalled = TRUE; // // Attach the VGA thunk driver. // Assume the video is installed. This prevents potential of infinite recursion. // Status = gBS->ConnectController ( VgaHandle, ConnectHandleBuffer, NULL, TRUE ); } FreePool (ConnectHandleBuffer); if (EFI_ERROR (Status)) { Private->VgaInstalled = FALSE; // // Reconnect the EFI VGA driver. // gBS->ConnectController (VgaHandle, NULL, NULL, TRUE); return EFI_DEVICE_ERROR; } return EFI_SUCCESS; } /** Load a legacy PC-AT OpROM. @param This Protocol instance pointer. @param Private Driver's private data. @param PciHandle The EFI handle for the PCI device. It could be NULL if the OpROM image is not associated with any device. @param OpromRevision The revision of PCI PC-AT ROM image. @param RomImage Pointer to PCI PC-AT ROM image header. It must not be NULL. @param ImageSize Size of the PCI PC-AT ROM image. @param RuntimeImageLength On input is the max runtime image length indicated by the PCIR structure On output is the actual runtime image length @param DiskStart Disk number of first device hooked by the ROM. If DiskStart is the same as DiskEnd no disked were hooked. @param DiskEnd Disk number of the last device hooked by the ROM. @param RomShadowAddress Shadow address of PC-AT ROM @retval EFI_SUCCESS Legacy ROM loaded for this device @retval EFI_OUT_OF_RESOURCES No more space for this ROM **/ EFI_STATUS EFIAPI LegacyBiosInstallRom ( IN EFI_LEGACY_BIOS_PROTOCOL *This, IN LEGACY_BIOS_INSTANCE *Private, IN EFI_HANDLE PciHandle, IN UINT8 OpromRevision, IN VOID *RomImage, IN UINTN ImageSize, IN OUT UINTN *RuntimeImageLength, OUT UINT8 *DiskStart, OPTIONAL OUT UINT8 *DiskEnd, OPTIONAL OUT VOID **RomShadowAddress OPTIONAL ) { EFI_STATUS Status; EFI_STATUS PciEnableStatus; EFI_PCI_IO_PROTOCOL *PciIo; UINT8 LocalDiskStart; UINT8 LocalDiskEnd; UINTN Segment; UINTN Bus; UINTN Device; UINTN Function; EFI_IA32_REGISTER_SET Regs; UINT8 VideoMode; UINT8 OldVideoMode; EFI_TIME BootTime; UINT32 *BdaPtr; UINT32 LocalTime; UINT32 StartBbsIndex; UINT32 EndBbsIndex; UINT32 MaxRomAddr; UINTN TempData; UINTN InitAddress; UINTN RuntimeAddress; EFI_PHYSICAL_ADDRESS PhysicalAddress; UINT32 Granularity; PciIo = NULL; LocalDiskStart = 0; LocalDiskEnd = 0; Segment = 0; Bus = 0; Device = 0; Function = 0; VideoMode = 0; OldVideoMode = 0; PhysicalAddress = 0; MaxRomAddr = PcdGet32 (PcdEndOpromShadowAddress); if ((Private->Legacy16Table->TableLength >= OFFSET_OF(EFI_COMPATIBILITY16_TABLE, HiPermanentMemoryAddress)) && (Private->Legacy16Table->UmaAddress != 0) && (Private->Legacy16Table->UmaSize != 0) && (MaxRomAddr > (Private->Legacy16Table->UmaAddress))) { MaxRomAddr = Private->Legacy16Table->UmaAddress; } PciProgramAllInterruptLineRegisters (Private); if ((OpromRevision >= 3) && (Private->Csm16PciInterfaceVersion >= 0x0300)) { // // CSM16 3.0 meets PCI 3.0 OpROM // first test if there is enough space for its INIT code // PhysicalAddress = CONVENTIONAL_MEMORY_TOP; Status = gBS->AllocatePages ( AllocateMaxAddress, EfiBootServicesCode, EFI_SIZE_TO_PAGES (ImageSize), &PhysicalAddress ); if (EFI_ERROR (Status)) { DEBUG ((EFI_D_ERROR, "return LegacyBiosInstallRom(%d): EFI_OUT_OF_RESOURCES (no more space for OpROM)\n", __LINE__)); // // Report Status Code to indicate that there is no enough space for OpROM // REPORT_STATUS_CODE ( EFI_ERROR_CODE | EFI_ERROR_MINOR, (EFI_SOFTWARE_DXE_BS_DRIVER | EFI_SW_DXE_BS_EC_LEGACY_OPROM_NO_SPACE) ); return EFI_OUT_OF_RESOURCES; } InitAddress = (UINTN) PhysicalAddress; // // then test if there is enough space for its RT code // RuntimeAddress = Private->OptionRom; if (RuntimeAddress + *RuntimeImageLength > MaxRomAddr) { DEBUG ((EFI_D_ERROR, "return LegacyBiosInstallRom(%d): EFI_OUT_OF_RESOURCES (no more space for OpROM)\n", __LINE__)); gBS->FreePages (PhysicalAddress, EFI_SIZE_TO_PAGES (ImageSize)); // // Report Status Code to indicate that there is no enough space for OpROM // REPORT_STATUS_CODE ( EFI_ERROR_CODE | EFI_ERROR_MINOR, (EFI_SOFTWARE_DXE_BS_DRIVER | EFI_SW_DXE_BS_EC_LEGACY_OPROM_NO_SPACE) ); return EFI_OUT_OF_RESOURCES; } } else { // CSM16 3.0 meets PCI 2.x OpROM // CSM16 2.x meets PCI 2.x/3.0 OpROM // test if there is enough space for its INIT code // InitAddress = PCI_START_ADDRESS (Private->OptionRom); if (InitAddress + ImageSize > MaxRomAddr) { DEBUG ((EFI_D_ERROR, "return LegacyBiosInstallRom(%d): EFI_OUT_OF_RESOURCES (no more space for OpROM)\n", __LINE__)); // // Report Status Code to indicate that there is no enough space for OpROM // REPORT_STATUS_CODE ( EFI_ERROR_CODE | EFI_ERROR_MINOR, (EFI_SOFTWARE_DXE_BS_DRIVER | EFI_SW_DXE_BS_EC_LEGACY_OPROM_NO_SPACE) ); return EFI_OUT_OF_RESOURCES; } RuntimeAddress = InitAddress; } Private->LegacyRegion->UnLock ( Private->LegacyRegion, 0xE0000, 0x20000, &Granularity ); Private->LegacyRegion->UnLock ( Private->LegacyRegion, (UINT32) RuntimeAddress, (UINT32) ImageSize, &Granularity ); DEBUG ((EFI_D_INFO, " Shadowing OpROM init/runtime/isize = %x/%x/%x\n", InitAddress, RuntimeAddress, ImageSize)); CopyMem ((VOID *) InitAddress, RomImage, ImageSize); // // Read the highest disk number "installed: and assume a new disk will // show up on the first drive past the current value. // There are several considerations here: // 1. Non-BBS compliant drives will change 40:75 but 16-bit CSM will undo // the change until boot selection time frame. // 2. BBS compliants drives will not change 40:75 until boot time. // 3. Onboard IDE controllers will change 40:75 // ACCESS_PAGE0_CODE ( LocalDiskStart = (UINT8) ((*(UINT8 *) ((UINTN) 0x475)) + 0x80); if ((Private->Disk4075 + 0x80) < LocalDiskStart) { // // Update table since onboard IDE drives found // Private->LegacyEfiHddTable[Private->LegacyEfiHddTableIndex].PciSegment = 0xff; Private->LegacyEfiHddTable[Private->LegacyEfiHddTableIndex].PciBus = 0xff; Private->LegacyEfiHddTable[Private->LegacyEfiHddTableIndex].PciDevice = 0xff; Private->LegacyEfiHddTable[Private->LegacyEfiHddTableIndex].PciFunction = 0xff; Private->LegacyEfiHddTable[Private->LegacyEfiHddTableIndex].StartDriveNumber = (UINT8) (Private->Disk4075 + 0x80); Private->LegacyEfiHddTable[Private->LegacyEfiHddTableIndex].EndDriveNumber = LocalDiskStart; Private->LegacyEfiHddTableIndex ++; Private->Disk4075 = (UINT8) (LocalDiskStart & 0x7f); Private->DiskEnd = LocalDiskStart; } if (PciHandle != mVgaHandle) { EnablePs2Keyboard (); // // Store current mode settings since PrepareToScanRom may change mode. // VideoMode = *(UINT8 *) ((UINTN) (0x400 + BDA_VIDEO_MODE)); } ); // // Notify the platform that we are about to scan the ROM // Status = Private->LegacyBiosPlatform->PlatformHooks ( Private->LegacyBiosPlatform, EfiPlatformHookPrepareToScanRom, 0, PciHandle, &InitAddress, NULL, NULL ); // // If Status returned is EFI_UNSUPPORTED then abort due to platform // policy. // if (Status == EFI_UNSUPPORTED) { goto Done; } // // Report corresponding status code // REPORT_STATUS_CODE ( EFI_PROGRESS_CODE, (EFI_SOFTWARE_DXE_BS_DRIVER | EFI_SW_CSM_LEGACY_ROM_INIT) ); // // Generate number of ticks since midnight for BDA. Some OPROMs require // this. Place result in 40:6C-6F // gRT->GetTime (&BootTime, NULL); LocalTime = BootTime.Hour * 3600 + BootTime.Minute * 60 + BootTime.Second; // // Multiply result by 18.2 for number of ticks since midnight. // Use 182/10 to avoid floating point math. // ACCESS_PAGE0_CODE ( LocalTime = (LocalTime * 182) / 10; BdaPtr = (UINT32 *) ((UINTN) 0x46C); *BdaPtr = LocalTime; ); // // Pass in handoff data // PciEnableStatus = EFI_UNSUPPORTED; ZeroMem (&Regs, sizeof (Regs)); if (PciHandle != NULL) { Status = gBS->HandleProtocol ( PciHandle, &gEfiPciIoProtocolGuid, (VOID **) &PciIo ); ASSERT_EFI_ERROR (Status); // // Enable command register. // PciEnableStatus = PciIo->Attributes ( PciIo, EfiPciIoAttributeOperationEnable, EFI_PCI_DEVICE_ENABLE, NULL ); PciIo->GetLocation ( PciIo, &Segment, &Bus, &Device, &Function ); DEBUG ((EFI_D_INFO, "Shadowing OpROM on the PCI device %x/%x/%x\n", Bus, Device, Function)); } mIgnoreBbsUpdateFlag = FALSE; Regs.X.AX = Legacy16DispatchOprom; // // Generate DispatchOpRomTable data // Private->IntThunk->DispatchOpromTable.PnPInstallationCheckSegment = Private->Legacy16Table->PnPInstallationCheckSegment; Private->IntThunk->DispatchOpromTable.PnPInstallationCheckOffset = Private->Legacy16Table->PnPInstallationCheckOffset; Private->IntThunk->DispatchOpromTable.OpromSegment = (UINT16) (InitAddress >> 4); Private->IntThunk->DispatchOpromTable.PciBus = (UINT8) Bus; Private->IntThunk->DispatchOpromTable.PciDeviceFunction = (UINT8) ((Device << 3) | Function); Private->IntThunk->DispatchOpromTable.NumberBbsEntries = (UINT8) Private->IntThunk->EfiToLegacy16BootTable.NumberBbsEntries; Private->IntThunk->DispatchOpromTable.BbsTablePointer = (UINT32) (UINTN) Private->BbsTablePtr; Private->IntThunk->DispatchOpromTable.RuntimeSegment = (UINT16)((OpromRevision < 3) ? 0xffff : (RuntimeAddress >> 4)); TempData = (UINTN) &Private->IntThunk->DispatchOpromTable; Regs.X.ES = EFI_SEGMENT ((UINT32) TempData); Regs.X.BX = EFI_OFFSET ((UINT32) TempData); // // Skip dispatching ROM for those PCI devices that can not be enabled by PciIo->Attributes // Otherwise, it may cause the system to hang in some cases // if (!EFI_ERROR (PciEnableStatus)) { DEBUG ((EFI_D_INFO, " Legacy16DispatchOprom - %02x/%02x/%02x\n", Bus, Device, Function)); Private->LegacyBios.FarCall86 ( &Private->LegacyBios, Private->Legacy16CallSegment, Private->Legacy16CallOffset, &Regs, NULL, 0 ); } else { Regs.X.BX = 0; } if (Private->IntThunk->DispatchOpromTable.NumberBbsEntries != (UINT8) Private->IntThunk->EfiToLegacy16BootTable.NumberBbsEntries) { Private->IntThunk->EfiToLegacy16BootTable.NumberBbsEntries = (UINT8) Private->IntThunk->DispatchOpromTable.NumberBbsEntries; mIgnoreBbsUpdateFlag = TRUE; } // // Check if non-BBS compliant drives found // if (Regs.X.BX != 0) { LocalDiskEnd = (UINT8) (LocalDiskStart + Regs.H.BL); Private->LegacyEfiHddTable[Private->LegacyEfiHddTableIndex].PciSegment = (UINT8) Segment; Private->LegacyEfiHddTable[Private->LegacyEfiHddTableIndex].PciBus = (UINT8) Bus; Private->LegacyEfiHddTable[Private->LegacyEfiHddTableIndex].PciDevice = (UINT8) Device; Private->LegacyEfiHddTable[Private->LegacyEfiHddTableIndex].PciFunction = (UINT8) Function; Private->LegacyEfiHddTable[Private->LegacyEfiHddTableIndex].StartDriveNumber = Private->DiskEnd; Private->DiskEnd = LocalDiskEnd; Private->LegacyEfiHddTable[Private->LegacyEfiHddTableIndex].EndDriveNumber = Private->DiskEnd; Private->LegacyEfiHddTableIndex += 1; } // // Skip video mode set, if installing VGA // if (PciHandle != mVgaHandle) { // // Set mode settings since PrepareToScanRom may change mode // ACCESS_PAGE0_CODE ({ OldVideoMode = *(UINT8 *) ((UINTN) (0x400 + BDA_VIDEO_MODE)); }); if (VideoMode != OldVideoMode) { // // The active video mode is changed, restore it to original mode. // Regs.H.AH = 0x00; Regs.H.AL = VideoMode; Private->LegacyBios.Int86 (&Private->LegacyBios, 0x10, &Regs); } } // // Regs.X.AX from the adapter initializion is ignored since some adapters // do not follow the standard of setting AX = 0 on success. // // // The ROM could have updated it's size so we need to read again. // if (((EFI_LEGACY_EXPANSION_ROM_HEADER *) RuntimeAddress)->Signature != PCI_EXPANSION_ROM_HEADER_SIGNATURE) { // // Now we check the signature (0xaa55) to judge whether the run-time code is truly generated by INIT function. // If signature is not valid, that means the INIT function didn't copy the run-time code to RuntimeAddress. // *RuntimeImageLength = 0; } else { *RuntimeImageLength = ((EFI_LEGACY_EXPANSION_ROM_HEADER *) RuntimeAddress)->Size512 * 512; } DEBUG ((EFI_D_INFO, " fsize = %x\n", *RuntimeImageLength)); // // If OpROM runs in 2.0 mode // if (PhysicalAddress == 0) { if (*RuntimeImageLength < ImageSize) { // // Make area from end of shadowed rom to end of original rom all ffs // gBS->SetMem ((VOID *) (InitAddress + *RuntimeImageLength), ImageSize - *RuntimeImageLength, 0xff); } } ACCESS_PAGE0_CODE ( LocalDiskEnd = (UINT8) ((*(UINT8 *) ((UINTN) 0x475)) + 0x80); ); // // Allow platform to perform any required actions after the // OPROM has been initialized. // Status = Private->LegacyBiosPlatform->PlatformHooks ( Private->LegacyBiosPlatform, EfiPlatformHookAfterRomInit, 0, PciHandle, &RuntimeAddress, NULL, NULL ); if (PciHandle != NULL) { // // If no PCI Handle then no header or Bevs. // if ((*RuntimeImageLength != 0) && (!mIgnoreBbsUpdateFlag)) { StartBbsIndex = Private->IntThunk->EfiToLegacy16BootTable.NumberBbsEntries; TempData = RuntimeAddress; UpdateBevBcvTable ( Private, (EFI_LEGACY_EXPANSION_ROM_HEADER *) TempData, PciIo ); EndBbsIndex = Private->IntThunk->EfiToLegacy16BootTable.NumberBbsEntries; LocalDiskEnd = (UINT8) (LocalDiskStart + (UINT8) (EndBbsIndex - StartBbsIndex)); if (LocalDiskEnd != LocalDiskStart) { Private->LegacyEfiHddTable[Private->LegacyEfiHddTableIndex].PciSegment = (UINT8) Segment; Private->LegacyEfiHddTable[Private->LegacyEfiHddTableIndex].PciBus = (UINT8) Bus; Private->LegacyEfiHddTable[Private->LegacyEfiHddTableIndex].PciDevice = (UINT8) Device; Private->LegacyEfiHddTable[Private->LegacyEfiHddTableIndex].PciFunction = (UINT8) Function; Private->LegacyEfiHddTable[Private->LegacyEfiHddTableIndex].StartDriveNumber = Private->DiskEnd; Private->DiskEnd = LocalDiskEnd; Private->LegacyEfiHddTable[Private->LegacyEfiHddTableIndex].EndDriveNumber = Private->DiskEnd; Private->LegacyEfiHddTableIndex += 1; } } // // Mark PCI device as having a legacy BIOS ROM loaded. // RomShadow ( PciHandle, (UINT32) RuntimeAddress, (UINT32) *RuntimeImageLength, LocalDiskStart, LocalDiskEnd ); } // // Stuff caller's OPTIONAL return parameters. // if (RomShadowAddress != NULL) { *RomShadowAddress = (VOID *) RuntimeAddress; } if (DiskStart != NULL) { *DiskStart = LocalDiskStart; } if (DiskEnd != NULL) { *DiskEnd = LocalDiskEnd; } Private->OptionRom = (UINT32) (RuntimeAddress + *RuntimeImageLength); Status = EFI_SUCCESS; Done: if (PhysicalAddress != 0) { // // Free pages when OpROM is 3.0 // gBS->FreePages (PhysicalAddress, EFI_SIZE_TO_PAGES (ImageSize)); } // // Insure all shadowed areas are locked // Private->LegacyRegion->Lock ( Private->LegacyRegion, 0xC0000, 0x40000, &Granularity ); return Status; } /** Let IOMMU grant DMA access for the PCI device. @param PciHandle The EFI handle for the PCI device. @param HostAddress The system memory address to map to the PCI controller. @param NumberOfBytes The number of bytes to map. @retval EFI_SUCCESS The DMA access is granted. **/ EFI_STATUS IoMmuGrantAccess ( IN EFI_HANDLE PciHandle, IN EFI_PHYSICAL_ADDRESS HostAddress, IN UINTN NumberOfBytes ) { EFI_PHYSICAL_ADDRESS DeviceAddress; VOID *Mapping; EFI_STATUS Status; if (PciHandle == NULL) { return EFI_UNSUPPORTED; } Status = EFI_SUCCESS; if (mIoMmu == NULL) { gBS->LocateProtocol (&gEdkiiIoMmuProtocolGuid, NULL, (VOID **)&mIoMmu); } if (mIoMmu != NULL) { Status = mIoMmu->Map ( mIoMmu, EdkiiIoMmuOperationBusMasterCommonBuffer, (VOID *)(UINTN)HostAddress, &NumberOfBytes, &DeviceAddress, &Mapping ); if (EFI_ERROR(Status)) { DEBUG ((DEBUG_ERROR, "LegacyPci - IoMmuMap - %r\n", Status)); } else { ASSERT (DeviceAddress == HostAddress); Status = mIoMmu->SetAttribute ( mIoMmu, PciHandle, Mapping, EDKII_IOMMU_ACCESS_READ | EDKII_IOMMU_ACCESS_WRITE ); if (EFI_ERROR(Status)) { DEBUG ((DEBUG_ERROR, "LegacyPci - IoMmuSetAttribute - %r\n", Status)); } } } return Status; } /** Load a legacy PC-AT OPROM on the PciHandle device. Return information about how many disks were added by the OPROM and the shadow address and size. DiskStart & DiskEnd are INT 13h drive letters. Thus 0x80 is C: @param This Protocol instance pointer. @param PciHandle The PCI PC-AT OPROM from this devices ROM BAR will be loaded. This value is NULL if RomImage is non-NULL. This is the normal case. @param RomImage A PCI PC-AT ROM image. This argument is non-NULL if there is no hardware associated with the ROM and thus no PciHandle, otherwise is must be NULL. Example is PXE base code. @param Flags Indicates if ROM found and if PC-AT. @param DiskStart Disk number of first device hooked by the ROM. If DiskStart is the same as DiskEnd no disked were hooked. @param DiskEnd Disk number of the last device hooked by the ROM. @param RomShadowAddress Shadow address of PC-AT ROM @param RomShadowedSize Size of RomShadowAddress in bytes @retval EFI_SUCCESS Legacy ROM loaded for this device @retval EFI_INVALID_PARAMETER PciHandle not found @retval EFI_UNSUPPORTED There is no PCI ROM in the ROM BAR or no onboard ROM **/ EFI_STATUS EFIAPI LegacyBiosInstallPciRom ( IN EFI_LEGACY_BIOS_PROTOCOL * This, IN EFI_HANDLE PciHandle, IN VOID **RomImage, OUT UINTN *Flags, OUT UINT8 *DiskStart, OPTIONAL OUT UINT8 *DiskEnd, OPTIONAL OUT VOID **RomShadowAddress, OPTIONAL OUT UINT32 *RomShadowedSize OPTIONAL ) { EFI_STATUS Status; LEGACY_BIOS_INSTANCE *Private; VOID *LocalRomImage; UINTN ImageSize; UINTN RuntimeImageLength; EFI_PCI_IO_PROTOCOL *PciIo; PCI_TYPE01 PciConfigHeader; UINTN HandleCount; EFI_HANDLE *HandleBuffer; UINTN PciSegment; UINTN PciBus; UINTN PciDevice; UINTN PciFunction; UINTN LastBus; UINTN Index; UINT8 OpromRevision; UINT32 Granularity; PCI_3_0_DATA_STRUCTURE *Pcir; OpromRevision = 0; Private = LEGACY_BIOS_INSTANCE_FROM_THIS (This); if (Private->Legacy16Table->LastPciBus == 0) { // // Get last bus number if not already found // Status = gBS->LocateHandleBuffer ( ByProtocol, &gEfiPciIoProtocolGuid, NULL, &HandleCount, &HandleBuffer ); LastBus = 0; for (Index = 0; Index < HandleCount; Index++) { Status = gBS->HandleProtocol ( HandleBuffer[Index], &gEfiPciIoProtocolGuid, (VOID **) &PciIo ); if (EFI_ERROR (Status)) { continue; } Status = PciIo->GetLocation ( PciIo, &PciSegment, &PciBus, &PciDevice, &PciFunction ); if (PciBus > LastBus) { LastBus = PciBus; } } Private->LegacyRegion->UnLock ( Private->LegacyRegion, 0xE0000, 0x20000, &Granularity ); Private->Legacy16Table->LastPciBus = (UINT8) LastBus; Private->LegacyRegion->Lock ( Private->LegacyRegion, 0xE0000, 0x20000, &Granularity ); } *Flags = 0; if ((PciHandle != NULL) && (RomImage == NULL)) { // // If PciHandle has OpRom to Execute // and OpRom are all associated with Hardware // Status = gBS->HandleProtocol ( PciHandle, &gEfiPciIoProtocolGuid, (VOID **) &PciIo ); if (!EFI_ERROR (Status)) { PciIo->Pci.Read ( PciIo, EfiPciIoWidthUint32, 0, sizeof (PciConfigHeader) / sizeof (UINT32), &PciConfigHeader ); // // if video installed & OPROM is video return // if ( ( ((PciConfigHeader.Hdr.ClassCode[2] == PCI_CLASS_OLD) && (PciConfigHeader.Hdr.ClassCode[1] == PCI_CLASS_OLD_VGA)) || ((PciConfigHeader.Hdr.ClassCode[2] == PCI_CLASS_DISPLAY) && (PciConfigHeader.Hdr.ClassCode[1] == PCI_CLASS_DISPLAY_VGA)) ) && (!Private->VgaInstalled) ) { mVgaInstallationInProgress = TRUE; // // return EFI_UNSUPPORTED; // } } // // To run any legacy image, the VGA needs to be installed first. // if installing the video, then don't need the thunk as already installed. // Status = Private->LegacyBiosPlatform->GetPlatformHandle ( Private->LegacyBiosPlatform, EfiGetPlatformVgaHandle, 0, &HandleBuffer, &HandleCount, NULL ); if (!EFI_ERROR (Status)) { mVgaHandle = HandleBuffer[0]; if ((!Private->VgaInstalled) && (PciHandle != mVgaHandle)) { // // A return status of EFI_NOT_FOUND is considered valid (No EFI // driver is controlling video. // mVgaInstallationInProgress = TRUE; Status = LegacyBiosInstallVgaRom (Private); if (EFI_ERROR (Status)) { if (Status != EFI_NOT_FOUND) { mVgaInstallationInProgress = FALSE; return Status; } } else { mVgaInstallationInProgress = FALSE; } } } // // See if the option ROM for PciHandle has already been executed // Status = IsLegacyRom (PciHandle); if (!EFI_ERROR (Status)) { mVgaInstallationInProgress = FALSE; GetShadowedRomParameters ( PciHandle, DiskStart, DiskEnd, RomShadowAddress, (UINTN *) RomShadowedSize ); return EFI_SUCCESS; } Status = LegacyBiosCheckPciRomEx ( &Private->LegacyBios, PciHandle, &LocalRomImage, &ImageSize, &RuntimeImageLength, Flags, &OpromRevision, NULL ); if (EFI_ERROR (Status)) { // // There is no PCI ROM in the ROM BAR or no onboard ROM // mVgaInstallationInProgress = FALSE; return EFI_UNSUPPORTED; } } else { if ((RomImage == NULL) || (*RomImage == NULL)) { // // If PciHandle is NULL, and no OpRom is to be associated // mVgaInstallationInProgress = FALSE; return EFI_UNSUPPORTED; } Status = Private->LegacyBiosPlatform->GetPlatformHandle ( Private->LegacyBiosPlatform, EfiGetPlatformVgaHandle, 0, &HandleBuffer, &HandleCount, NULL ); if ((!EFI_ERROR (Status)) && (!Private->VgaInstalled)) { // // A return status of EFI_NOT_FOUND is considered valid (No EFI // driver is controlling video. // mVgaInstallationInProgress = TRUE; Status = LegacyBiosInstallVgaRom (Private); if (EFI_ERROR (Status)) { if (Status != EFI_NOT_FOUND) { mVgaInstallationInProgress = FALSE; return Status; } } else { mVgaInstallationInProgress = FALSE; } } LocalRomImage = *RomImage; if (((PCI_EXPANSION_ROM_HEADER *) LocalRomImage)->Signature != PCI_EXPANSION_ROM_HEADER_SIGNATURE || ((PCI_EXPANSION_ROM_HEADER *) LocalRomImage)->PcirOffset == 0 || (((PCI_EXPANSION_ROM_HEADER *) LocalRomImage)->PcirOffset & 3 ) != 0) { mVgaInstallationInProgress = FALSE; return EFI_UNSUPPORTED; } Pcir = (PCI_3_0_DATA_STRUCTURE *) ((UINT8 *) LocalRomImage + ((PCI_EXPANSION_ROM_HEADER *) LocalRomImage)->PcirOffset); if ((Pcir->Signature != PCI_DATA_STRUCTURE_SIGNATURE) || (Pcir->CodeType != PCI_CODE_TYPE_PCAT_IMAGE)) { mVgaInstallationInProgress = FALSE; return EFI_UNSUPPORTED; } ImageSize = Pcir->ImageLength * 512; if (Pcir->Length >= 0x1C) { OpromRevision = Pcir->Revision; } else { OpromRevision = 0; } if (Pcir->Revision < 3) { RuntimeImageLength = 0; } else { RuntimeImageLength = Pcir->MaxRuntimeImageLength * 512; } } // // Grant access for below 1M // BDA/EBDA/LowPMM and scratch memory for OPROM. // IoMmuGrantAccess (PciHandle, 0, SIZE_1MB); // // Grant access for HiPmm // IoMmuGrantAccess ( PciHandle, Private->IntThunk->EfiToLegacy16InitTable.HiPmmMemory, Private->IntThunk->EfiToLegacy16InitTable.HiPmmMemorySizeInBytes ); // // Shadow and initialize the OpROM. // ASSERT (Private->TraceIndex < 0x200); Private->Trace[Private->TraceIndex] = LEGACY_PCI_TRACE_000; Private->TraceIndex ++; Private->TraceIndex = (UINT16) (Private->TraceIndex % 0x200); Status = LegacyBiosInstallRom ( This, Private, PciHandle, OpromRevision, LocalRomImage, ImageSize, &RuntimeImageLength, DiskStart, DiskEnd, RomShadowAddress ); if (RomShadowedSize != NULL) { *RomShadowedSize = (UINT32) RuntimeImageLength; } mVgaInstallationInProgress = FALSE; return Status; }