security: nv_tee_driver: file storage apis
Varun Wadekar [Thu, 18 Apr 2013 07:02:05 +0000 (12:02 +0530)]
- Add a new FS ioctl handler to service ioctls from the storage
daemon.
- Add read/write handler to help the secure world to get its
data across to the storage daemon.
- A read request with NULL input params will return the size of
the file. The client then can allocate memory accordingly and proceed
with the actual read.

The general sequence of events from the daemon would be:
- TEE_IOCTL_FILE_NEW_REQ to get the file name, size and type (read/write)
- TEE_IOCTL_FILE_FILL_BUF to get data in case of writes
- TEE_IOCTL_FILE_REQ_COMPLETE to signal completion to the secure world

Change-Id: I52450af8d79164338773c1575417a863978de3d6
Signed-off-by: Varun Wadekar <vwadekar@nvidia.com>
Reviewed-on: http://git-master/r/220982
GVS: Gerrit_Virtual_Submit

security/nv_tee_driver/Makefile
security/nv_tee_driver/tee_client_api.h
security/nv_tee_driver/tee_comms.c
security/nv_tee_driver/tee_device.c
security/nv_tee_driver/tee_fs.c [new file with mode: 0644]
security/nv_tee_driver/tee_protocol.h

index 96761c6..00b65f1 100644 (file)
 plus_sec := $(call as-instr,.arch_extension sec,+sec)
 AFLAGS_tee_irq.o :=-Wa,-march=armv7-a$(plus_sec)
 CFLAGS_tee_comms.o :=$(call as-instr,.arch_extension sec,-DREQUIRES_SEC=1)
+CFLAGS_tee_fs.o :=$(call as-instr,.arch_extension sec,-DREQUIRES_SEC=1)
 
 nv_tee_driver-objs += tee_device.o
 nv_tee_driver-objs += tee_comms.o
+nv_tee_driver-objs += tee_fs.o
 nv_tee_driver-objs += tee_irq.o
 
 obj-$(CONFIG_TRUSTED_LITTLE_KERNEL) += nv_tee_driver.o
index d7b00e1..0f0eed2 100644 (file)
@@ -88,4 +88,28 @@ struct TEEC_UUID {
        uint8_t clock_seq_and_node[8];
 };
 
+#define TEEC_MAX_FILE_NAME_LEN         64
+
+typedef enum {
+       TEEC_FILE_REQ_READ = 0,
+       TEEC_FILE_REQ_WRITE = 1,
+       TEEC_FILE_REQ_DELETE = 2,
+       TEEC_FILE_REQ_SIZE = 3,
+} TEEC_FileReqType;
+
+typedef struct {
+       char name[TEEC_MAX_FILE_NAME_LEN];
+       TEEC_FileReqType type;
+       void *user_data_buf;
+       void *kern_data_buf;
+       unsigned long data_len;
+       unsigned long result;
+       int error;
+} TEEC_FileReq;
+
+struct tee_file_req_node {
+       struct list_head node;
+       TEEC_FileReq *req;
+};
+
 #endif
