From: Marc Kleine-Budde <m...@pengutronix.de>

Userspace accesses OP-TEE via ioctls and mmaps of the /dev/tee0
device. Replicating this in barebox is useful for verifying proper
operation of CONFIG_OPTEE by hacking libteeclient + optee_tests to
run under barebox.

Signed-off-by: Ahmad Fatoum <a.fat...@pengutronix.de>
---
 drivers/tee/optee/Kconfig |   9 +
 drivers/tee/tee_core.c    | 406 ++++++++++++++++++++++++++++++++++++++
 drivers/tee/tee_private.h |   4 +
 drivers/tee/tee_shm.c     | 142 ++++++++++++-
 include/linux/tee_drv.h   |  33 ++++
 5 files changed, 589 insertions(+), 5 deletions(-)

diff --git a/drivers/tee/optee/Kconfig b/drivers/tee/optee/Kconfig
index 4eb0dd6ac61c..3c791a10c4ac 100644
--- a/drivers/tee/optee/Kconfig
+++ b/drivers/tee/optee/Kconfig
@@ -18,3 +18,12 @@ config OPTEE
          CONFIG_BOOTM_OPTEE and PBL_OPTEE.
 
          If unsure, say n here.
+
+config OPTEE_DEVFS
+       bool "Provide /dev/tee0 interface"
+       depends on OPTEE && FS_DEVFS && EXPERIMENTAL
+       help
+         Userspace accesses OP-TEE via ioctls and mmaps of the /dev/tee0
+         device. This are no current in-tree users of this interface,
+         but it's useful for compiling libteeclient + optee_tests for
+         use inside barebox to verify proper operation of CONFIG_OPTEE.
diff --git a/drivers/tee/tee_core.c b/drivers/tee/tee_core.c
index be92e3dfc920..0bf645a310eb 100644
--- a/drivers/tee/tee_core.c
+++ b/drivers/tee/tee_core.c
@@ -82,6 +82,32 @@ void teedev_close_context(struct tee_context *ctx)
 }
 EXPORT_SYMBOL_GPL(teedev_close_context);
 
