tee: new ioctl to a register tee_shm from a dmabuf file descriptor
authorEtienne Carriere <etienne.carriere@linaro.org>
Thu, 27 Oct 2016 21:18:35 +0000 (23:18 +0200)
committerLeonard Crestez <leonard.crestez@nxp.com>
Wed, 17 Apr 2019 23:51:34 +0000 (02:51 +0300)
This change allows userland to create a tee_shm object that refers
to a dmabuf reference.

Userland provides a dmabuf file descriptor as buffer reference.
The created tee_shm object exported as a brand new dmabuf reference
used to provide a clean fd to userland. Userland shall closed this new
fd to release the tee_shm object resources. The initial dmabuf resources
are tracked independently through original dmabuf file descriptor.

Once the buffer is registered and until it is released, TEE driver
keeps a refcount on the registered dmabuf structure.

This change only support dmabuf references that relates to physically
contiguous memory buffers.

New tee_shm flag to identify tee_shm objects built from a registered
dmabuf: TEE_SHM_EXT_DMA_BUF. Such tee_shm structures are flagged both
TEE_SHM_DMA_BUF and TEE_SHM_EXT_DMA_BUF.

Signed-off-by: Etienne Carriere <etienne.carriere@linaro.org>
Reviewed-by: Jens Wiklander <jens.wiklander@linaro.org>
From: https://github.com/linaro-swg/linux.git
(cherry picked from commit 41e21e5c405530590dc2dd10b2a8dbe64589840f)

(Vipul: Fixed merge conflicts)
Conflicts:
drivers/tee/tee_core.c
drivers/tee/tee_shm.c
include/linux/tee_drv.h
Signed-off-by: Vipul Kumar <vipul_kumar@mentor.com>
drivers/tee/tee_core.c
drivers/tee/tee_shm.c
include/linux/tee_drv.h
include/uapi/linux/tee.h

index dd46b75..57a0acf 100644 (file)
@@ -158,6 +158,42 @@ static int tee_ioctl_shm_alloc(struct tee_context *ctx,
        return ret;
 }
 
+static int tee_ioctl_shm_register_fd(struct tee_context *ctx,
+                       struct tee_ioctl_shm_register_fd_data __user *udata)
+{
+       struct tee_ioctl_shm_register_fd_data data;
+       struct tee_shm *shm;
+       long ret;
+
+       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_fd(ctx, data.fd);
+       if (IS_ERR_OR_NULL(shm))
+               return -EINVAL;
+
+       data.id = shm->id;
+       data.flags = shm->flags;
+       data.size = 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
 tee_ioctl_shm_register(struct tee_context *ctx,
                       struct tee_ioctl_shm_register_data __user *udata)
@@ -648,6 +684,8 @@ static long tee_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
                return tee_ioctl_shm_alloc(ctx, uarg);
        case TEE_IOC_SHM_REGISTER:
                return tee_ioctl_shm_register(ctx, uarg);
+       case TEE_IOC_SHM_REGISTER_FD:
+               return tee_ioctl_shm_register_fd(ctx, uarg);
        case TEE_IOC_OPEN_SESSION:
                return tee_ioctl_open_session(ctx, uarg);
        case TEE_IOC_INVOKE:
index 0b9ab1d..77be0a4 100644 (file)
 #include <linux/tee_drv.h>
 #include "tee_private.h"
 
+/* extra references appended to shm object for registered shared memory */
+struct tee_shm_dmabuf_ref {
+       struct tee_shm shm;
+       struct dma_buf *dmabuf;
+       struct dma_buf_attachment *attach;
+       struct sg_table *sgt;
+};
+
 static void tee_shm_release(struct tee_shm *shm)
 {
        struct tee_device *teedev = shm->teedev;
@@ -30,7 +38,15 @@ static void tee_shm_release(struct tee_shm *shm)
                list_del(&shm->link);
        mutex_unlock(&teedev->mutex);
 
-       if (shm->flags & TEE_SHM_POOL) {
+       if (shm->flags & TEE_SHM_EXT_DMA_BUF) {
+               struct tee_shm_dmabuf_ref *ref;
+
+               ref = container_of(shm, struct tee_shm_dmabuf_ref, shm);
+               dma_buf_unmap_attachment(ref->attach, ref->sgt,
+                                        DMA_BIDIRECTIONAL);
+               dma_buf_detach(shm->dmabuf, ref->attach);
+               dma_buf_put(ref->dmabuf);
+       } else {
                struct tee_shm_pool_mgr *poolm;
 
                if (shm->flags & TEE_SHM_DMA_BUF)
@@ -39,25 +55,9 @@ static void tee_shm_release(struct tee_shm *shm)
                        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);
        }
 
-       if (shm->ctx)
-               teedev_ctx_put(shm->ctx);
-
        kfree(shm);
-
        tee_device_put(teedev);
 }
 
@@ -342,6 +342,100 @@ err:
 }
 EXPORT_SYMBOL_GPL(tee_shm_register);
 
