| /** @file | |
| Arm Gic cpu parser. | |
| Copyright (c) 2021 - 2022, 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 | |
| - linux/Documentation/devicetree/bindings/arm/pmu.yaml | |
| **/ | |
| #include <Library/ArmLib.h> | |
| #include <Library/FdtLib.h> | |
| #include "FdtHwInfoParser.h" | |
| #include "CmObjectDescUtility.h" | |
| #include "Arm/Gic/ArmGicCParser.h" | |
| #include "Arm/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,armv8" }, | |
| { "arm,cortex-a15" }, | |
| { "arm,cortex-a7" }, | |
| { "arm,cortex-a57" } | |
| }; | |
| /** COMPATIBILITY_INFO structure for CPU nodes. | |
| */ | |
| STATIC CONST COMPATIBILITY_INFO CpuCompatibleInfo = { | |
| ARRAY_SIZE (CpuCompatibleStr), | |
| CpuCompatibleStr | |
| }; | |
| /** Pmu compatible strings. | |
| Any other "compatible" value is not supported by this module. | |
| */ | |
| STATIC CONST COMPATIBILITY_STR PmuCompatibleStr[] = { | |
| { "arm,armv8-pmuv3" } | |
| }; | |
| /** COMPATIBILITY_INFO structure for the PmuCompatibleStr. | |
| */ | |
| CONST COMPATIBILITY_INFO PmuCompatibleInfo = { | |
| ARRAY_SIZE (PmuCompatibleStr), | |
| PmuCompatibleStr | |
| }; | |
| /** 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 = FdtGetProp (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 = Fdt64ToCpu (*((UINT64 *)Data)); | |
| CheckAffMask |= ARM_CORE_AFF3; | |
| } else { | |
| MpIdr = Fdt32ToCpu (*((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, | |
| OUT UINTN *CpuCount | |
| ) | |
| { | |
| 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 = FdtAddressCells (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; | |
| } | |
| *CpuCount = CpuNodeCount; | |
| // 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 UINTN CpuCount, | |
| IN OUT CM_OBJ_DESCRIPTOR *GicCCmObjDesc | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| INT32 IntCells; | |
| CM_ARM_GICC_INFO *GicCInfo; | |
| CONST UINT8 *Data; | |
| INT32 DataSize; | |
| UINT32 MaintenanceInterrupt; | |
| UINT32 Flags; | |
| 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 = FdtGetProp (Fdt, GicIntcNode, "interrupts", &DataSize); | |
| if ((Data != NULL) && (DataSize == (IntCells * sizeof (UINT32)))) { | |
| MaintenanceInterrupt = FdtGetInterruptId ((CONST UINT32 *)Data); | |
| Flags = DT_IRQ_IS_EDGE_TRIGGERED ( | |
| Fdt32ToCpu (((UINT32 *)Data)[IRQ_FLAGS_OFFSET]) | |
| ) ? | |
| EFI_ACPI_6_3_VGIC_MAINTENANCE_INTERRUPT_MODE_FLAGS : | |
| 0; | |
| for (GicCInfo = (CM_ARM_GICC_INFO *)GicCCmObjDesc->Data; CpuCount--; GicCInfo++) { | |
| GicCInfo->VGICMaintenanceInterrupt = MaintenanceInterrupt; | |
| GicCInfo->Flags |= Flags; | |
| } | |
| 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 = FdtGetProp (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 = Fdt64ToCpu (*(UINT64 *)GicCValue); | |
| GicCInfo[Index].GICH = (GicHValue == NULL) ? 0 : | |
| Fdt64ToCpu (*(UINT64 *)GicHValue); | |
| GicCInfo[Index].GICV = (GicVValue == NULL) ? 0 : | |
| Fdt64ToCpu (*(UINT64 *)GicVValue); | |
| } else { | |
| GicCInfo[Index].PhysicalBaseAddress = Fdt32ToCpu (*(UINT32 *)GicCValue); | |
| GicCInfo[Index].GICH = (GicHValue == NULL) ? 0 : | |
| Fdt32ToCpu (*(UINT32 *)GicHValue); | |
| GicCInfo[Index].GICV = (GicVValue == NULL) ? 0 : | |
| Fdt32ToCpu (*(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 = FdtGetProp (Fdt, Gicv3IntcNode, "#redistributor-regions", &DataSize); | |
| if ((Data != NULL) && (DataSize == sizeof (UINT32))) { | |
| ASSERT (Fdt32ToCpu (*(UINT32 *)Data) > 1); | |
| AdditionalRedistReg = Fdt32ToCpu (*(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 = FdtGetProp (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 : | |
| Fdt64ToCpu (*(UINT64 *)GicCValue); | |
| GicCInfo[Index].GICH = (GicHValue == NULL) ? 0 : | |
| Fdt64ToCpu (*(UINT64 *)GicHValue); | |
| GicCInfo[Index].GICV = (GicVValue == NULL) ? 0 : | |
| Fdt64ToCpu (*(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 : | |
| Fdt32ToCpu (*(UINT32 *)GicCValue); | |
| GicCInfo[Index].GICH = (GicHValue == NULL) ? 0 : | |
| Fdt32ToCpu (*(UINT32 *)GicHValue); | |
| GicCInfo[Index].GICV = (GicVValue == NULL) ? 0 : | |
| Fdt32ToCpu (*(UINT32 *)GicVValue); | |
| } | |
| } | |
| return EFI_SUCCESS; | |
| } | |
| /** Parse a Pmu compatible node, extracting Pmu information. | |
| This function modifies a CM_OBJ_DESCRIPTOR object. | |
| The following CM_ARM_GICC_INFO fields are patched: | |
| - PerformanceInterruptGsiv; | |
| @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 | |
| GicCPmuNodeParser ( | |
| IN CONST VOID *Fdt, | |
| IN INT32 GicIntcNode, | |
| IN OUT CM_OBJ_DESCRIPTOR *GicCCmObjDesc | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| INT32 IntCells; | |
| INT32 PmuNode; | |
| UINT32 PmuNodeCount; | |
| UINT32 PmuIrq; | |
| UINT32 Index; | |
| CM_ARM_GICC_INFO *GicCInfo; | |
| CONST UINT8 *Data; | |
| INT32 DataSize; | |
| if (GicCCmObjDesc == NULL) { | |
| ASSERT (GicCCmObjDesc != NULL); | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| GicCInfo = (CM_ARM_GICC_INFO *)GicCCmObjDesc->Data; | |
| PmuNode = 0; | |
| // Count the number of pmu nodes. | |
| Status = FdtCountCompatNodeInBranch ( | |
| Fdt, | |
| 0, | |
| &PmuCompatibleInfo, | |
| &PmuNodeCount | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| ASSERT_EFI_ERROR (Status); | |
| return Status; | |
| } | |
| if (PmuNodeCount == 0) { | |
| return EFI_NOT_FOUND; | |
| } | |
| Status = FdtGetNextCompatNodeInBranch ( | |
| Fdt, | |
| 0, | |
| &PmuCompatibleInfo, | |
| &PmuNode | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| ASSERT_EFI_ERROR (Status); | |
| if (Status == EFI_NOT_FOUND) { | |
| // Should have found the node. | |
| Status = EFI_ABORTED; | |
| } | |
| } | |
| // Get the number of cells used to encode an interrupt. | |
| Status = FdtGetInterruptCellsInfo (Fdt, GicIntcNode, &IntCells); | |
| if (EFI_ERROR (Status)) { | |
| ASSERT_EFI_ERROR (Status); | |
| return Status; | |
| } | |
| Data = FdtGetProp (Fdt, PmuNode, "interrupts", &DataSize); | |
| if ((Data == NULL) || ((DataSize != (GicCCmObjDesc->Count * IntCells * sizeof (UINT32))) && (DataSize != (IntCells * sizeof (UINT32))))) { | |
| // If error, not 1 interrupt or not 1 interrupt per core. | |
| ASSERT (Data != NULL); | |
| ASSERT (DataSize == (IntCells * sizeof (UINT32)) || DataSize == (GicCCmObjDesc->Count * IntCells * sizeof (UINT32))); | |
| return EFI_ABORTED; | |
| } | |
| if (DataSize == (IntCells * sizeof (UINT32))) { | |
| // One shared PMU IRQ | |
| PmuIrq = FdtGetInterruptId ((CONST UINT32 *)Data); | |
| // Only supports PPI 23 for now. | |
| // According to BSA 1.0 s3.6 PPI assignments, PMU IRQ ID is 23. A non BSA | |
| // compliant system may assign a different IRQ for the PMU, however this | |
| // is not implemented for now. | |
| if (PmuIrq != BSA_PMU_IRQ) { | |
| ASSERT (PmuIrq == BSA_PMU_IRQ); | |
| return EFI_ABORTED; | |
| } | |
| for (Index = 0; Index < GicCCmObjDesc->Count; Index++) { | |
| GicCInfo[Index].PerformanceInterruptGsiv = PmuIrq; | |
| } | |
| } else { | |
| // One PMU IRQ per core | |
| for (Index = 0; Index < GicCCmObjDesc->Count; Index++) { | |
| GicCInfo[Index].PerformanceInterruptGsiv = FdtGetInterruptId ((CONST UINT32 *)Data); | |
| Data += IntCells * sizeof (UINT32); | |
| } | |
| } | |
| 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; // {Populated} | |
| 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; | |
| 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; | |
| UINTN CpuCount; | |
| 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, &CpuCount); | |
| 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, CpuCount, NewCmObjDesc); | |
| if (EFI_ERROR (Status)) { | |
| ASSERT (0); | |
| goto exit_handler; | |
| } | |
| // Parse the Pmu Interrupt. | |
| Status = GicCPmuNodeParser (Fdt, IntcNode, NewCmObjDesc); | |
| if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) { | |
| ASSERT_EFI_ERROR (Status); | |
| 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; | |
| } |