]> git.proxmox.com Git - mirror_edk2.git/commitdiff
DynamicTablesPkg: FdtHwInfoParser: Add PCI config parser
authorPierre Gondois <Pierre.Gondois@arm.com>
Thu, 9 Dec 2021 09:32:06 +0000 (10:32 +0100)
committermergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
Tue, 14 Dec 2021 16:07:00 +0000 (16:07 +0000)
On platforms that implement PCIe, the PCIe configuration space
information must be described to a standards-based operating
system in the Memory mapped configuration space base address
Description (MCFG) table.

The PCIe information is described in the platform Device Tree,
the bindings for which can be found at:
- linux/Documentation/devicetree/bindings/pci/
  host-generic-pci.yaml

The FdtHwInfoParser implements a PCI configuration space Parser
that parses the platform Device Tree to create
CM_ARM_PCI_CONFIG_SPACE_INFO objects which are encapsulated in a
Configuration Manager descriptor object and added to the platform
information repository.

The platform Configuration Manager can then utilise this
information when generating the MCFG table.

Signed-off-by: Pierre Gondois <Pierre.Gondois@arm.com>
Reviewed-by: Sami Mujawar <sami.mujawar@arm.com>
DynamicTablesPkg/Library/FdtHwInfoParserLib/Pci/ArmPciConfigSpaceParser.c [new file with mode: 0644]
DynamicTablesPkg/Library/FdtHwInfoParserLib/Pci/ArmPciConfigSpaceParser.h [new file with mode: 0644]

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