security: tlk_driver: add support for PHYS_LIST handling.
Chris Johnson [Mon, 23 Mar 2015 23:37:35 +0000 (16:37 -0700)]
Bug 200091941
Bug 1754253

Change-Id: Ia5b920e6f45280b7a3fb4360b828612225af5761
Signed-off-by: Chris Johnson <cwj@nvidia.com>
Signed-off-by: Mahesh Lagadapati <mlagadapati@nvidia.com>
Reviewed-on: http://git-master/r/1132411
(cherry picked from commit 4c5acba1dbe4439258f4203823cdac5bee24a00b)
Reviewed-on: http://git-master/r/1164583
Reviewed-on: http://git-master/r/1119126
Reviewed-by: Vinayak Pane <vpane@nvidia.com>

security/tlk_driver/Makefile
security/tlk_driver/ote_asm.S
security/tlk_driver/ote_comms.c
security/tlk_driver/ote_device.c
security/tlk_driver/ote_memory.c [new file with mode: 0644]
security/tlk_driver/ote_protocol.h

index 3e9598c..84a1b0b 100644 (file)
@@ -22,6 +22,7 @@ tlk_driver-objs += ote_comms.o
 tlk_driver-objs += ote_fs.o
 tlk_driver-objs += ote_asm.o
 tlk_driver-objs += ote_log.o
+tlk_driver-objs += ote_memory.o
 
 ifeq ($(CONFIG_ARM),y)
 plus_sec := $(call as-instr,.arch_extension sec,+sec)
index 8a3bfd0..5d60164 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, NVIDIA Corporation. All rights reserved.
+ * Copyright (c) 2014-2016, 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
@@ -53,6 +53,11 @@ ENTRY(tlk_fiq_glue_aarch64)
        b       .
 ENDPROC(tlk_fiq_glue_aarch64)
 
+ENTRY(_tlk_get_mair)
+       mrs     x0, mair_el1
+       ret
+ENDPROC(_tlk_get_mair)
+
 #else
 
 ENTRY(_tlk_generic_smc)
@@ -60,4 +65,10 @@ ENTRY(_tlk_generic_smc)
        mov     pc, lr
 ENDPROC(_tlk_generic_smc)
 
+ENTRY(_tlk_get_mair)
+       mrc     p15, 0, r0, c10, c2, 0
+       mrc     p15, 0, r1, c10, c2, 1
+       mov     pc, lr
+ENDPROC(_tlk_get_mair)
+
 #endif
index 57822c4..ac44f89 100644 (file)
@@ -36,173 +36,6 @@ core_param(verbose_smc, verbose_smc, bool, 0644);
 #define VR_AUTH_UUID   {0x0179ED96, 0x45A81ADB, 0x089DC68D, 0xBB520279}
 #define SET_RESULT(req, r, ro) { req->result = r; req->result_origin = ro; }
 
