public inbox for devel@edk2.groups.io
 help / color / mirror / Atom feed
From: "PierreGondois" <pierre.gondois@arm.com>
To: devel@edk2.groups.io
Cc: Sami Mujawar <sami.mujawar@arm.com>,
	Alexei Fedorov <Alexei.Fedorov@arm.com>
Subject: [PATCH v4 07/15] DynamicTablesPkg: FdtHwInfoParser: Add GICC parser
Date: Thu,  9 Dec 2021 10:32:00 +0100	[thread overview]
Message-ID: <20211209093208.1249257-8-Pierre.Gondois@arm.com> (raw)
In-Reply-To: <20211209093208.1249257-1-Pierre.Gondois@arm.com>

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

The GIC CPU Interface (GICC) structure is part of the Multiple
APIC Description Table (MADT) that describes the interrupt model
for the platform. The MADT table is a mandatory table required
for booting a standards-based operating system.

Arm requires the GIC interrupt model, in which the logical
processors are required to have a Processor Device object in
the DSDT, and must convey each processor's GIC information to
the OS using the GICC structure.

The CPU and GIC information is described in the platform Device
Tree, the bindings for which can be found at:
 - linux/Documentation/devicetree/bindings/arm/cpus.yaml
 - linux/Documentation/devicetree/bindings/interrupt-controller/
   arm,gic.yaml
 - linux/Documentation/devicetree/bindings/interrupt-controller/
   arm,gic-v3.yaml

The FdtHwInfoParser implements a GIC CPU Interface Parser that
parses the platform Device Tree to create CM_ARM_GICC_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 MADT and the SSDT CPU
information tables.

Signed-off-by: Pierre Gondois <Pierre.Gondois@arm.com>
---
 .../FdtHwInfoParserLib/Gic/ArmGicCParser.c    | 777 ++++++++++++++++++
 .../FdtHwInfoParserLib/Gic/ArmGicCParser.h    |  67 ++
 2 files changed, 844 insertions(+)
 create mode 100644 DynamicTablesPkg/Library/FdtHwInfoParserLib/Gic/ArmGicCParser.c
 create mode 100644 DynamicTablesPkg/Library/FdtHwInfoParserLib/Gic/ArmGicCParser.h

