summaryrefslogtreecommitdiffstats
path: root/DynamicTablesPkg/Library/FdtHwInfoParserLib/Pci/PciConfigSpaceParser.c
diff options
context:
space:
mode:
Diffstat (limited to 'DynamicTablesPkg/Library/FdtHwInfoParserLib/Pci/PciConfigSpaceParser.c')
-rw-r--r--DynamicTablesPkg/Library/FdtHwInfoParserLib/Pci/PciConfigSpaceParser.c806
1 files changed, 806 insertions, 0 deletions
diff --git a/DynamicTablesPkg/Library/FdtHwInfoParserLib/Pci/PciConfigSpaceParser.c b/DynamicTablesPkg/Library/FdtHwInfoParserLib/Pci/PciConfigSpaceParser.c
new file mode 100644
index 0000000000..76f9efdf64
--- /dev/null
+++ b/DynamicTablesPkg/Library/FdtHwInfoParserLib/Pci/PciConfigSpaceParser.c
@@ -0,0 +1,806 @@
+/** @file
+ PCI Configuration Space Parser.
+
+ Copyright (c) 2021, ARM Limited. All rights reserved.<BR>
+ 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 <Library/DebugLib.h>
+
+#include "FdtHwInfoParser.h"
+#include "Pci/PciConfigSpaceParser.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 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;
+ }
+
+ // 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
+PciConfigInfoParser (
+ 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;
+}