/** @file RISC-V Interrupt Controllers parser. Copyright (c) 2024, Ventana Micro Systems Inc. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent @par Reference(s): - linux/Documentation/devicetree/bindings/riscv/cpus.yaml **/ #include #include #include #include #include "FdtHwInfoParser.h" #include "CmObjectDescUtility.h" #include "RiscV/Intc/RiscVIntcParser.h" #define ACPI_BUILD_EXT_INTC_ID(PlicAplicId, CtxIdcId) \ ((PlicAplicId << 24) | (CtxIdcId)) /** List of "compatible" property values for CPU nodes. Any other "compatible" value is not supported by this module. */ STATIC CONST COMPATIBILITY_STR CpuCompatibleStr[] = { { "riscv" } }; /** COMPATIBILITY_INFO structure for CPU nodes. */ STATIC CONST COMPATIBILITY_INFO CpuCompatibleInfo = { ARRAY_SIZE (CpuCompatibleStr), CpuCompatibleStr }; /** List of "compatible" property values for IMSIC node. Any other "compatible" value is not supported by this module. */ STATIC CONST COMPATIBILITY_STR ImsicCompatibleStr[] = { { "riscv,imsics" } }; /** COMPATIBILITY_INFO structure for IMSIC node. */ STATIC CONST COMPATIBILITY_INFO ImsicCompatibleInfo = { ARRAY_SIZE (ImsicCompatibleStr), ImsicCompatibleStr }; /** List of "compatible" property values for APLIC node. Any other "compatible" value is not supported by this module. */ STATIC CONST COMPATIBILITY_STR AplicCompatibleStr[] = { { "riscv,aplic" } }; /** COMPATIBILITY_INFO structure for APLIC node. */ STATIC CONST COMPATIBILITY_INFO AplicCompatibleInfo = { ARRAY_SIZE (AplicCompatibleStr), AplicCompatibleStr }; /** List of "compatible" property values for PLIC node. Any other "compatible" value is not supported by this module. */ STATIC CONST COMPATIBILITY_STR PlicCompatibleStr[] = { { "riscv,plic0" } }; /** COMPATIBILITY_INFO structure for IMSIC node. */ STATIC CONST COMPATIBILITY_INFO PlicCompatibleInfo = { ARRAY_SIZE (PlicCompatibleStr), PlicCompatibleStr }; /** Create MMU info structure @param [in] FdtParserHandle A handle to the parser instance. @param [in] CpuNode cpu node. **/ STATIC EFI_STATUS EFIAPI MmuInfoParser ( IN CONST FDT_HW_INFO_PARSER_HANDLE FdtParserHandle, IN INT32 CpuNode, OUT CM_OBJECT_TOKEN *Token ) { CM_RISCV_MMU_NODE MmuInfo; CONST VOID *Prop; EFI_STATUS Status; INT32 PropSize; VOID *Fdt; Fdt = FdtParserHandle->Fdt; ZeroMem (&MmuInfo, sizeof (CM_RISCV_MMU_NODE)); Prop = FdtGetProp (Fdt, CpuNode, "mmu-type", &PropSize); if (!Prop) { return EFI_NOT_FOUND; } if (AsciiStrnCmp (Prop, "riscv,sv39", 10) == 0) { MmuInfo.MmuType = EFI_ACPI_6_6_RHCT_MMU_TYPE_SV39; } else if (AsciiStrnCmp (Prop, "riscv,sv48", 10) == 0) { MmuInfo.MmuType = EFI_ACPI_6_6_RHCT_MMU_TYPE_SV48; } else if (AsciiStrnCmp (Prop, "riscv,sv57", 10) == 0) { MmuInfo.MmuType = EFI_ACPI_6_6_RHCT_MMU_TYPE_SV57; } // Add the CmObj to the Configuration Manager. Status = AddSingleCmObj ( FdtParserHandle, CREATE_CM_RISCV_OBJECT_ID (ERiscVObjMmuInfo), &MmuInfo, sizeof (CM_RISCV_MMU_NODE), Token ); if (EFI_ERROR (Status)) { ASSERT (0); } return Status; } /** Create CMO info structure if CMO extension present Create CMO structure with CBOM, CBOP and CBOZ sizes. @param [in] FdtParserHandle A handle to the parser instance. @param [in] CpusNode cpus node. **/ STATIC EFI_STATUS EFIAPI CmoInfoParser ( IN CONST FDT_HW_INFO_PARSER_HANDLE FdtParserHandle, IN INT32 CpuNode, OUT CM_OBJECT_TOKEN *Token ) { CM_RISCV_CMO_NODE CmoInfo; EFI_STATUS Status; CONST VOID *Prop; BOOLEAN CmoSupported; INT32 PropSize; VOID *Fdt; Fdt = FdtParserHandle->Fdt; ZeroMem (&CmoInfo, sizeof (CM_RISCV_CMO_NODE)); CmoSupported = FALSE; Prop = FdtGetProp (Fdt, CpuNode, "riscv,cbom-block-size", &PropSize); if (Prop) { CmoSupported = TRUE; CmoInfo.CbomBlockSize = LowBitSet32 (Fdt32ToCpu (*(const UINT32 *)Prop)); } Prop = FdtGetProp (Fdt, CpuNode, "riscv,cboz-block-size", &PropSize); if (Prop) { CmoSupported = TRUE; CmoInfo.CbozBlockSize = LowBitSet32 (Fdt32ToCpu (*(const UINT32 *)Prop)); } Prop = FdtGetProp (Fdt, CpuNode, "riscv,cbop-block-size", &PropSize); if (Prop) { CmoSupported = TRUE; CmoInfo.CbopBlockSize = LowBitSet32 (Fdt32ToCpu (*(const UINT32 *)Prop)); } if (CmoSupported == TRUE) { // Add the CmObj to the Configuration Manager. Status = AddSingleCmObj ( FdtParserHandle, CREATE_CM_RISCV_OBJECT_ID (ERiscVObjCmoInfo), &CmoInfo, sizeof (CM_RISCV_CMO_NODE), Token ); if (EFI_ERROR (Status)) { ASSERT (0); } return Status; } return EFI_NOT_FOUND; } /** Create ISA string Info structure @param [in] FdtParserHandle A handle to the parser instance. @param [in] CpusNode cpus node. **/ STATIC EFI_STATUS EFIAPI IsaStringInfoParser ( IN CONST FDT_HW_INFO_PARSER_HANDLE FdtParserHandle, IN INT32 CpuNode, OUT CM_OBJECT_TOKEN *Token ) { CM_RISCV_ISA_STRING_NODE IsaStringInfo; CONST VOID *Prop; EFI_STATUS Status; INT32 PropSize; VOID *Fdt; Fdt = FdtParserHandle->Fdt; ZeroMem (&IsaStringInfo, sizeof (CM_RISCV_ISA_STRING_NODE)); Prop = FdtGetProp (Fdt, CpuNode, "riscv,isa", &PropSize); if (!Prop) { DEBUG ((DEBUG_ERROR, "Failed to parse cpu node: riscv,isa\n")); ASSERT (0); return EFI_ABORTED; } IsaStringInfo.Length = PropSize; Status = AsciiStrCpyS (IsaStringInfo.IsaString, MAX_ISA_STRING_LENGTH, (CHAR8 *)Prop); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_ERROR, "Failed to copy ISA string\n")); ASSERT (0); return EFI_ABORTED; } // Add the CmObj to the Configuration Manager. Status = AddSingleCmObj ( FdtParserHandle, CREATE_CM_RISCV_OBJECT_ID (ERiscVObjIsaStringInfo), &IsaStringInfo, sizeof (CM_RISCV_ISA_STRING_NODE), Token ); if (EFI_ERROR (Status)) { ASSERT (0); } return Status; } /** Parse a "cpu" node. CPU node parser. @param [in] FdtParserHandle A handle to the parser instance. @param [in] CpuNode cpu node. @param [in] AddressCells AddressCells info. @param [out] RintcInfo Pointer to RINTC Info structure. @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. **/ STATIC EFI_STATUS EFIAPI CpuNodeParser ( IN CONST FDT_HW_INFO_PARSER_HANDLE FdtParserHandle, IN INT32 CpuNode, IN UINT32 AddressCells, OUT CM_RISCV_RINTC_INFO *RintcInfo ) { CONST CHAR8 *CpuName; CONST VOID *Prop; EFI_STATUS Status; UINT64 HartId; INT32 PropSize; INT32 IntcNode; UINTN ProcUid; VOID *Fdt; if (RintcInfo == NULL) { ASSERT (0); return EFI_INVALID_PARAMETER; } Fdt = FdtParserHandle->Fdt; Prop = FdtGetProp (Fdt, CpuNode, "reg", &PropSize); if ((Prop == NULL) || ((PropSize != sizeof (UINT32)) && (PropSize != sizeof (UINT64)))) { ASSERT (0); return EFI_ABORTED; } IntcNode = CpuNode; Status = FdtGetNextNamedNodeInBranch (Fdt, CpuNode, "interrupt-controller", &IntcNode); if (EFI_ERROR (Status)) { ASSERT (0); if (Status == EFI_NOT_FOUND) { // Should have found the node. Status = EFI_ABORTED; } } CpuName = FdtGetName (Fdt, CpuNode, &PropSize); ProcUid = AsciiStrDecimalToUintn (&CpuName[4]); Status = ReadFdtCells64 ((UINT32 *)Prop, AddressCells, &HartId); if (EFI_ERROR (Status)) { ASSERT (FALSE); return EFI_ABORTED; } /* * Check for disabled cpu and mark appropriately in MADT. */ Prop = FdtGetProp (Fdt, CpuNode, "status", &PropSize); if (!Prop || (Prop && ((AsciiStrnCmp ((const CHAR8 *)Prop, "okay", PropSize) == 0) || (AsciiStrnCmp ((const CHAR8 *)Prop, "ok", PropSize) == 0)))) { RintcInfo->Flags = EFI_ACPI_6_6_RINTC_FLAG_ENABLE; } else { RintcInfo->Flags = 0; } RintcInfo->HartId = HartId; RintcInfo->Version = EFI_ACPI_6_6_RINTC_STRUCTURE_VERSION; RintcInfo->AcpiProcessorUid = ProcUid; RintcInfo->ExtIntcId = 0; // Will be updated later Status = IsaStringInfoParser (FdtParserHandle, CpuNode, &RintcInfo->IsaStringInfoToken); if (EFI_ERROR (Status)) { ASSERT (0); if (Status == EFI_NOT_FOUND) { // Should have found the node. Status = EFI_ABORTED; } return Status; } Status = CmoInfoParser (FdtParserHandle, CpuNode, &RintcInfo->CmoInfoToken); if (EFI_ERROR (Status)) { if (Status != EFI_NOT_FOUND) { ASSERT (0); return Status; } } Status = MmuInfoParser (FdtParserHandle, CpuNode, &RintcInfo->MmuInfoToken); if (EFI_ERROR (Status)) { if (Status != EFI_NOT_FOUND) { ASSERT (0); } } return Status; } /** CPU node parser CPU node parser. @param [in] FdtParserHandle A handle to the parser instance. @param [in] CpusNode cpus node. @param [out] NewRintcCmObjDesc Pointer to array of RINTC CM objects. @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. **/ STATIC EFI_STATUS EFIAPI CpusNodeParser ( IN CONST FDT_HW_INFO_PARSER_HANDLE FdtParserHandle, IN INT32 CpusNode, OUT CM_OBJ_DESCRIPTOR **NewRintcCmObjDesc ) { EFI_STATUS Status; UINT32 CpuNodeCount; INT32 CpuNode; INT32 AddressCells; UINT32 Index; CM_RISCV_RINTC_INFO *RintcInfoBuffer; UINT32 RintcInfoBufferSize; VOID *Fdt; if (NewRintcCmObjDesc == NULL) { ASSERT (0); return EFI_INVALID_PARAMETER; } Fdt = FdtParserHandle->Fdt; 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; } RintcInfoBufferSize = CpuNodeCount * sizeof (CM_RISCV_RINTC_INFO); RintcInfoBuffer = AllocateZeroPool (RintcInfoBufferSize); if (RintcInfoBuffer == 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 ( FdtParserHandle, CpuNode, AddressCells, &RintcInfoBuffer[Index] ); if (EFI_ERROR (Status)) { ASSERT (0); goto exit_handler; } } // for Status = CreateCmObjDesc ( CREATE_CM_RISCV_OBJECT_ID (ERiscVObjRintcInfo), CpuNodeCount, RintcInfoBuffer, RintcInfoBufferSize, NewRintcCmObjDesc ); ASSERT_EFI_ERROR (Status); exit_handler: FreePool (RintcInfoBuffer); return Status; } /** Find the RINTC structure for a phandle @param [in] NewRintcCmObjDesc Pointer to array of RINTC CM objects. @param [in] Phandle Phandle to search **/ STATIC CM_RISCV_RINTC_INFO * RiscVFindRintc ( IN CM_OBJ_DESCRIPTOR *NewRintcCmObjDesc, IN UINT64 HartId ) { CM_RISCV_RINTC_INFO *RintcInfo; INT32 Idx; RintcInfo = (CM_RISCV_RINTC_INFO *)NewRintcCmObjDesc->Data; for (Idx = 0; Idx < NewRintcCmObjDesc->Count; Idx++) { if (RintcInfo[Idx].HartId == HartId) { return &RintcInfo[Idx]; } } return NULL; } /** Check if it is a PLIC @param [in] Fdt Pointer to device tree. @param [in] AplicNode Node with APLIC compatible property. @retval TRUE The AplicNode is S-mode APLIC @retval FALSE The AplicNode is not S-mode APLIC **/ STATIC BOOLEAN IsPlicNode ( IN VOID *Fdt, IN INT32 ExtIntcNode ) { return FdtNodeIsCompatible (Fdt, ExtIntcNode, &PlicCompatibleInfo); } /** Check if it is S-mode IMSIC FDT will have entries for both M-mode and S-mode IMSIC. We need only S-mode IMSIC. @param [in] Fdt Pointer to device tree. @param [in] Node Node with IMSIC compatible property. @retval TRUE The Node is S-mode IMSIC @retval FALSE The Node is not S-mode IMSIC **/ STATIC BOOLEAN IsImsicNode ( IN VOID *Fdt, IN INT32 Node ) { INT32 *IrqProp; INT32 Len; if (!FdtNodeIsCompatible (Fdt, Node, &ImsicCompatibleInfo)) { return FALSE; } IrqProp = (INT32 *)FdtGetProp (Fdt, Node, "interrupts-extended", &Len); if (!IrqProp || (Len < 4) || (Fdt32ToCpu (IrqProp[1]) != IRQ_S_EXT)) { return FALSE; } return TRUE; } /** Check if it is S-mode APLIC FDT will have entries for both M-mode and S-mode APLIC. We need only S-mode APLIC. @param [in] Fdt Pointer to device tree. @param [in] AplicNode Node with APLIC compatible property. @retval TRUE The AplicNode is S-mode APLIC @retval FALSE The AplicNode is not S-mode APLIC **/ STATIC BOOLEAN IsAplicNode ( IN VOID *Fdt, IN INT32 ExtIntcNode ) { INT32 *IrqProp; INT32 *MsiProp; INT32 ImsicNode; INT32 Len; if (!FdtNodeIsCompatible (Fdt, ExtIntcNode, &AplicCompatibleInfo)) { return FALSE; } IrqProp = (INT32 *)FdtGetProp (Fdt, ExtIntcNode, "interrupts-extended", &Len); if ((IrqProp > 0) && (Len >= 4) && (Fdt32ToCpu (IrqProp[1]) == IRQ_S_EXT)) { return TRUE; } MsiProp = (INT32 *)FdtGetProp (Fdt, ExtIntcNode, "msi-parent", &Len); if (!MsiProp || (Len < sizeof (INT32))) { return FALSE; } ImsicNode = FdtNodeOffsetByPhandle (Fdt, Fdt32ToCpu (*MsiProp)); if (ImsicNode < 0) { return FALSE; } IrqProp = (INT32 *)FdtGetProp (Fdt, ImsicNode, "interrupts-extended", &Len); if ((!IrqProp) || (Len < 4) || (Fdt32ToCpu (IrqProp[1]) != IRQ_S_EXT)) { return FALSE; } return TRUE; } /** Update Rintc Info CM object Find the RINTC CM object of the CPU and update the External Interrupt controller ID. @param [in] Fdt Pointer to device tree. @param [in] NewRintcCmObjDesc Pointer to array of RINTC CM objects. @param [in] CpuNode Node with APLIC compatible property. @param [in] ExtIntcId External Interrupt controller (PLIC/APLIC) ID @retval EFI_SUCCESS The function completed successfully. @retval EFI_ABORTED An error occurred. @retval EFI_NOT_FOUND Not found. **/ STATIC EFI_STATUS RiscVUpdateRintc ( IN CONST VOID *Fdt, IN OUT CM_OBJ_DESCRIPTOR *NewRintcCmObjDesc, IN INT32 CpuNode, IN UINT32 ExtIntcId ) { CM_RISCV_RINTC_INFO *RintcInfo; CONST INT32 *Data; UINT32 AddressCells; INT32 DataSize; UINT64 HartId; INT32 CpusNode; EFI_STATUS Status; CpusNode = FdtParentOffset (Fdt, CpuNode); AddressCells = FdtAddressCells (Fdt, CpusNode); if (AddressCells < 0) { ASSERT (0); return EFI_ABORTED; } Data = FdtGetProp (Fdt, CpuNode, "reg", &DataSize); if ((Data == NULL) || ((DataSize != sizeof (UINT32)) && (DataSize != sizeof (UINT64)))) { ASSERT (0); return EFI_ABORTED; } Status = ReadFdtCells64 (Data, AddressCells, &HartId); if (EFI_ERROR (Status)) { ASSERT (FALSE); return EFI_ABORTED; } RintcInfo = RiscVFindRintc (NewRintcCmObjDesc, HartId); if (RintcInfo == NULL) { ASSERT (0); return EFI_NOT_FOUND; } /* Update RINTC EXT INTC ID */ RintcInfo->ExtIntcId = ExtIntcId; return EFI_SUCCESS; } /** PLIC parser and update RINTC @param [in] FdtParserHandle A handle to the parser instance. @param [in] ExtIntcNode PLIC node. @param [in] Id PLIC ID. @param [in] PlicAplicCommonInfo Common information for PLIC and APLIC. @param [in] NewRintcCmObjDesc Pointer to array of RINTC CM objects. @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. **/ STATIC EFI_STATUS EFIAPI PlicInfoParser ( IN CONST FDT_HW_INFO_PARSER_HANDLE FdtParserHandle, IN INT32 ExtIntcNode, IN UINT32 *Id, IN PLIC_APLIC_COMMON_INFO *PlicAplicCommonInfo, IN CM_OBJ_DESCRIPTOR *NewRintcCmObjDesc ) { CM_RISCV_PLIC_INFO *PlicInfo; CONST UINT32 *IntExtProp; EFI_STATUS Status; INT32 LocalCpuId; INT32 ExtIntcId; INT32 IntcNode; INT32 CpuNode; INT32 Phandle; INT32 Len; INT32 Idx1; INT32 Idx2; VOID *Fdt; ASSERT (Id != NULL); ASSERT (PlicAplicCommonInfo != NULL); if (FdtParserHandle == NULL) { ASSERT (0); return EFI_INVALID_PARAMETER; } Fdt = FdtParserHandle->Fdt; IntExtProp = FdtGetProp (Fdt, ExtIntcNode, "interrupts-extended", &Len); if ((IntExtProp == NULL) || (Len < 4)) { ASSERT (0); return EFI_INVALID_PARAMETER; } else { PlicInfo = AllocateZeroPool (sizeof (CM_RISCV_PLIC_INFO)); if (PlicInfo == NULL) { ASSERT (0); return EFI_OUT_OF_RESOURCES; } Len = Len / 4; for (Idx1 = 0, Idx2 = 0; Idx1 < Len; Idx1 += 2, Idx2++) { if (Fdt32ToCpu (IntExtProp[Idx1 + 1]) == IRQ_S_EXT) { Phandle = Fdt32ToCpu (IntExtProp[Idx1]); IntcNode = FdtNodeOffsetByPhandle (Fdt, Phandle); CpuNode = FdtParentOffset (Fdt, IntcNode); LocalCpuId = Idx2 / 2; ExtIntcId = ACPI_BUILD_EXT_INTC_ID (*Id, 2 * LocalCpuId + 1); Status = RiscVUpdateRintc (Fdt, NewRintcCmObjDesc, CpuNode, ExtIntcId); if (EFI_ERROR (Status)) { ASSERT_EFI_ERROR (Status); return Status; } } } CopyMem (&PlicAplicCommonInfo->Hid, "RSCV0001", RISCV_HWID_LENGTH); CopyMem (&PlicInfo->PlicAplicCommonInfo, PlicAplicCommonInfo, sizeof (PLIC_APLIC_COMMON_INFO)); // Add the CmObj to the Configuration Manager. Status = AddSingleCmObj ( FdtParserHandle, CREATE_CM_RISCV_OBJECT_ID (ERiscVObjPlicInfo), PlicInfo, sizeof (CM_RISCV_PLIC_INFO), NULL ); if (EFI_ERROR (Status)) { ASSERT (0); return Status; } } return EFI_SUCCESS; } /** APLIC parser and update RINTC @param [in] FdtParserHandle A handle to the parser instance. @param [in] ExtIntcNode APLIC node. @param [in] Id APLIC ID. @param [in] PlicAplicCommonInfo Common information for PLIC and APLIC. @param [in, out] NewRintcCmObjDesc Pointer to array of RINTC CM objects. @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. **/ STATIC EFI_STATUS EFIAPI AplicInfoParser ( IN CONST FDT_HW_INFO_PARSER_HANDLE FdtParserHandle, IN INT32 ExtIntcNode, IN UINT32 *Id, IN PLIC_APLIC_COMMON_INFO *PlicAplicCommonInfo, IN OUT CM_OBJ_DESCRIPTOR *NewRintcCmObjDesc ) { CM_RISCV_APLIC_INFO *AplicInfo; CONST UINT32 *IntExtProp; EFI_STATUS Status; INT32 ExtIntcId; INT32 IntcNode; INT32 CpuNode; INT32 Phandle; INT32 Len; INT32 Idx; VOID *Fdt; ASSERT (Id != NULL); ASSERT (PlicAplicCommonInfo != NULL); if (FdtParserHandle == NULL) { ASSERT (0); return EFI_INVALID_PARAMETER; } Fdt = FdtParserHandle->Fdt; AplicInfo = AllocateZeroPool (sizeof (CM_RISCV_APLIC_INFO)); if (AplicInfo == NULL) { ASSERT (0); return EFI_OUT_OF_RESOURCES; } IntExtProp = FdtGetProp (Fdt, ExtIntcNode, "interrupts-extended", &Len); if ((IntExtProp != 0) && ((Len / sizeof (UINT32)) % 2 == 0)) { Len = Len / 4; AplicInfo->NumIdcs = Len / 2; for (Idx = 0; Idx < Len; Idx += 2) { Phandle = Fdt32ToCpu (IntExtProp[Idx]); IntcNode = FdtNodeOffsetByPhandle (Fdt, Phandle); CpuNode = FdtParentOffset (Fdt, IntcNode); ExtIntcId = ACPI_BUILD_EXT_INTC_ID (*Id, Idx / 2); Status = RiscVUpdateRintc (Fdt, NewRintcCmObjDesc, CpuNode, ExtIntcId); if (EFI_ERROR (Status)) { ASSERT_EFI_ERROR (Status); return Status; } } } CopyMem (&PlicAplicCommonInfo->Hid, "RSCV0002", RISCV_HWID_LENGTH); CopyMem (&AplicInfo->PlicAplicCommonInfo, PlicAplicCommonInfo, sizeof (PLIC_APLIC_COMMON_INFO)); // Add the CmObj to the Configuration Manager. Status = AddSingleCmObj ( FdtParserHandle, CREATE_CM_RISCV_OBJECT_ID (ERiscVObjAplicInfo), AplicInfo, sizeof (CM_RISCV_APLIC_INFO), NULL ); if (EFI_ERROR (Status)) { ASSERT (0); return Status; } return EFI_SUCCESS; } /** PLIC/APLIC parser and update RINTC @param [in] FdtParserHandle A handle to the parser instance. @param [in] NewRintcCmObjDesc Pointer to array of RINTC CM objects. @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. **/ STATIC EFI_STATUS EFIAPI PlicAplicInfoParser ( IN CONST FDT_HW_INFO_PARSER_HANDLE FdtParserHandle, IN CM_OBJ_DESCRIPTOR *NewRintcCmObjDesc ) { PLIC_APLIC_COMMON_INFO PlicAplicCommonInfo; CONST UINT32 *Prop; EFI_STATUS Status; UINT32 Id; UINT32 GsiBase; INT32 Len; INT32 Prev; INT32 ExtIntcNode; VOID *Fdt; if (FdtParserHandle == NULL) { ASSERT (0); return EFI_INVALID_PARAMETER; } Fdt = FdtParserHandle->Fdt; GsiBase = 0; Id = 0; for (Prev = 0; ; Prev = ExtIntcNode) { ExtIntcNode = FdtNextNode (Fdt, Prev, NULL); if (ExtIntcNode < 0) { return EFI_SUCCESS; } if (!(IsPlicNode (Fdt, ExtIntcNode) || IsAplicNode (Fdt, ExtIntcNode))) { continue; } FdtCreateExtIntcList (ExtIntcNode, GsiBase); PlicAplicCommonInfo.Version = 1; PlicAplicCommonInfo.Id = Id; Prop = FdtGetProp (Fdt, ExtIntcNode, "riscv,num-sources", &Len); if (!Prop) { Prop = FdtGetProp (Fdt, ExtIntcNode, "riscv,ndev", &Len); if (!Prop) { ASSERT (0); return EFI_INVALID_PARAMETER; } } PlicAplicCommonInfo.NumSources = Fdt32ToCpu (*(const UINT32 *)Prop); Prop = FdtGetProp (Fdt, ExtIntcNode, "reg", &Len); if ((Prop == NULL) || (Len % 4 > 0)) { ASSERT (0); return EFI_INVALID_PARAMETER; } PlicAplicCommonInfo.BaseAddress = Fdt64ToCpu (*(const UINT64 *)Prop); PlicAplicCommonInfo.Size = Fdt64ToCpu (*((const UINT64 *)Prop + 1)); PlicAplicCommonInfo.GsiBase = GsiBase; GsiBase += PlicAplicCommonInfo.NumSources; if (FdtNodeIsCompatible (Fdt, ExtIntcNode, &PlicCompatibleInfo)) { Status = PlicInfoParser ( FdtParserHandle, ExtIntcNode, &Id, &PlicAplicCommonInfo, NewRintcCmObjDesc ); } else if (FdtNodeIsCompatible (Fdt, ExtIntcNode, &AplicCompatibleInfo)) { Status = AplicInfoParser ( FdtParserHandle, ExtIntcNode, &Id, &PlicAplicCommonInfo, NewRintcCmObjDesc ); } if (EFI_ERROR (Status)) { ASSERT (0); return Status; } Id++; } return EFI_SUCCESS; } /** RINTC parser using IMSIC node Parse RINTC information using IMSIC. @param [in] FdtParserHandle A handle to the parser instance. @param [in, out] NewRintcCmObjDesc Pointer to array of RINTC CM objects. @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. **/ STATIC EFI_STATUS EFIAPI UpdateRintcInfo ( IN CONST VOID *Fdt, IN CONST INT32 ImsicNode, IN OUT CM_OBJ_DESCRIPTOR *NewRintcCmObjDesc ) { CM_RISCV_RINTC_INFO *RintcInfoBuffer; CONST UINT32 *IntExtProp; CONST UINT64 *ImsicRegProp; CONST INT32 *Data; UINT64 ImsicBaseAddr; UINT64 ImsicBaseLen; UINT64 ImsicCpuBaseAddr; UINT64 ImsicCpuBaseLen; INT32 NumPhandle; INT32 Len; INT32 AddressCells; INT32 CpusNode; INT32 CpuNode; INT32 DataSize; INT32 IntcNode; INT32 Phandle; UINT64 HartId; UINTN NumImsicBase; INTN Limit; INTN Idx; INTN Idx1; INTN Idx2; IntExtProp = (UINT32 *)FdtGetProp (Fdt, ImsicNode, "interrupts-extended", &Len); if ((IntExtProp == 0) || ((Len / sizeof (UINT32)) % 2 != 0)) { /* interrupts-extended: , */ ASSERT (0); return EFI_INVALID_PARAMETER; } NumPhandle = (Len / sizeof (UINT32)) / 2; if (NumPhandle == 0) { ASSERT (0); return EFI_NOT_FOUND; } ImsicRegProp = FdtGetProp (Fdt, ImsicNode, "reg", &Len); if ((ImsicRegProp == 0) || (((Len / sizeof (UINT32)) % 4) != 0)) { // address-cells and size-cells are always 2 DEBUG ((DEBUG_ERROR, "Failed to parse imsic node: reg\n")); return EFI_INVALID_PARAMETER; } NumImsicBase = (Len / sizeof (UINT32)) / 4; Idx2 = 0; for (Idx = 0; Idx < NumImsicBase; Idx++) { ImsicBaseAddr = Fdt64ToCpu (ImsicRegProp[Idx * 2]); ImsicBaseLen = Fdt64ToCpu (ImsicRegProp[Idx * 2 + 1]); // Calculate the limit of number of cpu nodes this imsic can handle Limit = ImsicBaseLen / IMSIC_MMIO_PAGE_SZ; for (Idx1 = 0; Idx1 < Limit && Idx2 < NumPhandle; Idx1++, Idx2++) { Phandle = Fdt32ToCpu (IntExtProp[Idx2 * 2]); IntcNode = FdtNodeOffsetByPhandle (Fdt, Phandle); CpuNode = FdtParentOffset (Fdt, IntcNode); CpusNode = FdtParentOffset (Fdt, CpuNode); AddressCells = FdtAddressCells (Fdt, CpusNode); if (AddressCells < 0) { ASSERT (0); return EFI_ABORTED; } Data = FdtGetProp (Fdt, CpuNode, "reg", &DataSize); if (Data == NULL) { ASSERT (0); } if ((Data == NULL) || ((DataSize != sizeof (UINT32)) && (DataSize != sizeof (UINT64)))) { ASSERT (0); return EFI_ABORTED; } Status = ReadFdtCells64 (Data, AddressCells, &HartId); if (EFI_ERROR (Status)) { ASSERT (FALSE); return EFI_ABORTED; } RintcInfoBuffer = RiscVFindRintc (NewRintcCmObjDesc, HartId); if (RintcInfoBuffer == NULL) { return EFI_NOT_FOUND; } ImsicCpuBaseAddr = ImsicBaseAddr + Idx1 * IMSIC_MMIO_PAGE_SZ; ImsicCpuBaseLen = IMSIC_MMIO_PAGE_SZ; RintcInfoBuffer->ImsicBaseAddress = ImsicCpuBaseAddr; RintcInfoBuffer->ImsicSize = ImsicCpuBaseLen; } } return EFI_SUCCESS; } /** Get IMSIC information @param [in] Fdt FDT Base pointer. @param [in] ImsicNode IMSIC node in FDT. @param [in, out] ImsicInfo Pointer to IMSIC info structure. @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. **/ STATIC EFI_STATUS EFIAPI ImsicGetInfo ( IN CONST VOID *Fdt, IN CONST INT32 ImsicNode, IN OUT CM_RISCV_IMSIC_INFO *ImsicInfo ) { CONST UINT32 *IntExtProp; CONST UINT64 *Prop; INT32 Len; INT32 NumPhandle; if (ImsicInfo == NULL) { ASSERT (0); return EFI_INVALID_PARAMETER; } IntExtProp = FdtGetProp (Fdt, ImsicNode, "interrupts-extended", &Len); if ((IntExtProp == 0) || ((Len / sizeof (UINT32)) % 2 != 0)) { /* interrupts-extended: , */ ASSERT (0); return EFI_INVALID_PARAMETER; } /* There can be M-mode IMSIC in DT. Consider only S-mode */ if (Fdt32ToCpu (IntExtProp[1]) != IRQ_S_EXT) { ASSERT (0); return EFI_NOT_FOUND; } NumPhandle = (Len / sizeof (UINT32)) / 2; if (NumPhandle == 0) { ASSERT (0); return EFI_NOT_FOUND; } Prop = FdtGetProp (Fdt, ImsicNode, "riscv,num-ids", &Len); if (Prop == 0) { ASSERT (0); return EFI_INVALID_PARAMETER; } ImsicInfo->NumIds = Fdt32ToCpu (*(const UINT32 *)Prop); Prop = FdtGetProp (Fdt, ImsicNode, "riscv,num-guest-ids", &Len); if (Prop == 0) { ImsicInfo->NumGuestIds = ImsicInfo->NumIds; } else { ImsicInfo->NumGuestIds = Fdt32ToCpu (*(const UINT32 *)Prop); } Prop = FdtGetProp (Fdt, ImsicNode, "riscv,guest-index-bits", &Len); if (Prop == 0) { ImsicInfo->GuestIndexBits = 0; } else { ImsicInfo->GuestIndexBits = Fdt32ToCpu (*(const UINT32 *)Prop); } Prop = FdtGetProp (Fdt, ImsicNode, "riscv,hart-index-bits", &Len); if (Prop == 0) { ImsicInfo->HartIndexBits = 0; // update default value later } else { ImsicInfo->HartIndexBits = Fdt32ToCpu (*(const UINT32 *)Prop); } Prop = FdtGetProp (Fdt, ImsicNode, "riscv,group-index-bits", &Len); if (Prop == 0) { ImsicInfo->GroupIndexBits = 0; } else { ImsicInfo->GroupIndexBits = Fdt32ToCpu (*(const UINT32 *)Prop); } Prop = FdtGetProp (Fdt, ImsicNode, "riscv,group-index-shift", &Len); if (Prop == 0) { ImsicInfo->GroupIndexShift = IMSIC_MMIO_PAGE_SHIFT * 2; } else { ImsicInfo->GroupIndexShift = Fdt32ToCpu (*(const UINT32 *)Prop); } ImsicInfo->Version = 1; ImsicInfo->Flags = 0; Prop = FdtGetProp (Fdt, ImsicNode, "reg", &Len); if ((Prop == 0) || (((Len / sizeof (UINT32)) % 4) != 0)) { // address-cells and size-cells are always 2 DEBUG ((DEBUG_ERROR, "Failed to parse imsic node: reg\n")); return EFI_INVALID_PARAMETER; } if (ImsicInfo->HartIndexBits == 0) { Len = NumPhandle; while (Len > 0) { ImsicInfo->HartIndexBits++; Len = Len >> 1; } } return EFI_SUCCESS; } /** IMSIC parser using IMSIC node @param [in] FdtParserHandle A handle to the parser instance. @param [in, out] NewRintcCmObjDesc Pointer to array of RINTC CM objects. @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. **/ STATIC EFI_STATUS EFIAPI ImsicInfoParser ( IN CONST FDT_HW_INFO_PARSER_HANDLE FdtParserHandle, IN OUT CM_OBJ_DESCRIPTOR *NewRintcCmObjDesc ) { CM_RISCV_IMSIC_INFO ImsicInfo; EFI_STATUS Status; INT32 Prev; INT32 ImsicNode; VOID *Fdt; if (NewRintcCmObjDesc == NULL) { ASSERT (0); return EFI_INVALID_PARAMETER; } Fdt = FdtParserHandle->Fdt; ZeroMem (&ImsicInfo, sizeof (CM_RISCV_IMSIC_INFO)); for (Prev = 0; ; Prev = ImsicNode) { ImsicNode = FdtNextNode (Fdt, Prev, NULL); if (ImsicNode < 0) { return EFI_NOT_FOUND; } if (IsImsicNode (Fdt, ImsicNode) == FALSE) { continue; } Status = ImsicGetInfo (Fdt, ImsicNode, &ImsicInfo); if (EFI_ERROR (Status)) { ASSERT (0); return Status; } Status = UpdateRintcInfo (Fdt, ImsicNode, NewRintcCmObjDesc); if (EFI_ERROR (Status)) { ASSERT (0); return Status; } // Add the CmObj to the Configuration Manager. Status = AddSingleCmObj ( FdtParserHandle, CREATE_CM_RISCV_OBJECT_ID (ERiscVObjImsicInfo), &ImsicInfo, sizeof (CM_RISCV_IMSIC_INFO), NULL ); if (EFI_ERROR (Status)) { ASSERT (0); } return Status; } } /** CM_RISCV_RINTC_INFO and CM_RISCV_IMSIC_INFO parser function. 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. **/ STATIC EFI_STATUS EFIAPI RiscVIntcInfoParser ( IN CONST FDT_HW_INFO_PARSER_HANDLE FdtParserHandle, IN INT32 FdtBranch ) { CM_OBJ_DESCRIPTOR *NewCmObjDesc; EFI_STATUS Status; if (FdtParserHandle == NULL) { ASSERT (0); return EFI_INVALID_PARAMETER; } NewCmObjDesc = NULL; // Parse the "cpus" nodes and its children "cpu" nodes, // and create a CM_OBJ_DESCRIPTOR. Status = CpusNodeParser (FdtParserHandle, FdtBranch, &NewCmObjDesc); if (EFI_ERROR (Status)) { ASSERT (0); return Status; } /* Search for IMSIC presence and update RINTC structures if so */ Status = ImsicInfoParser (FdtParserHandle, NewCmObjDesc); if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) { ASSERT_EFI_ERROR (Status); goto exit_handler; } /* Search for APLIC/PLIC presence and update RINTC structures if so */ Status = PlicAplicInfoParser (FdtParserHandle, NewCmObjDesc); if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) { ASSERT_EFI_ERROR (Status); goto exit_handler; } // Finally, add all the RINTC 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; } /** MADT dispatcher. 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 RiscVIntcDispatcher ( IN CONST FDT_HW_INFO_PARSER_HANDLE FdtParserHandle, IN INT32 FdtBranch ) { EFI_STATUS Status; INT32 CpusNode; VOID *Fdt; if (FdtParserHandle == NULL) { ASSERT (0); return EFI_INVALID_PARAMETER; } Fdt = FdtParserHandle->Fdt; // The "cpus" node resides at the root of the DT. Fetch it. CpusNode = FdtPathOffset (Fdt, "/cpus"); if (CpusNode < 0) { return EFI_NOT_FOUND; } Status = RiscVIntcInfoParser (FdtParserHandle, CpusNode); if (EFI_ERROR (Status)) { // EFI_NOT_FOUND is not tolerated at this point. ASSERT (0); return Status; } return EFI_SUCCESS; }