+static int tee_open(struct cdev *cdev, unsigned long flags)
+{
+       struct tee_context *ctx;
+
+       if (cdev->priv)
+               return -EBUSY;
+
+       ctx = teedev_open(container_of(cdev, struct tee_device, cdev));
+       if (IS_ERR(ctx))
+               return PTR_ERR(ctx);
+
+       cdev->priv = ctx;
+
+       return 0;
+}
+
+static int tee_release(struct cdev *cdev)
+{
+       struct tee_context *ctx = cdev->priv;
+
+       teedev_close_context(ctx);
+       cdev->priv = NULL;
+
+       return 0;
+}
+
 int tee_session_calc_client_uuid(uuid_t *uuid, u32 connection_method,
                                 const u8 connection_data[TEE_IOCTL_UUID_LEN])
 {
@@ -96,6 +122,367 @@ int tee_session_calc_client_uuid(uuid_t *uuid, u32 
connection_method,
 }
 EXPORT_SYMBOL_GPL(tee_session_calc_client_uuid);
 
+static int tee_ioctl_version(struct tee_context *ctx,
+                            struct tee_ioctl_version_data __user *uvers)
+{
+       struct tee_ioctl_version_data vers;
+
+       ctx->teedev->desc->ops->get_version(ctx->teedev, &vers);
+
+       if (ctx->teedev->desc->flags & TEE_DESC_PRIVILEGED)
+               vers.gen_caps |= TEE_GEN_CAP_PRIVILEGED;
+
+       if (copy_to_user(uvers, &vers, sizeof(vers)))
+               return -EFAULT;
+
+       return 0;
+}
+
+static int tee_ioctl_shm_alloc(struct tee_context *ctx,
+                              struct tee_ioctl_shm_alloc_data *data)
+{
+       struct tee_shm *shm;
+
+       /* Currently no input flags are supported */
+       if (data->flags)
+               return -EINVAL;
+
+       shm = tee_shm_alloc_user_buf(ctx, data->size);
+       if (IS_ERR(shm))
+               return PTR_ERR(shm);
+
+       data->id = shm->dev.id;
+       data->flags = shm->flags;
+       data->size = shm->size;
+
+       return tee_shm_get_fd(shm);
+}
+
+static int
+tee_ioctl_shm_register(struct tee_context *ctx,
+                      struct tee_ioctl_shm_register_data *data)
+{
+       struct tee_shm *shm;
+
+       /* Currently no input flags are supported */
+       if (data->flags)
+               return -EINVAL;
+
+       shm = tee_shm_register_user_buf(ctx, data->addr, data->length);
+       if (IS_ERR(shm))
+               return PTR_ERR(shm);
+
+       data->id = shm->dev.id;
+       data->flags = shm->flags;
+       data->length = shm->size;
+
+       return tee_shm_get_fd(shm);
+}
+
+static int params_from_user(struct tee_context *ctx, struct tee_param *params,
+                           size_t num_params,
+                           struct tee_ioctl_param __user *uparams)
+{
+       size_t n;
+
+       for (n = 0; n < num_params; n++) {
+               struct tee_shm *shm;
+               struct tee_ioctl_param ip;
+
+               if (copy_from_user(&ip, uparams + n, sizeof(ip)))
+                       return -EFAULT;
+
+               /* All unused attribute bits has to be zero */
+               if (ip.attr & ~TEE_IOCTL_PARAM_ATTR_MASK)
+                       return -EINVAL;
+
+               params[n].attr = ip.attr;
+               switch (ip.attr & TEE_IOCTL_PARAM_ATTR_TYPE_MASK) {
+               case TEE_IOCTL_PARAM_ATTR_TYPE_NONE:
+               case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT:
+                       break;
+               case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT:
+               case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT:
+                       params[n].u.value.a = ip.a;
+                       params[n].u.value.b = ip.b;
+                       params[n].u.value.c = ip.c;
+                       break;
+               case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT:
+               case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT:
+               case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT:
+                       /*
+                        * If a NULL pointer is passed to a TA in the TEE,
+                        * the ip.c IOCTL parameters is set to TEE_MEMREF_NULL
+                        * indicating a NULL memory reference.
+                        */
+                       if (ip.c != TEE_MEMREF_NULL) {
+                               /*
+                                * If we fail to get a pointer to a shared
+                                * memory object (and increase the ref count)
+                                * from an identifier we return an error. All
+                                * pointers that has been added in params have
+                                * an increased ref count. It's the callers
+                                * responibility to do tee_shm_put() on all
+                                * resolved pointers.
+                                */
+                               shm = tee_shm_get_from_id(ctx, ip.c);
+                               if (IS_ERR(shm))
+                                       return PTR_ERR(shm);
+
+                               /*
+                                * Ensure offset + size does not overflow
+                                * offset and does not overflow the size of
+                                * the referred shared memory object.
+                                */
+                               if ((ip.a + ip.b) < ip.a ||
+                                   (ip.a + ip.b) > shm->size) {
+                                       tee_shm_put(shm);
+                                       return -EINVAL;
+                               }
+                       } else if (ctx->cap_memref_null) {
+                               /* Pass NULL pointer to OP-TEE */
+                               shm = NULL;
+                       } else {
+                               return -EINVAL;
+                       }
+
+                       params[n].u.memref.shm_offs = ip.a;
+                       params[n].u.memref.size = ip.b;
+                       params[n].u.memref.shm = shm;
+                       break;
+               default:
+                       /* Unknown attribute */
+                       return -EINVAL;
+               }
+       }
+       return 0;
+}
+
+static int params_to_user(struct tee_ioctl_param __user *uparams,
+                         size_t num_params, struct tee_param *params)
+{
+       size_t n;
+
+       for (n = 0; n < num_params; n++) {
+               struct tee_ioctl_param __user *up = uparams + n;
+               struct tee_param *p = params + n;
+
+               switch (p->attr) {
+               case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT:
+               case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT:
+                       if (put_user(p->u.value.a, &up->a) ||
+                           put_user(p->u.value.b, &up->b) ||
+                           put_user(p->u.value.c, &up->c))
+                               return -EFAULT;
+                       break;
+               case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT:
+               case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT:
+                       if (put_user((u64)p->u.memref.size, &up->b))
+                               return -EFAULT;
+                       break;
+               default:
+                       break;
+               }
+       }
+       return 0;
+}
+
+static int tee_ioctl_open_session(struct tee_context *ctx,
+                                 struct tee_ioctl_buf_data __user *ubuf)
+{
+       int rc;
+       size_t n;
+       struct tee_ioctl_buf_data buf;
+       struct tee_ioctl_open_session_arg __user *uarg;
+       struct tee_ioctl_open_session_arg arg;
+       struct tee_ioctl_param __user *uparams = NULL;
+       struct tee_param *params = NULL;
+       bool have_session = false;
+
+       if (!ctx->teedev->desc->ops->open_session)
+               return -EINVAL;
+
+       if (copy_from_user(&buf, ubuf, sizeof(buf)))
+               return -EFAULT;
+
+       if (buf.buf_len > TEE_MAX_ARG_SIZE ||
+           buf.buf_len < sizeof(struct tee_ioctl_open_session_arg))
+               return -EINVAL;
+
+       uarg = u64_to_user_ptr(buf.buf_ptr);
+       if (copy_from_user(&arg, uarg, sizeof(arg)))
+               return -EFAULT;
+
+       if (sizeof(arg) + TEE_IOCTL_PARAM_SIZE(arg.num_params) != buf.buf_len)
+               return -EINVAL;
+
+       if (arg.num_params) {
+               params = kcalloc(arg.num_params, sizeof(struct tee_param),
+                                GFP_KERNEL);
+               if (!params)
+                       return -ENOMEM;
+               uparams = uarg->params;
+               rc = params_from_user(ctx, params, arg.num_params, uparams);
+               if (rc)
+                       goto out;
+       }
+
+       if (arg.clnt_login >= TEE_IOCTL_LOGIN_REE_KERNEL_MIN &&
+           arg.clnt_login <= TEE_IOCTL_LOGIN_REE_KERNEL_MAX) {
+               pr_debug("login method not allowed for user-space client\n");
+               rc = -EPERM;
+               goto out;
+       }
+
+       rc = ctx->teedev->desc->ops->open_session(ctx, &arg, params);
+       if (rc)
+               goto out;
+       have_session = true;
+
+       if (put_user(arg.session, &uarg->session) ||
+           put_user(arg.ret, &uarg->ret) ||
+           put_user(arg.ret_origin, &uarg->ret_origin)) {
+               rc = -EFAULT;
+               goto out;
+       }
+       rc = params_to_user(uparams, arg.num_params, params);
+out:
+       /*
+        * If we've succeeded to open the session but failed to communicate
+        * it back to user space, close the session again to avoid leakage.
+        */
+       if (rc && have_session && ctx->teedev->desc->ops->close_session)
+               ctx->teedev->desc->ops->close_session(ctx, arg.session);
+
+       if (params) {
+               /* Decrease ref count for all valid shared memory pointers */
+               for (n = 0; n < arg.num_params; n++)
+                       if (tee_param_is_memref(params + n) &&
+                           params[n].u.memref.shm)
+                               tee_shm_put(params[n].u.memref.shm);
+               kfree(params);
+       }
+
+       return rc;
+}
+
+static int tee_ioctl_invoke(struct tee_context *ctx,
+                           struct tee_ioctl_buf_data __user *ubuf)
+{
+       int rc;
+       size_t n;
+       struct tee_ioctl_buf_data buf;
+       struct tee_ioctl_invoke_arg __user *uarg;
+       struct tee_ioctl_invoke_arg arg;
+       struct tee_ioctl_param __user *uparams = NULL;
+       struct tee_param *params = NULL;
+
+       if (!ctx->teedev->desc->ops->invoke_func)
+               return -EINVAL;
+
+       if (copy_from_user(&buf, ubuf, sizeof(buf)))
+               return -EFAULT;
+
+       if (buf.buf_len > TEE_MAX_ARG_SIZE ||
+           buf.buf_len < sizeof(struct tee_ioctl_invoke_arg))
+               return -EINVAL;
+
+       uarg = u64_to_user_ptr(buf.buf_ptr);
+       if (copy_from_user(&arg, uarg, sizeof(arg)))
+               return -EFAULT;
+
+       if (sizeof(arg) + TEE_IOCTL_PARAM_SIZE(arg.num_params) != buf.buf_len)
+               return -EINVAL;
+
+       if (arg.num_params) {
+               params = kcalloc(arg.num_params, sizeof(struct tee_param),
+                                GFP_KERNEL);
+               if (!params)
+                       return -ENOMEM;
+               uparams = uarg->params;
+               rc = params_from_user(ctx, params, arg.num_params, uparams);
+               if (rc)
+                       goto out;
+       }
+
+       rc = ctx->teedev->desc->ops->invoke_func(ctx, &arg, params);
+       if (rc)
+               goto out;
+
+       if (put_user(arg.ret, &uarg->ret) ||
+           put_user(arg.ret_origin, &uarg->ret_origin)) {
+               rc = -EFAULT;
+               goto out;
+       }
+       rc = params_to_user(uparams, arg.num_params, params);
+out:
+       if (params) {
+               /* Decrease ref count for all valid shared memory pointers */
+               for (n = 0; n < arg.num_params; n++)
+                       if (tee_param_is_memref(params + n) &&
+                           params[n].u.memref.shm)
+                               tee_shm_put(params[n].u.memref.shm);
+               kfree(params);
+       }
+       return rc;
+}
+
+static int tee_ioctl_cancel(struct tee_context *ctx,
+                           struct tee_ioctl_cancel_arg __user *uarg)
+{
+       return -EINVAL;
+}
+
+static int
+tee_ioctl_close_session(struct tee_context *ctx,
+                       struct tee_ioctl_close_session_arg __user *uarg)
+{
+       struct tee_ioctl_close_session_arg arg;
+
+       if (!ctx->teedev->desc->ops->close_session)
+               return -EINVAL;
+
+       if (copy_from_user(&arg, uarg, sizeof(arg)))
+               return -EFAULT;
+
+       return ctx->teedev->desc->ops->close_session(ctx, arg.session);
+}
+
+static int tee_ioctl(struct cdev *cdev, int cmd, void *arg)
+{
+       struct tee_context *ctx = cdev->priv;
+       void __user *uarg = (void __user *)arg;
+
+       switch (cmd) {
+       case TEE_IOC_VERSION:
+               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:
+               return tee_ioctl_invoke(ctx, uarg);
+       case TEE_IOC_CANCEL:
+               return tee_ioctl_cancel(ctx, uarg);
+       case TEE_IOC_CLOSE_SESSION:
+               return tee_ioctl_close_session(ctx, uarg);
+       case TEE_IOC_SUPPL_RECV:
+               return -ENOSYS;
+       case TEE_IOC_SUPPL_SEND:
+               return -ENOSYS;
+       default:
+               return -EINVAL;
+       }
+}
+
+static const struct cdev_operations tee_cdev_ops = {
+       .open = tee_open,
+       .close = tee_release,
+       .ioctl = tee_ioctl,
+};
+
 static void tee_devinfo(struct device *dev)
 {
        struct tee_device *teedev = dev->priv;
@@ -148,6 +535,11 @@ struct tee_device *tee_device_alloc(const struct tee_desc 
*teedesc,
                goto err;
        }
 
+       if (IS_ENABLED(CONFIG_OPTEE_DEVFS)) {
+               teedev->cdev.dev = &teedev->dev;
+               teedev->cdev.ops = &tee_cdev_ops;
+       }
+
        /* 1 as tee_device_unregister() does one final tee_device_put() */
        teedev->num_users = 1;
        mutex_init(&teedev->mutex);
@@ -190,10 +582,22 @@ int tee_device_register(struct tee_device *teedev)
        if (rc)
                return rc;
 
+       if (IS_ENABLED(CONFIG_OPTEE_DEVFS)) {
+               teedev->cdev.name = teedev->dev.unique_name;
+
+               rc = devfs_create(&teedev->cdev);
+               if (rc)
+                       goto out;
+       }
+
        list_add_tail(&teedev->list, &tee_clients);
 
        teedev->flags |= TEE_DEVICE_FLAG_REGISTERED;
        return 0;
+
+out:
+       unregister_device(&teedev->dev);
+       return rc;
 }
 EXPORT_SYMBOL_GPL(tee_device_register);
 
@@ -236,6 +640,8 @@ void tee_device_unregister(struct tee_device *teedev)
                return;
 
        list_del(&teedev->list);
+       if (IS_ENABLED(CONFIG_OPTEE_DEVFS))
+               devfs_remove(&teedev->cdev);
        unregister_device(&teedev->dev);
 }
 EXPORT_SYMBOL_GPL(tee_device_unregister);
