public inbox for devel@edk2.groups.io
 help / color / mirror / Atom feed
From: "PierreGondois" <pierre.gondois@arm.com>
To: devel@edk2.groups.io, Sami.Mujawar@arm.com, Alexei.Fedorov@arm.com
Subject: [PATCH v2 13/15] DynamicTablesPkg: FdtHwInfoParser: Add PCI config parser
Date: Thu, 18 Nov 2021 17:54:17 +0000	[thread overview]
Message-ID: <20211118175419.23610-14-Pierre.Gondois@arm.com> (raw)
In-Reply-To: <20211118175419.23610-1-Pierre.Gondois@arm.com>

From: Pierre Gondois <Pierre.Gondois@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@arm.com>
---

Notes:
    v2:
    - Handle absence of Pci legacy interrupts in Pci parser. [Pierre]

 .../Pci/ArmPciConfigSpaceParser.c             | 797 ++++++++++++++++++
 .../Pci/ArmPciConfigSpaceParser.h             | 142 ++++
 2 files changed, 939 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..95e750cbbe51
--- /dev/null
+++ b/DynamicTablesPkg/Library/FdtHwInfoParserLib/Pci/ArmPciConfigSpaceParser.c
@@ -0,0 +1,797 @@
+/** @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 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


  parent reply	other threads:[~2021-11-18 17:55 UTC|newest]

Thread overview: 16+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-11-18 17:54 [PATCH v2 00/15] Implement a FdtHwInfoParserLib PierreGondois
2021-11-18 17:54 ` [PATCH v2 01/15] DynamicTablesPkg: Definition for HwInfoParser interface PierreGondois
2021-11-18 17:54 ` [PATCH v2 02/15] DynamicTablesPkg: FdtHwInfoParser: CM Object descriptor helper PierreGondois
2021-11-18 17:54 ` [PATCH v2 03/15] DynamicTablesPkg: FdtHwInfoParser: Add FDT utility functions PierreGondois
2021-11-18 17:54 ` [PATCH v2 04/15] DynamicTablesPkg: FdtHwInfoParser: Add Boot Arch parser PierreGondois
2021-11-18 17:54 ` [PATCH v2 05/15] DynamicTablesPkg: FdtHwInfoParser: Generic Timer Parser PierreGondois
2021-11-18 17:54 ` [PATCH v2 06/15] DynamicTablesPkg: FdtHwInfoParser: Add Serial port parser PierreGondois
2021-11-18 17:54 ` [PATCH v2 07/15] DynamicTablesPkg: FdtHwInfoParser: Add GICC parser PierreGondois
2021-11-18 17:54 ` [PATCH v2 08/15] DynamicTablesPkg: FdtHwInfoParser: Add GICD parser PierreGondois
2021-11-18 17:54 ` [PATCH v2 09/15] DynamicTablesPkg: FdtHwInfoParser: Add MSI Frame parser PierreGondois
2021-11-18 17:54 ` [PATCH v2 10/15] DynamicTablesPkg: FdtHwInfoParser: Add ITS parser PierreGondois
2021-11-18 17:54 ` [PATCH v2 11/15] DynamicTablesPkg: FdtHwInfoParser: Add GICR parser PierreGondois
2021-11-18 17:54 ` [PATCH v2 12/15] DynamicTablesPkg: FdtHwInfoParser: Add GIC dispatcher PierreGondois
2021-11-18 17:54 ` PierreGondois [this message]
2021-11-18 17:54 ` [PATCH v2 14/15] DynamicTablesPkg: Add FdtHwInfoParser library PierreGondois
2021-11-18 17:54 ` [PATCH v2 15/15] DynamicTablesPkg: Handle 16550_WITH_GAS id PierreGondois

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-list from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20211118175419.23610-14-Pierre.Gondois@arm.com \
    --to=devel@edk2.groups.io \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox