2017-09-28 18:04:29

by Volodymyr Babchuk

[permalink] [raw]
Subject: [PATCH v1 00/14] tee: optee: add dynamic shared memory support

From: Volodymyr Babchuk <[email protected]>

This patch series enables dynamic shared memory support in the TEE
subsystem as a whole and in OP-TEE in particular.

Global Platform TEE specification [1] allows client applications
to register part of own memory as a shared buffer between
application and TEE. This allows fast zero-copy communication between
TEE and REE. But current implementation of TEE in Linux does not support
this feature.

Also, current implementation of OP-TEE transport uses fixed size
pre-shared buffer for all communications with OP-TEE OS. This is okay
in the most use cases. But this prevents use of OP-TEE in virtualized
environments, because:
a) We can't share the same buffer between different virtual machines
b) Physically contiguous memory as seen by VM can be non-contiguous
in reality (and as seen by OP-TEE OS) due to second stage of
MMU translation.
c) Size of this pre-shared buffer is limited.

So, first part of this patch series adds generic register/unregister
interface to tee subsystem. Next patches add necessary features
into OP-TEE driver, so it can use not only static pre-shared buffer,
but whole RAM to communicate with OP-TEE OS.

[1] https://www.globalplatform.org/specificationsdevice.asp

Jens Wiklander (2):
tee: flexible shared memory pool creation
tee: add register user memory

Volodymyr Babchuk (12):
tee: shm: add accessors for buffer size and page offset
tee: shm: add page accessor functions
tee: optee: Update protocol definitions
tee: optee: add page list manipulation functions
tee: optee: add shared buffer registration functions
tee: optee: add registered shared parameters handling
tee: optee: add registered buffers handling into RPC calls
tee: optee: store OP-TEE capabilities in private data
tee: optee: add optee-specific shared pool implementation
tee: optee: enable dynamic SHM support
tee: use reference counting for tee_context
tee: shm: inline tee_shm getter functions

drivers/tee/optee/Makefile | 1 +
drivers/tee/optee/call.c | 131 +++++++++++++++++++++-
drivers/tee/optee/core.c | 160 +++++++++++++++++++++------
drivers/tee/optee/optee_msg.h | 38 ++++++-
drivers/tee/optee/optee_private.h | 26 ++++-
drivers/tee/optee/optee_smc.h | 7 ++
drivers/tee/optee/rpc.c | 72 ++++++++++--
drivers/tee/optee/shm_pool.c | 75 +++++++++++++
drivers/tee/optee/shm_pool.h | 23 ++++
drivers/tee/tee_core.c | 81 ++++++++++++--
drivers/tee/tee_private.h | 60 +---------
drivers/tee/tee_shm.c | 226 +++++++++++++++++++++++++++++++-------
drivers/tee/tee_shm_pool.c | 165 +++++++++++++++++-----------
include/linux/tee_drv.h | 184 ++++++++++++++++++++++++++++++-
include/uapi/linux/tee.h | 30 +++++
15 files changed, 1058 insertions(+), 221 deletions(-)
create mode 100644 drivers/tee/optee/shm_pool.c
create mode 100644 drivers/tee/optee/shm_pool.h

--
2.7.4


2017-09-28 18:04:45

by Volodymyr Babchuk

[permalink] [raw]
Subject: [PATCH v1 01/14] tee: flexible shared memory pool creation

From: Jens Wiklander <[email protected]>

Makes creation of shm pools more flexible by adding new more primitive
functions to allocate a shm pool. This makes it easier to add driver
specific shm pool management.

Signed-off-by: Jens Wiklander <[email protected]>
Signed-off-by: Volodymyr Babchuk <[email protected]>
---
drivers/tee/tee_private.h | 57 +---------------
drivers/tee/tee_shm.c | 8 +--
drivers/tee/tee_shm_pool.c | 165 ++++++++++++++++++++++++++++-----------------
include/linux/tee_drv.h | 91 +++++++++++++++++++++++++
4 files changed, 199 insertions(+), 122 deletions(-)

diff --git a/drivers/tee/tee_private.h b/drivers/tee/tee_private.h
index 21cb6be..2bc2b5a 100644
--- a/drivers/tee/tee_private.h
+++ b/drivers/tee/tee_private.h
@@ -21,68 +21,15 @@
#include <linux/mutex.h>
#include <linux/types.h>

-struct tee_device;
-
-/**
- * struct tee_shm - shared memory object
- * @teedev: device used to allocate the object
- * @ctx: context using the object, if NULL the context is gone
- * @link link element
- * @paddr: physical address of the shared memory
- * @kaddr: virtual address of the shared memory
- * @size: size of shared memory
- * @dmabuf: dmabuf used to for exporting to user space
- * @flags: defined by TEE_SHM_* in tee_drv.h
- * @id: unique id of a shared memory object on this device
- */
-struct tee_shm {
- struct tee_device *teedev;
- struct tee_context *ctx;
- struct list_head link;
- phys_addr_t paddr;
- void *kaddr;
- size_t size;
- struct dma_buf *dmabuf;
- u32 flags;
- int id;
-};
-
-struct tee_shm_pool_mgr;
-
-/**
- * struct tee_shm_pool_mgr_ops - shared memory pool manager operations
- * @alloc: called when allocating shared memory
- * @free: called when freeing shared memory
- */
-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 - shared memory manager
- * @ops: operations
- * @private_data: private data for the shared memory manager
- */
-struct tee_shm_pool_mgr {
- const struct tee_shm_pool_mgr_ops *ops;
- void *private_data;
-};
-
/**
* struct tee_shm_pool - shared memory pool
* @private_mgr: pool manager for shared memory only between kernel
* and secure world
* @dma_buf_mgr: pool manager for shared memory exported to user space
- * @destroy: called when destroying the pool
- * @private_data: private data for the pool
*/
struct tee_shm_pool {
- struct tee_shm_pool_mgr private_mgr;
- struct tee_shm_pool_mgr dma_buf_mgr;
- void (*destroy)(struct tee_shm_pool *pool);
- void *private_data;
+ struct tee_shm_pool_mgr *private_mgr;
+ struct tee_shm_pool_mgr *dma_buf_mgr;
};

#define TEE_DEVICE_FLAG_REGISTERED 0x1
diff --git a/drivers/tee/tee_shm.c b/drivers/tee/tee_shm.c
index 4bc7956..fdda89e 100644
--- a/drivers/tee/tee_shm.c
+++ b/drivers/tee/tee_shm.c
@@ -32,9 +32,9 @@ static void tee_shm_release(struct tee_shm *shm)
mutex_unlock(&teedev->mutex);

if (shm->flags & TEE_SHM_DMA_BUF)
- poolm = &teedev->pool->dma_buf_mgr;
+ poolm = teedev->pool->dma_buf_mgr;
else
- poolm = &teedev->pool->private_mgr;
+ poolm = teedev->pool->private_mgr;

poolm->ops->free(poolm, shm);
kfree(shm);
@@ -139,9 +139,9 @@ struct tee_shm *tee_shm_alloc(struct tee_context *ctx, size_t size, u32 flags)
shm->teedev = teedev;
shm->ctx = ctx;
if (flags & TEE_SHM_DMA_BUF)
- poolm = &teedev->pool->dma_buf_mgr;
+ poolm = teedev->pool->dma_buf_mgr;
else
- poolm = &teedev->pool->private_mgr;
+ poolm = teedev->pool->private_mgr;

rc = poolm->ops->alloc(poolm, shm, size);
if (rc) {
diff --git a/drivers/tee/tee_shm_pool.c b/drivers/tee/tee_shm_pool.c
index fb4f852..e6d4b9e 100644
--- a/drivers/tee/tee_shm_pool.c
+++ b/drivers/tee/tee_shm_pool.c
@@ -44,49 +44,18 @@ static void pool_op_gen_free(struct tee_shm_pool_mgr *poolm,
shm->kaddr = NULL;
}

+static void pool_op_gen_destroy_poolmgr(struct tee_shm_pool_mgr *poolm)
+{
+ gen_pool_destroy(poolm->private_data);
+ kfree(poolm);
+}
+
static const struct tee_shm_pool_mgr_ops pool_ops_generic = {
.alloc = pool_op_gen_alloc,
.free = pool_op_gen_free,
+ .destroy_poolmgr = pool_op_gen_destroy_poolmgr,
};

-static void pool_res_mem_destroy(struct tee_shm_pool *pool)
-{
- gen_pool_destroy(pool->private_mgr.private_data);
- gen_pool_destroy(pool->dma_buf_mgr.private_data);
-}
-
-static int pool_res_mem_mgr_init(struct tee_shm_pool_mgr *mgr,
- struct tee_shm_pool_mem_info *info,
- int min_alloc_order)
-{
- size_t page_mask = PAGE_SIZE - 1;
- struct gen_pool *genpool = NULL;
- int rc;
-
- /*
- * Start and end must be page aligned
- */
- if ((info->vaddr & page_mask) || (info->paddr & page_mask) ||
- (info->size & page_mask))
- return -EINVAL;
-
- genpool = gen_pool_create(min_alloc_order, -1);
- if (!genpool)
- return -ENOMEM;
-
- gen_pool_set_algo(genpool, gen_pool_best_fit, NULL);
- rc = gen_pool_add_virt(genpool, info->vaddr, info->paddr, info->size,
- -1);
- if (rc) {
- gen_pool_destroy(genpool);
- return rc;
- }
-
- mgr->private_data = genpool;
- mgr->ops = &pool_ops_generic;
- return 0;
-}
-
/**
* tee_shm_pool_alloc_res_mem() - Create a shared memory pool from reserved
* memory range
@@ -104,42 +73,109 @@ struct tee_shm_pool *
tee_shm_pool_alloc_res_mem(struct tee_shm_pool_mem_info *priv_info,
struct tee_shm_pool_mem_info *dmabuf_info)
{
- struct tee_shm_pool *pool = NULL;
- int ret;
-
- pool = kzalloc(sizeof(*pool), GFP_KERNEL);
- if (!pool) {
- ret = -ENOMEM;
- goto err;
- }
+ struct tee_shm_pool_mgr *priv_mgr;
+ struct tee_shm_pool_mgr *dmabuf_mgr;
+ void *rc;

/*
* Create the pool for driver private shared memory
*/
- ret = pool_res_mem_mgr_init(&pool->private_mgr, priv_info,
- 3 /* 8 byte aligned */);
- if (ret)
- goto err;
+ rc = tee_shm_pool_mgr_alloc_res_mem(priv_info->vaddr, priv_info->paddr,
+ priv_info->size,
+ 3 /* 8 byte aligned */);
+ if (IS_ERR(rc))
+ return rc;
+ priv_mgr = rc;

/*
* Create the pool for dma_buf shared memory
*/
- ret = pool_res_mem_mgr_init(&pool->dma_buf_mgr, dmabuf_info,
- PAGE_SHIFT);
- if (ret)
+ rc = tee_shm_pool_mgr_alloc_res_mem(dmabuf_info->vaddr,
+ dmabuf_info->paddr,
+ dmabuf_info->size, PAGE_SHIFT);
+ if (IS_ERR(rc))
+ goto err_free_priv_mgr;
+ dmabuf_mgr = rc;
+
+ rc = tee_shm_pool_alloc(priv_mgr, dmabuf_mgr);
+ if (IS_ERR(rc))
+ goto err_free_dmabuf_mgr;
+
+ return rc;
+
+err_free_dmabuf_mgr:
+ tee_shm_pool_mgr_destroy(dmabuf_mgr);
+err_free_priv_mgr:
+ tee_shm_pool_mgr_destroy(priv_mgr);
+
+ return rc;
+}
+EXPORT_SYMBOL_GPL(tee_shm_pool_alloc_res_mem);
+
+struct tee_shm_pool_mgr *tee_shm_pool_mgr_alloc_res_mem(unsigned long vaddr,
+ phys_addr_t paddr,
+ size_t size,
+ int min_alloc_order)
+{
+ const size_t page_mask = PAGE_SIZE - 1;
+ struct tee_shm_pool_mgr *mgr;
+ int rc;
+
+ /* Start and end must be page aligned */
+ if (vaddr & page_mask || paddr & page_mask || size & page_mask)
+ return ERR_PTR(-EINVAL);
+
+ mgr = kzalloc(sizeof(*mgr), GFP_KERNEL);
+ if (!mgr)
+ return ERR_PTR(-ENOMEM);
+
+ mgr->private_data = gen_pool_create(min_alloc_order, -1);
+ if (!mgr->private_data) {
+ rc = -ENOMEM;
goto err;
+ }

- pool->destroy = pool_res_mem_destroy;
- return pool;
+ gen_pool_set_algo(mgr->private_data, gen_pool_best_fit, NULL);
+ rc = gen_pool_add_virt(mgr->private_data, vaddr, paddr, size, -1);
+ if (rc) {
+ gen_pool_destroy(mgr->private_data);
+ goto err;
+ }
+
+ mgr->ops = &pool_ops_generic;
+
+ return mgr;
err:
- if (ret == -ENOMEM)
- pr_err("%s: can't allocate memory for res_mem shared memory pool\n", __func__);
- if (pool && pool->private_mgr.private_data)
- gen_pool_destroy(pool->private_mgr.private_data);
- kfree(pool);
- return ERR_PTR(ret);
+ kfree(mgr);
+
+ return ERR_PTR(rc);
}
-EXPORT_SYMBOL_GPL(tee_shm_pool_alloc_res_mem);
+EXPORT_SYMBOL_GPL(tee_shm_pool_mgr_alloc_res_mem);
+
+static bool check_mgr_ops(struct tee_shm_pool_mgr *mgr)
+{
+ return mgr && mgr->ops && mgr->ops->alloc && mgr->ops->free &&
+ mgr->ops->destroy_poolmgr;
+}
+
+struct tee_shm_pool *tee_shm_pool_alloc(struct tee_shm_pool_mgr *priv_mgr,
+ struct tee_shm_pool_mgr *dmabuf_mgr)
+{
+ struct tee_shm_pool *pool;
+
+ if (!check_mgr_ops(priv_mgr) || !check_mgr_ops(dmabuf_mgr))
+ return ERR_PTR(-EINVAL);
+
+ pool = kzalloc(sizeof(*pool), GFP_KERNEL);
+ if (!pool)
+ return ERR_PTR(-ENOMEM);
+
+ pool->private_mgr = priv_mgr;
+ pool->dma_buf_mgr = dmabuf_mgr;
+
+ return pool;
+}
+EXPORT_SYMBOL_GPL(tee_shm_pool_alloc);

/**
* tee_shm_pool_free() - Free a shared memory pool
@@ -150,7 +186,10 @@ EXPORT_SYMBOL_GPL(tee_shm_pool_alloc_res_mem);
*/
void tee_shm_pool_free(struct tee_shm_pool *pool)
{
- pool->destroy(pool);
+ if (pool->private_mgr)
+ tee_shm_pool_mgr_destroy(pool->private_mgr);
+ if (pool->dma_buf_mgr)
+ tee_shm_pool_mgr_destroy(pool->dma_buf_mgr);
kfree(pool);
}
EXPORT_SYMBOL_GPL(tee_shm_pool_free);
diff --git a/include/linux/tee_drv.h b/include/linux/tee_drv.h
index cb889af..e9be4a4 100644
--- a/include/linux/tee_drv.h
+++ b/include/linux/tee_drv.h
@@ -150,6 +150,97 @@ int tee_device_register(struct tee_device *teedev);
void tee_device_unregister(struct tee_device *teedev);

/**
+ * struct tee_shm - shared memory object
+ * @teedev: device used to allocate the object
+ * @ctx: context using the object, if NULL the context is gone
+ * @link link element
+ * @paddr: physical address of the shared memory
+ * @kaddr: virtual address of the shared memory
+ * @size: size of shared memory
+ * @offset: offset of buffer in user space
+ * @pages: locked pages from userspace
+ * @num_pages: number of locked pages
+ * @dmabuf: dmabuf used to for exporting to user space
+ * @flags: defined by TEE_SHM_* in tee_drv.h
+ * @id: unique id of a shared memory object on this device
+ *
+ * This pool is only supposed to be accessed directly from the TEE
+ * subsystem and from drivers that implements their own shm pool manager.
+ */
+struct tee_shm {
+ struct tee_device *teedev;
+ struct tee_context *ctx;
+ struct list_head link;
+ phys_addr_t paddr;
+ void *kaddr;
+ size_t size;
+ unsigned int offset;
+ struct page **pages;
+ size_t num_pages;
+ struct dma_buf *dmabuf;
+ u32 flags;
+ int id;
+};
+
+/**
+ * struct tee_shm_pool_mgr - shared memory manager
+ * @ops: operations
+ * @private_data: private data for the shared memory manager
+ */
+struct tee_shm_pool_mgr {
+ const struct tee_shm_pool_mgr_ops *ops;
+ void *private_data;
+};
+
+/**
+ * struct tee_shm_pool_mgr_ops - shared memory pool manager operations
+ * @alloc: called when allocating shared memory
+ * @free: called when freeing shared memory
+ * @destroy_poolmgr: called when destroying the pool manager
+ */
+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);
+ void (*destroy_poolmgr)(struct tee_shm_pool_mgr *poolmgr);
+};
+
+/**
+ * tee_shm_pool_alloc() - Create a shared memory pool from shm managers
+ * @priv_mgr: manager for driver private shared memory allocations
+ * @dmabuf_mgr: manager for dma-buf shared memory allocations
+ *
+ * Allocation with the flag TEE_SHM_DMA_BUF set will use the range supplied
+ * in @dmabuf, others will use the range provided by @priv.
+ *
+ * @returns pointer to a 'struct tee_shm_pool' or an ERR_PTR on failure.
+ */
+struct tee_shm_pool *tee_shm_pool_alloc(struct tee_shm_pool_mgr *priv_mgr,
+ struct tee_shm_pool_mgr *dmabuf_mgr);
+
+/*
+ * tee_shm_pool_mgr_alloc_res_mem() - Create a shm manager for reserved
+ * memory
+ * @vaddr: Virtual address of start of pool
+ * @paddr: Physical address of start of pool
+ * @size: Size in bytes of the pool
+ *
+ * @returns pointer to a 'struct tee_shm_pool_mgr' or an ERR_PTR on failure.
+ */
+struct tee_shm_pool_mgr *tee_shm_pool_mgr_alloc_res_mem(unsigned long vaddr,
+ phys_addr_t paddr,
+ size_t size,
+ int min_alloc_order);
+
+/**
+ * tee_shm_pool_mgr_destroy() - Free a shared memory manager
+ */
+static inline void tee_shm_pool_mgr_destroy(struct tee_shm_pool_mgr *poolm)
+{
+ poolm->ops->destroy_poolmgr(poolm);
+}
+
+/**
* struct tee_shm_pool_mem_info - holds information needed to create a shared
* memory pool
* @vaddr: Virtual address of start of pool
--
2.7.4

2017-09-28 18:04:54

by Volodymyr Babchuk

[permalink] [raw]
Subject: [PATCH v1 02/14] tee: add register user memory

From: Jens Wiklander <[email protected]>

Added new ioctl to allow users register own buffers as a shared memory.

Signed-off-by: Jens Wiklander <[email protected]>
Signed-off-by: Volodymyr Babchuk <[email protected]>
---
drivers/tee/tee_core.c | 41 ++++++++-
drivers/tee/tee_shm.c | 210 +++++++++++++++++++++++++++++++++++++++++------
include/linux/tee_drv.h | 44 +++++++++-
include/uapi/linux/tee.h | 30 +++++++
4 files changed, 295 insertions(+), 30 deletions(-)

diff --git a/drivers/tee/tee_core.c b/drivers/tee/tee_core.c
index 58a5009..295910f 100644
--- a/drivers/tee/tee_core.c
+++ b/drivers/tee/tee_core.c
@@ -114,8 +114,6 @@ static int tee_ioctl_shm_alloc(struct tee_context *ctx,
if (data.flags)
return -EINVAL;

- data.id = -1;
-
shm = tee_shm_alloc(ctx, data.size, TEE_SHM_MAPPED | TEE_SHM_DMA_BUF);
if (IS_ERR(shm))
return PTR_ERR(shm);
@@ -138,6 +136,43 @@ static int tee_ioctl_shm_alloc(struct tee_context *ctx,
return ret;
}

+static int
+tee_ioctl_shm_register(struct tee_context *ctx,
+ struct tee_ioctl_shm_register_data __user *udata)
+{
+ long ret;
+ struct tee_ioctl_shm_register_data data;
+ struct tee_shm *shm;
+
+ if (copy_from_user(&data, udata, sizeof(data)))
+ return -EFAULT;
+
+ /* Currently no input flags are supported */
+ if (data.flags)
+ return -EINVAL;
+
+ shm = tee_shm_register(ctx, data.addr, data.length,
+ TEE_SHM_DMA_BUF | TEE_SHM_USER_MAPPED);
+ if (IS_ERR(shm))
+ return PTR_ERR(shm);
+
+ data.id = shm->id;
+ data.flags = shm->flags;
+ data.length = shm->size;
+
+ if (copy_to_user(udata, &data, sizeof(data)))
+ ret = -EFAULT;
+ else
+ ret = tee_shm_get_fd(shm);
+ /*
+ * When user space closes the file descriptor the shared memory
+ * should be freed or if tee_shm_get_fd() failed then it will
+ * be freed immediately.
+ */
+ tee_shm_put(shm);
+ return ret;
+}
+
static int params_from_user(struct tee_context *ctx, struct tee_param *params,
size_t num_params,
struct tee_ioctl_param __user *uparams)
@@ -586,6 +621,8 @@ static long tee_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
return tee_ioctl_version(ctx, uarg);
case TEE_IOC_SHM_ALLOC:
return tee_ioctl_shm_alloc(ctx, uarg);
+ case TEE_IOC_SHM_REGISTER:
+ return tee_ioctl_shm_register(ctx, uarg);
case TEE_IOC_OPEN_SESSION:
return tee_ioctl_open_session(ctx, uarg);
case TEE_IOC_INVOKE:
diff --git a/drivers/tee/tee_shm.c b/drivers/tee/tee_shm.c
index fdda89e..1ed708c 100644
--- a/drivers/tee/tee_shm.c
+++ b/drivers/tee/tee_shm.c
@@ -23,7 +23,6 @@
static void tee_shm_release(struct tee_shm *shm)
{
struct tee_device *teedev = shm->teedev;
- struct tee_shm_pool_mgr *poolm;

mutex_lock(&teedev->mutex);
idr_remove(&teedev->idr, shm->id);
@@ -31,12 +30,29 @@ static void tee_shm_release(struct tee_shm *shm)
list_del(&shm->link);
mutex_unlock(&teedev->mutex);

- if (shm->flags & TEE_SHM_DMA_BUF)
- poolm = teedev->pool->dma_buf_mgr;
- else
- poolm = teedev->pool->private_mgr;
+ if (shm->flags & TEE_SHM_POOL) {
+ struct tee_shm_pool_mgr *poolm;
+
+ if (shm->flags & TEE_SHM_DMA_BUF)
+ poolm = teedev->pool->dma_buf_mgr;
+ else
+ poolm = teedev->pool->private_mgr;
+
+ poolm->ops->free(poolm, shm);
+ } else if (shm->flags & TEE_SHM_REGISTER) {
+ size_t n;
+ int rc = teedev->desc->ops->shm_unregister(shm->ctx, shm);
+
+ if (rc)
+ dev_err(teedev->dev.parent,
+ "unregister shm %p failed: %d", shm, rc);
+
+ for (n = 0; n < shm->num_pages; n++)
+ put_page(shm->pages[n]);
+
+ kfree(shm->pages);
+ }

- poolm->ops->free(poolm, shm);
kfree(shm);

tee_device_put(teedev);
@@ -76,6 +92,10 @@ static int tee_shm_op_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;

+ /* Refuse sharing shared memory provided by application */
+ if (shm->flags & TEE_SHM_REGISTER)
+ return -EINVAL;
+
return remap_pfn_range(vma, vma->vm_start, shm->paddr >> PAGE_SHIFT,
size, vma->vm_page_prot);
}
@@ -89,26 +109,20 @@ static const struct dma_buf_ops tee_shm_dma_buf_ops = {
.mmap = tee_shm_op_mmap,
};

-/**
- * tee_shm_alloc() - Allocate shared memory
- * @ctx: Context that allocates the shared memory
- * @size: Requested size of shared memory
- * @flags: Flags setting properties for the requested shared memory.
- *
- * Memory allocated as global shared memory is automatically freed when the
- * TEE file pointer is closed. The @flags field uses the bits defined by
- * TEE_SHM_* in <linux/tee_drv.h>. TEE_SHM_MAPPED must currently always be
- * set. If TEE_SHM_DMA_BUF global shared memory will be allocated and
- * associated with a dma-buf handle, else driver private memory.
- */
-struct tee_shm *tee_shm_alloc(struct tee_context *ctx, size_t size, u32 flags)
+struct tee_shm *__tee_shm_alloc(struct tee_context *ctx,
+ struct tee_device *teedev,
+ size_t size, u32 flags)
{
- struct tee_device *teedev = ctx->teedev;
struct tee_shm_pool_mgr *poolm = NULL;
struct tee_shm *shm;
void *ret;
int rc;

+ if (ctx && ctx->teedev != teedev) {
+ dev_err(teedev->dev.parent, "ctx and teedev mismatch\n");
+ return ERR_PTR(-EINVAL);
+ }
+
if (!(flags & TEE_SHM_MAPPED)) {
dev_err(teedev->dev.parent,
"only mapped allocations supported\n");
@@ -135,7 +149,7 @@ struct tee_shm *tee_shm_alloc(struct tee_context *ctx, size_t size, u32 flags)
goto err_dev_put;
}

- shm->flags = flags;
+ shm->flags = flags | TEE_SHM_POOL;
shm->teedev = teedev;
shm->ctx = ctx;
if (flags & TEE_SHM_DMA_BUF)
@@ -171,9 +185,12 @@ struct tee_shm *tee_shm_alloc(struct tee_context *ctx, size_t size, u32 flags)
goto err_rem;
}
}
- mutex_lock(&teedev->mutex);
- list_add_tail(&shm->link, &ctx->list_shm);
- mutex_unlock(&teedev->mutex);
+
+ if (ctx) {
+ mutex_lock(&teedev->mutex);
+ list_add_tail(&shm->link, &ctx->list_shm);
+ mutex_unlock(&teedev->mutex);
+ }

return shm;
err_rem:
@@ -188,8 +205,138 @@ struct tee_shm *tee_shm_alloc(struct tee_context *ctx, size_t size, u32 flags)
tee_device_put(teedev);
return ret;
}
+/**
+ * tee_shm_alloc() - Allocate shared memory
+ * @ctx: Context that allocates the shared memory
+ * @size: Requested size of shared memory
+ * @flags: Flags setting properties for the requested shared memory.
+ *
+ * Memory allocated as global shared memory is automatically freed when the
+ * TEE file pointer is closed. The @flags field uses the bits defined by
+ * TEE_SHM_* in <linux/tee_drv.h>. TEE_SHM_MAPPED must currently always be
+ * set. If TEE_SHM_DMA_BUF global shared memory will be allocated and
+ * associated with a dma-buf handle, else driver private memory.
+ */
+struct tee_shm *tee_shm_alloc(struct tee_context *ctx, size_t size, u32 flags)
+{
+ return __tee_shm_alloc(ctx, ctx->teedev, size, flags);
+}
EXPORT_SYMBOL_GPL(tee_shm_alloc);

