/** @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;
}
}
}
}