// 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/firmware/qcom/qcom_scm.h>#include <linux/init.h>#include <linux/module.h>#include <linux/slab.h>#include <linux/uaccess.h>#include <linux/xarray.h> #include "qcomtee.h" /* QTEE root object. */struct qcomtee_object qcomtee_object_root = { .name = "root", .object_type = QCOMTEE_OBJECT_TYPE_ROOT, .info.qtee_id = QCOMTEE_MSG_OBJECT_ROOT,}; /* Next argument of type @type after index @i. */int qcomtee_next_arg_type(struct qcomtee_arg *u, int i, enum qcomtee_arg_type type){ while (u[i].type != QCOMTEE_ARG_TYPE_INV && u[i].type != type) i++; return i;} /* * QTEE expects IDs with QCOMTEE_MSG_OBJECT_NS_BIT set for objects of * QCOMTEE_OBJECT_TYPE_CB type. The first ID with QCOMTEE_MSG_OBJECT_NS_BIT * set is reserved for the primordial object. */#define QCOMTEE_OBJECT_PRIMORDIAL (QCOMTEE_MSG_OBJECT_NS_BIT)#define QCOMTEE_OBJECT_ID_START (QCOMTEE_OBJECT_PRIMORDIAL + 1)#define QCOMTEE_OBJECT_ID_END (U32_MAX) #define QCOMTEE_OBJECT_SET(p, type, ...) \ __QCOMTEE_OBJECT_SET(p, type, ##__VA_ARGS__, 0UL)#define __QCOMTEE_OBJECT_SET(p, type, optr, ...) \ do { \ (p)->object_type = (type); \ (p)->info.qtee_id = (unsigned long)(optr); \ } while (0) static struct qcomtee_object *qcomtee_qtee_object_alloc(struct qcomtee_object_invoke_ctx *oic, unsigned int object_id){ struct qcomtee *qcomtee = tee_get_drvdata(oic->ctx->teedev); struct qcomtee_object *object; object = kzalloc_obj(*object); if (!object) return NULL_QCOMTEE_OBJECT; /* If failed, "no-name". */ object->name = kasprintf(GFP_KERNEL, "qcomtee-%u", object_id); QCOMTEE_OBJECT_SET(object, QCOMTEE_OBJECT_TYPE_TEE, object_id); kref_init(&object->refcount); /* A QTEE object requires a context for async operations. */ object->info.qcomtee_async_ctx = qcomtee->ctx; teedev_ctx_get(object->info.qcomtee_async_ctx); return object;} static void qcomtee_qtee_object_free(struct qcomtee_object *object){ /* See qcomtee_qtee_object_alloc(). */ teedev_ctx_put(object->info.qcomtee_async_ctx); kfree(object->name); kfree(object);} static void qcomtee_do_release_qtee_object(struct work_struct *work){ struct qcomtee_object *object; struct qcomtee *qcomtee; int ret, result = 0; /* RELEASE does not require any argument. */ struct qcomtee_arg args[] = { { .type = QCOMTEE_ARG_TYPE_INV } }; object = container_of(work, struct qcomtee_object, work); qcomtee = tee_get_drvdata(object->info.qcomtee_async_ctx->teedev); /* Get the TEE context used for asynchronous operations. */ qcomtee->oic.ctx = object->info.qcomtee_async_ctx; ret = qcomtee_object_do_invoke_internal(&qcomtee->oic, object, QCOMTEE_MSG_OBJECT_OP_RELEASE, args, &result); /* Is it safe to retry the release? */ if (ret && ret != -ENODEV) { queue_work(qcomtee->wq, &object->work); } else { if (ret || result) pr_err("%s release failed, ret = %d (%x)\n", qcomtee_object_name(object), ret, result); qcomtee_qtee_object_free(object); }} static void qcomtee_release_qtee_object(struct qcomtee_object *object){ struct qcomtee *qcomtee = tee_get_drvdata(object->info.qcomtee_async_ctx->teedev); INIT_WORK(&object->work, qcomtee_do_release_qtee_object); queue_work(qcomtee->wq, &object->work);} static void qcomtee_object_release(struct kref *refcount){ struct qcomtee_object *object; const char *name; object = container_of(refcount, struct qcomtee_object, refcount); /* * qcomtee_object_get() is called in a RCU read lock. synchronize_rcu() * to avoid releasing the object while it is being accessed in * qcomtee_object_get(). */ synchronize_rcu(); switch (typeof_qcomtee_object(object)) { case QCOMTEE_OBJECT_TYPE_TEE: qcomtee_release_qtee_object(object); break; case QCOMTEE_OBJECT_TYPE_CB: name = object->name; if (object->ops->release) object->ops->release(object); kfree_const(name); break; case QCOMTEE_OBJECT_TYPE_ROOT: case QCOMTEE_OBJECT_TYPE_NULL: default: break; }} /** * qcomtee_object_get() - Increase the object's reference count. * @object: object to increase the reference count. * * Context: The caller should hold RCU read lock. */int qcomtee_object_get(struct qcomtee_object *object){ if (object != &qcomtee_primordial_object && object != NULL_QCOMTEE_OBJECT && object != ROOT_QCOMTEE_OBJECT) return kref_get_unless_zero(&object->refcount); return 0;} /** * qcomtee_object_put() - Decrease the object's reference count. * @object: object to decrease the reference count. */void qcomtee_object_put(struct qcomtee_object *object){ if (object != &qcomtee_primordial_object && object != NULL_QCOMTEE_OBJECT && object != ROOT_QCOMTEE_OBJECT) kref_put(&object->refcount, qcomtee_object_release);} static int qcomtee_idx_alloc(struct qcomtee_object_invoke_ctx *oic, u32 *idx, struct qcomtee_object *object){ struct qcomtee *qcomtee = tee_get_drvdata(oic->ctx->teedev); /* Every ID allocated here has QCOMTEE_MSG_OBJECT_NS_BIT set. */ return xa_alloc_cyclic(&qcomtee->xa_local_objects, idx, object, XA_LIMIT(QCOMTEE_OBJECT_ID_START, QCOMTEE_OBJECT_ID_END), &qcomtee->xa_last_id, GFP_KERNEL);} struct qcomtee_object *qcomtee_idx_erase(struct qcomtee_object_invoke_ctx *oic, u32 idx){ struct qcomtee *qcomtee = tee_get_drvdata(oic->ctx->teedev); if (idx < QCOMTEE_OBJECT_ID_START || idx > QCOMTEE_OBJECT_ID_END) return NULL_QCOMTEE_OBJECT; return xa_erase(&qcomtee->xa_local_objects, idx);} /** * qcomtee_object_id_get() - Get an ID for an object to send to QTEE. * @oic: context to use for the invocation. * @object: object to assign an ID. * @object_id: object ID. * * Called on the path to QTEE to construct the message; see * qcomtee_prepare_msg() and qcomtee_update_msg(). * * Return: On success, returns 0; on failure, returns < 0. */static int qcomtee_object_id_get(struct qcomtee_object_invoke_ctx *oic, struct qcomtee_object *object, unsigned int *object_id){ u32 idx; switch (typeof_qcomtee_object(object)) { case QCOMTEE_OBJECT_TYPE_CB: if (qcomtee_idx_alloc(oic, &idx, object) < 0) return -ENOSPC; *object_id = idx; break; case QCOMTEE_OBJECT_TYPE_ROOT: case QCOMTEE_OBJECT_TYPE_TEE: *object_id = object->info.qtee_id; break; case QCOMTEE_OBJECT_TYPE_NULL: *object_id = QCOMTEE_MSG_OBJECT_NULL; break; } return 0;} /* Release object ID assigned in qcomtee_object_id_get. */static void qcomtee_object_id_put(struct qcomtee_object_invoke_ctx *oic, unsigned int object_id){ qcomtee_idx_erase(oic, object_id);} /** * qcomtee_local_object_get() - Get the object referenced by the ID. * @oic: context to use for the invocation. * @object_id: object ID. * * It is called on the path from QTEE. * It is called on behalf of QTEE to obtain an instance of an object * for a given ID. It increases the object's reference count on success. * * Return: On error, returns %NULL_QCOMTEE_OBJECT. * On success, returns the object. */static struct qcomtee_object *qcomtee_local_object_get(struct qcomtee_object_invoke_ctx *oic, unsigned int object_id){ struct qcomtee *qcomtee = tee_get_drvdata(oic->ctx->teedev); struct qcomtee_object *object; if (object_id == QCOMTEE_OBJECT_PRIMORDIAL) return &qcomtee_primordial_object; guard(rcu)(); object = xa_load(&qcomtee->xa_local_objects, object_id); /* It already checks for %NULL_QCOMTEE_OBJECT. */ qcomtee_object_get(object); return object;} /** * qcomtee_object_user_init() - Initialize an object for the user. * @object: object to initialize. * @ot: type of object as &enum qcomtee_object_type. * @ops: instance of callbacks. * @fmt: name assigned to the object. * * Return: On success, returns 0; on failure, returns < 0. */int qcomtee_object_user_init(struct qcomtee_object *object, enum qcomtee_object_type ot, struct qcomtee_object_operations *ops, const char *fmt, ...){ va_list ap; int ret; kref_init(&object->refcount); QCOMTEE_OBJECT_SET(object, QCOMTEE_OBJECT_TYPE_NULL); va_start(ap, fmt); switch (ot) { case QCOMTEE_OBJECT_TYPE_NULL: ret = 0; break; case QCOMTEE_OBJECT_TYPE_CB: object->ops = ops; if (!object->ops->dispatch) { ret = -EINVAL; break; } /* If failed, "no-name". */ object->name = kvasprintf_const(GFP_KERNEL, fmt, ap); QCOMTEE_OBJECT_SET(object, QCOMTEE_OBJECT_TYPE_CB); ret = 0; break; case QCOMTEE_OBJECT_TYPE_ROOT: case QCOMTEE_OBJECT_TYPE_TEE: default: ret = -EINVAL; } va_end(ap); return ret;} /** * qcomtee_object_type() - Returns the type of object represented by an ID. * @object_id: object ID for the object. * * Similar to typeof_qcomtee_object(), but instead of receiving an object as * an argument, it receives an object ID. It is used internally on the return * path from QTEE. * * Return: Returns the type of object referenced by @object_id. */static enum qcomtee_object_type qcomtee_object_type(unsigned int object_id){ if (object_id == QCOMTEE_MSG_OBJECT_NULL) return QCOMTEE_OBJECT_TYPE_NULL; if (object_id & QCOMTEE_MSG_OBJECT_NS_BIT) return QCOMTEE_OBJECT_TYPE_CB; return QCOMTEE_OBJECT_TYPE_TEE;} /** * qcomtee_object_qtee_init() - Initialize an object for QTEE. * @oic: context to use for the invocation. * @object: object returned. * @object_id: object ID received from QTEE. * * Return: On failure, returns < 0 and sets @object to %NULL_QCOMTEE_OBJECT. * On success, returns 0 */static int qcomtee_object_qtee_init(struct qcomtee_object_invoke_ctx *oic, struct qcomtee_object **object, unsigned int object_id){ int ret = 0; switch (qcomtee_object_type(object_id)) { case QCOMTEE_OBJECT_TYPE_NULL: *object = NULL_QCOMTEE_OBJECT; break; case QCOMTEE_OBJECT_TYPE_CB: *object = qcomtee_local_object_get(oic, object_id); if (*object == NULL_QCOMTEE_OBJECT) ret = -EINVAL; break; default: /* QCOMTEE_OBJECT_TYPE_TEE */ *object = qcomtee_qtee_object_alloc(oic, object_id); if (*object == NULL_QCOMTEE_OBJECT) ret = -ENOMEM; break; } return ret;} /* * ''Marshaling API'' * qcomtee_prepare_msg - Prepare the inbound buffer for sending to QTEE * qcomtee_update_args - Parse the QTEE response in the inbound buffer * qcomtee_prepare_args - Parse the QTEE request from the outbound buffer * qcomtee_update_msg - Update the outbound buffer with the response for QTEE */ static int qcomtee_prepare_msg(struct qcomtee_object_invoke_ctx *oic, struct qcomtee_object *object, u32 op, struct qcomtee_arg *u){ struct qcomtee_msg_object_invoke *msg; unsigned int object_id; int i, ib, ob, io, oo; size_t offset; /* Use the input message buffer in 'oic'. */ msg = oic->in_msg.addr; /* Start offset in a message for buffer arguments. */ offset = qcomtee_msg_buffer_args(struct qcomtee_msg_object_invoke, qcomtee_args_len(u)); /* Get the ID of the object being invoked. */ if (qcomtee_object_id_get(oic, object, &object_id)) return -ENOSPC; ib = 0; qcomtee_arg_for_each_input_buffer(i, u) { void *msgptr; /* Address of buffer payload: */ /* Overflow already checked in qcomtee_msg_buffers_alloc(). */ msg->args[ib].b.offset = offset; msg->args[ib].b.size = u[i].b.size; msgptr = qcomtee_msg_offset_to_ptr(msg, offset); /* Userspace client or kernel client!? */ if (!(u[i].flags & QCOMTEE_ARG_FLAGS_UADDR)) memcpy(msgptr, u[i].b.addr, u[i].b.size); else if (copy_from_user(msgptr, u[i].b.uaddr, u[i].b.size)) return -EFAULT; offset += qcomtee_msg_offset_align(u[i].b.size); ib++; } ob = ib; qcomtee_arg_for_each_output_buffer(i, u) { /* Overflow already checked in qcomtee_msg_buffers_alloc(). */ msg->args[ob].b.offset = offset; msg->args[ob].b.size = u[i].b.size; offset += qcomtee_msg_offset_align(u[i].b.size); ob++; } io = ob; qcomtee_arg_for_each_input_object(i, u) { if (qcomtee_object_id_get(oic, u[i].o, &msg->args[io].o)) { qcomtee_object_id_put(oic, object_id); for (io--; io >= ob; io--) qcomtee_object_id_put(oic, msg->args[io].o); return -ENOSPC; } io++; } oo = io; qcomtee_arg_for_each_output_object(i, u) oo++; /* Set object, operation, and argument counts. */ qcomtee_msg_init(msg, object_id, op, ib, ob, io, oo); return 0;} /** * qcomtee_update_args() - Parse the QTEE response in the inbound buffer. * @u: array of arguments for the invocation. * @oic: context to use for the invocation. * * @u must be the same as the one used in qcomtee_prepare_msg() when * initializing the inbound buffer. * * On failure, it continues processing the QTEE message. The caller should * do the necessary cleanup, including calling qcomtee_object_put() * on the output objects. * * Return: On success, returns 0; on failure, returns < 0. */static int qcomtee_update_args(struct qcomtee_arg *u, struct qcomtee_object_invoke_ctx *oic){ struct qcomtee_msg_object_invoke *msg; int i, ib, ob, io, oo; int ret = 0; /* Use the input message buffer in 'oic'. */ msg = oic->in_msg.addr; ib = 0; qcomtee_arg_for_each_input_buffer(i, u) ib++; ob = ib; qcomtee_arg_for_each_output_buffer(i, u) { void *msgptr; /* Address of buffer payload: */ /* QTEE can override the size to a smaller value. */ u[i].b.size = msg->args[ob].b.size;Lines 1–500 of 917. 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/firmware/qcom/qcom_scm.h> #include <linux/init.h> #include <linux/module.h> #include <linux/slab.h> #include <linux/uaccess.h> #include <linux/xarray.h> #include "qcomtee.h" /* QTEE root object. */ struct qcomtee_object qcomtee_object_root = { .name = "root", .object_type = QCOMTEE_OBJECT_TYPE_ROOT, .info.qtee_id = QCOMTEE_MSG_OBJECT_ROOT, }; /* Next argument of type @type after index @i. */ int qcomtee_next_arg_type(struct qcomtee_arg *u, int i, enum qcomtee_arg_type type) { while (u[i].type != QCOMTEE_ARG_TYPE_INV && u[i].type != type) i++; return i; } /* * QTEE expects IDs with QCOMTEE_MSG_OBJECT_NS_BIT set for objects of * QCOMTEE_OBJECT_TYPE_CB type. The first ID with QCOMTEE_MSG_OBJECT_NS_BIT * set is reserved for the primordial object. */ #define QCOMTEE_OBJECT_PRIMORDIAL (QCOMTEE_MSG_OBJECT_NS_BIT) #define QCOMTEE_OBJECT_ID_START (QCOMTEE_OBJECT_PRIMORDIAL + 1) #define QCOMTEE_OBJECT_ID_END (U32_MAX) #define QCOMTEE_OBJECT_SET(p, type, ...) \ __QCOMTEE_OBJECT_SET(p, type, ##__VA_ARGS__, 0UL) #define __QCOMTEE_OBJECT_SET(p, type, optr, ...) \ do { \ (p)->object_type = (type); \ (p)->info.qtee_id = (unsigned long)(optr); \ } while (0) static struct qcomtee_object * qcomtee_qtee_object_alloc(struct qcomtee_object_invoke_ctx *oic, unsigned int object_id) { struct qcomtee *qcomtee = tee_get_drvdata(oic->ctx->teedev); struct qcomtee_object *object; object = kzalloc_obj(*object); if (!object) return NULL_QCOMTEE_OBJECT; /* If failed, "no-name". */ object->name = kasprintf(GFP_KERNEL, "qcomtee-%u", object_id); QCOMTEE_OBJECT_SET(object, QCOMTEE_OBJECT_TYPE_TEE, object_id); kref_init(&object->refcount); /* A QTEE object requires a context for async operations. */ object->info.qcomtee_async_ctx = qcomtee->ctx; teedev_ctx_get(object->info.qcomtee_async_ctx); return object; } static void qcomtee_qtee_object_free(struct qcomtee_object *object) { /* See qcomtee_qtee_object_alloc(). */ teedev_ctx_put(object->info.qcomtee_async_ctx); kfree(object->name); kfree(object); } static void qcomtee_do_release_qtee_object(struct work_struct *work) { struct qcomtee_object *object; struct qcomtee *qcomtee; int ret, result = 0; /* RELEASE does not require any argument. */ struct qcomtee_arg args[] = { { .type = QCOMTEE_ARG_TYPE_INV } }; object = container_of(work, struct qcomtee_object, work); qcomtee = tee_get_drvdata(object->info.qcomtee_async_ctx->teedev); /* Get the TEE context used for asynchronous operations. */ qcomtee->oic.ctx = object->info.qcomtee_async_ctx; ret = qcomtee_object_do_invoke_internal(&qcomtee->oic, object, QCOMTEE_MSG_OBJECT_OP_RELEASE, args, &result); /* Is it safe to retry the release? */ if (ret && ret != -ENODEV) { queue_work(qcomtee->wq, &object->work); } else { if (ret || result) pr_err("%s release failed, ret = %d (%x)\n", qcomtee_object_name(object), ret, result); qcomtee_qtee_object_free(object); } } static void qcomtee_release_qtee_object(struct qcomtee_object *object) { struct qcomtee *qcomtee = tee_get_drvdata(object->info.qcomtee_async_ctx->teedev); INIT_WORK(&object->work, qcomtee_do_release_qtee_object); queue_work(qcomtee->wq, &object->work); } static void qcomtee_object_release(struct kref *refcount) { struct qcomtee_object *object; const char *name; object = container_of(refcount, struct qcomtee_object, refcount); /* * qcomtee_object_get() is called in a RCU read lock. synchronize_rcu() * to avoid releasing the object while it is being accessed in * qcomtee_object_get(). */ synchronize_rcu(); switch (typeof_qcomtee_object(object)) { case QCOMTEE_OBJECT_TYPE_TEE: qcomtee_release_qtee_object(object); break; case QCOMTEE_OBJECT_TYPE_CB: name = object->name; if (object->ops->release) object->ops->release(object); kfree_const(name); break; case QCOMTEE_OBJECT_TYPE_ROOT: case QCOMTEE_OBJECT_TYPE_NULL: default: break; } } /** * qcomtee_object_get() - Increase the object's reference count. * @object: object to increase the reference count. * * Context: The caller should hold RCU read lock. */ int qcomtee_object_get(struct qcomtee_object *object) { if (object != &qcomtee_primordial_object && object != NULL_QCOMTEE_OBJECT && object != ROOT_QCOMTEE_OBJECT) return kref_get_unless_zero(&object->refcount); return 0; } /** * qcomtee_object_put() - Decrease the object's reference count. * @object: object to decrease the reference count. */ void qcomtee_object_put(struct qcomtee_object *object) { if (object != &qcomtee_primordial_object && object != NULL_QCOMTEE_OBJECT && object != ROOT_QCOMTEE_OBJECT) kref_put(&object->refcount, qcomtee_object_release); } static int qcomtee_idx_alloc(struct qcomtee_object_invoke_ctx *oic, u32 *idx, struct qcomtee_object *object) { struct qcomtee *qcomtee = tee_get_drvdata(oic->ctx->teedev); /* Every ID allocated here has QCOMTEE_MSG_OBJECT_NS_BIT set. */ return xa_alloc_cyclic(&qcomtee->xa_local_objects, idx, object, XA_LIMIT(QCOMTEE_OBJECT_ID_START, QCOMTEE_OBJECT_ID_END), &qcomtee->xa_last_id, GFP_KERNEL); } struct qcomtee_object *qcomtee_idx_erase(struct qcomtee_object_invoke_ctx *oic, u32 idx) { struct qcomtee *qcomtee = tee_get_drvdata(oic->ctx->teedev); if (idx < QCOMTEE_OBJECT_ID_START || idx > QCOMTEE_OBJECT_ID_END) return NULL_QCOMTEE_OBJECT; return xa_erase(&qcomtee->xa_local_objects, idx); } /** * qcomtee_object_id_get() - Get an ID for an object to send to QTEE. * @oic: context to use for the invocation. * @object: object to assign an ID. * @object_id: object ID. * * Called on the path to QTEE to construct the message; see * qcomtee_prepare_msg() and qcomtee_update_msg(). * * Return: On success, returns 0; on failure, returns < 0. */ static int qcomtee_object_id_get(struct qcomtee_object_invoke_ctx *oic, struct qcomtee_object *object, unsigned int *object_id) { u32 idx; switch (typeof_qcomtee_object(object)) { case QCOMTEE_OBJECT_TYPE_CB: if (qcomtee_idx_alloc(oic, &idx, object) < 0) return -ENOSPC; *object_id = idx; break; case QCOMTEE_OBJECT_TYPE_ROOT: case QCOMTEE_OBJECT_TYPE_TEE: *object_id = object->info.qtee_id; break; case QCOMTEE_OBJECT_TYPE_NULL: *object_id = QCOMTEE_MSG_OBJECT_NULL; break; } return 0; } /* Release object ID assigned in qcomtee_object_id_get. */ static void qcomtee_object_id_put(struct qcomtee_object_invoke_ctx *oic, unsigned int object_id) { qcomtee_idx_erase(oic, object_id); } /** * qcomtee_local_object_get() - Get the object referenced by the ID. * @oic: context to use for the invocation. * @object_id: object ID. * * It is called on the path from QTEE. * It is called on behalf of QTEE to obtain an instance of an object * for a given ID. It increases the object's reference count on success. * * Return: On error, returns %NULL_QCOMTEE_OBJECT. * On success, returns the object. */ static struct qcomtee_object * qcomtee_local_object_get(struct qcomtee_object_invoke_ctx *oic, unsigned int object_id) { struct qcomtee *qcomtee = tee_get_drvdata(oic->ctx->teedev); struct qcomtee_object *object; if (object_id == QCOMTEE_OBJECT_PRIMORDIAL) return &qcomtee_primordial_object; guard(rcu)(); object = xa_load(&qcomtee->xa_local_objects, object_id); /* It already checks for %NULL_QCOMTEE_OBJECT. */ qcomtee_object_get(object); return object; } /** * qcomtee_object_user_init() - Initialize an object for the user. * @object: object to initialize. * @ot: type of object as &enum qcomtee_object_type. * @ops: instance of callbacks. * @fmt: name assigned to the object. * * Return: On success, returns 0; on failure, returns < 0. */ int qcomtee_object_user_init(struct qcomtee_object *object, enum qcomtee_object_type ot, struct qcomtee_object_operations *ops, const char *fmt, ...) { va_list ap; int ret; kref_init(&object->refcount); QCOMTEE_OBJECT_SET(object, QCOMTEE_OBJECT_TYPE_NULL); va_start(ap, fmt); switch (ot) { case QCOMTEE_OBJECT_TYPE_NULL: ret = 0; break; case QCOMTEE_OBJECT_TYPE_CB: object->ops = ops; if (!object->ops->dispatch) { ret = -EINVAL; break; } /* If failed, "no-name". */ object->name = kvasprintf_const(GFP_KERNEL, fmt, ap); QCOMTEE_OBJECT_SET(object, QCOMTEE_OBJECT_TYPE_CB); ret = 0; break; case QCOMTEE_OBJECT_TYPE_ROOT: case QCOMTEE_OBJECT_TYPE_TEE: default: ret = -EINVAL; } va_end(ap); return ret; } /** * qcomtee_object_type() - Returns the type of object represented by an ID. * @object_id: object ID for the object. * * Similar to typeof_qcomtee_object(), but instead of receiving an object as * an argument, it receives an object ID. It is used internally on the return * path from QTEE. * * Return: Returns the type of object referenced by @object_id. */ static enum qcomtee_object_type qcomtee_object_type(unsigned int object_id) { if (object_id == QCOMTEE_MSG_OBJECT_NULL) return QCOMTEE_OBJECT_TYPE_NULL; if (object_id & QCOMTEE_MSG_OBJECT_NS_BIT) return QCOMTEE_OBJECT_TYPE_CB; return QCOMTEE_OBJECT_TYPE_TEE; } /** * qcomtee_object_qtee_init() - Initialize an object for QTEE. * @oic: context to use for the invocation. * @object: object returned. * @object_id: object ID received from QTEE. * * Return: On failure, returns < 0 and sets @object to %NULL_QCOMTEE_OBJECT. * On success, returns 0 */ static int qcomtee_object_qtee_init(struct qcomtee_object_invoke_ctx *oic, struct qcomtee_object **object, unsigned int object_id) { int ret = 0; switch (qcomtee_object_type(object_id)) { case QCOMTEE_OBJECT_TYPE_NULL: *object = NULL_QCOMTEE_OBJECT; break; case QCOMTEE_OBJECT_TYPE_CB: *object = qcomtee_local_object_get(oic, object_id); if (*object == NULL_QCOMTEE_OBJECT) ret = -EINVAL; break; default: /* QCOMTEE_OBJECT_TYPE_TEE */ *object = qcomtee_qtee_object_alloc(oic, object_id); if (*object == NULL_QCOMTEE_OBJECT) ret = -ENOMEM; break; } return ret; } /* * ''Marshaling API'' * qcomtee_prepare_msg - Prepare the inbound buffer for sending to QTEE * qcomtee_update_args - Parse the QTEE response in the inbound buffer * qcomtee_prepare_args - Parse the QTEE request from the outbound buffer * qcomtee_update_msg - Update the outbound buffer with the response for QTEE */ static int qcomtee_prepare_msg(struct qcomtee_object_invoke_ctx *oic, struct qcomtee_object *object, u32 op, struct qcomtee_arg *u) { struct qcomtee_msg_object_invoke *msg; unsigned int object_id; int i, ib, ob, io, oo; size_t offset; /* Use the input message buffer in 'oic'. */ msg = oic->in_msg.addr; /* Start offset in a message for buffer arguments. */ offset = qcomtee_msg_buffer_args(struct qcomtee_msg_object_invoke, qcomtee_args_len(u)); /* Get the ID of the object being invoked. */ if (qcomtee_object_id_get(oic, object, &object_id)) return -ENOSPC; ib = 0; qcomtee_arg_for_each_input_buffer(i, u) { void *msgptr; /* Address of buffer payload: */ /* Overflow already checked in qcomtee_msg_buffers_alloc(). */ msg->args[ib].b.offset = offset; msg->args[ib].b.size = u[i].b.size; msgptr = qcomtee_msg_offset_to_ptr(msg, offset); /* Userspace client or kernel client!? */ if (!(u[i].flags & QCOMTEE_ARG_FLAGS_UADDR)) memcpy(msgptr, u[i].b.addr, u[i].b.size); else if (copy_from_user(msgptr, u[i].b.uaddr, u[i].b.size)) return -EFAULT; offset += qcomtee_msg_offset_align(u[i].b.size); ib++; } ob = ib; qcomtee_arg_for_each_output_buffer(i, u) { /* Overflow already checked in qcomtee_msg_buffers_alloc(). */ msg->args[ob].b.offset = offset; msg->args[ob].b.size = u[i].b.size; offset += qcomtee_msg_offset_align(u[i].b.size); ob++; } io = ob; qcomtee_arg_for_each_input_object(i, u) { if (qcomtee_object_id_get(oic, u[i].o, &msg->args[io].o)) { qcomtee_object_id_put(oic, object_id); for (io--; io >= ob; io--) qcomtee_object_id_put(oic, msg->args[io].o); return -ENOSPC; } io++; } oo = io; qcomtee_arg_for_each_output_object(i, u) oo++; /* Set object, operation, and argument counts. */ qcomtee_msg_init(msg, object_id, op, ib, ob, io, oo); return 0; } /** * qcomtee_update_args() - Parse the QTEE response in the inbound buffer. * @u: array of arguments for the invocation. * @oic: context to use for the invocation. * * @u must be the same as the one used in qcomtee_prepare_msg() when * initializing the inbound buffer. * * On failure, it continues processing the QTEE message. The caller should * do the necessary cleanup, including calling qcomtee_object_put() * on the output objects. * * Return: On success, returns 0; on failure, returns < 0. */ static int qcomtee_update_args(struct qcomtee_arg *u, struct qcomtee_object_invoke_ctx *oic) { struct qcomtee_msg_object_invoke *msg; int i, ib, ob, io, oo; int ret = 0; /* Use the input message buffer in 'oic'. */ msg = oic->in_msg.addr; ib = 0; qcomtee_arg_for_each_input_buffer(i, u) ib++; ob = ib; qcomtee_arg_for_each_output_buffer(i, u) { void *msgptr; /* Address of buffer payload: */ /* QTEE can override the size to a smaller value. */ u[i].b.size = msg->args[ob].b.size;