edk2/MdeModulePkg/Library/ArmFfaLib/ArmFfaCommon.c

801 lines
18 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/** @file
Arm Ffa library common code.
Copyright (c) 2024, Arm Limited. All rights reserved.<BR>
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 <Uefi.h>
#include <Pi/PiMultiPhase.h>
#include <Library/ArmLib.h>
#include <Library/ArmFfaLib.h>
#include <Library/ArmSmcLib.h>
#include <Library/ArmSvcLib.h>
#include <Library/BaseLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/DebugLib.h>
#include <Library/PcdLib.h>
#include <IndustryStandard/ArmFfaSvc.h>
#include <IndustryStandard/ArmFfaPartInfo.h>
#include <IndustryStandard/ArmStdSmc.h>
#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 R4R7 as return register
// - Permits calls to use X4X17 as return registers
// - Permits calls to use X8X17 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;
}