diff --git a/drivers/tee/tee_private.h b/drivers/tee/tee_private.h
index ccd082c2c6bb..045f2df9f3b4 100644
--- a/drivers/tee/tee_private.h
+++ b/drivers/tee/tee_private.h
@@ -22,6 +22,7 @@ struct tee_context;
  * @id:                unique id of device
  * @flags:     represented by TEE_DEVICE_FLAG_REGISTERED above
  * @dev:       embedded basic device structure
+ * @cdev:      embedded cdev
  * @num_users: number of active users of this device
  * @mutex:     mutex protecting @num_users and @idr
  */
@@ -32,11 +33,14 @@ struct tee_device {
        unsigned int flags;
 
        struct device dev;
+       struct cdev cdev;
 
        size_t num_users;
        struct mutex mutex;     /* protects num_users and idr */
 };
 
+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);
 
diff --git a/drivers/tee/tee_shm.c b/drivers/tee/tee_shm.c
index acb74002bace..ea16c9cdd2e5 100644
--- a/drivers/tee/tee_shm.c
+++ b/drivers/tee/tee_shm.c
@@ -18,8 +18,13 @@ static void tee_shm_release(struct tee_device *teedev, 
struct tee_shm *shm)
        if (shm->flags & TEE_SHM_DYNAMIC)
                teedev->desc->ops->shm_unregister(shm->ctx, shm);
 
