| /* |
| * Copyright (c) 2013-2014 NVIDIA Corporation. All rights reserved. |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation; either version 2 of the License, or |
| * (at your option) any later version. |
| * |
| * This program is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
| * more details. |
| * |
| * You should have received a copy of the GNU General Public License along |
| * with this program; if not, write to the Free Software Foundation, Inc., |
| * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
| */ |
| |
| #include <linux/atomic.h> |
| #include <linux/uaccess.h> |
| #include <linux/module.h> |
| #include <linux/slab.h> |
| #include <linux/fs.h> |
| #include <linux/printk.h> |
| #include <linux/ioctl.h> |
| #include <linux/miscdevice.h> |
| #include <linux/mm.h> |
| #include <asm/cacheflush.h> |
| #include <asm/outercache.h> |
| #include <linux/list.h> |
| #include <linux/dma-mapping.h> |
| |
| #include "ote_protocol.h" |
| |
| #define SET_ANSWER(a, r, ro) { a.result = r; a.result_origin = ro; } |
| |
| struct tlk_device tlk_dev; |
| DEFINE_MUTEX(smc_lock); |
| |
| static int te_create_free_cmd_list(struct tlk_device *dev) |
| { |
| int cmd_desc_count, ret = 0; |
| struct te_cmd_req_desc *req_desc; |
| struct te_cmd_req_desc_compat *req_desc_compat; |
| int bitmap_size; |
| bool use_reqbuf; |
| |
| /* |
| * Check if new shared req/param register SMC is supported. |
| * |
| * If it is, TLK can map in the shared req/param buffers and do_smc |
| * only needs to send the offsets within each (with cache coherency |
| * being maintained by HW through an NS mapping). |
| * |
| * If the SMC support is not yet present, then fallback to the old |
| * mode of writing to an uncached buffer to maintain coherency (and |
| * phys addresses are passed in do_smc). |
| */ |
| dev->req_param_buf = NULL; |
| use_reqbuf = !tlk_generic_smc(dev->tlk_info, |
| TE_SMC_REGISTER_REQ_BUF, 0, 0, 0); |
| |
| if (use_reqbuf) { |
| dev->req_param_buf = kmalloc((2 * PAGE_SIZE), GFP_KERNEL); |
| |
| if (!dev->req_param_buf) { |
| ret = -ENOMEM; |
| goto error; |
| } |
| |
| /* requests in the first page, params in the second */ |
| dev->req_addr = (struct te_request *) dev->req_param_buf; |
| dev->param_addr = (struct te_oper_param *) |
| (dev->req_param_buf + PAGE_SIZE); |
| |
| tlk_generic_smc(dev->tlk_info, TE_SMC_REGISTER_REQ_BUF, |
| (uintptr_t)dev->req_addr, (2 * PAGE_SIZE), 0); |
| } else { |
| dev->req_addr = dma_alloc_coherent(NULL, PAGE_SIZE, |
| &dev->req_addr_phys, GFP_KERNEL); |
| dev->param_addr = dma_alloc_coherent(NULL, PAGE_SIZE, |
| &dev->param_addr_phys, GFP_KERNEL); |
| |
| #ifdef CONFIG_TRUSTY |
| dev->param_pages = dma_alloc_coherent(NULL, PAGE_SIZE, |
| &dev->param_pages_phys, GFP_KERNEL); |
| if (!dev->param_pages) { |
| ret = -ENOMEM; |
| goto error; |
| } |
| |
| if (dev->param_pages_phys & 0xFFFFFFFF00000000) { |
| pr_err("Unsupported address range\n"); |
| dma_free_coherent(NULL, PAGE_SIZE, |
| dev->param_pages, |
| dev->param_pages_phys); |
| ret = -ENOMEM; |
| goto error; |
| } |
| |
| dev->param_pages_size = PAGE_SIZE; |
| dev->param_pages_tail = 0; |
| #endif |
| } |
| |
| if (!dev->req_addr || !dev->param_addr) { |
| ret = -ENOMEM; |
| goto error; |
| } |
| |
| dev->req_addr_compat = (struct te_request_compat *) |
| dev->req_addr; |
| dev->param_addr_compat = (struct te_oper_param_compat *) |
| dev->param_addr; |
| /* alloc param bitmap allocator */ |
| bitmap_size = BITS_TO_LONGS(TE_PARAM_MAX) * sizeof(long); |
| dev->param_bitmap = kzalloc(bitmap_size, GFP_KERNEL); |
| if (dev->param_bitmap == NULL) { |
| pr_err("Failed to allocate param bitmap\n"); |
| ret = -ENOMEM; |
| goto error; |
| } |
| |
| for (cmd_desc_count = 0; |
| cmd_desc_count < TE_CMD_DESC_MAX; cmd_desc_count++) { |
| |
| req_desc = kzalloc(sizeof(struct te_cmd_req_desc), GFP_KERNEL); |
| if (req_desc == NULL) { |
| pr_err("Failed to allocate cmd req descriptor\n"); |
| ret = -ENOMEM; |
| goto error; |
| } |
| req_desc->req_addr = dev->req_addr + cmd_desc_count; |
| INIT_LIST_HEAD(&(req_desc->list)); |
| |
| /* Add the cmd param descriptor to free list */ |
| list_add_tail(&req_desc->list, &(dev->free_cmd_list)); |
| } |
| |
| for (cmd_desc_count = 0; |
| cmd_desc_count < TE_CMD_DESC_MAX_COMPAT; cmd_desc_count++) { |
| |
| req_desc_compat = kzalloc(sizeof(struct te_cmd_req_desc_compat), |
| GFP_KERNEL); |
| if (req_desc_compat == NULL) { |
| pr_err("Failed to allocate cmd req descriptor\n"); |
| ret = -ENOMEM; |
| goto error; |
| } |
| req_desc_compat->req_addr = |
| dev->req_addr_compat + cmd_desc_count; |
| INIT_LIST_HEAD(&(req_desc_compat->list)); |
| |
| /* Add the cmd param descriptor to free list */ |
| list_add_tail(&req_desc_compat->list, &(dev->free_cmd_list)); |
| } |
| |
| error: |
| return ret; |
| } |
| |
| static struct te_oper_param *te_get_free_params(struct tlk_device *dev, |
| unsigned int nparams) |
| { |
| struct te_oper_param *params = NULL; |
| int idx, nbits; |
| |
| if (nparams) { |
| nbits = get_count_order(nparams); |
| idx = bitmap_find_free_region(dev->param_bitmap, |
| TE_PARAM_MAX, nbits); |
| if (idx >= 0) |
| params = dev->param_addr + idx; |
| } |
| return params; |
| } |
| |
| static void te_put_free_params(struct tlk_device *dev, |
| struct te_oper_param *params, uint32_t nparams) |
| { |
| int idx, nbits; |
| |
| idx = (params - dev->param_addr); |
| nbits = get_count_order(nparams); |
| bitmap_release_region(dev->param_bitmap, idx, nbits); |
| } |
| |
| static struct te_oper_param_compat * |
| te_get_free_params_compat(struct tlk_device *dev, unsigned int nparams) |
| { |
| struct te_oper_param_compat *params = NULL; |
| int idx, nbits; |
| |
| if (nparams) { |
| nbits = get_count_order(nparams); |
| idx = bitmap_find_free_region(dev->param_bitmap, |
| TE_PARAM_MAX, nbits); |
| if (idx >= 0) |
| params = dev->param_addr_compat + idx; |
| } |
| return params; |
| } |
| |
| static void te_put_free_params_compat(struct tlk_device *dev, |
| struct te_oper_param_compat *params, uint32_t nparams) |
| { |
| int idx, nbits; |
| |
| idx = (params - dev->param_addr_compat); |
| nbits = get_count_order(nparams); |
| bitmap_release_region(dev->param_bitmap, idx, nbits); |
| } |
| |
| static struct te_cmd_req_desc *te_get_free_cmd_desc(struct tlk_device *dev) |
| { |
| struct te_cmd_req_desc *cmd_desc = NULL; |
| |
| if (!(list_empty(&(dev->free_cmd_list)))) { |
| cmd_desc = list_first_entry(&(dev->free_cmd_list), |
| struct te_cmd_req_desc, list); |
| list_del(&(cmd_desc->list)); |
| list_add_tail(&cmd_desc->list, &(dev->used_cmd_list)); |
| } |
| return cmd_desc; |
| } |
| |
| static void te_put_used_cmd_desc(struct tlk_device *dev, |
| struct te_cmd_req_desc *cmd_desc) |
| { |
| struct te_cmd_req_desc *param_desc, *tmp_param_desc; |
| |
| if (cmd_desc) { |
| list_for_each_entry_safe(param_desc, tmp_param_desc, |
| &(dev->used_cmd_list), list) { |
| if (cmd_desc->req_addr == param_desc->req_addr) { |
| list_del(¶m_desc->list); |
| list_add_tail(¶m_desc->list, |
| &(dev->free_cmd_list)); |
| } |
| } |
| } |
| } |
| |
| static struct te_cmd_req_desc_compat * |
| te_get_free_cmd_desc_compat(struct tlk_device *dev) |
| { |
| struct te_cmd_req_desc_compat *cmd_desc = NULL; |
| |
| if (!(list_empty(&(dev->free_cmd_list)))) { |
| cmd_desc = list_first_entry(&(dev->free_cmd_list), |
| struct te_cmd_req_desc_compat, list); |
| list_del(&(cmd_desc->list)); |
| list_add_tail(&cmd_desc->list, &(dev->used_cmd_list)); |
| } |
| return cmd_desc; |
| } |
| |
| static void te_put_used_cmd_desc_compat(struct tlk_device *dev, |
| struct te_cmd_req_desc_compat *cmd_desc) |
| { |
| struct te_cmd_req_desc_compat *param_desc, *tmp_param_desc; |
| |
| if (cmd_desc) { |
| list_for_each_entry_safe(param_desc, tmp_param_desc, |
| &(dev->used_cmd_list), list) { |
| if (cmd_desc->req_addr == param_desc->req_addr) { |
| list_del(¶m_desc->list); |
| list_add_tail(¶m_desc->list, |
| &(dev->free_cmd_list)); |
| } |
| } |
| } |
| } |
| |
| static void __attribute__((unused)) te_print_cmd_list( |
| struct tlk_device *dev, int used_list) |
| { |
| struct te_cmd_req_desc *param_desc; |
| |
| if (!used_list) { |
| pr_info("Printing free cmd list\n"); |
| if (!(list_empty(&(dev->free_cmd_list)))) { |
| list_for_each_entry(param_desc, &(dev->free_cmd_list), |
| list) |
| pr_info("Phys addr for cmd req desc (%p)\n", |
| param_desc->req_addr); |
| } |
| } else { |
| pr_info("Printing used cmd list\n"); |
| if (!(list_empty(&(dev->used_cmd_list)))) { |
| list_for_each_entry(param_desc, &(dev->used_cmd_list), |
| list) |
| pr_info("Phys addr for cmd req desc (%p)\n", |
| param_desc->req_addr); |
| } |
| } |
| } |
| |
| static int tlk_device_open(struct inode *inode, struct file *file) |
| { |
| struct tlk_context *context; |
| int ret = 0; |
| |
| context = kzalloc(sizeof(struct tlk_context), GFP_KERNEL); |
| if (!context) { |
| ret = -ENOMEM; |
| goto error; |
| } |
| context->dev = &tlk_dev; |
| INIT_LIST_HEAD(&(context->temp_shmem_list)); |
| INIT_LIST_HEAD(&(context->temp_persist_shmem_list)); |
| |
| file->private_data = context; |
| return 0; |
| error: |
| return ret; |
| } |
| |
| static int tlk_device_release(struct inode *inode, struct file *file) |
| { |
| struct tlk_context *context = file->private_data; |
| struct te_closesession_compat cmd; |
| struct te_cmd_req_desc_compat *cmd_desc = NULL; |
| struct te_request_compat *request; |
| struct rb_node *n; |
| struct te_session *session; |
| |
| if (context->is_ss_daemon) |
| tlk_ss_close(); |
| |
| mutex_lock(&smc_lock); |
| while ((n = rb_first(&context->sessions))) { |
| session = rb_entry(n, struct te_session, node); |
| cmd.session_id = session->session_id; |
| |
| cmd_desc = te_get_free_cmd_desc_compat(context->dev); |
| if (!cmd_desc) { |
| pr_err("%s: failed to get cmd_desc\n", __func__); |
| break; |
| } |
| |
| request = cmd_desc->req_addr; |
| memset(request, 0, sizeof(struct te_request)); |
| |
| /* close session cannot fail */ |
| te_close_session_compat(&cmd, request, context); |
| |
| if (cmd_desc) |
| te_put_used_cmd_desc_compat(context->dev, cmd_desc); |
| } |
| mutex_unlock(&smc_lock); |
| kfree(context); |
| file->private_data = NULL; |
| return 0; |
| } |
| |
| static int copy_params_from_user(struct te_request *req, |
| struct te_operation *operation) |
| { |
| struct te_oper_param *param_array; |
| struct te_oper_param *user_param; |
| uint32_t i; |
| |
| if (operation->list_count == 0) |
| return 0; |
| |
| param_array = req->params; |
| if (param_array == NULL) { |
| pr_err("param_array empty\n"); |
| return 1; |
| } |
| |
| user_param = operation->list_head; |
| for (i = 0; i < operation->list_count && user_param != NULL; i++) { |
| if (copy_from_user(param_array + i, user_param, |
| sizeof(struct te_oper_param))) { |
| pr_err("Failed to copy operation parameter:%d, %p, " \ |
| "list_count: %d\n", |
| i, user_param, operation->list_count); |
| return 1; |
| } |
| user_param = param_array[i].next_ptr_user; |
| } |
| return 0; |
| } |
| |
| static int copy_params_to_user(struct te_request *req, |
| struct te_operation *operation) |
| { |
| struct te_oper_param *param_array; |
| struct te_oper_param *user_param; |
| uint32_t i; |
| |
| if (operation->list_count == 0) |
| return 0; |
| |
| param_array = req->params; |
| if (param_array == NULL) { |
| pr_err("param_array empty\n"); |
| return 1; |
| } |
| |
| user_param = operation->list_head; |
| for (i = 0; i < req->params_size; i++) { |
| if (copy_to_user(user_param, param_array + i, |
| sizeof(struct te_oper_param))) { |
| pr_err("Failed to copy back parameter:%d %p\n", i, |
| user_param); |
| return 1; |
| } |
| user_param = param_array[i].next_ptr_user; |
| } |
| return 0; |
| } |
| |
| static long te_handle_trustedapp_ioctl(struct file *file, |
| unsigned int ioctl_num, unsigned long ioctl_param) |
| { |
| long err = 0; |
| union te_cmd cmd; |
| void *ptr_user_answer = NULL; |
| struct te_operation *operation = NULL; |
| struct te_oper_param *params = NULL; |
| struct te_answer answer; |
| struct te_request *request; |
| |
| struct te_cmd_req_desc *cmd_desc = NULL; |
| struct tlk_context *context = file->private_data; |
| struct tlk_device *dev = context->dev; |
| |
| if (copy_from_user(&cmd, (void __user *)ioctl_param, |
| sizeof(union te_cmd))) { |
| pr_err("Failed to copy command request\n"); |
| err = -EFAULT; |
| goto error; |
| } |
| |
| memset(&answer, 0, sizeof(struct te_answer)); |
| |
| switch (ioctl_num) { |
| case TE_IOCTL_OPEN_CLIENT_SESSION: |
| operation = &cmd.opensession.operation; |
| ptr_user_answer = (void *)cmd.opensession.answer; |
| |
| cmd_desc = te_get_free_cmd_desc(dev); |
| params = te_get_free_params(dev, operation->list_count); |
| |
| if (!cmd_desc || (operation->list_count && !params)) { |
| SET_ANSWER(answer, |
| OTE_ERROR_OUT_OF_MEMORY, |
| OTE_RESULT_ORIGIN_COMMS); |
| pr_err("failed to get cmd_desc/params\n"); |
| goto error; |
| } |
| |
| request = cmd_desc->req_addr; |
| memset(request, 0, sizeof(struct te_request)); |
| |
| request->params = params; |
| request->params_size = operation->list_count; |
| |
| if (copy_params_from_user(request, operation)) { |
| err = -EFAULT; |
| pr_info("failed to copy params from user\n"); |
| goto error; |
| } |
| |
| te_open_session(&cmd.opensession, request, context); |
| |
| SET_ANSWER(answer, request->result, request->result_origin); |
| answer.session_id = request->session_id; |
| break; |
| |
| case TE_IOCTL_CLOSE_CLIENT_SESSION: |
| ptr_user_answer = (void *)cmd.closesession.answer; |
| cmd_desc = te_get_free_cmd_desc(dev); |
| if (!cmd_desc) { |
| SET_ANSWER(answer, |
| OTE_ERROR_OUT_OF_MEMORY, |
| OTE_RESULT_ORIGIN_COMMS); |
| pr_err("failed to get cmd_desc\n"); |
| goto error; |
| } |
| |
| request = cmd_desc->req_addr; |
| memset(request, 0, sizeof(struct te_request)); |
| |
| /* close session cannot fail */ |
| te_close_session(&cmd.closesession, request, context); |
| break; |
| |
| case TE_IOCTL_LAUNCH_OPERATION: |
| operation = &cmd.launchop.operation; |
| ptr_user_answer = (void *)cmd.launchop.answer; |
| |
| cmd_desc = te_get_free_cmd_desc(dev); |
| params = te_get_free_params(dev, operation->list_count); |
| |
| if (!cmd_desc || (operation->list_count && !params)) { |
| SET_ANSWER(answer, |
| OTE_ERROR_OUT_OF_MEMORY, |
| OTE_RESULT_ORIGIN_COMMS); |
| pr_err("failed to get cmd_desc/params\n"); |
| goto error; |
| } |
| |
| request = cmd_desc->req_addr; |
| memset(request, 0, sizeof(struct te_request)); |
| |
| request->params = params; |
| request->params_size = operation->list_count; |
| |
| if (copy_params_from_user(request, operation)) { |
| err = -EFAULT; |
| pr_info("failed to copy params from user\n"); |
| goto error; |
| } |
| |
| te_launch_operation(&cmd.launchop, request, context); |
| |
| SET_ANSWER(answer, request->result, request->result_origin); |
| break; |
| |
| default: |
| pr_err("Invalid IOCTL Cmd\n"); |
| err = -EINVAL; |
| goto error; |
| } |
| if (ptr_user_answer && !err) { |
| if (copy_to_user(ptr_user_answer, &answer, |
| sizeof(struct te_answer))) { |
| pr_err("Failed to copy answer\n"); |
| err = -EFAULT; |
| } |
| } |
| if (request->params && !err) { |
| if (copy_params_to_user(request, operation)) { |
| pr_err("Failed to copy return params\n"); |
| err = -EFAULT; |
| } |
| } |
| |
| error: |
| if (cmd_desc) |
| te_put_used_cmd_desc(dev, cmd_desc); |
| if (params) |
| te_put_free_params(dev, params, operation->list_count); |
| return err; |
| } |
| |
| static int copy_params_from_user_compat(struct te_request_compat *req, |
| struct te_operation_compat *operation) |
| { |
| struct te_oper_param_compat *param_array; |
| struct te_oper_param_compat *user_param; |
| uint32_t i; |
| |
| if (operation->list_count == 0) |
| return 0; |
| |
| param_array = (struct te_oper_param_compat *)(uintptr_t)req->params; |
| if (param_array == NULL) { |
| pr_err("param_array empty\n"); |
| return 1; |
| } |
| |
| user_param = (struct te_oper_param_compat *)(uintptr_t) |
| operation->list_head; |
| for (i = 0; i < operation->list_count && user_param != NULL; i++) { |
| if (copy_from_user(param_array + i, user_param, |
| sizeof(struct te_oper_param_compat))) { |
| pr_err("Failed to copy operation parameter:%d, %p, " \ |
| "list_count: %d\n", |
| i, user_param, operation->list_count); |
| return 1; |
| } |
| user_param = (struct te_oper_param_compat *)(uintptr_t) |
| param_array[i].next_ptr_user; |
| } |
| |
| if (i != operation->list_count) { |
| pr_err("Malformed request: list_count=%d\n, params_num=%d\n", |
| operation->list_count, i); |
| return 1; |
| } |
| |
| return 0; |
| } |
| |
| static int copy_params_to_user_compat(struct te_request_compat *req, |
| struct te_operation_compat *operation) |
| { |
| struct te_oper_param_compat *param_array; |
| struct te_oper_param_compat *user_param; |
| uint32_t i; |
| |
| if (operation->list_count == 0) |
| return 0; |
| |
| param_array = |
| (struct te_oper_param_compat *)(uintptr_t)req->params; |
| if (param_array == NULL) { |
| pr_err("param_array empty\n"); |
| return 1; |
| } |
| |
| user_param = |
| (struct te_oper_param_compat *)(uintptr_t)operation->list_head; |
| for (i = 0; i < req->params_size; i++) { |
| if (copy_to_user(user_param, param_array + i, |
| sizeof(struct te_oper_param_compat))) { |
| pr_err("Failed to copy back parameter:%d %p\n", i, |
| user_param); |
| return 1; |
| } |
| user_param = (struct te_oper_param_compat *)(uintptr_t) |
| param_array[i].next_ptr_user; |
| } |
| return 0; |
| } |
| |
| static long te_handle_trustedapp_ioctl_compat(struct file *file, |
| unsigned int ioctl_num, unsigned long ioctl_param) |
| { |
| long err = 0; |
| union te_cmd_compat cmd_compat; |
| struct te_operation_compat *operation = NULL; |
| struct te_oper_param_compat *params = NULL; |
| struct te_request_compat *request; |
| void __user *ptr_user_answer = NULL; |
| struct te_answer answer; |
| struct te_cmd_req_desc_compat *cmd_desc = NULL; |
| struct tlk_context *context = file->private_data; |
| struct tlk_device *dev = context->dev; |
| |
| if (copy_from_user(&cmd_compat, (void __user *)ioctl_param, |
| sizeof(union te_cmd_compat))) { |
| pr_err("Failed to copy command request\n"); |
| err = -EFAULT; |
| goto error; |
| } |
| |
| memset(&answer, 0, sizeof(struct te_answer)); |
| |
| switch (ioctl_num) { |
| case TE_IOCTL_OPEN_CLIENT_SESSION_COMPAT: |
| operation = &cmd_compat.opensession.operation; |
| ptr_user_answer = (void *)(uintptr_t) |
| cmd_compat.opensession.answer; |
| |
| cmd_desc = te_get_free_cmd_desc_compat(dev); |
| params = te_get_free_params_compat(dev, operation->list_count); |
| |
| if (!cmd_desc || (operation->list_count && !params)) { |
| SET_ANSWER(answer, |
| OTE_ERROR_OUT_OF_MEMORY, |
| OTE_RESULT_ORIGIN_COMMS); |
| pr_err("failed to get cmd_desc/params\n"); |
| goto error; |
| } |
| |
| request = cmd_desc->req_addr; |
| memset(request, 0, sizeof(struct te_request_compat)); |
| |
| request->params = (uintptr_t)params; |
| request->params_size = operation->list_count; |
| |
| if (copy_params_from_user_compat(request, operation)) { |
| err = -EFAULT; |
| pr_info("failed to copy params from user\n"); |
| goto error; |
| } |
| |
| te_open_session_compat(&cmd_compat.opensession, |
| request, context); |
| |
| SET_ANSWER(answer, request->result, request->result_origin); |
| answer.session_id = request->session_id; |
| break; |
| |
| case TE_IOCTL_CLOSE_CLIENT_SESSION_COMPAT: |
| ptr_user_answer = (void *)(uintptr_t) |
| cmd_compat.closesession.answer; |
| cmd_desc = te_get_free_cmd_desc_compat(dev); |
| if (!cmd_desc) { |
| SET_ANSWER(answer, |
| OTE_ERROR_OUT_OF_MEMORY, |
| OTE_RESULT_ORIGIN_COMMS); |
| pr_err("failed to get cmd_desc\n"); |
| goto error; |
| } |
| |
| request = cmd_desc->req_addr; |
| memset(request, 0, sizeof(struct te_request_compat)); |
| |
| /* close session cannot fail */ |
| te_close_session_compat(&cmd_compat.closesession, |
| request, context); |
| break; |
| |
| case TE_IOCTL_LAUNCH_OPERATION_COMPAT: |
| operation = &cmd_compat.launchop.operation; |
| ptr_user_answer = (void *)(uintptr_t)cmd_compat.launchop.answer; |
| |
| cmd_desc = te_get_free_cmd_desc_compat(dev); |
| params = te_get_free_params_compat(dev, operation->list_count); |
| |
| if (!cmd_desc || (operation->list_count && !params)) { |
| SET_ANSWER(answer, |
| OTE_ERROR_OUT_OF_MEMORY, |
| OTE_RESULT_ORIGIN_COMMS); |
| pr_err("failed to get cmd_desc/params\n"); |
| goto error; |
| } |
| |
| request = cmd_desc->req_addr; |
| memset(request, 0, sizeof(struct te_request_compat)); |
| |
| request->params = (uintptr_t)params; |
| request->params_size = operation->list_count; |
| |
| if (copy_params_from_user_compat(request, operation)) { |
| err = -EFAULT; |
| pr_info("failed to copy params from user\n"); |
| goto error; |
| } |
| |
| te_launch_operation_compat(&cmd_compat.launchop, |
| request, context); |
| |
| SET_ANSWER(answer, request->result, request->result_origin); |
| break; |
| |
| default: |
| pr_err("Invalid IOCTL Cmd\n"); |
| err = -EINVAL; |
| goto error; |
| } |
| if (ptr_user_answer && !err) { |
| if (copy_to_user(ptr_user_answer, &answer, |
| sizeof(struct te_answer))) { |
| pr_err("Failed to copy answer\n"); |
| err = -EFAULT; |
| } |
| } |
| if (request->params && !err) { |
| if (copy_params_to_user_compat(request, operation)) { |
| pr_err("Failed to copy return params\n"); |
| err = -EFAULT; |
| } |
| } |
| |
| error: |
| if (cmd_desc) |
| te_put_used_cmd_desc_compat(dev, cmd_desc); |
| if (params) |
| te_put_free_params_compat(dev, params, operation->list_count); |
| return err; |
| } |
| |
| static long tlk_device_ioctl(struct file *file, unsigned int ioctl_num, |
| unsigned long ioctl_param) |
| { |
| int err; |
| |
| switch (ioctl_num) { |
| case TE_IOCTL_OPEN_CLIENT_SESSION: |
| case TE_IOCTL_CLOSE_CLIENT_SESSION: |
| case TE_IOCTL_LAUNCH_OPERATION: |
| mutex_lock(&smc_lock); |
| err = te_handle_trustedapp_ioctl(file, ioctl_num, ioctl_param); |
| mutex_unlock(&smc_lock); |
| break; |
| |
| case TE_IOCTL_OPEN_CLIENT_SESSION_COMPAT: |
| case TE_IOCTL_CLOSE_CLIENT_SESSION_COMPAT: |
| case TE_IOCTL_LAUNCH_OPERATION_COMPAT: |
| mutex_lock(&smc_lock); |
| err = te_handle_trustedapp_ioctl_compat(file, ioctl_num, |
| ioctl_param); |
| mutex_unlock(&smc_lock); |
| break; |
| |
| case TE_IOCTL_SS_NEW_REQ_LEGACY: |
| case TE_IOCTL_SS_REQ_COMPLETE_LEGACY: |
| err = te_handle_ss_ioctl_legacy(file, ioctl_num, ioctl_param); |
| break; |
| |
| case TE_IOCTL_SS_NEW_REQ: |
| case TE_IOCTL_SS_REQ_COMPLETE: |
| case TE_IOCTL_SS_CMD: |
| err = te_handle_ss_ioctl(file, ioctl_num, ioctl_param); |
| break; |
| |
| default: |
| pr_err("%s: Invalid IOCTL (0x%x) id 0x%x max 0x%lx\n", |
| __func__, ioctl_num, _IOC_NR(ioctl_num), |
| (unsigned long)TE_IOCTL_MAX_NR); |
| err = -EINVAL; |
| break; |
| } |
| |
| return err; |
| } |
| |
| /* |
| * tlk_driver function definitions. |
| */ |
| static const struct file_operations tlk_device_fops = { |
| .owner = THIS_MODULE, |
| .open = tlk_device_open, |
| .release = tlk_device_release, |
| .unlocked_ioctl = tlk_device_ioctl, |
| #ifdef CONFIG_COMPAT |
| .compat_ioctl = tlk_device_ioctl, |
| #endif |
| }; |
| |
| struct miscdevice tlk_misc_device = { |
| .minor = MISC_DYNAMIC_MINOR, |
| .name = "tlk_device", |
| .fops = &tlk_device_fops, |
| }; |
| |
| int tlk_device_init(struct tlk_info *tlk_info) |
| { |
| int ret; |
| |
| INIT_LIST_HEAD(&(tlk_dev.used_cmd_list)); |
| INIT_LIST_HEAD(&(tlk_dev.free_cmd_list)); |
| |
| tlk_dev.tlk_info = tlk_info; |
| ret = te_create_free_cmd_list(&tlk_dev); |
| if (ret != 0) |
| return ret; |
| |
| return misc_register(&tlk_misc_device); |
| } |