/** @file
Arm PCI Configuration Space Parser.
Copyright (c) 2021, ARM Limited. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent
@par Reference(s):
- linux/Documentation/devicetree/bindings/pci/host-generic-pci.yaml
- PCI Firmware Specification - Revision 3.0
- Open Firmware Recommended Practice: Interrupt Mapping, Version 0.9
- Devicetree Specification Release v0.3
- linux kernel code
**/
#include "CmObjectDescUtility.h"
#include
#include "FdtHwInfoParser.h"
#include "Pci/ArmPciConfigSpaceParser.h"
#include "Gic/ArmGicDispatcher.h"
/** List of "compatible" property values for host PCIe bridges nodes.
Any other "compatible" value is not supported by this module.
*/
STATIC CONST COMPATIBILITY_STR PciCompatibleStr[] = {
{ "pci-host-ecam-generic" }
};
/** COMPATIBILITY_INFO structure for the PCIe.
*/
STATIC CONST COMPATIBILITY_INFO PciCompatibleInfo = {
ARRAY_SIZE (PciCompatibleStr),
PciCompatibleStr
};
/** Get the Segment group (also called: Domain Id) of a host-pci node.
kernel/Documentation/devicetree/bindings/pci/pci.txt:
"It is required to either not set this property at all or set it for all
host bridges in the system"
The function checks the "linux,pci-domain" property of the host-pci node.
Either all host-pci nodes must have this property, or none of them. If the
property is available, read it. Otherwise dynamically assign the Ids.
@param [in] Fdt Pointer to a Flattened Device Tree (Fdt).
@param [in] HostPciNode Offset of a host-pci node.
@param [out] SegGroup Segment group assigned to the host-pci controller.
@retval EFI_SUCCESS The function completed successfully.
@retval EFI_ABORTED An error occurred.
@retval EFI_INVALID_PARAMETER Invalid parameter.
**/
STATIC
EFI_STATUS
EFIAPI
GetPciSegGroup (
IN CONST VOID *Fdt,
IN INT32 HostPciNode,
OUT INT32 *SegGroup
)
{
CONST UINT8 *Data;
INT32 DataSize;
STATIC INT32 LocalSegGroup = 0;
if ((Fdt == NULL) ||
(SegGroup == NULL))
{
ASSERT (0);
return EFI_INVALID_PARAMETER;
}
Data = fdt_getprop (Fdt, HostPciNode, "linux,pci-domain", &DataSize);
if ((Data == NULL) || (DataSize < 0)) {
// Did not find property, assign the DomainIds ourselves.
if (LocalSegGroup < 0) {
// "linux,pci-domain" property was defined for another node.
ASSERT (0);
return EFI_ABORTED;
}
*SegGroup = LocalSegGroup++;
return EFI_SUCCESS;
}
if ((DataSize > sizeof (UINT32)) ||
(LocalSegGroup > 0))
{
// Property on more than 1 cell or
// "linux,pci-domain" property was not defined for a node.
ASSERT (0);
return EFI_ABORTED;
}
// If one node has the "linux,pci-domain" property, then all the host-pci
// nodes must have it.
LocalSegGroup = -1;
*SegGroup = fdt32_to_cpu (*(UINT32 *)Data);
return EFI_SUCCESS;
}
/** Parse the bus-range controlled by this host-pci node.
@param [in] Fdt Pointer to a Flattened Device Tree (Fdt).
@param [in] HostPciNode Offset of a host-pci node.
@param [in, out] PciInfo PCI_PARSER_TABLE structure storing
information about the current host-pci.
@retval EFI_SUCCESS The function completed successfully.
@retval EFI_ABORTED An error occurred.
@retval EFI_INVALID_PARAMETER Invalid parameter.
**/
STATIC
EFI_STATUS
EFIAPI
PopulateBusRange (
IN CONST VOID *Fdt,
IN INT32 HostPciNode,
IN OUT PCI_PARSER_TABLE *PciInfo
)
{
CONST UINT8 *Data;
INT32 DataSize;
UINT32 StartBus;
UINT32 EndBus;
if ((Fdt == NULL) ||
(PciInfo == NULL))
{
ASSERT (0);
return EFI_INVALID_PARAMETER;
}
Data = fdt_getprop (Fdt, HostPciNode, "bus-range", &DataSize);
if ((Data == NULL) || (DataSize < 0)) {
// No evidence this property is mandatory. Use default values.
StartBus = 0;
EndBus = 255;
} else if (DataSize == (2 * sizeof (UINT32))) {
// If available, the property is on two integers.
StartBus = fdt32_to_cpu (((UINT32 *)Data)[0]);
EndBus = fdt32_to_cpu (((UINT32 *)Data)[1]);
} else {
ASSERT (0);
return EFI_ABORTED;
}
PciInfo->PciConfigSpaceInfo.StartBusNumber = StartBus;
PciInfo->PciConfigSpaceInfo.EndBusNumber = EndBus;
return EFI_SUCCESS;
}
/** Parse the PCI address map.
The PCI address map is available in the "ranges" device-tree property.
@param [in] Fdt Pointer to a Flattened Device Tree (Fdt).
@param [in] HostPciNode Offset of a host-pci node.
@param [in] AddressCells # of cells used to encode an address on
the parent bus.
@param [in, out] PciInfo PCI_PARSER_TABLE structure storing
information about the current host-pci.
@retval EFI_SUCCESS The function completed successfully.
@retval EFI_ABORTED An error occurred.
@retval EFI_INVALID_PARAMETER Invalid parameter.
@retval EFI_OUT_OF_RESOURCES An allocation has failed.
**/
STATIC
EFI_STATUS
EFIAPI
ParseAddressMap (
IN CONST VOID *Fdt,
IN INT32 HostPciNode,
IN INT32 AddressCells,
IN OUT PCI_PARSER_TABLE *PciInfo
)
{
CONST UINT8 *Data;
INT32 DataSize;
UINT32 Index;
UINT32 Offset;
UINT32 AddressMapSize;
UINT32 Count;
UINT32 PciAddressAttr;
CM_ARCH_COMMON_PCI_ADDRESS_MAP_INFO *PciAddressMapInfo;
UINT32 BufferSize;
// The mapping is done on AddressMapSize bytes.
AddressMapSize = (PCI_ADDRESS_CELLS + AddressCells + PCI_SIZE_CELLS) *
sizeof (UINT32);
Data = fdt_getprop (Fdt, HostPciNode, "ranges", &DataSize);
if ((Data == NULL) ||
(DataSize < 0) ||
((DataSize % AddressMapSize) != 0))
{
// If error or not on AddressMapSize bytes.
ASSERT (0);
return EFI_ABORTED;
}
Count = DataSize / AddressMapSize;
// Allocate a buffer to store each address mapping.
BufferSize = Count * sizeof (CM_ARCH_COMMON_PCI_ADDRESS_MAP_INFO);
PciAddressMapInfo = AllocateZeroPool (BufferSize);
if (PciAddressMapInfo == NULL) {
ASSERT (0);
return EFI_OUT_OF_RESOURCES;
}
for (Index = 0; Index < Count; Index++) {
Offset = Index * AddressMapSize;
// Pci address attributes
PciAddressAttr = fdt32_to_cpu (*(UINT32 *)&Data[Offset]);
PciAddressMapInfo[Index].SpaceCode = READ_PCI_SS (PciAddressAttr);
Offset += sizeof (UINT32);
// Pci address
PciAddressMapInfo[Index].PciAddress =
fdt64_to_cpu (*(UINT64 *)&Data[Offset]);
Offset += (PCI_ADDRESS_CELLS - 1) * sizeof (UINT32);
// Cpu address
if (AddressCells == 2) {
PciAddressMapInfo[Index].CpuAddress =
fdt64_to_cpu (*(UINT64 *)&Data[Offset]);
} else {
PciAddressMapInfo[Index].CpuAddress =
fdt32_to_cpu (*(UINT32 *)&Data[Offset]);
}
Offset += AddressCells * sizeof (UINT32);
// Address size
PciAddressMapInfo[Index].AddressSize =
fdt64_to_cpu (*(UINT64 *)&Data[Offset]);
Offset += PCI_SIZE_CELLS * sizeof (UINT32);
} // for
PciInfo->Mapping[PciMappingTableAddress].ObjectId =
CREATE_CM_ARCH_COMMON_OBJECT_ID (EArchCommonObjPciAddressMapInfo);
PciInfo->Mapping[PciMappingTableAddress].Size =
sizeof (CM_ARCH_COMMON_PCI_ADDRESS_MAP_INFO) * Count;
PciInfo->Mapping[PciMappingTableAddress].Data = PciAddressMapInfo;
PciInfo->Mapping[PciMappingTableAddress].Count = Count;
return EFI_SUCCESS;
}
/** Parse the PCI interrupt map.
The PCI interrupt map is available in the "interrupt-map"
and "interrupt-map-mask" device-tree properties.
Cf Devicetree Specification Release v0.3,
s2.4.3 Interrupt Nexus Properties
An interrupt-map must be as:
interrupt-map = < [child unit address] [child interrupt specifier]
[interrupt-parent]
[parent unit address] [parent interrupt specifier] >
@param [in] Fdt Pointer to a Flattened Device Tree (Fdt).
@param [in] HostPciNode Offset of a host-pci node.
@param [in, out] PciInfo PCI_PARSER_TABLE structure storing
information about the current host-pci.
@retval EFI_SUCCESS The function completed successfully.
@retval EFI_ABORTED An error occurred.
@retval EFI_INVALID_PARAMETER Invalid parameter.
@retval EFI_NOT_FOUND Not found.
@retval EFI_OUT_OF_RESOURCES An allocation has failed.
**/
STATIC
EFI_STATUS
EFIAPI
ParseIrqMap (
IN CONST VOID *Fdt,
IN INT32 HostPciNode,
IN OUT PCI_PARSER_TABLE *PciInfo
)
{
EFI_STATUS Status;
CONST UINT8 *Data;
INT32 DataSize;
UINT32 Index;
UINT32 Offset;
INT32 IntcNode;
INT32 IntcAddressCells;
INT32 IntcCells;
INT32 PciIntCells;
INT32 IntcPhandle;
INT32 IrqMapSize;
UINT32 IrqMapCount;
CONST UINT8 *IrqMapMask;
INT32 IrqMapMaskSize;
INT32 PHandleOffset;
UINT32 GicVersion;
UINT32 PciAddressAttr;
CM_ARCH_COMMON_PCI_INTERRUPT_MAP_INFO *PciInterruptMapInfo;
UINT32 BufferSize;
Data = fdt_getprop (Fdt, HostPciNode, "interrupt-map", &DataSize);
if ((Data == NULL) || (DataSize <= 0)) {
DEBUG ((
DEBUG_WARN,
"Fdt parser: No Legacy interrupts found for PCI configuration space at "
"address: 0x%lx, group segment: %d\n",
PciInfo->PciConfigSpaceInfo.BaseAddress,
PciInfo->PciConfigSpaceInfo.PciSegmentGroupNumber
));
return EFI_NOT_FOUND;
}
// PCI interrupts are expected to be on 1 cell. Check it.
Status = FdtGetInterruptCellsInfo (Fdt, HostPciNode, &PciIntCells);
if (EFI_ERROR (Status)) {
ASSERT (0);
return Status;
}
if (PciIntCells != PCI_INTERRUPTS_CELLS) {
ASSERT (0);
return EFI_ABORTED;
}
IrqMapMask = fdt_getprop (
Fdt,
HostPciNode,
"interrupt-map-mask",
&IrqMapMaskSize
);
if ((IrqMapMask == NULL) ||
(IrqMapMaskSize !=
(PCI_ADDRESS_CELLS + PCI_INTERRUPTS_CELLS) * sizeof (UINT32)))
{
ASSERT (0);
return EFI_ABORTED;
}
// Get the interrupt-controller of the first irq mapping.
PHandleOffset = (PCI_ADDRESS_CELLS + PciIntCells) * sizeof (UINT32);
if (PHandleOffset > DataSize) {
ASSERT (0);
return EFI_ABORTED;
}
IntcPhandle = fdt32_to_cpu (*(UINT32 *)&Data[PHandleOffset]);
IntcNode = fdt_node_offset_by_phandle (Fdt, IntcPhandle);
if (IntcNode < 0) {
ASSERT (0);
return EFI_ABORTED;
}
// Only support Gic(s) for now.
Status = GetGicVersion (Fdt, IntcNode, &GicVersion);
if (EFI_ERROR (Status)) {
ASSERT (0);
return Status;
}
// Get the "address-cells" property of the IntcNode.
Status = FdtGetAddressInfo (Fdt, IntcNode, &IntcAddressCells, NULL);
if (EFI_ERROR (Status)) {
ASSERT (0);
return Status;
}
// Get the "interrupt-cells" property of the IntcNode.
Status = FdtGetInterruptCellsInfo (Fdt, IntcNode, &IntcCells);
if (EFI_ERROR (Status)) {
ASSERT (0);
return Status;
}
// An irq mapping is done on IrqMapSize bytes
// (which includes 1 cell for the PHandle).
IrqMapSize = (PCI_ADDRESS_CELLS + PciIntCells + 1
+ IntcAddressCells + IntcCells) * sizeof (UINT32);
if ((DataSize % IrqMapSize) != 0) {
// The mapping is not done on IrqMapSize bytes.
ASSERT (0);
return EFI_ABORTED;
}
IrqMapCount = DataSize / IrqMapSize;
// We assume the same interrupt-controller is used for all the mappings.
// Check this is correct.
for (Index = 0; Index < IrqMapCount; Index++) {
if (IntcPhandle != fdt32_to_cpu (
*(UINT32 *)&Data[(Index * IrqMapSize) + PHandleOffset]
))
{
ASSERT (0);
return EFI_ABORTED;
}
}
// Allocate a buffer to store each interrupt mapping.
IrqMapCount = DataSize / IrqMapSize;
BufferSize = IrqMapCount * sizeof (CM_ARCH_COMMON_PCI_ADDRESS_MAP_INFO);
PciInterruptMapInfo = AllocateZeroPool (BufferSize);
if (PciInterruptMapInfo == NULL) {
ASSERT (0);
return EFI_OUT_OF_RESOURCES;
}
for (Index = 0; Index < IrqMapCount; Index++) {
Offset = Index * IrqMapSize;
// Pci address attributes
PciAddressAttr = fdt32_to_cpu (
(*(UINT32 *)&Data[Offset]) &
(*(UINT32 *)&IrqMapMask[0])
);
PciInterruptMapInfo[Index].PciBus = READ_PCI_BBBBBBBB (PciAddressAttr);
PciInterruptMapInfo[Index].PciDevice = READ_PCI_DDDDD (PciAddressAttr);
Offset += PCI_ADDRESS_CELLS * sizeof (UINT32);
// Pci irq
PciInterruptMapInfo[Index].PciInterrupt = fdt32_to_cpu (
(*(UINT32 *)&Data[Offset]) &
(*(UINT32 *)&IrqMapMask[3 * sizeof (UINT32)])
);
// -1 to translate from device-tree (INTA=1) to ACPI (INTA=0) irq IDs.
PciInterruptMapInfo[Index].PciInterrupt -= 1;
Offset += PCI_INTERRUPTS_CELLS * sizeof (UINT32);
// PHandle (skip it)
Offset += sizeof (UINT32);
// "Parent unit address" (skip it)
Offset += IntcAddressCells * sizeof (UINT32);
// Interrupt controller interrupt and flags
PciInterruptMapInfo[Index].IntcInterrupt.Interrupt =
FdtGetInterruptId ((UINT32 *)&Data[Offset]);
PciInterruptMapInfo[Index].IntcInterrupt.Flags =
FdtGetInterruptFlags ((UINT32 *)&Data[Offset]);
} // for
PciInfo->Mapping[PciMappingTableInterrupt].ObjectId =
CREATE_CM_ARCH_COMMON_OBJECT_ID (EArchCommonObjPciInterruptMapInfo);
PciInfo->Mapping[PciMappingTableInterrupt].Size =
sizeof (CM_ARCH_COMMON_PCI_INTERRUPT_MAP_INFO) * IrqMapCount;
PciInfo->Mapping[PciMappingTableInterrupt].Data = PciInterruptMapInfo;
PciInfo->Mapping[PciMappingTableInterrupt].Count = IrqMapCount;
return Status;
}
/** Parse a Host-pci node.
@param [in] Fdt Pointer to a Flattened Device Tree (Fdt).
@param [in] HostPciNode Offset of a host-pci node.
@param [in, out] PciInfo The CM_ARCH_COMMON_PCI_CONFIG_SPACE_INFO to populate.
@retval EFI_SUCCESS The function completed successfully.
@retval EFI_ABORTED An error occurred.
@retval EFI_INVALID_PARAMETER Invalid parameter.
@retval EFI_OUT_OF_RESOURCES An allocation has failed.
**/
STATIC
EFI_STATUS
EFIAPI
PciNodeParser (
IN CONST VOID *Fdt,
IN INT32 HostPciNode,
IN OUT PCI_PARSER_TABLE *PciInfo
)
{
EFI_STATUS Status;
INT32 AddressCells;
INT32 SizeCells;
CONST UINT8 *Data;
INT32 DataSize;
INT32 SegGroup;
if ((Fdt == NULL) ||
(PciInfo == NULL))
{
ASSERT (0);
return EFI_INVALID_PARAMETER;
}
// Segment Group / DomainId
Status = GetPciSegGroup (Fdt, HostPciNode, &SegGroup);
if (EFI_ERROR (Status)) {
ASSERT (0);
return Status;
}
PciInfo->PciConfigSpaceInfo.PciSegmentGroupNumber = SegGroup;
// Bus range
Status = PopulateBusRange (Fdt, HostPciNode, PciInfo);
if (EFI_ERROR (Status)) {
ASSERT (0);
return Status;
}
Status = FdtGetParentAddressInfo (
Fdt,
HostPciNode,
&AddressCells,
&SizeCells
);
if (EFI_ERROR (Status)) {
ASSERT (0);
return Status;
}
// Only support 32/64 bits addresses.
if ((AddressCells < 1) ||
(AddressCells > 2) ||
(SizeCells < 1) ||
(SizeCells > 2))
{
ASSERT (0);
return EFI_ABORTED;
}
Data = fdt_getprop (Fdt, HostPciNode, "reg", &DataSize);
if ((Data == NULL) ||
(DataSize != ((AddressCells + SizeCells) * sizeof (UINT32))))
{
// If error or wrong size.
ASSERT (0);
return EFI_ABORTED;
}
// Base address
if (AddressCells == 2) {
PciInfo->PciConfigSpaceInfo.BaseAddress = fdt64_to_cpu (*(UINT64 *)Data);
} else {
PciInfo->PciConfigSpaceInfo.BaseAddress = fdt32_to_cpu (*(UINT32 *)Data);
}
// Address map
Status = ParseAddressMap (
Fdt,
HostPciNode,
AddressCells,
PciInfo
);
if (EFI_ERROR (Status)) {
ASSERT (0);
return Status;
}
// Irq map
Status = ParseIrqMap (
Fdt,
HostPciNode,
PciInfo
);
if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) {
ASSERT (0);
}
return EFI_SUCCESS;
}
/** Add the parsed Pci information to the Configuration Manager.
CmObj of the following types are concerned:
- EArchCommonObjPciConfigSpaceInfo
- EArchCommonObjPciAddressMapInfo
- EArchCommonObjPciInterruptMapInfo
@param [in] FdtParserHandle A handle to the parser instance.
@param [in] PciTableInfo PCI_PARSER_TABLE structure containing the
CmObjs to add.
@retval EFI_SUCCESS The function completed successfully.
@retval EFI_INVALID_PARAMETER Invalid parameter.
@retval EFI_OUT_OF_RESOURCES An allocation has failed.
**/
STATIC
EFI_STATUS
EFIAPI
PciInfoAdd (
IN CONST FDT_HW_INFO_PARSER_HANDLE FdtParserHandle,
IN PCI_PARSER_TABLE *PciTableInfo
)
{
EFI_STATUS Status;
CM_ARCH_COMMON_PCI_CONFIG_SPACE_INFO *PciConfigSpaceInfo;
if ((FdtParserHandle == NULL) ||
(PciTableInfo == NULL))
{
ASSERT (0);
return EFI_INVALID_PARAMETER;
}
PciConfigSpaceInfo = &PciTableInfo->PciConfigSpaceInfo;
// Add the address map space CmObj to the Configuration Manager.
Status = AddMultipleCmObjWithCmObjRef (
FdtParserHandle,
&PciTableInfo->Mapping[PciMappingTableAddress],
&PciConfigSpaceInfo->AddressMapToken
);
if (EFI_ERROR (Status)) {
ASSERT (0);
return Status;
}
// Add the interrupt map space CmObj to the Configuration Manager.
// Possible to have no legacy interrupts, or no device described and
// thus no interrupt-mapping.
if (PciTableInfo->Mapping[PciMappingTableInterrupt].Count != 0) {
Status = AddMultipleCmObjWithCmObjRef (
FdtParserHandle,
&PciTableInfo->Mapping[PciMappingTableInterrupt],
&PciConfigSpaceInfo->InterruptMapToken
);
if (EFI_ERROR (Status)) {
ASSERT (0);
return Status;
}
}
// Add the configuration space CmObj to the Configuration Manager.
Status = AddSingleCmObj (
FdtParserHandle,
CREATE_CM_ARCH_COMMON_OBJECT_ID (
EArchCommonObjPciConfigSpaceInfo
),
&PciTableInfo->PciConfigSpaceInfo,
sizeof (CM_ARCH_COMMON_PCI_CONFIG_SPACE_INFO),
NULL
);
ASSERT_EFI_ERROR (Status);
return Status;
}
/** Free the CmObjDesc of the ParserTable.
@param [in] PciTableInfo PCI_PARSER_TABLE structure containing the
CmObjs to free.
@retval EFI_SUCCESS The function completed successfully.
@retval EFI_INVALID_PARAMETER Invalid parameter.
**/
STATIC
EFI_STATUS
EFIAPI
FreeParserTable (
IN PCI_PARSER_TABLE *PciTableInfo
)
{
UINT32 Index;
VOID *Data;
if (PciTableInfo == NULL) {
ASSERT (0);
return EFI_INVALID_PARAMETER;
}
for (Index = 0; Index < PciMappingTableMax; Index++) {
Data = PciTableInfo->Mapping[Index].Data;
if (Data != NULL) {
FreePool (Data);
}
}
return EFI_SUCCESS;
}
/** CM_ARCH_COMMON_PCI_CONFIG_SPACE_INFO parser function.
The following structure is populated:
typedef struct CmArchCommonPciConfigSpaceInfo {
UINT64 BaseAddress; // {Populated}
UINT16 PciSegmentGroupNumber; // {Populated}
UINT8 StartBusNumber; // {Populated}
UINT8 EndBusNumber; // {Populated}
} CM_ARCH_COMMON_PCI_CONFIG_SPACE_INFO;
typedef struct CmArchCommonPciAddressMapInfo {
UINT8 SpaceCode; // {Populated}
UINT64 PciAddress; // {Populated}
UINT64 CpuAddress; // {Populated}
UINT64 AddressSize; // {Populated}
} CM_ARCH_COMMON_PCI_ADDRESS_MAP_INFO;
typedef struct CmArchCommonPciInterruptMapInfo {
UINT8 PciBus; // {Populated}
UINT8 PciDevice; // {Populated}
UINT8 PciInterrupt; // {Populated}
CM_ARCH_COMMON_GENERIC_INTERRUPT IntcInterrupt; // {Populated}
} CM_ARCH_COMMON_PCI_INTERRUPT_MAP_INFO;
A parser parses a Device Tree to populate a specific CmObj type. None,
one or many CmObj can be created by the parser.
The created CmObj are then handed to the parser's caller through the
HW_INFO_ADD_OBJECT interface.
This can also be a dispatcher. I.e. a function that not parsing a
Device Tree but calling other parsers.
@param [in] FdtParserHandle A handle to the parser instance.
@param [in] FdtBranch When searching for DT node name, restrict
the search to this Device Tree branch.
@retval EFI_SUCCESS The function completed successfully.
@retval EFI_ABORTED An error occurred.
@retval EFI_INVALID_PARAMETER Invalid parameter.
@retval EFI_NOT_FOUND Not found.
@retval EFI_UNSUPPORTED Unsupported.
**/
EFI_STATUS
EFIAPI
ArmPciConfigInfoParser (
IN CONST FDT_HW_INFO_PARSER_HANDLE FdtParserHandle,
IN INT32 FdtBranch
)
{
EFI_STATUS Status;
UINT32 Index;
INT32 PciNode;
UINT32 PciNodeCount;
PCI_PARSER_TABLE PciTableInfo;
VOID *Fdt;
if (FdtParserHandle == NULL) {
ASSERT (0);
return EFI_INVALID_PARAMETER;
}
Fdt = FdtParserHandle->Fdt;
// Only search host-pci devices.
// PCI Firmware Specification Revision 3.0, s4.1.2. "MCFG Table Description":
// "This table directly refers to PCI Segment Groups defined in the system
// via the _SEG object in the ACPI name space for the applicable host bridge
// device."
Status = FdtCountCompatNodeInBranch (
Fdt,
FdtBranch,
&PciCompatibleInfo,
&PciNodeCount
);
if (EFI_ERROR (Status)) {
ASSERT (0);
return Status;
}
if (PciNodeCount == 0) {
return EFI_NOT_FOUND;
}
// Parse each host-pci node in the branch.
PciNode = FdtBranch;
for (Index = 0; Index < PciNodeCount; Index++) {
ZeroMem (&PciTableInfo, sizeof (PCI_PARSER_TABLE));
Status = FdtGetNextCompatNodeInBranch (
Fdt,
FdtBranch,
&PciCompatibleInfo,
&PciNode
);
if (EFI_ERROR (Status)) {
ASSERT (0);
if (Status == EFI_NOT_FOUND) {
// Should have found the node.
Status = EFI_ABORTED;
}
return Status;
}
Status = PciNodeParser (Fdt, PciNode, &PciTableInfo);
if (EFI_ERROR (Status)) {
ASSERT (0);
goto error_handler;
}
// Add Pci information to the Configuration Manager.
Status = PciInfoAdd (FdtParserHandle, &PciTableInfo);
if (EFI_ERROR (Status)) {
ASSERT (0);
goto error_handler;
}
Status = FreeParserTable (&PciTableInfo);
if (EFI_ERROR (Status)) {
ASSERT (0);
return Status;
}
} // for
return Status;
error_handler:
FreeParserTable (&PciTableInfo);
return Status;
}