From 01ba28a12e323d6385afeb96e8c473143214105a Mon Sep 17 00:00:00 2001 From: Hernan Gatta Date: Wed, 25 Mar 2020 21:40:36 -0700 Subject: [PATCH] core: OCALL support Adds the ability for TAs to perform "Out Calls", or OCALLs. OCALLs allow TAs to invoke commands on their corresponding CA in the same way that CAs can invoke commands on TAs. Also, adds a new capability that reports whether OP-TEE was built with OCALL support. Signed-off-by: Hernan Gatta Reviewed-by: Jerome Forissier --- core/arch/arm/include/kernel/thread.h | 20 +- core/arch/arm/include/sm/optee_smc.h | 2 + core/arch/arm/kernel/thread_optee_smc.c | 11 + core/arch/arm/tee/entry_fast.c | 3 + core/include/optee_rpc_cmd.h | 18 ++ core/pta/system.c | 302 ++++++++++++++++++ lib/libutee/include/pta_system.h | 20 ++ .../include/tee_api_defines_extensions.h | 7 + .../include/tee_internal_api_extensions.h | 11 + lib/libutee/tee_api.c | 141 ++++++++ lib/libutee/tee_api_private.h | 2 + lib/libutee/tee_system_pta.c | 13 +- mk/config.mk | 11 + 13 files changed, 555 insertions(+), 6 deletions(-) diff --git a/core/arch/arm/include/kernel/thread.h b/core/arch/arm/include/kernel/thread.h index 8eb298e6bb4..76f541c9fc1 100644 --- a/core/arch/arm/include/kernel/thread.h +++ b/core/arch/arm/include/kernel/thread.h @@ -20,7 +20,7 @@ #define THREAD_ID_0 0 #define THREAD_ID_INVALID -1 -#define THREAD_RPC_MAX_NUM_PARAMS 4 +#define THREAD_RPC_MAX_NUM_PARAMS 6 #ifndef __ASSEMBLER__ @@ -740,6 +740,24 @@ struct mobj *thread_rpc_alloc_global_payload(size_t size); */ void thread_rpc_free_global_payload(struct mobj *mobj); +/** + * Request that the Client Application allocate shared memory for OCALL payload + * buffers. + * + * @size: size in bytes of payload buffer + * + * @returns mobj that describes allocated buffer or NULL on error + */ +struct mobj *thread_rpc_alloc_client_app_payload(size_t size); + +/** + * Free physical memory previously allocated with + * thread_rpc_alloc_client_app_payload() + * + * @mobj: mobj that describes the buffer + */ +void thread_rpc_free_client_app_payload(struct mobj *mobj); + /* * enum thread_shm_type - type of non-secure shared memory * @THREAD_SHM_TYPE_APPLICATION - user space application shared memory diff --git a/core/arch/arm/include/sm/optee_smc.h b/core/arch/arm/include/sm/optee_smc.h index 8a8df34388e..21291eb251a 100644 --- a/core/arch/arm/include/sm/optee_smc.h +++ b/core/arch/arm/include/sm/optee_smc.h @@ -280,6 +280,8 @@ #define OPTEE_SMC_SEC_CAP_VIRTUALIZATION BIT(3) /* Secure world supports Shared Memory with a NULL reference */ #define OPTEE_SMC_SEC_CAP_MEMREF_NULL BIT(4) +/* Secure world is built with OCALL support */ +#define OPTEE_SMC_SEC_CAP_OCALL BIT(5) #define OPTEE_SMC_FUNCID_EXCHANGE_CAPABILITIES 9 #define OPTEE_SMC_EXCHANGE_CAPABILITIES \ diff --git a/core/arch/arm/kernel/thread_optee_smc.c b/core/arch/arm/kernel/thread_optee_smc.c index 25a90cf50cd..db7414b7576 100644 --- a/core/arch/arm/kernel/thread_optee_smc.c +++ b/core/arch/arm/kernel/thread_optee_smc.c @@ -626,3 +626,14 @@ void thread_rpc_free_global_payload(struct mobj *mobj) thread_rpc_free(OPTEE_RPC_SHM_TYPE_GLOBAL, mobj_get_cookie(mobj), mobj); } + +struct mobj *thread_rpc_alloc_client_app_payload(size_t size) +{ + return thread_rpc_alloc(size, 8, OPTEE_RPC_SHM_TYPE_CLIENT_APP); +} + +void thread_rpc_free_client_app_payload(struct mobj *mobj) +{ + thread_rpc_free(OPTEE_RPC_SHM_TYPE_CLIENT_APP, mobj_get_cookie(mobj), + mobj); +} diff --git a/core/arch/arm/tee/entry_fast.c b/core/arch/arm/tee/entry_fast.c index 26682baa75c..d927532e686 100644 --- a/core/arch/arm/tee/entry_fast.c +++ b/core/arch/arm/tee/entry_fast.c @@ -92,6 +92,9 @@ static void tee_entry_exchange_capabilities(struct thread_smc_args *args) args->a1 |= OPTEE_SMC_SEC_CAP_VIRTUALIZATION; #endif args->a1 |= OPTEE_SMC_SEC_CAP_MEMREF_NULL; +#if defined(CFG_OCALL) + args->a1 |= OPTEE_SMC_SEC_CAP_OCALL; +#endif #if defined(CFG_CORE_DYN_SHM) dyn_shm_en = core_mmu_nsec_ddr_is_defined(); diff --git a/core/include/optee_rpc_cmd.h b/core/include/optee_rpc_cmd.h index 624d5b2ac49..5306e384b8a 100644 --- a/core/include/optee_rpc_cmd.h +++ b/core/include/optee_rpc_cmd.h @@ -97,6 +97,12 @@ * space application */ #define OPTEE_RPC_SHM_TYPE_GLOBAL 2 +/* + * Memory shared with the non-secure application that is the client of the TA + * that requests shared memory of this type; the client could be running in + * non-secure user-mode or in non-secure kernel-mode + */ +#define OPTEE_RPC_SHM_TYPE_CLIENT_APP 3 /* * Free shared memory previously allocated with OPTEE_RPC_CMD_SHM_ALLOC @@ -175,6 +181,18 @@ /* I2C master control flags */ #define OPTEE_RPC_I2C_FLAGS_TEN_BIT BIT(0) +/* + * Send an OCALL to the Client Application + * + * [in] value[0].a CA Command ID (i.e., OCALL# for the CA to execute) + * [out] value[0].b OCALL return value + * [out] value[0].c OCALL return value origin + * [in] value[1].a UUID of TA whence OCALL originated (HI bits) + * [out] value[1].b UUID of TA whence OCALL originated (LO bits) + * [in/out] any[2..5].* OCALL parameters as specified by the TA, if any + */ +#define OPTEE_RPC_CMD_OCALL 22 + /* * Definition of protocol for command OPTEE_RPC_CMD_FS */ diff --git a/core/pta/system.c b/core/pta/system.c index 52e89517d14..351ca12520d 100644 --- a/core/pta/system.c +++ b/core/pta/system.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -30,6 +31,14 @@ #include #include +#define PTR_ADD(ptr, offs) ((void *)((uintptr_t)(ptr) + (uintptr_t)(offs))) + +#define ACCESS_RIGHTS_READ \ + (TEE_MEMORY_ACCESS_READ | TEE_MEMORY_ACCESS_ANY_OWNER) +#define ACCESS_RIGHTS_WRITE \ + (TEE_MEMORY_ACCESS_WRITE | TEE_MEMORY_ACCESS_ANY_OWNER) +#define ACCESS_RIGHTS_READ_WRITE (ACCESS_RIGHTS_READ | ACCESS_RIGHTS_WRITE) + static unsigned int system_pnum; static TEE_Result system_rng_reseed(uint32_t param_types, @@ -310,6 +319,297 @@ static TEE_Result system_supp_plugin_invoke(uint32_t param_types, return res; } +#ifdef CFG_OCALL +static TEE_Result +ocall_check_memref_access_rights(const struct user_mode_ctx *uctx, uint32_t pt, + uaddr_t buffer, size_t size) +{ + uint32_t flags = 0; + + switch (pt) { + case TEE_PARAM_TYPE_MEMREF_INPUT: + flags = ACCESS_RIGHTS_READ; + break; + case TEE_PARAM_TYPE_MEMREF_INOUT: + flags = ACCESS_RIGHTS_READ_WRITE; + break; + case TEE_PARAM_TYPE_MEMREF_OUTPUT: + flags = ACCESS_RIGHTS_WRITE; + break; + default: + return TEE_ERROR_BAD_PARAMETERS; + } + + return vm_check_access_rights(uctx, flags, buffer, size); +} + +static TEE_Result ocall_check_parameters(const struct user_mode_ctx *uctx, + const struct utee_params *up) +{ + TEE_Result res = TEE_SUCCESS; + uaddr_t buffer = 0; + size_t size = 0; + uint32_t pt = 0; + size_t n = 0; + + res = vm_check_access_rights(uctx, ACCESS_RIGHTS_READ_WRITE, + (uaddr_t)up, sizeof(*up)); + if (res) + return res; + + for (n = 0; n < TEE_NUM_PARAMS; n++) { + pt = TEE_PARAM_TYPE_GET(up->types, n); + switch (pt) { + case TEE_PARAM_TYPE_NONE: + case TEE_PARAM_TYPE_VALUE_INPUT: + case TEE_PARAM_TYPE_VALUE_INOUT: + case TEE_PARAM_TYPE_VALUE_OUTPUT: + break; + case TEE_PARAM_TYPE_MEMREF_INPUT: + case TEE_PARAM_TYPE_MEMREF_INOUT: + case TEE_PARAM_TYPE_MEMREF_OUTPUT: + buffer = (uaddr_t)up->vals[n * 2]; + size = up->vals[n * 2 + 1]; + if ((buffer && !size) || (!buffer && size)) + return TEE_ERROR_BAD_PARAMETERS; + res = ocall_check_memref_access_rights(uctx, pt, buffer, + size); + if (res) + return res; + break; + default: + return TEE_ERROR_BAD_PARAMETERS; + } + } + + return res; +} + +static TEE_Result ocall_compute_mobj_size(struct utee_params *up, + size_t *mobj_size) +{ + size_t total = 0; + size_t size = 0; + size_t n = 0; + + for (n = 0; n < TEE_NUM_PARAMS; n++) { + switch (TEE_PARAM_TYPE_GET(up->types, n)) { + case TEE_PARAM_TYPE_NONE: + case TEE_PARAM_TYPE_VALUE_INPUT: + case TEE_PARAM_TYPE_VALUE_INOUT: + case TEE_PARAM_TYPE_VALUE_OUTPUT: + break; + case TEE_PARAM_TYPE_MEMREF_INPUT: + case TEE_PARAM_TYPE_MEMREF_INOUT: + case TEE_PARAM_TYPE_MEMREF_OUTPUT: + size = up->vals[n * 2 + 1]; + if (ADD_OVERFLOW(total, size, &total)) + return TEE_ERROR_SECURITY; + break; + default: + return TEE_ERROR_BAD_PARAMETERS; + } + } + + *mobj_size = total; + + return TEE_SUCCESS; +} + +static TEE_Result ocall_pre_process(struct thread_param *params, + struct utee_params *up, + struct mobj *mobj, + void *mobj_va) +{ + uint32_t pt = 0; + void *buffer = NULL; + size_t size = 0; + void *destination = NULL; + size_t mobj_offs = 0; + size_t n = 0; + + for (n = 0; n < TEE_NUM_PARAMS; n++) { + pt = TEE_PARAM_TYPE_GET(up->types, n); + switch (pt) { + case TEE_PARAM_TYPE_NONE: + params[n].attr = THREAD_PARAM_ATTR_NONE; + break; + case TEE_PARAM_TYPE_VALUE_INPUT: + case TEE_PARAM_TYPE_VALUE_INOUT: + params[n].u.value.a = up->vals[n * 2]; + params[n].u.value.b = up->vals[n * 2 + 1]; + fallthrough; + case TEE_PARAM_TYPE_VALUE_OUTPUT: + params[n].attr = THREAD_PARAM_ATTR_VALUE_IN + pt - + TEE_PARAM_TYPE_VALUE_INPUT; + break; + case TEE_PARAM_TYPE_MEMREF_INPUT: + case TEE_PARAM_TYPE_MEMREF_INOUT: + case TEE_PARAM_TYPE_MEMREF_OUTPUT: + buffer = (void *)(uintptr_t)up->vals[n * 2]; + size = up->vals[n * 2 + 1]; + if (buffer && pt != TEE_PARAM_TYPE_MEMREF_OUTPUT) { + destination = PTR_ADD(mobj_va, mobj_offs); + memcpy(destination, buffer, size); + } + params[n].u.memref.mobj = mobj; + params[n].u.memref.offs = mobj_offs; + params[n].u.memref.size = size; + params[n].attr = THREAD_PARAM_ATTR_MEMREF_IN + pt - + TEE_PARAM_TYPE_MEMREF_INPUT; + if (ADD_OVERFLOW(mobj_offs, size, &mobj_offs)) + return TEE_ERROR_SECURITY; + break; + default: + return TEE_ERROR_BAD_PARAMETERS; + } + } + + return TEE_SUCCESS; +} + +static TEE_Result ocall_post_process(struct thread_param *params, + struct utee_params *up, + void *mobj_va) +{ + uint32_t pt = 0; + void *buffer = NULL; + size_t size = 0; + void *source = NULL; + size_t mobj_offs = 0; + size_t n = 0; + + for (n = 0; n < TEE_NUM_PARAMS; n++) { + pt = TEE_PARAM_TYPE_GET(up->types, n); + switch (pt) { + case TEE_PARAM_TYPE_NONE: + case TEE_PARAM_TYPE_VALUE_INPUT: + break; + case TEE_PARAM_TYPE_VALUE_INOUT: + case TEE_PARAM_TYPE_VALUE_OUTPUT: + up->vals[n * 2] = params[n].u.value.a; + up->vals[n * 2 + 1] = params[n].u.value.b; + break; + case TEE_PARAM_TYPE_MEMREF_INPUT: + size = up->vals[n * 2 + 1]; + if (params[n].u.memref.size != size) + return TEE_ERROR_BAD_PARAMETERS; + if (ADD_OVERFLOW(mobj_offs, size, &mobj_offs)) + return TEE_ERROR_SECURITY; + break; + case TEE_PARAM_TYPE_MEMREF_INOUT: + case TEE_PARAM_TYPE_MEMREF_OUTPUT: + buffer = (void *)(uintptr_t)up->vals[n * 2]; + size = up->vals[n * 2 + 1]; + if (params[n].u.memref.size > size) + return TEE_ERROR_BAD_PARAMETERS; + if (buffer) { + source = PTR_ADD(mobj_va, mobj_offs); + memcpy(buffer, source, params[n].u.memref.size); + if (ADD_OVERFLOW(mobj_offs, size, &mobj_offs)) + return TEE_ERROR_SECURITY; + } + break; + default: + return TEE_ERROR_BAD_PARAMETERS; + } + } + + return TEE_SUCCESS; +} + +static TEE_Result system_ocall(struct ts_session *cs, uint32_t param_types, + TEE_Param params[TEE_NUM_PARAMS]) +{ + const struct user_ta_ctx *utc = to_user_ta_ctx(cs->ctx); + struct thread_param rpc_params[THREAD_RPC_MAX_NUM_PARAMS] = { }; + const size_t rpc_num_params = ARRAY_SIZE(rpc_params); + uint32_t ocall_id = 0; + struct utee_params *ocall_up = NULL; + size_t ocall_up_size = 0; + struct mobj *mobj = NULL; + size_t mobj_size = 0; + void *mobj_va = NULL; + TEE_Result res = TEE_SUCCESS; + const uint32_t exp_pt = TEE_PARAM_TYPES(TEE_PARAM_TYPE_VALUE_INOUT, + TEE_PARAM_TYPE_MEMREF_INOUT, + TEE_PARAM_TYPE_NONE, + TEE_PARAM_TYPE_NONE); + + if (param_types != exp_pt) + return TEE_ERROR_BAD_PARAMETERS; + + ocall_up_size = params[1].memref.size; + if (ocall_up_size != sizeof(*ocall_up)) + return TEE_ERROR_BAD_PARAMETERS; + + ocall_id = params[0].value.a; + ocall_up = (struct utee_params *)params[1].memref.buffer; /* User ptr */ + + res = ocall_check_parameters(&utc->uctx, ocall_up); + if (res) + return res; + + res = ocall_compute_mobj_size(ocall_up, &mobj_size); + if (res) + return res; + + if (mobj_size) { + mobj = thread_rpc_alloc_client_app_payload(mobj_size); + if (!mobj) + return TEE_ERROR_OUT_OF_MEMORY; + + mobj_va = mobj_get_va(mobj, 0); + if (!mobj_va) { + res = TEE_ERROR_GENERIC; + goto exit; + } + } + + rpc_params[0] = THREAD_PARAM_VALUE(INOUT, ocall_id, 0, 0); + rpc_params[1] = THREAD_PARAM_VALUE(IN, 0, 0, 0); + tee_uuid_to_octets((uint8_t *)&rpc_params[1].u.value, + &cs->ctx->uuid); + + res = ocall_pre_process(rpc_params + 2, ocall_up, mobj, mobj_va); + if (res) + goto exit; + + res = thread_rpc_cmd(OPTEE_RPC_CMD_OCALL, rpc_num_params, rpc_params); + if (res) { + /* + * Failure to process the OCALL request, as indicated by the + * return code of the RPC, denotes that the state of normal + * world is such that it may not be able to handle an additional + * round-trip to the CA to free the SHM. As such, simply put the + * memory object here. + */ + mobj_put(mobj); + return res; + } + + res = ocall_post_process(rpc_params + 2, ocall_up, mobj_va); + if (res) + goto exit; + + params[0].value.a = rpc_params[0].u.value.b; /* OCALL ret val */ + params[0].value.b = rpc_params[0].u.value.c; /* OCALL ret val origin */ + +exit: + if (mobj) + thread_rpc_free_client_app_payload(mobj); + + return res; +} +#else +static TEE_Result system_ocall(struct ts_session *cs __unused, + uint32_t param_types __unused, + TEE_Param params[TEE_NUM_PARAMS] __unused) +{ + return TEE_ERROR_NOT_IMPLEMENTED; +} +#endif /*CFG_OCALL*/ + static TEE_Result open_session(uint32_t param_types __unused, TEE_Param params[TEE_NUM_PARAMS] __unused, void **sess_ctx __unused) @@ -350,6 +650,8 @@ static TEE_Result invoke_command(void *sess_ctx __unused, uint32_t cmd_id, return system_get_tpm_event_log(param_types, params); case PTA_SYSTEM_SUPP_PLUGIN_INVOKE: return system_supp_plugin_invoke(param_types, params); + case PTA_SYSTEM_OCALL: + return system_ocall(s, param_types, params); default: break; } diff --git a/lib/libutee/include/pta_system.h b/lib/libutee/include/pta_system.h index a23f60b8e81..3018e81fb65 100644 --- a/lib/libutee/include/pta_system.h +++ b/lib/libutee/include/pta_system.h @@ -201,4 +201,24 @@ */ #define PTA_SYSTEM_SUPP_PLUGIN_INVOKE 13 +/* + * Send an OCALL to the calling TA's Client Application + * + * Note that if TA 1 invokes TA 2 which invokes TA N+1 and the latter sends an + * OCALL, it is the CA of TA 1 that receives the OCALL. Nevertheless, the OCALL + * carries the UUID of the TA that sent it. Therefore, the CA can determine that + * the OCALL originated from TA N+1, even though the CA receives the OCALL as a + * result of having communicated with TA 1. + * + * [in/out] value[0].a: CA command Id (IN), CA command return value (OUT) + * [out] value[0].b: CA command return value origin + * [in/out] memref[1]: Array of TEE_Param[TEE_NUM_PARAMS], the OCALL params + * + * Returns TEE_SUCCESS if the OCALL was sent and was processed successfully. + * This value is not necessarily the same as the return value of the OCALL + * itself, whose interpretation is up to the CA & TA, and is passed to the TA + * along with its origin code via this command's parameters as specified above. + */ +#define PTA_SYSTEM_OCALL 14 + #endif /* __PTA_SYSTEM_H */ diff --git a/lib/libutee/include/tee_api_defines_extensions.h b/lib/libutee/include/tee_api_defines_extensions.h index 3375946f75b..a8daff93abe 100644 --- a/lib/libutee/include/tee_api_defines_extensions.h +++ b/lib/libutee/include/tee_api_defines_extensions.h @@ -102,4 +102,11 @@ /* Private login method for REE kernel clients */ #define TEE_LOGIN_REE_KERNEL 0x80000000 +/* + * Implementation-specific origin code constants + */ + +/* The error code originated from the TA's Client Application (CA) */ +#define TEE_ORIGIN_CLIENT_APP 0xF0000001 + #endif /* TEE_API_DEFINES_EXTENSIONS_H */ diff --git a/lib/libutee/include/tee_internal_api_extensions.h b/lib/libutee/include/tee_internal_api_extensions.h index 78b0e39f07e..e7fa46274af 100644 --- a/lib/libutee/include/tee_internal_api_extensions.h +++ b/lib/libutee/include/tee_internal_api_extensions.h @@ -35,6 +35,17 @@ TEE_Result TEE_CacheClean(char *buf, size_t len); TEE_Result TEE_CacheFlush(char *buf, size_t len); TEE_Result TEE_CacheInvalidate(char *buf, size_t len); +/* + * Send an OCALL to the Client Application + * + * The semantics are identical to TEEC_InvokeCommand but in the opposite + * direction. + */ +TEE_Result TEE_InvokeCACommand(uint32_t cancellationRequestTimeout, + uint32_t commandID, uint32_t paramTypes, + TEE_Param params[TEE_NUM_PARAMS], + uint32_t *returnOrigin); + /* * tee_map_zi() - Map zero initialized memory * @len: Number of bytes diff --git a/lib/libutee/tee_api.c b/lib/libutee/tee_api.c index 20acccbaba6..daef17c95fd 100644 --- a/lib/libutee/tee_api.c +++ b/lib/libutee/tee_api.c @@ -10,10 +10,13 @@ #include #include #include +#include "pta_system.h" #include "tee_api_private.h" static const void *tee_api_instance_data; +TEE_TASessionHandle __tee_api_system_session; + /* System API - Internal Client API */ static TEE_Result copy_param(struct utee_params *up, uint32_t param_types, @@ -245,6 +248,144 @@ TEE_Result TEE_InvokeTACommand(TEE_TASessionHandle session, return res; } +#ifdef CFG_OCALL +TEE_Result TEE_InvokeCACommand(uint32_t cancellationRequestTimeout, + uint32_t commandID, uint32_t paramTypes, + TEE_Param params[TEE_NUM_PARAMS], + uint32_t *returnOrigin) +{ + const TEE_UUID uuid = PTA_SYSTEM_UUID; + TEE_Result res = TEE_SUCCESS; + uint32_t ret_origin = TEE_ORIGIN_TEE; + struct utee_params pta_up = { }; + struct utee_params ocall_up = { }; + void *buffer = NULL; + size_t size = 0; + size_t n = 0; + + if (paramTypes) + __utee_check_inout_annotation(params, + sizeof(TEE_Param) * + TEE_NUM_PARAMS); + if (returnOrigin) + __utee_check_out_annotation(returnOrigin, + sizeof(*returnOrigin)); + + /* Open session with the System PTA, if necessary */ + if (__tee_api_system_session == TEE_HANDLE_NULL) { + res = TEE_OpenTASession(&uuid, TEE_TIMEOUT_INFINITE, 0, NULL, + &__tee_api_system_session, &ret_origin); + /* The System PTA is optional */ + if (res == TEE_ERROR_ITEM_NOT_FOUND && + ret_origin == TEE_ORIGIN_TEE) { + res = TEE_ERROR_NOT_SUPPORTED; + ret_origin = TEE_ORIGIN_API; + } + if (res) + goto exit; + } + + /* Convert the OCALL's parameters into a utee_params structure */ + ocall_up.types = paramTypes; + for (n = 0; n < TEE_NUM_PARAMS; n++) { + switch (TEE_PARAM_TYPE_GET(paramTypes, n)) { + case TEE_PARAM_TYPE_VALUE_INPUT: + case TEE_PARAM_TYPE_VALUE_INOUT: + ocall_up.vals[n * 2] = params[n].value.a; + ocall_up.vals[n * 2 + 1] = params[n].value.b; + break; + case TEE_PARAM_TYPE_MEMREF_INPUT: + case TEE_PARAM_TYPE_MEMREF_OUTPUT: + case TEE_PARAM_TYPE_MEMREF_INOUT: + buffer = params[n].memref.buffer; + size = params[n].memref.size; + if ((buffer && !size) || (!buffer && size)) { + res = TEE_ERROR_BAD_PARAMETERS; + ret_origin = TEE_ORIGIN_API; + goto exit; + } + ocall_up.vals[n * 2] = (vaddr_t)buffer; + ocall_up.vals[n * 2 + 1] = size; + break; + default: + break; + } + } + + /* Construct the parameters for the call to the PTA */ + pta_up.vals[0] = commandID; + pta_up.vals[2] = (uintptr_t)&ocall_up; + pta_up.vals[3] = sizeof(ocall_up); + pta_up.types = TEE_PARAM_TYPES(TEE_PARAM_TYPE_VALUE_INOUT, + TEE_PARAM_TYPE_MEMREF_INOUT, + TEE_PARAM_TYPE_NONE, + TEE_PARAM_TYPE_NONE); + + /* Send the OCALL request */ + res = _utee_invoke_ta_command((uintptr_t)__tee_api_system_session, + cancellationRequestTimeout, + PTA_SYSTEM_OCALL, + &pta_up, + &ret_origin); + if (res != TEE_SUCCESS) + goto exit; + + /* Convert the utee_params structure into the OCALL's parameters */ + for (n = 0; n < TEE_NUM_PARAMS; n++) { + switch (TEE_PARAM_TYPE_GET(paramTypes, n)) { + case TEE_PARAM_TYPE_VALUE_OUTPUT: + case TEE_PARAM_TYPE_VALUE_INOUT: + params[n].value.a = ocall_up.vals[n * 2]; + params[n].value.b = ocall_up.vals[n * 2 + 1]; + break; + case TEE_PARAM_TYPE_MEMREF_OUTPUT: + case TEE_PARAM_TYPE_MEMREF_INOUT: + buffer = (void *)(uintptr_t)ocall_up.vals[n * 2]; + size = ocall_up.vals[n * 2 + 1]; + if (buffer != params[n].memref.buffer || + size > params[n].memref.size) { + res = TEE_ERROR_BAD_PARAMETERS; + ret_origin = TEE_ORIGIN_API; + goto exit; + } + params[n].memref.size = size; + break; + default: + break; + } + } + + /* Extract the OCALL return value and error origin */ + res = (TEE_Result)pta_up.vals[0]; + ret_origin = (uint32_t)pta_up.vals[1]; + +exit: + /* The PTA is a communications conduit to normal world */ + if (ret_origin == TEE_ORIGIN_TRUSTED_APP) + ret_origin = TEE_ORIGIN_COMMS; + + if (returnOrigin) + *returnOrigin = ret_origin; + + if (ret_origin == TEE_ORIGIN_TEE && + (res != TEE_SUCCESS || + res != TEE_ERROR_OUT_OF_MEMORY || + res != TEE_ERROR_TARGET_DEAD)) + TEE_Panic(res); + + return res; +} +#else +TEE_Result TEE_InvokeCACommand(uint32_t cancellationRequestTimeout __unused, + uint32_t commandID __unused, + uint32_t paramTypes __unused, + TEE_Param params[TEE_NUM_PARAMS] __unused, + uint32_t *returnOrigin __unused) +{ + return TEE_ERROR_NOT_IMPLEMENTED; +} +#endif /*CFG_OCALL*/ + /* System API - Cancellations */ bool TEE_GetCancellationFlag(void) diff --git a/lib/libutee/tee_api_private.h b/lib/libutee/tee_api_private.h index 91518878c4c..445b30d060c 100644 --- a/lib/libutee/tee_api_private.h +++ b/lib/libutee/tee_api_private.h @@ -8,6 +8,8 @@ #include #include +/* From tee_api.c */ +extern TEE_TASessionHandle __tee_api_system_session; void __utee_from_attr(struct utee_attribute *ua, const TEE_Attribute *attrs, uint32_t attr_count); diff --git a/lib/libutee/tee_system_pta.c b/lib/libutee/tee_system_pta.c index 6c35045951b..bddf600971f 100644 --- a/lib/libutee/tee_system_pta.c +++ b/lib/libutee/tee_system_pta.c @@ -10,23 +10,26 @@ #include #include #include +#include "tee_api_private.h" static TEE_Result invoke_system_pta(uint32_t cmd_id, uint32_t param_types, TEE_Param params[TEE_NUM_PARAMS]) { - static TEE_TASessionHandle sess = TEE_HANDLE_NULL; static const TEE_UUID uuid = PTA_SYSTEM_UUID; - if (sess == TEE_HANDLE_NULL) { + if (__tee_api_system_session == TEE_HANDLE_NULL) { TEE_Result res = TEE_OpenTASession(&uuid, TEE_TIMEOUT_INFINITE, - 0, NULL, &sess, NULL); + 0, NULL, + &__tee_api_system_session, + NULL); if (res) return res; } - return TEE_InvokeTACommand(sess, TEE_TIMEOUT_INFINITE, cmd_id, - param_types, params, NULL); + return TEE_InvokeTACommand(__tee_api_system_session, + TEE_TIMEOUT_INFINITE, cmd_id, param_types, + params, NULL); } void *tee_map_zi(size_t len, uint32_t flags) diff --git a/mk/config.mk b/mk/config.mk index 661dd70afdb..63bc741a591 100644 --- a/mk/config.mk +++ b/mk/config.mk @@ -645,3 +645,14 @@ CFG_COMPAT_GP10_DES ?= y # Defines a limit for many levels TAs may call each others. CFG_CORE_MAX_SYSCALL_RECURSION ?= 4 + +# Enables support for OCALLs, allowing TAs to invoke commands on their CA. +# Since OCALLs are implemented in the System PTA, the latter is a prerequisite. +ifeq ($(CFG_CORE_SEL1_SPMC),y) +CFG_OCALL ?= n +else +CFG_OCALL ?= y +ifeq ($(CFG_OCALL),y) +$(call force,CFG_SYSTEM_PTA,y) +endif +endif