summaryrefslogtreecommitdiffstats
path: root/DynamicTablesPkg/Library/FdtHwInfoParserLib
diff options
context:
space:
mode:
Diffstat (limited to 'DynamicTablesPkg/Library/FdtHwInfoParserLib')
-rw-r--r--DynamicTablesPkg/Library/FdtHwInfoParserLib/Pci/ArmPciConfigSpaceParser.c813
-rw-r--r--DynamicTablesPkg/Library/FdtHwInfoParserLib/Pci/ArmPciConfigSpaceParser.h143
2 files changed, 956 insertions, 0 deletions
diff --git a/DynamicTablesPkg/Library/FdtHwInfoParserLib/Pci/ArmPciConfigSpaceParser.c b/DynamicTablesPkg/Library/FdtHwInfoParserLib/Pci/ArmPciConfigSpaceParser.c
new file mode 100644
index 0000000000..2ffff1ccd2
--- /dev/null
+++ b/DynamicTablesPkg/Library/FdtHwInfoParserLib/Pci/ArmPciConfigSpaceParser.c
@@ -0,0 +1,813 @@
+/** @file
+ Arm 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/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_ARM_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_ARM_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_ARM_OBJECT_ID (EArmObjPciAddressMapInfo);
+ PciInfo->Mapping[PciMappingTableAddress].Size =
+ sizeof (CM_ARM_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_ARM_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_ARM_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_ARM_OBJECT_ID (EArmObjPciInterruptMapInfo);
+ PciInfo->Mapping[PciMappingTableInterrupt].Size =
+ sizeof (CM_ARM_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_ARM_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:
+ - EArmObjPciConfigSpaceInfo
+ - EArmObjPciAddressMapInfo
+ - EArmObjPciInterruptMapInfo
+
+ @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_ARM_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_ARM_OBJECT_ID (EArmObjPciConfigSpaceInfo),
+ &PciTableInfo->PciConfigSpaceInfo,
+ sizeof (CM_ARM_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_ARM_PCI_CONFIG_SPACE_INFO parser function.
+
+ The following structure is populated:
+ typedef struct CmArmPciConfigSpaceInfo {
+ UINT64 BaseAddress; // {Populated}
+ UINT16 PciSegmentGroupNumber; // {Populated}
+ UINT8 StartBusNumber; // {Populated}
+ UINT8 EndBusNumber; // {Populated}
+ } CM_ARM_PCI_CONFIG_SPACE_INFO;
+
+ typedef struct CmArmPciAddressMapInfo {
+ UINT8 SpaceCode; // {Populated}
+ UINT64 PciAddress; // {Populated}
+ UINT64 CpuAddress; // {Populated}
+ UINT64 AddressSize; // {Populated}
+ } CM_ARM_PCI_ADDRESS_MAP_INFO;
+
+ typedef struct CmArmPciInterruptMapInfo {
+ UINT8 PciBus; // {Populated}
+ UINT8 PciDevice; // {Populated}
+ UINT8 PciInterrupt; // {Populated}
+ CM_ARM_GENERIC_INTERRUPT IntcInterrupt; // {Populated}
+ } CM_ARM_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;
+}
diff --git a/DynamicTablesPkg/Library/FdtHwInfoParserLib/Pci/ArmPciConfigSpaceParser.h b/DynamicTablesPkg/Library/FdtHwInfoParserLib/Pci/ArmPciConfigSpaceParser.h
new file mode 100644
index 0000000000..6e0027abea
--- /dev/null
+++ b/DynamicTablesPkg/Library/FdtHwInfoParserLib/Pci/ArmPciConfigSpaceParser.h
@@ -0,0 +1,143 @@
+/** @file
+ Arm 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
+**/
+
+#ifndef ARM_PCI_CONFIG_SPACE_PARSER_H_
+#define ARM_PCI_CONFIG_SPACE_PARSER_H_
+
+/** Read LEN bits at OFF offsets bits of the ADDR.
+
+ @param [in] ADDR Address to read the bits from.
+ @param [in] OFF Offset of the bits to read.
+ @param [in] LEN Number of bits to read.
+
+ @return The bits read.
+**/
+#define READ_BITS(ADDR, OFF, LEN) (((ADDR) >> (OFF)) & ((1<<(LEN))-1))
+
+/* Pci address attributes.
+*/
+/// 0 if relocatable.
+#define READ_PCI_N(ADDR) READ_BITS((ADDR), 31, 1)
+/// 1 if prefetchable.
+#define READ_PCI_P(ADDR) READ_BITS((ADDR), 30, 1)
+/// 1 if aliased.
+#define READ_PCI_T(ADDR) READ_BITS((ADDR), 29, 1)
+
+/** Space code.
+
+ 00: Configuration Space
+ 01: I/O Space
+ 10: 32-bit-address Memory Space
+ 11: 64-bit-address Memory Space
+*/
+#define READ_PCI_SS(ADDR) READ_BITS((ADDR), 24, 2)
+/// Bus number.
+#define READ_PCI_BBBBBBBB(ADDR) READ_BITS((ADDR), 16, 8)
+/// Device number.
+#define READ_PCI_DDDDD(ADDR) READ_BITS((ADDR), 11, 5)
+
+/** Number of device-tree cells used for PCI nodes properties.
+
+ Values are well defined, except the "#interrupt-cells" which
+ is assumed to be 1.
+*/
+#define PCI_ADDRESS_CELLS 3U
+#define PCI_SIZE_CELLS 2U
+#define PCI_INTERRUPTS_CELLS 1U
+
+/** PCI interrupt flags for device-tree.
+
+ Local Bus Specification Revision 3.0, s2.2.6., Interrupt Pins:
+ - 'Interrupts on PCI are optional and defined as "level sensitive,"
+ asserted low (negative true)'
+*/
+#define DT_PCI_IRQ_FLAGS(x) (((x) & 0xF) == BIT0)
+
+/** Indexes in the mapping table.
+*/
+typedef enum PciMappingTable {
+ PciMappingTableAddress, ///< 0 - Address mapping
+ PciMappingTableInterrupt, ///< 1 - Interrupt mapping
+ PciMappingTableMax, ///< 2 - Max
+} PCI_MAPPING_TABLE;
+
+#pragma pack(1)
+
+/** PCI parser table
+
+ Multiple address-map and interrupt map can correspond to
+ one host-pci device. This structure allows to temporarily
+ store the CmObjects created and generate tokens once
+ the whole device tree is parsed.
+*/
+typedef struct PciParserTable {
+ /// PCI Configuration Space Info
+ CM_ARM_PCI_CONFIG_SPACE_INFO PciConfigSpaceInfo;
+
+ /// Store the address mapping and interrupt mapping as CmObjDesc
+ /// before adding them to the Configuration Manager.
+ CM_OBJ_DESCRIPTOR Mapping[PciMappingTableMax];
+} PCI_PARSER_TABLE;
+
+#pragma pack()
+
+/** CM_ARM_PCI_CONFIG_SPACE_INFO parser function.
+
+ The following structure is populated:
+ typedef struct CmArmPciConfigSpaceInfo {
+ UINT64 BaseAddress; // {Populated}
+ UINT16 PciSegmentGroupNumber; // {Populated}
+ UINT8 StartBusNumber; // {Populated}
+ UINT8 EndBusNumber; // {Populated}
+ } CM_ARM_PCI_CONFIG_SPACE_INFO;
+
+ typedef struct CmArmPciAddressMapInfo {
+ UINT8 SpaceCode; // {Populated}
+ UINT64 PciAddress; // {Populated}
+ UINT64 CpuAddress; // {Populated}
+ UINT64 AddressSize; // {Populated}
+ } CM_ARM_PCI_ADDRESS_MAP_INFO;
+
+ typedef struct CmArmPciInterruptMapInfo {
+ UINT8 PciBus; // {Populated}
+ UINT8 PciDevice; // {Populated}
+ UINT8 PciInterrupt; // {Populated}
+ CM_ARM_GENERIC_INTERRUPT IntcInterrupt; // {Populated}
+ } CM_ARM_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
+ );
+
+#endif // ARM_PCI_CONFIG_SPACE_PARSER_H_