/** @file Arm Ffa library common code. Copyright (c) 2024, Arm Limited. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent @par Glossary: - FF-A - Firmware Framework for Arm A-profile @par Reference(s): - Arm Firmware Framework for Arm A-Profile [https://developer.arm.com/documentation/den0077/latest] **/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ArmFfaCommon.h" BOOLEAN gFfaSupported; UINT16 gPartId; /** Convert EFI_GUID to UUID format. for example, If there is EFI_GUID named "378daedc-f06b-4446-8314-40ab933c87a3", EFI_GUID is saved in memory like: dc ae 8d 37 6b f0 46 44 83 14 40 ab 93 3c 87 a3 However, UUID should be saved like: 37 8d ae dc f0 6b 44 46 83 14 40 ab 93 3c 87 a3 FF-A and other software components (i.e. linux-kernel) uses below format. @param [in] Guid EFI_GUID @param [out] Uuid Uuid **/ STATIC VOID EFIAPI ConvertEfiGuidToUuid ( IN EFI_GUID *Guid, OUT UINT64 *Uuid ) { UINT32 *Data32; UINT16 *Data16; CopyGuid ((EFI_GUID *)Uuid, Guid); Data32 = (UINT32 *)Uuid; Data32[0] = SwapBytes32 (Data32[0]); Data16 = (UINT16 *)&Data32[1]; Data16[0] = SwapBytes16 (Data16[0]); Data16[1] = SwapBytes16 (Data16[1]); } /** Convert EFI_STATUS to FFA return code. @param [in] Status edk2 status code. @retval ARM_FFA_RET_* return value correspond to EFI_STATUS. **/ UINTN EFIAPI EfiStatusToFfaStatus ( IN EFI_STATUS Status ) { switch (Status) { case EFI_SUCCESS: return ARM_FFA_RET_SUCCESS; case EFI_INVALID_PARAMETER: return ARM_FFA_RET_INVALID_PARAMETERS; case EFI_OUT_OF_RESOURCES: return ARM_FFA_RET_NO_MEMORY; case EFI_NO_RESPONSE: return ARM_FFA_RET_BUSY; case EFI_INTERRUPT_PENDING: return ARM_FFA_RET_INTERRUPTED; case EFI_ACCESS_DENIED: return ARM_FFA_RET_DENIED; case EFI_ABORTED: return ARM_FFA_RET_ABORTED; case EFI_NOT_FOUND: return ARM_FFA_RET_NODATA; case EFI_NOT_READY: return ARM_FFA_RET_NOT_READY; default: return ARM_FFA_RET_NOT_SUPPORTED; } } /** Convert FFA return code to EFI_STATUS. @param [in] FfaStatus Ffa return Status @retval EFI_STATUS return value correspond EFI_STATUS to FfaStatus **/ EFI_STATUS EFIAPI FfaStatusToEfiStatus ( IN UINTN FfaStatus ) { switch ((UINT32)FfaStatus) { case ARM_FFA_RET_SUCCESS: return EFI_SUCCESS; case ARM_FFA_RET_INVALID_PARAMETERS: return EFI_INVALID_PARAMETER; case ARM_FFA_RET_NO_MEMORY: return EFI_OUT_OF_RESOURCES; case ARM_FFA_RET_BUSY: return EFI_NO_RESPONSE; case ARM_FFA_RET_INTERRUPTED: return EFI_INTERRUPT_PENDING; case ARM_FFA_RET_DENIED: return EFI_ACCESS_DENIED; case ARM_FFA_RET_ABORTED: return EFI_ABORTED; case ARM_FFA_RET_NODATA: return EFI_NOT_FOUND; case ARM_FFA_RET_NOT_READY: return EFI_NOT_READY; default: return EFI_UNSUPPORTED; } } /** Convert FfArgs to EFI_STATUS. @param [in] FfaArgs Ffa arguments @retval EFI_STATUS return value correspond EFI_STATUS to FfaStatus **/ EFI_STATUS EFIAPI FfaArgsToEfiStatus ( IN ARM_FFA_ARGS *FfaArgs ) { UINT32 FfaStatus; if (FfaArgs == NULL) { FfaStatus = ARM_FFA_RET_INVALID_PARAMETERS; } else if (IS_FID_FFA_ERROR (FfaArgs->Arg0)) { /* * In case of error, the Arg0 will be set to the fid FFA_ERROR. * and Error code is set in Arg2. */ FfaStatus = FfaArgs->Arg2; } else if (FfaArgs->Arg0 == ARM_FFA_RET_NOT_SUPPORTED) { /* * If Some FF-A ABI doesn't support, it sets ARM_FFA_RET_NOT_SUPPORTED * in Arg0 and other register has no meaning. * In this case, set Arg2 as ARM_FFA_RET_NOT_SUPPORTED so that * FfaStatusToEfiStatus (FfaARgs.Arg2) returns proper EFI_STATUS. */ FfaStatus = ARM_FFA_RET_NOT_SUPPORTED; } else if (FfaArgs->Arg0 == ARM_FID_FFA_INTERRUPT) { FfaStatus = ARM_FFA_RET_INTERRUPTED; } else { FfaStatus = ARM_FFA_RET_SUCCESS; } return FfaStatusToEfiStatus (FfaStatus); } /** Trigger FF-A ABI call according to PcdFfaLibConduitSmc. @param [in, out] FfaArgs Ffa arguments **/ VOID EFIAPI ArmCallFfa ( IN OUT ARM_FFA_ARGS *FfaArgs ) { if (PcdGetBool (PcdFfaLibConduitSmc)) { ArmCallSmc ((ARM_SMC_ARGS *)FfaArgs); } else { ArmCallSvc ((ARM_SVC_ARGS *)FfaArgs); } } /** Check FF-A support or not. @retval TRUE Supported @retval FALSE Not supported **/ BOOLEAN EFIAPI IsFfaSupported ( IN VOID ) { return gFfaSupported; } /** Get FF-A version. @param [in] RequestMajorVersion Minimal request major version @param [in] RequestMinorVersion Minimal request minor version @param [out] CurrentMajorVersion Current major version @param [out] CurrentMinorVersion Current minor version **/ EFI_STATUS EFIAPI ArmFfaLibGetVersion ( IN UINT16 RequestMajorVersion, IN UINT16 RequestMinorVersion, OUT UINT16 *CurrentMajorVersion, OUT UINT16 *CurrentMinorVersion ) { EFI_STATUS Status; ARM_FFA_ARGS FfaArgs; ZeroMem (&FfaArgs, sizeof (ARM_FFA_ARGS)); FfaArgs.Arg0 = ARM_FID_FFA_VERSION; FfaArgs.Arg1 = ARM_FFA_CREATE_VERSION ( RequestMajorVersion, RequestMinorVersion ); ArmCallFfa (&FfaArgs); Status = FfaArgsToEfiStatus (&FfaArgs); if (EFI_ERROR (Status)) { return Status; } if (CurrentMajorVersion != NULL) { *CurrentMajorVersion = ARM_FFA_MAJOR_VERSION_GET (FfaArgs.Arg0); } if (CurrentMinorVersion != NULL) { *CurrentMinorVersion = ARM_FFA_MINOR_VERSION_GET (FfaArgs.Arg0); } return EFI_SUCCESS; } /** Get FF-A features. @param [in] Id Feature id or function id @param [in] InputProperties Input properties according to Id @param [out] Property1 First property. @param [out] Property2 Second property. @retval EFI_SUCCESS @retval Others Error **/ EFI_STATUS EFIAPI ArmFfaLibGetFeatures ( IN UINT32 Id, IN UINT32 InputProperties, OUT UINTN *Property1, OUT UINTN *Property2 ) { EFI_STATUS Status; ARM_FFA_ARGS FfaArgs; if ((Property1 == NULL) || (Property2 == NULL)) { return EFI_INVALID_PARAMETER; } *Property1 = 0x00; *Property2 = 0x00; switch (Id) { case ARM_FID_FFA_RXTX_MAP_AARCH32: case ARM_FID_FFA_RXTX_MAP_AARCH64: if ((InputProperties != FFA_RXTX_MAP_INPUT_PROPERTY_DEFAULT)) { DEBUG ((DEBUG_ERROR, "%a: Invalid Parameter for FunctionId: 0x%x", __func__, Id)); return EFI_INVALID_PARAMETER; } break; } ZeroMem (&FfaArgs, sizeof (ARM_FFA_ARGS)); FfaArgs.Arg0 = ARM_FID_FFA_FEATURES; FfaArgs.Arg1 = Id; FfaArgs.Arg2 = InputProperties; ArmCallFfa (&FfaArgs); Status = FfaArgsToEfiStatus (&FfaArgs); if (EFI_ERROR (Status)) { return Status; } switch (Id) { case ARM_FID_FFA_RXTX_MAP_AARCH32: case ARM_FID_FFA_RXTX_MAP_AARCH64: case ARM_FFA_FEATURE_ID_NOTIFICATION_PENDING_INTERRUPT: case ARM_FFA_FEATURE_ID_SCHEDULE_RECEIVER_INTERRUPT: case ARM_FFA_FEATURE_ID_MANAGED_EXIT_INTERRUPT: *Property1 = FfaArgs.Arg2; break; } return EFI_SUCCESS; } /** Acquire ownership of the Rx buffer. @param [in] PartId Partition Id @retval EFI_SUCCESS @retval Others Error **/ EFI_STATUS EFIAPI ArmFfaLibRxAcquire ( IN UINT16 PartId ) { ARM_FFA_ARGS FfaArgs; ZeroMem (&FfaArgs, sizeof (ARM_FFA_ARGS)); FfaArgs.Arg0 = ARM_FID_FFA_RX_ACQUIRE; FfaArgs.Arg1 = PartId; ArmCallFfa (&FfaArgs); return FfaArgsToEfiStatus (&FfaArgs); } /** Release ownership of the Rx buffer. @param [in] PartId Partition Id @retval EFI_SUCCESS @retval Others Error **/ EFI_STATUS EFIAPI ArmFfaLibRxRelease ( IN UINT16 PartId ) { ARM_FFA_ARGS FfaArgs; ZeroMem (&FfaArgs, sizeof (ARM_FFA_ARGS)); FfaArgs.Arg0 = ARM_FID_FFA_RX_RELEASE; FfaArgs.Arg1 = PartId; ArmCallFfa (&FfaArgs); return FfaArgsToEfiStatus (&FfaArgs); } /** Get partition or VM id. @param [out] PartId Partition id @retval EFI_SUCCESS @retval Others Error **/ EFI_STATUS EFIAPI ArmFfaLibPartitionIdGet ( OUT UINT16 *PartId ) { EFI_STATUS Status; ARM_FFA_ARGS FfaArgs; if (PartId == NULL) { return EFI_INVALID_PARAMETER; } ZeroMem (&FfaArgs, sizeof (ARM_FFA_ARGS)); FfaArgs.Arg0 = ARM_FID_FFA_ID_GET; ArmCallFfa (&FfaArgs); Status = FfaArgsToEfiStatus (&FfaArgs); if (EFI_ERROR (Status)) { DEBUG (( DEBUG_ERROR, "%a: Failed to get partition id. Status: %r\n", __func__, Status )); return Status; } *PartId = (FfaArgs.Arg2 >> ARM_FFA_DEST_EP_SHIFT) & ARM_FFA_PARTITION_ID_MASK; return EFI_SUCCESS; } /** Get spmc or spmd partition id. @param [out] SpmPartId spmc/spmd partition id @retval EFI_SUCCESS @retval Others Error **/ EFI_STATUS EFIAPI ArmFfaLibSpmIdGet ( OUT UINT16 *SpmPartId ) { EFI_STATUS Status; ARM_FFA_ARGS FfaArgs; if (SpmPartId == NULL) { return EFI_INVALID_PARAMETER; } ZeroMem (&FfaArgs, sizeof (ARM_FFA_ARGS)); FfaArgs.Arg0 = ARM_FID_FFA_SPM_ID_GET; ArmCallFfa (&FfaArgs); Status = FfaArgsToEfiStatus (&FfaArgs); if (EFI_ERROR (Status)) { DEBUG (( DEBUG_ERROR, "%a: Failed to get partition id. Status: %r\n", __func__, Status )); return Status; } *SpmPartId = (FfaArgs.Arg2 >> ARM_FFA_DEST_EP_SHIFT) & ARM_FFA_PARTITION_ID_MASK; return EFI_SUCCESS; } /** Get Partition info. If This function is called to get partition descriptors (Flags isn't set with FFA_PART_INFO_FLAG_TYPE_COUNT), It should call ArmFfaLibRxRelease() to release RX buffer. @param [in] ServiceGuid Service guid. @param [in] Flags If this function called to get partition desc and get successfully, Caller should release RX buffer by calling ArmFfaLibRxRelease @param [out] Count Number of partition or partition descriptor @param [out] Size Size of Partition Info structure in Rx Buffer @retval EFI_SUCCESS @retval Others Error **/ EFI_STATUS EFIAPI ArmFfaLibPartitionInfoGet ( IN EFI_GUID *ServiceGuid, IN UINT32 Flags, OUT UINT32 *Count, OUT UINT32 *Size OPTIONAL ) { EFI_STATUS Status; ARM_FFA_ARGS FfaArgs; UINT64 Uuid[2]; UINT32 *SmcUuid; if (Count == NULL) { return EFI_INVALID_PARAMETER; } if ((((Flags >> FFA_PART_INFO_FLAG_TYPE_SHIFT) & FFA_PART_INFO_FLAG_TYPE_MASK) != FFA_PART_INFO_FLAG_TYPE_COUNT) && (Size == NULL)) { return EFI_INVALID_PARAMETER; } if (ServiceGuid != NULL) { ConvertEfiGuidToUuid (ServiceGuid, Uuid); } else { ZeroMem (Uuid, sizeof (Uuid)); } ZeroMem (&FfaArgs, sizeof (ARM_FFA_ARGS)); SmcUuid = (UINT32 *)Uuid; FfaArgs.Arg0 = ARM_FID_FFA_PARTITION_INFO_GET; FfaArgs.Arg1 = SmcUuid[0]; FfaArgs.Arg2 = SmcUuid[1]; FfaArgs.Arg3 = SmcUuid[2]; FfaArgs.Arg4 = SmcUuid[3]; FfaArgs.Arg5 = Flags; ArmCallFfa (&FfaArgs); Status = FfaArgsToEfiStatus (&FfaArgs); if (EFI_ERROR (Status)) { DEBUG (( DEBUG_ERROR, "%a: Failed to get partition information of %g. Status: %r\n", __func__, (ServiceGuid != NULL) ? ServiceGuid : (EFI_GUID *)Uuid, Status )); goto ErrorHandler; } *Count = FfaArgs.Arg2; if (Size != NULL) { *Size = FfaArgs.Arg3; } return EFI_SUCCESS; ErrorHandler: *Count = 0; if (Size != NULL) { *Size = 0; } return Status; } /** Restore the context which was interrupted with FFA_INTERRUPT (EFI_INTERRUPT_PENDING). @param [in] PartId Partition id @param [in] CpuNumber Cpu number in partition @retval EFI_SUCCESS @retval Other Error **/ EFI_STATUS EFIAPI ArmFfaLibRun ( IN UINT16 PartId, IN UINT16 CpuNumber ) { ARM_FFA_ARGS FfaArgs; ZeroMem (&FfaArgs, sizeof (ARM_FFA_ARGS)); FfaArgs.Arg0 = ARM_FID_FFA_RUN; FfaArgs.Arg1 = PACK_PARTITION_ID_INFO (PartId, CpuNumber); ArmCallFfa (&FfaArgs); return FfaArgsToEfiStatus (&FfaArgs); } /** Send direct message request version 1. @param [in] DestPartId Dest partition id @param [in] Flags Message flags @param [in, out] ImpDefArgs Implemented defined arguments and Implemented defined return values @retval EFI_SUCCESS Success @retval Others Error **/ EFI_STATUS EFIAPI ArmFfaLibMsgSendDirectReq ( IN UINT16 DestPartId, IN UINT32 Flags, IN OUT DIRECT_MSG_ARGS *ImpDefArgs ) { EFI_STATUS Status; ARM_FFA_ARGS FfaArgs; if ((DestPartId == gPartId) || (ImpDefArgs == NULL)) { return EFI_INVALID_PARAMETER; } ZeroMem (&FfaArgs, sizeof (ARM_FFA_ARGS)); FfaArgs.Arg0 = ARM_FID_FFA_MSG_SEND_DIRECT_REQ; FfaArgs.Arg1 = PACK_PARTITION_ID_INFO (gPartId, DestPartId); FfaArgs.Arg2 = Flags; FfaArgs.Arg3 = ImpDefArgs->Arg0; FfaArgs.Arg4 = ImpDefArgs->Arg1; FfaArgs.Arg5 = ImpDefArgs->Arg2; FfaArgs.Arg6 = ImpDefArgs->Arg3; FfaArgs.Arg7 = ImpDefArgs->Arg4; ArmCallFfa (&FfaArgs); Status = FfaArgsToEfiStatus (&FfaArgs); if (EFI_ERROR (Status)) { return Status; } ImpDefArgs->Arg0 = FfaArgs.Arg3; ImpDefArgs->Arg1 = FfaArgs.Arg4; ImpDefArgs->Arg2 = FfaArgs.Arg5; ImpDefArgs->Arg3 = FfaArgs.Arg6; ImpDefArgs->Arg4 = FfaArgs.Arg7; return EFI_SUCCESS; } /** Send direct message request version 2. @param [in] DestPartId Dest partition id @param [in] ServiceGuid Service guid @param [in, out] ImpDefArgs Implemented defined arguments and Implemented defined return values @retval EFI_SUCCESS Success @retval Others Error **/ EFI_STATUS EFIAPI ArmFfaLibMsgSendDirectReq2 ( IN UINT16 DestPartId, IN EFI_GUID *ServiceGuid, IN OUT DIRECT_MSG_ARGS *ImpDefArgs ) { EFI_STATUS Status; UINT64 Uuid[2]; ARM_FFA_ARGS FfaArgs; /* * Direct message request 2 is only supported on AArch64. */ if (sizeof (UINTN) != sizeof (UINT64)) { return EFI_UNSUPPORTED; } if ((DestPartId == gPartId) || (ImpDefArgs == NULL)) { return EFI_INVALID_PARAMETER; } if (ServiceGuid != NULL) { ConvertEfiGuidToUuid (ServiceGuid, Uuid); } else { ZeroMem (Uuid, sizeof (Uuid)); } ZeroMem (&FfaArgs, sizeof (ARM_FFA_ARGS)); FfaArgs.Arg0 = ARM_FID_FFA_MSG_SEND_DIRECT_REQ2; FfaArgs.Arg1 = PACK_PARTITION_ID_INFO (gPartId, DestPartId); FfaArgs.Arg2 = Uuid[0]; FfaArgs.Arg3 = Uuid[1]; FfaArgs.Arg4 = ImpDefArgs->Arg0; FfaArgs.Arg5 = ImpDefArgs->Arg1; FfaArgs.Arg6 = ImpDefArgs->Arg2; FfaArgs.Arg7 = ImpDefArgs->Arg3; FfaArgs.Arg8 = ImpDefArgs->Arg4; FfaArgs.Arg9 = ImpDefArgs->Arg5; FfaArgs.Arg10 = ImpDefArgs->Arg6; FfaArgs.Arg11 = ImpDefArgs->Arg7; FfaArgs.Arg12 = ImpDefArgs->Arg8; FfaArgs.Arg13 = ImpDefArgs->Arg9; FfaArgs.Arg14 = ImpDefArgs->Arg10; FfaArgs.Arg15 = ImpDefArgs->Arg11; FfaArgs.Arg16 = ImpDefArgs->Arg12; FfaArgs.Arg17 = ImpDefArgs->Arg13; ArmCallFfa (&FfaArgs); Status = FfaArgsToEfiStatus (&FfaArgs); if (EFI_ERROR (Status)) { return Status; } ImpDefArgs->Arg0 = FfaArgs.Arg4; ImpDefArgs->Arg1 = FfaArgs.Arg5; ImpDefArgs->Arg2 = FfaArgs.Arg6; ImpDefArgs->Arg3 = FfaArgs.Arg7; ImpDefArgs->Arg4 = FfaArgs.Arg8; ImpDefArgs->Arg5 = FfaArgs.Arg9; ImpDefArgs->Arg6 = FfaArgs.Arg10; ImpDefArgs->Arg7 = FfaArgs.Arg11; ImpDefArgs->Arg8 = FfaArgs.Arg12; ImpDefArgs->Arg9 = FfaArgs.Arg13; ImpDefArgs->Arg10 = FfaArgs.Arg14; ImpDefArgs->Arg11 = FfaArgs.Arg15; ImpDefArgs->Arg12 = FfaArgs.Arg16; ImpDefArgs->Arg13 = FfaArgs.Arg17; return EFI_SUCCESS; } /** Common ArmFfaLib init. @retval EFI_SUCCESS Success @retval EFI_UNSUPPORTED FF-A isn't supported @retval Others Error **/ EFI_STATUS EFIAPI ArmFfaLibCommonInit ( IN VOID ) { EFI_STATUS Status; UINT16 CurrentMajorVersion; UINT16 CurrentMinorVersion; ARM_FFA_ARGS FfaArgs; gFfaSupported = FALSE; ZeroMem (&FfaArgs, sizeof (ARM_SMC_ARGS)); FfaArgs.Arg0 = SMCCC_VERSION; ArmCallFfa (&FfaArgs); if ((INT32)FfaArgs.Arg0 < 0) { DEBUG ((DEBUG_ERROR, "%a: SMCCC_VERSION not supported\n", __func__)); return EFI_UNSUPPORTED; } // According to SMCCC Specification v1.6 G BET0 // Table F0-1: Changelog: Starting from SMCCC_VERSION v1.2, the interface // - Permits calls to use R4–R7 as return register // - Permits calls to use X4–X17 as return registers // - Permits calls to use X8–X17 as argument registers if ((INT32)FfaArgs.Arg0 < 0x10002) { DEBUG ((DEBUG_ERROR, "%a: SMCCC_VERSION %x < 1.2\n", __func__, (UINT32)FfaArgs.Arg0)); return EFI_UNSUPPORTED; } Status = ArmFfaLibGetVersion ( ARM_FFA_MAJOR_VERSION, ARM_FFA_MINOR_VERSION, &CurrentMajorVersion, &CurrentMinorVersion ); if (EFI_ERROR (Status)) { return EFI_UNSUPPORTED; } Status = ArmFfaLibPartitionIdGet (&gPartId); if (EFI_ERROR (Status)) { return Status; } gFfaSupported = TRUE; return EFI_SUCCESS; }