[PATCH 7/8] optee: add experimental support for /dev/tee0

Ahmad Fatoum a.fatoum at pengutronix.de
Sun Nov 26 22:35:58 PST 2023


From: Marc Kleine-Budde <mkl at 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.fatoum at 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




More information about the barebox mailing list