+struct tee_shm *tee_shm_priv_alloc(struct tee_device *teedev, size_t size)
+{
+ return __tee_shm_alloc(NULL, teedev, size, TEE_SHM_MAPPED);
+}
+EXPORT_SYMBOL_GPL(tee_shm_priv_alloc);
+
+struct tee_shm *tee_shm_register(struct tee_context *ctx, unsigned long addr,
+ size_t length, u32 flags)
+{
+ struct tee_device *teedev = ctx->teedev;
+ const u32 req_flags = TEE_SHM_DMA_BUF | TEE_SHM_USER_MAPPED;
+ struct tee_shm *shm;
+ void *ret;
+ int rc;
+ int num_pages;
+ unsigned long start;
+
+ if (flags != req_flags) {
+ dev_err(teedev->dev.parent, "invliad shm flags %#x", flags);
+ return ERR_PTR(-EINVAL);
+ }
+
+ if (!tee_device_get(teedev))
+ return ERR_PTR(-EINVAL);
+
+ if (!teedev->desc->ops->shm_register ||
+ !teedev->desc->ops->shm_unregister) {
+ dev_err(teedev->dev.parent,
+ "register shared memory unspported by device");
+ tee_device_put(teedev);
+ return ERR_PTR(-EINVAL);
+ }
+
+ shm = kzalloc(sizeof(*shm), GFP_KERNEL);
+ if (!shm) {
+ ret = ERR_PTR(-ENOMEM);
+ goto err;
+ }
+
+ shm->flags = flags | TEE_SHM_REGISTER;
+ shm->teedev = teedev;
+ shm->ctx = ctx;
+ shm->id = -1;
+ start = rounddown(addr, PAGE_SIZE);
+ shm->offset = addr - start;
+ shm->size = length;
+ num_pages = (roundup(addr + length, PAGE_SIZE) - start) / PAGE_SIZE;
+ shm->pages = kcalloc(num_pages, sizeof(struct page), GFP_KERNEL);
+ if (!shm->pages) {
+ ret = ERR_PTR(-ENOMEM);
+ goto err;
+ }
+
+ rc = get_user_pages_fast(start, num_pages, 1, shm->pages);
+ if (rc > 0)
+ shm->num_pages = rc;
+ if (rc != num_pages) {
+ if (rc > 0)
+ rc = -ENOMEM;
+ ret = ERR_PTR(rc);
+ goto err;
+ }
+
+ mutex_lock(&teedev->mutex);
+ shm->id = idr_alloc(&teedev->idr, shm, 1, 0, GFP_KERNEL);
+ mutex_unlock(&teedev->mutex);
+
+ rc = teedev->desc->ops->shm_register(ctx, shm, shm->pages,
+ shm->num_pages);
+ if (rc) {
+ ret = ERR_PTR(rc);
+ goto err;
+ }
+
+ if (flags & TEE_SHM_DMA_BUF) {
+ DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
+
+ exp_info.ops = &tee_shm_dma_buf_ops;
+ exp_info.size = shm->size;
+ exp_info.flags = O_RDWR;
+ exp_info.priv = shm;
+
+ shm->dmabuf = dma_buf_export(&exp_info);
+ if (IS_ERR(shm->dmabuf)) {
+ ret = ERR_CAST(shm->dmabuf);
+ teedev->desc->ops->shm_unregister(ctx, shm);
+ goto err;
+ }
+ }
+
+ mutex_lock(&teedev->mutex);
+ list_add_tail(&shm->link, &ctx->list_shm);
+ mutex_unlock(&teedev->mutex);
+
+ return shm;
+err:
+ if (shm) {
+ size_t n;
+
+ if (shm->id >= 0) {
+ mutex_lock(&teedev->mutex);
+ idr_remove(&teedev->idr, shm->id);
+ mutex_unlock(&teedev->mutex);
+ }
+ for (n = 0; n < shm->num_pages; n++)
+ put_page(shm->pages[n]);
+ kfree(shm->pages);
+ }
+ kfree(shm);
+ tee_device_put(teedev);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(tee_shm_register);
+
/**
* tee_shm_get_fd() - Increase reference count and return file descriptor
* @shm: Shared memory handle
@@ -197,10 +344,9 @@ EXPORT_SYMBOL_GPL(tee_shm_alloc);
*/
int tee_shm_get_fd(struct tee_shm *shm)
{
- u32 req_flags = TEE_SHM_MAPPED | TEE_SHM_DMA_BUF;
int fd;

- if ((shm->flags & req_flags) != req_flags)
+ if (!(shm->flags & TEE_SHM_DMA_BUF))
return -EINVAL;

fd = dma_buf_fd(shm->dmabuf, O_CLOEXEC);
@@ -238,6 +384,8 @@ EXPORT_SYMBOL_GPL(tee_shm_free);
*/
int tee_shm_va2pa(struct tee_shm *shm, void *va, phys_addr_t *pa)
{
+ if (!(shm->flags & TEE_SHM_MAPPED))
+ return -EINVAL;
/* Check that we're in the range of the shm */
if ((char *)va < (char *)shm->kaddr)
return -EINVAL;
@@ -258,6 +406,8 @@ EXPORT_SYMBOL_GPL(tee_shm_va2pa);
*/
int tee_shm_pa2va(struct tee_shm *shm, phys_addr_t pa, void **va)
{
+ if (!(shm->flags & TEE_SHM_MAPPED))
+ return -EINVAL;
/* Check that we're in the range of the shm */
if (pa < shm->paddr)
return -EINVAL;
@@ -284,6 +434,8 @@ EXPORT_SYMBOL_GPL(tee_shm_pa2va);
*/
void *tee_shm_get_va(struct tee_shm *shm, size_t offs)
{
+ if (!(shm->flags & TEE_SHM_MAPPED))
+ return ERR_PTR(-EINVAL);
if (offs >= shm->size)
return ERR_PTR(-EINVAL);
return (char *)shm->kaddr + offs;
@@ -335,6 +487,12 @@ struct tee_shm *tee_shm_get_from_id(struct tee_context *ctx, int id)
}
EXPORT_SYMBOL_GPL(tee_shm_get_from_id);

+bool tee_shm_is_registered(struct tee_shm *shm)
+{
+ return shm && (shm->flags & TEE_SHM_REGISTER);
+}
+EXPORT_SYMBOL_GPL(tee_shm_is_registered);
+
/**
* tee_shm_get_id() - Get id of a shared memory object
* @shm: Shared memory handle
diff --git a/include/linux/tee_drv.h b/include/linux/tee_drv.h
index e9be4a4..49d6361 100644
--- a/include/linux/tee_drv.h
+++ b/include/linux/tee_drv.h
@@ -25,8 +25,12 @@
* specific TEE driver.
*/

-#define TEE_SHM_MAPPED 0x1 /* Memory mapped by the kernel */
-#define TEE_SHM_DMA_BUF 0x2 /* Memory with dma-buf handle */
+#define TEE_SHM_MAPPED BIT(0) /* Memory mapped by the kernel */
+#define TEE_SHM_DMA_BUF BIT(1) /* Memory with dma-buf handle */
+#define TEE_SHM_EXT_DMA_BUF BIT(2) /* Memory with dma-buf handle */
+#define TEE_SHM_REGISTER BIT(3) /* Memory registered in secure world */
+#define TEE_SHM_USER_MAPPED BIT(4) /* Memory mapped in user space */
+#define TEE_SHM_POOL BIT(5) /* Memory allocated from pool */

struct device;
struct tee_device;
@@ -76,6 +80,8 @@ struct tee_param {
* @cancel_req: request cancel of an ongoing invoke or open
* @supp_revc: called for supplicant to get a command
* @supp_send: called for supplicant to send a response
+ * @shm_register: register shared memory buffer in TEE
+ * @shm_unregister: unregister shared memory buffer in TEE
*/
struct tee_driver_ops {
void (*get_version)(struct tee_device *teedev,
@@ -94,6 +100,9 @@ struct tee_driver_ops {
struct tee_param *param);
int (*supp_send)(struct tee_context *ctx, u32 ret, u32 num_params,
struct tee_param *param);
+ int (*shm_register)(struct tee_context *ctx, struct tee_shm *shm,
+ struct page **pages, size_t num_pages);
+ int (*shm_unregister)(struct tee_context *ctx, struct tee_shm *shm);
};

/**
@@ -302,6 +311,30 @@ void *tee_get_drvdata(struct tee_device *teedev);
struct tee_shm *tee_shm_alloc(struct tee_context *ctx, size_t size, u32 flags);

/**
+ * tee_shm_priv_alloc() - Allocate shared memory privately
+ * @dev: Device that allocates the shared memory
+ * @size: Requested size of shared memory
+ *
+ * Allocates shared memory buffer that is not associated with any client
+ * context. Such buffers are owned by TEE driver and used for internal calls.
+ *
+ * @returns a pointer to 'struct tee_shm'
+ */
+struct tee_shm *tee_shm_priv_alloc(struct tee_device *teedev, size_t size);
+
+/**
+ * tee_shm_register() - Register shared memory buffer
+ * @ctx: Context that registers the shared memory
+ * @addr: Address is userspace of the shared buffer
+ * @length: Length of the shared buffer
+ * @flags: Flags setting properties for the requested shared memory.
+ *
+ * @returns a pointer to 'struct tee_shm'
+ */
+struct tee_shm *tee_shm_register(struct tee_context *ctx, unsigned long addr,
+ size_t length, u32 flags);
+
+/**
* tee_shm_free() - Free shared memory
* @shm: Handle to shared memory to free
*/
@@ -366,4 +399,11 @@ int tee_shm_get_id(struct tee_shm *shm);
*/
struct tee_shm *tee_shm_get_from_id(struct tee_context *ctx, int id);

+/**
+ * tee_shm_is_registered() - Check if shared memory object in registered in TEE
+ * @shm: Shared memory handle
+ * @returns true if object is registered in TEE
+ */
+bool tee_shm_is_registered(struct tee_shm *shm);
+
#endif /*__TEE_DRV_H*/
diff --git a/include/uapi/linux/tee.h b/include/uapi/linux/tee.h
index 688782e..d41a07a 100644
--- a/include/uapi/linux/tee.h
+++ b/include/uapi/linux/tee.h
@@ -50,6 +50,7 @@

#define TEE_GEN_CAP_GP (1 << 0)/* GlobalPlatform compliant TEE */
#define TEE_GEN_CAP_PRIVILEGED (1 << 1)/* Privileged device (for supplicant) */
+#define TEE_GEN_CAP_REG_MEM (1 << 2)/* Supports registering shared memory */

/*
* TEE Implementation ID
@@ -332,6 +333,35 @@ struct tee_iocl_supp_send_arg {
#define TEE_IOC_SUPPL_SEND _IOR(TEE_IOC_MAGIC, TEE_IOC_BASE + 7, \
struct tee_ioctl_buf_data)

+/**
+ * struct tee_ioctl_shm_register_data - Shared memory register argument
+ * @addr: [in] Start address of shared memory to register
+ * @length: [in/out] Length of shared memory to register
+ * @flags: [in/out] Flags to/from registration.
+ * @id: [out] Identifier of the shared memory
+ *
+ * The flags field should currently be zero as input. Updated by the call
+ * with actual flags as defined by TEE_IOCTL_SHM_* above.
+ * This structure is used as argument for TEE_IOC_SHM_REGISTER below.
+ */
+struct tee_ioctl_shm_register_data {
+ __u64 addr;
+ __u64 length;
+ __u32 flags;
+ __s32 id;
+};
+
+/**
+ * TEE_IOC_SHM_REGISTER - Register shared memory argument
+ *
+ * Registers shared memory between the user space process and secure OS.
+ *
+ * Returns a file descriptor on success or < 0 on failure
+ *
+ * The shared memory is unregisterred when the descriptor is closed.
+ */
+#define TEE_IOC_SHM_REGISTER _IOWR(TEE_IOC_MAGIC, TEE_IOC_BASE + 9, \
+ struct tee_ioctl_shm_register_data)
/*
* Five syscalls are used when communicating with the TEE driver.
* open(): opens the device associated with the driver
--
2.7.4

2017-09-28 18:05:02

by Volodymyr Babchuk

[permalink] [raw]
Subject: [PATCH v1 03/14] tee: shm: add accessors for buffer size and page offset

From: Volodymyr Babchuk <[email protected]>

These two function will be needed for shared memory registration in OP-TEE

Signed-off-by: Volodymyr Babchuk <[email protected]>
---
include/linux/tee_drv.h | 20 ++++++++++++++++++++
1 file changed, 20 insertions(+)

diff --git a/include/linux/tee_drv.h b/include/linux/tee_drv.h
index 49d6361..6ba8b76 100644
--- a/include/linux/tee_drv.h
+++ b/include/linux/tee_drv.h
@@ -384,6 +384,26 @@ void *tee_shm_get_va(struct tee_shm *shm, size_t offs);
int tee_shm_get_pa(struct tee_shm *shm, size_t offs, phys_addr_t *pa);

/**
+ * tee_shm_get_size() - Get size of shared memory buffer
+ * @shm: Shared memory handle
+ * @returns size of shared memory
+ */
+static inline size_t tee_shm_get_size(struct tee_shm *shm)
+{
+ return shm->size;
+}
+
+/**
+ * tee_shm_get_page_offset() - Get shared buffer offset from page start
+ * @shm: Shared memory handle
+ * @returns page offset of shared buffer
+ */
+static inline size_t tee_shm_get_page_offset(struct tee_shm *shm)
+{
+ return shm->offset;
+}
+
+/**
* tee_shm_get_id() - Get id of a shared memory object
* @shm: Shared memory handle
* @returns id
--
2.7.4

2017-09-28 18:05:07

by Volodymyr Babchuk

[permalink] [raw]
Subject: [PATCH v1 04/14] tee: shm: add page accessor functions

From: Volodymyr Babchuk <[email protected]>

In order to register a shared buffer in TEE, we need accessor
function that return list of pages for that buffer.

Signed-off-by: Volodymyr Babchuk <[email protected]>
---
include/linux/tee_drv.h | 14 ++++++++++++++
1 file changed, 14 insertions(+)

diff --git a/include/linux/tee_drv.h b/include/linux/tee_drv.h
index 6ba8b76..d773827 100644
--- a/include/linux/tee_drv.h
+++ b/include/linux/tee_drv.h
@@ -394,6 +394,20 @@ static inline size_t tee_shm_get_size(struct tee_shm *shm)
}

/**
+ * tee_shm_get_pages() - Get list of pages that hold shared buffer
+ * @shm: Shared memory handle
+ * @num_pages: Number of pages will be stored there
+ * @returns pointer to pages array
+ */
+static inline struct page **tee_shm_get_pages(struct tee_shm *shm,
+ size_t *num_pages)
+{
+ if (num_pages)
+ *num_pages = shm->num_pages;
+ return shm->pages;
+}
+
+/**
* tee_shm_get_page_offset() - Get shared buffer offset from page start
* @shm: Shared memory handle
* @returns page offset of shared buffer
--
2.7.4

2017-09-28 18:05:16

by Volodymyr Babchuk

[permalink] [raw]
Subject: [PATCH v1 05/14] tee: optee: Update protocol definitions

From: Volodymyr Babchuk <[email protected]>

There were changes in REE<->OP-TEE ABI recently.
Now ABI allows us to pass non-contiguous memory buffers as list of
pages to OP-TEE. This can be achieved by using new parameter attribute
OPTEE_MSG_ATTR_NONCONTIG.

OP-TEE also is able to use all non-secure RAM for shared buffers. This
new capability is enabled with OPTEE_SMC_SEC_CAP_DYNAMIC_SHM flag.

This patch adds necessary definitions to the protocol definition files at
Linux side.

Signed-off-by: Volodymyr Babchuk <[email protected]>
---
drivers/tee/optee/optee_msg.h | 38 ++++++++++++++++++++++++++++++++------
drivers/tee/optee/optee_smc.h | 7 +++++++
2 files changed, 39 insertions(+), 6 deletions(-)

diff --git a/drivers/tee/optee/optee_msg.h b/drivers/tee/optee/optee_msg.h
index dd7a06e..3050490 100644
--- a/drivers/tee/optee/optee_msg.h
+++ b/drivers/tee/optee/optee_msg.h
@@ -67,11 +67,32 @@
#define OPTEE_MSG_ATTR_META BIT(8)

/*
- * The temporary shared memory object is not physically contigous and this
- * temp memref is followed by another fragment until the last temp memref
- * that doesn't have this bit set.
+ * Pointer to a list of pages used to register user-defined SHM buffer.
+ * Used with OPTEE_MSG_ATTR_TYPE_TMEM_*.
+ * buf_ptr should point to the beginning of the buffer. Buffer will contain
+ * list of page addresses. OP-TEE core can reconstruct contiguous buffer from
+ * that page addresses list. Page addresses are stored as 64 bit values.
+ * Last entry on a page should point to the next page of buffer.
+ * Every entry in buffer should point to a 4k page beginning (12 least
+ * significant bits must be equal to zero).
+ *
+ * 12 least significant bints of optee_msg_param.u.tmem.buf_ptr should hold page
+ * offset of the user buffer.
+ *
+ * So, entries should be placed like members of this structure:
+ *
+ * struct page_data {
+ * uint64_t pages_array[OPTEE_MSG_NONCONTIG_PAGE_SIZE/sizeof(uint64_t) - 1];
+ * uint64_t next_page_data;
+ * };
+ *
+ * Structure is designed to exactly fit into the page size
+ * OPTEE_MSG_NONCONTIG_PAGE_SIZE which is a standard 4KB page.
+ *
+ * The size of 4KB is chosen because this is the smallest page size for ARM
+ * architectures. If REE uses larger pages, it should divide them to 4KB ones.
*/
-#define OPTEE_MSG_ATTR_FRAGMENT BIT(9)
+#define OPTEE_MSG_ATTR_NONCONTIG BIT(9)

/*
* Memory attributes for caching passed with temp memrefs. The actual value
@@ -94,6 +115,11 @@
#define OPTEE_MSG_LOGIN_APPLICATION_USER 0x00000005
#define OPTEE_MSG_LOGIN_APPLICATION_GROUP 0x00000006

+/*
+ * Page size used in non-contiguous buffer entries
+ */
+#define OPTEE_MSG_NONCONTIG_PAGE_SIZE 4096
+
/**
* struct optee_msg_param_tmem - temporary memory reference parameter
* @buf_ptr: Address of the buffer
@@ -145,8 +171,8 @@ struct optee_msg_param_value {
*
* @attr & OPTEE_MSG_ATTR_TYPE_MASK indicates if tmem, rmem or value is used in
* the union. OPTEE_MSG_ATTR_TYPE_VALUE_* indicates value,
- * OPTEE_MSG_ATTR_TYPE_TMEM_* indicates tmem and
- * OPTEE_MSG_ATTR_TYPE_RMEM_* indicates rmem.
+ * OPTEE_MSG_ATTR_TYPE_TMEM_* indicates @tmem and
+ * OPTEE_MSG_ATTR_TYPE_RMEM_* indicates @rmem,
* OPTEE_MSG_ATTR_TYPE_NONE indicates that none of the members are used.
*/
struct optee_msg_param {
diff --git a/drivers/tee/optee/optee_smc.h b/drivers/tee/optee/optee_smc.h
index 069c8e1..7cd3272 100644
--- a/drivers/tee/optee/optee_smc.h
+++ b/drivers/tee/optee/optee_smc.h
@@ -222,6 +222,13 @@ struct optee_smc_get_shm_config_result {
#define OPTEE_SMC_SEC_CAP_HAVE_RESERVED_SHM BIT(0)
/* Secure world can communicate via previously unregistered shared memory */
#define OPTEE_SMC_SEC_CAP_UNREGISTERED_SHM BIT(1)
+
+/*
+ * Secure world supports commands "register/unregister shared memory",
+ * secure world accepts command buffers located in any parts of non-secure RAM
+ */
+#define OPTEE_SMC_SEC_CAP_DYNAMIC_SHM BIT(2)
+
#define OPTEE_SMC_FUNCID_EXCHANGE_CAPABILITIES 9
#define OPTEE_SMC_EXCHANGE_CAPABILITIES \
OPTEE_SMC_FAST_CALL_VAL(OPTEE_SMC_FUNCID_EXCHANGE_CAPABILITIES)
--
2.7.4

2017-09-28 18:05:21

by Volodymyr Babchuk

[permalink] [raw]
Subject: [PATCH v1 06/14] tee: optee: add page list manipulation functions

From: Volodymyr Babchuk <[email protected]>

These functions will be used to pass information about shared
buffers to OP-TEE.

Signed-off-by: Volodymyr Babchuk <[email protected]>
---
drivers/tee/optee/call.c | 48 +++++++++++++++++++++++++++++++++++++++
drivers/tee/optee/optee_private.h | 4 ++++
2 files changed, 52 insertions(+)

diff --git a/drivers/tee/optee/call.c b/drivers/tee/optee/call.c
index f7b7b40..f8e044d 100644
--- a/drivers/tee/optee/call.c
+++ b/drivers/tee/optee/call.c
@@ -11,6 +11,7 @@
* GNU General Public License for more details.
*
*/
+#include <asm/pgtable.h>
#include <linux/arm-smccc.h>
#include <linux/device.h>
#include <linux/err.h>
@@ -442,3 +443,50 @@ void optee_disable_shm_cache(struct optee *optee)
}
optee_cq_wait_final(&optee->call_queue, &w);
}
+
+/**
+ * optee_fill_pages_list() - write list of user pages to given shared
+ * buffer.
+ *
+ * @dst: page-aligned buffer where list of pages will be stored
+ * @pages: array of pages that represents shared buffer
+ * @num_pages: number of entries in @pages
+ *
+ * @dst should be big enough to hold list of user page addresses and
+ * links to the next pages of buffer
+ */
+void optee_fill_pages_list(u64 *dst, struct page **pages, size_t num_pages)
+{
+ size_t i;
+
+ /* TODO: add support for RichOS page sizes that != 4096 */
+ BUILD_BUG_ON(PAGE_SIZE != OPTEE_MSG_NONCONTIG_PAGE_SIZE);
+ for (i = 0; i < num_pages; i++, dst++) {
+ /* Check if we are going to roll over the page boundary */
+ if (IS_ALIGNED((uintptr_t)(dst + 1),
+ OPTEE_MSG_NONCONTIG_PAGE_SIZE)) {
+ *dst = virt_to_phys(dst + 1);
+ dst++;
+ }
+ *dst = page_to_phys(pages[i]);
+ }
+}
+
+static size_t get_pages_array_size(size_t num_entries)
+{
+ /* Number of user pages + number of pages to hold list of user pages */
+ return sizeof(u64) *
+ (num_entries + (sizeof(u64) * num_entries) /
+ OPTEE_MSG_NONCONTIG_PAGE_SIZE);
+}
+
+u64 *optee_allocate_pages_array(size_t num_entries)
+{
+ return alloc_pages_exact(get_pages_array_size(num_entries), GFP_KERNEL);
+}
+
+void optee_free_pages_array(void *array, size_t num_entries)
+{
+ free_pages_exact(array, get_pages_array_size(num_entries));
+}
+
diff --git a/drivers/tee/optee/optee_private.h b/drivers/tee/optee/optee_private.h
index c374cd5..caa3c04 100644
--- a/drivers/tee/optee/optee_private.h
+++ b/drivers/tee/optee/optee_private.h
@@ -165,6 +165,10 @@ int optee_from_msg_param(struct tee_param *params, size_t num_params,
int optee_to_msg_param(struct optee_msg_param *msg_params, size_t num_params,
const struct tee_param *params);

+u64 *optee_allocate_pages_array(size_t num_entries);
+void optee_free_pages_array(void *array, size_t num_entries);
+void optee_fill_pages_list(u64 *dst, struct page **pages, size_t num_pages);
+
/*
* Small helpers
*/
--
2.7.4

2017-09-28 18:05:39

by Volodymyr Babchuk

[permalink] [raw]
Subject: [PATCH v1 09/14] tee: optee: add registered buffers handling into RPC calls

From: Volodymyr Babchuk <[email protected]>

With latest changes to OP-TEE we can use any buffers as a shared memory.
Thus, it is possible for supplicant to provide part of own memory
when OP-TEE asks to allocate a shared buffer.

This patch adds support for such feature into RPC handling code.
Now when OP-TEE asks supplicant to allocate shared buffer, supplicant
can use TEE_IOC_SHM_REGISTER to provide such buffer. RPC handler is
aware of this, so it will pass list of allocated pages to OP-TEE.

Signed-off-by: Volodymyr Babchuk <[email protected]>
---
drivers/tee/optee/call.c | 19 ++++++++++-
drivers/tee/optee/core.c | 2 ++
drivers/tee/optee/optee_private.h | 15 +++++++-
drivers/tee/optee/rpc.c | 72 ++++++++++++++++++++++++++++++++++-----
4 files changed, 97 insertions(+), 11 deletions(-)

diff --git a/drivers/tee/optee/call.c b/drivers/tee/optee/call.c
index ec53dca..7702991 100644
--- a/drivers/tee/optee/call.c
+++ b/drivers/tee/optee/call.c
@@ -136,6 +136,7 @@ u32 optee_do_call_with_arg(struct tee_context *ctx, phys_addr_t parg)
struct optee *optee = tee_get_drvdata(ctx->teedev);
struct optee_call_waiter w;
struct optee_rpc_param param = { };
+ struct optee_call_ctx call_ctx = { };
u32 ret;

param.a0 = OPTEE_SMC_CALL_WITH_ARG;
@@ -160,13 +161,14 @@ u32 optee_do_call_with_arg(struct tee_context *ctx, phys_addr_t parg)
param.a1 = res.a1;
param.a2 = res.a2;
param.a3 = res.a3;
- optee_handle_rpc(ctx, &param);
+ optee_handle_rpc(ctx, &param, &call_ctx);
} else {
ret = res.a0;
break;
}
}

+ optee_rpc_finalize_call(&call_ctx);
/*
* We're done with our thread in secure world, if there's any
* thread waiters wake up one.
@@ -554,3 +556,18 @@ int optee_shm_unregister(struct tee_context *ctx, struct tee_shm *shm)
tee_shm_free(shm_arg);
return rc;
}
+
+int optee_shm_register_supp(struct tee_context *ctx, struct tee_shm *shm,
+ struct page **pages, size_t num_pages)
+{
+ /*
+ * We don't want to register supplicant memory in OP-TEE.
+ * Instead information about it will be passed in RPC code.
+ */
+ return 0;
+}
+
+int optee_shm_unregister_supp(struct tee_context *ctx, struct tee_shm *shm)
+{
+ return 0;
+}
diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c
index a962f60..9883592 100644
--- a/drivers/tee/optee/core.c
+++ b/drivers/tee/optee/core.c
@@ -339,6 +339,8 @@ static const struct tee_driver_ops optee_supp_ops = {
.release = optee_release,
.supp_recv = optee_supp_recv,
.supp_send = optee_supp_send,
+ .shm_register = optee_shm_register_supp,
+ .shm_unregister = optee_shm_unregister_supp,
};

static const struct tee_desc optee_supp_desc = {
diff --git a/drivers/tee/optee/optee_private.h b/drivers/tee/optee/optee_private.h
index 3ea7f7a..dfe1158 100644
--- a/drivers/tee/optee/optee_private.h
+++ b/drivers/tee/optee/optee_private.h
@@ -130,7 +130,16 @@ struct optee_rpc_param {
u32 a7;
};

-void optee_handle_rpc(struct tee_context *ctx, struct optee_rpc_param *param);
+/* Holds context that is preserved during one STD call */
+struct optee_call_ctx {
+ /* information about page array used in last allocation */
+ void *pages_array;
+ size_t num_entries;
+};
+
+void optee_handle_rpc(struct tee_context *ctx, struct optee_rpc_param *param,
+ struct optee_call_ctx *call_ctx);
+void optee_rpc_finalize_call(struct optee_call_ctx *call_ctx);

void optee_wait_queue_init(struct optee_wait_queue *wq);
void optee_wait_queue_exit(struct optee_wait_queue *wq);
@@ -164,6 +173,10 @@ int optee_shm_register(struct tee_context *ctx, struct tee_shm *shm,
struct page **pages, size_t num_pages);
int optee_shm_unregister(struct tee_context *ctx, struct tee_shm *shm);

+int optee_shm_register_supp(struct tee_context *ctx, struct tee_shm *shm,
+ struct page **pages, size_t num_pages);
+int optee_shm_unregister_supp(struct tee_context *ctx, struct tee_shm *shm);
+
int optee_from_msg_param(struct tee_param *params, size_t num_params,
const struct optee_msg_param *msg_params);
int optee_to_msg_param(struct optee_msg_param *msg_params, size_t num_params,
diff --git a/drivers/tee/optee/rpc.c b/drivers/tee/optee/rpc.c
index cef417f..1ff86bd 100644
--- a/drivers/tee/optee/rpc.c
+++ b/drivers/tee/optee/rpc.c
@@ -200,7 +200,8 @@ static struct tee_shm *cmd_alloc_suppl(struct tee_context *ctx, size_t sz)
}

static void handle_rpc_func_cmd_shm_alloc(struct tee_context *ctx,
- struct optee_msg_arg *arg)
+ struct optee_msg_arg *arg,
+ struct optee_call_ctx *call_ctx)
{
phys_addr_t pa;
struct tee_shm *shm;
@@ -245,10 +246,44 @@ static void handle_rpc_func_cmd_shm_alloc(struct tee_context *ctx,
goto bad;
}

- arg->params[0].attr = OPTEE_MSG_ATTR_TYPE_TMEM_OUTPUT;
- arg->params[0].u.tmem.buf_ptr = pa;
- arg->params[0].u.tmem.size = sz;
- arg->params[0].u.tmem.shm_ref = (unsigned long)shm;
+ sz = tee_shm_get_size(shm);
+
+ if (tee_shm_is_registered(shm)) {
+ struct page **pages;
+ u64 *pages_array;
+ size_t page_num;
+
+ pages = tee_shm_get_pages(shm, &page_num);
+ if (!pages || !page_num) {
+ arg->ret = TEEC_ERROR_OUT_OF_MEMORY;
+ goto bad;
+ }
+
+ pages_array = optee_allocate_pages_array(page_num);
+ if (!pages_array) {
+ arg->ret = TEEC_ERROR_OUT_OF_MEMORY;
+ goto bad;
+ }
+
+ call_ctx->pages_array = pages_array;
+ call_ctx->num_entries = page_num;
+
+ arg->params[0].attr = OPTEE_MSG_ATTR_TYPE_TMEM_OUTPUT |
+ OPTEE_MSG_ATTR_NONCONTIG;
+ arg->params[0].u.tmem.buf_ptr = virt_to_phys(pages_array) +
+ tee_shm_get_page_offset(shm);
+
+ arg->params[0].u.tmem.size = tee_shm_get_size(shm);
+ arg->params[0].u.tmem.shm_ref = (unsigned long)shm;
+
+ optee_fill_pages_list(pages_array, pages, page_num);
+ } else {
+ arg->params[0].attr = OPTEE_MSG_ATTR_TYPE_TMEM_OUTPUT;
+ arg->params[0].u.tmem.buf_ptr = pa;
+ arg->params[0].u.tmem.size = sz;
+ arg->params[0].u.tmem.shm_ref = (unsigned long)shm;
+ }
+
arg->ret = TEEC_SUCCESS;
return;
bad:
@@ -307,8 +342,24 @@ static void handle_rpc_func_cmd_shm_free(struct tee_context *ctx,
arg->ret = TEEC_SUCCESS;
}

+static void free_page_array(struct optee_call_ctx *call_ctx)
+{
+ if (call_ctx->pages_array) {
+ optee_free_pages_array(call_ctx->pages_array,
+ call_ctx->num_entries);
+ call_ctx->pages_array = NULL;
+ call_ctx->num_entries = 0;
+ }
+}
+
+void optee_rpc_finalize_call(struct optee_call_ctx *call_ctx)
+{
+ free_page_array(call_ctx);
+}
+
static void handle_rpc_func_cmd(struct tee_context *ctx, struct optee *optee,
- struct tee_shm *shm)
+ struct tee_shm *shm,
+ struct optee_call_ctx *call_ctx)
{
struct optee_msg_arg *arg;

@@ -329,7 +380,8 @@ static void handle_rpc_func_cmd(struct tee_context *ctx, struct optee *optee,
handle_rpc_func_cmd_wait(arg);
break;
case OPTEE_MSG_RPC_CMD_SHM_ALLOC:
- handle_rpc_func_cmd_shm_alloc(ctx, arg);
+ free_page_array(call_ctx);
+ handle_rpc_func_cmd_shm_alloc(ctx, arg, call_ctx);
break;
case OPTEE_MSG_RPC_CMD_SHM_FREE:
handle_rpc_func_cmd_shm_free(ctx, arg);
@@ -343,10 +395,12 @@ static void handle_rpc_func_cmd(struct tee_context *ctx, struct optee *optee,
* optee_handle_rpc() - handle RPC from secure world
* @ctx: context doing the RPC
* @param: value of registers for the RPC
+ * @call_ctx: call context. Preserved during one OP-TEE invocation
*
* Result of RPC is written back into @param.
*/
-void optee_handle_rpc(struct tee_context *ctx, struct optee_rpc_param *param)
+void optee_handle_rpc(struct tee_context *ctx, struct optee_rpc_param *param,
+ struct optee_call_ctx *call_ctx)
{
struct tee_device *teedev = ctx->teedev;
struct optee *optee = tee_get_drvdata(teedev);
@@ -381,7 +435,7 @@ void optee_handle_rpc(struct tee_context *ctx, struct optee_rpc_param *param)
break;
case OPTEE_SMC_RPC_FUNC_CMD:
shm = reg_pair_to_ptr(param->a1, param->a2);
- handle_rpc_func_cmd(ctx, optee, shm);
+ handle_rpc_func_cmd(ctx, optee, shm, call_ctx);
break;
default:
pr_warn("Unknown RPC func 0x%x\n",
--
2.7.4

2017-09-28 18:05:59

by Volodymyr Babchuk

[permalink] [raw]
Subject: [PATCH v1 12/14] tee: optee: enable dynamic SHM support

From: Volodymyr Babchuk <[email protected]>

Previous patches added various features that are needed for dynamic SHM.
Dynamic SHM allows Normal World to share any buffers with OP-TEE.
While original design suggested to use pre-allocated region (usually of
1M to 2M of size), this new approach allows to use all non-secure RAM for
command buffers, RPC allocations and TA parameters.

This patch checks capability OPTEE_SMC_SEC_CAP_DYNAMIC_SHM. If it was set
by OP-TEE, then kernel part of OP-TEE will use kernel page allocator
to allocate command buffers. Also it will set TEE_GEN_CAP_REG_MEM
capability to tell userspace that it supports shared memory registration.

Signed-off-by: Volodymyr Babchuk <[email protected]>
---
drivers/tee/optee/core.c | 69 +++++++++++++++++++++++++++++++++++-------------
1 file changed, 51 insertions(+), 18 deletions(-)

diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c
index 8e012ea..e8fd9af 100644
--- a/drivers/tee/optee/core.c
+++ b/drivers/tee/optee/core.c
@@ -28,6 +28,7 @@
#include <linux/uaccess.h>
#include "optee_private.h"
#include "optee_smc.h"
+#include "shm_pool.h"

#define DRIVER_NAME "optee"

@@ -227,6 +228,10 @@ static void optee_get_version(struct tee_device *teedev,
.impl_caps = TEE_OPTEE_CAP_TZ,
.gen_caps = TEE_GEN_CAP_GP,
};
+ struct optee *optee = tee_get_drvdata(teedev);
+
+ if (optee->sec_caps & OPTEE_SMC_SEC_CAP_DYNAMIC_SHM)
+ v.gen_caps |= TEE_GEN_CAP_REG_MEM;
*vers = v;
}

@@ -405,21 +410,22 @@ static bool optee_msg_exchange_capabilities(optee_invoke_fn *invoke_fn,
}

static struct tee_shm_pool *
-optee_config_shm_memremap(optee_invoke_fn *invoke_fn, void **memremaped_shm)
+optee_config_shm_memremap(optee_invoke_fn *invoke_fn, void **memremaped_shm,
+ u32 sec_caps)
{
union {
struct arm_smccc_res smccc;
struct optee_smc_get_shm_config_result result;
} res;
- struct tee_shm_pool *pool;
unsigned long vaddr;
phys_addr_t paddr;
size_t size;
phys_addr_t begin;
phys_addr_t end;
void *va;
- struct tee_shm_pool_mem_info priv_info;
- struct tee_shm_pool_mem_info dmabuf_info;
+ struct tee_shm_pool_mgr *priv_mgr;
+ struct tee_shm_pool_mgr *dmabuf_mgr;
+ void *rc;

invoke_fn(OPTEE_SMC_GET_SHM_CONFIG, 0, 0, 0, 0, 0, 0, 0, &res.smccc);
if (res.result.status != OPTEE_SMC_RETURN_OK) {
@@ -449,22 +455,49 @@ optee_config_shm_memremap(optee_invoke_fn *invoke_fn, void **memremaped_shm)
}
vaddr = (unsigned long)va;

- priv_info.vaddr = vaddr;
- priv_info.paddr = paddr;
- priv_info.size = OPTEE_SHM_NUM_PRIV_PAGES * PAGE_SIZE;
- dmabuf_info.vaddr = vaddr + OPTEE_SHM_NUM_PRIV_PAGES * PAGE_SIZE;
- dmabuf_info.paddr = paddr + OPTEE_SHM_NUM_PRIV_PAGES * PAGE_SIZE;
- dmabuf_info.size = size - OPTEE_SHM_NUM_PRIV_PAGES * PAGE_SIZE;
-
- pool = tee_shm_pool_alloc_res_mem(&priv_info, &dmabuf_info);
- if (IS_ERR(pool)) {
- memunmap(va);
- goto out;
+ /*
+ * If OP-TEE can work with unregistered SHM, we will use own pool
+ * for private shm
+ */
+ if (sec_caps & OPTEE_SMC_SEC_CAP_DYNAMIC_SHM) {
+ rc = optee_shm_pool_alloc_pages();
+ if (IS_ERR(rc))
+ goto err_memunmap;
+ priv_mgr = rc;
+ } else {
+ const size_t sz = OPTEE_SHM_NUM_PRIV_PAGES * PAGE_SIZE;
+
+ rc = tee_shm_pool_mgr_alloc_res_mem(vaddr, paddr, sz,
+ 3 /* 8 bytes aligned */);
+ if (IS_ERR(rc))
+ goto err_memunmap;
+ priv_mgr = rc;
+
+ vaddr += sz;
+ paddr += sz;
+ size -= sz;
}

+ rc = tee_shm_pool_mgr_alloc_res_mem(vaddr, paddr, size, PAGE_SHIFT);
+ if (IS_ERR(rc))
+ goto err_free_priv_mgr;
+ dmabuf_mgr = rc;
+
+ rc = tee_shm_pool_alloc(priv_mgr, dmabuf_mgr);
+ if (IS_ERR(rc))
+ goto err_free_dmabuf_mgr;
+
*memremaped_shm = va;
-out:
- return pool;
+
+ return rc;
+
+err_free_dmabuf_mgr:
+ tee_shm_pool_mgr_destroy(dmabuf_mgr);
+err_free_priv_mgr:
+ tee_shm_pool_mgr_destroy(priv_mgr);
+err_memunmap:
+ memunmap(va);
+ return rc;
}

/* Simple wrapper functions to be able to use a function pointer */
@@ -542,7 +575,7 @@ static struct optee *optee_probe(struct device_node *np)
if (!(sec_caps & OPTEE_SMC_SEC_CAP_HAVE_RESERVED_SHM))
return ERR_PTR(-EINVAL);

- pool = optee_config_shm_memremap(invoke_fn, &memremaped_shm);
+ pool = optee_config_shm_memremap(invoke_fn, &memremaped_shm, sec_caps);
if (IS_ERR(pool))
return (void *)pool;

--
2.7.4

2017-09-28 18:06:24

by Volodymyr Babchuk

[permalink] [raw]
Subject: [PATCH v1 13/14] tee: use reference counting for tee_context

From: Volodymyr Babchuk <[email protected]>

We need to ensure that tee_context is present until last
shared buffer will be freed.

Signed-off-by: Volodymyr Babchuk <[email protected]>
---
drivers/tee/tee_core.c | 40 +++++++++++++++++++++++++++++++---------
drivers/tee/tee_private.h | 3 +++
drivers/tee/tee_shm.c | 7 +++++++
include/linux/tee_drv.h | 7 +++++++
4 files changed, 48 insertions(+), 9 deletions(-)

diff --git a/drivers/tee/tee_core.c b/drivers/tee/tee_core.c
index 295910f..3d49ac2 100644
--- a/drivers/tee/tee_core.c
+++ b/drivers/tee/tee_core.c
@@ -54,6 +54,7 @@ static int tee_open(struct inode *inode, struct file *filp)
goto err;
}

+ kref_init(&ctx->refcount);
ctx->teedev = teedev;
INIT_LIST_HEAD(&ctx->list_shm);
filp->private_data = ctx;
@@ -68,19 +69,40 @@ static int tee_open(struct inode *inode, struct file *filp)
return rc;
}

-static int tee_release(struct inode *inode, struct file *filp)
+void teedev_ctx_get(struct tee_context *ctx)
{
- struct tee_context *ctx = filp->private_data;
- struct tee_device *teedev = ctx->teedev;
- struct tee_shm *shm;
+ if (ctx->releasing)
+ return;
+
+ kref_get(&ctx->refcount);
+}

+static void teedev_ctx_release(struct kref *ref)
+{
+ struct tee_context *ctx = container_of(ref, struct tee_context,
+ refcount);
+ ctx->releasing = true;
ctx->teedev->desc->ops->release(ctx);
- mutex_lock(&ctx->teedev->mutex);
- list_for_each_entry(shm, &ctx->list_shm, link)
- shm->ctx = NULL;
- mutex_unlock(&ctx->teedev->mutex);
kfree(ctx);
- tee_device_put(teedev);
+}
+
+void teedev_ctx_put(struct tee_context *ctx)
+{
+ if (ctx->releasing)
+ return;
+
+ kref_put(&ctx->refcount, teedev_ctx_release);
+}
+
+static void teedev_close_context(struct tee_context *ctx)
+{
+ tee_device_put(ctx->teedev);
+ teedev_ctx_put(ctx);
+}
+
+static int tee_release(struct inode *inode, struct file *filp)
+{
+ teedev_close_context(filp->private_data);
return 0;
}

diff --git a/drivers/tee/tee_private.h b/drivers/tee/tee_private.h
index 2bc2b5a..85d99d6 100644
--- a/drivers/tee/tee_private.h
+++ b/drivers/tee/tee_private.h
@@ -73,4 +73,7 @@ int tee_shm_get_fd(struct tee_shm *shm);
bool tee_device_get(struct tee_device *teedev);
void tee_device_put(struct tee_device *teedev);

+void teedev_ctx_get(struct tee_context *ctx);
+void teedev_ctx_put(struct tee_context *ctx);
+
#endif /*TEE_PRIVATE_H*/
diff --git a/drivers/tee/tee_shm.c b/drivers/tee/tee_shm.c
index 1ed708c..5176c83 100644
--- a/drivers/tee/tee_shm.c
+++ b/drivers/tee/tee_shm.c
@@ -53,6 +53,9 @@ static void tee_shm_release(struct tee_shm *shm)
kfree(shm->pages);
}

+ if (shm->ctx)
+ teedev_ctx_put(shm->ctx);
+
kfree(shm);

tee_device_put(teedev);
@@ -187,6 +190,7 @@ struct tee_shm *__tee_shm_alloc(struct tee_context *ctx,
}

if (ctx) {
+ teedev_ctx_get(ctx);
mutex_lock(&teedev->mutex);
list_add_tail(&shm->link, &ctx->list_shm);
mutex_unlock(&teedev->mutex);
@@ -256,6 +260,8 @@ struct tee_shm *tee_shm_register(struct tee_context *ctx, unsigned long addr,
return ERR_PTR(-EINVAL);
}

+ teedev_ctx_get(ctx);
+
shm = kzalloc(sizeof(*shm), GFP_KERNEL);
if (!shm) {
ret = ERR_PTR(-ENOMEM);
@@ -332,6 +338,7 @@ struct tee_shm *tee_shm_register(struct tee_context *ctx, unsigned long addr,
kfree(shm->pages);
}
kfree(shm);
+ teedev_ctx_put(ctx);
tee_device_put(teedev);
return ret;
}
diff --git a/include/linux/tee_drv.h b/include/linux/tee_drv.h
index d773827..6aaef65 100644
--- a/include/linux/tee_drv.h
+++ b/include/linux/tee_drv.h
@@ -17,6 +17,7 @@

#include <linux/types.h>
#include <linux/idr.h>
+#include <linux/kref.h>
#include <linux/list.h>
#include <linux/tee.h>

@@ -42,11 +43,17 @@ struct tee_shm_pool;
* @teedev: pointer to this drivers struct tee_device
* @list_shm: List of shared memory object owned by this context
* @data: driver specific context data, managed by the driver
+ * @refcount: reference counter for this structure
+ * @releasing: flag that indicates if context is being released right now.
+ * It is needed to break circular dependency on context during
+ * shared memory release.
*/
struct tee_context {
struct tee_device *teedev;
struct list_head list_shm;
void *data;
+ struct kref refcount;
+ bool releasing;
};

struct tee_param_memref {
--
2.7.4

2017-09-28 18:05:28

by Volodymyr Babchuk

[permalink] [raw]
Subject: [PATCH v1 07/14] tee: optee: add shared buffer registration functions

From: Volodymyr Babchuk <[email protected]>

This change adds ops for shm_(un)register functions in tee interface.
Client application can use these functions to (un)register an own shared
buffer in OP-TEE address space. This allows zero copy data sharing between
Normal and Secure Worlds.

Please note that while those functions were added to optee code,
it does not report to userspace that those functions are available.
OP-TEE code does not set TEE_GEN_CAP_REG_MEM flag. This flag will be
enabled only after all other features of dynamic shared memory will be
implemented in subsequent patches.

Signed-off-by: Volodymyr Babchuk <[email protected]>
---
drivers/tee/optee/call.c | 64 +++++++++++++++++++++++++++++++++++++++
drivers/tee/optee/core.c | 2 ++
drivers/tee/optee/optee_private.h | 4 +++
3 files changed, 70 insertions(+)

diff --git a/drivers/tee/optee/call.c b/drivers/tee/optee/call.c
index f8e044d..ec53dca 100644
--- a/drivers/tee/optee/call.c
+++ b/drivers/tee/optee/call.c
@@ -490,3 +490,67 @@ void optee_free_pages_array(void *array, size_t num_entries)
free_pages_exact(array, get_pages_array_size(num_entries));
}

+int optee_shm_register(struct tee_context *ctx, struct tee_shm *shm,
+ struct page **pages, size_t num_pages)
+{
+ struct tee_shm *shm_arg = NULL;
+ struct optee_msg_arg *msg_arg;
+ u64 *pages_array;
+ phys_addr_t msg_parg;
+ int rc = 0;
+
+ if (!num_pages)
+ return -EINVAL;
+
+ pages_array = optee_allocate_pages_array(num_pages);
+ if (!pages_array)
+ return -ENOMEM;
+
+ shm_arg = get_msg_arg(ctx, 1, &msg_arg, &msg_parg);
+ if (IS_ERR(shm_arg)) {
+ rc = PTR_ERR(shm_arg);
+ goto out;
+ }
+
+ optee_fill_pages_list(pages_array, pages, num_pages);
+
+ msg_arg->cmd = OPTEE_MSG_CMD_REGISTER_SHM;
+ msg_arg->params->attr = OPTEE_MSG_ATTR_TYPE_TMEM_OUTPUT |
+ OPTEE_MSG_ATTR_NONCONTIG;
+ msg_arg->params->u.tmem.shm_ref = (unsigned long)shm;
+ msg_arg->params->u.tmem.size = tee_shm_get_size(shm);
+ msg_arg->params->u.tmem.buf_ptr = virt_to_phys(pages_array) |
+ tee_shm_get_page_offset(shm);
+
+ if (optee_do_call_with_arg(ctx, msg_parg) ||
+ msg_arg->ret != TEEC_SUCCESS)
+ rc = -EINVAL;
+
+ tee_shm_free(shm_arg);
+out:
+ optee_free_pages_array(pages_array, num_pages);
+ return rc;
+}
+
+int optee_shm_unregister(struct tee_context *ctx, struct tee_shm *shm)
+{
+ struct tee_shm *shm_arg;
+ struct optee_msg_arg *msg_arg;
+ phys_addr_t msg_parg;
+ int rc = 0;
+
+ shm_arg = get_msg_arg(ctx, 1, &msg_arg, &msg_parg);
+ if (IS_ERR(shm_arg))
+ return PTR_ERR(shm_arg);
+
+ msg_arg->cmd = OPTEE_MSG_CMD_UNREGISTER_SHM;
+
+ msg_arg->params[0].attr = OPTEE_MSG_ATTR_TYPE_RMEM_INPUT;
+ msg_arg->params[0].u.rmem.shm_ref = (unsigned long)shm;
+
+ if (optee_do_call_with_arg(ctx, msg_parg) ||
+ msg_arg->ret != TEEC_SUCCESS)
+ rc = -EINVAL;
+ tee_shm_free(shm_arg);
+ return rc;
+}
diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c
index 7952357..4d448bf 100644
--- a/drivers/tee/optee/core.c
+++ b/drivers/tee/optee/core.c
@@ -267,6 +267,8 @@ static const struct tee_driver_ops optee_ops = {
.close_session = optee_close_session,
.invoke_func = optee_invoke_func,
.cancel_req = optee_cancel_req,
+ .shm_register = optee_shm_register,
+ .shm_unregister = optee_shm_unregister,
};

static const struct tee_desc optee_desc = {
diff --git a/drivers/tee/optee/optee_private.h b/drivers/tee/optee/optee_private.h
index caa3c04..3ea7f7a 100644
--- a/drivers/tee/optee/optee_private.h
+++ b/drivers/tee/optee/optee_private.h
@@ -160,6 +160,10 @@ int optee_cancel_req(struct tee_context *ctx, u32 cancel_id, u32 session);
void optee_enable_shm_cache(struct optee *optee);
void optee_disable_shm_cache(struct optee *optee);

+int optee_shm_register(struct tee_context *ctx, struct tee_shm *shm,
+ struct page **pages, size_t num_pages);
+int optee_shm_unregister(struct tee_context *ctx, struct tee_shm *shm);
+
int optee_from_msg_param(struct tee_param *params, size_t num_params,
const struct optee_msg_param *msg_params);
int optee_to_msg_param(struct optee_msg_param *msg_params, size_t num_params,
--
2.7.4

2017-09-28 18:05:46

by Volodymyr Babchuk

[permalink] [raw]
Subject: [PATCH v1 10/14] tee: optee: store OP-TEE capabilities in private data

From: Volodymyr Babchuk <[email protected]>

Those capabilities will be used in subsequent patches.

Signed-off-by: Volodymyr Babchuk <[email protected]>
---
drivers/tee/optee/core.c | 1 +
drivers/tee/optee/optee_private.h | 3 +++
2 files changed, 4 insertions(+)

diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c
index 9883592..8e012ea 100644
--- a/drivers/tee/optee/core.c
+++ b/drivers/tee/optee/core.c
@@ -553,6 +553,7 @@ static struct optee *optee_probe(struct device_node *np)
}

optee->invoke_fn = invoke_fn;
+ optee->sec_caps = sec_caps;

teedev = tee_device_alloc(&optee_desc, NULL, pool, optee);
if (IS_ERR(teedev)) {
diff --git a/drivers/tee/optee/optee_private.h b/drivers/tee/optee/optee_private.h
index dfe1158..fc534d7 100644
--- a/drivers/tee/optee/optee_private.h
+++ b/drivers/tee/optee/optee_private.h
@@ -96,6 +96,8 @@ struct optee_supp {
* @supp: supplicant synchronization struct for RPC to supplicant
* @pool: shared memory pool
* @memremaped_shm virtual address of memory in shared memory pool
+ * @sec_caps: secure world capabilities defined by
+ * OPTEE_SMC_SEC_CAP_* in optee_smc.h
*/
struct optee {
struct tee_device *supp_teedev;
@@ -106,6 +108,7 @@ struct optee {
struct optee_supp supp;
struct tee_shm_pool *pool;
void *memremaped_shm;
+ u32 sec_caps;
};

struct optee_session {
--
2.7.4

2017-09-28 18:06:09

by Volodymyr Babchuk

[permalink] [raw]
Subject: [PATCH v1 14/14] tee: shm: inline tee_shm getter functions

From: Volodymyr Babchuk <[email protected]>

Now, when struct tee_shm is defined in public header,
we can inline small getter functions.

Signed-off-by: Volodymyr Babchuk <[email protected]>
---
drivers/tee/tee_shm.c | 17 -----------------
include/linux/tee_drv.h | 10 ++++++++--
2 files changed, 8 insertions(+), 19 deletions(-)

diff --git a/drivers/tee/tee_shm.c b/drivers/tee/tee_shm.c
index 5176c83..453700a 100644
--- a/drivers/tee/tee_shm.c
+++ b/drivers/tee/tee_shm.c
@@ -494,23 +494,6 @@ struct tee_shm *tee_shm_get_from_id(struct tee_context *ctx, int id)
}
EXPORT_SYMBOL_GPL(tee_shm_get_from_id);

-bool tee_shm_is_registered(struct tee_shm *shm)
-{
- return shm && (shm->flags & TEE_SHM_REGISTER);
-}
-EXPORT_SYMBOL_GPL(tee_shm_is_registered);
-
-/**
- * tee_shm_get_id() - Get id of a shared memory object
- * @shm: Shared memory handle
- * @returns id
- */
-int tee_shm_get_id(struct tee_shm *shm)
-{
- return shm->id;
-}
-EXPORT_SYMBOL_GPL(tee_shm_get_id);
-
/**
* tee_shm_put() - Decrease reference count on a shared memory handle
* @shm: Shared memory handle
diff --git a/include/linux/tee_drv.h b/include/linux/tee_drv.h
index 6aaef65..2ae0286 100644
--- a/include/linux/tee_drv.h
+++ b/include/linux/tee_drv.h
@@ -429,7 +429,10 @@ static inline size_t tee_shm_get_page_offset(struct tee_shm *shm)
* @shm: Shared memory handle
* @returns id
*/
-int tee_shm_get_id(struct tee_shm *shm);
+static inline int tee_shm_get_id(struct tee_shm *shm)
+{
+ return shm->id;
+}

/**
* tee_shm_get_from_id() - Find shared memory object and increase reference
@@ -445,6 +448,9 @@ struct tee_shm *tee_shm_get_from_id(struct tee_context *ctx, int id);
* @shm: Shared memory handle
* @returns true if object is registered in TEE
*/
-bool tee_shm_is_registered(struct tee_shm *shm);
+static inline bool tee_shm_is_registered(struct tee_shm *shm)
+{
+ return shm && (shm->flags & TEE_SHM_REGISTER);
+}

#endif /*__TEE_DRV_H*/
--
2.7.4

2017-09-28 18:05:33

by Volodymyr Babchuk

[permalink] [raw]
Subject: [PATCH v1 08/14] tee: optee: add registered shared parameters handling

From: Volodymyr Babchuk <[email protected]>

Now, when client applications can register own shared buffers in OP-TEE,
we need to extend ABI for parameter passing to/from OP-TEE.

So, if OP-TEE core detects that parameter belongs to registered shared
memory, it will use corresponding parameter attribute.

Signed-off-by: Volodymyr Babchuk <[email protected]>
---
drivers/tee/optee/core.c | 86 +++++++++++++++++++++++++++++++++++++++---------
1 file changed, 71 insertions(+), 15 deletions(-)

diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c
index 4d448bf..a962f60 100644
--- a/drivers/tee/optee/core.c
+++ b/drivers/tee/optee/core.c
@@ -97,6 +97,33 @@ int optee_from_msg_param(struct tee_param *params, size_t num_params,
return rc;
}
break;
+ case OPTEE_MSG_ATTR_TYPE_RMEM_INPUT:
+ case OPTEE_MSG_ATTR_TYPE_RMEM_OUTPUT:
+ case OPTEE_MSG_ATTR_TYPE_RMEM_INOUT:
+ p->attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT +
+ attr - OPTEE_MSG_ATTR_TYPE_RMEM_INPUT;
+ p->u.memref.size = mp->u.rmem.size;
+ shm = (struct tee_shm *)(unsigned long)
+ mp->u.rmem.shm_ref;
+
+ if (!shm) {
+ p->u.memref.shm_offs = 0;
+ p->u.memref.shm = NULL;
+ break;
+ }
+ p->u.memref.shm_offs = mp->u.rmem.offs;
+ p->u.memref.shm = shm;
+
+ /* Check that the memref is covered by the shm object */
+ if (p->u.memref.size) {
+ size_t o = p->u.memref.shm_offs +
+ p->u.memref.size;
+
+ if (o > tee_shm_get_size(shm))
+ return -EINVAL;
+ }
+ break;
+
default:
return -EINVAL;
}
@@ -104,6 +131,46 @@ int optee_from_msg_param(struct tee_param *params, size_t num_params,
return 0;
}

+static int to_msg_param_tmp_mem(struct optee_msg_param *mp,
+ const struct tee_param *p)
+{
+ int rc;
+ phys_addr_t pa;
+
+ mp->attr = OPTEE_MSG_ATTR_TYPE_TMEM_INPUT + p->attr -
+ TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT;
+
+ mp->u.tmem.shm_ref = (unsigned long)p->u.memref.shm;
+ mp->u.tmem.size = p->u.memref.size;
+
+ if (!p->u.memref.shm) {
+ mp->u.tmem.buf_ptr = 0;
+ return 0;
+ }
+
+ rc = tee_shm_get_pa(p->u.memref.shm, p->u.memref.shm_offs, &pa);
+ if (rc)
+ return rc;
+
+ mp->u.tmem.buf_ptr = pa;
+ mp->attr |= OPTEE_MSG_ATTR_CACHE_PREDEFINED <<
+ OPTEE_MSG_ATTR_CACHE_SHIFT;
+
+ return 0;
+}
+
+static int to_msg_param_reg_mem(struct optee_msg_param *mp,
+ const struct tee_param *p)
+{
+ mp->attr = OPTEE_MSG_ATTR_TYPE_RMEM_INPUT + p->attr -
+ TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT;
+
+ mp->u.rmem.shm_ref = (unsigned long)p->u.memref.shm;
+ mp->u.rmem.size = p->u.memref.size;
+ mp->u.rmem.offs = p->u.memref.shm_offs;
+ return 0;
+}
+
/**
* optee_to_msg_param() - convert from struct tee_params to OPTEE_MSG parameters
* @msg_params: OPTEE_MSG parameters
@@ -116,7 +183,6 @@ int optee_to_msg_param(struct optee_msg_param *msg_params, size_t num_params,
{
int rc;
size_t n;
- phys_addr_t pa;

for (n = 0; n < num_params; n++) {
const struct tee_param *p = params + n;
@@ -139,22 +205,12 @@ int optee_to_msg_param(struct optee_msg_param *msg_params, size_t num_params,
case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT:
case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT:
case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT:
- mp->attr = OPTEE_MSG_ATTR_TYPE_TMEM_INPUT +
- p->attr -
- TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT;
- mp->u.tmem.shm_ref = (unsigned long)p->u.memref.shm;
- mp->u.tmem.size = p->u.memref.size;
- if (!p->u.memref.shm) {
- mp->u.tmem.buf_ptr = 0;
- break;
- }
- rc = tee_shm_get_pa(p->u.memref.shm,
- p->u.memref.shm_offs, &pa);
+ if (tee_shm_is_registered(p->u.memref.shm))
+ rc = to_msg_param_reg_mem(mp, p);
+ else
+ rc = to_msg_param_tmp_mem(mp, p);
if (rc)
return rc;
- mp->u.tmem.buf_ptr = pa;
- mp->attr |= OPTEE_MSG_ATTR_CACHE_PREDEFINED <<
- OPTEE_MSG_ATTR_CACHE_SHIFT;
break;
default:
return -EINVAL;
--
2.7.4

2017-09-28 18:05:51

by Volodymyr Babchuk

[permalink] [raw]
Subject: [PATCH v1 11/14] tee: optee: add optee-specific shared pool implementation

From: Volodymyr Babchuk <[email protected]>

This is simple pool that uses kernel page allocator. This pool can be
used in case OP-TEE supports dynamic shared memory.

Signed-off-by: Volodymyr Babchuk <[email protected]>
---
drivers/tee/optee/Makefile | 1 +
drivers/tee/optee/shm_pool.c | 75 ++++++++++++++++++++++++++++++++++++++++++++
drivers/tee/optee/shm_pool.h | 23 ++++++++++++++
3 files changed, 99 insertions(+)
create mode 100644 drivers/tee/optee/shm_pool.c
create mode 100644 drivers/tee/optee/shm_pool.h

diff --git a/drivers/tee/optee/Makefile b/drivers/tee/optee/Makefile
index 92fe578..220cf42 100644
--- a/drivers/tee/optee/Makefile
+++ b/drivers/tee/optee/Makefile
@@ -3,3 +3,4 @@ optee-objs += core.o
optee-objs += call.o
optee-objs += rpc.o
optee-objs += supp.o
+optee-objs += shm_pool.o
diff --git a/drivers/tee/optee/shm_pool.c b/drivers/tee/optee/shm_pool.c
new file mode 100644
index 0000000..4939781
--- /dev/null
+++ b/drivers/tee/optee/shm_pool.c
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2015, Linaro Limited
+ * Copyright (c) 2017, EPAM Systems
+ *
+ * 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/genalloc.h>
+#include <linux/slab.h>
+#include <linux/tee_drv.h>
+#include "optee_private.h"
+#include "optee_smc.h"
+#include "shm_pool.h"
+
+static int pool_op_alloc(struct tee_shm_pool_mgr *poolm,
+ struct tee_shm *shm, size_t size)
+{
+ unsigned int order = get_order(size);
+ struct page *page;
+
+ page = alloc_pages(GFP_KERNEL | __GFP_ZERO, order);
+ if (!page)
+ return -ENOMEM;
+
+ shm->kaddr = page_address(page);
+ shm->paddr = page_to_phys(page);
+ shm->size = PAGE_SIZE << order;
+
+ return 0;
+}
+
+static void pool_op_free(struct tee_shm_pool_mgr *poolm,
+ struct tee_shm *shm)
+{
+ free_pages((unsigned long)shm->kaddr, get_order(shm->size));
+ shm->kaddr = NULL;
+}
+
+static void pool_op_destroy_poolmgr(struct tee_shm_pool_mgr *poolm)
+{
+ kfree(poolm);
+}
+
+static const struct tee_shm_pool_mgr_ops pool_ops = {
+ .alloc = pool_op_alloc,
+ .free = pool_op_free,
+ .destroy_poolmgr = pool_op_destroy_poolmgr,
+};
+
+/**
+ * optee_shm_pool_alloc_pages() - create page-based allocator pool
+ *
+ * This pool is used when OP-TEE supports dymanic SHM. In this case
+ * command buffers and such are allocated from kernel's own memory.
+ */
+struct tee_shm_pool_mgr *optee_shm_pool_alloc_pages(void)
+{
+ struct tee_shm_pool_mgr *mgr = kzalloc(sizeof(*mgr), GFP_KERNEL);
+
+ if (!mgr)
+ return ERR_PTR(-ENOMEM);
+
+ mgr->ops = &pool_ops;
+
+ return mgr;
+}
diff --git a/drivers/tee/optee/shm_pool.h b/drivers/tee/optee/shm_pool.h
new file mode 100644
index 0000000..4e753c3
--- /dev/null
+++ b/drivers/tee/optee/shm_pool.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2015, Linaro Limited
+ * Copyright (c) 2016, EPAM Systems
+ *
+ * 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.
+ *
+ */
+
+#ifndef SHM_POOL_H
+#define SHM_POOL_H
+
+#include <linux/tee_drv.h>
+
+struct tee_shm_pool_mgr *optee_shm_pool_alloc_pages(void);
+
+#endif
--
2.7.4

2017-09-28 22:15:04

by Yury Norov

[permalink] [raw]
Subject: Re: [PATCH v1 04/14] tee: shm: add page accessor functions

Hi Volodymyr,

On Thu, Sep 28, 2017 at 09:04:01PM +0300, Volodymyr Babchuk wrote:
> From: Volodymyr Babchuk <[email protected]>
>
> In order to register a shared buffer in TEE, we need accessor
> function that return list of pages for that buffer.
>
> Signed-off-by: Volodymyr Babchuk <[email protected]>
> ---
> include/linux/tee_drv.h | 14 ++++++++++++++
> 1 file changed, 14 insertions(+)
>
> diff --git a/include/linux/tee_drv.h b/include/linux/tee_drv.h
> index 6ba8b76..d773827 100644
> --- a/include/linux/tee_drv.h
> +++ b/include/linux/tee_drv.h
> @@ -394,6 +394,20 @@ static inline size_t tee_shm_get_size(struct tee_shm *shm)
> }
>
> /**
> + * tee_shm_get_pages() - Get list of pages that hold shared buffer
> + * @shm: Shared memory handle
> + * @num_pages: Number of pages will be stored there
> + * @returns pointer to pages array
> + */
> +static inline struct page **tee_shm_get_pages(struct tee_shm *shm,
> + size_t *num_pages)
> +{
> + if (num_pages)
> + *num_pages = shm->num_pages;

My concern is about this check

The only use of the tee_shm_get_pages() I found is in patch #9:
+ size_t page_num;
+
+ pages = tee_shm_get_pages(shm, &page_num);

So there's no any valid scenario where you should pass NULL to the
function. And I don't understand why you do this check.

Even more, if in future there will be an occasion when function will
be passed with NULL, the error will become hidden by this code.

What about just drop it?

Yury

> + return shm->pages;
> +}
> +
> +/**
> * tee_shm_get_page_offset() - Get shared buffer offset from page start
> * @shm: Shared memory handle
> * @returns page offset of shared buffer
> --
> 2.7.4

2017-09-29 00:23:19

by Yury Norov

[permalink] [raw]
Subject: Re: [PATCH v1 06/14] tee: optee: add page list manipulation functions

On Thu, Sep 28, 2017 at 09:04:03PM +0300, Volodymyr Babchuk wrote:
> From: Volodymyr Babchuk <[email protected]>
>
> These functions will be used to pass information about shared
> buffers to OP-TEE.
>
> Signed-off-by: Volodymyr Babchuk <[email protected]>
> ---
> drivers/tee/optee/call.c | 48 +++++++++++++++++++++++++++++++++++++++
> drivers/tee/optee/optee_private.h | 4 ++++
> 2 files changed, 52 insertions(+)
>
> diff --git a/drivers/tee/optee/call.c b/drivers/tee/optee/call.c
> index f7b7b40..f8e044d 100644
> --- a/drivers/tee/optee/call.c
> +++ b/drivers/tee/optee/call.c
> @@ -11,6 +11,7 @@
> * GNU General Public License for more details.
> *
> */
> +#include <asm/pgtable.h>
> #include <linux/arm-smccc.h>
> #include <linux/device.h>
> #include <linux/err.h>
> @@ -442,3 +443,50 @@ void optee_disable_shm_cache(struct optee *optee)
> }
> optee_cq_wait_final(&optee->call_queue, &w);
> }
> +
> +/**
> + * optee_fill_pages_list() - write list of user pages to given shared
> + * buffer.
> + *
> + * @dst: page-aligned buffer where list of pages will be stored

I'm not much familiar with the subsystem you work on, but I don't
understand why the type of dst is u64*. If it's just a buffer, it
should be void *. Also, if we assuming running it on arm were pointers
are 32-bit, the result of page_to_phys() will be u32, and you will
waste half of your u64 array for storing zeroes; this line:
*dst = page_to_phys(pages[i]);

> + * @pages: array of pages that represents shared buffer
> + * @num_pages: number of entries in @pages
> + *
> + * @dst should be big enough to hold list of user page addresses and
> + * links to the next pages of buffer
> + */
> +void optee_fill_pages_list(u64 *dst, struct page **pages, size_t num_pages)
> +{
> + size_t i;
> +
> + /* TODO: add support for RichOS page sizes that != 4096 */
> + BUILD_BUG_ON(PAGE_SIZE != OPTEE_MSG_NONCONTIG_PAGE_SIZE);

RichOS stands for Linux? Why I am still not a rich OS developer? :)
This is the first occurrence of the term in kernel sources, please
explain it.

Also, I think that it would be more logical to add the dependency on
page size to Kconfig, not here, and move the comment there, so user
will be simply unable to build the whole module.

> + for (i = 0; i < num_pages; i++, dst++) {
> + /* Check if we are going to roll over the page boundary */
> + if (IS_ALIGNED((uintptr_t)(dst + 1),
> + OPTEE_MSG_NONCONTIG_PAGE_SIZE)) {
> + *dst = virt_to_phys(dst + 1);
> + dst++;
> + }

Is my understanding correct that @dst is not a simple array of buffer
page addresses? Instead, it has a complex structure: First 511 records
store buffer page entries, and last one points to the next page of dst.
Is it somehow documented? Also, did you consider to create a header structure
for the buffer page, like memory allocators do? You can place there number
of entries, pointer to the next page, maybe some flags. I think it will be
more transparent, especially if we consider communication protocol between
independent software products.

> + *dst = page_to_phys(pages[i]);
> + }
> +}
> +
> +static size_t get_pages_array_size(size_t num_entries)
> +{
> + /* Number of user pages + number of pages to hold list of user pages */
> + return sizeof(u64) *
> + (num_entries + (sizeof(u64) * num_entries) /
> + OPTEE_MSG_NONCONTIG_PAGE_SIZE);
> +}
> +
> +u64 *optee_allocate_pages_array(size_t num_entries)
> +{
> + return alloc_pages_exact(get_pages_array_size(num_entries), GFP_KERNEL);
> +}
> +
> +void optee_free_pages_array(void *array, size_t num_entries)
> +{
> + free_pages_exact(array, get_pages_array_size(num_entries));
> +}
> +
> diff --git a/drivers/tee/optee/optee_private.h b/drivers/tee/optee/optee_private.h
> index c374cd5..caa3c04 100644
> --- a/drivers/tee/optee/optee_private.h
> +++ b/drivers/tee/optee/optee_private.h
> @@ -165,6 +165,10 @@ int optee_from_msg_param(struct tee_param *params, size_t num_params,
> int optee_to_msg_param(struct optee_msg_param *msg_params, size_t num_params,
> const struct tee_param *params);
>
> +u64 *optee_allocate_pages_array(size_t num_entries);
> +void optee_free_pages_array(void *array, size_t num_entries);
> +void optee_fill_pages_list(u64 *dst, struct page **pages, size_t num_pages);
> +
> /*
> * Small helpers
> */
> --
> 2.7.4

2017-09-29 00:50:45

by Yury Norov

[permalink] [raw]
Subject: Re: [PATCH v1 14/14] tee: shm: inline tee_shm getter functions

On Thu, Sep 28, 2017 at 09:04:11PM +0300, Volodymyr Babchuk wrote:
> From: Volodymyr Babchuk <[email protected]>
>
> Now, when struct tee_shm is defined in public header,
> we can inline small getter functions.

struct tee_shm is moved to public header in first patch of series,
so you can put tee_shm_is_registered() in proper place at once, right?

>
> Signed-off-by: Volodymyr Babchuk <[email protected]>
> ---
> drivers/tee/tee_shm.c | 17 -----------------
> include/linux/tee_drv.h | 10 ++++++++--
> 2 files changed, 8 insertions(+), 19 deletions(-)
>
> diff --git a/drivers/tee/tee_shm.c b/drivers/tee/tee_shm.c
> index 5176c83..453700a 100644
> --- a/drivers/tee/tee_shm.c
> +++ b/drivers/tee/tee_shm.c
> @@ -494,23 +494,6 @@ struct tee_shm *tee_shm_get_from_id(struct tee_context *ctx, int id)
> }
> EXPORT_SYMBOL_GPL(tee_shm_get_from_id);
>
> -bool tee_shm_is_registered(struct tee_shm *shm)
> -{
> - return shm && (shm->flags & TEE_SHM_REGISTER);
> -}
> -EXPORT_SYMBOL_GPL(tee_shm_is_registered);
> -
> -/**
> - * tee_shm_get_id() - Get id of a shared memory object
> - * @shm: Shared memory handle
> - * @returns id
> - */
> -int tee_shm_get_id(struct tee_shm *shm)
> -{
> - return shm->id;
> -}
> -EXPORT_SYMBOL_GPL(tee_shm_get_id);
> -
> /**
> * tee_shm_put() - Decrease reference count on a shared memory handle
> * @shm: Shared memory handle
> diff --git a/include/linux/tee_drv.h b/include/linux/tee_drv.h
> index 6aaef65..2ae0286 100644
> --- a/include/linux/tee_drv.h
> +++ b/include/linux/tee_drv.h
> @@ -429,7 +429,10 @@ static inline size_t tee_shm_get_page_offset(struct tee_shm *shm)
> * @shm: Shared memory handle
> * @returns id
> */
> -int tee_shm_get_id(struct tee_shm *shm);
> +static inline int tee_shm_get_id(struct tee_shm *shm)
> +{
> + return shm->id;
> +}
>
> /**
> * tee_shm_get_from_id() - Find shared memory object and increase reference
> @@ -445,6 +448,9 @@ struct tee_shm *tee_shm_get_from_id(struct tee_context *ctx, int id);
> * @shm: Shared memory handle
> * @returns true if object is registered in TEE
> */
> -bool tee_shm_is_registered(struct tee_shm *shm);
> +static inline bool tee_shm_is_registered(struct tee_shm *shm)
> +{
> + return shm && (shm->flags & TEE_SHM_REGISTER);
> +}
>
> #endif /*__TEE_DRV_H*/
> --
> 2.7.4

2017-09-29 10:17:12

by Volodymyr Babchuk

[permalink] [raw]
Subject: Re: [PATCH v1 04/14] tee: shm: add page accessor functions

Hi Yury,

On 29.09.17 01:14, Yury Norov wrote:
> Hi Volodymyr,
>
> On Thu, Sep 28, 2017 at 09:04:01PM +0300, Volodymyr Babchuk wrote:
>> From: Volodymyr Babchuk <[email protected]>
>>
>> In order to register a shared buffer in TEE, we need accessor
>> function that return list of pages for that buffer.
>>
>> Signed-off-by: Volodymyr Babchuk <[email protected]>
>> ---
>> include/linux/tee_drv.h | 14 ++++++++++++++
>> 1 file changed, 14 insertions(+)
>>
>> diff --git a/include/linux/tee_drv.h b/include/linux/tee_drv.h
>> index 6ba8b76..d773827 100644
>> --- a/include/linux/tee_drv.h
>> +++ b/include/linux/tee_drv.h
>> @@ -394,6 +394,20 @@ static inline size_t tee_shm_get_size(struct tee_shm *shm)
>> }
>>
>> /**
>> + * tee_shm_get_pages() - Get list of pages that hold shared buffer
>> + * @shm: Shared memory handle
>> + * @num_pages: Number of pages will be stored there
>> + * @returns pointer to pages array
>> + */
>> +static inline struct page **tee_shm_get_pages(struct tee_shm *shm,
>> + size_t *num_pages)
>> +{
>> + if (num_pages)
>> + *num_pages = shm->num_pages;
>
> My concern is about this check
>
> The only use of the tee_shm_get_pages() I found is in patch #9:
> + size_t page_num;
> +
> + pages = tee_shm_get_pages(shm, &page_num);
>
> So there's no any valid scenario where you should pass NULL to the
> function. And I don't understand why you do this check.
>
> Even more, if in future there will be an occasion when function will
> be passed with NULL, the error will become hidden by this code.
Yes, I think you are right. I added that check in case someone want
to get only pointer to pages. But this is semantically invalid.
I'll remove that check. Thank you for review.

2017-09-29 10:33:19

by Mark Rutland

[permalink] [raw]
Subject: Re: [PATCH v1 00/14] tee: optee: add dynamic shared memory support

Hi,

On Thu, Sep 28, 2017 at 09:03:57PM +0300, Volodymyr Babchuk wrote:
> From: Volodymyr Babchuk <[email protected]>
>
> This patch series enables dynamic shared memory support in the TEE
> subsystem as a whole and in OP-TEE in particular.
>
> Global Platform TEE specification [1] allows client applications
> to register part of own memory as a shared buffer between
> application and TEE. This allows fast zero-copy communication between
> TEE and REE. But current implementation of TEE in Linux does not support
> this feature.
>
> Also, current implementation of OP-TEE transport uses fixed size
> pre-shared buffer for all communications with OP-TEE OS. This is okay
> in the most use cases. But this prevents use of OP-TEE in virtualized
> environments, because:
> a) We can't share the same buffer between different virtual machines
> b) Physically contiguous memory as seen by VM can be non-contiguous
> in reality (and as seen by OP-TEE OS) due to second stage of
> MMU translation.
> c) Size of this pre-shared buffer is limited.

I'm afraid that I don't follow the arguments for virtualized OP-TEE
usage.

In a virtualised environment, TEE access *must* be mediated via the
hypervisor, which can virtualise the interface, pin pages, etc.

Could you elaborate on how you expect TEE access to work in a
virtualised environment?

Thanks,
Mark.

> So, first part of this patch series adds generic register/unregister
> interface to tee subsystem. Next patches add necessary features
> into OP-TEE driver, so it can use not only static pre-shared buffer,
> but whole RAM to communicate with OP-TEE OS.
>
> [1] https://www.globalplatform.org/specificationsdevice.asp
>
> Jens Wiklander (2):
> tee: flexible shared memory pool creation
> tee: add register user memory
>
> Volodymyr Babchuk (12):
> tee: shm: add accessors for buffer size and page offset
> tee: shm: add page accessor functions
> tee: optee: Update protocol definitions
> tee: optee: add page list manipulation functions
> tee: optee: add shared buffer registration functions
> tee: optee: add registered shared parameters handling
> tee: optee: add registered buffers handling into RPC calls
> tee: optee: store OP-TEE capabilities in private data
> tee: optee: add optee-specific shared pool implementation
> tee: optee: enable dynamic SHM support
> tee: use reference counting for tee_context
> tee: shm: inline tee_shm getter functions
>
> drivers/tee/optee/Makefile | 1 +
> drivers/tee/optee/call.c | 131 +++++++++++++++++++++-
> drivers/tee/optee/core.c | 160 +++++++++++++++++++++------
> drivers/tee/optee/optee_msg.h | 38 ++++++-
> drivers/tee/optee/optee_private.h | 26 ++++-
> drivers/tee/optee/optee_smc.h | 7 ++
> drivers/tee/optee/rpc.c | 72 ++++++++++--
> drivers/tee/optee/shm_pool.c | 75 +++++++++++++
> drivers/tee/optee/shm_pool.h | 23 ++++
> drivers/tee/tee_core.c | 81 ++++++++++++--
> drivers/tee/tee_private.h | 60 +---------
> drivers/tee/tee_shm.c | 226 +++++++++++++++++++++++++++++++-------
> drivers/tee/tee_shm_pool.c | 165 +++++++++++++++++-----------
> include/linux/tee_drv.h | 184 ++++++++++++++++++++++++++++++-
> include/uapi/linux/tee.h | 30 +++++
> 15 files changed, 1058 insertions(+), 221 deletions(-)
> create mode 100644 drivers/tee/optee/shm_pool.c
> create mode 100644 drivers/tee/optee/shm_pool.h
>
> --
> 2.7.4
>
>
> _______________________________________________
> linux-arm-kernel mailing list
> [email protected]
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

2017-09-29 10:34:24

by Volodymyr Babchuk

[permalink] [raw]
Subject: Re: [PATCH v1 06/14] tee: optee: add page list manipulation functions



On 29.09.17 03:23, Yury Norov wrote:
> On Thu, Sep 28, 2017 at 09:04:03PM +0300, Volodymyr Babchuk wrote:
>> From: Volodymyr Babchuk <[email protected]>
>>
>> These functions will be used to pass information about shared
>> buffers to OP-TEE.
>>
>> Signed-off-by: Volodymyr Babchuk <[email protected]>
>> ---
>> drivers/tee/optee/call.c | 48 +++++++++++++++++++++++++++++++++++++++
>> drivers/tee/optee/optee_private.h | 4 ++++
>> 2 files changed, 52 insertions(+)
>>
>> diff --git a/drivers/tee/optee/call.c b/drivers/tee/optee/call.c
>> index f7b7b40..f8e044d 100644
>> --- a/drivers/tee/optee/call.c
>> +++ b/drivers/tee/optee/call.c
>> @@ -11,6 +11,7 @@
>> * GNU General Public License for more details.
>> *
>> */
>> +#include <asm/pgtable.h>
>> #include <linux/arm-smccc.h>
>> #include <linux/device.h>
>> #include <linux/err.h>
>> @@ -442,3 +443,50 @@ void optee_disable_shm_cache(struct optee *optee)
>> }
>> optee_cq_wait_final(&optee->call_queue, &w);
>> }
>> +
>> +/**
>> + * optee_fill_pages_list() - write list of user pages to given shared
>> + * buffer.
>> + *
>> + * @dst: page-aligned buffer where list of pages will be stored
>
> I'm not much familiar with the subsystem you work on, but I don't
> understand why the type of dst is u64*. If it's just a buffer, it
> should be void *. Also, if we assuming running it on arm were pointers
> are 32-bit, the result of page_to_phys() will be u32, and you will
> waste half of your u64 array for storing zeroes; this line:
> *dst = page_to_phys(pages[i]);
Yep. There is defined ABI between OP-TEE OS and OP-TEE clients. That ABI
demands that page addresses should be stored in 64-bit fields even on
32-bit architectures.


>> + * @pages: array of pages that represents shared buffer
>> + * @num_pages: number of entries in @pages
>> + *
>> + * @dst should be big enough to hold list of user page addresses and
>> + * links to the next pages of buffer
>> + */
>> +void optee_fill_pages_list(u64 *dst, struct page **pages, size_t num_pages)
>> +{
>> + size_t i;
>> +
>> + /* TODO: add support for RichOS page sizes that != 4096 */
>> + BUILD_BUG_ON(PAGE_SIZE != OPTEE_MSG_NONCONTIG_PAGE_SIZE);
>
> RichOS stands for Linux? Why I am still not a rich OS developer? :)
I'm asking the same question :) Yes, in terms of TEE, Linux is RichOS
and OP-TEE is TrustedOS.

