From: Pierre Gondois <pierre.gond...@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.gond...@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



-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#84592): https://edk2.groups.io/g/devel/message/84592
Mute This Topic: https://groups.io/mt/87608910/21656
Group Owner: devel+ow...@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [arch...@mail-archive.com]
-=-=-=-=-=-=-=-=-=-=-=-


Reply via email to