diff options
Diffstat (limited to 'drivers/tee/optee/call.c')
-rw-r--r-- | drivers/tee/optee/call.c | 613 |
1 files changed, 239 insertions, 374 deletions
diff --git a/drivers/tee/optee/call.c b/drivers/tee/optee/call.c index cf2367ba08d6..290b1bb0e9cd 100644 --- a/drivers/tee/optee/call.c +++ b/drivers/tee/optee/call.c @@ -1,8 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2015, Linaro Limited + * Copyright (c) 2015-2021, Linaro Limited */ -#include <linux/arm-smccc.h> #include <linux/device.h> #include <linux/err.h> #include <linux/errno.h> @@ -10,17 +9,38 @@ #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" -struct optee_call_waiter { +#define MAX_ARG_PARAM_COUNT 6 + +/* + * How much memory we allocate for each entry. This doesn't have to be a + * single page, but it makes sense to keep at least keep it as multiples of + * the page size. + */ +#define SHM_ENTRY_SIZE PAGE_SIZE + +/* + * We need to have a compile time constant to be able to determine the + * maximum needed size of the bit field. + */ +#define MIN_ARG_SIZE OPTEE_MSG_GET_ARG_SIZE(MAX_ARG_PARAM_COUNT) +#define MAX_ARG_COUNT_PER_ENTRY (SHM_ENTRY_SIZE / MIN_ARG_SIZE) + +/* + * Shared memory for argument structs are cached here. The number of + * arguments structs that can fit is determined at runtime depending on the + * needed RPC parameter count reported by secure world + * (optee->rpc_param_count). + */ +struct optee_shm_arg_entry { struct list_head list_node; - struct completion c; + struct tee_shm *shm; + DECLARE_BITMAP(map, MAX_ARG_COUNT_PER_ENTRY); }; -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 @@ -44,8 +64,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); @@ -71,8 +91,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 @@ -112,114 +132,170 @@ 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 - * @parg: physical address of 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 - */ -u32 optee_do_call_with_arg(struct tee_context *ctx, phys_addr_t parg) +void optee_shm_arg_cache_init(struct optee *optee, u32 flags) { - struct optee *optee = tee_get_drvdata(ctx->teedev); - struct optee_call_waiter w; - struct optee_rpc_param param = { }; - struct optee_call_ctx call_ctx = { }; - u32 ret; - - 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; - - optee->invoke_fn(param.a0, param.a1, param.a2, param.a3, - param.a4, param.a5, param.a6, param.a7, - &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)) { - might_sleep(); - param.a0 = res.a0; - param.a1 = res.a1; - param.a2 = res.a2; - param.a3 = res.a3; - optee_handle_rpc(ctx, ¶m, &call_ctx); - } else { - ret = res.a0; - break; + INIT_LIST_HEAD(&optee->shm_arg_cache.shm_args); + mutex_init(&optee->shm_arg_cache.mutex); + optee->shm_arg_cache.flags = flags; +} + +void optee_shm_arg_cache_uninit(struct optee *optee) +{ + struct list_head *head = &optee->shm_arg_cache.shm_args; + struct optee_shm_arg_entry *entry; + + mutex_destroy(&optee->shm_arg_cache.mutex); + while (!list_empty(head)) { + entry = list_first_entry(head, struct optee_shm_arg_entry, + list_node); + list_del(&entry->list_node); + if (find_first_bit(entry->map, MAX_ARG_COUNT_PER_ENTRY) != + MAX_ARG_COUNT_PER_ENTRY) { + pr_err("Freeing non-free entry\n"); } + tee_shm_free(entry->shm); + kfree(entry); } +} - 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); +size_t optee_msg_arg_size(size_t rpc_param_count) +{ + size_t sz = OPTEE_MSG_GET_ARG_SIZE(MAX_ARG_PARAM_COUNT); + + if (rpc_param_count) + sz += OPTEE_MSG_GET_ARG_SIZE(rpc_param_count); - return ret; + return sz; } -static struct tee_shm *get_msg_arg(struct tee_context *ctx, size_t num_params, - struct optee_msg_arg **msg_arg, - phys_addr_t *msg_parg) +/** + * optee_get_msg_arg() - Provide shared memory for argument struct + * @ctx: Caller TEE context + * @num_params: Number of parameter to store + * @entry_ret: Entry pointer, needed when freeing the buffer + * @shm_ret: Shared memory buffer + * @offs_ret: Offset of argument strut in shared memory buffer + * + * @returns a pointer to the argument struct in memory, else an ERR_PTR + */ +struct optee_msg_arg *optee_get_msg_arg(struct tee_context *ctx, + size_t num_params, + struct optee_shm_arg_entry **entry_ret, + struct tee_shm **shm_ret, + u_int *offs_ret) { - int rc; - struct tee_shm *shm; + struct optee *optee = tee_get_drvdata(ctx->teedev); + size_t sz = optee_msg_arg_size(optee->rpc_param_count); + struct optee_shm_arg_entry *entry; struct optee_msg_arg *ma; + size_t args_per_entry; + u_long bit; + u_int offs; + void *res; + + if (num_params > MAX_ARG_PARAM_COUNT) + return ERR_PTR(-EINVAL); + + if (optee->shm_arg_cache.flags & OPTEE_SHM_ARG_SHARED) + args_per_entry = SHM_ENTRY_SIZE / sz; + else + args_per_entry = 1; + + mutex_lock(&optee->shm_arg_cache.mutex); + list_for_each_entry(entry, &optee->shm_arg_cache.shm_args, list_node) { + bit = find_first_zero_bit(entry->map, MAX_ARG_COUNT_PER_ENTRY); + if (bit < args_per_entry) + goto have_entry; + } - shm = tee_shm_alloc(ctx, OPTEE_MSG_GET_ARG_SIZE(num_params), - TEE_SHM_MAPPED); - if (IS_ERR(shm)) - return shm; - - ma = tee_shm_get_va(shm, 0); - if (IS_ERR(ma)) { - rc = PTR_ERR(ma); + /* + * No entry was found, let's allocate a new. + */ + entry = kzalloc(sizeof(*entry), GFP_KERNEL); + if (!entry) { + res = ERR_PTR(-ENOMEM); goto out; } - rc = tee_shm_get_pa(shm, 0, msg_parg); - if (rc) - goto out; + if (optee->shm_arg_cache.flags & OPTEE_SHM_ARG_ALLOC_PRIV) + res = tee_shm_alloc_priv_buf(ctx, SHM_ENTRY_SIZE); + else + res = tee_shm_alloc_kernel_buf(ctx, SHM_ENTRY_SIZE); - memset(ma, 0, OPTEE_MSG_GET_ARG_SIZE(num_params)); + if (IS_ERR(res)) { + kfree(entry); + goto out; + } + entry->shm = res; + list_add(&entry->list_node, &optee->shm_arg_cache.shm_args); + bit = 0; + +have_entry: + offs = bit * sz; + res = tee_shm_get_va(entry->shm, offs); + if (IS_ERR(res)) + goto out; + ma = res; + set_bit(bit, entry->map); + memset(ma, 0, sz); ma->num_params = num_params; - *msg_arg = ma; + *entry_ret = entry; + *shm_ret = entry->shm; + *offs_ret = offs; out: - if (rc) { - tee_shm_free(shm); - return ERR_PTR(rc); + mutex_unlock(&optee->shm_arg_cache.mutex); + return res; +} + +/** + * optee_free_msg_arg() - Free previsouly obtained shared memory + * @ctx: Caller TEE context + * @entry: Pointer returned when the shared memory was obtained + * @offs: Offset of shared memory buffer to free + * + * This function frees the shared memory obtained with optee_get_msg_arg(). + */ +void optee_free_msg_arg(struct tee_context *ctx, + struct optee_shm_arg_entry *entry, u_int offs) +{ + struct optee *optee = tee_get_drvdata(ctx->teedev); + size_t sz = optee_msg_arg_size(optee->rpc_param_count); + u_long bit; + + if (offs > SHM_ENTRY_SIZE || offs % sz) { + pr_err("Invalid offs %u\n", offs); + return; } + bit = offs / sz; + + mutex_lock(&optee->shm_arg_cache.mutex); + + if (!test_bit(bit, entry->map)) + pr_err("Bit pos %lu is already free\n", bit); + clear_bit(bit, entry->map); - return shm; + mutex_unlock(&optee->shm_arg_cache.mutex); } int optee_open_session(struct tee_context *ctx, struct tee_ioctl_open_session_arg *arg, struct tee_param *param) { + struct optee *optee = tee_get_drvdata(ctx->teedev); struct optee_context_data *ctxdata = ctx->data; - int rc; + struct optee_shm_arg_entry *entry; struct tee_shm *shm; struct optee_msg_arg *msg_arg; - phys_addr_t msg_parg; struct optee_session *sess = NULL; + uuid_t client_uuid; + u_int offs; + int rc; /* +2 for the meta parameters added below */ - shm = get_msg_arg(ctx, arg->num_params + 2, &msg_arg, &msg_parg); - if (IS_ERR(shm)) - return PTR_ERR(shm); + msg_arg = optee_get_msg_arg(ctx, arg->num_params + 2, + &entry, &shm, &offs); + if (IS_ERR(msg_arg)) + return PTR_ERR(msg_arg); msg_arg->cmd = OPTEE_MSG_CMD_OPEN_SESSION; msg_arg->cancel_id = arg->cancel_id; @@ -233,10 +309,16 @@ int optee_open_session(struct tee_context *ctx, msg_arg->params[1].attr = OPTEE_MSG_ATTR_TYPE_VALUE_INPUT | OPTEE_MSG_ATTR_META; memcpy(&msg_arg->params[0].u.value, arg->uuid, sizeof(arg->uuid)); - memcpy(&msg_arg->params[1].u.value, arg->uuid, sizeof(arg->clnt_uuid)); msg_arg->params[1].u.value.c = arg->clnt_login; - rc = optee_to_msg_param(msg_arg->params + 2, arg->num_params, param); + rc = tee_session_calc_client_uuid(&client_uuid, arg->clnt_login, + arg->clnt_uuid); + if (rc) + goto out; + export_uuid(msg_arg->params[1].u.octets, &client_uuid); + + rc = optee->ops->to_msg_param(optee, msg_arg->params + 2, + arg->num_params, param); if (rc) goto out; @@ -246,7 +328,7 @@ int optee_open_session(struct tee_context *ctx, goto out; } - if (optee_do_call_with_arg(ctx, msg_parg)) { + if (optee->ops->do_call_with_arg(ctx, shm, offs)) { msg_arg->ret = TEEC_ERROR_COMMUNICATION; msg_arg->ret_origin = TEEC_ORIGIN_COMMS; } @@ -261,7 +343,8 @@ int optee_open_session(struct tee_context *ctx, kfree(sess); } - if (optee_from_msg_param(param, arg->num_params, msg_arg->params + 2)) { + if (optee->ops->from_msg_param(optee, param, arg->num_params, + msg_arg->params + 2)) { arg->ret = TEEC_ERROR_COMMUNICATION; arg->ret_origin = TEEC_ORIGIN_COMMS; /* Close session again to avoid leakage */ @@ -272,17 +355,35 @@ int optee_open_session(struct tee_context *ctx, arg->ret_origin = msg_arg->ret_origin; } out: - tee_shm_free(shm); + optee_free_msg_arg(ctx, entry, offs); return rc; } +int optee_close_session_helper(struct tee_context *ctx, u32 session) +{ + struct optee *optee = tee_get_drvdata(ctx->teedev); + struct optee_shm_arg_entry *entry; + struct optee_msg_arg *msg_arg; + struct tee_shm *shm; + u_int offs; + + msg_arg = optee_get_msg_arg(ctx, 0, &entry, &shm, &offs); + if (IS_ERR(msg_arg)) + return PTR_ERR(msg_arg); + + msg_arg->cmd = OPTEE_MSG_CMD_CLOSE_SESSION; + msg_arg->session = session; + optee->ops->do_call_with_arg(ctx, shm, offs); + + optee_free_msg_arg(ctx, entry, offs); + + return 0; +} + int optee_close_session(struct tee_context *ctx, u32 session) { struct optee_context_data *ctxdata = ctx->data; - struct tee_shm *shm; - struct optee_msg_arg *msg_arg; - phys_addr_t msg_parg; struct optee_session *sess; /* Check that the session is valid and remove it from the list */ @@ -295,26 +396,19 @@ int optee_close_session(struct tee_context *ctx, u32 session) return -EINVAL; kfree(sess); - shm = get_msg_arg(ctx, 0, &msg_arg, &msg_parg); - if (IS_ERR(shm)) - return PTR_ERR(shm); - - msg_arg->cmd = OPTEE_MSG_CMD_CLOSE_SESSION; - msg_arg->session = session; - optee_do_call_with_arg(ctx, msg_parg); - - tee_shm_free(shm); - return 0; + return optee_close_session_helper(ctx, session); } int optee_invoke_func(struct tee_context *ctx, struct tee_ioctl_invoke_arg *arg, struct tee_param *param) { + struct optee *optee = tee_get_drvdata(ctx->teedev); struct optee_context_data *ctxdata = ctx->data; - struct tee_shm *shm; + struct optee_shm_arg_entry *entry; struct optee_msg_arg *msg_arg; - phys_addr_t msg_parg; struct optee_session *sess; + struct tee_shm *shm; + u_int offs; int rc; /* Check that the session is valid */ @@ -324,24 +418,27 @@ 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, &msg_parg); - if (IS_ERR(shm)) - return PTR_ERR(shm); + msg_arg = optee_get_msg_arg(ctx, arg->num_params, + &entry, &shm, &offs); + if (IS_ERR(msg_arg)) + return PTR_ERR(msg_arg); msg_arg->cmd = OPTEE_MSG_CMD_INVOKE_COMMAND; msg_arg->func = arg->func; msg_arg->session = arg->session; msg_arg->cancel_id = arg->cancel_id; - rc = optee_to_msg_param(msg_arg->params, arg->num_params, param); + rc = optee->ops->to_msg_param(optee, msg_arg->params, arg->num_params, + param); if (rc) goto out; - if (optee_do_call_with_arg(ctx, msg_parg)) { + if (optee->ops->do_call_with_arg(ctx, shm, offs)) { msg_arg->ret = TEEC_ERROR_COMMUNICATION; msg_arg->ret_origin = TEEC_ORIGIN_COMMS; } - if (optee_from_msg_param(param, arg->num_params, msg_arg->params)) { + if (optee->ops->from_msg_param(optee, param, arg->num_params, + msg_arg->params)) { msg_arg->ret = TEEC_ERROR_COMMUNICATION; msg_arg->ret_origin = TEEC_ORIGIN_COMMS; } @@ -349,17 +446,19 @@ int optee_invoke_func(struct tee_context *ctx, struct tee_ioctl_invoke_arg *arg, arg->ret = msg_arg->ret; arg->ret_origin = msg_arg->ret_origin; out: - tee_shm_free(shm); + optee_free_msg_arg(ctx, entry, offs); return rc; } int optee_cancel_req(struct tee_context *ctx, u32 cancel_id, u32 session) { + struct optee *optee = tee_get_drvdata(ctx->teedev); struct optee_context_data *ctxdata = ctx->data; - struct tee_shm *shm; + struct optee_shm_arg_entry *entry; struct optee_msg_arg *msg_arg; - phys_addr_t msg_parg; struct optee_session *sess; + struct tee_shm *shm; + u_int offs; /* Check that the session is valid */ mutex_lock(&ctxdata->mutex); @@ -368,169 +467,24 @@ 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, &msg_parg); - if (IS_ERR(shm)) - return PTR_ERR(shm); + msg_arg = optee_get_msg_arg(ctx, 0, &entry, &shm, &offs); + if (IS_ERR(msg_arg)) + return PTR_ERR(msg_arg); msg_arg->cmd = OPTEE_MSG_CMD_CANCEL; msg_arg->session = session; msg_arg->cancel_id = cancel_id; - optee_do_call_with_arg(ctx, msg_parg); + optee->ops->do_call_with_arg(ctx, shm, offs); - tee_shm_free(shm); + optee_free_msg_arg(ctx, entry, offs); 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 - */ -void optee_disable_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) { - 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; - - 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); -} - -#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) - return (pgprot_val(p) & L_PTE_MT_MASK) == L_PTE_MT_WRITEALLOC; + return (((pgprot_val(p) & L_PTE_MT_MASK) == L_PTE_MT_WRITEALLOC) || + ((pgprot_val(p) & L_PTE_MT_MASK) == L_PTE_MT_WRITEBACK)); #elif defined(CONFIG_ARM64) return (pgprot_val(p) & PTE_ATTRINDX_MASK) == PTE_ATTRINDX(MT_NORMAL); #else @@ -538,18 +492,21 @@ static bool is_normal_memory(pgprot_t p) #endif } -static int __check_mem_type(struct vm_area_struct *vma, unsigned long end) +static int __check_mem_type(struct mm_struct *mm, unsigned long start, + unsigned long end) { - while (vma && is_normal_memory(vma->vm_page_prot)) { - if (vma->vm_end >= end) - return 0; - vma = vma->vm_next; + struct vm_area_struct *vma; + VMA_ITERATOR(vmi, mm, start); + + for_each_vma_range(vmi, vma, end) { + if (!is_normal_memory(vma->vm_page_prot)) + return -EINVAL; } - return -EINVAL; + return 0; } -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; @@ -558,104 +515,12 @@ static int check_mem_type(unsigned long start, size_t num_pages) * Allow kernel address to register with OP-TEE as kernel * pages are configured as normal memory only. */ - if (virt_addr_valid(start)) + if (virt_addr_valid((void *)start) || is_vmalloc_addr((void *)start)) return 0; - down_read(&mm->mmap_sem); - rc = __check_mem_type(find_vma(mm, start), - start + num_pages * PAGE_SIZE); - up_read(&mm->mmap_sem); + mmap_read_lock(mm); + rc = __check_mem_type(mm, start, start + num_pages * PAGE_SIZE); + mmap_read_unlock(mm); 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 tee_shm *shm_arg = NULL; - struct optee_msg_arg *msg_arg; - u64 *pages_list; - phys_addr_t msg_parg; - 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, &msg_parg); - 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_do_call_with_arg(ctx, msg_parg) || - 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 tee_shm *shm_arg; - struct optee_msg_arg *msg_arg; - phys_addr_t msg_parg; - int rc = 0; - - shm_arg = get_msg_arg(ctx, 1, &msg_arg, &msg_parg); - 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_do_call_with_arg(ctx, msg_parg) || - 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; -} |