> This is the first occurrence of the term in kernel sources, please
> explain it.
I'd rather change "RichOS" to "Linux".

> Also, I think that it would be more logical to add the dependency on
> page size to Kconfig, not here, and move the comment there, so user
> will be simply unable to build the whole module.
I event didn't thought of this. Thank you for suggestion. Will do in
this way.

>> + for (i = 0; i < num_pages; i++, dst++) {
>> + /* Check if we are going to roll over the page boundary */
>> + if (IS_ALIGNED((uintptr_t)(dst + 1),
>> + OPTEE_MSG_NONCONTIG_PAGE_SIZE)) {
>> + *dst = virt_to_phys(dst + 1);
>> + dst++;
>> + }
>
> Is my understanding correct that @dst is not a simple array of buffer
> page addresses? Instead, it has a complex structure: First 511 records
> store buffer page entries, and last one points to the next page of dst.
> Is it somehow documented? Also, did you consider to create a header structure
> for the buffer page, like memory allocators do? You can place there number
> of entries, pointer to the next page, maybe some flags. I think it will be
> more transparent, especially if we consider communication protocol between
> independent software products.
This is documented in the previous patch "tee: optee: Update protocol
definitions" (5/14).
I like your idea about header structure. Just to clarify: it should be
structure that covers whole page. Like that described in the previous patch:

+ * struct page_data {
+ * uint64_t
pages_array[OPTEE_MSG_NONCONTIG_PAGE_SIZE/sizeof(uint64_t) - 1];
+ * uint64_t next_page_data;
+ * };

Right?

2017-09-29 10:51:16

by Volodymyr Babchuk

[permalink] [raw]
Subject: Re: [PATCH v1 00/14] tee: optee: add dynamic shared memory support

Hello Mark,

On 29.09.17 13:31, Mark Rutland wrote:
> Hi,
>
> On Thu, Sep 28, 2017 at 09:03:57PM +0300, Volodymyr Babchuk wrote:
>> From: Volodymyr Babchuk <[email protected]>
>>
>> This patch series enables dynamic shared memory support in the TEE
>> subsystem as a whole and in OP-TEE in particular.
>>
>> Global Platform TEE specification [1] allows client applications
>> to register part of own memory as a shared buffer between
>> application and TEE. This allows fast zero-copy communication between
>> TEE and REE. But current implementation of TEE in Linux does not support
>> this feature.
>>
>> Also, current implementation of OP-TEE transport uses fixed size
>> pre-shared buffer for all communications with OP-TEE OS. This is okay
>> in the most use cases. But this prevents use of OP-TEE in virtualized
>> environments, because:
>> a) We can't share the same buffer between different virtual machines
>> b) Physically contiguous memory as seen by VM can be non-contiguous
>> in reality (and as seen by OP-TEE OS) due to second stage of
>> MMU translation.
>> c) Size of this pre-shared buffer is limited.
>
> I'm afraid that I don't follow the arguments for virtualized OP-TEE
> usage.
Yes, this is short summary. This is patches for Linux kernel, so I
thought it is okay to mention only linux part.