+struct tee_shm *tee_shm_register_fd(struct tee_context *ctx, int fd)
+{
+       struct tee_shm_dmabuf_ref *ref;
+       void *rc;
+       DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
+
+       if (!tee_device_get(ctx->teedev))
+               return ERR_PTR(-EINVAL);
+
+       ref = kzalloc(sizeof(*ref), GFP_KERNEL);
+       if (!ref) {
+               rc = ERR_PTR(-ENOMEM);
+               goto err;
+       }
+
+       ref->shm.ctx = ctx;
+       ref->shm.teedev = ctx->teedev;
+       ref->shm.id = -1;
+
+       ref->dmabuf = dma_buf_get(fd);
+       if (!ref->dmabuf) {
+               rc = ERR_PTR(-EINVAL);
+               goto err;
+       }
+
+       ref->attach = dma_buf_attach(ref->dmabuf, &ref->shm.teedev->dev);
+       if (IS_ERR_OR_NULL(ref->attach)) {
+               rc = ERR_PTR(-EINVAL);
+               goto err;
+       }
+
+       ref->sgt = dma_buf_map_attachment(ref->attach, DMA_BIDIRECTIONAL);
+       if (IS_ERR_OR_NULL(ref->sgt)) {
+               rc = ERR_PTR(-EINVAL);
+               goto err;
+       }
+
+       if (sg_nents(ref->sgt->sgl) != 1) {
+               rc = ERR_PTR(-EINVAL);
+               goto err;
+       }
+
+       ref->shm.paddr = sg_dma_address(ref->sgt->sgl);
+       ref->shm.size = sg_dma_len(ref->sgt->sgl);
+       ref->shm.flags = TEE_SHM_DMA_BUF | TEE_SHM_EXT_DMA_BUF;
+
+       mutex_lock(&ref->shm.teedev->mutex);
+       ref->shm.id = idr_alloc(&ref->shm.teedev->idr, &ref->shm,
+                               1, 0, GFP_KERNEL);
+       mutex_unlock(&ref->shm.teedev->mutex);
+       if (ref->shm.id < 0) {
+               rc = ERR_PTR(ref->shm.id);
+               goto err;
+       }
+
+       /* export a dmabuf to later get a userland ref */
+       exp_info.ops = &tee_shm_dma_buf_ops;
+       exp_info.size = ref->shm.size;
+       exp_info.flags = O_RDWR;
+       exp_info.priv = &ref->shm;
+
+       ref->shm.dmabuf = dma_buf_export(&exp_info);
+       if (IS_ERR(ref->shm.dmabuf)) {
+               rc = ERR_PTR(-EINVAL);
+               goto err;
+       }
+
+       mutex_lock(&ref->shm.teedev->mutex);
+       list_add_tail(&ref->shm.link, &ctx->list_shm);
+       mutex_unlock(&ref->shm.teedev->mutex);
+
+       return &ref->shm;
+
+err:
+       if (ref) {
+               if (ref->shm.id >= 0) {
+                       mutex_lock(&ctx->teedev->mutex);
+                       idr_remove(&ctx->teedev->idr, ref->shm.id);
+                       mutex_unlock(&ctx->teedev->mutex);
+               }
+               if (ref->sgt)
+                       dma_buf_unmap_attachment(ref->attach, ref->sgt,
+                                                DMA_BIDIRECTIONAL);
+               if (ref->attach)
+                       dma_buf_detach(ref->dmabuf, ref->attach);
+               if (ref->dmabuf)
+                       dma_buf_put(ref->dmabuf);
+       }
+       kfree(ref);
+       tee_device_put(ctx->teedev);
+       return rc;
+}
+EXPORT_SYMBOL_GPL(tee_shm_register_fd);
+
 /**
  * tee_shm_get_fd() - Increase reference count and return file descriptor
  * @shm:       Shared memory handle
index a2b3dfc..c40a8ce 100644 (file)
@@ -352,6 +352,16 @@ static inline bool tee_shm_is_registered(struct tee_shm *shm)
        return shm && (shm->flags & TEE_SHM_REGISTER);
 }
 
+/**
+ * tee_shm_register_fd() - Register shared memory from file descriptor
+ *
+ * @ctx:       Context that allocates the shared memory
+ * @fd:                shared memory file descriptor reference.
+ *
+ * @returns a pointer to 'struct tee_shm'
+ */
+struct tee_shm *tee_shm_register_fd(struct tee_context *ctx, int fd);
+
 /**
  * tee_shm_free() - Free shared memory
  * @shm:       Handle to shared memory to free
index 4b9eb06..08fb98f 100644 (file)
@@ -117,6 +117,35 @@ struct tee_ioctl_shm_alloc_data {
 #define TEE_IOC_SHM_ALLOC      _IOWR(TEE_IOC_MAGIC, TEE_IOC_BASE + 1, \
                                     struct tee_ioctl_shm_alloc_data)
 
+/**
+ * struct tee_ioctl_shm_register_fd_data - Shared memory registering argument
+ * @fd:                [in] file descriptor identifying the shared memory
+ * @size:      [out] Size of shared memory to allocate
+ * @flags:     [in] Flags to/from allocation.
+ * @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_ALLOC below.
+ */
+struct tee_ioctl_shm_register_fd_data {
+       __s64 fd;
+       __u64 size;
+       __u32 flags;
+       __s32 id;
+} __aligned(8);
+
+/**
+ * TEE_IOC_SHM_REGISTER_FD - register a shared memory from a file descriptor
+ *
+ * Returns a file descriptor on success or < 0 on failure
+ *
+ * The returned file descriptor refers to the shared memory object in kernel
+ * land. The shared memory is freed when the descriptor is closed.
+ */
+#define TEE_IOC_SHM_REGISTER_FD        _IOWR(TEE_IOC_MAGIC, TEE_IOC_BASE + 8, \
+                                    struct tee_ioctl_shm_register_fd_data)
+
 /**
  * struct tee_ioctl_buf_data - Variable sized buffer
  * @buf_ptr:   [in] A __user pointer to a buffer