The GIC Redistributor (GICR) structure is part of the Multiple APIC Description Table (MADT) that enables the discovery of GIC Redistributor base addresses by providing the Physical Base Address of a page range containing the GIC Redistributors. More than one GICR Structure may be presented in the MADT. The GICR structures should only be used when describing GIC version 3 or higher. The GIC Redistributor information is described in the platform Device Tree, the bindings for which can be found at: - linux/Documentation/devicetree/bindings/interrupt-controller/ arm,gic-v3.yaml The FdtHwInfoParser implements a GIC Redistributor Parser that parses the platform Device Tree to create CM_ARM_GIC_REDIST_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 table. Signed-off-by: Pierre Gondois <Pierre.Gondois@arm.com> Reviewed-by: Sami Mujawar <sami.mujawar@arm.com>
		
			
				
	
	
		
			239 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			239 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /** @file
 | |
|   Arm Gic Redistributor 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-v3.yaml
 | |
| **/
 | |
| 
 | |
| #include "CmObjectDescUtility.h"
 | |
| #include "FdtHwInfoParser.h"
 | |
| #include "Gic/ArmGicDispatcher.h"
 | |
| #include "Gic/ArmGicRParser.h"
 | |
| 
 | |
| /** Parse a Gic compatible interrupt-controller node,
 | |
|     extracting GicR information.
 | |
| 
 | |
|   This parser is valid for Gic v3 and higher.
 | |
| 
 | |
|   @param [in]  FdtParserHandle  A handle to the parser instance.
 | |
|   @param [in]  GicIntcNode      Offset of a Gic compatible
 | |
|                                 interrupt-controller node.
 | |
| 
 | |
|   @retval EFI_SUCCESS             The function completed successfully.
 | |
|   @retval EFI_ABORTED             An error occurred.
 | |
|   @retval EFI_INVALID_PARAMETER   Invalid parameter.
 | |
| **/
 | |