-static int te_pin_user_pages(void *buffer, size_t size,
-               struct page ***pages_ptr, uint32_t buf_type, uint32_t nr_pages)
-{
-       int ret = 0;
-       struct page **pages = NULL;
-       bool writable;
-       int i;
-
-       pages = kzalloc(nr_pages * sizeof(struct page *), GFP_KERNEL);
-       if (!pages)
-               return -ENOMEM;
-
-       writable = (buf_type == TE_PARAM_TYPE_MEM_RW ||
-               buf_type == TE_PARAM_TYPE_PERSIST_MEM_RW);
-
-       down_read(&current->mm->mmap_sem);
-       ret = get_user_pages(current, current->mm, (unsigned long)buffer,
-                       nr_pages, writable,
-                       0, pages, NULL);
-
-       up_read(&current->mm->mmap_sem);
-
-       if (ret < nr_pages) {
-               nr_pages = ret;
-               pr_err("%s: Error %d in get_user_pages\n", __func__, ret);
-               goto error;
-       }
-
-       *pages_ptr = pages;
-
-       return OTE_SUCCESS;
-error:
-       for (i = 0; i < nr_pages; i++)
-               put_page(pages[i]);
-       kfree(pages);
-       return OTE_ERROR_OUT_OF_MEMORY;
-}
-
-static void te_release_mem_buffer(struct te_shmem_desc *shmem_desc)
-{
-       uint32_t i;
-
-       list_del(&shmem_desc->list);
-       for (i = 0; i < shmem_desc->nr_pages; i++) {
-               if ((shmem_desc->type == TE_PARAM_TYPE_MEM_RW) ||
-                       (shmem_desc->type == TE_PARAM_TYPE_PERSIST_MEM_RW))
-                       set_page_dirty_lock(shmem_desc->pages[i]);
-               put_page(shmem_desc->pages[i]);
-       }
-       kfree(shmem_desc->pages);
-       kfree(shmem_desc);
-}
-
-static void te_release_mem_buffers(struct list_head *buflist)
-{
-       struct te_shmem_desc *shmem_desc, *tmp_shmem_desc;
-
-       list_for_each_entry_safe(shmem_desc, tmp_shmem_desc, buflist, list) {
-               te_release_mem_buffer(shmem_desc);
-       }
-}
-
-static void te_activate_persist_mem_buffers(struct te_session *session)
-{
-       struct te_shmem_desc *shmem_desc, *tmp_shmem_desc;
-
-       /* move persist mem buffers from inactive list to active list */
-       list_for_each_entry_safe(shmem_desc, tmp_shmem_desc,
-               &session->inactive_persist_shmem_list, list) {
-
-               list_move_tail(&shmem_desc->list, &session->persist_shmem_list);
-       }
-}
-
-static int te_prep_mem_buffer(uint32_t session_id,
-               void *buffer, size_t size, uint32_t buf_type,
-               struct te_session *session)
-{
-       struct page **pages = NULL;
-       struct te_shmem_desc *shmem_desc = NULL;
-       int ret = 0;
-       uint32_t nr_pages;
-
-       /* allocate new shmem descriptor */
-       shmem_desc = kzalloc(sizeof(struct te_shmem_desc), GFP_KERNEL);
-       if (!shmem_desc) {
-               pr_err("%s: memory allocation failed\n", __func__);
-               ret = OTE_ERROR_OUT_OF_MEMORY;
-               goto error;
-       }
-
-       nr_pages = (((uintptr_t)buffer & (PAGE_SIZE - 1)) +
-                               (size + PAGE_SIZE - 1)) >> PAGE_SHIFT;
-       /* pin pages */
-       ret = te_pin_user_pages(buffer, size, &pages, buf_type, nr_pages);
-       if (ret != OTE_SUCCESS) {
-               pr_err("%s: te_pin_user_pages failed (%d)\n", __func__,
-                       nr_pages);
-               kfree(shmem_desc);
-               goto error;
-       }
-
-       /* initialize shmem descriptor */
-       INIT_LIST_HEAD(&(shmem_desc->list));
-       shmem_desc->buffer = buffer;
-       shmem_desc->size = size;
-       shmem_desc->nr_pages = nr_pages;
-       shmem_desc->pages = pages;
-
-       /* add shmem descriptor to proper list */
-       if ((buf_type == TE_PARAM_TYPE_MEM_RO) ||
-               (buf_type == TE_PARAM_TYPE_MEM_RW))
-               list_add_tail(&shmem_desc->list, &session->temp_shmem_list);
-       else {
-               list_add_tail(&shmem_desc->list,
-                       &session->inactive_persist_shmem_list);
-       }
-
-       return OTE_SUCCESS;
-error:
-       return ret;
-}
-
-static int te_prep_mem_buffers(struct te_request *request,
-                       struct te_session *session)
-{
-       uint32_t i;
-       int ret = OTE_SUCCESS;
-       struct te_oper_param *params;
-
-       params = (struct te_oper_param *)(uintptr_t)request->params;
-       for (i = 0; i < request->params_size; i++) {
-               switch (params[i].type) {
-               case TE_PARAM_TYPE_NONE:
-               case TE_PARAM_TYPE_INT_RO:
-               case TE_PARAM_TYPE_INT_RW:
-                       break;
-               case TE_PARAM_TYPE_MEM_RO:
-               case TE_PARAM_TYPE_MEM_RW:
-               case TE_PARAM_TYPE_PERSIST_MEM_RO:
-               case TE_PARAM_TYPE_PERSIST_MEM_RW:
-                       ret = te_prep_mem_buffer(request->session_id,
-                               (void *)(uintptr_t)params[i].u.Mem.base,
-                               params[i].u.Mem.len,
-                               params[i].type,
-                               session);
-                       if (ret != OTE_SUCCESS) {
-                               pr_err("%s failed with err (%d)\n",
-                                       __func__, ret);
-                               ret = OTE_ERROR_BAD_PARAMETERS;
-                               goto error;
-                       }
-                       break;
-               default:
-                       pr_err("%s: OTE_ERROR_BAD_PARAMETERS\n", __func__);
-                       ret = OTE_ERROR_BAD_PARAMETERS;
-                       break;
-               }
-       }
-       return OTE_SUCCESS;
-error:
-       /* release mem buffers */
-       te_release_mem_buffers(&session->temp_shmem_list);
-       te_release_mem_buffers(&session->inactive_persist_shmem_list);
-       return ret;
-}
-
 static struct te_session *te_get_session(struct tlk_context *context,
                                        uint32_t session_id)
 {
@@ -326,10 +159,10 @@ static void do_smc(struct te_request *request, struct tlk_device *dev)
        uint32_t smc_args;
        uint32_t smc_params = 0;
 
-       smc_args = (char *)request - dev->req_param_buf;
+       smc_args = (uintptr_t)request - (uintptr_t)dev->req_addr;
        if (request->params) {
                smc_params =
-                       (char *)(uintptr_t)request->params - dev->req_param_buf;
+                       (uintptr_t)request->params - (uintptr_t)dev->req_addr;
        }
 
        (void)send_smc(request->type, smc_args, smc_params);
index 103d8aa..5656e2e 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013-2015 NVIDIA Corporation. All rights reserved.
+ * Copyright (c) 2013-2016 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
@@ -40,32 +40,46 @@ 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, *tmp_req_desc;
+       struct te_cmd_req_desc *req_desc = NULL, *tmp_req_desc = NULL;
        int bitmap_size;
        int req_buf_size;
+       void *req_buf;
 
        /*
         * 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).
         */
-       req_buf_size = (2 * PAGE_SIZE);
-       dev->req_param_buf = kmalloc(req_buf_size, GFP_KERNEL);
-       if (!dev->req_param_buf) {
+       req_buf_size = (4 * PAGE_SIZE);
+       req_buf = kmalloc(req_buf_size, GFP_KERNEL);
+       if (!req_buf) {
+               pr_err("%s: Failed to allocate param buffer!\n", __func__);
                ret = -ENOMEM;
                goto error;
        }
 
-       /* requests in the first page, params in the second */
-       dev->req_addr   = (struct te_request *) dev->req_param_buf;
+       /* requests in 1st page, params in 2nd, pagelists in 3rd and 4th pages */
+       dev->req_addr = (struct te_request *)
+                       (req_buf + (0 * PAGE_SIZE));
        dev->param_addr = (struct te_oper_param *)
-                               (dev->req_param_buf + PAGE_SIZE);
+                       (req_buf + (1 * PAGE_SIZE));
+       dev->plist_addr = (uint64_t *)
+                       (req_buf + (2 * PAGE_SIZE));
 
        /* 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) {
-               pr_err("Failed to allocate param bitmap\n");
+               pr_err("%s: Failed to allocate param bitmap\n", __func__);
+               ret = -ENOMEM;
+               goto error;
+       }
+
+       /* alloc plist bitmap allocator */
+       bitmap_size = BITS_TO_LONGS(TE_PLIST_MAX) * sizeof(long);
+       dev->plist_bitmap = kzalloc(bitmap_size, GFP_KERNEL);
+       if (!dev->plist_bitmap) {
+               pr_err("%s: Failed to allocate plist bitmap\n", __func__);
                ret = -ENOMEM;
                goto error;
        }
@@ -73,12 +87,20 @@ static int te_create_free_cmd_list(struct tlk_device *dev)
        send_smc(TE_SMC_REGISTER_REQ_BUF,
                        (uintptr_t)dev->req_addr, req_buf_size);
 
+       if (!dev->req_addr || !dev->param_addr || !dev->plist_addr) {
+               pr_err("%s: Bad dev request addr/param addr/plist addr!\n",
+                       __func__);
+               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");
+                       pr_err("%s: Failed to allocate cmd req descriptor\n",
+                               __func__);
                        ret = -ENOMEM;
                        goto error;
                }
@@ -90,10 +112,10 @@ static int te_create_free_cmd_list(struct tlk_device *dev)
        }
        return 0;
 error:
-       if (dev->req_param_buf)
-               kfree(dev->req_param_buf);
-       if (dev->param_bitmap)
-               kfree(dev->param_bitmap);
+       pr_err("%s: Error, returning %d\n", __func__, ret);
+       kfree(req_buf);
+       kfree(dev->param_bitmap);
+       kfree(dev->plist_bitmap);
        list_for_each_entry_safe(req_desc, tmp_req_desc,
                        &(dev->free_cmd_list), list)
                kfree(req_desc);
@@ -247,7 +269,7 @@ static int tlk_device_release(struct inode *inode, struct file *file)
 }
 
 static int copy_params_from_user(struct te_request *req,
-       struct te_operation *operation)
+       struct te_operation *operation, struct te_oper_param *caller_params)
 {
        struct te_oper_param *param_array;
        struct te_oper_param *user_param;
@@ -267,18 +289,22 @@ static int copy_params_from_user(struct te_request *req,
                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);
+                               "list_count: %d\n",
+                               i, user_param, operation->list_count);
                        return 1;
                }
                user_param = (struct te_oper_param *)(uintptr_t)
                        param_array[i].next_ptr_user;
        }
