diff --git a/.shippable.yml b/.shippable.yml index 22445c25a43..739cb5a4e74 100644 --- a/.shippable.yml +++ b/.shippable.yml @@ -45,6 +45,7 @@ build: - _make CFG_TA_GPROF_SUPPORT=y CFG_ULIBS_MCOUNT=y - _make CFG_SECURE_DATA_PATH=y - _make CFG_REE_FS_TA_BUFFERED=n + - _make CFG_GRPC=y - _make PLATFORM=vexpress-qemu_armv8a CFG_ARM64_core=y - _make PLATFORM=vexpress-qemu_armv8a CFG_ARM64_core=y CFG_WITH_PAGER=y - _make PLATFORM=vexpress-qemu_armv8a CFG_ARM64_core=y CFG_TA_GPROF_SUPPORT=y CFG_ULIBS_MCOUNT=y diff --git a/core/arch/arm/include/kernel/thread.h b/core/arch/arm/include/kernel/thread.h index 3e4c0e70495..d65b4b7c369 100644 --- a/core/arch/arm/include/kernel/thread.h +++ b/core/arch/arm/include/kernel/thread.h @@ -2,6 +2,7 @@ /* * Copyright (c) 2014, STMicroelectronics International N.V. * Copyright (c) 2016-2017, Linaro Limited + * Copyright (c) 2019, Microsoft Corporation */ #ifndef KERNEL_THREAD_H @@ -562,6 +563,22 @@ struct mobj *thread_rpc_alloc_payload(size_t size); */ void thread_rpc_free_payload(struct mobj *mobj); +/** + * Allocates data for host application payload buffers. + * + * @size: size in bytes of payload buffer + * + * @returns mobj that describes allocated buffer or NULL on error + */ +struct mobj *thread_rpc_alloc_host_payload(size_t size); + +/** + * Free physical memory previously allocated with + * thread_rpc_alloc_host_payload() + * + * @mobj: mobj that describes the buffer + */ +void thread_rpc_free_host_payload(struct mobj *mobj); struct thread_param_memref { size_t offs; diff --git a/core/arch/arm/kernel/thread_optee_smc.c b/core/arch/arm/kernel/thread_optee_smc.c index dc4e93a9e55..869464ea515 100644 --- a/core/arch/arm/kernel/thread_optee_smc.c +++ b/core/arch/arm/kernel/thread_optee_smc.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: BSD-2-Clause /* * Copyright (c) 2019, Linaro Limited + * Copyright (c) 2019, Microsoft Corporation */ #include @@ -601,3 +602,13 @@ 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_host_payload(size_t size) +{ + return thread_rpc_alloc(size, 8, OPTEE_RPC_SHM_TYPE_HOST); +} + +void thread_rpc_free_host_payload(struct mobj *mobj) +{ + thread_rpc_free(OPTEE_RPC_SHM_TYPE_HOST, mobj_get_cookie(mobj), mobj); +} diff --git a/core/include/optee_rpc_cmd.h b/core/include/optee_rpc_cmd.h index 03f80323119..2fecb27a5db 100644 --- a/core/include/optee_rpc_cmd.h +++ b/core/include/optee_rpc_cmd.h @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: BSD-2-Clause */ /* * Copyright (c) 2016-2017, Linaro Limited + * Copyright (c) 2019, Microsoft Corporation */ #ifndef __OPTEE_RPC_CMD_H @@ -97,6 +98,11 @@ * space application */ #define OPTEE_RPC_SHM_TYPE_GLOBAL 2 +/* + * Memory shared with the non-secure user space application that owns the + * current session + */ +#define OPTEE_RPC_SHM_TYPE_HOST 3 /* * Free shared memory previously allocated with OPTEE_RPC_CMD_SHM_ALLOC diff --git a/core/pta/grpc.c b/core/pta/grpc.c new file mode 100644 index 00000000000..b38d713c31f --- /dev/null +++ b/core/pta/grpc.c @@ -0,0 +1,240 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (c) 2019, Microsoft Corporation + */ + +#include +#include +#include +#include +#include +#include + +static TEE_Result rpc_calc_param_size(uint32_t param_type, TEE_Param *param, + uint32_t *size) +{ + switch (param_type) { + case TEE_PARAM_TYPE_NONE: + case TEE_PARAM_TYPE_VALUE_INPUT: + case TEE_PARAM_TYPE_VALUE_OUTPUT: + case TEE_PARAM_TYPE_VALUE_INOUT: + *size = 0; + break; + case TEE_PARAM_TYPE_MEMREF_INPUT: + case TEE_PARAM_TYPE_MEMREF_OUTPUT: + case TEE_PARAM_TYPE_MEMREF_INOUT: + *size = param->memref.size; + break; + default: + return TEE_ERROR_BAD_PARAMETERS; + } + + return TEE_SUCCESS; +} + +static TEE_Result rpc_preprocess_param(struct thread_param *rpc_msg_param, + struct mobj *mobj, + uint8_t *mobj_va, + uint32_t *mobj_offset, + uint32_t param_type, + TEE_Param *param) +{ + /* Fill the thread_param struct */ + switch (param_type) { + case TEE_PARAM_TYPE_NONE: + rpc_msg_param->attr = THREAD_PARAM_ATTR_NONE; + break; + case TEE_PARAM_TYPE_VALUE_INPUT: + *rpc_msg_param = THREAD_PARAM_VALUE(IN, param->value.a, + param->value.b, 0); + break; + case TEE_PARAM_TYPE_VALUE_OUTPUT: + *rpc_msg_param = THREAD_PARAM_VALUE(OUT, param->value.a, + param->value.b, 0); + break; + case TEE_PARAM_TYPE_VALUE_INOUT: + *rpc_msg_param = THREAD_PARAM_VALUE(INOUT, param->value.a, + param->value.b, 0); + break; + case TEE_PARAM_TYPE_MEMREF_INPUT: + *rpc_msg_param = THREAD_PARAM_MEMREF(IN, mobj, *mobj_offset, + param->memref.size); + break; + case TEE_PARAM_TYPE_MEMREF_OUTPUT: + *rpc_msg_param = THREAD_PARAM_MEMREF(OUT, mobj, *mobj_offset, + param->memref.size); + break; + case TEE_PARAM_TYPE_MEMREF_INOUT: + *rpc_msg_param = THREAD_PARAM_MEMREF(INOUT, mobj, *mobj_offset, + param->memref.size); + break; + default: + return TEE_ERROR_BAD_PARAMETERS; + } + + /* Perform copies into shared memory, if necessary */ + switch (param_type) { + case TEE_PARAM_TYPE_MEMREF_INPUT: + case TEE_PARAM_TYPE_MEMREF_OUTPUT: + case TEE_PARAM_TYPE_MEMREF_INOUT: + if (!mobj) + return TEE_ERROR_BAD_PARAMETERS; + + memcpy(mobj_va + *mobj_offset, param->memref.buffer, + param->memref.size); + *mobj_offset += param->memref.size; + break; + default: + break; + } + + return TEE_SUCCESS; +} + +static TEE_Result rpc_postprocess_param(struct thread_param *rpc_msg_param, + uint8_t *mobj_va, + uint32_t *mobj_offset, + uint32_t param_type, + TEE_Param *param) +{ + switch (param_type) { + case TEE_PARAM_TYPE_VALUE_OUTPUT: + case TEE_PARAM_TYPE_VALUE_INOUT: + if (rpc_msg_param->u.value.a > UINT32_MAX || + rpc_msg_param->u.value.b > UINT32_MAX) + return TEE_ERROR_BAD_PARAMETERS; + + param->value.a = (uint32_t)rpc_msg_param->u.value.a; + param->value.b = (uint32_t)rpc_msg_param->u.value.b; + break; + case TEE_PARAM_TYPE_MEMREF_OUTPUT: + case TEE_PARAM_TYPE_MEMREF_INOUT: + if (!mobj_va || + (rpc_msg_param->u.memref.size > param->memref.size)) + return TEE_ERROR_BAD_PARAMETERS; + + memcpy(param->memref.buffer, mobj_va + *mobj_offset, + rpc_msg_param->u.memref.size); + *mobj_offset += rpc_msg_param->u.memref.size; + default: + break; + } + + return TEE_SUCCESS; +} + +static TEE_Result rpc_execute(uint32_t cmd_id, uint32_t param_types, + TEE_Param params[TEE_NUM_PARAMS]) +{ + TEE_Result res; + + struct mobj *mobj = NULL; + uint8_t *mobj_va = NULL; + + uint32_t mobj_size = 0; + uint32_t mobj_offset = 0; + + struct thread_param rpc_msg_params[TEE_NUM_PARAMS] = { 0 }; + + uint8_t i; + uint32_t param_type; + uint32_t param_size; + + /* Compute RPC payload size */ + for (i = 0; i < TEE_NUM_PARAMS; i++) { + param_type = TEE_PARAM_TYPE_GET(param_types, i); + res = rpc_calc_param_size(param_type, ¶ms[i], ¶m_size); + if (res != TEE_SUCCESS) + goto exit; + + if (ADD_OVERFLOW(mobj_size, param_size, &mobj_size)) { + res = TEE_ERROR_SECURITY; + goto exit; + } + } + + /* Allocate RPC payload with host, if necessary */ + if (mobj_size) { + mobj = thread_rpc_alloc_host_payload(mobj_size); + if (!mobj) { + res = TEE_ERROR_OUT_OF_MEMORY; + goto exit; + } + + mobj_va = mobj_get_va(mobj, 0); + if (!mobj_va) { + res = TEE_ERROR_OUT_OF_MEMORY; + goto exit; + } + } + + /* Prepare parameters for the RPC */ + for (i = 0; i < TEE_NUM_PARAMS; i++) { + param_type = TEE_PARAM_TYPE_GET(param_types, i); + res = rpc_preprocess_param(&rpc_msg_params[i], mobj, mobj_va, + &mobj_offset, param_type, ¶ms[i]); + } + + /* Send RPC message to the host */ + res = thread_rpc_cmd(cmd_id, ARRAY_SIZE(rpc_msg_params), + rpc_msg_params); + if (res != TEE_SUCCESS) + goto exit; + + /* Process output parameters from the RPC */ + mobj_offset = 0; + for (i = 0; i < TEE_NUM_PARAMS; i++) { + param_type = TEE_PARAM_TYPE_GET(param_types, i); + res = rpc_postprocess_param(&rpc_msg_params[i], mobj_va, + &mobj_offset, param_type, ¶ms[i]); + if (res != TEE_SUCCESS) + goto exit; + } + +exit: + if (mobj) + thread_rpc_free_host_payload(mobj); + + return res; +} + +static TEE_Result invoke_command(void *sess_ctx __unused, uint32_t cmd_id, + uint32_t param_types, + TEE_Param params[TEE_NUM_PARAMS]) +{ + switch (PTA_GRPC_GET_CMD_ID(cmd_id)) { + case PTA_GRPC_EXECUTE: + return rpc_execute(cmd_id, param_types, params); + default: + break; + } + + return TEE_ERROR_NOT_IMPLEMENTED; +} + +static TEE_Result open_session(uint32_t param_types __unused, + TEE_Param params[TEE_NUM_PARAMS] __unused, + void **sess_ctx __unused) +{ + struct tee_ta_session *sess = NULL; + + sess = tee_ta_get_calling_session(); + if (!sess) + return TEE_ERROR_ACCESS_DENIED; + + if (!is_user_ta_ctx(sess->ctx)) + return TEE_ERROR_ACCESS_DENIED; + + return TEE_SUCCESS; +} + +static void close_session(void *sess_ctx __unused) +{ + /* Nothing */ +} + +pseudo_ta_register(.uuid = PTA_RPC_UUID, .name = "system.rpc", + .flags = PTA_DEFAULT_FLAGS | TA_FLAG_CONCURRENT, + .open_session_entry_point = open_session, + .close_session_entry_point = close_session, + .invoke_command_entry_point = invoke_command); diff --git a/core/pta/sub.mk b/core/pta/sub.mk index eff2ddfecdb..6e3dd112d16 100644 --- a/core/pta/sub.mk +++ b/core/pta/sub.mk @@ -2,6 +2,7 @@ subdirs-$(CFG_TEE_CORE_EMBED_INTERNAL_TESTS) += tests srcs-$(CFG_TEE_BENCHMARK) += benchmark.c srcs-$(CFG_DEVICE_ENUM_PTA) += device.c +srcs-$(CFG_GENERIC_RPC) += grpc.c srcs-$(CFG_TA_GPROF_SUPPORT) += gprof.c srcs-$(CFG_SDP_PTA) += sdp.c ifeq ($(CFG_WITH_USER_TA),y) diff --git a/lib/libutee/include/pta_grpc.h b/lib/libutee/include/pta_grpc.h new file mode 100644 index 00000000000..4ae288b08f0 --- /dev/null +++ b/lib/libutee/include/pta_grpc.h @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (c) 2019, Microsoft Corporation + */ + +#ifndef __PTA_GRPC_H +#define __PTA_GRPC_H + +/* PTA UUID: {ad0fd0ae-09e1-464b-98ed-0607ec9ebd8b} */ +#define PTA_RPC_UUID { 0xad0fd0ae, 0x09e1, 0x464b, { \ + 0x98, 0xed, 0x06, 0x07, 0xec, 0x9e, 0xbd, 0x8b } } + +/* + * The GRPC PTA accepts 32-bit commands whose bits are interpreted as follows: + * + * | xxxx | xxxx xxxx xxxx xxxx xxxx xxxx xxxx | + * CMD ID FUNC ID + * + * The CMD ID part is interpreted by the GRPC PTA. If the CMD ID is 0x1, this + * indicates that the caller wishes to send a generic RPC request to its host + * application. The FUNC ID indicates the function ID that the host application + * knows how to interpret and handle. + * + * For example, if the GRPC PTA receives the following bitmap as its command: + * + * | 0001 | 0000 0000 0000 0000 0000 0000 0011 | + * + * This means "send a generic RPC request to my host application with function + * ID 3." + * + * The reason for using 0x1 is that the entire value is passed to the REE. When + * the REE sees an RPC request, if can determine that it is a generic RPC + * request by examining the upper four bits while the bottom ones are used for + * data transfer. This way, it is not necessary to reserve one of the four + * TEE_Param structs to pass the host function ID. + * + * In the future, if the GRPC PTA must perform other tasks, the upper four bits + * can be used to indicate which of these other tasks to execute. + */ + +/* Reserve the upper four bits */ +#define PTA_GRPC_CMD_ID_MASK 0xF0000000 +#define PTA_GRPC_CMD_ID_SHIFT (32 - 4) + +/* + * Send a generic RPC to the host application of the calling TA. + * + * The four TEE_Param structs are marshalled to the REE as requested by the + * caller. That is, the structs carry no special meaning as far as the GRPC PTA + * is concerned. + */ +#define PTA_GRPC_EXECUTE 1 + +/* + * Verify that the host function ID is not so large that it overlaps with the + * reserved bits. TA's can use this to assert that their function ID's are OK. + */ +#define PTA_GRPC_IS_FUNC_ID_VALID(func_id) \ + (!((func_id) & PTA_GRPC_CMD_ID_MASK)) + +/* + * Create a composite command with the GRPC PTA command ID and the host function + * ID + */ +#define PTA_GRPC_ENCODE_CMD(cmd, func_id) \ + (((cmd) << PTA_GRPC_CMD_ID_SHIFT) | (func_id)) + +/* Retrieve the GRPC PTA command ID from a composite command */ +#define PTA_GRPC_GET_CMD_ID(cmd) ((cmd) >> PTA_GRPC_CMD_ID_SHIFT) + +/* Retrieve the host function ID from a composite command */ +#define PTA_GRPC_GET_FUNC_ID(cmd) ((cmd) & ~PTA_GRPC_CMD_ID_MASK) + +#endif /* __PTA_GRPC_H */ diff --git a/mk/config.mk b/mk/config.mk index e446b565176..b050fd6f1a3 100644 --- a/mk/config.mk +++ b/mk/config.mk @@ -369,6 +369,10 @@ CFG_ULIBS_MCOUNT ?= n # Profiling/tracing of syscall wrapper (utee_*) CFG_SYSCALL_WRAPPERS_MCOUNT ?= $(CFG_ULIBS_MCOUNT) +# Enable Generic RPC's to allow TA's to request services from their host +# application running in the rich OS on a per-session basis. +CFG_GENERIC_RPC ?= y + ifeq (y,$(filter y,$(CFG_ULIBS_MCOUNT) $(CFG_SYSCALL_WRAPPERS_MCOUNT))) ifeq (,$(filter y,$(CFG_TA_GPROF_SUPPORT) $(CFG_TA_FTRACE_SUPPORT))) $(error Cannot instrument user libraries if user mode profiling is disabled)