index e60f1fd..8101389 100644 (file)
@@ -273,6 +273,7 @@ static void tee_unpin_temp_buffers(struct TEEC_Operation *oper,
 static void do_smc(struct tee_request *request)
 {
        phys_addr_t smc_args = virt_to_phys(request);
+       unsigned long regs[15];
 
 #ifdef CONFIG_SMP
        long ret;
@@ -287,15 +288,17 @@ static void do_smc(struct tee_request *request)
 #endif
 
        asm volatile (
-               "stmdb  sp!, {r4-r12}\n"
-               "mov    r0,  %0\n"
-               "mov    r1,  %1\n"
+               "mov    r0, %0          \n"
+               "stmia  r0, {r4-r12}    \n"
+               "mov    r0, %1          \n"
+               "mov    r1, %2          \n"
 #ifdef REQUIRES_SEC
-               ".arch_extension sec\n"
+               ".arch_extension sec    \n"
 #endif
-               "smc    #0\n"
-               "ldmia  sp!, {r4-r12}\n"
-               : : "r" (request->type), "r" (smc_args)
+               "smc    #0              \n"
+               "mov    r1, %0          \n"
+               "ldmia  r1, {r4-r12}    \n"
+               : : "r" (regs), "r" (request->type), "r" (smc_args)
                : "r0", "r1"
        );
 
index 3015a12..57a5f5f 100644 (file)
@@ -172,7 +172,7 @@ static int tee_device_release(struct inode *inode, struct file *file)
        return 0;
 }
 
-static long tee_device_ioctl(struct file *file, unsigned int ioctl_num,
+static long tee_handle_trustedapp_ioctl(struct file *file, unsigned int ioctl_num,
        unsigned long ioctl_param)
 {
        long err = 0;
@@ -301,6 +301,36 @@ error:
        return err;
 }
 
+static long tee_device_ioctl(struct file *file, unsigned int ioctl_num,
+       unsigned long ioctl_param)
+{
+       int err;
+
+       switch (ioctl_num) {
+       case TEE_IOCTL_OPEN_CLIENT_SESSION:
+       case TEE_IOCTL_CLOSE_CLIENT_SESSION:
+       case TEE_IOCTL_REGISTER_MEMORY:
+       case TEE_IOCTL_RELEASE_SHARED_MEM:
+       case TEE_IOCTL_INVOKE_COMMAND:
+               err = tee_handle_trustedapp_ioctl(file, ioctl_num, ioctl_param);
+               break;
+
+       case TEE_IOCTL_FILE_NEW_REQ:
+       case TEE_IOCTL_FILE_FILL_BUF:
+       case TEE_IOCTL_FILE_REQ_COMPLETE:
+               err = tee_handle_fs_ioctl(file, ioctl_num, ioctl_param);
+               break;
+
+       default:
+               pr_err("%s: Invalid IOCTL (0x%lx) 0x%lx, %d\n", __func__,
+                       ioctl_num, TEE_IOCTL_FILE_NEW_REQ,
+                       sizeof(TEEC_FileReq));
+               err = -EINVAL;
+       }
+
+       return err;
+}
+
 /*
  * tee_driver function definitions.
  */
diff --git a/security/nv_tee_driver/tee_fs.c b/security/nv_tee_driver/tee_fs.c
new file mode 100644 (file)
index 0000000..192bc2d
--- /dev/null
@@ -0,0 +1,235 @@
+/*
+ * Copyright (c) 2013, NVIDIA Corporation.
+ *
+ * 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/slab.h>
+#include <linux/syscalls.h>
+#include <linux/list.h>
+#include <linux/completion.h>
+#include <linux/workqueue.h>
+
+#include <asm/uaccess.h>
+
+#include "tee_protocol.h"
+
+#define TEE_SHMEM_FNAME_SZ     SZ_64
+#define TEE_SHMEM_DATA_SZ      SZ_128K
+
+struct tee_shmem {
+       char    file_name[TEE_SHMEM_FNAME_SZ];
+       char    file_data[TEE_SHMEM_DATA_SZ];
+};
+
+struct list_head req_list;
+DECLARE_COMPLETION(req_ready);
+DECLARE_COMPLETION(req_complete);
+static unsigned long secure_error;
+
+static void indicate_complete(unsigned long ret)
+{
+       asm volatile (
+               "mov    r1, %0                  \n"
+               "movw   r0, #0x1FFF             \n"
+               "movt   r0, #0xFFFF             \n"
+#ifdef REQUIRES_SEC
+               ".arch_extension sec            \n"
+#endif
+               "smc    #0                      \n"
+               : : "r" (ret)
+               : "r0", "r1"
+       );
+}
+
+int tee_handle_fs_ioctl(struct file *file, unsigned int ioctl_num,
+       unsigned long ioctl_param)
+{
+       TEEC_FileReq new_req, *ptr_user_req = NULL;
+       struct tee_file_req_node *req_node;
+
+       switch (ioctl_num) {
+       case TEE_IOCTL_FILE_NEW_REQ: /* new request */
+
+               ptr_user_req = (TEEC_FileReq *)ioctl_param;
+
+               /* wait for a new request */
+               wait_for_completion(&req_ready);
+
+               /* dequeue new request from the secure world */
+               req_node = list_first_entry(&req_list, struct tee_file_req_node,
+                               node);
+
+               /* populate request for the non-secure client */
+               if (req_node) {
+                       if (copy_to_user(ptr_user_req, req_node->req,
+                               sizeof(TEEC_FileReq))) {
+                               pr_err("copy_to_user failed for new request\n");
+                               return -EFAULT;
+                       }
+
+                       list_del(&req_node->node);
+                       kfree(req_node);
+               } else {
+                       pr_err("no request available\n");
+                       return -ENOMEM;
+               }
+
+               break;
+
+       case TEE_IOCTL_FILE_FILL_BUF: /* pass data to be written to the file */
+
+               if (copy_from_user(&new_req, (void __user *)ioctl_param,
+                       sizeof(TEEC_FileReq))) {
+                       pr_err("copy_from_user failed for request\n");
+                       return -EFAULT;
+               }
+
+               if (new_req.type != TEEC_FILE_REQ_WRITE)
+                       return -EINVAL;
+
+               if (!new_req.kern_data_buf || !new_req.user_data_buf)
+                       return -EINVAL;
+
+               if (copy_to_user(new_req.user_data_buf, new_req.kern_data_buf,
+                       new_req.data_len)) {
+                       pr_err("copy_to_user failed for fill buffer\n");
+                       return -EFAULT;
+               }
+               break;
+
+       case TEE_IOCTL_FILE_REQ_COMPLETE: /* request complete */
+
+               if (copy_from_user(&new_req, (void __user *)ioctl_param,
+                       sizeof(TEEC_FileReq))) {
+                       pr_err("copy_from_user failed for request\n");
+                       return -EFAULT;
+               }
+
+               if (new_req.type == TEEC_FILE_REQ_READ && !new_req.error) {
+                       if (copy_from_user(new_req.kern_data_buf,
+                               (void __user *)new_req.user_data_buf,
+                               new_req.data_len)) {
+                               pr_err("copy_from_user failed for request\n");
+                               return -EFAULT;
+                       }
+               }
+
+               /* get error code */
+               secure_error = (new_req.error) ? TEEC_ERROR_NO_DATA : new_req.result;
+
+               /* signal the producer */
+               complete(&req_complete);
+               break;
+       }
+
+       return 0;
+}
+
+static void _tee_fs_file_operation(const char *name, void *buf, int len,
+               TEEC_FileReqType type)
+{
+       TEEC_FileReq *new_req;
+       struct tee_file_req_node *req_node;
+
+       BUG_ON(!name);
+
+       if (type == TEEC_FILE_REQ_READ || type == TEEC_FILE_REQ_WRITE)
+               BUG_ON(!buf);
+
+       /* allocate TEEC_FileReq structure */
+       new_req = kzalloc(sizeof(TEEC_FileReq), GFP_KERNEL);
+       BUG_ON(!new_req);
+
+       /* prepare a new request */
+       strncpy(new_req->name, name, strlen(name));
+       new_req->type = type;
+       new_req->data_len = len;
+       new_req->result = 0;
+       new_req->kern_data_buf = buf;
+       new_req->error = 0;
+
+       req_node = kzalloc(sizeof(struct tee_file_req_node), GFP_KERNEL);
+       BUG_ON(!req_node);
+
+       req_node->req = new_req;
+       INIT_LIST_HEAD(&req_node->node);
+
+       /* add it to the pending queue and signal the consumer */
+       list_add_tail(&req_list, &req_node->node);
+       complete(&req_ready);
+
+       /* wait for the consumer's signal */
+       wait_for_completion(&req_complete);
+
+       kfree(new_req);
+
+       /* signal completion to the secure world */
+       indicate_complete(secure_error);
+}
+
+void nv_tee_fread(const char *name, void *buf, int len)
+{
+       if (!buf)
+               _tee_fs_file_operation(name, buf, len, TEEC_FILE_REQ_SIZE);
+       else
+               _tee_fs_file_operation(name, buf, len, TEEC_FILE_REQ_READ);
+}
+
+void nv_tee_fwrite(const char *name, void *buf, int len)
+{
+       _tee_fs_file_operation(name, buf, len, TEEC_FILE_REQ_WRITE);
+}
+
+void nv_tee_fdelete(const char *name)
+{
+       _tee_fs_file_operation(name, NULL, 0, TEEC_FILE_REQ_DELETE);
+}
+
+static int __init nv_tee_fs_register_handlers(void)
+{
+       struct tee_shmem *shmem_ptr;
+
+       shmem_ptr = kzalloc(sizeof(struct tee_shmem), GFP_KERNEL);
+       if (!shmem_ptr) {
+               pr_err("%s: no memory available for fs operations\n", __func__);
+               return -ENOMEM;
+       }
+
+       INIT_LIST_HEAD(&req_list);
+       init_completion(&req_ready);
+       init_completion(&req_complete);
+
+       asm volatile (
+               "movw   r0, #0x1FF2             \n"
+               "movt   r0, #0xFFFF             \n"
+               "mov    r1, %0                  \n"
+               "mov    r2, %1                  \n"
+               "mov    r3, %2                  \n"
+               "mov    r4, %3                  \n"
+               "mov    r5, %4                  \n"
+#ifdef REQUIRES_SEC
+               ".arch_extension sec            \n"
+#endif
+               "smc    #0                      \n"
+               : : "r" (nv_tee_fread), "r" (nv_tee_fwrite), "r" (nv_tee_fdelete),
+                   "r" (shmem_ptr->file_name), "r" (shmem_ptr->file_data)
+               : "r0", "r1", "r2", "r3", "r4", "r13", "r14"
+       );
+
+       return 0;
+}
+
+arch_initcall(nv_tee_fs_register_handlers);
index c953efe..2c6e71e 100644 (file)
        _IOWR(TEE_IOCTL_MAGIC_NUMBER, 0x14, union tee_cmd)
 #define TEE_IOCTL_REQ_CANCELLATION \
        _IOR(TEE_IOCTL_MAGIC_NUMBER, 0x15, union tee_cmd)
