// SPDX-License-Identifier: GPL-2.0-only/* * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/slab.h>#include <linux/tee.h>#include <linux/platform_device.h>#include <linux/xarray.h> #include "qcomtee.h" static int find_qtee_object(struct qcomtee_object **object, unsigned long id, struct qcomtee_context_data *ctxdata){ int err = 0; guard(rcu)(); /* Object release is RCU protected. */ *object = idr_find(&ctxdata->qtee_objects_idr, id); if (!qcomtee_object_get(*object)) err = -EINVAL; return err;} static void del_qtee_object(unsigned long id, struct qcomtee_context_data *ctxdata){ struct qcomtee_object *object; scoped_guard(mutex, &ctxdata->qtee_lock) object = idr_remove(&ctxdata->qtee_objects_idr, id); qcomtee_object_put(object);} /** * qcomtee_context_add_qtee_object() - Add a QTEE object to the context. * @param: TEE parameter representing @object. * @object: QTEE object. * @ctx: context to add the object. * * It assumes @object is %QCOMTEE_OBJECT_TYPE_TEE and the caller has already * issued qcomtee_object_get() for @object. * * Return: On success, returns 0; on failure, returns < 0. */int qcomtee_context_add_qtee_object(struct tee_param *param, struct qcomtee_object *object, struct tee_context *ctx){ int ret; struct qcomtee_context_data *ctxdata = ctx->data; scoped_guard(mutex, &ctxdata->qtee_lock) ret = idr_alloc(&ctxdata->qtee_objects_idr, object, 0, 0, GFP_KERNEL); if (ret < 0) return ret; param->u.objref.id = ret; /* QTEE Object: QCOMTEE_OBJREF_FLAG_TEE set. */ param->u.objref.flags = QCOMTEE_OBJREF_FLAG_TEE; return 0;} /* Retrieve the QTEE object added with qcomtee_context_add_qtee_object(). */int qcomtee_context_find_qtee_object(struct qcomtee_object **object, struct tee_param *param, struct tee_context *ctx){ struct qcomtee_context_data *ctxdata = ctx->data; return find_qtee_object(object, param->u.objref.id, ctxdata);} /** * qcomtee_context_del_qtee_object() - Delete a QTEE object from the context. * @param: TEE parameter representing @object. * @ctx: context for deleting the object. * * The @param has been initialized by qcomtee_context_add_qtee_object(). */void qcomtee_context_del_qtee_object(struct tee_param *param, struct tee_context *ctx){ struct qcomtee_context_data *ctxdata = ctx->data; /* 'qtee_objects_idr' stores QTEE objects only. */ if (param->u.objref.flags & QCOMTEE_OBJREF_FLAG_TEE) del_qtee_object(param->u.objref.id, ctxdata);} /** * qcomtee_objref_to_arg() - Convert OBJREF parameter to QTEE argument. * @arg: QTEE argument. * @param: TEE parameter. * @ctx: context in which the conversion should happen. * * It assumes @param is an OBJREF. * It does not set @arg.type; the caller should initialize it to a correct * &enum qcomtee_arg_type value. It gets the object's refcount in @arg; * the caller should manage to put it afterward. * * Return: On success, returns 0; on failure, returns < 0. */int qcomtee_objref_to_arg(struct qcomtee_arg *arg, struct tee_param *param, struct tee_context *ctx){ int err = -EINVAL; arg->o = NULL_QCOMTEE_OBJECT; /* param is a NULL object: */ if (param->u.objref.id == TEE_OBJREF_NULL) return 0; /* param is a callback object: */ if (param->u.objref.flags & QCOMTEE_OBJREF_FLAG_USER) err = qcomtee_user_param_to_object(&arg->o, param, ctx); /* param is a QTEE object: */ else if (param->u.objref.flags & QCOMTEE_OBJREF_FLAG_TEE) err = qcomtee_context_find_qtee_object(&arg->o, param, ctx); /* param is a memory object: */ else if (param->u.objref.flags & QCOMTEE_OBJREF_FLAG_MEM) err = qcomtee_memobj_param_to_object(&arg->o, param, ctx); /* * For callback objects, call qcomtee_object_get() to keep a temporary * copy for the driver, as these objects are released asynchronously * and may disappear even before returning from QTEE. * * - For direct object invocations, the matching put is called in * qcomtee_object_invoke() when parsing the QTEE response. * - For callback responses, put is called in qcomtee_user_object_notify() * after QTEE has received its copies. */ if (!err && (typeof_qcomtee_object(arg->o) == QCOMTEE_OBJECT_TYPE_CB)) qcomtee_object_get(arg->o); return err;} /** * qcomtee_objref_from_arg() - Convert QTEE argument to OBJREF param. * @param: TEE parameter. * @arg: QTEE argument. * @ctx: context in which the conversion should happen. * * It assumes @arg is of %QCOMTEE_ARG_TYPE_IO or %QCOMTEE_ARG_TYPE_OO. * It does not set @param.attr; the caller should initialize it to a * correct type. * * Return: On success, returns 0; on failure, returns < 0. */int qcomtee_objref_from_arg(struct tee_param *param, struct qcomtee_arg *arg, struct tee_context *ctx){ struct qcomtee_object *object = arg->o; switch (typeof_qcomtee_object(object)) { case QCOMTEE_OBJECT_TYPE_NULL: param->u.objref.id = TEE_OBJREF_NULL; return 0; case QCOMTEE_OBJECT_TYPE_CB: /* object is a callback object: */ if (is_qcomtee_user_object(object)) return qcomtee_user_param_from_object(param, object, ctx); /* object is a memory object: */ else if (is_qcomtee_memobj_object(object)) return qcomtee_memobj_param_from_object(param, object, ctx); break; case QCOMTEE_OBJECT_TYPE_TEE: return qcomtee_context_add_qtee_object(param, object, ctx); case QCOMTEE_OBJECT_TYPE_ROOT: default: break; } return -EINVAL;} /** * qcomtee_params_to_args() - Convert TEE parameters to QTEE arguments. * @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_params + 1 entries and has been initialized * with %QCOMTEE_ARG_TYPE_INV as &struct qcomtee_arg.type. * * Return: On success, returns 0; on failure, returns < 0. */static int qcomtee_params_to_args(struct qcomtee_arg *u, struct tee_param *params, int num_params, struct tee_context *ctx){ int i; for (i = 0; i < num_params; i++) { switch (params[i].attr) { case TEE_IOCTL_PARAM_ATTR_TYPE_UBUF_INPUT: case TEE_IOCTL_PARAM_ATTR_TYPE_UBUF_OUTPUT: u[i].flags = QCOMTEE_ARG_FLAGS_UADDR; u[i].b.uaddr = params[i].u.ubuf.uaddr; u[i].b.size = params[i].u.ubuf.size; if (params[i].attr == TEE_IOCTL_PARAM_ATTR_TYPE_UBUF_INPUT) u[i].type = QCOMTEE_ARG_TYPE_IB; else /* TEE_IOCTL_PARAM_ATTR_TYPE_UBUF_OUTPUT */ u[i].type = QCOMTEE_ARG_TYPE_OB; break; case TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_INPUT: u[i].type = QCOMTEE_ARG_TYPE_IO; if (qcomtee_objref_to_arg(&u[i], ¶ms[i], ctx)) goto out_failed; break; case TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_OUTPUT: u[i].type = QCOMTEE_ARG_TYPE_OO; u[i].o = NULL_QCOMTEE_OBJECT; break; default: goto out_failed; } } return 0; out_failed: /* Undo qcomtee_objref_to_arg(). */ for (i--; i >= 0; i--) { if (u[i].type != QCOMTEE_ARG_TYPE_IO) continue; qcomtee_user_object_set_notify(u[i].o, false); /* See docs for qcomtee_objref_to_arg() for double put. */ if (typeof_qcomtee_object(u[i].o) == QCOMTEE_OBJECT_TYPE_CB) qcomtee_object_put(u[i].o); qcomtee_object_put(u[i].o); } return -EINVAL;} /** * qcomtee_params_from_args() - Convert QTEE arguments to TEE parameters. * @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 already been initialized by qcomtee_params_to_args(). * This also represents the end of a QTEE invocation that started with * qcomtee_params_to_args() by releasing %QCOMTEE_ARG_TYPE_IO objects. * * Return: On success, returns 0; on failure, returns < 0. */static int qcomtee_params_from_args(struct tee_param *params, struct qcomtee_arg *u, int num_params, struct tee_context *ctx){ int i, np; qcomtee_arg_for_each(np, u) { switch (u[np].type) { case QCOMTEE_ARG_TYPE_OB: /* TEE_IOCTL_PARAM_ATTR_TYPE_UBUF_OUTPUT */ params[np].u.ubuf.size = u[np].b.size; break; case QCOMTEE_ARG_TYPE_IO: /* IEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_INPUT */ qcomtee_object_put(u[np].o); break; case QCOMTEE_ARG_TYPE_OO: /* TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_OUTPUT */ if (qcomtee_objref_from_arg(¶ms[np], &u[np], ctx)) goto out_failed; break; case QCOMTEE_ARG_TYPE_IB: default: break; } } return 0; out_failed: /* Undo qcomtee_objref_from_arg(). */ for (i = 0; i < np; i++) { if (params[i].attr == TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_OUTPUT) qcomtee_context_del_qtee_object(¶ms[i], ctx); } /* Release any IO and OO objects not processed. */ for (; i < num_params && u[i].type; i++) { if (u[i].type == QCOMTEE_ARG_TYPE_OO || u[i].type == QCOMTEE_ARG_TYPE_IO) qcomtee_object_put(u[i].o); } return -EINVAL;} /* TEE Device Ops. */ static int qcomtee_params_check(struct tee_param *params, int num_params){ int io = 0, oo = 0, ib = 0, ob = 0; int i; /* QTEE can accept 64 arguments. */ if (num_params > QCOMTEE_ARGS_MAX) return -EINVAL; /* Supported parameter types. */ for (i = 0; i < num_params; i++) { switch (params[i].attr) { case TEE_IOCTL_PARAM_ATTR_TYPE_UBUF_INPUT: ib++; break; case TEE_IOCTL_PARAM_ATTR_TYPE_UBUF_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 can accept 16 arguments of each supported types. */ if (io > QCOMTEE_ARGS_PER_TYPE || oo > QCOMTEE_ARGS_PER_TYPE || ib > QCOMTEE_ARGS_PER_TYPE || ob > QCOMTEE_ARGS_PER_TYPE) return -EINVAL; return 0;} /* Check if an operation on ROOT_QCOMTEE_OBJECT from userspace is permitted. */static int qcomtee_root_object_check(u32 op, struct tee_param *params, int num_params){ /* Some privileged operations recognized by QTEE. */ if (op == QCOMTEE_ROOT_OP_NOTIFY_DOMAIN_CHANGE || op == QCOMTEE_ROOT_OP_ADCI_ACCEPT || op == QCOMTEE_ROOT_OP_ADCI_SHUTDOWN) return -EINVAL; /* * QCOMTEE_ROOT_OP_REG_WITH_CREDENTIALS is to register with QTEE * by passing a credential object as input OBJREF. TEE_OBJREF_NULL as a * credential object represents a privileged client for QTEE and * is used by the kernel only. */ if (op == QCOMTEE_ROOT_OP_REG_WITH_CREDENTIALS && num_params == 2) { 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; } } return 0;} /** * qcomtee_object_invoke() - Invoke a QTEE object. * @ctx: TEE context. * @arg: ioctl arguments. * @params: parameters for the object. * * Return: On success, returns 0; on failure, returns < 0. */static int qcomtee_object_invoke(struct tee_context *ctx, struct tee_ioctl_object_invoke_arg *arg, struct tee_param *params){ struct qcomtee_context_data *ctxdata = ctx->data; struct qcomtee_object *object; int i, ret, result; if (qcomtee_params_check(params, arg->num_params)) return -EINVAL; /* First, handle reserved operations: */ if (arg->op == QCOMTEE_MSG_OBJECT_OP_RELEASE) { del_qtee_object(arg->id, ctxdata); return 0; } /* Otherwise, invoke a QTEE object: */ struct qcomtee_object_invoke_ctx *oic __free(kfree) = qcomtee_object_invoke_ctx_alloc(ctx); if (!oic) return -ENOMEM; /* +1 for ending QCOMTEE_ARG_TYPE_INV. */ struct qcomtee_arg *u __free(kfree) = kzalloc_objs(*u, arg->num_params + 1); if (!u) return -ENOMEM; /* Get an object to invoke. */ if (arg->id == TEE_OBJREF_NULL) { /* Use ROOT if TEE_OBJREF_NULL is invoked. */ if (qcomtee_root_object_check(arg->op, params, arg->num_params)) return -EINVAL; object = ROOT_QCOMTEE_OBJECT; } else if (find_qtee_object(&object, arg->id, ctxdata)) { return -EINVAL; } ret = qcomtee_params_to_args(u, params, arg->num_params, ctx); if (ret) goto out; ret = qcomtee_object_do_invoke(oic, object, arg->op, u, &result); if (ret) { qcomtee_arg_for_each_input_object(i, u) { qcomtee_user_object_set_notify(u[i].o, false); qcomtee_object_put(u[i].o); } goto out; } /* Prase QTEE response and put driver's object copies: */ if (!result) { /* Assume service is UNAVAIL if unable to process the result. */ if (qcomtee_params_from_args(params, u, arg->num_params, ctx)) result = QCOMTEE_MSG_ERROR_UNAVAIL; } else { /* * qcomtee_params_to_args() gets a copy of IO for the driver to * make sure they do not get released while in the middle of * invocation. On success (!result), qcomtee_params_from_args() * puts them; Otherwise, put them here. */ qcomtee_arg_for_each_input_object(i, u) qcomtee_object_put(u[i].o); } arg->ret = result;out: qcomtee_object_put(object); return ret;} /** * qcomtee_supp_recv() - Wait for a request for the supplicant. * @ctx: TEE context. * @op: requested operation on the object. * @num_params: number of elements in the parameter array. * @params: parameters for @op. * * The first parameter is a meta %TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT. * On input, it provides a user buffer. This buffer is used for parameters of * type %TEE_IOCTL_PARAM_ATTR_TYPE_UBUF_INPUT in qcomtee_cb_params_from_args(). * On output, the object ID and request ID are stored in the meta parameter. * * @num_params is updated to the number of parameters that actually exist * in @params on return. * * Return: On success, returns 0; on failure, returns < 0. */static int qcomtee_supp_recv(struct tee_context *ctx, u32 *op, u32 *num_params, struct tee_param *params){ struct qcomtee_user_object_request_data data; void __user *uaddr; size_t ubuf_size; int i, ret; if (!*num_params) return -EINVAL; Lines 1–500 of 823. Load more
// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/slab.h> #include <linux/tee.h> #include <linux/platform_device.h> #include <linux/xarray.h> #include "qcomtee.h" static int find_qtee_object(struct qcomtee_object **object, unsigned long id, struct qcomtee_context_data *ctxdata) { int err = 0; guard(rcu)(); /* Object release is RCU protected. */ *object = idr_find(&ctxdata->qtee_objects_idr, id); if (!qcomtee_object_get(*object)) err = -EINVAL; return err; } static void del_qtee_object(unsigned long id, struct qcomtee_context_data *ctxdata) { struct qcomtee_object *object; scoped_guard(mutex, &ctxdata->qtee_lock) object = idr_remove(&ctxdata->qtee_objects_idr, id); qcomtee_object_put(object); } /** * qcomtee_context_add_qtee_object() - Add a QTEE object to the context. * @param: TEE parameter representing @object. * @object: QTEE object. * @ctx: context to add the object. * * It assumes @object is %QCOMTEE_OBJECT_TYPE_TEE and the caller has already * issued qcomtee_object_get() for @object. * * Return: On success, returns 0; on failure, returns < 0. */ int qcomtee_context_add_qtee_object(struct tee_param *param, struct qcomtee_object *object, struct tee_context *ctx) { int ret; struct qcomtee_context_data *ctxdata = ctx->data; scoped_guard(mutex, &ctxdata->qtee_lock) ret = idr_alloc(&ctxdata->qtee_objects_idr, object, 0, 0, GFP_KERNEL); if (ret < 0) return ret; param->u.objref.id = ret; /* QTEE Object: QCOMTEE_OBJREF_FLAG_TEE set. */ param->u.objref.flags = QCOMTEE_OBJREF_FLAG_TEE; return 0; } /* Retrieve the QTEE object added with qcomtee_context_add_qtee_object(). */ int qcomtee_context_find_qtee_object(struct qcomtee_object **object, struct tee_param *param, struct tee_context *ctx) { struct qcomtee_context_data *ctxdata = ctx->data; return find_qtee_object(object, param->u.objref.id, ctxdata); } /** * qcomtee_context_del_qtee_object() - Delete a QTEE object from the context. * @param: TEE parameter representing @object. * @ctx: context for deleting the object. * * The @param has been initialized by qcomtee_context_add_qtee_object(). */ void qcomtee_context_del_qtee_object(struct tee_param *param, struct tee_context *ctx) { struct qcomtee_context_data *ctxdata = ctx->data; /* 'qtee_objects_idr' stores QTEE objects only. */ if (param->u.objref.flags & QCOMTEE_OBJREF_FLAG_TEE) del_qtee_object(param->u.objref.id, ctxdata); } /** * qcomtee_objref_to_arg() - Convert OBJREF parameter to QTEE argument. * @arg: QTEE argument. * @param: TEE parameter. * @ctx: context in which the conversion should happen. * * It assumes @param is an OBJREF. * It does not set @arg.type; the caller should initialize it to a correct * &enum qcomtee_arg_type value. It gets the object's refcount in @arg; * the caller should manage to put it afterward. * * Return: On success, returns 0; on failure, returns < 0. */ int qcomtee_objref_to_arg(struct qcomtee_arg *arg, struct tee_param *param, struct tee_context *ctx) { int err = -EINVAL; arg->o = NULL_QCOMTEE_OBJECT; /* param is a NULL object: */ if (param->u.objref.id == TEE_OBJREF_NULL) return 0; /* param is a callback object: */ if (param->u.objref.flags & QCOMTEE_OBJREF_FLAG_USER) err = qcomtee_user_param_to_object(&arg->o, param, ctx); /* param is a QTEE object: */ else if (param->u.objref.flags & QCOMTEE_OBJREF_FLAG_TEE) err = qcomtee_context_find_qtee_object(&arg->o, param, ctx); /* param is a memory object: */ else if (param->u.objref.flags & QCOMTEE_OBJREF_FLAG_MEM) err = qcomtee_memobj_param_to_object(&arg->o, param, ctx); /* * For callback objects, call qcomtee_object_get() to keep a temporary * copy for the driver, as these objects are released asynchronously * and may disappear even before returning from QTEE. * * - For direct object invocations, the matching put is called in * qcomtee_object_invoke() when parsing the QTEE response. * - For callback responses, put is called in qcomtee_user_object_notify() * after QTEE has received its copies. */ if (!err && (typeof_qcomtee_object(arg->o) == QCOMTEE_OBJECT_TYPE_CB)) qcomtee_object_get(arg->o); return err; } /** * qcomtee_objref_from_arg() - Convert QTEE argument to OBJREF param. * @param: TEE parameter. * @arg: QTEE argument. * @ctx: context in which the conversion should happen. * * It assumes @arg is of %QCOMTEE_ARG_TYPE_IO or %QCOMTEE_ARG_TYPE_OO. * It does not set @param.attr; the caller should initialize it to a * correct type. * * Return: On success, returns 0; on failure, returns < 0. */ int qcomtee_objref_from_arg(struct tee_param *param, struct qcomtee_arg *arg, struct tee_context *ctx) { struct qcomtee_object *object = arg->o; switch (typeof_qcomtee_object(object)) { case QCOMTEE_OBJECT_TYPE_NULL: param->u.objref.id = TEE_OBJREF_NULL; return 0; case QCOMTEE_OBJECT_TYPE_CB: /* object is a callback object: */ if (is_qcomtee_user_object(object)) return qcomtee_user_param_from_object(param, object, ctx); /* object is a memory object: */ else if (is_qcomtee_memobj_object(object)) return qcomtee_memobj_param_from_object(param, object, ctx); break; case QCOMTEE_OBJECT_TYPE_TEE: return qcomtee_context_add_qtee_object(param, object, ctx); case QCOMTEE_OBJECT_TYPE_ROOT: default: break; } return -EINVAL; } /** * qcomtee_params_to_args() - Convert TEE parameters to QTEE arguments. * @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_params + 1 entries and has been initialized * with %QCOMTEE_ARG_TYPE_INV as &struct qcomtee_arg.type. * * Return: On success, returns 0; on failure, returns < 0. */ static int qcomtee_params_to_args(struct qcomtee_arg *u, struct tee_param *params, int num_params, struct tee_context *ctx) { int i; for (i = 0; i < num_params; i++) { switch (params[i].attr) { case TEE_IOCTL_PARAM_ATTR_TYPE_UBUF_INPUT: case TEE_IOCTL_PARAM_ATTR_TYPE_UBUF_OUTPUT: u[i].flags = QCOMTEE_ARG_FLAGS_UADDR; u[i].b.uaddr = params[i].u.ubuf.uaddr; u[i].b.size = params[i].u.ubuf.size; if (params[i].attr == TEE_IOCTL_PARAM_ATTR_TYPE_UBUF_INPUT) u[i].type = QCOMTEE_ARG_TYPE_IB; else /* TEE_IOCTL_PARAM_ATTR_TYPE_UBUF_OUTPUT */ u[i].type = QCOMTEE_ARG_TYPE_OB; break; case TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_INPUT: u[i].type = QCOMTEE_ARG_TYPE_IO; if (qcomtee_objref_to_arg(&u[i], ¶ms[i], ctx)) goto out_failed; break; case TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_OUTPUT: u[i].type = QCOMTEE_ARG_TYPE_OO; u[i].o = NULL_QCOMTEE_OBJECT; break; default: goto out_failed; } } return 0; out_failed: /* Undo qcomtee_objref_to_arg(). */ for (i--; i >= 0; i--) { if (u[i].type != QCOMTEE_ARG_TYPE_IO) continue; qcomtee_user_object_set_notify(u[i].o, false); /* See docs for qcomtee_objref_to_arg() for double put. */ if (typeof_qcomtee_object(u[i].o) == QCOMTEE_OBJECT_TYPE_CB) qcomtee_object_put(u[i].o); qcomtee_object_put(u[i].o); } return -EINVAL; } /** * qcomtee_params_from_args() - Convert QTEE arguments to TEE parameters. * @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 already been initialized by qcomtee_params_to_args(). * This also represents the end of a QTEE invocation that started with * qcomtee_params_to_args() by releasing %QCOMTEE_ARG_TYPE_IO objects. * * Return: On success, returns 0; on failure, returns < 0. */ static int qcomtee_params_from_args(struct tee_param *params, struct qcomtee_arg *u, int num_params, struct tee_context *ctx) { int i, np; qcomtee_arg_for_each(np, u) { switch (u[np].type) { case QCOMTEE_ARG_TYPE_OB: /* TEE_IOCTL_PARAM_ATTR_TYPE_UBUF_OUTPUT */ params[np].u.ubuf.size = u[np].b.size; break; case QCOMTEE_ARG_TYPE_IO: /* IEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_INPUT */ qcomtee_object_put(u[np].o); break; case QCOMTEE_ARG_TYPE_OO: /* TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_OUTPUT */ if (qcomtee_objref_from_arg(¶ms[np], &u[np], ctx)) goto out_failed; break; case QCOMTEE_ARG_TYPE_IB: default: break; } } return 0; out_failed: /* Undo qcomtee_objref_from_arg(). */ for (i = 0; i < np; i++) { if (params[i].attr == TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_OUTPUT) qcomtee_context_del_qtee_object(¶ms[i], ctx); } /* Release any IO and OO objects not processed. */ for (; i < num_params && u[i].type; i++) { if (u[i].type == QCOMTEE_ARG_TYPE_OO || u[i].type == QCOMTEE_ARG_TYPE_IO) qcomtee_object_put(u[i].o); } return -EINVAL; } /* TEE Device Ops. */ static int qcomtee_params_check(struct tee_param *params, int num_params) { int io = 0, oo = 0, ib = 0, ob = 0; int i; /* QTEE can accept 64 arguments. */ if (num_params > QCOMTEE_ARGS_MAX) return -EINVAL; /* Supported parameter types. */ for (i = 0; i < num_params; i++) { switch (params[i].attr) { case TEE_IOCTL_PARAM_ATTR_TYPE_UBUF_INPUT: ib++; break; case TEE_IOCTL_PARAM_ATTR_TYPE_UBUF_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 can accept 16 arguments of each supported types. */ if (io > QCOMTEE_ARGS_PER_TYPE || oo > QCOMTEE_ARGS_PER_TYPE || ib > QCOMTEE_ARGS_PER_TYPE || ob > QCOMTEE_ARGS_PER_TYPE) return -EINVAL; return 0; } /* Check if an operation on ROOT_QCOMTEE_OBJECT from userspace is permitted. */ static int qcomtee_root_object_check(u32 op, struct tee_param *params, int num_params) { /* Some privileged operations recognized by QTEE. */ if (op == QCOMTEE_ROOT_OP_NOTIFY_DOMAIN_CHANGE || op == QCOMTEE_ROOT_OP_ADCI_ACCEPT || op == QCOMTEE_ROOT_OP_ADCI_SHUTDOWN) return -EINVAL; /* * QCOMTEE_ROOT_OP_REG_WITH_CREDENTIALS is to register with QTEE * by passing a credential object as input OBJREF. TEE_OBJREF_NULL as a * credential object represents a privileged client for QTEE and * is used by the kernel only. */ if (op == QCOMTEE_ROOT_OP_REG_WITH_CREDENTIALS && num_params == 2) { 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; } } return 0; } /** * qcomtee_object_invoke() - Invoke a QTEE object. * @ctx: TEE context. * @arg: ioctl arguments. * @params: parameters for the object. * * Return: On success, returns 0; on failure, returns < 0. */ static int qcomtee_object_invoke(struct tee_context *ctx, struct tee_ioctl_object_invoke_arg *arg, struct tee_param *params) { struct qcomtee_context_data *ctxdata = ctx->data; struct qcomtee_object *object; int i, ret, result; if (qcomtee_params_check(params, arg->num_params)) return -EINVAL; /* First, handle reserved operations: */ if (arg->op == QCOMTEE_MSG_OBJECT_OP_RELEASE) { del_qtee_object(arg->id, ctxdata); return 0; } /* Otherwise, invoke a QTEE object: */ struct qcomtee_object_invoke_ctx *oic __free(kfree) = qcomtee_object_invoke_ctx_alloc(ctx); if (!oic) return -ENOMEM; /* +1 for ending QCOMTEE_ARG_TYPE_INV. */ struct qcomtee_arg *u __free(kfree) = kzalloc_objs(*u, arg->num_params + 1); if (!u) return -ENOMEM; /* Get an object to invoke. */ if (arg->id == TEE_OBJREF_NULL) { /* Use ROOT if TEE_OBJREF_NULL is invoked. */ if (qcomtee_root_object_check(arg->op, params, arg->num_params)) return -EINVAL; object = ROOT_QCOMTEE_OBJECT; } else if (find_qtee_object(&object, arg->id, ctxdata)) { return -EINVAL; } ret = qcomtee_params_to_args(u, params, arg->num_params, ctx); if (ret) goto out; ret = qcomtee_object_do_invoke(oic, object, arg->op, u, &result); if (ret) { qcomtee_arg_for_each_input_object(i, u) { qcomtee_user_object_set_notify(u[i].o, false); qcomtee_object_put(u[i].o); } goto out; } /* Prase QTEE response and put driver's object copies: */ if (!result) { /* Assume service is UNAVAIL if unable to process the result. */ if (qcomtee_params_from_args(params, u, arg->num_params, ctx)) result = QCOMTEE_MSG_ERROR_UNAVAIL; } else { /* * qcomtee_params_to_args() gets a copy of IO for the driver to * make sure they do not get released while in the middle of * invocation. On success (!result), qcomtee_params_from_args() * puts them; Otherwise, put them here. */ qcomtee_arg_for_each_input_object(i, u) qcomtee_object_put(u[i].o); } arg->ret = result; out: qcomtee_object_put(object); return ret; } /** * qcomtee_supp_recv() - Wait for a request for the supplicant. * @ctx: TEE context. * @op: requested operation on the object. * @num_params: number of elements in the parameter array. * @params: parameters for @op. * * The first parameter is a meta %TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT. * On input, it provides a user buffer. This buffer is used for parameters of * type %TEE_IOCTL_PARAM_ATTR_TYPE_UBUF_INPUT in qcomtee_cb_params_from_args(). * On output, the object ID and request ID are stored in the meta parameter. * * @num_params is updated to the number of parameters that actually exist * in @params on return. * * Return: On success, returns 0; on failure, returns < 0. */ static int qcomtee_supp_recv(struct tee_context *ctx, u32 *op, u32 *num_params, struct tee_param *params) { struct qcomtee_user_object_request_data data; void __user *uaddr; size_t ubuf_size; int i, ret; if (!*num_params) return -EINVAL;