Hi,
Here's my latest patches for the generic TEE subsystem. There's two patches with incompatible driver API changes which makes the OP-TEE driver fail to compile. I'm fixing that in "optee: incompatible tee subsystem api change" as a separate commit to make it easy to squash the different patches later when we send it to the kernel mailing list.
While this review is ongoing the patches are also available at https://github.com/jenswi-linaro/linux/tree/tee-dev3 I'll make a pull request of this when this review is done.
Jens Wiklander (7): tee: bugfix refcount tee: fix tee_shm_va2pa() and tee_shm_pa2va() tee: shared memory from CMA or res_mem tee: rename struct tee_filp optee: incompatible tee subsystem api change tee: update module reference counter tee: bugfix error handling of tee_shm_alloc()
drivers/sec-hw/Makefile | 3 +- drivers/sec-hw/optee/Kconfig | 3 +- drivers/sec-hw/optee/Makefile | 3 +- drivers/sec-hw/optee/core.c | 222 +++++++++++++++++++++++++++++++++++++++ drivers/sec-hw/optee/optee.c | 201 ----------------------------------- drivers/sec-hw/tee.c | 76 ++++++++------ drivers/sec-hw/tee_private.h | 51 ++++++--- drivers/sec-hw/tee_shm.c | 204 +++++++++++++++++------------------- drivers/sec-hw/tee_shm_pool.c | 232 +++++++++++++++++++++++++++++++++++++++++ include/linux/sec-hw/tee_drv.h | 75 ++++++++++--- 10 files changed, 697 insertions(+), 373 deletions(-) create mode 100644 drivers/sec-hw/optee/core.c delete mode 100644 drivers/sec-hw/optee/optee.c create mode 100644 drivers/sec-hw/tee_shm_pool.c
Bugfix reference counter on file descriptor.
Before this all user space allocated shared memory allocations where only freed when the session was closed due to the refcount being +1 too much.
Signed-off-by: Jens Wiklander jens.wiklander@linaro.org --- drivers/sec-hw/tee.c | 5 +++++ drivers/sec-hw/tee_shm.c | 7 ++++--- 2 files changed, 9 insertions(+), 3 deletions(-)
diff --git a/drivers/sec-hw/tee.c b/drivers/sec-hw/tee.c index 4f4badb..28d8bc6 100644 --- a/drivers/sec-hw/tee.c +++ b/drivers/sec-hw/tee.c @@ -117,6 +117,11 @@ static long tee_ioctl_shm_alloc(struct tee_filp *teefilp, ret = -EFAULT; goto err; } + /* + * When user space closes the file descriptor the shared memory + * should be freed + */ + tee_shm_put(shm); return 0; err: if (data.fd >= 0) diff --git a/drivers/sec-hw/tee_shm.c b/drivers/sec-hw/tee_shm.c index 61ca5aa..6050644 100644 --- a/drivers/sec-hw/tee_shm.c +++ b/drivers/sec-hw/tee_shm.c @@ -123,6 +123,7 @@ struct tee_shm *tee_shm_alloc(struct tee_device *teedev, ret = ERR_CAST(shm->dmabuf); goto err; } + get_dma_buf(shm->dmabuf);
mutex = &teeshm_list_mutex; list_shm = &teeshm_list; @@ -188,8 +189,6 @@ int tee_shm_fd(struct tee_shm *shm) return -EINVAL;
fd = dma_buf_fd(shm->dmabuf, O_CLOEXEC); - if (fd >= 0) - get_dma_buf(shm->dmabuf); return fd; } EXPORT_SYMBOL_GPL(tee_shm_fd); @@ -391,8 +390,10 @@ struct tee_shm *tee_shm_get_from_fd(int fd) if (IS_ERR(dmabuf)) return ERR_CAST(dmabuf);
- if (!is_shm_dma_buf(dmabuf)) + if (!is_shm_dma_buf(dmabuf)) { + dma_buf_put(dmabuf); return ERR_PTR(-EINVAL); + } return dmabuf->priv; } EXPORT_SYMBOL_GPL(tee_shm_get_from_fd);
Fixes to tee_shm_va2pa() and tee_shm_pa2va() to do address translations based on addresses in struct tee_shm instead of using virt_to_phys() and phys_to_virt().
Signed-off-by: Jens Wiklander jens.wiklander@linaro.org --- drivers/sec-hw/tee_shm.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-)
diff --git a/drivers/sec-hw/tee_shm.c b/drivers/sec-hw/tee_shm.c index 6050644..d70b5ad 100644 --- a/drivers/sec-hw/tee_shm.c +++ b/drivers/sec-hw/tee_shm.c @@ -340,9 +340,7 @@ int tee_shm_va2pa(struct tee_shm *shm, void *va, phys_addr_t *pa) if ((char *)va >= ((char *)shm->kaddr + shm->size)) return -EINVAL;
- if (pa) - *pa = virt_to_phys(va); - return 0; + return tee_shm_get_pa(shm, (u_long)va - (u_long)shm->kaddr, pa); } EXPORT_SYMBOL_GPL(tee_shm_va2pa);
@@ -354,8 +352,13 @@ int tee_shm_pa2va(struct tee_shm *shm, phys_addr_t pa, void **va) if (pa >= (shm->paddr + shm->size)) return -EINVAL;
- if (va) - *va = phys_to_virt(pa); + if (va) { + void *v = tee_shm_get_va(shm, pa - shm->paddr); + + if (IS_ERR(v)) + return PTR_ERR(v); + *va = v; + } return 0; } EXPORT_SYMBOL_GPL(tee_shm_pa2va); @@ -364,7 +367,7 @@ void *tee_shm_get_va(struct tee_shm *shm, size_t offs) { if (offs >= shm->size) return ERR_PTR(-EINVAL); - return shm->kaddr; + return (char *)shm->kaddr + offs; } EXPORT_SYMBOL_GPL(tee_shm_get_va);
* Moves shared memory pool handing into tee_shm_pool.c and contains it in a 'struct tee_shm_pool' which the specific driver creates before the driver is registered in the subsystem. * Adds support for allocating shared memory from either CMA or previously reserved memory. * Removes support for allocating shared memory using __get_free_pages(). * Removes teedev pointer from 'struct tee_shm', the pointer can be acquired through the teefilp pointer instead. For private shared memory object the teefilp pointer points to teefilp_private inside the 'struct tee_device' which holds a pointer to teedev.
Note that there's an incompatible API change in tee_drv.h which causes a compile error in OP-TEE. This is fixed in next OP-TEE patch.
Signed-off-by: Jens Wiklander jens.wiklander@linaro.org --- drivers/sec-hw/Makefile | 1 + drivers/sec-hw/tee.c | 13 ++- drivers/sec-hw/tee_private.h | 47 +++++++-- drivers/sec-hw/tee_shm.c | 148 ++++++++++---------------- drivers/sec-hw/tee_shm_pool.c | 232 +++++++++++++++++++++++++++++++++++++++++ include/linux/sec-hw/tee_drv.h | 54 +++++++++- 6 files changed, 383 insertions(+), 112 deletions(-) create mode 100644 drivers/sec-hw/tee_shm_pool.c
diff --git a/drivers/sec-hw/Makefile b/drivers/sec-hw/Makefile index 1c3d990..77a3e9c 100644 --- a/drivers/sec-hw/Makefile +++ b/drivers/sec-hw/Makefile @@ -1,3 +1,4 @@ obj-$(CONFIG_TEE_GENERIC) += tee.o obj-$(CONFIG_TEE_GENERIC) += tee_shm.o +obj-$(CONFIG_TEE_GENERIC) += tee_shm_pool.o obj-$(CONFIG_TEE_OPTEE) += optee/ diff --git a/drivers/sec-hw/tee.c b/drivers/sec-hw/tee.c index 28d8bc6..afd2925 100644 --- a/drivers/sec-hw/tee.c +++ b/drivers/sec-hw/tee.c @@ -15,10 +15,7 @@ #include <linux/fs.h> #include <linux/module.h> #include <linux/slab.h> -#include <linux/fdtable.h> -#include <linux/thread_info.h> #include <linux/uaccess.h> -#include <linux/sched.h> #include <linux/sec-hw/tee_drv.h> #include "tee_private.h"
@@ -125,7 +122,7 @@ static long tee_ioctl_shm_alloc(struct tee_filp *teefilp, return 0; err: if (data.fd >= 0) - __close_fd(current->files, data.fd); + tee_shm_put_fd(data.fd); tee_shm_free(shm); return ret; } @@ -174,14 +171,15 @@ static const struct file_operations tee_fops = { };
struct tee_device *tee_register(const struct tee_desc *teedesc, - struct device *dev, void *driver_data) + struct device *dev, struct tee_shm_pool *pool, + void *driver_data) { static atomic_t device_no = ATOMIC_INIT(-1); static atomic_t priv_device_no = ATOMIC_INIT(-1); struct tee_device *teedev; int ret;
- if (!teedesc || !teedesc->name || !dev) + if (!teedesc || !teedesc->name || !dev || !pool) return NULL;
teedev = kzalloc(sizeof(*teedev), GFP_KERNEL); @@ -190,6 +188,7 @@ struct tee_device *tee_register(const struct tee_desc *teedesc,
teedev->dev = dev; teedev->desc = teedesc; + teedev->pool = pool; teedev->driver_data = driver_data;
if (teedesc->flags & TEE_DESC_PRIVILEGED) @@ -212,7 +211,7 @@ struct tee_device *tee_register(const struct tee_desc *teedesc, }
INIT_LIST_HEAD(&teedev->list_shm); - mutex_init(&teedev->mutex); + teedev->teefilp_private.teedev = teedev;
dev_set_drvdata(teedev->miscdev.this_device, teedev);
diff --git a/drivers/sec-hw/tee_private.h b/drivers/sec-hw/tee_private.h index 0bf4f52..06a59ab 100644 --- a/drivers/sec-hw/tee_private.h +++ b/drivers/sec-hw/tee_private.h @@ -13,28 +13,53 @@ #ifndef TEE_PRIVATE_H #define TEE_PRIVATE_H
-#define TEE_MAX_DEV_NAME_LEN 32 -struct tee_device { - char name[TEE_MAX_DEV_NAME_LEN]; - const struct tee_desc *desc; - struct device *dev; - struct miscdevice miscdev; - struct list_head list_shm; - struct mutex mutex; - void *driver_data; -}; +struct tee_device;
struct tee_shm { struct list_head list_node; - struct tee_device *teedev; struct tee_filp *teefilp; phys_addr_t paddr; void *kaddr; size_t size; struct dma_buf *dmabuf; + struct page *pages; u32 flags; };
+struct tee_shm_pool_mgr; +struct tee_shm_pool_mgr_ops { + int (*alloc)(struct tee_shm_pool_mgr *poolmgr, struct tee_shm *shm, + size_t size); + void (*free)(struct tee_shm_pool_mgr *poolmgr, struct tee_shm *shm); +}; + +struct tee_shm_pool_mgr { + const struct tee_shm_pool_mgr_ops *ops; + void *private_data; +}; + +struct tee_shm_pool { + struct tee_shm_pool_mgr private_mgr; + struct tee_shm_pool_mgr dma_buf_mgr; + void *private_data; +}; + +#define TEE_MAX_DEV_NAME_LEN 32 +struct tee_device { + char name[TEE_MAX_DEV_NAME_LEN]; + const struct tee_desc *desc; + struct device *dev; + struct miscdevice miscdev; + + void *driver_data; + + struct list_head list_shm; + struct tee_filp teefilp_private; + struct tee_shm_pool *pool; +}; + void tee_shm_free_by_teefilp(struct tee_filp *teefilp);
+int tee_shm_init(void); + #endif /*TEE_PRIVATE_H*/ diff --git a/drivers/sec-hw/tee_shm.c b/drivers/sec-hw/tee_shm.c index d70b5ad..7e8a082 100644 --- a/drivers/sec-hw/tee_shm.c +++ b/drivers/sec-hw/tee_shm.c @@ -12,13 +12,16 @@ * */ #include <linux/device.h> +#include <linux/fdtable.h> +#include <linux/sched.h> #include <linux/dma-buf.h> #include <linux/slab.h> +#include <linux/module.h> #include <linux/sec-hw/tee_drv.h> #include "tee_private.h"
-static DEFINE_MUTEX(teeshm_list_mutex); -static LIST_HEAD(teeshm_list); +/* Mutex for all shm objects and lists */ +static DEFINE_MUTEX(teeshm_mutex); static LIST_HEAD(teeshm_pending_free_list);
static void tee_shm_release(struct tee_shm *shm); @@ -74,10 +77,10 @@ static struct dma_buf_ops tee_shm_dma_buf_ops = { struct tee_shm *tee_shm_alloc(struct tee_device *teedev, struct tee_filp *teefilp, size_t size, u32 flags) { + struct tee_shm_pool_mgr *poolm = NULL; struct tee_shm *shm; void *ret; - struct mutex *mutex; - struct list_head *list_shm; + int rc;
if (!(flags & TEE_SHM_MAPPED)) { dev_err(teedev->dev, "only mapped allocations supported\n"); @@ -96,27 +99,30 @@ struct tee_shm *tee_shm_alloc(struct tee_device *teedev, shm->flags = flags;
if (flags & TEE_SHM_DMA_BUF) { - int order = get_order(size); - - /* Global shm's must have a teefilp attached */ + /* dma_buf shm:s must have a teefilp attached */ if (!teefilp) { ret = ERR_PTR(-EINVAL); goto err; } - shm->teefilp = teefilp; - shm->size = (1 << order) << PAGE_SHIFT; - /* Request zeroed pages to not leak information */ - shm->kaddr = (void *)__get_free_pages(GFP_KERNEL|__GFP_ZERO, - order); - if (!shm->kaddr) { - dev_err(teedev->dev, - "failed to get order %d pages for shared memory\n", - order); - ret = ERR_PTR(-ENOMEM); + poolm = &teedev->pool->dma_buf_mgr; + } else { + /* Driver private shm:s must not have a teefilp attached */ + if (teefilp) { + ret = ERR_PTR(-EINVAL); goto err; } + shm->teefilp = &teedev->teefilp_private; + poolm = &teedev->pool->private_mgr; + } + + rc = poolm->ops->alloc(poolm, shm, size); + if (rc) { + ret = ERR_PTR(rc); + goto err; + }
+ if (flags & TEE_SHM_DMA_BUF) { shm->dmabuf = dma_buf_export(shm, &tee_shm_dma_buf_ops, shm->size, O_RDWR, NULL); if (IS_ERR(shm->dmabuf)) { @@ -125,56 +131,25 @@ struct tee_shm *tee_shm_alloc(struct tee_device *teedev, } get_dma_buf(shm->dmabuf);
- mutex = &teeshm_list_mutex; - list_shm = &teeshm_list; - } else { - /* Driver private shm's must not have a teefilp attached */ - if (teefilp) { - ret = ERR_PTR(-EINVAL); - goto err; - } - shm->size = size; - shm->kaddr = kzalloc(size, GFP_KERNEL); - if (!shm->kaddr) { - dev_err(teedev->dev, - "failed to allocate %zu bytes of shared memory\n", - size); - ret = ERR_PTR(-ENOMEM); - goto err; - } - - mutex = &teedev->mutex; - list_shm = &teedev->list_shm; - } - - shm->teedev = teedev; - shm->paddr = virt_to_phys(shm->kaddr); - - if (flags & TEE_SHM_DMA_BUF) { /* * Only call share on global shm:s, as the driver private * shm:s always originates from the driver itself. */ - int rc = teedev->desc->ops->shm_share(shm); - + rc = teedev->desc->ops->shm_share(shm); if (rc) { ret = ERR_PTR(rc); goto err; } }
- mutex_lock(mutex); - list_add_tail(&shm->list_node, list_shm); - mutex_unlock(mutex); + mutex_lock(&teeshm_mutex); + list_add_tail(&shm->list_node, &teedev->list_shm); + mutex_unlock(&teeshm_mutex);
return shm; err: - if (shm->kaddr) { - if (shm->flags & TEE_SHM_DMA_BUF) - free_pages((unsigned long)shm->kaddr, get_order(size)); - else - kfree(shm->kaddr); - } + if (poolm && shm->kaddr) + poolm->ops->free(poolm, shm); kfree(shm); return ret; } @@ -193,30 +168,34 @@ int tee_shm_fd(struct tee_shm *shm) } EXPORT_SYMBOL_GPL(tee_shm_fd);
+int tee_shm_put_fd(int fd) +{ + return __close_fd(current->files, fd); +} +EXPORT_SYMBOL_GPL(tee_shm_put_fd); + + static void tee_shm_release(struct tee_shm *shm) { - struct tee_device *teedev = shm->teedev; + struct tee_device *teedev = shm->teefilp->teedev; + struct tee_shm_pool_mgr *poolm;
- if (shm->flags & TEE_SHM_DMA_BUF) { - /* Only unshare global shm:s */ - shm->teedev->desc->ops->shm_unshare(shm); + mutex_lock(&teeshm_mutex); + list_del(&shm->list_node); + mutex_unlock(&teeshm_mutex);
- free_pages((unsigned long)shm->kaddr, get_order(shm->size)); - mutex_lock(&teeshm_list_mutex); - list_del(&shm->list_node); - mutex_unlock(&teeshm_list_mutex); - } else { - kfree(shm->kaddr); - mutex_lock(&teedev->mutex); - list_del(&shm->list_node); - mutex_unlock(&teedev->mutex); - } + if (shm->flags & TEE_SHM_DMA_BUF) + poolm = &teedev->pool->dma_buf_mgr; + else + poolm = &teedev->pool->private_mgr;
+ poolm->ops->free(poolm, shm); kfree(shm); }
void tee_shm_free_by_teefilp(struct tee_filp *teefilp) { + struct tee_device *teedev = teefilp->teedev; struct tee_shm *shm; struct tee_shm *tmp; LIST_HEAD(tmp_list); @@ -224,36 +203,30 @@ void tee_shm_free_by_teefilp(struct tee_filp *teefilp) /* * Move all matching shm:s to a temporary list */ - mutex_lock(&teeshm_list_mutex); - list_for_each_entry_safe(shm, tmp, &teeshm_list, list_node) { + mutex_lock(&teeshm_mutex); + list_for_each_entry_safe(shm, tmp, &teedev->list_shm, list_node) { if (shm->teefilp == teefilp) { list_del(&shm->list_node); list_add_tail(&shm->list_node, &tmp_list); } } - mutex_unlock(&teeshm_list_mutex); + mutex_unlock(&teeshm_mutex);
/* * For each shm in the temporary list move it to the pending free * list and call tee_shm_free(). Once the ref_count is 0 the shm * will be removed from this list. - * - * Since the 'struct tee_filp' is about to be freed (the reason - * this function was called) set the teefilp to NULL. The only - * purpose of the teefilp in 'struct tee_shm' is to be able to find - * all shm:s related to a teefilp. */ while (true) { - mutex_lock(&teeshm_list_mutex); + mutex_lock(&teeshm_mutex); shm = list_first_entry_or_null(&tmp_list, struct tee_shm, list_node); if (shm) { list_del(&shm->list_node); list_add_tail(&shm->list_node, &teeshm_pending_free_list); - shm->teefilp = NULL; } - mutex_unlock(&teeshm_list_mutex); + mutex_unlock(&teeshm_mutex); if (!shm) break; tee_shm_free(shm); @@ -263,6 +236,7 @@ void tee_shm_free_by_teefilp(struct tee_filp *teefilp)
void tee_shm_free(struct tee_shm *shm) { + /* * dma_buf_put() decreases the dmabuf reference counter and will * call tee_shm_release() when the last reference is gone. @@ -295,25 +269,15 @@ static struct tee_shm *tee_shm_find_by_key(struct tee_device *teedev, u32 flags, { struct tee_shm *ret = NULL; struct tee_shm *shm; - struct mutex *mutex; - struct list_head *list_shm; - - if (flags & TEE_SHM_DMA_BUF) { - mutex = &teeshm_list_mutex; - list_shm = &teeshm_list; - } else { - mutex = &teedev->mutex; - list_shm = &teedev->list_shm; - }
- mutex_lock(mutex); - list_for_each_entry(shm, list_shm, list_node) { + mutex_lock(&teeshm_mutex); + list_for_each_entry(shm, &teedev->list_shm, list_node) { if (cmp(shm, key)) { ret = shm; break; } } - mutex_unlock(mutex); + mutex_unlock(&teeshm_mutex);
return ret; } diff --git a/drivers/sec-hw/tee_shm_pool.c b/drivers/sec-hw/tee_shm_pool.c new file mode 100644 index 0000000..b1abad7 --- /dev/null +++ b/drivers/sec-hw/tee_shm_pool.c @@ -0,0 +1,232 @@ +/* + * Copyright (c) 2015, Linaro Limited + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include <linux/device.h> +#include <linux/dma-buf.h> +#include <linux/slab.h> +#include <linux/genalloc.h> +#ifdef CONFIG_CMA +#include <linux/cma.h> +#include <linux/dma-contiguous.h> +#endif +#include <linux/sec-hw/tee_drv.h> +#include "tee_private.h" + +#define SHM_POOL_NUM_PRIV_PAGES 1 + +static int pool_op_gen_alloc(struct tee_shm_pool_mgr *poolm, + struct tee_shm *shm, size_t size) +{ + unsigned long va; + struct gen_pool *genpool = poolm->private_data; + size_t s = roundup(size, 1 << genpool->min_alloc_order); + + va = gen_pool_alloc(genpool, s); + if (!va) + return -ENOMEM; + shm->kaddr = (void *)va; + shm->paddr = gen_pool_virt_to_phys(genpool, va); + shm->size = s; + return 0; +} + +static void pool_op_gen_free(struct tee_shm_pool_mgr *poolm, + struct tee_shm *shm) +{ + gen_pool_free(poolm->private_data, (unsigned long)shm->kaddr, + shm->size); + shm->kaddr = NULL; +} + +static const struct tee_shm_pool_mgr_ops pool_ops_generic = { + .alloc = pool_op_gen_alloc, + .free = pool_op_gen_free, +}; + +#ifdef CONFIG_CMA +static int pool_op_cma_alloc(struct tee_shm_pool_mgr *poolm, + struct tee_shm *shm, size_t size) +{ + unsigned long order = get_order(PAGE_SIZE); + size_t count; + struct page *pages; + + /* + * It's not valid to call this function with size = 0, but if size + * is 0 we'll get a very large number and the allocation will fail. + */ + count = ((size - 1) >> PAGE_SHIFT) + 1; + pages = cma_alloc(poolm->private_data, count, order); + if (!pages) + return -ENOMEM; + shm->kaddr = page_address(pages); + shm->pages = pages; + shm->paddr = virt_to_phys(shm->kaddr); + shm->size = count << PAGE_SHIFT; + return 0; +} + +static void pool_op_cma_free(struct tee_shm_pool_mgr *poolm, + struct tee_shm *shm) +{ + size_t count; + + count = shm->size >> PAGE_SHIFT; + cma_release(poolm->private_data, shm->pages, count); + shm->kaddr = NULL; +} + +static const struct tee_shm_pool_mgr_ops pool_ops_cma = { + .alloc = pool_op_cma_alloc, + .free = pool_op_cma_free, +}; + +struct tee_shm_pool *tee_shm_pool_alloc_cma(struct device *dev, u_long *vaddr, + phys_addr_t *paddr, size_t *size) +{ + struct cma *cma = dev_get_cma_area(dev); + struct tee_shm_pool *pool; + struct page *page; + size_t order = get_order(PAGE_SIZE); + struct gen_pool *genpool = NULL; + void *va; + int ret; + + pool = kzalloc(sizeof(*pool), GFP_KERNEL); + if (!pool) + return ERR_PTR(-ENOMEM); + + page = cma_alloc(cma, SHM_POOL_NUM_PRIV_PAGES, order); + if (!page) { + ret = -ENOMEM; + goto err; + } + genpool = gen_pool_create(get_order(8), -1); + if (!genpool) { + ret = -ENOMEM; + goto err; + } + gen_pool_set_algo(genpool, gen_pool_best_fit, NULL); + + va = page_address(page); + ret = gen_pool_add_virt(genpool, (u_long)va, virt_to_phys(va), + SHM_POOL_NUM_PRIV_PAGES * PAGE_SIZE, -1); + if (ret) + goto err; + + pool->private_data = page; + pool->private_mgr.private_data = genpool; + pool->private_mgr.ops = &pool_ops_generic; + pool->dma_buf_mgr.private_data = cma; + pool->dma_buf_mgr.ops = &pool_ops_cma; + + *paddr = cma_get_base(cma); + *vaddr = (u_long)phys_to_virt(*paddr); + *size = cma_get_size(cma); + return pool; +err: + if (genpool) + gen_pool_destroy(genpool); + if (page) + cma_release(cma, page, SHM_POOL_NUM_PRIV_PAGES); + kfree(pool); + return ERR_PTR(ret); +} +EXPORT_SYMBOL_GPL(tee_shm_pool_alloc_cma); +#endif + +struct tee_shm_pool *tee_shm_pool_alloc_res_mem(u_long vaddr, + phys_addr_t paddr, size_t size) +{ + size_t page_mask = PAGE_SIZE - 1; + size_t priv_size = PAGE_SIZE * SHM_POOL_NUM_PRIV_PAGES; + struct tee_shm_pool *pool; + struct gen_pool *genpool = NULL; + int ret; + + /* + * Start and end must be page aligned + */ + if ((vaddr & page_mask) || (paddr & page_mask) || (size & page_mask)) + return ERR_PTR(-EINVAL); + + /* + * Wouldn't make sense to have less than twice the number of + * private pages, in practice the size has to be much larger, but + * this is the absolute minimum. + */ + if (size < priv_size * 2) + return ERR_PTR(-EINVAL); + + pool = kzalloc(sizeof(*pool), GFP_KERNEL); + if (!pool) + return ERR_PTR(-ENOMEM); + + /* + * Create the pool for driver private shared memory + */ + genpool = gen_pool_create(3 /* 8 byte aligned */, -1); + if (!genpool) { + ret = -ENOMEM; + goto err; + } + gen_pool_set_algo(genpool, gen_pool_best_fit, NULL); + ret = gen_pool_add_virt(genpool, vaddr, paddr, priv_size, -1); + if (ret) + goto err; + pool->private_mgr.private_data = genpool; + pool->private_mgr.ops = &pool_ops_generic; + + /* + * Create the pool for dma_buf shared memory + */ + genpool = gen_pool_create(PAGE_SHIFT, -1); + gen_pool_set_algo(genpool, gen_pool_best_fit, NULL); + if (!genpool) { + ret = -ENOMEM; + goto err; + } + ret = gen_pool_add_virt(genpool, vaddr + priv_size, paddr + priv_size, + size - priv_size, -1); + if (ret) + goto err; + pool->dma_buf_mgr.private_data = genpool; + pool->dma_buf_mgr.ops = &pool_ops_generic; + return pool; +err: + if (pool->private_mgr.private_data) + gen_pool_destroy(pool->private_mgr.private_data); + if (genpool) + gen_pool_destroy(genpool); + kfree(pool); + return ERR_PTR(ret); +} +EXPORT_SYMBOL_GPL(tee_shm_pool_alloc_res_mem); + +void tee_shm_pool_free(struct tee_shm_pool *pool) +{ +#ifdef CONFIG_CMA + if (pool->dma_buf_mgr.ops == &pool_ops_cma) { + gen_pool_destroy(pool->private_mgr.private_data); + cma_release(pool->dma_buf_mgr.private_data, pool->private_data, + SHM_POOL_NUM_PRIV_PAGES); + } else +#endif + if (pool->dma_buf_mgr.ops == &pool_ops_generic) { + gen_pool_destroy(pool->private_mgr.private_data); + gen_pool_destroy(pool->dma_buf_mgr.private_data); + } + + kfree(pool); +} +EXPORT_SYMBOL_GPL(tee_shm_pool_free); diff --git a/include/linux/sec-hw/tee_drv.h b/include/linux/sec-hw/tee_drv.h index 0ea8c10..f9dd9d6 100644 --- a/include/linux/sec-hw/tee_drv.h +++ b/include/linux/sec-hw/tee_drv.h @@ -32,7 +32,7 @@
struct tee_device; struct tee_shm; - +struct tee_shm_pool;
/** * struct tee_filp - driver specific file pointer data @@ -90,7 +90,8 @@ struct tee_desc { * @returns a pointer to struct tee_device */ struct tee_device *tee_register(const struct tee_desc *teedesc, - struct device *dev, void *driver_data); + struct device *dev, struct tee_shm_pool *pool, + void *driver_data);
/** * tee_unregister() - Unregister a specific TEE driver @@ -99,6 +100,48 @@ struct tee_device *tee_register(const struct tee_desc *teedesc, void tee_unregister(struct tee_device *teedev);
/** + * tee_shm_pool_alloc_cma() - Create a shared memory pool based on device default CMA area + * @dev: Device to get default CMA area from + * @vaddr: Returned virtual address of start of CMA area + * @paddr: Returned physical address of start of CMA area + * @size: Returned size of CMA area + * @returns pointer to a 'struct tee_shm_pool' or an ERR_PTR on failure. + */ +#ifdef CONFIG_CMA +struct tee_shm_pool *tee_shm_pool_alloc_cma(struct device *dev, u_long *vaddr, + phys_addr_t *paddr, size_t *size); +#else +struct tee_shm_pool *tee_shm_pool_alloc_cma(struct device *dev, u_long *vaddr, + phys_addr_t *paddr, size_t *size) +{ + return ERR_PTR(-ENOENT); +} +#endif + +/** + * tee_shm_pool_alloc_res_mem() - Create a shared memory pool a reserved memory range + * @vaddr: Virtual address of start of pool + * @paddr: Physical address of start of pool + * @size: Size in bytes of the pool + * + * Start of pool will be rounded up to the nearest page, end of pool will + * be rounded down to the nearest page. + * + * @returns pointer to a 'struct tee_shm_pool' or an ERR_PTR on failure. + */ +struct tee_shm_pool *tee_shm_pool_alloc_res_mem(u_long vaddr, + phys_addr_t paddr, size_t size); + +/** + * tee_shm_pool_free() - Free a shared memory pool + * @pool: The shared memory pool to free + * + * The must be no remaining shared memory allocated from this pool when + * this function is called. + */ +void tee_shm_pool_free(struct tee_shm_pool *pool); + +/** * tee_get_drvdata() - Return driver_data pointer * @returns the driver_data pointer supplied to tee_register(). */ @@ -218,4 +261,11 @@ void tee_shm_put(struct tee_shm *shm); */ int tee_shm_fd(struct tee_shm *shm);
+/** + * tee_shm_put_fd() - Decrease reference count and close file descriptor + * @fd: File descriptor to close + * @returns < 0 on failure + */ +int tee_shm_put_fd(int fd); + #endif /*__TEE_DRV_H*/
Renames struct tee_filp to struct tee_context.
Note that this is an incompatible API change in tee_drv.h which causes a compile error in OP-TEE. This is fixed in next OP-TEE patch.
Signed-off-by: Jens Wiklander jens.wiklander@linaro.org --- drivers/sec-hw/tee.c | 56 +++++++++++++++++++++--------------------- drivers/sec-hw/tee_private.h | 6 ++--- drivers/sec-hw/tee_shm.c | 22 ++++++++--------- include/linux/sec-hw/tee_drv.h | 20 +++++++-------- 4 files changed, 52 insertions(+), 52 deletions(-)
diff --git a/drivers/sec-hw/tee.c b/drivers/sec-hw/tee.c index afd2925..ce54be2 100644 --- a/drivers/sec-hw/tee.c +++ b/drivers/sec-hw/tee.c @@ -23,46 +23,46 @@ static int tee_open(struct inode *inode, struct file *filp) { int ret; struct tee_device *teedev; - struct tee_filp *teefilp; + struct tee_context *ctx;
teedev = container_of(filp->private_data, struct tee_device, miscdev); - teefilp = kzalloc(sizeof(*teefilp), GFP_KERNEL); - if (!teefilp) + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + if (!ctx) return -ENOMEM;
- teefilp->teedev = teedev; - filp->private_data = teefilp; - ret = teedev->desc->ops->open(teefilp); + ctx->teedev = teedev; + filp->private_data = ctx; + ret = teedev->desc->ops->open(ctx); if (ret) - kfree(teefilp); + kfree(ctx); return ret; }
static int tee_release(struct inode *inode, struct file *filp) { - struct tee_filp *teefilp = filp->private_data; + struct tee_context *ctx = filp->private_data;
- /* Free all shm:s related to this teefilp */ - tee_shm_free_by_teefilp(teefilp); + /* Free all shm:s related to this ctx */ + tee_shm_free_by_tee_context(ctx);
- teefilp->teedev->desc->ops->release(teefilp); + ctx->teedev->desc->ops->release(ctx); return 0; }
-static long tee_ioctl_version(struct tee_filp *teefilp, +static long tee_ioctl_version(struct tee_context *ctx, struct tee_ioctl_version __user *uvers) { struct tee_ioctl_version vers;
memset(&vers, 0, sizeof(vers)); vers.gen_version = TEE_SUBSYS_VERSION; - teefilp->teedev->desc->ops->get_version(teefilp, &vers.spec_version, - vers.uuid); + ctx->teedev->desc->ops->get_version(ctx, &vers.spec_version, + vers.uuid);
return copy_to_user(uvers, &vers, sizeof(vers)); }
-static long tee_ioctl_cmd(struct tee_filp *teefilp, +static long tee_ioctl_cmd(struct tee_context *ctx, struct tee_ioctl_cmd_data __user *ucmd) { long ret; @@ -74,10 +74,10 @@ static long tee_ioctl_cmd(struct tee_filp *teefilp, return ret;
buf_ptr = (void __user *)(uintptr_t)cmd.buf_ptr; - return teefilp->teedev->desc->ops->cmd(teefilp, buf_ptr, cmd.buf_len); + return ctx->teedev->desc->ops->cmd(ctx, buf_ptr, cmd.buf_len); }
-static long tee_ioctl_shm_alloc(struct tee_filp *teefilp, +static long tee_ioctl_shm_alloc(struct tee_context *ctx, struct tee_ioctl_shm_alloc_data __user *udata) { long ret; @@ -93,12 +93,12 @@ static long tee_ioctl_shm_alloc(struct tee_filp *teefilp,
data.fd = -1;
- shm = tee_shm_alloc(teefilp->teedev, teefilp, data.size, + shm = tee_shm_alloc(ctx->teedev, ctx, data.size, TEE_SHM_MAPPED | TEE_SHM_DMA_BUF); if (IS_ERR(shm)) return PTR_ERR(shm);
- ret = teefilp->teedev->desc->ops->shm_share(shm); + ret = ctx->teedev->desc->ops->shm_share(shm); if (ret) goto err;
@@ -127,14 +127,14 @@ err: return ret; }
-static long tee_ioctl_mem_share(struct tee_filp *teefilp, +static long tee_ioctl_mem_share(struct tee_context *ctx, struct tee_ioctl_mem_share_data __user *udata) { /* Not supported yet */ return -ENOENT; }
-static long tee_ioctl_mem_unshare(struct tee_filp *teefilp, +static long tee_ioctl_mem_unshare(struct tee_context *ctx, struct tee_ioctl_mem_share_data __user *udata) { /* Not supported yet */ @@ -143,20 +143,20 @@ static long tee_ioctl_mem_unshare(struct tee_filp *teefilp,
static long tee_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { - struct tee_filp *teefilp = filp->private_data; + struct tee_context *ctx = filp->private_data; void __user *uarg = (void __user *)arg;
switch (cmd) { case TEE_IOC_VERSION: - return tee_ioctl_version(teefilp, uarg); + return tee_ioctl_version(ctx, uarg); case TEE_IOC_CMD: - return tee_ioctl_cmd(teefilp, uarg); + return tee_ioctl_cmd(ctx, uarg); case TEE_IOC_SHM_ALLOC: - return tee_ioctl_shm_alloc(teefilp, uarg); + return tee_ioctl_shm_alloc(ctx, uarg); case TEE_IOC_MEM_SHARE: - return tee_ioctl_mem_share(teefilp, uarg); + return tee_ioctl_mem_share(ctx, uarg); case TEE_IOC_MEM_UNSHARE: - return tee_ioctl_mem_unshare(teefilp, uarg); + return tee_ioctl_mem_unshare(ctx, uarg); default: return -EINVAL; } @@ -211,7 +211,7 @@ struct tee_device *tee_register(const struct tee_desc *teedesc, }
INIT_LIST_HEAD(&teedev->list_shm); - teedev->teefilp_private.teedev = teedev; + teedev->teectx_private.teedev = teedev;
dev_set_drvdata(teedev->miscdev.this_device, teedev);
diff --git a/drivers/sec-hw/tee_private.h b/drivers/sec-hw/tee_private.h index 06a59ab..291ec52 100644 --- a/drivers/sec-hw/tee_private.h +++ b/drivers/sec-hw/tee_private.h @@ -17,7 +17,7 @@ struct tee_device;
struct tee_shm { struct list_head list_node; - struct tee_filp *teefilp; + struct tee_context *teectx; phys_addr_t paddr; void *kaddr; size_t size; @@ -54,11 +54,11 @@ struct tee_device { void *driver_data;
struct list_head list_shm; - struct tee_filp teefilp_private; + struct tee_context teectx_private; struct tee_shm_pool *pool; };
-void tee_shm_free_by_teefilp(struct tee_filp *teefilp); +void tee_shm_free_by_tee_context(struct tee_context *ctx);
int tee_shm_init(void);
diff --git a/drivers/sec-hw/tee_shm.c b/drivers/sec-hw/tee_shm.c index 7e8a082..b8127c0 100644 --- a/drivers/sec-hw/tee_shm.c +++ b/drivers/sec-hw/tee_shm.c @@ -75,7 +75,7 @@ static struct dma_buf_ops tee_shm_dma_buf_ops = { };
struct tee_shm *tee_shm_alloc(struct tee_device *teedev, - struct tee_filp *teefilp, size_t size, u32 flags) + struct tee_context *ctx, size_t size, u32 flags) { struct tee_shm_pool_mgr *poolm = NULL; struct tee_shm *shm; @@ -99,20 +99,20 @@ struct tee_shm *tee_shm_alloc(struct tee_device *teedev, shm->flags = flags;
if (flags & TEE_SHM_DMA_BUF) { - /* dma_buf shm:s must have a teefilp attached */ - if (!teefilp) { + /* dma_buf shm:s must have a context attached */ + if (!ctx) { ret = ERR_PTR(-EINVAL); goto err; } - shm->teefilp = teefilp; + shm->teectx = ctx; poolm = &teedev->pool->dma_buf_mgr; } else { - /* Driver private shm:s must not have a teefilp attached */ - if (teefilp) { + /* Driver private shm:s must not have a context attached */ + if (ctx) { ret = ERR_PTR(-EINVAL); goto err; } - shm->teefilp = &teedev->teefilp_private; + shm->teectx = &teedev->teectx_private; poolm = &teedev->pool->private_mgr; }
@@ -177,7 +177,7 @@ EXPORT_SYMBOL_GPL(tee_shm_put_fd);
static void tee_shm_release(struct tee_shm *shm) { - struct tee_device *teedev = shm->teefilp->teedev; + struct tee_device *teedev = shm->teectx->teedev; struct tee_shm_pool_mgr *poolm;
mutex_lock(&teeshm_mutex); @@ -193,9 +193,9 @@ static void tee_shm_release(struct tee_shm *shm) kfree(shm); }
-void tee_shm_free_by_teefilp(struct tee_filp *teefilp) +void tee_shm_free_by_tee_context(struct tee_context *ctx) { - struct tee_device *teedev = teefilp->teedev; + struct tee_device *teedev = ctx->teedev; struct tee_shm *shm; struct tee_shm *tmp; LIST_HEAD(tmp_list); @@ -205,7 +205,7 @@ void tee_shm_free_by_teefilp(struct tee_filp *teefilp) */ mutex_lock(&teeshm_mutex); list_for_each_entry_safe(shm, tmp, &teedev->list_shm, list_node) { - if (shm->teefilp == teefilp) { + if (shm->teectx == ctx) { list_del(&shm->list_node); list_add_tail(&shm->list_node, &tmp_list); } diff --git a/include/linux/sec-hw/tee_drv.h b/include/linux/sec-hw/tee_drv.h index f9dd9d6..c47b06b 100644 --- a/include/linux/sec-hw/tee_drv.h +++ b/include/linux/sec-hw/tee_drv.h @@ -35,13 +35,13 @@ struct tee_shm; struct tee_shm_pool;
/** - * struct tee_filp - driver specific file pointer data + * struct tee_context - driver specific context on file pointer data * @teedev: pointer to this drivers struct tee_device - * @filp_data: driver specific file pointer data, managed by the driver + * @data: driver specific context data, managed by the driver */ -struct tee_filp { +struct tee_context { struct tee_device *teedev; - void *filp_data; + void *data; };
/** @@ -54,10 +54,10 @@ struct tee_filp { * @shm_unshare: unshare some memory with Secure OS */ struct tee_driver_ops { - void (*get_version)(struct tee_filp *teefilp, u32 *version, u8 *uuid); - int (*open)(struct tee_filp *teefilp); - void (*release)(struct tee_filp *teefilp); - int (*cmd)(struct tee_filp *teefilp, void __user *buf, size_t len); + void (*get_version)(struct tee_context *ctx, u32 *version, u8 *uuid); + int (*open)(struct tee_context *ctx); + void (*release)(struct tee_context *ctx); + int (*cmd)(struct tee_context *ctx, void __user *buf, size_t len); int (*shm_share)(struct tee_shm *shm); void (*shm_unshare)(struct tee_shm *shm); }; @@ -150,7 +150,7 @@ void *tee_get_drvdata(struct tee_device *teedev); /** * tee_shm_alloc() - Allocate shared memory * @teedev: Driver that allocates the shared memory - * @teefilp: TEE file pointer when allocating global shared memory, must be + * @ctx: TEE file pointer when allocating global shared memory, must be * NULL for driver private shared memory. * @size: Requested size of shared memory * @flags: Flags setting properties for the requested shared memory. @@ -164,7 +164,7 @@ void *tee_get_drvdata(struct tee_device *teedev); * @returns a pointer to 'struct tee_shm' */ struct tee_shm *tee_shm_alloc(struct tee_device *teedev, - struct tee_filp *teefilp, size_t size, u32 flags); + struct tee_context *ctx, size_t size, u32 flags);
/** * tee_shm_free() - Free shared memory
* Updated to incompatible tee subsystem API change * Uses CMA for shared memory pool * Can be compiled as a loadable module
Signed-off-by: Jens Wiklander jens.wiklander@linaro.org --- drivers/sec-hw/Makefile | 2 +- drivers/sec-hw/optee/Kconfig | 3 +- drivers/sec-hw/optee/Makefile | 3 +- drivers/sec-hw/optee/core.c | 222 ++++++++++++++++++++++++++++++++++++++++++ drivers/sec-hw/optee/optee.c | 201 -------------------------------------- 5 files changed, 227 insertions(+), 204 deletions(-) create mode 100644 drivers/sec-hw/optee/core.c delete mode 100644 drivers/sec-hw/optee/optee.c
diff --git a/drivers/sec-hw/Makefile b/drivers/sec-hw/Makefile index 77a3e9c..c068fd4 100644 --- a/drivers/sec-hw/Makefile +++ b/drivers/sec-hw/Makefile @@ -1,4 +1,4 @@ obj-$(CONFIG_TEE_GENERIC) += tee.o obj-$(CONFIG_TEE_GENERIC) += tee_shm.o obj-$(CONFIG_TEE_GENERIC) += tee_shm_pool.o -obj-$(CONFIG_TEE_OPTEE) += optee/ +obj-$(CONFIG_OPTEE) += optee/ diff --git a/drivers/sec-hw/optee/Kconfig b/drivers/sec-hw/optee/Kconfig index e7d639e..b196628 100644 --- a/drivers/sec-hw/optee/Kconfig +++ b/drivers/sec-hw/optee/Kconfig @@ -14,9 +14,10 @@ # with this program. If not, see http://www.gnu.org/licenses/
# OP-TEE Trusted Execution Environment Configuration -config TEE_OPTEE +config OPTEE tristate "OP-TEE" default n depends on TEE_GENERIC + select DMA_CMA help This implements the OP-TEE Trusted Execution Environment (TEE) driver. diff --git a/drivers/sec-hw/optee/Makefile b/drivers/sec-hw/optee/Makefile index 11a3dfc..124d22c 100644 --- a/drivers/sec-hw/optee/Makefile +++ b/drivers/sec-hw/optee/Makefile @@ -1 +1,2 @@ -obj-y += optee.o +obj-$(CONFIG_OPTEE) += optee.o +optee-objs += core.o diff --git a/drivers/sec-hw/optee/core.c b/drivers/sec-hw/optee/core.c new file mode 100644 index 0000000..8363542 --- /dev/null +++ b/drivers/sec-hw/optee/core.c @@ -0,0 +1,222 @@ +/* + * Copyright (c) 2015, Linaro Limited + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/dma-contiguous.h> +#include <linux/cma.h> +#include <linux/sec-hw/tee_drv.h> + +#define DRIVER_NAME "tee-optee" +#define OPTEE_VERSION 1 + +struct optee { + struct tee_device *supp_teedev; + struct tee_device *teedev; + struct device *dev; + struct tee_shm_pool *pool; +}; + +struct optee_context_data { + int dummy; +}; + +static struct optee_context_data *optee_new_ctx_data(void) +{ + struct optee_context_data *ctxdata; + + ctxdata = kzalloc(sizeof(struct optee_context_data), GFP_KERNEL); + return ctxdata; +} + +static void optee_destroy_ctx_data(struct optee_context_data *ctxdata) +{ + if (ctxdata) + kzfree(ctxdata); +} + +static void optee_get_version(struct tee_context *ctx, + u32 *version, u8 *uuid) +{ + *version = OPTEE_VERSION; + memset(uuid, 0, TEE_UUID_SIZE); +} + +static int optee_open(struct tee_context *ctx) +{ + ctx->data = optee_new_ctx_data(); + if (!ctx->data) + return -ENOMEM; + return 0; +} + +static void optee_release(struct tee_context *ctx) +{ + optee_destroy_ctx_data(ctx->data); + ctx->data = NULL; +} + +static int optee_cmd(struct tee_context *ctx, void __user *buf, size_t len) +{ + return -EINVAL; +} + +static int optee_shm_share(struct tee_shm *shm) +{ + /* No special action needed to share memory with OP-TEE */ + return 0; +} + +static void optee_shm_unshare(struct tee_shm *shm) +{ +} + +static struct tee_driver_ops optee_ops = { + .get_version = optee_get_version, + .open = optee_open, + .release = optee_release, + .cmd = optee_cmd, + .shm_share = optee_shm_share, + .shm_unshare = optee_shm_unshare, +}; + +static struct tee_desc optee_desc = { + .name = DRIVER_NAME "-clnt", + .ops = &optee_ops, + .owner = THIS_MODULE, +}; + +static int optee_supp_cmd(struct tee_context *teectx, void __user *buf, + size_t len) +{ + return -EINVAL; +} + +static struct tee_driver_ops optee_supp_ops = { + .get_version = optee_get_version, + .open = optee_open, + .release = optee_release, + .cmd = optee_supp_cmd, + .shm_share = optee_shm_share, + .shm_unshare = optee_shm_unshare, +}; + +static struct tee_desc optee_supp_desc = { + .name = DRIVER_NAME "-supp", + .ops = &optee_supp_ops, + .owner = THIS_MODULE, + .flags = TEE_DESC_PRIVILEGED, +}; + +static int optee_probe(struct platform_device *pdev) +{ + struct tee_shm_pool *pool; + struct optee *optee; + u_long vaddr; + phys_addr_t paddr; + size_t size; + int ret; + + pool = tee_shm_pool_alloc_cma(&pdev->dev, &vaddr, &paddr, &size); + if (IS_ERR(pool)) { + dev_err(&pdev->dev, + "can't allocate memory for shared memory pool\n"); + return PTR_ERR(pool); + } + + dev_info(&pdev->dev, "pool: va 0x%lx pa 0x%lx size %zx\n", + vaddr, (u_long)paddr, size); + + optee = devm_kzalloc(&pdev->dev, sizeof(*optee), GFP_KERNEL); + if (!optee) + return -ENOMEM; + + optee->dev = &pdev->dev; + + optee->teedev = tee_register(&optee_desc, &pdev->dev, pool, optee); + if (!optee->teedev) { + dev_err(&pdev->dev, "could not register client driver\n"); + ret = -EINVAL; + goto err; + } + + optee->supp_teedev = tee_register(&optee_supp_desc, &pdev->dev, pool, + optee); + if (!optee->teedev) { + dev_err(&pdev->dev, + "could not register supplicant driver\n"); + ret = -EINVAL; + goto err; + } + + optee->pool = pool; + platform_set_drvdata(pdev, optee); + + dev_info(&pdev->dev, "initialized driver\n"); + return 0; +err: + if (optee) { + if (optee->teedev) + tee_unregister(optee->teedev); + devm_kfree(&pdev->dev, optee); + } + if (pool) + tee_shm_pool_free(pool); + return ret; +} + +static int optee_remove(struct platform_device *pdev) +{ + struct optee *optee = platform_get_drvdata(pdev); + + tee_unregister(optee->teedev); + tee_unregister(optee->supp_teedev); + tee_shm_pool_free(optee->pool); + + return 0; +} + + +static const struct of_device_id optee_match[] = { + { .compatible = "tee-optee" }, + {}, +}; + +static struct platform_driver optee_driver = { + .driver = { + .name = DRIVER_NAME, + .of_match_table = optee_match, + }, + .probe = optee_probe, + .remove = optee_remove, +}; + +static int __init optee_init(void) +{ + return platform_driver_register(&optee_driver); +} + +static void __exit optee_exit(void) +{ + platform_driver_unregister(&optee_driver); +} + +module_init(optee_init); +module_exit(optee_exit); + +MODULE_AUTHOR("Linaro"); +MODULE_DESCRIPTION("OP-TEE TEE driver"); +MODULE_SUPPORTED_DEVICE(""); +MODULE_VERSION("1.0"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/sec-hw/optee/optee.c b/drivers/sec-hw/optee/optee.c deleted file mode 100644 index f60f061..0000000 --- a/drivers/sec-hw/optee/optee.c +++ /dev/null @@ -1,201 +0,0 @@ -/* - * Copyright (c) 2015, Linaro Limited - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - */ -#include <linux/module.h> -#include <linux/platform_device.h> -#include <linux/slab.h> -#include <linux/sec-hw/tee_drv.h> - -#define DRIVER_NAME "tee-optee" -#define OPTEE_VERSION 1 - -struct optee { - struct tee_device *supp_teedev; - struct tee_device *teedev; - struct device *dev; -}; - -struct optee_filp_state { - int dummy; -}; - -static struct optee_filp_state *optee_new_filp_state(void) -{ - struct optee_filp_state *filpstate; - - filpstate = kzalloc(sizeof(struct optee_filp_state), GFP_KERNEL); - return filpstate; -} - -static void optee_destroy_filp_state(struct optee_filp_state *filpstate) -{ - if (filpstate) - kzfree(filpstate); -} - -static void optee_get_version(struct tee_filp *teefilp, - u32 *version, u8 *uuid) -{ - *version = OPTEE_VERSION; - memset(uuid, 0, TEE_UUID_SIZE); -} - -static int optee_open(struct tee_filp *teefilp) -{ - teefilp->filp_data = optee_new_filp_state(); - if (!teefilp->filp_data) - return -ENOMEM; - return 0; -} - -static void optee_release(struct tee_filp *teefilp) -{ - optee_destroy_filp_state(teefilp->filp_data); - teefilp->filp_data = NULL; -} - -static int optee_cmd(struct tee_filp *teefilp, void __user *buf, size_t len) -{ - return -EINVAL; -} - -static int optee_shm_share(struct tee_shm *shm) -{ - /* No special action needed to share memory with OP-TEE */ - return 0; -} - -static void optee_shm_unshare(struct tee_shm *shm) -{ -} - -static struct tee_driver_ops optee_ops = { - .get_version = optee_get_version, - .open = optee_open, - .release = optee_release, - .cmd = optee_cmd, - .shm_share = optee_shm_share, - .shm_unshare = optee_shm_unshare, -}; - -static struct tee_desc optee_desc = { - .name = DRIVER_NAME "-clnt", - .ops = &optee_ops, - .owner = THIS_MODULE, -}; - -static int optee_supp_cmd(struct tee_filp *teefilp, void __user *buf, - size_t len) -{ - return -EINVAL; -} - -static struct tee_driver_ops optee_supp_ops = { - .get_version = optee_get_version, - .open = optee_open, - .release = optee_release, - .cmd = optee_supp_cmd, - .shm_share = optee_shm_share, - .shm_unshare = optee_shm_unshare, -}; - -static struct tee_desc optee_supp_desc = { - .name = DRIVER_NAME "-supp", - .ops = &optee_supp_ops, - .owner = THIS_MODULE, - .flags = TEE_DESC_PRIVILEGED, -}; - -static int optee_probe(struct platform_device *pdev) -{ - struct optee *optee; - int ret; - - optee = devm_kzalloc(&pdev->dev, sizeof(*optee), GFP_KERNEL); - if (!optee) - return -ENOMEM; - - optee->dev = &pdev->dev; - - optee->teedev = tee_register(&optee_desc, &pdev->dev, optee); - if (!optee->teedev) { - dev_err(&pdev->dev, "could not register client driver\n"); - ret = -EINVAL; - goto err; - } - - optee->supp_teedev = tee_register(&optee_supp_desc, &pdev->dev, optee); - if (!optee->teedev) { - dev_err(&pdev->dev, - "could not register supplicant driver\n"); - ret = -EINVAL; - goto err; - } - - platform_set_drvdata(pdev, optee); - - dev_info(&pdev->dev, "initialized driver\n"); - return 0; -err: - if (optee->teedev) - tee_unregister(optee->teedev); - devm_kfree(&pdev->dev, optee); - return ret; -} - -static int optee_remove(struct platform_device *pdev) -{ - struct optee *optee = platform_get_drvdata(pdev); - - tee_unregister(optee->teedev); - tee_unregister(optee->supp_teedev); - - return 0; -} - - -static const struct of_device_id optee_match[] = { - { .compatible = "tee-optee" }, - {}, -}; - -static struct platform_driver optee_driver = { - .driver = { - .name = DRIVER_NAME, - .of_match_table = optee_match, - }, - .probe = optee_probe, - .remove = optee_remove, -}; - -static int __init optee_init(void) -{ - pr_info("%s", __func__); - - return platform_driver_register(&optee_driver); -} - -static void __exit optee_exit(void) -{ - pr_info("%s", __func__); - platform_driver_unregister(&optee_driver); -} - -module_init(optee_init); -module_exit(optee_exit); - -MODULE_AUTHOR("Linaro"); -MODULE_DESCRIPTION("OP-TEE TEE driver"); -MODULE_SUPPORTED_DEVICE(""); -MODULE_VERSION("1.0"); -MODULE_LICENSE("GPL v2");
Updates the module of the specific driver reference counter when a shared memory object is added/removed and when device is opened/released.
Signed-off-by: Jens Wiklander jens.wiklander@linaro.org --- drivers/sec-hw/tee.c | 4 ++++ drivers/sec-hw/tee_shm.c | 7 +++++++ 2 files changed, 11 insertions(+)
diff --git a/drivers/sec-hw/tee.c b/drivers/sec-hw/tee.c index ce54be2..9af6e7d 100644 --- a/drivers/sec-hw/tee.c +++ b/drivers/sec-hw/tee.c @@ -26,6 +26,8 @@ static int tee_open(struct inode *inode, struct file *filp) struct tee_context *ctx;
teedev = container_of(filp->private_data, struct tee_device, miscdev); + if (!try_module_get(teedev->desc->owner)) + return -EINVAL; ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); if (!ctx) return -ENOMEM; @@ -41,11 +43,13 @@ static int tee_open(struct inode *inode, struct file *filp) static int tee_release(struct inode *inode, struct file *filp) { struct tee_context *ctx = filp->private_data; + struct tee_device *teedev = ctx->teedev;
/* Free all shm:s related to this ctx */ tee_shm_free_by_tee_context(ctx);
ctx->teedev->desc->ops->release(ctx); + module_put(teedev->desc->owner); return 0; }
diff --git a/drivers/sec-hw/tee_shm.c b/drivers/sec-hw/tee_shm.c index b8127c0..a3e5af0 100644 --- a/drivers/sec-hw/tee_shm.c +++ b/drivers/sec-hw/tee_shm.c @@ -96,6 +96,11 @@ struct tee_shm *tee_shm_alloc(struct tee_device *teedev, if (!shm) return ERR_PTR(-ENOMEM);
+ if (!try_module_get(teedev->desc->owner)) { + ret = ERR_PTR(-EINVAL); + goto err; + } + shm->flags = flags;
if (flags & TEE_SHM_DMA_BUF) { @@ -191,6 +196,8 @@ static void tee_shm_release(struct tee_shm *shm)
poolm->ops->free(poolm, shm); kfree(shm); + + module_put(teedev->desc->owner); }
void tee_shm_free_by_tee_context(struct tee_context *ctx)
Signed-off-by: Jens Wiklander jens.wiklander@linaro.org --- drivers/sec-hw/tee_shm.c | 23 ++++++++++++++++------- include/linux/sec-hw/tee_drv.h | 1 + 2 files changed, 17 insertions(+), 7 deletions(-)
diff --git a/drivers/sec-hw/tee_shm.c b/drivers/sec-hw/tee_shm.c index a3e5af0..cfb313b 100644 --- a/drivers/sec-hw/tee_shm.c +++ b/drivers/sec-hw/tee_shm.c @@ -117,6 +117,11 @@ struct tee_shm *tee_shm_alloc(struct tee_device *teedev, ret = ERR_PTR(-EINVAL); goto err; } + /* + * Assign the private teefilp to private shared memory + * objects to be able to find the teedev pointer from + * private shared memory object. + */ shm->teectx = &teedev->teectx_private; poolm = &teedev->pool->private_mgr; } @@ -127,6 +132,10 @@ struct tee_shm *tee_shm_alloc(struct tee_device *teedev, goto err; }
+ mutex_lock(&teeshm_mutex); + list_add_tail(&shm->list_node, &teedev->list_shm); + mutex_unlock(&teeshm_mutex); + if (flags & TEE_SHM_DMA_BUF) { shm->dmabuf = dma_buf_export(shm, &tee_shm_dma_buf_ops, shm->size, O_RDWR, NULL); @@ -137,25 +146,23 @@ struct tee_shm *tee_shm_alloc(struct tee_device *teedev, get_dma_buf(shm->dmabuf);
/* - * Only call share on global shm:s, as the driver private + * Only call share on dma_buf shm:s, as the driver private * shm:s always originates from the driver itself. */ rc = teedev->desc->ops->shm_share(shm); if (rc) { - ret = ERR_PTR(rc); - goto err; + dma_buf_put(shm->dmabuf); + return ERR_PTR(rc); } + shm->flags |= __TEE_SHM_SHARED; }
- mutex_lock(&teeshm_mutex); - list_add_tail(&shm->list_node, &teedev->list_shm); - mutex_unlock(&teeshm_mutex); - return shm; err: if (poolm && shm->kaddr) poolm->ops->free(poolm, shm); kfree(shm); + module_put(teedev->desc->owner); return ret; } EXPORT_SYMBOL_GPL(tee_shm_alloc); @@ -194,6 +201,8 @@ static void tee_shm_release(struct tee_shm *shm) else poolm = &teedev->pool->private_mgr;
+ if (shm->flags & __TEE_SHM_SHARED) + teedev->desc->ops->shm_unshare(shm); poolm->ops->free(poolm, shm); kfree(shm);
diff --git a/include/linux/sec-hw/tee_drv.h b/include/linux/sec-hw/tee_drv.h index c47b06b..4b149b2 100644 --- a/include/linux/sec-hw/tee_drv.h +++ b/include/linux/sec-hw/tee_drv.h @@ -27,6 +27,7 @@
#define TEE_SHM_MAPPED 0x1 /* Memory mapped by the kernel */ #define TEE_SHM_DMA_BUF 0x2 /* Memory with dma-buf handle */ +#define __TEE_SHM_SHARED 0x4 /* Has been shared with secure world */
#define TEE_UUID_SIZE 16
Hi Jens,,
The code that refers to the generic tee part looks good to me. The only comment I have for the op-tee code is regarding the “tee-optee” compatible tag, is that something that you intend to read from the device tree? Are you thinking of a tee node?
The rest looks good to me.
And btw, thanks for renaming tee_filp :)
Best, Javier
On 08 Apr 2015, at 13:52, Jens Wiklander jens.wiklander@linaro.org wrote:
Hi,
Here's my latest patches for the generic TEE subsystem. There's two patches with incompatible driver API changes which makes the OP-TEE driver fail to compile. I'm fixing that in "optee: incompatible tee subsystem api change" as a separate commit to make it easy to squash the different patches later when we send it to the kernel mailing list.
While this review is ongoing the patches are also available at https://github.com/jenswi-linaro/linux/tree/tee-dev3 I'll make a pull request of this when this review is done.
Jens Wiklander (7): tee: bugfix refcount tee: fix tee_shm_va2pa() and tee_shm_pa2va() tee: shared memory from CMA or res_mem tee: rename struct tee_filp optee: incompatible tee subsystem api change tee: update module reference counter tee: bugfix error handling of tee_shm_alloc()
drivers/sec-hw/Makefile | 3 +- drivers/sec-hw/optee/Kconfig | 3 +- drivers/sec-hw/optee/Makefile | 3 +- drivers/sec-hw/optee/core.c | 222 +++++++++++++++++++++++++++++++++++++++ drivers/sec-hw/optee/optee.c | 201 ----------------------------------- drivers/sec-hw/tee.c | 76 ++++++++------ drivers/sec-hw/tee_private.h | 51 ++++++--- drivers/sec-hw/tee_shm.c | 204 +++++++++++++++++------------------- drivers/sec-hw/tee_shm_pool.c | 232 +++++++++++++++++++++++++++++++++++++++++ include/linux/sec-hw/tee_drv.h | 75 ++++++++++--- 10 files changed, 697 insertions(+), 373 deletions(-) create mode 100644 drivers/sec-hw/optee/core.c delete mode 100644 drivers/sec-hw/optee/optee.c create mode 100644 drivers/sec-hw/tee_shm_pool.c
-- 1.9.1
Tee-dev mailing list Tee-dev@lists.linaro.org https://lists.linaro.org/mailman/listinfo/tee-dev