From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by mx.groups.io with SMTP id smtpd.web08.9306.1639042343525138926 for ; Thu, 09 Dec 2021 01:32:23 -0800 Authentication-Results: mx.groups.io; dkim=missing; spf=pass (domain: arm.com, ip: 217.140.110.172, mailfrom: pierre.gondois@arm.com) Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 29C5611FB; Thu, 9 Dec 2021 01:32:23 -0800 (PST) Received: from e126645.nice.arm.com (unknown [10.34.129.54]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPA id 1C3AA3F73B; Thu, 9 Dec 2021 01:32:21 -0800 (PST) From: "PierreGondois" To: devel@edk2.groups.io Cc: Sami Mujawar , Alexei Fedorov Subject: [PATCH v4 07/15] DynamicTablesPkg: FdtHwInfoParser: Add GICC parser Date: Thu, 9 Dec 2021 10:32:00 +0100 Message-Id: <20211209093208.1249257-8-Pierre.Gondois@arm.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20211209093208.1249257-1-Pierre.Gondois@arm.com> References: <20211209093208.1249257-1-Pierre.Gondois@arm.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable From: Pierre Gondois 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 --- .../FdtHwInfoParserLib/Gic/ArmGicCParser.c | 777 ++++++++++++++++++ .../FdtHwInfoParserLib/Gic/ArmGicCParser.h | 67 ++ 2 files changed, 844 insertions(+) create mode 100644 DynamicTablesPkg/Library/FdtHwInfoParserLib/Gic/ArmGi= cCParser.c create mode 100644 DynamicTablesPkg/Library/FdtHwInfoParserLib/Gic/ArmGi= cCParser.h diff --git a/DynamicTablesPkg/Library/FdtHwInfoParserLib/Gic/ArmGicCParse= r.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.
+ 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[] =3D { + { "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 =3D { + 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 =3D 0; + CheckAffMask =3D ARM_CORE_AFF0 | ARM_CORE_AFF1 | ARM_CORE_AFF2; + + if (GicCInfo =3D=3D NULL) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + Data =3D fdt_getprop (Fdt, CpuNode, "reg", &DataSize); + if ((Data =3D=3D NULL) || + ((DataSize !=3D sizeof (UINT32)) && + (DataSize !=3D 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 =3D=3D 2) { + MpIdr =3D fdt64_to_cpu (*((UINT64 *)Data)); + CheckAffMask |=3D ARM_CORE_AFF3; + } else { + MpIdr =3D fdt32_to_cpu (*((UINT32 *)Data)); + } + + if ((MpIdr & ~CheckAffMask) !=3D 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 =3D 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 supporte= d + by the implementation, then this field must be zero. + + Note: We do not support compatibility mode for GicV3 + */ + if (GicVersion =3D=3D 2) { + GicCInfo->CPUInterfaceNumber =3D ProcUid; + } else { + GicCInfo->CPUInterfaceNumber =3D 0; + } + + GicCInfo->AcpiProcessorUid =3D ProcUid; + GicCInfo->Flags =3D EFI_ACPI_6_3_GIC_ENABLED; + GicCInfo->MPIDR =3D 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 =3D=3D NULL) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + AddressCells =3D fdt_address_cells (Fdt, CpusNode); + if (AddressCells < 0) { + ASSERT (0); + return EFI_ABORTED; + } + + // Count the number of "cpu" nodes under the "cpus" node. + Status =3D FdtCountNamedNodeInBranch (Fdt, CpusNode, "cpu", &CpuNodeCo= unt); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + if (CpuNodeCount =3D=3D 0) { + ASSERT (0); + return EFI_NOT_FOUND; + } + + // Allocate memory for CpuNodeCount CM_ARM_GICC_INFO structures. + GicCInfoBufferSize =3D CpuNodeCount * sizeof (CM_ARM_GICC_INFO); + GicCInfoBuffer =3D AllocateZeroPool (GicCInfoBufferSize); + if (GicCInfoBuffer =3D=3D NULL) { + ASSERT (0); + return EFI_OUT_OF_RESOURCES; + } + + CpuNode =3D CpusNode; + for (Index =3D 0; Index < CpuNodeCount; Index++) { + Status =3D FdtGetNextNamedNodeInBranch (Fdt, CpusNode, "cpu", &CpuNo= de); + if (EFI_ERROR (Status)) { + ASSERT (0); + if (Status =3D=3D EFI_NOT_FOUND) { + // Should have found the node. + Status =3D EFI_ABORTED; + } + + goto exit_handler; + } + + // Parse the "cpu" node. + if (!FdtNodeIsCompatible (Fdt, CpuNode, &CpuCompatibleInfo)) { + ASSERT (0); + Status =3D EFI_UNSUPPORTED; + goto exit_handler; + } + + Status =3D CpuNodeParser ( + Fdt, + CpuNode, + GicVersion, + AddressCells, + &GicCInfoBuffer[Index] + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + goto exit_handler; + } + } // for + + Status =3D 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 =3D=3D NULL) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // Get the number of cells used to encode an interrupt. + Status =3D 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 G= ICs" + // but it is assumed that only one Gic is available. + Data =3D fdt_getprop (Fdt, GicIntcNode, "interrupts", &DataSize); + if ((Data !=3D NULL) && (DataSize =3D=3D (IntCells * sizeof (UINT32)))= ) { + GicCInfo =3D (CM_ARM_GICC_INFO *)GicCCmObj= Desc->Data; + GicCInfo->VGICMaintenanceInterrupt =3D + FdtGetInterruptId ((CONST UINT32 *)Data); + GicCInfo->Flags =3D 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 =3D=3D NULL) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + GicCInfo =3D (CM_ARM_GICC_INFO *)GicCCmObjDesc->Data; + GicVValue =3D NULL; + GicHValue =3D NULL; + + // Get the #address-cells and #size-cells property values. + Status =3D 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 =3D (AddressCells + SizeCells) * sizeof (UINT32); + + Data =3D fdt_getprop (Fdt, Gicv2IntcNode, "reg", &DataSize); + if ((Data =3D=3D NULL) || + (DataSize < 0) || + ((DataSize % RegSize) !=3D 0)) + { + // If error or wrong size. + ASSERT (0); + return EFI_ABORTED; + } + + RegCount =3D DataSize/RegSize; + + switch (RegCount) { + case 4: + { + // GicV is at index 3 in the reg property. GicV is optional. + GicVValue =3D Data + (sizeof (UINT32) * + GET_DT_REG_ADDRESS_OFFSET (3, AddressCells, Si= zeCells)); + // fall-through. + } + case 3: + { + // GicH is at index 2 in the reg property. GicH is optional. + GicHValue =3D Data + (sizeof (UINT32) * + GET_DT_REG_ADDRESS_OFFSET (2, AddressCells, Si= zeCells)); + // fall-through. + } + case 2: + { + // GicC is at index 1 in the reg property. GicC is mandatory. + GicCValue =3D Data + (sizeof (UINT32) * + GET_DT_REG_ADDRESS_OFFSET (1, AddressCells, Si= zeCells)); + 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 =3D 0; Index < GicCCmObjDesc->Count; Index++) { + if (AddressCells =3D=3D 2) { + GicCInfo[Index].PhysicalBaseAddress =3D fdt64_to_cpu (*(UINT64 *)G= icCValue); + GicCInfo[Index].GICH =3D (GicHValue =3D=3D NULL) ? = 0 : + fdt64_to_cpu (*(UINT64 *)Gic= HValue); + GicCInfo[Index].GICV =3D (GicVValue =3D=3D NULL) ? 0 : + fdt64_to_cpu (*(UINT64 *)GicVValue); + } else { + GicCInfo[Index].PhysicalBaseAddress =3D fdt32_to_cpu (*(UINT32 *)G= icCValue); + GicCInfo[Index].GICH =3D (GicHValue =3D=3D NULL) ? = 0 : + fdt32_to_cpu (*(UINT32 *)Gic= HValue); + GicCInfo[Index].GICV =3D (GicVValue =3D=3D 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 =3D=3D NULL) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + GicCInfo =3D (CM_ARM_GICC_INFO *)GicCCmObjDesc->Data; + GicCValue =3D NULL; + GicVValue =3D NULL; + GicHValue =3D NULL; + + // Get the #address-cells and #size-cells property values. + Status =3D 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 =3D fdt_getprop (Fdt, Gicv3IntcNode, "#redistributor-regions", &D= ataSize); + if ((Data !=3D NULL) && (DataSize =3D=3D sizeof (UINT32))) { + ASSERT (fdt32_to_cpu (*(UINT32 *)Data) > 1); + AdditionalRedistReg =3D fdt32_to_cpu (*(UINT32 *)Data) - 1; + } else { + AdditionalRedistReg =3D 0; + } + + RegSize =3D (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 =3D fdt_getprop (Fdt, Gicv3IntcNode, "reg", &DataSize); + if ((Data =3D=3D NULL) || + (DataSize < 0) || + ((DataSize % RegSize) !=3D 0)) + { + // If error or wrong size. + ASSERT (0); + return EFI_ABORTED; + } + + RegCount =3D (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 =3D 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 =3D 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 pars= er. + GicCValue =3D 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 =3D=3D 2) { + for (Index =3D 0; Index < GicCCmObjDesc->Count; Index++) { + // GicR is discribed by the CM_ARM_GIC_REDIST_INFO object. + GicCInfo[Index].GICRBaseAddress =3D 0; + GicCInfo[Index].PhysicalBaseAddress =3D (GicCValue =3D=3D NULL) ? = 0 : + fdt64_to_cpu (*(UINT64 *)Gic= CValue); + GicCInfo[Index].GICH =3D (GicHValue =3D=3D NULL) ? 0 : + fdt64_to_cpu (*(UINT64 *)GicHValue); + GicCInfo[Index].GICV =3D (GicVValue =3D=3D NULL) ? 0 : + fdt64_to_cpu (*(UINT64 *)GicVValue); + } + } else { + for (Index =3D 0; Index < GicCCmObjDesc->Count; Index++) { + // GicR is discribed by the CM_ARM_GIC_REDIST_INFO object. + GicCInfo[Index].GICRBaseAddress =3D 0; + GicCInfo[Index].PhysicalBaseAddress =3D (GicCValue =3D=3D NULL) ? = 0 : + fdt32_to_cpu (*(UINT32 *)Gic= CValue); + GicCInfo[Index].GICH =3D (GicHValue =3D=3D NULL) ? 0 : + fdt32_to_cpu (*(UINT32 *)GicHValue); + GicCInfo[Index].GICV =3D (GicVValue =3D=3D 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 =3D 0} + UINT32 PerformanceInterruptGsiv; // {default =3D 0} + UINT64 ParkedAddress; // {default =3D 0} + UINT64 PhysicalBaseAddress; // {Populated} + UINT64 GICV; // {Populated} + UINT64 GICH; // {Populated} + UINT32 VGICMaintenanceInterrupt; // {Populated} + UINT64 GICRBaseAddress; // {default =3D 0} + UINT64 MPIDR; // {Populated} + UINT8 ProcessorPowerEfficiencyClass; // {default =3D 0} + UINT16 SpeOverflowInterrupt; // {default =3D 0} + UINT32 ProximityDomain; // {default =3D 0} + UINT32 ClockDomain; // {default =3D 0} + UINT32 AffinityFlags; // {default =3D 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 =3D=3D NULL) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + Fdt =3D FdtParserHandle->Fdt; + NewCmObjDesc =3D NULL; + + // The FdtBranch points to the Cpus Node. + // Get the interrupt-controller node associated to the "cpus" node. + Status =3D FdtGetIntcParentNode (Fdt, FdtBranch, &IntcNode); + if (EFI_ERROR (Status)) { + ASSERT (0); + if (Status =3D=3D EFI_NOT_FOUND) { + // Should have found the node. + Status =3D EFI_ABORTED; + } + + return Status; + } + + Status =3D 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 =3D 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 =3D GicCv2IntcNodeParser (Fdt, IntcNode, NewCmObjDesc); + break; + } + case 3: + { + Status =3D GicCv3IntcNodeParser (Fdt, IntcNode, NewCmObjDesc); + break; + } + default: + { + // Unsupported Gic version. + ASSERT (0); + Status =3D EFI_UNSUPPORTED; + } + } + + if (EFI_ERROR (Status)) { + ASSERT (0); + goto exit_handler; + } + + // Parse the Gic information common to Gic v2 and v3. + Status =3D GicCIntcNodeParser (Fdt, IntcNode, NewCmObjDesc); + if (EFI_ERROR (Status)) { + ASSERT (0); + goto exit_handler; + } + + // Add all the CmObjs to the Configuration Manager. + Status =3D 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/ArmGicCParse= r.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.
+ 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 =3D 0} + UINT32 PerformanceInterruptGsiv; // {default =3D 0} + UINT64 ParkedAddress; // {default =3D 0} + UINT64 PhysicalBaseAddress; // {Populated} + UINT64 GICV; // {Populated} + UINT64 GICH; // {Populated} + UINT32 VGICMaintenanceInterrupt; // {Populated} + UINT64 GICRBaseAddress; // {default =3D 0} + UINT64 MPIDR; // {Populated} + UINT8 ProcessorPowerEfficiencyClass; // {default =3D 0} + UINT16 SpeOverflowInterrupt; // {default =3D 0} + UINT32 ProximityDomain; // {default =3D 0} + UINT32 ClockDomain; // {default =3D 0} + UINT32 AffinityFlags; // {default =3D 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_ --=20 2.25.1