diff options
author | Cedric Chaumont <cedric.chaumont@st.com> | 2014-10-06 09:36:38 +0200 |
---|---|---|
committer | Pascal Brand <pascal.brand@st.com> | 2015-02-06 16:41:39 +0100 |
commit | 724298b6e425d9ce6daae3131bb4b1029190aa2a (patch) | |
tree | f5937b0a2399b8f331f2727f1bf95330335c7706 | |
parent | 9f504dbd39efb84805d1abe81d3844cd46954f67 (diff) |
Linux driver refactoring
New architecture of the optee linux driver split the driver in 2 parts:
- a generic driver, called optee
- a specific backend. In this implementation, a single backend is
provided for TrustZone. The name of the module is optee_armtz
In order to load the linux driver, the following should be done:
- modprobe optee_armtz
This loads the specific backend optee_armtz.ko, linked
with the generic module optee.ko
Device /dev/opteearmtz00 is instanciated
- tee-supplicant opteearmtz00
Fix #7
Reviewed-by: Joakim Bech <joakim.bech@linaro.org>
Tested-by: Pascal Brand <pascal.brand@linaro.org> (STM platform)
Tested-by: Pascal Brand <pascal.brand@linaro.org> (QEMU)
Tested-by: Cedric Chaumont <cedric.chaumont@linaro.org> (FVP)
Tested-by: Cedric Chaumont <cedric.chaumont@linaro.org> (Juno)
Signed-off-by: Cedric Chaumont <cedric.chaumont@st.com>
53 files changed, 5565 insertions, 5289 deletions
@@ -11,3 +11,6 @@ Debug .*.swp .project .settings +.optee_armtz.ko.cmd +optee_armtz.ko +optee_armtz.mod.c @@ -1,47 +1,10 @@ -CFG_TEE_DRV_DEBUGFS?=0 - -ccflags-y+=-Werror -ccflags-y+=-I$(M)/include/linux -iquote$(M)/generic -ccflags-y+=-I$(src)/include - -ccflags-y+=-DCFG_TEE_DRV_DEBUGFS=${CFG_TEE_DRV_DEBUGFS} - -obj-m += optee.o - -optee-objs:= \ - generic/tee_service.o \ - generic/tee_driver.o \ - generic/tee_kernel_api.o \ - generic/tee_supp_com.o \ - generic/tee_mem.o \ - generic/tee_op.o \ - generic/tee_mutex_wait.o - -ifeq ($(ARCH),arm) -ccflags-y+=-iquote$(M)/core/armv7 -optee-objs += core/armv7/tee_tz.o -optee-objs += core/armv7/stm-smc.o -# "smc" assembly intruction requires dedicated "armv7 secure extension" -secext := $(call as-instr,.arch_extension sec,+sec) -AFLAGS_stm-smc.o := -Wa,-march=armv7-a$(secext) -endif - -ifeq ($(ARCH),arm64) -ccflags-y+=-iquote$(M)/core/arm64 -optee-objs += core/arm64/tee_tz.o -optee-objs += core/arm64/smc.o -optee-objs += core/arm64/handle.o -endif - -ifeq ($(CFG_TEE_DRV_DEBUGFS),1) - -ifeq ($(ARCH),arm) -optee-objs += core/armv7/tee_tz_debug.o -endif - -ifeq ($(ARCH),arm64) -optee-objs += core/arm64/tee_tz_debug.o -endif - -optee-objs += generic/tee_debug.o -endif # CFG_TEE_DRV_DEBUGFS +# +# Copyright (C) STMicroelectronics 2014. All rights reserved. +# +# This code is STMicroelectronics proprietary and confidential. +# Any use of the code for whatever purpose is subject to +# specific written permission of STMicroelectronics SA. +# + +obj-y += core/ +obj-y += armtz/ diff --git a/armtz/Makefile b/armtz/Makefile new file mode 100644 index 0000000..af2b10c --- /dev/null +++ b/armtz/Makefile @@ -0,0 +1,40 @@ + +######################################################################### +# Set Internal Variables # +# May be modified to match your setup # +######################################################################### +CFG_TEE_DRV_DEBUGFS?=0 +CFG_TEE_CORE_LOG_LEVEL?=2 +CFG_TEE_TA_LOG_LEVEL?=2 + +ccflags-y+=-Werror +ccflags-y+=-I$(M)/include/arm_common +ccflags-y+=-I$(M)/include/linux +ccflags-y+=-I$(M)/include +ccflags-y+=-I$(M)/core + +ccflags-y+=-DCFG_TEE_DRV_DEBUGFS=${CFG_TEE_DRV_DEBUGFS} +ccflags-y+=-DCFG_TEE_CORE_LOG_LEVEL=${CFG_TEE_CORE_LOG_LEVEL} +ccflags-y+=-DCFG_TEE_TA_LOG_LEVEL=${CFG_TEE_TA_LOG_LEVEL} + +obj-m += optee_armtz.o + +optee_armtz-objs:= \ + tee_tz_drv.o \ + tee_mem.o \ + handle.o + + +ifeq ($(CONFIG_ARM),y) +# "smc" assembly intruction requires dedicated "armv7 secure extension" +secext := $(call as-instr,.arch_extension sec,+sec) +AFLAGS_tee_smc-arm.o := -Wa,-march=armv7-a$(secext) +optee_armtz-objs += \ + tee_smc-arm.o +endif + +ifeq ($(CONFIG_ARM64),y) +optee_armtz-objs += \ + tee_smc-arm64.o +endif + diff --git a/core/arm64/handle.c b/armtz/handle.c index 33a0541..33a0541 100644 --- a/core/arm64/handle.c +++ b/armtz/handle.c diff --git a/core/arm64/handle.h b/armtz/handle.h index 7132630..7132630 100644 --- a/core/arm64/handle.h +++ b/armtz/handle.h diff --git a/generic/tee_mem.c b/armtz/tee_mem.c index 92c0982..aeb6dbf 100644 --- a/generic/tee_mem.c +++ b/armtz/tee_mem.c @@ -1,19 +1,10 @@ /* - * 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. - * - * 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. - */ +* Copyright (C) STMicroelectronics 2014. All rights reserved. +* +* This code is STMicroelectronics proprietary and confidential. +* Any use of the code for whatever purpose is subject to +* specific written permission of STMicroelectronics SA. +*/ /** * \file tee_mem.c @@ -32,7 +23,6 @@ #include "tee_mem.h" - #define _DUMP_INFO_ALLOCATOR 0 #define USE_DEVM_ALLOC 1 @@ -58,9 +48,9 @@ */ struct mem_chunk { struct list_head node; - uint32_t counter; - size_t size; - unsigned long paddr; + uint32_t counter; + size_t size; + unsigned long paddr; }; /** @@ -76,18 +66,17 @@ struct mem_chunk { * Shared memory pool structure definition */ struct shm_pool { - struct mutex lock; /* Shared memory lock */ - size_t size; /* Size of pool/heap memory segment */ - size_t used; /* Number of bytes allocated */ - void *vaddr; /* Associated Virtual address */ - unsigned long paddr; /* Associated Physical address */ - bool cached; /* true if pool is cacheable */ - struct list_head mchunks; /* Head of memory chunk/block list */ + struct mutex lock; + size_t size; /* Size of pool/heap memory segment */ + size_t used; /* Number of bytes allocated */ + void *vaddr; /* Associated Virtual address */ + unsigned long paddr; /* Associated Physical address */ + bool cached; /* true if pool is cacheable */ + struct list_head mchunks; /* Head of memory chunk/block list */ }; #define __CALCULATE_RATIO_MEM_USED(a) (((a->used)*100)/(a->size)) - /** * \brief Dumps the information of the shared memory pool * @@ -97,8 +86,7 @@ struct shm_pool { * Dump/log the meta data of the shared memory pool on the standard output. * */ -void tee_shm_pool_dump(struct device *dev, struct shm_pool *pool, - bool forced) +void tee_shm_pool_dump(struct device *dev, struct shm_pool *pool, bool forced) { struct mem_chunk *chunk; @@ -110,9 +98,7 @@ void tee_shm_pool_dump(struct device *dev, struct shm_pool *pool, (void *)pool, (void *)pool->paddr, (void *)pool->vaddr, - pool->size, - pool->used, - __CALCULATE_RATIO_MEM_USED(pool)); + pool->size, pool->used, __CALCULATE_RATIO_MEM_USED(pool)); if ((pool->used != 0) || (forced == true)) { dev_info(dev, " \\ HEAD next:[0x%p] prev:[0x%p]\n", @@ -128,8 +114,7 @@ void tee_shm_pool_dump(struct device *dev, struct shm_pool *pool, (void *)chunk->node.next, (void *)chunk->node.prev, (void *)chunk->paddr, - chunk->size, - chunk->counter); + chunk->size, chunk->counter); } } } @@ -160,29 +145,29 @@ void tee_shm_pool_set_cached(struct shm_pool *pool) * If a error is detected returned pool is NULL. */ struct shm_pool *tee_shm_pool_create(struct device *dev, size_t shm_size, - void *shm_vaddr, unsigned long shm_paddr) + void *shm_vaddr, unsigned long shm_paddr) { struct mem_chunk *chunk = NULL; - struct shm_pool *pool = NULL; + struct shm_pool *pool = NULL; if (WARN_ON(!dev)) goto alloc_failed; - dev_dbg(dev, "> vaddr=0x%p, paddr=0x%lx, size=%zuKiB\n", - shm_vaddr, shm_paddr, shm_size/1024); + dev_dbg(dev, "> vaddr=0x%p, paddr=0x%p, size=%zuKiB\n", + shm_vaddr, (void *)shm_paddr, shm_size / 1024); /* Alloc and initialize the shm_pool structure */ pool = _KMALLOC(sizeof(struct shm_pool), GFP_KERNEL); if (!pool) { dev_err(dev, "kmalloc <struct shm_pool> failed\n"); - goto alloc_failed; + goto alloc_failed; } memset(pool, 0, sizeof(*pool)); mutex_init(&pool->lock); mutex_lock(&pool->lock); INIT_LIST_HEAD(&(pool->mchunks)); - pool->size = shm_size; + pool->size = shm_size; pool->vaddr = shm_vaddr; pool->paddr = shm_paddr; @@ -191,11 +176,11 @@ struct shm_pool *tee_shm_pool_create(struct device *dev, size_t shm_size, chunk = _KMALLOC(sizeof(struct mem_chunk), GFP_KERNEL); if (!chunk) { dev_err(dev, "kmalloc <struct MemChunk> failed\n"); - goto alloc_failed; + goto alloc_failed; } memset(chunk, 0, sizeof(*chunk)); - chunk->paddr = shm_paddr; - chunk->size = shm_size; + chunk->paddr = shm_paddr; + chunk->size = shm_size; /* Adds the new entry immediately after the list head */ list_add(&(chunk->node), &(pool->mchunks)); @@ -266,7 +251,6 @@ void tee_shm_pool_destroy(struct device *dev, struct shm_pool *pool) dev_dbg(dev, "<\n"); } - /** * \brief Free all reserved chunk if any, and set pool at it initial state * @@ -295,9 +279,9 @@ void tee_shm_pool_reset(struct device *dev, struct shm_pool *pool) } first->counter = 0; - first->paddr = pool->paddr; - first->size = pool->size; - pool->used = 0; + first->paddr = pool->paddr; + first->size = pool->size; + pool->used = 0; mutex_unlock(&pool->lock); } @@ -315,7 +299,7 @@ void tee_shm_pool_reset(struct device *dev, struct shm_pool *pool) * */ void *tee_shm_pool_p2v(struct device *dev, struct shm_pool *pool, - unsigned long paddr) + unsigned long paddr) { if (WARN_ON(!dev || !pool)) return NULL; @@ -348,7 +332,7 @@ void *tee_shm_pool_p2v(struct device *dev, struct shm_pool *pool, * */ unsigned long tee_shm_pool_v2p(struct device *dev, struct shm_pool *pool, - void *vaddr) + void *vaddr) { if (WARN_ON(!dev || !pool)) return 0UL; @@ -361,7 +345,7 @@ unsigned long tee_shm_pool_v2p(struct device *dev, struct shm_pool *pool, mutex_unlock(&pool->lock); return 0UL; } else { - unsigned long offset = vaddr - pool->vaddr; + unsigned long offset = vaddr - pool->vaddr; unsigned long p = pool->paddr + offset; mutex_unlock(&pool->lock); return p; @@ -381,21 +365,22 @@ unsigned long tee_shm_pool_v2p(struct device *dev, struct shm_pool *pool, * */ unsigned long tee_shm_pool_alloc(struct device *dev, - struct shm_pool *pool, - size_t size, size_t alignment) + struct shm_pool *pool, + size_t size, size_t alignment) { struct mem_chunk *chunk; struct mem_chunk *betterchunk = NULL; - struct mem_chunk *prev_chunk = NULL; - struct mem_chunk *next_chunk = NULL; + struct mem_chunk *prev_chunk = NULL; + struct mem_chunk *next_chunk = NULL; unsigned long begAddr; unsigned long endAddr; if (WARN_ON(!dev || !pool)) return 0UL; - dev_dbg(dev, "> poolH(0x%p) size=0x%zx align=0x%zx\n", - pool, size, alignment); + dev_dbg(dev, "> poolH(%p:%p:%x) size=0x%zx align=0x%zx\n", + pool, (void *)pool->paddr, (unsigned int)pool->size, size, + alignment); /* Align on cache line of the target */ /* \todo(jmd) Should be defined by a global target specific parameter */ @@ -422,15 +407,15 @@ unsigned long tee_shm_pool_alloc(struct device *dev, * size(b) - size is as small as possible. */ list_for_each_entry(chunk, &pool->mchunks, node) { - if (chunk->counter == 0) { /* Free chunk */ + if (chunk->counter == 0) { /* Free chunk */ begAddr = ALIGN(chunk->paddr, alignment); endAddr = begAddr + size; - if (begAddr >= chunk->paddr && - endAddr <= (chunk->paddr + chunk->size) && - (betterchunk == NULL || - /* Always split smaller block */ - chunk->size < betterchunk->size)) + if (begAddr >= chunk->paddr + && endAddr <= (chunk->paddr + chunk->size) + && (betterchunk == NULL + /* Always split smaller block */ + || chunk->size < betterchunk->size)) betterchunk = chunk; } } @@ -452,8 +437,8 @@ unsigned long tee_shm_pool_alloc(struct device *dev, /* memory between begin of chunk and begin * of created memory => create a free chunk */ prev_chunk->counter = 0; - prev_chunk->paddr = betterchunk->paddr; - prev_chunk->size = begAddr - betterchunk->paddr; + prev_chunk->paddr = betterchunk->paddr; + prev_chunk->size = begAddr - betterchunk->paddr; betterchunk->paddr = begAddr; betterchunk->size -= prev_chunk->size; @@ -461,8 +446,7 @@ unsigned long tee_shm_pool_alloc(struct device *dev, dev_dbg(dev, "create p_chunkH=0x%p paddr=0x%p (s=%zu)\n", (void *)prev_chunk, - (void *)prev_chunk->paddr, - prev_chunk->size); + (void *)prev_chunk->paddr, prev_chunk->size); list_add_tail(&(prev_chunk->node), &(betterchunk->node)); @@ -475,14 +459,13 @@ unsigned long tee_shm_pool_alloc(struct device *dev, /* memory between end of chunk and end of * created memory => create a free chunk */ next_chunk->counter = 0; - next_chunk->paddr = endAddr; - next_chunk->size = betterchunk->size - size; + next_chunk->paddr = endAddr; + next_chunk->size = betterchunk->size - size; dev_dbg(dev, "create n_chunkH=0x%p paddr=0x%p (s=%zu)\n", (void *)next_chunk, - (void *)next_chunk->paddr, - next_chunk->size); + (void *)next_chunk->paddr, next_chunk->size); betterchunk->size = size; @@ -505,9 +488,7 @@ unsigned long tee_shm_pool_alloc(struct device *dev, "< chunkH=0x%p paddr=%p (s=%zu) align=0x%zx\n", (void *)betterchunk, (void *)betterchunk->paddr, - betterchunk->size, - alignment); - + betterchunk->size, alignment); return betterchunk->paddr; } @@ -542,14 +523,13 @@ failed_out: * the memory region managed by the pool. * */ -void tee_shm_pool_free(struct device *dev, struct shm_pool *pool, - unsigned long paddr, size_t *size) +int tee_shm_pool_free(struct device *dev, struct shm_pool *pool, + unsigned long paddr, size_t *size) { struct mem_chunk *chunk; - struct mem_chunk *tmp; if (WARN_ON(!dev || !pool)) - return; + return -EINVAL; dev_dbg(dev, "> Try to free ... poolH(0x%p) paddr=0x%p\n", (void *)pool, (void *)paddr); @@ -563,15 +543,16 @@ void tee_shm_pool_free(struct device *dev, struct shm_pool *pool, if (!is_valid_paddr(pool, paddr)) goto out_failed; - list_for_each_entry_safe(chunk, tmp, &pool->mchunks, node) { + list_for_each_entry(chunk, &pool->mchunks, node) { if (chunk->paddr == paddr) { if (size != NULL) *size = chunk->size; if (chunk->counter == 0) { dev_warn(dev, - "tee_shm_pool_free() WARNING, paddr=0x%p already released\n", - (void *)paddr); + "< tee_shm_pool_free() WARNING, paddr=0x%p already released\n", + (void *)paddr); + return -EINVAL; } else if (--chunk->counter == 0) { dev_dbg(dev, "paddr=%p\n", (void *)paddr); @@ -580,9 +561,8 @@ void tee_shm_pool_free(struct device *dev, struct shm_pool *pool, /* Merge with previous */ if (chunk->node.prev != &pool->mchunks) { struct mem_chunk *prev = - list_entry(chunk->node.prev, - struct mem_chunk, - node); + list_entry(chunk->node.prev, + struct mem_chunk, node); if (prev->counter == 0) { dev_dbg(dev, "chunkH=0x%p paddr=0x%p free ok\n", @@ -597,9 +577,8 @@ void tee_shm_pool_free(struct device *dev, struct shm_pool *pool, /* Merge with next */ if (chunk->node.next != &pool->mchunks) { struct mem_chunk *next = - list_entry(chunk->node.next, - struct mem_chunk, - node); + list_entry(chunk->node.next, + struct mem_chunk, node); if (next->counter == 0) { dev_dbg(dev, "chunkH=0x%p paddr=0x%p free ok\n", @@ -615,17 +594,16 @@ void tee_shm_pool_free(struct device *dev, struct shm_pool *pool, #if defined(_DUMP_INFO_ALLOCATOR) && (_DUMP_INFO_ALLOCATOR > 1) tee_shm_pool_dump(dev, pool, false); #endif + dev_dbg(dev, "< freed\n"); + return 0; } else { mutex_unlock(&pool->lock); dev_dbg(dev, - "paddr=0x%p (--) refcounter is decremented\n", + "< paddr=0x%p (--) refcounter is decremented ret=1\n", (void *)paddr); + return 1; } - - dev_dbg(dev, "<\n"); - - return; } } @@ -637,6 +615,7 @@ out_failed: dev_err(dev, "< tee_shm_pool_free() FAILED, pAddr=0x%p not found\n", (void *)paddr); + return -EINVAL; } /** @@ -652,7 +631,7 @@ out_failed: * */ bool tee_shm_pool_incref(struct device *dev, struct shm_pool *pool, - unsigned long paddr) + unsigned long paddr) { struct mem_chunk *chunk; diff --git a/armtz/tee_mem.h b/armtz/tee_mem.h new file mode 100644 index 0000000..c60efa0 --- /dev/null +++ b/armtz/tee_mem.h @@ -0,0 +1,46 @@ +/* +* Copyright (C) STMicroelectronics 2014. All rights reserved. +* +* This code is STMicroelectronics proprietary and confidential. +* Any use of the code for whatever purpose is subject to +* specific written permission of STMicroelectronics SA. +*/ + +#ifndef TEE_MEM_H +#define TEE_MEM_H + +#include <linux/types.h> +#include <linux/device.h> + +struct shm_pool; + +struct shm_pool *tee_shm_pool_create(struct device *dev, size_t shm_size, + void *shm_vaddr, unsigned long shm_paddr); + +void tee_shm_pool_destroy(struct device *dev, struct shm_pool *pool); + +void *tee_shm_pool_p2v(struct device *dev, struct shm_pool *pool, + unsigned long paddr); + +unsigned long tee_shm_pool_v2p(struct device *dev, struct shm_pool *pool, + void *vaddr); + +unsigned long tee_shm_pool_alloc(struct device *dev, + struct shm_pool *pool, + size_t size, size_t alignment); + +int tee_shm_pool_free(struct device *dev, struct shm_pool *pool, + unsigned long paddr, size_t *size); + +bool tee_shm_pool_incref(struct device *dev, struct shm_pool *pool, + unsigned long paddr); + +void tee_shm_pool_dump(struct device *dev, struct shm_pool *pool, bool forced); + +void tee_shm_pool_reset(struct device *dev, struct shm_pool *pool); + +bool tee_shm_pool_is_cached(struct shm_pool *pool); + +void tee_shm_pool_set_cached(struct shm_pool *pool); + +#endif diff --git a/armtz/tee_smc-arm.S b/armtz/tee_smc-arm.S new file mode 100644 index 0000000..b5ae88d --- /dev/null +++ b/armtz/tee_smc-arm.S @@ -0,0 +1,27 @@ +/* +* Copyright (C) STMicroelectronics 2014. All rights reserved. +* +* This code is STMicroelectronics proprietary and confidential. +* Any use of the code for whatever purpose is subject to +* specific written permission of STMicroelectronics SA. +*/ + +#include <linux/linkage.h> + +.text +.balign 4 +.code 32 + + /* void tee_smc_call(struct smc_param *param); */ + .globl tee_smc_call +ENTRY(tee_smc_call) + push {r4-r8, lr} + mov r8, r0 + ldm r8, {r0-r7} +.arch_extension sec + smc #0 + stm r8, {r0-r7} + pop {r4-r8, pc} +ENDPROC(tee_smc_call) + + diff --git a/core/arm64/smc.S b/armtz/tee_smc-arm64.S index 462f9a4..105e18f 100644 --- a/core/arm64/smc.S +++ b/armtz/tee_smc-arm64.S @@ -23,9 +23,9 @@ #define SMC_PARAM_X4_OFFS 32 #define SMC_PARAM_X6_OFFS 48 - /* void tee_smc_call64(struct smc_param64 *param); */ - .globl tee_smc_call64 -ENTRY(tee_smc_call64) + /* void tee_smc_call(struct smc_param *param); */ + .globl tee_smc_call +ENTRY(tee_smc_call) stp x28, x30, [sp, #-16]! mov x28, x0 ldp x0, x1, [x28, #SMC_PARAM_X0_OFFS] @@ -37,4 +37,4 @@ ENTRY(tee_smc_call64) stp x2, x3, [x28, #SMC_PARAM_X2_OFFS] ldp x28, x30, [sp], #16 ret -ENDPROC(tee_smc_call64) +ENDPROC(tee_smc_call) diff --git a/armtz/tee_tz_drv.c b/armtz/tee_tz_drv.c new file mode 100644 index 0000000..fa5e4e9 --- /dev/null +++ b/armtz/tee_tz_drv.c @@ -0,0 +1,1297 @@ +/* #define DEBUG */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/io.h> +#include <linux/sched.h> +#include <linux/jiffies.h> + +#include <linux/tee_core.h> +#include <linux/tee_ioc.h> + +#include <tee_shm.h> +#include <tee_supp_com.h> +#include <tee_mutex_wait.h> + +#include <arm_common/teesmc.h> +#include <arm_common/teesmc_st.h> + +#include "tee_mem.h" +#include "tee_tz_op.h" +#include "tee_tz_priv.h" +#include "handle.h" + +#define _TEE_TZ_NAME "armtz" +#define DEV (ptee->tee->dev) + +/* #define TEE_STRESS_OUTERCACHE_FLUSH */ + +/* magic config: bit 1 is set, Secure TEE shall handler NSec IRQs */ +#define SEC_ROM_NO_FLAG_MASK 0x0000 +#define SEC_ROM_IRQ_ENABLE_MASK 0x0001 +#define SEC_ROM_DEFAULT SEC_ROM_IRQ_ENABLE_MASK +#define TEE_RETURN_BUSY 0x3 +#define ALLOC_ALIGN SZ_4K + +#define CAPABLE(tee) !(tee->conf & TEE_CONF_FW_NOT_CAPABLE) + +static struct tee_tz *tee_tz; + +static struct handle_db shm_handle_db = HANDLE_DB_INITIALIZER; + + +/* Temporary workaround until we're only using post 3.13 kernels */ +#ifdef ioremap_cached +#define ioremap_cache ioremap_cached +#endif + + +/******************************************************************* + * Calling TEE + *******************************************************************/ + +static void e_lock_teez(struct tee_tz *ptee) +{ + mutex_lock(&ptee->mutex); +} + +static void e_lock_wait_completion_teez(struct tee_tz *ptee) +{ + /* + * Release the lock until "something happens" and then reacquire it + * again. + * + * This is needed when TEE returns "busy" and we need to try again + * later. + */ + ptee->c_waiters++; + mutex_unlock(&ptee->mutex); + /* + * Wait at most one second. Secure world is normally never busy + * more than that so we should normally never timeout. + */ + wait_for_completion_timeout(&ptee->c, HZ); + mutex_lock(&ptee->mutex); + ptee->c_waiters--; +} + +static void e_unlock_teez(struct tee_tz *ptee) +{ + /* + * If at least one thread is waiting for "something to happen" let + * one thread know that "something has happened". + */ + if (ptee->c_waiters) + complete(&ptee->c); + mutex_unlock(&ptee->mutex); +} + +static void handle_rpc_func_cmd_mutex_wait(struct tee_tz *ptee, + struct teesmc32_arg *arg32) +{ + struct teesmc32_param *params; + + if (arg32->num_params != 2) + goto bad; + + params = TEESMC32_GET_PARAMS(arg32); + + if ((params[0].attr & TEESMC_ATTR_TYPE_MASK) != + TEESMC_ATTR_TYPE_VALUE_INPUT) + goto bad; + if ((params[1].attr & TEESMC_ATTR_TYPE_MASK) != + TEESMC_ATTR_TYPE_VALUE_INPUT) + goto bad; + + switch (params[0].u.value.a) { + case TEE_MUTEX_WAIT_SLEEP: + tee_mutex_wait_sleep(DEV, &ptee->mutex_wait, + params[1].u.value.a, + params[1].u.value.b); + break; + case TEE_MUTEX_WAIT_WAKEUP: + tee_mutex_wait_wakeup(DEV, &ptee->mutex_wait, + params[1].u.value.a, + params[1].u.value.b); + break; + case TEE_MUTEX_WAIT_DELETE: + tee_mutex_wait_delete(DEV, &ptee->mutex_wait, + params[1].u.value.a); + break; + default: + goto bad; + } + + arg32->ret = TEEC_SUCCESS; + return; +bad: + arg32->ret = TEEC_ERROR_BAD_PARAMETERS; +} + +static void handle_rpc_func_cmd_wait(struct teesmc32_arg *arg32) +{ + struct teesmc32_param *params; + u32 msec_to_wait; + + if (arg32->num_params != 1) + goto bad; + + params = TEESMC32_GET_PARAMS(arg32); + msec_to_wait = params[0].u.value.a; + + /* set task's state to interruptible sleep */ + set_current_state(TASK_INTERRUPTIBLE); + + /* take a nap */ + schedule_timeout(msecs_to_jiffies(msec_to_wait)); + + arg32->ret = TEEC_SUCCESS; + return; +bad: + arg32->ret = TEEC_ERROR_BAD_PARAMETERS; +} + +static void handle_rpc_func_cmd_to_supplicant(struct tee_tz *ptee, + struct teesmc32_arg *arg32) +{ + struct teesmc32_param *params; + struct tee_rpc_invoke inv; + size_t n; + uint32_t ret; + + if (arg32->num_params > TEE_RPC_BUFFER_NUMBER) { + arg32->ret = TEEC_ERROR_GENERIC; + return; + } + + params = TEESMC32_GET_PARAMS(arg32); + + memset(&inv, 0, sizeof(inv)); + inv.cmd = arg32->cmd; + /* + * Set a suitable error code in case tee-supplicant + * ignores the request. + */ + inv.res = TEEC_ERROR_NOT_IMPLEMENTED; + inv.nbr_bf = arg32->num_params; + for (n = 0; n < arg32->num_params; n++) { + inv.cmds[n].buffer = + (void *)(uintptr_t)params[n].u.memref.buf_ptr; + inv.cmds[n].size = params[n].u.memref.size; + switch (params[n].attr & TEESMC_ATTR_TYPE_MASK) { + case TEESMC_ATTR_TYPE_VALUE_INPUT: + case TEESMC_ATTR_TYPE_VALUE_OUTPUT: + case TEESMC_ATTR_TYPE_VALUE_INOUT: + inv.cmds[n].type = TEE_RPC_VALUE; + break; + case TEESMC_ATTR_TYPE_MEMREF_INPUT: + case TEESMC_ATTR_TYPE_MEMREF_OUTPUT: + case TEESMC_ATTR_TYPE_MEMREF_INOUT: + inv.cmds[n].type = TEE_RPC_BUFFER; + break; + default: + arg32->ret = TEEC_ERROR_GENERIC; + return; + } + } + + ret = tee_supp_cmd(ptee->tee, TEE_RPC_ICMD_INVOKE, + &inv, sizeof(inv)); + if (ret == TEEC_RPC_OK) + arg32->ret = inv.res; + + for (n = 0; n < arg32->num_params; n++) { + switch (params[n].attr & TEESMC_ATTR_TYPE_MASK) { + case TEESMC_ATTR_TYPE_VALUE_INPUT: + case TEESMC_ATTR_TYPE_VALUE_OUTPUT: + case TEESMC_ATTR_TYPE_VALUE_INOUT: + case TEESMC_ATTR_TYPE_MEMREF_OUTPUT: + case TEESMC_ATTR_TYPE_MEMREF_INOUT: + /* + * Allow supplicant to assign a new pointer + * to an out-buffer. Needed when the + * supplicant allocates a new buffer, for + * instance when loading a TA. + */ + params[n].u.memref.buf_ptr = + (uint32_t)(uintptr_t)inv.cmds[n].buffer; + params[n].u.memref.size = inv.cmds[n].size; + break; + default: + break; + } + } +} + +static void handle_rpc_func_cmd(struct tee_tz *ptee, u32 parg32) +{ + struct teesmc32_arg *arg32; + + arg32 = tee_shm_pool_p2v(DEV, ptee->shm_pool, parg32); + if (!arg32) + return; + + switch (arg32->cmd) { + case TEE_RPC_MUTEX_WAIT: + handle_rpc_func_cmd_mutex_wait(ptee, arg32); + break; + case TEE_RPC_WAIT: + handle_rpc_func_cmd_wait(arg32); + break; + default: + handle_rpc_func_cmd_to_supplicant(ptee, arg32); + } +} + +static u32 handle_rpc(struct tee_tz *ptee, struct smc_param *param) +{ + struct tee_shm *shm; + int cookie; + + switch (TEESMC_RETURN_GET_RPC_FUNC(param->a0)) { + case TEESMC_RPC_FUNC_ALLOC_ARG: + param->a1 = tee_shm_pool_alloc(DEV, ptee->shm_pool, + param->a1, 4); + break; + case TEESMC_RPC_FUNC_ALLOC_PAYLOAD: + /* Can't support payload shared memory with this interface */ + param->a2 = 0; + break; + case TEESMC_RPC_FUNC_FREE_ARG: + tee_shm_pool_free(DEV, ptee->shm_pool, param->a1, 0); + break; + case TEESMC_RPC_FUNC_FREE_PAYLOAD: + /* Can't support payload shared memory with this interface */ + break; + case TEESMC_ST_RPC_FUNC_ALLOC_PAYLOAD: + shm = tee_shm_alloc_from_rpc(ptee->tee, param->a1, + TEE_SHM_TEMP | TEE_SHM_FROM_RPC); + if (!shm) { + param->a1 = 0; + break; + } + cookie = handle_get(&shm_handle_db, shm); + if (cookie < 0) { + tee_shm_free_from_rpc(shm); + param->a1 = 0; + break; + } + param->a1 = shm->paddr; + param->a2 = cookie; + break; + case TEESMC_ST_RPC_FUNC_FREE_PAYLOAD: + if (true || param->a1) { + shm = handle_put(&shm_handle_db, param->a1); + if (shm) + tee_shm_free_from_rpc(shm); + } + break; + case TEESMC_RPC_FUNC_IRQ: + break; + case TEESMC_RPC_FUNC_CMD: + handle_rpc_func_cmd(ptee, param->a1); + break; + default: + dev_warn(DEV, "Unknown RPC func 0x%x\n", + (u32)TEESMC_RETURN_GET_RPC_FUNC(param->a0)); + break; + } + + if (irqs_disabled()) + return TEESMC32_FASTCALL_RETURN_FROM_RPC; + else + return TEESMC32_CALL_RETURN_FROM_RPC; +} + +static void call_tee(struct tee_tz *ptee, + uintptr_t parg32, struct teesmc32_arg *arg32) +{ + u32 ret; + u32 funcid; + struct smc_param param = { 0 }; + + if (irqs_disabled()) + funcid = TEESMC32_FASTCALL_WITH_ARG; + else + funcid = TEESMC32_CALL_WITH_ARG; + + /* + * Commented out elements used to visualize the layout dynamic part + * of the struct. Note that these fields are not available at all + * if num_params == 0. + * + * params is accessed through the macro TEESMC32_GET_PARAMS + */ + + /* struct teesmc32_param params[num_params]; */ + + + param.a1 = parg32; + e_lock_teez(ptee); + while (true) { + param.a0 = funcid; + + tee_smc_call(¶m); + ret = param.a0; + + if (ret == TEESMC_RETURN_EBUSY) { + /* + * Since secure world returned busy, release the + * lock we had when entering this function and wait + * for "something to happen" (something else to + * exit from secure world and needed resources may + * have become available). + */ + e_lock_wait_completion_teez(ptee); + } else if (TEESMC_RETURN_IS_RPC(ret)) { + /* Process the RPC. */ + e_unlock_teez(ptee); + funcid = handle_rpc(ptee, ¶m); + e_lock_teez(ptee); + } else { + break; + } + } + e_unlock_teez(ptee); + + switch (ret) { + case TEESMC_RETURN_UNKNOWN_FUNCTION: + break; + case TEESMC_RETURN_OK: + /* arg32->ret set by secure world */ + break; + default: + /* Should not happen */ + arg32->ret = TEEC_ERROR_COMMUNICATION; + arg32->ret_origin = TEEC_ORIGIN_COMMS; + break; + } +} + +/******************************************************************* + * TEE service invoke formating + *******************************************************************/ + +/* allocate tee service argument buffer and return virtual address */ +static void *alloc_tee_arg(struct tee_tz *ptee, unsigned long *p, size_t l) +{ + void *vaddr; + dev_dbg(DEV, ">\n"); + BUG_ON(!CAPABLE(ptee->tee)); + + if ((p == NULL) || (l == 0)) + return NULL; + + /* assume a 4 bytes aligned is sufficient */ + *p = tee_shm_pool_alloc(DEV, ptee->shm_pool, l, ALLOC_ALIGN); + if (*p == 0) + return NULL; + + vaddr = tee_shm_pool_p2v(DEV, ptee->shm_pool, *p); + + dev_dbg(DEV, "< %p\n", vaddr); + + return vaddr; +} + +/* free tee service argument buffer (from its physical address) */ +static void free_tee_arg(struct tee_tz *ptee, unsigned long p) +{ + dev_dbg(DEV, ">\n"); + BUG_ON(!CAPABLE(ptee->tee)); + + if (p) + tee_shm_pool_free(DEV, ptee->shm_pool, p, 0); + + dev_dbg(DEV, "<\n"); +} + +static uint32_t get_cache_attrs(struct tee_tz *ptee) +{ + if (tee_shm_pool_is_cached(ptee->shm_pool)) + return TEESMC_ATTR_CACHE_DEFAULT << TEESMC_ATTR_CACHE_SHIFT; + else + return TEESMC_ATTR_CACHE_NONCACHE << TEESMC_ATTR_CACHE_SHIFT; +} + +static uint32_t param_type_teec2teesmc(uint8_t type) +{ + switch (type) { + case TEEC_NONE: + return TEESMC_ATTR_TYPE_NONE; + case TEEC_VALUE_INPUT: + return TEESMC_ATTR_TYPE_VALUE_INPUT; + case TEEC_VALUE_OUTPUT: + return TEESMC_ATTR_TYPE_VALUE_OUTPUT; + case TEEC_VALUE_INOUT: + return TEESMC_ATTR_TYPE_VALUE_INOUT; + case TEEC_MEMREF_TEMP_INPUT: + case TEEC_MEMREF_PARTIAL_INPUT: + return TEESMC_ATTR_TYPE_MEMREF_INPUT; + case TEEC_MEMREF_TEMP_OUTPUT: + case TEEC_MEMREF_PARTIAL_OUTPUT: + return TEESMC_ATTR_TYPE_MEMREF_OUTPUT; + case TEEC_MEMREF_WHOLE: + case TEEC_MEMREF_TEMP_INOUT: + case TEEC_MEMREF_PARTIAL_INOUT: + return TEESMC_ATTR_TYPE_MEMREF_INOUT; + default: + WARN_ON(true); + return 0; + } +} + +static void set_params(struct tee_tz *ptee, + struct teesmc32_param params32[TEEC_CONFIG_PAYLOAD_REF_COUNT], + uint32_t param_types, + struct tee_data *data) +{ + size_t n; + struct tee_shm **shm; + TEEC_Value *value; + + for (n = 0; n < TEEC_CONFIG_PAYLOAD_REF_COUNT; n++) { + uint32_t type = TEEC_PARAM_TYPE_GET(param_types, n); + + params32[n].attr = param_type_teec2teesmc(type); + if (params32[n].attr == TEESMC_ATTR_TYPE_NONE) + continue; + if (params32[n].attr < TEESMC_ATTR_TYPE_MEMREF_INPUT) { + value = (TEEC_Value *)&data->params[n]; + params32[n].u.value.a = value->a; + params32[n].u.value.b = value->b; + continue; + } + shm = (struct tee_shm **)&data->params[n]; + params32[n].attr |= get_cache_attrs(ptee); + params32[n].u.memref.buf_ptr = (*shm)->paddr; + params32[n].u.memref.size = (*shm)->size_req; + } +} + +static void get_params(struct tee_data *data, + struct teesmc32_param params32[TEEC_CONFIG_PAYLOAD_REF_COUNT]) +{ + size_t n; + struct tee_shm **shm; + TEEC_Value *value; + + for (n = 0; n < TEEC_CONFIG_PAYLOAD_REF_COUNT; n++) { + if (params32[n].attr == TEESMC_ATTR_TYPE_NONE) + continue; + if (params32[n].attr < TEESMC_ATTR_TYPE_MEMREF_INPUT) { + value = (TEEC_Value *)&data->params[n]; + value->a = params32[n].u.value.a; + value->b = params32[n].u.value.b; + continue; + } + shm = (struct tee_shm **)&data->params[n]; + (*shm)->size_req = params32[n].u.memref.size; + } +} + + +/* + * tee_open_session - invoke TEE to open a GP TEE session + */ +static int tz_open(struct tee_session *sess, struct tee_cmd *cmd) +{ + struct tee *tee; + struct tee_tz *ptee; + int ret = 0; + + struct teesmc32_arg *arg32; + struct teesmc32_param *params32; + struct teesmc_meta_open_session *meta; + uintptr_t parg32; + uintptr_t pmeta; + size_t num_meta = 1; + uint8_t *ta; + TEEC_UUID *uuid; + + BUG_ON(!sess->ctx->tee); + BUG_ON(!sess->ctx->tee->priv); + tee = sess->ctx->tee; + ptee = tee->priv; + + if (cmd->uuid) + uuid = cmd->uuid->kaddr; + else + uuid = NULL; + + dev_dbg(tee->dev, "> ta kaddr %p, uuid=%08x-%04x-%04x\n", + (cmd->ta) ? cmd->ta->kaddr : NULL, + ((uuid) ? uuid->timeLow : 0xDEAD), + ((uuid) ? uuid->timeMid : 0xDEAD), + ((uuid) ? uuid->timeHiAndVersion : 0xDEAD)); + + if (!CAPABLE(ptee->tee)) { + dev_dbg(tee->dev, "< not capable\n"); + return -EBUSY; + } + + /* case ta binary is inside the open request */ + ta = NULL; + if (cmd->ta) + ta = cmd->ta->kaddr; + if (ta) + num_meta++; + + arg32 = alloc_tee_arg(ptee, &parg32, TEESMC32_GET_ARG_SIZE( + TEEC_CONFIG_PAYLOAD_REF_COUNT + num_meta)); + meta = alloc_tee_arg(ptee, &pmeta, sizeof(*meta)); + + if ((arg32 == NULL) || (meta == NULL)) { + free_tee_arg(ptee, parg32); + free_tee_arg(ptee, pmeta); + return TEEC_ERROR_OUT_OF_MEMORY; + } + + memset(arg32, 0, sizeof(*arg32)); + memset(meta, 0, sizeof(*meta)); + arg32->num_params = TEEC_CONFIG_PAYLOAD_REF_COUNT + num_meta; + params32 = TEESMC32_GET_PARAMS(arg32); + + arg32->cmd = TEESMC_CMD_OPEN_SESSION; + + params32[0].u.memref.buf_ptr = pmeta; + params32[0].u.memref.size = sizeof(*meta); + params32[0].attr = TEESMC_ATTR_TYPE_MEMREF_INPUT | + TEESMC_ATTR_META | get_cache_attrs(ptee); + + if (ta) { + params32[1].u.memref.buf_ptr = + tee_shm_pool_v2p(DEV, ptee->shm_pool, cmd->ta->kaddr); + params32[1].u.memref.size = cmd->ta->size_req; + params32[1].attr = TEESMC_ATTR_TYPE_MEMREF_INPUT | + TEESMC_ATTR_META | get_cache_attrs(ptee); + } + + if (uuid != NULL) + memcpy(meta->uuid, uuid, TEESMC_UUID_LEN); + meta->clnt_login = 0; /* FIXME: is this reliable ? used ? */ + + params32 += num_meta; + set_params(ptee, params32, cmd->param.type, &cmd->param); + + call_tee(ptee, parg32, arg32); + + get_params(&cmd->param, params32); + + if (arg32->ret != TEEC_ERROR_COMMUNICATION) { + sess->sessid = arg32->session; + cmd->err = arg32->ret; + cmd->origin = arg32->ret_origin; + } else + ret = -EBUSY; + + free_tee_arg(ptee, parg32); + free_tee_arg(ptee, pmeta); + + dev_dbg(DEV, "< %x:%d\n", arg32->ret, ret); + return ret; +} + +/* + * tee_invoke_command - invoke TEE to invoke a GP TEE command + */ +static int tz_invoke(struct tee_session *sess, struct tee_cmd *cmd) +{ + struct tee *tee; + struct tee_tz *ptee; + int ret = 0; + + struct teesmc32_arg *arg32; + uintptr_t parg32; + struct teesmc32_param *params32; + + BUG_ON(!sess->ctx->tee); + BUG_ON(!sess->ctx->tee->priv); + tee = sess->ctx->tee; + ptee = tee->priv; + + dev_dbg(DEV, "> sessid %x cmd %x type %x\n", + sess->sessid, cmd->cmd, cmd->param.type); + + if (!CAPABLE(tee)) { + dev_dbg(tee->dev, "< not capable\n"); + return -EBUSY; + } + + arg32 = (typeof(arg32))alloc_tee_arg(ptee, &parg32, + TEESMC32_GET_ARG_SIZE(TEEC_CONFIG_PAYLOAD_REF_COUNT)); + if (!arg32) { + free_tee_arg(ptee, parg32); + return TEEC_ERROR_OUT_OF_MEMORY; + } + + memset(arg32, 0, sizeof(*arg32)); + arg32->num_params = TEEC_CONFIG_PAYLOAD_REF_COUNT; + params32 = TEESMC32_GET_PARAMS(arg32); + + arg32->cmd = TEESMC_CMD_INVOKE_COMMAND; + arg32->session = sess->sessid; + arg32->ta_func = cmd->cmd; + + set_params(ptee, params32, cmd->param.type, &cmd->param); + + call_tee(ptee, parg32, arg32); + + get_params(&cmd->param, params32); + + if (arg32->ret != TEEC_ERROR_COMMUNICATION) { + cmd->err = arg32->ret; + cmd->origin = arg32->ret_origin; + } else + ret = -EBUSY; + + free_tee_arg(ptee, parg32); + + dev_dbg(DEV, "< %x:%d\n", arg32->ret, ret); + return ret; +} + +/* + * tee_cancel_command - invoke TEE to cancel a GP TEE command + */ +static int tz_cancel(struct tee_session *sess, struct tee_cmd *cmd) +{ + struct tee *tee; + struct tee_tz *ptee; + int ret = 0; + + struct teesmc32_arg *arg32; + uintptr_t parg32; + + BUG_ON(!sess->ctx->tee); + BUG_ON(!sess->ctx->tee->priv); + tee = sess->ctx->tee; + ptee = tee->priv; + + dev_dbg(DEV, "cancel on sessid=%08x\n", sess->sessid); + + arg32 = alloc_tee_arg(ptee, &parg32, TEESMC32_GET_ARG_SIZE(0)); + if (arg32 == NULL) { + free_tee_arg(ptee, parg32); + return TEEC_ERROR_OUT_OF_MEMORY; + } + + memset(arg32, 0, sizeof(*arg32)); + arg32->cmd = TEESMC_CMD_CANCEL; + arg32->session = sess->sessid; + + call_tee(ptee, parg32, arg32); + + if (arg32->ret == TEEC_ERROR_COMMUNICATION) + ret = -EBUSY; + + free_tee_arg(ptee, parg32); + + dev_dbg(DEV, "< %x:%d\n", arg32->ret, ret); + return ret; +} + +/* + * tee_close_session - invoke TEE to close a GP TEE session + */ +static int tz_close(struct tee_session *sess) +{ + struct tee *tee; + struct tee_tz *ptee; + int ret = 0; + + struct teesmc32_arg *arg32; + uintptr_t parg32; + + BUG_ON(!sess->ctx->tee); + BUG_ON(!sess->ctx->tee->priv); + tee = sess->ctx->tee; + ptee = tee->priv; + + dev_dbg(DEV, "close on sessid=%08x\n", sess->sessid); + + if (!CAPABLE(tee)) { + dev_dbg(tee->dev, "< not capable\n"); + return -EBUSY; + } + + arg32 = alloc_tee_arg(ptee, &parg32, TEESMC32_GET_ARG_SIZE(0)); + if (arg32 == NULL) { + free_tee_arg(ptee, parg32); + return TEEC_ERROR_OUT_OF_MEMORY; + } + + dev_dbg(DEV, "> [%x]\n", sess->sessid); + + memset(arg32, 0, sizeof(*arg32)); + arg32->cmd = TEESMC_CMD_CLOSE_SESSION; + arg32->session = sess->sessid; + + call_tee(ptee, parg32, arg32); + + if (arg32->ret == TEEC_ERROR_COMMUNICATION) + ret = -EBUSY; + + free_tee_arg(ptee, parg32); + + dev_dbg(DEV, "< %x:%d\n", arg32->ret, ret); + return ret; +} + +static struct tee_shm *tz_alloc(struct tee *tee, size_t size, uint32_t flags) +{ + struct tee_shm *shm = NULL; + struct tee_tz *ptee; + size_t size_aligned; + BUG_ON(!tee->priv); + ptee = tee->priv; + + dev_dbg(DEV, "%s: s=%d,flags=0x%08x\n", __func__, (int)size, flags); + +/* comment due to #6357 + * if ( (flags & ~(tee->shm_flags | TEE_SHM_MAPPED + * | TEE_SHM_TEMP | TEE_SHM_FROM_RPC)) != 0 ) { + dev_err(tee->dev, "%s: flag parameter is invalid\n", __func__); + return ERR_PTR(-EINVAL); + }*/ + + size_aligned = ((size / SZ_4K) + 1) * SZ_4K; + if (unlikely(size_aligned == 0)) { + dev_err(DEV, "[%s] requested size too big\n", __func__); + return NULL; + } + + shm = devm_kzalloc(tee->dev, sizeof(struct tee_shm), GFP_KERNEL); + if (!shm) { + dev_err(tee->dev, "%s: kzalloc failed\n", __func__); + return ERR_PTR(-ENOMEM); + } + + shm->size_alloc = ((size / SZ_4K) + 1) * SZ_4K; + shm->size_req = size; + shm->paddr = tee_shm_pool_alloc(tee->dev, ptee->shm_pool, + shm->size_alloc, ALLOC_ALIGN); + if (!shm->paddr) { + dev_err(tee->dev, "%s: cannot alloc memory, size 0x%lx\n", + __func__, (unsigned long)shm->size_alloc); + devm_kfree(tee->dev, shm); + return ERR_PTR(-ENOMEM); + } + shm->kaddr = tee_shm_pool_p2v(tee->dev, ptee->shm_pool, shm->paddr); + if (!shm->kaddr) { + dev_err(tee->dev, "%s: p2v(%p)=0\n", __func__, + (void *)shm->paddr); + tee_shm_pool_free(tee->dev, ptee->shm_pool, shm->paddr, NULL); + devm_kfree(tee->dev, shm); + return ERR_PTR(-EFAULT); + } + shm->flags = flags; + if (ptee->shm_cached) + shm->flags |= TEE_SHM_CACHED; + + dev_dbg(tee->dev, "%s: kaddr=%p, paddr=%p, shm=%p, size %x:%x\n", + __func__, shm->kaddr, (void *)shm->paddr, shm, + (unsigned int)shm->size_req, (unsigned int)shm->size_alloc); + + return shm; +} + +static void tz_free(struct tee_shm *shm) +{ + size_t size; + int ret; + struct tee *tee; + struct tee_tz *ptee; + + BUG_ON(!shm->tee); + BUG_ON(!shm->tee->priv); + tee = shm->tee; + ptee = tee->priv; + + dev_dbg(tee->dev, "%s: shm=%p\n", __func__, shm); + + ret = tee_shm_pool_free(tee->dev, ptee->shm_pool, shm->paddr, &size); + if (!ret) { + devm_kfree(tee->dev, shm); + shm = NULL; + } +} + +static int tz_shm_inc_ref(struct tee_shm *shm) +{ + struct tee *tee; + struct tee_tz *ptee; + + BUG_ON(!shm->tee); + BUG_ON(!shm->tee->priv); + tee = shm->tee; + ptee = tee->priv; + + return tee_shm_pool_incref(tee->dev, ptee->shm_pool, shm->paddr); +} + +/******************************************************************************/ +/* +static void tee_get_status(struct tee_tz *ptee) +{ + TEEC_Result ret; + struct tee_msg_send *arg; + struct tee_core_status_out *res; + unsigned long parg, pres; + + if (!CAPABLE(ptee->tee)) + return; + + arg = (typeof(arg))alloc_tee_arg(ptee, &parg, sizeof(*arg)); + res = (typeof(res))alloc_tee_arg(ptee, &pres, sizeof(*res)); + + if ((arg == NULL) || (res == NULL)) { + dev_err(DEV, "TZ outercache mutex error: alloc shm failed\n"); + goto out; + } + + memset(arg, 0, sizeof(*arg)); + memset(res, 0, sizeof(*res)); + arg->service = ISSWAPI_TEE_GET_CORE_STATUS; + ret = send_and_wait(ptee, ISSWAPI_TEE_GET_CORE_STATUS, SEC_ROM_DEFAULT, + parg, pres); + if (ret != TEEC_SUCCESS) { + dev_warn(DEV, "get statuc failed\n"); + goto out; + } + + pr_info("TEETZ Firmware status:\n"); + pr_info("%s", res->raw); + +out: + free_tee_arg(ptee, parg); + free_tee_arg(ptee, pres); +}*/ + +#ifdef CONFIG_OUTER_CACHE +/* + * Synchronised outer cache maintenance support + */ +#ifndef CONFIG_ARM_TZ_SUPPORT +/* weak outer_tz_mutex in case not supported by kernel */ +bool __weak outer_tz_mutex(unsigned long *p) +{ + pr_err("weak outer_tz_mutex"); + if (p != NULL) + return false; + return true; +} +#endif + +/* register_outercache_mutex - Negotiate/Disable outer cache shared mutex */ +static int register_outercache_mutex(struct tee_tz *ptee, bool reg) +{ + unsigned long *vaddr = NULL; + int ret = 0; + struct smc_param param; + uintptr_t paddr = 0; + + dev_dbg(ptee->tee->dev, ">\n"); + BUG_ON(!CAPABLE(ptee->tee)); + + if ((reg == true) && (ptee->tz_outer_cache_mutex != NULL)) { + dev_err(DEV, "outer cache shared mutex already registered\n"); + return -EINVAL; + } + if ((reg == false) && (ptee->tz_outer_cache_mutex == NULL)) + return 0; + + mutex_lock(&ptee->mutex); + + if (reg == false) { + vaddr = ptee->tz_outer_cache_mutex; + ptee->tz_outer_cache_mutex = NULL; + goto out; + } + + memset(¶m, 0, sizeof(param)); + param.a0 = TEESMC32_ST_FASTCALL_L2CC_MUTEX; + param.a1 = TEESMC_ST_L2CC_MUTEX_GET_ADDR; + tee_smc_call(¶m); + + if (param.a0 != TEESMC_RETURN_OK) { + dev_warn(DEV, "no TZ l2cc mutex service supported\n"); + goto out; + } + paddr = param.a2; + dev_dbg(DEV, "outer cache shared mutex paddr 0x%lx\n", paddr); + + vaddr = ioremap_cache(paddr, sizeof(u32)); + if (vaddr == NULL) { + dev_warn(DEV, "TZ l2cc mutex disabled: ioremap failed\n"); + ret = -ENOMEM; + goto out; + } + + dev_dbg(DEV, "outer cache shared mutex vaddr %p\n", vaddr); + if (outer_tz_mutex(vaddr) == false) { + dev_warn(DEV, "TZ l2cc mutex disabled: outer cache refused\n"); + goto out; + } + + memset(¶m, 0, sizeof(param)); + param.a0 = TEESMC32_ST_FASTCALL_L2CC_MUTEX; + param.a1 = TEESMC_ST_L2CC_MUTEX_ENABLE; + tee_smc_call(¶m); + + if (param.a0 != TEESMC_RETURN_OK) { + + dev_warn(DEV, "TZ l2cc mutex disabled: TZ enable failed\n"); + goto out; + } + ptee->tz_outer_cache_mutex = vaddr; + +out: + if (ptee->tz_outer_cache_mutex == NULL) { + memset(¶m, 0, sizeof(param)); + param.a0 = TEESMC32_ST_FASTCALL_L2CC_MUTEX; + param.a1 = TEESMC_ST_L2CC_MUTEX_DISABLE; + tee_smc_call(¶m); + outer_tz_mutex(NULL); + if (vaddr) + iounmap(vaddr); + dev_dbg(DEV, "outer cache shared mutex disabled\n"); + } + + mutex_unlock(&ptee->mutex); + dev_dbg(DEV, "< teetz outer mutex: ret=%d pa=0x%lX va=0x%p %sabled\n", + ret, paddr, vaddr, ptee->tz_outer_cache_mutex ? "en" : "dis"); + return ret; +} +#endif + +/* configure_shm - Negotiate Shared Memory configuration with teetz. */ +static int configure_shm(struct tee_tz *ptee) +{ + struct smc_param param = { 0 }; + size_t shm_size = -1; + int ret = 0; + + dev_dbg(DEV, ">\n"); + BUG_ON(!CAPABLE(ptee->tee)); + + mutex_lock(&ptee->mutex); + param.a0 = TEESMC32_ST_FASTCALL_GET_SHM_CONFIG; + tee_smc_call(¶m); + mutex_unlock(&ptee->mutex); + + if (param.a0 != TEESMC_RETURN_OK) { + dev_err(DEV, "shm service not available: %X", (uint)param.a0); + ret = -EINVAL; + goto out; + } + + ptee->shm_paddr = param.a1; + shm_size = param.a2; + ptee->shm_cached = (bool)param.a3; + + if (ptee->shm_cached) + ptee->shm_vaddr = ioremap_cache(ptee->shm_paddr, shm_size); + else + ptee->shm_vaddr = ioremap_nocache(ptee->shm_paddr, shm_size); + + if (ptee->shm_vaddr == NULL) { + dev_err(DEV, "shm ioremap failed\n"); + ret = -ENOMEM; + goto out; + } + + ptee->shm_pool = tee_shm_pool_create(DEV, shm_size, + ptee->shm_vaddr, ptee->shm_paddr); + + if (!ptee->shm_pool) { + dev_err(DEV, "shm pool creation failed (%zu)", shm_size); + ret = -EINVAL; + goto out; + } + + if (ptee->shm_cached) + tee_shm_pool_set_cached(ptee->shm_pool); +out: + dev_dbg(DEV, "< ret=%d pa=0x%lX va=0x%p size=%zu, %scached", + ret, ptee->shm_paddr, ptee->shm_vaddr, shm_size, + (ptee->shm_cached == 1) ? "" : "un"); + return ret; +} + + +/******************************************************************************/ + +static int tz_start(struct tee *tee) +{ + struct tee_tz *ptee; + int ret; + + BUG_ON(!tee || !tee->priv); + dev_dbg(tee->dev, ">\n"); + if (!CAPABLE(tee)) { + dev_dbg(tee->dev, "< not capable\n"); + return -EBUSY; + } + + ptee = tee->priv; + BUG_ON(ptee->started); + ptee->started = true; + + ret = configure_shm(ptee); + if (ret) + goto exit; + + +#ifdef CONFIG_OUTER_CACHE + ret = register_outercache_mutex(ptee, true); + if (ret) + goto exit; +#endif + +exit: + if (ret) + ptee->started = false; + + dev_dbg(tee->dev, "< ret=%d dev=%s\n", ret, tee->name); + return ret; +} + +static int tz_stop(struct tee *tee) +{ + struct tee_tz *ptee; + + BUG_ON(!tee || !tee->priv); + + ptee = tee->priv; + + dev_dbg(tee->dev, "> dev=%s\n", tee->name); + if (!CAPABLE(tee)) { + dev_dbg(tee->dev, "< not capable\n"); + return -EBUSY; + } + +#ifdef CONFIG_OUTER_CACHE + register_outercache_mutex(ptee, false); +#endif + tee_shm_pool_destroy(tee->dev, ptee->shm_pool); + iounmap(ptee->shm_vaddr); + ptee->started = false; + + dev_dbg(tee->dev, "< ret=0 dev=%s\n", tee->name); + return 0; +} + +/******************************************************************************/ + +const struct tee_ops tee_fops = { + .type = "tz", + .owner = THIS_MODULE, + .start = tz_start, + .stop = tz_stop, + .invoke = tz_invoke, + .cancel = tz_cancel, + .open = tz_open, + .close = tz_close, + .alloc = tz_alloc, + .free = tz_free, + .shm_inc_ref = tz_shm_inc_ref, +}; + +static int tz_tee_init(struct platform_device *pdev) +{ + int ret = 0; + + struct tee *tee = platform_get_drvdata(pdev); + struct tee_tz *ptee = tee->priv; + + tee_tz = ptee; + +#if 0 + /* To replace by a syscall */ +#ifndef CONFIG_ARM_TZ_SUPPORT + dev_err(tee->dev, + "%s: dev=%s, TZ fw is not loaded: TEE TZ is not supported.\n", + __func__, tee->name); + tee->conf = TEE_CONF_FW_NOT_CAPABLE; + return 0; +#endif +#endif + + tee->shm_flags = TEEC_MEM_INPUT | TEEC_MEM_OUTPUT; + tee->test = 0; + + ptee->started = false; + ptee->sess_id = 0xAB000000; + mutex_init(&ptee->mutex); + init_completion(&ptee->c); + ptee->c_waiters = 0; + + ret = tee_mutex_wait_init(&ptee->mutex_wait); + + if (ret) + dev_err(tee->dev, "%s: dev=%s, Secure armv7 failed (%d)\n", + __func__, tee->name, ret); + else + dev_dbg(tee->dev, "%s: dev=%s, Secure armv7\n", + __func__, tee->name); + return ret; +} + +static void tz_tee_deinit(struct platform_device *pdev) +{ + struct tee *tee = platform_get_drvdata(pdev); + struct tee_tz *ptee = tee->priv; + + if (!CAPABLE(tee)) + return; + + tee_mutex_wait_exit(&ptee->mutex_wait); + + dev_dbg(tee->dev, "%s: dev=%s, Secure armv7 started=%d\n", __func__, + tee->name, ptee->started); + + return; +} + +static int tz_tee_probe(struct platform_device *pdev) +{ + int ret = 0; + struct device *dev = &pdev->dev; + struct tee *tee; + struct tee_tz *ptee; + + pr_info("%s: name=\"%s\", id=%d, pdev_name=\"%s\"\n", __func__, + pdev->name, pdev->id, dev_name(dev)); +#ifdef _TEE_DEBUG + pr_debug("- dev=%p\n", dev); + pr_debug("- dev->parent=%p\n", dev->ctx); + pr_debug("- dev->driver=%p\n", dev->driver); +#endif + + tee = tee_core_alloc(dev, _TEE_TZ_NAME, pdev->id, &tee_fops, + sizeof(struct tee_tz)); + if (!tee) + return -ENOMEM; + + ptee = tee->priv; + ptee->tee = tee; + + platform_set_drvdata(pdev, tee); + + ret = tz_tee_init(pdev); + if (ret) + goto bail1; + + ret = tee_core_add(tee); + if (ret) + goto bail0; + +#ifdef _TEE_DEBUG + pr_debug("- tee=%p, id=%d, iminor=%d\n", tee, tee->id, + tee->miscdev.minor); +#endif + return 0; + +bail1: + tz_tee_deinit(pdev); +bail0: + return ret; +} + +static int tz_tee_remove(struct platform_device *pdev) +{ + struct tee *tee = platform_get_drvdata(pdev); + struct device *dev = &pdev->dev; + /*struct tee_tz *ptee;*/ + + pr_info("%s: name=\"%s\", id=%d, pdev_name=\"%s\"\n", __func__, + pdev->name, pdev->id, dev_name(dev)); +#ifdef _TEE_DEBUG + pr_debug("- tee=%p, id=%d, iminor=%d, name=%s\n", + tee, tee->id, tee->miscdev.minor, tee->name); +#endif + +/* ptee = tee->priv; + tee_get_status(ptee);*/ + + tz_tee_deinit(pdev); + tee_core_del(tee); + return 0; +} + +static struct of_device_id tz_tee_match[] = { + { + .compatible = "stm,armv7sec", + }, + {}, +}; + +static struct platform_driver tz_tee_driver = { + .probe = tz_tee_probe, + .remove = tz_tee_remove, + .driver = { + .name = "armv7sec", + .owner = THIS_MODULE, + .of_match_table = tz_tee_match, + }, +}; + +static struct platform_device tz_0_plt_device = { + .name = "armv7sec", + .id = 0, + .dev = { +/* .platform_data = tz_0_tee_data,*/ + }, +}; + +static int __init tee_tz_init(void) +{ + int rc; + + pr_info("TEE armv7 Driver initialization\n"); + +#ifdef _TEE_DEBUG + pr_debug("- Register the platform_driver \"%s\" %p\n", + tz_tee_driver.driver.name, &tz_tee_driver.driver); +#endif + + rc = platform_driver_register(&tz_tee_driver); + if (rc != 0) { + pr_err("failed to register the platform driver (rc=%d)\n", rc); + goto bail0; + } + + rc = platform_device_register(&tz_0_plt_device); + if (rc != 0) { + pr_err("failed to register the platform devices 0 (rc=%d)\n", + rc); + goto bail1; + } + + return rc; + +bail1: + platform_driver_unregister(&tz_tee_driver); +bail0: + return rc; +} + +static void __exit tee_tz_exit(void) +{ + pr_info("TEE ARMV7 Driver de-initialization\n"); + + platform_device_unregister(&tz_0_plt_device); + platform_driver_unregister(&tz_tee_driver); +} + +module_init(tee_tz_init); +module_exit(tee_tz_exit); + +MODULE_AUTHOR("STMicroelectronics"); +MODULE_DESCRIPTION("STM Secure TEE ARMV7 TZ driver"); +MODULE_SUPPORTED_DEVICE(""); +MODULE_LICENSE("GPL"); +MODULE_VERSION("1.0"); diff --git a/armtz/tee_tz_op.h b/armtz/tee_tz_op.h new file mode 100644 index 0000000..d260b61 --- /dev/null +++ b/armtz/tee_tz_op.h @@ -0,0 +1,259 @@ + +#ifndef __TEE_ARMV7_OP_H__ +#define __TEE_ARMV7_OP_H__ + +enum t_issw_service_id { + /* + * ("SSAPI_PRE_INIT_SERV") + */ + SSAPI_PRE_INIT_SERV = 1, + + /* + * ("SSAPI_POST_SPEEDUP_INIT_SERV") + * Reserved, Not used + */ + SSAPI_POST_SPEEDUP_INIT_SERV = 2, + + /* + * ("SSAPI_ISSW_IMPORT_SERV") + */ + SSAPI_ISSW_IMPORT_SERV = 3, + + /* + * ("SSAPI_RET_FROM_INT_SERV") + */ + SSAPI_RET_FROM_INT_SERV = 4, + + /* + * ("SSAPI_RET_FROM_RPC_SERV") + */ + SSAPI_RET_FROM_RPC_SERV = 5, + + /* + * "ISSWAPI_ISSW_EXECUTE_SERV" is linked to ROM code + * ("SSAPI_ISSW_EXECUTE_SERV") + */ + ISSWAPI_ISSW_EXECUTE_SERV = 6, + ISSWAPI_PROT_APPL_MSG_SEND = 0x10000000, + ISSWAPI_EXTERNAL_CODE_CHECK = 0x10000001, + ISSWAPI_SECURE_LOAD = 0x10000002, + ISSWAPI_ISSW_REIMPORT_PUB_KEYS = 0x10000003, + + /* Accessible only on request */ + ISSWAPI_WRITE_L2CC = 0x10000004, + ISSWAPI_WRITE_CP15_SCTLR = 0x10000005, + ISSWAPI_READ_CP15_SCTLR = 0x10000006, + ISSWAPI_WRITE_CP15_ACTLR = 0x10000007, + ISSWAPI_READ_CP15_ACTLR = 0x10000008, + ISSWAPI_WRITE_CP15_DIAGR = 0x10000009, + ISSWAPI_READ_CP15_DIAGR = 0x1000000A, + + ISSWAPI_EXECUTE_TA = 0x11000001, + ISSWAPI_CLOSE_TA = 0x11000002, + ISSWAPI_FLUSH_BOOT_CODE = 0x11000003, + /* Generic, restricted to be used by u-boot */ + ISSWAPI_VERIFY_SIGNED_HEADER = 0x11000005, + ISSWAPI_VERIFY_HASH = 0x11000006, + /* 8500 only, restricted to be used by u-boot */ + ISSWAPI_GET_RT_FLAGS = 0x11000007, + + /* For TEE Client API 1.0 */ + ISSWAPI_TEEC_OPEN_SESSION = 0x11000008, + ISSWAPI_TEEC_CLOSE_SESSION = 0x11000009, + ISSWAPI_TEEC_INVOKE_COMMAND = 0x1100000a, + ISSWAPI_REGISTER_RPC = 0x1100000b, /* this is NOT a GP TEE API ! */ + ISSWAPI_SET_SEC_DDR = 0x1100000c, /* this is NOT a GP TEE API ! */ + ISSWAPI_TEEC_CANCEL_COMMAND = 0x1100000d, + ISSWAPI_TEEC_REGISTER_MEMORY = 0x1100000e, + ISSWAPI_TEEC_UNREGISTER_MEMORY = 0x1100000f, + + /* Internal command */ + ISSWAPI_TEE_DEINIT_CPU = 0x11000010, + ISSWAPI_TEE_CRASH_CPU = 0x11000011, + ISSWAPI_TEE_SET_CORE_TRACE_LEVEL = 0x11000012, + ISSWAPI_TEE_GET_CORE_TRACE_LEVEL = 0x11000013, + ISSWAPI_TEE_SET_TA_TRACE_LEVEL = 0x11000014, + ISSWAPI_TEE_GET_TA_TRACE_LEVEL = 0x11000015, + ISSWAPI_TEE_GET_CORE_STATUS = 0x11000016, + ISSWAPI_TEE_FLUSH_CACHE = 0x11000017, + + ISSWAPI_REGISTER_DEF_SHM = 0x11000020, + ISSWAPI_UNREGISTER_DEF_SHM = 0x11000021, + ISSWAPI_REGISTER_IRQFWD = 0x11000022, + ISSWAPI_UNREGISTER_IRQFWD = 0x11000023, + ISSWAPI_GET_SHM_START = 0x11000024, + ISSWAPI_GET_SHM_SIZE = 0x11000025, + ISSWAPI_GET_SHM_CACHED = 0x11000026, + + ISSWAPI_ENABLE_L2CC_MUTEX = 0x20000000, + ISSWAPI_DISABLE_L2CC_MUTEX = 0x20000001, + ISSWAPI_GET_L2CC_MUTEX = 0x20000002, + ISSWAPI_SET_L2CC_MUTEX = 0x20000003, + + ISSWAPI_LOAD_TEE = 0x20000004, + +}; + +/* + * tee_msg_send - generic part of the msg sent to the TEE + */ +struct tee_msg_send { + unsigned int service; +}; + +/* + * tee_msg_recv - default strcutre of TEE service output message + */ +struct tee_msg_recv { + int duration; + uint32_t res; + uint32_t origin; +}; + +/* + * tee_register_irqfwd_xxx - (un)register callback for interrupt forwarding + */ +struct tee_register_irqfwd_send { + struct tee_msg_send header; + struct { + unsigned long cb; + } data; +}; +struct tee_register_irqfwd_recv { + struct tee_msg_recv header; +}; + +/* + * tee_get_l2cc_mutex - input/output argument structures + */ +struct tee_get_l2cc_mutex_send { + struct tee_msg_send header; +}; +struct tee_get_l2cc_mutex_recv { + struct tee_msg_recv header; + struct { + unsigned long paddr; + } data; +}; + +/** + * struct tee_identity - Represents the identity of the client + * @login: Login id + * @uuid: UUID as defined above + */ +struct tee_identity { + uint32_t login; + TEEC_UUID uuid; +}; + +/* + * tee_open_session_data - input arg structure for TEE open session service + */ +struct tee_open_session_data { + struct ta_signed_header_t *ta; + TEEC_UUID uuid; + uint32_t param_types; + TEEC_Value params[TEEC_CONFIG_PAYLOAD_REF_COUNT]; + struct tee_identity client_id; + uint32_t params_flags[TEEC_CONFIG_PAYLOAD_REF_COUNT]; +}; + +/* + * tee_open_session_send - input arg msg for TEE open session service + */ +struct tee_open_session_send { + struct tee_msg_send header; + struct tee_open_session_data data; +}; + +/* + * tee_open_session_recv - output arg structure for TEE open session service + */ +struct tee_open_session_recv { + struct tee_msg_recv header; + uint32_t sess; + TEEC_Value params[TEEC_CONFIG_PAYLOAD_REF_COUNT]; +}; + +/* + * tee_invoke_command_data - input arg structure for TEE invoke cmd service + */ +struct tee_invoke_command_data { + uint32_t sess; + uint32_t cmd; + uint32_t param_types; + TEEC_Value params[TEEC_CONFIG_PAYLOAD_REF_COUNT]; + uint32_t params_flags[TEEC_CONFIG_PAYLOAD_REF_COUNT]; +}; + +struct tee_invoke_command_send { + struct tee_msg_send header; + struct tee_invoke_command_data data; +}; + +/* + * tee_invoke_command_recv - output arg structure for TEE invoke cmd service + */ +struct tee_invoke_command_recv { + struct tee_msg_recv header; + TEEC_Value params[TEEC_CONFIG_PAYLOAD_REF_COUNT]; +}; + +/* + * tee_cancel_command_data - input arg structure for TEE cancel service + */ +struct tee_cancel_command_data { + uint32_t sess; +}; + +/* + * tee_cancel_command_send - input msg structure for TEE cancel service + */ +struct tee_cancel_command_send { + struct tee_msg_send header; + struct tee_cancel_command_data data; +}; + +/* + * tee_close_session_data - input arg structure for TEE close session service + */ +struct tee_close_session_data { + uint32_t sess; +}; + +/* + * tee_close_session_send - input arg msg for TEE close session service + */ +struct tee_close_session_send { + struct tee_msg_send header; + struct tee_close_session_data data; +}; + +/* + * tee_register_rpc_send_data - input arg structure for TEE register rpc service + */ +struct tee_register_rpc_send_data { + uint32_t fnk; + uint32_t bf; + uint32_t nbr_bf; +}; + +/* + * tee_register_rpc_send - input msg structure for TEE register rpc service + */ +struct tee_register_rpc_send { + struct tee_msg_send header; + struct tee_register_rpc_send_data data; +}; + +/* + * tee_core_status_out - output arg structure for TEE status service + */ +#define TEEC_STATUS_MSG_SIZE 80 + +struct tee_core_status_out { + struct tee_msg_recv header; + char raw[TEEC_STATUS_MSG_SIZE]; +}; + +#endif /* __TEE_ARMV7_OP_H__ */ diff --git a/armtz/tee_tz_priv.h b/armtz/tee_tz_priv.h new file mode 100644 index 0000000..afcc8da --- /dev/null +++ b/armtz/tee_tz_priv.h @@ -0,0 +1,52 @@ + +#ifndef __TEE_TZ_PRIV__ +#define __TEE_TZ_PRIV__ + +struct tee; +struct shm_pool; +struct tee_rpc_bf; + +#ifdef CONFIG_ARM +struct smc_param { + uint32_t a0; + uint32_t a1; + uint32_t a2; + uint32_t a3; + uint32_t a4; + uint32_t a5; + uint32_t a6; + uint32_t a7; +}; +#endif +#ifdef CONFIG_ARM64 +struct smc_param { + uint64_t a0; + uint64_t a1; + uint64_t a2; + uint64_t a3; + uint64_t a4; + uint64_t a5; + uint64_t a6; + uint64_t a7; +}; +#endif + +struct tee_tz { + uint32_t sess_id; + bool started; + struct tee *tee; + unsigned long shm_paddr; + void *shm_vaddr; + struct shm_pool *shm_pool; + struct mutex mutex; + struct completion c; + int c_waiters; + void *tz_outer_cache_mutex; + struct tee_rpc_bf *rpc_buffers; + bool shm_cached; + struct tee_mutex_wait_private mutex_wait; +}; + +int tee_smc_call(struct smc_param *param); + +#endif /* __TEE_TZ_PRIV__ */ diff --git a/core/Makefile b/core/Makefile new file mode 100644 index 0000000..a9193fd --- /dev/null +++ b/core/Makefile @@ -0,0 +1,32 @@ +CFG_TEE_CORE_CORE_TARGET := armv7 + +######################################################################### +# Set Internal Variables # +# May be modified to match your setup # +######################################################################### +CFG_TEE_DRV_DEBUGFS?=0 +CFG_TEE_CORE_LOG_LEVEL?=2 +CFG_TEE_TA_LOG_LEVEL?=2 + +ccflags-y+=-Werror +ccflags-y+=-I$(M)/include/linux +ccflags-y+=-I$(M)/include + +ccflags-y+=-DCFG_TEE_DRV_DEBUGFS=${CFG_TEE_DRV_DEBUGFS} +ccflags-y+=-DCFG_TEE_CORE_LOG_LEVEL=${CFG_TEE_CORE_LOG_LEVEL} +ccflags-y+=-DCFG_TEE_TA_LOG_LEVEL=${CFG_TEE_TA_LOG_LEVEL} + +obj-m += optee.o + +optee-objs:= \ + tee_core.o \ + tee_context.o \ + tee_session.o \ + tee_shm.o \ + tee_supp_com.o \ + tee_sysfs.o \ + tee_debugfs.o \ + tee_kernel_api.o \ + tee_mutex_wait.o + + diff --git a/core/arm64/tee_tz.c b/core/arm64/tee_tz.c deleted file mode 100644 index df54a56..0000000 --- a/core/arm64/tee_tz.c +++ /dev/null @@ -1,989 +0,0 @@ -/* - * 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. - * - * 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/kernel.h> -#include <linux/device.h> -#include <linux/miscdevice.h> -#include <linux/delay.h> -#include <linux/io.h> -#include <linux/errno.h> -#include <linux/string.h> -#include <linux/err.h> -#include <linux/slab.h> -#include <linux/moduleparam.h> -#include <linux/sched.h> -#include <linux/jiffies.h> - -#include <asm/pgtable.h> - -#include "tee-op.h" -#include "tee_supp_com.h" -#include "tee_mem.h" -#include "tee_service.h" -#include "tee_driver.h" -#include "tee_debug.h" -#include "tee_tz.h" -#include <arm_common/teesmc.h> -#include <arm_common/teesmc_st.h> -#include "handle.h" - -#define DEV (tee_tz_miscdev.this_device) - -/* Shared Memory data (config loaded from secure world) */ -static unsigned long shm_paddr; -static size_t shm_size; -static bool shm_cached; -static void *shm_vaddr; - -/* TZ shared mutex service */ -static void *tz_outer_cache_mutex; - -/* protect concurrent access to the tee-tz: inits, entry */ -static DEFINE_MUTEX(g_mutex_teez); - -static DEFINE_MUTEX(e_mutex_teez); -static DECLARE_COMPLETION(e_comp_teez); -static int e_num_waiters; - -/* device data */ -struct tee_driver tee_tz_data; -static struct miscdevice tee_tz_miscdev; - -static bool tee_tz_ready; - -static struct handle_db shm_handle_db = HANDLE_DB_INITIALIZER; - - -/******************************************************************* - * Calling TEE - *******************************************************************/ - -static void e_lock_teez(void) -{ - mutex_lock(&e_mutex_teez); -} - -static void e_lock_wait_completion_teez(void) -{ - /* - * Release the lock until "something happens" and then reacquire it - * again. - * - * This is needed when TEE returns "busy" and we need to try again - * later. - */ - e_num_waiters++; - mutex_unlock(&e_mutex_teez); - /* - * Wait at most one second. Secure world is normally never busy - * more than that so we should normally never timeout. - */ - wait_for_completion_timeout(&e_comp_teez, HZ); - mutex_lock(&e_mutex_teez); - e_num_waiters--; -} - -static void e_unlock_teez(void) -{ - /* - * If at least one thread is waiting for "something to happen" let - * one thread know that "something has happened". - */ - if (e_num_waiters) - complete(&e_comp_teez); - mutex_unlock(&e_mutex_teez); -} - -static void handle_rpc_func_cmd_mutex_wait(struct teesmc32_arg *arg32) -{ - struct teesmc32_param *params; - - if (arg32->num_params != 2) - goto bad; - - params = TEESMC32_GET_PARAMS(arg32); - - if ((params[0].attr & TEESMC_ATTR_TYPE_MASK) != - TEESMC_ATTR_TYPE_VALUE_INPUT) - goto bad; - if ((params[1].attr & TEESMC_ATTR_TYPE_MASK) != - TEESMC_ATTR_TYPE_VALUE_INPUT) - goto bad; - - switch (params[0].u.value.a) { - case TEE_MUTEX_WAIT_SLEEP: - tee_mutex_wait_sleep(DEV, params[1].u.value.a, - params[1].u.value.b); - break; - case TEE_MUTEX_WAIT_WAKEUP: - tee_mutex_wait_wakeup(DEV, params[1].u.value.a, - params[1].u.value.b); - break; - case TEE_MUTEX_WAIT_DELETE: - tee_mutex_wait_delete(DEV, params[1].u.value.a); - break; - default: - goto bad; - } - - arg32->ret = TEEC_SUCCESS;; - return; -bad: - arg32->ret = TEEC_ERROR_BAD_PARAMETERS; -} - -static void handle_rpc_func_cmd_wait(struct teesmc32_arg *arg32) -{ - struct teesmc32_param *params; - u32 msec_to_wait; - - if (arg32->num_params != 1) - goto bad; - - params = TEESMC32_GET_PARAMS(arg32); - msec_to_wait = params[0].u.value.a; - - /* set task's state to interruptible sleep */ - set_current_state(TASK_INTERRUPTIBLE); - - /* take a nap */ - schedule_timeout(msecs_to_jiffies(msec_to_wait)); - - arg32->ret = TEEC_SUCCESS;; - return; -bad: - arg32->ret = TEEC_ERROR_BAD_PARAMETERS; -} - -static void handle_rpc_func_cmd_to_supplicant(struct teesmc32_arg *arg32) -{ - struct teesmc32_param *params; - struct tee_rpc_invoke inv; - size_t n; - uint32_t ret; - - if (arg32->num_params > TEE_RPC_BUFFER_NUMBER) { - arg32->ret = TEEC_ERROR_GENERIC; - return; - } - - params = TEESMC32_GET_PARAMS(arg32); - - memset(&inv, 0, sizeof(inv)); - inv.cmd = arg32->cmd; - /* - * Set a suitable error code in case tee-supplicant - * ignores the request. - */ - inv.res = TEEC_ERROR_NOT_IMPLEMENTED; - inv.nbr_bf = arg32->num_params; - for (n = 0; n < arg32->num_params; n++) { - inv.cmds[n].buffer = - (void *)(uintptr_t)params[n].u.memref.buf_ptr; - inv.cmds[n].size = params[n].u.memref.size; - switch (params[n].attr & TEESMC_ATTR_TYPE_MASK) { - case TEESMC_ATTR_TYPE_VALUE_INPUT: - case TEESMC_ATTR_TYPE_VALUE_OUTPUT: - case TEESMC_ATTR_TYPE_VALUE_INOUT: - inv.cmds[n].type = TEE_RPC_VALUE; - break; - case TEESMC_ATTR_TYPE_MEMREF_INPUT: - case TEESMC_ATTR_TYPE_MEMREF_OUTPUT: - case TEESMC_ATTR_TYPE_MEMREF_INOUT: - inv.cmds[n].type = TEE_RPC_BUFFER; - break; - default: - arg32->ret = TEEC_ERROR_GENERIC; - return; - } - } - - ret = tee_supp_cmd(&TZop, TEE_RPC_ICMD_INVOKE, - &inv, sizeof(inv)); - if (ret == TEEC_RPC_OK) - arg32->ret = inv.res; - - for (n = 0; n < arg32->num_params; n++) { - switch (params[n].attr & TEESMC_ATTR_TYPE_MASK) { - case TEESMC_ATTR_TYPE_VALUE_INPUT: - case TEESMC_ATTR_TYPE_VALUE_OUTPUT: - case TEESMC_ATTR_TYPE_VALUE_INOUT: - case TEESMC_ATTR_TYPE_MEMREF_OUTPUT: - case TEESMC_ATTR_TYPE_MEMREF_INOUT: - /* - * Allow supplicant to assign a new pointer - * to an out-buffer. Needed when the - * supplicant allocates a new buffer, for - * instance when loading a TA. - */ - params[n].u.memref.buf_ptr = - (uint32_t)(uintptr_t)inv.cmds[n].buffer; - params[n].u.memref.size = inv.cmds[n].size; - break; - default: - break; - } - } -} - -static void handle_rpc_func_cmd(u32 parg32) -{ - struct teesmc32_arg *arg32; - - arg32 = tee_shm_pool_p2v(DEV, TZop.Allocator, parg32); - if (!arg32) - return; - - switch (arg32->cmd) { - case TEE_RPC_MUTEX_WAIT: - handle_rpc_func_cmd_mutex_wait(arg32); - break; - case TEE_RPC_WAIT: - handle_rpc_func_cmd_wait(arg32); - break; - default: - handle_rpc_func_cmd_to_supplicant(arg32); - } -} - -static u32 handle_rpc(struct smc_param64 *param) -{ - switch (TEESMC_RETURN_GET_RPC_FUNC(param->a0)) { - case TEESMC_RPC_FUNC_ALLOC_ARG: - param->a1 = tee_shm_pool_alloc(DEV, TZop.Allocator, - param->a1, 4); - break; - case TEESMC_RPC_FUNC_ALLOC_PAYLOAD: - /* Can't support payload shared memory with this interface */ - param->a2 = 0; - break; - case TEESMC_RPC_FUNC_FREE_ARG: - tee_shm_pool_free(DEV, TZop.Allocator, param->a1, 0); - break; - case TEESMC_RPC_FUNC_FREE_PAYLOAD: - /* Can't support payload shared memory with this interface */ - break; - case TEESMC_ST_RPC_FUNC_ALLOC_PAYLOAD: - { - struct tee_shm *shm; - int cookie; - - shm = tee_shm_allocate(&TZop, 0, param->a1, 0); - if (!shm) { - param->a1 = 0; - break; - } - - cookie = handle_get(&shm_handle_db, shm); - if (cookie < 0) { - tee_shm_unallocate(shm); - param->a1 = 0; - break; - } - param->a1 = shm->paddr; - param->a2 = cookie; - break; - } - case TEESMC_ST_RPC_FUNC_FREE_PAYLOAD: - if (param->a1) { - struct tee_shm *shm; - - shm = handle_put(&shm_handle_db, param->a1); - if (shm) - tee_shm_unallocate(shm); - } - break; - case TEESMC_RPC_FUNC_IRQ: - break; - case TEESMC_RPC_FUNC_CMD: - handle_rpc_func_cmd(param->a1); - break; - default: - dev_warn(DEV, "Unknown RPC func 0x%x\n", - (u32)TEESMC_RETURN_GET_RPC_FUNC(param->a0)); - break; - } - - if (irqs_disabled()) - return TEESMC32_FASTCALL_RETURN_FROM_RPC; - else - return TEESMC32_CALL_RETURN_FROM_RPC; -} - -static void call_tee(uintptr_t parg32, struct teesmc32_arg *arg32) -{ - u32 ret; - u32 funcid; - struct smc_param64 param = { 0 }; - - /* Note that we're using TEESMC32 calls since OP-TEE is still 32bit */ - if (irqs_disabled()) - funcid = TEESMC32_FASTCALL_WITH_ARG; - else - funcid = TEESMC32_CALL_WITH_ARG; - - param.a1 = parg32; - e_lock_teez(); - while (true) { - param.a0 = funcid; - - tee_smc_call64(¶m); - ret = param.a0; - - if (ret == TEESMC_RETURN_EBUSY) { - /* - * Since secure world returned busy, release the - * lock we had when entering this function and wait - * for "something to happen" (something else to - * exit from secure world and needed resources may - * have become available). - */ - e_lock_wait_completion_teez(); - } else if (TEESMC_RETURN_IS_RPC(ret)) { - /* Process the RPC. */ - e_unlock_teez(); - funcid = handle_rpc(¶m); - e_lock_teez(); - } else { - break; - } - } - e_unlock_teez(); - - switch (ret) { - case TEESMC_RETURN_UNKNOWN_FUNCTION: - arg32->ret = TEEC_ERROR_NOT_IMPLEMENTED; - arg32->ret_origin = TEEC_ORIGIN_COMMS; - break; - case TEESMC_RETURN_OK: - /* arg32->ret set by secure world */ - break; - default: - /* Should not happen */ - arg32->ret = TEEC_ERROR_COMMUNICATION; - arg32->ret_origin = TEEC_ORIGIN_COMMS; - break; - } -} - -/******************************************************************* - * TEE service invoke formating - *******************************************************************/ - -/* allocate tee service argument buffer and return virtual address */ -static void *alloc_tee_arg(unsigned long *p, size_t l) -{ - if ((p == NULL) || (l == 0)) - return NULL; - - /* assume a 4 bytes aligned is sufficient */ - *p = tee_shm_pool_alloc(DEV, TZop.Allocator, l, 4); - if (*p == 0) - return NULL; - - return tee_shm_pool_p2v(DEV, TZop.Allocator, *p); -} - -/* free tee service argument buffer (from its physical address) */ -static void free_tee_arg(unsigned long p) -{ - if (p) - tee_shm_pool_free(DEV, TZop.Allocator, p, 0); -} - -static uint32_t get_cache_attrs(void) -{ - if (tee_shm_pool_is_cached(TZop.Allocator)) - return TEESMC_ATTR_CACHE_DEFAULT << TEESMC_ATTR_CACHE_SHIFT; - else - return 0; -} - -static void set_params( - struct teesmc32_param params32[TEEC_CONFIG_PAYLOAD_REF_COUNT], - uint32_t param_types, - TEEC_Value params[TEEC_CONFIG_PAYLOAD_REF_COUNT]) -{ - size_t n; - - for (n = 0; n < TEEC_CONFIG_PAYLOAD_REF_COUNT; n++) { - uint8_t a = TEEC_PARAM_TYPE_GET(param_types, n); - - if (a == TEEC_MEMREF_TEMP_INPUT || - a == TEEC_MEMREF_TEMP_OUTPUT || - a == TEEC_MEMREF_TEMP_INOUT) - a |= get_cache_attrs(); - - params32[n].attr = a; - params32[n].u.value.a = params[n].a; - params32[n].u.value.b = params[n].b; - } -} - -static void get_params(TEEC_Value params[TEEC_CONFIG_PAYLOAD_REF_COUNT], - struct teesmc32_param params32[TEEC_CONFIG_PAYLOAD_REF_COUNT]) -{ - size_t n; - - for (n = 0; n < TEEC_CONFIG_PAYLOAD_REF_COUNT; n++) { - params[n].a = params32[n].u.value.a; - params[n].b = params32[n].u.value.b; - } -} - -/* - * tee_open_session - invoke TEE to open a GP TEE session - */ -static TEEC_Result tee_open_session(struct tee_session *ts, - enum t_cmd_service_id sec_cmd, - uint32_t ta_cmd, - uint32_t param_type, - TEEC_Value params[TEEC_CONFIG_PAYLOAD_REF_COUNT], - uint32_t *origin) -{ - TEEC_Result ret_tee; - struct teesmc32_arg *arg32; - uintptr_t parg32; - struct teesmc32_param *params32; - struct teesmc_meta_open_session *meta; - uintptr_t pmeta; - size_t num_meta = 1; - - - dev_dbg(DEV, "> uuid=%08x-%04x-%04x\n", - ((ts->uuid) ? ts->uuid->timeLow : 0xDEAD), - ((ts->uuid) ? ts->uuid->timeMid : 0xDEAD), - ((ts->uuid) ? ts->uuid-> - timeHiAndVersion : 0xDEAD)); - - if (tee_tz_ready == false) - return TEEC_ERROR_BUSY; - - if (ts->ta) - num_meta++; - - arg32 = (typeof(arg32))alloc_tee_arg(&parg32, TEESMC32_GET_ARG_SIZE( - TEEC_CONFIG_PAYLOAD_REF_COUNT + num_meta)); - meta = (typeof(meta))alloc_tee_arg(&pmeta, sizeof(*meta)); - - if ((arg32 == NULL) || (meta == NULL)) { - free_tee_arg(parg32); - free_tee_arg(pmeta); - return TEEC_ERROR_OUT_OF_MEMORY; - } - - memset(arg32, 0, sizeof(*arg32)); - memset(meta, 0, sizeof(*meta)); - arg32->num_params = TEEC_CONFIG_PAYLOAD_REF_COUNT + num_meta; - params32 = TEESMC32_GET_PARAMS(arg32); - - arg32->cmd = TEESMC_CMD_OPEN_SESSION; - - params32[0].u.memref.buf_ptr = pmeta; - params32[0].u.memref.size = sizeof(*meta); - params32[0].attr = TEESMC_ATTR_TYPE_MEMREF_INPUT | - TEESMC_ATTR_META | get_cache_attrs(); - - if (ts->ta != NULL) { - params32[1].u.memref.buf_ptr = - tee_shm_pool_v2p(DEV, TZop.Allocator, ts->ta); - params32[1].u.memref.size = ts->tasize; - params32[1].attr = TEESMC_ATTR_TYPE_MEMREF_INPUT | - TEESMC_ATTR_META | get_cache_attrs(); - } - - if (ts->uuid != NULL) - memcpy(meta->uuid, ts->uuid, TEESMC_UUID_LEN); - meta->clnt_login = ts->login; - - set_params(params32 + num_meta, param_type, params); - - call_tee(parg32, arg32); - - ts->id = arg32->session; - ret_tee = arg32->ret; - if (origin) - *origin = arg32->ret_origin; - - get_params(params, params32 + num_meta); - - free_tee_arg(parg32); - free_tee_arg(pmeta); - dev_dbg(DEV, "< [%d]\n", ret_tee); - return ret_tee; -} - -/* - * tee_invoke_command - invoke TEE to invoke a GP TEE command - */ -static TEEC_Result tee_invoke_command(struct tee_session *ts, - enum t_cmd_service_id sec_cmd, - uint32_t ta_cmd, - uint32_t param_type, - TEEC_Value params[TEEC_CONFIG_PAYLOAD_REF_COUNT], - uint32_t *origin) -{ - TEEC_Result ret_tee; - struct teesmc32_arg *arg32; - uintptr_t parg32; - struct teesmc32_param *params32; - - dev_dbg(DEV, "> [0x%x] [%d]\n", ts->id, ta_cmd); - - arg32 = (typeof(arg32))alloc_tee_arg(&parg32, - TEESMC32_GET_ARG_SIZE(TEEC_CONFIG_PAYLOAD_REF_COUNT)); - if (!arg32) { - free_tee_arg(parg32); - return TEEC_ERROR_OUT_OF_MEMORY; - } - - memset(arg32, 0, sizeof(*arg32)); - arg32->num_params = TEEC_CONFIG_PAYLOAD_REF_COUNT; - params32 = TEESMC32_GET_PARAMS(arg32); - - arg32->cmd = TEESMC_CMD_INVOKE_COMMAND; - arg32->session = ts->id; - arg32->ta_func = ta_cmd; - - set_params(params32, param_type, params); - - call_tee(parg32, arg32); - - ret_tee = arg32->ret; - - get_params(params, params32); - - if (origin) - *origin = arg32->ret_origin; - - free_tee_arg(parg32); - dev_dbg(DEV, "< [0x%x]\n", ret_tee); - return ret_tee; -} - -/* - * tee_cancel_command - invoke TEE to cancel a GP TEE command - */ -static TEEC_Result tee_cancel_command(struct tee_session *ts, - enum t_cmd_service_id sec_cmd, - uint32_t ta_cmd, - uint32_t param_type, - TEEC_Value params[TEEC_CONFIG_PAYLOAD_REF_COUNT], - uint32_t *origin) -{ - TEEC_Result ret_tee = TEEC_SUCCESS; - struct teesmc32_arg *arg32; - uintptr_t parg32; - - arg32 = (typeof(arg32))alloc_tee_arg(&parg32, TEESMC32_GET_ARG_SIZE(0)); - if (arg32 == NULL) { - free_tee_arg(parg32); - return TEEC_ERROR_OUT_OF_MEMORY; - } - - dev_dbg(DEV, "> [0x%x] [%d] [%d]\n", - ts->id, ta_cmd, mutex_is_locked(&g_mutex_teez)); - - memset(arg32, 0, sizeof(*arg32)); - arg32->cmd = TEESMC_CMD_CANCEL; - arg32->session = ts->id; - - call_tee(parg32, arg32); - - ret_tee = arg32->ret; - if (origin) - *origin = arg32->ret_origin; - - free_tee_arg(parg32); - dev_dbg(DEV, "< [0x%x]\n", ret_tee); - return ret_tee; -} - -/* - * tee_close_session - invoke TEE to close a GP TEE session - */ -static TEEC_Result tee_close_session(struct tee_session *ts, - enum t_cmd_service_id sec_cmd, - u32 ta_cmd, - u32 param_type, - TEEC_Value params[TEEC_CONFIG_PAYLOAD_REF_COUNT], - u32 *origin) -{ - TEEC_Result ret_tee; - struct teesmc32_arg *arg32; - uintptr_t parg32; - - arg32 = (typeof(arg32))alloc_tee_arg(&parg32, TEESMC32_GET_ARG_SIZE(0)); - if (arg32 == NULL) { - free_tee_arg(parg32); - return TEEC_ERROR_OUT_OF_MEMORY; - } - - dev_dbg(DEV, "> [0x%x]\n", ts->id); - - memset(arg32, 0, sizeof(*arg32)); - arg32->cmd = TEESMC_CMD_CLOSE_SESSION; - arg32->session = ts->id; - - call_tee(parg32, arg32); - - ret_tee = arg32->ret; - if (origin) - *origin = arg32->ret_origin; - - free_tee_arg(parg32); - dev_dbg(DEV, "< [0x%x]\n", ret_tee); - return ret_tee; -} - -/* - * Synchronised L2 cache maintenance support - */ -#ifndef CONFIG_ARM_TZ_SUPPORT -/* weak outer_tz_mutex in case not supported by kernel */ -bool __weak outer_tz_mutex(unsigned long *p) -{ - return !p; -} -#endif - -/* register_l2cc_mutex - Negotiate/Disable outer cache shared mutex */ -static int register_l2cc_mutex(bool reg) -{ - unsigned long *vaddr = NULL; - int ret = 0; - struct smc_param64 param; - uintptr_t paddr = 0; - - if ((reg == true) && (tz_outer_cache_mutex != NULL)) { - dev_err(DEV, "outer cache shared mutex already registered\n"); - return -EINVAL; - } - if ((reg == false) && (tz_outer_cache_mutex == NULL)) - return 0; - - if (reg == false) { - vaddr = tz_outer_cache_mutex; - tz_outer_cache_mutex = NULL; - goto out; - } - - memset(¶m, 0, sizeof(param)); - param.a0 = TEESMC32_ST_FASTCALL_L2CC_MUTEX; - param.a1 = TEESMC_ST_L2CC_MUTEX_GET_ADDR; - tee_smc_call64(¶m); - - if (param.a0 != TEESMC_RETURN_OK) { - dev_warn(DEV, "no TZ l2cc mutex service supported\n"); - goto out; - } - paddr = param.a2; - - vaddr = ioremap_cache(paddr, sizeof(u32)); - if (vaddr == NULL) { - dev_warn(DEV, "TZ l2cc mutex disabled: ioremap failed\n"); - ret = -ENOMEM; - goto out; - } - - if (outer_tz_mutex(vaddr) == false) { - dev_warn(DEV, "TZ l2cc mutex disabled: outer cache refused\n"); - goto out; - } - - memset(¶m, 0, sizeof(param)); - param.a0 = TEESMC32_ST_FASTCALL_L2CC_MUTEX; - param.a1 = TEESMC_ST_L2CC_MUTEX_ENABLE; - tee_smc_call64(¶m); - - if (param.a0 != TEESMC_RETURN_OK) { - dev_warn(DEV, "TZ l2cc mutex disabled: TZ enable failed\n"); - goto out; - } - tz_outer_cache_mutex = vaddr; - -out: - if (tz_outer_cache_mutex == NULL) { - memset(¶m, 0, sizeof(param)); - param.a0 = TEESMC32_ST_FASTCALL_L2CC_MUTEX; - param.a1 = TEESMC_ST_L2CC_MUTEX_DISABLE; - tee_smc_call64(¶m); - outer_tz_mutex(NULL); - if (vaddr) - iounmap(vaddr); - dev_info(DEV, "outer cache shared mutex disabled\n"); - } - - dev_dbg(DEV, "teetz outer mutex: ret=%d pa=0x%lX va=0x%p %sabled\n", - ret, paddr, vaddr, tz_outer_cache_mutex ? "en" : "dis"); - return ret; -} - -/* configure_shm - Negotiate Shared Memory configuration with teetz. */ -static int configure_shm(void) -{ - struct smc_param64 param = { 0 }; - int ret = 0; - - if (shm_paddr) - return -EINVAL; - - param.a0 = TEESMC32_ST_FASTCALL_GET_SHM_CONFIG; - tee_smc_call64(¶m); - - if (param.a0 != TEESMC_RETURN_OK) { - dev_err(DEV, "shm service not available: %X", (uint)param.a0); - ret = -EINVAL; - goto out; - } - - shm_paddr = param.a1; - shm_size = param.a2; - shm_cached = (bool)param.a3; - - if (shm_cached) - shm_vaddr = ioremap_cache(shm_paddr, shm_size); - else - shm_vaddr = ioremap_nocache(shm_paddr, shm_size); - - if (shm_vaddr == NULL) { - dev_err(DEV, "shm ioremap failed\n"); - ret = -ENOMEM; - goto out; - } - - TZop.Allocator = tee_shm_pool_create( - DEV, shm_size, shm_vaddr, shm_paddr); - - if (!TZop.Allocator) { - dev_err(DEV, "shm pool creation failed (%zu)", shm_size); - ret = -EINVAL; - goto out; - } - - if (shm_cached) - tee_shm_pool_set_cached(TZop.Allocator); -out: - if (ret) - shm_paddr = 0; - - dev_dbg(DEV, "teetz shm: ret=%d pa=0x%lX va=0x%p size=%zu, %scached", - ret, shm_paddr, shm_vaddr, shm_size, - shm_cached == 1 ? "" : "un"); - return ret; -} -/* - * call_tz_sec_world - wrapper for invoking TEE services - */ -static TEEC_Result call_tz_sec_world(struct tee_session *ts, - enum t_cmd_service_id sec_cmd, - u32 ta_cmd, - u32 param_type, - TEEC_Value params[TEEC_CONFIG_PAYLOAD_REF_COUNT], - u32 *origin) -{ - int ret; - - switch (sec_cmd) { - - case CMD_TEEC_OPEN_SESSION: - ret = tee_open_session(ts, sec_cmd, ta_cmd, param_type, - params, origin); - break; - - case CMD_TEEC_INVOKE_COMMAND: - ret = tee_invoke_command(ts, sec_cmd, ta_cmd, param_type, - params, origin); - break; - - case CMD_TEEC_CANCEL_COMMAND: - ret = tee_cancel_command(ts, sec_cmd, ta_cmd, param_type, - params, origin); - break; - - case CMD_TEEC_CLOSE_SESSION: - ret = tee_close_session(ts, sec_cmd, ta_cmd, param_type, - params, origin); - break; - - case CMD_TEEC_REGISTER_MEMORY: - case CMD_TEEC_UNREGISTER_MEMORY: - ret = TEEC_SUCCESS; - break; /* TODO: check if these shall be transfered to TEE */ - - default: - ret = TEEC_ERROR_BAD_PARAMETERS; - } - - return ret; -} - -static TEEC_Result TZ_register_shm(ulong paddr, ulong size, void **handle) -{ - return TEEC_SUCCESS; /* nothing to do ? */ -} - -static TEEC_Result TZ_unregister_shm(void *handle) -{ - return TEEC_SUCCESS; /* nothing to do ! */ -} - -static struct miscdevice tee_tz_miscdev = { - .minor = MISC_DYNAMIC_MINOR, - .name = TEE_TZ_NAME, - .fops = &tee_fops, -}; - -struct tee_targetop TZop = { - .miscdev = &tee_tz_miscdev, - .call_sec_world = call_tz_sec_world, - .register_shm = TZ_register_shm, - .unregister_shm = TZ_unregister_shm, - .page_size = SZ_4K, /* min size alignment */ - .Allocator = NULL, -}; - -/******************************************************************* - * Starting TEE support - *******************************************************************/ - -static bool teesmc_api_uid_is_st(void) -{ - struct smc_param64 param = { .a0 = TEESMC32_CALLS_UID }; - - tee_smc_call64(¶m); - - if (param.a0 == TEESMC_ST_UID_R0 && param.a1 == TEESMC_ST_UID_R1 && - param.a2 == TEESMC_ST_UID_R2 && (param.a3 == TEESMC_ST_UID32_R3 || - param.a3 == TEESMC_ST_UID64_R3)) - return true; - - return false; -} - -static bool teesmc_os_uuid_is_optee(void) -{ - struct smc_param64 param = { .a0 = TEESMC32_CALL_GET_OS_UUID }; - - tee_smc_call64(¶m); - - if (param.a0 == TEESMC_OS_OPTEE_UUID_R0 && - param.a1 == TEESMC_OS_OPTEE_UUID_R1 && - param.a2 == TEESMC_OS_OPTEE_UUID_R2 && - param.a3 == TEESMC_OS_OPTEE_UUID_R3) - return true; - - return false; -} - -static int start_tz_world(void) -{ - int ret; - - /* allow SMC call, mutex will prevent any other access */ - mutex_lock(&g_mutex_teez); - tee_tz_ready = true; - - /* Check that we're talking to the expected TEE */ - if (!teesmc_api_uid_is_st() || !teesmc_os_uuid_is_optee()) { - ret = -EINVAL; - goto out; - } - - ret = configure_shm(); - if (ret) - goto out; - - ret = register_l2cc_mutex(true); - -out: - if (ret) - tee_tz_ready = false; - - mutex_unlock(&g_mutex_teez); - return ret; -} - -static void stop_tz_world(void) -{ - mutex_lock(&g_mutex_teez); - register_l2cc_mutex(false); - tee_shm_pool_destroy(DEV, TZop.Allocator); - iounmap(shm_vaddr); - mutex_unlock(&g_mutex_teez); -} - -/******************************************************************* - * TEE TZ driver inits - *******************************************************************/ - -int __init tee_tz_init(void) -{ - int ret; - - mutex_init(&tee_tz_data.mutex_tee); - - tee_tz_data.memory_pool = NULL; - - ret = misc_register(&tee_tz_miscdev); - if (ret) { - pr_err("Can't register tee_tz\n"); - goto exit; - } - -#if (CFG_TEE_DRV_DEBUGFS == 1) - ret = tee_debug_init(DEV); - if (ret) - goto err_deregister; -#endif - - ret = start_tz_world(); - if (ret) { - dev_err(DEV, "Can't start tee-tz\n"); - goto err_dbg; - } - - return 0; - -err_dbg: -#if (CFG_TEE_DRV_DEBUGFS == 1) - tee_debug_remove(DEV); -err_deregister: -#endif - misc_deregister(&tee_tz_miscdev); -exit: - /* - * Temporary workaround: TEE TZ firmware may not be available. - * In case TZ driver fails, just forbid access to TZ-TEE. - */ - if (ret) { - pr_err("TEE/TZ driver failed. It is now disabled.\n"); - ret = 0; - } - return ret; -} - -void tee_tz_exit(void) -{ - if (tee_tz_ready == false) - return; - - stop_tz_world(); -#if (CFG_TEE_DRV_DEBUGFS == 1) - tee_debug_remove(DEV); -#endif - misc_deregister(&tee_tz_miscdev); -} diff --git a/core/arm64/tee_tz.h b/core/arm64/tee_tz.h deleted file mode 100644 index dce337b..0000000 --- a/core/arm64/tee_tz.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * 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. - * - * 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. - */ -#ifndef TEE_TZ_H -#define TEE_TZ_H - -struct smc_param64 { - uint64_t a0; - uint64_t a1; - uint64_t a2; - uint64_t a3; - uint64_t a4; - uint64_t a5; - uint64_t a6; - uint64_t a7; -}; - -int __init tee_tz_init(void); -void tee_tz_exit(void); - -const char *tee_tz_get_memory_pool(void); -int tee_smc_call64(struct smc_param64 *param); - -extern struct tee_driver tee_tz_data; -extern struct tee_targetop TZop; - -#endif /* TEE_TZ_H */ - diff --git a/core/arm64/tee_tz_debug.c b/core/arm64/tee_tz_debug.c deleted file mode 100644 index e740e68..0000000 --- a/core/arm64/tee_tz_debug.c +++ /dev/null @@ -1,113 +0,0 @@ -/* - * 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. - * - * 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/debugfs.h> -#include <linux/module.h> -#include <linux/device.h> - -#include "tee-op.h" -#include "tee_driver.h" -#include "tee_debug.h" -#include "tee_tz.h" - -static const char STR_CMD_HIST[] = "hist"; -static const char STR_CMD_HIST_HELP[] = "cmd"; -static const char STR_DUMP_ALLOCATOR[] = "dump"; -static const char STR_DUMP_ALLOCATOR_HELP[] = "shared memory"; - -/*****************************************************************************/ - -static ssize_t tee_write_file_settings_tz(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct device *dev = file->private_data; - char *buf; - char *str; - int val; - - buf = devm_kzalloc(dev, count, GFP_KERNEL); - if (!buf) { - dev_err(dev, "can't allocate work buffer\n"); - return count; - } - - val = simple_write_to_buffer(buf, count, ppos, user_buf, count); - if (!val) { - dev_err(dev, "no user data\n"); - goto out; - } - - str = strstr(buf, STR_DUMP_ALLOCATOR); - if (str) - tee_shm_pool_dump(dev, TZop.Allocator, true); - - str = strstr(buf, STR_CMD_HIST); - if (str) - tee_debug_dump_cmd_hist(dev, NULL, 0); - -out: - devm_kfree(dev, buf); - return count; -} - -static ssize_t tee_read_file_settings_tz( - struct file *file, char __user *user_buf, size_t count, loff_t *ppos) -{ - ssize_t ret = 0; - struct device *dev = file->private_data; - static const int MAX_SIZE = 2 * PAGE_SIZE; - char *buf; - - buf = devm_kzalloc(dev, MAX_SIZE, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - ret = snprintf(buf, MAX_SIZE, "teetz debug:\n=========\n"); - - ret += snprintf(buf + ret, MAX_SIZE - ret, "Status:\n"); - - ret += snprintf(buf + ret, MAX_SIZE - ret, - "\tOpened session:\t\t[%d]\n", tee_tz_data.count_session); - - ret += snprintf(buf + ret, MAX_SIZE - ret, - "\tMemory pool:\t[%s]\n", tee_tz_get_memory_pool()); - - ret += snprintf(buf + ret, MAX_SIZE - ret, - "\nAvailable cmd:\n\t[%s] (%s)\n\t[%s] (%s)\n", - STR_CMD_HIST, STR_CMD_HIST_HELP, - STR_DUMP_ALLOCATOR, STR_DUMP_ALLOCATOR_HELP); - - ret += snprintf(buf + ret, MAX_SIZE - ret, - "\n\ti.e.: 'echo dump > /sys/kernel/debug/tee/teetz'\n"); - - ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret); - - devm_kfree(dev, buf); - - return ret; -} - - -const struct file_operations tee_debug_fops_tee_tz = { - .open = simple_open, - .read = tee_read_file_settings_tz, - .write = tee_write_file_settings_tz, - .llseek = default_llseek, -}; - -/*****************************************************************************/ - diff --git a/core/armv7/stm-smc.S b/core/armv7/stm-smc.S deleted file mode 100644 index 5157e50..0000000 --- a/core/armv7/stm-smc.S +++ /dev/null @@ -1,33 +0,0 @@ -/* - * 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. - * - * 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/linkage.h> - - .text - .balign 4 - .code 32 - - /* void tee_smc_call(struct smc_param *param); */ - .globl tee_smc_call -ENTRY(tee_smc_call) - push {r4-r8, lr} - mov r8, r0 - ldm r8, {r0-r7} -.arch_extension sec - smc #0 - stm r8, {r0-r7} - pop {r4-r8, pc} -ENDPROC(tee_smc_call) diff --git a/core/armv7/tee_tz.c b/core/armv7/tee_tz.c deleted file mode 100644 index b4666b6..0000000 --- a/core/armv7/tee_tz.c +++ /dev/null @@ -1,975 +0,0 @@ -/* - * 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. - * - * 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/kernel.h> -#include <linux/device.h> -#include <linux/miscdevice.h> -#include <linux/delay.h> -#include <linux/io.h> -#include <linux/errno.h> -#include <linux/string.h> -#include <linux/err.h> -#include <linux/slab.h> -#include <linux/moduleparam.h> -#include <linux/sched.h> -#include <linux/jiffies.h> - -#include <asm/pgtable.h> - -#include "tee-op.h" -#include "tee_supp_com.h" -#include "tee_mem.h" -#include "tee_service.h" -#include "tee_driver.h" -#include "tee_debug.h" -#include "tee_tz.h" -#include <arm_common/teesmc.h> -#include <arm_common/teesmc_st.h> - -#define DEV (tee_tz_miscdev.this_device) - -/* Shared Memory data (config loaded from secure world) */ -static unsigned long shm_paddr; -static size_t shm_size; -static bool shm_cached; -static void *shm_vaddr; - -/* TZ shared mutex service */ -static void *tz_outer_cache_mutex; - -/* protect concurrent access to the tee-tz: inits, entry */ -static DEFINE_MUTEX(g_mutex_teez); - -static DEFINE_MUTEX(e_mutex_teez); -static DECLARE_COMPLETION(e_comp_teez); -static int e_num_waiters; - -/* device data */ -struct tee_driver tee_tz_data; -static struct miscdevice tee_tz_miscdev; - -static bool tee_tz_ready; - -/* Temporary workaround until we're only using post 3.13 kernels */ -#ifdef ioremap_cached -#define ioremap_cache ioremap_cached -#endif - -/******************************************************************* - * Calling TEE - *******************************************************************/ - -static void e_lock_teez(void) -{ - mutex_lock(&e_mutex_teez); -} - -static void e_lock_wait_completion_teez(void) -{ - /* - * Release the lock until "something happens" and then reacquire it - * again. - * - * This is needed when TEE returns "busy" and we need to try again - * later. - */ - e_num_waiters++; - mutex_unlock(&e_mutex_teez); - /* - * Wait at most one second. Secure world is normally never busy - * more than that so we should normally never timeout. - */ - wait_for_completion_timeout(&e_comp_teez, HZ); - mutex_lock(&e_mutex_teez); - e_num_waiters--; -} - -static void e_unlock_teez(void) -{ - /* - * If at least one thread is waiting for "something to happen" let - * one thread know that "something has happened". - */ - if (e_num_waiters) - complete(&e_comp_teez); - mutex_unlock(&e_mutex_teez); -} - -static void handle_rpc_func_cmd_mutex_wait(struct teesmc32_arg *arg32) -{ - struct teesmc32_param *params; - - if (arg32->num_params != 2) - goto bad; - - params = TEESMC32_GET_PARAMS(arg32); - - if ((params[0].attr & TEESMC_ATTR_TYPE_MASK) != - TEESMC_ATTR_TYPE_VALUE_INPUT) - goto bad; - if ((params[1].attr & TEESMC_ATTR_TYPE_MASK) != - TEESMC_ATTR_TYPE_VALUE_INPUT) - goto bad; - - switch (params[0].u.value.a) { - case TEE_MUTEX_WAIT_SLEEP: - tee_mutex_wait_sleep(DEV, params[1].u.value.a, - params[1].u.value.b); - break; - case TEE_MUTEX_WAIT_WAKEUP: - tee_mutex_wait_wakeup(DEV, params[1].u.value.a, - params[1].u.value.b); - break; - case TEE_MUTEX_WAIT_DELETE: - tee_mutex_wait_delete(DEV, params[1].u.value.a); - break; - default: - goto bad; - } - - arg32->ret = TEEC_SUCCESS;; - return; -bad: - arg32->ret = TEEC_ERROR_BAD_PARAMETERS; -} - -static void handle_rpc_func_cmd_wait(struct teesmc32_arg *arg32) -{ - struct teesmc32_param *params; - u32 msec_to_wait; - - if (arg32->num_params != 1) - goto bad; - - params = TEESMC32_GET_PARAMS(arg32); - msec_to_wait = params[0].u.value.a; - - /* set task's state to interruptible sleep */ - set_current_state(TASK_INTERRUPTIBLE); - - /* take a nap */ - schedule_timeout(msecs_to_jiffies(msec_to_wait)); - - arg32->ret = TEEC_SUCCESS;; - return; -bad: - arg32->ret = TEEC_ERROR_BAD_PARAMETERS; -} - -static void handle_rpc_func_cmd_to_supplicant(struct teesmc32_arg *arg32) -{ - struct teesmc32_param *params; - struct tee_rpc_invoke inv; - size_t n; - uint32_t ret; - - if (arg32->num_params > TEE_RPC_BUFFER_NUMBER) { - arg32->ret = TEEC_ERROR_GENERIC; - return; - } - - params = TEESMC32_GET_PARAMS(arg32); - - memset(&inv, 0, sizeof(inv)); - inv.cmd = arg32->cmd; - /* - * Set a suitable error code in case tee-supplicant - * ignores the request. - */ - inv.res = TEEC_ERROR_NOT_IMPLEMENTED; - inv.nbr_bf = arg32->num_params; - for (n = 0; n < arg32->num_params; n++) { - inv.cmds[n].buffer = (void *)params[n].u.memref.buf_ptr; - inv.cmds[n].size = params[n].u.memref.size; - switch (params[n].attr & TEESMC_ATTR_TYPE_MASK) { - case TEESMC_ATTR_TYPE_VALUE_INPUT: - case TEESMC_ATTR_TYPE_VALUE_OUTPUT: - case TEESMC_ATTR_TYPE_VALUE_INOUT: - inv.cmds[n].type = TEE_RPC_VALUE; - break; - case TEESMC_ATTR_TYPE_MEMREF_INPUT: - case TEESMC_ATTR_TYPE_MEMREF_OUTPUT: - case TEESMC_ATTR_TYPE_MEMREF_INOUT: - inv.cmds[n].type = TEE_RPC_BUFFER; - break; - default: - arg32->ret = TEEC_ERROR_GENERIC; - return; - } - } - - ret = tee_supp_cmd(&TZop, TEE_RPC_ICMD_INVOKE, - &inv, sizeof(inv)); - if (ret == TEEC_RPC_OK) - arg32->ret = inv.res; - - for (n = 0; n < arg32->num_params; n++) { - switch (params[n].attr & TEESMC_ATTR_TYPE_MASK) { - case TEESMC_ATTR_TYPE_VALUE_INPUT: - case TEESMC_ATTR_TYPE_VALUE_OUTPUT: - case TEESMC_ATTR_TYPE_VALUE_INOUT: - case TEESMC_ATTR_TYPE_MEMREF_OUTPUT: - case TEESMC_ATTR_TYPE_MEMREF_INOUT: - /* - * Allow supplicant to assign a new pointer - * to an out-buffer. Needed when the - * supplicant allocates a new buffer, for - * instance when loading a TA. - */ - params[n].u.memref.buf_ptr = - (uint32_t)inv.cmds[n].buffer; - params[n].u.memref.size = inv.cmds[n].size; - break; - default: - break; - } - } -} - -static void handle_rpc_func_cmd(u32 parg32) -{ - struct teesmc32_arg *arg32; - - arg32 = tee_shm_pool_p2v(DEV, TZop.Allocator, parg32); - if (!arg32) - return; - - switch (arg32->cmd) { - case TEE_RPC_MUTEX_WAIT: - handle_rpc_func_cmd_mutex_wait(arg32); - break; - case TEE_RPC_WAIT: - handle_rpc_func_cmd_wait(arg32); - break; - default: - handle_rpc_func_cmd_to_supplicant(arg32); - } -} - -static u32 handle_rpc(struct smc_param *param) -{ - switch (TEESMC_RETURN_GET_RPC_FUNC(param->a0)) { - case TEESMC_RPC_FUNC_ALLOC_ARG: - param->a1 = tee_shm_pool_alloc(DEV, TZop.Allocator, - param->a1, 4); - break; - case TEESMC_RPC_FUNC_ALLOC_PAYLOAD: - /* Can't support payload shared memory with this interface */ - param->a2 = 0; - break; - case TEESMC_RPC_FUNC_FREE_ARG: - tee_shm_pool_free(DEV, TZop.Allocator, param->a1, 0); - break; - case TEESMC_RPC_FUNC_FREE_PAYLOAD: - /* Can't support payload shared memory with this interface */ - break; - case TEESMC_ST_RPC_FUNC_ALLOC_PAYLOAD: - { - struct tee_shm *shm; - - shm = tee_shm_allocate(&TZop, 0, param->a1, 0); - if (!shm) { - param->a1 = 0; - break; - } - param->a1 = shm->paddr; - param->a2 = (uint32_t)shm; - break; - } - case TEESMC_ST_RPC_FUNC_FREE_PAYLOAD: - if (param->a1) - tee_shm_unallocate((struct tee_shm *)param->a1); - break; - case TEESMC_RPC_FUNC_IRQ: - break; - case TEESMC_RPC_FUNC_CMD: - handle_rpc_func_cmd(param->a1); - break; - default: - dev_warn(DEV, "Unknown RPC func 0x%x\n", - TEESMC_RETURN_GET_RPC_FUNC(param->a0)); - break; - } - - if (irqs_disabled()) - return TEESMC32_FASTCALL_RETURN_FROM_RPC; - else - return TEESMC32_CALL_RETURN_FROM_RPC; -} - -static void call_tee(uintptr_t parg32, struct teesmc32_arg *arg32) -{ - u32 ret; - u32 funcid; - struct smc_param param = { 0 }; - - if (irqs_disabled()) - funcid = TEESMC32_FASTCALL_WITH_ARG; - else - funcid = TEESMC32_CALL_WITH_ARG; - - param.a1 = parg32; - e_lock_teez(); - while (true) { - param.a0 = funcid; - - tee_smc_call(¶m); - ret = param.a0; - - if (ret == TEESMC_RETURN_EBUSY) { - /* - * Since secure world returned busy, release the - * lock we had when entering this function and wait - * for "something to happen" (something else to - * exit from secure world and needed resources may - * have become available). - */ - e_lock_wait_completion_teez(); - } else if (TEESMC_RETURN_IS_RPC(ret)) { - /* Process the RPC. */ - e_unlock_teez(); - funcid = handle_rpc(¶m); - e_lock_teez(); - } else { - break; - } - } - e_unlock_teez(); - - switch (ret) { - case TEESMC_RETURN_UNKNOWN_FUNCTION: - arg32->ret = TEEC_ERROR_NOT_IMPLEMENTED; - arg32->ret_origin = TEEC_ORIGIN_COMMS; - break; - case TEESMC_RETURN_OK: - /* arg32->ret set by secure world */ - break; - default: - /* Should not happen */ - arg32->ret = TEEC_ERROR_COMMUNICATION; - arg32->ret_origin = TEEC_ORIGIN_COMMS; - break; - } -} - -/******************************************************************* - * TEE service invoke formating - *******************************************************************/ - -/* allocate tee service argument buffer and return virtual address */ -static void *alloc_tee_arg(unsigned long *p, size_t l) -{ - if ((p == NULL) || (l == 0)) - return NULL; - - /* assume a 4 bytes aligned is sufficient */ - *p = tee_shm_pool_alloc(DEV, TZop.Allocator, l, 4); - if (*p == 0) - return NULL; - - return tee_shm_pool_p2v(DEV, TZop.Allocator, *p); -} - -/* free tee service argument buffer (from its physical address) */ -static void free_tee_arg(unsigned long p) -{ - if (p) - tee_shm_pool_free(DEV, TZop.Allocator, p, 0); -} - -static uint32_t get_cache_attrs(void) -{ - if (tee_shm_pool_is_cached(TZop.Allocator)) - return TEESMC_ATTR_CACHE_DEFAULT << TEESMC_ATTR_CACHE_SHIFT; - else - return 0; -} - -static void set_params( - struct teesmc32_param params32[TEEC_CONFIG_PAYLOAD_REF_COUNT], - uint32_t param_types, - TEEC_Value params[TEEC_CONFIG_PAYLOAD_REF_COUNT]) -{ - size_t n; - - for (n = 0; n < TEEC_CONFIG_PAYLOAD_REF_COUNT; n++) { - uint8_t a = TEEC_PARAM_TYPE_GET(param_types, n); - - if (a == TEEC_MEMREF_TEMP_INPUT || - a == TEEC_MEMREF_TEMP_OUTPUT || - a == TEEC_MEMREF_TEMP_INOUT) - a |= get_cache_attrs(); - - params32[n].attr = a; - params32[n].u.value.a = params[n].a; - params32[n].u.value.b = params[n].b; - } -} - -static void get_params(TEEC_Value params[TEEC_CONFIG_PAYLOAD_REF_COUNT], - struct teesmc32_param params32[TEEC_CONFIG_PAYLOAD_REF_COUNT]) -{ - size_t n; - - for (n = 0; n < TEEC_CONFIG_PAYLOAD_REF_COUNT; n++) { - params[n].a = params32[n].u.value.a; - params[n].b = params32[n].u.value.b; - } -} - -/* - * tee_open_session - invoke TEE to open a GP TEE session - */ -static TEEC_Result tee_open_session(struct tee_session *ts, - enum t_cmd_service_id sec_cmd, - uint32_t ta_cmd, - uint32_t param_type, - TEEC_Value params[TEEC_CONFIG_PAYLOAD_REF_COUNT], - uint32_t *origin) -{ - TEEC_Result ret_tee; - struct teesmc32_arg *arg32; - uintptr_t parg32; - struct teesmc32_param *params32; - struct teesmc_meta_open_session *meta; - uintptr_t pmeta; - size_t num_meta = 1; - - - dev_dbg(DEV, "> uuid=%08x-%04x-%04x\n", - ((ts->uuid) ? ts->uuid->timeLow : 0xDEAD), - ((ts->uuid) ? ts->uuid->timeMid : 0xDEAD), - ((ts->uuid) ? ts->uuid-> - timeHiAndVersion : 0xDEAD)); - - if (tee_tz_ready == false) - return TEEC_ERROR_BUSY; - - if (ts->ta) - num_meta++; - - arg32 = (typeof(arg32))alloc_tee_arg(&parg32, TEESMC32_GET_ARG_SIZE( - TEEC_CONFIG_PAYLOAD_REF_COUNT + num_meta)); - meta = (typeof(meta))alloc_tee_arg(&pmeta, sizeof(*meta)); - - if ((arg32 == NULL) || (meta == NULL)) { - free_tee_arg(parg32); - free_tee_arg(pmeta); - return TEEC_ERROR_OUT_OF_MEMORY; - } - - memset(arg32, 0, sizeof(*arg32)); - memset(meta, 0, sizeof(*meta)); - arg32->num_params = TEEC_CONFIG_PAYLOAD_REF_COUNT + num_meta; - params32 = TEESMC32_GET_PARAMS(arg32); - - arg32->cmd = TEESMC_CMD_OPEN_SESSION; - - params32[0].u.memref.buf_ptr = pmeta; - params32[0].u.memref.size = sizeof(*meta); - params32[0].attr = TEESMC_ATTR_TYPE_MEMREF_INPUT | - TEESMC_ATTR_META | get_cache_attrs(); - - if (ts->ta != NULL) { - params32[1].u.memref.buf_ptr = - tee_shm_pool_v2p(DEV, TZop.Allocator, ts->ta); - params32[1].u.memref.size = ts->tasize; - params32[1].attr = TEESMC_ATTR_TYPE_MEMREF_INPUT | - TEESMC_ATTR_META | get_cache_attrs(); - } - - if (ts->uuid != NULL) - memcpy(meta->uuid, ts->uuid, TEESMC_UUID_LEN); - meta->clnt_login = ts->login; - - set_params(params32 + num_meta, param_type, params); - - call_tee(parg32, arg32); - - ts->id = arg32->session; - ret_tee = arg32->ret; - if (origin) - *origin = arg32->ret_origin; - - get_params(params, params32 + num_meta); - - free_tee_arg(parg32); - free_tee_arg(pmeta); - dev_dbg(DEV, "< [%d]\n", ret_tee); - return ret_tee; -} - -/* - * tee_invoke_command - invoke TEE to invoke a GP TEE command - */ -static TEEC_Result tee_invoke_command(struct tee_session *ts, - enum t_cmd_service_id sec_cmd, - uint32_t ta_cmd, - uint32_t param_type, - TEEC_Value params[TEEC_CONFIG_PAYLOAD_REF_COUNT], - uint32_t *origin) -{ - TEEC_Result ret_tee; - struct teesmc32_arg *arg32; - uintptr_t parg32; - struct teesmc32_param *params32; - - dev_dbg(DEV, "> [%p] [%d]\n", (void *)ts->id, ta_cmd); - - arg32 = (typeof(arg32))alloc_tee_arg(&parg32, - TEESMC32_GET_ARG_SIZE(TEEC_CONFIG_PAYLOAD_REF_COUNT)); - if (!arg32) { - free_tee_arg(parg32); - return TEEC_ERROR_OUT_OF_MEMORY; - } - - memset(arg32, 0, sizeof(*arg32)); - arg32->num_params = TEEC_CONFIG_PAYLOAD_REF_COUNT; - params32 = TEESMC32_GET_PARAMS(arg32); - - arg32->cmd = TEESMC_CMD_INVOKE_COMMAND; - arg32->session = ts->id; - arg32->ta_func = ta_cmd; - - set_params(params32, param_type, params); - - call_tee(parg32, arg32); - - ret_tee = arg32->ret; - - get_params(params, params32); - - if (origin) - *origin = arg32->ret_origin; - - free_tee_arg(parg32); - dev_dbg(DEV, "< [%p]\n", (void *)ret_tee); - return ret_tee; -} - -/* - * tee_cancel_command - invoke TEE to cancel a GP TEE command - */ -static TEEC_Result tee_cancel_command(struct tee_session *ts, - enum t_cmd_service_id sec_cmd, - uint32_t ta_cmd, - uint32_t param_type, - TEEC_Value params[TEEC_CONFIG_PAYLOAD_REF_COUNT], - uint32_t *origin) -{ - TEEC_Result ret_tee = TEEC_SUCCESS; - struct teesmc32_arg *arg32; - uintptr_t parg32; - - arg32 = (typeof(arg32))alloc_tee_arg(&parg32, TEESMC32_GET_ARG_SIZE(0)); - if (arg32 == NULL) { - free_tee_arg(parg32); - return TEEC_ERROR_OUT_OF_MEMORY; - } - - dev_dbg(DEV, "> [%p] [%d] [%d]\n", - (void *)ts->id, ta_cmd, mutex_is_locked(&g_mutex_teez)); - - memset(arg32, 0, sizeof(*arg32)); - arg32->cmd = TEESMC_CMD_CANCEL; - arg32->session = ts->id; - - call_tee(parg32, arg32); - - ret_tee = arg32->ret; - if (origin) - *origin = arg32->ret_origin; - - free_tee_arg(parg32); - dev_dbg(DEV, "< [%p]\n", (void *)ret_tee); - return ret_tee; -} - -/* - * tee_close_session - invoke TEE to close a GP TEE session - */ -static TEEC_Result tee_close_session(struct tee_session *ts, - enum t_cmd_service_id sec_cmd, - u32 ta_cmd, - u32 param_type, - TEEC_Value params[TEEC_CONFIG_PAYLOAD_REF_COUNT], - u32 *origin) -{ - TEEC_Result ret_tee; - struct teesmc32_arg *arg32; - uintptr_t parg32; - - arg32 = (typeof(arg32))alloc_tee_arg(&parg32, TEESMC32_GET_ARG_SIZE(0)); - if (arg32 == NULL) { - free_tee_arg(parg32); - return TEEC_ERROR_OUT_OF_MEMORY; - } - - dev_dbg(DEV, "> [%p]\n", (void *)ts->id); - - memset(arg32, 0, sizeof(*arg32)); - arg32->cmd = TEESMC_CMD_CLOSE_SESSION; - arg32->session = ts->id; - - call_tee(parg32, arg32); - - ret_tee = arg32->ret; - if (origin) - *origin = arg32->ret_origin; - - free_tee_arg(parg32); - dev_dbg(DEV, "< [%p]\n", (void *)ret_tee); - return ret_tee; -} - -/* - * Synchronised L2 cache maintenance support - */ -#ifndef CONFIG_ARM_TZ_SUPPORT -/* weak outer_tz_mutex in case not supported by kernel */ -bool __weak outer_tz_mutex(unsigned long *p) -{ - return !p; -} -#endif - -/* register_l2cc_mutex - Negotiate/Disable outer cache shared mutex */ -static int register_l2cc_mutex(bool reg) -{ - unsigned long *vaddr = NULL; - int ret = 0; - struct smc_param param; - uintptr_t paddr = 0; - - if ((reg == true) && (tz_outer_cache_mutex != NULL)) { - dev_err(DEV, "outer cache shared mutex already registered\n"); - return -EINVAL; - } - if ((reg == false) && (tz_outer_cache_mutex == NULL)) - return 0; - - if (reg == false) { - vaddr = tz_outer_cache_mutex; - tz_outer_cache_mutex = NULL; - goto out; - } - - memset(¶m, 0, sizeof(param)); - param.a0 = TEESMC32_ST_FASTCALL_L2CC_MUTEX; - param.a1 = TEESMC_ST_L2CC_MUTEX_GET_ADDR; - tee_smc_call(¶m); - - if (param.a0 != TEESMC_RETURN_OK) { - dev_warn(DEV, "no TZ l2cc mutex service supported\n"); - goto out; - } - paddr = param.a2; - - vaddr = ioremap_cache(paddr, sizeof(u32)); - if (vaddr == NULL) { - dev_warn(DEV, "TZ l2cc mutex disabled: ioremap failed\n"); - ret = -ENOMEM; - goto out; - } - - if (outer_tz_mutex(vaddr) == false) { - dev_warn(DEV, "TZ l2cc mutex disabled: outer cache refused\n"); - goto out; - } - - memset(¶m, 0, sizeof(param)); - param.a0 = TEESMC32_ST_FASTCALL_L2CC_MUTEX; - param.a1 = TEESMC_ST_L2CC_MUTEX_ENABLE; - tee_smc_call(¶m); - - if (param.a0 != TEESMC_RETURN_OK) { - dev_warn(DEV, "TZ l2cc mutex disabled: TZ enable failed\n"); - goto out; - } - tz_outer_cache_mutex = vaddr; - -out: - if (tz_outer_cache_mutex == NULL) { - memset(¶m, 0, sizeof(param)); - param.a0 = TEESMC32_ST_FASTCALL_L2CC_MUTEX; - param.a1 = TEESMC_ST_L2CC_MUTEX_DISABLE; - tee_smc_call(¶m); - outer_tz_mutex(NULL); - if (vaddr) - iounmap(vaddr); - dev_info(DEV, "outer cache shared mutex disabled\n"); - } - - dev_dbg(DEV, "teetz outer mutex: ret=%d pa=0x%lX va=0x%p %sabled\n", - ret, paddr, vaddr, tz_outer_cache_mutex ? "en" : "dis"); - return ret; -} - -/* configure_shm - Negotiate Shared Memory configuration with teetz. */ -static int configure_shm(void) -{ - struct smc_param param = { 0 }; - int ret = 0; - - if (shm_paddr) - return -EINVAL; - - param.a0 = TEESMC32_ST_FASTCALL_GET_SHM_CONFIG; - tee_smc_call(¶m); - - if (param.a0 != TEESMC_RETURN_OK) { - dev_err(DEV, "shm service not available: %X", (uint)param.a0); - ret = -EINVAL; - goto out; - } - - shm_paddr = param.a1; - shm_size = param.a2; - shm_cached = (bool)param.a3; - - if (shm_cached) - shm_vaddr = ioremap_cache(shm_paddr, shm_size); - else - shm_vaddr = ioremap_nocache(shm_paddr, shm_size); - - if (shm_vaddr == NULL) { - dev_err(DEV, "shm ioremap failed\n"); - ret = -ENOMEM; - goto out; - } - - TZop.Allocator = tee_shm_pool_create( - DEV, shm_size, shm_vaddr, shm_paddr); - - if (!TZop.Allocator) { - dev_err(DEV, "shm pool creation failed (%d)", shm_size); - ret = -EINVAL; - goto out; - } - - if (shm_cached) - tee_shm_pool_set_cached(TZop.Allocator); -out: - if (ret) - shm_paddr = 0; - - dev_dbg(DEV, "teetz shm: ret=%d pa=0x%lX va=0x%p size=%d, %scached", - ret, shm_paddr, shm_vaddr, shm_size, - shm_cached == 1 ? "" : "un"); - return ret; -} -/* - * call_tz_sec_world - wrapper for invoking TEE services - */ -static TEEC_Result call_tz_sec_world(struct tee_session *ts, - enum t_cmd_service_id sec_cmd, - u32 ta_cmd, - u32 param_type, - TEEC_Value params[TEEC_CONFIG_PAYLOAD_REF_COUNT], - u32 *origin) -{ - int ret; - - switch (sec_cmd) { - - case CMD_TEEC_OPEN_SESSION: - ret = tee_open_session(ts, sec_cmd, ta_cmd, param_type, - params, origin); - break; - - case CMD_TEEC_INVOKE_COMMAND: - ret = tee_invoke_command(ts, sec_cmd, ta_cmd, param_type, - params, origin); - break; - - case CMD_TEEC_CANCEL_COMMAND: - ret = tee_cancel_command(ts, sec_cmd, ta_cmd, param_type, - params, origin); - break; - - case CMD_TEEC_CLOSE_SESSION: - ret = tee_close_session(ts, sec_cmd, ta_cmd, param_type, - params, origin); - break; - - case CMD_TEEC_REGISTER_MEMORY: - case CMD_TEEC_UNREGISTER_MEMORY: - ret = TEEC_SUCCESS; - break; /* TODO: check if these shall be transfered to TEE */ - - default: - ret = TEEC_ERROR_BAD_PARAMETERS; - } - - return ret; -} - -static TEEC_Result TZ_register_shm(ulong paddr, ulong size, void **handle) -{ - return TEEC_SUCCESS; /* nothing to do ? */ -} - -static TEEC_Result TZ_unregister_shm(void *handle) -{ - return TEEC_SUCCESS; /* nothing to do ! */ -} - -static struct miscdevice tee_tz_miscdev = { - .minor = MISC_DYNAMIC_MINOR, - .name = TEE_TZ_NAME, - .fops = &tee_fops, -}; - -struct tee_targetop TZop = { - .miscdev = &tee_tz_miscdev, - .call_sec_world = call_tz_sec_world, - .register_shm = TZ_register_shm, - .unregister_shm = TZ_unregister_shm, - .page_size = SZ_4K, /* min size alignment */ - .Allocator = NULL, -}; - -/******************************************************************* - * Starting TEE support - *******************************************************************/ - -static bool teesmc_api_uid_is_st(void) -{ - struct smc_param param = { .a0 = TEESMC32_CALLS_UID }; - - tee_smc_call(¶m); - - if (param.a0 == TEESMC_ST_UID_R0 && param.a1 == TEESMC_ST_UID_R1 && - param.a2 == TEESMC_ST_UID_R2 && (param.a3 == TEESMC_ST_UID32_R3 || - param.a3 == TEESMC_ST_UID64_R3)) - return true; - - return false; -} - -static bool teesmc_os_uuid_is_optee(void) -{ - struct smc_param param = { .a0 = TEESMC32_CALL_GET_OS_UUID }; - - tee_smc_call(¶m); - - if (param.a0 == TEESMC_OS_OPTEE_UUID_R0 && - param.a1 == TEESMC_OS_OPTEE_UUID_R1 && - param.a2 == TEESMC_OS_OPTEE_UUID_R2 && - param.a3 == TEESMC_OS_OPTEE_UUID_R3) - return true; - - return false; -} - -static int start_tz_world(void) -{ - int ret; - - /* allow SMC call, mutex will prevent any other access */ - mutex_lock(&g_mutex_teez); - tee_tz_ready = true; - - /* Check that we're talking to the expected TEE */ - if (!teesmc_api_uid_is_st() || !teesmc_os_uuid_is_optee()) { - ret = -EINVAL; - goto out; - } - - ret = configure_shm(); - if (ret) - goto out; - - ret = register_l2cc_mutex(true); - -out: - if (ret) - tee_tz_ready = false; - - mutex_unlock(&g_mutex_teez); - return ret; -} - -static void stop_tz_world(void) -{ - mutex_lock(&g_mutex_teez); - register_l2cc_mutex(false); - tee_shm_pool_destroy(DEV, TZop.Allocator); - iounmap(shm_vaddr); - mutex_unlock(&g_mutex_teez); -} - -/******************************************************************* - * TEE TZ driver inits - *******************************************************************/ - -int __init tee_tz_init(void) -{ - int ret; - - mutex_init(&tee_tz_data.mutex_tee); - - tee_tz_data.memory_pool = NULL; - - ret = misc_register(&tee_tz_miscdev); - if (ret) { - pr_err("Can't register tee_tz\n"); - goto exit; - } - -#if (CFG_TEE_DRV_DEBUGFS == 1) - ret = tee_debug_init(DEV); - if (ret) - goto err_deregister; -#endif - - ret = start_tz_world(); - if (ret) { - dev_err(DEV, "Can't start tee-tz\n"); - goto err_dbg; - } - - return 0; - -err_dbg: -#if (CFG_TEE_DRV_DEBUGFS == 1) - tee_debug_remove(DEV); -err_deregister: -#endif - misc_deregister(&tee_tz_miscdev); -exit: - /* - * Temporary workaround: TEE TZ firmware may not be available. - * In case TZ driver fails, just forbid access to TZ-TEE. - */ - if (ret) { - pr_err("TEE/TZ driver failed. It is now disabled.\n"); - ret = 0; - } - return ret; -} - -void tee_tz_exit(void) -{ - if (tee_tz_ready == false) - return; - - stop_tz_world(); -#if (CFG_TEE_DRV_DEBUGFS == 1) - tee_debug_remove(DEV); -#endif - misc_deregister(&tee_tz_miscdev); -} diff --git a/core/armv7/tee_tz.h b/core/armv7/tee_tz.h deleted file mode 100644 index caeda0b..0000000 --- a/core/armv7/tee_tz.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * 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. - * - * 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. - */ -#ifndef TEE_TZ_H -#define TEE_TZ_H - -struct smc_param { - uint32_t a0; - uint32_t a1; - uint32_t a2; - uint32_t a3; - uint32_t a4; - uint32_t a5; - uint32_t a6; - uint32_t a7; -}; - -int __init tee_tz_init(void); -void tee_tz_exit(void); - -const char *tee_tz_get_memory_pool(void); -int tee_smc_call(struct smc_param *param); - -extern struct tee_driver tee_tz_data; -extern struct tee_targetop TZop; - -#endif /* TEE_TZ_H */ - diff --git a/core/armv7/tee_tz_debug.c b/core/armv7/tee_tz_debug.c deleted file mode 100644 index c116d81..0000000 --- a/core/armv7/tee_tz_debug.c +++ /dev/null @@ -1,113 +0,0 @@ -/* - * 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. - * - * 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/debugfs.h> -#include <linux/module.h> -#include <linux/device.h> - -#include "generic/tee-op.h" -#include "generic/tee_driver.h" -#include "generic/tee_debug.h" -#include "tee_tz.h" - -static const char STR_CMD_HIST[] = "hist"; -static const char STR_CMD_HIST_HELP[] = "cmd"; -static const char STR_DUMP_ALLOCATOR[] = "dump"; -static const char STR_DUMP_ALLOCATOR_HELP[] = "shared memory"; - -/*****************************************************************************/ - -static ssize_t tee_write_file_settings_tz(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct device *dev = file->private_data; - char *buf; - char *str; - int val; - - buf = devm_kzalloc(dev, count, GFP_KERNEL); - if (!buf) { - dev_err(dev, "can't allocate work buffer\n"); - return count; - } - - val = simple_write_to_buffer(buf, count, ppos, user_buf, count); - if (!val) { - dev_err(dev, "no user data\n"); - goto out; - } - - str = strstr(buf, STR_DUMP_ALLOCATOR); - if (str) - tee_shm_pool_dump(dev, TZop.Allocator, true); - - str = strstr(buf, STR_CMD_HIST); - if (str) - tee_debug_dump_cmd_hist(dev, NULL, 0); - -out: - devm_kfree(dev, buf); - return count; -} - -static ssize_t tee_read_file_settings_tz( - struct file *file, char __user *user_buf, size_t count, loff_t *ppos) -{ - ssize_t ret = 0; - struct device *dev = file->private_data; - static const int MAX_SIZE = 2 * PAGE_SIZE; - char *buf; - - buf = devm_kzalloc(dev, MAX_SIZE, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - ret = snprintf(buf, MAX_SIZE, "teetz debug:\n=========\n"); - - ret += snprintf(buf + ret, MAX_SIZE - ret, "Status:\n"); - - ret += snprintf(buf + ret, MAX_SIZE - ret, - "\tOpened session:\t\t[%d]\n", tee_tz_data.count_session); - - ret += snprintf(buf + ret, MAX_SIZE - ret, - "\tMemory pool:\t[%s]\n", tee_tz_get_memory_pool()); - - ret += snprintf(buf + ret, MAX_SIZE - ret, - "\nAvailable cmd:\n\t[%s] (%s)\n\t[%s] (%s)\n", - STR_CMD_HIST, STR_CMD_HIST_HELP, - STR_DUMP_ALLOCATOR, STR_DUMP_ALLOCATOR_HELP); - - ret += snprintf(buf + ret, MAX_SIZE - ret, - "\n\ti.e.: 'echo dump > /sys/kernel/debug/tee/teetz'\n"); - - ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret); - - devm_kfree(dev, buf); - - return ret; -} - - -const struct file_operations tee_debug_fops_tee_tz = { - .open = simple_open, - .read = tee_read_file_settings_tz, - .write = tee_write_file_settings_tz, - .llseek = default_llseek, -}; - -/*****************************************************************************/ - diff --git a/core/tee_context.c b/core/tee_context.c new file mode 100644 index 0000000..30615c0 --- /dev/null +++ b/core/tee_context.c @@ -0,0 +1,299 @@ + +#include <linux/slab.h> +#include <linux/device.h> +#include <linux/types.h> +#include <linux/file.h> +#include <linux/atomic.h> +#include <linux/uaccess.h> +#include <linux/sched.h> + +#include "tee_shm.h" +#include "tee_core_priv.h" + + +/** + * tee_context_dump - Dump in a buffer the informations (ctx, sess & shm) + * associated to a tee. + */ +int tee_context_dump(struct tee *tee, char *buff, size_t len) +{ + struct list_head *ptrCtx, *ptrSess, *ptrShm; + struct tee_context *ctx; + struct tee_session *sess; + struct tee_shm *shm; + int i = 0; + int j = 0; + + int pos = 0; + + BUG_ON(!tee); + + if (len < 80) + return 0; + + mutex_lock(&tee->lock); + if (!list_empty(&tee->list_ctx)) { + list_for_each(ptrCtx, &tee->list_ctx) { + ctx = list_entry(ptrCtx, struct tee_context, entry); + + pos += sprintf(buff + pos, + "[%02d] ctx=%p (refcount=%d) (usr=%d) " + "name=\"%s\" (tgid=%d)\n", + i, ctx, + (int)atomic_read(&ctx->refcount. + refcount), + ctx->usr_client, ctx->name, ctx->tgid); + + if ((len - pos) < 80) { + pos = 0; + goto out; + } + + j = 0; + if (!list_empty(&ctx->list_sess)) { + list_for_each(ptrSess, &ctx->list_sess) { + sess = list_entry(ptrSess, + struct tee_session, + entry); + + pos += sprintf(buff + pos, + " [%02d.%d] sess=%p sessid=%08x\n", + i, j, sess, + sess->sessid); + + if ((len - pos) < 80) { + pos = 0; + goto out; + } + + j++; + } + } + + j = 0; + if (!list_empty(&ctx->list_shm)) { + list_for_each(ptrShm, &ctx->list_shm) { + shm = + list_entry(ptrShm, struct tee_shm, + entry); + + pos += sprintf(buff + pos, + " [%02d.%d] shm=%p paddr=%pad " + "kaddr=%p s=%zu(%zu)\n", + i, j, shm, + &shm->paddr, + shm->kaddr, + shm->size_req, + shm->size_alloc); + + if ((len - pos) < 80) { + pos = 0; + goto out; + } + + j++; + } + } + i++; + } + } + +out: + mutex_unlock(&tee->lock); + return pos; +} + +/** + * tee_context_create - Allocate and create a new context. + * Reference on the back-end is requested. + */ +struct tee_context *tee_context_create(struct tee *tee) +{ + int ret; + struct tee_context *ctx; + + dev_dbg(_DEV(tee), "%s: >\n", __func__); + + ctx = devm_kzalloc(_DEV(tee), sizeof(struct tee_context), GFP_KERNEL); + if (!ctx) { + dev_err(_DEV(tee), "%s: tee_context allocation failed\n", + __func__); + return ERR_PTR(-ENOMEM); + } + + kref_init(&ctx->refcount); + INIT_LIST_HEAD(&ctx->list_sess); + INIT_LIST_HEAD(&ctx->list_shm); + + ctx->tee = tee; + snprintf(ctx->name, sizeof(ctx->name), "%s", current->comm); + ctx->tgid = current->tgid; + + ret = tee_get(tee); + if (ret) { + devm_kfree(_DEV(tee), ctx); + return ERR_PTR(ret); + } + + mutex_lock(&tee->lock); + tee_inc_stats(&tee->stats[TEE_STATS_CONTEXT_IDX]); + list_add_tail(&ctx->entry, &tee->list_ctx); + mutex_unlock(&tee->lock); + + dev_dbg(_DEV(ctx->tee), "%s: < ctx=%p is created\n", __func__, ctx); + return ctx; +} + +/** + * _tee_context_do_release - Final function to release + * and free a context. + */ +static void _tee_context_do_release(struct kref *kref) +{ + struct tee_context *ctx; + struct tee *tee; + + ctx = container_of(kref, struct tee_context, refcount); + + BUG_ON(!ctx || !ctx->tee); + + tee = ctx->tee; + + dev_dbg(_DEV(tee), "%s: > ctx=%p\n", __func__, ctx); + + mutex_lock(&tee->lock); + tee_dec_stats(&tee->stats[TEE_STATS_CONTEXT_IDX]); + list_del(&ctx->entry); + mutex_unlock(&tee->lock); + + devm_kfree(_DEV(tee), ctx); + tee_put(tee); + + dev_dbg(_DEV(tee), "%s: < ctx=%p is destroyed\n", __func__, ctx); +} + +/** + * tee_context_get - Increase the reference count of + * the context. + */ +void tee_context_get(struct tee_context *ctx) +{ + BUG_ON(!ctx || !ctx->tee); + + kref_get(&ctx->refcount); + + dev_dbg(_DEV(ctx->tee), "%s: ctx=%p, kref=%d\n", __func__, + ctx, (int)atomic_read(&ctx->refcount.refcount)); +} + +static int is_in_list(struct tee *tee, struct list_head *entry) +{ + int present = 1; + mutex_lock(&tee->lock); + if ((entry->next == LIST_POISON1) && (entry->prev == LIST_POISON2)) + present = 0; + mutex_unlock(&tee->lock); + return present; +} + +/** + * tee_context_put - Decreases the reference count of + * the context. If 0, the final + * release function is called. + */ +void tee_context_put(struct tee_context *ctx) +{ + struct tee_context *_ctx = ctx; + struct tee *tee; + BUG_ON(!ctx || !ctx->tee); + tee = ctx->tee; + + if (!is_in_list(tee, &ctx->entry)) + return; + + kref_put(&ctx->refcount, _tee_context_do_release); + + dev_dbg(_DEV(tee), "%s: ctx=%p, kref=%d\n", __func__, + _ctx, (int)atomic_read(&ctx->refcount.refcount)); +} + +/** + * tee_context_destroy - Request to destroy a context. + */ +void tee_context_destroy(struct tee_context *ctx) +{ + struct tee *tee; + + if (!ctx || !ctx->tee) + return; + + tee = ctx->tee; + + dev_dbg(_DEV(tee), "%s: ctx=%p\n", __func__, ctx); + + tee_context_put(ctx); +} + +int tee_context_copy_from_client(const struct tee_context *ctx, + void *dest, const void *src, size_t size) +{ + int res = 0; + if (dest && src && (size > 0)) { + if (ctx->usr_client) + res = copy_from_user(dest, src, size); + else + memcpy(dest, src, size); + } + return res; +} + +struct tee_shm *tee_context_alloc_shm_tmp(struct tee_context *ctx, + size_t size, const void *data, + int type) +{ + struct tee_shm *shm; + + type &= (TEEC_MEM_INPUT | TEEC_MEM_OUTPUT); + + shm = tee_shm_alloc(ctx, size, TEE_SHM_MAPPED | TEE_SHM_TEMP | type); + if (IS_ERR_OR_NULL(shm)) { + dev_err(_DEV(ctx->tee), "%s: buffer allocation failed (%ld)\n", + __func__, PTR_ERR(shm)); + return shm; + } + + if (shm && (type & TEEC_MEM_INPUT)) { + if (tee_context_copy_from_client(ctx, shm->kaddr, data, size)) { + dev_err(_DEV(ctx->tee), + "%s: tee_context_copy_from_client failed\n", + __func__); + tee_shm_free(shm); + shm = NULL; + } + } + return shm; +} + +struct tee_shm *tee_context_create_tmpref_buffer(struct tee_context *ctx, + size_t size, + const void *buffer, int type) +{ + struct tee_shm *shm = NULL; + int flags; + + switch (type) { + case TEEC_MEMREF_TEMP_OUTPUT: + flags = TEEC_MEM_OUTPUT; + break; + case TEEC_MEMREF_TEMP_INPUT: + flags = TEEC_MEM_INPUT; + break; + case TEEC_MEMREF_TEMP_INOUT: + flags = TEEC_MEM_INPUT | TEEC_MEM_OUTPUT; + break; + default: + BUG_ON(1); + }; + shm = tee_context_alloc_shm_tmp(ctx, size, buffer, flags); + return shm; +} diff --git a/core/tee_core.c b/core/tee_core.c new file mode 100644 index 0000000..5fd3d3b --- /dev/null +++ b/core/tee_core.c @@ -0,0 +1,524 @@ +/* + * tee_driver.c - TEE core kernel module. + * + */ + +/* #define DEBUG */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/types.h> +#include <linux/slab.h> +#include <linux/cdev.h> +#include <linux/idr.h> +#include <linux/platform_device.h> +#include <linux/device.h> +#include <linux/fs.h> +#include <linux/file.h> +#include <linux/uaccess.h> +#include <asm-generic/ioctl.h> +#include <linux/sched.h> + +#include "linux/tee_core.h" +#include "linux/tee_ioc.h" + +#include "tee_core_priv.h" + +#include "tee_sysfs.h" +#include "tee_debugfs.h" +#include "tee_shm.h" +#include "tee_supp_com.h" + +#define _TEE_CORE_FW_VER "1:0.1" + +static char *_tee_supp_app_name = "tee-supplicant"; + +/* Store the class misc reference */ +static struct class *misc_class; + +static int device_match(struct device *device, const void *devname) +{ + struct tee *tee = dev_get_drvdata(device); + int ret = strncmp(devname, tee->name, sizeof(tee->name)); + BUG_ON(!tee); + if (ret == 0) + return 1; + else + return 0; +} + +/* + * For the kernel api. + * Get a reference on a device tee from the device needed + */ +struct tee *tee_get_tee(const char *devname) +{ + struct device *device; + if (!devname) + return NULL; + device = class_find_device(misc_class, NULL, devname, device_match); + if (!device) { + pr_err("%s:%d - can't find device [%s]\n", __func__, __LINE__, + devname); + return NULL; + } + + return dev_get_drvdata(device); +} + +void tee_inc_stats(struct tee_stats_entry *entry) +{ + entry->count++; + if (entry->count > entry->max) + entry->max = entry->count; +} + +void tee_dec_stats(struct tee_stats_entry *entry) +{ + entry->count--; +} + +/** + * tee_get - increases refcount of the tee + * @tee: [in] tee to increase refcount of + * + * @note: If tee.ops.start() callback function is available, + * it is called when refcount is equal at 1. + */ +int tee_get(struct tee *tee) +{ + int ret = 0; + + BUG_ON(!tee); + + if (atomic_inc_return(&tee->refcount) == 1) { + BUG_ON(!try_module_get(tee->ops->owner)); + dev_dbg(_DEV(tee), "%s: refcount=1 call %s::start()...\n", + __func__, tee->name); + get_device(tee->dev); + if (tee->ops->start) + ret = tee->ops->start(tee); + } + if (ret) { + put_device(tee->dev); + module_put(tee->ops->owner); + dev_err(_DEV(tee), "%s: %s::start() failed, err=%d\n", + __func__, tee->name, ret); + atomic_dec(&tee->refcount); + } else { + int count = (int)atomic_read(&tee->refcount); + dev_dbg(_DEV(tee), "%s: refcount=%d\n", __func__, count); + if (count > tee->max_refcount) + tee->max_refcount = count; + } + return ret; +} + +/** + * tee_put - decreases refcount of the tee + * @tee: [in] tee to reduce refcount of + * + * @note: If tee.ops.stop() callback function is available, + * it is called when refcount is equal at 0. + */ +int tee_put(struct tee *tee) +{ + int ret = 0; + int count; + + BUG_ON(!tee); + + if (atomic_dec_and_test(&tee->refcount)) { + dev_dbg(_DEV(tee), "%s: refcount=0 call %s::stop()...\n", + __func__, tee->name); + if (tee->ops->stop) + ret = tee->ops->stop(tee); + module_put(tee->ops->owner); + put_device(tee->dev); + } + if (ret) { + dev_err(_DEV(tee), "%s: %s::stop() has failed, ret=%d\n", + __func__, tee->name, ret); + } + + count = (int)atomic_read(&tee->refcount); + dev_dbg(_DEV(tee), "%s: refcount=%d\n", __func__, count); + return ret; +} + +static int tee_supp_open(struct tee *tee) +{ + int ret = 0; + dev_dbg(_DEV(tee), "%s: appclient=\"%s\" pid=%d\n", __func__, + current->comm, current->pid); + + BUG_ON(!tee->rpc); + + if (strncmp(_tee_supp_app_name, current->comm, + strlen(_tee_supp_app_name)) == 0) { + if (atomic_add_return(1, &tee->rpc->used) > 1) { + ret = -EBUSY; + dev_err(tee->dev, "%s: ERROR Only one Supplicant is allowed\n", + __func__); + atomic_sub(1, &tee->rpc->used); + } + } + + return ret; +} + +static void tee_supp_release(struct tee *tee) +{ + dev_dbg(_DEV(tee), "%s: appclient=\"%s\" pid=%d\n", __func__, + current->comm, current->pid); + + BUG_ON(!tee->rpc); + + if ((atomic_read(&tee->rpc->used) == 1) && + (strncmp(_tee_supp_app_name, current->comm, + strlen(_tee_supp_app_name)) == 0)) + atomic_sub(1, &tee->rpc->used); +} + +static int tee_ctx_open(struct inode *inode, struct file *filp) +{ + struct tee_context *ctx; + struct tee *tee; + int ret; + + tee = container_of(filp->private_data, struct tee, miscdev); + + BUG_ON(!tee); + BUG_ON(tee->miscdev.minor != iminor(inode)); + + dev_dbg(_DEV(tee), "%s: > name=\"%s\"\n", __func__, tee->name); + + ret = tee_supp_open(tee); + if (ret) + return ret; + + ctx = tee_context_create(tee); + if (IS_ERR_OR_NULL(ctx)) + return PTR_ERR(ctx); + + ctx->usr_client = 1; + filp->private_data = ctx; + + dev_dbg(_DEV(tee), "%s: < ctx=%p is created\n", __func__, (void *)ctx); + + return 0; +} + +static int tee_ctx_release(struct inode *inode, struct file *filp) +{ + struct tee_context *ctx = filp->private_data; + struct tee *tee; + + if (!ctx) + return -EINVAL; + + BUG_ON(!ctx->tee); + tee = ctx->tee; + BUG_ON(tee->miscdev.minor != iminor(inode)); + + dev_dbg(_DEV(tee), "%s: > ctx=%p\n", __func__, ctx); + + tee_context_destroy(ctx); + tee_supp_release(tee); + + dev_dbg(_DEV(tee), "%s: < ctx=%p is destroyed\n", __func__, ctx); + return 0; +} + +static int tee_do_create_session(struct tee_context *ctx, + struct tee_cmd_io __user *u_cmd) +{ + int ret = -EINVAL; + struct tee_cmd_io k_cmd; + struct tee *tee; + + tee = ctx->tee; + BUG_ON(!ctx->usr_client); + + dev_dbg(_DEV(tee), "%s: >\n", __func__); + + if (copy_from_user(&k_cmd, (void *)u_cmd, sizeof(struct tee_cmd_io))) { + dev_err(_DEV(tee), "%s: copy_from_user failed\n", __func__); + goto exit; + } + + if (k_cmd.fd_sess > 0) { + dev_err(_DEV(tee), "%s: invalid fd_sess %d\n", __func__, + k_cmd.fd_sess); + goto exit; + } + + if ((k_cmd.op == NULL) || (k_cmd.uuid == NULL) || + ((k_cmd.data != NULL) && (k_cmd.data_size == 0)) || + ((k_cmd.data == NULL) && (k_cmd.data_size != 0))) { + dev_err(_DEV(tee), + "%s: op or/and data parameters are not valid\n", + __func__); + goto exit; + } + + ret = tee_session_create_fd(ctx, &k_cmd); + put_user(k_cmd.err, &u_cmd->err); + put_user(k_cmd.origin, &u_cmd->origin); + if (ret) + goto exit; + + put_user(k_cmd.fd_sess, &u_cmd->fd_sess); + +exit: + dev_dbg(_DEV(tee), "%s: < ret=%d, sessfd=%d\n", __func__, ret, + k_cmd.fd_sess); + return ret; +} + +static int tee_do_shm_alloc(struct tee_context *ctx, + struct tee_shm_io __user *u_shm) +{ + int ret = -EINVAL; + struct tee_shm_io k_shm; + struct tee *tee = ctx->tee; + BUG_ON(!ctx->usr_client); + + dev_dbg(_DEV(tee), "%s: >\n", __func__); + + if (copy_from_user(&k_shm, (void *)u_shm, sizeof(struct tee_shm_io))) { + dev_err(_DEV(tee), "%s: copy_from_user failed\n", __func__); + goto exit; + } + + if ((k_shm.buffer != NULL) || (k_shm.fd_shm != 0) || + /*(k_shm.flags & ~(tee->shm_flags)) ||*/ + ((k_shm.flags & tee->shm_flags) == 0) || (k_shm.registered != 0)) { + dev_err(_DEV(tee), + "%s: shm parameters are not valid %p %d %08x %08x %d\n", + __func__, (void *)k_shm.buffer, k_shm.fd_shm, + (unsigned int)k_shm.flags, (unsigned int)tee->shm_flags, + k_shm.registered); + goto exit; + } + + ret = tee_shm_alloc_fd(ctx, &k_shm); + if (ret) + goto exit; + + put_user(k_shm.fd_shm, &u_shm->fd_shm); + +exit: + dev_dbg(_DEV(tee), "%s: < ret=%d, shmfd=%d\n", __func__, ret, + k_shm.fd_shm); + return ret; +} + +static int tee_do_get_fd_for_rpc_shm(struct tee_context *ctx, + struct tee_shm_io __user *u_shm) +{ + int ret = -EINVAL; + struct tee_shm_io k_shm; + struct tee *tee = ctx->tee; + + dev_dbg(_DEV(tee), "%s: >\n", __func__); + BUG_ON(!ctx->usr_client); + + if (copy_from_user(&k_shm, (void *)u_shm, sizeof(struct tee_shm_io))) { + dev_err(_DEV(tee), "%s: copy_from_user failed\n", __func__); + goto exit; + } + + if ((k_shm.buffer == NULL) || (k_shm.size == 0) || (k_shm.fd_shm != 0) + || (k_shm.flags & ~(tee->shm_flags)) + || ((k_shm.flags & tee->shm_flags) == 0) + || (k_shm.registered != 0)) { + dev_err(_DEV(tee), "%s: shm parameters are not valid\n", + __func__); + goto exit; + } + + ret = tee_shm_get_fd(ctx, &k_shm); + if (ret) + goto exit; + + put_user(k_shm.fd_shm, &u_shm->fd_shm); + +exit: + dev_dbg(_DEV(tee), "%s: < ret=%d, shmfd=%d\n", __func__, ret, + k_shm.fd_shm); + return ret; +} + +static long tee_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + int ret = -EINVAL; + struct tee_context *ctx = filp->private_data; + + BUG_ON(!ctx); + BUG_ON(!ctx->tee); + + dev_dbg(_DEV(ctx->tee), "%s: > cmd nr=%d\n", __func__, _IOC_NR(cmd)); + + switch (cmd) { + case TEE_OPEN_SESSION_IOC: + ret = + tee_do_create_session(ctx, (struct tee_cmd_io __user *)arg); + break; + case TEE_ALLOC_SHM_IOC: + ret = tee_do_shm_alloc(ctx, (struct tee_shm_io __user *)arg); + break; + case TEE_GET_FD_FOR_RPC_SHM_IOC: + ret = + tee_do_get_fd_for_rpc_shm(ctx, + (struct tee_shm_io __user *)arg); + break; + default: + ret = -ENOSYS; + break; + } + + dev_dbg(_DEV(ctx->tee), "%s: < ret=%d\n", __func__, ret); + + return ret; +} + +const struct file_operations tee_fops = { + .owner = THIS_MODULE, + .read = tee_supp_read, + .write = tee_supp_write, + .open = tee_ctx_open, + .release = tee_ctx_release, + .unlocked_ioctl = tee_ioctl +}; + +static void tee_plt_device_release(struct device *dev) +{ + pr_debug("%s: (dev=%p)....\n", __func__, dev); +} + +struct tee *tee_core_alloc(struct device *dev, char *name, int id, + const struct tee_ops *ops, size_t len) +{ + struct tee *tee; + + if (!dev || !name || !ops || + !ops->open || !ops->close || !ops->alloc || !ops->free) + return NULL; + + tee = devm_kzalloc(dev, sizeof(struct tee) + len, GFP_KERNEL); + if (!tee) { + dev_err(dev, "%s: kzalloc failed\n", __func__); + return NULL; + } + + if (!dev->release) + dev->release = tee_plt_device_release; + + tee->dev = dev; + tee->id = id; + tee->ops = ops; + tee->priv = &tee[1]; + + snprintf(tee->name, sizeof(tee->name), "optee%s%02d", name, tee->id); + pr_info("TEE core: Alloc the misc device \"%s\" (id=%d)\n", tee->name, + tee->id); + + tee->miscdev.parent = dev; + tee->miscdev.minor = MISC_DYNAMIC_MINOR; + tee->miscdev.name = tee->name; + tee->miscdev.fops = &tee_fops; + + mutex_init(&tee->lock); + atomic_set(&tee->refcount, 0); + INIT_LIST_HEAD(&tee->list_ctx); + INIT_LIST_HEAD(&tee->list_rpc_shm); + + tee->state = TEE_OFFLINE; + + tee_supp_init(tee); + + return tee; +} +EXPORT_SYMBOL(tee_core_alloc); + +int tee_core_add(struct tee *tee) +{ + int rc = 0; + + if (!tee) + return -EINVAL; + + rc = misc_register(&tee->miscdev); + if (rc != 0) { + pr_err("TEE Core: misc_register() failed name=\"%s\"\n", + tee->name); + return rc; + } + + dev_set_drvdata(tee->miscdev.this_device, tee); + + tee_init_sysfs(tee); + tee_create_debug_dir(tee); + + /* Register a static reference on the class misc + * to allow finding device by class */ + BUG_ON(!tee->miscdev.this_device->class); + if (misc_class) + BUG_ON(misc_class != tee->miscdev.this_device->class); + else + misc_class = tee->miscdev.this_device->class; + + pr_info("TEE Core: Register the misc device \"%s\" (id=%d,minor=%d)\n", + dev_name(tee->miscdev.this_device), tee->id, + tee->miscdev.minor); + return rc; +} +EXPORT_SYMBOL(tee_core_add); + +int tee_core_del(struct tee *tee) +{ + if (tee) { + pr_info("TEE Core: Destroy the misc device \"%s\" (id=%d)\n", + dev_name(tee->miscdev.this_device), tee->id); + + tee_supp_deinit(tee); + + tee_cleanup_sysfs(tee); + tee_delete_debug_dir(tee); + + if (tee->miscdev.minor != MISC_DYNAMIC_MINOR) { + pr_info("TEE Core: Deregister the misc device \"%s\" (id=%d)\n", + dev_name(tee->miscdev.this_device), tee->id); + misc_deregister(&tee->miscdev); + } + } + + return 0; +} +EXPORT_SYMBOL(tee_core_del); + +static int __init tee_core_init(void) +{ + pr_info("\nTEE Core Framework initialization (ver %s)\n", + _TEE_CORE_FW_VER); + tee_init_debugfs(); + + return 0; +} + +static void __exit tee_core_exit(void) +{ + tee_exit_debugfs(); + pr_info("TEE Core Framework unregistered\n"); +} + +module_init(tee_core_init); +module_exit(tee_core_exit); + +MODULE_AUTHOR("STMicroelectronics"); +MODULE_DESCRIPTION("STM Secure TEE Framework/Core TEEC v1.0"); +MODULE_SUPPORTED_DEVICE(""); +MODULE_VERSION(_TEE_CORE_FW_VER); +MODULE_LICENSE("GPL"); diff --git a/core/tee_core_priv.h b/core/tee_core_priv.h new file mode 100644 index 0000000..9032ece --- /dev/null +++ b/core/tee_core_priv.h @@ -0,0 +1,41 @@ + +#ifndef __TEE_CORE_PRIV_H__ +#define __TEE_CORE_PRIV_H__ + +#include "linux/tee_core.h" +#include "linux/tee_ioc.h" + +/* from tee_core_module.c */ +int tee_get(struct tee *tee); +int tee_put(struct tee *tee); + +void tee_inc_stats(struct tee_stats_entry *entry); +void tee_dec_stats(struct tee_stats_entry *entry); + +/* from tee_context.c */ +int tee_context_dump(struct tee *tee, char *buff, size_t len); + +struct tee_context *tee_context_create(struct tee *tee); +void tee_context_destroy(struct tee_context *ctx); + +void tee_context_get(struct tee_context *ctx); +void tee_context_put(struct tee_context *ctx); + +struct tee_shm *tee_context_create_tmpref_buffer(struct tee_context *ctx, + size_t size, + const void *buffer, int type); +struct tee_shm *tee_context_alloc_shm_tmp(struct tee_context *ctx, size_t size, + const void *data, int type); +int tee_context_copy_from_client(const struct tee_context *ctx, void *dest, + const void *src, size_t size); + +/* from tee_session.c */ +int tee_session_create_fd(struct tee_context *ctx, struct tee_cmd_io *cmd_io); +struct tee_session *tee_session_create_and_open(struct tee_context *ctx, + struct tee_cmd_io *cmd_io); +int tee_session_close_and_destroy(struct tee_session *sess); + +struct tee *tee_get_tee(const char *devname); +int tee_session_invoke_be(struct tee_session *sess, struct tee_cmd_io *cmd_io); + +#endif diff --git a/core/tee_debugfs.c b/core/tee_debugfs.c new file mode 100644 index 0000000..8bb7307 --- /dev/null +++ b/core/tee_debugfs.c @@ -0,0 +1,74 @@ + +#include <linux/kernel.h> +#include <linux/debugfs.h> +#include <linux/device.h> +#include <linux/uaccess.h> + +#include "linux/tee_core.h" +#include "tee_debugfs.h" + +static struct dentry *tee_debugfs_dir; + +static ssize_t tee_trace_read(struct file *filp, char __user *userbuf, + size_t count, loff_t *ppos) +{ + struct tee *tee = filp->private_data; + + char buff[258]; + int len = sprintf(buff, "device=%s\n NO LOG AVAILABLE\n", tee->name); + + return simple_read_from_buffer(userbuf, count, ppos, buff, len); +} + +static const struct file_operations log_tee_ops = { + .read = tee_trace_read, + .open = simple_open, + .llseek = generic_file_llseek, +}; + +void tee_create_debug_dir(struct tee *tee) +{ + struct dentry *entry; + struct device *dev = tee->miscdev.this_device; + + if (!tee_debugfs_dir) + return; + + tee->dbg_dir = debugfs_create_dir(dev_name(dev), tee_debugfs_dir); + if (!tee->dbg_dir) + goto error_create_file; + + entry = debugfs_create_file("log", S_IRUGO, tee->dbg_dir, + tee, &log_tee_ops); + if (!entry) + goto error_create_file; + + return; + +error_create_file: + dev_err(dev, "can't create debugfs file\n"); + tee_delete_debug_dir(tee); +} + +void tee_delete_debug_dir(struct tee *tee) +{ + if (!tee || !tee->dbg_dir) + return; + + debugfs_remove_recursive(tee->dbg_dir); +} + +void __init tee_init_debugfs(void) +{ + if (debugfs_initialized()) { + tee_debugfs_dir = debugfs_create_dir("tee", NULL); + if (IS_ERR(tee_debugfs_dir)) + pr_err("can't create debugfs dir\n"); + } +} + +void __exit tee_exit_debugfs(void) +{ + if (tee_debugfs_dir) + debugfs_remove(tee_debugfs_dir); +} diff --git a/core/tee_debugfs.h b/core/tee_debugfs.h new file mode 100644 index 0000000..d712987 --- /dev/null +++ b/core/tee_debugfs.h @@ -0,0 +1,13 @@ + +#ifndef __TEE_DEBUGFS_H__ +#define __TEE_DEBUGFS_H__ + +struct tee; + +void tee_create_debug_dir(struct tee *tee); +void tee_delete_debug_dir(struct tee *tee); + +void __init tee_init_debugfs(void); +void __exit tee_exit_debugfs(void); + +#endif /* __TEE_DEBUGFS_H__ */ diff --git a/core/tee_kernel_api.c b/core/tee_kernel_api.c new file mode 100644 index 0000000..d3242ec --- /dev/null +++ b/core/tee_kernel_api.c @@ -0,0 +1,264 @@ +/* +* Copyright (C) STMicroelectronics 2014. All rights reserved. +* +* This code is STMicroelectronics proprietary and confidential. +* Any use of the code for whatever purpose is subject to +* specific written permission of STMicroelectronics SA. +*/ + +#include <linux/module.h> +#include <linux/device.h> +#include <linux/miscdevice.h> +#include <linux/io.h> +#include <linux/vmalloc.h> + +#include "linux/tee_kernel_api.h" +#include "linux/tee_core.h" +#include "linux/tee_ioc.h" + +#include "tee_core_priv.h" +#include "tee_shm.h" +#include "tee_supp_com.h" + +#define TEE_TZ_DEVICE_NAME "opteearm3200" + +static void reset_tee_cmd(struct tee_cmd_io *cmd) +{ + cmd->fd_sess = -1; + cmd->cmd = 0; + cmd->uuid = NULL; + cmd->origin = TEEC_ORIGIN_API; + cmd->err = TEEC_SUCCESS; + cmd->data = NULL; + cmd->data_size = 0; + cmd->op = NULL; +} + +TEEC_Result TEEC_InitializeContext(const char *name, TEEC_Context *context) +{ + struct tee *tee; + struct tee_context *ctx; + pr_cont("%s: > name=\"%s\"\n", __func__, name); + + if (!context) + return TEEC_ERROR_BAD_PARAMETERS; + + context->fd = 0; + + if (name == NULL) + strncpy(context->devname, TEE_TZ_DEVICE_NAME, + sizeof(context->devname)); + else + strncpy(context->devname, name, sizeof(context->devname)); + + tee = tee_get_tee(context->devname); + if (!tee) { + pr_err("%s - can't get device [%s]\n", __func__, name); + return TEEC_ERROR_BAD_PARAMETERS; + } + + ctx = tee_context_create(tee); + if (IS_ERR_OR_NULL(ctx)) + return TEEC_ERROR_BAD_PARAMETERS; + + ctx->usr_client = 0; + + /* TODO fixme will not work on 64-bit platform */ + context->fd = (int)(uintptr_t)ctx; + BUG_ON(ctx != (struct tee_context *)(uintptr_t)context->fd); + + pr_cont("%s: < ctx=%p is created\n", __func__, (void *)ctx); + return TEEC_SUCCESS; +} +EXPORT_SYMBOL(TEEC_InitializeContext); + +TEEC_Result TEEC_FinalizeContext(TEEC_Context *context) +{ + if (!context || !context->fd) { + pr_err("%s - can't release context %p:[%s]\n", __func__, + context, (context + && context->devname) ? context->devname : ""); + return TEEC_ERROR_BAD_PARAMETERS; + } + /* TODO fixme will not work on 64-bit platform */ + tee_context_destroy((struct tee_context *)(uintptr_t)context->fd); + return TEEC_SUCCESS; +} +EXPORT_SYMBOL(TEEC_FinalizeContext); + +TEEC_Result TEEC_OpenSession(TEEC_Context *context, + TEEC_Session *session, + const TEEC_UUID *destination, + uint32_t connectionMethod, + const void *connectionData, + TEEC_Operation *operation, + uint32_t *return_origin) +{ + TEEC_Operation dummy_op; + struct tee_cmd_io cmd; + struct tee_session *sess; + struct tee_context *ctx; + + if (!operation) { + /* + * The code here exist because Global Platform API states that + * it is allowed to give operation as a NULL pointer. + * In kernel and secure world we in most cases don't want + * this to be NULL, hence we use this dummy operation when + * a client doesn't provide any operation. + */ + memset(&dummy_op, 0, sizeof(TEEC_Operation)); + operation = &dummy_op; + } + + if (!context || !session || !destination || !operation + || !return_origin) + return TEEC_ERROR_BAD_PARAMETERS; + + session->fd = 0; + + /* TODO fixme will not work on 64-bit platform */ + ctx = (struct tee_context *)(uintptr_t)context->fd; + reset_tee_cmd(&cmd); + cmd.op = operation; + cmd.uuid = (TEEC_UUID *) destination; + + sess = tee_session_create_and_open(ctx, &cmd); + if (IS_ERR_OR_NULL(sess)) { + if (cmd.origin) + *return_origin = cmd.origin; + else + *return_origin = TEEC_ORIGIN_COMMS; + if (cmd.err) + return cmd.err; + else + return TEEC_ERROR_COMMUNICATION; + } else { + *return_origin = cmd.origin; + /* TODO fixme will not work on 64-bit platform */ + session->fd = (int)(uintptr_t)sess; + BUG_ON(sess != (struct tee_session *)(uintptr_t)session->fd); + return cmd.err; + } +} +EXPORT_SYMBOL(TEEC_OpenSession); + +void TEEC_CloseSession(TEEC_Session *session) +{ + if (session && session->fd) { + /* TODO fixme will not work on 64-bit platform */ + struct tee_session *sess = + (struct tee_session *)(uintptr_t)session->fd; + tee_session_close_and_destroy(sess); + } +} +EXPORT_SYMBOL(TEEC_CloseSession); + +TEEC_Result TEEC_InvokeCommand(TEEC_Session *session, + uint32_t commandID, + TEEC_Operation *operation, + uint32_t *return_origin) +{ + int ret = 0; + struct tee_cmd_io cmd; + struct tee_session *sess; + + if (!session || !operation || !return_origin || !session->fd) + return TEEC_ERROR_BAD_PARAMETERS; + + /* TODO fixme will not work on 64-bit platform */ + sess = (struct tee_session *)(uintptr_t)session->fd; + reset_tee_cmd(&cmd); + cmd.cmd = commandID; + cmd.op = operation; + + ret = tee_session_invoke_be(sess, &cmd); + if (ret) { + if (cmd.origin) + *return_origin = cmd.origin; + else + *return_origin = TEEC_ORIGIN_COMMS; + if (cmd.err) + return cmd.err; + else + return TEEC_ERROR_COMMUNICATION; + } else { + *return_origin = cmd.origin; + return cmd.err; + } +} +EXPORT_SYMBOL(TEEC_InvokeCommand); + +TEEC_Result TEEC_RegisterSharedMemory(TEEC_Context *context, + TEEC_SharedMemory *sharedMem) +{ + if (!sharedMem) + return TEEC_ERROR_BAD_PARAMETERS; + + sharedMem->registered = 1; + return TEEC_SUCCESS; +} +EXPORT_SYMBOL(TEEC_RegisterSharedMemory); + +TEEC_Result TEEC_AllocateSharedMemory(TEEC_Context *context, + TEEC_SharedMemory *sharedMem) +{ + struct tee_shm *tee_shm; + struct tee_context *ctx; + + if (!context || !sharedMem) + return TEEC_ERROR_BAD_PARAMETERS; + + /* TODO fixme will not work on 64-bit platform */ + ctx = (struct tee_context *)(uintptr_t)context->fd; + + tee_shm = tee_shm_alloc(ctx, sharedMem->size, sharedMem->flags); + if (IS_ERR_OR_NULL(tee_shm)) { + pr_err + ("TEEC_AllocateSharedMemory: tee_shm_allocate(%zu) failed\n", + sharedMem->size); + return TEEC_ERROR_OUT_OF_MEMORY; + } + + pr_info("TEEC_AllocateSharedMemory (%zu) => paddr = %p, flags %x\n", + sharedMem->size, (void *)tee_shm->paddr, tee_shm->flags); + + sharedMem->buffer = ioremap_nocache(tee_shm->paddr, sharedMem->size); + if (!sharedMem->buffer) { + pr_err("TEEC_AllocateSharedMemory: ioremap_nocache(%p, %zu) failed\n", + (void *)tee_shm->paddr, sharedMem->size); + tee_shm_free(tee_shm); + return TEEC_ERROR_OUT_OF_MEMORY; + } + + sharedMem->registered = 0; + sharedMem->flags |= tee_shm->flags; + /* TODO fixme will not work on 64-bit platform */ + sharedMem->d.fd = (int)(uintptr_t)tee_shm; + BUG_ON(tee_shm != (struct tee_shm *)(uintptr_t)sharedMem->d.fd); + + return TEEC_SUCCESS; +} +EXPORT_SYMBOL(TEEC_AllocateSharedMemory); + +void TEEC_ReleaseSharedMemory(TEEC_SharedMemory *sharedMemory) +{ + struct tee_shm *shm; + + if (!sharedMemory) + return; + + if (sharedMemory->registered) + return; + + /* TODO fixme will not work on 64-bit platform */ + shm = (struct tee_shm *)(uintptr_t)sharedMemory->d.fd; + + pr_info("TEEC_ReleaseSharedMemory (vaddr = %p)\n", + sharedMemory->buffer); + + iounmap(sharedMemory->buffer); + sharedMemory->buffer = NULL; + tee_shm_free(shm); +} +EXPORT_SYMBOL(TEEC_ReleaseSharedMemory); diff --git a/generic/tee_mutex_wait.c b/core/tee_mutex_wait.c index 3648588..0574726 100644 --- a/generic/tee_mutex_wait.c +++ b/core/tee_mutex_wait.c @@ -16,8 +16,6 @@ */ #include <linux/slab.h> #include "tee_mutex_wait.h" -#include "tee-op.h" -#include "tee_driver.h" struct tee_mutex_wait { struct list_head link; @@ -69,10 +67,10 @@ static void tee_mutex_wait_delete_entry(struct tee_mutex_wait *w) kfree(w); } -void tee_mutex_wait_delete(struct device *dev, u32 key) +void tee_mutex_wait_delete(struct device *dev, + struct tee_mutex_wait_private *priv, + u32 key) { - struct tee_mutex_wait_private *priv = - &tee_get_drvdata(dev)->mutex_wait; struct tee_mutex_wait *w; mutex_lock(&priv->mu); @@ -86,11 +84,12 @@ void tee_mutex_wait_delete(struct device *dev, u32 key) mutex_unlock(&priv->mu); } +EXPORT_SYMBOL(tee_mutex_wait_delete); -void tee_mutex_wait_wakeup(struct device *dev, u32 key, u32 wait_after) +void tee_mutex_wait_wakeup(struct device *dev, + struct tee_mutex_wait_private *priv, + u32 key, u32 wait_after) { - struct tee_mutex_wait_private *priv = - &tee_get_drvdata(dev)->mutex_wait; struct tee_mutex_wait *w = tee_mutex_wait_get(dev, priv, key); if (!w) @@ -101,11 +100,12 @@ void tee_mutex_wait_wakeup(struct device *dev, u32 key, u32 wait_after) mutex_unlock(&w->mu); complete(&w->comp); } +EXPORT_SYMBOL(tee_mutex_wait_wakeup); -void tee_mutex_wait_sleep(struct device *dev, u32 key, u32 wait_tick) +void tee_mutex_wait_sleep(struct device *dev, + struct tee_mutex_wait_private *priv, + u32 key, u32 wait_tick) { - struct tee_mutex_wait_private *priv = - &tee_get_drvdata(dev)->mutex_wait; struct tee_mutex_wait *w = tee_mutex_wait_get(dev, priv, key); u32 wait_after; @@ -119,6 +119,7 @@ void tee_mutex_wait_sleep(struct device *dev, u32 key, u32 wait_tick) if (TICK_GT(wait_tick, wait_after)) wait_for_completion_timeout(&w->comp, HZ); } +EXPORT_SYMBOL(tee_mutex_wait_sleep); int tee_mutex_wait_init(struct tee_mutex_wait_private *priv) { @@ -126,6 +127,7 @@ int tee_mutex_wait_init(struct tee_mutex_wait_private *priv) INIT_LIST_HEAD(&priv->db); return 0; } +EXPORT_SYMBOL(tee_mutex_wait_init); void tee_mutex_wait_exit(struct tee_mutex_wait_private *priv) { @@ -143,3 +145,4 @@ void tee_mutex_wait_exit(struct tee_mutex_wait_private *priv) tee_mutex_wait_delete_entry(w); } } +EXPORT_SYMBOL(tee_mutex_wait_exit); diff --git a/generic/tee_mutex_wait.h b/core/tee_mutex_wait.h index 0c359ac..20bd09d 100644 --- a/generic/tee_mutex_wait.h +++ b/core/tee_mutex_wait.h @@ -28,8 +28,15 @@ struct tee_mutex_wait_private { int tee_mutex_wait_init(struct tee_mutex_wait_private *priv); void tee_mutex_wait_exit(struct tee_mutex_wait_private *priv); -void tee_mutex_wait_delete(struct device *dev, u32 key); -void tee_mutex_wait_wakeup(struct device *dev, u32 key, u32 wait_after); -void tee_mutex_wait_sleep(struct device *dev, u32 key, u32 wait_tick); + +void tee_mutex_wait_delete(struct device *dev, + struct tee_mutex_wait_private *priv, + u32 key); +void tee_mutex_wait_wakeup(struct device *dev, + struct tee_mutex_wait_private *priv, + u32 key, u32 wait_after); +void tee_mutex_wait_sleep(struct device *dev, + struct tee_mutex_wait_private *priv, + u32 key, u32 wait_tick); #endif /*TEE_MUTEX_WAIT_H*/ diff --git a/core/tee_session.c b/core/tee_session.c new file mode 100644 index 0000000..70166f2 --- /dev/null +++ b/core/tee_session.c @@ -0,0 +1,1042 @@ + +#include <linux/slab.h> +#include <linux/device.h> +#include <linux/types.h> +#include <linux/file.h> +#include <linux/atomic.h> +#include <linux/uaccess.h> +#include <linux/anon_inodes.h> + +#include "tee_shm.h" +#include "tee_core_priv.h" + +static int _init_tee_cmd(struct tee_session *sess, struct tee_cmd_io *cmd_io, + struct tee_cmd *cmd); +static void _update_client_tee_cmd(struct tee_session *sess, + struct tee_cmd_io *cmd_io, + struct tee_cmd *cmd); +static void _release_tee_cmd(struct tee_session *sess, struct tee_cmd *cmd); + +#define _DEV_TEE _DEV(sess->ctx->tee) + +#define INMSG dev_dbg(_DEV_TEE, "%s: >\n", __func__) +#define OUTMSG(val) dev_dbg(_DEV_TEE, "%s: < %d\n", __func__, (int)val) + +#define _UUID_STR_SIZE 35 +static char *_uuid_to_str(const TEEC_UUID *uuid) +{ + static char uuid_str[_UUID_STR_SIZE]; + + if (uuid) { + sprintf(uuid_str, + "%08x-%04x-%04x-%02x%02x%02x%02x%02x%02x%02x%02x", + uuid->timeLow, uuid->timeMid, uuid->timeHiAndVersion, + uuid->clockSeqAndNode[0], uuid->clockSeqAndNode[1], + uuid->clockSeqAndNode[2], uuid->clockSeqAndNode[3], + uuid->clockSeqAndNode[4], uuid->clockSeqAndNode[5], + uuid->clockSeqAndNode[6], uuid->clockSeqAndNode[7]); + } else { + sprintf(uuid_str, "NULL"); + } + + return uuid_str; +} + +static int tee_copy_from_user(struct tee_context *ctx, void *to, void *from, + size_t size) +{ + if (ctx->usr_client) + return copy_from_user(to, from, size); + else { + memcpy(to, from, size); + return 0; + } +} + +static int tee_copy_to_user(struct tee_context *ctx, void *to, void *from, + size_t size) +{ + if (ctx->usr_client) + return copy_to_user(to, from, size); + else { + memcpy(to, from, size); + return 0; + } +} + +/* Defined as macro to let the put_user macro see the types */ +#define tee_put_user(ctx, from, to) \ + do { \ + if ((ctx)->usr_client) \ + put_user(from, to); \ + else \ + *to = from; \ + } while (0) + +static inline int tee_session_is_opened(struct tee_session *sess) +{ + if (sess && sess->sessid) + return (sess->sessid != 0); + return 0; +} + +static int tee_session_open_be(struct tee_session *sess, + struct tee_cmd_io *cmd_io) +{ + int ret = -EINVAL; + struct tee *tee; + struct tee_cmd cmd; + + BUG_ON(!sess || !sess->ctx || !sess->ctx->tee); + + tee = sess->ctx->tee; + + dev_dbg(_DEV(tee), "%s: > open a new session", __func__); + + sess->sessid = 0; + ret = _init_tee_cmd(sess, cmd_io, &cmd); + if (ret) + goto out; + + if (cmd.uuid) { + dev_dbg(_DEV(tee), "%s: UUID=%s\n", __func__, + _uuid_to_str((TEEC_UUID *) cmd.uuid->kaddr)); + } + + ret = tee->ops->open(sess, &cmd); + if (ret == 0) + _update_client_tee_cmd(sess, cmd_io, &cmd); + else { + /* propagate the reason of the error */ + cmd_io->origin = cmd.origin; + cmd_io->err = cmd.err; + } + +out: + _release_tee_cmd(sess, &cmd); + dev_dbg(_DEV(tee), "%s: < ret=%d, sessid=%08x", __func__, ret, + sess->sessid); + return ret; +} + +int tee_session_invoke_be(struct tee_session *sess, struct tee_cmd_io *cmd_io) +{ + int ret = -EINVAL; + struct tee *tee; + struct tee_cmd cmd; + + BUG_ON(!sess || !sess->ctx || !sess->ctx->tee); + + tee = sess->ctx->tee; + + dev_dbg(_DEV(tee), "%s: > sessid=%08x, cmd=0x%08x\n", __func__, + sess->sessid, cmd_io->cmd); + + ret = _init_tee_cmd(sess, cmd_io, &cmd); + if (ret) + goto out; + + ret = tee->ops->invoke(sess, &cmd); + if (!ret) + _update_client_tee_cmd(sess, cmd_io, &cmd); + else { + /* propagate the reason of the error */ + cmd_io->origin = cmd.origin; + cmd_io->err = cmd.err; + } + +out: + _release_tee_cmd(sess, &cmd); + dev_dbg(_DEV(tee), "%s: < ret=%d", __func__, ret); + return ret; +} + +static int tee_session_close_be(struct tee_session *sess) +{ + int ret = -EINVAL; + struct tee *tee; + + BUG_ON(!sess || !sess->ctx || !sess->ctx->tee); + + tee = sess->ctx->tee; + + dev_dbg(_DEV(tee), "%s: > sessid=%08x", __func__, sess->sessid); + + ret = tee->ops->close(sess); + sess->sessid = 0; + + dev_dbg(_DEV(tee), "%s: < ret=%d", __func__, ret); + return ret; +} + +static int tee_session_cancel_be(struct tee_session *sess, + struct tee_cmd_io *cmd_io) +{ + int ret = -EINVAL; + struct tee *tee; + struct tee_cmd cmd; + + BUG_ON(!sess || !sess->ctx || !sess->ctx->tee); + + tee = sess->ctx->tee; + + dev_dbg(_DEV(tee), "%s: > sessid=%08x, cmd=0x%08x\n", __func__, + sess->sessid, cmd_io->cmd); + + ret = _init_tee_cmd(sess, cmd_io, &cmd); + if (ret) + goto out; + + ret = tee->ops->cancel(sess, &cmd); + +out: + _release_tee_cmd(sess, &cmd); + dev_dbg(_DEV(tee), "%s: < ret=%d", __func__, ret); + return ret; +} + +static int tee_do_invoke_command(struct tee_session *sess, + struct tee_cmd_io __user *u_cmd) +{ + int ret = -EINVAL; + struct tee *tee; + struct tee_cmd_io k_cmd; + struct tee_context *ctx; + + BUG_ON(!sess->ctx); + BUG_ON(!sess->ctx->tee); + ctx = sess->ctx; + tee = sess->ctx->tee; + + dev_dbg(_DEV(tee), "%s: > sessid=%08x\n", __func__, sess->sessid); + + BUG_ON(!sess->sessid); + + if (tee_copy_from_user + (ctx, &k_cmd, (void *)u_cmd, sizeof(struct tee_cmd_io))) { + dev_err(_DEV(tee), "%s: tee_copy_from_user failed\n", __func__); + goto exit; + } + + if ((k_cmd.op == NULL) || (k_cmd.uuid != NULL) || + (k_cmd.data != NULL) || (k_cmd.data_size != 0)) { + dev_err(_DEV(tee), + "%s: op or/and data parameters are not valid\n", + __func__); + goto exit; + } + + ret = tee_session_invoke_be(sess, &k_cmd); + if (ret) + dev_err(_DEV(tee), "%s: tee_invoke_command failed\n", __func__); + + tee_put_user(ctx, k_cmd.err, &u_cmd->err); + tee_put_user(ctx, k_cmd.origin, &u_cmd->origin); + +exit: + dev_dbg(_DEV(tee), "%s: < ret=%d\n", __func__, ret); + return ret; +} + +static int tee_do_cancel_cmd(struct tee_session *sess, + struct tee_cmd_io __user *u_cmd) +{ + int ret = -EINVAL; + struct tee *tee; + struct tee_cmd_io k_cmd; + struct tee_context *ctx; + + BUG_ON(!sess->ctx); + BUG_ON(!sess->ctx->tee); + ctx = sess->ctx; + tee = sess->ctx->tee; + + dev_dbg(sess->ctx->tee->dev, "%s: > sessid=%08x\n", __func__, + sess->sessid); + + BUG_ON(!sess->sessid); + + if (tee_copy_from_user + (ctx, &k_cmd, (void *)u_cmd, sizeof(struct tee_cmd_io))) { + dev_err(_DEV(tee), "%s: tee_copy_from_user failed\n", __func__); + goto exit; + } + + if ((k_cmd.op == NULL) || (k_cmd.uuid != NULL) || + (k_cmd.data != NULL) || (k_cmd.data_size != 0)) { + dev_err(_DEV(tee), + "%s: op or/and data parameters are not valid\n", + __func__); + goto exit; + } + + ret = tee_session_cancel_be(sess, &k_cmd); + if (ret) + dev_err(_DEV(tee), "%s: tee_invoke_command failed\n", __func__); + + tee_put_user(ctx, k_cmd.err, &u_cmd->err); + tee_put_user(ctx, k_cmd.origin, &u_cmd->origin); + +exit: + dev_dbg(_DEV(tee), "%s: < ret=%d", __func__, ret); + return ret; +} + +static long tee_session_ioctl(struct file *filp, unsigned int cmd, + unsigned long arg) +{ + struct tee *tee; + struct tee_session *sess = filp->private_data; + int ret; + + BUG_ON(!sess || !sess->ctx || !sess->ctx->tee); + + tee = sess->ctx->tee; + + dev_dbg(_DEV(tee), "%s: > cmd nr=%d\n", __func__, _IOC_NR(cmd)); + + switch (cmd) { + case TEE_INVOKE_COMMAND_IOC: + ret = + tee_do_invoke_command(sess, + (struct tee_cmd_io __user *)arg); + break; + case TEE_REQUEST_CANCELLATION_IOC: + ret = tee_do_cancel_cmd(sess, (struct tee_cmd_io __user *)arg); + break; + default: + ret = -ENOSYS; + break; + } + + dev_dbg(_DEV(tee), "%s: < ret=%d\n", __func__, ret); + + return ret; +} + +static int tee_session_release(struct inode *inode, struct file *filp) +{ + struct tee_session *sess = filp->private_data; + int ret = 0; + struct tee *tee; + + BUG_ON(!sess || !sess->ctx || !sess->ctx->tee); + tee = sess->ctx->tee; + + ret = tee_session_close_and_destroy(sess); + return ret; +} + +const struct file_operations tee_session_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = tee_session_ioctl, + .release = tee_session_release, +}; + +int tee_session_close_and_destroy(struct tee_session *sess) +{ + int ret; + struct tee *tee; + struct tee_context *ctx; + + if (!sess || !sess->ctx || !sess->ctx->tee) + return -EINVAL; + + ctx = sess->ctx; + tee = ctx->tee; + + dev_dbg(_DEV(tee), "%s: > sess=%p\n", __func__, sess); + + if (!tee_session_is_opened(sess)) + return -EINVAL; + + ret = tee_session_close_be(sess); + + mutex_lock(&sess->ctx->tee->lock); + tee_dec_stats(&tee->stats[TEE_STATS_SESSION_IDX]); + list_del(&sess->entry); + mutex_unlock(&sess->ctx->tee->lock); + + devm_kfree(_DEV(tee), sess); + tee_context_put(ctx); + tee_put(tee); + + dev_dbg(_DEV(tee), "%s: <\n", __func__); + return ret; +} + +struct tee_session *tee_session_create_and_open(struct tee_context *ctx, + struct tee_cmd_io *cmd_io) +{ + int ret = 0; + struct tee_session *sess; + struct tee *tee; + + BUG_ON(!ctx->tee); + + tee = ctx->tee; + + dev_dbg(_DEV(tee), "%s: >\n", __func__); + ret = tee_get(tee); + if (ret) + return ERR_PTR(-EBUSY); + + sess = devm_kzalloc(_DEV(tee), sizeof(struct tee_session), GFP_KERNEL); + if (!sess) { + dev_err(_DEV(tee), "%s: tee_session allocation() failed\n", + __func__); + return ERR_PTR(-ENOMEM); + } + + tee_context_get(ctx); + sess->ctx = ctx; + + ret = tee_session_open_be(sess, cmd_io); + if (ret || !sess->sessid || cmd_io->err) { + dev_err(_DEV(tee), "%s: ERROR ret=%d (err=0x%08x, org=%d, sessid=0x%08x)\n", + __func__, ret, cmd_io->err, + cmd_io->origin, sess->sessid); + tee_put(tee); + tee_context_put(ctx); + devm_kfree(_DEV(tee), sess); + if (ret) + return ERR_PTR(ret); + else + return NULL; + } + + mutex_lock(&tee->lock); + tee_inc_stats(&tee->stats[TEE_STATS_SESSION_IDX]); + list_add_tail(&sess->entry, &ctx->list_sess); + mutex_unlock(&tee->lock); + + dev_dbg(_DEV(tee), "%s: < sess=%p\n", __func__, sess); + return sess; +} + +int tee_session_create_fd(struct tee_context *ctx, struct tee_cmd_io *cmd_io) +{ + int ret; + struct tee_session *sess; + struct tee *tee = ctx->tee; + + BUG_ON(cmd_io->fd_sess > 0); + + dev_dbg(_DEV(tee), "%s: >\n", __func__); + + sess = tee_session_create_and_open(ctx, cmd_io); + if (IS_ERR_OR_NULL(sess)) { + ret = PTR_ERR(sess); + dev_dbg(_DEV(tee), "%s: ERROR can't create the session (ret=%d, err=0x%08x, org=%d)\n", + __func__, ret, cmd_io->err, cmd_io->origin); + cmd_io->fd_sess = -1; + goto out; + } + + /* Retrieve a fd */ + cmd_io->fd_sess = -1; + ret = + anon_inode_getfd("tee_session", &tee_session_fops, sess, O_CLOEXEC); + if (ret < 0) { + dev_err(_DEV(tee), "%s: ERROR can't get a fd (ret=%d)\n", + __func__, ret); + tee_session_close_and_destroy(sess); + goto out; + } + cmd_io->fd_sess = ret; + ret = 0; + +out: + dev_dbg(_DEV(tee), "%s: < ret=%d, sess=%p, fd=%d\n", __func__, + ret, sess, cmd_io->fd_sess); + return ret; +} + +static bool tee_session_is_supported_type(struct tee_session *sess, int type) +{ + switch (type) { + case TEEC_NONE: + case TEEC_VALUE_INPUT: + case TEEC_VALUE_OUTPUT: + case TEEC_VALUE_INOUT: + case TEEC_MEMREF_TEMP_INPUT: + case TEEC_MEMREF_TEMP_OUTPUT: + case TEEC_MEMREF_TEMP_INOUT: + case TEEC_MEMREF_WHOLE: + case TEEC_MEMREF_PARTIAL_INPUT: + case TEEC_MEMREF_PARTIAL_OUTPUT: + case TEEC_MEMREF_PARTIAL_INOUT: + return true; + default: + dev_err(_DEV_TEE, "type is invalid (type %02x)\n", type); + return false; + } +} + +static int _copy_op(struct tee_session *sess, struct tee_cmd_io *cmd_io, + struct tee_cmd *cmd) +{ + int res = -EINVAL; + int idx; + TEEC_Operation op; + struct tee_data *param = &cmd->param; + struct tee *tee; + struct tee_context *ctx; + + BUG_ON(!sess->ctx); + BUG_ON(!sess->ctx->tee); + ctx = sess->ctx; + tee = sess->ctx->tee; + + dev_dbg(_DEV(tee), "%s: > sessid=%08x\n", __func__, sess->sessid); + + if (tee_context_copy_from_client + (sess->ctx, &op, cmd_io->op, sizeof(TEEC_Operation))) + goto out; + + cmd->param.type_original = op.paramTypes; + + if (cmd->param.type_original == TEEC_PARAM_TYPES(TEEC_NONE, + TEEC_NONE, TEEC_NONE, TEEC_NONE)) { + param->type = cmd->param.type_original; + res = 0; + goto out; + } + + for (idx = 0; idx < TEEC_CONFIG_PAYLOAD_REF_COUNT; ++idx) { + int type = TEEC_PARAM_TYPE_GET(op.paramTypes, idx); + switch (type) { + case TEEC_NONE: + break; + case TEEC_VALUE_INPUT: + case TEEC_VALUE_OUTPUT: + case TEEC_VALUE_INOUT: + param->params[idx].value = op.params[idx].value; + dev_dbg(_DEV_TEE, + "%s: param[%d]:type=%d,a=%08x,b=%08x (VALUE)\n", + __func__, idx, type, param->params[idx].value.a, + param->params[idx].value.b); + break; + + case TEEC_MEMREF_TEMP_INPUT: + case TEEC_MEMREF_TEMP_OUTPUT: + case TEEC_MEMREF_TEMP_INOUT: + dev_dbg(_DEV_TEE, + "> param[%d]:type=%d,buffer=%p,s=%zu (TMPREF)\n", + idx, type, op.params[idx].tmpref.buffer, + op.params[idx].tmpref.size); + param->params[idx].shm = + tee_context_create_tmpref_buffer(sess->ctx, + op.params[idx]. + tmpref.size, + op.params[idx]. + tmpref.buffer, + type); + if (IS_ERR_OR_NULL(param->params[idx].shm)) + return -ENOMEM; + dev_dbg(_DEV_TEE, "< %d %p:%zu (TMPREF)\n", + idx, (void *)param->params[idx].shm->paddr, + param->params[idx].shm->size_req); + break; + + case TEEC_MEMREF_WHOLE: + if (sess->ctx->usr_client) { + if (tee_copy_from_user(ctx, ¶m->c_shm[idx], + op.params[idx].memref. + parent, + sizeof + (TEEC_SharedMemory))) { + res = TEEC_ERROR_BAD_PARAMETERS; + goto out; + } + } else + param->c_shm[idx] = + *op.params[idx].memref.parent; + + BUG_ON(!param->c_shm[idx].buffer); + BUG_ON(!param->c_shm[idx].size); + + if (param->c_shm[idx].flags == TEEC_MEM_INPUT) + type = TEEC_MEMREF_TEMP_INPUT; + else if (param->c_shm[idx].flags == TEEC_MEM_OUTPUT) + type = TEEC_MEMREF_TEMP_OUTPUT; + else if (param->c_shm[idx].flags == + (TEEC_MEM_INPUT | TEEC_MEM_OUTPUT)) + type = TEEC_MEMREF_TEMP_INOUT; + + if (check_shm + (tee, (struct tee_shm_io *)¶m->c_shm[idx])) { + dev_dbg(_DEV_TEE, + "> param[%d]:type=%d,buffer=%p, s=%zu (WHOLE)\n", + idx, type, op.params[idx].tmpref.buffer, + param->c_shm[idx].size); + + param->params[idx].shm = + tee_context_create_tmpref_buffer(sess->ctx, + param->c_shm[idx].size, + param->c_shm[idx].buffer, + type); + if (IS_ERR_OR_NULL(param->params[idx].shm)) + return -ENOMEM; + + dev_dbg(_DEV_TEE, "< %d %p:%zu (WHOLE)\n", + idx, + (void *)param->params[idx].shm->paddr, + param->params[idx].shm->size_req); + } else { + struct tee_shm *shm; + /* The buffer is already allocated by the tee + * get a reference on it + */ + shm = + tee_shm_get(sess->ctx, + (struct tee_shm_io *)¶m-> + c_shm[idx]); + if (!shm) + /* not allocated by us, + * is it a use case ? */ + BUG_ON(1); + + param->params[idx].shm = + devm_kzalloc(tee->dev, + sizeof(struct tee_shm), + GFP_KERNEL); + if (!param->params[idx].shm) + return -ENOMEM; + + param->params[idx].shm->parent = shm; + param->params[idx].shm->ctx = sess->ctx; + param->params[idx].shm->tee = tee; + param->params[idx].shm->dev = tee->dev; + param->params[idx].shm->size_req = + param->c_shm[idx].size; + param->params[idx].shm->size_alloc = 0; + param->params[idx].shm->kaddr = shm->kaddr; + param->params[idx].shm->paddr = shm->paddr; + param->params[idx].shm->flags = + shm->flags | TEE_SHM_PARENT; + } + break; + + case TEEC_MEMREF_PARTIAL_INPUT: + case TEEC_MEMREF_PARTIAL_OUTPUT: + case TEEC_MEMREF_PARTIAL_INOUT:{ + uint32_t offset = op.params[idx].memref.offset; + uint32_t size = op.params[idx].memref.size; + + if (sess->ctx->usr_client) { + if (tee_copy_from_user + (ctx, ¶m->c_shm[idx], + op.params[idx].memref.parent, + sizeof(TEEC_SharedMemory))) { + res = TEEC_ERROR_BAD_PARAMETERS; + goto out; + } + } else + param->c_shm[idx] = + *op.params[idx].memref.parent; + + dev_dbg(_DEV_TEE, + "> param[%d]:type=%d,buffer=%p, offset=%x s=%d (PARTIAL)\n", + idx, type, param->c_shm[idx].buffer, + offset, size); + + if (type == TEEC_MEMREF_PARTIAL_INPUT) + type = TEEC_MEMREF_TEMP_INPUT; + else if (type == TEEC_MEMREF_PARTIAL_OUTPUT) + type = TEEC_MEMREF_TEMP_OUTPUT; + else if (type == TEEC_MEMREF_PARTIAL_INOUT) + type = TEEC_MEMREF_TEMP_INOUT; + + + if (check_shm + (tee, + (struct tee_shm_io *)¶m->c_shm[idx])) { + + param->params[idx].shm = + tee_context_create_tmpref_buffer + (sess->ctx, size, + param->c_shm[idx].buffer + offset, + type); + if (IS_ERR_OR_NULL( + param->params[idx].shm)) + return -ENOMEM; + + } else { + struct tee_shm *shm; + /* The buffer is already allocated by + * the tee + * get a reference on it + */ + + shm = + tee_shm_get(sess->ctx, + (struct tee_shm_io *) + ¶m->c_shm[idx]); + if (!shm) + /* not allocated by us, + * is it a use case ? */ + BUG_ON(1); + + param->params[idx].shm = + devm_kzalloc(tee->dev, + sizeof(struct tee_shm), + GFP_KERNEL); + if (!param->params[idx].shm) + return -ENOMEM; + + param->params[idx].shm->parent = shm; + param->params[idx].shm->ctx = sess->ctx; + param->params[idx].shm->tee = tee; + param->params[idx].shm->dev = tee->dev; + param->params[idx].shm->size_req = size; + param->params[idx].shm->size_alloc = 0; + param->params[idx].shm->kaddr = + shm->kaddr + offset; + param->params[idx].shm->paddr = + shm->paddr + offset; + param->params[idx].shm->flags = + shm->flags | TEE_SHM_PARENT; + + + } + dev_dbg(_DEV_TEE, "< %d %p:%zu (PARTIAL)\n", + idx, + (void *)param->params[idx].shm->paddr, + param->params[idx].shm->size_req); + break; + } + default: + BUG_ON(1); + } + + param->type |= (type << (idx * 4)); + } + res = 0; + +out: + dev_dbg(_DEV(tee), "%s: < fd=%d\n", __func__, res); + return res; +} + +static int _copy_ta_image(struct tee_session *sess, struct tee_cmd_io *cmd_io, + struct tee_cmd *cmd) +{ + int res = -EINVAL; + + dev_dbg(_DEV_TEE, "%s: > data=%p uuid=%p\n", + __func__, cmd_io->data, cmd_io->uuid); + + if (((cmd_io->data != NULL) && (cmd_io->data_size == 0)) || + ((cmd_io->data == NULL) && (cmd_io->data_size != 0))) + goto out_failed; + + if ((cmd_io->data != NULL) && (cmd_io->data_size > 0)) { + dev_dbg(_DEV_TEE, "%s: copy DATA image (s=%d)...\n", __func__, + cmd_io->data_size); + cmd->ta = + tee_context_alloc_shm_tmp(sess->ctx, cmd_io->data_size, + cmd_io->data, TEEC_MEM_INPUT); + if (IS_ERR_OR_NULL(cmd->ta)) + goto out_failed; + } + + if (cmd_io->uuid != NULL) { + dev_dbg(_DEV_TEE, "%s: copy UUID value...\n", __func__); + cmd->uuid = + tee_context_alloc_shm_tmp(sess->ctx, sizeof(*cmd_io->uuid), + cmd_io->uuid, TEEC_MEM_INPUT); + if (IS_ERR_OR_NULL(cmd->uuid)) + goto out_failed; + } + + res = 0; + goto out; + +out_failed: + tee_shm_free(cmd->uuid); + tee_shm_free(cmd->ta); + +out: + dev_dbg(_DEV_TEE, "%s: < res=%d", __func__, res); + return res; +} + +static int _init_tee_cmd(struct tee_session *sess, struct tee_cmd_io *cmd_io, + struct tee_cmd *cmd) +{ + int ret = -EINVAL; + + dev_dbg(_DEV_TEE, "%s: > set tee_cmd...\n", __func__); + + memset(cmd, 0, sizeof(struct tee_cmd)); + + cmd->cmd = cmd_io->cmd; + cmd->origin = TEEC_ORIGIN_TEE; + cmd->err = TEEC_ERROR_BAD_PARAMETERS; + cmd_io->origin = cmd->origin; + cmd_io->err = cmd->err; + + ret = _copy_op(sess, cmd_io, cmd); + if (ret) + goto out; + + ret = _copy_ta_image(sess, cmd_io, cmd); + +out: + if (ret) + _release_tee_cmd(sess, cmd); + dev_dbg(_DEV_TEE, "%s: < ret=%d\n", __func__, ret); + return ret; +} + +static void _update_client_tee_cmd(struct tee_session *sess, + struct tee_cmd_io *cmd_io, + struct tee_cmd *cmd) +{ + int idx; + struct tee_context *ctx; + BUG_ON(!cmd_io); + BUG_ON(!cmd_io->op); + BUG_ON(!cmd_io->op->params); + BUG_ON(!cmd); + BUG_ON(!sess->ctx); + ctx = sess->ctx; + + dev_dbg(_DEV_TEE, "%s: returned err=0x%08x (origin=%d)\n", __func__, + cmd->err, cmd->origin); + + cmd_io->origin = cmd->origin; + cmd_io->err = cmd->err; + + if (cmd->param.type_original == TEEC_PARAM_TYPES(TEEC_NONE, + TEEC_NONE, TEEC_NONE, TEEC_NONE)) + return; + + for (idx = 0; idx < TEEC_CONFIG_PAYLOAD_REF_COUNT; ++idx) { + int type = TEEC_PARAM_TYPE_GET(cmd->param.type_original, idx); + dev_dbg(_DEV_TEE, "%s: id %d type %d\n", __func__, idx, type); + BUG_ON(!tee_session_is_supported_type(sess, type)); + switch (type) { + case TEEC_NONE: + case TEEC_VALUE_INPUT: + case TEEC_MEMREF_TEMP_INPUT: + case TEEC_MEMREF_PARTIAL_INPUT: + break; + case TEEC_VALUE_OUTPUT: + case TEEC_VALUE_INOUT:{ + dev_dbg(_DEV_TEE, "%s: a=%08x, b=%08x\n", + __func__, + cmd->param.params[idx].value.a, + cmd->param.params[idx].value.b); + if (tee_copy_to_user + (ctx, &cmd_io->op->params[idx].value, + &cmd->param.params[idx].value, + sizeof(cmd_io->op->params[idx].value))) + dev_err(_DEV_TEE, + "%s:%d: can't update %d result to user\n", + __func__, __LINE__, idx); + break; + } + case TEEC_MEMREF_TEMP_OUTPUT: + case TEEC_MEMREF_TEMP_INOUT:{ + /* Returned updated size */ + size_t size = + cmd->param.params[idx].shm->size_req; + if (size != + cmd_io->op->params[idx].tmpref.size) { + dev_dbg(_DEV_TEE, + "Size has been updated by the TA %zu != %zu\n", + size, + cmd_io->op->params[idx].tmpref. + size); + tee_put_user(ctx, size, + &cmd_io->op->params[idx]. + tmpref.size); + } + + BUG_ON(!cmd->param.params[idx].shm); + BUG_ON(! + (cmd->param.params[idx].shm-> + flags & TEE_SHM_TEMP)); + dev_dbg(_DEV_TEE, "%s: tmpref %p\n", __func__, + cmd->param.params[idx].shm->kaddr); + + /* ensure we do not exceed + * the shared buffer length */ + if (size > cmd_io->op->params[idx].tmpref.size) + dev_err(_DEV_TEE, + " *** Wrong returned size from %d:%zu > %zu\n", + idx, size, + cmd_io->op->params[idx].tmpref. + size); + + else if (tee_copy_to_user + (ctx, + cmd_io->op->params[idx].tmpref.buffer, + cmd->param.params[idx].shm->kaddr, + size)) + dev_err(_DEV_TEE, + "%s:%d: can't update %d result to user\n", + __func__, __LINE__, idx); + break; + } + case TEEC_MEMREF_WHOLE:{ + /* Returned updated size */ + size_t size = + cmd->param.params[idx].shm->size_req; + if (size != + cmd_io->op->params[idx].memref.size) { + dev_dbg(_DEV_TEE, + "Size has been updated by the TA %zu != %zu\n", + size, + cmd_io->op->params[idx].memref. + size); + tee_put_user(ctx, size, + &cmd_io->op->params[idx]. + memref.size); + } + + /* ensure we do not exceed + * the shared buffer length */ + if (size > cmd->param.c_shm[idx].size) + dev_err(_DEV_TEE, + " *** Wrong returned size from %d:%zu > %zu\n", + idx, size, + cmd->param.c_shm[idx].size); + + else if ((cmd->param.params[idx].shm->flags & + (TEE_SHM_MAPPED | TEE_SHM_TEMP)) == + (TEE_SHM_MAPPED | TEE_SHM_TEMP)) { + BUG_ON(!cmd->param.c_shm[idx].buffer); + BUG_ON(!cmd->param.c_shm[idx].size > 0); + dev_dbg(_DEV_TEE, "%s: whole %p\n", + __func__, + cmd->param.params[idx].shm-> + kaddr); + if (tee_copy_to_user + (ctx, cmd->param.c_shm[idx].buffer, + cmd->param.params[idx].shm->kaddr, + size)) + dev_err(_DEV_TEE, + "%s: can't update %d result to user\n", + __func__, idx); + } + break; + } + case TEEC_MEMREF_PARTIAL_OUTPUT: + case TEEC_MEMREF_PARTIAL_INOUT:{ + int offset = + cmd_io->op->params[idx].memref.offset; + /* Returned updated size */ + size_t size = + cmd->param.params[idx].shm->size_req; + + if (size != + cmd_io->op->params[idx].memref.size) { + dev_dbg(_DEV_TEE, + "Size has been updated by the TA %zu != %zu\n", + size, + cmd_io->op->params[idx].memref. + size); + tee_put_user(ctx, size, + &cmd_io->op->params[idx]. + memref.size); + } + + /* ensure we do not exceed + * the shared buffer length */ + if ((offset + size) > + cmd->param.c_shm[idx].size) + dev_err(_DEV_TEE, + " *** Wrong returned size from %d:%d +%zu > %zu\n", + idx, offset, size, + cmd->param.c_shm[idx].size); + + /* If we allocated a tmpref buffer, + * copy back data to the user buffer */ + else if ((cmd->param.params[idx].shm->flags & + (TEE_SHM_MAPPED | TEE_SHM_TEMP)) == + (TEE_SHM_MAPPED | TEE_SHM_TEMP)) { + BUG_ON(!cmd->param.c_shm[idx].buffer); + BUG_ON(!cmd->param.c_shm[idx].size > 0); + if (tee_copy_to_user + (ctx, + cmd->param.c_shm[idx].buffer + + offset, + cmd->param.params[idx].shm->kaddr, + size)) + dev_err(_DEV_TEE, + "%s: can't update %d result to user\n", + __func__, idx); + } + break; + } + default: + BUG_ON(1); + } + } + +} + +static void _release_tee_cmd(struct tee_session *sess, struct tee_cmd *cmd) +{ + int idx; + struct tee_context *ctx; + + BUG_ON(!cmd); + BUG_ON(!sess); + BUG_ON(!sess->ctx); + BUG_ON(!sess->ctx->tee); + + ctx = sess->ctx; + + dev_dbg(_DEV_TEE, "%s: > free the temporary objects...\n", __func__); + + tee_shm_free(cmd->ta); + tee_shm_free(cmd->uuid); + + if (cmd->param.type_original == TEEC_PARAM_TYPES(TEEC_NONE, + TEEC_NONE, TEEC_NONE, TEEC_NONE)) + goto out; + + for (idx = 0; idx < TEEC_CONFIG_PAYLOAD_REF_COUNT; ++idx) { + int type = TEEC_PARAM_TYPE_GET(cmd->param.type_original, idx); + switch (type) { + case TEEC_NONE: + case TEEC_VALUE_INPUT: + case TEEC_VALUE_OUTPUT: + case TEEC_VALUE_INOUT: + break; + case TEEC_MEMREF_TEMP_INPUT: + case TEEC_MEMREF_TEMP_OUTPUT: + case TEEC_MEMREF_TEMP_INOUT: + case TEEC_MEMREF_WHOLE: + case TEEC_MEMREF_PARTIAL_INPUT: + case TEEC_MEMREF_PARTIAL_OUTPUT: + case TEEC_MEMREF_PARTIAL_INOUT: + if (IS_ERR_OR_NULL(cmd->param.params[idx].shm)) + break; + + if ((cmd->param.params[idx].shm->flags & + (TEE_SHM_MAPPED | TEE_SHM_TEMP)) == + (TEE_SHM_MAPPED | TEE_SHM_TEMP)) { + tee_shm_free(cmd->param.params[idx].shm); + } else { + BUG_ON(!cmd->param.params[idx].shm->parent); + tee_shm_put(cmd->param.params[idx].shm->parent); + BUG_ON(!(cmd->param.params[idx].shm->flags & + TEE_SHM_PARENT)); + devm_kfree(ctx->tee->dev, + cmd->param.params[idx].shm); + } + break; + default: + BUG_ON(1); + } + } + +out: + memset(cmd, 0, sizeof(struct tee_cmd)); + dev_dbg(_DEV_TEE, "%s: <\n", __func__); +} diff --git a/core/tee_shm.c b/core/tee_shm.c new file mode 100644 index 0000000..b312b8e --- /dev/null +++ b/core/tee_shm.c @@ -0,0 +1,455 @@ + +#include <linux/types.h> +#include <linux/dma-buf.h> + +#include <linux/sched.h> +#include <linux/mm.h> + +#include "tee_core_priv.h" +#include "tee_shm.h" + +#define INMSG dev_dbg(_DEV(tee), "%s: >\n", __func__) +#define OUTMSG(val) \ + dev_dbg(_DEV(tee), "%s: < %lld\n", __func__, \ + (long long int)(uintptr_t)val) + +/* TODO +#if (sizeof(TEEC_SharedMemory) != sizeof(tee_shm)) +#error "sizeof(TEEC_SharedMemory) != sizeof(tee_shm))" +#endif +*/ + +struct tee_shm *tee_shm_alloc_from_rpc(struct tee *tee, size_t size, + uint32_t flags) +{ + struct tee_shm *shm; + + INMSG; + + shm = tee->ops->alloc(tee, size, flags); + if (IS_ERR_OR_NULL(shm)) { + dev_err(_DEV(tee), + "%s: allocation failed (s=%d,flags=0x%08x) err=%ld\n", + __func__, (int)size, flags, PTR_ERR(shm)); + shm = NULL; + } else { + mutex_lock(&tee->lock); + tee_inc_stats(&tee->stats[TEE_STATS_SHM_IDX]); + list_add_tail(&shm->entry, &tee->list_rpc_shm); + mutex_unlock(&tee->lock); + shm->ctx = NULL; + shm->tee = tee; + } + + OUTMSG(shm); + return shm; +} +EXPORT_SYMBOL(tee_shm_alloc_from_rpc); + +void tee_shm_free_from_rpc(struct tee_shm *shm) +{ + if (shm == NULL) + return; + if (shm->ctx == NULL) { + mutex_lock(&shm->tee->lock); + tee_dec_stats(&shm->tee->stats[TEE_STATS_SHM_IDX]); + list_del(&shm->entry); + mutex_unlock(&shm->tee->lock); + shm->tee->ops->free(shm); + } else + tee_shm_free(shm); +} +EXPORT_SYMBOL(tee_shm_free_from_rpc); + + +struct tee_shm *tee_shm_alloc(struct tee_context *ctx, size_t size, + uint32_t flags) +{ + struct tee_shm *shm; + struct tee *tee; + + BUG_ON(!ctx); + BUG_ON(!ctx->tee); + + tee = ctx->tee; + + INMSG; + + if (!ctx->usr_client) + flags |= TEE_SHM_FROM_KAPI; + + shm = tee->ops->alloc(tee, size, flags); + if (IS_ERR_OR_NULL(shm)) { + dev_err(_DEV(tee), + "%s: allocation failed (s=%d,flags=0x%08x) err=%ld\n", + __func__, (int)size, flags, PTR_ERR(shm)); + } else { + shm->ctx = ctx; + shm->tee = tee; + + dev_dbg(_DEV(ctx->tee), "%s: shm=%p, paddr=%p,s=%d/%d app=\"%s\" pid=%d\n", + __func__, shm, (void *)shm->paddr, (int)shm->size_req, + (int)shm->size_alloc, current->comm, current->pid); + } + + + + OUTMSG(shm); + return shm; +} + +void tee_shm_free(struct tee_shm *shm) +{ + struct tee *tee; + + if (IS_ERR_OR_NULL(shm)) + return; + tee = shm->tee; + if (tee == NULL) + pr_warn("invalid call to tee_shm_free(%p): NULL tee\n", shm); + else if (shm->ctx == NULL) + dev_warn(_DEV(tee), "tee_shm_free(%p): NULL context\n", shm); + else if (shm->ctx->tee == NULL) + dev_warn(_DEV(tee), "tee_shm_free(%p): NULL tee\n", shm); + else + shm->ctx->tee->ops->free(shm); +} + +/* + * tee_shm dma_buf operations + */ +static struct sg_table *_tee_shm_dmabuf_map_dma_buf(struct dma_buf_attachment + *attach, + enum dma_data_direction dir) +{ + return NULL; +} + +static void _tee_shm_dmabuf_unmap_dma_buf(struct dma_buf_attachment *attach, + struct sg_table *table, + enum dma_data_direction dir) +{ + return; +} + +static void _tee_shm_dmabuf_release(struct dma_buf *dmabuf) +{ + struct tee_shm *shm = dmabuf->priv; + struct device *dev; + struct tee_context *ctx; + struct tee *tee; + BUG_ON(!shm); + BUG_ON(!shm->ctx); + BUG_ON(!shm->ctx->tee); + tee = shm->ctx->tee; + + INMSG; + + ctx = shm->ctx; + dev = shm->dev; + dev_dbg(_DEV(ctx->tee), "%s: shm=%p, paddr=%p,s=%d/%d app=\"%s\" pid=%d\n", + __func__, shm, (void *)shm->paddr, (int)shm->size_req, + (int)shm->size_alloc, current->comm, current->pid); + + mutex_lock(&ctx->tee->lock); + tee_dec_stats(&tee->stats[TEE_STATS_SHM_IDX]); + list_del(&shm->entry); + mutex_unlock(&ctx->tee->lock); + + tee_shm_free(shm); + tee_put(ctx->tee); + tee_context_put(ctx); + if (dev) + put_device(dev); + + OUTMSG(0); +} + +static int _tee_shm_dmabuf_mmap(struct dma_buf *dmabuf, + struct vm_area_struct *vma) +{ + struct tee_shm *shm = dmabuf->priv; + size_t size = vma->vm_end - vma->vm_start; + struct tee *tee; + int ret; + pgprot_t prot; + BUG_ON(!shm); + BUG_ON(!shm->ctx); + BUG_ON(!shm->ctx->tee); + tee = shm->ctx->tee; + + INMSG; + + if (shm->flags & TEE_SHM_CACHED) + prot = vma->vm_page_prot; + else + prot = pgprot_noncached(vma->vm_page_prot); + + ret = + remap_pfn_range(vma, vma->vm_start, shm->paddr >> PAGE_SHIFT, size, + prot); + if (!ret) + vma->vm_private_data = (void *)shm; + + dev_dbg(_DEV(shm->ctx->tee), "%s: map the shm (p@=%p,s=%dKiB) => %x\n", + __func__, (void *)shm->paddr, (int)size / 1024, + (unsigned int)vma->vm_start); + + OUTMSG(ret); + return ret; +} + +static void *_tee_shm_dmabuf_kmap_atomic(struct dma_buf *dmabuf, + unsigned long pgnum) +{ + return NULL; +} + +static void *_tee_shm_dmabuf_kmap(struct dma_buf *dmabuf, unsigned long pgnum) +{ + return NULL; +} + +struct dma_buf_ops _tee_shm_dma_buf_ops = { + .map_dma_buf = _tee_shm_dmabuf_map_dma_buf, + .unmap_dma_buf = _tee_shm_dmabuf_unmap_dma_buf, + .release = _tee_shm_dmabuf_release, + .kmap_atomic = _tee_shm_dmabuf_kmap_atomic, + .kmap = _tee_shm_dmabuf_kmap, + .mmap = _tee_shm_dmabuf_mmap, +}; + +static int get_fd(struct tee *tee, struct tee_shm *shm) +{ + struct dma_buf *dmabuf; + int fd = -1; + + dmabuf = dma_buf_export(shm, &_tee_shm_dma_buf_ops, shm->size_alloc, + O_RDWR, NULL); + if (IS_ERR_OR_NULL(dmabuf)) { + dev_err(_DEV(tee), "%s: dmabuf: couldn't export buffer (%ld)\n", + __func__, PTR_ERR(dmabuf)); + goto out; + } + + fd = dma_buf_fd(dmabuf, O_CLOEXEC); + +out: + OUTMSG(fd); + return fd; +} + +int tee_shm_alloc_fd(struct tee_context *ctx, struct tee_shm_io *shm_io) +{ + struct tee_shm *shm; + struct tee *tee = ctx->tee; + int ret; + + INMSG; + + shm_io->fd_shm = 0; + + shm = tee_shm_alloc(ctx, shm_io->size, shm_io->flags); + if (IS_ERR_OR_NULL(shm)) { + dev_err(_DEV(tee), "%s: buffer allocation failed (%ld)\n", + __func__, PTR_ERR(shm)); + return PTR_ERR(shm); + } + + shm_io->fd_shm = get_fd(tee, shm); + if (shm_io->fd_shm <= 0) { + tee_shm_free(shm); + ret = -ENOMEM; + goto out; + } + shm->dev = get_device(tee->dev); + ret = tee_get(tee); + BUG_ON(ret); /* tee_core_get must not issue */ + tee_context_get(ctx); + + mutex_lock(&tee->lock); + tee_inc_stats(&tee->stats[TEE_STATS_SHM_IDX]); + list_add_tail(&shm->entry, &ctx->list_shm); + mutex_unlock(&tee->lock); +out: + OUTMSG(ret); + return ret; +} + +/* Buffer allocated by rpc from fw and to be accessed by the user + * Not need to be registered as it is not allocated by the user */ +int tee_shm_get_fd(struct tee_context *ctx, struct tee_shm_io *shm_io) +{ + struct tee_shm *shm = NULL; + struct tee *tee = ctx->tee; + int ret; + struct list_head *pshm; + + INMSG; + + shm_io->fd_shm = 0; + + if (!list_empty(&tee->list_rpc_shm)) { + list_for_each(pshm, &tee->list_rpc_shm) { + shm = list_entry(pshm, struct tee_shm, entry); + if ((void *)shm->paddr == shm_io->buffer) + goto found; + } + } + + dev_err(tee->dev, "Can't find shm for %p\n", (void *)shm_io->buffer); + ret = -ENOMEM; + goto out; + +found: + shm_io->fd_shm = get_fd(tee, shm); + if (shm_io->fd_shm <= 0) { + tee_shm_free(shm); + ret = -ENOMEM; + goto out; + } + + shm->ctx = ctx; + ret = tee->ops->shm_inc_ref(shm); + BUG_ON(!ret); /* to do: path error */ + mutex_lock(&tee->lock); + list_move(&shm->entry, &ctx->list_shm); + mutex_unlock(&tee->lock); + + shm->dev = get_device(tee->dev); + ret = tee_get(tee); + BUG_ON(ret); + tee_context_get(ctx); + +out: + OUTMSG(ret); + return ret; +} + +int check_shm(struct tee *tee, struct tee_shm_io *shm_io) +{ + struct vm_area_struct *vma; + struct mm_struct *mm = current->mm; + struct tee_shm *shm; + int ret = 0; + + INMSG; + + if (shm_io->flags & TEE_SHM_FROM_KAPI) { + /* TODO fixme will not work on 64-bit platform */ + shm = (struct tee_shm *)(uintptr_t)shm_io->fd_shm; + BUG_ON(!shm); + /* must be size_req but not in line with above test */ + if (shm->size_req < shm_io->size) { + dev_err(tee->dev, "[%s] %p not big enough %x %zu %zu\n", + __func__, shm_io->buffer, + (unsigned int)shm->paddr, shm->size_req, + shm_io->size); + ret = -EINVAL; + } + goto out; + } + + /* if the caller is the kernel api, active_mm is mm */ + if (!mm) + mm = current->active_mm; + + vma = find_vma(mm, (unsigned long)shm_io->buffer); + if (!vma) { + dev_err(tee->dev, "[%s] %p can't find vma\n", __func__, + shm_io->buffer); + ret = -EINVAL; + goto out; + } + + shm = vma->vm_private_data; + + /* It's a VMA => consider it a a user address */ + if (!(vma->vm_flags & (VM_IO | VM_PFNMAP))) { + dev_err(tee->dev, "[%s] %p not Contiguous %x\n", __func__, + shm_io->buffer, shm ? (unsigned int)shm->paddr : 0); + ret = -EINVAL; + goto out; + } + + /* Contiguous ? */ + if (vma->vm_end - vma->vm_start < shm_io->size) { + dev_err(tee->dev, "[%s] %p not big enough %x %ld %zu\n", + __func__, shm_io->buffer, + shm ? (unsigned int)shm->paddr : 0, + vma->vm_end - vma->vm_start, shm_io->size); + ret = -EINVAL; + } + +out: + OUTMSG(ret); + return ret; +} + +static dma_addr_t get_phy_addr(struct tee *tee, struct tee_shm_io *shm_io) +{ + struct vm_area_struct *vma; + struct mm_struct *mm = current->mm; + struct tee_shm *shm; + + INMSG; + + /* if the caller is the kernel api, active_mm is mm */ + if (!mm) + mm = current->active_mm; + + vma = find_vma(mm, (unsigned long)shm_io->buffer); + BUG_ON(!vma); + shm = vma->vm_private_data; + + OUTMSG(shm->paddr); + /* Consider it has been allowd by the TEE */ + return shm->paddr; +} + +struct tee_shm *tee_shm_get(struct tee_context *ctx, struct tee_shm_io *shm_io) +{ + struct tee_shm *shm; + struct list_head *pshm; + int ret; + dma_addr_t buffer; + struct tee *tee = ctx->tee; + + INMSG; + + if (shm_io->flags & TEE_SHM_FROM_KAPI) { + /* TODO fixme will not work on 64-bit platform */ + shm = (struct tee_shm *)(uintptr_t)shm_io->fd_shm; + BUG_ON(!shm); + ret = ctx->tee->ops->shm_inc_ref(shm); + BUG_ON(!ret); /* to do: path error */ + OUTMSG(shm); + return shm; + } + + buffer = get_phy_addr(ctx->tee, shm_io); + if (!buffer) + return NULL; + + if (!list_empty(&ctx->list_shm)) { + list_for_each(pshm, &ctx->list_shm) { + shm = list_entry(pshm, struct tee_shm, entry); + BUG_ON(!shm); + /* if this ok, do not need to get_phys_addr + * if ((void *)shm->kaddr == shm_io->buffer) { */ + if (shm->paddr == buffer) { + ret = ctx->tee->ops->shm_inc_ref(shm); + BUG_ON(!ret); + OUTMSG(shm); + return shm; + } + } + } + BUG_ON(1); + return NULL; +} + +void tee_shm_put(struct tee_shm *shm) +{ + tee_shm_free(shm); +} diff --git a/core/tee_shm.h b/core/tee_shm.h new file mode 100644 index 0000000..08ee139 --- /dev/null +++ b/core/tee_shm.h @@ -0,0 +1,20 @@ + +#ifndef __TEE_SHM_H__ +#define __TEE_SHM_H__ + +struct tee_context; +struct tee_shm_io; +struct tee; + +int tee_shm_alloc_fd(struct tee_context *ctx, struct tee_shm_io *shm_io); +int tee_shm_get_fd(struct tee_context *ctx, struct tee_shm_io *shm_io); + +struct tee_shm *tee_shm_alloc(struct tee_context *ctx, size_t size, + uint32_t flags); +void tee_shm_free(struct tee_shm *shm); + +int check_shm(struct tee *tee, struct tee_shm_io *shm_io); +struct tee_shm *tee_shm_get(struct tee_context *ctx, struct tee_shm_io *shm_io); +void tee_shm_put(struct tee_shm *shm); + +#endif /* __TEE_SHM_H__ */ diff --git a/core/tee_supp_com.c b/core/tee_supp_com.c new file mode 100644 index 0000000..2776dbc --- /dev/null +++ b/core/tee_supp_com.c @@ -0,0 +1,271 @@ +/* +* Copyright (C) STMicroelectronics 2014. All rights reserved. +* +* This code is STMicroelectronics proprietary and confidential. +* Any use of the code for whatever purpose is subject to +* specific written permission of STMicroelectronics SA. +*/ + +#include <linux/kernel.h> +#include <linux/fs.h> +#include <linux/mutex.h> +#include <linux/miscdevice.h> +#include <linux/uaccess.h> +#include <linux/anon_inodes.h> +#include <linux/semaphore.h> +#include <linux/mm.h> +#include <linux/sched.h> +#include <linux/device.h> + +#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, + TEE_SHM_TEMP | + TEE_SHM_FROM_RPC); + if (shmint == NULL) + break; + + alloc->size = size; + alloc->data = (void *)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(free->shm); + res = TEEC_RPC_OK; + break; + } + case TEE_RPC_ICMD_INVOKE: + { + if (sizeof(rpc->commToUser) < datalen) + break; + + 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); + + 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 *)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; + } + + rpc->datafromuser = (struct semaphore) + __SEMAPHORE_INITIALIZER(rpc->datafromuser, 0); + rpc->datatouser = (struct semaphore) + __SEMAPHORE_INITIALIZER(rpc->datatouser, 0); + 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; +} diff --git a/generic/tee_supp_com.h b/core/tee_supp_com.h index a9a3655..8c81756 100644 --- a/generic/tee_supp_com.h +++ b/core/tee_supp_com.h @@ -1,25 +1,17 @@ /* - * 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. - * - * 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. - */ +* Copyright (C) STMicroelectronics 2014. All rights reserved. +* +* This code is STMicroelectronics proprietary and confidential. +* Any use of the code for whatever purpose is subject to +* specific written permission of STMicroelectronics SA. +*/ + #ifndef TEE_SUPP_COMM_H #define TEE_SUPP_COMM_H -#define TEE_RPC_ICMD_ALLOCATE 0x1001 -#define TEE_RPC_ICMD_FREE 0x1002 -#define TEE_RPC_ICMD_INVOKE 0x1003 +#define TEE_RPC_ICMD_ALLOCATE 0x1001 +#define TEE_RPC_ICMD_FREE 0x1002 +#define TEE_RPC_ICMD_INVOKE 0x1003 #define TEE_RPC_NBR_BUFF 1 #define TEE_RPC_DATA_SIZE 64 @@ -58,13 +50,13 @@ struct tee_rpc_bf { }; struct tee_rpc_alloc { - uint32_t size; /* size of block */ - void *data; /* pointer to data */ - void *shm; /* pointer to an opaque data, being shm structure */ + uint32_t size; /* size of block */ + void *data; /* pointer to data */ + void *shm; /* pointer to an opaque data, being shm structure */ }; struct tee_rpc_free { - void *shm; /* pointer to an opaque data, being shm structure */ + void *shm; /* pointer to an opaque data, being shm structure */ }; struct tee_rpc_cmd { @@ -81,7 +73,7 @@ struct tee_rpc_invoke { struct tee_rpc_cmd cmds[TEE_RPC_BUFFER_NUMBER]; }; -struct tee_rpc_priv_data { +struct tee_rpc { struct tee_rpc_invoke commToUser; struct tee_rpc_invoke commFromUser; struct semaphore datatouser; @@ -89,6 +81,7 @@ struct tee_rpc_priv_data { struct mutex outsync; /* Out sync mutex */ struct mutex insync; /* In sync mutex */ struct mutex reqsync; /* Request sync mutex */ + atomic_t used; }; enum teec_rpc_result { @@ -96,18 +89,18 @@ enum teec_rpc_result { TEEC_RPC_FAIL }; -int tee_supp_init(struct tee_rpc_priv_data *rpc); -void tee_supp_exit(void); +struct tee; -enum teec_rpc_result tee_supp_cmd(struct tee_targetop *op, - uint32_t id, void *data, - unsigned int datalen); +int tee_supp_init(struct tee *tee); +void tee_supp_deinit(struct tee *tee); + +enum teec_rpc_result tee_supp_cmd(struct tee *tee, + uint32_t id, void *data, size_t datalen); ssize_t tee_supp_read(struct file *filp, char __user *buffer, - size_t length, loff_t *offset); + size_t length, loff_t *offset); ssize_t tee_supp_write(struct file *filp, const char __user *buffer, - size_t length, loff_t *offset); - + size_t length, loff_t *offset); #endif diff --git a/core/tee_sysfs.c b/core/tee_sysfs.c new file mode 100644 index 0000000..38fe28a --- /dev/null +++ b/core/tee_sysfs.c @@ -0,0 +1,193 @@ + +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/stat.h> +#include <linux/atomic.h> +#include <asm/page.h> + +#include "tee_core_priv.h" + +static ssize_t dump_show(struct device *device, + struct device_attribute *attr, char *buf) +{ + struct tee *tee = dev_get_drvdata(device); + int len; + char *tmp_buf; + + tmp_buf = kzalloc(PAGE_SIZE, GFP_KERNEL); + if (!tmp_buf) { + printk(KERN_ALERT "%s : Unable to get buf memory\n", __func__); + return -ENOMEM; + } + + len = tee_context_dump(tee, tmp_buf, PAGE_SIZE - 128); + + if (len > 0) + len = snprintf(buf, PAGE_SIZE, "%s", tmp_buf); + kfree(tmp_buf); + + return len; +} + +static ssize_t stat_show(struct device *device, + struct device_attribute *attr, char *buf) +{ + struct tee *tee = dev_get_drvdata(device); + + return snprintf(buf, PAGE_SIZE, "%d/%d %d/%d %d/%d %d/%d\n", + atomic_read(&tee->refcount), + tee->max_refcount, + tee->stats[TEE_STATS_CONTEXT_IDX].count, + tee->stats[TEE_STATS_CONTEXT_IDX].max, + tee->stats[TEE_STATS_SESSION_IDX].count, + tee->stats[TEE_STATS_SESSION_IDX].max, + tee->stats[TEE_STATS_SHM_IDX].count, + tee->stats[TEE_STATS_SHM_IDX].max); +} + +static ssize_t info_show(struct device *device, + struct device_attribute *attr, char *buf) +{ + struct tee *tee = dev_get_drvdata(device); + + return snprintf(buf, PAGE_SIZE, "%s iminor=%d dev=\"%s\" state=%d\n", + dev_name(tee->dev), tee->miscdev.minor, + dev_name(tee->miscdev.this_device), tee->state); +} + +static ssize_t name_show(struct device *device, + struct device_attribute *attr, char *buf) +{ + struct tee *tee = dev_get_drvdata(device); + + return snprintf(buf, PAGE_SIZE, "%s\n", tee->name); +} + +static ssize_t type_show(struct device *device, + struct device_attribute *attr, char *buf) +{ + struct tee *tee = dev_get_drvdata(device); + + return snprintf(buf, PAGE_SIZE, "%s\n", tee->ops->type); +} + +static ssize_t refcount_show(struct device *device, + struct device_attribute *attr, char *buf) +{ + struct tee *tee = dev_get_drvdata(device); + + return snprintf(buf, PAGE_SIZE, "%d\n", atomic_read(&tee->refcount)); +} + +static ssize_t conf_show(struct device *device, + struct device_attribute *attr, char *buf) +{ + struct tee *tee = dev_get_drvdata(device); + + return snprintf(buf, PAGE_SIZE, "0x%08x\n", tee->conf); +} + +static ssize_t test_show(struct device *device, + struct device_attribute *attr, char *buf) +{ + struct tee *tee = dev_get_drvdata(device); + + return snprintf(buf, PAGE_SIZE, "%08X\n", tee->test); +} + +static ssize_t test_store(struct device *device, + struct device_attribute *attr, const char *buf, + size_t count) +{ + struct tee *tee = dev_get_drvdata(device); + unsigned long val; + int status; + + status = kstrtoul(buf, 0, &val); + if (status) + return status; + + if ((tee->conf & TEE_CONF_TEST_MODE) == TEE_CONF_TEST_MODE) + tee->test = val; + + return count; +} + +/* + * A state-to-string lookup table, for exposing a human readable state + * via sysfs. Always keep in sync with enum tee_state + */ +static const char *const tee_state_string[] = { + "offline", + "online", + "suspended", + "running", + "crashed", + "invalid", +}; + +static ssize_t tee_show_state(struct device *device, + struct device_attribute *attr, char *buf) +{ + struct tee *tee = dev_get_drvdata(device); + + int state = tee->state > TEE_LAST ? TEE_LAST : tee->state; + + return snprintf(buf, PAGE_SIZE, "%s (%d)\n", tee_state_string[state], + tee->state); +} + +/* + * In the following, 0660 is (S_IWUGO | S_IRUGO) + */ +static struct device_attribute device_attrs[] = { + __ATTR_RO(dump), + __ATTR_RO(stat), + __ATTR_RO(info), + __ATTR(test, (0660), test_show, test_store), + __ATTR(state, S_IRUGO, tee_show_state, NULL), + __ATTR(name, S_IRUGO, name_show, NULL), + __ATTR(refcount, S_IRUGO, refcount_show, NULL), + __ATTR(type, S_IRUGO, type_show, NULL), + __ATTR(conf, S_IRUGO, conf_show, NULL), +}; + +void tee_init_sysfs(struct tee *tee) +{ + int i, error = 0; + + if (!tee) + return; + + if (dev_get_drvdata(tee->miscdev.this_device) != tee) { + dev_err(_DEV(tee), "drvdata is not valid\n"); + return; + } + + for (i = 0; i < ARRAY_SIZE(device_attrs); i++) { + error = + device_create_file(tee->miscdev.this_device, + &device_attrs[i]); + if (error) + break; + } + + if (error) { + while (--i >= 0) + device_remove_file(tee->miscdev.this_device, + &device_attrs[i]); + } + /* location /sys/class/<class name>/<dev_name()>/<name> -> + * /sys/class/misc/teelx00/info */ +} + +void tee_cleanup_sysfs(struct tee *tee) +{ + int i; + + if (!tee) + return; + + for (i = 0; i < ARRAY_SIZE(device_attrs); i++) + device_remove_file(tee->miscdev.this_device, &device_attrs[i]); +} diff --git a/core/tee_sysfs.h b/core/tee_sysfs.h new file mode 100644 index 0000000..24c3b3d --- /dev/null +++ b/core/tee_sysfs.h @@ -0,0 +1,10 @@ + +#ifndef __TEE_SYSFS_H__ +#define __TEE_SYSFS_H__ + +struct tee; + +void tee_init_sysfs(struct tee *tee); +void tee_cleanup_sysfs(struct tee *tee); + +#endif diff --git a/generic/tee-op.h b/generic/tee-op.h deleted file mode 100644 index 65aca23..0000000 --- a/generic/tee-op.h +++ /dev/null @@ -1,318 +0,0 @@ -/* - * 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. - * - * 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. - */ -#ifndef TEE_OP_H -#define TEE_OP_H - -#include <linux/mutex.h> - -#include "tee_client_api.h" -#include "tee_ioctl.h" -#include "tee_mem.h" - -#define TEE_TZ_NAME "teetz" - -/* - * Target virtualization - */ -struct tee_session; - -enum t_cmd_service_id { - /* For TEE Client API 1.0 */ - CMD_TEEC_OPEN_SESSION = 0x11000008, - CMD_TEEC_CLOSE_SESSION = 0x11000009, - CMD_TEEC_INVOKE_COMMAND = 0x1100000a, - CMD_REGISTER_RPC = 0x1100000b, /* this is NOT a GP TEE API ! */ - CMD_SET_SEC_DDR = 0x1100000c, /* this is NOT a GP TEE API ! */ - CMD_TEEC_CANCEL_COMMAND = 0x1100000d, - CMD_TEEC_REGISTER_MEMORY = 0x1100000e, - CMD_TEEC_UNREGISTER_MEMORY = 0x1100000f, - - /* Internal command */ - CMD_TEE_DEINIT_CPU = 0x11000010, - CMD_TEE_SET_CORE_TRACE_LEVEL = 0x11000012, - CMD_TEE_GET_CORE_TRACE_LEVEL = 0x11000013, - CMD_TEE_SET_TA_TRACE_LEVEL = 0x11000014, - CMD_TEE_GET_TA_TRACE_LEVEL = 0x11000015, - - CMD_REGISTER_DEF_SHM = 0x11000020, - CMD_UNREGISTER_DEF_SHM = 0x11000021 -}; - -struct tee_targetop { - struct miscdevice *miscdev; - TEEC_Result(*call_sec_world) (struct tee_session *ts, - enum t_cmd_service_id sec_cmd, - uint32_t ta_cmd, - uint32_t param_type, - TEEC_Value - params[TEEC_CONFIG_PAYLOAD_REF_COUNT], - uint32_t *origin); - TEEC_Result(*register_shm) (unsigned long paddr, unsigned long size, - void **handle); - TEEC_Result(*unregister_shm) (void *handle); - uint32_t page_size; - - struct shm_pool *Allocator; -}; - -enum TEED_State { - TEED_STATE_OPEN_DEV = 0, - TEED_STATE_OPEN_SESSION = 1 -}; - -/** - * struct tee_session - The session data of an open tee device. - * @uc: The command struct - * @id: The session ID returned and managed by the secure world - * @state: The current state of the session in the linux kernel. - * @vaddr: Virtual address for the operation memrefs currently in use. - */ -struct tee_session { - __u32 id; - enum TEED_State state; - struct mutex syncstate; /* Sync state mutex */ - - TEEC_UUID *uuid; /* !< The uuid for the trusted application */ - int tafd; /* !< Trusted App SHM file */ - void *ta; /* !< Trusted App allocated memory (in SHM) */ - __u32 tasize; /* !< Trusted App allocated memory size */ - - __u32 login; - bool userApi; - - struct tee_targetop *op; -}; - -/** - * struct tee_identity - Represents the identity of the client - * @login: Login id - * @uuid: UUID as defined above - */ -struct tee_identity { - uint32_t login; - TEEC_UUID uuid; -}; - -/* - * tee_cmd_str() - return the string corresponding to this command - * @cmd: id of the command - * @return null if error - */ -const char *tee_cmd_str(enum t_cmd_service_id cmd); - -/* - * Definitions of messages to communicate with TEE - * TODO: clean - * 1st attribute of send msg must be the service - * 1st attribute of rcv msg must be the duration - */ - -/* - * tee_msg_recv - default strcutre of TEE service output message - */ -struct tee_msg_recv { - int duration; - uint32_t res; - uint32_t origin; -}; - -/* - * tee_msg_send - generic part of the msg sent to the TEE - */ -struct tee_msg_send { - unsigned int service; -}; - -/* - * tee_open_session_data - input arg structure for TEE open session service - */ -struct tee_open_session_data { - struct ta_signed_header_t *ta; - TEEC_UUID *uuid; - uint32_t param_types; - TEEC_Value params[TEEC_CONFIG_PAYLOAD_REF_COUNT]; - struct tee_identity client_id; - uint32_t params_flags[TEEC_CONFIG_PAYLOAD_REF_COUNT]; -}; - -/* - * tee_open_session_send - input arg msg for TEE open session service - */ -struct tee_open_session_send { - struct tee_msg_send header; - struct tee_open_session_data data; -}; - -/* - * tee_open_session_recv - output arg structure for TEE open session service - */ -struct tee_open_session_recv { - struct tee_msg_recv header; - uint32_t sess; - TEEC_Value params[TEEC_CONFIG_PAYLOAD_REF_COUNT]; -}; - -/* - * tee_invoke_command_data - input arg structure for TEE invoke cmd service - */ -struct tee_invoke_command_data { - uint32_t sess; - uint32_t cmd; - uint32_t param_types; - TEEC_Value params[TEEC_CONFIG_PAYLOAD_REF_COUNT]; - struct tee_identity client_id; - uint32_t params_flags[TEEC_CONFIG_PAYLOAD_REF_COUNT]; -}; - -struct tee_invoke_command_send { - struct tee_msg_send header; - struct tee_invoke_command_data data; -}; - -/* - * tee_invoke_command_recv - output arg structure for TEE invoke cmd service - */ -struct tee_invoke_command_recv { - struct tee_msg_recv header; - TEEC_Value params[TEEC_CONFIG_PAYLOAD_REF_COUNT]; -}; - -/* - * tee_close_session_data - input arg structure for TEE close session service - */ -struct tee_close_session_data { - uint32_t sess; -}; - -/* - * tee_close_session_send - input arg msg for TEE close session service - */ -struct tee_close_session_send { - struct tee_msg_send header; - struct tee_close_session_data data; -}; - -/* - * tee_cancel_command_data - input arg structure for TEE cancel service - */ -struct tee_cancel_command_data { - uint32_t sess; - struct tee_identity client_id; -}; - -/* - * tee_cancel_command_send - input msg structure for TEE cancel service - */ -struct tee_cancel_command_send { - struct tee_msg_send header; - struct tee_cancel_command_data data; -}; - -/* - * tee_register_rpc_send_data - input arg structure for TEE register rpc service - */ -struct tee_register_rpc_send_data { - uint32_t fnk; - uint32_t bf; - uint32_t nbr_bf; -}; - -/* - * tee_register_rpc_send - input msg structure for TEE register rpc service - */ -struct tee_register_rpc_send { - struct tee_msg_send header; - struct tee_register_rpc_send_data data; -}; - -/* - * tee_register_rpc_recv_data - ouput arg structure for TEE register rpc service - */ -struct tee_register_rpc_recv_data { - uint32_t fnk; - uint32_t bf; - uint32_t nbr_bf; -}; - -/* - * tee_register_rpc_recv - ouput msg structure for TEE register rpc service - */ -struct tee_register_rpc_recv { - struct tee_msg_recv header; - struct tee_register_rpc_recv_data data; -}; - -/* - * tee_register_irqfwd_xxx - (un)register callback for interrupt forwarding - */ -struct tee_register_irqfwd_send { - struct tee_msg_send header; - struct { - unsigned long cb; - } data; -}; -struct tee_register_irqfwd_recv { - struct tee_msg_recv header; -}; - -/* - * tee_trace_level_data - input arg structure for TEE trace level service - */ -struct tee_trace_level_data { - int trace_level; -}; - -/* - * tee_trace_level_send - input msg structure for TEE trace level service - */ -struct tee_trace_level_send { - struct tee_msg_send header; - struct tee_trace_level_data data; -}; - -/* - * tee_trace_level_recv - output arg structure for TEE trace level service - */ -struct tee_trace_level_recv { - struct tee_msg_recv header; - int trace_level; -}; - -/* - * tee_core_status_out - output arg structure for TEE status service - */ -#define TEEC_STATUS_MSG_SIZE 80 - -struct tee_core_status_out { - struct tee_msg_recv header; - char raw[TEEC_STATUS_MSG_SIZE]; -}; - -/* - * tee_get_l2cc_mutex - input/output argument structures - */ -struct tee_get_l2cc_mutex_send { - struct tee_msg_send header; -}; -struct tee_get_l2cc_mutex_recv { - struct tee_msg_recv header; - struct { - unsigned long paddr; - } data; -}; - -#endif diff --git a/generic/tee_debug.c b/generic/tee_debug.c deleted file mode 100644 index 34d893b..0000000 --- a/generic/tee_debug.c +++ /dev/null @@ -1,125 +0,0 @@ -/* - * 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. - * - * 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/debugfs.h> -#include <linux/module.h> -#include <linux/device.h> - -#include "tee-op.h" -#include "tee_driver.h" -#include "tee_debug.h" - -static struct dentry *tee_debug_root; - -#define CMD_HIST_DEPTH 20 -#define CMD_HIST_FIFO_SIZE roundup_pow_of_two( \ - CMD_HIST_DEPTH * sizeof(struct tee_debug_cmd)) - -static ssize_t tee_read_file_hist(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) -{ - ssize_t ret = 0; - struct device *dev = file->private_data; - static const int MAX_SIZE = 2 * PAGE_SIZE; - char *buf; - - buf = devm_kzalloc(dev, MAX_SIZE, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - ret = tee_debug_dump_cmd_hist(dev, buf, MAX_SIZE); - - ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret); - - devm_kfree(dev, buf); - - return ret; -} - -static const struct file_operations tee_debug_fops_hist = { - .open = simple_open, - .read = tee_read_file_hist, - .llseek = default_llseek, -}; - -int tee_debug_init(struct device *dev) -{ - char buf[20]; - struct dentry *entry = 0; - int ret = 0; - struct tee_driver *tee = tee_get_drvdata(dev); - - dev_dbg(dev, ">\n"); - - ret = kfifo_alloc(&tee->cmds, CMD_HIST_FIFO_SIZE, GFP_KERNEL); - if (ret) { - dev_err(dev, "Can't allocate [%lu] bytes the fifo for the history cmds\n", - CMD_HIST_FIFO_SIZE); - ret = -ENOMEM; - goto exit; - } - - if (!tee_debug_root) { - tee_debug_root = debugfs_create_dir("tee", NULL); - if (!tee_debug_root) { - dev_err(dev, "Failed to create tee debugfs root\n"); - ret = -EIO; - goto err_dealloc; - } - } - - if (strcmp(DEV_NAME(dev), TEE_TZ_NAME) == 0) - entry = debugfs_create_file(TEE_TZ_NAME, 0644, tee_debug_root, - dev, &tee_debug_fops_tee_tz); - if (!entry) { - dev_err(dev, "Failed to create tee debugfs file\n"); - ret = -EIO; - goto err_remove; - } - - snprintf(buf, sizeof(buf), "%s_hist", DEV_NAME(dev)); - entry = debugfs_create_file(buf, 0644, tee_debug_root, - dev, &tee_debug_fops_hist); - if (!entry) { - dev_err(dev, "Failed to create tee debugfs hist file\n"); - ret = -EIO; - goto err_remove; - } - - goto exit; - -err_remove: - debugfs_remove_recursive(tee_debug_root); -err_dealloc: - kfifo_free(&tee->cmds); -exit: - dev_dbg(dev, "< [%d]\n", ret); - return ret; -} - -void tee_debug_remove(struct device *dev) -{ - struct tee_driver *tee = tee_get_drvdata(dev); - dev_dbg(dev, ">\n"); - - if (tee_debug_root) - debugfs_remove_recursive(tee_debug_root); - tee_debug_root = NULL; - - kfifo_free(&tee->cmds); - - dev_dbg(dev, "<\n"); -} diff --git a/generic/tee_debug.h b/generic/tee_debug.h deleted file mode 100644 index 0c79673..0000000 --- a/generic/tee_debug.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * 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. - * - * 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. - */ -#ifndef TEE_DEBUG_H -#define TEE_DEBUG_H - -int tee_debug_init(struct device *dev); -void tee_debug_remove(struct device *dev); - -extern const struct file_operations tee_debug_fops_tee_tz; - -#endif /* TEE_DEBUG_H */ diff --git a/generic/tee_driver.c b/generic/tee_driver.c deleted file mode 100644 index ae4a9c7..0000000 --- a/generic/tee_driver.c +++ /dev/null @@ -1,481 +0,0 @@ -/* - * 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. - * - * 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/module.h> -#include <linux/device.h> -#include <linux/fs.h> -#include <linux/cdev.h> -#include <linux/mutex.h> -#include <linux/miscdevice.h> -#include <linux/uaccess.h> -#include <linux/file.h> -#include <linux/anon_inodes.h> -#include <linux/mm.h> -#include <linux/sched.h> -#include <linux/slab.h> -#include <linux/string.h> - -#include "tee-op.h" -#include "tee_supp_com.h" -#include "tee_mem.h" -#include "tee_service.h" -#include "tee_debug.h" -#include "tee_tz.h" -#include "tee_mutex_wait.h" - - -#include "tee_driver.h" - -/******************************************************************************/ - -static TEEC_Result copy_ta( - struct device *dev, struct tee_session *ts, struct tee_cmd *ku_buffer) -{ - unsigned long paddr; - dev_dbg(dev, "> session: [0x%p]\n", ts); - - paddr = tee_shm_pool_alloc(dev, ts->op->Allocator, - ku_buffer->data_size, 0); - if (paddr == 0x0) { - dev_err(dev, "error, out of memory\n"); - return TEEC_ERROR_OUT_OF_MEMORY; - } - - ts->ta = tee_shm_pool_p2v(dev, ts->op->Allocator, paddr); - ts->tasize = ku_buffer->data_size; - ts->tafd = 0; - - if (copy_from_user(ts->ta, ku_buffer->data, ku_buffer->data_size)) { - tee_shm_pool_free(dev, ts->op->Allocator, paddr, - NULL); - return TEEC_ERROR_BAD_PARAMETERS; - } - - dev_dbg(dev, "<\n"); - - return TEEC_SUCCESS; -} - -/* - * Direct return => Linux Error - */ -static int invoke_command(struct device *dev, struct tee_session *ts, - int sec_cmd, struct tee_cmd __user *u_buffer) -{ - struct tee_cmd ku_buffer; - TEEC_Operation op; - uint32_t param_type = 0x0; - TEEC_Value params[TEEC_CONFIG_PAYLOAD_REF_COUNT]; - unsigned long tmp_allocated_memories[TEEC_CONFIG_PAYLOAD_REF_COUNT] = { - 0, }; - TEEC_Result res; - bool ta_allocated = false; - bool uid_allocated = false; - int ret = 0; - unsigned long paddr; - - if (copy_from_user - (&ku_buffer, (void *)u_buffer, sizeof(struct tee_cmd))) { - dev_err(dev, "[%s] copy_from_user failed\n", - __func__); - ret = -EINVAL; - goto exit; - } - - if (ku_buffer.op == NULL) - goto inval; - - if (ku_buffer.data && ts->ta == NULL) { - res = copy_ta(dev, ts, &ku_buffer); - if (res != TEEC_SUCCESS) - goto error; - ta_allocated = true; - } - if (ku_buffer.uuid && ts->uuid == NULL) { - res = allocate_uuid(ts); - if (res != TEEC_SUCCESS) - goto error; - uid_allocated = true; - - if (copy_from_user( - ts->uuid, ku_buffer.uuid, sizeof(TEEC_UUID))) { - ret = -EINVAL; - goto exit; - } - } - - if (copy_from_user(&op, ku_buffer.op, sizeof(TEEC_Operation))) { - ret = -EINVAL; - goto exit; - } - - res = copy_op(ts, &op, tmp_allocated_memories, ¶m_type, params); - if (res != TEEC_SUCCESS) - goto error; - - res = ts->op->call_sec_world(ts, sec_cmd, ku_buffer.cmd, param_type, - params, &ku_buffer.origin); - - if (res != TEEC_SUCCESS) { - dev_err(dev, - "invoke_command: call_sec_world , err [%x], org [%x]\n", - res, ku_buffer.origin); - - (void)uncopy_op(ts, &op, tmp_allocated_memories, params); - } else { - res = uncopy_op(ts, &op, tmp_allocated_memories, params); - } - - if (copy_to_user(ku_buffer.op, &op, sizeof(TEEC_Operation))) { - ret = -EINVAL; - goto exit; - } - - goto out; - -inval: - res = TEEC_ERROR_BAD_PARAMETERS; -error: - ku_buffer.origin = TEEC_ORIGIN_API; -out: - /* Update error code */ - put_user(res, &u_buffer->err); - put_user(ku_buffer.origin, &u_buffer->origin); - -exit: - if (ret && ta_allocated) { - paddr = tee_shm_pool_v2p( - dev, ts->op->Allocator, ts->ta); - tee_shm_pool_free( - dev, ts->op->Allocator, paddr, NULL); - } - - if (ret && uid_allocated) { - paddr = tee_shm_pool_v2p( - dev, ts->op->Allocator, ts->uuid); - tee_shm_pool_free( - dev, ts->op->Allocator, paddr, NULL); - } - - return ret; -} - -static int tee_share_mmap(struct file *filp, struct vm_area_struct *vma) -{ - struct tee_shm *shmint = (struct tee_shm *)(filp->private_data); - struct device *dev = shmint->op->miscdev->this_device; - size_t size = vma->vm_end - vma->vm_start; - pgprot_t prot; - - if (shmint != NULL) { - dev_dbg(dev, "[%s] %x => %x (+%zu)\n", __func__, - (unsigned int)shmint-> - paddr, (unsigned int)vma->vm_start, - size); - - if (tee_shm_pool_is_cached(shmint->op->Allocator)) - prot = vma->vm_page_prot; - else - prot = pgprot_noncached(vma->vm_page_prot); - - if (remap_pfn_range(vma, vma->vm_start, - shmint->paddr >> PAGE_SHIFT, size, prot)) - return -EAGAIN; - BUG_ON(vma->vm_private_data != NULL); - vma->vm_private_data = (void *)shmint->paddr; - } - - return 0; - -} - -static int tee_share_release(struct inode *inode, struct file *filp) -{ - struct tee_shm *shmint = (struct tee_shm *)(filp->private_data); - struct device *dev = shmint->op->miscdev->this_device; - - dev_dbg(dev, "> %p\n", (void *)shmint); - if (shmint != NULL) { - tee_shm_unallocate(shmint); - filp->private_data = NULL; - } - - dev_dbg(dev, "< [0]\n"); - return 0; -} - -const struct file_operations tee_share_fops = { - .owner = THIS_MODULE, - .release = tee_share_release, - .mmap = tee_share_mmap, -}; - -static int tee_open(struct inode *inode, struct file *filp) -{ - filp->private_data = - tee_create_session(filp->f_path.dentry->d_iname, true); - if (filp->private_data == NULL) - return -ENOMEM; - - return 0; -} - -static int tee_release(struct inode *inode, struct file *filp) -{ - struct tee_session *ts = filp->private_data; - - tee_delete_session(ts); - - return 0; -} - -#if (CFG_TEE_DRV_DEBUGFS == 1) -inline int tee_debug_do_dump_cmd_hist(struct device *dev, - const char line[], int ret, char *output, int max_size) -{ - if (max_size > 0 && output) - ret += snprintf(output + ret, max_size - ret, line); - else - dev_info(dev, line); - - return ret; -} - -int tee_debug_dump_cmd_hist(struct device *dev, char *output, int max_size) -{ - struct tee_debug_cmd cmd; - int age = 1; - struct tee_driver *tee = tee_get_drvdata(dev); - int ret = 0; - char tmp[256]; - - ret = tee_debug_do_dump_cmd_hist(dev, - "History of the commands\n", ret, output, max_size); - - ret = tee_debug_do_dump_cmd_hist(dev, - "\t\t\t\t\t\t\t\t\t\tduration (ms)\n", ret, output, max_size); - - ret = tee_debug_do_dump_cmd_hist(dev, - "\tdate (ms)\tsession\t\ttee cmd\t\t\tta cmd\tres\tarm\n", - ret, output, max_size); - - mutex_lock(&tee->mutex_tee); - - while (kfifo_out(&tee->cmds, &cmd, sizeof(cmd))) { - snprintf(tmp, sizeof(tmp), - "%04d\t[%lld]\t[0x%p]\t[%s]\t[%d]\t[%d]\t[%lld]\n", - age++, cmd.begin, cmd.ts, tee_cmd_str(cmd.cmd), - cmd.ta_cmd, cmd.ret, cmd.duration); - ret = tee_debug_do_dump_cmd_hist( - dev, tmp, ret, output, max_size); - } - - mutex_unlock(&tee->mutex_tee); - return ret; -} -#endif - -static long tee_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) -{ - struct tee_session *ts = (struct tee_session *)(filp->private_data); - struct device *dev = ts->op->miscdev->this_device; - __u32 state; - int ret = 0; - - dev_dbg(dev, "> cmd:[0x%x]\n", cmd); - - mutex_lock(&ts->syncstate); - state = ts->state; - mutex_unlock(&ts->syncstate); - - switch (cmd) { - case TEE_OPEN_SESSION_IOC: - if (state != TEED_STATE_OPEN_DEV) { - dev_err(dev, "[%s] invalid state\n", - __func__); - ret = -EINVAL; - } else { - ret = invoke_command(dev, ts, CMD_TEEC_OPEN_SESSION, - (struct tee_cmd *)arg); - if (ret == 0) { - mutex_lock(&ts->syncstate); - ts->state = TEED_STATE_OPEN_SESSION; - mutex_unlock(&ts->syncstate); - } - } - break; - - case TEE_CLOSE_SESSION_IOC: - if (state != TEED_STATE_OPEN_SESSION) { - dev_err(dev, "[%s] invalid state\n", - __func__); - ret = -EINVAL; - } else { - if (ts-> - op->call_sec_world(ts, CMD_TEEC_CLOSE_SESSION, - 0, 0x0, NULL, - NULL) != TEEC_SUCCESS) - ret = -EINVAL; - - mutex_lock(&ts->syncstate); - ts->state = TEED_STATE_OPEN_DEV; - mutex_unlock(&ts->syncstate); - } - - break; - - case TEE_INVOKE_COMMAND_IOC: - if (state != TEED_STATE_OPEN_SESSION) { - dev_err(dev, "[%s] invalid state\n", - __func__); - ret = -EINVAL; - } else { - ret = - invoke_command(dev, ts, CMD_TEEC_INVOKE_COMMAND, - (struct tee_cmd *)arg); - } - break; - case TEE_REQUEST_CANCELLATION_IOC: - if (state != TEED_STATE_OPEN_SESSION) { - dev_err(dev, "[%s] invalid state\n", - __func__); - ret = -EINVAL; - } else { - if (ts-> - op->call_sec_world(ts, CMD_TEEC_CANCEL_COMMAND, - 0, 0x0, NULL, - NULL) != TEEC_SUCCESS) - ret = -EINVAL; - } - break; - - case TEE_ALLOC_SHM_IOC: - { - TEEC_SharedMemory shm; - struct file *file; - struct tee_shm *shmint; - - if (copy_from_user(&shm, (void __user *)arg, - sizeof(TEEC_SharedMemory))) - return -EFAULT; - - shmint = - tee_shm_allocate(ts->op, shm.buffer, - shm.size, shm.flags); - if (shmint == NULL) - return -ENOMEM; - - shm.d.fd = get_unused_fd(); - if (shm.d.fd < 0) { - tee_shm_unallocate(shmint); - return -ENFILE; - } - - file = - anon_inode_getfile("tee_share_fd", &tee_share_fops, - shmint, O_RDWR); - if (IS_ERR_OR_NULL(file)) { - put_unused_fd(shm.d.fd); - tee_shm_unallocate(shmint); - return -ENFILE; - } - fd_install(shm.d.fd, file); - - if (copy_to_user((void __user *)arg, - &shm, sizeof(TEEC_SharedMemory))) - return -EFAULT; - }; - break; - - default: - ret = -ENOSYS; - break; - } - - dev_dbg(dev, "< [%d]\n", ret); - - return ret; -} - - -struct tee_driver *tee_get_drvdata(struct device *dev) -{ - if (strcmp(DEV_NAME(dev), TEE_TZ_NAME) == 0) - return &tee_tz_data; - BUG_ON(1); - return NULL; -} - -const struct file_operations tee_fops = { - .owner = THIS_MODULE, - .open = tee_open, - .release = tee_release, - .unlocked_ioctl = tee_ioctl, - .read = tee_supp_read, - .write = tee_supp_write, -}; - - -static int __init tee_init(void) -{ - int ret = 0; - bool tz_init = false; - bool supp_init = false; - bool wait_init = false; - - ret = tee_tz_init(); - if (ret) - goto err; - tz_init = true; - - ret = tee_supp_init(&tee_tz_data.rpc); - if (ret) - goto err; - supp_init = true; - - ret = tee_mutex_wait_init(&tee_tz_data.mutex_wait); - if (ret) - goto err; - - goto exit; -err: - if (wait_init) - tee_mutex_wait_exit(&tee_tz_data.mutex_wait); - if (supp_init) - tee_supp_exit(); - if (tz_init) - tee_tz_exit(); -exit: - return ret; -} - -static void __exit tee_exit(void) -{ - pr_info("in tee_exit\n"); - - tee_mutex_wait_exit(&tee_tz_data.mutex_wait); - - tee_supp_exit(); - - tee_tz_exit(); -} - -module_init(tee_init); -module_exit(tee_exit); - -MODULE_LICENSE("Dual BSD/GPL"); -MODULE_DESCRIPTION("Trusted Execution Enviroment driver"); - diff --git a/generic/tee_driver.h b/generic/tee_driver.h deleted file mode 100644 index fc05e90..0000000 --- a/generic/tee_driver.h +++ /dev/null @@ -1,73 +0,0 @@ -/* - * 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. - * - * 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. - */ -#ifndef TEE_DRIVER_H -#define TEE_DRIVER_H - -#include <linux/mutex.h> - -#include "tee_supp_com.h" -#include "tee_mutex_wait.h" - -/******************************************************************************/ - -/* Helpers */ -#define DEV_NAME(dev) (dev->kobj.name) - -/******************************************************************************/ - -#if (CFG_TEE_DRV_DEBUGFS == 1) - -#include <linux/kfifo.h> - -struct tee_debug_cmd { - enum t_cmd_service_id cmd; - struct tee_session *ts; - uint32_t ta_cmd; - int ret; - s64 begin; - s64 duration; -}; - -#endif /* CFG_TEE_DRV_DEBUGFS */ - -struct tee_driver { - /* protect concurrent access to the tee_driver */ - struct mutex mutex_tee; - int count_session; - char *memory_pool; - struct tee_rpc_priv_data rpc; - struct tee_mutex_wait_private mutex_wait; -#if (CFG_TEE_DRV_DEBUGFS == 1) - struct kfifo cmds; -#endif -}; - - -struct device; -extern const struct file_operations tee_fops; - -/******************************************************************************/ - -struct tee_driver *tee_get_drvdata(struct device *dev); - -#if (CFG_TEE_DRV_DEBUGFS == 1) -int tee_debug_dump_cmd_hist(struct device *dev, char *output, int max_size); -#endif - -/******************************************************************************/ - -#endif /* TEE_DRIVER_H */ diff --git a/generic/tee_kernel_api.c b/generic/tee_kernel_api.c deleted file mode 100644 index ff49af6..0000000 --- a/generic/tee_kernel_api.c +++ /dev/null @@ -1,375 +0,0 @@ -/* - * 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. - * - * 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/module.h> -#include <linux/device.h> -#include <linux/miscdevice.h> -#include <linux/io.h> -#include <linux/vmalloc.h> - -#include "linux/tee_kernel_api.h" - -#include "tee-op.h" -#include "tee_supp_com.h" -#include "tee_service.h" - -struct tee_supp_arg { - uint32_t res; - uint32_t id; - uint32_t datasize; - uint32_t reserved; - /* uint32_t instead of uint8_t in order to ensure it is 4 aligned */ - uint32_t data[TEE_RPC_DATA_SIZE / sizeof(uint32_t)]; -}; - - -struct tee_uuid { - uint32_t timeLow; - uint16_t timeMid; - uint16_t timeHiAndVersion; - uint8_t clockSeqAndNode[8]; -}; - - -struct tee_rpc_load_ta_cmd { - struct tee_uuid uuid; - void *va; -}; - -static int alloc_ta_bin(struct tee_session *ts) -{ - struct tee_supp_arg supp_arg; - struct tee_rpc_invoke *head; - struct tee_rpc_load_ta_cmd *supp_cmd; - struct tee_shm *tee_shm; - struct device *dev = ts->op->miscdev->this_device; - - dev_dbg(dev, ">\n"); - head = (struct tee_rpc_invoke *)&supp_arg.data; - head->cmd = TEE_RPC_LOAD_TA; - head->res = 0; - head->nbr_bf = 2; - - tee_shm = tee_shm_allocate( - ts->op, NULL, sizeof(struct tee_rpc_load_ta_cmd), 0); - if (!tee_shm) - return -ENOMEM; - - head->cmds[0].buffer = (void *)tee_shm->paddr; - - supp_cmd = (struct tee_rpc_load_ta_cmd *)tee_shm_pool_p2v( - dev, ts->op->Allocator, (unsigned long)head->cmds[0].buffer); - memcpy(&supp_cmd->uuid, ts->uuid, sizeof(struct tee_uuid)); - head->cmds[0].size = sizeof(struct tee_rpc_load_ta_cmd); - head->cmds[0].type = TEE_RPC_BUFFER; - - head->cmds[1].buffer = NULL; - head->cmds[1].size = 0; - head->cmds[1].type = TEE_RPC_BUFFER; - - supp_arg.datasize = sizeof(*head) - sizeof(head->cmds) + - sizeof(head->cmds[0]) * head->nbr_bf; - supp_arg.id = TEE_RPC_ICMD_INVOKE; - supp_arg.res = tee_supp_cmd( - ts->op, supp_arg.id, supp_arg.data, supp_arg.datasize); - ts->ta = (void *)tee_shm_pool_p2v( - dev, ts->op->Allocator, (unsigned long)head->cmds[1].buffer); - ts->tasize = head->cmds[1].size; - ts->tafd = head->cmds[1].fd; - dev_dbg(dev, "ta loaded pa %p va %p fd %d\n", - head->cmds[1].buffer, ts->ta, ts->tafd); - dev_dbg(dev, "Going to free %p\n", head->cmds[0].buffer); - tee_shm_unallocate(tee_shm); - if (supp_arg.res != TEEC_SUCCESS) { - dev_err(dev, "can't load ta\n"); - return -ENOENT; - } - - dev_dbg(dev, "<\n"); - return 0; -} - -static int free_ta_bin(struct tee_session *ts) -{ - struct tee_supp_arg supp_arg; - struct tee_rpc_invoke *head; - struct tee_shm *tee_shm; - struct device *dev = ts->op->miscdev->this_device; - - dev_dbg(dev, ">\n"); - head = (struct tee_rpc_invoke *)&supp_arg.data; - head->cmd = TEE_RPC_FREE_TA_WITH_FD; - head->res = 0; - head->nbr_bf = 1; - - tee_shm = tee_shm_allocate( - ts->op, NULL, sizeof(struct tee_rpc_load_ta_cmd), 0); - if (!tee_shm) - return -ENOMEM; - - dev_dbg(dev, "free ta va %p fd %d\n", ts->ta, ts->tafd); - head->cmds[0].size = ts->tasize; - head->cmds[0].type = TEE_RPC_BUFFER; - head->cmds[0].fd = ts->tafd; - - supp_arg.datasize = sizeof(*head) - sizeof(head->cmds) + - sizeof(head->cmds[0]) * head->nbr_bf; - supp_arg.id = TEE_RPC_ICMD_INVOKE; - supp_arg.res = tee_supp_cmd( - ts->op, supp_arg.id, supp_arg.data, supp_arg.datasize); - tee_shm_unallocate(tee_shm); - if (supp_arg.res != TEEC_SUCCESS) { - dev_err(dev, "can't unload ta\n"); - return -ENOENT; - } - ts->ta = NULL; - dev_dbg(dev, "<\n"); - return 0; -} - - -TEEC_Result TEEC_InitializeContext(const char *name, TEEC_Context *context) -{ - if (name == NULL) - strcpy(context->devname, TEE_TZ_NAME); - else - strcpy(context->devname, name); - - return TEEC_SUCCESS; -} -EXPORT_SYMBOL(TEEC_InitializeContext); - -TEEC_Result TEEC_FinalizeContext(TEEC_Context *context) -{ - return TEEC_SUCCESS; -} -EXPORT_SYMBOL(TEEC_FinalizeContext); - -TEEC_Result TEEC_OpenSession(TEEC_Context *context, - TEEC_Session *session, - const TEEC_UUID *destination, - uint32_t connectionMethod, - const void *connectionData, - TEEC_Operation *operation, - uint32_t *returnOrigin) -{ - struct tee_session *ts; - uint32_t param_type = 0x0; - TEEC_Value params[TEEC_CONFIG_PAYLOAD_REF_COUNT]; - unsigned long tmp_allocated_memories[TEEC_CONFIG_PAYLOAD_REF_COUNT] = { - 0, }; - TEEC_Result ret; - TEEC_Operation dummy_op; - struct device *dev = NULL; - - if (operation == NULL) { - /* - * The code here exist because Global Platform API states that - * it is allowed to give operation as a NULL pointer. - * In kernel and secure world we in most cases don't want - * this to be NULL, hence we use this dummy operation when - * a client doesn't provide any operation. - */ - memset(&dummy_op, 0, sizeof(TEEC_Operation)); - operation = &dummy_op; - } - - - if (context == NULL || session == NULL || destination == NULL || - operation == NULL || returnOrigin == NULL || - connectionData != NULL) - return TEEC_ERROR_BAD_PARAMETERS; - - *returnOrigin = TEEC_ORIGIN_API; - - ts = tee_create_session(context->devname, false); - if (ts == NULL) - return TEEC_ERROR_OUT_OF_MEMORY; - - dev = ts->op->miscdev->this_device; - - ret = allocate_uuid(ts); - if (ret != TEEC_SUCCESS) - goto error; - - *ts->uuid = *destination; - - ret = copy_op(ts, operation, tmp_allocated_memories, - ¶m_type, params); - if (ret != TEEC_SUCCESS) - goto error; - - dev_dbg(dev, "uuid=%08x-%04x-%04x\n", - ((ts->uuid) ? ts->uuid->timeLow : 0xDEAD), - ((ts->uuid) ? ts->uuid->timeMid : 0xDEAD), - ((ts->uuid) ? ts->uuid-> - timeHiAndVersion : 0xDEAD)); - - alloc_ta_bin(ts); - - ret = ts->op->call_sec_world(ts, CMD_TEEC_OPEN_SESSION, 0, - param_type, params, returnOrigin); - - (void)uncopy_op(ts, operation, tmp_allocated_memories, params); - - if (ret != TEEC_SUCCESS) { - dev_err(dev, - "TEEC_OpenSession: call_sec_world , err [%x], org [%x]\n", - ret, *returnOrigin); - goto error; - } - - ts->state = TEED_STATE_OPEN_SESSION; - session->session = ts; - - return TEEC_SUCCESS; -error: - tee_delete_session(ts); - return ret; -} -EXPORT_SYMBOL(TEEC_OpenSession); - -void TEEC_CloseSession(TEEC_Session *session) -{ - if (session != NULL && session->session != NULL) { - struct tee_session *ts = (struct tee_session *)session->session; - - free_ta_bin(ts); - - tee_delete_session(ts); - } -} -EXPORT_SYMBOL(TEEC_CloseSession); - -TEEC_Result TEEC_InvokeCommand(TEEC_Session *session, - uint32_t commandID, - TEEC_Operation *operation, - uint32_t *returnOrigin) -{ - struct tee_session *ts; - uint32_t param_type = 0x0; - TEEC_Value params[TEEC_CONFIG_PAYLOAD_REF_COUNT]; - unsigned long tmp_allocated_memories[TEEC_CONFIG_PAYLOAD_REF_COUNT] = { - 0, }; - TEEC_Result ret; - struct device *dev = NULL; - - if (session == NULL || operation == NULL || returnOrigin == NULL || - session->session == NULL) - return TEEC_ERROR_BAD_PARAMETERS; - - *returnOrigin = TEEC_ORIGIN_API; - ts = (struct tee_session *)session->session; - dev = ts->op->miscdev->this_device; - - if (ts->state != TEED_STATE_OPEN_SESSION) - return TEEC_ERROR_BAD_PARAMETERS; - - ret = copy_op(ts, operation, tmp_allocated_memories, - ¶m_type, params); - if (ret != TEEC_SUCCESS) - return ret; - - ret = ts->op->call_sec_world(ts, CMD_TEEC_INVOKE_COMMAND, commandID, - param_type, params, returnOrigin); - - (void)uncopy_op(ts, operation, tmp_allocated_memories, params); - - if (ret != TEEC_SUCCESS) - dev_err(dev, - "TEEC_InvokeCommand: call_sec_world , err [%x], org [%x]\n", - ret, *returnOrigin); - - return ret; -} -EXPORT_SYMBOL(TEEC_InvokeCommand); - -TEEC_Result TEEC_RegisterSharedMemory(TEEC_Context *context, - TEEC_SharedMemory *sharedMem) -{ - pr_info("TEEC_RegisterSharedMemory (vaddr=%p, size=%d)\n", - sharedMem->buffer, (int)sharedMem->size); - - if (sharedMem == NULL || sharedMem->buffer == NULL || - sharedMem->size == 0) - return TEEC_ERROR_BAD_PARAMETERS; - - sharedMem->d.shm = NULL; - sharedMem->registered = 1; - - /* - * Note: memory register in this context and allocate by previous - * TEEC_AllocateSharedMemory - * in another context will be consider as continuous by infrastructure. - * Elsewhere, it will be always uncontinuous. - * - * A potential optimization could be to pass flags to indicate - * continuity !!!! - */ - return 0; -} -EXPORT_SYMBOL(TEEC_RegisterSharedMemory); - -TEEC_Result TEEC_AllocateSharedMemory(TEEC_Context *context, - TEEC_SharedMemory *sharedMem) -{ - struct tee_targetop *op; - - if (context == NULL) - return TEEC_ERROR_BAD_PARAMETERS; - - op = tee_get_target(context->devname); - if (op == NULL) - return TEEC_ERROR_BAD_PARAMETERS; - - sharedMem->d.shm = - tee_shm_allocate(op, NULL, sharedMem->size, sharedMem->flags); - if (sharedMem->d.shm == NULL) { - pr_err("TEEC_AllocateSharedMemory: tee_shm_allocate(%zu) failed\n", - sharedMem->size); - return TEEC_ERROR_OUT_OF_MEMORY; - } - - pr_info("TEEC_AllocateSharedMemory (%zu) => paddr = %lx\n", - sharedMem->size, sharedMem->d.shm->paddr); - - sharedMem->buffer = - ioremap_nocache(sharedMem->d.shm->paddr, sharedMem->size); - if (sharedMem->buffer == NULL) { - pr_err("TEEC_AllocateSharedMemory: ioremap_nocache(%lx, %zu) failed\n", - sharedMem->d.shm->paddr, sharedMem->size); - tee_shm_unallocate(sharedMem->d.shm); - return TEEC_ERROR_OUT_OF_MEMORY; - } - - sharedMem->registered = 0; - - return TEEC_SUCCESS; -} -EXPORT_SYMBOL(TEEC_AllocateSharedMemory); - -void TEEC_ReleaseSharedMemory(TEEC_SharedMemory *sharedMemory) -{ - pr_info("TEEC_ReleaseSharedMemory (vaddr = %p)\n", - sharedMemory->buffer); - - if (sharedMemory->registered == 0) { - iounmap(sharedMemory->buffer); - sharedMemory->buffer = NULL; - tee_shm_unallocate(sharedMemory->d.shm); - } -} -EXPORT_SYMBOL(TEEC_ReleaseSharedMemory); diff --git a/generic/tee_mem.h b/generic/tee_mem.h deleted file mode 100644 index ed91eb3..0000000 --- a/generic/tee_mem.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * 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. - * - * 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. - */ -#ifndef TEE_MEM_H -#define TEE_MEM_H - -#include <linux/types.h> -#include <linux/device.h> - -struct shm_pool; - - -struct shm_pool *tee_shm_pool_create(struct device *dev, size_t shm_size, - void *shm_vaddr, unsigned long shm_paddr); - -void tee_shm_pool_destroy(struct device *dev, struct shm_pool *pool); - - -void *tee_shm_pool_p2v(struct device *dev, struct shm_pool *pool, - unsigned long paddr); - -unsigned long tee_shm_pool_v2p(struct device *dev, struct shm_pool *pool, - void *vaddr); - - -unsigned long tee_shm_pool_alloc(struct device *dev, - struct shm_pool *pool, - size_t size, size_t alignment); - - -void tee_shm_pool_free(struct device *dev, struct shm_pool *pool, - unsigned long paddr, size_t *size); - - -bool tee_shm_pool_incref(struct device *dev, struct shm_pool *pool, - unsigned long paddr); - - -void tee_shm_pool_dump(struct device *dev, struct shm_pool *pool, - bool forced); - -void tee_shm_pool_reset(struct device *dev, struct shm_pool *pool); - -bool tee_shm_pool_is_cached(struct shm_pool *pool); - -void tee_shm_pool_set_cached(struct shm_pool *pool); - - -#endif diff --git a/generic/tee_op.c b/generic/tee_op.c deleted file mode 100644 index d8947da..0000000 --- a/generic/tee_op.c +++ /dev/null @@ -1,82 +0,0 @@ -/* - * 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. - * - * 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 "tee-op.h" - -static const char CMD_TEEC_OPEN_SESSION_STR[] = "OPEN_SESSION "; - -static const char CMD_TEEC_CLOSE_SESSION_STR[] = "CLOSE_SESSION "; -static const char CMD_TEEC_INVOKE_COMMAND_STR[] = "INVOKE_COMMAND "; -static const char CMD_REGISTER_RPC_STR[] = "REGISTER_RPC "; -static const char CMD_SET_SEC_DDR_STR[] = "SET_SEC_DDR "; -static const char CMD_TEEC_CANCEL_COMMAND_STR[] = "CANCEL_COMMAND "; - -static const char CMD_TEE_DEINIT_CPU_STR[] = "DEINIT_CPU "; -static const char CMD_TEE_SET_CORE_TRACE_LEVEL_STR[] = - "SET_CORE_TRACE_LEVEL"; -static const char CMD_TEE_GET_CORE_TRACE_LEVEL_STR[] = - "GET_CORE_TRACE_LEVEL"; -static const char CMD_TEE_SET_TA_TRACE_LEVEL_STR[] = "SET_TA_TRACE_LEVEL"; -static const char CMD_TEE_GET_TA_TRACE_LEVEL_STR[] = "GET_TA_TRACE_LEVEL"; - -static const char CMD_REGISTER_DEF_SHM_STR[] = "REGISTER_DEF_SHM"; -static const char CMD_UNREGISTER_DEF_SHM_STR[] = "UNREGISTER_DEF_SHM"; - -const char *tee_cmd_str(enum t_cmd_service_id cmd) -{ - switch (cmd) { - case CMD_TEEC_OPEN_SESSION: - return CMD_TEEC_OPEN_SESSION_STR; - - case CMD_TEEC_CLOSE_SESSION: - return CMD_TEEC_CLOSE_SESSION_STR; - - case CMD_TEEC_INVOKE_COMMAND: - return CMD_TEEC_INVOKE_COMMAND_STR; - - case CMD_REGISTER_RPC: - return CMD_REGISTER_RPC_STR; - - case CMD_SET_SEC_DDR: - return CMD_SET_SEC_DDR_STR; - - case CMD_TEEC_CANCEL_COMMAND: - return CMD_TEEC_CANCEL_COMMAND_STR; - - case CMD_TEE_DEINIT_CPU: - return CMD_TEE_DEINIT_CPU_STR; - - case CMD_TEE_SET_CORE_TRACE_LEVEL: - return CMD_TEE_SET_CORE_TRACE_LEVEL_STR; - - case CMD_TEE_GET_CORE_TRACE_LEVEL: - return CMD_TEE_GET_CORE_TRACE_LEVEL_STR; - - case CMD_TEE_SET_TA_TRACE_LEVEL: - return CMD_TEE_SET_TA_TRACE_LEVEL_STR; - - case CMD_TEE_GET_TA_TRACE_LEVEL: - return CMD_TEE_GET_TA_TRACE_LEVEL_STR; - - case CMD_REGISTER_DEF_SHM: - return CMD_REGISTER_DEF_SHM_STR; - case CMD_UNREGISTER_DEF_SHM: - return CMD_UNREGISTER_DEF_SHM_STR; - - default: - return NULL; - } -} diff --git a/generic/tee_service.c b/generic/tee_service.c deleted file mode 100644 index 78fe38e..0000000 --- a/generic/tee_service.c +++ /dev/null @@ -1,888 +0,0 @@ -/* - * 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. - * - * 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/module.h> -#include <linux/mm.h> -#include <linux/slab.h> -#include <linux/sched.h> -#include <linux/uaccess.h> -#include <linux/device.h> -#include <linux/miscdevice.h> -#include <linux/highmem.h> - -#include "tee-op.h" -#include "tee_driver.h" -#include "tee_service.h" -#include "tee_tz.h" - -struct tee_targetop *tee_get_target(const char *devname) -{ - if (strcmp(devname, TEE_TZ_NAME) == 0) - return &TZop; - return NULL; -} - -struct tee_session *tee_create_session(const char *devname, bool userApi) -{ - struct tee_targetop *op; - struct tee_session *ts; - struct tee_driver *tee = NULL; - int count_session = -1; - struct device *dev = NULL; - - op = tee_get_target(devname); - if (op == NULL) { - dev_err(dev, "[%s] Invalid TEE device name '%s'", - __func__, devname); - return NULL; - } - - dev = op->miscdev->this_device; - dev_dbg(dev, "> count_session:[%d] devname:[%s]\n", - count_session, devname); - - tee = tee_get_drvdata(dev); - count_session = tee->count_session; - - ts = (struct tee_session *)devm_kzalloc(dev, sizeof(struct tee_session), - GFP_KERNEL); - if (ts == NULL) { - dev_err(dev, "[%s] allocation failed", __func__); - return NULL; - } - mutex_lock(&tee->mutex_tee); - tee->count_session++; - mutex_unlock(&tee->mutex_tee); - - mutex_init(&ts->syncstate); - ts->op = op; - ts->state = TEED_STATE_OPEN_DEV; - ts->id = 0; - ts->ta = NULL; - ts->tasize = 0; - ts->uuid = NULL; - ts->login = TEEC_LOGIN_PUBLIC; - ts->userApi = userApi; - - dev_dbg(dev, "< count_session:[%d] ts:[%p]\n", count_session, ts); - return ts; -} - -void tee_delete_session(struct tee_session *ts) -{ - struct device *dev = ts->op->miscdev->this_device; - struct tee_driver *tee = tee_get_drvdata(dev); - int count_session = tee->count_session; - - dev_dbg(dev, "> session:[%p] count_session:[%d]\n", ts, count_session); - - /* Close session to secure world if a session is open */ - if (ts->state == TEED_STATE_OPEN_SESSION) { - ts->op->call_sec_world(ts, CMD_TEEC_CLOSE_SESSION, 0, 0x0, - NULL, NULL); - } - - if (ts->ta != NULL) - tee_shm_pool_free(dev, ts->op->Allocator, - tee_shm_pool_v2p(dev, ts->op->Allocator, - ts->ta), NULL); - - if (ts->uuid != NULL) - tee_shm_pool_free(dev, ts->op->Allocator, - tee_shm_pool_v2p(dev, ts->op->Allocator, - ts->uuid), NULL); - - mutex_lock(&tee->mutex_tee); - count_session = --tee->count_session; - BUG_ON(tee->count_session < 0); - mutex_unlock(&tee->mutex_tee); - - if (!count_session) { -#if defined(_DUMP_INFO_ALLOCATOR) && (_DUMP_INFO_ALLOCATOR > 1) - tee_shm_pool_dump(dev, ts->op->Allocator, true); -#endif - tee_shm_pool_reset(dev, ts->op->Allocator); - } - - devm_kfree(dev, ts); - - dev_dbg(dev, "<\n"); -} - -struct ListPhysicalAddr { - unsigned long paddr; /* Physical address */ - unsigned int refcounter; /* Refcounter */ - struct mem_part *part; /* memory part of the allocation */ - void *handle; /* handle to registered shared memory */ - struct ListPhysicalAddr *next; -}; - -struct ListPhysicalAddr *_listPhysicalAddr; -static DEFINE_MUTEX(list_paddr_lock); - -#define _DUMP_INFO_SHM 0 -#if _DUMP_INFO_SHM == 1 -static void tee_DumpShm(struct device *dev, char *message) -{ - struct ListPhysicalAddr *list; - - dev_info(dev, "tee_DumpShm() %s\n", message); - if (!_listPhysicalAddr) - dev_info(dev, " | No more shared memory\n"); - for (list = _listPhysicalAddr; list; list = list->next) { - dev_info(dev, - " | paddr [0x%p] refcounter [%d] part [0x%p]\n", - (void *)list->paddr, list->refcounter, list->part); - - } -} -#endif - -struct ListPhysicalAddr *addPhysicalAddr(struct device *dev, - unsigned long paddr, struct mem_part *part, bool mem_allocated) -{ - struct ListPhysicalAddr *list, *ret; - - dev_dbg(dev, "Adding physical address %p\n", (void *)paddr); - - mutex_lock(&list_paddr_lock); - list = _listPhysicalAddr; - - while (list) { - if (list->paddr == paddr) { - if (part) { - dev_err(dev, "[%s] Found physical address %p\n", - __func__, (void *)paddr); - dev_err(dev, " but with part==%p\n", - (void *)part); - ret = NULL; - goto out; - } - list->refcounter++; -#if _DUMP_INFO_SHM == 1 - tee_DumpShm(dev, "addPhysicalAddr() reference found"); -#endif - ret = list; - goto out; - } - list = list->next; - } - mutex_unlock(&list_paddr_lock); - - if (mem_allocated && !part) { - dev_err(dev, "[%s] New physical address with part==NULL\n", - __func__); - return NULL; - } - - list = (struct ListPhysicalAddr *)devm_kzalloc(dev, - sizeof(struct ListPhysicalAddr), GFP_KERNEL); - if (list == NULL) { - dev_err(dev, - "[%s] Cannot allocate a new element in the list\n", - __func__); - return NULL; - } - - memset(list, 0, sizeof(struct ListPhysicalAddr)); - list->paddr = paddr; - list->part = part; - list->refcounter = 1; - mutex_lock(&list_paddr_lock); - list->next = _listPhysicalAddr; - _listPhysicalAddr = list; - ret = list; -out: - mutex_unlock(&list_paddr_lock); -#if _DUMP_INFO_SHM == 1 - tee_DumpShm(dev, "addPhysicalAddr() new reference"); -#endif - return ret; -} - -static bool isPhysicalAddr(struct device *dev, unsigned long paddr) -{ - struct ListPhysicalAddr *list; - - mutex_lock(&list_paddr_lock); - - list = _listPhysicalAddr; - while (list) { - if (list->paddr == paddr) { - mutex_unlock(&list_paddr_lock); - return true; - } - list = list->next; - } - mutex_unlock(&list_paddr_lock); - return false; -} - -unsigned int removePhysicalAddr(struct device *dev, unsigned long paddr, - struct mem_part **part, void **handle) -{ - struct ListPhysicalAddr *list, *prev = 0; - - mutex_lock(&list_paddr_lock); - - list = _listPhysicalAddr; - *part = 0; - *handle = 0; - - while (list) { - if (list->paddr == paddr) { - *part = list->part; - *handle = list->handle; - if (list->refcounter == 1) { - if (prev) - prev->next = list->next; - else - _listPhysicalAddr = list->next; - - mutex_unlock(&list_paddr_lock); - devm_kfree(dev, list); -#if _DUMP_INFO_SHM == 1 - dev_info(dev, "[0x%p] is removed\n", paddr); - tee_DumpShm(dev, - "removePhysicalAddr() removing a reference"); -#endif - - return 0; - } else { - list->refcounter--; - mutex_unlock(&list_paddr_lock); -#if _DUMP_INFO_SHM == 1 - dev_info(dev, "[0x%p] has its refcounter decreased [%d]\n", - paddr, list->refcounter); - tee_DumpShm(dev, - "removePhysicalAddr() decreased refcounter"); -#endif - return list->refcounter; - } - } - prev = list; - list = list->next; - } - mutex_unlock(&list_paddr_lock); - - /* error */ - dev_err(dev, "[%s] Cannot find physical address to remove\n", __func__); - return -1; -} - -static unsigned long GetPhysicalContiguous(unsigned long ptr) -{ - struct mm_struct *mm = current->mm; - /* struct vma_area_struct *vma = find_vma(mm, ptr); */ - unsigned virt_base = (ptr / PAGE_SIZE) * PAGE_SIZE; - unsigned phys_base = 0; - - pgd_t *pgd; - pmd_t *pmd; - pte_t *ptep, pte; - - /* if the caller is the kernel api, active_mm is mm */ - if (!mm) - mm = current->active_mm; - - spin_lock(&mm->page_table_lock); - - pgd = pgd_offset(mm, virt_base); - if (pgd_none(*pgd) || pgd_bad(*pgd)) - goto out; - - pmd = pmd_offset((pud_t *)pgd, virt_base); - if (pmd_none(*pmd) || pmd_bad(*pmd)) - goto out; - - ptep = pte_offset_map(pmd, virt_base); - - if (!ptep) - goto out; - - pte = *ptep; - - if (pte_present(pte)) - phys_base = __pa(page_address(pte_page(pte))); - - if (!phys_base) - goto out; - - spin_unlock(&mm->page_table_lock); - return phys_base + (ptr - virt_base); - -out: - spin_unlock(&mm->page_table_lock); - return 0; -} - -static unsigned long tee_shm_iscontinuous( - struct device *dev, - void *vaddr, - unsigned long size) -{ - struct vm_area_struct *vma; - struct mm_struct *mm = current->mm; - - /* if the caller is the kernel api, active_mm is mm */ - if (!mm) - mm = current->active_mm; - - vma = find_vma(mm, (unsigned long)vaddr); - - if (vma == NULL) { - /* It's not a VMA => consider it as a kernel address - * And look if it's an internal known phys addr - * Note: virt_to_phys is not usable since it can be a direct - * map or a vremap address - */ - unsigned long paddr; - - paddr = GetPhysicalContiguous((unsigned long)vaddr); - if (isPhysicalAddr(dev, paddr)) - return paddr; - } else { - void *paddr = vma->vm_private_data; - /* It's a VMA => consider it a a user address */ - if (!(vma->vm_flags & (VM_IO | VM_PFNMAP))) { - dev_err(dev, "[%s] 0x%p not Contiguous %p\n", __func__, - vaddr, paddr); - return 0x0; - } - - if (vma->vm_end - vma->vm_start < size) { - dev_err(dev, "[%s] 0x%p not big enough %p %ld %ld\n", - __func__, vaddr, paddr, - vma->vm_end - vma->vm_start, size); - return 0x0; - } - - return (unsigned long)paddr; - } - - return 0x0; -} - -struct tee_shm *tee_shm_allocate(struct tee_targetop *op, - void *vaddr, size_t size, uint32_t flags) -{ - struct device *dev = op->miscdev->this_device; - struct tee_shm *shm; - struct ListPhysicalAddr *list = 0; - struct mem_part *part = 0, *part_removed = 0; - void *handle; - struct tee_driver *tee = NULL; - tee = tee_get_drvdata(dev); - - dev_dbg(dev, "> vaddr:[%p] size:[%zu]\n", (void *)vaddr, size); - - /* - * Adjust the size in case it is 0 as, from the spec: - * The size is allowed to be zero. In this case memory is - * allocated and the pointer written in to the buffer field - * on return MUST not be NULL but MUST never be de-referenced - * by the Client Application. In this case however, the - * Shared Memory block can be used in Registered Memory References - */ - if (size == 0) - size = 8; - - /* Align the size to be ICS compliant */ - if ((size % op->page_size) != 0) - size = ((size / op->page_size) + 1) * op->page_size; - - if (unlikely(size == 0)) { - dev_err(dev, "[%s] requested size too big\n", __func__); - return NULL; - } - - shm = (struct tee_shm *)devm_kzalloc(dev, sizeof(struct tee_shm), - GFP_KERNEL); - if (shm == NULL) - return NULL; - - shm->op = op; - - if (vaddr == NULL) { - shm->paddr = tee_shm_pool_alloc(dev, - op->Allocator, size, op->page_size); - - if (shm->paddr == 0x0) { - dev_err(dev, "[%s] out of shared memory (%zu)\n", - __func__, size); - goto out_shm; - } - } else { - if (flags & SHM_ALLOCATE_FROM_PHYSICAL) { - shm->paddr = (unsigned long)vaddr; - } else { - shm->paddr = tee_shm_iscontinuous(dev, vaddr, size); - if (shm->paddr == 0x0) { - dev_err(dev, "[%s] SHM not contiguous (0x%p + %zu)\n", - __func__, vaddr, size); - goto out_shm; - } - } - } - - list = addPhysicalAddr(dev, shm->paddr, part, - false); - if (!list) - goto out_mem; - if (list->refcounter == 1) - if (op->register_shm(shm->paddr, size, &list->handle) - != TEEC_SUCCESS) { - dev_err(dev, "[%s] Cannot register [0x%p] size [%zu]\n", - __func__, (void *)shm->paddr, size); - goto out_mem; - } - - dev_dbg(dev, "< %p + %zd\n", (void *)shm->paddr, size); - return shm; - -out_mem: - if (list) - removePhysicalAddr(dev, shm->paddr, &part_removed, &handle); - - tee_shm_pool_free(dev, shm->op->Allocator, shm->paddr, &size); -out_shm: - if (shm) - devm_kfree(dev, shm); - dev_dbg(dev, "<\n"); - return NULL; -} - -void tee_shm_unallocate(struct tee_shm *shm) -{ - struct device *dev = shm->op->miscdev->this_device; - struct mem_part *part; - void *handle; - size_t size; - struct tee_driver *tee = NULL; - unsigned int refcounter = - removePhysicalAddr(dev, shm->paddr, &part, &handle); - tee = tee_get_drvdata(dev); - - if (refcounter == 0) { - if (handle) - shm->op->unregister_shm(handle); - - tee_shm_pool_free(dev, - shm->op->Allocator, shm->paddr, &size); - } - - devm_kfree(dev, shm); -} - -TEEC_Result allocate_uuid(struct tee_session *ts) -{ - unsigned long paddr; - struct device *dev = ts->op->miscdev->this_device; - - dev_dbg(dev, "> session: [0x%p]\n", ts); - - paddr = tee_shm_pool_alloc( - dev, ts->op->Allocator, sizeof(TEEC_UUID), 0); - if (paddr == 0x0) { - dev_err(dev, "[%s] error, out of memory\n", __func__); - return TEEC_ERROR_OUT_OF_MEMORY; - } - - ts->uuid = tee_shm_pool_p2v(dev, ts->op->Allocator, paddr); - - dev_dbg(dev, "<\n"); - return TEEC_SUCCESS; -} - -static TEEC_Result tee_cpy_memref(struct tee_session *ts, void *buffer, - uint32_t size, TEEC_Value *param, int type) -{ - unsigned long paddr; - void *vaddr; - size_t size_allocate = (size_t)size; - struct device *dev = ts->op->miscdev->this_device; - - dev_dbg(dev, "> session: [0x%p] buffer [0x%p]\n", ts, buffer); - param->b = size; - - /* Size 0 is OK to use. - * Artificially set the size to 8 for buffer allocation - */ - if (size_allocate == 0) - size_allocate = 8; - - /* - * Allocate consecutive memory - */ - paddr = tee_shm_pool_alloc(dev, ts->op->Allocator, size_allocate, 0); - if (!paddr) { - dev_err(dev, "[%s] couldn't alloc tee memory\n", __func__); - return TEEC_ERROR_OUT_OF_MEMORY; - } - - vaddr = tee_shm_pool_p2v(dev, ts->op->Allocator, paddr); - - if ((size) && (type != TEEC_MEMREF_TEMP_OUTPUT)) { - if (ts->userApi) { - if (copy_from_user(vaddr, buffer, size)) { - dev_err(dev, " *** tee_cpy_memref(0x%p, %lx, %d) failed\n", - buffer, paddr, size); - tee_shm_pool_free(dev, ts->op->Allocator, - paddr, NULL); - return TEEC_ERROR_BAD_PARAMETERS; - } - } else { - memcpy(vaddr, buffer, size); - } - } - - param->a = paddr; - - dev_dbg(dev, "< copied to vaddr [0x%p] paddr [0x%p]\n", - vaddr, (void *)paddr); - return TEEC_SUCCESS; -} - -static TEEC_Result tee_resolve_shm(struct device *dev, TEEC_SharedMemory *shm, - TEEC_Value *param) -{ - unsigned long paddr; - - BUG_ON(shm->buffer == NULL); - - paddr = tee_shm_iscontinuous(dev, shm->buffer, shm->size); - if (paddr == 0x0) - return TEEC_ERROR_NOT_SUPPORTED; - - dev_dbg(dev, "[%s] => %lx\n", __func__, paddr); - - param->a = (uint32_t) (paddr); - param->b = shm->size; - - return TEEC_SUCCESS; -} - -TEEC_Result copy_op(struct tee_session *ts, TEEC_Operation *op, - unsigned long - tmp_allocated_memories[TEEC_CONFIG_PAYLOAD_REF_COUNT], - uint32_t *param_type, - TEEC_Value params[TEEC_CONFIG_PAYLOAD_REF_COUNT]) -{ - TEEC_Result ret; - int memref; - struct device *dev = ts->op->miscdev->this_device; - dev_dbg(dev, ">\n"); - - *param_type = 0; - - for (memref = 0; memref < TEEC_CONFIG_PAYLOAD_REF_COUNT; ++memref) - tmp_allocated_memories[memref] = 0UL; - - for (memref = 0; memref < TEEC_CONFIG_PAYLOAD_REF_COUNT; ++memref) { - int type = TEEC_PARAM_TYPE_GET(op->paramTypes, memref); - dev_dbg(dev, "type [0x%x]\n", type); - - switch (type) { - case TEEC_NONE: - break; - case TEEC_VALUE_INPUT: - case TEEC_VALUE_OUTPUT: - case TEEC_VALUE_INOUT: { - /* nothing to copy or allocate */ - params[memref] = op->params[memref].value; - dev_dbg(dev, " params[%d] = %x:%x (VALUE)\n", - memref, params[memref].a, - params[memref].b); - break; - } - case TEEC_MEMREF_TEMP_INPUT: - case TEEC_MEMREF_TEMP_OUTPUT: - case TEEC_MEMREF_TEMP_INOUT: { - ret = tee_cpy_memref(ts, - op->params[memref].tmpref.buffer, - op->params[memref].tmpref.size, - ¶ms[memref], type); - if (ret != TEEC_SUCCESS) - goto out_failed; - tmp_allocated_memories[memref] = - params[memref].a; - dev_dbg(dev, " params[%d] = %x + %d (TEMP)\n", - memref, params[memref].a, - params[memref].b); - break; - } - case TEEC_MEMREF_WHOLE: { - TEEC_SharedMemory shm; - - if (ts->userApi) { - if (copy_from_user(&shm, - op-> - params[memref].memref.parent, - sizeof(TEEC_SharedMemory))) { - ret = TEEC_ERROR_BAD_PARAMETERS; - goto out_failed; - } - } else { - shm = *op->params[memref].memref.parent; - } - - if (shm.flags == TEEC_MEM_INPUT) - type = TEEC_MEMREF_TEMP_INPUT; - else if (shm.flags == TEEC_MEM_OUTPUT) - type = TEEC_MEMREF_TEMP_OUTPUT; - else if (shm.flags == - (TEEC_MEM_INPUT | TEEC_MEM_OUTPUT)) - type = TEEC_MEMREF_TEMP_INOUT; - - if (tee_resolve_shm(dev, &shm, ¶ms[memref]) - != TEEC_SUCCESS) { - /* This is not a continuous - * allocated buffer => Do copy */ - ret = tee_cpy_memref(ts, - shm.buffer, - shm.size, - ¶ms[memref], - type); - if (ret != TEEC_SUCCESS) - goto out_failed; - tmp_allocated_memories[memref] = - params[memref].a; - } - - dev_dbg(dev, - " params[%d] = %x + %d (WHOLE)\n", - memref, params[memref].a, - params[memref].b); - break; - } - case TEEC_MEMREF_PARTIAL_INPUT: - case TEEC_MEMREF_PARTIAL_OUTPUT: - case TEEC_MEMREF_PARTIAL_INOUT: { - u32 offset = op->params[memref].memref.offset; - u32 size = op->params[memref].memref.size; - TEEC_SharedMemory shm; - - if (ts->userApi) { - if (copy_from_user(&shm , - op-> - params[memref].memref.parent, - sizeof(TEEC_SharedMemory))) { - ret = TEEC_ERROR_BAD_PARAMETERS; - goto out_failed; - } - } else { - shm = *op->params[memref].memref.parent; - } - - if (type == TEEC_MEMREF_PARTIAL_INPUT) - type = TEEC_MEMREF_TEMP_INPUT; - else if (type == TEEC_MEMREF_PARTIAL_OUTPUT) - type = TEEC_MEMREF_TEMP_OUTPUT; - else if (type == TEEC_MEMREF_PARTIAL_INOUT) - type = TEEC_MEMREF_TEMP_INOUT; - - if (tee_resolve_shm(dev, &shm, ¶ms[memref]) - != TEEC_SUCCESS) { - /* This is not a continuous - * allocated buffer => Do copy */ - ret = tee_cpy_memref(ts, - (uint8_t *)shm. - buffer + offset, - size, - ¶ms[memref], - type); - if (ret != TEEC_SUCCESS) - goto out_failed; - tmp_allocated_memories[memref] = - params[memref].a; - } else { - params[memref].a += offset; - params[memref].b = size; - } - dev_dbg(dev, - " params[%d] = %x + %d (PARTIAL)\n", - memref, params[memref].a, - params[memref].b); - break; - } - default: - ret = TEEC_ERROR_BAD_PARAMETERS; - goto out_failed; - } - - *param_type |= (type << (memref * 4)); - } - - dev_dbg(dev, "< TEEC_SUCCESS\n"); - return TEEC_SUCCESS; - -out_failed: - for (memref = 0; memref < TEEC_CONFIG_PAYLOAD_REF_COUNT; memref++) - if (tmp_allocated_memories[memref] != 0UL) { - tee_shm_pool_free(dev, ts->op->Allocator, - tmp_allocated_memories[memref], NULL); - tmp_allocated_memories[memref] = 0UL; - } - return ret; -} - -static TEEC_Result tee_update_buffer(struct device *dev, struct tee_session *ts, - void *buffer, - unsigned long paddr, unsigned int size) -{ - void *vaddr = tee_shm_pool_p2v(dev, ts->op->Allocator, paddr); - - dev_dbg(dev, "[%d] [%p] [%p] [%p] %d\n", - ts->userApi, buffer, (void *)paddr, vaddr, size); - - if (ts->userApi) { - if (copy_to_user(buffer, vaddr, size)) { - dev_err(dev, - " *** tee_update_buffer(0x%p, %lx, %d) failed\n", - buffer, paddr, size); - return TEEC_ERROR_BAD_PARAMETERS; - } - } else { - memcpy(buffer, vaddr, size); - } - - dev_dbg(dev, "< TEEC_SUCCESS\n"); - return TEEC_SUCCESS; -} - -TEEC_Result uncopy_op(struct tee_session *ts, TEEC_Operation *op, - unsigned long - tmp_allocated_memories[TEEC_CONFIG_PAYLOAD_REF_COUNT], - TEEC_Value params[TEEC_CONFIG_PAYLOAD_REF_COUNT]) -{ - int memref; - TEEC_Result ret = TEEC_SUCCESS; - struct device *dev = ts->op->miscdev->this_device; - - dev_dbg(dev, "> session: [0x%p]\n", ts); - - - for (memref = 0; memref < TEEC_CONFIG_PAYLOAD_REF_COUNT; ++memref) { - int type = TEEC_PARAM_TYPE_GET(op->paramTypes, memref); - dev_dbg(dev, "type [0x%x]\n", type); - switch (type) { - case TEEC_NONE: - case TEEC_VALUE_INPUT: - case TEEC_MEMREF_TEMP_INPUT: - case TEEC_MEMREF_PARTIAL_INPUT: - /* nothing to copy */ - break; - - case TEEC_VALUE_OUTPUT: - case TEEC_VALUE_INOUT: - op->params[memref].value = params[memref]; - break; - - case TEEC_MEMREF_TEMP_OUTPUT: - case TEEC_MEMREF_TEMP_INOUT: - { - op->params[memref].tmpref.size = - params[memref].b; - ret = - tee_update_buffer(dev, ts, - op->params[memref].tmpref. - buffer, params[memref].a, - params[memref].b); - break; - } - case TEEC_MEMREF_WHOLE: - { - TEEC_SharedMemory shm; - - if (ts->userApi) { - if (copy_from_user - (&shm, - op->params[memref].memref.parent, - sizeof(TEEC_SharedMemory))) - goto inval; - } else { - shm = *op->params[memref].memref.parent; - } - - op->params[memref].memref.size = - params[memref].b; - if (tmp_allocated_memories[memref] != 0x0) { - ret = - tee_update_buffer(dev, ts, - shm.buffer, - params[memref].a, - shm.size); - } - break; - } - case TEEC_MEMREF_PARTIAL_OUTPUT: - case TEEC_MEMREF_PARTIAL_INOUT: - { - u32 offset = op->params[memref].memref.offset; - size_t size = params[memref].b; - TEEC_SharedMemory shm; - - if (ts->userApi) { - if (copy_from_user - (&shm, - op->params[memref].memref.parent, - sizeof(TEEC_SharedMemory))) - goto inval; - } else { - shm = *op->params[memref].memref.parent; - } - - /* ensure we do not exceed - * the shared buffer length */ - if ((offset + size) > shm.size) { - dev_err(dev, - " *** Wrong returned size from %d\n", - memref); - goto inval; - } - - op->params[memref].memref.size = size; - if (tmp_allocated_memories[memref] != 0x0) { - ret = - tee_update_buffer(dev, ts, - shm.buffer + - offset, - params[memref].a, - size); - } - break; - } - default: - goto inval; - } - if (ret != TEEC_SUCCESS) - goto out; - } - - goto out; -inval: - ret = TEEC_ERROR_BAD_PARAMETERS; -out: - for (memref = 0; memref < TEEC_CONFIG_PAYLOAD_REF_COUNT; memref++) - if (tmp_allocated_memories[memref] != 0x0) - tee_shm_pool_free(dev, ts->op->Allocator, - tmp_allocated_memories[memref], - NULL); - - dev_dbg(dev, "< [%d]\n", ret); - - return ret; -} diff --git a/generic/tee_service.h b/generic/tee_service.h deleted file mode 100644 index 316555a..0000000 --- a/generic/tee_service.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * 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. - * - * 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. - */ -#ifndef TEE_SERVICE_H -#define TEE_SERVICE_H - -#include <linux/types.h> - -#define SHM_ALLOCATE_FROM_PHYSICAL 0x100 - -struct tee_shm { - struct tee_targetop *op; - unsigned long paddr; -}; - -struct tee_targetop *tee_get_target(const char *devname); - -struct tee_session *tee_create_session(const char *devname, bool userApi); -void tee_delete_session(struct tee_session *ts); - -struct tee_shm *tee_shm_allocate(struct tee_targetop *op, - void *vaddr, size_t size, uint32_t flags); -void tee_shm_unallocate(struct tee_shm *shm); - -TEEC_Result allocate_uuid(struct tee_session *ts); - -TEEC_Result copy_op(struct tee_session *ts, TEEC_Operation *op, - unsigned long - tmp_allocated_memories[TEEC_CONFIG_PAYLOAD_REF_COUNT], - uint32_t *param_type, - TEEC_Value params[TEEC_CONFIG_PAYLOAD_REF_COUNT]); -TEEC_Result uncopy_op(struct tee_session *ts, TEEC_Operation *op, - unsigned long - tmp_allocated_memories[TEEC_CONFIG_PAYLOAD_REF_COUNT], - TEEC_Value params[TEEC_CONFIG_PAYLOAD_REF_COUNT]); -void free_temp_op(struct tee_session *ts, - unsigned long - tmp_allocated_memories[TEEC_CONFIG_PAYLOAD_REF_COUNT]); - -#endif diff --git a/generic/tee_supp_com.c b/generic/tee_supp_com.c deleted file mode 100644 index 1b74172..0000000 --- a/generic/tee_supp_com.c +++ /dev/null @@ -1,234 +0,0 @@ -/* - * 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. - * - * 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/kernel.h> -#include <linux/fs.h> -#include <linux/mutex.h> -#include <linux/miscdevice.h> -#include <linux/uaccess.h> -#include <linux/anon_inodes.h> -#include <linux/semaphore.h> -#include <linux/mm.h> -#include <linux/sched.h> -#include <linux/device.h> - -#include "tee-op.h" -#include "tee_mem.h" -#include "tee_service.h" -#include "tee_supp_com.h" -#include "tee_driver.h" - -enum teec_rpc_result tee_supp_cmd(struct tee_targetop *op, - uint32_t id, void *data, - unsigned int datalen) -{ - struct device *dev = op->miscdev->this_device; - struct tee_rpc_priv_data *rpc = &tee_get_drvdata(dev)->rpc; - enum teec_rpc_result res = TEEC_RPC_FAIL; - size_t size; /* size of block */ - struct task_struct *task = current; - - dev_dbg(dev, "> tgid:[%d] op:[0x%p] id:[0x%x]\n", - task->tgid, (void *)op, id); - - 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_allocate(op, 0, size, 0); - if (shmint == NULL) - break; - - alloc->size = size; - alloc->data = (void *)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_unallocate(free->shm); - res = TEEC_RPC_OK; - break; - } - case TEE_RPC_ICMD_INVOKE: - { - if (sizeof(rpc->commToUser) < datalen) - break; - - /* - * Don't allow interleaved requests (from two - * different threads) as the second request will - * overwrite the first request. - */ - mutex_lock(&rpc->reqsync); - - mutex_lock(&rpc->outsync); - - memcpy(&rpc->commToUser, data, datalen); - - mutex_unlock(&rpc->outsync); - - dev_dbg(dev, "Supplicant Cmd: %x. Give hand to supplicant\n", - rpc->commToUser.cmd); - - up(&rpc->datatouser); - - down(&rpc->datafromuser); - - dev_dbg(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->reqsync); - - res = TEEC_RPC_OK; - - break; - } - default: - /* not supported */ - break; - } - - dev_dbg(dev, "< res: [%d]\n", res); - - return res; -} - -ssize_t tee_supp_read(struct file *filp, char __user *buffer, - size_t length, loff_t *offset) -{ - struct tee_session *ts = (struct tee_session *)(filp->private_data); - struct device *dev = ts->op->miscdev->this_device; - struct tee_rpc_priv_data *rpc = &tee_get_drvdata(dev)->rpc; - struct task_struct *task = current; - int ret; - - if (down_interruptible(&rpc->datatouser)) - return -ERESTARTSYS; - - dev_dbg(dev, "> tgid:[%d] ts:[0x%p]\n", task->tgid, (void *)ts); - - 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(dev, - "[%s] error, copy_to_user failed!\n", __func__); - ret = -EINVAL; - } - } - - mutex_unlock(&rpc->outsync); - - dev_dbg(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_session *ts = (struct tee_session *)(filp->private_data); - struct device *dev = ts->op->miscdev->this_device; - struct tee_rpc_priv_data *rpc = &tee_get_drvdata(dev)->rpc; - struct task_struct *task = current; - dev_dbg(dev, "> tgid:[%d] ts:[0x%p]\n", task->tgid, (void *)ts); - - if (length > 0 && length < sizeof(rpc->commFromUser)) { - uint32_t i; - - mutex_lock(&rpc->insync); - - if (copy_from_user(&rpc->commFromUser, buffer, length)) { - dev_err(dev, - "[%s] error, tee_session copy_from_user failed\n", - __func__); - mutex_unlock(&rpc->insync); - return -EINVAL; - } - - /* 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) { - - unsigned long paddr = - (unsigned long)vma->vm_private_data; - - dev_dbg(dev, " gid2pa(0x%p => %lx)\n", - rpc-> - commFromUser.cmds[i].buffer, - paddr); - rpc->commFromUser.cmds[i].buffer = - (void *)paddr; - } else - dev_dbg(dev, - " gid2pa(0x%p => NULL\n)", - rpc->commFromUser.cmds[i].buffer); - } - } - - mutex_unlock(&rpc->insync); - up(&rpc->datafromuser); - dev_dbg(dev, "< [%zu]\n", length); - return length; - } - - dev_dbg(dev, "< [0]\n"); - return 0; -} - -int tee_supp_init(struct tee_rpc_priv_data *rpc) -{ - rpc->datafromuser = (struct semaphore) - __SEMAPHORE_INITIALIZER(rpc->datafromuser, 0); - rpc->datatouser = (struct semaphore) - __SEMAPHORE_INITIALIZER(rpc->datatouser, 0); - mutex_init(&rpc->outsync); - mutex_init(&rpc->insync); - mutex_init(&rpc->reqsync); - return 0; -} - -void tee_supp_exit(void) -{ -} - diff --git a/include/arm_common/teesmc.h b/include/arm_common/teesmc.h index 26cc9c2..cd0e70a 100644 --- a/include/arm_common/teesmc.h +++ b/include/arm_common/teesmc.h @@ -81,6 +81,8 @@ #define TEESMC_ATTR_CACHE_O_WRITE_THR 0x4 #define TEESMC_ATTR_CACHE_O_WRITE_BACK 0x8 +#define TEESMC_ATTR_CACHE_NONCACHE (TEESMC_ATTR_CACHE_I_NONCACHE | \ + TEESMC_ATTR_CACHE_O_NONCACHE) #define TEESMC_ATTR_CACHE_DEFAULT (TEESMC_ATTR_CACHE_I_WRITE_BACK | \ TEESMC_ATTR_CACHE_O_WRITE_BACK) diff --git a/include/arm_common/teesmc_st.h b/include/arm_common/teesmc_st.h index 915c7bb..58c00bb 100644 --- a/include/arm_common/teesmc_st.h +++ b/include/arm_common/teesmc_st.h @@ -47,10 +47,10 @@ TEESMC_ST_FUNCID_GET_SHM_CONFIG) /* - * Configures L2CC mutex + * Configures TZ/NS shared mutex for outer cache maintenance * - * Disables, enables usage of L2CC mutex. Returns or sets physical address - * of L2CC mutex. + * Disables, enables usage of outercache mutex. + * Returns or sets physical address of outercache mutex. * * Call register usage: * r0 SMC Function ID, TEESMC32_ST_FASTCALL_L2CC_MUTEX diff --git a/include/linux/tee_client_api.h b/include/linux/tee_client_api.h index 4445ed1..17ce6e5 100644 --- a/include/linux/tee_client_api.h +++ b/include/linux/tee_client_api.h @@ -1,22 +1,37 @@ /* * 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. - * - * 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. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. */ #ifndef TEE_CLIENT_API_H #define TEE_CLIENT_API_H +#ifndef __KERNEL__ +#include <stdint.h> +#include <stddef.h> +#endif /* __KERNEL__ */ + /* * Defines the number of available memory references in an open session or * invoke command operation payload. @@ -135,6 +150,8 @@ * TEEC_ERROR_SECURITY A security fault was detected. * TEEC_ERROR_SHORT_BUFFER The supplied buffer is too short for the * generated output. + * TEEC_ERROR_TARGET_DEAD Trusted Application has panicked + * during the operation. */ /** @@ -158,6 +175,7 @@ #define TEEC_ERROR_COMMUNICATION 0xFFFF000E #define TEEC_ERROR_SECURITY 0xFFFF000F #define TEEC_ERROR_SHORT_BUFFER 0xFFFF0010 +#define TEEC_ERROR_TARGET_DEAD 0xFFFF3024 /** * Function error origins, of type TEEC_ErrorOrigin. These indicate where in @@ -216,6 +234,15 @@ typedef uint32_t TEEC_Result; /** + * struct TEEC_Context - Represents a connection between a client application + * and a TEE. + */ +typedef struct { + char devname[256]; + int fd; +} TEEC_Context; + +/** * This type contains a Universally Unique Resource Identifier (UUID) type as * defined in RFC4122. These UUID values are used to identify Trusted * Applications. @@ -227,8 +254,6 @@ typedef struct { uint8_t clockSeqAndNode[8]; } TEEC_UUID; -struct tee_shm; - /** * struct TEEC_SharedMemory - Memory to transfer data between a client * application and trusted code. @@ -249,9 +274,10 @@ typedef struct { void *buffer; size_t size; uint32_t flags; + /* Implementation-Defined, must match what the kernel driver have */ union { int fd; - struct tee_shm *shm; + void *ptr; } d; uint8_t registered; } TEEC_SharedMemory; @@ -330,6 +356,14 @@ typedef union { } TEEC_Parameter; /** + * struct TEEC_Session - Represents a connection between a client application + * and a trusted application. + */ +typedef struct { + int fd; +} TEEC_Session; + +/** * struct TEEC_Operation - Holds information and memory references used in * TEEC_InvokeCommand(). * @@ -348,7 +382,171 @@ typedef struct { uint32_t paramTypes; TEEC_Parameter params[TEEC_CONFIG_PAYLOAD_REF_COUNT]; /* Implementation-Defined */ - void *session; + TEEC_Session *session; + TEEC_SharedMemory memRefs[TEEC_CONFIG_PAYLOAD_REF_COUNT]; + uint32_t flags; } TEEC_Operation; +/** + * TEEC_InitializeContext() - Initializes a context holding connection + * information on the specific TEE, designated by the name string. + + * @param name A zero-terminated string identifying the TEE to connect to. + * If name is set to NULL, the default TEE is connected to. NULL + * is the only supported value in this version of the API + * implementation. + * + * @param context The context structure which is to be initialized. + * + * @return TEEC_SUCCESS The initialization was successful. + * @return TEEC_Result Something failed. + */ +TEEC_Result TEEC_InitializeContext(const char *name, TEEC_Context *context); + +/** + * TEEC_FinalizeContext() - Destroys a context holding connection information + * on the specific TEE. + * + * This function destroys an initialized TEE context, closing the connection + * between the client application and the TEE. This function must only be + * called when all sessions related to this TEE context have been closed and + * all shared memory blocks have been released, otherwise an error will be + * returned. + * + * @param context The context to be destroyed. + * + * @return TEEC_SUCCESS The function call was successful. + * @return TEEC_Result Something failed. + */ +TEEC_Result TEEC_FinalizeContext(TEEC_Context *context); + +/** + * TEEC_OpenSession() - Opens a new session with the specified trusted + * application. + * + * @param context The initialized TEE context structure in which + * scope to open the session. + * @param session The session to initialize. + * @param destination A structure identifying the trusted application + * with which to open a session. + * + * @param connectionMethod The connection method to use. + * @param connectionData Any data necessary to connect with the chosen + * connection method. Not supported, should be set to + * NULL. + * @param operation An operation structure to use in the session. May + * be set to NULL to signify no operation structure + * needed. + * + * @param returnOrigin A parameter which will hold the error origin if + * this function returns any value other than + * TEEC_SUCCESS. + * + * @return TEEC_SUCCESS OpenSession successfully opened a new session. + * @return TEEC_Result Something failed. + * + */ +TEEC_Result TEEC_OpenSession(TEEC_Context *context, + TEEC_Session *session, + const TEEC_UUID *destination, + uint32_t connectionMethod, + const void *connectionData, + TEEC_Operation *operation, + uint32_t *returnOrigin); + +/** + * TEEC_CloseSession() - Closes the session which has been opened with the + * specific trusted application. + * + * @param session The opened session to close. + */ +void TEEC_CloseSession(TEEC_Session *session); + +/** + * TEEC_InvokeCommand() - Executes a command in the specified trusted + * application. + * + * @param session A handle to an open connection to the trusted + * application. + * @param commandID Identifier of the command in the trusted application + * to invoke. + * @param operation An operation structure to use in the invoke command. + * May be set to NULL to signify no operation structure + * needed. + * @param returnOrigin A parameter which will hold the error origin if this + * function returns any value other than TEEC_SUCCESS. + * + * @return TEEC_SUCCESS OpenSession successfully opened a new session. + * @return TEEC_Result Something failed. + */ +TEEC_Result TEEC_InvokeCommand(TEEC_Session *session, + uint32_t commandID, + TEEC_Operation *operation, + uint32_t *returnOrigin); + +/** + * TEEC_RegisterSharedMemory() - Register a block of existing memory as a + * shared block within the scope of the specified context. + * + * @param context The initialized TEE context structure in which scope to + * open the session. + * @param sharedMem pointer to the shared memory structure to register. + * + * @return TEEC_SUCCESS The registration was successful. + * @return TEEC_ERROR_OUT_OF_MEMORY Memory exhaustion. + * @return TEEC_Result Something failed. + */ +TEEC_Result TEEC_RegisterSharedMemory(TEEC_Context *context, + TEEC_SharedMemory *sharedMem); + +/** + * TEEC_AllocateSharedMemory() - Allocate shared memory for TEE. + * + * @param context The initialized TEE context structure in which scope to + * open the session. + * @param sharedMem Pointer to the allocated shared memory. + * + * @return TEEC_SUCCESS The registration was successful. + * @return TEEC_ERROR_OUT_OF_MEMORY Memory exhaustion. + * @return TEEC_Result Something failed. + */ +TEEC_Result TEEC_AllocateSharedMemory(TEEC_Context *context, + TEEC_SharedMemory *sharedMem); + +/** + * TEEC_ReleaseSharedMemory() - Free or deregister the shared memory. + * + * @param sharedMem Pointer to the shared memory to be freed. + */ +void TEEC_ReleaseSharedMemory(TEEC_SharedMemory *sharedMemory); + +/** + * TEEC_RequestCancellation() - Request the cancellation of a pending open + * session or command invocation. + * + * @param operation Pointer to an operation previously passed to open session + * or invoke. + */ +void TEEC_RequestCancellation(TEEC_Operation *operation); + +/** + * Register a pre-allocated Trusted Application This is mainly intended for + * OS-FREE contexts or when a filesystem is not available. + * + * @param ta Pointer to the trusted application binary + * @param size The size of the TA binary + * + * @return TEEC_SUCCESS if successful. + * @return TEEC_Result something failed. + */ +TEEC_Result TEEC_RegisterTA(const void *ta, const size_t size); + +/** + * Unregister a pre-allocated Trusted Application This is mainly intended for + * OS-FREE contexts or when a filesystem is not available. + * + * @param ta Pointer to the trusted application binary + */ +void TEEC_UnregisterTA(const void *ta); + #endif diff --git a/include/linux/tee_core.h b/include/linux/tee_core.h new file mode 100644 index 0000000..4baeb92 --- /dev/null +++ b/include/linux/tee_core.h @@ -0,0 +1,186 @@ + +#ifndef __TEE_CORE_DRV_H__ +#define __TEE_CORE_DRV_H__ + +#include <linux/klist.h> +#include <linux/device.h> +#include <linux/file.h> +#include <linux/cdev.h> +#include <linux/debugfs.h> +#include <linux/miscdevice.h> +#include <linux/types.h> +#include <linux/atomic.h> + +#include <linux/types.h> + +#include <linux/tee_client_api.h> + +struct tee_cmd_io; +struct tee_shm_io; +struct tee_rpc; + +enum tee_state { + TEE_OFFLINE = 0, + TEE_ONLINE = 1, + TEE_SUSPENDED = 2, + TEE_RUNNING = 3, + TEE_CRASHED = 4, + TEE_LAST = 5, +}; + +#define TEE_CONF_TEST_MODE 0x01000000 +#define TEE_CONF_FW_NOT_CAPABLE 0x00000001 + +struct tee_stats_entry { + int count; + int max; +}; + +#define TEE_STATS_CONTEXT_IDX 0 +#define TEE_STATS_SESSION_IDX 1 +#define TEE_STATS_SHM_IDX 2 + +#define TEE_MAX_TEE_DEV_NAME (64) +struct tee { + struct klist_node node; + char name[TEE_MAX_TEE_DEV_NAME]; + int id; + void *priv; + const struct tee_ops *ops; + struct device *dev; + struct miscdevice miscdev; + struct tee_rpc *rpc; + struct dentry *dbg_dir; + atomic_t refcount; + int max_refcount; + struct tee_stats_entry stats[3]; + struct list_head list_ctx; + struct list_head list_rpc_shm; + struct mutex lock; + unsigned int state; + uint32_t shm_flags; /* supported flags for shm allocation */ + uint32_t conf; + uint32_t test; +}; + +#define _DEV(tee) (tee->miscdev.this_device) + +#define TEE_MAX_CLIENT_NAME (128) + +/** + * struct tee_context - internal structure to store a TEE context. + * + * @tee: tee attached to the tee_context + * @usr_client: flag to known if the client is user side client + * @entry: list of tee_context + * @list_sess: list of tee_session that denotes all tee_session attached + * @list_shm: list of tee_shm that denotes all tee_shm attached + * @refcount: number of objects which reference it (including itself) + */ +struct tee_context { + struct tee *tee; + char name[TEE_MAX_CLIENT_NAME]; + int tgid; + int usr_client; + struct list_head entry; + struct list_head list_sess; + struct list_head list_shm; + struct kref refcount; +}; + +/** + * struct tee_session - internal structure to store a TEE session. + * + * @entry: list of tee_context + * @ctx: tee_context attached to the tee_session + * @sessid: session ID returned by the secure world + * @priv: exporter specific private data for this buffer object + */ +struct tee_session { + struct list_head entry; + struct tee_context *ctx; + uint32_t sessid; + void *priv; +}; + +/** + * struct tee_shm - internal structure to store a shm object. + * + * @ctx: tee_context attached to the buffer. + * @tee: tee attached to the buffer. + * @dev: device attached to the buffer. + * @size_req: requested size for the buffer + * @size_alloc: effective size of the buffer + * @kaddr: kernel address if mapped kernel side + * @paddr: physical address + * @flags: flags which denote the type of the buffer + * @entry: list of tee_shm + */ +struct tee_shm { + struct list_head entry; + struct tee_context *ctx; + struct tee *tee; + struct device *dev; + size_t size_req; + size_t size_alloc; + void *kaddr; + dma_addr_t paddr; + uint32_t flags; + struct tee_shm *parent; +}; + +#define TEE_SHM_MAPPED 0x01000000 +#define TEE_SHM_TEMP 0x02000000 +#define TEE_SHM_FROM_RPC 0x04000000 +#define TEE_SHM_REGISTERED 0x08000000 +#define TEE_SHM_PARENT 0x10000000 +#define TEE_SHM_CACHED 0x20000000 +#define TEE_SHM_FROM_KAPI 0x40000000 + +#define TEE_SHM_DRV_PRIV_MASK 0xFF000000 + +struct tee_data { + uint32_t type; + uint32_t type_original; + TEEC_SharedMemory c_shm[TEEC_CONFIG_PAYLOAD_REF_COUNT]; + union { + struct tee_shm *shm; + TEEC_Value value; + } params[TEEC_CONFIG_PAYLOAD_REF_COUNT]; +}; + +struct tee_cmd { + TEEC_Result err; + uint32_t origin; + uint32_t cmd; + struct tee_shm *uuid; + struct tee_shm *ta; + struct tee_data param; +}; + +struct tee_shm *tee_shm_alloc_from_rpc(struct tee *tee, size_t size, + uint32_t flags); +void tee_shm_free_from_rpc(struct tee_shm *); + +int tee_core_add(struct tee *tee); +int tee_core_del(struct tee *tee); + +struct tee *tee_core_alloc(struct device *dev, char *name, int id, + const struct tee_ops *ops, size_t len); + +struct tee_ops { + struct module *owner; + const char *type; + int (*start) (struct tee *tee); + int (*stop) (struct tee *tee); + int (*open) (struct tee_session *sess, struct tee_cmd *cmd); + int (*close) (struct tee_session *sess); + int (*invoke) (struct tee_session *sess, struct tee_cmd *cmd); + int (*cancel) (struct tee_session *sess, struct tee_cmd *cmd); + struct tee_shm *(*alloc) (struct tee *tee, size_t size, + uint32_t flags); + void (*free) (struct tee_shm *shm); + int (*shm_inc_ref) (struct tee_shm *shm); +}; + +#endif /* __TEE_CORE_DRV_H__ */ diff --git a/include/linux/tee_ioc.h b/include/linux/tee_ioc.h new file mode 100644 index 0000000..43af082 --- /dev/null +++ b/include/linux/tee_ioc.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) ST-Microelectronics 2013. All rights reserved. + */ +#ifndef _TEE_IOC_H +#define _TEE_IOC_H + +#include <linux/tee_client_api.h> + +#ifndef __KERNEL__ +#define __user +#endif + +/** + * struct tee_cmd_io - The command sent to an open tee device. + * @err: Error code (as in Global Platform TEE Client API spec) + * @origin: Origin for the error code (also from spec). + * @cmd: The command to be executed in the trusted application. + * @uuid: The uuid for the trusted application. + * @data: The trusted application or memory block. + * @data_size: The size of the trusted application or memory block. + * @op: The cmd payload operation for the trusted application. + * + * This structure is mainly used in the Linux kernel for communication + * with the user space. + */ +struct tee_cmd_io { + TEEC_Result err; + uint32_t origin; + uint32_t cmd; + TEEC_UUID __user *uuid; + void __user *data; + uint32_t data_size; + TEEC_Operation __user *op; + int fd_sess; +}; + +struct tee_shm_io { + void __user *buffer; + size_t size; + uint32_t flags; + int fd_shm; + uint8_t registered; +}; + +#define TEE_OPEN_SESSION_IOC _IOWR('t', 161, struct tee_cmd_io) +#define TEE_INVOKE_COMMAND_IOC _IOWR('t', 163, struct tee_cmd_io) +#define TEE_REQUEST_CANCELLATION_IOC _IOWR('t', 164, struct tee_cmd_io) +#define TEE_ALLOC_SHM_IOC _IOWR('t', 165, struct tee_shm_io) +#define TEE_GET_FD_FOR_RPC_SHM_IOC _IOWR('t', 167, struct tee_shm_io) + +#endif /* _TEE_IOC_H */ diff --git a/include/linux/tee_ioctl.h b/include/linux/tee_ioctl.h deleted file mode 100644 index 003dbdc..0000000 --- a/include/linux/tee_ioctl.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * 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. - * - * 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. - */ -#ifndef TEE_H -#define TEE_H - -/** - * struct tee_cmd - The command sent to an open tee device. - * @err: Error code (as in Global Platform TEE Client API spec) - * @origin: Origin for the error code (also from spec). - * @cmd: The command to be executed in the trusted application. - * @uuid: The uuid for the trusted application. - * @data: The trusted application or memory block. - * @data_size: The size of the trusted application or memory block. - * @op: The payload for the trusted application. - * - * This structure is mainly used in the Linux kernel for communication - * with the user space. - */ -struct tee_cmd { - TEEC_Result err; - uint32_t origin; - uint32_t cmd; - TEEC_UUID *uuid; - void *data; - uint32_t data_size; - TEEC_Operation *op; -}; - -#define TEE_OPEN_SESSION_IOC _IOWR('t', 161, struct tee_cmd) -#define TEE_CLOSE_SESSION_IOC _IOWR('t', 162, unsigned long) -#define TEE_INVOKE_COMMAND_IOC _IOWR('t', 163, struct tee_cmd) -#define TEE_REQUEST_CANCELLATION_IOC _IOWR('t', 164, struct tee_cmd) -#define TEE_ALLOC_SHM_IOC _IOWR('t', 165, TEEC_SharedMemory) - -#endif diff --git a/include/linux/tee_kernel_api.h b/include/linux/tee_kernel_api.h index cd37df8..2093f1d 100644 --- a/include/linux/tee_kernel_api.h +++ b/include/linux/tee_kernel_api.h @@ -23,17 +23,17 @@ * struct TEEC_Context - Represents a connection between a client application * and a TEE. */ -typedef struct { +/*typedef struct { char devname[256]; -} TEEC_Context; +} TEEC_Context;*/ /** * struct TEEC_Session - Represents a connection between a client application * and a trusted application. */ -typedef struct { +/*typedef struct { void *session; -} TEEC_Session; +} TEEC_Session;*/ /** * TEEC_InitializeContext() - Initializes a context holding connection @@ -95,11 +95,11 @@ TEEC_Result TEEC_FinalizeContext(TEEC_Context *context); * */ TEEC_Result TEEC_OpenSession(TEEC_Context *context, - TEEC_Session * session, - const TEEC_UUID * destination, + TEEC_Session *session, + const TEEC_UUID *destination, uint32_t connectionMethod, const void *connectionData, - TEEC_Operation * operation, + TEEC_Operation *operation, uint32_t *returnOrigin); /** @@ -129,7 +129,7 @@ void TEEC_CloseSession(TEEC_Session *session); */ TEEC_Result TEEC_InvokeCommand(TEEC_Session *session, uint32_t commandID, - TEEC_Operation * operation, + TEEC_Operation *operation, uint32_t *returnOrigin); /** @@ -145,7 +145,7 @@ TEEC_Result TEEC_InvokeCommand(TEEC_Session *session, * @return TEEC_Result Something failed. */ TEEC_Result TEEC_RegisterSharedMemory(TEEC_Context *context, - TEEC_SharedMemory * sharedMem); + TEEC_SharedMemory *sharedMem); /** * TEEC_AllocateSharedMemory() - Allocate shared memory for TEE. @@ -159,7 +159,7 @@ TEEC_Result TEEC_RegisterSharedMemory(TEEC_Context *context, * @return TEEC_Result Something failed. */ TEEC_Result TEEC_AllocateSharedMemory(TEEC_Context *context, - TEEC_SharedMemory * sharedMem); + TEEC_SharedMemory *sharedMem); /** * TEEC_ReleaseSharedMemory() - Free or deregister the shared memory. @@ -168,6 +168,7 @@ TEEC_Result TEEC_AllocateSharedMemory(TEEC_Context *context, */ void TEEC_ReleaseSharedMemory(TEEC_SharedMemory *sharedMemory); +#if 0 /** * TEEC_RequestCancellation() - Request the cancellation of a pending open * session or command invocation. @@ -175,6 +176,7 @@ void TEEC_ReleaseSharedMemory(TEEC_SharedMemory *sharedMemory); * @param operation Pointer to an operation previously passed to open session * or invoke. */ -/* void TEEC_RequestCancellation(TEEC_Operation *operation); */ +void TEEC_RequestCancellation(TEEC_Operation *operation); +#endif #endif |