DynamicTablesPkg: FdtHwInfoParser: Add GICC parser

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>
Reviewed-by: Sami Mujawar <sami.mujawar@arm.com>
This commit is contained in:
Pierre Gondois 2021-12-09 10:32:00 +01:00 committed by mergify[bot]
parent 51941f7558
commit e366a41ef0
2 changed files with 844 additions and 0 deletions

View File

@ -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;
}

View File

@ -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_