> In a virtualised environment, TEE access *must* be mediated via the
> hypervisor, which can virtualise the interface, pin pages, etc.
Absolutely right. I had many discussions with XEN community on this
topic there: [2]
Also I had discussions with OP-TEE guys there: [3]

> Could you elaborate on how you expect TEE access to work in a
> virtualised environment?
Hypervisor will trap all SMCs. SMCs that belong to TEE OS and TEE apps
will be handled by corresponding TEE mediator in a hypervsior.

TEE mediator will:

1) check if this guest/domain have right to work with TEE at all.
2) Mangle pointers in command buffer (translate IPA to PA)
3) Add VMID to request
3) Forward mangled request to TEE
4) Mangle pointers in response buffer (if any)
5) Return response back to guest.

Besides this, TEE mediator will inform TEE about guest creation and
destruction, so TEE can track opened sessions, shared buffers and such.

[2] http://xen.markmail.org/thread/6pwpa2j6sbqkxgge
[3] https://github.com/OP-TEE/optee_os/issues/1019

2017-09-29 10:54:41

by Mark Rutland

[permalink] [raw]
Subject: Re: [PATCH v1 02/14] tee: add register user memory

On Thu, Sep 28, 2017 at 09:03:59PM +0300, Volodymyr Babchuk wrote:
> +static int
> +tee_ioctl_shm_register(struct tee_context *ctx,
> + struct tee_ioctl_shm_register_data __user *udata)
> +{
> + long ret;
> + struct tee_ioctl_shm_register_data data;
> + struct tee_shm *shm;
> +
> + if (copy_from_user(&data, udata, sizeof(data)))
> + return -EFAULT;
> +
> + /* Currently no input flags are supported */
> + if (data.flags)
> + return -EINVAL;
> +
> + shm = tee_shm_register(ctx, data.addr, data.length,
> + TEE_SHM_DMA_BUF | TEE_SHM_USER_MAPPED);
> + if (IS_ERR(shm))
> + return PTR_ERR(shm);
> +
> + data.id = shm->id;
> + data.flags = shm->flags;
> + data.length = shm->size;
> +
> + if (copy_to_user(udata, &data, sizeof(data)))
> + ret = -EFAULT;
> + else
> + ret = tee_shm_get_fd(shm);

Why do you need both the fd and an id? That seems redundant.

[...]

> +struct tee_shm *tee_shm_register(struct tee_context *ctx, unsigned long addr,
> + size_t length, u32 flags)
> +{
> + struct tee_device *teedev = ctx->teedev;
> + const u32 req_flags = TEE_SHM_DMA_BUF | TEE_SHM_USER_MAPPED;
> + struct tee_shm *shm;
> + void *ret;
> + int rc;
> + int num_pages;
> + unsigned long start;
> +
> + if (flags != req_flags) {
> + dev_err(teedev->dev.parent, "invliad shm flags %#x", flags);
> + return ERR_PTR(-EINVAL);
> + }
> +
> + if (!tee_device_get(teedev))
> + return ERR_PTR(-EINVAL);
> +
> + if (!teedev->desc->ops->shm_register ||
> + !teedev->desc->ops->shm_unregister) {
> + dev_err(teedev->dev.parent,
> + "register shared memory unspported by device");

I don't think this should be a dev_err. The user requested something
that the device did not support, but that's not a device-side error.

A user may legitmiately do this to probe whether the TEE supports
registering memory.

> + tee_device_put(teedev);
> + return ERR_PTR(-EINVAL);

Perhaps EOPNOTSUPP?

> + }
> +
> + shm = kzalloc(sizeof(*shm), GFP_KERNEL);
> + if (!shm) {
> + ret = ERR_PTR(-ENOMEM);
> + goto err;
> + }
> +
> + shm->flags = flags | TEE_SHM_REGISTER;
> + shm->teedev = teedev;
> + shm->ctx = ctx;
> + shm->id = -1;
> + start = rounddown(addr, PAGE_SIZE);
> + shm->offset = addr - start;
> + shm->size = length;
> + num_pages = (roundup(addr + length, PAGE_SIZE) - start) / PAGE_SIZE;

Why not mandate that the user passes a buffer which has a start and end
aligned to PAGE_SIZE?

Otherwise, the buffer is size is silently upgraded without the user's
knowledge, which seems likely to result in bugs.

> + shm->pages = kcalloc(num_pages, sizeof(struct page), GFP_KERNEL);

I think you mean sizeof(struct page *) here.

Generally, for:

lhs = some_alloc(sizeof(x))

... it's preferred that x is *lhs, so as to keep the types in sync. e.g.

shm->pages = kcalloc(num_pages, sizeof(*shm->pages), GFP_KERNEL);

> + if (!shm->pages) {
> + ret = ERR_PTR(-ENOMEM);
> + goto err;
> + }
> +
> + rc = get_user_pages_fast(start, num_pages, 1, shm->pages);
> + if (rc > 0)
> + shm->num_pages = rc;
> + if (rc != num_pages) {
> + if (rc > 0)
> + rc = -ENOMEM;
> + ret = ERR_PTR(rc);
> + goto err;
> + }
> +
> + mutex_lock(&teedev->mutex);
> + shm->id = idr_alloc(&teedev->idr, shm, 1, 0, GFP_KERNEL);
> + mutex_unlock(&teedev->mutex);

AFAICT, idr_alloc() can fail, so I beleive you're missing a sanity check
on the return value here.

THanks,
Mark.

2017-09-29 13:02:19

by Mark Rutland

[permalink] [raw]
Subject: Re: [PATCH v1 06/14] tee: optee: add page list manipulation functions

On Thu, Sep 28, 2017 at 09:04:03PM +0300, Volodymyr Babchuk wrote:
> +/**
> + * optee_fill_pages_list() - write list of user pages to given shared
> + * buffer.
> + *
> + * @dst: page-aligned buffer where list of pages will be stored
> + * @pages: array of pages that represents shared buffer
> + * @num_pages: number of entries in @pages
> + *
> + * @dst should be big enough to hold list of user page addresses and
> + * links to the next pages of buffer
> + */
> +void optee_fill_pages_list(u64 *dst, struct page **pages, size_t num_pages)
> +{
> + size_t i;

Why size_t? It's unusual for an array index.

> + /* TODO: add support for RichOS page sizes that != 4096 */
> + BUILD_BUG_ON(PAGE_SIZE != OPTEE_MSG_NONCONTIG_PAGE_SIZE);

This must be fixed before this can be considered for merging.

A large number of people build arm64 kernels with 64K pages, and this
will need to see some testing.

> + for (i = 0; i < num_pages; i++, dst++) {
> + /* Check if we are going to roll over the page boundary */
> + if (IS_ALIGNED((uintptr_t)(dst + 1),
> + OPTEE_MSG_NONCONTIG_PAGE_SIZE)) {
> + *dst = virt_to_phys(dst + 1);
> + dst++;
> + }
> + *dst = page_to_phys(pages[i]);

... so this pagelist management will need to be reworked.

> + }
> +}
> +
> +static size_t get_pages_array_size(size_t num_entries)
> +{
> + /* Number of user pages + number of pages to hold list of user pages */
> + return sizeof(u64) *
> + (num_entries + (sizeof(u64) * num_entries) /
> + OPTEE_MSG_NONCONTIG_PAGE_SIZE);
> +}

I don't think this is correct.

For P 4096-byte pages, we can have 511 * P (8-byte) page entries, and P
(8-byte) next entries.

So if we need to list 1023 page entries, we need 3 (4096-byte) pages.
The first page holds 511 entries, the second holds 511 entries, and the
third holds 1 entry.

However, the above calculates that we need 2 (4096-byte) pages, as it
calculates that in bytes we need:

8 * (1023 + (8 * 1023) / 4096)
8 * (1023 + (8184) / 4096)
8 * (1023 + 1)
8 * 1024
8192

... or 2 (4096-byte) pages.


I think it would be clearer to write this over a number of steps, e.g.

/*
* The final entry in each pagelist page is a pointer to the next
* pagelist page.
*/
#define PAGELIST_ENTRIES_PER_PAGE \
((OPTEE_MSG_NONCONTIG_PAGE_SIZE / sizeof(u64)) - 1)

static size_t get_pages_array_size(size_t num_entries)
{
int pages = DIV_ROUND_UP(num_entries, PAGELIST_ENTRIES_PER_PAGE);

return pages * OPTEE_MSG_NONCONTIG_PAGE_SIZE;
}

> +
> +u64 *optee_allocate_pages_array(size_t num_entries)
> +{
> + return alloc_pages_exact(get_pages_array_size(num_entries), GFP_KERNEL);
> +}
> +
> +void optee_free_pages_array(void *array, size_t num_entries)
> +{
> + free_pages_exact(array, get_pages_array_size(num_entries));
> +}
> +
> diff --git a/drivers/tee/optee/optee_private.h b/drivers/tee/optee/optee_private.h
> index c374cd5..caa3c04 100644
> --- a/drivers/tee/optee/optee_private.h
> +++ b/drivers/tee/optee/optee_private.h
> @@ -165,6 +165,10 @@ int optee_from_msg_param(struct tee_param *params, size_t num_params,
> int optee_to_msg_param(struct optee_msg_param *msg_params, size_t num_params,
> const struct tee_param *params);
>
> +u64 *optee_allocate_pages_array(size_t num_entries);
> +void optee_free_pages_array(void *array, size_t num_entries);
> +void optee_fill_pages_list(u64 *dst, struct page **pages, size_t num_pages);

Any reason for the array/list naming disparity? IIUC, these are the same
structure.

Thanks,
Mark.

2017-09-29 13:08:13

by Mark Rutland

[permalink] [raw]
Subject: Re: [PATCH v1 07/14] tee: optee: add shared buffer registration functions

On Thu, Sep 28, 2017 at 09:04:04PM +0300, Volodymyr Babchuk wrote:
> From: Volodymyr Babchuk <[email protected]>
>
> This change adds ops for shm_(un)register functions in tee interface.
> Client application can use these functions to (un)register an own shared
> buffer in OP-TEE address space. This allows zero copy data sharing between
> Normal and Secure Worlds.
>
> Please note that while those functions were added to optee code,
> it does not report to userspace that those functions are available.
> OP-TEE code does not set TEE_GEN_CAP_REG_MEM flag. This flag will be
> enabled only after all other features of dynamic shared memory will be
> implemented in subsequent patches.

While it's not adveritsed to the user, AFAICT the user could still
invoke these via ioctls, right?

Is there a problem if the user were to do so, or is it simply not useful
without the other features?

[...]

> +int optee_shm_register(struct tee_context *ctx, struct tee_shm *shm,
> + struct page **pages, size_t num_pages)
> +{

> + pages_array = optee_allocate_pages_array(num_pages);
> + if (!pages_array)
> + return -ENOMEM;

> + msg_arg->params->u.tmem.buf_ptr = virt_to_phys(pages_array) |
> + tee_shm_get_page_offset(shm);

This doesn't look right. Why is the shm page offset being orred-in to
the pages_array physical address? They're completely separate objects.

Thanks,
Mark.

2017-09-29 15:19:16

by Volodymyr Babchuk

[permalink] [raw]
Subject: Re: [PATCH v1 02/14] tee: add register user memory



On 29.09.17 13:53, Mark Rutland wrote:
> On Thu, Sep 28, 2017 at 09:03:59PM +0300, Volodymyr Babchuk wrote:
>> +static int
>> +tee_ioctl_shm_register(struct tee_context *ctx,
>> + struct tee_ioctl_shm_register_data __user *udata)
>> +{
>> + long ret;
>> + struct tee_ioctl_shm_register_data data;
>> + struct tee_shm *shm;
>> +
>> + if (copy_from_user(&data, udata, sizeof(data)))
>> + return -EFAULT;
>> +
>> + /* Currently no input flags are supported */
>> + if (data.flags)
>> + return -EINVAL;
>> +
>> + shm = tee_shm_register(ctx, data.addr, data.length,
>> + TEE_SHM_DMA_BUF | TEE_SHM_USER_MAPPED);
>> + if (IS_ERR(shm))
>> + return PTR_ERR(shm);
>> +
>> + data.id = shm->id;
>> + data.flags = shm->flags;
>> + data.length = shm->size;
>> +
>> + if (copy_to_user(udata, &data, sizeof(data)))
>> + ret = -EFAULT;
>> + else
>> + ret = tee_shm_get_fd(shm);
>
> Why do you need both the fd and an id? That seems redundant.
>
> [...]
Not exactly. This approach is used for all shared memory object types.
fd is used to control life cycle. id identifies the buffer.
There are at least three types of shared memory objects available:

- Allocated memory is already present in tee subsystem
- My patch series add registered shared memory
- There are patches in linaro branch that add support for
dma_buf shared memory objects

It it easier to identify them all with id, that with fd (which can be
tricky in case of dma_buf objects, by the way).

>> +struct tee_shm *tee_shm_register(struct tee_context *ctx, unsigned long addr,
>> + size_t length, u32 flags)
>> +{
>> + struct tee_device *teedev = ctx->teedev;
>> + const u32 req_flags = TEE_SHM_DMA_BUF | TEE_SHM_USER_MAPPED;
>> + struct tee_shm *shm;
>> + void *ret;
>> + int rc;
>> + int num_pages;
>> + unsigned long start;
>> +
>> + if (flags != req_flags) {
>> + dev_err(teedev->dev.parent, "invliad shm flags %#x", flags);
>> + return ERR_PTR(-EINVAL);
>> + }
>> +
>> + if (!tee_device_get(teedev))
>> + return ERR_PTR(-EINVAL);
>> +
>> + if (!teedev->desc->ops->shm_register ||
>> + !teedev->desc->ops->shm_unregister) {
>> + dev_err(teedev->dev.parent,
>> + "register shared memory unspported by device");
>
> I don't think this should be a dev_err. The user requested something
> that the device did not support, but that's not a device-side error.
>
> A user may legitmiately do this to probe whether the TEE supports
> registering memory.
Agree. I'll remove dev_err() invocation.

>> + tee_device_put(teedev);
>> + return ERR_PTR(-EINVAL);
>
> Perhaps EOPNOTSUPP?
Sure. Thanks.

>> + }
>> +
>> + shm = kzalloc(sizeof(*shm), GFP_KERNEL);
>> + if (!shm) {
>> + ret = ERR_PTR(-ENOMEM);
>> + goto err;
>> + }
>> +
>> + shm->flags = flags | TEE_SHM_REGISTER;
>> + shm->teedev = teedev;
>> + shm->ctx = ctx;
>> + shm->id = -1;
>> + start = rounddown(addr, PAGE_SIZE);
>> + shm->offset = addr - start;
>> + shm->size = length;
>> + num_pages = (roundup(addr + length, PAGE_SIZE) - start) / PAGE_SIZE;
>
> Why not mandate that the user passes a buffer which has a start and end
> aligned to PAGE_SIZE?
>
> Otherwise, the buffer is size is silently upgraded without the user's
> knowledge, which seems likely to result in bugs.
Because according to GlobalPlatform TEE specification, client can
register any portion of own memory. I agree that it is error-prone to
allow TEE (and TA) to see not shared parts of client memory. But in
terms of GlobalPlatform, Linux and its userspace considered as
non-secure anyways. While TEE and TAs are considered trusted.
Misbehaved TEE or TA anyways can do many bad things, so corruption of
userspace memory does not look so bad.

>> + shm->pages = kcalloc(num_pages, sizeof(struct page), GFP_KERNEL);
>
> I think you mean sizeof(struct page *) here.
Ooops. Good catch. Thank you

> Generally, for:
>
> lhs = some_alloc(sizeof(x))
>
> ... it's preferred that x is *lhs, so as to keep the types in sync. e.g.
>
> shm->pages = kcalloc(num_pages, sizeof(*shm->pages), GFP_KERNEL);
Yes, will do in this way. Thank you.


>> + if (!shm->pages) {
>> + ret = ERR_PTR(-ENOMEM);
>> + goto err;
>> + }
>> +
>> + rc = get_user_pages_fast(start, num_pages, 1, shm->pages);
>> + if (rc > 0)
>> + shm->num_pages = rc;
>> + if (rc != num_pages) {
>> + if (rc > 0)
>> + rc = -ENOMEM;
>> + ret = ERR_PTR(rc);
>> + goto err;
>> + }
>> +
>> + mutex_lock(&teedev->mutex);
>> + shm->id = idr_alloc(&teedev->idr, shm, 1, 0, GFP_KERNEL);
>> + mutex_unlock(&teedev->mutex);
>
> AFAICT, idr_alloc() can fail, so I beleive you're missing a sanity check
> on the return value here.
You are right. Will fix.

Thank you for the review.

2017-09-29 15:37:08

by Volodymyr Babchuk

[permalink] [raw]
Subject: Re: [PATCH v1 07/14] tee: optee: add shared buffer registration functions



On 29.09.17 16:06, Mark Rutland wrote:
> On Thu, Sep 28, 2017 at 09:04:04PM +0300, Volodymyr Babchuk wrote:
>> From: Volodymyr Babchuk <[email protected]>
>>
>> This change adds ops for shm_(un)register functions in tee interface.
>> Client application can use these functions to (un)register an own shared
>> buffer in OP-TEE address space. This allows zero copy data sharing between
>> Normal and Secure Worlds.
>>
>> Please note that while those functions were added to optee code,
>> it does not report to userspace that those functions are available.
>> OP-TEE code does not set TEE_GEN_CAP_REG_MEM flag. This flag will be
>> enabled only after all other features of dynamic shared memory will be
>> implemented in subsequent patches.
>
> While it's not adveritsed to the user, AFAICT the user could still
> invoke these via ioctls, right?

> Is there a problem if the user were to do so, or is it simply not useful
> without the other features?
Yes, user can invoke this via ioctl. And this buffer will be
registeredin OP-TEE. But user will not be able to use this registered
buffer, because optee driver does not know how to handle references to
registered buffers.

This is a complex feature and I tried to split it into different commits
to ease up review.

Probably, I can remove
+ .shm_register = optee_shm_register,
+ .shm_unregister = optee_shm_unregister,

from this patch, so user will be not able to call this functions at all.
How do you thing? Should I do this?

>
>> +int optee_shm_register(struct tee_context *ctx, struct tee_shm *shm,
>> + struct page **pages, size_t num_pages)
>> +{
>
>> + pages_array = optee_allocate_pages_array(num_pages);
>> + if (!pages_array)
>> + return -ENOMEM;
>
>> + msg_arg->params->u.tmem.buf_ptr = virt_to_phys(pages_array) |
>> + tee_shm_get_page_offset(shm);
>
> This doesn't look right. Why is the shm page offset being orred-in to
> the pages_array physical address? They're completely separate objects.
They present the same registered shared buffer. You have a list of pages
and offset from the first page. Strictly speaking this is not buf_ptr
anymore, but it is named so...

This is a part of OP-TEE ABI. We considered different approaches at [1].
This fits into general ABI design.

[1] https://github.com/OP-TEE/optee_os/pull/1232#issuecomment-301851514

2017-09-29 16:23:35

by Yury Norov

[permalink] [raw]
Subject: Re: [PATCH v1 06/14] tee: optee: add page list manipulation functions

On Fri, Sep 29, 2017 at 01:34:13PM +0300, Volodymyr Babchuk wrote:
>
>
> On 29.09.17 03:23, Yury Norov wrote:
> > On Thu, Sep 28, 2017 at 09:04:03PM +0300, Volodymyr Babchuk wrote:
> > > From: Volodymyr Babchuk <[email protected]>
> > >
> > > These functions will be used to pass information about shared
> > > buffers to OP-TEE.
> > >
> > > Signed-off-by: Volodymyr Babchuk <[email protected]>
> > > ---
> > > drivers/tee/optee/call.c | 48 +++++++++++++++++++++++++++++++++++++++
> > > drivers/tee/optee/optee_private.h | 4 ++++
> > > 2 files changed, 52 insertions(+)
> > >
> > > diff --git a/drivers/tee/optee/call.c b/drivers/tee/optee/call.c
> > > index f7b7b40..f8e044d 100644
> > > --- a/drivers/tee/optee/call.c
> > > +++ b/drivers/tee/optee/call.c
> > > @@ -11,6 +11,7 @@
> > > * GNU General Public License for more details.
> > > *
> > > */
> > > +#include <asm/pgtable.h>
> > > #include <linux/arm-smccc.h>
> > > #include <linux/device.h>
> > > #include <linux/err.h>
> > > @@ -442,3 +443,50 @@ void optee_disable_shm_cache(struct optee *optee)
> > > }
> > > optee_cq_wait_final(&optee->call_queue, &w);
> > > }
> > > +
> > > +/**
> > > + * optee_fill_pages_list() - write list of user pages to given shared
> > > + * buffer.
> > > + *
> > > + * @dst: page-aligned buffer where list of pages will be stored
> >
> > I'm not much familiar with the subsystem you work on, but I don't
> > understand why the type of dst is u64*. If it's just a buffer, it
> > should be void *. Also, if we assuming running it on arm were pointers
> > are 32-bit, the result of page_to_phys() will be u32, and you will
> > waste half of your u64 array for storing zeroes; this line:
> > *dst = page_to_phys(pages[i]);
> Yep. There is defined ABI between OP-TEE OS and OP-TEE clients. That ABI
> demands that page addresses should be stored in 64-bit fields even on 32-bit
> architectures.
>
>
> > > + * @pages: array of pages that represents shared buffer
> > > + * @num_pages: number of entries in @pages
> > > + *
> > > + * @dst should be big enough to hold list of user page addresses and
> > > + * links to the next pages of buffer
> > > + */
> > > +void optee_fill_pages_list(u64 *dst, struct page **pages, size_t num_pages)
> > > +{
> > > + size_t i;
> > > +
> > > + /* TODO: add support for RichOS page sizes that != 4096 */
> > > + BUILD_BUG_ON(PAGE_SIZE != OPTEE_MSG_NONCONTIG_PAGE_SIZE);
> >
> > RichOS stands for Linux? Why I am still not a rich OS developer? :)
> I'm asking the same question :) Yes, in terms of TEE, Linux is RichOS
> and OP-TEE is TrustedOS.
>
> > This is the first occurrence of the term in kernel sources, please
> > explain it.
> I'd rather change "RichOS" to "Linux".
>
> > Also, I think that it would be more logical to add the dependency on
> > page size to Kconfig, not here, and move the comment there, so user
> > will be simply unable to build the whole module.
> I event didn't thought of this. Thank you for suggestion. Will do in this
> way.
>
> > > + for (i = 0; i < num_pages; i++, dst++) {
> > > + /* Check if we are going to roll over the page boundary */
> > > + if (IS_ALIGNED((uintptr_t)(dst + 1),
> > > + OPTEE_MSG_NONCONTIG_PAGE_SIZE)) {
> > > + *dst = virt_to_phys(dst + 1);
> > > + dst++;
> > > + }
> >
> > Is my understanding correct that @dst is not a simple array of buffer
> > page addresses? Instead, it has a complex structure: First 511 records
> > store buffer page entries, and last one points to the next page of dst.
> > Is it somehow documented? Also, did you consider to create a header structure
> > for the buffer page, like memory allocators do? You can place there number
> > of entries, pointer to the next page, maybe some flags. I think it will be
> > more transparent, especially if we consider communication protocol between
> > independent software products.
> This is documented in the previous patch "tee: optee: Update protocol
> definitions" (5/14).

Ah, OK.

> I like your idea about header structure. Just to clarify: it should be
> structure that covers whole page. Like that described in the previous patch:
>
> + * struct page_data {
> + * uint64_t pages_array[OPTEE_MSG_NONCONTIG_PAGE_SIZE/sizeof(uint64_t) -
> 1];
> + * uint64_t next_page_data;
> + * };
>
> Right?

It's OK, if there's the requirement to allocate the whole page for
shmem pagerefs array. If not, the proposed approach means that you'll
waste the whole page to store shared memory descriptors, even if
shared memory is as small as one page, and so a single u64 is needed
to describe it. I think it makes sense for compile-time declared
shmems.

Yury

2017-10-13 19:41:13

by Volodymyr Babchuk

[permalink] [raw]
Subject: Re: [PATCH v1 00/14] tee: optee: add dynamic shared memory support

Hello all,

I'm terribly sorry. This is v2 patches, but I forgot to change subject.
I think I'll leave this as, because I don't want to spam mailing lists
with this series again.

Or should I resend exactly this patches with right subject?

From 1581172105597797729@xxx Fri Oct 13 19:33:46 +0000 2017
X-GM-THRID: 1579807611018198018
X-Gmail-Labels: Inbox,Category Forums

2017-10-13 19:33:47

by Volodymyr Babchuk

[permalink] [raw]
Subject: [PATCH v1 00/14] tee: optee: add dynamic shared memory support

From: Volodymyr Babchuk <[email protected]>

Changes from v1:

* Added support for 16K and 64K pages.
* Elaborated commit messages
* More minor changes are described in corresponding patches.

While this patches are necessary for use OP-TEE in virualized environment,
they are not sufficient. Stuart Yoder was right, when proposed to make
static SHM optional. But that would be a breaking change, because this
will render unusable older client libraries and supplicants.

Also, as Mark Rutland pointed, OP-TEE virtualization will need support
from hypervisor side.

Anyways, those patches allowed me to call OP-TEE from XEN's Dom0 without
remapping static SHM region into Dom0 userspace.

Also, this patches allow me to share quite big buffers (I tried to
use three 2M buffers at a time). This is imposible with static SHM,
because static SHM on most platforms is only 2M.

Thanks to Yury Norov <[email protected]> and
Mark Rutland <[email protected]> for reviewing this patch series.

---
v1 cover letter:
This patch series enables dynamic shared memory support in the TEE
subsystem as a whole and in OP-TEE in particular.

Global Platform TEE specification [1] allows client applications
to register part of own memory as a shared buffer between
application and TEE. This allows fast zero-copy communication between
TEE and REE. But current implementation of TEE in Linux does not support
this feature.

Also, current implementation of OP-TEE transport uses fixed size
pre-shared buffer for all communications with OP-TEE OS. This is okay
in the most use cases. But this prevents use of OP-TEE in virtualized
environments, because:
a) We can't share the same buffer between different virtual machines
b) Physically contiguous memory as seen by VM can be non-contiguous
in reality (and as seen by OP-TEE OS) due to second stage of
MMU translation.
c) Size of this pre-shared buffer is limited.

So, first part of this patch series adds generic register/unregister
interface to tee subsystem. Next patches add necessary features
into OP-TEE driver, so it can use not only static pre-shared buffer,
but whole RAM to communicate with OP-TEE OS.

[1] https://www.globalplatform.org/specificationsdevice.asp

Jens Wiklander (2):
tee: flexible shared memory pool creation
tee: add register user memory

Volodymyr Babchuk (12):
tee: shm: add accessors for buffer size and page offset
tee: shm: add page accessor functions
tee: optee: Update protocol definitions
tee: optee: add page list manipulation functions
tee: optee: add shared buffer registration functions
tee: optee: add registered shared parameters handling
tee: optee: add registered buffers handling into RPC calls
tee: optee: store OP-TEE capabilities in private data
tee: optee: add optee-specific shared pool implementation
tee: optee: enable dynamic SHM support
tee: use reference counting for tee_context
tee: shm: inline tee_shm_get_id()

drivers/tee/optee/Makefile | 1 +
drivers/tee/optee/call.c | 179 +++++++++++++++++++++++++++++-
drivers/tee/optee/core.c | 152 +++++++++++++++++++------
drivers/tee/optee/optee_msg.h | 38 ++++++-
drivers/tee/optee/optee_private.h | 27 ++++-
drivers/tee/optee/optee_smc.h | 7 ++
drivers/tee/optee/rpc.c | 77 +++++++++++--
drivers/tee/optee/shm_pool.c | 75 +++++++++++++
drivers/tee/optee/shm_pool.h | 23 ++++
drivers/tee/tee_core.c | 81 ++++++++++++--
drivers/tee/tee_private.h | 60 +---------
drivers/tee/tee_shm.c | 227 +++++++++++++++++++++++++++++++-------
drivers/tee/tee_shm_pool.c | 165 ++++++++++++++++-----------
include/linux/tee_drv.h | 183 +++++++++++++++++++++++++++++-
include/uapi/linux/tee.h | 30 +++++
15 files changed, 1104 insertions(+), 221 deletions(-)
create mode 100644 drivers/tee/optee/shm_pool.c
create mode 100644 drivers/tee/optee/shm_pool.h

--
2.7.4


From 1580348591892252464@xxx Wed Oct 04 17:24:23 +0000 2017
X-GM-THRID: 1579807611018198018
X-Gmail-Labels: Inbox,Category Forums

2017-10-03 16:06:06

by Stuart Yoder

[permalink] [raw]
Subject: Re: [Tee-dev] [PATCH v1 00/14] tee: optee: add dynamic shared memory support



On 9/28/17 1:03 PM, Volodymyr Babchuk wrote:
> From: Volodymyr Babchuk <[email protected]>
>
> This patch series enables dynamic shared memory support in the TEE
> subsystem as a whole and in OP-TEE in particular.
>
> Global Platform TEE specification [1] allows client applications
> to register part of own memory as a shared buffer between
> application and TEE. This allows fast zero-copy communication between
> TEE and REE. But current implementation of TEE in Linux does not support
> this feature.
>
> Also, current implementation of OP-TEE transport uses fixed size
> pre-shared buffer for all communications with OP-TEE OS. This is okay
> in the most use cases. But this prevents use of OP-TEE in virtualized
> environments, because:
> a) We can't share the same buffer between different virtual machines
> b) Physically contiguous memory as seen by VM can be non-contiguous
> in reality (and as seen by OP-TEE OS) due to second stage of
> MMU translation.
> c) Size of this pre-shared buffer is limited.
>
> So, first part of this patch series adds generic register/unregister
> interface to tee subsystem. Next patches add necessary features
> into OP-TEE driver, so it can use not only static pre-shared buffer,
> but whole RAM to communicate with OP-TEE OS.

