450 lines
10 KiB
C
450 lines
10 KiB
C
/*
|
|
* Copyright 2017, Rockchip Electronics Co., Ltd
|
|
* hisping lin, <hisping.lin@rock-chips.com>
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0+
|
|
*/
|
|
#include <common.h>
|
|
#include <optee_include/OpteeClientApiLib.h>
|
|
#include <optee_include/OpteeClientMem.h>
|
|
#include <optee_include/OpteeClientRPC.h>
|
|
#include <optee_include/OpteeClientSMC.h>
|
|
#include <optee_include/OpteeClientRkFs.h>
|
|
#include <optee_include/teesmc.h>
|
|
#include <optee_include/teesmc_optee.h>
|
|
#include <optee_include/teesmc_v2.h>
|
|
|
|
#define OPTEE_MSG_REVISION_MAJOR 2
|
|
#define OPTEE_MSG_REVISION_MINOR 0
|
|
|
|
static bool optee_is_init;
|
|
|
|
static bool optee_vm_create(uint32_t client_id)
|
|
{
|
|
ARM_SMC_ARGS ArmSmcArgs = {0};
|
|
|
|
ArmSmcArgs.Arg0 = OPTEE_SMC_VM_CREATED;
|
|
ArmSmcArgs.Arg1 = client_id;
|
|
|
|
tee_smc_call(&ArmSmcArgs);
|
|
|
|
if (ArmSmcArgs.Arg0 != 0)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool optee_vm_destroyed(uint32_t client_id)
|
|
{
|
|
ARM_SMC_ARGS ArmSmcArgs = {0};
|
|
|
|
ArmSmcArgs.Arg0 = OPTEE_SMC_VM_DESTROYED;
|
|
ArmSmcArgs.Arg1 = client_id;
|
|
|
|
tee_smc_call(&ArmSmcArgs);
|
|
|
|
if (ArmSmcArgs.Arg0 != 0)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool optee_exchange_capabilities(uint32_t *sec_caps)
|
|
{
|
|
ARM_SMC_ARGS ArmSmcArgs = {0};
|
|
|
|
ArmSmcArgs.Arg0 = OPTEE_SMC_EXCHANGE_CAPABILITIES;
|
|
|
|
tee_smc_call(&ArmSmcArgs);
|
|
|
|
if (ArmSmcArgs.Arg0 != 0)
|
|
return false;
|
|
*sec_caps = ArmSmcArgs.Arg1;
|
|
return true;
|
|
}
|
|
|
|
static bool optee_api_revision_is_compatible(void)
|
|
{
|
|
ARM_SMC_ARGS ArmSmcArgs = {0};
|
|
|
|
ArmSmcArgs.Arg0 = OPTEE_SMC_CALLS_REVISION;
|
|
|
|
tee_smc_call(&ArmSmcArgs);
|
|
|
|
if (ArmSmcArgs.Arg0 == OPTEE_MSG_REVISION_MAJOR &&
|
|
ArmSmcArgs.Arg1 >= OPTEE_MSG_REVISION_MINOR) {
|
|
printf("optee api revision: %d.%d\n",
|
|
ArmSmcArgs.Arg0, ArmSmcArgs.Arg1);
|
|
return true;
|
|
} else {
|
|
printf("optee check api revision fail: %d.%d\n",
|
|
ArmSmcArgs.Arg0, ArmSmcArgs.Arg1);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void optee_get_shm_config(phys_addr_t *base, phys_size_t *size)
|
|
{
|
|
ARM_SMC_ARGS ArmSmcArgs = {0};
|
|
|
|
ArmSmcArgs.Arg0 = OPTEE_SMC_GET_SHM_CONFIG_V2;
|
|
|
|
tee_smc_call(&ArmSmcArgs);
|
|
|
|
*base = ArmSmcArgs.Arg1;
|
|
*size = ArmSmcArgs.Arg2;
|
|
}
|
|
|
|
/*
|
|
* Initlialize the library
|
|
*/
|
|
TEEC_Result OpteeClientApiLibInitialize(void)
|
|
{
|
|
TEEC_Result status = TEEC_SUCCESS;
|
|
|
|
if (optee_is_init)
|
|
return TEEC_SUCCESS;
|
|
|
|
/* check api revision compatibility */
|
|
if (!optee_api_revision_is_compatible())
|
|
panic("optee api revision is too low");
|
|
|
|
status = OpteeClientMemInit();
|
|
if (status != TEEC_SUCCESS) {
|
|
printf("TEEC: OpteeClientMemInit fail!\n");
|
|
return status;
|
|
}
|
|
status = OpteeClientRkFsInit();
|
|
if (status != TEEC_SUCCESS) {
|
|
printf("TEEC: OpteeClientRkFsInit fail!\n");
|
|
return status;
|
|
}
|
|
|
|
optee_is_init = true;
|
|
|
|
return TEEC_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
* This function initializes a new TEE Context, connecting this Client
|
|
* application to the TEE indentified by the name name.
|
|
*
|
|
* name == NULL will give the default TEE.
|
|
*
|
|
* In this implementation only the default name is supported.
|
|
* If name != NULL then TEEC_ERROR_ITEM_NOT_FOUND is returned.
|
|
*/
|
|
TEEC_Result TEEC_InitializeContext(const char *name,
|
|
TEEC_Context *context)
|
|
{
|
|
TEEC_Result teecresult = TEEC_SUCCESS;
|
|
uint32_t sec_caps = 0;
|
|
|
|
debug("TEEC_InitializeContext Enter: name=%s context=%s 0x%X\n",
|
|
name, context->devname, context->fd);
|
|
|
|
|
|
if (context == NULL) {
|
|
teecresult = TEEC_ERROR_BAD_PARAMETERS;
|
|
goto exit;
|
|
}
|
|
|
|
if (name != NULL) {
|
|
teecresult = TEEC_ERROR_ITEM_NOT_FOUND;
|
|
goto exit;
|
|
}
|
|
|
|
memset(context, 0, sizeof(*context));
|
|
|
|
/* get optee capabilities */
|
|
if (!optee_exchange_capabilities(&sec_caps))
|
|
panic("optee exchange capabilities fail!");
|
|
|
|
/* optee vm create */
|
|
if (sec_caps & OPTEE_SMC_SEC_CAP_VIRTUALIZATION) {
|
|
if (!optee_vm_create(0))
|
|
panic("optee vm create fail!");
|
|
}
|
|
|
|
exit:
|
|
debug("TEEC_InitializeContext Exit : teecresult=0x%X\n", teecresult);
|
|
return teecresult;
|
|
}
|
|
|
|
/*
|
|
* This function destroys an initialized TEE Context, closing the connection
|
|
* between the Client and the TEE.
|
|
* The function implementation MUST do nothing if context is NULL
|
|
*
|
|
* There is nothing to do here since there is no context state.
|
|
*/
|
|
TEEC_Result TEEC_FinalizeContext(TEEC_Context *context)
|
|
{
|
|
uint32_t sec_caps = 0;
|
|
|
|
/* get optee capabilities */
|
|
if (!optee_exchange_capabilities(&sec_caps))
|
|
panic("optee exchange capabilities fail!");
|
|
|
|
/* optee vm destroyed */
|
|
if (sec_caps & OPTEE_SMC_SEC_CAP_VIRTUALIZATION) {
|
|
if (!optee_vm_destroyed(0))
|
|
panic("optee vm destroyed fail!");
|
|
}
|
|
|
|
debug("TEEC_FinalizeContext Enter-Exit: context=0x%zu\n",
|
|
(size_t)context);
|
|
return TEEC_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
* Allocates or registers shared memory.
|
|
*
|
|
* Since EDK2 is configured flat with virtual memory == physical memory
|
|
* then we don't need to perform any special operations to get physical
|
|
* contiguous memory.
|
|
*/
|
|
TEEC_Result TEEC_AllocateSharedMemory(TEEC_Context *context,
|
|
TEEC_SharedMemory *shared_memory)
|
|
{
|
|
TEEC_Result TeecResult = TEEC_SUCCESS;
|
|
|
|
debug("TEEC_AllocateSharedMemory Enter: context=%s 0x%X, shared_memory=0x%zu\n",
|
|
context->devname, context->fd, shared_memory->size);
|
|
|
|
if ((context == NULL) || (shared_memory == NULL)) {
|
|
TeecResult = TEEC_ERROR_BAD_PARAMETERS;
|
|
goto Exit;
|
|
}
|
|
|
|
if (shared_memory->flags != 0) {
|
|
TeecResult = TEEC_ERROR_BAD_PARAMETERS;
|
|
goto Exit;
|
|
}
|
|
|
|
shared_memory->buffer = NULL;
|
|
shared_memory->alloc_buffer = 0;
|
|
|
|
debug("TEEC_AllocateSharedMemory: size=0x%zu, flags=0x%X\n",
|
|
shared_memory->size, shared_memory->flags);
|
|
|
|
shared_memory->buffer = OpteeClientMemAlloc(shared_memory->size);
|
|
if (shared_memory->buffer == NULL) {
|
|
TeecResult = TEEC_ERROR_OUT_OF_MEMORY;
|
|
goto Exit;
|
|
}
|
|
|
|
shared_memory->alloc_buffer = shared_memory->buffer;
|
|
|
|
Exit:
|
|
debug("TEEC_AllocateSharedMemory Exit : TeecResult=0x%X\n", TeecResult);
|
|
return TeecResult;
|
|
}
|
|
|
|
/*
|
|
* Releases shared memory.
|
|
*
|
|
* The optee_client implementation allows this to be called with a null pointer
|
|
* and null buffer but we'll assert this is not the case for better debugging.
|
|
*/
|
|
void TEEC_ReleaseSharedMemory(TEEC_SharedMemory *shared_memory)
|
|
{
|
|
debug("TEEC_ReleaseSharedMemory Enter: shared_memory=0x%zu\n",
|
|
shared_memory->size);
|
|
|
|
if (shared_memory == NULL)
|
|
goto Exit;
|
|
|
|
if (shared_memory->buffer == NULL)
|
|
goto Exit;
|
|
|
|
if (shared_memory->alloc_buffer != 0) {
|
|
OpteeClientMemFree(shared_memory->alloc_buffer);
|
|
shared_memory->alloc_buffer = 0;
|
|
}
|
|
|
|
shared_memory->buffer = NULL;
|
|
shared_memory->size = 0;
|
|
|
|
Exit:
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Register shared memory
|
|
*
|
|
* If the supplied buffer is compatible we can use it as supplied otherwise
|
|
* we'll need to allocate a copy buffer for the transfer instead.
|
|
*/
|
|
TEEC_Result TEEC_RegisterSharedMemory(TEEC_Context *context,
|
|
TEEC_SharedMemory *shared_memory)
|
|
{
|
|
TEEC_Result TeecResult = TEEC_SUCCESS;
|
|
|
|
if ((context == NULL) || (shared_memory == NULL)) {
|
|
TeecResult = TEEC_ERROR_BAD_PARAMETERS;
|
|
goto Exit;
|
|
}
|
|
|
|
if (shared_memory->buffer == NULL) {
|
|
TeecResult = TEEC_ERROR_BAD_PARAMETERS;
|
|
goto Exit;
|
|
}
|
|
|
|
shared_memory->alloc_buffer = 0;
|
|
|
|
phys_addr_t start = (phys_addr_t) shared_memory->buffer;
|
|
|
|
if ((start % 4096) != 0) {
|
|
TEEC_SharedMemory TempSharedMemory;
|
|
TempSharedMemory.size = shared_memory->size;
|
|
TempSharedMemory.flags = shared_memory->flags;
|
|
|
|
TeecResult = TEEC_AllocateSharedMemory
|
|
(context, &TempSharedMemory);
|
|
|
|
if (TeecResult != TEEC_SUCCESS)
|
|
goto Exit;
|
|
|
|
shared_memory->alloc_buffer = TempSharedMemory.alloc_buffer;
|
|
}
|
|
|
|
Exit:
|
|
debug("TEEC_RegisterSharedMemory Exit : TeecResult=0x%X\n", TeecResult);
|
|
return TeecResult;
|
|
}
|
|
|
|
/*
|
|
* This function opens a new Session between the Client application and the
|
|
* specified TEE application.
|
|
*
|
|
* Only connection_method == TEEC_LOGIN_PUBLIC is supported connection_data and
|
|
* operation shall be set to NULL.
|
|
*/
|
|
TEEC_Result TEEC_OpenSession(TEEC_Context *context,
|
|
TEEC_Session *session,
|
|
const TEEC_UUID *destination,
|
|
uint32_t connection_method,
|
|
const void *connection_data,
|
|
TEEC_Operation *operation,
|
|
uint32_t *error_origin)
|
|
{
|
|
TEEC_Result TeecResult = TEEC_SUCCESS;
|
|
uint32_t TeecErrorOrigin = TEEC_ORIGIN_API;
|
|
|
|
debug("TEEC_OpenSession: session=0x%X, ...\n", session->id);
|
|
|
|
if ((context == NULL) || (session == NULL) || (destination == NULL)) {
|
|
TeecResult = TEEC_ERROR_BAD_PARAMETERS;
|
|
goto Exit;
|
|
}
|
|
|
|
if (connection_method != TEEC_LOGIN_PUBLIC) {
|
|
TeecResult = TEEC_ERROR_NOT_SUPPORTED;
|
|
goto Exit;
|
|
}
|
|
|
|
TEEC_Operation TeecNullOperation = {0};
|
|
TEEC_Operation *TeecOperation;
|
|
|
|
if (operation == NULL) {
|
|
memset(&TeecNullOperation, 0, sizeof(TEEC_Operation));
|
|
TeecOperation = &TeecNullOperation;
|
|
} else {
|
|
TeecOperation = operation;
|
|
}
|
|
|
|
TeecResult = TEEC_SMC_OpenSession(context, session, destination,
|
|
TeecOperation, &TeecErrorOrigin);
|
|
|
|
Exit:
|
|
if (error_origin != NULL)
|
|
*error_origin = TeecErrorOrigin;
|
|
|
|
debug("TEEC_OpenSession Exit : TeecResult=0x%X, TeecErrorOrigin=0x%X\n",
|
|
TeecResult, TeecErrorOrigin);
|
|
return TeecResult;
|
|
}
|
|
|
|
/*
|
|
* This function closes a session which has been opened with a TEE
|
|
* application.
|
|
*/
|
|
void TEEC_CloseSession(TEEC_Session *session)
|
|
{
|
|
TEEC_Result TeecResult = TEEC_SUCCESS;
|
|
uint32_t TeecErrorOrigin = TEEC_ORIGIN_API;
|
|
|
|
debug("TEEC_CloseSession Enter: session=0x%X\n", session->id);
|
|
|
|
if (session == NULL)
|
|
goto Exit;
|
|
|
|
TeecResult = TEEC_SMC_CloseSession(session, &TeecErrorOrigin);
|
|
|
|
Exit:
|
|
debug("TEEC_CloseSession Exit : TeecResult=0x%X, TeecErrorOrigin=0x%X\n",
|
|
TeecResult, TeecErrorOrigin);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Invokes a TEE command (secure service, sub-PA or whatever).
|
|
*/
|
|
TEEC_Result TEEC_InvokeCommand(TEEC_Session *session,
|
|
uint32_t cmd_id,
|
|
TEEC_Operation *operation,
|
|
uint32_t *error_origin)
|
|
{
|
|
TEEC_Result TeecResult = TEEC_SUCCESS;
|
|
uint32_t TeecErrorOrigin = TEEC_ORIGIN_API;
|
|
|
|
debug("TEEC_InvokeCommand Enter: session=0x%X, cmd_id=0x%X\n",
|
|
session->id, cmd_id);
|
|
|
|
if (session == NULL) {
|
|
TeecResult = TEEC_ERROR_BAD_PARAMETERS;
|
|
goto Exit;
|
|
}
|
|
|
|
TEEC_Operation TeecNullOperation = {0};
|
|
TEEC_Operation *TeecOperation;
|
|
|
|
if (operation == NULL)
|
|
TeecOperation = &TeecNullOperation;
|
|
else
|
|
TeecOperation = operation;
|
|
|
|
TeecResult = TEEC_SMC_InvokeCommand(session, cmd_id,
|
|
TeecOperation, &TeecErrorOrigin);
|
|
|
|
Exit:
|
|
if (error_origin != NULL)
|
|
*error_origin = TeecErrorOrigin;
|
|
|
|
debug("TEEC_InvokeCommand Exit : TeecResult=0x%X, TeecErrorOrigin=0x%X\n",
|
|
TeecResult, TeecErrorOrigin);
|
|
|
|
return TeecResult;
|
|
}
|
|
|
|
/*
|
|
* Request a cancellation of a in-progress operation (best effort)
|
|
*/
|
|
void TEEC_RequestCancellation(TEEC_Operation *operation)
|
|
{
|
|
TEEC_Result TeecResult = TEEC_SUCCESS;
|
|
uint32_t TeecErrorOrigin = TEEC_ORIGIN_API;
|
|
|
|
if (operation == NULL)
|
|
goto Exit;
|
|
|
|
TeecResult = TEEC_SMC_RequestCancellation(operation, &TeecErrorOrigin);
|
|
|
|
Exit:
|
|
debug("TEEC_RequestCancellation Exit : TeecResult=0x%X, TeecErrorOrigin=0x%X\n",
|
|
TeecResult, TeecErrorOrigin);
|
|
|
|
return;
|
|
}
|