Hi Amirreza,
On Tue, Dec 3, 2024 at 5:20 AM Amirreza Zarrabi quic_azarrabi@quicinc.com wrote:
Introduce basic support for invoking objects hosted in QTEE and userspace through the TEE subsystem.
Signed-off-by: Amirreza Zarrabi quic_azarrabi@quicinc.com
drivers/tee/qcomtee/Makefile | 2 + drivers/tee/qcomtee/call.c | 707 ++++++++++++++++++++++++++++++++++ drivers/tee/qcomtee/core.c | 8 + drivers/tee/qcomtee/qcomtee_private.h | 166 ++++++++ drivers/tee/qcomtee/user_obj.c | 625 ++++++++++++++++++++++++++++++ include/uapi/linux/tee.h | 1 + 6 files changed, 1509 insertions(+)
diff --git a/drivers/tee/qcomtee/Makefile b/drivers/tee/qcomtee/Makefile index 108bc7fdabcb..6bf91481fde3 100644 --- a/drivers/tee/qcomtee/Makefile +++ b/drivers/tee/qcomtee/Makefile @@ -1,7 +1,9 @@ # SPDX-License-Identifier: GPL-2.0-only obj-$(CONFIG_QCOMTEE) += qcomtee.o qcomtee-objs += async.o +qcomtee-objs += call.o qcomtee-objs += core.o qcomtee-objs += primordial_obj.o qcomtee-objs += qcom_scm.o qcomtee-objs += release.o +qcomtee-objs += user_obj.o diff --git a/drivers/tee/qcomtee/call.c b/drivers/tee/qcomtee/call.c new file mode 100644 index 000000000000..11bb31836808 --- /dev/null +++ b/drivers/tee/qcomtee/call.c @@ -0,0 +1,707 @@ +// SPDX-License-Identifier: GPL-2.0-only +/*
- Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
- */
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+#include <linux/tee.h> +#include <linux/mm.h> +#include <linux/platform_device.h> +#include <linux/firmware/qcom/qcom_tzmem.h> +#include <linux/firmware/qcom/qcom_scm.h>
+#include "qcomtee_private.h"
+/**
- enum qcom_tee_object_host - Object host where it is managed.
- @QCOM_TEE_OBJECT_HOST_USER: objects in userspace.
- @QCOM_TEE_OBJECT_HOST_TEE: objects in QTEE.
- We refer to object hosted in userspace as 'Local Object' and objects hosted
- in QTEE as 'Remote Object'.
- */
+enum qcom_tee_object_host {
QCOM_TEE_OBJECT_HOST_USER, /* Object that is managed in userspace. */QCOM_TEE_OBJECT_HOST_TEE, /* Object that is managed in QTEE. */+};
+/* Read object ID host information. */ +static enum qcom_tee_object_host qcom_tee_object_host(struct tee_param *param) +{
if (param->u.objref.flags & QCOM_TEE_OBJREF_FLAG_USER)return QCOM_TEE_OBJECT_HOST_USER;return QCOM_TEE_OBJECT_HOST_TEE;+}
+/* qcom_tee_context_add_qtee_object() - Add a QTEE object to the context.
- @param: TEE parameter represents @object.
- @object: QTEE object.
- @ctx: context to add the object.
- It assumes @object is %QCOM_TEE_OBJECT_TYPE_TEE and caller has already issued
- qcom_tee_object_get() for @object.
- Return: On success return 0 or <0 on failure.
- */
+int qcom_tee_context_add_qtee_object(struct tee_param *param, struct qcom_tee_object *object,
struct qcom_tee_context *ctx)+{
int ret;guard(mutex)(&ctx->lock);ret = idr_alloc(&ctx->qtee_objects_idr, object, 0, 0, GFP_KERNEL);if (ret < 0)return ret;param->u.objref.id = ret;/* QTEE Object: !QCOM_TEE_OBJREF_FLAG_USER. */param->u.objref.flags = 0;return 0;+}
+static int find_qtee_object(struct qcom_tee_object **object, unsigned long id,
struct qcom_tee_context *ctx)+{
int err = 0;guard(rcu)();/* Object release is RCU protected. */*object = idr_find(&ctx->qtee_objects_idr, id);if (!qcom_tee_object_get(*object))err = -EINVAL;return err;+}
+static void del_qtee_object(unsigned long id, struct qcom_tee_context *ctx) +{
struct qcom_tee_object *object;scoped_guard(mutex, &ctx->lock)object = idr_remove(&ctx->qtee_objects_idr, id);qcom_tee_object_put(object);+}
+/* Get the QTEE object added with qcom_tee_context_add_qtee_object(). */ +int qcom_tee_context_find_qtee_object(struct qcom_tee_object **object, struct tee_param *param,
struct qcom_tee_context *ctx)+{
/* 'qtee_objects_idr' stores QTEE objects only. */if (qcom_tee_object_host(param) != QCOM_TEE_OBJECT_HOST_TEE)return -EINVAL;return find_qtee_object(object, param->u.objref.id, ctx);+}
+/**
- qcom_tee_context_del_qtee_object() - Delete a QTEE object from the context.
- @param: TEE parameter represents @object.
- @ctx: context to delete the object.
- @param returned by qcom_tee_context_add_qtee_object().
- */
+void qcom_tee_context_del_qtee_object(struct tee_param *param, struct qcom_tee_context *ctx) +{
/* 'qtee_objects_idr' stores QTEE objects only. */if (qcom_tee_object_host(param) == QCOM_TEE_OBJECT_HOST_TEE)del_qtee_object(param->u.objref.id, ctx);+}
+/**
- qcom_tee_objref_to_arg() - Convert OBJREF parameter to QTEE argument in a context.
- @arg: QTEE argument.
- @param: TEE parameter.
- @ctx: context in which the conversion should happen.
- It assumes @param is OBJREF.
- It does not set @arg.type; caller should initialize it to a correct
- &enum qcom_tee_arg_type value.
- Return: On success return 0 or <0 on failure.
- */
+int qcom_tee_objref_to_arg(struct qcom_tee_arg *arg, struct tee_param *param,
struct qcom_tee_context *ctx)+{
struct qcom_tee_object *object;int err;if (arg->type != QCOM_TEE_ARG_TYPE_IO &&arg->type != QCOM_TEE_ARG_TYPE_OO)return -EINVAL;/* It is a NULL object?! */if (param->u.objref.id == TEE_OBJREF_NULL) {arg->o = NULL_QCOM_TEE_OBJECT;return 0;}switch (qcom_tee_object_host(param)) {case QCOM_TEE_OBJECT_HOST_USER:err = qcom_tee_user_param_to_object(&object, param, ctx);if (err)break;/* Keep a copy for driver as QTEE may release it (e.g. using async msg). */qcom_tee_object_get(object);break;case QCOM_TEE_OBJECT_HOST_TEE:err = qcom_tee_context_find_qtee_object(&object, param, ctx);break;}arg->o = err ? NULL_QCOM_TEE_OBJECT : object;return err;+}
+/**
- qcom_tee_objref_from_arg() - Convert QTEE argument to OBJREF param in a context.
- @param: TEE parameter.
- @arg: QTEE argument.
- @ctx: context in which the conversion should happen.
- It assumes @arg is of %QCOM_TEE_ARG_TYPE_IO or %QCOM_TEE_ARG_TYPE_OO.
- It does not set @param.attr; caller should initialize it to a correct OBJREF type.
- Return: On success return 0 or <0 on failure.
- */
+int qcom_tee_objref_from_arg(struct tee_param *param, struct qcom_tee_arg *arg,
struct qcom_tee_context *ctx)+{
struct qcom_tee_object *object;int err;/* param should be of OBJREF. */if (param->attr != TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_INPUT &¶m->attr != TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_OUTPUT &¶m->attr != TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_INOUT)return -EINVAL;object = arg->o;switch (typeof_qcom_tee_object(object)) {case QCOM_TEE_OBJECT_TYPE_NULL:param->u.objref.id = TEE_OBJREF_NULL;err = 0;break;case QCOM_TEE_OBJECT_TYPE_CB_OBJECT:err = qcom_tee_user_param_from_object(param, object, ctx);break;case QCOM_TEE_OBJECT_TYPE_TEE:err = qcom_tee_context_add_qtee_object(param, object, ctx);break;case QCOM_TEE_OBJECT_TYPE_ROOT:default:return -EINVAL;}return err;+}
+/**
- qcom_tee_params_to_args() - Convert TEE parameters to QTEE arguments in a context.
- @u: QTEE arguments.
- @params: TEE parameters.
- @num_params: number of elements in the parameter array.
- @ctx: context in which the conversion should happen.
- It assumes @u has at least @num_param + 1 entries and has been initialized
- with %QCOM_TEE_ARG_TYPE_INV as &struct qcom_tee_arg.type.
- Return: On success return 0 or <0 on failure.
- */
+static int qcom_tee_params_to_args(struct qcom_tee_arg *u,
struct tee_param *params, int num_params,struct qcom_tee_context *ctx)+{
struct qcom_tee_object *object;int i;for (i = 0; i < num_params; i++) {switch (params[i].attr) {case TEE_IOCTL_PARAM_ATTR_TYPE_MEMBUF_INPUT:case TEE_IOCTL_PARAM_ATTR_TYPE_MEMBUF_OUTPUT:u[i].flags = QCOM_TEE_ARG_FLAGS_UADDR;u[i].b.uaddr = params[i].u.membuf.uaddr;u[i].b.size = params[i].u.membuf.size;if (params[i].attr == TEE_IOCTL_PARAM_ATTR_TYPE_MEMBUF_INPUT)u[i].type = QCOM_TEE_ARG_TYPE_IB;else /* TEE_IOCTL_PARAM_ATTR_TYPE_MEMBUF_OUTPUT */u[i].type = QCOM_TEE_ARG_TYPE_OB;break;case TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_INPUT:u[i].type = QCOM_TEE_ARG_TYPE_IO;if (qcom_tee_objref_to_arg(&u[i], ¶ms[i], ctx))goto out_failed;break;case TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_OUTPUT:u[i].type = QCOM_TEE_ARG_TYPE_OO;u[i].o = NULL_QCOM_TEE_OBJECT;break;default: /* Unsupported TEE parameters. */goto out_failed;}}return 0;+out_failed:
/* On ERROR: *//* Put IO objects processed so far. */for (--i; i >= 0; i--) {if (u[i].type != QCOM_TEE_ARG_TYPE_IO)continue;object = u[i].o;qcom_tee_user_object_set_notify(object, false);/* For callback object, we hold a reference for the driver, put it. */if (typeof_qcom_tee_object(object) == QCOM_TEE_OBJECT_TYPE_CB_OBJECT)qcom_tee_object_put(object);/* Put QTEE copy of object. */qcom_tee_object_put(object);}return -EINVAL;+}
+/**
- qcom_tee_params_from_args() - Convert QTEE arguments to TEE parameters in a context.
- @params: TEE parameters.
- @u: QTEE arguments.
- @num_params: number of elements in the parameter array.
- @ctx: context in which the conversion should happen.
- @u should have been already initialized by qcom_tee_params_to_args().
- This also represents end of a QTEE invocation that started with qcom_tee_params_to_args()
- by releasing %QCOM_TEE_ARG_TYPE_IO objects.
- Return: On success return 0 or <0 on failure.
- */
+static int qcom_tee_params_from_args(struct tee_param *params,
struct qcom_tee_arg *u, int num_params,struct qcom_tee_context *ctx)+{
int i, np;for (np = 0; u[np].type; np++) {enum qcom_tee_arg_type at = u[np].type;if (at == QCOM_TEE_ARG_TYPE_OB) {/* TEE_IOCTL_PARAM_ATTR_TYPE_MEMBUF_OUTPUT */params[np].u.value.b = u[np].b.size;} else if (at == QCOM_TEE_ARG_TYPE_IO) {/* IEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_INPUT */qcom_tee_object_put(u[np].o);} else if (at == QCOM_TEE_ARG_TYPE_OO) {/* TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_OUTPUT */if (qcom_tee_objref_from_arg(¶ms[np], &u[np], ctx))goto out_failed;}}return 0;+out_failed:
/* On ERROR: *//* - Release params associated to QTEE objects in this context so far. */for (i = 0; i < np; i++) {if (params[i].attr == TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_OUTPUT)qcom_tee_context_del_qtee_object(¶ms[i], ctx);}/* - Release any IO and OO objects not processed so far. */while (u[i].type) {if (u[i].type == QCOM_TEE_ARG_TYPE_OO ||u[i].type == QCOM_TEE_ARG_TYPE_IO)qcom_tee_object_put(u[i++].o);}return -EINVAL;+}
+/* TEE Device Ops. */
+static int qcom_tee_params_check(struct tee_param *params, int num_params) +{
int io = 0, oo = 0, ib = 0, ob = 0;int i;/* QTEE accepts 64 arguments. */if (num_params > QCOM_TEE_ARGS_MAX)return -EINVAL;/* Supported parameter types. */for (i = 0; i < num_params; i++) {switch (params[i].attr) {case TEE_IOCTL_PARAM_ATTR_TYPE_MEMBUF_INPUT:ib++; break;case TEE_IOCTL_PARAM_ATTR_TYPE_MEMBUF_OUTPUT:ob++; break;case TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_INPUT:io++; break;case TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_OUTPUT:oo++; break;default:return -EINVAL;}}/* QTEE accepts 16 arguments of each supported types. */if (io > QCOM_TEE_ARGS_PER_TYPE || oo > QCOM_TEE_ARGS_PER_TYPE ||ib > QCOM_TEE_ARGS_PER_TYPE || ob > QCOM_TEE_ARGS_PER_TYPE)return -EINVAL;return 0;+}
+/* Check if user issued a permitted operation on ROOT_QCOM_TEE_OBJECT from userspace. */ +static int qcom_tee_root_object_check(u32 op, struct tee_param *params, int num_params) +{
/* Some privileged operations recognized by QTEE. */if (op == 4 || op == 8 || op == 9)return -EINVAL;/* OP 5 is to register with QTEE by passing credential object as input OBJREF. *//* TEE_OBJREF_NULL as credential object represents a privileged client for QTEE,* only kernel can pass TEE_OBJREF_NULL.*/if (op == 5) {if (num_params != 2)return -EINVAL;if (params[0].attr == TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_INPUT &¶ms[1].attr == TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_OUTPUT) {if (params[0].u.objref.id == TEE_OBJREF_NULL)return -EINVAL;} else if (params[0].attr == TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_OUTPUT &¶ms[1].attr == TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_INPUT) {if (params[1].u.objref.id == TEE_OBJREF_NULL)return -EINVAL;}}return 0;+}
+/**
- qcom_tee_object_invoke() - Invoke a QTEE object.
- @tee_ctx: TEE context.
- @arg: ioctl arguments.
- @params: parameters for the object.
- Return: On success return 0 or <0 on failure.
- */
+static int qcom_tee_object_invoke(struct tee_context *tee_ctx,
struct tee_ioctl_object_invoke_arg *arg,struct tee_param *params)+{
struct qcom_tee_object_invoke_ctx *oic __free(kfree) = NULL;struct qcom_tee_context *ctx = tee_ctx->data;struct qcom_tee_arg *u __free(kfree) = NULL;struct qcom_tee_object *object;int i, ret, result;if (qcom_tee_params_check(params, arg->num_params))return -EINVAL;/* Handle OBJREF reserved operations. */if (arg->op == QCOM_TEE_OBJREF_OP_RELEASE) {del_qtee_object(arg->object, ctx);return 0;}/* Unsupported reserved operation. */if (arg->op > QCOM_TEE_OBJREF_OP_MIN)return -EINVAL;oic = kzalloc(sizeof(*oic), GFP_KERNEL);if (!oic)return -ENOMEM;/* +1 for ending QCOM_TEE_ARG_TYPE_INV. */u = kzalloc(sizeof(*u) * (arg->num_params + 1), GFP_KERNEL);
Please use kcalloc().
if (!u)return -ENOMEM;if (arg->object == TEE_OBJREF_NULL &&!qcom_tee_root_object_check(arg->op, params, arg->num_params)) {/* Use ROOT if NULL is invoked. */object = ROOT_QCOM_TEE_OBJECT;} else {/* Get object being invoked. */ret = find_qtee_object(&object, arg->object, ctx);if (ret)return ret;}ret = qcom_tee_params_to_args(u, params, arg->num_params, ctx);if (ret)goto out;ret = qcom_tee_object_do_invoke(oic, object, arg->op, u, &result);if (!ret) {if (!result) {/* Only parse QTEE response on SUCCESS. */ret = qcom_tee_params_from_args(params, u, arg->num_params, ctx);} else {/* Put driver's IO objects copy; get in qcom_tee_params_to_args(). */qcom_tee_arg_for_each_input_object(i, u)qcom_tee_object_put(u[i++].o);}} else if (ret != -EAGAIN && ret != -ENODEV) {/* Unable to initiate a QTEE invocation; cleanup qcom_tee_params_to_args(). */qcom_tee_arg_for_each_input_object(i, u) {qcom_tee_user_object_set_notify(u[i].o, false);if (typeof_qcom_tee_object(u[i].o) == QCOM_TEE_OBJECT_TYPE_CB_OBJECT)qcom_tee_object_put(u[i].o);qcom_tee_object_put(u[i].o);}}arg->ret = result;+out:
qcom_tee_object_put(object);return ret;+}
+/**
- qcom_tee_supp_recv() - Pick a request for the supplicant.
- @tee_ctx: TEE context.
- @op: requested operation on object.
- @num_params: number of elements in the parameter array, updated with number used.
- @params: parameters for @op.
- The first parameter is a %TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT meta parameter.
- On input, it provides a user buffer. This buffer is used for parameters of type
- %TEE_IOCTL_PARAM_ATTR_TYPE_MEMBUF_INPUT in qcom_tee_cb_params_from_args().
- On output, object id and request id are stored in the meta parameter.
- Return: On success return 0 or <0 on failure.
- */
+static int qcom_tee_supp_recv(struct tee_context *tee_ctx, u32 *op, u32 *num_params,
struct tee_param *params)+{
struct qcom_tee_user_object_request_data data;struct qcom_tee_context *ctx = tee_ctx->data;void __user *uaddr;size_t ubuf_size;int i, ret;if (!*num_params)return -EINVAL;/* We expect the first parameter to be an INOUT + meta parameter. */if (params->attr != (TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT | TEE_IOCTL_PARAM_ATTR_META))return -EINVAL;/* Others parameters are none. */for (i = 1; i < *num_params; i++)if (params[i].attr)return -EINVAL;if (IS_ALIGNED(params->u.value.a, 8))return -EINVAL;/* User buffer and size from meta parameter. */uaddr = u64_to_user_ptr(params->u.value.a);ubuf_size = params->u.value.b;/* Process TEE parameters. +/-1 to ignore meta parameter. */ret = qcom_tee_user_object_pop(ctx, params + 1, *num_params - 1, uaddr, ubuf_size, &data);if (ret)return ret;params->u.value.a = data.object_id;params->u.value.b = data.id;params->u.value.c = 0;*op = data.op;*num_params = data.np + 1;return 0;+}
+/**
- qcom_tee_supp_send() - Pick a request for the supplicant.
- @tee_ctx: TEE context.
- @ret: return value of the request.
- @num_params: number of elements in the parameter array.
- @params: returned parameters.
- The first parameter is a %TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT meta parameter.
- It specifies the request id this response is belong to.
- Return: On success return 0 or <0 on failure.
- */
+static int qcom_tee_supp_send(struct tee_context *tee_ctx, u32 errno, u32 num_params,
struct tee_param *params)+{
struct qcom_tee_context *ctx = tee_ctx->data;int id;if (!num_params)return -EINVAL;/* We expect the first parameter to be an INPUT + meta parameter. */if (params->attr != (TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT | TEE_IOCTL_PARAM_ATTR_META))return -EINVAL;/* Get the req_id of response. */id = params->u.value.a;/* Process TEE parameters. +/-1 to ignore meta parameter. */return qcom_tee_user_object_submit(ctx, params + 1, num_params - 1, id, errno);+}
+static int qcom_tee_open(struct tee_context *tee_context) +{
struct qcom_tee_context *ctx __free(kfree) = NULL;int err;ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);if (!ctx)return -ENOMEM;err = init_srcu_struct(&ctx->req_srcu);if (err)return err;ctx->tee_context = tee_context;idr_init(&ctx->qtee_objects_idr);idr_init(&ctx->reqs_idr);mutex_init(&ctx->lock);init_completion(&ctx->req_c);kref_init(&ctx->ref_cnt);tee_context->data = no_free_ptr(ctx);return 0;+}
+static void qcom_tee_release(struct tee_context *tee_context) +{
struct qcom_tee_context *ctx = tee_context->data;struct qcom_tee_object *object;int id;/* Process QUEUED or PROCESSING requests. */qcom_tee_requests_destroy(ctx);/* Release QTEE objects. */idr_for_each_entry(&ctx->qtee_objects_idr, object, id)qcom_tee_object_put(object);/* Put the context; wait for all user objects to go away. */kref_put(&ctx->ref_cnt, __qcom_tee_context_destroy);+}
+void __qcom_tee_context_destroy(struct kref *ref_cnt) +{
struct qcom_tee_context *ctx = container_of(ref_cnt, struct qcom_tee_context, ref_cnt);idr_destroy(&ctx->qtee_objects_idr);idr_destroy(&ctx->reqs_idr);cleanup_srcu_struct(&ctx->req_srcu);kfree(ctx);+}
+static void qcom_tee_get_version(struct tee_device *teedev, struct tee_ioctl_version_data *vers) +{
struct tee_ioctl_version_data v = {.impl_id = TEE_IMPL_ID_QTEE,.gen_caps = TEE_GEN_CAP_OBJREF,};*vers = v;+}
+static const struct tee_driver_ops qcom_tee_ops = {
.get_version = qcom_tee_get_version,.open = qcom_tee_open,.release = qcom_tee_release,.object_invoke_func = qcom_tee_object_invoke,.supp_recv = qcom_tee_supp_recv,.supp_send = qcom_tee_supp_send,+};
+static const struct tee_desc qcom_tee_desc = {
.name = "qcom_tee",.ops = &qcom_tee_ops,.owner = THIS_MODULE,+};
+static int qcom_tee_probe(struct platform_device *pdev) +{
struct tee_device *teedev;int err;if (!qcom_scm_is_available())return -EPROBE_DEFER;teedev = tee_device_alloc(&qcom_tee_desc, NULL, NULL, NULL);if (IS_ERR(teedev))return PTR_ERR(teedev);err = tee_device_register(teedev);if (err)goto err_unreg_teedev;platform_set_drvdata(pdev, teedev);return 0;+err_unreg_teedev:
tee_device_unregister(teedev);return err;+}
+static void qcom_tee_remove(struct platform_device *pdev) +{
struct tee_device *teedev = platform_get_drvdata(pdev);/* Keep a copy, tee_device_unregister() sets it to NULL. */
teedev shouldn't be accessed after a call to tee_device_unregister()
struct tee_shm_pool *pool = teedev->pool;/* Wait for users to go away. */tee_device_unregister(teedev);tee_shm_pool_free(pool);
Why? You supplied NULL when tee_device_alloc() was called.
+}
+static const struct of_device_id qcom_tee_dt_match[] = {
{ .compatible = "qcom,tee" },{},+}; +MODULE_DEVICE_TABLE(of, qcom_tee_dt_match);
+static struct platform_driver qcom_tee_platform_driver = {
.probe = qcom_tee_probe,.remove = qcom_tee_remove,.driver = {.name = "qcom_tee",.of_match_table = qcom_tee_dt_match,},+};
+int qcom_tee_driver_register(void) +{
return platform_driver_register(&qcom_tee_platform_driver);+}
+void qcom_tee_driver_unregister(void) +{
platform_driver_unregister(&qcom_tee_platform_driver);+} diff --git a/drivers/tee/qcomtee/core.c b/drivers/tee/qcomtee/core.c index 79f1181cf676..545857e117db 100644 --- a/drivers/tee/qcomtee/core.c +++ b/drivers/tee/qcomtee/core.c @@ -904,8 +904,14 @@ static int __init qcom_tee_object_invoke_init(void) if (ret) goto err_kobject_put;
ret = qcom_tee_driver_register();if (ret)goto err_remove_group;return 0;+err_remove_group:
sysfs_remove_group(qcom_tee_object_invoke_kobj, &attr_group);err_kobject_put: /* Remove '/sys/firmware/qcom_tee'. */ kobject_put(qcom_tee_object_invoke_kobj); @@ -920,6 +926,8 @@ module_init(qcom_tee_object_invoke_init);
static void __exit qcom_tee_object_invoke_deinit(void) {
qcom_tee_driver_unregister();/* Wait for RELEASE operations for QTEE objects. */ qcom_tee_release_destroy(); qcom_tee_msg_buffers_destroy();diff --git a/drivers/tee/qcomtee/qcomtee_private.h b/drivers/tee/qcomtee/qcomtee_private.h index c718cd2d8463..15f358260ed7 100644 --- a/drivers/tee/qcomtee/qcomtee_private.h +++ b/drivers/tee/qcomtee/qcomtee_private.h @@ -10,6 +10,14 @@ #include <linux/kobject.h> #include <linux/tee_core.h>
+/* Flags relating to object reference. */ +#define QCOM_TEE_OBJREF_FLAG_USER 1
+/* Reserved OBJREF operations. */ +/* These operations are not sent to QTEE and handled in driver. */ +#define QCOM_TEE_OBJREF_OP_MIN USHRT_MAX +#define QCOM_TEE_OBJREF_OP_RELEASE (QCOM_TEE_OBJREF_OP_MIN + 1)
struct qcom_tee_object *qcom_tee_idx_erase(u32 idx); void qcom_tee_object_free(struct qcom_tee_object *object);
@@ -44,9 +52,167 @@ int __qcom_tee_object_do_invoke(struct qcom_tee_object_invoke_ctx *oic, struct qcom_tee_object *object, u32 op, struct qcom_tee_arg *u, int *result);
+/**
- struct qcom_tee_context - Clients or supplicants context.
- @tee_context: TEE context.
- @qtee_objects_idr: QTEE objects in this context.
- @reqs_idr: Requests currently being processed.
- @lock: mutex for @reqs_idr and @qtee_objects_idr.
- @req_srcu: srcu for exclusive access to requests.
- @req_c: completion used when supplicant is waiting for requests.
- @released: state of this context.
- @ref_cnt: ref count.
- */
+struct qcom_tee_context {
Other drivers call their conterpart of this struct *_context_data. Using the same pattern here makes it easier to recognize the struct in the rest of the code.
struct tee_context *tee_context;struct idr qtee_objects_idr;struct idr reqs_idr;/* Synchronize access to @reqs_idr, @qtee_objects_idr and updating requests state. */struct mutex lock;struct srcu_struct req_srcu;
Why do you use this synchronization primitive? I don't know enough about this primitive to tell if you use it for the right purpose so perhaps you can help me understand which properties you need.
struct completion req_c;int released;struct kref ref_cnt;
Why does this struct need a different lifetime than struct tee_context?
+};
+void __qcom_tee_context_destroy(struct kref *ref_cnt);
+/* qcom_tee_context_add_qtee_object() - Add a QTEE object to the context.
- @param: TEE parameter represents @object.
- @object: QTEE object.
- @ctx: context to add the object.
- It assumes @object is %QCOM_TEE_OBJECT_TYPE_TEE and caller has already issued
- qcom_tee_object_get() for @object.
- Return: On success return 0 or <0 on failure.
- */
+int qcom_tee_context_add_qtee_object(struct tee_param *param, struct qcom_tee_object *object,
struct qcom_tee_context *ctx);+/* Get the QTEE object added with qcom_tee_context_add_qtee_object(). */ +int qcom_tee_context_find_qtee_object(struct qcom_tee_object **object, struct tee_param *param,
struct qcom_tee_context *ctx);+/**
- qcom_tee_context_del_qtee_object() - Delete a QTEE object from the context.
- @param: TEE parameter represents @object.
- @ctx: context to delete the object.
- @param returned by qcom_tee_context_add_qtee_object().
- */
+void qcom_tee_context_del_qtee_object(struct tee_param *param, struct qcom_tee_context *ctx);
+/**
- qcom_tee_objref_to_arg() - Convert OBJREF parameter to QTEE argument in a context.
- @arg: QTEE argument.
- @param: TEE parameter.
- @ctx: context in which the conversion should happen.
- It assumes @param is OBJREF.
- It does not set @arg.type; caller should initialize it to a correct
- &enum qcom_tee_arg_type value.
- Return: On success return 0 or <0 on failure.
- */
+int qcom_tee_objref_to_arg(struct qcom_tee_arg *arg, struct tee_param *param,
struct qcom_tee_context *ctx);+/**
- qcom_tee_objref_from_arg() - Convert QTEE argument to OBJREF param in a context.
- @param: TEE parameter.
- @arg: QTEE argument.
- @ctx: context in which the conversion should happen.
- It assumes @arg is of %QCOM_TEE_ARG_TYPE_IO or %QCOM_TEE_ARG_TYPE_OO.
- It does not set @param.attr; caller should initialize it to a correct OBJREF type.
- Return: On success return 0 or <0 on failure.
- */
+int qcom_tee_objref_from_arg(struct tee_param *param, struct qcom_tee_arg *arg,
struct qcom_tee_context *ctx);+int qcom_tee_driver_register(void); +void qcom_tee_driver_unregister(void);
/* OBJECTS: */
/* (1) Primordial Object. */ extern struct qcom_tee_object qcom_tee_primordial_object;
+/* (2) User Object API. */
+/* Is it a user object? */ +int is_qcom_tee_user_object(struct qcom_tee_object *object);
+/* Set user object's 'notify on release' flag. */ +void qcom_tee_user_object_set_notify(struct qcom_tee_object *object, bool notify);
+/**
- qcom_tee_user_param_to_object() - Convert OBJREF parameter to &struct qcom_tee_object.
- @object: object returned.
- @param: TEE parameter.
- @ctx: context in which the conversion should happen.
- @param is OBJREF with %TEE_IOCTL_OBJREF_USER flags.
- Return: On success return 0 or <0 on failure.
- */
+int qcom_tee_user_param_to_object(struct qcom_tee_object **object, struct tee_param *param,
struct qcom_tee_context *ctx);+/* Reverse what qcom_tee_user_param_to_object() does. */ +int qcom_tee_user_param_from_object(struct tee_param *param, struct qcom_tee_object *object,
struct qcom_tee_context *ctx);+struct qcom_tee_user_object_request_data {
int id; /* Id assigned to the request. */u64 object_id; /* Object id being invoked by QTEE. */u32 op; /* Requested operation on object. */int np; /* Number of parameters in the request.*/+};
+/**
- qcom_tee_user_object_pop() - Pop a request for a user object.
- @ctx: context to look for user object.
- @params: parameters for @op.
- @num_params: number of elements in the parameter array.
- @uaddr: user buffer for output MEMBUF parameters.
- @size: size of user buffer @uaddr.
- @data: information for the pop request.
- @params is filled along with @data for the picked request.
- Return: On success return 0 or <0 on failure.
- */
+int qcom_tee_user_object_pop(struct qcom_tee_context *ctx,
struct tee_param *params, int num_params,void __user *uaddr, size_t size,struct qcom_tee_user_object_request_data *data);+/**
- qcom_tee_user_object_submit() - Submit a response for a user object.
- @ctx: context to look for user object.
- @params: returned parameters.
- @num_params: number of elements in the parameter array.
- @id: request id for the response.
- @errno: result of user object invocation.
- Return: On success return 0 or <0 on failure.
- */
+int qcom_tee_user_object_submit(struct qcom_tee_context *ctx,
struct tee_param *params, int num_params, int id, int errno);+/**
- qcom_tee_requests_destroy() - Destroy requests in a context.
- @ctx: context for which to destroy requests.
- After calling qcom_tee_requests_destroy(), @ctx can not be reused.
- It should be called on @ctx cleanup path.
- */
+void qcom_tee_requests_destroy(struct qcom_tee_context *ctx);
#endif /* QCOM_TEE_PRIVATE_H */ diff --git a/drivers/tee/qcomtee/user_obj.c b/drivers/tee/qcomtee/user_obj.c new file mode 100644 index 000000000000..4c671a3ae0de --- /dev/null +++ b/drivers/tee/qcomtee/user_obj.c @@ -0,0 +1,625 @@ +// SPDX-License-Identifier: GPL-2.0-only +/*
- Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
- */
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+#include "qcomtee_private.h"
+/**
- DOC: User Objects aka Supplicants
- Any userspace process with access to the TEE device file can behave as a supplicant
- by creating a user object. Any TEE parameter of type OBJREF with %QCOM_TEE_OBJREF_FLAG_USER
- flag set is considered as user object.
- A supplicant uses qcom_tee_user_object_pick() (i.e. TEE_IOC_SUPPL_RECV) to receive a
- QTEE user object request and qcom_tee_user_object_submit() (i.e. TEE_IOC_SUPPL_SEND)
- to submit a response. QTEE expects to receive the response, including OB and OO in
- specific order in the message; parameters submitted with qcom_tee_user_object_submit()
- should maintain this order.
- */
+/**
- struct qcom_tee_user_object - User object.
- @object: &struct qcom_tee_object representing this user object.
- @ctx: context for which user object is defined.
- @object_id: object ID in @ctx.
- @nor: notify userspace if object is released.
- Any object managed in userspace is represented with this struct.
- If @nor is set, on release a notification message is send back to the userspace.
- */
+struct qcom_tee_user_object {
struct qcom_tee_object object;struct qcom_tee_context *ctx;u64 object_id;bool nor;+};
+#define to_qcom_tee_user_object(o) container_of((o), struct qcom_tee_user_object, object)
+static struct qcom_tee_object_operations qcom_tee_user_object_ops;
+/* Is it a user object? */ +int is_qcom_tee_user_object(struct qcom_tee_object *object) +{
return object != NULL_QCOM_TEE_OBJECT &&typeof_qcom_tee_object(object) == QCOM_TEE_OBJECT_TYPE_CB_OBJECT &&object->ops == &qcom_tee_user_object_ops;+}
+/* Set user object's 'notify on release' flag. */ +void qcom_tee_user_object_set_notify(struct qcom_tee_object *object, bool notify) +{
if (is_qcom_tee_user_object(object))WRITE_ONCE(to_qcom_tee_user_object(object)->nor, notify);
It looks like this depends on some synchronization. Please add a comment on how this works.
+}
+/**
- enum qcom_tee_req_state - Current state of request.
- @QCOM_TEE_REQ_QUEUED: Request is waiting for supplicant.
- @QCOM_TEE_REQ_PROCESSING: Request has been picked by the supplicant.
- @QCOM_TEE_REQ_PROCESSED: Response has been submitted for the request.
- */
+enum qcom_tee_req_state {
QCOM_TEE_REQ_QUEUED = 1,QCOM_TEE_REQ_PROCESSING,QCOM_TEE_REQ_PROCESSED,+};
+/* User requests sent to supplicants. */ +struct qcom_tee_user_req {
enum qcom_tee_req_state state;int req_id; /* Request ID. */u64 object_id; /* User object ID. */u32 op; /* Operation to perform on object. */struct qcom_tee_arg *args; /* QTEE arguments for this operation. */int errno; /* Result of operation. */struct completion c; /* Completion for whoever wait for results. */+};
+/* Static placeholder for a request in PROCESSING state in qcom_tee_context.reqs_idr.
- If the thread initiated the QTEE call using qcom_tee_object_invoke() dies, and supplicant
- is processing the request, we replace the entry in qcom_tee_context.reqs_idr with
- __empty_ureq. So (1) the req_id remains busy and not reused, and (2) supplicant fails to
- submit response and does the necessary rollback.
- */
+static struct qcom_tee_user_req __empty_ureq = { .state = QCOM_TEE_REQ_PROCESSING };
+/* Enqueue a user request for a context. */ +static int qcom_tee_request_enqueue(struct qcom_tee_user_req *ureq, struct qcom_tee_context *ctx) +{
int ret;guard(mutex)(&ctx->lock);/* Supplicant is dying. */if (ctx->released)return -ENODEV;ret = idr_alloc(&ctx->reqs_idr, ureq, 0, 0, GFP_KERNEL);if (ret < 0)return ret;ureq->req_id = ret;ureq->state = QCOM_TEE_REQ_QUEUED;return 0;+}
+/**
- qcom_tee_requests_destroy() - Destroy requests in a context.
- @ctx: context for which to destroy requests.
- */
+void qcom_tee_requests_destroy(struct qcom_tee_context *ctx) +{
struct qcom_tee_user_req *ureq;int id;guard(mutex)(&ctx->lock);/* So qcom_tee_request_enqueue() refuses new requests. */ctx->released = 1;idr_for_each_entry(&ctx->reqs_idr, ureq, id) {if (ureq == &__empty_ureq)continue;/* ureq in QUEUED or PROCESSING state, terminate them. */if (ureq->op == QCOM_TEE_OBJREF_OP_RELEASE) {kfree(ureq);} else {ureq->state = QCOM_TEE_REQ_PROCESSED;ureq->errno = -ENODEV;complete(&ureq->c);}}+}
+/**
- qcom_tee_supp_pop_entry() - Pop the next request in a context.
When you pop something you'd expect it to be removed also.
- @ctx: context from which to pop a request.
- @ubuf_size: size of available buffer for MEMBUF parameters.
- @num_params: number of entries for TEE parameter array.
- It does not remove the request from &qcom_tee_context.reqs_idr.
- It checks if @num_params is large enough to fit the next request arguments.
- It checks if @ubuf_size is large enough to fit IB buffer arguments from QTEE.
- It updates request state to %QCOM_TEE_REQ_PROCESSING state.
- Return: On success return a request or NULL and ERR_PTR on failure.
- */
+static struct qcom_tee_user_req *qcom_tee_supp_pop_entry(struct qcom_tee_context *ctx,
size_t ubuf_size, int num_params)+{
struct qcom_tee_user_req *ureq;struct qcom_tee_arg *u;int i, id;guard(mutex)(&ctx->lock);/* Find the a QUEUED request. */
Is it _a_ or _the_?
idr_for_each_entry(&ctx->reqs_idr, ureq, id)if (ureq->state == QCOM_TEE_REQ_QUEUED)break;
Will this always result in a FIFO processing?
if (!ureq)return NULL;u = ureq->args;/* (1) Is there enough TEE parameters? */if (num_params < qcom_tee_args_len(u))return ERR_PTR(-EINVAL);/* (2) Is there enough space to pass input buffers? */qcom_tee_arg_for_each_input_buffer(i, u) {ubuf_size = size_sub(ubuf_size, u[i].b.size);if (ubuf_size == SIZE_MAX)return ERR_PTR(-EINVAL);ubuf_size = round_down(ubuf_size, 8);}/* Ready to process request 'QUEUED -> PROCESSING'. */ureq->state = QCOM_TEE_REQ_PROCESSING;return ureq;+}
+/* User object dispatcher. */ +static int qcom_tee_user_object_dispatch(struct qcom_tee_object_invoke_ctx *oic,
struct qcom_tee_object *object, u32 op,struct qcom_tee_arg *args)+{
struct qcom_tee_user_object *uo = to_qcom_tee_user_object(object);struct qcom_tee_user_req *ureq __free(kfree);struct qcom_tee_context *ctx = uo->ctx;int errno;ureq = kzalloc(sizeof(*ureq), GFP_KERNEL);if (!ureq)return -ENOMEM;init_completion(&ureq->c);ureq->object_id = uo->object_id;ureq->op = op;ureq->args = args;/* Queue the request. */if (qcom_tee_request_enqueue(ureq, ctx))return -ENODEV;/* Wakeup supplicant to process it. */complete(&ctx->req_c);/* Wait for supplicant to process the request. *//* Supplicant is expected to process request in a timely manner. We wait as KILLABLE,
requests
* in case supplicant and invoke thread both running from a same user process, otherwise
the same
* the process stuck on fatal signal.
might get stuck on a fatal signal?
*/
Please combine into one comment.
if (!wait_for_completion_state(&ureq->c, TASK_KILLABLE | TASK_FREEZABLE)) {errno = ureq->errno;/* On SUCCESS, end_cb_notify frees the request. */if (!errno)oic->data = no_free_ptr(ureq);} else {enum qcom_tee_req_state prev_state;errno = -ENODEV;scoped_guard(mutex, &ctx->lock) {prev_state = ureq->state;/* Replace ureq with '__empty_ureq' to keep req_id reserved. */if (prev_state == QCOM_TEE_REQ_PROCESSING)idr_replace(&ctx->reqs_idr, &__empty_ureq, ureq->req_id);/* Remove ureq as supplicant has never seen this request. */else if (prev_state == QCOM_TEE_REQ_QUEUED)idr_remove(&ctx->reqs_idr, ureq->req_id);}/* Wait for exclusive access to ureq. */synchronize_srcu(&ctx->req_srcu);
I'm sorry, I don't follow.
Cheers, Jens
/* Supplicant did some work for us, we should not discard it. */if (prev_state == QCOM_TEE_REQ_PROCESSED) {errno = ureq->errno;/* On SUCCESS, end_cb_notify frees the request. */if (!errno)oic->data = no_free_ptr(ureq);}}return errno;+}
+/* Called after submitting the callback response. */ +static void qcom_tee_user_object_end_cb_notify(struct qcom_tee_object_invoke_ctx *oic,
struct qcom_tee_object *unused_object, int err)+{
struct qcom_tee_user_req *ureq = oic->data;struct qcom_tee_arg *u = ureq->args;struct qcom_tee_object *object;int i;qcom_tee_arg_for_each_output_object(i, u) {object = u[i].o;/* If err, drop QTEE copy otherwise just drop driver's copy. */if (err && (typeof_qcom_tee_object(object) == QCOM_TEE_OBJECT_TYPE_CB_OBJECT))qcom_tee_object_put(object);qcom_tee_object_put(object);}kfree(ureq);+}
+static void qcom_tee_user_object_release(struct qcom_tee_object *object) +{
struct qcom_tee_user_object *uo = to_qcom_tee_user_object(object);struct qcom_tee_context *ctx = uo->ctx;struct qcom_tee_user_req *ureq;static struct qcom_tee_arg args[] = { { .type = QCOM_TEE_ARG_TYPE_INV } };if (READ_ONCE(uo->nor)) {ureq = kzalloc(sizeof(*ureq), GFP_KERNEL);if (ureq) {ureq->object_id = uo->object_id;ureq->op = QCOM_TEE_OBJREF_OP_RELEASE;ureq->args = args;/* Queue the RELEASE request and wake supplicant to process it. */qcom_tee_request_enqueue(ureq, ctx);complete(&ctx->req_c);} else {pr_err("failed to notify user object (%s) release.\n",qcom_tee_object_name(object));}}/* Matching get is in qcom_tee_user_param_to_object(). */kref_put(&ctx->ref_cnt, __qcom_tee_context_destroy);kfree(uo);+}
+static struct qcom_tee_object_operations qcom_tee_user_object_ops = {
.release = qcom_tee_user_object_release,.notify = qcom_tee_user_object_end_cb_notify,.dispatch = qcom_tee_user_object_dispatch,+};
+/**
- qcom_tee_user_param_to_object() - Convert OBJREF parameter to &struct qcom_tee_object.
- @object: object returned.
- @param: TEE parameter.
- @ctx: context in which the conversion should happen.
- @param is OBJREF with %QCOM_TEE_OBJREF_FLAG_USER flags.
- Return: On success return 0 or <0 on failure.
- */
+int qcom_tee_user_param_to_object(struct qcom_tee_object **object, struct tee_param *param,
struct qcom_tee_context *ctx)+{
struct qcom_tee_user_object *user_object __free(kfree) = NULL;struct qcom_tee_object *uo;int err;user_object = kzalloc(sizeof(*user_object), GFP_KERNEL);if (!user_object)return -ENOMEM;user_object->ctx = ctx;user_object->object_id = param->u.objref.id;/* By default, always notify userspace on release. */user_object->nor = true;err = qcom_tee_object_user_init(&user_object->object, QCOM_TEE_OBJECT_TYPE_CB_OBJECT,&qcom_tee_user_object_ops, "uo-%lu", param->u.objref.id);if (err)return err;uo = &no_free_ptr(user_object)->object;/* Keep context alive as user object is alive. */kref_get(&ctx->ref_cnt);*object = uo;return 0;+}
+/* Reverse what qcom_tee_user_param_to_object() does. */ +int qcom_tee_user_param_from_object(struct tee_param *param, struct qcom_tee_object *object,
struct qcom_tee_context *ctx)+{
struct qcom_tee_user_object *uo;if (!is_qcom_tee_user_object(object))return -EINVAL;uo = to_qcom_tee_user_object(object);/* Sure if the object is in a same context as caller? */if (uo->ctx != ctx)return -EINVAL;param->u.objref.id = uo->object_id;param->u.objref.flags = QCOM_TEE_OBJREF_FLAG_USER;/* User objects are valid in the context of userspace; drop the driver copy. */qcom_tee_object_put(object);return 0;+}
+/**
- qcom_tee_cb_params_from_args() - Convert QTEE arguments to TEE parameters in a context.
- @params: TEE parameters.
- @u: QTEE arguments.
- @num_params: number of elements in the parameter array.
- @ubuf_addr: user buffer for argument of type %QCOM_TEE_ARG_TYPE_IB.
- @ubuf_size: size of user buffer.
- @ctx: context in which the conversion should happen.
- It expects @params to have enough entries for @u. Entries in @params are of
- %TEE_IOCTL_PARAM_ATTR_TYPE_NONE. On failure, it puts IO objects.
- Return: On success return number of input parameters processed or <0 on failure.
- */
+static int qcom_tee_cb_params_from_args(struct tee_param *params,
struct qcom_tee_arg *u, int num_params,void __user *ubuf_addr, size_t ubuf_size,struct qcom_tee_context *ctx)+{
int i, np = 0;qcom_tee_arg_for_each(i, u) {enum qcom_tee_arg_type at = u[i].type;if (at == QCOM_TEE_ARG_TYPE_IB) {params[np].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMBUF_INPUT;/* Underflow already checked in qcom_tee_supp_pop_entry(). */ubuf_size = round_down(ubuf_size - u[i].b.size, 8);params[np].u.membuf.uaddr = (void * __user)(ubuf_addr + ubuf_size);params[np].u.membuf.size = u[i].b.size;if (copy_to_user(params[np].u.membuf.uaddr, u[i].b.addr, u[i].b.size))goto out_failed;np++;} else if (at == QCOM_TEE_ARG_TYPE_IO) {params[np].attr = TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_INPUT;if (qcom_tee_objref_from_arg(¶ms[np], &u[i], ctx))goto out_failed;np++;}}return np;+out_failed:
/* On ERROR: *//* - Release params associated to QTEE objects in this context so far. */for (; np >= 0; np--) {if (params[np].attr == TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_INPUT)qcom_tee_context_del_qtee_object(¶ms[np], ctx);}/* - Release any IO objects not processed so far. */while (u[i].type) {if (u[i].type == QCOM_TEE_ARG_TYPE_IO)qcom_tee_object_put(u[i++].o);}return -EINVAL;+}
+/**
- qcom_tee_cb_params_to_args() - Convert TEE parameters to QTEE arguments in a context.
- @u: QTEE arguments.
- @params: TEE parameters.
- @num_params: number of elements in the parameter array.
- @ctx: context in which the conversion should happen.
- Return: On success return 0 or <0 on failure.
- */
+static int qcom_tee_cb_params_to_args(struct qcom_tee_arg *u,
struct tee_param *params, int num_params,struct qcom_tee_context *ctx)+{
struct qcom_tee_object *object;int i, np = 0;qcom_tee_arg_for_each(i, u) {enum qcom_tee_arg_type at = u[i].type;if (at == QCOM_TEE_ARG_TYPE_OB) {if (params[np].attr != TEE_IOCTL_PARAM_ATTR_TYPE_MEMBUF_OUTPUT ||params[np].u.membuf.size > u[i].b.size)goto out_failed;if (copy_from_user(u[i].b.addr, params[np].u.membuf.uaddr, u[i].b.size))goto out_failed;u[i].b.size = params[np].u.membuf.size;np++;} else if (at == QCOM_TEE_ARG_TYPE_OO) {if (params[np].attr == TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_OUTPUT) {if (qcom_tee_objref_to_arg(&u[i], ¶ms[np], ctx))goto out_failed;} else {goto out_failed;}np++;}}return 0;+out_failed:
/* On ERROR: *//* Put OO objects processed so far. */for (--i; i >= 0; i--) {if (u[i].type != QCOM_TEE_ARG_TYPE_OO)continue;object = u[i].o;qcom_tee_user_object_set_notify(object, false);/* For callback object, we hold a reference for the driver, put it. */if (typeof_qcom_tee_object(object) == QCOM_TEE_OBJECT_TYPE_CB_OBJECT)qcom_tee_object_put(object);/* Put QTEE copy of object. */qcom_tee_object_put(object);}return -EINVAL;+}
+/**
- qcom_tee_user_object_pop() - Pop a request for a user object.
- @ctx: context to look for user object.
- @params: parameters for @op.
- @num_params: number of elements in the parameter array.
- @uaddr: user buffer for output MEMBUF parameters.
- @size: size of user buffer @uaddr.
- @data: information for the pop request.
- @params is filled along with @data for the picked request.
- Return: On success return 0 or <0 on failure.
- */
+int qcom_tee_user_object_pop(struct qcom_tee_context *ctx,
struct tee_param *params, int num_params,void __user *uaddr, size_t size,struct qcom_tee_user_object_request_data *data)+{
struct qcom_tee_user_req *ureq;struct qcom_tee_arg *req_args;u64 req_object_id;u32 req_op;int req_id;int ret;while (1) {scoped_guard(srcu, &ctx->req_srcu) {/* Pop a request 'QUEUED -> PROCESSING'. */ureq = qcom_tee_supp_pop_entry(ctx, size, num_params);if (!ureq)goto wait_for_request;/* On failure, issue with params, e.g. not enough space in user buffer. */if (IS_ERR(ureq))return PTR_ERR(ureq);/* ''Prepare user request:'' */req_id = ureq->req_id;req_object_id = ureq->object_id;req_op = ureq->op;req_args = ureq->args;ret = qcom_tee_cb_params_from_args(params, req_args,num_params, uaddr,size, ctx);}if (ret >= 0)break;/* On failure, issue with req_args, e.g. invalid object. */scoped_guard(mutex, &ctx->lock) {/* If (!= __empty_req) then 'PROCESSING -> PROCESSED'. */if (idr_remove(&ctx->reqs_idr, req_id) == ureq) {ureq->state = QCOM_TEE_REQ_PROCESSED;ureq->errno = ret;/* Send error to QTEE. */complete(&ureq->c);}}/* Try next request. */continue;+wait_for_request:
/* Wait for a new QUEUED request. */if (wait_for_completion_interruptible(&ctx->req_c))return -ERESTARTSYS;}/* It is a RELEASE message; no one is waiting for result. */if (req_op == QCOM_TEE_OBJREF_OP_RELEASE) {scoped_guard(mutex, &ctx->lock)idr_remove(&ctx->reqs_idr, req_id);kfree(ureq);}/* ''Pick a pending request:'' */data->id = req_id;data->object_id = req_object_id;data->op = req_op;data->np = ret;return 0;+}
+/**
- qcom_tee_user_object_submit() - Submit a response for a user object.
- @ctx: context to look for user object.
- @params: returned parameters.
- @num_params: number of elements in the parameter array.
- @id: request id for the response.
- @errno: result of user object invocation.
- Return: On success return 0 or <0 on failure.
- */
+int qcom_tee_user_object_submit(struct qcom_tee_context *ctx,
struct tee_param *params, int num_params, int id, int errno)+{
struct qcom_tee_user_req *ureq;scoped_guard(srcu, &ctx->req_srcu) {scoped_guard(mutex, &ctx->lock) {ureq = idr_remove(&ctx->reqs_idr, id);/* Is id invalid, or no one is waiting on response. */if (ureq == &__empty_ureq || !ureq)return -ENODEV;ureq->state = QCOM_TEE_REQ_PROCESSED;}ureq->errno = errno;/* Process params only on SUCCESS. */if (!errno)ureq->errno = qcom_tee_cb_params_to_args(ureq->args, params,num_params, ctx);errno = (!errno && ureq->errno) ? ureq->errno : 0;/* Send result to QTEE. */complete(&ureq->c);}return errno;+} diff --git a/include/uapi/linux/tee.h b/include/uapi/linux/tee.h index 5d33a8009efb..00e599d1582d 100644 --- a/include/uapi/linux/tee.h +++ b/include/uapi/linux/tee.h @@ -59,6 +59,7 @@ #define TEE_IMPL_ID_OPTEE 1 #define TEE_IMPL_ID_AMDTEE 2 #define TEE_IMPL_ID_TSTEE 3 +#define TEE_IMPL_ID_QTEE 4
/*
- OP-TEE specific capabilities
-- 2.34.1