summaryrefslogtreecommitdiffstats
path: root/OvmfPkg/Csm/LegacyBiosDxe/LegacyPci.c
diff options
context:
space:
mode:
Diffstat (limited to 'OvmfPkg/Csm/LegacyBiosDxe/LegacyPci.c')
-rw-r--r--OvmfPkg/Csm/LegacyBiosDxe/LegacyPci.c3083
1 files changed, 3083 insertions, 0 deletions
diff --git a/OvmfPkg/Csm/LegacyBiosDxe/LegacyPci.c b/OvmfPkg/Csm/LegacyBiosDxe/LegacyPci.c
new file mode 100644
index 0000000000..dc1f760876
--- /dev/null
+++ b/OvmfPkg/Csm/LegacyBiosDxe/LegacyPci.c
@@ -0,0 +1,3083 @@
+/** @file
+
+Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
+
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "LegacyBiosInterface.h"
+#include <IndustryStandard/Pci30.h>
+
+#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;
+}
+