summaryrefslogtreecommitdiff
path: root/security/tlk_driver/ote_device.c
diff options
context:
space:
mode:
Diffstat (limited to 'security/tlk_driver/ote_device.c')
-rw-r--r--security/tlk_driver/ote_device.c456
1 files changed, 456 insertions, 0 deletions
diff --git a/security/tlk_driver/ote_device.c b/security/tlk_driver/ote_device.c
new file mode 100644
index 000000000000..44d8a09971d0
--- /dev/null
+++ b/security/tlk_driver/ote_device.c
@@ -0,0 +1,456 @@
+/*
+ * Copyright (c) 2013 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 "ote_protocol.h"
+
+#define SET_ANSWER(a, r, ro) { a.result = r; a.return_origin = ro; }
+
+#define CREATE_TRACE_POINTS
+#include <trace/events/nvsecurity.h>
+
+struct tlk_device tlk_dev;
+
+u32 notrace tegra_read_cycle(void)
+{
+ u32 cycle_count;
+
+ asm volatile("mrc p15, 0, %0, c9, c13, 0" : "=r"(cycle_count));
+
+ return cycle_count;
+}
+
+/*
+ * The maximum number of outstanding command requests.
+ */
+#define TE_CMD_DESC_MAX (PAGE_SIZE / sizeof(struct te_request))
+#define TE_PARAM_MAX (PAGE_SIZE / sizeof(struct te_oper_param))
+
+static int te_create_free_cmd_list(struct tlk_device *dev)
+{
+ struct page *te_cmd_pages;
+ int cmd_desc_count = 0, ret = 0;
+ struct te_cmd_req_desc *req_desc;
+ int bitmap_size;
+
+ te_cmd_pages = alloc_pages(GFP_KERNEL, get_count_order(2));
+ if (!te_cmd_pages) {
+ ret = -ENOMEM;
+ goto error;
+ }
+
+ /* first 4KB page is request freelist, second is param freelist */
+ dev->req_addr = (unsigned long) page_address(te_cmd_pages);
+ dev->param_addr = (struct te_oper_param *)(dev->req_addr + PAGE_SIZE);
+ set_memory_uc(dev->req_addr, 2);
+
+ /* alloc param bitmap allocator */
+ bitmap_size = BITS_TO_LONGS(TE_PARAM_MAX) * sizeof(long);
+ dev->param_bitmap = kzalloc(bitmap_size, GFP_KERNEL);
+
+ 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 +
+ sizeof(struct te_request) * 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));
+ }
+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_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(&param_desc->list);
+ list_add_tail(&param_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 (%lx)\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 (%lx)\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->shmem_alloc_list));
+
+ file->private_data = context;
+ return 0;
+error:
+ return ret;
+}
+
+static int tlk_device_release(struct inode *inode, struct file *file)
+{
+ kfree(file->private_data);
+ 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_ERROR_ORIGIN_COMMS);
+ pr_err("failed to get cmd_desc/params\n");
+ goto error;
+ }
+
+ request = (struct te_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_ERROR_ORIGIN_COMMS);
+ pr_err("failed to get cmd_desc\n");
+ goto error;
+ }
+
+ request = (struct te_request *)cmd_desc->req_addr;
+ memset(request, 0, sizeof(struct te_request));
+
+ /* close session cannot fail */
+ te_close_session(&cmd.closesession, request);
+ 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_ERROR_ORIGIN_COMMS);
+ pr_err("failed to get cmd_desc/params\n");
+ goto error;
+ }
+
+ request = (struct te_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 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:
+ err = te_handle_trustedapp_ioctl(file, ioctl_num, ioctl_param);
+ break;
+
+ case TE_IOCTL_FILE_NEW_REQ:
+ case TE_IOCTL_FILE_FILL_BUF:
+ case TE_IOCTL_FILE_REQ_COMPLETE:
+ err = te_handle_fs_ioctl(file, ioctl_num, ioctl_param);
+ break;
+
+ default:
+ pr_err("%s: Invalid IOCTL (0x%x) id 0x%x max 0x%x\n", __func__,
+ ioctl_num, _IOC_NR(ioctl_num), TE_IOCTL_MAX_NR);
+ err = -EINVAL;
+ }
+
+ 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,
+};
+
+struct miscdevice tlk_misc_device = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "tlk_device",
+ .fops = &tlk_device_fops,
+};
+
+static int __init tlk_init(void)
+{
+ int ret;
+
+ INIT_LIST_HEAD(&(tlk_dev.used_cmd_list));
+ INIT_LIST_HEAD(&(tlk_dev.free_cmd_list));
+
+ ret = te_create_free_cmd_list(&tlk_dev);
+ if (ret != 0)
+ return ret;
+
+ return misc_register(&tlk_misc_device);
+}
+
+module_init(tlk_init);