+
+       memcpy(caller_params, param_array,
+                       sizeof(struct te_oper_param) * operation->list_count);
+
        return 0;
 }
 
 static int copy_params_to_user(struct te_request *req,
-       struct te_operation *operation)
+       struct te_operation *operation, struct te_oper_param *caller_params)
 {
        struct te_oper_param *param_array;
        struct te_oper_param *user_param;
@@ -296,10 +322,25 @@ static int copy_params_to_user(struct te_request *req,
        user_param =
                (struct te_oper_param *)(uintptr_t)operation->list_head;
        for (i = 0; i < req->params_size; i++) {
+               /* clear flags */
+               param_array[i].type &= ~TE_PARAM_TYPE_ALL_FLAGS;
+
+               switch(param_array[i].type) {
+               /*
+                * Restore the memory base address as it can be overridden
+                * while sending it to secure world
+                */
+               case TE_PARAM_TYPE_MEM_RO:
+               case TE_PARAM_TYPE_MEM_RW:
+               case TE_PARAM_TYPE_PERSIST_MEM_RO:
+               case TE_PARAM_TYPE_PERSIST_MEM_RW:
+                       param_array[i].u.Mem.base = caller_params[i].u.Mem.base;
+               }
+
                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);
+                               user_param);
                        return 1;
                }
                user_param = (struct te_oper_param *)(uintptr_t)