As is, the patch series enables dynamic shared memory, but keeps the
assumption that there must static shared memory as well. In the case
of virtual machines this isn't what we want (at least that's the way
I see KVM working).

The series substantially re-works optee_config_shm_memremap() and it
seems like it would not be that difficult to remove the assumptions that
there _must_ be static shared memory available.

...I have some more specific comments on patch 12/14.

Thanks,
Stuart

From 1579870916511794946@xxx Fri Sep 29 10:51:56 +0000 2017
X-GM-THRID: 1579807611018198018
X-Gmail-Labels: Inbox,Category Forums

2017-10-13 19:36:49

by Volodymyr Babchuk

[permalink] [raw]
Subject: [PATCH v1 10/14] tee: optee: store OP-TEE capabilities in private data

From: Volodymyr Babchuk <[email protected]>

Those capabilities will be used in subsequent patches.

Signed-off-by: Volodymyr Babchuk <[email protected]>
---
drivers/tee/optee/core.c | 1 +
drivers/tee/optee/optee_private.h | 3 +++
2 files changed, 4 insertions(+)

diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c
index d22433c..dbe5a61 100644
--- a/drivers/tee/optee/core.c
+++ b/drivers/tee/optee/core.c
@@ -545,6 +545,7 @@ static struct optee *optee_probe(struct device_node *np)
}

optee->invoke_fn = invoke_fn;
+ optee->sec_caps = sec_caps;

