/** @file PCI emumeration support functions implementation for PCI Bus module. Copyright (c) 2006 - 2021, Intel Corporation. All rights reserved.
(C) Copyright 2015 Hewlett Packard Enterprise Development LP
Copyright (C) 2023 Advanced Micro Devices, Inc. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include "PciBus.h" extern CHAR16 *mBarTypeStr[]; extern EDKII_DEVICE_SECURITY_PROTOCOL *mDeviceSecurityProtocol; #define OLD_ALIGN 0xFFFFFFFFFFFFFFFFULL #define EVEN_ALIGN 0xFFFFFFFFFFFFFFFEULL #define SQUAD_ALIGN 0xFFFFFFFFFFFFFFFDULL #define DQUAD_ALIGN 0xFFFFFFFFFFFFFFFCULL /** This routine is used to check whether the pci device is present. @param PciRootBridgeIo Pointer to instance of EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL. @param Pci Output buffer for PCI device configuration space. @param Bus PCI bus NO. @param Device PCI device NO. @param Func PCI Func NO. @retval EFI_NOT_FOUND PCI device not present. @retval EFI_SUCCESS PCI device is found. **/ EFI_STATUS PciDevicePresent ( IN EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *PciRootBridgeIo, OUT PCI_TYPE00 *Pci, IN UINT8 Bus, IN UINT8 Device, IN UINT8 Func ) { UINT64 Address; EFI_STATUS Status; // // Create PCI address map in terms of Bus, Device and Func // Address = EFI_PCI_ADDRESS (Bus, Device, Func, 0); // // Read the Vendor ID register // Status = PciRootBridgeIo->Pci.Read ( PciRootBridgeIo, EfiPciWidthUint32, Address, 1, Pci ); if (!EFI_ERROR (Status) && ((Pci->Hdr).VendorId != 0xffff)) { // // Read the entire config header for the device // Status = PciRootBridgeIo->Pci.Read ( PciRootBridgeIo, EfiPciWidthUint32, Address, sizeof (PCI_TYPE00) / sizeof (UINT32), Pci ); return EFI_SUCCESS; } return EFI_NOT_FOUND; } /** Collect all the resource information under this root bridge. A database that records all the information about pci device subject to this root bridge will then be created. @param Bridge Parent bridge instance. @param StartBusNumber Bus number of beginning. @retval EFI_SUCCESS PCI device is found. @retval other Some error occurred when reading PCI bridge information. **/ EFI_STATUS PciPciDeviceInfoCollector ( IN PCI_IO_DEVICE *Bridge, IN UINT8 StartBusNumber ) { EFI_STATUS Status; PCI_TYPE00 Pci; UINT8 Device; UINT8 Func; UINT8 SecBus; PCI_IO_DEVICE *PciIoDevice; EFI_PCI_IO_PROTOCOL *PciIo; Status = EFI_SUCCESS; SecBus = 0; for (Device = 0; Device <= PCI_MAX_DEVICE; Device++) { for (Func = 0; Func <= PCI_MAX_FUNC; Func++) { // // Check to see whether PCI device is present // Status = PciDevicePresent ( Bridge->PciRootBridgeIo, &Pci, (UINT8)StartBusNumber, (UINT8)Device, (UINT8)Func ); if (EFI_ERROR (Status) && (Func == 0)) { // // go to next device if there is no Function 0 // break; } if (!EFI_ERROR (Status)) { // // Call back to host bridge function // PreprocessController (Bridge, (UINT8)StartBusNumber, Device, Func, EfiPciBeforeResourceCollection); // // Collect all the information about the PCI device discovered // Status = PciSearchDevice ( Bridge, &Pci, (UINT8)StartBusNumber, Device, Func, &PciIoDevice ); // // Recursively scan PCI busses on the other side of PCI-PCI bridges // // if (!EFI_ERROR (Status) && (IS_PCI_BRIDGE (&Pci) || IS_CARDBUS_BRIDGE (&Pci))) { // // If it is PPB, we need to get the secondary bus to continue the enumeration // PciIo = &(PciIoDevice->PciIo); Status = PciIo->Pci.Read (PciIo, EfiPciIoWidthUint8, PCI_BRIDGE_SECONDARY_BUS_REGISTER_OFFSET, 1, &SecBus); if (EFI_ERROR (Status)) { return Status; } // // Ensure secondary bus number is greater than the primary bus number to avoid // any potential dead loop when PcdPciDisableBusEnumeration is set to TRUE // if (SecBus <= StartBusNumber) { break; } // // Get resource padding for PPB // GetResourcePaddingPpb (PciIoDevice); // // Deep enumerate the next level bus // Status = PciPciDeviceInfoCollector ( PciIoDevice, (UINT8)(SecBus) ); } if ((Func == 0) && !IS_PCI_MULTI_FUNC (&Pci)) { // // Skip sub functions, this is not a multi function device // Func = PCI_MAX_FUNC; } } } } return EFI_SUCCESS; } /** Search required device and create PCI device instance. @param Bridge Parent bridge instance. @param Pci Input PCI device information block. @param Bus PCI bus NO. @param Device PCI device NO. @param Func PCI func NO. @param PciDevice Output of searched PCI device instance. @retval EFI_SUCCESS Successfully created PCI device instance. @retval EFI_OUT_OF_RESOURCES Cannot get PCI device information. **/ EFI_STATUS PciSearchDevice ( IN PCI_IO_DEVICE *Bridge, IN PCI_TYPE00 *Pci, IN UINT8 Bus, IN UINT8 Device, IN UINT8 Func, OUT PCI_IO_DEVICE **PciDevice ) { PCI_IO_DEVICE *PciIoDevice; BOOLEAN IgnoreOptionRom; PciIoDevice = NULL; IgnoreOptionRom = FALSE; DEBUG (( DEBUG_INFO, "PciBus: Discovered %s @ [%02x|%02x|%02x] [VID = 0x%x, DID = 0x%0x]\n", IS_PCI_BRIDGE (Pci) ? L"PPB" : IS_CARDBUS_BRIDGE (Pci) ? L"P2C" : L"PCI", Bus, Device, Func, Pci->Hdr.VendorId, Pci->Hdr.DeviceId )); if (!IS_PCI_BRIDGE (Pci)) { if (IS_CARDBUS_BRIDGE (Pci)) { PciIoDevice = GatherP2CInfo ( Bridge, Pci, Bus, Device, Func ); if ((PciIoDevice != NULL) && gFullEnumeration) { InitializeP2C (PciIoDevice); } } else { // // Create private data for Pci Device // PciIoDevice = GatherDeviceInfo ( Bridge, Pci, Bus, Device, Func ); } } else { // // Create private data for PPB // PciIoDevice = GatherPpbInfo ( Bridge, Pci, Bus, Device, Func ); // // Special initialization for PPB including making the PPB quiet // if ((PciIoDevice != NULL) && gFullEnumeration) { InitializePpb (PciIoDevice); } } if (PciIoDevice == NULL) { return EFI_OUT_OF_RESOURCES; } // // Update the bar information for this PCI device so as to support some specific device // UpdatePciInfo (PciIoDevice, &IgnoreOptionRom); if (PciIoDevice->DevicePath == NULL) { return EFI_OUT_OF_RESOURCES; } // // Detect this function has option rom // if (gFullEnumeration) { if (!IS_CARDBUS_BRIDGE (Pci) && !IgnoreOptionRom) { GetOpRomInfo (PciIoDevice); } ResetPowerManagementFeature (PciIoDevice); } // // Insert it into a global tree for future reference // InsertPciDevice (Bridge, PciIoDevice); // // Determine PCI device attributes // if (PciDevice != NULL) { *PciDevice = PciIoDevice; } return EFI_SUCCESS; } /** Dump the PPB padding resource information. @param PciIoDevice PCI IO instance. @param ResourceType The desired resource type to dump. PciBarTypeUnknown means to dump all types of resources. **/ VOID DumpPpbPaddingResource ( IN PCI_IO_DEVICE *PciIoDevice, IN PCI_BAR_TYPE ResourceType ) { EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *Descriptor; PCI_BAR_TYPE Type; if (PciIoDevice->ResourcePaddingDescriptors == NULL) { return; } if ((ResourceType == PciBarTypeIo16) || (ResourceType == PciBarTypeIo32)) { ResourceType = PciBarTypeIo; } for (Descriptor = PciIoDevice->ResourcePaddingDescriptors; Descriptor->Desc != ACPI_END_TAG_DESCRIPTOR; Descriptor++) { Type = PciBarTypeUnknown; if ((Descriptor->Desc == ACPI_ADDRESS_SPACE_DESCRIPTOR) && (Descriptor->ResType == ACPI_ADDRESS_SPACE_TYPE_IO)) { Type = PciBarTypeIo; } else if ((Descriptor->Desc == ACPI_ADDRESS_SPACE_DESCRIPTOR) && (Descriptor->ResType == ACPI_ADDRESS_SPACE_TYPE_MEM)) { if (Descriptor->AddrSpaceGranularity == 32) { // // prefetchable // if (Descriptor->SpecificFlag == EFI_ACPI_MEMORY_RESOURCE_SPECIFIC_FLAG_CACHEABLE_PREFETCHABLE) { Type = PciBarTypePMem32; } // // Non-prefetchable // if (Descriptor->SpecificFlag == 0) { Type = PciBarTypeMem32; } } if (Descriptor->AddrSpaceGranularity == 64) { // // prefetchable // if (Descriptor->SpecificFlag == EFI_ACPI_MEMORY_RESOURCE_SPECIFIC_FLAG_CACHEABLE_PREFETCHABLE) { Type = PciBarTypePMem64; } // // Non-prefetchable // if (Descriptor->SpecificFlag == 0) { Type = PciBarTypeMem64; } } } if ((Type != PciBarTypeUnknown) && ((ResourceType == PciBarTypeUnknown) || (ResourceType == Type))) { DEBUG (( DEBUG_INFO, " Padding: Type = %s; Alignment = 0x%lx;\tLength = 0x%lx\n", mBarTypeStr[Type], Descriptor->AddrRangeMax, Descriptor->AddrLen )); } } } /** Dump the PCI BAR information. @param PciIoDevice PCI IO instance. **/ VOID DumpPciBars ( IN PCI_IO_DEVICE *PciIoDevice ) { UINTN Index; for (Index = 0; Index < PCI_MAX_BAR; Index++) { if (PciIoDevice->PciBar[Index].BarType == PciBarTypeUnknown) { continue; } DEBUG (( DEBUG_INFO, " BAR[%d]: Type = %s; Alignment = 0x%lx;\tLength = 0x%lx;\tOffset = 0x%02x\n", Index, mBarTypeStr[MIN (PciIoDevice->PciBar[Index].BarType, PciBarTypeMaxType)], PciIoDevice->PciBar[Index].Alignment, PciIoDevice->PciBar[Index].Length, PciIoDevice->PciBar[Index].Offset )); } for (Index = 0; Index < PCI_MAX_BAR; Index++) { if ((PciIoDevice->VfPciBar[Index].BarType == PciBarTypeUnknown) && (PciIoDevice->VfPciBar[Index].Length == 0)) { continue; } DEBUG (( DEBUG_INFO, " VFBAR[%d]: Type = %s; Alignment = 0x%lx;\tLength = 0x%lx;\tOffset = 0x%02x\n", Index, mBarTypeStr[MIN (PciIoDevice->VfPciBar[Index].BarType, PciBarTypeMaxType)], PciIoDevice->VfPciBar[Index].Alignment, PciIoDevice->VfPciBar[Index].Length, PciIoDevice->VfPciBar[Index].Offset )); } DEBUG ((DEBUG_INFO, "\n")); } /** Create PCI device instance for PCI device. @param Bridge Parent bridge instance. @param Pci Input PCI device information block. @param Bus PCI device Bus NO. @param Device PCI device Device NO. @param Func PCI device's func NO. @return Created PCI device instance. **/ PCI_IO_DEVICE * GatherDeviceInfo ( IN PCI_IO_DEVICE *Bridge, IN PCI_TYPE00 *Pci, IN UINT8 Bus, IN UINT8 Device, IN UINT8 Func ) { UINTN Offset; UINTN BarIndex; PCI_IO_DEVICE *PciIoDevice; PciIoDevice = CreatePciIoDevice ( Bridge, Pci, Bus, Device, Func ); if (PciIoDevice == NULL) { return NULL; } // // If it is a full enumeration, disconnect the device in advance // if (gFullEnumeration) { PCI_DISABLE_COMMAND_REGISTER (PciIoDevice, EFI_PCI_COMMAND_BITS_OWNED); } // // Start to parse the bars // for (Offset = 0x10, BarIndex = 0; Offset <= 0x24 && BarIndex < PCI_MAX_BAR; BarIndex++) { Offset = PciParseBar (PciIoDevice, Offset, BarIndex); } // // Parse the SR-IOV VF bars // if (PcdGetBool (PcdSrIovSupport) && (PciIoDevice->SrIovCapabilityOffset != 0)) { for (Offset = PciIoDevice->SrIovCapabilityOffset + EFI_PCIE_CAPABILITY_ID_SRIOV_BAR0, BarIndex = 0; Offset <= PciIoDevice->SrIovCapabilityOffset + EFI_PCIE_CAPABILITY_ID_SRIOV_BAR5; BarIndex++) { ASSERT (BarIndex < PCI_MAX_BAR); Offset = PciIovParseVfBar (PciIoDevice, Offset, BarIndex); } } DEBUG_CODE ( DumpPciBars (PciIoDevice); ); return PciIoDevice; } /** Create PCI device instance for PCI-PCI bridge. @param Bridge Parent bridge instance. @param Pci Input PCI device information block. @param Bus PCI device Bus NO. @param Device PCI device Device NO. @param Func PCI device's func NO. @return Created PCI device instance. **/ PCI_IO_DEVICE * GatherPpbInfo ( IN PCI_IO_DEVICE *Bridge, IN PCI_TYPE00 *Pci, IN UINT8 Bus, IN UINT8 Device, IN UINT8 Func ) { PCI_IO_DEVICE *PciIoDevice; EFI_STATUS Status; UINT8 Value; EFI_PCI_IO_PROTOCOL *PciIo; UINT8 Temp; UINT32 PMemBaseLimit; UINT16 PrefetchableMemoryBase; UINT16 PrefetchableMemoryLimit; PciIoDevice = CreatePciIoDevice ( Bridge, Pci, Bus, Device, Func ); if (PciIoDevice == NULL) { return NULL; } if (gFullEnumeration) { PCI_DISABLE_COMMAND_REGISTER (PciIoDevice, EFI_PCI_COMMAND_BITS_OWNED); // // Initialize the bridge control register // PCI_DISABLE_BRIDGE_CONTROL_REGISTER (PciIoDevice, EFI_PCI_BRIDGE_CONTROL_BITS_OWNED); } // // PPB can have two BARs // if (PciParseBar (PciIoDevice, 0x10, PPB_BAR_0) == 0x14) { // // Not 64-bit bar // PciParseBar (PciIoDevice, 0x14, PPB_BAR_1); } PciIo = &PciIoDevice->PciIo; // // Test whether it support 32 decode or not // PciIo->Pci.Read (PciIo, EfiPciIoWidthUint8, 0x1C, 1, &Temp); PciIo->Pci.Write (PciIo, EfiPciIoWidthUint8, 0x1C, 1, &gAllOne); PciIo->Pci.Read (PciIo, EfiPciIoWidthUint8, 0x1C, 1, &Value); PciIo->Pci.Write (PciIo, EfiPciIoWidthUint8, 0x1C, 1, &Temp); if (Value != 0) { if ((Value & 0x01) != 0) { PciIoDevice->Decodes |= EFI_BRIDGE_IO32_DECODE_SUPPORTED; } else { PciIoDevice->Decodes |= EFI_BRIDGE_IO16_DECODE_SUPPORTED; } } // // if PcdPciBridgeIoAlignmentProbe is TRUE, PCI bus driver probes // PCI bridge supporting non-standard I/O window alignment less than 4K. // PciIoDevice->BridgeIoAlignment = 0xFFF; if (FeaturePcdGet (PcdPciBridgeIoAlignmentProbe)) { // // Check any bits of bit 3-1 of I/O Base Register are writable. // if so, it is assumed non-standard I/O window alignment is supported by this bridge. // Per spec, bit 3-1 of I/O Base Register are reserved bits, so its content can't be assumed. // Value = (UINT8)(Temp ^ (BIT3 | BIT2 | BIT1)); PciIo->Pci.Write (PciIo, EfiPciIoWidthUint8, 0x1C, 1, &Value); PciIo->Pci.Read (PciIo, EfiPciIoWidthUint8, 0x1C, 1, &Value); PciIo->Pci.Write (PciIo, EfiPciIoWidthUint8, 0x1C, 1, &Temp); Value = (UINT8)((Value ^ Temp) & (BIT3 | BIT2 | BIT1)); switch (Value) { case BIT3: PciIoDevice->BridgeIoAlignment = 0x7FF; break; case BIT3 | BIT2: PciIoDevice->BridgeIoAlignment = 0x3FF; break; case BIT3 | BIT2 | BIT1: PciIoDevice->BridgeIoAlignment = 0x1FF; break; } } Status = BarExisted ( PciIoDevice, 0x24, NULL, &PMemBaseLimit ); // // Test if it supports 64 memory or not // // The bottom 4 bits of both the Prefetchable Memory Base and Prefetchable Memory Limit // registers: // 0 - the bridge supports only 32 bit addresses. // 1 - the bridge supports 64-bit addresses. // PrefetchableMemoryBase = (UINT16)(PMemBaseLimit & 0xffff); PrefetchableMemoryLimit = (UINT16)(PMemBaseLimit >> 16); if (!EFI_ERROR (Status) && ((PrefetchableMemoryBase & 0x000f) == 0x0001) && ((PrefetchableMemoryLimit & 0x000f) == 0x0001)) { Status = BarExisted ( PciIoDevice, 0x28, NULL, NULL ); if (!EFI_ERROR (Status)) { PciIoDevice->Decodes |= EFI_BRIDGE_PMEM32_DECODE_SUPPORTED; PciIoDevice->Decodes |= EFI_BRIDGE_PMEM64_DECODE_SUPPORTED; } else { PciIoDevice->Decodes |= EFI_BRIDGE_PMEM32_DECODE_SUPPORTED; } } // // Memory 32 code is required for ppb // PciIoDevice->Decodes |= EFI_BRIDGE_MEM32_DECODE_SUPPORTED; GetResourcePaddingPpb (PciIoDevice); DEBUG_CODE ( DumpPpbPaddingResource (PciIoDevice, PciBarTypeUnknown); DumpPciBars (PciIoDevice); ); return PciIoDevice; } /** Create PCI device instance for PCI Card bridge device. @param Bridge Parent bridge instance. @param Pci Input PCI device information block. @param Bus PCI device Bus NO. @param Device PCI device Device NO. @param Func PCI device's func NO. @return Created PCI device instance. **/ PCI_IO_DEVICE * GatherP2CInfo ( IN PCI_IO_DEVICE *Bridge, IN PCI_TYPE00 *Pci, IN UINT8 Bus, IN UINT8 Device, IN UINT8 Func ) { PCI_IO_DEVICE *PciIoDevice; PciIoDevice = CreatePciIoDevice ( Bridge, Pci, Bus, Device, Func ); if (PciIoDevice == NULL) { return NULL; } if (gFullEnumeration) { PCI_DISABLE_COMMAND_REGISTER (PciIoDevice, EFI_PCI_COMMAND_BITS_OWNED); // // Initialize the bridge control register // PCI_DISABLE_BRIDGE_CONTROL_REGISTER (PciIoDevice, EFI_PCCARD_BRIDGE_CONTROL_BITS_OWNED); } // // P2C only has one bar that is in 0x10 // PciParseBar (PciIoDevice, 0x10, P2C_BAR_0); // // Read PciBar information from the bar register // GetBackPcCardBar (PciIoDevice); PciIoDevice->Decodes = EFI_BRIDGE_MEM32_DECODE_SUPPORTED | EFI_BRIDGE_PMEM32_DECODE_SUPPORTED | EFI_BRIDGE_IO32_DECODE_SUPPORTED; DEBUG_CODE ( DumpPciBars (PciIoDevice); ); return PciIoDevice; } /** Create device path for pci device. @param ParentDevicePath Parent bridge's path. @param PciIoDevice Pci device instance. @return Device path protocol instance for specific pci device. **/ EFI_DEVICE_PATH_PROTOCOL * CreatePciDevicePath ( IN EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath, IN PCI_IO_DEVICE *PciIoDevice ) { PCI_DEVICE_PATH PciNode; // // Create PCI device path // PciNode.Header.Type = HARDWARE_DEVICE_PATH; PciNode.Header.SubType = HW_PCI_DP; SetDevicePathNodeLength (&PciNode.Header, sizeof (PciNode)); PciNode.Device = PciIoDevice->DeviceNumber; PciNode.Function = PciIoDevice->FunctionNumber; PciIoDevice->DevicePath = AppendDevicePathNode (ParentDevicePath, &PciNode.Header); return PciIoDevice->DevicePath; } /** Check whether the PCI IOV VF bar is existed or not. @param PciIoDevice A pointer to the PCI_IO_DEVICE. @param Offset The offset. @param BarLengthValue The bar length value returned. @param OriginalBarValue The original bar value returned. @retval EFI_NOT_FOUND The bar doesn't exist. @retval EFI_SUCCESS The bar exist. **/ EFI_STATUS VfBarExisted ( IN PCI_IO_DEVICE *PciIoDevice, IN UINTN Offset, OUT UINT32 *BarLengthValue, OUT UINT32 *OriginalBarValue ) { EFI_PCI_IO_PROTOCOL *PciIo; UINT32 OriginalValue; UINT32 Value; EFI_TPL OldTpl; // // Ensure it is called properly // ASSERT (PciIoDevice->SrIovCapabilityOffset != 0); if (PciIoDevice->SrIovCapabilityOffset == 0) { return EFI_NOT_FOUND; } PciIo = &PciIoDevice->PciIo; // // Preserve the original value // PciIo->Pci.Read (PciIo, EfiPciIoWidthUint32, (UINT32)Offset, 1, &OriginalValue); // // Raise TPL to high level to disable timer interrupt while the BAR is probed // OldTpl = gBS->RaiseTPL (TPL_HIGH_LEVEL); PciIo->Pci.Write (PciIo, EfiPciIoWidthUint32, (UINT32)Offset, 1, &gAllOne); PciIo->Pci.Read (PciIo, EfiPciIoWidthUint32, (UINT32)Offset, 1, &Value); // // Write back the original value // PciIo->Pci.Write (PciIo, EfiPciIoWidthUint32, (UINT32)Offset, 1, &OriginalValue); // // Restore TPL to its original level // gBS->RestoreTPL (OldTpl); if (BarLengthValue != NULL) { *BarLengthValue = Value; } if (OriginalBarValue != NULL) { *OriginalBarValue = OriginalValue; } if (Value == 0) { return EFI_NOT_FOUND; } else { return EFI_SUCCESS; } } /** Check whether the bar is existed or not. @param PciIoDevice A pointer to the PCI_IO_DEVICE. @param Offset The offset. @param BarLengthValue The bar length value returned. @param OriginalBarValue The original bar value returned. @retval EFI_NOT_FOUND The bar doesn't exist. @retval EFI_SUCCESS The bar exist. **/ EFI_STATUS BarExisted ( IN PCI_IO_DEVICE *PciIoDevice, IN UINTN Offset, OUT UINT32 *BarLengthValue, OUT UINT32 *OriginalBarValue ) { EFI_PCI_IO_PROTOCOL *PciIo; UINT32 OriginalValue; UINT32 Value; EFI_TPL OldTpl; PciIo = &PciIoDevice->PciIo; // // Preserve the original value // PciIo->Pci.Read (PciIo, EfiPciIoWidthUint32, (UINT8)Offset, 1, &OriginalValue); // // Raise TPL to high level to disable timer interrupt while the BAR is probed // OldTpl = gBS->RaiseTPL (TPL_HIGH_LEVEL); PciIo->Pci.Write (PciIo, EfiPciIoWidthUint32, (UINT8)Offset, 1, &gAllOne); PciIo->Pci.Read (PciIo, EfiPciIoWidthUint32, (UINT8)Offset, 1, &Value); // // Write back the original value // PciIo->Pci.Write (PciIo, EfiPciIoWidthUint32, (UINT8)Offset, 1, &OriginalValue); // // Restore TPL to its original level // gBS->RestoreTPL (OldTpl); if (BarLengthValue != NULL) { *BarLengthValue = Value; } if (OriginalBarValue != NULL) { *OriginalBarValue = OriginalValue; } if (Value == 0) { return EFI_NOT_FOUND; } else { return EFI_SUCCESS; } } /** Test whether the device can support given attributes. @param PciIoDevice Pci device instance. @param Command Input command register value, and returned supported register value. @param BridgeControl Input bridge control value for PPB or P2C, and returned supported bridge control value. @param OldCommand Returned and stored old command register offset. @param OldBridgeControl Returned and stored old Bridge control value for PPB or P2C. **/ VOID PciTestSupportedAttribute ( IN PCI_IO_DEVICE *PciIoDevice, IN OUT UINT16 *Command, IN OUT UINT16 *BridgeControl, OUT UINT16 *OldCommand, OUT UINT16 *OldBridgeControl ) { EFI_TPL OldTpl; UINT16 CommandValue; // // Preserve the original value // PCI_READ_COMMAND_REGISTER (PciIoDevice, OldCommand); // // Raise TPL to high level to disable timer interrupt while the BAR is probed // OldTpl = gBS->RaiseTPL (TPL_HIGH_LEVEL); CommandValue = *Command | *OldCommand; PCI_SET_COMMAND_REGISTER (PciIoDevice, CommandValue); PCI_READ_COMMAND_REGISTER (PciIoDevice, &CommandValue); *Command = *Command & CommandValue; // // Write back the original value // PCI_SET_COMMAND_REGISTER (PciIoDevice, *OldCommand); // // Restore TPL to its original level // gBS->RestoreTPL (OldTpl); if (IS_PCI_BRIDGE (&PciIoDevice->Pci) || IS_CARDBUS_BRIDGE (&PciIoDevice->Pci)) { // // Preserve the original value // PCI_READ_BRIDGE_CONTROL_REGISTER (PciIoDevice, OldBridgeControl); // // Raise TPL to high level to disable timer interrupt while the BAR is probed // OldTpl = gBS->RaiseTPL (TPL_HIGH_LEVEL); PCI_SET_BRIDGE_CONTROL_REGISTER (PciIoDevice, *BridgeControl); PCI_READ_BRIDGE_CONTROL_REGISTER (PciIoDevice, BridgeControl); // // Write back the original value // PCI_SET_BRIDGE_CONTROL_REGISTER (PciIoDevice, *OldBridgeControl); // // Restore TPL to its original level // gBS->RestoreTPL (OldTpl); } else { *OldBridgeControl = 0; *BridgeControl = 0; } } /** Set the supported or current attributes of a PCI device. @param PciIoDevice Structure pointer for PCI device. @param Command Command register value. @param BridgeControl Bridge control value for PPB or P2C. @param Option Make a choice of EFI_SET_SUPPORTS or EFI_SET_ATTRIBUTES. **/ VOID PciSetDeviceAttribute ( IN PCI_IO_DEVICE *PciIoDevice, IN UINT16 Command, IN UINT16 BridgeControl, IN UINTN Option ) { UINT64 Attributes; Attributes = 0; if ((Command & EFI_PCI_COMMAND_IO_SPACE) != 0) { Attributes |= EFI_PCI_IO_ATTRIBUTE_IO; } if ((Command & EFI_PCI_COMMAND_MEMORY_SPACE) != 0) { Attributes |= EFI_PCI_IO_ATTRIBUTE_MEMORY; } if ((Command & EFI_PCI_COMMAND_BUS_MASTER) != 0) { Attributes |= EFI_PCI_IO_ATTRIBUTE_BUS_MASTER; } if ((Command & EFI_PCI_COMMAND_VGA_PALETTE_SNOOP) != 0) { Attributes |= EFI_PCI_IO_ATTRIBUTE_VGA_PALETTE_IO; } if ((BridgeControl & EFI_PCI_BRIDGE_CONTROL_ISA) != 0) { Attributes |= EFI_PCI_IO_ATTRIBUTE_ISA_IO; } if ((BridgeControl & EFI_PCI_BRIDGE_CONTROL_VGA) != 0) { Attributes |= EFI_PCI_IO_ATTRIBUTE_VGA_IO; Attributes |= EFI_PCI_IO_ATTRIBUTE_VGA_MEMORY; Attributes |= EFI_PCI_IO_ATTRIBUTE_VGA_PALETTE_IO; } if ((BridgeControl & EFI_PCI_BRIDGE_CONTROL_VGA_16) != 0) { Attributes |= EFI_PCI_IO_ATTRIBUTE_VGA_IO_16; Attributes |= EFI_PCI_IO_ATTRIBUTE_VGA_PALETTE_IO_16; } if (Option == EFI_SET_SUPPORTS) { Attributes |= (UINT64)(EFI_PCI_IO_ATTRIBUTE_MEMORY_WRITE_COMBINE | EFI_PCI_IO_ATTRIBUTE_MEMORY_CACHED | EFI_PCI_IO_ATTRIBUTE_MEMORY_DISABLE | EFI_PCI_IO_ATTRIBUTE_EMBEDDED_DEVICE | EFI_PCI_IO_ATTRIBUTE_EMBEDDED_ROM | EFI_PCI_IO_ATTRIBUTE_DUAL_ADDRESS_CYCLE); if (IS_PCI_LPC (&PciIoDevice->Pci)) { Attributes |= EFI_PCI_IO_ATTRIBUTE_ISA_MOTHERBOARD_IO; Attributes |= (mReserveIsaAliases ? (UINT64)EFI_PCI_IO_ATTRIBUTE_ISA_IO : \ (UINT64)EFI_PCI_IO_ATTRIBUTE_ISA_IO_16); } if (IS_PCI_BRIDGE (&PciIoDevice->Pci) || IS_CARDBUS_BRIDGE (&PciIoDevice->Pci)) { // // For bridge, it should support IDE attributes // Attributes |= EFI_PCI_IO_ATTRIBUTE_IDE_SECONDARY_IO; Attributes |= EFI_PCI_IO_ATTRIBUTE_IDE_PRIMARY_IO; if (mReserveVgaAliases) { Attributes &= ~(UINT64)(EFI_PCI_IO_ATTRIBUTE_VGA_IO_16 | \ EFI_PCI_IO_ATTRIBUTE_VGA_PALETTE_IO_16); } else { Attributes &= ~(UINT64)(EFI_PCI_IO_ATTRIBUTE_VGA_IO | \ EFI_PCI_IO_ATTRIBUTE_VGA_PALETTE_IO); } } else { if (IS_PCI_IDE (&PciIoDevice->Pci)) { Attributes |= EFI_PCI_IO_ATTRIBUTE_IDE_SECONDARY_IO; Attributes |= EFI_PCI_IO_ATTRIBUTE_IDE_PRIMARY_IO; } if (IS_PCI_VGA (&PciIoDevice->Pci)) { Attributes |= EFI_PCI_IO_ATTRIBUTE_VGA_MEMORY; Attributes |= (mReserveVgaAliases ? (UINT64)EFI_PCI_IO_ATTRIBUTE_VGA_IO : \ (UINT64)EFI_PCI_IO_ATTRIBUTE_VGA_IO_16); } } PciIoDevice->Supports = Attributes; PciIoDevice->Supports &= ((PciIoDevice->Parent->Supports) | \ EFI_PCI_IO_ATTRIBUTE_IO | EFI_PCI_IO_ATTRIBUTE_MEMORY | \ EFI_PCI_IO_ATTRIBUTE_BUS_MASTER); } else { // // When this attribute is clear, the RomImage and RomSize fields in the PCI IO were // initialized based on the PCI option ROM found through the ROM BAR of the PCI controller. // When this attribute is set, the PCI option ROM described by the RomImage and RomSize // fields is not from the the ROM BAR of the PCI controller. // if (!PciIoDevice->EmbeddedRom) { Attributes |= EFI_PCI_IO_ATTRIBUTE_EMBEDDED_ROM; } PciIoDevice->Attributes = Attributes; } } /** Determine if the device can support Fast Back to Back attribute. @param PciIoDevice Pci device instance. @param StatusIndex Status register value. @retval EFI_SUCCESS This device support Fast Back to Back attribute. @retval EFI_UNSUPPORTED This device doesn't support Fast Back to Back attribute. **/ EFI_STATUS GetFastBackToBackSupport ( IN PCI_IO_DEVICE *PciIoDevice, IN UINT8 StatusIndex ) { EFI_PCI_IO_PROTOCOL *PciIo; EFI_STATUS Status; UINT32 StatusRegister; // // Read the status register // PciIo = &PciIoDevice->PciIo; Status = PciIo->Pci.Read (PciIo, EfiPciIoWidthUint16, StatusIndex, 1, &StatusRegister); if (EFI_ERROR (Status)) { return EFI_UNSUPPORTED; } // // Check the Fast B2B bit // if ((StatusRegister & EFI_PCI_FAST_BACK_TO_BACK_CAPABLE) != 0) { return EFI_SUCCESS; } else { return EFI_UNSUPPORTED; } } /** Process the option ROM for all the children of the specified parent PCI device. It can only be used after the first full Option ROM process. @param PciIoDevice Pci device instance. **/ VOID ProcessOptionRomLight ( IN PCI_IO_DEVICE *PciIoDevice ) { PCI_IO_DEVICE *Temp; LIST_ENTRY *CurrentLink; // // For RootBridge, PPB , P2C, go recursively to traverse all its children // CurrentLink = PciIoDevice->ChildList.ForwardLink; while (CurrentLink != NULL && CurrentLink != &PciIoDevice->ChildList) { Temp = PCI_IO_DEVICE_FROM_LINK (CurrentLink); if (!IsListEmpty (&Temp->ChildList)) { ProcessOptionRomLight (Temp); } Temp->AllOpRomProcessed = PciRomGetImageMapping (Temp); CurrentLink = CurrentLink->ForwardLink; } } /** Determine the related attributes of all devices under a Root Bridge. @param PciIoDevice PCI device instance. **/ EFI_STATUS DetermineDeviceAttribute ( IN PCI_IO_DEVICE *PciIoDevice ) { UINT16 Command; UINT16 BridgeControl; UINT16 OldCommand; UINT16 OldBridgeControl; BOOLEAN FastB2BSupport; PCI_IO_DEVICE *Temp; LIST_ENTRY *CurrentLink; EFI_STATUS Status; // // For Root Bridge, just copy it by RootBridgeIo protocol // so as to keep consistent with the actual attribute // if (PciIoDevice->Parent == NULL) { Status = PciIoDevice->PciRootBridgeIo->GetAttributes ( PciIoDevice->PciRootBridgeIo, &PciIoDevice->Supports, &PciIoDevice->Attributes ); if (EFI_ERROR (Status)) { return Status; } // // Assume the PCI Root Bridge supports DAC // PciIoDevice->Supports |= (UINT64)(EFI_PCI_IO_ATTRIBUTE_EMBEDDED_DEVICE | EFI_PCI_IO_ATTRIBUTE_EMBEDDED_ROM | EFI_PCI_IO_ATTRIBUTE_DUAL_ADDRESS_CYCLE); } else { // // Set the attributes to be checked for common PCI devices and PPB or P2C // Since some devices only support part of them, it is better to set the // attribute according to its command or bridge control register // Command = EFI_PCI_COMMAND_IO_SPACE | EFI_PCI_COMMAND_MEMORY_SPACE | EFI_PCI_COMMAND_BUS_MASTER | EFI_PCI_COMMAND_VGA_PALETTE_SNOOP; BridgeControl = EFI_PCI_BRIDGE_CONTROL_ISA | EFI_PCI_BRIDGE_CONTROL_VGA | EFI_PCI_BRIDGE_CONTROL_VGA_16; // // Test whether the device can support attributes above // PciTestSupportedAttribute (PciIoDevice, &Command, &BridgeControl, &OldCommand, &OldBridgeControl); // // Set the supported attributes for specified PCI device // PciSetDeviceAttribute (PciIoDevice, Command, BridgeControl, EFI_SET_SUPPORTS); // // Set the current attributes for specified PCI device // PciSetDeviceAttribute (PciIoDevice, OldCommand, OldBridgeControl, EFI_SET_ATTRIBUTES); // // Enable other PCI supported attributes but not defined in PCI_IO_PROTOCOL // For PCI Express devices, Memory Write and Invalidate is hardwired to 0b so only enable it for PCI devices. if (!PciIoDevice->IsPciExp) { PCI_ENABLE_COMMAND_REGISTER (PciIoDevice, EFI_PCI_COMMAND_MEMORY_WRITE_AND_INVALIDATE); } } FastB2BSupport = TRUE; // // P2C can not support FB2B on the secondary side // if (IS_CARDBUS_BRIDGE (&PciIoDevice->Pci)) { FastB2BSupport = FALSE; } // // For RootBridge, PPB , P2C, go recursively to traverse all its children // CurrentLink = PciIoDevice->ChildList.ForwardLink; while (CurrentLink != NULL && CurrentLink != &PciIoDevice->ChildList) { Temp = PCI_IO_DEVICE_FROM_LINK (CurrentLink); Status = DetermineDeviceAttribute (Temp); if (EFI_ERROR (Status)) { return Status; } // // Detect Fast Back to Back support for the device under the bridge // Status = GetFastBackToBackSupport (Temp, PCI_PRIMARY_STATUS_OFFSET); if (FastB2BSupport && EFI_ERROR (Status)) { FastB2BSupport = FALSE; } CurrentLink = CurrentLink->ForwardLink; } // // Set or clear Fast Back to Back bit for the whole bridge // if (!IsListEmpty (&PciIoDevice->ChildList)) { if (IS_PCI_BRIDGE (&PciIoDevice->Pci)) { Status = GetFastBackToBackSupport (PciIoDevice, PCI_BRIDGE_STATUS_REGISTER_OFFSET); if (EFI_ERROR (Status) || (!FastB2BSupport)) { FastB2BSupport = FALSE; PCI_DISABLE_BRIDGE_CONTROL_REGISTER (PciIoDevice, EFI_PCI_BRIDGE_CONTROL_FAST_BACK_TO_BACK); } else { PCI_ENABLE_BRIDGE_CONTROL_REGISTER (PciIoDevice, EFI_PCI_BRIDGE_CONTROL_FAST_BACK_TO_BACK); } } CurrentLink = PciIoDevice->ChildList.ForwardLink; while (CurrentLink != NULL && CurrentLink != &PciIoDevice->ChildList) { Temp = PCI_IO_DEVICE_FROM_LINK (CurrentLink); if (FastB2BSupport) { PCI_ENABLE_COMMAND_REGISTER (Temp, EFI_PCI_COMMAND_FAST_BACK_TO_BACK); } else { PCI_DISABLE_COMMAND_REGISTER (Temp, EFI_PCI_COMMAND_FAST_BACK_TO_BACK); } CurrentLink = CurrentLink->ForwardLink; } } // // End for IsListEmpty // return EFI_SUCCESS; } /** This routine is used to update the bar information for those incompatible PCI device. @param PciIoDevice Input Pci device instance. Output Pci device instance with updated Bar information. @param IgnoreOptionRom Output If the option rom of incompatible device need to be ignored. @retval EFI_SUCCESS Successfully updated bar information. @retval EFI_UNSUPPORTED Given PCI device doesn't belong to incompatible PCI device list. **/ EFI_STATUS UpdatePciInfo ( IN OUT PCI_IO_DEVICE *PciIoDevice, OUT BOOLEAN *IgnoreOptionRom ) { EFI_STATUS Status; UINTN BarIndex; BOOLEAN SetFlag; VOID *Configuration; EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *Ptr; Configuration = NULL; Status = EFI_SUCCESS; if (gIncompatiblePciDeviceSupport == NULL) { // // It can only be supported after the Incompatible PCI Device // Support Protocol has been installed // Status = gBS->LocateProtocol ( &gEfiIncompatiblePciDeviceSupportProtocolGuid, NULL, (VOID **)&gIncompatiblePciDeviceSupport ); } if (Status == EFI_SUCCESS) { // // Check whether the device belongs to incompatible devices from protocol or not // If it is , then get its special requirement in the ACPI table // Status = gIncompatiblePciDeviceSupport->CheckDevice ( gIncompatiblePciDeviceSupport, PciIoDevice->Pci.Hdr.VendorId, PciIoDevice->Pci.Hdr.DeviceId, PciIoDevice->Pci.Hdr.RevisionID, PciIoDevice->Pci.Device.SubsystemVendorID, PciIoDevice->Pci.Device.SubsystemID, &Configuration ); } if (EFI_ERROR (Status) || (Configuration == NULL)) { return EFI_UNSUPPORTED; } // // Update PCI device information from the ACPI table // Ptr = (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *)Configuration; while (Ptr->Desc != ACPI_END_TAG_DESCRIPTOR) { if (Ptr->Desc != ACPI_ADDRESS_SPACE_DESCRIPTOR) { // // The format is not support // break; } // // According to "Table 20. ACPI 2.0 & 3.0 QWORD Address Space Descriptor Usage" // in PI Spec 1.7, Type-specific flags can be set to 0 when Address Translation // Offset == 6 to skip device option ROM (do not probe option rom BAR). // if (((Ptr->AddrTranslationOffset == PCI_MAX_BAR) && (Ptr->SpecificFlag == 0))) { *IgnoreOptionRom = TRUE; Ptr++; continue; } for (BarIndex = 0; BarIndex < PCI_MAX_BAR; BarIndex++) { if ((Ptr->AddrTranslationOffset != MAX_UINT64) && (Ptr->AddrTranslationOffset != MAX_UINT8) && (Ptr->AddrTranslationOffset != BarIndex) ) { // // Skip updating when AddrTranslationOffset is not MAX_UINT64 or MAX_UINT8 (wide match). // Skip updating when current BarIndex doesn't equal to AddrTranslationOffset. // Comparing against MAX_UINT8 is to keep backward compatibility. // continue; } SetFlag = FALSE; switch (Ptr->ResType) { case ACPI_ADDRESS_SPACE_TYPE_MEM: // // Make sure the bar is memory type // if (CheckBarType (PciIoDevice, (UINT8)BarIndex, PciBarTypeMem)) { SetFlag = TRUE; // // Ignored if granularity is 0. // Ignored if PCI BAR is I/O or 32-bit memory. // If PCI BAR is 64-bit memory and granularity is 32, then // the PCI BAR resource is allocated below 4GB. // If PCI BAR is 64-bit memory and granularity is 64, then // the PCI BAR resource is allocated above 4GB. // if (PciIoDevice->PciBar[BarIndex].BarType == PciBarTypeMem64) { switch (Ptr->AddrSpaceGranularity) { case 32: PciIoDevice->PciBar[BarIndex].BarType = PciBarTypeMem32; case 64: PciIoDevice->PciBar[BarIndex].BarTypeFixed = TRUE; break; default: break; } } if (PciIoDevice->PciBar[BarIndex].BarType == PciBarTypePMem64) { switch (Ptr->AddrSpaceGranularity) { case 32: PciIoDevice->PciBar[BarIndex].BarType = PciBarTypePMem32; case 64: PciIoDevice->PciBar[BarIndex].BarTypeFixed = TRUE; break; default: break; } } } break; case ACPI_ADDRESS_SPACE_TYPE_IO: // // Make sure the bar is IO type // if (CheckBarType (PciIoDevice, (UINT8)BarIndex, PciBarTypeIo)) { SetFlag = TRUE; } break; } if (SetFlag) { // // Update the new alignment for the device // SetNewAlign (&(PciIoDevice->PciBar[BarIndex].Alignment), Ptr->AddrRangeMax); // // Update the new length for the device // if (Ptr->AddrLen != 0) { PciIoDevice->PciBar[BarIndex].Length = Ptr->AddrLen; } } } Ptr++; } FreePool (Configuration); return EFI_SUCCESS; } /** This routine will update the alignment with the new alignment. Compare with OLD_ALIGN/EVEN_ALIGN/SQUAD_ALIGN/DQUAD_ALIGN is to keep backward compatibility. @param Alignment Input Old alignment. Output updated alignment. @param NewAlignment New alignment. **/ VOID SetNewAlign ( IN OUT UINT64 *Alignment, IN UINT64 NewAlignment ) { UINT64 OldAlignment; UINTN ShiftBit; // // The new alignment is the same as the original, // so skip it // if ((NewAlignment == 0) || (NewAlignment == OLD_ALIGN)) { return; } // // Check the validity of the parameter // if ((NewAlignment != EVEN_ALIGN) && (NewAlignment != SQUAD_ALIGN) && (NewAlignment != DQUAD_ALIGN)) { *Alignment = NewAlignment; return; } OldAlignment = (*Alignment) + 1; ShiftBit = 0; // // Get the first non-zero hex value of the length // while ((OldAlignment & 0x0F) == 0x00) { OldAlignment = RShiftU64 (OldAlignment, 4); ShiftBit += 4; } // // Adjust the alignment to even, quad or double quad boundary // if (NewAlignment == EVEN_ALIGN) { if ((OldAlignment & 0x01) != 0) { OldAlignment = OldAlignment + 2 - (OldAlignment & 0x01); } } else if (NewAlignment == SQUAD_ALIGN) { if ((OldAlignment & 0x03) != 0) { OldAlignment = OldAlignment + 4 - (OldAlignment & 0x03); } } else if (NewAlignment == DQUAD_ALIGN) { if ((OldAlignment & 0x07) != 0) { OldAlignment = OldAlignment + 8 - (OldAlignment & 0x07); } } // // Update the old value // NewAlignment = LShiftU64 (OldAlignment, ShiftBit) - 1; *Alignment = NewAlignment; return; } /** Parse PCI IOV VF bar information and fill them into PCI device instance. @param PciIoDevice Pci device instance. @param Offset Bar offset. @param BarIndex Bar index. @return Next bar offset. **/ UINTN PciIovParseVfBar ( IN PCI_IO_DEVICE *PciIoDevice, IN UINTN Offset, IN UINTN BarIndex ) { UINT32 Value; UINT32 OriginalValue; UINT32 Mask; EFI_STATUS Status; // // Ensure it is called properly // ASSERT (PciIoDevice->SrIovCapabilityOffset != 0); if (PciIoDevice->SrIovCapabilityOffset == 0) { return 0; } OriginalValue = 0; Value = 0; Status = VfBarExisted ( PciIoDevice, Offset, &Value, &OriginalValue ); if (EFI_ERROR (Status)) { PciIoDevice->VfPciBar[BarIndex].BaseAddress = 0; PciIoDevice->VfPciBar[BarIndex].Length = 0; PciIoDevice->VfPciBar[BarIndex].Alignment = 0; // // Scan all the BARs anyway // PciIoDevice->VfPciBar[BarIndex].Offset = (UINT16)Offset; return Offset + 4; } PciIoDevice->VfPciBar[BarIndex].Offset = (UINT16)Offset; if ((Value & 0x01) != 0) { // // Device I/Os. Impossible // ASSERT (FALSE); return Offset + 4; } else { Mask = 0xfffffff0; PciIoDevice->VfPciBar[BarIndex].BaseAddress = OriginalValue & Mask; switch (Value & 0x07) { // // memory space; anywhere in 32 bit address space // case 0x00: if ((Value & 0x08) != 0) { PciIoDevice->VfPciBar[BarIndex].BarType = PciBarTypePMem32; } else { PciIoDevice->VfPciBar[BarIndex].BarType = PciBarTypeMem32; } PciIoDevice->VfPciBar[BarIndex].Length = (~(Value & Mask)) + 1; PciIoDevice->VfPciBar[BarIndex].Alignment = PciIoDevice->VfPciBar[BarIndex].Length - 1; // // Adjust Length // PciIoDevice->VfPciBar[BarIndex].Length = MultU64x32 (PciIoDevice->VfPciBar[BarIndex].Length, PciIoDevice->InitialVFs); // // Adjust Alignment // if (PciIoDevice->VfPciBar[BarIndex].Alignment < PciIoDevice->SystemPageSize - 1) { PciIoDevice->VfPciBar[BarIndex].Alignment = PciIoDevice->SystemPageSize - 1; } break; // // memory space; anywhere in 64 bit address space // case 0x04: if ((Value & 0x08) != 0) { PciIoDevice->VfPciBar[BarIndex].BarType = PciBarTypePMem64; } else { PciIoDevice->VfPciBar[BarIndex].BarType = PciBarTypeMem64; } // // According to PCI 2.2,if the bar indicates a memory 64 decoding, next bar // is regarded as an extension for the first bar. As a result // the sizing will be conducted on combined 64 bit value // Here just store the masked first 32bit value for future size // calculation // PciIoDevice->VfPciBar[BarIndex].Length = Value & Mask; PciIoDevice->VfPciBar[BarIndex].Alignment = PciIoDevice->VfPciBar[BarIndex].Length - 1; if (PciIoDevice->VfPciBar[BarIndex].Alignment < PciIoDevice->SystemPageSize - 1) { PciIoDevice->VfPciBar[BarIndex].Alignment = PciIoDevice->SystemPageSize - 1; } // // Increment the offset to point to next DWORD // Offset += 4; Status = VfBarExisted ( PciIoDevice, Offset, &Value, &OriginalValue ); if (EFI_ERROR (Status)) { PciIoDevice->VfPciBar[BarIndex].BarType = PciBarTypeUnknown; return Offset + 4; } // // Fix the length to support some special 64 bit BAR // Value |= ((UINT32)-1 << HighBitSet32 (Value)); // // Calculate the size of 64bit bar // PciIoDevice->VfPciBar[BarIndex].BaseAddress |= LShiftU64 ((UINT64)OriginalValue, 32); PciIoDevice->VfPciBar[BarIndex].Length = PciIoDevice->VfPciBar[BarIndex].Length | LShiftU64 ((UINT64)Value, 32); PciIoDevice->VfPciBar[BarIndex].Length = (~(PciIoDevice->VfPciBar[BarIndex].Length)) + 1; PciIoDevice->VfPciBar[BarIndex].Alignment = PciIoDevice->VfPciBar[BarIndex].Length - 1; // // Adjust Length // PciIoDevice->VfPciBar[BarIndex].Length = MultU64x32 (PciIoDevice->VfPciBar[BarIndex].Length, PciIoDevice->InitialVFs); // // Adjust Alignment // if (PciIoDevice->VfPciBar[BarIndex].Alignment < PciIoDevice->SystemPageSize - 1) { PciIoDevice->VfPciBar[BarIndex].Alignment = PciIoDevice->SystemPageSize - 1; } break; // // reserved // default: PciIoDevice->VfPciBar[BarIndex].BarType = PciBarTypeUnknown; PciIoDevice->VfPciBar[BarIndex].Length = (~(Value & Mask)) + 1; PciIoDevice->VfPciBar[BarIndex].Alignment = PciIoDevice->VfPciBar[BarIndex].Length - 1; if (PciIoDevice->VfPciBar[BarIndex].Alignment < PciIoDevice->SystemPageSize - 1) { PciIoDevice->VfPciBar[BarIndex].Alignment = PciIoDevice->SystemPageSize - 1; } break; } } // // Check the length again so as to keep compatible with some special bars // if (PciIoDevice->VfPciBar[BarIndex].Length == 0) { PciIoDevice->VfPciBar[BarIndex].BarType = PciBarTypeUnknown; PciIoDevice->VfPciBar[BarIndex].BaseAddress = 0; PciIoDevice->VfPciBar[BarIndex].Alignment = 0; } // // Increment number of bar // return Offset + 4; } /** Parse PCI bar information and fill them into PCI device instance. @param PciIoDevice Pci device instance. @param Offset Bar offset. @param BarIndex Bar index. @return Next bar offset. **/ UINTN PciParseBar ( IN PCI_IO_DEVICE *PciIoDevice, IN UINTN Offset, IN UINTN BarIndex ) { UINT32 Value; UINT32 OriginalValue; UINT32 Mask; EFI_STATUS Status; OriginalValue = 0; Value = 0; Status = BarExisted ( PciIoDevice, Offset, &Value, &OriginalValue ); if (EFI_ERROR (Status)) { PciIoDevice->PciBar[BarIndex].BaseAddress = 0; PciIoDevice->PciBar[BarIndex].Length = 0; PciIoDevice->PciBar[BarIndex].Alignment = 0; // // Some devices don't fully comply to PCI spec 2.2. So be to scan all the BARs anyway // PciIoDevice->PciBar[BarIndex].Offset = (UINT8)Offset; return Offset + 4; } PciIoDevice->PciBar[BarIndex].BarTypeFixed = FALSE; PciIoDevice->PciBar[BarIndex].Offset = (UINT8)Offset; if ((Value & 0x01) != 0) { // // Device I/Os // Mask = 0xfffffffc; if ((Value & 0xFFFF0000) != 0) { // // It is a IO32 bar // PciIoDevice->PciBar[BarIndex].BarType = PciBarTypeIo32; PciIoDevice->PciBar[BarIndex].Length = ((~(Value & Mask)) + 1); PciIoDevice->PciBar[BarIndex].Alignment = PciIoDevice->PciBar[BarIndex].Length - 1; } else { // // It is a IO16 bar // PciIoDevice->PciBar[BarIndex].BarType = PciBarTypeIo16; PciIoDevice->PciBar[BarIndex].Length = 0x0000FFFF & ((~(Value & Mask)) + 1); PciIoDevice->PciBar[BarIndex].Alignment = PciIoDevice->PciBar[BarIndex].Length - 1; } // // Workaround. Some platforms implement IO bar with 0 length // Need to treat it as no-bar // if (PciIoDevice->PciBar[BarIndex].Length == 0) { PciIoDevice->PciBar[BarIndex].BarType = (PCI_BAR_TYPE)0; } PciIoDevice->PciBar[BarIndex].BaseAddress = OriginalValue & Mask; } else { Mask = 0xfffffff0; PciIoDevice->PciBar[BarIndex].BaseAddress = OriginalValue & Mask; switch (Value & 0x07) { // // memory space; anywhere in 32 bit address space // case 0x00: if ((Value & 0x08) != 0) { PciIoDevice->PciBar[BarIndex].BarType = PciBarTypePMem32; } else { PciIoDevice->PciBar[BarIndex].BarType = PciBarTypeMem32; } PciIoDevice->PciBar[BarIndex].Length = (~(Value & Mask)) + 1; if (PciIoDevice->PciBar[BarIndex].Length < (SIZE_4KB)) { // // Force minimum 4KByte alignment for Virtualization technology for Directed I/O // PciIoDevice->PciBar[BarIndex].Alignment = (SIZE_4KB - 1); } else { PciIoDevice->PciBar[BarIndex].Alignment = PciIoDevice->PciBar[BarIndex].Length - 1; } break; // // memory space; anywhere in 64 bit address space // case 0x04: if ((Value & 0x08) != 0) { PciIoDevice->PciBar[BarIndex].BarType = PciBarTypePMem64; } else { PciIoDevice->PciBar[BarIndex].BarType = PciBarTypeMem64; } // // According to PCI 2.2,if the bar indicates a memory 64 decoding, next bar // is regarded as an extension for the first bar. As a result // the sizing will be conducted on combined 64 bit value // Here just store the masked first 32bit value for future size // calculation // PciIoDevice->PciBar[BarIndex].Length = Value & Mask; PciIoDevice->PciBar[BarIndex].Alignment = PciIoDevice->PciBar[BarIndex].Length - 1; // // Increment the offset to point to next DWORD // Offset += 4; Status = BarExisted ( PciIoDevice, Offset, &Value, &OriginalValue ); if (EFI_ERROR (Status)) { // // the high 32 bit does not claim any BAR, we need to re-check the low 32 bit BAR again // if (PciIoDevice->PciBar[BarIndex].Length == 0) { // // some device implement MMIO bar with 0 length, need to treat it as no-bar // PciIoDevice->PciBar[BarIndex].BarType = PciBarTypeUnknown; return Offset + 4; } } // // Fix the length to support some special 64 bit BAR // if (Value == 0) { DEBUG ((DEBUG_INFO, "[PciBus]BAR probing for upper 32bit of MEM64 BAR returns 0, change to 0xFFFFFFFF.\n")); Value = (UINT32)-1; } else { Value |= ((UINT32)(-1) << HighBitSet32 (Value)); } // // Calculate the size of 64bit bar // PciIoDevice->PciBar[BarIndex].BaseAddress |= LShiftU64 ((UINT64)OriginalValue, 32); PciIoDevice->PciBar[BarIndex].Length = PciIoDevice->PciBar[BarIndex].Length | LShiftU64 ((UINT64)Value, 32); PciIoDevice->PciBar[BarIndex].Length = (~(PciIoDevice->PciBar[BarIndex].Length)) + 1; if (PciIoDevice->PciBar[BarIndex].Length < (SIZE_4KB)) { // // Force minimum 4KByte alignment for Virtualization technology for Directed I/O // PciIoDevice->PciBar[BarIndex].Alignment = (SIZE_4KB - 1); } else { PciIoDevice->PciBar[BarIndex].Alignment = PciIoDevice->PciBar[BarIndex].Length - 1; } break; // // reserved // default: PciIoDevice->PciBar[BarIndex].BarType = PciBarTypeUnknown; PciIoDevice->PciBar[BarIndex].Length = (~(Value & Mask)) + 1; if (PciIoDevice->PciBar[BarIndex].Length < (SIZE_4KB)) { // // Force minimum 4KByte alignment for Virtualization technology for Directed I/O // PciIoDevice->PciBar[BarIndex].Alignment = (SIZE_4KB - 1); } else { PciIoDevice->PciBar[BarIndex].Alignment = PciIoDevice->PciBar[BarIndex].Length - 1; } break; } } // // Check the length again so as to keep compatible with some special bars // if (PciIoDevice->PciBar[BarIndex].Length == 0) { PciIoDevice->PciBar[BarIndex].BarType = PciBarTypeUnknown; PciIoDevice->PciBar[BarIndex].BaseAddress = 0; PciIoDevice->PciBar[BarIndex].Alignment = 0; } // // Increment number of bar // return Offset + 4; } /** This routine is used to initialize the bar of a PCI device. @param PciIoDevice Pci device instance. @note It can be called typically when a device is going to be rejected. **/ VOID InitializePciDevice ( IN PCI_IO_DEVICE *PciIoDevice ) { EFI_PCI_IO_PROTOCOL *PciIo; UINT8 Offset; PciIo = &(PciIoDevice->PciIo); // // Put all the resource apertures // Resource base is set to all ones so as to indicate its resource // has not been allocated // for (Offset = 0x10; Offset <= 0x24; Offset += sizeof (UINT32)) { PciIo->Pci.Write (PciIo, EfiPciIoWidthUint32, Offset, 1, &gAllOne); } } /** This routine is used to initialize the bar of a PCI-PCI Bridge device. @param PciIoDevice PCI-PCI bridge device instance. **/ VOID InitializePpb ( IN PCI_IO_DEVICE *PciIoDevice ) { EFI_PCI_IO_PROTOCOL *PciIo; PciIo = &(PciIoDevice->PciIo); // // Put all the resource apertures including IO16 // Io32, pMem32, pMem64 to quiescent state // Resource base all ones, Resource limit all zeros // PciIo->Pci.Write (PciIo, EfiPciIoWidthUint8, 0x1C, 1, &gAllOne); PciIo->Pci.Write (PciIo, EfiPciIoWidthUint8, 0x1D, 1, &gAllZero); PciIo->Pci.Write (PciIo, EfiPciIoWidthUint16, 0x20, 1, &gAllOne); PciIo->Pci.Write (PciIo, EfiPciIoWidthUint16, 0x22, 1, &gAllZero); PciIo->Pci.Write (PciIo, EfiPciIoWidthUint16, 0x24, 1, &gAllOne); PciIo->Pci.Write (PciIo, EfiPciIoWidthUint16, 0x26, 1, &gAllZero); PciIo->Pci.Write (PciIo, EfiPciIoWidthUint32, 0x28, 1, &gAllOne); PciIo->Pci.Write (PciIo, EfiPciIoWidthUint32, 0x2C, 1, &gAllZero); // // Don't support use io32 as for now // PciIo->Pci.Write (PciIo, EfiPciIoWidthUint16, 0x30, 1, &gAllOne); PciIo->Pci.Write (PciIo, EfiPciIoWidthUint16, 0x32, 1, &gAllZero); // // Force Interrupt line to zero for cards that come up randomly // PciIo->Pci.Write (PciIo, EfiPciIoWidthUint8, 0x3C, 1, &gAllZero); } /** This routine is used to initialize the bar of a PCI Card Bridge device. @param PciIoDevice PCI Card bridge device. **/ VOID InitializeP2C ( IN PCI_IO_DEVICE *PciIoDevice ) { EFI_PCI_IO_PROTOCOL *PciIo; PciIo = &(PciIoDevice->PciIo); // // Put all the resource apertures including IO16 // Io32, pMem32, pMem64 to quiescent state( // Resource base all ones, Resource limit all zeros // PciIo->Pci.Write (PciIo, EfiPciIoWidthUint32, 0x1c, 1, &gAllOne); PciIo->Pci.Write (PciIo, EfiPciIoWidthUint32, 0x20, 1, &gAllZero); PciIo->Pci.Write (PciIo, EfiPciIoWidthUint32, 0x24, 1, &gAllOne); PciIo->Pci.Write (PciIo, EfiPciIoWidthUint32, 0x28, 1, &gAllZero); PciIo->Pci.Write (PciIo, EfiPciIoWidthUint32, 0x2c, 1, &gAllOne); PciIo->Pci.Write (PciIo, EfiPciIoWidthUint32, 0x30, 1, &gAllZero); PciIo->Pci.Write (PciIo, EfiPciIoWidthUint32, 0x34, 1, &gAllOne); PciIo->Pci.Write (PciIo, EfiPciIoWidthUint32, 0x38, 1, &gAllZero); // // Force Interrupt line to zero for cards that come up randomly // PciIo->Pci.Write (PciIo, EfiPciIoWidthUint8, 0x3C, 1, &gAllZero); } /** Authenticate the PCI device by using DeviceSecurityProtocol. @param PciIoDevice PCI device. @retval EFI_SUCCESS The device passes the authentication. @return not EFI_SUCCESS The device failes the authentication or unexpected error happen during authentication. **/ EFI_STATUS AuthenticatePciDevice ( IN PCI_IO_DEVICE *PciIoDevice ) { EDKII_DEVICE_IDENTIFIER DeviceIdentifier; EFI_STATUS Status; if (mDeviceSecurityProtocol != NULL) { // // Prepare the parameter // DeviceIdentifier.Version = EDKII_DEVICE_IDENTIFIER_REVISION; CopyGuid (&DeviceIdentifier.DeviceType, &gEdkiiDeviceIdentifierTypePciGuid); DeviceIdentifier.DeviceHandle = NULL; Status = gBS->InstallMultipleProtocolInterfaces ( &DeviceIdentifier.DeviceHandle, &gEfiDevicePathProtocolGuid, PciIoDevice->DevicePath, &gEdkiiDeviceIdentifierTypePciGuid, &PciIoDevice->PciIo, NULL ); if (EFI_ERROR (Status)) { return Status; } // // Do DeviceAuthentication // Status = mDeviceSecurityProtocol->DeviceAuthenticate (mDeviceSecurityProtocol, &DeviceIdentifier); // // Always uninstall, because they are only for Authentication. // No need to check return Status. // gBS->UninstallMultipleProtocolInterfaces ( DeviceIdentifier.DeviceHandle, &gEfiDevicePathProtocolGuid, PciIoDevice->DevicePath, &gEdkiiDeviceIdentifierTypePciGuid, &PciIoDevice->PciIo, NULL ); return Status; } // // Device Security Protocol is not found, just return success // return EFI_SUCCESS; } /** Checks if PCI device is Root Bridge. @param PciIoDevice Instance of PCI device @retval TRUE Device is Root Bridge @retval FALSE Device is not Root Bridge **/ BOOLEAN IsRootBridge ( IN PCI_IO_DEVICE *PciIoDevice ) { if (PciIoDevice->Parent == NULL) { return TRUE; } else { return FALSE; } } /** Create and initialize general PCI I/O device instance for PCI device/bridge device/hotplug bridge device. @param Bridge Parent bridge instance. @param Pci Input Pci information block. @param Bus Device Bus NO. @param Device Device device NO. @param Func Device func NO. @return Instance of PCI device. NULL means no instance created. **/ PCI_IO_DEVICE * CreatePciIoDevice ( IN PCI_IO_DEVICE *Bridge, IN PCI_TYPE00 *Pci, IN UINT8 Bus, IN UINT8 Device, IN UINT8 Func ) { PCI_IO_DEVICE *PciIoDevice; EFI_PCI_IO_PROTOCOL *PciIo; EFI_STATUS Status; PciIoDevice = AllocateZeroPool (sizeof (PCI_IO_DEVICE)); if (PciIoDevice == NULL) { return NULL; } PciIoDevice->Signature = PCI_IO_DEVICE_SIGNATURE; PciIoDevice->Handle = NULL; PciIoDevice->PciRootBridgeIo = Bridge->PciRootBridgeIo; PciIoDevice->DevicePath = NULL; PciIoDevice->BusNumber = Bus; PciIoDevice->DeviceNumber = Device; PciIoDevice->FunctionNumber = Func; PciIoDevice->Decodes = 0; if (gFullEnumeration) { PciIoDevice->Allocated = FALSE; } else { PciIoDevice->Allocated = TRUE; } PciIoDevice->Registered = FALSE; PciIoDevice->Attributes = 0; PciIoDevice->Supports = 0; PciIoDevice->BusOverride = FALSE; PciIoDevice->AllOpRomProcessed = FALSE; PciIoDevice->IsPciExp = FALSE; CopyMem (&(PciIoDevice->Pci), Pci, sizeof (PCI_TYPE01)); // // Initialize the PCI I/O instance structure // InitializePciIoInstance (PciIoDevice); InitializePciDriverOverrideInstance (PciIoDevice); InitializePciLoadFile2 (PciIoDevice); PciIo = &PciIoDevice->PciIo; // // Create a device path for this PCI device and store it into its private data // CreatePciDevicePath ( Bridge->DevicePath, PciIoDevice ); // // Detect if PCI Express Device // PciIoDevice->PciExpressCapabilityOffset = 0; Status = LocateCapabilityRegBlock ( PciIoDevice, EFI_PCI_CAPABILITY_ID_PCIEXP, &PciIoDevice->PciExpressCapabilityOffset, NULL ); if (!EFI_ERROR (Status)) { PciIoDevice->IsPciExp = TRUE; } // // Now we can do the authentication check for the device. // Status = AuthenticatePciDevice (PciIoDevice); // // If authentication fails, skip this device. // if (EFI_ERROR (Status)) { if (PciIoDevice->DevicePath != NULL) { FreePool (PciIoDevice->DevicePath); } FreePool (PciIoDevice); return NULL; } // // Check if device's parent is not Root Bridge // if (PcdGetBool (PcdAriSupport) && !IsRootBridge (Bridge)) { // // Check if the device is an ARI device. // Status = LocatePciExpressCapabilityRegBlock ( PciIoDevice, EFI_PCIE_CAPABILITY_ID_ARI, &PciIoDevice->AriCapabilityOffset, NULL ); if (!EFI_ERROR (Status)) { // // We need to enable ARI feature before calculate BusReservation, // because FirstVFOffset and VFStride may change after that. // EFI_PCI_IO_PROTOCOL *ParentPciIo; UINT32 Data32; // // Check if its parent supports ARI forwarding. // ParentPciIo = &Bridge->PciIo; ParentPciIo->Pci.Read ( ParentPciIo, EfiPciIoWidthUint32, Bridge->PciExpressCapabilityOffset + EFI_PCIE_CAPABILITY_DEVICE_CAPABILITIES_2_OFFSET, 1, &Data32 ); if ((Data32 & EFI_PCIE_CAPABILITY_DEVICE_CAPABILITIES_2_ARI_FORWARDING) != 0) { PciIoDevice->IsAriEnabled = TRUE; // // ARI forward support in bridge, so enable it. // ParentPciIo->Pci.Read ( ParentPciIo, EfiPciIoWidthUint32, Bridge->PciExpressCapabilityOffset + EFI_PCIE_CAPABILITY_DEVICE_CONTROL_2_OFFSET, 1, &Data32 ); if ((Data32 & EFI_PCIE_CAPABILITY_DEVICE_CONTROL_2_ARI_FORWARDING) == 0) { Data32 |= EFI_PCIE_CAPABILITY_DEVICE_CONTROL_2_ARI_FORWARDING; ParentPciIo->Pci.Write ( ParentPciIo, EfiPciIoWidthUint32, Bridge->PciExpressCapabilityOffset + EFI_PCIE_CAPABILITY_DEVICE_CONTROL_2_OFFSET, 1, &Data32 ); DEBUG (( DEBUG_INFO, " ARI: forwarding enabled for PPB[%02x:%02x:%02x]\n", Bridge->BusNumber, Bridge->DeviceNumber, Bridge->FunctionNumber )); } } DEBUG ((DEBUG_INFO, " ARI: CapOffset = 0x%x\n", PciIoDevice->AriCapabilityOffset)); } } // // Initialization for SR-IOV // if (PcdGetBool (PcdSrIovSupport)) { Status = LocatePciExpressCapabilityRegBlock ( PciIoDevice, EFI_PCIE_CAPABILITY_ID_SRIOV, &PciIoDevice->SrIovCapabilityOffset, NULL ); if (!EFI_ERROR (Status)) { UINT32 SupportedPageSize; UINT16 VFStride; UINT16 FirstVFOffset; UINT16 Data16; UINT32 PFRid; UINT32 LastVF; // // If the SR-IOV device is an ARI device, then Set ARI Capable Hierarchy for the device. // if (PcdGetBool (PcdAriSupport) && (PciIoDevice->AriCapabilityOffset != 0)) { PciIo->Pci.Read ( PciIo, EfiPciIoWidthUint16, PciIoDevice->SrIovCapabilityOffset + EFI_PCIE_CAPABILITY_ID_SRIOV_CONTROL, 1, &Data16 ); Data16 |= EFI_PCIE_CAPABILITY_ID_SRIOV_CONTROL_ARI_HIERARCHY; PciIo->Pci.Write ( PciIo, EfiPciIoWidthUint16, PciIoDevice->SrIovCapabilityOffset + EFI_PCIE_CAPABILITY_ID_SRIOV_CONTROL, 1, &Data16 ); } // // Calculate SystemPageSize // PciIo->Pci.Read ( PciIo, EfiPciIoWidthUint32, PciIoDevice->SrIovCapabilityOffset + EFI_PCIE_CAPABILITY_ID_SRIOV_SUPPORTED_PAGE_SIZE, 1, &SupportedPageSize ); PciIoDevice->SystemPageSize = (PcdGet32 (PcdSrIovSystemPageSize) & SupportedPageSize); ASSERT (PciIoDevice->SystemPageSize != 0); PciIo->Pci.Write ( PciIo, EfiPciIoWidthUint32, PciIoDevice->SrIovCapabilityOffset + EFI_PCIE_CAPABILITY_ID_SRIOV_SYSTEM_PAGE_SIZE, 1, &PciIoDevice->SystemPageSize ); // // Adjust SystemPageSize for Alignment usage later // PciIoDevice->SystemPageSize <<= 12; // // Calculate BusReservation for PCI IOV // // // Read First FirstVFOffset, InitialVFs, and VFStride // PciIo->Pci.Read ( PciIo, EfiPciIoWidthUint16, PciIoDevice->SrIovCapabilityOffset + EFI_PCIE_CAPABILITY_ID_SRIOV_FIRSTVF, 1, &FirstVFOffset ); PciIo->Pci.Read ( PciIo, EfiPciIoWidthUint16, PciIoDevice->SrIovCapabilityOffset + EFI_PCIE_CAPABILITY_ID_SRIOV_INITIALVFS, 1, &PciIoDevice->InitialVFs ); PciIo->Pci.Read ( PciIo, EfiPciIoWidthUint16, PciIoDevice->SrIovCapabilityOffset + EFI_PCIE_CAPABILITY_ID_SRIOV_VFSTRIDE, 1, &VFStride ); // // Calculate LastVF // if (PciIoDevice->InitialVFs == 0) { PciIoDevice->ReservedBusNum = 0; } else { PFRid = EFI_PCI_RID (Bus, Device, Func); LastVF = PFRid + FirstVFOffset + (PciIoDevice->InitialVFs - 1) * VFStride; // // Calculate ReservedBusNum for this PF // PciIoDevice->ReservedBusNum = (UINT16)(EFI_PCI_BUS_OF_RID (LastVF) - Bus); } DEBUG (( DEBUG_INFO, " SR-IOV: SupportedPageSize = 0x%x; SystemPageSize = 0x%x; FirstVFOffset = 0x%x;\n", SupportedPageSize, PciIoDevice->SystemPageSize >> 12, FirstVFOffset )); DEBUG (( DEBUG_INFO, " InitialVFs = 0x%x; ReservedBusNum = 0x%x; CapOffset = 0x%x\n", PciIoDevice->InitialVFs, PciIoDevice->ReservedBusNum, PciIoDevice->SrIovCapabilityOffset )); } } if (PcdGetBool (PcdMrIovSupport)) { Status = LocatePciExpressCapabilityRegBlock ( PciIoDevice, EFI_PCIE_CAPABILITY_ID_MRIOV, &PciIoDevice->MrIovCapabilityOffset, NULL ); if (!EFI_ERROR (Status)) { DEBUG ((DEBUG_INFO, " MR-IOV: CapOffset = 0x%x\n", PciIoDevice->MrIovCapabilityOffset)); } } PciIoDevice->ResizableBarOffset = 0; if (PcdGetBool (PcdPcieResizableBarSupport)) { Status = LocatePciExpressCapabilityRegBlock ( PciIoDevice, PCI_EXPRESS_EXTENDED_CAPABILITY_RESIZABLE_BAR_ID, &PciIoDevice->ResizableBarOffset, NULL ); if (!EFI_ERROR (Status)) { PCI_EXPRESS_EXTENDED_CAPABILITIES_RESIZABLE_BAR_CONTROL ResizableBarControl; UINT32 Offset; Offset = PciIoDevice->ResizableBarOffset + sizeof (PCI_EXPRESS_EXTENDED_CAPABILITIES_HEADER) + sizeof (PCI_EXPRESS_EXTENDED_CAPABILITIES_RESIZABLE_BAR_CAPABILITY), PciIo->Pci.Read ( PciIo, EfiPciIoWidthUint8, Offset, sizeof (PCI_EXPRESS_EXTENDED_CAPABILITIES_RESIZABLE_BAR_CONTROL), &ResizableBarControl ); PciIoDevice->ResizableBarNumber = ResizableBarControl.Bits.ResizableBarNumber; PciProgramResizableBar (PciIoDevice, PciResizableBarMax); } } // // Initialize the reserved resource list // InitializeListHead (&PciIoDevice->ReservedResourceList); // // Initialize the driver list // InitializeListHead (&PciIoDevice->OptionRomDriverList); // // Initialize the child list // InitializeListHead (&PciIoDevice->ChildList); return PciIoDevice; } /** This routine is used to enumerate entire pci bus system in a given platform. It is only called on the second start on the same Root Bridge. @param Controller Parent bridge handler. @retval EFI_SUCCESS PCI enumeration finished successfully. @retval other Some error occurred when enumerating the pci bus system. **/ EFI_STATUS PciEnumeratorLight ( IN EFI_HANDLE Controller ) { EFI_STATUS Status; EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *PciRootBridgeIo; PCI_IO_DEVICE *RootBridgeDev; UINT16 MinBus; UINT16 MaxBus; EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *Descriptors; MinBus = 0; MaxBus = PCI_MAX_BUS; Descriptors = NULL; // // If this root bridge has been already enumerated, then return successfully // if (GetRootBridgeByHandle (Controller) != NULL) { return EFI_SUCCESS; } // // Open pci root bridge io protocol // Status = gBS->OpenProtocol ( Controller, &gEfiPciRootBridgeIoProtocolGuid, (VOID **)&PciRootBridgeIo, gPciBusDriverBinding.DriverBindingHandle, Controller, EFI_OPEN_PROTOCOL_BY_DRIVER ); if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) { return Status; } Status = PciRootBridgeIo->Configuration (PciRootBridgeIo, (VOID **)&Descriptors); if (EFI_ERROR (Status)) { return Status; } while (PciGetBusRange (&Descriptors, &MinBus, &MaxBus, NULL) == EFI_SUCCESS) { // // Create a device node for root bridge device with a NULL host bridge controller handle // RootBridgeDev = CreateRootBridge (Controller); if (RootBridgeDev == NULL) { Descriptors++; continue; } // // Record the root bridge-io protocol // RootBridgeDev->PciRootBridgeIo = PciRootBridgeIo; Status = PciPciDeviceInfoCollector ( RootBridgeDev, (UINT8)MinBus ); if (!EFI_ERROR (Status)) { // // Remove those PCI devices which are rejected when full enumeration // RemoveRejectedPciDevices (RootBridgeDev->Handle, RootBridgeDev); // // Process option rom light // ProcessOptionRomLight (RootBridgeDev); // // Determine attributes for all devices under this root bridge // DetermineDeviceAttribute (RootBridgeDev); // // If successfully, insert the node into device pool // InsertRootBridge (RootBridgeDev); } else { // // If unsuccessfully, destroy the entire node // DestroyRootBridge (RootBridgeDev); } Descriptors++; } return EFI_SUCCESS; } /** Get bus range from PCI resource descriptor list. @param Descriptors A pointer to the address space descriptor. @param MinBus The min bus returned. @param MaxBus The max bus returned. @param BusRange The bus range returned. @retval EFI_SUCCESS Successfully got bus range. @retval EFI_NOT_FOUND Can not find the specific bus. **/ EFI_STATUS PciGetBusRange ( IN EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR **Descriptors, OUT UINT16 *MinBus, OUT UINT16 *MaxBus, OUT UINT16 *BusRange ) { while ((*Descriptors)->Desc != ACPI_END_TAG_DESCRIPTOR) { if ((*Descriptors)->ResType == ACPI_ADDRESS_SPACE_TYPE_BUS) { if (MinBus != NULL) { *MinBus = (UINT16)(*Descriptors)->AddrRangeMin; } if (MaxBus != NULL) { *MaxBus = (UINT16)(*Descriptors)->AddrRangeMax; } if (BusRange != NULL) { *BusRange = (UINT16)(*Descriptors)->AddrLen; } return EFI_SUCCESS; } (*Descriptors)++; } return EFI_NOT_FOUND; } /** This routine can be used to start the root bridge. @param RootBridgeDev Pci device instance. @retval EFI_SUCCESS This device started. @retval other Failed to get PCI Root Bridge I/O protocol. **/ EFI_STATUS StartManagingRootBridge ( IN PCI_IO_DEVICE *RootBridgeDev ) { EFI_HANDLE RootBridgeHandle; EFI_STATUS Status; EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *PciRootBridgeIo; // // Get the root bridge handle // RootBridgeHandle = RootBridgeDev->Handle; PciRootBridgeIo = NULL; // // Get the pci root bridge io protocol // Status = gBS->OpenProtocol ( RootBridgeHandle, &gEfiPciRootBridgeIoProtocolGuid, (VOID **)&PciRootBridgeIo, gPciBusDriverBinding.DriverBindingHandle, RootBridgeHandle, EFI_OPEN_PROTOCOL_BY_DRIVER ); if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) { return Status; } // // Store the PciRootBridgeIo protocol into root bridge private data // RootBridgeDev->PciRootBridgeIo = PciRootBridgeIo; return EFI_SUCCESS; } /** This routine can be used to check whether a PCI device should be rejected when light enumeration. @param PciIoDevice Pci device instance. @retval TRUE This device should be rejected. @retval FALSE This device shouldn't be rejected. **/ BOOLEAN IsPciDeviceRejected ( IN PCI_IO_DEVICE *PciIoDevice ) { EFI_STATUS Status; UINT32 TestValue; UINT32 OldValue; UINT32 Mask; UINT8 BarOffset; // // PPB should be skip! // if (IS_PCI_BRIDGE (&PciIoDevice->Pci)) { return FALSE; } if (IS_CARDBUS_BRIDGE (&PciIoDevice->Pci)) { // // Only test base registers for P2C // for (BarOffset = 0x1C; BarOffset <= 0x38; BarOffset += 2 * sizeof (UINT32)) { Mask = (BarOffset < 0x2C) ? 0xFFFFF000 : 0xFFFFFFFC; Status = BarExisted (PciIoDevice, BarOffset, &TestValue, &OldValue); if (EFI_ERROR (Status)) { continue; } TestValue = TestValue & Mask; if ((TestValue != 0) && (TestValue == (OldValue & Mask))) { // // The bar isn't programed, so it should be rejected // return TRUE; } } return FALSE; } for (BarOffset = 0x14; BarOffset <= 0x24; BarOffset += sizeof (UINT32)) { // // Test PCI devices // Status = BarExisted (PciIoDevice, BarOffset, &TestValue, &OldValue); if (EFI_ERROR (Status)) { continue; } if ((TestValue & 0x01) != 0) { // // IO Bar // Mask = 0xFFFFFFFC; TestValue = TestValue & Mask; if ((TestValue != 0) && (TestValue == (OldValue & Mask))) { return TRUE; } } else { // // Mem Bar // Mask = 0xFFFFFFF0; TestValue = TestValue & Mask; if ((TestValue & 0x07) == 0x04) { // // Mem64 or PMem64 // BarOffset += sizeof (UINT32); if ((TestValue != 0) && (TestValue == (OldValue & Mask))) { // // Test its high 32-Bit BAR // Status = BarExisted (PciIoDevice, BarOffset, &TestValue, &OldValue); if (TestValue == OldValue) { return TRUE; } } } else { // // Mem32 or PMem32 // if ((TestValue != 0) && (TestValue == (OldValue & Mask))) { return TRUE; } } } } return FALSE; } /** Reset all bus number from specific bridge. @param Bridge Parent specific bridge. @param StartBusNumber Start bus number. **/ VOID ResetAllPpbBusNumber ( IN PCI_IO_DEVICE *Bridge, IN UINT8 StartBusNumber ) { EFI_STATUS Status; PCI_TYPE00 Pci; UINT8 Device; UINT32 Register; UINT8 Func; UINT64 Address; UINT8 SecondaryBus; EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *PciRootBridgeIo; PciRootBridgeIo = Bridge->PciRootBridgeIo; for (Device = 0; Device <= PCI_MAX_DEVICE; Device++) { for (Func = 0; Func <= PCI_MAX_FUNC; Func++) { // // Check to see whether a pci device is present // Status = PciDevicePresent ( PciRootBridgeIo, &Pci, StartBusNumber, Device, Func ); if (EFI_ERROR (Status) && (Func == 0)) { // // go to next device if there is no Function 0 // break; } if (!EFI_ERROR (Status) && (IS_PCI_BRIDGE (&Pci))) { Register = 0; Address = EFI_PCI_ADDRESS (StartBusNumber, Device, Func, 0x18); Status = PciRootBridgeIo->Pci.Read ( PciRootBridgeIo, EfiPciWidthUint32, Address, 1, &Register ); SecondaryBus = (UINT8)(Register >> 8); if (SecondaryBus != 0) { ResetAllPpbBusNumber (Bridge, SecondaryBus); } // // Reset register 18h, 19h, 1Ah on PCI Bridge // Register &= 0xFF000000; Status = PciRootBridgeIo->Pci.Write ( PciRootBridgeIo, EfiPciWidthUint32, Address, 1, &Register ); } if ((Func == 0) && !IS_PCI_MULTI_FUNC (&Pci)) { // // Skip sub functions, this is not a multi function device // Func = PCI_MAX_FUNC; } } } }