/** @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.
Copyright (c) 2021, Arm Limited. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include #include #include #include #include #include #include #include #include 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) != 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; }