teedev = tee_device_alloc(&optee_desc, NULL, pool, optee);
if (IS_ERR(teedev)) {
diff --git a/drivers/tee/optee/optee_private.h b/drivers/tee/optee/optee_private.h
index 61a0052..de7962e 100644
--- a/drivers/tee/optee/optee_private.h
+++ b/drivers/tee/optee/optee_private.h
@@ -96,6 +96,8 @@ struct optee_supp {
* @supp: supplicant synchronization struct for RPC to supplicant
* @pool: shared memory pool
* @memremaped_shm virtual address of memory in shared memory pool
+ * @sec_caps: secure world capabilities defined by
+ * OPTEE_SMC_SEC_CAP_* in optee_smc.h
*/
struct optee {
struct tee_device *supp_teedev;
@@ -106,6 +108,7 @@ struct optee {
struct optee_supp supp;
struct tee_shm_pool *pool;
void *memremaped_shm;
+ u32 sec_caps;
};

struct optee_session {
--
2.7.4


From 1579807684427921904@xxx Thu Sep 28 18:06:53 +0000 2017
X-GM-THRID: 1579807684427921904
X-Gmail-Labels: Inbox,Category Forums

2017-10-13 19:36:14

by Volodymyr Babchuk

[permalink] [raw]
Subject: [PATCH v1 14/14] tee: shm: inline tee_shm_get_id()

From: Volodymyr Babchuk <[email protected]>

Now, when struct tee_shm is defined in public header,
we can inline small getter functions like this one.

Signed-off-by: Volodymyr Babchuk <[email protected]>
---

* Previously this patch moved tee_shm_is_registered() also,
but now it is declared as inline in the same patch that
adds it. So now this change moves only tee_shm_get_id().
Commit message was changed accordingly.

---
drivers/tee/tee_shm.c | 11 -----------
include/linux/tee_drv.h | 5 ++++-
2 files changed, 4 insertions(+), 12 deletions(-)

diff --git a/drivers/tee/tee_shm.c b/drivers/tee/tee_shm.c
index b48f83d..5f5f771 100644
--- a/drivers/tee/tee_shm.c
+++ b/drivers/tee/tee_shm.c
@@ -496,17 +496,6 @@ struct tee_shm *tee_shm_get_from_id(struct tee_context *ctx, int id)
EXPORT_SYMBOL_GPL(tee_shm_get_from_id);

/**
- * tee_shm_get_id() - Get id of a shared memory object
- * @shm: Shared memory handle
- * @returns id
- */
-int tee_shm_get_id(struct tee_shm *shm)
-{
- return shm->id;
-}
-EXPORT_SYMBOL_GPL(tee_shm_get_id);
-
-/**
* tee_shm_put() - Decrease reference count on a shared memory handle
* @shm: Shared memory handle
*/
diff --git a/include/linux/tee_drv.h b/include/linux/tee_drv.h
index 9cd4e5f..c36ecea 100644
--- a/include/linux/tee_drv.h
+++ b/include/linux/tee_drv.h
@@ -428,7 +428,10 @@ static inline size_t tee_shm_get_page_offset(struct tee_shm *shm)
* @shm: Shared memory handle
* @returns id
*/
-int tee_shm_get_id(struct tee_shm *shm);
+static inline int tee_shm_get_id(struct tee_shm *shm)
+{
+ return shm->id;
+}

/**
* tee_shm_get_from_id() - Find shared memory object and increase reference
--
2.7.4


From 1585726493809886867@xxx Sun Dec 03 02:03:49 +0000 2017
X-GM-THRID: 1585726493809886867
X-Gmail-Labels: Inbox,Category Forums,HistoricalUnread

2017-10-13 19:33:55

by Volodymyr Babchuk

[permalink] [raw]
Subject: [PATCH v1 01/14] tee: flexible shared memory pool creation

From: Jens Wiklander <[email protected]>

Makes creation of shm pools more flexible by adding new more primitive
functions to allocate a shm pool. This makes it easier to add driver
specific shm pool management.

Signed-off-by: Jens Wiklander <[email protected]>
Signed-off-by: Volodymyr Babchuk <[email protected]>
---
drivers/tee/tee_private.h | 57 +---------------
drivers/tee/tee_shm.c | 8 +--
drivers/tee/tee_shm_pool.c | 165 ++++++++++++++++++++++++++++-----------------
include/linux/tee_drv.h | 91 +++++++++++++++++++++++++
4 files changed, 199 insertions(+), 122 deletions(-)

diff --git a/drivers/tee/tee_private.h b/drivers/tee/tee_private.h
index 21cb6be..2bc2b5a 100644
--- a/drivers/tee/tee_private.h
+++ b/drivers/tee/tee_private.h
@@ -21,68 +21,15 @@
#include <linux/mutex.h>
#include <linux/types.h>

-struct tee_device;
-
-/**
- * struct tee_shm - shared memory object
- * @teedev: device used to allocate the object
- * @ctx: context using the object, if NULL the context is gone
- * @link link element
- * @paddr: physical address of the shared memory
- * @kaddr: virtual address of the shared memory
- * @size: size of shared memory
- * @dmabuf: dmabuf used to for exporting to user space
- * @flags: defined by TEE_SHM_* in tee_drv.h
- * @id: unique id of a shared memory object on this device
- */
-struct tee_shm {
- struct tee_device *teedev;
- struct tee_context *ctx;
- struct list_head link;
- phys_addr_t paddr;
- void *kaddr;
- size_t size;
- struct dma_buf *dmabuf;
- u32 flags;
- int id;
-};
-
-struct tee_shm_pool_mgr;
-
-/**
- * struct tee_shm_pool_mgr_ops - shared memory pool manager operations
- * @alloc: called when allocating shared memory
- * @free: called when freeing shared memory
- */
-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 - shared memory manager
- * @ops: operations
- * @private_data: private data for the shared memory manager
- */
-struct tee_shm_pool_mgr {
- const struct tee_shm_pool_mgr_ops *ops;
- void *private_data;
-};
-
/**
* struct tee_shm_pool - shared memory pool
* @private_mgr: pool manager for shared memory only between kernel
* and secure world
* @dma_buf_mgr: pool manager for shared memory exported to user space
- * @destroy: called when destroying the pool
- * @private_data: private data for the pool
*/
struct tee_shm_pool {
- struct tee_shm_pool_mgr private_mgr;
- struct tee_shm_pool_mgr dma_buf_mgr;
- void (*destroy)(struct tee_shm_pool *pool);
- void *private_data;
+ struct tee_shm_pool_mgr *private_mgr;
+ struct tee_shm_pool_mgr *dma_buf_mgr;
};

#define TEE_DEVICE_FLAG_REGISTERED 0x1
diff --git a/drivers/tee/tee_shm.c b/drivers/tee/tee_shm.c
index 4bc7956..fdda89e 100644
--- a/drivers/tee/tee_shm.c
+++ b/drivers/tee/tee_shm.c
@@ -32,9 +32,9 @@ static void tee_shm_release(struct tee_shm *shm)
mutex_unlock(&teedev->mutex);

if (shm->flags & TEE_SHM_DMA_BUF)
- poolm = &teedev->pool->dma_buf_mgr;
+ poolm = teedev->pool->dma_buf_mgr;
else
- poolm = &teedev->pool->private_mgr;
+ poolm = teedev->pool->private_mgr;

poolm->ops->free(poolm, shm);
kfree(shm);
@@ -139,9 +139,9 @@ struct tee_shm *tee_shm_alloc(struct tee_context *ctx, size_t size, u32 flags)
shm->teedev = teedev;
shm->ctx = ctx;
if (flags & TEE_SHM_DMA_BUF)
- poolm = &teedev->pool->dma_buf_mgr;
+ poolm = teedev->pool->dma_buf_mgr;
else
- poolm = &teedev->pool->private_mgr;
+ poolm = teedev->pool->private_mgr;

rc = poolm->ops->alloc(poolm, shm, size);
if (rc) {
diff --git a/drivers/tee/tee_shm_pool.c b/drivers/tee/tee_shm_pool.c
index fb4f852..e6d4b9e 100644
--- a/drivers/tee/tee_shm_pool.c
+++ b/drivers/tee/tee_shm_pool.c
@@ -44,49 +44,18 @@ static void pool_op_gen_free(struct tee_shm_pool_mgr *poolm,
shm->kaddr = NULL;
}

+static void pool_op_gen_destroy_poolmgr(struct tee_shm_pool_mgr *poolm)
+{
+ gen_pool_destroy(poolm->private_data);
+ kfree(poolm);
+}
+
static const struct tee_shm_pool_mgr_ops pool_ops_generic = {
.alloc = pool_op_gen_alloc,
.free = pool_op_gen_free,
+ .destroy_poolmgr = pool_op_gen_destroy_poolmgr,
};

-static void pool_res_mem_destroy(struct tee_shm_pool *pool)
-{
- gen_pool_destroy(pool->private_mgr.private_data);
- gen_pool_destroy(pool->dma_buf_mgr.private_data);
-}
-
-static int pool_res_mem_mgr_init(struct tee_shm_pool_mgr *mgr,
- struct tee_shm_pool_mem_info *info,
- int min_alloc_order)
-{
- size_t page_mask = PAGE_SIZE - 1;
- struct gen_pool *genpool = NULL;
- int rc;
-
- /*
- * Start and end must be page aligned
- */
- if ((info->vaddr & page_mask) || (info->paddr & page_mask) ||
- (info->size & page_mask))
- return -EINVAL;
-
- genpool = gen_pool_create(min_alloc_order, -1);
- if (!genpool)
- return -ENOMEM;
-
- gen_pool_set_algo(genpool, gen_pool_best_fit, NULL);
- rc = gen_pool_add_virt(genpool, info->vaddr, info->paddr, info->size,
- -1);
- if (rc) {
- gen_pool_destroy(genpool);
- return rc;
- }
-
- mgr->private_data = genpool;
- mgr->ops = &pool_ops_generic;
- return 0;
-}
-
/**
* tee_shm_pool_alloc_res_mem() - Create a shared memory pool from reserved
* memory range
@@ -104,42 +73,109 @@ struct tee_shm_pool *
tee_shm_pool_alloc_res_mem(struct tee_shm_pool_mem_info *priv_info,
struct tee_shm_pool_mem_info *dmabuf_info)
{
- struct tee_shm_pool *pool = NULL;
- int ret;
-
- pool = kzalloc(sizeof(*pool), GFP_KERNEL);
- if (!pool) {
- ret = -ENOMEM;
- goto err;
- }
+ struct tee_shm_pool_mgr *priv_mgr;
+ struct tee_shm_pool_mgr *dmabuf_mgr;
+ void *rc;

/*
* Create the pool for driver private shared memory
*/
- ret = pool_res_mem_mgr_init(&pool->private_mgr, priv_info,
- 3 /* 8 byte aligned */);
- if (ret)
- goto err;
+ rc = tee_shm_pool_mgr_alloc_res_mem(priv_info->vaddr, priv_info->paddr,
+ priv_info->size,
+ 3 /* 8 byte aligned */);
+ if (IS_ERR(rc))
+ return rc;
+ priv_mgr = rc;

/*
* Create the pool for dma_buf shared memory
*/
- ret = pool_res_mem_mgr_init(&pool->dma_buf_mgr, dmabuf_info,
- PAGE_SHIFT);
- if (ret)
+ rc = tee_shm_pool_mgr_alloc_res_mem(dmabuf_info->vaddr,
+ dmabuf_info->paddr,
+ dmabuf_info->size, PAGE_SHIFT);
+ if (IS_ERR(rc))
+ goto err_free_priv_mgr;
+ dmabuf_mgr = rc;
+
+ rc = tee_shm_pool_alloc(priv_mgr, dmabuf_mgr);
+ if (IS_ERR(rc))
+ goto err_free_dmabuf_mgr;
+
+ return rc;
+
+err_free_dmabuf_mgr:
+ tee_shm_pool_mgr_destroy(dmabuf_mgr);
+err_free_priv_mgr:
+ tee_shm_pool_mgr_destroy(priv_mgr);
+
+ return rc;
+}
+EXPORT_SYMBOL_GPL(tee_shm_pool_alloc_res_mem);
+
+struct tee_shm_pool_mgr *tee_shm_pool_mgr_alloc_res_mem(unsigned long vaddr,
+ phys_addr_t paddr,
+ size_t size,
+ int min_alloc_order)
+{
+ const size_t page_mask = PAGE_SIZE - 1;
+ struct tee_shm_pool_mgr *mgr;
+ int rc;
+
+ /* Start and end must be page aligned */
+ if (vaddr & page_mask || paddr & page_mask || size & page_mask)
+ return ERR_PTR(-EINVAL);
+
+ mgr = kzalloc(sizeof(*mgr), GFP_KERNEL);
+ if (!mgr)
+ return ERR_PTR(-ENOMEM);
+
+ mgr->private_data = gen_pool_create(min_alloc_order, -1);
+ if (!mgr->private_data) {
+ rc = -ENOMEM;
goto err;
+ }

- pool->destroy = pool_res_mem_destroy;
- return pool;
+ gen_pool_set_algo(mgr->private_data, gen_pool_best_fit, NULL);
+ rc = gen_pool_add_virt(mgr->private_data, vaddr, paddr, size, -1);
+ if (rc) {
+ gen_pool_destroy(mgr->private_data);
+ goto err;
+ }
+
+ mgr->ops = &pool_ops_generic;
+
+ return mgr;
err:
- if (ret == -ENOMEM)
- pr_err("%s: can't allocate memory for res_mem shared memory pool\n", __func__);
- if (pool && pool->private_mgr.private_data)
- gen_pool_destroy(pool->private_mgr.private_data);
- kfree(pool);
- return ERR_PTR(ret);
+ kfree(mgr);
+
+ return ERR_PTR(rc);
}
-EXPORT_SYMBOL_GPL(tee_shm_pool_alloc_res_mem);
+EXPORT_SYMBOL_GPL(tee_shm_pool_mgr_alloc_res_mem);
+
+static bool check_mgr_ops(struct tee_shm_pool_mgr *mgr)
+{
+ return mgr && mgr->ops && mgr->ops->alloc && mgr->ops->free &&
+ mgr->ops->destroy_poolmgr;
+}
+
+struct tee_shm_pool *tee_shm_pool_alloc(struct tee_shm_pool_mgr *priv_mgr,
+ struct tee_shm_pool_mgr *dmabuf_mgr)
+{
+ struct tee_shm_pool *pool;
+
+ if (!check_mgr_ops(priv_mgr) || !check_mgr_ops(dmabuf_mgr))
+ return ERR_PTR(-EINVAL);
+
+ pool = kzalloc(sizeof(*pool), GFP_KERNEL);
+ if (!pool)
+ return ERR_PTR(-ENOMEM);
+
+ pool->private_mgr = priv_mgr;
+ pool->dma_buf_mgr = dmabuf_mgr;
+
+ return pool;
+}
+EXPORT_SYMBOL_GPL(tee_shm_pool_alloc);

/**
* tee_shm_pool_free() - Free a shared memory pool
@@ -150,7 +186,10 @@ EXPORT_SYMBOL_GPL(tee_shm_pool_alloc_res_mem);
*/
void tee_shm_pool_free(struct tee_shm_pool *pool)
{
- pool->destroy(pool);
+ if (pool->private_mgr)
+ tee_shm_pool_mgr_destroy(pool->private_mgr);
+ if (pool->dma_buf_mgr)
+ tee_shm_pool_mgr_destroy(pool->dma_buf_mgr);
kfree(pool);
}
EXPORT_SYMBOL_GPL(tee_shm_pool_free);
diff --git a/include/linux/tee_drv.h b/include/linux/tee_drv.h
index cb889af..e9be4a4 100644
--- a/include/linux/tee_drv.h
+++ b/include/linux/tee_drv.h
@@ -150,6 +150,97 @@ int tee_device_register(struct tee_device *teedev);
void tee_device_unregister(struct tee_device *teedev);

/**
+ * struct tee_shm - shared memory object
+ * @teedev: device used to allocate the object
+ * @ctx: context using the object, if NULL the context is gone
+ * @link link element
+ * @paddr: physical address of the shared memory
+ * @kaddr: virtual address of the shared memory
+ * @size: size of shared memory
+ * @offset: offset of buffer in user space
+ * @pages: locked pages from userspace
+ * @num_pages: number of locked pages
+ * @dmabuf: dmabuf used to for exporting to user space
+ * @flags: defined by TEE_SHM_* in tee_drv.h
+ * @id: unique id of a shared memory object on this device
+ *
+ * This pool is only supposed to be accessed directly from the TEE
+ * subsystem and from drivers that implements their own shm pool manager.
+ */
+struct tee_shm {
+ struct tee_device *teedev;
+ struct tee_context *ctx;
+ struct list_head link;
+ phys_addr_t paddr;
+ void *kaddr;
+ size_t size;
+ unsigned int offset;
+ struct page **pages;
+ size_t num_pages;
+ struct dma_buf *dmabuf;
+ u32 flags;
+ int id;
+};
+
+/**
+ * struct tee_shm_pool_mgr - shared memory manager
+ * @ops: operations
+ * @private_data: private data for the shared memory manager
+ */
+struct tee_shm_pool_mgr {
+ const struct tee_shm_pool_mgr_ops *ops;
+ void *private_data;
+};
+
+/**
+ * struct tee_shm_pool_mgr_ops - shared memory pool manager operations
+ * @alloc: called when allocating shared memory
+ * @free: called when freeing shared memory
+ * @destroy_poolmgr: called when destroying the pool manager
+ */
+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);
+ void (*destroy_poolmgr)(struct tee_shm_pool_mgr *poolmgr);
+};
+
+/**
+ * tee_shm_pool_alloc() - Create a shared memory pool from shm managers
+ * @priv_mgr: manager for driver private shared memory allocations
+ * @dmabuf_mgr: manager for dma-buf shared memory allocations
+ *
+ * Allocation with the flag TEE_SHM_DMA_BUF set will use the range supplied
+ * in @dmabuf, others will use the range provided by @priv.
+ *
+ * @returns pointer to a 'struct tee_shm_pool' or an ERR_PTR on failure.
+ */
+struct tee_shm_pool *tee_shm_pool_alloc(struct tee_shm_pool_mgr *priv_mgr,
+ struct tee_shm_pool_mgr *dmabuf_mgr);
+
+/*
+ * tee_shm_pool_mgr_alloc_res_mem() - Create a shm manager for reserved
+ * memory
+ * @vaddr: Virtual address of start of pool
+ * @paddr: Physical address of start of pool
+ * @size: Size in bytes of the pool
+ *
+ * @returns pointer to a 'struct tee_shm_pool_mgr' or an ERR_PTR on failure.
+ */
+struct tee_shm_pool_mgr *tee_shm_pool_mgr_alloc_res_mem(unsigned long vaddr,
+ phys_addr_t paddr,
+ size_t size,
+ int min_alloc_order);
+
+/**
+ * tee_shm_pool_mgr_destroy() - Free a shared memory manager
+ */
+static inline void tee_shm_pool_mgr_destroy(struct tee_shm_pool_mgr *poolm)
+{
+ poolm->ops->destroy_poolmgr(poolm);
+}
+
+/**
* struct tee_shm_pool_mem_info - holds information needed to create a shared
* memory pool
* @vaddr: Virtual address of start of pool
--
2.7.4


From 1579807631256362414@xxx Thu Sep 28 18:06:02 +0000 2017
X-GM-THRID: 1579807631256362414
X-Gmail-Labels: Inbox,Category Forums

2017-10-13 19:35:05

by Volodymyr Babchuk

[permalink] [raw]
Subject: [PATCH v1 07/14] tee: optee: add shared buffer registration functions

From: Volodymyr Babchuk <[email protected]>

This change adds ops for shm_(un)register functions in tee interface.
Client application can use these functions to (un)register an own shared
buffer in OP-TEE address space. This allows zero copy data sharing between
Normal and Secure Worlds.

Please note that while those functions were added to optee code,
it does not report to userspace that those functions are available.
OP-TEE code does not set TEE_GEN_CAP_REG_MEM flag. This flag will be
enabled only after all other features of dynamic shared memory will be
implemented in subsequent patches. Of course user can ignore presence of
TEE_GEN_CAP_REG_MEM flag and try do call those functions. This is okay,
driver will register shared buffer in OP-TEE, but any attempts to use
this shared buffer will fail.

Signed-off-by: Volodymyr Babchuk <[email protected]>
---
* Extended commit message. Now it describes what will happen if user
will call optee_shm_register() exactly at this patch (without next ones)

* Added comment that describesx desctibe why we abuse least bits of
msg_arg->params->u.tmem.buf_ptr
---
drivers/tee/optee/call.c | 69 +++++++++++++++++++++++++++++++++++++++
drivers/tee/optee/core.c | 2 ++
drivers/tee/optee/optee_private.h | 4 +++
3 files changed, 75 insertions(+)

diff --git a/drivers/tee/optee/call.c b/drivers/tee/optee/call.c
index 4873666..a22f5f0 100644
--- a/drivers/tee/optee/call.c
+++ b/drivers/tee/optee/call.c
@@ -533,3 +533,72 @@ void optee_free_pages_list(void *array, size_t num_entries)
free_pages_exact(array, get_pages_list_size(num_entries));
}

+int optee_shm_register(struct tee_context *ctx, struct tee_shm *shm,
+ struct page **pages, size_t num_pages)
+{
+ struct tee_shm *shm_arg = NULL;
+ struct optee_msg_arg *msg_arg;
+ u64 *pages_list;
+ phys_addr_t msg_parg;
+ int rc = 0;
+
+ if (!num_pages)
+ return -EINVAL;
+
+ pages_list = optee_allocate_pages_list(num_pages);
+ if (!pages_list)
+ return -ENOMEM;
+
+ shm_arg = get_msg_arg(ctx, 1, &msg_arg, &msg_parg);
+ if (IS_ERR(shm_arg)) {
+ rc = PTR_ERR(shm_arg);
+ goto out;
+ }
+
+ optee_fill_pages_list(pages_list, pages, num_pages,
+ tee_shm_get_page_offset(shm));
+
+ msg_arg->cmd = OPTEE_MSG_CMD_REGISTER_SHM;
+ msg_arg->params->attr = OPTEE_MSG_ATTR_TYPE_TMEM_OUTPUT |
+ OPTEE_MSG_ATTR_NONCONTIG;
+ msg_arg->params->u.tmem.shm_ref = (unsigned long)shm;
+ msg_arg->params->u.tmem.size = tee_shm_get_size(shm);
+ /*
+ * In the least bits of msg_arg->params->u.tmem.buf_ptr we
+ * store buffer offset from 4k page, as described in OP-TEE ABI.
+ */
+ msg_arg->params->u.tmem.buf_ptr = virt_to_phys(pages_list) |
+ (tee_shm_get_page_offset(shm) & (OPTEE_MSG_NONCONTIG_PAGE_SIZE - 1));
+
+ if (optee_do_call_with_arg(ctx, msg_parg) ||
+ msg_arg->ret != TEEC_SUCCESS)
+ rc = -EINVAL;
+
+ tee_shm_free(shm_arg);
+out:
+ optee_free_pages_list(pages_list, num_pages);
+ return rc;
+}
+
+int optee_shm_unregister(struct tee_context *ctx, struct tee_shm *shm)
+{
+ struct tee_shm *shm_arg;
+ struct optee_msg_arg *msg_arg;
+ phys_addr_t msg_parg;
+ int rc = 0;
+
+ shm_arg = get_msg_arg(ctx, 1, &msg_arg, &msg_parg);
+ if (IS_ERR(shm_arg))
+ return PTR_ERR(shm_arg);
+
+ msg_arg->cmd = OPTEE_MSG_CMD_UNREGISTER_SHM;
+
+ msg_arg->params[0].attr = OPTEE_MSG_ATTR_TYPE_RMEM_INPUT;
+ msg_arg->params[0].u.rmem.shm_ref = (unsigned long)shm;
+
+ if (optee_do_call_with_arg(ctx, msg_parg) ||
+ msg_arg->ret != TEEC_SUCCESS)
+ rc = -EINVAL;
+ tee_shm_free(shm_arg);
+ return rc;
+}
diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c
index 7952357..4d448bf 100644
--- a/drivers/tee/optee/core.c
+++ b/drivers/tee/optee/core.c
@@ -267,6 +267,8 @@ static const struct tee_driver_ops optee_ops = {
.close_session = optee_close_session,
.invoke_func = optee_invoke_func,
.cancel_req = optee_cancel_req,
+ .shm_register = optee_shm_register,
+ .shm_unregister = optee_shm_unregister,
};

static const struct tee_desc optee_desc = {
diff --git a/drivers/tee/optee/optee_private.h b/drivers/tee/optee/optee_private.h
index b63213d..d7bc77d 100644
--- a/drivers/tee/optee/optee_private.h
+++ b/drivers/tee/optee/optee_private.h
@@ -160,6 +160,10 @@ int optee_cancel_req(struct tee_context *ctx, u32 cancel_id, u32 session);
void optee_enable_shm_cache(struct optee *optee);
void optee_disable_shm_cache(struct optee *optee);

+int optee_shm_register(struct tee_context *ctx, struct tee_shm *shm,
+ struct page **pages, size_t num_pages);
+int optee_shm_unregister(struct tee_context *ctx, struct tee_shm *shm);
+
int optee_from_msg_param(struct tee_param *params, size_t num_params,
const struct optee_msg_param *msg_params);
int optee_to_msg_param(struct optee_msg_param *msg_params, size_t num_params,
--
2.7.4


From 1579888902223510063@xxx Fri Sep 29 15:37:48 +0000 2017
X-GM-THRID: 1579807711774948113
X-Gmail-Labels: Inbox,Category Forums

2017-10-13 19:34:43

by Volodymyr Babchuk

[permalink] [raw]
Subject: [PATCH v1 03/14] tee: shm: add accessors for buffer size and page offset

From: Volodymyr Babchuk <[email protected]>

These two function will be needed for shared memory registration in OP-TEE

Signed-off-by: Volodymyr Babchuk <[email protected]>
---
include/linux/tee_drv.h | 20 ++++++++++++++++++++
1 file changed, 20 insertions(+)

diff --git a/include/linux/tee_drv.h b/include/linux/tee_drv.h
index 70b9c73..0d7f2a5 100644
--- a/include/linux/tee_drv.h
+++ b/include/linux/tee_drv.h
@@ -384,6 +384,26 @@ void *tee_shm_get_va(struct tee_shm *shm, size_t offs);
int tee_shm_get_pa(struct tee_shm *shm, size_t offs, phys_addr_t *pa);

/**
+ * tee_shm_get_size() - Get size of shared memory buffer
+ * @shm: Shared memory handle
+ * @returns size of shared memory
+ */
+static inline size_t tee_shm_get_size(struct tee_shm *shm)
+{
+ return shm->size;
+}
+
+/**
+ * tee_shm_get_page_offset() - Get shared buffer offset from page start
+ * @shm: Shared memory handle
+ * @returns page offset of shared buffer
+ */
+static inline size_t tee_shm_get_page_offset(struct tee_shm *shm)
+{
+ return shm->offset;
+}
+
+/**
* tee_shm_get_id() - Get id of a shared memory object
* @shm: Shared memory handle
* @returns id
--
2.7.4


From 1579807706125043350@xxx Thu Sep 28 18:07:14 +0000 2017
X-GM-THRID: 1579807706125043350
X-Gmail-Labels: Inbox,Category Forums

2017-10-13 19:34:53

by Volodymyr Babchuk

[permalink] [raw]
Subject: [PATCH v1 06/14] tee: optee: add page list manipulation functions

From: Volodymyr Babchuk <[email protected]>

These functions will be used to pass information about shared
buffers to OP-TEE. ABI between Linux and OP-TEE is defined
in optee_msg.h and optee_smc.h.

optee_msg.h defines OPTEE_MSG_ATTR_NONCONTIG attribute
for shared memory references and describes how such references
should be passed. Note that it uses 64-bit page addresses even
on 32 bit systems. This is done to support LPAE and to unify
interface.

Signed-off-by: Volodymyr Babchuk <[email protected]>
---
* optee_allocate_pages_array() and friends were renamed to
optee_allocate_pages_list() (and friends) to make naming consistent.

* Reworked get_pages_list_size(). Now calculations are right. Thanks
to Mark Rutland for suggestion.

* Totaly reworked optee_fill_pages_list(). Now it support all possible
page sizes. It was tested on qemu with page sizes of 4k and 64k.

---
drivers/tee/optee/call.c | 91 +++++++++++++++++++++++++++++++++++++++
drivers/tee/optee/optee_private.h | 5 +++
2 files changed, 96 insertions(+)

diff --git a/drivers/tee/optee/call.c b/drivers/tee/optee/call.c
index f7b7b40..2366300 100644
--- a/drivers/tee/optee/call.c
+++ b/drivers/tee/optee/call.c
@@ -11,6 +11,7 @@
* GNU General Public License for more details.
*
*/
+#include <asm/pgtable.h>
#include <linux/arm-smccc.h>
#include <linux/device.h>
#include <linux/err.h>
@@ -442,3 +443,93 @@ void optee_disable_shm_cache(struct optee *optee)
}
optee_cq_wait_final(&optee->call_queue, &w);
}
+
+#define PAGELIST_ENTRIES_PER_PAGE \
+ ((OPTEE_MSG_NONCONTIG_PAGE_SIZE / sizeof(u64)) - 1)
+
+/**
+ * optee_fill_pages_list() - write list of user pages to given shared
+ * buffer.
+ *
+ * @dst: page-aligned buffer where list of pages will be stored
+ * @pages: array of pages that represents shared buffer
+ * @num_pages: number of entries in @pages
+ * @page_offset: offset of user buffer from page start
+ *
+ * @dst should be big enough to hold list of user page addresses and
+ * links to the next pages of buffer
+ */
+void optee_fill_pages_list(u64 *dst, struct page **pages, int num_pages,
+ size_t page_offset)
+{
+ int n = 0;
+ phys_addr_t optee_page;
+ /*
+ * Refer to OPTEE_MSG_ATTR_NONCONTIG description in optee_msg.h
+ * for details.
+ */
+ struct {
+ uint64_t pages_list[PAGELIST_ENTRIES_PER_PAGE];
+ uint64_t next_page_data;
+ } *pages_data;
+
+ /*
+ * Currently OP-TEE uses 4k page size and it does not looks
+ * like this will change in the future. On other hand, there are
+ * no know ARM architectures with page size < 4k.
+ * Thus the next built assert looks redundant. But the following
+ * code heavily relies on this assumption, so it is better be
+ * safe than sorry.
+ */
+ BUILD_BUG_ON(PAGE_SIZE < OPTEE_MSG_NONCONTIG_PAGE_SIZE);
+
+ pages_data = (void *)dst;
+ /*
+ * If linux page is bigger than 4k, and user buffer offset is
+ * larger than 4k/8k/12k/etc this will skip first 4k pages,
+ * because they bear no value data for OP-TEE.
+ */
+ optee_page = page_to_phys(*pages) +
+ round_down(page_offset, OPTEE_MSG_NONCONTIG_PAGE_SIZE);
+
+ while (true) {
+ pages_data->pages_list[n++] = optee_page;
+
+ if (n == PAGELIST_ENTRIES_PER_PAGE) {
+ pages_data->next_page_data =
+ virt_to_phys(pages_data + 1);
+ pages_data++;
+ n = 0;
+ }
+
+ optee_page += OPTEE_MSG_NONCONTIG_PAGE_SIZE;
+ if (!(optee_page & ~PAGE_MASK)) {
+ if (!--num_pages)
+ break;
+ pages++;
+ optee_page = page_to_phys(*pages);
+ }
+ }
+}
+
+/*
+ * The final entry in each pagelist page is a pointer to the next
+ * pagelist page.
+ */
+static size_t get_pages_list_size(size_t num_entries)
+{
+ int pages = DIV_ROUND_UP(num_entries, PAGELIST_ENTRIES_PER_PAGE);
+
+ return pages * OPTEE_MSG_NONCONTIG_PAGE_SIZE;
+}
+
+u64 *optee_allocate_pages_list(size_t num_entries)
+{
+ return alloc_pages_exact(get_pages_list_size(num_entries), GFP_KERNEL);
+}
+
+void optee_free_pages_list(void *list, size_t num_entries)
+{
+ free_pages_exact(list, get_pages_list_size(num_entries));
+}
+
diff --git a/drivers/tee/optee/optee_private.h b/drivers/tee/optee/optee_private.h
index c374cd5..b63213d 100644
--- a/drivers/tee/optee/optee_private.h
+++ b/drivers/tee/optee/optee_private.h
@@ -165,6 +165,11 @@ int optee_from_msg_param(struct tee_param *params, size_t num_params,
int optee_to_msg_param(struct optee_msg_param *msg_params, size_t num_params,
const struct tee_param *params);

+u64 *optee_allocate_pages_list(size_t num_entries);
+void optee_free_pages_list(void *array, size_t num_entries);
+void optee_fill_pages_list(u64 *dst, struct page **pages, int num_pages,
+ size_t page_offset);
+
/*
* Small helpers
*/
--
2.7.4


From 1579891835768412578@xxx Fri Sep 29 16:24:26 +0000 2017
X-GM-THRID: 1579807733367478744
X-Gmail-Labels: Inbox,Category Forums

2017-10-13 19:36:38

by Volodymyr Babchuk

[permalink] [raw]
Subject: [PATCH v1 09/14] tee: optee: add registered buffers handling into RPC calls

From: Volodymyr Babchuk <[email protected]>

With latest changes to OP-TEE we can use any buffers as a shared memory.
Thus, it is possible for supplicant to provide part of own memory
when OP-TEE asks to allocate a shared buffer.

This patch adds support for such feature into RPC handling code.
Now when OP-TEE asks supplicant to allocate shared buffer, supplicant
can use TEE_IOC_SHM_REGISTER to provide such buffer. RPC handler is
aware of this, so it will pass list of allocated pages to OP-TEE.

Signed-off-by: Volodymyr Babchuk <[email protected]>
---

* free_pages_array() and friends were renamed to free_pages_list()

---
drivers/tee/optee/call.c | 19 +++++++++-
drivers/tee/optee/core.c | 2 +
drivers/tee/optee/optee_private.h | 15 +++++++-
drivers/tee/optee/rpc.c | 77 ++++++++++++++++++++++++++++++++++-----
4 files changed, 102 insertions(+), 11 deletions(-)

diff --git a/drivers/tee/optee/call.c b/drivers/tee/optee/call.c
index a22f5f0..ed5e82d 100644
--- a/drivers/tee/optee/call.c
+++ b/drivers/tee/optee/call.c
@@ -136,6 +136,7 @@ u32 optee_do_call_with_arg(struct tee_context *ctx, phys_addr_t parg)
struct optee *optee = tee_get_drvdata(ctx->teedev);
struct optee_call_waiter w;
struct optee_rpc_param param = { };
+ struct optee_call_ctx call_ctx = { };
u32 ret;

param.a0 = OPTEE_SMC_CALL_WITH_ARG;
@@ -160,13 +161,14 @@ u32 optee_do_call_with_arg(struct tee_context *ctx, phys_addr_t parg)
param.a1 = res.a1;
param.a2 = res.a2;
param.a3 = res.a3;
- optee_handle_rpc(ctx, &param);
+ optee_handle_rpc(ctx, &param, &call_ctx);
} else {
ret = res.a0;
break;
}
}

+ optee_rpc_finalize_call(&call_ctx);
/*
* We're done with our thread in secure world, if there's any
* thread waiters wake up one.
@@ -602,3 +604,18 @@ int optee_shm_unregister(struct tee_context *ctx, struct tee_shm *shm)
tee_shm_free(shm_arg);
return rc;
}
+
+int optee_shm_register_supp(struct tee_context *ctx, struct tee_shm *shm,
+ struct page **pages, size_t num_pages)
+{
+ /*
+ * We don't want to register supplicant memory in OP-TEE.
+ * Instead information about it will be passed in RPC code.
+ */
+ return 0;
+}
+
+int optee_shm_unregister_supp(struct tee_context *ctx, struct tee_shm *shm)
+{
+ return 0;
+}
diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c
index 3729ebb..d22433c 100644
--- a/drivers/tee/optee/core.c
+++ b/drivers/tee/optee/core.c
@@ -331,6 +331,8 @@ static const struct tee_driver_ops optee_supp_ops = {
.release = optee_release,
.supp_recv = optee_supp_recv,
.supp_send = optee_supp_send,
+ .shm_register = optee_shm_register_supp,
+ .shm_unregister = optee_shm_unregister_supp,
};