-       if (!(shm->flags & TEE_SHM_PRIV))
+       if (!(shm->flags & TEE_SHM_PRIV)) {
                list_del(&shm->link);
+               if (IS_ENABLED(CONFIG_OPTEE_DEVFS)) {
+                       devfs_remove(&shm->cdev);
+                       unregister_device(&shm->dev);
+               }
+       }
 
        if (shm->flags & TEE_SHM_POOL)
                free(shm->kaddr);
@@ -31,6 +36,11 @@ static void tee_shm_release(struct tee_device *teedev, 
struct tee_shm *shm)
        tee_device_put(teedev);
 }
 
+static const struct cdev_operations tee_shm_ops = {
+       .read = mem_read,
+       .memmap = generic_memmap_ro,
+};
+
 static struct tee_shm *
 register_shm_helper(struct tee_context *ctx, void *addr,
                    size_t size, u32 flags)
@@ -53,14 +63,45 @@ register_shm_helper(struct tee_context *ctx, void *addr,
                goto err;
        }
 
+       shm->fd = -EBADF;
+       shm->dev.id = -EACCES;
        shm->ctx = ctx;
        shm->kaddr = addr;
        shm->paddr = virt_to_phys(shm->kaddr);
        shm->size = size;
        shm->flags = flags;
 
