audk/ArmPkg/Library/OpteeLib/Optee.c

459 lines
13 KiB
C

/** @file
Api's to communicate with OP-TEE OS (Trusted OS based on ARM TrustZone) via
secure monitor calls.
Copyright (c) 2018, Linaro Ltd. All rights reserved.<BR>
This program and the accompanying materials
are licensed and made available under the terms and conditions of the BSD License
which accompanies this distribution. The full text of the license may be found at
http://opensource.org/licenses/bsd-license.php
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
**/
#include <Library/ArmMmuLib.h>
#include <Library/ArmSmcLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/BaseLib.h>
#include <Library/DebugLib.h>
#include <Library/OpteeLib.h>
#include <IndustryStandard/ArmStdSmc.h>
#include <OpteeSmc.h>
#include <Uefi.h>
STATIC OPTEE_SHARED_MEMORY_INFORMATION OpteeSharedMemoryInformation = { 0 };
/**
Check for OP-TEE presence.
**/
BOOLEAN
EFIAPI
IsOpteePresent (
VOID
)
{
ARM_SMC_ARGS ArmSmcArgs;
ZeroMem (&ArmSmcArgs, sizeof (ARM_SMC_ARGS));
// Send a Trusted OS Calls UID command
ArmSmcArgs.Arg0 = ARM_SMC_ID_TOS_UID;
ArmCallSmc (&ArmSmcArgs);
if ((ArmSmcArgs.Arg0 == OPTEE_OS_UID0) &&
(ArmSmcArgs.Arg1 == OPTEE_OS_UID1) &&
(ArmSmcArgs.Arg2 == OPTEE_OS_UID2) &&
(ArmSmcArgs.Arg3 == OPTEE_OS_UID3)) {
return TRUE;
} else {
return FALSE;
}
}
STATIC
EFI_STATUS
OpteeSharedMemoryRemap (
VOID
)
{
ARM_SMC_ARGS ArmSmcArgs;
EFI_PHYSICAL_ADDRESS PhysicalAddress;
EFI_PHYSICAL_ADDRESS Start;
EFI_PHYSICAL_ADDRESS End;
EFI_STATUS Status;
UINTN Size;
ZeroMem (&ArmSmcArgs, sizeof (ARM_SMC_ARGS));
ArmSmcArgs.Arg0 = OPTEE_SMC_GET_SHARED_MEMORY_CONFIG;
ArmCallSmc (&ArmSmcArgs);
if (ArmSmcArgs.Arg0 != OPTEE_SMC_RETURN_OK) {
DEBUG ((DEBUG_WARN, "OP-TEE shared memory not supported\n"));
return EFI_UNSUPPORTED;
}
if (ArmSmcArgs.Arg3 != OPTEE_SMC_SHARED_MEMORY_CACHED) {
DEBUG ((DEBUG_WARN, "OP-TEE: Only normal cached shared memory supported\n"));
return EFI_UNSUPPORTED;
}
Start = (ArmSmcArgs.Arg1 + SIZE_4KB - 1) & ~(SIZE_4KB - 1);
End = (ArmSmcArgs.Arg1 + ArmSmcArgs.Arg2) & ~(SIZE_4KB - 1);
PhysicalAddress = Start;
Size = End - Start;
if (Size < SIZE_4KB) {
DEBUG ((DEBUG_WARN, "OP-TEE shared memory too small\n"));
return EFI_BUFFER_TOO_SMALL;
}
Status = ArmSetMemoryAttributes (PhysicalAddress, Size, EFI_MEMORY_WB);
if (EFI_ERROR (Status)) {
return Status;
}
OpteeSharedMemoryInformation.Base = (UINTN)PhysicalAddress;
OpteeSharedMemoryInformation.Size = Size;
return EFI_SUCCESS;
}
EFI_STATUS
EFIAPI
OpteeInit (
VOID
)
{
EFI_STATUS Status;
if (!IsOpteePresent ()) {
DEBUG ((DEBUG_WARN, "OP-TEE not present\n"));
return EFI_UNSUPPORTED;
}
Status = OpteeSharedMemoryRemap ();
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_WARN, "OP-TEE shared memory remap failed\n"));
return Status;
}
return EFI_SUCCESS;
}
STATIC
BOOLEAN
IsOpteeSmcReturnRpc (
UINT32 Return
)
{
return (Return != OPTEE_SMC_RETURN_UNKNOWN_FUNCTION) &&
((Return & OPTEE_SMC_RETURN_RPC_PREFIX_MASK) ==
OPTEE_SMC_RETURN_RPC_PREFIX);
}
/**
Does Standard SMC to OP-TEE in secure world.
@param[in] PhysicalArg Physical address of message to pass to secure world
@return 0 on success, secure world return code otherwise
**/
STATIC
UINT32
OpteeCallWithArg (
IN UINT64 PhysicalArg
)
{
ARM_SMC_ARGS ArmSmcArgs;
ZeroMem (&ArmSmcArgs, sizeof (ARM_SMC_ARGS));
ArmSmcArgs.Arg0 = OPTEE_SMC_CALL_WITH_ARG;
ArmSmcArgs.Arg1 = (UINT32)(PhysicalArg >> 32);
ArmSmcArgs.Arg2 = (UINT32)PhysicalArg;
while (TRUE) {
ArmCallSmc (&ArmSmcArgs);
if (IsOpteeSmcReturnRpc (ArmSmcArgs.Arg0)) {
switch (ArmSmcArgs.Arg0) {
case OPTEE_SMC_RETURN_RPC_FOREIGN_INTERRUPT:
//
// A foreign interrupt was raised while secure world was
// executing, since they are handled in UEFI a dummy RPC is
// performed to let UEFI take the interrupt through the normal
// vector.
//
break;
default:
// Do nothing in case RPC is not implemented.
break;
}
ArmSmcArgs.Arg0 = OPTEE_SMC_RETURN_FROM_RPC;
} else {
break;
}
}
return ArmSmcArgs.Arg0;
}
STATIC
VOID
EfiGuidToRfc4122Uuid (
OUT RFC4122_UUID *Rfc4122Uuid,
IN EFI_GUID *Guid
)
{
Rfc4122Uuid->Data1 = SwapBytes32 (Guid->Data1);
Rfc4122Uuid->Data2 = SwapBytes16 (Guid->Data2);
Rfc4122Uuid->Data3 = SwapBytes16 (Guid->Data3);
CopyMem (Rfc4122Uuid->Data4, Guid->Data4, sizeof (Rfc4122Uuid->Data4));
}
EFI_STATUS
EFIAPI
OpteeOpenSession (
IN OUT OPTEE_OPEN_SESSION_ARG *OpenSessionArg
)
{
OPTEE_MESSAGE_ARG *MessageArg;
MessageArg = NULL;
if (OpteeSharedMemoryInformation.Base == 0) {
DEBUG ((DEBUG_WARN, "OP-TEE not initialized\n"));
return EFI_NOT_STARTED;
}
MessageArg = (OPTEE_MESSAGE_ARG *)OpteeSharedMemoryInformation.Base;
ZeroMem (MessageArg, sizeof (OPTEE_MESSAGE_ARG));
MessageArg->Command = OPTEE_MESSAGE_COMMAND_OPEN_SESSION;
//
// Initialize and add the meta parameters needed when opening a
// session.
//
MessageArg->Params[0].Attribute = OPTEE_MESSAGE_ATTRIBUTE_TYPE_VALUE_INPUT |
OPTEE_MESSAGE_ATTRIBUTE_META;
MessageArg->Params[1].Attribute = OPTEE_MESSAGE_ATTRIBUTE_TYPE_VALUE_INPUT |
OPTEE_MESSAGE_ATTRIBUTE_META;
EfiGuidToRfc4122Uuid (
(RFC4122_UUID *)&MessageArg->Params[0].Union.Value,
&OpenSessionArg->Uuid
);
ZeroMem (&MessageArg->Params[1].Union.Value, sizeof (EFI_GUID));
MessageArg->Params[1].Union.Value.C = OPTEE_LOGIN_PUBLIC;
MessageArg->NumParams = 2;
if (OpteeCallWithArg ((UINTN)MessageArg)) {
MessageArg->Return = OPTEE_ERROR_COMMUNICATION;
MessageArg->ReturnOrigin = OPTEE_ORIGIN_COMMUNICATION;
}
OpenSessionArg->Session = MessageArg->Session;
OpenSessionArg->Return = MessageArg->Return;
OpenSessionArg->ReturnOrigin = MessageArg->ReturnOrigin;
return EFI_SUCCESS;
}
EFI_STATUS
EFIAPI
OpteeCloseSession (
IN UINT32 Session
)
{
OPTEE_MESSAGE_ARG *MessageArg;
MessageArg = NULL;
if (OpteeSharedMemoryInformation.Base == 0) {
DEBUG ((DEBUG_WARN, "OP-TEE not initialized\n"));
return EFI_NOT_STARTED;
}
MessageArg = (OPTEE_MESSAGE_ARG *)OpteeSharedMemoryInformation.Base;
ZeroMem (MessageArg, sizeof (OPTEE_MESSAGE_ARG));
MessageArg->Command = OPTEE_MESSAGE_COMMAND_CLOSE_SESSION;
MessageArg->Session = Session;
OpteeCallWithArg ((UINTN)MessageArg);
return EFI_SUCCESS;
}
STATIC
EFI_STATUS
OpteeToMessageParam (
OUT OPTEE_MESSAGE_PARAM *MessageParams,
IN UINT32 NumParams,
IN OPTEE_MESSAGE_PARAM *InParams
)
{
UINT32 Idx;
UINTN ParamSharedMemoryAddress;
UINTN SharedMemorySize;
UINTN Size;
Size = (sizeof (OPTEE_MESSAGE_ARG) + sizeof (UINT64) - 1) &
~(sizeof (UINT64) - 1);
ParamSharedMemoryAddress = OpteeSharedMemoryInformation.Base + Size;
SharedMemorySize = OpteeSharedMemoryInformation.Size - Size;
for (Idx = 0; Idx < NumParams; Idx++) {
CONST OPTEE_MESSAGE_PARAM *InParam;
OPTEE_MESSAGE_PARAM *MessageParam;
UINT32 Attribute;
InParam = InParams + Idx;
MessageParam = MessageParams + Idx;
Attribute = InParam->Attribute & OPTEE_MESSAGE_ATTRIBUTE_TYPE_MASK;
switch (Attribute) {
case OPTEE_MESSAGE_ATTRIBUTE_TYPE_NONE:
MessageParam->Attribute = OPTEE_MESSAGE_ATTRIBUTE_TYPE_NONE;
ZeroMem (&MessageParam->Union, sizeof (MessageParam->Union));
break;
case OPTEE_MESSAGE_ATTRIBUTE_TYPE_VALUE_INPUT:
case OPTEE_MESSAGE_ATTRIBUTE_TYPE_VALUE_OUTPUT:
case OPTEE_MESSAGE_ATTRIBUTE_TYPE_VALUE_INOUT:
MessageParam->Attribute = Attribute;
MessageParam->Union.Value.A = InParam->Union.Value.A;
MessageParam->Union.Value.B = InParam->Union.Value.B;
MessageParam->Union.Value.C = InParam->Union.Value.C;
break;
case OPTEE_MESSAGE_ATTRIBUTE_TYPE_MEMORY_INPUT:
case OPTEE_MESSAGE_ATTRIBUTE_TYPE_MEMORY_OUTPUT:
case OPTEE_MESSAGE_ATTRIBUTE_TYPE_MEMORY_INOUT:
MessageParam->Attribute = Attribute;
if (InParam->Union.Memory.Size > SharedMemorySize) {
return EFI_OUT_OF_RESOURCES;
}
CopyMem (
(VOID *)ParamSharedMemoryAddress,
(VOID *)(UINTN)InParam->Union.Memory.BufferAddress,
InParam->Union.Memory.Size
);
MessageParam->Union.Memory.BufferAddress = (UINT64)ParamSharedMemoryAddress;
MessageParam->Union.Memory.Size = InParam->Union.Memory.Size;
Size = (InParam->Union.Memory.Size + sizeof (UINT64) - 1) &
~(sizeof (UINT64) - 1);
ParamSharedMemoryAddress += Size;
SharedMemorySize -= Size;
break;
default:
return EFI_INVALID_PARAMETER;
}
}
return EFI_SUCCESS;
}
STATIC
EFI_STATUS
OpteeFromMessageParam (
OUT OPTEE_MESSAGE_PARAM *OutParams,
IN UINT32 NumParams,
IN OPTEE_MESSAGE_PARAM *MessageParams
)
{
UINT32 Idx;
for (Idx = 0; Idx < NumParams; Idx++) {
OPTEE_MESSAGE_PARAM *OutParam;
CONST OPTEE_MESSAGE_PARAM *MessageParam;
UINT32 Attribute;
OutParam = OutParams + Idx;
MessageParam = MessageParams + Idx;
Attribute = MessageParam->Attribute & OPTEE_MESSAGE_ATTRIBUTE_TYPE_MASK;
switch (Attribute) {
case OPTEE_MESSAGE_ATTRIBUTE_TYPE_NONE:
OutParam->Attribute = OPTEE_MESSAGE_ATTRIBUTE_TYPE_NONE;
ZeroMem (&OutParam->Union, sizeof (OutParam->Union));
break;
case OPTEE_MESSAGE_ATTRIBUTE_TYPE_VALUE_INPUT:
case OPTEE_MESSAGE_ATTRIBUTE_TYPE_VALUE_OUTPUT:
case OPTEE_MESSAGE_ATTRIBUTE_TYPE_VALUE_INOUT:
OutParam->Attribute = Attribute;
OutParam->Union.Value.A = MessageParam->Union.Value.A;
OutParam->Union.Value.B = MessageParam->Union.Value.B;
OutParam->Union.Value.C = MessageParam->Union.Value.C;
break;
case OPTEE_MESSAGE_ATTRIBUTE_TYPE_MEMORY_INPUT:
case OPTEE_MESSAGE_ATTRIBUTE_TYPE_MEMORY_OUTPUT:
case OPTEE_MESSAGE_ATTRIBUTE_TYPE_MEMORY_INOUT:
OutParam->Attribute = Attribute;
if (MessageParam->Union.Memory.Size > OutParam->Union.Memory.Size) {
return EFI_BAD_BUFFER_SIZE;
}
CopyMem (
(VOID *)(UINTN)OutParam->Union.Memory.BufferAddress,
(VOID *)(UINTN)MessageParam->Union.Memory.BufferAddress,
MessageParam->Union.Memory.Size
);
OutParam->Union.Memory.Size = MessageParam->Union.Memory.Size;
break;
default:
return EFI_INVALID_PARAMETER;
}
}
return EFI_SUCCESS;
}
EFI_STATUS
EFIAPI
OpteeInvokeFunction (
IN OUT OPTEE_INVOKE_FUNCTION_ARG *InvokeFunctionArg
)
{
EFI_STATUS Status;
OPTEE_MESSAGE_ARG *MessageArg;
MessageArg = NULL;
if (OpteeSharedMemoryInformation.Base == 0) {
DEBUG ((DEBUG_WARN, "OP-TEE not initialized\n"));
return EFI_NOT_STARTED;
}
MessageArg = (OPTEE_MESSAGE_ARG *)OpteeSharedMemoryInformation.Base;
ZeroMem (MessageArg, sizeof (OPTEE_MESSAGE_ARG));
MessageArg->Command = OPTEE_MESSAGE_COMMAND_INVOKE_FUNCTION;
MessageArg->Function = InvokeFunctionArg->Function;
MessageArg->Session = InvokeFunctionArg->Session;
Status = OpteeToMessageParam (
MessageArg->Params,
OPTEE_MAX_CALL_PARAMS,
InvokeFunctionArg->Params
);
if (Status) {
return Status;
}
MessageArg->NumParams = OPTEE_MAX_CALL_PARAMS;
if (OpteeCallWithArg ((UINTN)MessageArg)) {
MessageArg->Return = OPTEE_ERROR_COMMUNICATION;
MessageArg->ReturnOrigin = OPTEE_ORIGIN_COMMUNICATION;
}
if (OpteeFromMessageParam (
InvokeFunctionArg->Params,
OPTEE_MAX_CALL_PARAMS,
MessageArg->Params
)) {
MessageArg->Return = OPTEE_ERROR_COMMUNICATION;
MessageArg->ReturnOrigin = OPTEE_ORIGIN_COMMUNICATION;
}
InvokeFunctionArg->Return = MessageArg->Return;
InvokeFunctionArg->ReturnOrigin = MessageArg->ReturnOrigin;
return EFI_SUCCESS;
}