diff options
Diffstat (limited to 'meta-arm/meta-arm-bsp/recipes-kernel/linux/linux-arm64-ack-5.15/tc/0011-optee-isolate-smc-abi.patch')
-rw-r--r-- | meta-arm/meta-arm-bsp/recipes-kernel/linux/linux-arm64-ack-5.15/tc/0011-optee-isolate-smc-abi.patch | 3168 |
1 files changed, 0 insertions, 3168 deletions
diff --git a/meta-arm/meta-arm-bsp/recipes-kernel/linux/linux-arm64-ack-5.15/tc/0011-optee-isolate-smc-abi.patch b/meta-arm/meta-arm-bsp/recipes-kernel/linux/linux-arm64-ack-5.15/tc/0011-optee-isolate-smc-abi.patch deleted file mode 100644 index 15270bb1ea..0000000000 --- a/meta-arm/meta-arm-bsp/recipes-kernel/linux/linux-arm64-ack-5.15/tc/0011-optee-isolate-smc-abi.patch +++ /dev/null @@ -1,3168 +0,0 @@ -From bfcb795efc2d07bf99fd6c6e4f43951bc7354d5e Mon Sep 17 00:00:00 2001 -From: Jens Wiklander <jens.wiklander@linaro.org> -Date: Wed, 21 Jul 2021 16:30:28 +0200 -Subject: [PATCH 11/40] optee: isolate smc abi - -Isolate the ABI based on raw SMCs. Code specific to the raw SMC ABI is -moved into smc_abi.c. This makes room for other ABIs with a clear -separation. - -The driver changes to use module_init()/module_exit() instead of -module_platform_driver(). The platform_driver_register() and -platform_driver_unregister() functions called directly to keep the same -behavior. This is needed because module_platform_driver() is based on -module_driver() which can only be used once in a module. - -A function optee_rpc_cmd() is factored out from the function -handle_rpc_func_cmd() to handle the ABI independent part of RPC -processing. - -This patch is not supposed to change the driver behavior, it's only a -matter of reorganizing the code. - -Reviewed-by: Sumit Garg <sumit.garg@linaro.org> -Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org> -Upstream-Status: Backport [c51a564a5b48355f30309b84cdffe3f96d1ae0d3] -Signed-off-by: Rupinderjit Singh <rupinderjit.singh@arm.com> ---- - drivers/tee/optee/Makefile | 4 +- - drivers/tee/optee/call.c | 369 +------- - drivers/tee/optee/core.c | 721 ++------------- - drivers/tee/optee/optee_private.h | 106 ++- - drivers/tee/optee/rpc.c | 218 +---- - drivers/tee/optee/shm_pool.h | 14 - - drivers/tee/optee/smc_abi.c | 1361 +++++++++++++++++++++++++++++ - 7 files changed, 1506 insertions(+), 1287 deletions(-) - delete mode 100644 drivers/tee/optee/shm_pool.h - create mode 100644 drivers/tee/optee/smc_abi.c - -diff --git a/drivers/tee/optee/Makefile b/drivers/tee/optee/Makefile -index 3aa33ea9e6a6..97ac3ab3e1c0 100644 ---- a/drivers/tee/optee/Makefile -+++ b/drivers/tee/optee/Makefile -@@ -4,8 +4,8 @@ optee-objs += core.o - optee-objs += call.o - optee-objs += rpc.o - optee-objs += supp.o --optee-objs += shm_pool.o - optee-objs += device.o -+optee-objs += smc_abi.o - - # for tracing framework to find optee_trace.h --CFLAGS_call.o := -I$(src) -+CFLAGS_smc_abi.o := -I$(src) -diff --git a/drivers/tee/optee/call.c b/drivers/tee/optee/call.c -index ddedde45f1ee..9ff4f0812825 100644 ---- a/drivers/tee/optee/call.c -+++ b/drivers/tee/optee/call.c -@@ -2,28 +2,17 @@ - /* - * Copyright (c) 2015-2021, Linaro Limited - */ --#include <linux/arm-smccc.h> - #include <linux/device.h> - #include <linux/err.h> - #include <linux/errno.h> - #include <linux/mm.h> --#include <linux/sched.h> - #include <linux/slab.h> - #include <linux/tee_drv.h> - #include <linux/types.h> --#include <linux/uaccess.h> - #include "optee_private.h" --#include "optee_smc.h" --#define CREATE_TRACE_POINTS --#include "optee_trace.h" - --struct optee_call_waiter { -- struct list_head list_node; -- struct completion c; --}; -- --static void optee_cq_wait_init(struct optee_call_queue *cq, -- struct optee_call_waiter *w) -+void optee_cq_wait_init(struct optee_call_queue *cq, -+ struct optee_call_waiter *w) - { - /* - * We're preparing to make a call to secure world. In case we can't -@@ -47,8 +36,8 @@ static void optee_cq_wait_init(struct optee_call_queue *cq, - mutex_unlock(&cq->mutex); - } - --static void optee_cq_wait_for_completion(struct optee_call_queue *cq, -- struct optee_call_waiter *w) -+void optee_cq_wait_for_completion(struct optee_call_queue *cq, -+ struct optee_call_waiter *w) - { - wait_for_completion(&w->c); - -@@ -74,8 +63,8 @@ static void optee_cq_complete_one(struct optee_call_queue *cq) - } - } - --static void optee_cq_wait_final(struct optee_call_queue *cq, -- struct optee_call_waiter *w) -+void optee_cq_wait_final(struct optee_call_queue *cq, -+ struct optee_call_waiter *w) - { - /* - * We're done with the call to secure world. The thread in secure -@@ -115,73 +104,8 @@ static struct optee_session *find_session(struct optee_context_data *ctxdata, - return NULL; - } - --/** -- * optee_do_call_with_arg() - Do an SMC to OP-TEE in secure world -- * @ctx: calling context -- * @arg: shared memory holding the message to pass to secure world -- * -- * Does and SMC to OP-TEE in secure world and handles eventual resulting -- * Remote Procedure Calls (RPC) from OP-TEE. -- * -- * Returns return code from secure world, 0 is OK -- */ --int optee_do_call_with_arg(struct tee_context *ctx, struct tee_shm *arg) --{ -- struct optee *optee = tee_get_drvdata(ctx->teedev); -- struct optee_call_waiter w; -- struct optee_rpc_param param = { }; -- struct optee_call_ctx call_ctx = { }; -- phys_addr_t parg; -- int rc; -- -- rc = tee_shm_get_pa(arg, 0, &parg); -- if (rc) -- return rc; -- -- param.a0 = OPTEE_SMC_CALL_WITH_ARG; -- reg_pair_from_64(¶m.a1, ¶m.a2, parg); -- /* Initialize waiter */ -- optee_cq_wait_init(&optee->call_queue, &w); -- while (true) { -- struct arm_smccc_res res; -- -- trace_optee_invoke_fn_begin(¶m); -- optee->invoke_fn(param.a0, param.a1, param.a2, param.a3, -- param.a4, param.a5, param.a6, param.a7, -- &res); -- trace_optee_invoke_fn_end(¶m, &res); -- -- if (res.a0 == OPTEE_SMC_RETURN_ETHREAD_LIMIT) { -- /* -- * Out of threads in secure world, wait for a thread -- * become available. -- */ -- optee_cq_wait_for_completion(&optee->call_queue, &w); -- } else if (OPTEE_SMC_RETURN_IS_RPC(res.a0)) { -- cond_resched(); -- param.a0 = res.a0; -- param.a1 = res.a1; -- param.a2 = res.a2; -- param.a3 = res.a3; -- optee_handle_rpc(ctx, ¶m, &call_ctx); -- } else { -- rc = res.a0; -- break; -- } -- } -- -- optee_rpc_finalize_call(&call_ctx); -- /* -- * We're done with our thread in secure world, if there's any -- * thread waiters wake up one. -- */ -- optee_cq_wait_final(&optee->call_queue, &w); -- -- return rc; --} -- --static struct tee_shm *get_msg_arg(struct tee_context *ctx, size_t num_params, -- struct optee_msg_arg **msg_arg) -+struct tee_shm *optee_get_msg_arg(struct tee_context *ctx, size_t num_params, -+ struct optee_msg_arg **msg_arg) - { - struct tee_shm *shm; - struct optee_msg_arg *ma; -@@ -217,7 +141,7 @@ int optee_open_session(struct tee_context *ctx, - uuid_t client_uuid; - - /* +2 for the meta parameters added below */ -- shm = get_msg_arg(ctx, arg->num_params + 2, &msg_arg); -+ shm = optee_get_msg_arg(ctx, arg->num_params + 2, &msg_arg); - if (IS_ERR(shm)) - return PTR_ERR(shm); - -@@ -290,7 +214,7 @@ int optee_close_session_helper(struct tee_context *ctx, u32 session) - struct optee *optee = tee_get_drvdata(ctx->teedev); - struct optee_msg_arg *msg_arg; - -- shm = get_msg_arg(ctx, 0, &msg_arg); -+ shm = optee_get_msg_arg(ctx, 0, &msg_arg); - if (IS_ERR(shm)) - return PTR_ERR(shm); - -@@ -338,7 +262,7 @@ int optee_invoke_func(struct tee_context *ctx, struct tee_ioctl_invoke_arg *arg, - if (!sess) - return -EINVAL; - -- shm = get_msg_arg(ctx, arg->num_params, &msg_arg); -+ shm = optee_get_msg_arg(ctx, arg->num_params, &msg_arg); - if (IS_ERR(shm)) - return PTR_ERR(shm); - msg_arg->cmd = OPTEE_MSG_CMD_INVOKE_COMMAND; -@@ -384,7 +308,7 @@ int optee_cancel_req(struct tee_context *ctx, u32 cancel_id, u32 session) - if (!sess) - return -EINVAL; - -- shm = get_msg_arg(ctx, 0, &msg_arg); -+ shm = optee_get_msg_arg(ctx, 0, &msg_arg); - if (IS_ERR(shm)) - return PTR_ERR(shm); - -@@ -397,182 +321,6 @@ int optee_cancel_req(struct tee_context *ctx, u32 cancel_id, u32 session) - return 0; - } - --/** -- * optee_enable_shm_cache() - Enables caching of some shared memory allocation -- * in OP-TEE -- * @optee: main service struct -- */ --void optee_enable_shm_cache(struct optee *optee) --{ -- struct optee_call_waiter w; -- -- /* We need to retry until secure world isn't busy. */ -- optee_cq_wait_init(&optee->call_queue, &w); -- while (true) { -- struct arm_smccc_res res; -- -- optee->invoke_fn(OPTEE_SMC_ENABLE_SHM_CACHE, 0, 0, 0, 0, 0, 0, -- 0, &res); -- if (res.a0 == OPTEE_SMC_RETURN_OK) -- break; -- optee_cq_wait_for_completion(&optee->call_queue, &w); -- } -- optee_cq_wait_final(&optee->call_queue, &w); --} -- --/** -- * __optee_disable_shm_cache() - Disables caching of some shared memory -- * allocation in OP-TEE -- * @optee: main service struct -- * @is_mapped: true if the cached shared memory addresses were mapped by this -- * kernel, are safe to dereference, and should be freed -- */ --static void __optee_disable_shm_cache(struct optee *optee, bool is_mapped) --{ -- struct optee_call_waiter w; -- -- /* We need to retry until secure world isn't busy. */ -- optee_cq_wait_init(&optee->call_queue, &w); -- while (true) { -- union { -- struct arm_smccc_res smccc; -- struct optee_smc_disable_shm_cache_result result; -- } res; -- -- optee->invoke_fn(OPTEE_SMC_DISABLE_SHM_CACHE, 0, 0, 0, 0, 0, 0, -- 0, &res.smccc); -- if (res.result.status == OPTEE_SMC_RETURN_ENOTAVAIL) -- break; /* All shm's freed */ -- if (res.result.status == OPTEE_SMC_RETURN_OK) { -- struct tee_shm *shm; -- -- /* -- * Shared memory references that were not mapped by -- * this kernel must be ignored to prevent a crash. -- */ -- if (!is_mapped) -- continue; -- -- shm = reg_pair_to_ptr(res.result.shm_upper32, -- res.result.shm_lower32); -- tee_shm_free(shm); -- } else { -- optee_cq_wait_for_completion(&optee->call_queue, &w); -- } -- } -- optee_cq_wait_final(&optee->call_queue, &w); --} -- --/** -- * optee_disable_shm_cache() - Disables caching of mapped shared memory -- * allocations in OP-TEE -- * @optee: main service struct -- */ --void optee_disable_shm_cache(struct optee *optee) --{ -- return __optee_disable_shm_cache(optee, true); --} -- --/** -- * optee_disable_unmapped_shm_cache() - Disables caching of shared memory -- * allocations in OP-TEE which are not -- * currently mapped -- * @optee: main service struct -- */ --void optee_disable_unmapped_shm_cache(struct optee *optee) --{ -- return __optee_disable_shm_cache(optee, false); --} -- --#define PAGELIST_ENTRIES_PER_PAGE \ -- ((OPTEE_MSG_NONCONTIG_PAGE_SIZE / sizeof(u64)) - 1) -- --/** -- * optee_fill_pages_list() - write list of user pages to given shared -- * buffer. -- * -- * @dst: page-aligned buffer where list of pages will be stored -- * @pages: array of pages that represents shared buffer -- * @num_pages: number of entries in @pages -- * @page_offset: offset of user buffer from page start -- * -- * @dst should be big enough to hold list of user page addresses and -- * links to the next pages of buffer -- */ --void optee_fill_pages_list(u64 *dst, struct page **pages, int num_pages, -- size_t page_offset) --{ -- int n = 0; -- phys_addr_t optee_page; -- /* -- * Refer to OPTEE_MSG_ATTR_NONCONTIG description in optee_msg.h -- * for details. -- */ -- struct { -- u64 pages_list[PAGELIST_ENTRIES_PER_PAGE]; -- u64 next_page_data; -- } *pages_data; -- -- /* -- * Currently OP-TEE uses 4k page size and it does not looks -- * like this will change in the future. On other hand, there are -- * no know ARM architectures with page size < 4k. -- * Thus the next built assert looks redundant. But the following -- * code heavily relies on this assumption, so it is better be -- * safe than sorry. -- */ -- BUILD_BUG_ON(PAGE_SIZE < OPTEE_MSG_NONCONTIG_PAGE_SIZE); -- -- pages_data = (void *)dst; -- /* -- * If linux page is bigger than 4k, and user buffer offset is -- * larger than 4k/8k/12k/etc this will skip first 4k pages, -- * because they bear no value data for OP-TEE. -- */ -- optee_page = page_to_phys(*pages) + -- round_down(page_offset, OPTEE_MSG_NONCONTIG_PAGE_SIZE); -- -- while (true) { -- pages_data->pages_list[n++] = optee_page; -- -- if (n == PAGELIST_ENTRIES_PER_PAGE) { -- pages_data->next_page_data = -- virt_to_phys(pages_data + 1); -- pages_data++; -- n = 0; -- } -- -- optee_page += OPTEE_MSG_NONCONTIG_PAGE_SIZE; -- if (!(optee_page & ~PAGE_MASK)) { -- if (!--num_pages) -- break; -- pages++; -- optee_page = page_to_phys(*pages); -- } -- } --} -- --/* -- * The final entry in each pagelist page is a pointer to the next -- * pagelist page. -- */ --static size_t get_pages_list_size(size_t num_entries) --{ -- int pages = DIV_ROUND_UP(num_entries, PAGELIST_ENTRIES_PER_PAGE); -- -- return pages * OPTEE_MSG_NONCONTIG_PAGE_SIZE; --} -- --u64 *optee_allocate_pages_list(size_t num_entries) --{ -- return alloc_pages_exact(get_pages_list_size(num_entries), GFP_KERNEL); --} -- --void optee_free_pages_list(void *list, size_t num_entries) --{ -- free_pages_exact(list, get_pages_list_size(num_entries)); --} -- - static bool is_normal_memory(pgprot_t p) - { - #if defined(CONFIG_ARM) -@@ -596,7 +344,7 @@ static int __check_mem_type(struct vm_area_struct *vma, unsigned long end) - return -EINVAL; - } - --static int check_mem_type(unsigned long start, size_t num_pages) -+int optee_check_mem_type(unsigned long start, size_t num_pages) - { - struct mm_struct *mm = current->mm; - int rc; -@@ -615,94 +363,3 @@ static int check_mem_type(unsigned long start, size_t num_pages) - - return rc; - } -- --int optee_shm_register(struct tee_context *ctx, struct tee_shm *shm, -- struct page **pages, size_t num_pages, -- unsigned long start) --{ -- struct optee *optee = tee_get_drvdata(ctx->teedev); -- struct optee_msg_arg *msg_arg; -- struct tee_shm *shm_arg; -- u64 *pages_list; -- int rc; -- -- if (!num_pages) -- return -EINVAL; -- -- rc = check_mem_type(start, num_pages); -- if (rc) -- return rc; -- -- pages_list = optee_allocate_pages_list(num_pages); -- if (!pages_list) -- return -ENOMEM; -- -- shm_arg = get_msg_arg(ctx, 1, &msg_arg); -- if (IS_ERR(shm_arg)) { -- rc = PTR_ERR(shm_arg); -- goto out; -- } -- -- optee_fill_pages_list(pages_list, pages, num_pages, -- tee_shm_get_page_offset(shm)); -- -- msg_arg->cmd = OPTEE_MSG_CMD_REGISTER_SHM; -- msg_arg->params->attr = OPTEE_MSG_ATTR_TYPE_TMEM_OUTPUT | -- OPTEE_MSG_ATTR_NONCONTIG; -- msg_arg->params->u.tmem.shm_ref = (unsigned long)shm; -- msg_arg->params->u.tmem.size = tee_shm_get_size(shm); -- /* -- * In the least bits of msg_arg->params->u.tmem.buf_ptr we -- * store buffer offset from 4k page, as described in OP-TEE ABI. -- */ -- msg_arg->params->u.tmem.buf_ptr = virt_to_phys(pages_list) | -- (tee_shm_get_page_offset(shm) & (OPTEE_MSG_NONCONTIG_PAGE_SIZE - 1)); -- -- if (optee->ops->do_call_with_arg(ctx, shm_arg) || -- msg_arg->ret != TEEC_SUCCESS) -- rc = -EINVAL; -- -- tee_shm_free(shm_arg); --out: -- optee_free_pages_list(pages_list, num_pages); -- return rc; --} -- --int optee_shm_unregister(struct tee_context *ctx, struct tee_shm *shm) --{ -- struct optee *optee = tee_get_drvdata(ctx->teedev); -- struct optee_msg_arg *msg_arg; -- struct tee_shm *shm_arg; -- int rc = 0; -- -- shm_arg = get_msg_arg(ctx, 1, &msg_arg); -- if (IS_ERR(shm_arg)) -- return PTR_ERR(shm_arg); -- -- msg_arg->cmd = OPTEE_MSG_CMD_UNREGISTER_SHM; -- -- msg_arg->params[0].attr = OPTEE_MSG_ATTR_TYPE_RMEM_INPUT; -- msg_arg->params[0].u.rmem.shm_ref = (unsigned long)shm; -- -- if (optee->ops->do_call_with_arg(ctx, shm_arg) || -- msg_arg->ret != TEEC_SUCCESS) -- rc = -EINVAL; -- tee_shm_free(shm_arg); -- return rc; --} -- --int optee_shm_register_supp(struct tee_context *ctx, struct tee_shm *shm, -- struct page **pages, size_t num_pages, -- unsigned long start) --{ -- /* -- * We don't want to register supplicant memory in OP-TEE. -- * Instead information about it will be passed in RPC code. -- */ -- return check_mem_type(start, num_pages); --} -- --int optee_shm_unregister_supp(struct tee_context *ctx, struct tee_shm *shm) --{ -- return 0; --} -diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c -index 26492d3115f5..27b855325b33 100644 ---- a/drivers/tee/optee/core.c -+++ b/drivers/tee/optee/core.c -@@ -1,260 +1,71 @@ - // SPDX-License-Identifier: GPL-2.0-only - /* - * Copyright (c) 2015-2021, Linaro Limited -+ * Copyright (c) 2016, EPAM Systems - */ - - #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - --#include <linux/arm-smccc.h> - #include <linux/crash_dump.h> - #include <linux/errno.h> - #include <linux/io.h> -+#include <linux/mm.h> - #include <linux/module.h> --#include <linux/of.h> --#include <linux/of_platform.h> --#include <linux/platform_device.h> - #include <linux/slab.h> - #include <linux/string.h> - #include <linux/tee_drv.h> - #include <linux/types.h> --#include <linux/uaccess.h> - #include <linux/workqueue.h> - #include "optee_private.h" --#include "optee_smc.h" --#include "shm_pool.h" - --#define DRIVER_NAME "optee" -- --#define OPTEE_SHM_NUM_PRIV_PAGES CONFIG_OPTEE_SHM_NUM_PRIV_PAGES -- --static void from_msg_param_value(struct tee_param *p, u32 attr, -- const struct optee_msg_param *mp) --{ -- p->attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT + -- attr - OPTEE_MSG_ATTR_TYPE_VALUE_INPUT; -- p->u.value.a = mp->u.value.a; -- p->u.value.b = mp->u.value.b; -- p->u.value.c = mp->u.value.c; --} -- --static int from_msg_param_tmp_mem(struct tee_param *p, u32 attr, -- const struct optee_msg_param *mp) --{ -- struct tee_shm *shm; -- phys_addr_t pa; -- int rc; -- -- p->attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT + -- attr - OPTEE_MSG_ATTR_TYPE_TMEM_INPUT; -- p->u.memref.size = mp->u.tmem.size; -- shm = (struct tee_shm *)(unsigned long)mp->u.tmem.shm_ref; -- if (!shm) { -- p->u.memref.shm_offs = 0; -- p->u.memref.shm = NULL; -- return 0; -- } -- -- rc = tee_shm_get_pa(shm, 0, &pa); -- if (rc) -- return rc; -- -- p->u.memref.shm_offs = mp->u.tmem.buf_ptr - pa; -- p->u.memref.shm = shm; -- -- /* Check that the memref is covered by the shm object */ -- if (p->u.memref.size) { -- size_t o = p->u.memref.shm_offs + -- p->u.memref.size - 1; -- -- rc = tee_shm_get_pa(shm, o, NULL); -- if (rc) -- return rc; -- } -- -- return 0; --} -- --static void from_msg_param_reg_mem(struct tee_param *p, u32 attr, -- const struct optee_msg_param *mp) -+int optee_pool_op_alloc_helper(struct tee_shm_pool_mgr *poolm, -+ struct tee_shm *shm, size_t size, -+ int (*shm_register)(struct tee_context *ctx, -+ struct tee_shm *shm, -+ struct page **pages, -+ size_t num_pages, -+ unsigned long start)) - { -- struct tee_shm *shm; -+ unsigned int order = get_order(size); -+ struct page *page; -+ int rc = 0; - -- p->attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT + -- attr - OPTEE_MSG_ATTR_TYPE_RMEM_INPUT; -- p->u.memref.size = mp->u.rmem.size; -- shm = (struct tee_shm *)(unsigned long)mp->u.rmem.shm_ref; -- -- if (shm) { -- p->u.memref.shm_offs = mp->u.rmem.offs; -- p->u.memref.shm = shm; -- } else { -- p->u.memref.shm_offs = 0; -- p->u.memref.shm = NULL; -- } --} -- --/** -- * optee_from_msg_param() - convert from OPTEE_MSG parameters to -- * struct tee_param -- * @optee: main service struct -- * @params: subsystem internal parameter representation -- * @num_params: number of elements in the parameter arrays -- * @msg_params: OPTEE_MSG parameters -- * Returns 0 on success or <0 on failure -- */ --static int optee_from_msg_param(struct optee *optee, struct tee_param *params, -- size_t num_params, -- const struct optee_msg_param *msg_params) --{ -- int rc; -- size_t n; -+ page = alloc_pages(GFP_KERNEL | __GFP_ZERO, order); -+ if (!page) -+ return -ENOMEM; - -- for (n = 0; n < num_params; n++) { -- struct tee_param *p = params + n; -- const struct optee_msg_param *mp = msg_params + n; -- u32 attr = mp->attr & OPTEE_MSG_ATTR_TYPE_MASK; -+ shm->kaddr = page_address(page); -+ shm->paddr = page_to_phys(page); -+ shm->size = PAGE_SIZE << order; - -- switch (attr) { -- case OPTEE_MSG_ATTR_TYPE_NONE: -- p->attr = TEE_IOCTL_PARAM_ATTR_TYPE_NONE; -- memset(&p->u, 0, sizeof(p->u)); -- break; -- case OPTEE_MSG_ATTR_TYPE_VALUE_INPUT: -- case OPTEE_MSG_ATTR_TYPE_VALUE_OUTPUT: -- case OPTEE_MSG_ATTR_TYPE_VALUE_INOUT: -- from_msg_param_value(p, attr, mp); -- break; -- case OPTEE_MSG_ATTR_TYPE_TMEM_INPUT: -- case OPTEE_MSG_ATTR_TYPE_TMEM_OUTPUT: -- case OPTEE_MSG_ATTR_TYPE_TMEM_INOUT: -- rc = from_msg_param_tmp_mem(p, attr, mp); -- if (rc) -- return rc; -- break; -- case OPTEE_MSG_ATTR_TYPE_RMEM_INPUT: -- case OPTEE_MSG_ATTR_TYPE_RMEM_OUTPUT: -- case OPTEE_MSG_ATTR_TYPE_RMEM_INOUT: -- from_msg_param_reg_mem(p, attr, mp); -- break; -+ if (shm_register) { -+ unsigned int nr_pages = 1 << order, i; -+ struct page **pages; - -- default: -- return -EINVAL; -+ pages = kcalloc(nr_pages, sizeof(*pages), GFP_KERNEL); -+ if (!pages) { -+ rc = -ENOMEM; -+ goto err; - } -- } -- return 0; --} -- --static void to_msg_param_value(struct optee_msg_param *mp, -- const struct tee_param *p) --{ -- mp->attr = OPTEE_MSG_ATTR_TYPE_VALUE_INPUT + p->attr - -- TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT; -- mp->u.value.a = p->u.value.a; -- mp->u.value.b = p->u.value.b; -- mp->u.value.c = p->u.value.c; --} -- --static int to_msg_param_tmp_mem(struct optee_msg_param *mp, -- const struct tee_param *p) --{ -- int rc; -- phys_addr_t pa; - -- mp->attr = OPTEE_MSG_ATTR_TYPE_TMEM_INPUT + p->attr - -- TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT; -- -- mp->u.tmem.shm_ref = (unsigned long)p->u.memref.shm; -- mp->u.tmem.size = p->u.memref.size; -+ for (i = 0; i < nr_pages; i++) { -+ pages[i] = page; -+ page++; -+ } - -- if (!p->u.memref.shm) { -- mp->u.tmem.buf_ptr = 0; -- return 0; -+ shm->flags |= TEE_SHM_REGISTER; -+ rc = shm_register(shm->ctx, shm, pages, nr_pages, -+ (unsigned long)shm->kaddr); -+ kfree(pages); -+ if (rc) -+ goto err; - } - -- rc = tee_shm_get_pa(p->u.memref.shm, p->u.memref.shm_offs, &pa); -- if (rc) -- return rc; -- -- mp->u.tmem.buf_ptr = pa; -- mp->attr |= OPTEE_MSG_ATTR_CACHE_PREDEFINED << -- OPTEE_MSG_ATTR_CACHE_SHIFT; -- -- return 0; --} -- --static int to_msg_param_reg_mem(struct optee_msg_param *mp, -- const struct tee_param *p) --{ -- mp->attr = OPTEE_MSG_ATTR_TYPE_RMEM_INPUT + p->attr - -- TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT; -- -- mp->u.rmem.shm_ref = (unsigned long)p->u.memref.shm; -- mp->u.rmem.size = p->u.memref.size; -- mp->u.rmem.offs = p->u.memref.shm_offs; -- return 0; --} -- --/** -- * optee_to_msg_param() - convert from struct tee_params to OPTEE_MSG parameters -- * @optee: main service struct -- * @msg_params: OPTEE_MSG parameters -- * @num_params: number of elements in the parameter arrays -- * @params: subsystem itnernal parameter representation -- * Returns 0 on success or <0 on failure -- */ --static int optee_to_msg_param(struct optee *optee, -- struct optee_msg_param *msg_params, -- size_t num_params, const struct tee_param *params) --{ -- int rc; -- size_t n; -- -- for (n = 0; n < num_params; n++) { -- const struct tee_param *p = params + n; -- struct optee_msg_param *mp = msg_params + n; -- -- switch (p->attr) { -- case TEE_IOCTL_PARAM_ATTR_TYPE_NONE: -- mp->attr = TEE_IOCTL_PARAM_ATTR_TYPE_NONE; -- memset(&mp->u, 0, sizeof(mp->u)); -- break; -- case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT: -- case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT: -- case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT: -- to_msg_param_value(mp, p); -- break; -- case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT: -- case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT: -- case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT: -- if (tee_shm_is_registered(p->u.memref.shm)) -- rc = to_msg_param_reg_mem(mp, p); -- else -- rc = to_msg_param_tmp_mem(mp, p); -- if (rc) -- return rc; -- break; -- default: -- return -EINVAL; -- } -- } - return 0; --} - --static void optee_get_version(struct tee_device *teedev, -- struct tee_ioctl_version_data *vers) --{ -- struct tee_ioctl_version_data v = { -- .impl_id = TEE_IMPL_ID_OPTEE, -- .impl_caps = TEE_OPTEE_CAP_TZ, -- .gen_caps = TEE_GEN_CAP_GP, -- }; -- struct optee *optee = tee_get_drvdata(teedev); -- -- if (optee->sec_caps & OPTEE_SMC_SEC_CAP_DYNAMIC_SHM) -- v.gen_caps |= TEE_GEN_CAP_REG_MEM; -- if (optee->sec_caps & OPTEE_SMC_SEC_CAP_MEMREF_NULL) -- v.gen_caps |= TEE_GEN_CAP_MEMREF_NULL; -- *vers = v; -+err: -+ __free_pages(page, order); -+ return rc; - } - - static void optee_bus_scan(struct work_struct *work) -@@ -262,7 +73,7 @@ static void optee_bus_scan(struct work_struct *work) - WARN_ON(optee_enumerate_devices(PTA_CMD_GET_DEVICES_SUPP)); - } - --static int optee_open(struct tee_context *ctx) -+int optee_open(struct tee_context *ctx, bool cap_memref_null) - { - struct optee_context_data *ctxdata; - struct tee_device *teedev = ctx->teedev; -@@ -300,11 +111,7 @@ static int optee_open(struct tee_context *ctx) - mutex_init(&ctxdata->mutex); - INIT_LIST_HEAD(&ctxdata->sess_list); - -- if (optee->sec_caps & OPTEE_SMC_SEC_CAP_MEMREF_NULL) -- ctx->cap_memref_null = true; -- else -- ctx->cap_memref_null = false; -- -+ ctx->cap_memref_null = cap_memref_null; - ctx->data = ctxdata; - return 0; - } -@@ -330,12 +137,12 @@ static void optee_release_helper(struct tee_context *ctx, - ctx->data = NULL; - } - --static void optee_release(struct tee_context *ctx) -+void optee_release(struct tee_context *ctx) - { - optee_release_helper(ctx, optee_close_session_helper); - } - --static void optee_release_supp(struct tee_context *ctx) -+void optee_release_supp(struct tee_context *ctx) - { - struct optee *optee = tee_get_drvdata(ctx->teedev); - -@@ -347,287 +154,11 @@ static void optee_release_supp(struct tee_context *ctx) - optee_supp_release(&optee->supp); - } - --static const struct tee_driver_ops optee_clnt_ops = { -- .get_version = optee_get_version, -- .open = optee_open, -- .release = optee_release, -- .open_session = optee_open_session, -- .close_session = optee_close_session, -- .invoke_func = optee_invoke_func, -- .cancel_req = optee_cancel_req, -- .shm_register = optee_shm_register, -- .shm_unregister = optee_shm_unregister, --}; -- --static const struct tee_desc optee_clnt_desc = { -- .name = DRIVER_NAME "-clnt", -- .ops = &optee_clnt_ops, -- .owner = THIS_MODULE, --}; -- --static const struct tee_driver_ops optee_supp_ops = { -- .get_version = optee_get_version, -- .open = optee_open, -- .release = optee_release_supp, -- .supp_recv = optee_supp_recv, -- .supp_send = optee_supp_send, -- .shm_register = optee_shm_register_supp, -- .shm_unregister = optee_shm_unregister_supp, --}; -- --static const struct tee_desc optee_supp_desc = { -- .name = DRIVER_NAME "-supp", -- .ops = &optee_supp_ops, -- .owner = THIS_MODULE, -- .flags = TEE_DESC_PRIVILEGED, --}; -- --static const struct optee_ops optee_ops = { -- .do_call_with_arg = optee_do_call_with_arg, -- .to_msg_param = optee_to_msg_param, -- .from_msg_param = optee_from_msg_param, --}; -- --static bool optee_msg_api_uid_is_optee_api(optee_invoke_fn *invoke_fn) --{ -- struct arm_smccc_res res; -- -- invoke_fn(OPTEE_SMC_CALLS_UID, 0, 0, 0, 0, 0, 0, 0, &res); -- -- if (res.a0 == OPTEE_MSG_UID_0 && res.a1 == OPTEE_MSG_UID_1 && -- res.a2 == OPTEE_MSG_UID_2 && res.a3 == OPTEE_MSG_UID_3) -- return true; -- return false; --} -- --static void optee_msg_get_os_revision(optee_invoke_fn *invoke_fn) --{ -- union { -- struct arm_smccc_res smccc; -- struct optee_smc_call_get_os_revision_result result; -- } res = { -- .result = { -- .build_id = 0 -- } -- }; -- -- invoke_fn(OPTEE_SMC_CALL_GET_OS_REVISION, 0, 0, 0, 0, 0, 0, 0, -- &res.smccc); -- -- if (res.result.build_id) -- pr_info("revision %lu.%lu (%08lx)", res.result.major, -- res.result.minor, res.result.build_id); -- else -- pr_info("revision %lu.%lu", res.result.major, res.result.minor); --} -- --static bool optee_msg_api_revision_is_compatible(optee_invoke_fn *invoke_fn) --{ -- union { -- struct arm_smccc_res smccc; -- struct optee_smc_calls_revision_result result; -- } res; -- -- invoke_fn(OPTEE_SMC_CALLS_REVISION, 0, 0, 0, 0, 0, 0, 0, &res.smccc); -- -- if (res.result.major == OPTEE_MSG_REVISION_MAJOR && -- (int)res.result.minor >= OPTEE_MSG_REVISION_MINOR) -- return true; -- return false; --} -- --static bool optee_msg_exchange_capabilities(optee_invoke_fn *invoke_fn, -- u32 *sec_caps) --{ -- union { -- struct arm_smccc_res smccc; -- struct optee_smc_exchange_capabilities_result result; -- } res; -- u32 a1 = 0; -- -- /* -- * TODO This isn't enough to tell if it's UP system (from kernel -- * point of view) or not, is_smp() returns the the information -- * needed, but can't be called directly from here. -- */ -- if (!IS_ENABLED(CONFIG_SMP) || nr_cpu_ids == 1) -- a1 |= OPTEE_SMC_NSEC_CAP_UNIPROCESSOR; -- -- invoke_fn(OPTEE_SMC_EXCHANGE_CAPABILITIES, a1, 0, 0, 0, 0, 0, 0, -- &res.smccc); -- -- if (res.result.status != OPTEE_SMC_RETURN_OK) -- return false; -- -- *sec_caps = res.result.capabilities; -- return true; --} -- --static struct tee_shm_pool *optee_config_dyn_shm(void) --{ -- struct tee_shm_pool_mgr *priv_mgr; -- struct tee_shm_pool_mgr *dmabuf_mgr; -- void *rc; -- -- rc = optee_shm_pool_alloc_pages(); -- if (IS_ERR(rc)) -- return rc; -- priv_mgr = rc; -- -- rc = optee_shm_pool_alloc_pages(); -- if (IS_ERR(rc)) { -- tee_shm_pool_mgr_destroy(priv_mgr); -- return rc; -- } -- dmabuf_mgr = rc; -- -- rc = tee_shm_pool_alloc(priv_mgr, dmabuf_mgr); -- if (IS_ERR(rc)) { -- tee_shm_pool_mgr_destroy(priv_mgr); -- tee_shm_pool_mgr_destroy(dmabuf_mgr); -- } -- -- return rc; --} -- --static struct tee_shm_pool * --optee_config_shm_memremap(optee_invoke_fn *invoke_fn, void **memremaped_shm) -+void optee_remove_common(struct optee *optee) - { -- union { -- struct arm_smccc_res smccc; -- struct optee_smc_get_shm_config_result result; -- } res; -- unsigned long vaddr; -- phys_addr_t paddr; -- size_t size; -- phys_addr_t begin; -- phys_addr_t end; -- void *va; -- struct tee_shm_pool_mgr *priv_mgr; -- struct tee_shm_pool_mgr *dmabuf_mgr; -- void *rc; -- const int sz = OPTEE_SHM_NUM_PRIV_PAGES * PAGE_SIZE; -- -- invoke_fn(OPTEE_SMC_GET_SHM_CONFIG, 0, 0, 0, 0, 0, 0, 0, &res.smccc); -- if (res.result.status != OPTEE_SMC_RETURN_OK) { -- pr_err("static shm service not available\n"); -- return ERR_PTR(-ENOENT); -- } -- -- if (res.result.settings != OPTEE_SMC_SHM_CACHED) { -- pr_err("only normal cached shared memory supported\n"); -- return ERR_PTR(-EINVAL); -- } -- -- begin = roundup(res.result.start, PAGE_SIZE); -- end = rounddown(res.result.start + res.result.size, PAGE_SIZE); -- paddr = begin; -- size = end - begin; -- -- if (size < 2 * OPTEE_SHM_NUM_PRIV_PAGES * PAGE_SIZE) { -- pr_err("too small shared memory area\n"); -- return ERR_PTR(-EINVAL); -- } -- -- va = memremap(paddr, size, MEMREMAP_WB); -- if (!va) { -- pr_err("shared memory ioremap failed\n"); -- return ERR_PTR(-EINVAL); -- } -- vaddr = (unsigned long)va; -- -- rc = tee_shm_pool_mgr_alloc_res_mem(vaddr, paddr, sz, -- 3 /* 8 bytes aligned */); -- if (IS_ERR(rc)) -- goto err_memunmap; -- priv_mgr = rc; -- -- vaddr += sz; -- paddr += sz; -- size -= sz; -- -- rc = tee_shm_pool_mgr_alloc_res_mem(vaddr, paddr, size, PAGE_SHIFT); -- if (IS_ERR(rc)) -- goto err_free_priv_mgr; -- dmabuf_mgr = rc; -- -- rc = tee_shm_pool_alloc(priv_mgr, dmabuf_mgr); -- if (IS_ERR(rc)) -- goto err_free_dmabuf_mgr; -- -- *memremaped_shm = va; -- -- return rc; -- --err_free_dmabuf_mgr: -- tee_shm_pool_mgr_destroy(dmabuf_mgr); --err_free_priv_mgr: -- tee_shm_pool_mgr_destroy(priv_mgr); --err_memunmap: -- memunmap(va); -- return rc; --} -- --/* Simple wrapper functions to be able to use a function pointer */ --static void optee_smccc_smc(unsigned long a0, unsigned long a1, -- unsigned long a2, unsigned long a3, -- unsigned long a4, unsigned long a5, -- unsigned long a6, unsigned long a7, -- struct arm_smccc_res *res) --{ -- arm_smccc_smc(a0, a1, a2, a3, a4, a5, a6, a7, res); --} -- --static void optee_smccc_hvc(unsigned long a0, unsigned long a1, -- unsigned long a2, unsigned long a3, -- unsigned long a4, unsigned long a5, -- unsigned long a6, unsigned long a7, -- struct arm_smccc_res *res) --{ -- arm_smccc_hvc(a0, a1, a2, a3, a4, a5, a6, a7, res); --} -- --static optee_invoke_fn *get_invoke_func(struct device *dev) --{ -- const char *method; -- -- pr_info("probing for conduit method.\n"); -- -- if (device_property_read_string(dev, "method", &method)) { -- pr_warn("missing \"method\" property\n"); -- return ERR_PTR(-ENXIO); -- } -- -- if (!strcmp("hvc", method)) -- return optee_smccc_hvc; -- else if (!strcmp("smc", method)) -- return optee_smccc_smc; -- -- pr_warn("invalid \"method\" property: %s\n", method); -- return ERR_PTR(-EINVAL); --} -- --/* optee_remove - Device Removal Routine -- * @pdev: platform device information struct -- * -- * optee_remove is called by platform subsystem to alert the driver -- * that it should release the device -- */ -- --static int optee_remove(struct platform_device *pdev) --{ -- struct optee *optee = platform_get_drvdata(pdev); -- - /* Unregister OP-TEE specific client devices on TEE bus */ - optee_unregister_devices(); - -- /* -- * Ask OP-TEE to free all cached shared memory objects to decrease -- * reference counters and also avoid wild pointers in secure world -- * into the old shared memory range. -- */ -- optee_disable_shm_cache(optee); -- - /* - * The two devices have to be unregistered before we can free the - * other resources. -@@ -636,39 +167,13 @@ static int optee_remove(struct platform_device *pdev) - tee_device_unregister(optee->teedev); - - tee_shm_pool_free(optee->pool); -- if (optee->memremaped_shm) -- memunmap(optee->memremaped_shm); - optee_wait_queue_exit(&optee->wait_queue); - optee_supp_uninit(&optee->supp); - mutex_destroy(&optee->call_queue.mutex); -- -- kfree(optee); -- -- return 0; --} -- --/* optee_shutdown - Device Removal Routine -- * @pdev: platform device information struct -- * -- * platform_shutdown is called by the platform subsystem to alert -- * the driver that a shutdown, reboot, or kexec is happening and -- * device must be disabled. -- */ --static void optee_shutdown(struct platform_device *pdev) --{ -- optee_disable_shm_cache(platform_get_drvdata(pdev)); - } - --static int optee_probe(struct platform_device *pdev) -+static int optee_core_init(void) - { -- optee_invoke_fn *invoke_fn; -- struct tee_shm_pool *pool = ERR_PTR(-EINVAL); -- struct optee *optee = NULL; -- void *memremaped_shm = NULL; -- struct tee_device *teedev; -- u32 sec_caps; -- int rc; -- - /* - * The kernel may have crashed at the same time that all available - * secure world threads were suspended and we cannot reschedule the -@@ -679,139 +184,15 @@ static int optee_probe(struct platform_device *pdev) - if (is_kdump_kernel()) - return -ENODEV; - -- invoke_fn = get_invoke_func(&pdev->dev); -- if (IS_ERR(invoke_fn)) -- return PTR_ERR(invoke_fn); -- -- if (!optee_msg_api_uid_is_optee_api(invoke_fn)) { -- pr_warn("api uid mismatch\n"); -- return -EINVAL; -- } -- -- optee_msg_get_os_revision(invoke_fn); -- -- if (!optee_msg_api_revision_is_compatible(invoke_fn)) { -- pr_warn("api revision mismatch\n"); -- return -EINVAL; -- } -- -- if (!optee_msg_exchange_capabilities(invoke_fn, &sec_caps)) { -- pr_warn("capabilities mismatch\n"); -- return -EINVAL; -- } -- -- /* -- * Try to use dynamic shared memory if possible -- */ -- if (sec_caps & OPTEE_SMC_SEC_CAP_DYNAMIC_SHM) -- pool = optee_config_dyn_shm(); -- -- /* -- * If dynamic shared memory is not available or failed - try static one -- */ -- if (IS_ERR(pool) && (sec_caps & OPTEE_SMC_SEC_CAP_HAVE_RESERVED_SHM)) -- pool = optee_config_shm_memremap(invoke_fn, &memremaped_shm); -- -- if (IS_ERR(pool)) -- return PTR_ERR(pool); -- -- optee = kzalloc(sizeof(*optee), GFP_KERNEL); -- if (!optee) { -- rc = -ENOMEM; -- goto err; -- } -- -- optee->ops = &optee_ops; -- optee->invoke_fn = invoke_fn; -- optee->sec_caps = sec_caps; -- -- teedev = tee_device_alloc(&optee_clnt_desc, NULL, pool, optee); -- if (IS_ERR(teedev)) { -- rc = PTR_ERR(teedev); -- goto err; -- } -- optee->teedev = teedev; -- -- teedev = tee_device_alloc(&optee_supp_desc, NULL, pool, optee); -- if (IS_ERR(teedev)) { -- rc = PTR_ERR(teedev); -- goto err; -- } -- optee->supp_teedev = teedev; -- -- rc = tee_device_register(optee->teedev); -- if (rc) -- goto err; -- -- rc = tee_device_register(optee->supp_teedev); -- if (rc) -- goto err; -- -- mutex_init(&optee->call_queue.mutex); -- INIT_LIST_HEAD(&optee->call_queue.waiters); -- optee_wait_queue_init(&optee->wait_queue); -- optee_supp_init(&optee->supp); -- optee->memremaped_shm = memremaped_shm; -- optee->pool = pool; -- -- /* -- * Ensure that there are no pre-existing shm objects before enabling -- * the shm cache so that there's no chance of receiving an invalid -- * address during shutdown. This could occur, for example, if we're -- * kexec booting from an older kernel that did not properly cleanup the -- * shm cache. -- */ -- optee_disable_unmapped_shm_cache(optee); -- -- optee_enable_shm_cache(optee); -- -- if (optee->sec_caps & OPTEE_SMC_SEC_CAP_DYNAMIC_SHM) -- pr_info("dynamic shared memory is enabled\n"); -- -- platform_set_drvdata(pdev, optee); -- -- rc = optee_enumerate_devices(PTA_CMD_GET_DEVICES); -- if (rc) { -- optee_remove(pdev); -- return rc; -- } -- -- pr_info("initialized driver\n"); -- return 0; --err: -- if (optee) { -- /* -- * tee_device_unregister() is safe to call even if the -- * devices hasn't been registered with -- * tee_device_register() yet. -- */ -- tee_device_unregister(optee->supp_teedev); -- tee_device_unregister(optee->teedev); -- kfree(optee); -- } -- if (pool) -- tee_shm_pool_free(pool); -- if (memremaped_shm) -- memunmap(memremaped_shm); -- return rc; -+ return optee_smc_abi_register(); - } -+module_init(optee_core_init); - --static const struct of_device_id optee_dt_match[] = { -- { .compatible = "linaro,optee-tz" }, -- {}, --}; --MODULE_DEVICE_TABLE(of, optee_dt_match); -- --static struct platform_driver optee_driver = { -- .probe = optee_probe, -- .remove = optee_remove, -- .shutdown = optee_shutdown, -- .driver = { -- .name = "optee", -- .of_match_table = optee_dt_match, -- }, --}; --module_platform_driver(optee_driver); -+static void optee_core_exit(void) -+{ -+ optee_smc_abi_unregister(); -+} -+module_exit(optee_core_exit); - - MODULE_AUTHOR("Linaro"); - MODULE_DESCRIPTION("OP-TEE driver"); -diff --git a/drivers/tee/optee/optee_private.h b/drivers/tee/optee/optee_private.h -index beca97017996..40af6b059b20 100644 ---- a/drivers/tee/optee/optee_private.h -+++ b/drivers/tee/optee/optee_private.h -@@ -12,6 +12,8 @@ - #include <linux/types.h> - #include "optee_msg.h" - -+#define DRIVER_NAME "optee" -+ - #define OPTEE_MAX_ARG_SIZE 1024 - - /* Some Global Platform error codes used in this driver */ -@@ -29,6 +31,11 @@ typedef void (optee_invoke_fn)(unsigned long, unsigned long, unsigned long, - unsigned long, unsigned long, - struct arm_smccc_res *); - -+struct optee_call_waiter { -+ struct list_head list_node; -+ struct completion c; -+}; -+ - struct optee_call_queue { - /* Serializes access to this struct */ - struct mutex mutex; -@@ -66,6 +73,19 @@ struct optee_supp { - struct completion reqs_c; - }; - -+/** -+ * struct optee_smc - SMC ABI specifics -+ * @invoke_fn: function to issue smc or hvc -+ * @memremaped_shm virtual address of memory in shared memory pool -+ * @sec_caps: secure world capabilities defined by -+ * OPTEE_SMC_SEC_CAP_* in optee_smc.h -+ */ -+struct optee_smc { -+ optee_invoke_fn *invoke_fn; -+ void *memremaped_shm; -+ u32 sec_caps; -+}; -+ - struct optee; - - /** -@@ -95,15 +115,12 @@ struct optee_ops { - * @ops: internal callbacks for different ways to reach secure - * world - * @teedev: client device -- * @invoke_fn: function to issue smc or hvc -+ * @smc: specific to SMC ABI - * @call_queue: queue of threads waiting to call @invoke_fn - * @wait_queue: queue of threads from secure world waiting for a - * secure world sync object - * @supp: supplicant synchronization struct for RPC to supplicant - * @pool: shared memory pool -- * @memremaped_shm virtual address of memory in shared memory pool -- * @sec_caps: secure world capabilities defined by -- * OPTEE_SMC_SEC_CAP_* in optee_smc.h - * @scan_bus_done flag if device registation was already done. - * @scan_bus_wq workqueue to scan optee bus and register optee drivers - * @scan_bus_work workq to scan optee bus and register optee drivers -@@ -112,13 +129,11 @@ struct optee { - struct tee_device *supp_teedev; - struct tee_device *teedev; - const struct optee_ops *ops; -- optee_invoke_fn *invoke_fn; -+ struct optee_smc smc; - struct optee_call_queue call_queue; - struct optee_wait_queue wait_queue; - struct optee_supp supp; - struct tee_shm_pool *pool; -- void *memremaped_shm; -- u32 sec_caps; - bool scan_bus_done; - struct workqueue_struct *scan_bus_wq; - struct work_struct scan_bus_work; -@@ -153,10 +168,6 @@ struct optee_call_ctx { - size_t num_entries; - }; - --void optee_handle_rpc(struct tee_context *ctx, struct optee_rpc_param *param, -- struct optee_call_ctx *call_ctx); --void optee_rpc_finalize_call(struct optee_call_ctx *call_ctx); -- - void optee_wait_queue_init(struct optee_wait_queue *wq); - void optee_wait_queue_exit(struct optee_wait_queue *wq); - -@@ -174,7 +185,6 @@ int optee_supp_recv(struct tee_context *ctx, u32 *func, u32 *num_params, - int optee_supp_send(struct tee_context *ctx, u32 ret, u32 num_params, - struct tee_param *param); - --int optee_do_call_with_arg(struct tee_context *ctx, struct tee_shm *arg); - int optee_open_session(struct tee_context *ctx, - struct tee_ioctl_open_session_arg *arg, - struct tee_param *param); -@@ -184,30 +194,60 @@ int optee_invoke_func(struct tee_context *ctx, struct tee_ioctl_invoke_arg *arg, - struct tee_param *param); - int optee_cancel_req(struct tee_context *ctx, u32 cancel_id, u32 session); - --void optee_enable_shm_cache(struct optee *optee); --void optee_disable_shm_cache(struct optee *optee); --void optee_disable_unmapped_shm_cache(struct optee *optee); -- --int optee_shm_register(struct tee_context *ctx, struct tee_shm *shm, -- struct page **pages, size_t num_pages, -- unsigned long start); --int optee_shm_unregister(struct tee_context *ctx, struct tee_shm *shm); -- --int optee_shm_register_supp(struct tee_context *ctx, struct tee_shm *shm, -- struct page **pages, size_t num_pages, -- unsigned long start); --int optee_shm_unregister_supp(struct tee_context *ctx, struct tee_shm *shm); -- --u64 *optee_allocate_pages_list(size_t num_entries); --void optee_free_pages_list(void *array, size_t num_entries); --void optee_fill_pages_list(u64 *dst, struct page **pages, int num_pages, -- size_t page_offset); -- - #define PTA_CMD_GET_DEVICES 0x0 - #define PTA_CMD_GET_DEVICES_SUPP 0x1 - int optee_enumerate_devices(u32 func); - void optee_unregister_devices(void); - -+int optee_pool_op_alloc_helper(struct tee_shm_pool_mgr *poolm, -+ struct tee_shm *shm, size_t size, -+ int (*shm_register)(struct tee_context *ctx, -+ struct tee_shm *shm, -+ struct page **pages, -+ size_t num_pages, -+ unsigned long start)); -+ -+ -+void optee_remove_common(struct optee *optee); -+int optee_open(struct tee_context *ctx, bool cap_memref_null); -+void optee_release(struct tee_context *ctx); -+void optee_release_supp(struct tee_context *ctx); -+ -+static inline void optee_from_msg_param_value(struct tee_param *p, u32 attr, -+ const struct optee_msg_param *mp) -+{ -+ p->attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT + -+ attr - OPTEE_MSG_ATTR_TYPE_VALUE_INPUT; -+ p->u.value.a = mp->u.value.a; -+ p->u.value.b = mp->u.value.b; -+ p->u.value.c = mp->u.value.c; -+} -+ -+static inline void optee_to_msg_param_value(struct optee_msg_param *mp, -+ const struct tee_param *p) -+{ -+ mp->attr = OPTEE_MSG_ATTR_TYPE_VALUE_INPUT + p->attr - -+ TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT; -+ mp->u.value.a = p->u.value.a; -+ mp->u.value.b = p->u.value.b; -+ mp->u.value.c = p->u.value.c; -+} -+ -+void optee_cq_wait_init(struct optee_call_queue *cq, -+ struct optee_call_waiter *w); -+void optee_cq_wait_for_completion(struct optee_call_queue *cq, -+ struct optee_call_waiter *w); -+void optee_cq_wait_final(struct optee_call_queue *cq, -+ struct optee_call_waiter *w); -+int optee_check_mem_type(unsigned long start, size_t num_pages); -+struct tee_shm *optee_get_msg_arg(struct tee_context *ctx, size_t num_params, -+ struct optee_msg_arg **msg_arg); -+ -+struct tee_shm *optee_rpc_cmd_alloc_suppl(struct tee_context *ctx, size_t sz); -+void optee_rpc_cmd_free_suppl(struct tee_context *ctx, struct tee_shm *shm); -+void optee_rpc_cmd(struct tee_context *ctx, struct optee *optee, -+ struct optee_msg_arg *arg); -+ - /* - * Small helpers - */ -@@ -223,4 +263,8 @@ static inline void reg_pair_from_64(u32 *reg0, u32 *reg1, u64 val) - *reg1 = val; - } - -+/* Registration of the ABIs */ -+int optee_smc_abi_register(void); -+void optee_smc_abi_unregister(void); -+ - #endif /*OPTEE_PRIVATE_H*/ -diff --git a/drivers/tee/optee/rpc.c b/drivers/tee/optee/rpc.c -index 309258d47790..cd642e340eaf 100644 ---- a/drivers/tee/optee/rpc.c -+++ b/drivers/tee/optee/rpc.c -@@ -6,12 +6,10 @@ - #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - - #include <linux/delay.h> --#include <linux/device.h> - #include <linux/i2c.h> - #include <linux/slab.h> - #include <linux/tee_drv.h> - #include "optee_private.h" --#include "optee_smc.h" - #include "optee_rpc_cmd.h" - - struct wq_entry { -@@ -266,7 +264,7 @@ static void handle_rpc_supp_cmd(struct tee_context *ctx, struct optee *optee, - kfree(params); - } - --static struct tee_shm *cmd_alloc_suppl(struct tee_context *ctx, size_t sz) -+struct tee_shm *optee_rpc_cmd_alloc_suppl(struct tee_context *ctx, size_t sz) - { - u32 ret; - struct tee_param param; -@@ -289,103 +287,7 @@ static struct tee_shm *cmd_alloc_suppl(struct tee_context *ctx, size_t sz) - return shm; - } - --static void handle_rpc_func_cmd_shm_alloc(struct tee_context *ctx, -- struct optee_msg_arg *arg, -- struct optee_call_ctx *call_ctx) --{ -- phys_addr_t pa; -- struct tee_shm *shm; -- size_t sz; -- size_t n; -- -- arg->ret_origin = TEEC_ORIGIN_COMMS; -- -- if (!arg->num_params || -- arg->params[0].attr != OPTEE_MSG_ATTR_TYPE_VALUE_INPUT) { -- arg->ret = TEEC_ERROR_BAD_PARAMETERS; -- return; -- } -- -- for (n = 1; n < arg->num_params; n++) { -- if (arg->params[n].attr != OPTEE_MSG_ATTR_TYPE_NONE) { -- arg->ret = TEEC_ERROR_BAD_PARAMETERS; -- return; -- } -- } -- -- sz = arg->params[0].u.value.b; -- switch (arg->params[0].u.value.a) { -- case OPTEE_RPC_SHM_TYPE_APPL: -- shm = cmd_alloc_suppl(ctx, sz); -- break; -- case OPTEE_RPC_SHM_TYPE_KERNEL: -- shm = tee_shm_alloc(ctx, sz, TEE_SHM_MAPPED | TEE_SHM_PRIV); -- break; -- default: -- arg->ret = TEEC_ERROR_BAD_PARAMETERS; -- return; -- } -- -- if (IS_ERR(shm)) { -- arg->ret = TEEC_ERROR_OUT_OF_MEMORY; -- return; -- } -- -- if (tee_shm_get_pa(shm, 0, &pa)) { -- arg->ret = TEEC_ERROR_BAD_PARAMETERS; -- goto bad; -- } -- -- sz = tee_shm_get_size(shm); -- -- if (tee_shm_is_registered(shm)) { -- struct page **pages; -- u64 *pages_list; -- size_t page_num; -- -- pages = tee_shm_get_pages(shm, &page_num); -- if (!pages || !page_num) { -- arg->ret = TEEC_ERROR_OUT_OF_MEMORY; -- goto bad; -- } -- -- pages_list = optee_allocate_pages_list(page_num); -- if (!pages_list) { -- arg->ret = TEEC_ERROR_OUT_OF_MEMORY; -- goto bad; -- } -- -- call_ctx->pages_list = pages_list; -- call_ctx->num_entries = page_num; -- -- arg->params[0].attr = OPTEE_MSG_ATTR_TYPE_TMEM_OUTPUT | -- OPTEE_MSG_ATTR_NONCONTIG; -- /* -- * In the least bits of u.tmem.buf_ptr we store buffer offset -- * from 4k page, as described in OP-TEE ABI. -- */ -- arg->params[0].u.tmem.buf_ptr = virt_to_phys(pages_list) | -- (tee_shm_get_page_offset(shm) & -- (OPTEE_MSG_NONCONTIG_PAGE_SIZE - 1)); -- arg->params[0].u.tmem.size = tee_shm_get_size(shm); -- arg->params[0].u.tmem.shm_ref = (unsigned long)shm; -- -- optee_fill_pages_list(pages_list, pages, page_num, -- tee_shm_get_page_offset(shm)); -- } else { -- arg->params[0].attr = OPTEE_MSG_ATTR_TYPE_TMEM_OUTPUT; -- arg->params[0].u.tmem.buf_ptr = pa; -- arg->params[0].u.tmem.size = sz; -- arg->params[0].u.tmem.shm_ref = (unsigned long)shm; -- } -- -- arg->ret = TEEC_SUCCESS; -- return; --bad: -- tee_shm_free(shm); --} -- --static void cmd_free_suppl(struct tee_context *ctx, struct tee_shm *shm) -+void optee_rpc_cmd_free_suppl(struct tee_context *ctx, struct tee_shm *shm) - { - struct tee_param param; - -@@ -410,60 +312,9 @@ static void cmd_free_suppl(struct tee_context *ctx, struct tee_shm *shm) - optee_supp_thrd_req(ctx, OPTEE_RPC_CMD_SHM_FREE, 1, ¶m); - } - --static void handle_rpc_func_cmd_shm_free(struct tee_context *ctx, -- struct optee_msg_arg *arg) --{ -- struct tee_shm *shm; -- -- arg->ret_origin = TEEC_ORIGIN_COMMS; -- -- if (arg->num_params != 1 || -- arg->params[0].attr != OPTEE_MSG_ATTR_TYPE_VALUE_INPUT) { -- arg->ret = TEEC_ERROR_BAD_PARAMETERS; -- return; -- } -- -- shm = (struct tee_shm *)(unsigned long)arg->params[0].u.value.b; -- switch (arg->params[0].u.value.a) { -- case OPTEE_RPC_SHM_TYPE_APPL: -- cmd_free_suppl(ctx, shm); -- break; -- case OPTEE_RPC_SHM_TYPE_KERNEL: -- tee_shm_free(shm); -- break; -- default: -- arg->ret = TEEC_ERROR_BAD_PARAMETERS; -- } -- arg->ret = TEEC_SUCCESS; --} -- --static void free_pages_list(struct optee_call_ctx *call_ctx) --{ -- if (call_ctx->pages_list) { -- optee_free_pages_list(call_ctx->pages_list, -- call_ctx->num_entries); -- call_ctx->pages_list = NULL; -- call_ctx->num_entries = 0; -- } --} -- --void optee_rpc_finalize_call(struct optee_call_ctx *call_ctx) --{ -- free_pages_list(call_ctx); --} -- --static void handle_rpc_func_cmd(struct tee_context *ctx, struct optee *optee, -- struct tee_shm *shm, -- struct optee_call_ctx *call_ctx) -+void optee_rpc_cmd(struct tee_context *ctx, struct optee *optee, -+ struct optee_msg_arg *arg) - { -- struct optee_msg_arg *arg; -- -- arg = tee_shm_get_va(shm, 0); -- if (IS_ERR(arg)) { -- pr_err("%s: tee_shm_get_va %p failed\n", __func__, shm); -- return; -- } -- - switch (arg->cmd) { - case OPTEE_RPC_CMD_GET_TIME: - handle_rpc_func_cmd_get_time(arg); -@@ -474,13 +325,6 @@ static void handle_rpc_func_cmd(struct tee_context *ctx, struct optee *optee, - case OPTEE_RPC_CMD_SUSPEND: - handle_rpc_func_cmd_wait(arg); - break; -- case OPTEE_RPC_CMD_SHM_ALLOC: -- free_pages_list(call_ctx); -- handle_rpc_func_cmd_shm_alloc(ctx, arg, call_ctx); -- break; -- case OPTEE_RPC_CMD_SHM_FREE: -- handle_rpc_func_cmd_shm_free(ctx, arg); -- break; - case OPTEE_RPC_CMD_I2C_TRANSFER: - handle_rpc_func_cmd_i2c_transfer(ctx, arg); - break; -@@ -489,58 +333,4 @@ static void handle_rpc_func_cmd(struct tee_context *ctx, struct optee *optee, - } - } - --/** -- * optee_handle_rpc() - handle RPC from secure world -- * @ctx: context doing the RPC -- * @param: value of registers for the RPC -- * @call_ctx: call context. Preserved during one OP-TEE invocation -- * -- * Result of RPC is written back into @param. -- */ --void optee_handle_rpc(struct tee_context *ctx, struct optee_rpc_param *param, -- struct optee_call_ctx *call_ctx) --{ -- struct tee_device *teedev = ctx->teedev; -- struct optee *optee = tee_get_drvdata(teedev); -- struct tee_shm *shm; -- phys_addr_t pa; -- -- switch (OPTEE_SMC_RETURN_GET_RPC_FUNC(param->a0)) { -- case OPTEE_SMC_RPC_FUNC_ALLOC: -- shm = tee_shm_alloc(ctx, param->a1, -- TEE_SHM_MAPPED | TEE_SHM_PRIV); -- if (!IS_ERR(shm) && !tee_shm_get_pa(shm, 0, &pa)) { -- reg_pair_from_64(¶m->a1, ¶m->a2, pa); -- reg_pair_from_64(¶m->a4, ¶m->a5, -- (unsigned long)shm); -- } else { -- param->a1 = 0; -- param->a2 = 0; -- param->a4 = 0; -- param->a5 = 0; -- } -- break; -- case OPTEE_SMC_RPC_FUNC_FREE: -- shm = reg_pair_to_ptr(param->a1, param->a2); -- tee_shm_free(shm); -- break; -- case OPTEE_SMC_RPC_FUNC_FOREIGN_INTR: -- /* -- * A foreign interrupt was raised while secure world was -- * executing, since they are handled in Linux a dummy RPC is -- * performed to let Linux take the interrupt through the normal -- * vector. -- */ -- break; -- case OPTEE_SMC_RPC_FUNC_CMD: -- shm = reg_pair_to_ptr(param->a1, param->a2); -- handle_rpc_func_cmd(ctx, optee, shm, call_ctx); -- break; -- default: -- pr_warn("Unknown RPC func 0x%x\n", -- (u32)OPTEE_SMC_RETURN_GET_RPC_FUNC(param->a0)); -- break; -- } - -- param->a0 = OPTEE_SMC_CALL_RETURN_FROM_RPC; --} -diff --git a/drivers/tee/optee/shm_pool.h b/drivers/tee/optee/shm_pool.h -deleted file mode 100644 -index 28109d991c4b..000000000000 ---- a/drivers/tee/optee/shm_pool.h -+++ /dev/null -@@ -1,14 +0,0 @@ --/* SPDX-License-Identifier: GPL-2.0-only */ --/* -- * Copyright (c) 2015, Linaro Limited -- * Copyright (c) 2016, EPAM Systems -- */ -- --#ifndef SHM_POOL_H --#define SHM_POOL_H -- --#include <linux/tee_drv.h> -- --struct tee_shm_pool_mgr *optee_shm_pool_alloc_pages(void); -- --#endif -diff --git a/drivers/tee/optee/smc_abi.c b/drivers/tee/optee/smc_abi.c -new file mode 100644 -index 000000000000..9a787fb4f5e5 ---- /dev/null -+++ b/drivers/tee/optee/smc_abi.c -@@ -0,0 +1,1361 @@ -+// SPDX-License-Identifier: GPL-2.0-only -+/* -+ * Copyright (c) 2015-2021, Linaro Limited -+ * Copyright (c) 2016, EPAM Systems -+ */ -+ -+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -+ -+#include <linux/arm-smccc.h> -+#include <linux/errno.h> -+#include <linux/io.h> -+#include <linux/sched.h> -+#include <linux/module.h> -+#include <linux/of.h> -+#include <linux/of_platform.h> -+#include <linux/platform_device.h> -+#include <linux/slab.h> -+#include <linux/string.h> -+#include <linux/tee_drv.h> -+#include <linux/types.h> -+#include <linux/workqueue.h> -+#include "optee_private.h" -+#include "optee_smc.h" -+#include "optee_rpc_cmd.h" -+#define CREATE_TRACE_POINTS -+#include "optee_trace.h" -+ -+/* -+ * This file implement the SMC ABI used when communicating with secure world -+ * OP-TEE OS via raw SMCs. -+ * This file is divided into the following sections: -+ * 1. Convert between struct tee_param and struct optee_msg_param -+ * 2. Low level support functions to register shared memory in secure world -+ * 3. Dynamic shared memory pool based on alloc_pages() -+ * 4. Do a normal scheduled call into secure world -+ * 5. Driver initialization. -+ */ -+ -+#define OPTEE_SHM_NUM_PRIV_PAGES CONFIG_OPTEE_SHM_NUM_PRIV_PAGES -+ -+/* -+ * 1. Convert between struct tee_param and struct optee_msg_param -+ * -+ * optee_from_msg_param() and optee_to_msg_param() are the main -+ * functions. -+ */ -+ -+static int from_msg_param_tmp_mem(struct tee_param *p, u32 attr, -+ const struct optee_msg_param *mp) -+{ -+ struct tee_shm *shm; -+ phys_addr_t pa; -+ int rc; -+ -+ p->attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT + -+ attr - OPTEE_MSG_ATTR_TYPE_TMEM_INPUT; -+ p->u.memref.size = mp->u.tmem.size; -+ shm = (struct tee_shm *)(unsigned long)mp->u.tmem.shm_ref; -+ if (!shm) { -+ p->u.memref.shm_offs = 0; -+ p->u.memref.shm = NULL; -+ return 0; -+ } -+ -+ rc = tee_shm_get_pa(shm, 0, &pa); -+ if (rc) -+ return rc; -+ -+ p->u.memref.shm_offs = mp->u.tmem.buf_ptr - pa; -+ p->u.memref.shm = shm; -+ -+ /* Check that the memref is covered by the shm object */ -+ if (p->u.memref.size) { -+ size_t o = p->u.memref.shm_offs + -+ p->u.memref.size - 1; -+ -+ rc = tee_shm_get_pa(shm, o, NULL); -+ if (rc) -+ return rc; -+ } -+ -+ return 0; -+} -+ -+static void from_msg_param_reg_mem(struct tee_param *p, u32 attr, -+ const struct optee_msg_param *mp) -+{ -+ struct tee_shm *shm; -+ -+ p->attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT + -+ attr - OPTEE_MSG_ATTR_TYPE_RMEM_INPUT; -+ p->u.memref.size = mp->u.rmem.size; -+ shm = (struct tee_shm *)(unsigned long)mp->u.rmem.shm_ref; -+ -+ if (shm) { -+ p->u.memref.shm_offs = mp->u.rmem.offs; -+ p->u.memref.shm = shm; -+ } else { -+ p->u.memref.shm_offs = 0; -+ p->u.memref.shm = NULL; -+ } -+} -+ -+/** -+ * optee_from_msg_param() - convert from OPTEE_MSG parameters to -+ * struct tee_param -+ * @optee: main service struct -+ * @params: subsystem internal parameter representation -+ * @num_params: number of elements in the parameter arrays -+ * @msg_params: OPTEE_MSG parameters -+ * Returns 0 on success or <0 on failure -+ */ -+static int optee_from_msg_param(struct optee *optee, struct tee_param *params, -+ size_t num_params, -+ const struct optee_msg_param *msg_params) -+{ -+ int rc; -+ size_t n; -+ -+ for (n = 0; n < num_params; n++) { -+ struct tee_param *p = params + n; -+ const struct optee_msg_param *mp = msg_params + n; -+ u32 attr = mp->attr & OPTEE_MSG_ATTR_TYPE_MASK; -+ -+ switch (attr) { -+ case OPTEE_MSG_ATTR_TYPE_NONE: -+ p->attr = TEE_IOCTL_PARAM_ATTR_TYPE_NONE; -+ memset(&p->u, 0, sizeof(p->u)); -+ break; -+ case OPTEE_MSG_ATTR_TYPE_VALUE_INPUT: -+ case OPTEE_MSG_ATTR_TYPE_VALUE_OUTPUT: -+ case OPTEE_MSG_ATTR_TYPE_VALUE_INOUT: -+ optee_from_msg_param_value(p, attr, mp); -+ break; -+ case OPTEE_MSG_ATTR_TYPE_TMEM_INPUT: -+ case OPTEE_MSG_ATTR_TYPE_TMEM_OUTPUT: -+ case OPTEE_MSG_ATTR_TYPE_TMEM_INOUT: -+ rc = from_msg_param_tmp_mem(p, attr, mp); -+ if (rc) -+ return rc; -+ break; -+ case OPTEE_MSG_ATTR_TYPE_RMEM_INPUT: -+ case OPTEE_MSG_ATTR_TYPE_RMEM_OUTPUT: -+ case OPTEE_MSG_ATTR_TYPE_RMEM_INOUT: -+ from_msg_param_reg_mem(p, attr, mp); -+ break; -+ -+ default: -+ return -EINVAL; -+ } -+ } -+ return 0; -+} -+ -+static int to_msg_param_tmp_mem(struct optee_msg_param *mp, -+ const struct tee_param *p) -+{ -+ int rc; -+ phys_addr_t pa; -+ -+ mp->attr = OPTEE_MSG_ATTR_TYPE_TMEM_INPUT + p->attr - -+ TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT; -+ -+ mp->u.tmem.shm_ref = (unsigned long)p->u.memref.shm; -+ mp->u.tmem.size = p->u.memref.size; -+ -+ if (!p->u.memref.shm) { -+ mp->u.tmem.buf_ptr = 0; -+ return 0; -+ } -+ -+ rc = tee_shm_get_pa(p->u.memref.shm, p->u.memref.shm_offs, &pa); -+ if (rc) -+ return rc; -+ -+ mp->u.tmem.buf_ptr = pa; -+ mp->attr |= OPTEE_MSG_ATTR_CACHE_PREDEFINED << -+ OPTEE_MSG_ATTR_CACHE_SHIFT; -+ -+ return 0; -+} -+ -+static int to_msg_param_reg_mem(struct optee_msg_param *mp, -+ const struct tee_param *p) -+{ -+ mp->attr = OPTEE_MSG_ATTR_TYPE_RMEM_INPUT + p->attr - -+ TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT; -+ -+ mp->u.rmem.shm_ref = (unsigned long)p->u.memref.shm; -+ mp->u.rmem.size = p->u.memref.size; -+ mp->u.rmem.offs = p->u.memref.shm_offs; -+ return 0; -+} -+ -+/** -+ * optee_to_msg_param() - convert from struct tee_params to OPTEE_MSG parameters -+ * @optee: main service struct -+ * @msg_params: OPTEE_MSG parameters -+ * @num_params: number of elements in the parameter arrays -+ * @params: subsystem itnernal parameter representation -+ * Returns 0 on success or <0 on failure -+ */ -+static int optee_to_msg_param(struct optee *optee, -+ struct optee_msg_param *msg_params, -+ size_t num_params, const struct tee_param *params) -+{ -+ int rc; -+ size_t n; -+ -+ for (n = 0; n < num_params; n++) { -+ const struct tee_param *p = params + n; -+ struct optee_msg_param *mp = msg_params + n; -+ -+ switch (p->attr) { -+ case TEE_IOCTL_PARAM_ATTR_TYPE_NONE: -+ mp->attr = TEE_IOCTL_PARAM_ATTR_TYPE_NONE; -+ memset(&mp->u, 0, sizeof(mp->u)); -+ break; -+ case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT: -+ case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT: -+ case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT: -+ optee_to_msg_param_value(mp, p); -+ break; -+ case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT: -+ case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT: -+ case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT: -+ if (tee_shm_is_registered(p->u.memref.shm)) -+ rc = to_msg_param_reg_mem(mp, p); -+ else -+ rc = to_msg_param_tmp_mem(mp, p); -+ if (rc) -+ return rc; -+ break; -+ default: -+ return -EINVAL; -+ } -+ } -+ return 0; -+} -+ -+/* -+ * 2. Low level support functions to register shared memory in secure world -+ * -+ * Functions to enable/disable shared memory caching in secure world, that -+ * is, lazy freeing of previously allocated shared memory. Freeing is -+ * performed when a request has been compled. -+ * -+ * Functions to register and unregister shared memory both for normal -+ * clients and for tee-supplicant. -+ */ -+ -+/** -+ * optee_enable_shm_cache() - Enables caching of some shared memory allocation -+ * in OP-TEE -+ * @optee: main service struct -+ */ -+static void optee_enable_shm_cache(struct optee *optee) -+{ -+ struct optee_call_waiter w; -+ -+ /* We need to retry until secure world isn't busy. */ -+ optee_cq_wait_init(&optee->call_queue, &w); -+ while (true) { -+ struct arm_smccc_res res; -+ -+ optee->smc.invoke_fn(OPTEE_SMC_ENABLE_SHM_CACHE, -+ 0, 0, 0, 0, 0, 0, 0, &res); -+ if (res.a0 == OPTEE_SMC_RETURN_OK) -+ break; -+ optee_cq_wait_for_completion(&optee->call_queue, &w); -+ } -+ optee_cq_wait_final(&optee->call_queue, &w); -+} -+ -+/** -+ * __optee_disable_shm_cache() - Disables caching of some shared memory -+ * allocation in OP-TEE -+ * @optee: main service struct -+ * @is_mapped: true if the cached shared memory addresses were mapped by this -+ * kernel, are safe to dereference, and should be freed -+ */ -+static void __optee_disable_shm_cache(struct optee *optee, bool is_mapped) -+{ -+ struct optee_call_waiter w; -+ -+ /* We need to retry until secure world isn't busy. */ -+ optee_cq_wait_init(&optee->call_queue, &w); -+ while (true) { -+ union { -+ struct arm_smccc_res smccc; -+ struct optee_smc_disable_shm_cache_result result; -+ } res; -+ -+ optee->smc.invoke_fn(OPTEE_SMC_DISABLE_SHM_CACHE, -+ 0, 0, 0, 0, 0, 0, 0, &res.smccc); -+ if (res.result.status == OPTEE_SMC_RETURN_ENOTAVAIL) -+ break; /* All shm's freed */ -+ if (res.result.status == OPTEE_SMC_RETURN_OK) { -+ struct tee_shm *shm; -+ -+ /* -+ * Shared memory references that were not mapped by -+ * this kernel must be ignored to prevent a crash. -+ */ -+ if (!is_mapped) -+ continue; -+ -+ shm = reg_pair_to_ptr(res.result.shm_upper32, -+ res.result.shm_lower32); -+ tee_shm_free(shm); -+ } else { -+ optee_cq_wait_for_completion(&optee->call_queue, &w); -+ } -+ } -+ optee_cq_wait_final(&optee->call_queue, &w); -+} -+ -+/** -+ * optee_disable_shm_cache() - Disables caching of mapped shared memory -+ * allocations in OP-TEE -+ * @optee: main service struct -+ */ -+static void optee_disable_shm_cache(struct optee *optee) -+{ -+ return __optee_disable_shm_cache(optee, true); -+} -+ -+/** -+ * optee_disable_unmapped_shm_cache() - Disables caching of shared memory -+ * allocations in OP-TEE which are not -+ * currently mapped -+ * @optee: main service struct -+ */ -+static void optee_disable_unmapped_shm_cache(struct optee *optee) -+{ -+ return __optee_disable_shm_cache(optee, false); -+} -+ -+#define PAGELIST_ENTRIES_PER_PAGE \ -+ ((OPTEE_MSG_NONCONTIG_PAGE_SIZE / sizeof(u64)) - 1) -+ -+/* -+ * The final entry in each pagelist page is a pointer to the next -+ * pagelist page. -+ */ -+static size_t get_pages_list_size(size_t num_entries) -+{ -+ int pages = DIV_ROUND_UP(num_entries, PAGELIST_ENTRIES_PER_PAGE); -+ -+ return pages * OPTEE_MSG_NONCONTIG_PAGE_SIZE; -+} -+ -+static u64 *optee_allocate_pages_list(size_t num_entries) -+{ -+ return alloc_pages_exact(get_pages_list_size(num_entries), GFP_KERNEL); -+} -+ -+static void optee_free_pages_list(void *list, size_t num_entries) -+{ -+ free_pages_exact(list, get_pages_list_size(num_entries)); -+} -+ -+/** -+ * optee_fill_pages_list() - write list of user pages to given shared -+ * buffer. -+ * -+ * @dst: page-aligned buffer where list of pages will be stored -+ * @pages: array of pages that represents shared buffer -+ * @num_pages: number of entries in @pages -+ * @page_offset: offset of user buffer from page start -+ * -+ * @dst should be big enough to hold list of user page addresses and -+ * links to the next pages of buffer -+ */ -+static void optee_fill_pages_list(u64 *dst, struct page **pages, int num_pages, -+ size_t page_offset) -+{ -+ int n = 0; -+ phys_addr_t optee_page; -+ /* -+ * Refer to OPTEE_MSG_ATTR_NONCONTIG description in optee_msg.h -+ * for details. -+ */ -+ struct { -+ u64 pages_list[PAGELIST_ENTRIES_PER_PAGE]; -+ u64 next_page_data; -+ } *pages_data; -+ -+ /* -+ * Currently OP-TEE uses 4k page size and it does not looks -+ * like this will change in the future. On other hand, there are -+ * no know ARM architectures with page size < 4k. -+ * Thus the next built assert looks redundant. But the following -+ * code heavily relies on this assumption, so it is better be -+ * safe than sorry. -+ */ -+ BUILD_BUG_ON(PAGE_SIZE < OPTEE_MSG_NONCONTIG_PAGE_SIZE); -+ -+ pages_data = (void *)dst; -+ /* -+ * If linux page is bigger than 4k, and user buffer offset is -+ * larger than 4k/8k/12k/etc this will skip first 4k pages, -+ * because they bear no value data for OP-TEE. -+ */ -+ optee_page = page_to_phys(*pages) + -+ round_down(page_offset, OPTEE_MSG_NONCONTIG_PAGE_SIZE); -+ -+ while (true) { -+ pages_data->pages_list[n++] = optee_page; -+ -+ if (n == PAGELIST_ENTRIES_PER_PAGE) { -+ pages_data->next_page_data = -+ virt_to_phys(pages_data + 1); -+ pages_data++; -+ n = 0; -+ } -+ -+ optee_page += OPTEE_MSG_NONCONTIG_PAGE_SIZE; -+ if (!(optee_page & ~PAGE_MASK)) { -+ if (!--num_pages) -+ break; -+ pages++; -+ optee_page = page_to_phys(*pages); -+ } -+ } -+} -+ -+static int optee_shm_register(struct tee_context *ctx, struct tee_shm *shm, -+ struct page **pages, size_t num_pages, -+ unsigned long start) -+{ -+ struct optee *optee = tee_get_drvdata(ctx->teedev); -+ struct optee_msg_arg *msg_arg; -+ struct tee_shm *shm_arg; -+ u64 *pages_list; -+ int rc; -+ -+ if (!num_pages) -+ return -EINVAL; -+ -+ rc = optee_check_mem_type(start, num_pages); -+ if (rc) -+ return rc; -+ -+ pages_list = optee_allocate_pages_list(num_pages); -+ if (!pages_list) -+ return -ENOMEM; -+ -+ shm_arg = optee_get_msg_arg(ctx, 1, &msg_arg); -+ if (IS_ERR(shm_arg)) { -+ rc = PTR_ERR(shm_arg); -+ goto out; -+ } -+ -+ optee_fill_pages_list(pages_list, pages, num_pages, -+ tee_shm_get_page_offset(shm)); -+ -+ msg_arg->cmd = OPTEE_MSG_CMD_REGISTER_SHM; -+ msg_arg->params->attr = OPTEE_MSG_ATTR_TYPE_TMEM_OUTPUT | -+ OPTEE_MSG_ATTR_NONCONTIG; -+ msg_arg->params->u.tmem.shm_ref = (unsigned long)shm; -+ msg_arg->params->u.tmem.size = tee_shm_get_size(shm); -+ /* -+ * In the least bits of msg_arg->params->u.tmem.buf_ptr we -+ * store buffer offset from 4k page, as described in OP-TEE ABI. -+ */ -+ msg_arg->params->u.tmem.buf_ptr = virt_to_phys(pages_list) | -+ (tee_shm_get_page_offset(shm) & (OPTEE_MSG_NONCONTIG_PAGE_SIZE - 1)); -+ -+ if (optee->ops->do_call_with_arg(ctx, shm_arg) || -+ msg_arg->ret != TEEC_SUCCESS) -+ rc = -EINVAL; -+ -+ tee_shm_free(shm_arg); -+out: -+ optee_free_pages_list(pages_list, num_pages); -+ return rc; -+} -+ -+static int optee_shm_unregister(struct tee_context *ctx, struct tee_shm *shm) -+{ -+ struct optee *optee = tee_get_drvdata(ctx->teedev); -+ struct optee_msg_arg *msg_arg; -+ struct tee_shm *shm_arg; -+ int rc = 0; -+ -+ shm_arg = optee_get_msg_arg(ctx, 1, &msg_arg); -+ if (IS_ERR(shm_arg)) -+ return PTR_ERR(shm_arg); -+ -+ msg_arg->cmd = OPTEE_MSG_CMD_UNREGISTER_SHM; -+ -+ msg_arg->params[0].attr = OPTEE_MSG_ATTR_TYPE_RMEM_INPUT; -+ msg_arg->params[0].u.rmem.shm_ref = (unsigned long)shm; -+ -+ if (optee->ops->do_call_with_arg(ctx, shm_arg) || -+ msg_arg->ret != TEEC_SUCCESS) -+ rc = -EINVAL; -+ tee_shm_free(shm_arg); -+ return rc; -+} -+ -+static int optee_shm_register_supp(struct tee_context *ctx, struct tee_shm *shm, -+ struct page **pages, size_t num_pages, -+ unsigned long start) -+{ -+ /* -+ * We don't want to register supplicant memory in OP-TEE. -+ * Instead information about it will be passed in RPC code. -+ */ -+ return optee_check_mem_type(start, num_pages); -+} -+ -+static int optee_shm_unregister_supp(struct tee_context *ctx, -+ struct tee_shm *shm) -+{ -+ return 0; -+} -+ -+/* -+ * 3. Dynamic shared memory pool based on alloc_pages() -+ * -+ * Implements an OP-TEE specific shared memory pool which is used -+ * when dynamic shared memory is supported by secure world. -+ * -+ * The main function is optee_shm_pool_alloc_pages(). -+ */ -+ -+static int pool_op_alloc(struct tee_shm_pool_mgr *poolm, -+ struct tee_shm *shm, size_t size) -+{ -+ /* -+ * Shared memory private to the OP-TEE driver doesn't need -+ * to be registered with OP-TEE. -+ */ -+ if (shm->flags & TEE_SHM_PRIV) -+ return optee_pool_op_alloc_helper(poolm, shm, size, NULL); -+ -+ return optee_pool_op_alloc_helper(poolm, shm, size, optee_shm_register); -+} -+ -+static void pool_op_free(struct tee_shm_pool_mgr *poolm, -+ struct tee_shm *shm) -+{ -+ if (!(shm->flags & TEE_SHM_PRIV)) -+ optee_shm_unregister(shm->ctx, shm); -+ -+ free_pages((unsigned long)shm->kaddr, get_order(shm->size)); -+ shm->kaddr = NULL; -+} -+ -+static void pool_op_destroy_poolmgr(struct tee_shm_pool_mgr *poolm) -+{ -+ kfree(poolm); -+} -+ -+static const struct tee_shm_pool_mgr_ops pool_ops = { -+ .alloc = pool_op_alloc, -+ .free = pool_op_free, -+ .destroy_poolmgr = pool_op_destroy_poolmgr, -+}; -+ -+/** -+ * optee_shm_pool_alloc_pages() - create page-based allocator pool -+ * -+ * This pool is used when OP-TEE supports dymanic SHM. In this case -+ * command buffers and such are allocated from kernel's own memory. -+ */ -+static struct tee_shm_pool_mgr *optee_shm_pool_alloc_pages(void) -+{ -+ struct tee_shm_pool_mgr *mgr = kzalloc(sizeof(*mgr), GFP_KERNEL); -+ -+ if (!mgr) -+ return ERR_PTR(-ENOMEM); -+ -+ mgr->ops = &pool_ops; -+ -+ return mgr; -+} -+ -+/* -+ * 4. Do a normal scheduled call into secure world -+ * -+ * The function optee_smc_do_call_with_arg() performs a normal scheduled -+ * call into secure world. During this call may normal world request help -+ * from normal world using RPCs, Remote Procedure Calls. This includes -+ * delivery of non-secure interrupts to for instance allow rescheduling of -+ * the current task. -+ */ -+ -+static void handle_rpc_func_cmd_shm_free(struct tee_context *ctx, -+ struct optee_msg_arg *arg) -+{ -+ struct tee_shm *shm; -+ -+ arg->ret_origin = TEEC_ORIGIN_COMMS; -+ -+ if (arg->num_params != 1 || -+ arg->params[0].attr != OPTEE_MSG_ATTR_TYPE_VALUE_INPUT) { -+ arg->ret = TEEC_ERROR_BAD_PARAMETERS; -+ return; -+ } -+ -+ shm = (struct tee_shm *)(unsigned long)arg->params[0].u.value.b; -+ switch (arg->params[0].u.value.a) { -+ case OPTEE_RPC_SHM_TYPE_APPL: -+ optee_rpc_cmd_free_suppl(ctx, shm); -+ break; -+ case OPTEE_RPC_SHM_TYPE_KERNEL: -+ tee_shm_free(shm); -+ break; -+ default: -+ arg->ret = TEEC_ERROR_BAD_PARAMETERS; -+ } -+ arg->ret = TEEC_SUCCESS; -+} -+ -+static void handle_rpc_func_cmd_shm_alloc(struct tee_context *ctx, -+ struct optee_msg_arg *arg, -+ struct optee_call_ctx *call_ctx) -+{ -+ phys_addr_t pa; -+ struct tee_shm *shm; -+ size_t sz; -+ size_t n; -+ -+ arg->ret_origin = TEEC_ORIGIN_COMMS; -+ -+ if (!arg->num_params || -+ arg->params[0].attr != OPTEE_MSG_ATTR_TYPE_VALUE_INPUT) { -+ arg->ret = TEEC_ERROR_BAD_PARAMETERS; -+ return; -+ } -+ -+ for (n = 1; n < arg->num_params; n++) { -+ if (arg->params[n].attr != OPTEE_MSG_ATTR_TYPE_NONE) { -+ arg->ret = TEEC_ERROR_BAD_PARAMETERS; -+ return; -+ } -+ } -+ -+ sz = arg->params[0].u.value.b; -+ switch (arg->params[0].u.value.a) { -+ case OPTEE_RPC_SHM_TYPE_APPL: -+ shm = optee_rpc_cmd_alloc_suppl(ctx, sz); -+ break; -+ case OPTEE_RPC_SHM_TYPE_KERNEL: -+ shm = tee_shm_alloc(ctx, sz, TEE_SHM_MAPPED | TEE_SHM_PRIV); -+ break; -+ default: -+ arg->ret = TEEC_ERROR_BAD_PARAMETERS; -+ return; -+ } -+ -+ if (IS_ERR(shm)) { -+ arg->ret = TEEC_ERROR_OUT_OF_MEMORY; -+ return; -+ } -+ -+ if (tee_shm_get_pa(shm, 0, &pa)) { -+ arg->ret = TEEC_ERROR_BAD_PARAMETERS; -+ goto bad; -+ } -+ -+ sz = tee_shm_get_size(shm); -+ -+ if (tee_shm_is_registered(shm)) { -+ struct page **pages; -+ u64 *pages_list; -+ size_t page_num; -+ -+ pages = tee_shm_get_pages(shm, &page_num); -+ if (!pages || !page_num) { -+ arg->ret = TEEC_ERROR_OUT_OF_MEMORY; -+ goto bad; -+ } -+ -+ pages_list = optee_allocate_pages_list(page_num); -+ if (!pages_list) { -+ arg->ret = TEEC_ERROR_OUT_OF_MEMORY; -+ goto bad; -+ } -+ -+ call_ctx->pages_list = pages_list; -+ call_ctx->num_entries = page_num; -+ -+ arg->params[0].attr = OPTEE_MSG_ATTR_TYPE_TMEM_OUTPUT | -+ OPTEE_MSG_ATTR_NONCONTIG; -+ /* -+ * In the least bits of u.tmem.buf_ptr we store buffer offset -+ * from 4k page, as described in OP-TEE ABI. -+ */ -+ arg->params[0].u.tmem.buf_ptr = virt_to_phys(pages_list) | -+ (tee_shm_get_page_offset(shm) & -+ (OPTEE_MSG_NONCONTIG_PAGE_SIZE - 1)); -+ arg->params[0].u.tmem.size = tee_shm_get_size(shm); -+ arg->params[0].u.tmem.shm_ref = (unsigned long)shm; -+ -+ optee_fill_pages_list(pages_list, pages, page_num, -+ tee_shm_get_page_offset(shm)); -+ } else { -+ arg->params[0].attr = OPTEE_MSG_ATTR_TYPE_TMEM_OUTPUT; -+ arg->params[0].u.tmem.buf_ptr = pa; -+ arg->params[0].u.tmem.size = sz; -+ arg->params[0].u.tmem.shm_ref = (unsigned long)shm; -+ } -+ -+ arg->ret = TEEC_SUCCESS; -+ return; -+bad: -+ tee_shm_free(shm); -+} -+ -+static void free_pages_list(struct optee_call_ctx *call_ctx) -+{ -+ if (call_ctx->pages_list) { -+ optee_free_pages_list(call_ctx->pages_list, -+ call_ctx->num_entries); -+ call_ctx->pages_list = NULL; -+ call_ctx->num_entries = 0; -+ } -+} -+ -+static void optee_rpc_finalize_call(struct optee_call_ctx *call_ctx) -+{ -+ free_pages_list(call_ctx); -+} -+ -+static void handle_rpc_func_cmd(struct tee_context *ctx, struct optee *optee, -+ struct tee_shm *shm, -+ struct optee_call_ctx *call_ctx) -+{ -+ struct optee_msg_arg *arg; -+ -+ arg = tee_shm_get_va(shm, 0); -+ if (IS_ERR(arg)) { -+ pr_err("%s: tee_shm_get_va %p failed\n", __func__, shm); -+ return; -+ } -+ -+ switch (arg->cmd) { -+ case OPTEE_RPC_CMD_SHM_ALLOC: -+ free_pages_list(call_ctx); -+ handle_rpc_func_cmd_shm_alloc(ctx, arg, call_ctx); -+ break; -+ case OPTEE_RPC_CMD_SHM_FREE: -+ handle_rpc_func_cmd_shm_free(ctx, arg); -+ break; -+ default: -+ optee_rpc_cmd(ctx, optee, arg); -+ } -+} -+ -+/** -+ * optee_handle_rpc() - handle RPC from secure world -+ * @ctx: context doing the RPC -+ * @param: value of registers for the RPC -+ * @call_ctx: call context. Preserved during one OP-TEE invocation -+ * -+ * Result of RPC is written back into @param. -+ */ -+static void optee_handle_rpc(struct tee_context *ctx, -+ struct optee_rpc_param *param, -+ struct optee_call_ctx *call_ctx) -+{ -+ struct tee_device *teedev = ctx->teedev; -+ struct optee *optee = tee_get_drvdata(teedev); -+ struct tee_shm *shm; -+ phys_addr_t pa; -+ -+ switch (OPTEE_SMC_RETURN_GET_RPC_FUNC(param->a0)) { -+ case OPTEE_SMC_RPC_FUNC_ALLOC: -+ shm = tee_shm_alloc(ctx, param->a1, -+ TEE_SHM_MAPPED | TEE_SHM_PRIV); -+ if (!IS_ERR(shm) && !tee_shm_get_pa(shm, 0, &pa)) { -+ reg_pair_from_64(¶m->a1, ¶m->a2, pa); -+ reg_pair_from_64(¶m->a4, ¶m->a5, -+ (unsigned long)shm); -+ } else { -+ param->a1 = 0; -+ param->a2 = 0; -+ param->a4 = 0; -+ param->a5 = 0; -+ } -+ break; -+ case OPTEE_SMC_RPC_FUNC_FREE: -+ shm = reg_pair_to_ptr(param->a1, param->a2); -+ tee_shm_free(shm); -+ break; -+ case OPTEE_SMC_RPC_FUNC_FOREIGN_INTR: -+ /* -+ * A foreign interrupt was raised while secure world was -+ * executing, since they are handled in Linux a dummy RPC is -+ * performed to let Linux take the interrupt through the normal -+ * vector. -+ */ -+ break; -+ case OPTEE_SMC_RPC_FUNC_CMD: -+ shm = reg_pair_to_ptr(param->a1, param->a2); -+ handle_rpc_func_cmd(ctx, optee, shm, call_ctx); -+ break; -+ default: -+ pr_warn("Unknown RPC func 0x%x\n", -+ (u32)OPTEE_SMC_RETURN_GET_RPC_FUNC(param->a0)); -+ break; -+ } -+ -+ param->a0 = OPTEE_SMC_CALL_RETURN_FROM_RPC; -+} -+ -+/** -+ * optee_smc_do_call_with_arg() - Do an SMC to OP-TEE in secure world -+ * @ctx: calling context -+ * @arg: shared memory holding the message to pass to secure world -+ * -+ * Does and SMC to OP-TEE in secure world and handles eventual resulting -+ * Remote Procedure Calls (RPC) from OP-TEE. -+ * -+ * Returns return code from secure world, 0 is OK -+ */ -+static int optee_smc_do_call_with_arg(struct tee_context *ctx, -+ struct tee_shm *arg) -+{ -+ struct optee *optee = tee_get_drvdata(ctx->teedev); -+ struct optee_call_waiter w; -+ struct optee_rpc_param param = { }; -+ struct optee_call_ctx call_ctx = { }; -+ phys_addr_t parg; -+ int rc; -+ -+ rc = tee_shm_get_pa(arg, 0, &parg); -+ if (rc) -+ return rc; -+ -+ param.a0 = OPTEE_SMC_CALL_WITH_ARG; -+ reg_pair_from_64(¶m.a1, ¶m.a2, parg); -+ /* Initialize waiter */ -+ optee_cq_wait_init(&optee->call_queue, &w); -+ while (true) { -+ struct arm_smccc_res res; -+ -+ trace_optee_invoke_fn_begin(¶m); -+ optee->smc.invoke_fn(param.a0, param.a1, param.a2, param.a3, -+ param.a4, param.a5, param.a6, param.a7, -+ &res); -+ trace_optee_invoke_fn_end(¶m, &res); -+ -+ if (res.a0 == OPTEE_SMC_RETURN_ETHREAD_LIMIT) { -+ /* -+ * Out of threads in secure world, wait for a thread -+ * become available. -+ */ -+ optee_cq_wait_for_completion(&optee->call_queue, &w); -+ } else if (OPTEE_SMC_RETURN_IS_RPC(res.a0)) { -+ cond_resched(); -+ param.a0 = res.a0; -+ param.a1 = res.a1; -+ param.a2 = res.a2; -+ param.a3 = res.a3; -+ optee_handle_rpc(ctx, ¶m, &call_ctx); -+ } else { -+ rc = res.a0; -+ break; -+ } -+ } -+ -+ optee_rpc_finalize_call(&call_ctx); -+ /* -+ * We're done with our thread in secure world, if there's any -+ * thread waiters wake up one. -+ */ -+ optee_cq_wait_final(&optee->call_queue, &w); -+ -+ return rc; -+} -+ -+/* -+ * 5. Driver initialization -+ * -+ * During driver inititialization is secure world probed to find out which -+ * features it supports so the driver can be initialized with a matching -+ * configuration. This involves for instance support for dynamic shared -+ * memory instead of a static memory carvout. -+ */ -+ -+static void optee_get_version(struct tee_device *teedev, -+ struct tee_ioctl_version_data *vers) -+{ -+ struct tee_ioctl_version_data v = { -+ .impl_id = TEE_IMPL_ID_OPTEE, -+ .impl_caps = TEE_OPTEE_CAP_TZ, -+ .gen_caps = TEE_GEN_CAP_GP, -+ }; -+ struct optee *optee = tee_get_drvdata(teedev); -+ -+ if (optee->smc.sec_caps & OPTEE_SMC_SEC_CAP_DYNAMIC_SHM) -+ v.gen_caps |= TEE_GEN_CAP_REG_MEM; -+ if (optee->smc.sec_caps & OPTEE_SMC_SEC_CAP_MEMREF_NULL) -+ v.gen_caps |= TEE_GEN_CAP_MEMREF_NULL; -+ *vers = v; -+} -+ -+static int optee_smc_open(struct tee_context *ctx) -+{ -+ struct optee *optee = tee_get_drvdata(ctx->teedev); -+ u32 sec_caps = optee->smc.sec_caps; -+ -+ return optee_open(ctx, sec_caps & OPTEE_SMC_SEC_CAP_MEMREF_NULL); -+} -+ -+static const struct tee_driver_ops optee_clnt_ops = { -+ .get_version = optee_get_version, -+ .open = optee_smc_open, -+ .release = optee_release, -+ .open_session = optee_open_session, -+ .close_session = optee_close_session, -+ .invoke_func = optee_invoke_func, -+ .cancel_req = optee_cancel_req, -+ .shm_register = optee_shm_register, -+ .shm_unregister = optee_shm_unregister, -+}; -+ -+static const struct tee_desc optee_clnt_desc = { -+ .name = DRIVER_NAME "-clnt", -+ .ops = &optee_clnt_ops, -+ .owner = THIS_MODULE, -+}; -+ -+static const struct tee_driver_ops optee_supp_ops = { -+ .get_version = optee_get_version, -+ .open = optee_smc_open, -+ .release = optee_release_supp, -+ .supp_recv = optee_supp_recv, -+ .supp_send = optee_supp_send, -+ .shm_register = optee_shm_register_supp, -+ .shm_unregister = optee_shm_unregister_supp, -+}; -+ -+static const struct tee_desc optee_supp_desc = { -+ .name = DRIVER_NAME "-supp", -+ .ops = &optee_supp_ops, -+ .owner = THIS_MODULE, -+ .flags = TEE_DESC_PRIVILEGED, -+}; -+ -+static const struct optee_ops optee_ops = { -+ .do_call_with_arg = optee_smc_do_call_with_arg, -+ .to_msg_param = optee_to_msg_param, -+ .from_msg_param = optee_from_msg_param, -+}; -+ -+static bool optee_msg_api_uid_is_optee_api(optee_invoke_fn *invoke_fn) -+{ -+ struct arm_smccc_res res; -+ -+ invoke_fn(OPTEE_SMC_CALLS_UID, 0, 0, 0, 0, 0, 0, 0, &res); -+ -+ if (res.a0 == OPTEE_MSG_UID_0 && res.a1 == OPTEE_MSG_UID_1 && -+ res.a2 == OPTEE_MSG_UID_2 && res.a3 == OPTEE_MSG_UID_3) -+ return true; -+ return false; -+} -+ -+static void optee_msg_get_os_revision(optee_invoke_fn *invoke_fn) -+{ -+ union { -+ struct arm_smccc_res smccc; -+ struct optee_smc_call_get_os_revision_result result; -+ } res = { -+ .result = { -+ .build_id = 0 -+ } -+ }; -+ -+ invoke_fn(OPTEE_SMC_CALL_GET_OS_REVISION, 0, 0, 0, 0, 0, 0, 0, -+ &res.smccc); -+ -+ if (res.result.build_id) -+ pr_info("revision %lu.%lu (%08lx)", res.result.major, -+ res.result.minor, res.result.build_id); -+ else -+ pr_info("revision %lu.%lu", res.result.major, res.result.minor); -+} -+ -+static bool optee_msg_api_revision_is_compatible(optee_invoke_fn *invoke_fn) -+{ -+ union { -+ struct arm_smccc_res smccc; -+ struct optee_smc_calls_revision_result result; -+ } res; -+ -+ invoke_fn(OPTEE_SMC_CALLS_REVISION, 0, 0, 0, 0, 0, 0, 0, &res.smccc); -+ -+ if (res.result.major == OPTEE_MSG_REVISION_MAJOR && -+ (int)res.result.minor >= OPTEE_MSG_REVISION_MINOR) -+ return true; -+ return false; -+} -+ -+static bool optee_msg_exchange_capabilities(optee_invoke_fn *invoke_fn, -+ u32 *sec_caps) -+{ -+ union { -+ struct arm_smccc_res smccc; -+ struct optee_smc_exchange_capabilities_result result; -+ } res; -+ u32 a1 = 0; -+ -+ /* -+ * TODO This isn't enough to tell if it's UP system (from kernel -+ * point of view) or not, is_smp() returns the information -+ * needed, but can't be called directly from here. -+ */ -+ if (!IS_ENABLED(CONFIG_SMP) || nr_cpu_ids == 1) -+ a1 |= OPTEE_SMC_NSEC_CAP_UNIPROCESSOR; -+ -+ invoke_fn(OPTEE_SMC_EXCHANGE_CAPABILITIES, a1, 0, 0, 0, 0, 0, 0, -+ &res.smccc); -+ -+ if (res.result.status != OPTEE_SMC_RETURN_OK) -+ return false; -+ -+ *sec_caps = res.result.capabilities; -+ return true; -+} -+ -+static struct tee_shm_pool *optee_config_dyn_shm(void) -+{ -+ struct tee_shm_pool_mgr *priv_mgr; -+ struct tee_shm_pool_mgr *dmabuf_mgr; -+ void *rc; -+ -+ rc = optee_shm_pool_alloc_pages(); -+ if (IS_ERR(rc)) -+ return rc; -+ priv_mgr = rc; -+ -+ rc = optee_shm_pool_alloc_pages(); -+ if (IS_ERR(rc)) { -+ tee_shm_pool_mgr_destroy(priv_mgr); -+ return rc; -+ } -+ dmabuf_mgr = rc; -+ -+ rc = tee_shm_pool_alloc(priv_mgr, dmabuf_mgr); -+ if (IS_ERR(rc)) { -+ tee_shm_pool_mgr_destroy(priv_mgr); -+ tee_shm_pool_mgr_destroy(dmabuf_mgr); -+ } -+ -+ return rc; -+} -+ -+static struct tee_shm_pool * -+optee_config_shm_memremap(optee_invoke_fn *invoke_fn, void **memremaped_shm) -+{ -+ union { -+ struct arm_smccc_res smccc; -+ struct optee_smc_get_shm_config_result result; -+ } res; -+ unsigned long vaddr; -+ phys_addr_t paddr; -+ size_t size; -+ phys_addr_t begin; -+ phys_addr_t end; -+ void *va; -+ struct tee_shm_pool_mgr *priv_mgr; -+ struct tee_shm_pool_mgr *dmabuf_mgr; -+ void *rc; -+ const int sz = OPTEE_SHM_NUM_PRIV_PAGES * PAGE_SIZE; -+ -+ invoke_fn(OPTEE_SMC_GET_SHM_CONFIG, 0, 0, 0, 0, 0, 0, 0, &res.smccc); -+ if (res.result.status != OPTEE_SMC_RETURN_OK) { -+ pr_err("static shm service not available\n"); -+ return ERR_PTR(-ENOENT); -+ } -+ -+ if (res.result.settings != OPTEE_SMC_SHM_CACHED) { -+ pr_err("only normal cached shared memory supported\n"); -+ return ERR_PTR(-EINVAL); -+ } -+ -+ begin = roundup(res.result.start, PAGE_SIZE); -+ end = rounddown(res.result.start + res.result.size, PAGE_SIZE); -+ paddr = begin; -+ size = end - begin; -+ -+ if (size < 2 * OPTEE_SHM_NUM_PRIV_PAGES * PAGE_SIZE) { -+ pr_err("too small shared memory area\n"); -+ return ERR_PTR(-EINVAL); -+ } -+ -+ va = memremap(paddr, size, MEMREMAP_WB); -+ if (!va) { -+ pr_err("shared memory ioremap failed\n"); -+ return ERR_PTR(-EINVAL); -+ } -+ vaddr = (unsigned long)va; -+ -+ rc = tee_shm_pool_mgr_alloc_res_mem(vaddr, paddr, sz, -+ 3 /* 8 bytes aligned */); -+ if (IS_ERR(rc)) -+ goto err_memunmap; -+ priv_mgr = rc; -+ -+ vaddr += sz; -+ paddr += sz; -+ size -= sz; -+ -+ rc = tee_shm_pool_mgr_alloc_res_mem(vaddr, paddr, size, PAGE_SHIFT); -+ if (IS_ERR(rc)) -+ goto err_free_priv_mgr; -+ dmabuf_mgr = rc; -+ -+ rc = tee_shm_pool_alloc(priv_mgr, dmabuf_mgr); -+ if (IS_ERR(rc)) -+ goto err_free_dmabuf_mgr; -+ -+ *memremaped_shm = va; -+ -+ return rc; -+ -+err_free_dmabuf_mgr: -+ tee_shm_pool_mgr_destroy(dmabuf_mgr); -+err_free_priv_mgr: -+ tee_shm_pool_mgr_destroy(priv_mgr); -+err_memunmap: -+ memunmap(va); -+ return rc; -+} -+ -+/* Simple wrapper functions to be able to use a function pointer */ -+static void optee_smccc_smc(unsigned long a0, unsigned long a1, -+ unsigned long a2, unsigned long a3, -+ unsigned long a4, unsigned long a5, -+ unsigned long a6, unsigned long a7, -+ struct arm_smccc_res *res) -+{ -+ arm_smccc_smc(a0, a1, a2, a3, a4, a5, a6, a7, res); -+} -+ -+static void optee_smccc_hvc(unsigned long a0, unsigned long a1, -+ unsigned long a2, unsigned long a3, -+ unsigned long a4, unsigned long a5, -+ unsigned long a6, unsigned long a7, -+ struct arm_smccc_res *res) -+{ -+ arm_smccc_hvc(a0, a1, a2, a3, a4, a5, a6, a7, res); -+} -+ -+static optee_invoke_fn *get_invoke_func(struct device *dev) -+{ -+ const char *method; -+ -+ pr_info("probing for conduit method.\n"); -+ -+ if (device_property_read_string(dev, "method", &method)) { -+ pr_warn("missing \"method\" property\n"); -+ return ERR_PTR(-ENXIO); -+ } -+ -+ if (!strcmp("hvc", method)) -+ return optee_smccc_hvc; -+ else if (!strcmp("smc", method)) -+ return optee_smccc_smc; -+ -+ pr_warn("invalid \"method\" property: %s\n", method); -+ return ERR_PTR(-EINVAL); -+} -+ -+/* optee_remove - Device Removal Routine -+ * @pdev: platform device information struct -+ * -+ * optee_remove is called by platform subsystem to alert the driver -+ * that it should release the device -+ */ -+static int optee_smc_remove(struct platform_device *pdev) -+{ -+ struct optee *optee = platform_get_drvdata(pdev); -+ -+ /* -+ * Ask OP-TEE to free all cached shared memory objects to decrease -+ * reference counters and also avoid wild pointers in secure world -+ * into the old shared memory range. -+ */ -+ optee_disable_shm_cache(optee); -+ -+ optee_remove_common(optee); -+ -+ if (optee->smc.memremaped_shm) -+ memunmap(optee->smc.memremaped_shm); -+ -+ kfree(optee); -+ -+ return 0; -+} -+ -+/* optee_shutdown - Device Removal Routine -+ * @pdev: platform device information struct -+ * -+ * platform_shutdown is called by the platform subsystem to alert -+ * the driver that a shutdown, reboot, or kexec is happening and -+ * device must be disabled. -+ */ -+static void optee_shutdown(struct platform_device *pdev) -+{ -+ optee_disable_shm_cache(platform_get_drvdata(pdev)); -+} -+ -+static int optee_probe(struct platform_device *pdev) -+{ -+ optee_invoke_fn *invoke_fn; -+ struct tee_shm_pool *pool = ERR_PTR(-EINVAL); -+ struct optee *optee = NULL; -+ void *memremaped_shm = NULL; -+ struct tee_device *teedev; -+ u32 sec_caps; -+ int rc; -+ -+ invoke_fn = get_invoke_func(&pdev->dev); -+ if (IS_ERR(invoke_fn)) -+ return PTR_ERR(invoke_fn); -+ -+ if (!optee_msg_api_uid_is_optee_api(invoke_fn)) { -+ pr_warn("api uid mismatch\n"); -+ return -EINVAL; -+ } -+ -+ optee_msg_get_os_revision(invoke_fn); -+ -+ if (!optee_msg_api_revision_is_compatible(invoke_fn)) { -+ pr_warn("api revision mismatch\n"); -+ return -EINVAL; -+ } -+ -+ if (!optee_msg_exchange_capabilities(invoke_fn, &sec_caps)) { -+ pr_warn("capabilities mismatch\n"); -+ return -EINVAL; -+ } -+ -+ /* -+ * Try to use dynamic shared memory if possible -+ */ -+ if (sec_caps & OPTEE_SMC_SEC_CAP_DYNAMIC_SHM) -+ pool = optee_config_dyn_shm(); -+ -+ /* -+ * If dynamic shared memory is not available or failed - try static one -+ */ -+ if (IS_ERR(pool) && (sec_caps & OPTEE_SMC_SEC_CAP_HAVE_RESERVED_SHM)) -+ pool = optee_config_shm_memremap(invoke_fn, &memremaped_shm); -+ -+ if (IS_ERR(pool)) -+ return PTR_ERR(pool); -+ -+ optee = kzalloc(sizeof(*optee), GFP_KERNEL); -+ if (!optee) { -+ rc = -ENOMEM; -+ goto err; -+ } -+ -+ optee->ops = &optee_ops; -+ optee->smc.invoke_fn = invoke_fn; -+ optee->smc.sec_caps = sec_caps; -+ -+ teedev = tee_device_alloc(&optee_clnt_desc, NULL, pool, optee); -+ if (IS_ERR(teedev)) { -+ rc = PTR_ERR(teedev); -+ goto err; -+ } -+ optee->teedev = teedev; -+ -+ teedev = tee_device_alloc(&optee_supp_desc, NULL, pool, optee); -+ if (IS_ERR(teedev)) { -+ rc = PTR_ERR(teedev); -+ goto err; -+ } -+ optee->supp_teedev = teedev; -+ -+ rc = tee_device_register(optee->teedev); -+ if (rc) -+ goto err; -+ -+ rc = tee_device_register(optee->supp_teedev); -+ if (rc) -+ goto err; -+ -+ mutex_init(&optee->call_queue.mutex); -+ INIT_LIST_HEAD(&optee->call_queue.waiters); -+ optee_wait_queue_init(&optee->wait_queue); -+ optee_supp_init(&optee->supp); -+ optee->smc.memremaped_shm = memremaped_shm; -+ optee->pool = pool; -+ -+ /* -+ * Ensure that there are no pre-existing shm objects before enabling -+ * the shm cache so that there's no chance of receiving an invalid -+ * address during shutdown. This could occur, for example, if we're -+ * kexec booting from an older kernel that did not properly cleanup the -+ * shm cache. -+ */ -+ optee_disable_unmapped_shm_cache(optee); -+ -+ optee_enable_shm_cache(optee); -+ -+ if (optee->smc.sec_caps & OPTEE_SMC_SEC_CAP_DYNAMIC_SHM) -+ pr_info("dynamic shared memory is enabled\n"); -+ -+ platform_set_drvdata(pdev, optee); -+ -+ rc = optee_enumerate_devices(PTA_CMD_GET_DEVICES); -+ if (rc) { -+ optee_smc_remove(pdev); -+ return rc; -+ } -+ -+ pr_info("initialized driver\n"); -+ return 0; -+err: -+ if (optee) { -+ /* -+ * tee_device_unregister() is safe to call even if the -+ * devices hasn't been registered with -+ * tee_device_register() yet. -+ */ -+ tee_device_unregister(optee->supp_teedev); -+ tee_device_unregister(optee->teedev); -+ kfree(optee); -+ } -+ if (pool) -+ tee_shm_pool_free(pool); -+ if (memremaped_shm) -+ memunmap(memremaped_shm); -+ return rc; -+} -+ -+static const struct of_device_id optee_dt_match[] = { -+ { .compatible = "linaro,optee-tz" }, -+ {}, -+}; -+MODULE_DEVICE_TABLE(of, optee_dt_match); -+ -+static struct platform_driver optee_driver = { -+ .probe = optee_probe, -+ .remove = optee_smc_remove, -+ .shutdown = optee_shutdown, -+ .driver = { -+ .name = "optee", -+ .of_match_table = optee_dt_match, -+ }, -+}; -+ -+int optee_smc_abi_register(void) -+{ -+ return platform_driver_register(&optee_driver); -+} -+ -+void optee_smc_abi_unregister(void) -+{ -+ platform_driver_unregister(&optee_driver); -+} --- -2.34.1 - |