diff --git a/DynamicTablesPkg/Library/FdtHwInfoParserLib/Gic/ArmGicCParser.c b/DynamicTablesPkg/Library/FdtHwInfoParserLib/Gic/ArmGicCParser.c
new file mode 100644
index 000000000000..b4e6729a4ab2
--- /dev/null
+++ b/DynamicTablesPkg/Library/FdtHwInfoParserLib/Gic/ArmGicCParser.c
@@ -0,0 +1,777 @@
+/** @file
+  Arm Gic cpu parser.
+
+  Copyright (c) 2021, ARM Limited. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+  @par Reference(s):
+  - linux/Documentation/devicetree/bindings/arm/cpus.yaml
+  - linux/Documentation/devicetree/bindings/interrupt-controller/arm,gic.yaml
+  - linux/Documentation/devicetree/bindings/interrupt-controller/arm,gic-v3.yaml
+**/
+
+#include "FdtHwInfoParser.h"
+#include "CmObjectDescUtility.h"
+#include "Gic/ArmGicCParser.h"
+#include "Gic/ArmGicDispatcher.h"
+
+/** List of "compatible" property values for CPU nodes.
+
+  Any other "compatible" value is not supported by this module.
+*/
+STATIC CONST COMPATIBILITY_STR  CpuCompatibleStr[] = {
+  { "arm,arm-v7"     },
+  { "arm,arm-v8"     },
+  { "arm,cortex-a15" },
+  { "arm,cortex-a7"  },
+  { "arm,cortex-a57" }
+};
+
+/** COMPATIBILITY_INFO structure for CPU nodes.
+*/
+STATIC CONST COMPATIBILITY_INFO  CpuCompatibleInfo = {
+  ARRAY_SIZE (CpuCompatibleStr),
+  CpuCompatibleStr
+};
+
+/** Parse a "cpu" node.
+
+  @param [in]  Fdt              Pointer to a Flattened Device Tree (Fdt).
+  @param [in]  CpuNode          Offset of a cpu node.
+  @param [in]  GicVersion       Version of the GIC.
+  @param [in]  AddressCells     Number of address cells used for the reg
+                                property.
+  @param [out] GicCInfo         CM_ARM_GICC_INFO structure to populate.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_ABORTED             An error occurred.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+  @retval EFI_UNSUPPORTED         Unsupported.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+CpuNodeParser (
+  IN  CONST VOID              *Fdt,
+  IN        INT32             CpuNode,
+  IN        UINT32            GicVersion,
+  IN        UINT32            AddressCells,
+  OUT       CM_ARM_GICC_INFO  *GicCInfo
+  )
+{
+  CONST UINT8  *Data;
+  INT32        DataSize;
+  UINT32       ProcUid;
+  UINT64       MpIdr;
+  UINT64       CheckAffMask;
+
+  MpIdr        = 0;
+  CheckAffMask = ARM_CORE_AFF0 | ARM_CORE_AFF1 | ARM_CORE_AFF2;
+
+  if (GicCInfo == NULL) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Data = fdt_getprop (Fdt, CpuNode, "reg", &DataSize);
+  if ((Data == NULL)                  ||
+      ((DataSize != sizeof (UINT32))  &&
+       (DataSize != sizeof (UINT64))))
+  {
+    ASSERT (0);
+    return EFI_ABORTED;
+  }
+
+  /* If cpus node's #address-cells property is set to 2
+     The first reg cell bits [7:0] must be set to
+     bits [39:32] of MPIDR_EL1.
+     The second reg cell bits [23:0] must be set to
+     bits [23:0] of MPIDR_EL1.
+   */
+  if (AddressCells == 2) {
+    MpIdr         = fdt64_to_cpu (*((UINT64 *)Data));
+    CheckAffMask |= ARM_CORE_AFF3;
+  } else {
+    MpIdr = fdt32_to_cpu (*((UINT32 *)Data));
+  }
+
+  if ((MpIdr & ~CheckAffMask) != 0) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // To fit the Affinity [0-3] a 32bits value, place the Aff3 on bits
+  // [31:24] instead of their original place ([39:32]).
+  ProcUid = MpIdr | ((MpIdr & ARM_CORE_AFF3) >> 8);
+
+  /* ACPI 6.3, s5.2.12.14 GIC CPU Interface (GICC) Structure:
+     GIC 's CPU Interface Number. In GICv1/v2 implementations,
+     this value matches the bit index of the associated processor
+     in the GIC distributor's GICD_ITARGETSR register. For
+     GICv3/4 implementations this field must be provided by the
+     platform, if compatibility mode is supported. If it is not supported
+     by the implementation, then this field must be zero.
+
+     Note: We do not support compatibility mode for GicV3
+  */
+  if (GicVersion == 2) {
+    GicCInfo->CPUInterfaceNumber = ProcUid;
+  } else {
+    GicCInfo->CPUInterfaceNumber = 0;
+  }
+
+  GicCInfo->AcpiProcessorUid = ProcUid;
+  GicCInfo->Flags            = EFI_ACPI_6_3_GIC_ENABLED;
+  GicCInfo->MPIDR            = MpIdr;
+
+  return EFI_SUCCESS;
+}
+
+/** Parse a "cpus" node and its children "cpu" nodes.
+
+  Create as many CM_ARM_GICC_INFO structures as "cpu" nodes.
+
+  @param [in]  Fdt              Pointer to a Flattened Device Tree (Fdt).
+  @param [in]  CpusNode         Offset of a cpus node.
+  @param [in]  GicVersion       Version of the GIC.
+  @param [out] NewGicCmObjDesc  If success, CM_OBJ_DESCRIPTOR containing
+                                all the created CM_ARM_GICC_INFO.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_ABORTED             An error occurred.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+  @retval EFI_UNSUPPORTED         Unsupported.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+CpusNodeParser (
+  IN  CONST VOID               *Fdt,
+  IN        INT32              CpusNode,
+  IN        UINT32             GicVersion,
+  OUT       CM_OBJ_DESCRIPTOR  **NewGicCmObjDesc
+  )
+{
+  EFI_STATUS  Status;
+  INT32       CpuNode;
+  UINT32      CpuNodeCount;
+  INT32       AddressCells;
+
+  UINT32            Index;
+  CM_ARM_GICC_INFO  *GicCInfoBuffer;
+  UINT32            GicCInfoBufferSize;
+
+  if (NewGicCmObjDesc == NULL) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  AddressCells = fdt_address_cells (Fdt, CpusNode);
+  if (AddressCells < 0) {
+    ASSERT (0);
+    return EFI_ABORTED;
+  }
+
+  // Count the number of "cpu" nodes under the "cpus" node.
+  Status = FdtCountNamedNodeInBranch (Fdt, CpusNode, "cpu", &CpuNodeCount);
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return Status;
+  }
+
+  if (CpuNodeCount == 0) {
+    ASSERT (0);
+    return EFI_NOT_FOUND;
+  }
+
+  // Allocate memory for CpuNodeCount CM_ARM_GICC_INFO structures.
+  GicCInfoBufferSize = CpuNodeCount * sizeof (CM_ARM_GICC_INFO);
+  GicCInfoBuffer     = AllocateZeroPool (GicCInfoBufferSize);
+  if (GicCInfoBuffer == NULL) {
+    ASSERT (0);
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  CpuNode = CpusNode;
+  for (Index = 0; Index < CpuNodeCount; Index++) {
+    Status = FdtGetNextNamedNodeInBranch (Fdt, CpusNode, "cpu", &CpuNode);
+    if (EFI_ERROR (Status)) {
+      ASSERT (0);
+      if (Status == EFI_NOT_FOUND) {
+        // Should have found the node.
+        Status = EFI_ABORTED;
+      }
+
+      goto exit_handler;
+    }
+
+    // Parse the "cpu" node.
+    if (!FdtNodeIsCompatible (Fdt, CpuNode, &CpuCompatibleInfo)) {
+      ASSERT (0);
+      Status = EFI_UNSUPPORTED;
+      goto exit_handler;
+    }
+
+    Status = CpuNodeParser (
+               Fdt,
+               CpuNode,
+               GicVersion,
+               AddressCells,
+               &GicCInfoBuffer[Index]
+               );
+    if (EFI_ERROR (Status)) {
+      ASSERT (0);
+      goto exit_handler;
+    }
+  } // for
+
+  Status = CreateCmObjDesc (
+             CREATE_CM_ARM_OBJECT_ID (EArmObjGicCInfo),
+             CpuNodeCount,
+             GicCInfoBuffer,
+             GicCInfoBufferSize,
+             NewGicCmObjDesc
+             );
+  ASSERT_EFI_ERROR (Status);
+
+exit_handler:
+  FreePool (GicCInfoBuffer);
+  return Status;
+}
+
+/** Parse a Gic compatible interrupt-controller node,
+    extracting GicC information generic to Gic v2 and v3.
+
+  This function modifies a CM_OBJ_DESCRIPTOR object.
+  The following CM_ARM_GICC_INFO fields are patched:
+    - VGICMaintenanceInterrupt;
+    - Flags;
+
+  @param [in]       Fdt              Pointer to a Flattened Device Tree (Fdt).
+  @param [in]       GicIntcNode      Offset of a Gic compatible
+                                     interrupt-controller node.
+  @param [in, out]  GicCCmObjDesc    The CM_ARM_GICC_INFO to patch.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_ABORTED             An error occurred.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+GicCIntcNodeParser (
+  IN      CONST VOID               *Fdt,
+  IN            INT32              GicIntcNode,
+  IN  OUT       CM_OBJ_DESCRIPTOR  *GicCCmObjDesc
+  )
+{
+  EFI_STATUS        Status;
+  INT32             IntCells;
+  CM_ARM_GICC_INFO  *GicCInfo;
+
+  CONST UINT8  *Data;
+  INT32        DataSize;
+
+  if (GicCCmObjDesc == NULL) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // Get the number of cells used to encode an interrupt.
+  Status = FdtGetInterruptCellsInfo (Fdt, GicIntcNode, &IntCells);
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return Status;
+  }
+
+  // Get the GSIV maintenance interrupt.
+  // According to the DT bindings, this could be the:
+  // "Interrupt source of the parent interrupt controller on secondary GICs"
+  // but it is assumed that only one Gic is available.
+  Data = fdt_getprop (Fdt, GicIntcNode, "interrupts", &DataSize);
+  if ((Data != NULL) && (DataSize == (IntCells * sizeof (UINT32)))) {
+    GicCInfo                           = (CM_ARM_GICC_INFO *)GicCCmObjDesc->Data;
+    GicCInfo->VGICMaintenanceInterrupt =
+      FdtGetInterruptId ((CONST UINT32 *)Data);
+    GicCInfo->Flags = DT_IRQ_IS_EDGE_TRIGGERED (
+                        fdt32_to_cpu (((UINT32 *)Data)[IRQ_FLAGS_OFFSET])
+                        ) ?
+                      EFI_ACPI_6_3_VGIC_MAINTENANCE_INTERRUPT_MODE_FLAGS :
+                      0;
+    return Status;
+  } else if (DataSize < 0) {
+    // This property is optional and was not found. Just return.
+    return Status;
+  }
+
+  // The property exists and its size doesn't match for one interrupt.
+  ASSERT (0);
+  return EFI_ABORTED;
+}
+
+/** Parse a Gic compatible interrupt-controller node,
+    extracting GicCv2 information.
+
+  This function modifies a CM_OBJ_DESCRIPTOR object.
+  The following CM_ARM_GICC_INFO fields are patched:
+    - PhysicalAddress;
+    - GICH;
+    - GICV;
+
+  @param [in]       Fdt              Pointer to a Flattened Device Tree (Fdt).
+  @param [in]       Gicv2IntcNode    Offset of a Gicv2 compatible
+                                     interrupt-controller node.
+  @param [in, out]  GicCCmObjDesc    The CM_ARM_GICC_INFO to patch.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_ABORTED             An error occurred.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+GicCv2IntcNodeParser (
+  IN      CONST VOID               *Fdt,
+  IN            INT32              Gicv2IntcNode,
+  IN  OUT       CM_OBJ_DESCRIPTOR  *GicCCmObjDesc
+  )
+{
+  EFI_STATUS        Status;
+  UINT32            Index;
+  CM_ARM_GICC_INFO  *GicCInfo;
+  INT32             AddressCells;
+  INT32             SizeCells;
+
+  CONST UINT8  *GicCValue;
+  CONST UINT8  *GicVValue;
+  CONST UINT8  *GicHValue;
+
+  CONST UINT8  *Data;
+  INT32        DataSize;
+  UINT32       RegSize;
+  UINT32       RegCount;
+
+  if (GicCCmObjDesc == NULL) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  GicCInfo  = (CM_ARM_GICC_INFO *)GicCCmObjDesc->Data;
+  GicVValue = NULL;
+  GicHValue = NULL;
+
+  // Get the #address-cells and #size-cells property values.
+  Status = FdtGetParentAddressInfo (
+             Fdt,
+             Gicv2IntcNode,
+             &AddressCells,
+             &SizeCells
+             );
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return Status;
+  }
+
+  // Don't support more than 64 bits and less than 32 bits addresses.
+  if ((AddressCells < 1)  ||
+      (AddressCells > 2)  ||
+      (SizeCells < 1)     ||
+      (SizeCells > 2))
+  {
+    ASSERT (0);
+    return EFI_ABORTED;
+  }
+
+  RegSize = (AddressCells + SizeCells) * sizeof (UINT32);
+
+  Data = fdt_getprop (Fdt, Gicv2IntcNode, "reg", &DataSize);
+  if ((Data == NULL)  ||
+      (DataSize < 0)  ||
+      ((DataSize % RegSize) != 0))
+  {
+    // If error or wrong size.
+    ASSERT (0);
+    return EFI_ABORTED;
+  }
+
+  RegCount = DataSize/RegSize;
+
+  switch (RegCount) {
+    case 4:
+    {
+      // GicV is at index 3 in the reg property. GicV is optional.
+      GicVValue = Data + (sizeof (UINT32) *
+                          GET_DT_REG_ADDRESS_OFFSET (3, AddressCells, SizeCells));
+      // fall-through.
+    }
+    case 3:
+    {
+      // GicH is at index 2 in the reg property. GicH is optional.
+      GicHValue = Data + (sizeof (UINT32) *
+                          GET_DT_REG_ADDRESS_OFFSET (2, AddressCells, SizeCells));
+      // fall-through.
+    }
+    case 2:
+    {
+      // GicC is at index 1 in the reg property. GicC is mandatory.
+      GicCValue = Data + (sizeof (UINT32) *
+                          GET_DT_REG_ADDRESS_OFFSET (1, AddressCells, SizeCells));
+      break;
+    }
+    default:
+    {
+      // Not enough or too much information.
+      ASSERT (0);
+      return EFI_ABORTED;
+    }
+  }
+
+  // Patch the relevant fields of the CM_ARM_GICC_INFO objects.
+  for (Index = 0; Index < GicCCmObjDesc->Count; Index++) {
+    if (AddressCells == 2) {
+      GicCInfo[Index].PhysicalBaseAddress = fdt64_to_cpu (*(UINT64 *)GicCValue);
+      GicCInfo[Index].GICH                = (GicHValue == NULL) ? 0 :
+                                            fdt64_to_cpu (*(UINT64 *)GicHValue);
+      GicCInfo[Index].GICV = (GicVValue == NULL) ? 0 :
+                             fdt64_to_cpu (*(UINT64 *)GicVValue);
+    } else {
+      GicCInfo[Index].PhysicalBaseAddress = fdt32_to_cpu (*(UINT32 *)GicCValue);
+      GicCInfo[Index].GICH                = (GicHValue == NULL) ? 0 :
+                                            fdt32_to_cpu (*(UINT32 *)GicHValue);
+      GicCInfo[Index].GICV = (GicVValue == NULL) ? 0 :
+                             fdt32_to_cpu (*(UINT32 *)GicVValue);
+    }
+  } // for
+
+  return EFI_SUCCESS;
+}
+
+/** Parse a Gic compatible interrupt-controller node,
+    extracting GicCv3 information.
+
+  This function modifies a CM_OBJ_DESCRIPTOR object.
+  The following CM_ARM_GICC_INFO fields are patched:
+    - PhysicalAddress;
+    - GICH;
+    - GICV;
+
+  @param [in]       Fdt              Pointer to a Flattened Device Tree (Fdt).
+  @param [in]       Gicv3IntcNode    Offset of a Gicv3 compatible
+                                     interrupt-controller node.
+  @param [in, out]  GicCCmObjDesc    The CM_ARM_GICC_INFO to patch.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_ABORTED             An error occurred.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+GicCv3IntcNodeParser (
+  IN      CONST VOID               *Fdt,
+  IN            INT32              Gicv3IntcNode,
+  IN  OUT       CM_OBJ_DESCRIPTOR  *GicCCmObjDesc
+  )
+{
+  EFI_STATUS        Status;
+  UINT32            Index;
+  CM_ARM_GICC_INFO  *GicCInfo;
+  INT32             AddressCells;
+  INT32             SizeCells;
+  UINT32            AdditionalRedistReg;
+
+  CONST UINT8  *GicCValue;
+  CONST UINT8  *GicVValue;
+  CONST UINT8  *GicHValue;
+
+  CONST UINT8  *Data;
+  INT32        DataSize;
+  UINT32       RegSize;
+  UINT32       RegCount;
+
+  if (GicCCmObjDesc == NULL) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  GicCInfo  = (CM_ARM_GICC_INFO *)GicCCmObjDesc->Data;
+  GicCValue = NULL;
+  GicVValue = NULL;
+  GicHValue = NULL;
+
+  // Get the #address-cells and #size-cells property values.
+  Status = FdtGetParentAddressInfo (
+             Fdt,
+             Gicv3IntcNode,
+             &AddressCells,
+             &SizeCells
+             );
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return Status;
+  }
+
+  // Don't support more than 64 bits and less than 32 bits addresses.
+  if ((AddressCells < 1)  ||
+      (AddressCells > 2)  ||
+      (SizeCells < 1)     ||
+      (SizeCells > 2))
+  {
+    ASSERT (0);
+    return EFI_ABORTED;
+  }
+
+  // The "#redistributor-regions" property is optional.
+  Data = fdt_getprop (Fdt, Gicv3IntcNode, "#redistributor-regions", &DataSize);
+  if ((Data != NULL) && (DataSize == sizeof (UINT32))) {
+    ASSERT (fdt32_to_cpu (*(UINT32 *)Data) > 1);
+    AdditionalRedistReg = fdt32_to_cpu (*(UINT32 *)Data) - 1;
+  } else {
+    AdditionalRedistReg = 0;
+  }
+
+  RegSize = (AddressCells + SizeCells) * sizeof (UINT32);
+
+  /*
+    Ref: linux/blob/master/Documentation/devicetree/bindings/
+         interrupt-controller/arm%2Cgic-v3.yaml
+
+    reg:
+    description: |
+      Specifies base physical address(s) and size of the GIC
+      registers, in the following order:
+      - GIC Distributor interface (GICD)
+      - GIC Redistributors (GICR), one range per redistributor region
+      - GIC CPU interface (GICC)
+      - GIC Hypervisor interface (GICH)
+      - GIC Virtual CPU interface (GICV)
+      GICC, GICH and GICV are optional.
+    minItems: 2
+    maxItems: 4096
+  */
+  Data = fdt_getprop (Fdt, Gicv3IntcNode, "reg", &DataSize);
+  if ((Data == NULL)  ||
+      (DataSize < 0)  ||
+      ((DataSize % RegSize) != 0))
+  {
+    // If error or wrong size.
+    ASSERT (0);
+    return EFI_ABORTED;
+  }
+
+  RegCount = (DataSize / RegSize) - AdditionalRedistReg;
+
+  // The GicD and GicR info is mandatory.
+  switch (RegCount) {
+    case 5:
+    {
+      // GicV is at index 4 in the reg property. GicV is optional.
+      GicVValue = Data + (sizeof (UINT32) *
+                          GET_DT_REG_ADDRESS_OFFSET (
+                            4 + AdditionalRedistReg,
+                            AddressCells,
+                            SizeCells
+                            ));
+      // fall-through.
+    }
+    case 4:
+    {
+      // GicH is at index 3 in the reg property. GicH is optional.
+      GicHValue = Data + (sizeof (UINT32) *
+                          GET_DT_REG_ADDRESS_OFFSET (
+                            3 + AdditionalRedistReg,
+                            AddressCells,
+                            SizeCells
+                            ));
+      // fall-through.
+    }
+    case 3:
+    {
+      // GicC is at index 2 in the reg property. GicC is optional.
+      // Even though GicC is optional, it is made mandatory in this parser.
+      GicCValue = Data + (sizeof (UINT32) *
+                          GET_DT_REG_ADDRESS_OFFSET (
+                            2 + AdditionalRedistReg,
+                            AddressCells,
+                            SizeCells
+                            ));
+      // fall-through
+    }
+    case 2:
+    {
+      // GicR is discribed by the CM_ARM_GIC_REDIST_INFO object.
+      // GicD is described by the CM_ARM_GICD_INFO object.
+      break;
+    }
+    default:
+    {
+      // Not enough or too much information.
+      ASSERT (0);
+      return EFI_ABORTED;
+    }
+  }
+
+  // Patch the relevant fields of the CM_ARM_GICC_INFO objects.
+  if (AddressCells == 2) {
+    for (Index = 0; Index < GicCCmObjDesc->Count; Index++) {
+      // GicR is discribed by the CM_ARM_GIC_REDIST_INFO object.
+      GicCInfo[Index].GICRBaseAddress     = 0;
+      GicCInfo[Index].PhysicalBaseAddress = (GicCValue == NULL) ? 0 :
+                                            fdt64_to_cpu (*(UINT64 *)GicCValue);
+      GicCInfo[Index].GICH = (GicHValue == NULL) ? 0 :
+                             fdt64_to_cpu (*(UINT64 *)GicHValue);
+      GicCInfo[Index].GICV = (GicVValue == NULL) ? 0 :
+                             fdt64_to_cpu (*(UINT64 *)GicVValue);
+    }
+  } else {
+    for (Index = 0; Index < GicCCmObjDesc->Count; Index++) {
+      // GicR is discribed by the CM_ARM_GIC_REDIST_INFO object.
+      GicCInfo[Index].GICRBaseAddress     = 0;
+      GicCInfo[Index].PhysicalBaseAddress = (GicCValue == NULL) ? 0 :
+                                            fdt32_to_cpu (*(UINT32 *)GicCValue);
+      GicCInfo[Index].GICH = (GicHValue == NULL) ? 0 :
+                             fdt32_to_cpu (*(UINT32 *)GicHValue);
+      GicCInfo[Index].GICV = (GicVValue == NULL) ? 0 :
+                             fdt32_to_cpu (*(UINT32 *)GicVValue);
+    }
+  }
+
+  return EFI_SUCCESS;
+}
+
+/** CM_ARM_GICC_INFO parser function.
+
+  This parser expects FdtBranch to be the "\cpus" node node.
+  At most one CmObj is created.
+  The following structure is populated:
+  typedef struct CmArmGicCInfo {
+    UINT32  CPUInterfaceNumber;               // {Populated}
+    UINT32  AcpiProcessorUid;                 // {Populated}
+    UINT32  Flags;                            // {Populated}
+    UINT32  ParkingProtocolVersion;           // {default = 0}
+    UINT32  PerformanceInterruptGsiv;         // {default = 0}
+    UINT64  ParkedAddress;                    // {default = 0}
+    UINT64  PhysicalBaseAddress;              // {Populated}
+    UINT64  GICV;                             // {Populated}
+    UINT64  GICH;                             // {Populated}
+    UINT32  VGICMaintenanceInterrupt;         // {Populated}
+    UINT64  GICRBaseAddress;                  // {default = 0}
+    UINT64  MPIDR;                            // {Populated}
+    UINT8   ProcessorPowerEfficiencyClass;    // {default = 0}
+    UINT16  SpeOverflowInterrupt;             // {default = 0}
+    UINT32  ProximityDomain;                  // {default = 0}
+    UINT32  ClockDomain;                      // {default = 0}
+    UINT32  AffinityFlags;                    // {default = 0}
+  } CM_ARM_GICC_INFO;
+
+  The pmu information can be found in the pmu node. There is no support
+  for now.
+
+  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
+ArmGicCInfoParser (
+  IN  CONST FDT_HW_INFO_PARSER_HANDLE  FdtParserHandle,
+  IN        INT32                      FdtBranch
+  )
+{
+  EFI_STATUS         Status;
+  INT32              IntcNode;
+  UINT32             GicVersion;
+  CM_OBJ_DESCRIPTOR  *NewCmObjDesc;
+  VOID               *Fdt;
+
+  if (FdtParserHandle == NULL) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Fdt          = FdtParserHandle->Fdt;
+  NewCmObjDesc = NULL;
+
+  // The FdtBranch points to the Cpus Node.
+  // Get the interrupt-controller node associated to the "cpus" node.
+  Status = FdtGetIntcParentNode (Fdt, FdtBranch, &IntcNode);
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    if (Status == EFI_NOT_FOUND) {
+      // Should have found the node.
+      Status = EFI_ABORTED;
+    }
+
+    return Status;
+  }
+
+  Status = GetGicVersion (Fdt, IntcNode, &GicVersion);
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return Status;
+  }
+
+  // Parse the "cpus" nodes and its children "cpu" nodes,
+  // and create a CM_OBJ_DESCRIPTOR.
+  Status = CpusNodeParser (Fdt, FdtBranch, GicVersion, &NewCmObjDesc);
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return Status;
+  }
+
+  // Parse the interrupt-controller node according to the Gic version.
+  switch (GicVersion) {
+    case 2:
+    {
+      Status = GicCv2IntcNodeParser (Fdt, IntcNode, NewCmObjDesc);
+      break;
+    }
+    case 3:
+    {
+      Status = GicCv3IntcNodeParser (Fdt, IntcNode, NewCmObjDesc);
+      break;
+    }
+    default:
+    {
+      // Unsupported Gic version.
+      ASSERT (0);
+      Status = EFI_UNSUPPORTED;
+    }
+  }
+
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    goto exit_handler;
+  }
+
+  // Parse the Gic information common to Gic v2 and v3.
+  Status = GicCIntcNodeParser (Fdt, IntcNode, NewCmObjDesc);
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    goto exit_handler;
+  }
+
+  // Add all the CmObjs to the Configuration Manager.
+  Status = AddMultipleCmObj (FdtParserHandle, NewCmObjDesc, 0, NULL);
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    goto exit_handler;
+  }
+
+exit_handler:
+  FreeCmObjDesc (NewCmObjDesc);
+  return Status;
+}
diff --git a/DynamicTablesPkg/Library/FdtHwInfoParserLib/Gic/ArmGicCParser.h b/DynamicTablesPkg/Library/FdtHwInfoParserLib/Gic/ArmGicCParser.h
new file mode 100644
index 000000000000..2a0f966bf0c2
--- /dev/null
+++ b/DynamicTablesPkg/Library/FdtHwInfoParserLib/Gic/ArmGicCParser.h
@@ -0,0 +1,67 @@
+/** @file
+  Arm Gic cpu parser.
+
+  Copyright (c) 2021, ARM Limited. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+  @par Reference(s):
+  - linux/Documentation/devicetree/bindings/interrupt-controller/arm,gic.yaml
+  - linux/Documentation/devicetree/bindings/interrupt-controller/arm,gic-v3.yaml
+**/
+
+#ifndef ARM_GICC_PARSER_H_
+#define ARM_GICC_PARSER_H_
+
+/** CM_ARM_GICC_INFO parser function.
+
+  This parser expects FdtBranch to be the "\cpus" node node.
+  At most one CmObj is created.
+  The following structure is populated:
+  typedef struct CmArmGicCInfo {
+    UINT32  CPUInterfaceNumber;               // {Populated}
+    UINT32  AcpiProcessorUid;                 // {Populated}
+    UINT32  Flags;                            // {Populated}
+    UINT32  ParkingProtocolVersion;           // {default = 0}
+    UINT32  PerformanceInterruptGsiv;         // {default = 0}
+    UINT64  ParkedAddress;                    // {default = 0}
+    UINT64  PhysicalBaseAddress;              // {Populated}
+    UINT64  GICV;                             // {Populated}
+    UINT64  GICH;                             // {Populated}
+    UINT32  VGICMaintenanceInterrupt;         // {Populated}
+    UINT64  GICRBaseAddress;                  // {default = 0}
+    UINT64  MPIDR;                            // {Populated}
+    UINT8   ProcessorPowerEfficiencyClass;    // {default = 0}
+    UINT16  SpeOverflowInterrupt;             // {default = 0}
+    UINT32  ProximityDomain;                  // {default = 0}
+    UINT32  ClockDomain;                      // {default = 0}
+    UINT32  AffinityFlags;                    // {default = 0}
+  } CM_ARM_GICC_INFO;
+
+  The pmu information can be found in the pmu node. There is no support
+  for now.
+
+  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
+ArmGicCInfoParser (
+  IN  CONST FDT_HW_INFO_PARSER_HANDLE  FdtParserHandle,
+  IN        INT32                      FdtBranch
+  );
+
+#endif // ARM_GICC_PARSER_H_
-- 
2.25.1


  parent reply	other threads:[~2021-12-09  9:32 UTC|newest]

Thread overview: 18+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-12-09  9:31 [PATCH v4 00/15] Implement a FdtHwInfoParserLib PierreGondois
2021-12-09  9:31 ` [PATCH v4 01/15] DynamicTablesPkg: Definition for HwInfoParser interface PierreGondois
2021-12-14 15:10   ` [edk2-devel] " Joey Gouly
2021-12-09  9:31 ` [PATCH v4 02/15] DynamicTablesPkg: FdtHwInfoParser: CM Object descriptor helper PierreGondois
2021-12-09  9:31 ` [PATCH v4 03/15] DynamicTablesPkg: FdtHwInfoParser: Add FDT utility functions PierreGondois
2021-12-09  9:31 ` [PATCH v4 04/15] DynamicTablesPkg: FdtHwInfoParser: Add Boot Arch parser PierreGondois
2021-12-09  9:31 ` [PATCH v4 05/15] DynamicTablesPkg: FdtHwInfoParser: Generic Timer Parser PierreGondois
2021-12-09  9:31 ` [PATCH v4 06/15] DynamicTablesPkg: FdtHwInfoParser: Add Serial port parser PierreGondois
2021-12-09  9:32 ` PierreGondois [this message]
2021-12-09  9:32 ` [PATCH v4 08/15] DynamicTablesPkg: FdtHwInfoParser: Add GICD parser PierreGondois
2021-12-09  9:32 ` [PATCH v4 09/15] DynamicTablesPkg: FdtHwInfoParser: Add MSI Frame parser PierreGondois
2021-12-09  9:32 ` [PATCH v4 10/15] DynamicTablesPkg: FdtHwInfoParser: Add ITS parser PierreGondois
2021-12-09  9:32 ` [PATCH v4 11/15] DynamicTablesPkg: FdtHwInfoParser: Add GICR parser PierreGondois
2021-12-09  9:32 ` [PATCH v4 12/15] DynamicTablesPkg: FdtHwInfoParser: Add GIC dispatcher PierreGondois
2021-12-09  9:32 ` [PATCH v4 13/15] DynamicTablesPkg: FdtHwInfoParser: Add PCI config parser PierreGondois
2021-12-09  9:32 ` [PATCH v4 14/15] DynamicTablesPkg: Add FdtHwInfoParser library PierreGondois
2021-12-09  9:32 ` [PATCH v4 15/15] DynamicTablesPkg: Handle 16550_WITH_GAS id PierreGondois
2021-12-14 15:59 ` [PATCH v4 00/15] Implement a FdtHwInfoParserLib Sami Mujawar

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=20211209093208.1249257-8-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