diff --git a/ArmPkg/ArmPkg.dsc b/ArmPkg/ArmPkg.dsc index 85d3f49c18..8f64eb4c9b 100644 --- a/ArmPkg/ArmPkg.dsc +++ b/ArmPkg/ArmPkg.dsc @@ -70,6 +70,7 @@ ArmGicLib|ArmPkg/Drivers/ArmGic/ArmGicLib.inf ArmGicArchLib|ArmPkg/Library/ArmGicArchLib/ArmGicArchLib.inf ArmGenericTimerCounterLib|ArmPkg/Library/ArmGenericTimerPhyCounterLib/ArmGenericTimerPhyCounterLib.inf + ArmSvcLib|ArmPkg/Library/ArmSvcLib/ArmSvcLib.inf ArmSmcLib|ArmPkg/Library/ArmSmcLib/ArmSmcLib.inf ArmDisassemblerLib|ArmPkg/Library/ArmDisassemblerLib/ArmDisassemblerLib.inf OpteeLib|ArmPkg/Library/OpteeLib/OpteeLib.inf @@ -87,6 +88,7 @@ ArmLib|ArmPkg/Library/ArmLib/ArmBaseLib.inf ArmMmuLib|ArmPkg/Library/ArmMmuLib/ArmMmuBaseLib.inf ArmTransferListLib|ArmPkg/Library/ArmTransferListLib/ArmTransferListLib.inf + ArmFfaLib|ArmPkg/Library/ArmFfaLib/ArmFfaDxeLib.inf ArmMtlLib|ArmPkg/Library/ArmMtlNullLib/ArmMtlNullLib.inf @@ -131,6 +133,7 @@ ArmPkg/Library/ArmSvcLib/ArmSvcLib.inf ArmPkg/Library/OpteeLib/OpteeLib.inf ArmPkg/Library/ArmTransferListLib/ArmTransferListLib.inf + ArmPkg/Library/ArmFfaLib/ArmFfaDxeLib.inf ArmPkg/Filesystem/SemihostFs/SemihostFs.inf diff --git a/ArmPkg/Library/ArmFfaLib/ArmFfaCommon.c b/ArmPkg/Library/ArmFfaLib/ArmFfaCommon.c new file mode 100644 index 0000000000..4a7c3fbd67 --- /dev/null +++ b/ArmPkg/Library/ArmFfaLib/ArmFfaCommon.c @@ -0,0 +1,760 @@ +/** @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 "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; + + 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; + + 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; + + gFfaSupported = FALSE; + + 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; +} diff --git a/ArmPkg/Library/ArmFfaLib/ArmFfaCommon.h b/ArmPkg/Library/ArmFfaLib/ArmFfaCommon.h new file mode 100644 index 0000000000..f87c7f3b04 --- /dev/null +++ b/ArmPkg/Library/ArmFfaLib/ArmFfaCommon.h @@ -0,0 +1,76 @@ +/** @file + Arm FF-A ns common library Header file + + 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 + - spmc - Secure Partition Manager Core + - spmd - Secure Partition Manager Dispatcher + + @par Reference(s): + - Arm Firmware Framework for Arm A-Profile [https://developer.arm.com/documentation/den0077/latest] + +**/ + +#ifndef ARM_FFA_COMMON_LIB_H_ +#define ARM_FFA_COMMON_LIB_H_ + +/** + * Arguments to call FF-A request via SMC/SVC. + */ +typedef struct ArmFfaArgs { + UINTN Arg0; + UINTN Arg1; + UINTN Arg2; + UINTN Arg3; + UINTN Arg4; + UINTN Arg5; + UINTN Arg6; + UINTN Arg7; +} ARM_FFA_ARGS; + +extern BOOLEAN gFfaSupported; +extern UINT16 gPartId; + +/** + 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 + ); + +/** + Trigger FF-A ABI call according to PcdFfaLibConduitSmc. + + @param [in out] FfaArgs Ffa arguments + +**/ +VOID +EFIAPI +ArmCallFfa ( + IN OUT ARM_FFA_ARGS *FfaArgs + ); + +/** + Common ArmFfaLib Constructor. + + @retval EFI_SUCCESS + @retval Others Error + +**/ +EFI_STATUS +EFIAPI +ArmFfaLibCommonInit ( + IN VOID + ); + +#endif diff --git a/ArmPkg/Library/ArmFfaLib/ArmFfaDxeLib.c b/ArmPkg/Library/ArmFfaLib/ArmFfaDxeLib.c new file mode 100644 index 0000000000..4d82844ee1 --- /dev/null +++ b/ArmPkg/Library/ArmFfaLib/ArmFfaDxeLib.c @@ -0,0 +1,158 @@ +/** @file + Arm Ffa library code for Dxe Driver + + 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" +#include "ArmFfaRxTxMap.h" + +STATIC EFI_EVENT mFfaExitBootServiceEvent; + +/** + Unmap RX/TX buffer on Exit Boot Service. + + @param [in] Event Registered exit boot service event. + @param [in] Context Additional data. + +**/ +STATIC +VOID +EFIAPI +ArmFfaLibExitBootServiceEvent ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + ArmFfaLibRxTxUnmap (); +} + +/** + ArmFfaLib Constructor. + + @param [in] ImageHandle Image Handle + @param [in] SystemTable System Table + + @retval EFI_SUCCESS Success + @retval EFI_INVALID_PARAMETER Invalid alignment of Rx/Tx buffer + @retval EFI_OUT_OF_RESOURCES Out of memory + @retval Others Error + +**/ +EFI_STATUS +EFIAPI +ArmFfaDxeLibConstructor ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + EFI_HOB_GUID_TYPE *RxTxBufferHob; + ARM_FFA_RX_TX_BUFFER_INFO *BufferInfo; + + Status = ArmFfaLibCommonInit (); + if (EFI_ERROR (Status)) { + if (Status == EFI_UNSUPPORTED) { + /* + * EFI_UNSUPPORTED return from ArmFfaLibCommonInit() means + * FF-A interface doesn't support. + * However, It doesn't make failure of loading driver/library instance + * (i.e) ArmPkg's MmCommunication Dxe/PEI Driver uses as well as SpmMm. + * So If FF-A is not supported the the MmCommunication Dxe/PEI falls + * back to SpmMm. + * For this case, return EFI_SUCCESS. + */ + return EFI_SUCCESS; + } + + return Status; + } + + if (PcdGetBool (PcdFfaExitBootEventRegistered)) { + return EFI_SUCCESS; + } + + /* + * If PEIM uses ArmFfaPeiLib, the Rx/Tx buffers is already mapped in PEI phase. + * In this case, get Rx/Tx buffer info from Hob. + */ + RxTxBufferHob = GetFirstGuidHob (&gArmFfaRxTxBufferInfoGuid); + if (RxTxBufferHob != NULL) { + BufferInfo = GET_GUID_HOB_DATA (RxTxBufferHob); + PcdSet64S (PcdFfaTxBuffer, (UINTN)BufferInfo->TxBufferAddr); + PcdSet64S (PcdFfaRxBuffer, (UINTN)BufferInfo->RxBufferAddr); + } else { + Status = ArmFfaLibRxTxMap (); + + /* + * When first Dxe instance (library or driver) which uses ArmFfaLib loaded, + * It already maps Rx/Tx buffer. + * From Next Dxe instance which uses ArmFfaLib it doesn't need to map Rx/Tx + * buffer again but it uses the mapped one. + * ArmFfaLibRxTxMap() returns EFI_ALREADY_STARTED when the Rx/Tx buffers + * already maps. + */ + if ((Status != EFI_SUCCESS) && (Status != EFI_ALREADY_STARTED)) { + DEBUG (( + DEBUG_ERROR, + "%a: Failed to Map Rx/Tx buffer. Status: %r\n", + __func__, + Status + )); + return Status; + } + } + + Status = gBS->CreateEventEx ( + EVT_NOTIFY_SIGNAL, + TPL_CALLBACK, + ArmFfaLibExitBootServiceEvent, + NULL, + &gEfiEventExitBootServicesGuid, + &mFfaExitBootServiceEvent + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "%a: Failed to register ExitBootService event. Status: %r\n", + __func__, + Status + )); + goto ErrorHandler; + } + + PcdSetBoolS (PcdFfaExitBootEventRegistered, TRUE); + + return EFI_SUCCESS; + +ErrorHandler: + if (RxTxBufferHob != NULL) { + ArmFfaLibRxTxUnmap (); + } + + return Status; +} diff --git a/ArmPkg/Library/ArmFfaLib/ArmFfaDxeLib.inf b/ArmPkg/Library/ArmFfaLib/ArmFfaDxeLib.inf new file mode 100644 index 0000000000..96525cfba3 --- /dev/null +++ b/ArmPkg/Library/ArmFfaLib/ArmFfaDxeLib.inf @@ -0,0 +1,46 @@ +## @file +# Provides FF-A ABI Library used in Dxe Driver. +# +# Copyright (c) 2024, Arm Limited. All rights reserved.
+# +# SPDX-License-Identifier: BSD-2-Clause-Patent +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = ArmFfaDxeLib + FILE_GUID = e2a8e040-5346-11ef-8454-eff3c163f615 + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + LIBRARY_CLASS = ArmFfaLib|DXE_DRIVER DXE_RUNTIME_DRIVER UEFI_APPLICATION UEFI_DRIVER + CONSTRUCTOR = ArmFfaDxeLibConstructor + +[Sources] + ArmFfaCommon.h + ArmFfaCommon.c + ArmFfaRxTxMap.h + ArmFfaRxTxMap.c + ArmFfaDxeLib.c + +[Packages] + MdePkg/MdePkg.dec + ArmPkg/ArmPkg.dec + +[LibraryClasses] + ArmSmcLib + ArmSvcLib + BaseLib + BaseMemoryLib + DebugLib + HobLib + +[Pcd] + gArmTokenSpaceGuid.PcdFfaLibConduitSmc + gArmTokenSpaceGuid.PcdFfaTxBuffer + gArmTokenSpaceGuid.PcdFfaRxBuffer + gArmTokenSpaceGuid.PcdFfaTxRxPageCount + gArmTokenSpaceGuid.PcdFfaExitBootEventRegistered + +[Guids] + gArmFfaRxTxBufferInfoGuid + gEfiEventExitBootServicesGuid diff --git a/ArmPkg/Library/ArmFfaLib/ArmFfaRxTxMap.c b/ArmPkg/Library/ArmFfaLib/ArmFfaRxTxMap.c new file mode 100644 index 0000000000..37d3e80c3c --- /dev/null +++ b/ArmPkg/Library/ArmFfaLib/ArmFfaRxTxMap.c @@ -0,0 +1,272 @@ +/** @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 "ArmFfaCommon.h" +#include "ArmFfaRxTxMap.h" + +/** + Get mapped Rx/Tx buffers. + + @param [out] TxBuffer Address of TxBuffer + @param [out] TxBufferSize Size of TxBuffer + @param [out] RxBuffer Address of RxBuffer + @param [out] RxBufferSize Size of RxBuffer + + @retval EFI_SUCCESS + @retval Others Error. + +**/ +EFI_STATUS +EFIAPI +ArmFfaLibGetRxTxBuffers ( + OUT VOID **TxBuffer OPTIONAL, + OUT UINT64 *TxBufferSize OPTIONAL, + OUT VOID **RxBuffer OPTIONAL, + OUT UINT64 *RxBufferSize OPTIONAL + ) +{ + UINTN TxBufferAddr; + UINTN RxBufferAddr; + + TxBufferAddr = (UINTN)PcdGet64 (PcdFfaTxBuffer); + RxBufferAddr = (UINTN)PcdGet64 (PcdFfaRxBuffer); + + if ((TxBufferAddr == 0x00) || (RxBufferAddr == 0x00)) { + return EFI_NOT_READY; + } + + if (TxBuffer != NULL) { + *TxBuffer = (VOID *)TxBufferAddr; + } + + if (TxBufferSize != NULL) { + *TxBufferSize = PcdGet64 (PcdFfaTxRxPageCount) * EFI_PAGE_SIZE; + } + + if (RxBuffer != NULL) { + *RxBuffer = (VOID *)RxBufferAddr; + } + + if (RxBufferSize != NULL) { + *RxBufferSize = PcdGet64 (PcdFfaTxRxPageCount) * EFI_PAGE_SIZE; + } + + return EFI_SUCCESS; +} + +/** + Mapping Rx/Tx buffers. + This function is only called in ArmFfaLibConstructor because + Rx/Tx buffer is registered only once per partition. + + @retval EFI_SUCCESS + @retval EFI_ALREADY_STARTED Rx/Tx buffer already mapped. + @retval EFI_OUT_OF_RESOURCE Out of memory + @retval EFI_INVALID_PARAMETER Invalid alignment of Rx/Tx buffer + @retval Others Error + +**/ +EFI_STATUS +EFIAPI +ArmFfaLibRxTxMap ( + IN VOID + ) +{ + EFI_STATUS Status; + ARM_FFA_ARGS FfaArgs; + UINTN Property1; + UINTN Property2; + UINTN MinSizeAndAlign; + UINTN MaxSize; + VOID *Buffers; + VOID *TxBuffer; + VOID *RxBuffer; + UINT64 BufferSize; + + TxBuffer = (VOID *)(UINTN)PcdGet64 (PcdFfaTxBuffer); + RxBuffer = (VOID *)(UINTN)PcdGet64 (PcdFfaRxBuffer); + BufferSize = PcdGet64 (PcdFfaTxRxPageCount) * EFI_PAGE_SIZE; + + /* + * If someone already mapped Rx/Tx Buffers, return EFI_ALREADY_STARTED. + * return EFI_ALREADY_STARTED. + */ + if ((TxBuffer != NULL) && (RxBuffer != NULL)) { + return EFI_ALREADY_STARTED; + } + + Status = ArmFfaLibGetFeatures ( + ARM_FID_FFA_RXTX_MAP, + FFA_RXTX_MAP_INPUT_PROPERTY_DEFAULT, + &Property1, + &Property2 + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "%a: Failed to get RX/TX buffer property... Status: %r\n", + __func__, + Status + )); + return Status; + } + + ZeroMem (&FfaArgs, sizeof (ARM_FFA_ARGS)); + + MinSizeAndAlign = + ((Property1 >> + ARM_FFA_BUFFER_MINSIZE_AND_ALIGN_SHIFT) & + ARM_FFA_BUFFER_MINSIZE_AND_ALIGN_MASK); + + switch (MinSizeAndAlign) { + case ARM_FFA_BUFFER_MINSIZE_AND_ALIGN_4K: + MinSizeAndAlign = SIZE_4KB; + break; + case ARM_FFA_BUFFER_MINSIZE_AND_ALIGN_16K: + MinSizeAndAlign = SIZE_16KB; + break; + case ARM_FFA_BUFFER_MINSIZE_AND_ALIGN_64K: + MinSizeAndAlign = SIZE_64KB; + break; + default: + DEBUG ((DEBUG_ERROR, "%a: Invalid MinSizeAndAlign: 0x%x\n", __func__, MinSizeAndAlign)); + return EFI_UNSUPPORTED; + } + + MaxSize = + (((Property1 >> + ARM_FFA_BUFFER_MAXSIZE_PAGE_COUNT_SHIFT) & + ARM_FFA_BUFFER_MAXSIZE_PAGE_COUNT_MASK)); + + MaxSize = ((MaxSize == 0) ? MAX_UINTN : (MaxSize * MinSizeAndAlign)); + + if ((MinSizeAndAlign > (PcdGet64 (PcdFfaTxRxPageCount) * EFI_PAGE_SIZE)) || + (MaxSize < (PcdGet64 (PcdFfaTxRxPageCount) * EFI_PAGE_SIZE))) + { + DEBUG (( + DEBUG_ERROR, + "%a: Buffer is too small! MinSize: 0x%x, MaxSize: 0x%x, PageCount: %d\n", + __func__, + MinSizeAndAlign, + MaxSize, + PcdGet64 (PcdFfaTxRxPageCount) + )); + return EFI_INVALID_PARAMETER; + } + + Buffers = AllocateAlignedPages ((PcdGet64 (PcdFfaTxRxPageCount) * 2), MinSizeAndAlign); + if (Buffers == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + TxBuffer = Buffers; + RxBuffer = Buffers + BufferSize; + + FfaArgs.Arg0 = ARM_FID_FFA_RXTX_MAP; + FfaArgs.Arg1 = (UINTN)TxBuffer; + FfaArgs.Arg2 = (UINTN)RxBuffer; + + /* + * PcdFfaTxRxPageCount sets with count of EFI_PAGE_SIZE granularity + * But, PageCounts for Tx/Rx buffer should set with + * count of Tx/Rx Buffer's MinSizeAndAlign. granularity. + */ + FfaArgs.Arg3 = PcdGet64 (PcdFfaTxRxPageCount) / EFI_SIZE_TO_PAGES (MinSizeAndAlign); + + ArmCallFfa (&FfaArgs); + + Status = FfaArgsToEfiStatus (&FfaArgs); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "%a: Failed to map Rx/Tx buffer. Status: %r\n", + __func__, + Status + )); + goto ErrorHandler; + } + + PcdSet64S (PcdFfaTxBuffer, (UINTN)TxBuffer); + PcdSet64S (PcdFfaRxBuffer, (UINTN)RxBuffer); + + return EFI_SUCCESS; + +ErrorHandler: + FreeAlignedPages (Buffers, (PcdGet64 (PcdFfaTxRxPageCount) * 2)); + TxBuffer = NULL; + RxBuffer = NULL; + + return Status; +} + +/** + Unmap Rx/Tx buffer. + This function is only called in Exit boot service because + Rx/Tx buffer is registered only once per partition. + + @retval EFI_SUCCESS + @retval EFI_INVALID_PARAMETERS Already unregistered + @retval EFI_UNSUPPORTED Not supported + +**/ +EFI_STATUS +EFIAPI +ArmFfaLibRxTxUnmap ( + IN VOID + ) +{ + EFI_STATUS Status; + ARM_FFA_ARGS FfaArgs; + VOID *Buffers; + + ZeroMem (&FfaArgs, sizeof (ARM_FFA_ARGS)); + + FfaArgs.Arg0 = ARM_FID_FFA_RXTX_UNMAP; + FfaArgs.Arg1 = (gPartId << ARM_FFA_SOURCE_EP_SHIFT); + + ArmCallFfa (&FfaArgs); + + Status = FfaArgsToEfiStatus (&FfaArgs); + if (EFI_ERROR (Status)) { + return Status; + } + + /* + * Rx/Tx Buffer are allocated with continuous pages. + * and start address of these pages is set on PcdFfaTxBuffer. + * See ArmFfaLibRxTxMap(). + */ + Buffers = (VOID *)(UINTN)PcdGet64 (PcdFfaTxBuffer); + if (Buffers != NULL) { + FreeAlignedPages (Buffers, (PcdGet64 (PcdFfaTxRxPageCount) * 2)); + } + + PcdSet64S (PcdFfaTxBuffer, 0x00); + PcdSet64S (PcdFfaRxBuffer, 0x00); + + return EFI_SUCCESS; +} diff --git a/ArmPkg/Library/ArmFfaLib/ArmFfaRxTxMap.h b/ArmPkg/Library/ArmFfaLib/ArmFfaRxTxMap.h new file mode 100644 index 0000000000..01db339108 --- /dev/null +++ b/ArmPkg/Library/ArmFfaLib/ArmFfaRxTxMap.h @@ -0,0 +1,71 @@ +/** @file + Arm FF-A ns common library Header file + + 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 + - spmc - Secure Partition Manager Core + - spmd - Secure Partition Manager Dispatcher + + @par Reference(s): + - Arm Firmware Framework for Arm A-Profile [https://developer.arm.com/documentation/den0077/latest] + +**/ + +#ifndef ARM_FFA_RX_TX_MAP_LIB_H_ +#define ARM_FFA_RX_TX_MAP_LIB_H_ + +/** + * Guid Hob Data for gArmFfaRxTxBufferInfoGuid Guid Hob. + */ +typedef struct ArmFfaRxTxBuffersInfo { + /// Tx Buffer Address. + VOID *TxBufferAddr; + + /// Tx Buffer Size. + UINT64 TxBufferSize; + + /// Rx Buffer Address. + VOID *RxBufferAddr; + + /// Rx Buffer Size. + UINT64 RxBufferSize; +} ARM_FFA_RX_TX_BUFFER_INFO; + +/** + Mapping Rx/Tx buffers. + This function is only called in ArmFfaLibConstructor because + Rx/Tx buffer is registered only once per partition. + + @retval EFI_SUCCESS + @retval EFI_ALREADY_STARTED Rx/Tx buffer already mapped in PEI phase + @retval EFI_OUT_OF_RESOURCE Out of memory + @retval EFI_INVALID_PARAMETER Invalid alignment of Rx/Tx buffer + @retval Others Error + +**/ +EFI_STATUS +EFIAPI +ArmFfaLibRxTxMap ( + IN VOID + ); + +/** + Unmap Rx/Tx buffer. + This function is only called in Exit boot service because + Rx/Tx buffer is registered only once per partition. + + @retval EFI_SUCCESS + @retval EFI_INVALID_PARAMETERS Already unregistered + @retval EFI_UNSUPPORTED Not supported + +**/ +EFI_STATUS +EFIAPI +ArmFfaLibRxTxUnmap ( + IN VOID + ); + +#endif