@@ -315,6 +356,7 @@ static long te_handle_trustedapp_ioctl(struct file *file,
        union te_cmd cmd;
        struct te_operation *operation = NULL;
        struct te_oper_param *params = NULL;
+       struct te_oper_param *caller_params = NULL;
        struct te_request *request;
        void __user *ptr_user_answer = NULL;
        struct te_answer answer;
@@ -353,9 +395,19 @@ static long te_handle_trustedapp_ioctl(struct file *file,
                request->params = (uintptr_t)params;
                request->params_size = operation->list_count;
 
-               if (copy_params_from_user(request, operation)) {
+               if (operation->list_count > 0) {
+                       caller_params = kmalloc(sizeof(struct te_oper_param) *
+                                       operation->list_count, GFP_KERNEL);
+                       if (!caller_params) {
+                               pr_err("%s: failed to allocate caller params\n", __func__);
+                               err = -ENOMEM;
+                               goto error;
+                       }
+               }
+
+               if (copy_params_from_user(request, operation, caller_params)) {
                        err = -EFAULT;
-                       pr_info("failed to copy params from user\n");
+                       pr_err("%s: failed to copy params from user\n", __func__);
                        goto error;
                }
 
@@ -404,9 +456,19 @@ static long te_handle_trustedapp_ioctl(struct file *file,
                request->params = (uintptr_t)params;
                request->params_size = operation->list_count;
 
-               if (copy_params_from_user(request, operation)) {
+               if (operation->list_count > 0) {
+                       caller_params = kmalloc(sizeof(struct te_oper_param) *
+                                       operation->list_count, GFP_KERNEL);
+                       if (!caller_params) {
+                               pr_err("%s: failed to allocate caller params\n", __func__);
+                               err = -ENOMEM;
+                               goto error;
+                       }
+               }
+
+               if (copy_params_from_user(request, operation, caller_params)) {
                        err = -EFAULT;
-                       pr_info("failed to copy params from user\n");
+                       pr_info("%s: failed to copy params from user\n", __func__);
                        goto error;
                }
 
@@ -428,7 +490,7 @@ static long te_handle_trustedapp_ioctl(struct file *file,
                }
        }
        if (request->params && !err) {
-               if (copy_params_to_user(request, operation)) {
+               if (copy_params_to_user(request, operation, caller_params)) {
                        pr_err("Failed to copy return params\n");
                        err = -EFAULT;
                }
@@ -439,6 +501,7 @@ error:
                te_put_used_cmd_desc(dev, cmd_desc);
        if (params)
                te_put_free_params(dev, params, operation->list_count);
+       kfree(caller_params);
        return err;
 }
 
diff --git a/security/tlk_driver/ote_memory.c b/security/tlk_driver/ote_memory.c
new file mode 100644 (file)
index 0000000..dbacf5c
--- /dev/null
@@ -0,0 +1,345 @@
+/*
+ * Copyright (c) 2016 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.
+ */
+
+#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/sched.h>
+#include <linux/mm.h>
+#include <linux/pagemap.h>
+#include <linux/syscalls.h>
+#include <asm/smp_plat.h>
+
+#include "ote_protocol.h"
+
+#define PHYS_PAGELIST_ALIGNED_PA(ent)          ((ent) & 0xFFFFFFFFF000ULL)
+
+#ifndef CONFIG_ARM64
+#define PTE_RDONLY     PTE_AP2
+#endif
+
+extern uint64_t _tlk_get_mair(void);
+static uint64_t te_get_mair_attrs(void) { return _tlk_get_mair(); }
+static uint64_t te_get_pte_attrs(uint64_t pte, uint64_t mair)
+{
+       uint64_t pte_attrs, shareable;
+       uint32_t idx;
+       uint8_t attrs;
+
+       /* Bits 2:4 holds attributes index */
+       idx = (pte >> 2) & 0x7;
+       attrs = *((uint8_t *)&mair + idx);
+
+       /* Bits 8:9 holds shareability field */
+       shareable = (pte >> 8) & 0x3;
+
+       /*
+        * TLK expects that attributes are stored in the first byte of PTE.
+        * MSB holds inner(4 bits), outer(4 bits) and shareable (2 bits)
+        * attributes respectively.
+        */
+       pte_attrs = attrs;
+       pte_attrs = pte_attrs << 56;
+       pte_attrs |= (shareable << 54);
+       return pte_attrs;
+}
+
+static void te_put_free_plist(struct te_shmem_desc *shmem_desc)
+{
+       struct tlk_device *dev = &tlk_dev;
+       int idx, nbits;
+
+       idx = shmem_desc->plist_idx;
+       nbits = get_count_order(shmem_desc->nr_pages);
+
+       bitmap_release_region(dev->plist_bitmap, idx, nbits);
+}
+
+static void te_release_mem_buffer(struct te_shmem_desc *shmem_desc)
+{
+       uint32_t i;
+
+       list_del(&shmem_desc->list);
+       for (i = 0; i < shmem_desc->nr_pages; i++) {
+               if ((shmem_desc->type == TE_PARAM_TYPE_MEM_RW) ||
+                       (shmem_desc->type == TE_PARAM_TYPE_PERSIST_MEM_RW))
+                       set_page_dirty_lock(shmem_desc->pages[i]);
+               put_page(shmem_desc->pages[i]);
+       }
+
+       te_put_free_plist(shmem_desc);
+
+       kfree(shmem_desc->pages);
+       kfree(shmem_desc);
+}
+
+void te_release_mem_buffers(struct list_head *buflist)
+{
+       struct te_shmem_desc *shmem_desc, *tmp_shmem_desc;
+
+       list_for_each_entry_safe(shmem_desc, tmp_shmem_desc, buflist, list) {
+               te_release_mem_buffer(shmem_desc);
+       }
+}
+
+static int te_load_page_list(unsigned long start,
+                            unsigned int npages,
+                            struct page **pages,
+                            struct vm_area_struct **vmas)
+{
+       struct tlk_device *dev = &tlk_dev;
+       uint64_t *ptes;
+       uint64_t mair;
+       int i, idx, nbits;
+
+       nbits = get_count_order(npages);
+       idx = bitmap_find_free_region(dev->plist_bitmap,
+                               TE_PLIST_MAX, nbits);
+       if (idx < 0) {
+               pr_err("%s: ERROR: plist bitmap is full, order %d max size %lu\n",
+                                               __func__, nbits, TE_PLIST_MAX);
+               return -ENOMEM;
+       }
+
+       mair = te_get_mair_attrs();
+       ptes = dev->plist_addr + idx;
+
+       for (i = 0; i < npages; i++, start += PAGE_SIZE) {
+               uint64_t pte;
+#ifdef CONFIG_ARM64
+               /*
+                * Not performing this sanity check for 32 bit kernel as
+                * PTE_ATTRINDX* macros are not available in 32 bit headers
+                */
+               if ((vmas[i]->vm_page_prot & PTE_ATTRINDX_MASK) !=
+                       PTE_ATTRINDX(MT_NORMAL)) {
+                       pr_err("%s: unsupported memory type: %llx\n",
+                              __func__,
+                              vmas[i]->vm_page_prot & PTE_ATTRINDX_MASK);
+                       bitmap_release_region(dev->plist_bitmap, idx, nbits);
+                       return -EINVAL;
+               }
+#endif
+
+               /*
+                * Recreate the pte of the page - we can't access it
+                * safely here race-free.
+                */
+
+               pte = page_to_phys(pages[i]);
+               pte |= pgprot_val(vmas[i]->vm_page_prot);
+               if (vmas[i]->vm_flags & VM_WRITE)
+                       pte &= ~PTE_RDONLY;
+
+               ptes[i]  = PHYS_PAGELIST_ALIGNED_PA(pte);
+               ptes[i] |= (start & (PAGE_SIZE-1));
+               ptes[i] |= te_get_pte_attrs(pte, mair);
+       }
+       return idx;
+}
+
+static int te_pin_user_pages(struct te_oper_param *param,
+                            struct page ***pages,
+                            struct vm_area_struct **vmas,
+                            uint32_t nr_pages, uint32_t *plist_idx)
+{
+       struct tlk_device *dev = &tlk_dev;
+       int idx, ret = 0;
+       int nr_pinned = 0;
+       unsigned long start;
+       uint32_t length;
+       bool writable;
+       int i;
+
+       start = (unsigned long)param->u.Mem.base,
+       length = param->u.Mem.len;
+
+       *pages = kzalloc(nr_pages * sizeof(struct page **), GFP_KERNEL);
+       if (!*pages) {
+               pr_err("%s: Error allocating %d pages!\n",
+                      __func__, (int)nr_pages);
+               return OTE_ERROR_OUT_OF_MEMORY;
+       }
+
+       writable = (param->type == TE_PARAM_TYPE_MEM_RW ||
+                       param->type == TE_PARAM_TYPE_PERSIST_MEM_RW);
+
+       down_read(&current->mm->mmap_sem);
+       /*
+        * vmas are valid only when mmap_sem is held. Hence holding the lock
+        * across get user pages and process returned vmas.
+        */
+       nr_pinned = get_user_pages(current, current->mm, start, nr_pages,
+                               writable, 0, *pages, vmas);
+       if (nr_pinned != nr_pages) {
+               pr_err("%s: Error %d in get_user_pages for buffer 0x%lx\n",
+                               __func__, nr_pinned, start);
+               ret = OTE_ERROR_GENERIC;
+               goto error;
+       }
+
+       idx = te_load_page_list(start, nr_pages, *pages, vmas);
+       if (idx < 0) {
+               pr_err("%s: Error loading page list, idx = %d!\n",
+                      __func__, idx);
+               ret = OTE_ERROR_OUT_OF_MEMORY;
+               goto error;
+       }
+       up_read(&current->mm->mmap_sem);
+
+       /* stores the index from the beginning of shared buffer */
+       param->u.Mem.base = (uintptr_t)(dev->plist_addr + idx) -
+                                               (uintptr_t)dev->req_addr;
+
+       param->type |= TE_PARAM_TYPE_FLAGS_PHYS_LIST;
+
+       *plist_idx = idx;
+
+       return OTE_SUCCESS;
+error:
+       up_read(&current->mm->mmap_sem);
+       for (i = 0; i < nr_pinned; i++)
+               put_page((*pages)[i]);
+       kfree(*pages);
+       return ret;
+}
+
+static int te_prep_mem_buffer(struct te_oper_param *param,
+                               struct te_session *session)
+{
+       struct page **pages = NULL;
+       struct te_shmem_desc *shmem_desc = NULL;
+       int ret = 0, nr_pages = 0;
+       uint32_t buf_type;
+       unsigned long start;
+       uint32_t length;
+       struct vm_area_struct **vmas;
+       uint32_t plist_idx = 0;
+
+       /* allocate new shmem descriptor */
+       shmem_desc = kzalloc(sizeof(struct te_shmem_desc), GFP_KERNEL);
+       if (!shmem_desc) {
+               pr_err("%s: out of memory for shmem_desc!\n", __func__);
+               ret = OTE_ERROR_OUT_OF_MEMORY;
+               goto error;
+       }
+
+       /* Need this for vma alloc prior to pin_user_pages */
+       start = (unsigned long)param->u.Mem.base,
+       length = param->u.Mem.len;
+       nr_pages = (((uintptr_t)start & (PAGE_SIZE - 1)) +
+                       (length + PAGE_SIZE - 1)) >> PAGE_SHIFT;
+
+       vmas = kzalloc(sizeof(*vmas) * nr_pages, GFP_KERNEL);
+       if (!vmas) {
+               pr_err("%s: out of memory for vmas! (%d pages)\n",
+                      __func__, nr_pages);
+               ret = OTE_ERROR_OUT_OF_MEMORY;
+               goto error_alloc_vmas;
+       }
+
+       /* pin pages */
+       ret = te_pin_user_pages(param, &pages, vmas, nr_pages, &plist_idx);
+       if (ret != OTE_SUCCESS) {
+               pr_err("%s: te_pin_user_pages failed (%d)\n", __func__, ret);
+               goto error_pin_pages;
+       }
+       kfree(vmas);
+
+       /* initialize shmem descriptor */
+       INIT_LIST_HEAD(&(shmem_desc->list));
+       shmem_desc->plist_idx = plist_idx;
+       shmem_desc->size = param->u.Mem.len;
+       shmem_desc->nr_pages = nr_pages;
+       shmem_desc->pages = pages;
+
+       /* just type (no flags) */
+       buf_type = param->type & ~TE_PARAM_TYPE_ALL_FLAGS;
+
+       /* add shmem descriptor to proper list */
+       if ((buf_type == TE_PARAM_TYPE_MEM_RO) ||
+               (buf_type == TE_PARAM_TYPE_MEM_RW))
+               list_add_tail(&shmem_desc->list, &session->temp_shmem_list);
+       else {
+               list_add_tail(&shmem_desc->list,
+                       &session->inactive_persist_shmem_list);
+       }
+
+       return OTE_SUCCESS;
+
+error_pin_pages:
+       kfree(vmas);
+error_alloc_vmas:
+       kfree(shmem_desc);
+error:
+       return ret;
+}
+
+int te_prep_mem_buffers(struct te_request *request,
+                       struct te_session *session)
+{
+       uint32_t i;
+       int ret = OTE_SUCCESS;
+       struct te_oper_param *params;
+
+       params = (struct te_oper_param *)(uintptr_t)request->params;
+       for (i = 0; i < request->params_size; i++) {
+               switch (params[i].type) {
+               case TE_PARAM_TYPE_NONE:
+               case TE_PARAM_TYPE_INT_RO:
+               case TE_PARAM_TYPE_INT_RW:
+                       break;
+               case TE_PARAM_TYPE_MEM_RO:
+               case TE_PARAM_TYPE_MEM_RW:
+               case TE_PARAM_TYPE_PERSIST_MEM_RO:
+               case TE_PARAM_TYPE_PERSIST_MEM_RW:
+                       ret = te_prep_mem_buffer(params + i, session);
+                       if (ret != OTE_SUCCESS) {
+                               pr_err("%s failed with err (%d)\n",
+                                       __func__, ret);
+                               ret = OTE_ERROR_BAD_PARAMETERS;
+                               goto error;
+                       }
+                       break;
+               default:
+                       pr_err("%s: OTE_ERROR_BAD_PARAMETERS\n", __func__);
+                       ret = OTE_ERROR_BAD_PARAMETERS;
+                       break;
+               }
+       }
+       return OTE_SUCCESS;
+error:
+       /* release mem buffers */
+       te_release_mem_buffers(&session->temp_shmem_list);
+       te_release_mem_buffers(&session->inactive_persist_shmem_list);
+       return ret;
+}
+
+void te_activate_persist_mem_buffers(struct te_session *session)
+{
+       struct te_shmem_desc *shmem_desc, *tmp_shmem_desc;
+
+       /* move persist mem buffers from inactive list to active list */
+       list_for_each_entry_safe(shmem_desc, tmp_shmem_desc,
+               &session->inactive_persist_shmem_list, list) {
+               list_move_tail(&shmem_desc->list, &session->persist_shmem_list);
+       }
+}
index 6c4f61d..9416805 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013-2015 NVIDIA Corporation. All rights reserved.
+ * Copyright (c) 2013-2016 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
 #define TE_IOCTL_MIN_NR        _IOC_NR(TE_IOCTL_OPEN_CLIENT_SESSION)
 #define TE_IOCTL_MAX_NR        _IOC_NR(TE_IOCTL_SS_CMD)
 
-/* shared buffer is 2 pages: 1st are requests, 2nd are params */
+/*
+ * shared buffer is 4 pages: 1st are requests, 2nd are params and the
+ * remaining 2 are for params physical pages list
+ */
 #define TE_CMD_DESC_MAX        (PAGE_SIZE / sizeof(struct te_request))
 #define TE_PARAM_MAX   (PAGE_SIZE / sizeof(struct te_oper_param))
+#define TE_PLIST_MAX   ((2 * PAGE_SIZE) / sizeof(uint64_t))
 
+#define MAX_BUFFER_MAP_SIZE    (TE_PLIST_MAX * PAGE_SIZE)
 #define MAX_EXT_SMC_ARGS       12
 
 extern struct mutex smc_lock;
@@ -71,10 +76,11 @@ struct tlk_device {
        dma_addr_t req_addr_phys;
        struct te_oper_param *param_addr;
        dma_addr_t param_addr_phys;
-
-       char *req_param_buf;
+       uint64_t *plist_addr;
+       dma_addr_t plist_addr_phys;
 
        unsigned long *param_bitmap;
+       unsigned long *plist_bitmap;
 
        struct list_head used_cmd_list;
        struct list_head free_cmd_list;
@@ -88,7 +94,7 @@ struct te_cmd_req_desc {
 struct te_shmem_desc {
        struct list_head list;
        uint32_t type;
-       void *buffer;
+       uint32_t plist_idx;
        size_t size;
        struct page **pages;
        unsigned int nr_pages;
@@ -145,6 +151,8 @@ enum {
        TE_PARAM_TYPE_MEM_RW            = 0x4,
        TE_PARAM_TYPE_PERSIST_MEM_RO    = 0x100,
        TE_PARAM_TYPE_PERSIST_MEM_RW    = 0x101,
+       TE_PARAM_TYPE_FLAGS_PHYS_LIST   = 0x1000,
+       TE_PARAM_TYPE_ALL_FLAGS         = TE_PARAM_TYPE_FLAGS_PHYS_LIST
 };
 
 enum {
@@ -252,9 +260,13 @@ enum ta_event_id {
 };
 
 int te_handle_ss_ioctl(struct file *file, unsigned int ioctl_num,
-               unsigned long ioctl_param);
+                       unsigned long ioctl_param);
 void ote_print_logs(void);
 int tlk_ss_op(void);
 int ote_property_is_disabled(const char *str);
+int te_prep_mem_buffers(struct te_request *request,
+                       struct te_session *session);
+void te_release_mem_buffers(struct list_head *buflist);
+void te_activate_persist_mem_buffers(struct te_session *session);
 
 #endif