| STATIC
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| GicRIntcNodeParser (
 | |
|   IN  CONST FDT_HW_INFO_PARSER_HANDLE  FdtParserHandle,
 | |
|   IN        INT32                      GicIntcNode
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS              Status;
 | |
|   UINT32                  Index;
 | |
|   UINT32                  RedistReg;
 | |
|   UINT32                  RegSize;
 | |
|   INT32                   AddressCells;
 | |
|   INT32                   SizeCells;
 | |
|   CONST UINT8             *Data;
 | |
|   INT32                   DataSize;
 | |
|   CM_ARM_GIC_REDIST_INFO  GicRInfo;
 | |
|   VOID                    *Fdt;
 | |
| 
 | |
|   if (FdtParserHandle == NULL) {
 | |
|     ASSERT (0);
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   Fdt = FdtParserHandle->Fdt;
 | |
| 
 | |
|   Status = FdtGetParentAddressInfo (
 | |
|              Fdt,
 | |
|              GicIntcNode,
 | |
|              &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.
 | |
|   // It indicates the number of GicR.
 | |
|   Data = fdt_getprop (Fdt, GicIntcNode, "#redistributor-regions", &DataSize);
 | |
|   if ((Data != NULL) && (DataSize == sizeof (UINT32))) {
 | |
|     // If available, must be on one cell.
 | |
|     RedistReg = fdt32_to_cpu (*(UINT32 *)Data);
 | |
|   } else {
 | |
|     // The DT Spec says GicR is mandatory so we will
 | |
|     // always have one.
 | |
|     RedistReg = 1;
 | |
|   }
 | |
| 
 | |
|   /*
 | |
|     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
 | |
| 
 | |
|     Example:
 | |
|       interrupt-controller@2c010000 {
 | |
|         compatible = "arm,gic-v3";
 | |
|         #interrupt-cells = <4>;
 | |
|         #address-cells = <1>;
 | |
|         #size-cells = <1>;
 | |
|         ranges;
 | |
|         interrupt-controller;
 | |
|         redistributor-stride = <0x0 0x40000>;  // 256kB stride
 | |
|         #redistributor-regions = <2>;
 | |
|         reg = <0x2c010000 0x10000>,  // GICD
 | |
|               <0x2d000000 0x800000>,  // GICR 1: CPUs 0-31
 | |
|               <0x2e000000 0x800000>,  // GICR 2: CPUs 32-63
 | |
|               <0x2c040000 0x2000>,  // GICC
 | |
|               <0x2c060000 0x2000>,  // GICH
 | |
|               <0x2c080000 0x2000>;  // GICV
 | |
|         interrupts = <1 9 4>;
 | |
|         ...
 | |
|       }
 | |
|   */
 | |
|   RegSize = (AddressCells + SizeCells) * sizeof (UINT32);
 | |
|   Data    = fdt_getprop (Fdt, GicIntcNode, "reg", &DataSize);
 | |
|   if ((Data == NULL)  ||
 | |
|       (DataSize < 0)  ||
 | |
|       ((DataSize % RegSize) != 0))
 | |
|   {
 | |
|     // If error or wrong size.
 | |
|     ASSERT (0);
 | |
|     return EFI_ABORTED;
 | |
|   }
 | |
| 
 | |
|   Data += GET_DT_REG_ADDRESS_OFFSET (1, AddressCells, SizeCells)
 | |
|           * sizeof (UINT32);
 | |
|   for (Index = 0; Index < RedistReg; Index++) {
 | |
|     ZeroMem (&GicRInfo, sizeof (CM_ARM_GIC_REDIST_INFO));
 | |
| 
 | |
|     if (AddressCells == 2) {
 | |
|       GicRInfo.DiscoveryRangeBaseAddress = fdt64_to_cpu (*(UINT64 *)Data);
 | |
|     } else {
 | |
|       GicRInfo.DiscoveryRangeBaseAddress = fdt32_to_cpu (*(UINT32 *)Data);
 | |
|     }
 | |
| 
 | |
|     Data += sizeof (UINT32) * AddressCells;
 | |
| 
 | |
|     if (SizeCells == 2) {
 | |
|       GicRInfo.DiscoveryRangeLength = (UINT32)fdt64_to_cpu (*(UINT64 *)Data);
 | |
|     } else {
 | |
|       GicRInfo.DiscoveryRangeLength = fdt32_to_cpu (*(UINT32 *)Data);
 | |
|     }
 | |
| 
 | |
|     // Add the CmObj to the Configuration Manager.
 | |
|     Status = AddSingleCmObj (
 | |
|                FdtParserHandle,
 | |
|                CREATE_CM_ARM_OBJECT_ID (EArmObjGicRedistributorInfo),
 | |
|                &GicRInfo,
 | |
|                sizeof (CM_ARM_GIC_REDIST_INFO),
 | |
|                NULL
 | |
|                );
 | |
|     if (EFI_ERROR (Status)) {
 | |
|       ASSERT (0);
 | |
|       return Status;
 | |
|     }
 | |
| 
 | |
|     Data += sizeof (UINT32) * SizeCells;
 | |
|   } // for
 | |
| 
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| /** CM_ARM_GIC_REDIST_INFO parser function.
 | |
| 
 | |
|   This parser expects FdtBranch to be a Gic interrupt-controller node.
 | |
|   Gic version must be v3 or higher.
 | |
|   typedef struct CmArmGicRedistInfo {
 | |
|     UINT64  DiscoveryRangeBaseAddress;        // {Populated}
 | |
|     UINT32  DiscoveryRangeLength;             // {Populated}
 | |
|   } CM_ARM_GIC_REDIST_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
 | |
| ArmGicRInfoParser (
 | |
|   IN  CONST FDT_HW_INFO_PARSER_HANDLE  FdtParserHandle,
 | |
|   IN        INT32                      FdtBranch
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS  Status;
 | |
|   UINT32      GicVersion;
 | |
|   VOID        *Fdt;
 | |
| 
 | |
|   if (FdtParserHandle == NULL) {
 | |
|     ASSERT (0);
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   Fdt = FdtParserHandle->Fdt;
 | |
| 
 | |
|   if (!FdtNodeHasProperty (Fdt, FdtBranch, "interrupt-controller")) {
 | |
|     ASSERT (0);
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   // Get the Gic version of the interrupt-controller.
 | |
|   Status = GetGicVersion (Fdt, FdtBranch, &GicVersion);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     ASSERT (0);
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   if (GicVersion < 3) {
 | |
|     ASSERT (0);
 | |
|     return EFI_UNSUPPORTED;
 | |
|   }
 | |
| 
 | |
|   Status = GicRIntcNodeParser (FdtParserHandle, FdtBranch);
 | |
|   ASSERT_EFI_ERROR (Status);
 | |
|   return Status;
 | |
| }
 |