[edk2-devel] [PATCH v1 13/14] DynamicTablesPkg: FdtHwInfoParser: Add PCI config parser

PierreGondois pierre.gondois at arm.com
Wed Jun 23 12:38:27 UTC 2021


From: Pierre Gondois <Pierre.Gondois at arm.com>

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 at arm.com>
Signed-off-by: Sami Mujawar <sami.mujawar at arm.com>
---
 .../Pci/ArmPciConfigSpaceParser.c             | 799 ++++++++++++++++++
 .../Pci/ArmPciConfigSpaceParser.h             | 142 ++++
 2 files changed, 941 insertions(+)
 create mode 100644 DynamicTablesPkg/Library/FdtHwInfoParserLib/Pci/ArmPciConfigSpaceParser.c
 create mode 100644 DynamicTablesPkg/Library/FdtHwInfoParserLib/Pci/ArmPciConfigSpaceParser.h

diff --git a/DynamicTablesPkg/Library/FdtHwInfoParserLib/Pci/ArmPciConfigSpaceParser.c b/DynamicTablesPkg/Library/FdtHwInfoParserLib/Pci/ArmPciConfigSpaceParser.c
new file mode 100644
index 000000000000..2b1b0749b903
--- /dev/null
+++ b/DynamicTablesPkg/Library/FdtHwInfoParserLib/Pci/ArmPciConfigSpaceParser.c
@@ -0,0 +1,799 @@
+/** @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_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)) {
+    // We cannot check the # for now
+    ASSERT (0);
+    return EFI_ABORTED;
+  } else if (DataSize == 0) {
+    // No device described, so no interrupt-mapping.
+    DEBUG ((
+      DEBUG_WARN,
+      "Fdt parser: No Pci device described in the device tree.\n"
+      ));
+    return EFI_SUCCESS;
+  }
+
+  // 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)) {
+    ASSERT (0);
+  }
+
+  return Status;
+}
+
+/** 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 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
+             );
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+  }
+  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 000000000000..b260c53a82ab
--- /dev/null
+++ b/DynamicTablesPkg/Library/FdtHwInfoParserLib/Pci/ArmPciConfigSpaceParser.h
@@ -0,0 +1,142 @@
+/** @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_
-- 
2.17.1



-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#76980): https://edk2.groups.io/g/devel/message/76980
Mute This Topic: https://groups.io/mt/83736648/1813853
Group Owner: devel+owner at edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [edk2-devel-archive at redhat.com]
-=-=-=-=-=-=-=-=-=-=-=-





More information about the edk2-devel-archive mailing list