diff options
Diffstat (limited to 'src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Pci/PciBusDxe/PciEnumeratorSupport.c')
-rw-r--r-- | src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Pci/PciBusDxe/PciEnumeratorSupport.c | 2869 |
1 files changed, 2869 insertions, 0 deletions
diff --git a/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Pci/PciBusDxe/PciEnumeratorSupport.c b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Pci/PciBusDxe/PciEnumeratorSupport.c new file mode 100644 index 00000000..15bd182b --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Pci/PciBusDxe/PciEnumeratorSupport.c @@ -0,0 +1,2869 @@ +/** @file + PCI emumeration support functions implementation for PCI Bus module. + +Copyright (c) 2006 - 2021, Intel Corporation. All rights reserved.<BR> +(C) Copyright 2015 Hewlett Packard Enterprise Development LP<BR> +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; + + PciIoDevice = NULL; + + DEBUG (( + EFI_D_INFO, + "PciBus: Discovered %s @ [%02x|%02x|%02x]\n", + IS_PCI_BRIDGE (Pci) ? L"PPB" : + IS_CARDBUS_BRIDGE (Pci) ? L"P2C" : + L"PCI", + Bus, Device, Func + )); + + 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); + + if (PciIoDevice->DevicePath == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Detect this function has option rom + // + if (gFullEnumeration) { + + if (!IS_CARDBUS_BRIDGE (Pci)) { + + 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 (( + EFI_D_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 (( + EFI_D_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 (( + EFI_D_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 ((EFI_D_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; + + // + // 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); + + PCI_SET_COMMAND_REGISTER (PciIoDevice, *Command); + PCI_READ_COMMAND_REGISTER (PciIoDevice, Command); + + // + // 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. + + @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 + ) +{ + 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; + } + + 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 ((EFI_D_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; +} + +/** + 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; + } + + if (PcdGetBool (PcdAriSupport)) { + // + // 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) { + // + // 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 (( + EFI_D_INFO, + " ARI: forwarding enabled for PPB[%02x:%02x:%02x]\n", + Bridge->BusNumber, + Bridge->DeviceNumber, + Bridge->FunctionNumber + )); + } + } + + DEBUG ((EFI_D_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 + // + 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 + 1); + + DEBUG (( + EFI_D_INFO, + " SR-IOV: SupportedPageSize = 0x%x; SystemPageSize = 0x%x; FirstVFOffset = 0x%x;\n", + SupportedPageSize, PciIoDevice->SystemPageSize >> 12, FirstVFOffset + )); + DEBUG (( + EFI_D_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 ((EFI_D_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; + } + } + } +} + |