-       if (!(flags & TEE_SHM_PRIV))
+       if (!(flags & TEE_SHM_PRIV)) {
+               if (IS_ENABLED(CONFIG_OPTEE_DEVFS)) {
+                       shm->res.start = (resource_size_t)addr;
+                       shm->res.end = (resource_size_t)(addr + size - 1);
+                       shm->res.flags = IORESOURCE_MEM;
+
+                       shm->dev.id = DEVICE_ID_DYNAMIC;
+                       shm->dev.parent = &ctx->teedev->dev;
+                       shm->dev.resource = &shm->res;
+                       shm->dev.num_resources = 1;
+                       rc = dev_set_name(&shm->dev, "%s-shm", 
ctx->teedev->dev.unique_name);
+                       if (rc)
+                               goto err;
+
+                       rc = register_device(&shm->dev);
+                       if (rc)
+                               goto err;
+
+                       shm->res.name = shm->dev.unique_name;
+
+                       shm->cdev.dev = &shm->dev;
+                       shm->cdev.ops = &tee_shm_ops;
+                       shm->cdev.size = size;
+                       shm->cdev.name = shm->dev.unique_name;
+                       rc = devfs_create(&shm->cdev);
+                       if (rc)
+                               goto err;
+               }
+
                list_add(&shm->link, &ctx->list_shm);
+       }
 
        if (flags & TEE_SHM_DYNAMIC) {
                rc = ctx->teedev->desc->ops->shm_register(ctx, shm);
@@ -70,13 +111,18 @@ register_shm_helper(struct tee_context *ctx, void *addr,
 
        refcount_set(&shm->refcount, 1);
 
-       pr_debug("%s: shm=%p addr=%p size=%zu\n", __func__, shm,
-                addr, size);
+       pr_debug("%s: shm=%p cdev=%s addr=%p size=%zu\n", __func__, shm,
+                shm->cdev.name ?: "(priv)", addr, size);
 
        return shm;
 err:
-       if (!(flags & TEE_SHM_PRIV))
+       if (!(flags & TEE_SHM_PRIV)) {
                list_del(&shm->link);
+               if (IS_ENABLED(CONFIG_OPTEE_DEVFS)) {
+                       devfs_remove(&shm->cdev);
+                       unregister_device(&shm->dev);
+               }
+       }
 
        free(shm);
        teedev_ctx_put(ctx);
@@ -85,6 +131,22 @@ register_shm_helper(struct tee_context *ctx, void *addr,
        return ERR_PTR(rc);
 }
 
+/**
+ * tee_shm_register_user_buf() - Register a userspace shared memory buffer
+ * @ctx:       Context that registers the shared memory
+ * @addr:      The userspace address of the shared buffer
+ * @length:    Length of the shared buffer
+ *
+ * @returns a pointer to 'struct tee_shm'
+ */
+struct tee_shm *tee_shm_register_user_buf(struct tee_context *ctx,
+                                         unsigned long addr, size_t length)
+{
+       u32 flags = TEE_SHM_USER_MAPPED | TEE_SHM_DYNAMIC;
+
+       return register_shm_helper(ctx, (void *)addr, length, flags);
+}
+
 static struct tee_shm *shm_alloc_helper(struct tee_context *ctx, size_t size,
                                        size_t align, u32 flags)
 {
@@ -104,6 +166,21 @@ static struct tee_shm *shm_alloc_helper(struct tee_context 
*ctx, size_t size,
        return shm;
 }
 
+/**
+ * tee_shm_alloc_user_buf() - Allocate shared memory for user space
+ * @ctx:       Context that allocates the shared memory
+ * @size:      Requested size of shared memory
+ *
+ * Memory allocated as user space shared memory is automatically freed when
+ * the TEE file pointer is closed. The primary usage of this function is
+ * when the TEE driver doesn't support registering ordinary user space
+ * memory.
+ *
+ * @returns a pointer to 'struct tee_shm'
+ */
+struct tee_shm *tee_shm_alloc_user_buf(struct tee_context *ctx, size_t size)
+       __alias(tee_shm_alloc_kernel_buf);
+
 /**
  * tee_shm_alloc_kernel_buf() - Allocate shared memory for kernel buffer
  * @ctx:       Context that allocates the shared memory
@@ -147,6 +224,39 @@ struct tee_shm *tee_shm_alloc_priv_buf(struct tee_context 
*ctx, size_t size)
 }
 EXPORT_SYMBOL_GPL(tee_shm_alloc_priv_buf);
 
+/**
+ * tee_shm_get_fd() - Increase reference count and return file descriptor
+ * @shm:       Shared memory handle
+ * @returns user space file descriptor to shared memory
+ */
+int tee_shm_get_fd(struct tee_shm *shm)
+{
+       int fd;
+
+       if (!IS_ENABLED(CONFIG_OPTEE_DEVFS))
+               return -ENOSYS;
+
+       refcount_inc(&shm->refcount);
+
+       if (shm->fd < 0) {
+               char *tmp;
+
+               tmp = basprintf("/dev/%s", shm->cdev.name);
+               if (!tmp)
+                       return -ENOMEM;
+
+               shm->fd = open(tmp, O_RDONLY);
+               free(tmp);
+       }
+
+       fd = shm->fd;
+
+       if (shm->fd < 0)
+               tee_shm_put(shm);
+
+       return fd;
+}
+
 /**
  * tee_shm_free() - Free shared memory
  * @shm:       Handle to shared memory to free
@@ -192,6 +302,28 @@ int tee_shm_get_pa(struct tee_shm *shm, size_t offs, 
phys_addr_t *pa)
 }
 EXPORT_SYMBOL_GPL(tee_shm_get_pa);
 
+/**
+ * tee_shm_get_from_id() - Find shared memory object and increase reference
+ * count
+ * @ctx:       Context owning the shared memory
+ * @id:                Id of shared memory object
+ * @returns a pointer to 'struct tee_shm' on success or an ERR_PTR on failure
+ */
+struct tee_shm *tee_shm_get_from_id(struct tee_context *ctx, int id)
+{
+       struct tee_shm *shm;
+
+       list_for_each_entry(shm, &ctx->list_shm, link) {
+               if (shm->dev.id == id) {
+                       refcount_inc(&shm->refcount);
+                       return shm;
+               }
+       }
+
+       return NULL;
+}
+EXPORT_SYMBOL_GPL(tee_shm_get_from_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 e5f0344d4d06..4a5cb0f0a50f 100644
--- a/include/linux/tee_drv.h
+++ b/include/linux/tee_drv.h
@@ -182,6 +182,9 @@ int tee_session_calc_client_uuid(uuid_t *uuid, u32 
connection_method,
  * @refcount:  reference counter
  * @flags:     defined by TEE_SHM_* in tee_drv.h
  * @link:      list head for registering object globally
+ * @fd:                file descriptor for use in userspace
+ * @dev:       device for registering shared memory
+ * @res:       resource to be associated with device
  *
  * This pool is only supposed to be accessed directly from the TEE
  * subsystem and from drivers that implements their own shm pool manager.
@@ -194,6 +197,11 @@ struct tee_shm {
        refcount_t refcount;
        u32 flags;
        struct list_head link;
+
+       int fd;
+       struct device_d dev;
+       struct cdev cdev;
+       struct resource res;
 };
 
 /**
@@ -205,6 +213,10 @@ void *tee_get_drvdata(struct tee_device *teedev);
 struct tee_shm *tee_shm_alloc_priv_buf(struct tee_context *ctx, size_t size);
 struct tee_shm *tee_shm_alloc_kernel_buf(struct tee_context *ctx, size_t size);
 
+struct tee_shm *tee_shm_alloc_user_buf(struct tee_context *ctx, size_t size);
+struct tee_shm *tee_shm_register_user_buf(struct tee_context *ctx,
+                                         unsigned long addr, size_t length);
+
 /**
  * tee_shm_is_dynamic() - Check if shared memory object is of the dynamic kind
  * @shm:       Shared memory handle
@@ -256,6 +268,27 @@ static inline size_t tee_shm_get_size(struct tee_shm *shm)
        return shm->size;
 }
 
+/**
+ * tee_shm_get_id() - Get id of a shared memory object
+ * @shm:       Shared memory handle
+ * @returns id
+ */
+static inline int tee_shm_get_id(struct tee_shm *shm)
+{
+       /* Only call on non-private SHMs */
+       BUG_ON(shm->dev.id < 0);
+       return shm->dev.id;
+}
+
+/**
+ * tee_shm_get_from_id() - Find shared memory object and increase reference
+ * count
+ * @ctx:       Context owning the shared memory
+ * @id:                Id of shared memory object
+ * @returns a pointer to 'struct tee_shm' on success or an ERR_PTR on failure
+ */
+struct tee_shm *tee_shm_get_from_id(struct tee_context *ctx, int id);
+
 /**
  * tee_client_open_context() - Open a TEE context
  * @start:     if not NULL, continue search after this context
-- 
2.39.2


Reply via email to