+#define TEE_IOCTL_FILE_NEW_REQ \
+       _IOR(TEE_IOCTL_MAGIC_NUMBER, 0x16, TEEC_FileReq)
+#define TEE_IOCTL_FILE_FILL_BUF \
+       _IOR(TEE_IOCTL_MAGIC_NUMBER, 0x17, TEEC_FileReq)
+#define TEE_IOCTL_FILE_REQ_COMPLETE \
+       _IOWR(TEE_IOCTL_MAGIC_NUMBER, 0x18, TEEC_FileReq)
 
 #define TEE_IOCTL_MIN_NR       _IOC_NR(TEE_IOCTL_OPEN_CLIENT_SESSION)
-#define TEE_IOCTL_MAX_NR       _IOC_NR(TEE_IOCTL_REQ_CANCELLATION)
+#define TEE_IOCTL_MAX_NR       _IOC_NR(TEE_IOCTL_FILE_REQ_COMPLETE)
 
 #define NV_CMD_DESC_MAX        120
 
@@ -204,4 +210,7 @@ void tee_invoke_command(struct tee_invokecmd *cmd,
 void tee_unregister_memory(void *buffer,
        struct nv_tee_context *context);
 
+int tee_handle_fs_ioctl(struct file * file, unsigned int ioctl_num,
+       unsigned long ioctl_param);
+
 #endif