Hi Amirreza, On Tue, Dec 3, 2024 at 5:20 AM Amirreza Zarrabi <quic_azarrabi@xxxxxxxxxxx> wrote: > > Introduce basic support for invoking objects hosted in QTEE and userspace > through the TEE subsystem. > > Signed-off-by: Amirreza Zarrabi <quic_azarrabi@xxxxxxxxxxx> > --- > 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 && > + param->attr != TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_OUTPUT && > + param->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 && > + params[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 && > + params[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 >