| /** @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> | |
| Copyright (c) 2021, Arm Limited. All rights reserved.<BR> | |
| SPDX-License-Identifier: BSD-2-Clause-Patent | |
| **/ | |
| #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 | EFI_MEMORY_XP, | |
| 0 | |
| ); | |
| 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) != 0) { | |
| 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) != 0) { | |
| MessageArg->Return = OPTEE_ERROR_COMMUNICATION; | |
| MessageArg->ReturnOrigin = OPTEE_ORIGIN_COMMUNICATION; | |
| } | |
| if (OpteeFromMessageParam ( | |
| InvokeFunctionArg->Params, | |
| OPTEE_MAX_CALL_PARAMS, | |
| MessageArg->Params | |
| ) != 0) | |
| { | |
| MessageArg->Return = OPTEE_ERROR_COMMUNICATION; | |
| MessageArg->ReturnOrigin = OPTEE_ORIGIN_COMMUNICATION; | |
| } | |
| InvokeFunctionArg->Return = MessageArg->Return; | |
| InvokeFunctionArg->ReturnOrigin = MessageArg->ReturnOrigin; | |
| return EFI_SUCCESS; | |
| } |