static const struct tee_desc optee_supp_desc = {
diff --git a/drivers/tee/optee/optee_private.h b/drivers/tee/optee/optee_private.h
index d7bc77d..61a0052 100644
--- a/drivers/tee/optee/optee_private.h
+++ b/drivers/tee/optee/optee_private.h
@@ -130,7 +130,16 @@ struct optee_rpc_param {
u32 a7;
};

-void optee_handle_rpc(struct tee_context *ctx, struct optee_rpc_param *param);
+/* Holds context that is preserved during one STD call */
+struct optee_call_ctx {
+ /* information about pages list used in last allocation */
+ void *pages_list;
+ size_t num_entries;
+};
+
+void optee_handle_rpc(struct tee_context *ctx, struct optee_rpc_param *param,
+ struct optee_call_ctx *call_ctx);
+void optee_rpc_finalize_call(struct optee_call_ctx *call_ctx);

void optee_wait_queue_init(struct optee_wait_queue *wq);
void optee_wait_queue_exit(struct optee_wait_queue *wq);
@@ -164,6 +173,10 @@ int optee_shm_register(struct tee_context *ctx, struct tee_shm *shm,
struct page **pages, size_t num_pages);
int optee_shm_unregister(struct tee_context *ctx, struct tee_shm *shm);

+int optee_shm_register_supp(struct tee_context *ctx, struct tee_shm *shm,
+ struct page **pages, size_t num_pages);
+int optee_shm_unregister_supp(struct tee_context *ctx, struct tee_shm *shm);
+
int optee_from_msg_param(struct tee_param *params, size_t num_params,
const struct optee_msg_param *msg_params);
int optee_to_msg_param(struct optee_msg_param *msg_params, size_t num_params,
diff --git a/drivers/tee/optee/rpc.c b/drivers/tee/optee/rpc.c
index cef417f..7bd18e3 100644
--- a/drivers/tee/optee/rpc.c
+++ b/drivers/tee/optee/rpc.c
@@ -200,7 +200,8 @@ static struct tee_shm *cmd_alloc_suppl(struct tee_context *ctx, size_t sz)
}

static void handle_rpc_func_cmd_shm_alloc(struct tee_context *ctx,
- struct optee_msg_arg *arg)
+ struct optee_msg_arg *arg,
+ struct optee_call_ctx *call_ctx)
{
phys_addr_t pa;
struct tee_shm *shm;
@@ -245,10 +246,49 @@ static void handle_rpc_func_cmd_shm_alloc(struct tee_context *ctx,
goto bad;
}

- arg->params[0].attr = OPTEE_MSG_ATTR_TYPE_TMEM_OUTPUT;
- arg->params[0].u.tmem.buf_ptr = pa;
- arg->params[0].u.tmem.size = sz;
- arg->params[0].u.tmem.shm_ref = (unsigned long)shm;
+ sz = tee_shm_get_size(shm);
+
+ if (tee_shm_is_registered(shm)) {
+ struct page **pages;
+ u64 *pages_list;
+ size_t page_num;
+
+ pages = tee_shm_get_pages(shm, &page_num);
+ if (!pages || !page_num) {
+ arg->ret = TEEC_ERROR_OUT_OF_MEMORY;
+ goto bad;
+ }
+
+ pages_list = optee_allocate_pages_list(page_num);
+ if (!pages_list) {
+ arg->ret = TEEC_ERROR_OUT_OF_MEMORY;
+ goto bad;
+ }
+
+ call_ctx->pages_list = pages_list;
+ call_ctx->num_entries = page_num;
+
+ arg->params[0].attr = OPTEE_MSG_ATTR_TYPE_TMEM_OUTPUT |
+ OPTEE_MSG_ATTR_NONCONTIG;
+ /*
+ * In the least bits of u.tmem.buf_ptr we store buffer offset
+ * from 4k page, as described in OP-TEE ABI.
+ */
+ arg->params[0].u.tmem.buf_ptr = virt_to_phys(pages_list) |
+ (tee_shm_get_page_offset(shm) &
+ (OPTEE_MSG_NONCONTIG_PAGE_SIZE - 1));
+ arg->params[0].u.tmem.size = tee_shm_get_size(shm);
+ arg->params[0].u.tmem.shm_ref = (unsigned long)shm;
+
+ optee_fill_pages_list(pages_list, pages, page_num,
+ tee_shm_get_page_offset(shm));
+ } else {
+ arg->params[0].attr = OPTEE_MSG_ATTR_TYPE_TMEM_OUTPUT;
+ arg->params[0].u.tmem.buf_ptr = pa;
+ arg->params[0].u.tmem.size = sz;
+ arg->params[0].u.tmem.shm_ref = (unsigned long)shm;
+ }
+
arg->ret = TEEC_SUCCESS;
return;
bad:
@@ -307,8 +347,24 @@ static void handle_rpc_func_cmd_shm_free(struct tee_context *ctx,
arg->ret = TEEC_SUCCESS;
}

+static void free_pages_list(struct optee_call_ctx *call_ctx)
+{
+ if (call_ctx->pages_list) {
+ optee_free_pages_list(call_ctx->pages_list,
+ call_ctx->num_entries);
+ call_ctx->pages_list = NULL;
+ call_ctx->num_entries = 0;
+ }
+}
+
+void optee_rpc_finalize_call(struct optee_call_ctx *call_ctx)
+{
+ free_pages_list(call_ctx);
+}
+
static void handle_rpc_func_cmd(struct tee_context *ctx, struct optee *optee,
- struct tee_shm *shm)
+ struct tee_shm *shm,
+ struct optee_call_ctx *call_ctx)
{
struct optee_msg_arg *arg;

@@ -329,7 +385,8 @@ static void handle_rpc_func_cmd(struct tee_context *ctx, struct optee *optee,
handle_rpc_func_cmd_wait(arg);
break;
case OPTEE_MSG_RPC_CMD_SHM_ALLOC:
- handle_rpc_func_cmd_shm_alloc(ctx, arg);
+ free_pages_list(call_ctx);
+ handle_rpc_func_cmd_shm_alloc(ctx, arg, call_ctx);
break;
case OPTEE_MSG_RPC_CMD_SHM_FREE:
handle_rpc_func_cmd_shm_free(ctx, arg);
@@ -343,10 +400,12 @@ static void handle_rpc_func_cmd(struct tee_context *ctx, struct optee *optee,
* optee_handle_rpc() - handle RPC from secure world
* @ctx: context doing the RPC
* @param: value of registers for the RPC
+ * @call_ctx: call context. Preserved during one OP-TEE invocation
*
* Result of RPC is written back into @param.
*/
-void optee_handle_rpc(struct tee_context *ctx, struct optee_rpc_param *param)
+void optee_handle_rpc(struct tee_context *ctx, struct optee_rpc_param *param,
+ struct optee_call_ctx *call_ctx)
{
struct tee_device *teedev = ctx->teedev;
struct optee *optee = tee_get_drvdata(teedev);
@@ -381,7 +440,7 @@ void optee_handle_rpc(struct tee_context *ctx, struct optee_rpc_param *param)
break;
case OPTEE_SMC_RPC_FUNC_CMD:
shm = reg_pair_to_ptr(param->a1, param->a2);
- handle_rpc_func_cmd(ctx, optee, shm);
+ handle_rpc_func_cmd(ctx, optee, shm, call_ctx);
break;
default:
pr_warn("Unknown RPC func 0x%x\n",
--
2.7.4


From 1579807681650156014@xxx Thu Sep 28 18:06:50 +0000 2017
X-GM-THRID: 1579807681650156014
X-Gmail-Labels: Inbox,Category Forums

2017-10-13 19:36:55

by Volodymyr Babchuk

[permalink] [raw]
Subject: [PATCH v1 12/14] tee: optee: enable dynamic SHM support

From: Volodymyr Babchuk <[email protected]>

Previous patches added various features that are needed for dynamic SHM.
Dynamic SHM allows Normal World to share any buffers with OP-TEE.
While original design suggested to use pre-allocated region (usually of
1M to 2M of size), this new approach allows to use all non-secure RAM for
command buffers, RPC allocations and TA parameters.

This patch checks capability OPTEE_SMC_SEC_CAP_DYNAMIC_SHM. If it was set
by OP-TEE, then kernel part of OP-TEE will use kernel page allocator
to allocate command buffers. Also it will set TEE_GEN_CAP_REG_MEM
capability to tell userspace that it supports shared memory registration.

Signed-off-by: Volodymyr Babchuk <[email protected]>
---
drivers/tee/optee/core.c | 69 +++++++++++++++++++++++++++++++++++-------------
1 file changed, 51 insertions(+), 18 deletions(-)

diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c
index dbe5a61..51d5575 100644
--- a/drivers/tee/optee/core.c
+++ b/drivers/tee/optee/core.c
@@ -28,6 +28,7 @@
#include <linux/uaccess.h>
#include "optee_private.h"
#include "optee_smc.h"
+#include "shm_pool.h"

#define DRIVER_NAME "optee"

@@ -219,6 +220,10 @@ static void optee_get_version(struct tee_device *teedev,
.impl_caps = TEE_OPTEE_CAP_TZ,
.gen_caps = TEE_GEN_CAP_GP,
};
+ struct optee *optee = tee_get_drvdata(teedev);
+
+ if (optee->sec_caps & OPTEE_SMC_SEC_CAP_DYNAMIC_SHM)
+ v.gen_caps |= TEE_GEN_CAP_REG_MEM;
*vers = v;
}

@@ -397,21 +402,22 @@ static bool optee_msg_exchange_capabilities(optee_invoke_fn *invoke_fn,
}

static struct tee_shm_pool *
-optee_config_shm_memremap(optee_invoke_fn *invoke_fn, void **memremaped_shm)
+optee_config_shm_memremap(optee_invoke_fn *invoke_fn, void **memremaped_shm,
+ u32 sec_caps)
{
union {
struct arm_smccc_res smccc;
struct optee_smc_get_shm_config_result result;
} res;
- struct tee_shm_pool *pool;
unsigned long vaddr;
phys_addr_t paddr;
size_t size;
phys_addr_t begin;
phys_addr_t end;
void *va;
- struct tee_shm_pool_mem_info priv_info;
- struct tee_shm_pool_mem_info dmabuf_info;
+ struct tee_shm_pool_mgr *priv_mgr;
+ struct tee_shm_pool_mgr *dmabuf_mgr;
+ void *rc;

invoke_fn(OPTEE_SMC_GET_SHM_CONFIG, 0, 0, 0, 0, 0, 0, 0, &res.smccc);
if (res.result.status != OPTEE_SMC_RETURN_OK) {
@@ -441,22 +447,49 @@ optee_config_shm_memremap(optee_invoke_fn *invoke_fn, void **memremaped_shm)
}
vaddr = (unsigned long)va;

- priv_info.vaddr = vaddr;
- priv_info.paddr = paddr;
- priv_info.size = OPTEE_SHM_NUM_PRIV_PAGES * PAGE_SIZE;
- dmabuf_info.vaddr = vaddr + OPTEE_SHM_NUM_PRIV_PAGES * PAGE_SIZE;
- dmabuf_info.paddr = paddr + OPTEE_SHM_NUM_PRIV_PAGES * PAGE_SIZE;
- dmabuf_info.size = size - OPTEE_SHM_NUM_PRIV_PAGES * PAGE_SIZE;
-
- pool = tee_shm_pool_alloc_res_mem(&priv_info, &dmabuf_info);
- if (IS_ERR(pool)) {
- memunmap(va);
- goto out;
+ /*
+ * If OP-TEE can work with unregistered SHM, we will use own pool
+ * for private shm
+ */
+ if (sec_caps & OPTEE_SMC_SEC_CAP_DYNAMIC_SHM) {
+ rc = optee_shm_pool_alloc_pages();
+ if (IS_ERR(rc))
+ goto err_memunmap;
+ priv_mgr = rc;
+ } else {
+ const size_t sz = OPTEE_SHM_NUM_PRIV_PAGES * PAGE_SIZE;
+
+ rc = tee_shm_pool_mgr_alloc_res_mem(vaddr, paddr, sz,
+ 3 /* 8 bytes aligned */);
+ if (IS_ERR(rc))
+ goto err_memunmap;
+ priv_mgr = rc;
+
+ vaddr += sz;
+ paddr += sz;
+ size -= sz;
}

+ rc = tee_shm_pool_mgr_alloc_res_mem(vaddr, paddr, size, PAGE_SHIFT);
+ if (IS_ERR(rc))
+ goto err_free_priv_mgr;
+ dmabuf_mgr = rc;
+
+ rc = tee_shm_pool_alloc(priv_mgr, dmabuf_mgr);
+ if (IS_ERR(rc))
+ goto err_free_dmabuf_mgr;
+
*memremaped_shm = va;
-out:
- return pool;
+
+ return rc;
+
+err_free_dmabuf_mgr:
+ tee_shm_pool_mgr_destroy(dmabuf_mgr);
+err_free_priv_mgr:
+ tee_shm_pool_mgr_destroy(priv_mgr);
+err_memunmap:
+ memunmap(va);
+ return rc;
}

/* Simple wrapper functions to be able to use a function pointer */
@@ -534,7 +567,7 @@ static struct optee *optee_probe(struct device_node *np)
if (!(sec_caps & OPTEE_SMC_SEC_CAP_HAVE_RESERVED_SHM))
return ERR_PTR(-EINVAL);

- pool = optee_config_shm_memremap(invoke_fn, &memremaped_shm);
+ pool = optee_config_shm_memremap(invoke_fn, &memremaped_shm, sec_caps);
if (IS_ERR(pool))
return (void *)pool;

--
2.7.4


From 1580327570507056233@xxx Wed Oct 04 11:50:15 +0000 2017
X-GM-THRID: 1579807701882531863
X-Gmail-Labels: Inbox,Category Forums

2017-10-03 16:09:03

by Stuart Yoder

[permalink] [raw]
Subject: Re: [Tee-dev] [PATCH v1 12/14] tee: optee: enable dynamic SHM support



On 9/28/17 1:04 PM, Volodymyr Babchuk wrote:
> From: Volodymyr Babchuk <[email protected]>
>
> Previous patches added various features that are needed for dynamic SHM.
> Dynamic SHM allows Normal World to share any buffers with OP-TEE.
> While original design suggested to use pre-allocated region (usually of
> 1M to 2M of size), this new approach allows to use all non-secure RAM for
> command buffers, RPC allocations and TA parameters.
>
> This patch checks capability OPTEE_SMC_SEC_CAP_DYNAMIC_SHM. If it was set
> by OP-TEE, then kernel part of OP-TEE will use kernel page allocator
> to allocate command buffers. Also it will set TEE_GEN_CAP_REG_MEM
> capability to tell userspace that it supports shared memory registration.
>
> Signed-off-by: Volodymyr Babchuk <[email protected]>
> ---
> drivers/tee/optee/core.c | 69 +++++++++++++++++++++++++++++++++++-------------
> 1 file changed, 51 insertions(+), 18 deletions(-)
>
> diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c
> index 8e012ea..e8fd9af 100644
> --- a/drivers/tee/optee/core.c
> +++ b/drivers/tee/optee/core.c
> @@ -28,6 +28,7 @@
> #include <linux/uaccess.h>
> #include "optee_private.h"
> #include "optee_smc.h"
> +#include "shm_pool.h"
>
> #define DRIVER_NAME "optee"
>
> @@ -227,6 +228,10 @@ static void optee_get_version(struct tee_device *teedev,
> .impl_caps = TEE_OPTEE_CAP_TZ,
> .gen_caps = TEE_GEN_CAP_GP,
> };
> + struct optee *optee = tee_get_drvdata(teedev);
> +
> + if (optee->sec_caps & OPTEE_SMC_SEC_CAP_DYNAMIC_SHM)
> + v.gen_caps |= TEE_GEN_CAP_REG_MEM;
> *vers = v;
> }
>
> @@ -405,21 +410,22 @@ static bool optee_msg_exchange_capabilities(optee_invoke_fn *invoke_fn,
> }
>
> static struct tee_shm_pool *
> -optee_config_shm_memremap(optee_invoke_fn *invoke_fn, void **memremaped_shm)
> +optee_config_shm_memremap(optee_invoke_fn *invoke_fn, void **memremaped_shm,
> + u32 sec_caps)
> {
> union {
> struct arm_smccc_res smccc;
> struct optee_smc_get_shm_config_result result;
> } res;
> - struct tee_shm_pool *pool;
> unsigned long vaddr;
> phys_addr_t paddr;
> size_t size;
> phys_addr_t begin;
> phys_addr_t end;
> void *va;
> - struct tee_shm_pool_mem_info priv_info;
> - struct tee_shm_pool_mem_info dmabuf_info;
> + struct tee_shm_pool_mgr *priv_mgr;
> + struct tee_shm_pool_mgr *dmabuf_mgr;
> + void *rc;
>
> invoke_fn(OPTEE_SMC_GET_SHM_CONFIG, 0, 0, 0, 0, 0, 0, 0, &res.smccc);
> if (res.result.status != OPTEE_SMC_RETURN_OK) {
> @@ -449,22 +455,49 @@ optee_config_shm_memremap(optee_invoke_fn *invoke_fn, void **memremaped_shm)
> }
> vaddr = (unsigned long)va;
>
> - priv_info.vaddr = vaddr;
> - priv_info.paddr = paddr;
> - priv_info.size = OPTEE_SHM_NUM_PRIV_PAGES * PAGE_SIZE;
> - dmabuf_info.vaddr = vaddr + OPTEE_SHM_NUM_PRIV_PAGES * PAGE_SIZE;
> - dmabuf_info.paddr = paddr + OPTEE_SHM_NUM_PRIV_PAGES * PAGE_SIZE;
> - dmabuf_info.size = size - OPTEE_SHM_NUM_PRIV_PAGES * PAGE_SIZE;
> -
> - pool = tee_shm_pool_alloc_res_mem(&priv_info, &dmabuf_info);
> - if (IS_ERR(pool)) {
> - memunmap(va);
> - goto out;

Now that you removed the call to tee_shm_pool_alloc_res_mem() it is dead
code and no longer used. Do we still need tee_shm_pool_alloc_res_mem at
all?

> + /*
> + * If OP-TEE can work with unregistered SHM, we will use own pool
> + * for private shm
> + */
> + if (sec_caps & OPTEE_SMC_SEC_CAP_DYNAMIC_SHM) {
> + rc = optee_shm_pool_alloc_pages();
> + if (IS_ERR(rc))
> + goto err_memunmap;
> + priv_mgr = rc;
> + } else {
> + const size_t sz = OPTEE_SHM_NUM_PRIV_PAGES * PAGE_SIZE;
> +
> + rc = tee_shm_pool_mgr_alloc_res_mem(vaddr, paddr, sz,
> + 3 /* 8 bytes aligned */);
> + if (IS_ERR(rc))
> + goto err_memunmap;
> + priv_mgr = rc;
> +
> + vaddr += sz;
> + paddr += sz;
> + size -= sz;
> }
>
> + rc = tee_shm_pool_mgr_alloc_res_mem(vaddr, paddr, size, PAGE_SHIFT);
> + if (IS_ERR(rc))
> + goto err_free_priv_mgr;
> + dmabuf_mgr = rc;
> +
> + rc = tee_shm_pool_alloc(priv_mgr, dmabuf_mgr);
> + if (IS_ERR(rc))
> + goto err_free_dmabuf_mgr;
> +
> *memremaped_shm = va;
> -out:
> - return pool;
> +
> + return rc;
> +
> +err_free_dmabuf_mgr:
> + tee_shm_pool_mgr_destroy(dmabuf_mgr);
> +err_free_priv_mgr:
> + tee_shm_pool_mgr_destroy(priv_mgr);
> +err_memunmap:
> + memunmap(va);
> + return rc;
> }

This function now mixes dynamic and shared memory based allocation in a way that
only applies to certain cases.

We're going to have the following cases:
-Linux OP-TEE driver sees only static shared memory advertised (older versions
of OP-TEE)
-Linux OP-TEE driver sees only dynamic shared memory advertised (e.g. a guest
kernel in a VM)
-Linux OP-TEE driver sees both static and dynamic memory advertised

We are not handling the 'only dynamic shared memory' case currently and this code
is going to have to be refactored again to support that. Since we are substantially
re-working it anyway here, why don't we support all the cases.

It seems like we need logic along the lines of:

invoke_fn(OPTEE_SMC_GET_SHM_CONFIG, 0, 0, 0, 0, 0, 0, 0, &res.smccc);
if (res.result.status == OPTEE_SMC_RETURN_OK)
optee_static_shm = true;
else
optee_static_shm = false;

if (sec_caps & OPTEE_SMC_SEC_CAP_DYNAMIC_SHM)
optee_dynamic_shm = true;
else
optee_dynamic_shm = false;

/* allocate private pool */
if (optee_dynamic_shm) {
rc = optee_shm_pool_alloc_pages();
priv_mgr = rc;
} else {
rc = tee_shm_pool_mgr_alloc_res_mem(vaddr, paddr, sz, 3);
priv_mgr = rc;
}

/* allocate dmabuf pool */
if (optee_dynamic_shm && !optee_static_shm) {
dmabuf_mgr = NULL;
} else {
rc = tee_shm_pool_mgr_alloc_res_mem(vaddr, paddr, size, PAGE_SHIFT);
dmabuf_mgr = rc;
}

rc = tee_shm_pool_alloc(priv_mgr, dmabuf_mgr);

>
> /* Simple wrapper functions to be able to use a function pointer */
> @@ -542,7 +575,7 @@ static struct optee *optee_probe(struct device_node *np)
> if (!(sec_caps & OPTEE_SMC_SEC_CAP_HAVE_RESERVED_SHM))
> return ERR_PTR(-EINVAL);

We should remove the above assumption that there must be static shared memory. It
shouldn't be an error.

Thanks,
Stuart

From 1579807701882531863@xxx Thu Sep 28 18:07:10 +0000 2017
X-GM-THRID: 1579807701882531863
X-Gmail-Labels: Inbox,Category Forums

2017-10-13 19:35:49

by Volodymyr Babchuk

[permalink] [raw]
Subject: [PATCH v1 05/14] tee: optee: Update protocol definitions

From: Volodymyr Babchuk <[email protected]>

There were changes in REE<->OP-TEE ABI recently.
Now ABI allows us to pass non-contiguous memory buffers as list of
pages to OP-TEE. This can be achieved by using new parameter attribute
OPTEE_MSG_ATTR_NONCONTIG.

OP-TEE also is able to use all non-secure RAM for shared buffers. This
new capability is enabled with OPTEE_SMC_SEC_CAP_DYNAMIC_SHM flag.

This patch adds necessary definitions to the protocol definition files at
Linux side.

Signed-off-by: Volodymyr Babchuk <[email protected]>
---
drivers/tee/optee/optee_msg.h | 38 ++++++++++++++++++++++++++++++++------
drivers/tee/optee/optee_smc.h | 7 +++++++
2 files changed, 39 insertions(+), 6 deletions(-)

diff --git a/drivers/tee/optee/optee_msg.h b/drivers/tee/optee/optee_msg.h
index dd7a06e..3050490 100644
--- a/drivers/tee/optee/optee_msg.h
+++ b/drivers/tee/optee/optee_msg.h
@@ -67,11 +67,32 @@
#define OPTEE_MSG_ATTR_META BIT(8)

/*
- * The temporary shared memory object is not physically contigous and this
- * temp memref is followed by another fragment until the last temp memref
- * that doesn't have this bit set.
+ * Pointer to a list of pages used to register user-defined SHM buffer.
+ * Used with OPTEE_MSG_ATTR_TYPE_TMEM_*.
+ * buf_ptr should point to the beginning of the buffer. Buffer will contain
+ * list of page addresses. OP-TEE core can reconstruct contiguous buffer from
+ * that page addresses list. Page addresses are stored as 64 bit values.
+ * Last entry on a page should point to the next page of buffer.
+ * Every entry in buffer should point to a 4k page beginning (12 least
+ * significant bits must be equal to zero).
+ *
+ * 12 least significant bints of optee_msg_param.u.tmem.buf_ptr should hold page
+ * offset of the user buffer.
+ *
+ * So, entries should be placed like members of this structure:
+ *
+ * struct page_data {
+ * uint64_t pages_array[OPTEE_MSG_NONCONTIG_PAGE_SIZE/sizeof(uint64_t) - 1];
+ * uint64_t next_page_data;
+ * };
+ *
+ * Structure is designed to exactly fit into the page size
+ * OPTEE_MSG_NONCONTIG_PAGE_SIZE which is a standard 4KB page.
+ *
+ * The size of 4KB is chosen because this is the smallest page size for ARM
+ * architectures. If REE uses larger pages, it should divide them to 4KB ones.
*/
-#define OPTEE_MSG_ATTR_FRAGMENT BIT(9)
+#define OPTEE_MSG_ATTR_NONCONTIG BIT(9)

/*
* Memory attributes for caching passed with temp memrefs. The actual value
@@ -94,6 +115,11 @@
#define OPTEE_MSG_LOGIN_APPLICATION_USER 0x00000005
#define OPTEE_MSG_LOGIN_APPLICATION_GROUP 0x00000006

+/*
+ * Page size used in non-contiguous buffer entries
+ */
+#define OPTEE_MSG_NONCONTIG_PAGE_SIZE 4096
+
/**
* struct optee_msg_param_tmem - temporary memory reference parameter
* @buf_ptr: Address of the buffer
@@ -145,8 +171,8 @@ struct optee_msg_param_value {
*
* @attr & OPTEE_MSG_ATTR_TYPE_MASK indicates if tmem, rmem or value is used in
* the union. OPTEE_MSG_ATTR_TYPE_VALUE_* indicates value,
- * OPTEE_MSG_ATTR_TYPE_TMEM_* indicates tmem and
- * OPTEE_MSG_ATTR_TYPE_RMEM_* indicates rmem.
+ * OPTEE_MSG_ATTR_TYPE_TMEM_* indicates @tmem and
+ * OPTEE_MSG_ATTR_TYPE_RMEM_* indicates @rmem,
* OPTEE_MSG_ATTR_TYPE_NONE indicates that none of the members are used.
*/
struct optee_msg_param {
diff --git a/drivers/tee/optee/optee_smc.h b/drivers/tee/optee/optee_smc.h
index 069c8e1..7cd3272 100644
--- a/drivers/tee/optee/optee_smc.h
+++ b/drivers/tee/optee/optee_smc.h
@@ -222,6 +222,13 @@ struct optee_smc_get_shm_config_result {
#define OPTEE_SMC_SEC_CAP_HAVE_RESERVED_SHM BIT(0)
/* Secure world can communicate via previously unregistered shared memory */
#define OPTEE_SMC_SEC_CAP_UNREGISTERED_SHM BIT(1)
+
+/*
+ * Secure world supports commands "register/unregister shared memory",
+ * secure world accepts command buffers located in any parts of non-secure RAM
+ */
+#define OPTEE_SMC_SEC_CAP_DYNAMIC_SHM BIT(2)
+
#define OPTEE_SMC_FUNCID_EXCHANGE_CAPABILITIES 9
#define OPTEE_SMC_EXCHANGE_CAPABILITIES \
OPTEE_SMC_FAST_CALL_VAL(OPTEE_SMC_FUNCID_EXCHANGE_CAPABILITIES)
--
2.7.4


From 1579807725668187688@xxx Thu Sep 28 18:07:32 +0000 2017
X-GM-THRID: 1579807725668187688
X-Gmail-Labels: Inbox,Category Forums

2017-10-13 19:38:47

by Volodymyr Babchuk

[permalink] [raw]
Subject: [PATCH v1 02/14] tee: add register user memory

From: Jens Wiklander <[email protected]>

Added new ioctl to allow users register own buffers as a shared memory.

Signed-off-by: Jens Wiklander <[email protected]>
Signed-off-by: Volodymyr Babchuk <[email protected]>
---

* Removed dev_err() in cases when this is not exactly an error
* tee_shm_register() return ENOTSUPP in cases when registration
of memory is not supported.
* Fixed nasty mistake with sizeof(struct page) instead of
sizeof(struct page*)
* tee_shm_is_registered() made inline. In v1 there was separate
patch that did this.

---
drivers/tee/tee_core.c | 41 +++++++++-
drivers/tee/tee_shm.c | 205 +++++++++++++++++++++++++++++++++++++++++------
include/linux/tee_drv.h | 47 ++++++++++-
include/uapi/linux/tee.h | 30 +++++++
4 files changed, 293 insertions(+), 30 deletions(-)

diff --git a/drivers/tee/tee_core.c b/drivers/tee/tee_core.c
index 58a5009..295910f 100644
--- a/drivers/tee/tee_core.c
+++ b/drivers/tee/tee_core.c
@@ -114,8 +114,6 @@ static int tee_ioctl_shm_alloc(struct tee_context *ctx,
if (data.flags)
return -EINVAL;

- data.id = -1;
-
shm = tee_shm_alloc(ctx, data.size, TEE_SHM_MAPPED | TEE_SHM_DMA_BUF);
if (IS_ERR(shm))
return PTR_ERR(shm);
@@ -138,6 +136,43 @@ static int tee_ioctl_shm_alloc(struct tee_context *ctx,
return ret;
}

+static int
+tee_ioctl_shm_register(struct tee_context *ctx,
+ struct tee_ioctl_shm_register_data __user *udata)
+{
+ long ret;
+ struct tee_ioctl_shm_register_data data;
+ struct tee_shm *shm;
+
+ if (copy_from_user(&data, udata, sizeof(data)))
+ return -EFAULT;
+
+ /* Currently no input flags are supported */
+ if (data.flags)
+ return -EINVAL;
+
+ shm = tee_shm_register(ctx, data.addr, data.length,
+ TEE_SHM_DMA_BUF | TEE_SHM_USER_MAPPED);
+ if (IS_ERR(shm))
+ return PTR_ERR(shm);
+
+ data.id = shm->id;
+ data.flags = shm->flags;
+ data.length = shm->size;
+
+ if (copy_to_user(udata, &data, sizeof(data)))
+ ret = -EFAULT;
+ else
+ ret = tee_shm_get_fd(shm);
+ /*
+ * When user space closes the file descriptor the shared memory
+ * should be freed or if tee_shm_get_fd() failed then it will
+ * be freed immediately.
+ */
+ tee_shm_put(shm);
+ return ret;
+}
+
static int params_from_user(struct tee_context *ctx, struct tee_param *params,
size_t num_params,
struct tee_ioctl_param __user *uparams)
@@ -586,6 +621,8 @@ static long tee_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
return tee_ioctl_version(ctx, uarg);
case TEE_IOC_SHM_ALLOC:
return tee_ioctl_shm_alloc(ctx, uarg);
+ case TEE_IOC_SHM_REGISTER:
+ return tee_ioctl_shm_register(ctx, uarg);
case TEE_IOC_OPEN_SESSION:
return tee_ioctl_open_session(ctx, uarg);
case TEE_IOC_INVOKE:
diff --git a/drivers/tee/tee_shm.c b/drivers/tee/tee_shm.c
index fdda89e..177e341 100644
--- a/drivers/tee/tee_shm.c
+++ b/drivers/tee/tee_shm.c
@@ -23,7 +23,6 @@
static void tee_shm_release(struct tee_shm *shm)
{
struct tee_device *teedev = shm->teedev;
- struct tee_shm_pool_mgr *poolm;

mutex_lock(&teedev->mutex);
idr_remove(&teedev->idr, shm->id);
@@ -31,12 +30,29 @@ static void tee_shm_release(struct tee_shm *shm)
list_del(&shm->link);
mutex_unlock(&teedev->mutex);

- if (shm->flags & TEE_SHM_DMA_BUF)
- poolm = teedev->pool->dma_buf_mgr;
- else
- poolm = teedev->pool->private_mgr;
+ if (shm->flags & TEE_SHM_POOL) {
+ struct tee_shm_pool_mgr *poolm;
+
+ if (shm->flags & TEE_SHM_DMA_BUF)
+ poolm = teedev->pool->dma_buf_mgr;
+ else
+ poolm = teedev->pool->private_mgr;
+
+ poolm->ops->free(poolm, shm);
+ } else if (shm->flags & TEE_SHM_REGISTER) {
+ size_t n;
+ int rc = teedev->desc->ops->shm_unregister(shm->ctx, shm);
+
+ if (rc)
+ dev_err(teedev->dev.parent,
+ "unregister shm %p failed: %d", shm, rc);
+
+ for (n = 0; n < shm->num_pages; n++)
+ put_page(shm->pages[n]);
+
+ kfree(shm->pages);
+ }

- poolm->ops->free(poolm, shm);
kfree(shm);

tee_device_put(teedev);
@@ -76,6 +92,10 @@ static int tee_shm_op_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;

+ /* Refuse sharing shared memory provided by application */
+ if (shm->flags & TEE_SHM_REGISTER)
+ return -EINVAL;
+
return remap_pfn_range(vma, vma->vm_start, shm->paddr >> PAGE_SHIFT,
size, vma->vm_page_prot);
}
@@ -89,26 +109,20 @@ static const struct dma_buf_ops tee_shm_dma_buf_ops = {
.mmap = tee_shm_op_mmap,
};

-/**
- * tee_shm_alloc() - Allocate shared memory
- * @ctx: Context that allocates the shared memory
- * @size: Requested size of shared memory
- * @flags: Flags setting properties for the requested shared memory.
- *
- * Memory allocated as global shared memory is automatically freed when the
- * TEE file pointer is closed. The @flags field uses the bits defined by
- * TEE_SHM_* in <linux/tee_drv.h>. TEE_SHM_MAPPED must currently always be
- * set. If TEE_SHM_DMA_BUF global shared memory will be allocated and
- * associated with a dma-buf handle, else driver private memory.
- */
-struct tee_shm *tee_shm_alloc(struct tee_context *ctx, size_t size, u32 flags)
+struct tee_shm *__tee_shm_alloc(struct tee_context *ctx,
+ struct tee_device *teedev,
+ size_t size, u32 flags)
{
- struct tee_device *teedev = ctx->teedev;
struct tee_shm_pool_mgr *poolm = NULL;
struct tee_shm *shm;
void *ret;
int rc;

+ if (ctx && ctx->teedev != teedev) {
+ dev_err(teedev->dev.parent, "ctx and teedev mismatch\n");
+ return ERR_PTR(-EINVAL);
+ }
+
if (!(flags & TEE_SHM_MAPPED)) {
dev_err(teedev->dev.parent,
"only mapped allocations supported\n");
@@ -135,7 +149,7 @@ struct tee_shm *tee_shm_alloc(struct tee_context *ctx, size_t size, u32 flags)
goto err_dev_put;
}

- shm->flags = flags;
+ shm->flags = flags | TEE_SHM_POOL;
shm->teedev = teedev;
shm->ctx = ctx;
if (flags & TEE_SHM_DMA_BUF)
@@ -171,9 +185,12 @@ struct tee_shm *tee_shm_alloc(struct tee_context *ctx, size_t size, u32 flags)
goto err_rem;
}
}
- mutex_lock(&teedev->mutex);
- list_add_tail(&shm->link, &ctx->list_shm);
- mutex_unlock(&teedev->mutex);
+
+ if (ctx) {
+ mutex_lock(&teedev->mutex);
+ list_add_tail(&shm->link, &ctx->list_shm);
+ mutex_unlock(&teedev->mutex);
+ }

return shm;
err_rem:
@@ -188,8 +205,139 @@ struct tee_shm *tee_shm_alloc(struct tee_context *ctx, size_t size, u32 flags)
tee_device_put(teedev);
return ret;
}
+/**
+ * tee_shm_alloc() - Allocate shared memory
+ * @ctx: Context that allocates the shared memory
+ * @size: Requested size of shared memory
+ * @flags: Flags setting properties for the requested shared memory.
+ *
+ * Memory allocated as global shared memory is automatically freed when the
+ * TEE file pointer is closed. The @flags field uses the bits defined by
+ * TEE_SHM_* in <linux/tee_drv.h>. TEE_SHM_MAPPED must currently always be
+ * set. If TEE_SHM_DMA_BUF global shared memory will be allocated and
+ * associated with a dma-buf handle, else driver private memory.
+ */
+struct tee_shm *tee_shm_alloc(struct tee_context *ctx, size_t size, u32 flags)
+{
+ return __tee_shm_alloc(ctx, ctx->teedev, size, flags);
+}
EXPORT_SYMBOL_GPL(tee_shm_alloc);

+struct tee_shm *tee_shm_priv_alloc(struct tee_device *teedev, size_t size)
+{
+ return __tee_shm_alloc(NULL, teedev, size, TEE_SHM_MAPPED);
+}
+EXPORT_SYMBOL_GPL(tee_shm_priv_alloc);
+
+struct tee_shm *tee_shm_register(struct tee_context *ctx, unsigned long addr,
+ size_t length, u32 flags)
+{
+ struct tee_device *teedev = ctx->teedev;
+ const u32 req_flags = TEE_SHM_DMA_BUF | TEE_SHM_USER_MAPPED;
+ struct tee_shm *shm;
+ void *ret;
+ int rc;
+ int num_pages;
+ unsigned long start;
+
+ if (flags != req_flags)
+ return ERR_PTR(-ENOTSUPP);
+
+ if (!tee_device_get(teedev))
+ return ERR_PTR(-EINVAL);
+
+ if (!teedev->desc->ops->shm_register ||
+ !teedev->desc->ops->shm_unregister) {
+ tee_device_put(teedev);
+ return ERR_PTR(-ENOTSUPP);
+ }
+
+ shm = kzalloc(sizeof(*shm), GFP_KERNEL);
+ if (!shm) {
+ ret = ERR_PTR(-ENOMEM);
+ goto err;
+ }
+
+ shm->flags = flags | TEE_SHM_REGISTER;
+ shm->teedev = teedev;
+ shm->ctx = ctx;
+ shm->id = -1;
+ start = rounddown(addr, PAGE_SIZE);
+ shm->offset = addr - start;
+ shm->size = length;
+ num_pages = (roundup(addr + length, PAGE_SIZE) - start) / PAGE_SIZE;
+ shm->pages = kcalloc(num_pages, sizeof(*shm->pages), GFP_KERNEL);
+ if (!shm->pages) {
+ ret = ERR_PTR(-ENOMEM);
+ goto err;
+ }
+
+ rc = get_user_pages_fast(start, num_pages, 1, shm->pages);
+ if (rc > 0)
+ shm->num_pages = rc;
+ if (rc != num_pages) {
+ if (rc > 0)
+ rc = -ENOMEM;
+ ret = ERR_PTR(rc);
+ goto err;
+ }
+
+ mutex_lock(&teedev->mutex);
+ shm->id = idr_alloc(&teedev->idr, shm, 1, 0, GFP_KERNEL);
+ mutex_unlock(&teedev->mutex);
+
+ if (shm->id < 0) {
+ ret = ERR_PTR(shm->id);
+ goto err;
+ }
+
+ rc = teedev->desc->ops->shm_register(ctx, shm, shm->pages,
+ shm->num_pages);
+ if (rc) {
+ ret = ERR_PTR(rc);
+ goto err;
+ }
+
+ if (flags & TEE_SHM_DMA_BUF) {
+ DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
+
+ exp_info.ops = &tee_shm_dma_buf_ops;
+ exp_info.size = shm->size;
+ exp_info.flags = O_RDWR;
+ exp_info.priv = shm;
+
+ shm->dmabuf = dma_buf_export(&exp_info);
+ if (IS_ERR(shm->dmabuf)) {
+ ret = ERR_CAST(shm->dmabuf);
+ teedev->desc->ops->shm_unregister(ctx, shm);
+ goto err;
+ }
+ }
+
+ mutex_lock(&teedev->mutex);
+ list_add_tail(&shm->link, &ctx->list_shm);
+ mutex_unlock(&teedev->mutex);
+
+ return shm;
+err:
+ if (shm) {
+ size_t n;
+
+ if (shm->id >= 0) {
+ mutex_lock(&teedev->mutex);
+ idr_remove(&teedev->idr, shm->id);
+ mutex_unlock(&teedev->mutex);
+ }
+ for (n = 0; n < shm->num_pages; n++)
+ put_page(shm->pages[n]);
+ kfree(shm->pages);
+ }
+ kfree(shm);
+ tee_device_put(teedev);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(tee_shm_register);
+
/**
* tee_shm_get_fd() - Increase reference count and return file descriptor
* @shm: Shared memory handle
@@ -197,10 +345,9 @@ EXPORT_SYMBOL_GPL(tee_shm_alloc);
*/
int tee_shm_get_fd(struct tee_shm *shm)
{
- u32 req_flags = TEE_SHM_MAPPED | TEE_SHM_DMA_BUF;
int fd;

- if ((shm->flags & req_flags) != req_flags)
+ if (!(shm->flags & TEE_SHM_DMA_BUF))
return -EINVAL;

fd = dma_buf_fd(shm->dmabuf, O_CLOEXEC);
@@ -238,6 +385,8 @@ EXPORT_SYMBOL_GPL(tee_shm_free);
*/
int tee_shm_va2pa(struct tee_shm *shm, void *va, phys_addr_t *pa)
{
+ if (!(shm->flags & TEE_SHM_MAPPED))
+ return -EINVAL;
/* Check that we're in the range of the shm */
if ((char *)va < (char *)shm->kaddr)
return -EINVAL;
@@ -258,6 +407,8 @@ EXPORT_SYMBOL_GPL(tee_shm_va2pa);
*/
int tee_shm_pa2va(struct tee_shm *shm, phys_addr_t pa, void **va)
{
+ if (!(shm->flags & TEE_SHM_MAPPED))
+ return -EINVAL;
/* Check that we're in the range of the shm */
if (pa < shm->paddr)
return -EINVAL;
@@ -284,6 +435,8 @@ EXPORT_SYMBOL_GPL(tee_shm_pa2va);
*/
void *tee_shm_get_va(struct tee_shm *shm, size_t offs)
{
+ if (!(shm->flags & TEE_SHM_MAPPED))
+ return ERR_PTR(-EINVAL);
if (offs >= shm->size)
return ERR_PTR(-EINVAL);
return (char *)shm->kaddr + offs;
diff --git a/include/linux/tee_drv.h b/include/linux/tee_drv.h
index e9be4a4..70b9c73 100644
--- a/include/linux/tee_drv.h
+++ b/include/linux/tee_drv.h
@@ -25,8 +25,12 @@
* specific TEE driver.
*/

-#define TEE_SHM_MAPPED 0x1 /* Memory mapped by the kernel */
-#define TEE_SHM_DMA_BUF 0x2 /* Memory with dma-buf handle */
+#define TEE_SHM_MAPPED BIT(0) /* Memory mapped by the kernel */
+#define TEE_SHM_DMA_BUF BIT(1) /* Memory with dma-buf handle */
+#define TEE_SHM_EXT_DMA_BUF BIT(2) /* Memory with dma-buf handle */
+#define TEE_SHM_REGISTER BIT(3) /* Memory registered in secure world */
+#define TEE_SHM_USER_MAPPED BIT(4) /* Memory mapped in user space */
+#define TEE_SHM_POOL BIT(5) /* Memory allocated from pool */

struct device;
struct tee_device;
@@ -76,6 +80,8 @@ struct tee_param {
* @cancel_req: request cancel of an ongoing invoke or open
* @supp_revc: called for supplicant to get a command
* @supp_send: called for supplicant to send a response
+ * @shm_register: register shared memory buffer in TEE
+ * @shm_unregister: unregister shared memory buffer in TEE
*/
struct tee_driver_ops {
void (*get_version)(struct tee_device *teedev,
@@ -94,6 +100,9 @@ struct tee_driver_ops {
struct tee_param *param);
int (*supp_send)(struct tee_context *ctx, u32 ret, u32 num_params,
struct tee_param *param);
+ int (*shm_register)(struct tee_context *ctx, struct tee_shm *shm,
+ struct page **pages, size_t num_pages);
+ int (*shm_unregister)(struct tee_context *ctx, struct tee_shm *shm);
};

/**
@@ -302,6 +311,30 @@ void *tee_get_drvdata(struct tee_device *teedev);
struct tee_shm *tee_shm_alloc(struct tee_context *ctx, size_t size, u32 flags);

/**
+ * tee_shm_priv_alloc() - Allocate shared memory privately
+ * @dev: Device that allocates the shared memory
+ * @size: Requested size of shared memory
+ *
+ * Allocates shared memory buffer that is not associated with any client
+ * context. Such buffers are owned by TEE driver and used for internal calls.
+ *
+ * @returns a pointer to 'struct tee_shm'
+ */
+struct tee_shm *tee_shm_priv_alloc(struct tee_device *teedev, size_t size);
+
+/**
+ * tee_shm_register() - Register shared memory buffer
+ * @ctx: Context that registers the shared memory
+ * @addr: Address is userspace of the shared buffer
+ * @length: Length of the shared buffer
+ * @flags: Flags setting properties for the requested shared memory.
+ *
+ * @returns a pointer to 'struct tee_shm'
+ */
+struct tee_shm *tee_shm_register(struct tee_context *ctx, unsigned long addr,
+ size_t length, u32 flags);
+
+/**
* tee_shm_free() - Free shared memory
* @shm: Handle to shared memory to free
*/
@@ -366,4 +399,14 @@ int tee_shm_get_id(struct tee_shm *shm);
*/
struct tee_shm *tee_shm_get_from_id(struct tee_context *ctx, int id);

+/**
+ * tee_shm_is_registered() - Check if shared memory object in registered in TEE
+ * @shm: Shared memory handle
+ * @returns true if object is registered in TEE
+ */
+static inline bool tee_shm_is_registered(struct tee_shm *shm)
+{
+ return shm && (shm->flags & TEE_SHM_REGISTER);
+}
+
#endif /*__TEE_DRV_H*/
diff --git a/include/uapi/linux/tee.h b/include/uapi/linux/tee.h
index 688782e..d41a07a 100644
--- a/include/uapi/linux/tee.h
+++ b/include/uapi/linux/tee.h
@@ -50,6 +50,7 @@

#define TEE_GEN_CAP_GP (1 << 0)/* GlobalPlatform compliant TEE */
#define TEE_GEN_CAP_PRIVILEGED (1 << 1)/* Privileged device (for supplicant) */
+#define TEE_GEN_CAP_REG_MEM (1 << 2)/* Supports registering shared memory */

/*
* TEE Implementation ID
@@ -332,6 +333,35 @@ struct tee_iocl_supp_send_arg {
#define TEE_IOC_SUPPL_SEND _IOR(TEE_IOC_MAGIC, TEE_IOC_BASE + 7, \
struct tee_ioctl_buf_data)

+/**
+ * struct tee_ioctl_shm_register_data - Shared memory register argument
+ * @addr: [in] Start address of shared memory to register
+ * @length: [in/out] Length of shared memory to register
+ * @flags: [in/out] Flags to/from registration.
+ * @id: [out] Identifier of the shared memory
+ *
+ * The flags field should currently be zero as input. Updated by the call
+ * with actual flags as defined by TEE_IOCTL_SHM_* above.
+ * This structure is used as argument for TEE_IOC_SHM_REGISTER below.
+ */
+struct tee_ioctl_shm_register_data {
+ __u64 addr;
+ __u64 length;
+ __u32 flags;
+ __s32 id;
+};
+
+/**
+ * TEE_IOC_SHM_REGISTER - Register shared memory argument
+ *
+ * Registers shared memory between the user space process and secure OS.
+ *
+ * Returns a file descriptor on success or < 0 on failure
+ *
+ * The shared memory is unregisterred when the descriptor is closed.
+ */
+#define TEE_IOC_SHM_REGISTER _IOWR(TEE_IOC_MAGIC, TEE_IOC_BASE + 9, \
+ struct tee_ioctl_shm_register_data)
/*
* Five syscalls are used when communicating with the TEE driver.
* open(): opens the device associated with the driver
--
2.7.4


From 1579887815324670253@xxx Fri Sep 29 15:20:32 +0000 2017
X-GM-THRID: 1579807631250973084
X-Gmail-Labels: Inbox,Category Forums

2017-10-13 19:35:22

by Volodymyr Babchuk

[permalink] [raw]
Subject: [PATCH v1 08/14] tee: optee: add registered shared parameters handling

From: Volodymyr Babchuk <[email protected]>

Now, when client applications can register own shared buffers in OP-TEE,
we need to extend ABI for parameter passing to/from OP-TEE.

So, if OP-TEE core detects that parameter belongs to registered shared
memory, it will use corresponding parameter attribute.

Signed-off-by: Volodymyr Babchuk <[email protected]>
---

* Removed check on u.memref.size at optee_from_msg_param(), because
it caused fail on some tests. According to GlobalPlatform specs,
we don't need to check returned SHM size, because in this case
TA can indicate desired buffer size.

---
drivers/tee/optee/core.c | 78 ++++++++++++++++++++++++++++++++++++++----------
1 file changed, 63 insertions(+), 15 deletions(-)

diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c
index 4d448bf..3729ebb 100644
--- a/drivers/tee/optee/core.c
+++ b/drivers/tee/optee/core.c
@@ -97,6 +97,25 @@ int optee_from_msg_param(struct tee_param *params, size_t num_params,
return rc;
}
break;
+ case OPTEE_MSG_ATTR_TYPE_RMEM_INPUT:
+ case OPTEE_MSG_ATTR_TYPE_RMEM_OUTPUT:
+ case OPTEE_MSG_ATTR_TYPE_RMEM_INOUT:
+ p->attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT +
+ attr - OPTEE_MSG_ATTR_TYPE_RMEM_INPUT;
+ p->u.memref.size = mp->u.rmem.size;
+ shm = (struct tee_shm *)(unsigned long)
+ mp->u.rmem.shm_ref;
+
+ if (!shm) {
+ p->u.memref.shm_offs = 0;
+ p->u.memref.shm = NULL;
+ break;
+ }
+ p->u.memref.shm_offs = mp->u.rmem.offs;
+ p->u.memref.shm = shm;
+
+ break;
+
default:
return -EINVAL;
}
@@ -104,6 +123,46 @@ int optee_from_msg_param(struct tee_param *params, size_t num_params,
return 0;
}

+static int to_msg_param_tmp_mem(struct optee_msg_param *mp,
+ const struct tee_param *p)
+{
+ int rc;
+ phys_addr_t pa;
+
+ mp->attr = OPTEE_MSG_ATTR_TYPE_TMEM_INPUT + p->attr -
+ TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT;
+
+ mp->u.tmem.shm_ref = (unsigned long)p->u.memref.shm;
+ mp->u.tmem.size = p->u.memref.size;
+
+ if (!p->u.memref.shm) {
+ mp->u.tmem.buf_ptr = 0;
+ return 0;
+ }
+
+ rc = tee_shm_get_pa(p->u.memref.shm, p->u.memref.shm_offs, &pa);
+ if (rc)
+ return rc;
+
+ mp->u.tmem.buf_ptr = pa;
+ mp->attr |= OPTEE_MSG_ATTR_CACHE_PREDEFINED <<
+ OPTEE_MSG_ATTR_CACHE_SHIFT;
+
+ return 0;
+}
+
+static int to_msg_param_reg_mem(struct optee_msg_param *mp,
+ const struct tee_param *p)
+{
+ mp->attr = OPTEE_MSG_ATTR_TYPE_RMEM_INPUT + p->attr -
+ TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT;
+
+ mp->u.rmem.shm_ref = (unsigned long)p->u.memref.shm;
+ mp->u.rmem.size = p->u.memref.size;
+ mp->u.rmem.offs = p->u.memref.shm_offs;
+ return 0;
+}
+
/**
* optee_to_msg_param() - convert from struct tee_params to OPTEE_MSG parameters
* @msg_params: OPTEE_MSG parameters
@@ -116,7 +175,6 @@ int optee_to_msg_param(struct optee_msg_param *msg_params, size_t num_params,
{
int rc;
size_t n;
- phys_addr_t pa;

for (n = 0; n < num_params; n++) {
const struct tee_param *p = params + n;
@@ -139,22 +197,12 @@ int optee_to_msg_param(struct optee_msg_param *msg_params, size_t num_params,
case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT:
case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT:
case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT:
- mp->attr = OPTEE_MSG_ATTR_TYPE_TMEM_INPUT +
- p->attr -
- TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT;
- mp->u.tmem.shm_ref = (unsigned long)p->u.memref.shm;
- mp->u.tmem.size = p->u.memref.size;
- if (!p->u.memref.shm) {
- mp->u.tmem.buf_ptr = 0;
- break;
- }
- rc = tee_shm_get_pa(p->u.memref.shm,
- p->u.memref.shm_offs, &pa);
+ if (tee_shm_is_registered(p->u.memref.shm))
+ rc = to_msg_param_reg_mem(mp, p);
+ else
+ rc = to_msg_param_tmp_mem(mp, p);
if (rc)
return rc;
- mp->u.tmem.buf_ptr = pa;
- mp->attr |= OPTEE_MSG_ATTR_CACHE_PREDEFINED <<
- OPTEE_MSG_ATTR_CACHE_SHIFT;
break;
default:
return -EINVAL;
--
2.7.4


From 1579807677666738661@xxx Thu Sep 28 18:06:47 +0000 2017
X-GM-THRID: 1579807677666738661
X-Gmail-Labels: Inbox,Category Forums

2017-10-13 19:35:53

by Volodymyr Babchuk

[permalink] [raw]
Subject: [PATCH v1 13/14] tee: use reference counting for tee_context

From: Volodymyr Babchuk <[email protected]>

We need to ensure that tee_context is present until last
shared buffer will be freed.

Signed-off-by: Volodymyr Babchuk <[email protected]>
---
drivers/tee/tee_core.c | 40 +++++++++++++++++++++++++++++++---------
drivers/tee/tee_private.h | 3 +++
drivers/tee/tee_shm.c | 7 +++++++
include/linux/tee_drv.h | 7 +++++++
4 files changed, 48 insertions(+), 9 deletions(-)

diff --git a/drivers/tee/tee_core.c b/drivers/tee/tee_core.c
index 295910f..3d49ac2 100644
--- a/drivers/tee/tee_core.c
+++ b/drivers/tee/tee_core.c
@@ -54,6 +54,7 @@ static int tee_open(struct inode *inode, struct file *filp)
goto err;
}

+ kref_init(&ctx->refcount);
ctx->teedev = teedev;
INIT_LIST_HEAD(&ctx->list_shm);
filp->private_data = ctx;
@@ -68,19 +69,40 @@ static int tee_open(struct inode *inode, struct file *filp)
return rc;
}

-static int tee_release(struct inode *inode, struct file *filp)
+void teedev_ctx_get(struct tee_context *ctx)
{
- struct tee_context *ctx = filp->private_data;
- struct tee_device *teedev = ctx->teedev;
- struct tee_shm *shm;
+ if (ctx->releasing)
+ return;
+
+ kref_get(&ctx->refcount);
+}

+static void teedev_ctx_release(struct kref *ref)
+{
+ struct tee_context *ctx = container_of(ref, struct tee_context,
+ refcount);
+ ctx->releasing = true;
ctx->teedev->desc->ops->release(ctx);
- mutex_lock(&ctx->teedev->mutex);
- list_for_each_entry(shm, &ctx->list_shm, link)
- shm->ctx = NULL;
- mutex_unlock(&ctx->teedev->mutex);
kfree(ctx);
- tee_device_put(teedev);
+}
+
+void teedev_ctx_put(struct tee_context *ctx)
+{
+ if (ctx->releasing)
+ return;
+
+ kref_put(&ctx->refcount, teedev_ctx_release);
+}
+
+static void teedev_close_context(struct tee_context *ctx)
+{
+ tee_device_put(ctx->teedev);
+ teedev_ctx_put(ctx);
+}
+
+static int tee_release(struct inode *inode, struct file *filp)
+{
+ teedev_close_context(filp->private_data);
return 0;
}

diff --git a/drivers/tee/tee_private.h b/drivers/tee/tee_private.h
index 2bc2b5a..85d99d6 100644
--- a/drivers/tee/tee_private.h
+++ b/drivers/tee/tee_private.h
@@ -73,4 +73,7 @@ int tee_shm_get_fd(struct tee_shm *shm);
bool tee_device_get(struct tee_device *teedev);
void tee_device_put(struct tee_device *teedev);

+void teedev_ctx_get(struct tee_context *ctx);
+void teedev_ctx_put(struct tee_context *ctx);
+
#endif /*TEE_PRIVATE_H*/
diff --git a/drivers/tee/tee_shm.c b/drivers/tee/tee_shm.c
index 177e341..b48f83d 100644
--- a/drivers/tee/tee_shm.c
+++ b/drivers/tee/tee_shm.c
@@ -53,6 +53,9 @@ static void tee_shm_release(struct tee_shm *shm)
kfree(shm->pages);
}

+ if (shm->ctx)
+ teedev_ctx_put(shm->ctx);
+
kfree(shm);

tee_device_put(teedev);
@@ -187,6 +190,7 @@ struct tee_shm *__tee_shm_alloc(struct tee_context *ctx,
}

if (ctx) {
+ teedev_ctx_get(ctx);
mutex_lock(&teedev->mutex);
list_add_tail(&shm->link, &ctx->list_shm);
mutex_unlock(&teedev->mutex);
@@ -252,6 +256,8 @@ struct tee_shm *tee_shm_register(struct tee_context *ctx, unsigned long addr,
return ERR_PTR(-ENOTSUPP);
}

+ teedev_ctx_get(ctx);
+
shm = kzalloc(sizeof(*shm), GFP_KERNEL);
if (!shm) {
ret = ERR_PTR(-ENOMEM);
@@ -333,6 +339,7 @@ struct tee_shm *tee_shm_register(struct tee_context *ctx, unsigned long addr,
kfree(shm->pages);
}
kfree(shm);
+ teedev_ctx_put(ctx);
tee_device_put(teedev);
return ret;
}
diff --git a/include/linux/tee_drv.h b/include/linux/tee_drv.h
index 5489c38..9cd4e5f 100644
--- a/include/linux/tee_drv.h
+++ b/include/linux/tee_drv.h
@@ -17,6 +17,7 @@

#include <linux/types.h>
#include <linux/idr.h>
+#include <linux/kref.h>
#include <linux/list.h>
#include <linux/tee.h>

@@ -42,11 +43,17 @@ struct tee_shm_pool;
* @teedev: pointer to this drivers struct tee_device
* @list_shm: List of shared memory object owned by this context
* @data: driver specific context data, managed by the driver
+ * @refcount: reference counter for this structure
+ * @releasing: flag that indicates if context is being released right now.
+ * It is needed to break circular dependency on context during
+ * shared memory release.
*/
struct tee_context {
struct tee_device *teedev;
struct list_head list_shm;
void *data;
+ struct kref refcount;
+ bool releasing;
};

struct tee_param_memref {
--
2.7.4


From 1579807807429777945@xxx Thu Sep 28 18:08:50 +0000 2017
X-GM-THRID: 1579807807429777945
X-Gmail-Labels: Inbox,Category Forums

2017-10-13 19:34:35

by Volodymyr Babchuk

[permalink] [raw]
Subject: [PATCH v1 04/14] tee: shm: add page accessor functions

From: Volodymyr Babchuk <[email protected]>

In order to register a shared buffer in TEE, we need accessor
function that return list of pages for that buffer.

Signed-off-by: Volodymyr Babchuk <[email protected]>
---

* Removed check for validity of num_pages pointer. As Yury Norov pointed,
this check can lead to mistakes in the future.

---
include/linux/tee_drv.h | 13 +++++++++++++
1 file changed, 13 insertions(+)

diff --git a/include/linux/tee_drv.h b/include/linux/tee_drv.h
index 0d7f2a5..5489c38 100644
--- a/include/linux/tee_drv.h
+++ b/include/linux/tee_drv.h
@@ -394,6 +394,19 @@ static inline size_t tee_shm_get_size(struct tee_shm *shm)
}

/**
+ * tee_shm_get_pages() - Get list of pages that hold shared buffer
+ * @shm: Shared memory handle
+ * @num_pages: Number of pages will be stored there
+ * @returns pointer to pages array
+ */
+static inline struct page **tee_shm_get_pages(struct tee_shm *shm,
+ size_t *num_pages)
+{
+ *num_pages = shm->num_pages;
+ return shm->pages;
+}
+
+/**
* tee_shm_get_page_offset() - Get shared buffer offset from page start
* @shm: Shared memory handle
* @returns page offset of shared buffer
--
2.7.4


From 1579868769100946394@xxx Fri Sep 29 10:17:48 +0000 2017
X-GM-THRID: 1579807647998704518
X-Gmail-Labels: Inbox,Category Forums

2017-10-13 19:37:17

by Volodymyr Babchuk

[permalink] [raw]
Subject: [PATCH v1 11/14] tee: optee: add optee-specific shared pool implementation

From: Volodymyr Babchuk <[email protected]>

This is simple pool that uses kernel page allocator. This pool can be
used in case OP-TEE supports dynamic shared memory.

Signed-off-by: Volodymyr Babchuk <[email protected]>
---
drivers/tee/optee/Makefile | 1 +
drivers/tee/optee/shm_pool.c | 75 ++++++++++++++++++++++++++++++++++++++++++++
drivers/tee/optee/shm_pool.h | 23 ++++++++++++++
3 files changed, 99 insertions(+)
create mode 100644 drivers/tee/optee/shm_pool.c
create mode 100644 drivers/tee/optee/shm_pool.h

diff --git a/drivers/tee/optee/Makefile b/drivers/tee/optee/Makefile
index 92fe578..220cf42 100644
--- a/drivers/tee/optee/Makefile
+++ b/drivers/tee/optee/Makefile
@@ -3,3 +3,4 @@ optee-objs += core.o
optee-objs += call.o
optee-objs += rpc.o
optee-objs += supp.o
+optee-objs += shm_pool.o
diff --git a/drivers/tee/optee/shm_pool.c b/drivers/tee/optee/shm_pool.c
new file mode 100644
index 0000000..4939781
--- /dev/null
+++ b/drivers/tee/optee/shm_pool.c
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2015, Linaro Limited
+ * Copyright (c) 2017, EPAM Systems
+ *
+ * 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/genalloc.h>
+#include <linux/slab.h>
+#include <linux/tee_drv.h>
+#include "optee_private.h"
+#include "optee_smc.h"
+#include "shm_pool.h"
+
+static int pool_op_alloc(struct tee_shm_pool_mgr *poolm,
+ struct tee_shm *shm, size_t size)
+{
+ unsigned int order = get_order(size);
+ struct page *page;
+
+ page = alloc_pages(GFP_KERNEL | __GFP_ZERO, order);
+ if (!page)
+ return -ENOMEM;
+
+ shm->kaddr = page_address(page);
+ shm->paddr = page_to_phys(page);
+ shm->size = PAGE_SIZE << order;
+
+ return 0;
+}
+
+static void pool_op_free(struct tee_shm_pool_mgr *poolm,
+ struct tee_shm *shm)
+{
+ free_pages((unsigned long)shm->kaddr, get_order(shm->size));
+ shm->kaddr = NULL;
+}
+
+static void pool_op_destroy_poolmgr(struct tee_shm_pool_mgr *poolm)
+{
+ kfree(poolm);
+}
+
+static const struct tee_shm_pool_mgr_ops pool_ops = {
+ .alloc = pool_op_alloc,
+ .free = pool_op_free,
+ .destroy_poolmgr = pool_op_destroy_poolmgr,
+};
+
+/**
+ * optee_shm_pool_alloc_pages() - create page-based allocator pool
+ *
+ * This pool is used when OP-TEE supports dymanic SHM. In this case
+ * command buffers and such are allocated from kernel's own memory.
+ */
+struct tee_shm_pool_mgr *optee_shm_pool_alloc_pages(void)
+{
+ struct tee_shm_pool_mgr *mgr = kzalloc(sizeof(*mgr), GFP_KERNEL);
+
+ if (!mgr)
+ return ERR_PTR(-ENOMEM);
+
+ mgr->ops = &pool_ops;
+
+ return mgr;
+}
diff --git a/drivers/tee/optee/shm_pool.h b/drivers/tee/optee/shm_pool.h
new file mode 100644
index 0000000..4e753c3
--- /dev/null
+++ b/drivers/tee/optee/shm_pool.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2015, Linaro Limited
+ * Copyright (c) 2016, EPAM Systems
+ *
+ * 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.
+ *
+ */
+
+#ifndef SHM_POOL_H
+#define SHM_POOL_H
+
+#include <linux/tee_drv.h>
+
+struct tee_shm_pool_mgr *optee_shm_pool_alloc_pages(void);
+
+#endif
--
2.7.4


From 1579807692076557678@xxx Thu Sep 28 18:07:00 +0000 2017
X-GM-THRID: 1579807692076557678
X-Gmail-Labels: Inbox,Category Forums