/* * Copyright (c) 2014, STMicroelectronics International N.V. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. * * 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. */ #include #include #include #include #include #include #include #include #include #include #include "tee_shm.h" #include "tee_core.h" #include "tee_supp_com.h" #define TEE_RPC_BUFFER 0x00000001 #define TEE_RPC_VALUE 0x00000002 enum teec_rpc_result tee_supp_cmd(struct tee *tee, uint32_t id, void *data, size_t datalen) { struct tee_rpc *rpc = tee->rpc; enum teec_rpc_result res = TEEC_RPC_FAIL; size_t size; struct task_struct *task = current; dev_dbg(tee->dev, "> tgid:[%d] id:[0x%08x]\n", task->tgid, id); if (atomic_read(&rpc->used) == 0) { dev_err(tee->dev, "%s: ERROR Supplicant application NOT ready\n" , __func__); goto out; } switch (id) { case TEE_RPC_ICMD_ALLOCATE: { struct tee_rpc_alloc *alloc; struct tee_shm *shmint; alloc = (struct tee_rpc_alloc *)data; size = alloc->size; memset(alloc, 0, sizeof(struct tee_rpc_alloc)); shmint = tee_shm_alloc_from_rpc(tee, size); if (IS_ERR_OR_NULL(shmint)) break; alloc->size = size; alloc->data = (void *)(unsigned long)shmint->paddr; alloc->shm = shmint; res = TEEC_RPC_OK; break; } case TEE_RPC_ICMD_FREE: { struct tee_rpc_free *free; free = (struct tee_rpc_free *)data; tee_shm_free_from_rpc(free->shm); res = TEEC_RPC_OK; break; } case TEE_RPC_ICMD_INVOKE: { if (sizeof(rpc->commToUser) < datalen) break; /* * Other threads blocks here until we've copied our * answer from the supplicant */ mutex_lock(&rpc->thrd_mutex); mutex_lock(&rpc->outsync); memcpy(&rpc->commToUser, data, datalen); mutex_unlock(&rpc->outsync); dev_dbg(tee->dev, "Supplicant Cmd: %x. Give hand to supplicant\n", rpc->commToUser.cmd); up(&rpc->datatouser); down(&rpc->datafromuser); dev_dbg(tee->dev, "Supplicant Cmd: %x. Give hand to fw\n", rpc->commToUser.cmd); mutex_lock(&rpc->insync); memcpy(data, &rpc->commFromUser, datalen); mutex_unlock(&rpc->insync); mutex_unlock(&rpc->thrd_mutex); res = TEEC_RPC_OK; break; } default: /* not supported */ break; } out: dev_dbg(tee->dev, "< res: [%d]\n", res); return res; } EXPORT_SYMBOL(tee_supp_cmd); ssize_t tee_supp_read(struct file *filp, char __user *buffer, size_t length, loff_t *offset) { struct tee_context *ctx = (struct tee_context *)(filp->private_data); struct tee *tee; struct tee_rpc *rpc; struct task_struct *task = current; int ret; BUG_ON(!ctx); tee = ctx->tee; BUG_ON(!tee); BUG_ON(!tee->dev); BUG_ON(!tee->rpc); dev_dbg(tee->dev, "> ctx %p\n", ctx); rpc = tee->rpc; if (atomic_read(&rpc->used) == 0) { dev_err(tee->dev, "%s: ERROR Supplicant application NOT ready\n" , __func__); ret = -EPERM; goto out; } if (down_interruptible(&rpc->datatouser)) return -ERESTARTSYS; dev_dbg(tee->dev, "> tgid:[%d]\n", task->tgid); mutex_lock(&rpc->outsync); ret = sizeof(rpc->commToUser) - sizeof(rpc->commToUser.cmds) + sizeof(rpc->commToUser.cmds[0]) * rpc->commToUser.nbr_bf; if (length < ret) { ret = -EINVAL; } else { if (copy_to_user(buffer, &rpc->commToUser, ret)) { dev_err(tee->dev, "[%s] error, copy_to_user failed!\n", __func__); ret = -EINVAL; } } mutex_unlock(&rpc->outsync); out: dev_dbg(tee->dev, "< [%d]\n", ret); return ret; } ssize_t tee_supp_write(struct file *filp, const char __user *buffer, size_t length, loff_t *offset) { struct tee_context *ctx = (struct tee_context *)(filp->private_data); struct tee *tee; struct tee_rpc *rpc; struct task_struct *task = current; int ret = 0; BUG_ON(!ctx); BUG_ON(!ctx->tee); BUG_ON(!ctx->tee->rpc); tee = ctx->tee; rpc = tee->rpc; dev_dbg(tee->dev, "> tgid:[%d]\n", task->tgid); if (atomic_read(&rpc->used) == 0) { dev_err(tee->dev, "%s: ERROR Supplicant application NOT ready\n" , __func__); goto out; } if (length > 0 && length < sizeof(rpc->commFromUser)) { uint32_t i; mutex_lock(&rpc->insync); if (copy_from_user(&rpc->commFromUser, buffer, length)) { dev_err(tee->dev, "%s: ERROR, tee_session copy_from_user failed\n", __func__); mutex_unlock(&rpc->insync); ret = -EINVAL; goto out; } /* Translate virtual address of caller into physical address */ for (i = 0; i < rpc->commFromUser.nbr_bf; i++) { if (rpc->commFromUser.cmds[i].type == TEE_RPC_BUFFER && rpc->commFromUser.cmds[i].buffer) { struct vm_area_struct *vma = find_vma(current->mm, (unsigned long)rpc-> commFromUser.cmds[i].buffer); if (vma != NULL) { struct tee_shm *shm = vma->vm_private_data; BUG_ON(!shm); dev_dbg(tee->dev, "%d gid2pa(0x%p => %x)\n", i, rpc->commFromUser.cmds[i]. buffer, (unsigned int)shm->paddr); rpc->commFromUser.cmds[i].buffer = (void *)(unsigned long)shm->paddr; } else dev_dbg(tee->dev, " gid2pa(0x%p => NULL\n)", rpc->commFromUser.cmds[i]. buffer); } } mutex_unlock(&rpc->insync); up(&rpc->datafromuser); ret = length; } out: dev_dbg(tee->dev, "< [%d]\n", ret); return ret; } int tee_supp_init(struct tee *tee) { struct tee_rpc *rpc = devm_kzalloc(tee->dev, sizeof(struct tee_rpc), GFP_KERNEL); if (!rpc) { dev_err(tee->dev, "%s: can't allocate tee_rpc structure\n", __func__); return -ENOMEM; } sema_init(&rpc->datafromuser, 0); sema_init(&rpc->datatouser, 0); mutex_init(&rpc->thrd_mutex); mutex_init(&rpc->outsync); mutex_init(&rpc->insync); atomic_set(&rpc->used, 0); tee->rpc = rpc; return 0; } void tee_supp_deinit(struct tee *tee) { devm_kfree(tee->dev, tee->rpc); tee->rpc = NULL; }