Introduce qcom_tee_object, which represents an object in both QTEE and the kernel. QTEE clients can invoke an instance of qcom_tee_object to access QTEE services. If this invocation produces a new object in QTEE, an instance of qcom_tee_object will be returned.
Similarly, QTEE can request services from the kernel by issuing a callback request, which invokes an instance of qcom_tee_object in the kernel. Any subsystem that exposes a service to QTEE should allocate and initialize an instance of qcom_tee_object with a dispatcher callback that is called when the object is invoked.
Signed-off-by: Amirreza Zarrabi quic_azarrabi@quicinc.com --- drivers/tee/Kconfig | 1 + drivers/tee/Makefile | 1 + drivers/tee/qcomtee/Kconfig | 10 + drivers/tee/qcomtee/Makefile | 6 + drivers/tee/qcomtee/async.c | 153 ++++++ drivers/tee/qcomtee/core.c | 928 +++++++++++++++++++++++++++++++++ drivers/tee/qcomtee/qcom_scm.c | 36 ++ drivers/tee/qcomtee/qcomtee_msg.h | 217 ++++++++ drivers/tee/qcomtee/qcomtee_private.h | 47 ++ drivers/tee/qcomtee/release.c | 66 +++ include/linux/firmware/qcom/qcom_tee.h | 284 ++++++++++ 11 files changed, 1749 insertions(+)
diff --git a/drivers/tee/Kconfig b/drivers/tee/Kconfig index 61b507c18780..3a995d7f0d74 100644 --- a/drivers/tee/Kconfig +++ b/drivers/tee/Kconfig @@ -16,5 +16,6 @@ if TEE source "drivers/tee/optee/Kconfig" source "drivers/tee/amdtee/Kconfig" source "drivers/tee/tstee/Kconfig" +source "drivers/tee/qcomtee/Kconfig"
endif diff --git a/drivers/tee/Makefile b/drivers/tee/Makefile index 5488cba30bd2..74e987f8f7ea 100644 --- a/drivers/tee/Makefile +++ b/drivers/tee/Makefile @@ -6,3 +6,4 @@ tee-objs += tee_shm_pool.o obj-$(CONFIG_OPTEE) += optee/ obj-$(CONFIG_AMDTEE) += amdtee/ obj-$(CONFIG_ARM_TSTEE) += tstee/ +obj-$(CONFIG_QCOMTEE) += qcomtee/ diff --git a/drivers/tee/qcomtee/Kconfig b/drivers/tee/qcomtee/Kconfig new file mode 100644 index 000000000000..d180a6d07d33 --- /dev/null +++ b/drivers/tee/qcomtee/Kconfig @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0-only +# Qualcomm Trusted Execution Environment Configuration +config QCOMTEE + tristate "Qualcomm TEE Support" + select QCOM_SCM + help + This option enables the Qualcomm Trusted Execution Environment (QTEE) + driver. It provides an API to access services offered by QTEE and any + loaded Trusted Applications (TAs), as well as exporting kernel + services to QTEE. diff --git a/drivers/tee/qcomtee/Makefile b/drivers/tee/qcomtee/Makefile new file mode 100644 index 000000000000..7dc5e6373042 --- /dev/null +++ b/drivers/tee/qcomtee/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0-only +obj-$(CONFIG_QCOMTEE) += qcomtee.o +qcomtee-objs += async.o +qcomtee-objs += core.o +qcomtee-objs += qcom_scm.o +qcomtee-objs += release.o diff --git a/drivers/tee/qcomtee/async.c b/drivers/tee/qcomtee/async.c new file mode 100644 index 000000000000..218ec0209722 --- /dev/null +++ b/drivers/tee/qcomtee/async.c @@ -0,0 +1,153 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#include <linux/mutex.h> +#include <linux/slab.h> + +#include "qcomtee_private.h" +#include "qcomtee_msg.h" + +#define QCOM_TEE_ASYNC_VERSION_1_0 0x00010000U /* Major: 0x0001, Minor: 0x0000. */ +#define QCOM_TEE_ASYNC_VERSION_1_1 0x00010001U /* Major: 0x0001, Minor: 0x0001. */ +#define QCOM_TEE_ASYNC_VERSION_1_2 0x00010002U /* Major: 0x0001, Minor: 0x0002. */ +#define QCOM_TEE_ASYNC_VERSION QCOM_TEE_ASYNC_VERSION_1_2 /* Current Version. */ + +#define QCOM_TEE_ASYNC_VERSION_MAJOR(n) upper_16_bits(n) +#define QCOM_TEE_ASYNC_VERSION_MINOR(n) lower_16_bits(n) + +/** + * struct qcom_tee_async_msg_hdr - Asynchronous message header format. + * @version: current async protocol version of remote endpoint + * @op: async operation + * + * @version specifies the endpoints (QTEE or driver) supported async protocol, e.g. + * if QTEE set @version to %QCOM_TEE_ASYNC_VERSION_1_1, QTEE handles operations + * supported in %QCOM_TEE_ASYNC_VERSION_1_1 or %QCOM_TEE_ASYNC_VERSION_1_0. + * @op determins the message format. + */ +struct qcom_tee_async_msg_hdr { + u32 version; + u32 op; +}; + +/** + * struct qcom_tee_async_release_msg - Release asynchronous message. + * @hdr: message header as &struct qcom_tee_async_msg_hdr + * @counts: number of objects in @object_ids + * @object_ids: array of object ids should be released + * + * Available in Major = 0x0001, Minor >= 0x0000. + */ +struct qcom_tee_async_release_msg { + struct qcom_tee_async_msg_hdr hdr; + u32 counts; + u32 object_ids[] __counted_by(counts); +}; + +/** + * qcom_tee_get_async_buffer() - Get start of the asynchronous message in outbound buffer. + * @oic: context used for current invocation + * @async_buffer: return buffer to extract from or fill in async messages + * + * If @oic is used for direct object invocation, whole outbound buffer is available for + * async message. If @oic is used for callback request, the tail of outbound buffer (after + * the callback request message) is available for async message. + */ +static void qcom_tee_get_async_buffer(struct qcom_tee_object_invoke_ctx *oic, + struct qcom_tee_buffer *async_buffer) +{ + struct qcom_tee_msg_callback *msg; + unsigned int offset; + int i; + + if (!(oic->flags & QCOM_TEE_OIC_FLAG_BUSY)) { + /* The outbound buffer is empty. Using the whole buffer. */ + offset = 0; + } else { + msg = (struct qcom_tee_msg_callback *)oic->out_msg.addr; + + /* Start offset in a message for buffer arguments. */ + offset = qcom_tee_msg_buffer_args(struct qcom_tee_msg_callback, + qcom_tee_msg_args(msg)); + + /* Add size of IB arguments. */ + qcom_tee_msg_for_each_input_buffer(i, msg) + offset += qcom_tee_msg_offset_align(msg->args[i].b.size); + + /* Add size of OB arguments. */ + qcom_tee_msg_for_each_output_buffer(i, msg) + offset += qcom_tee_msg_offset_align(msg->args[i].b.size); + } + + async_buffer->addr = oic->out_msg.addr + offset; + async_buffer->size = oic->out_msg.size - offset; +} + +/** + * qcom_tee_async_release_handler() - Process QTEE async requests for releasing objects. + * @oic: context used for current invocation + * @msg: async message for object release + * @size: size of the async buffer available + * + * Return: Size of outbound buffer used when processing @msg. + */ +static size_t qcom_tee_async_release_handler(struct qcom_tee_object_invoke_ctx *oic, + struct qcom_tee_async_msg_hdr *async_msg, size_t size) +{ + struct qcom_tee_async_release_msg *msg = (struct qcom_tee_async_release_msg *)async_msg; + struct qcom_tee_object *object; + int i; + + for (i = 0; i < msg->counts; i++) { + object = qcom_tee_idx_erase(msg->object_ids[i]); + qcom_tee_object_put(object); + } + + return struct_size_t(struct qcom_tee_async_release_msg, object_ids, i); +} + +/** + * qcom_tee_fetch_async_reqs() - Fetch and process asynchronous messages. + * @oic: context used for current invocation + * + * It looks for handler to process the requested operations in the async message. + * Currently, only support async release requests. + */ +void qcom_tee_fetch_async_reqs(struct qcom_tee_object_invoke_ctx *oic) +{ + struct qcom_tee_async_msg_hdr *async_msg; + struct qcom_tee_buffer async_buffer; + size_t consumed, used = 0; + + qcom_tee_get_async_buffer(oic, &async_buffer); + + while (async_buffer.size - used > sizeof(struct qcom_tee_async_msg_hdr)) { + async_msg = (struct qcom_tee_async_msg_hdr *)(async_buffer.addr + used); + + if (QCOM_TEE_ASYNC_VERSION_MAJOR(async_msg->version) != + QCOM_TEE_ASYNC_VERSION_MAJOR(QCOM_TEE_ASYNC_VERSION)) + goto out; + + switch (async_msg->op) { + case QCOM_TEE_MSG_OBJECT_OP_RELEASE: + consumed = qcom_tee_async_release_handler(oic, async_msg, + async_buffer.size - used); + break; + default: + /* Unsupported operations. */ + goto out; + } + + /* Supported operation but unable to parse the message. */ + if (!consumed) + goto out; + + used += qcom_tee_msg_offset_align(consumed); + } + + out: + /* Reset the async messages buffer so async requests do not loopback to QTEE. */ + memzero_explicit(async_buffer.addr, async_buffer.size); +} diff --git a/drivers/tee/qcomtee/core.c b/drivers/tee/qcomtee/core.c new file mode 100644 index 000000000000..a949ef4cceee --- /dev/null +++ b/drivers/tee/qcomtee/core.c @@ -0,0 +1,928 @@ +// 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/firmware/qcom/qcom_tzmem.h> +#include <linux/init.h> +#include <linux/kobject.h> +#include <linux/mm.h> +#include <linux/module.h> +#include <linux/sysfs.h> +#include <linux/slab.h> +#include <linux/xarray.h> + +#include "qcomtee_msg.h" +#include "qcomtee_private.h" + +/* Static instance of object represents QTEE root object. */ +struct qcom_tee_object qcom_tee_object_root = { + .name = "root", + .object_type = QCOM_TEE_OBJECT_TYPE_ROOT, + .info.qtee_id = QCOM_TEE_MSG_OBJECT_ROOT, +}; +EXPORT_SYMBOL_GPL(qcom_tee_object_root); + +/* Next argument of type @type after index @i. */ +int qcom_tee_next_arg_type(struct qcom_tee_arg *u, int i, enum qcom_tee_arg_type type) +{ + while (u[i].type != QCOM_TEE_ARG_TYPE_INV && u[i].type != type) + i++; + return i; +} + +/* QTEE expects IDs with QCOM_TEE_MSG_OBJECT_NS_BIT set for object of + * QCOM_TEE_OBJECT_TYPE_CB_OBJECT type. + */ +#define QCOM_TEE_OBJECT_ID_START (QCOM_TEE_MSG_OBJECT_NS_BIT + 1) +#define QCOM_TEE_OBJECT_ID_END (UINT_MAX) + +#define QCOM_TEE_OBJECT_SET(p, type, ...) __QCOM_TEE_OBJECT_SET(p, type, ##__VA_ARGS__, 0UL) +#define __QCOM_TEE_OBJECT_SET(p, type, optr, ...) do { \ + (p)->object_type = (type); \ + (p)->info.qtee_id = (unsigned long)(optr); \ + } while (0) + +static struct qcom_tee_object *qcom_tee_object_alloc(void) +{ + struct qcom_tee_object *object; + + object = kzalloc(sizeof(*object), GFP_KERNEL); + if (object) { + QCOM_TEE_OBJECT_SET(object, QCOM_TEE_OBJECT_TYPE_NULL); + kref_init(&object->refcount); + } + + return object; +} + +void qcom_tee_object_free(struct qcom_tee_object *object) +{ + kfree(object->name); + kfree(object); +} + +static void qcom_tee_object_release(struct kref *refcount) +{ + struct qcom_tee_object *object; + struct module *owner; + const char *name; + + object = container_of(refcount, struct qcom_tee_object, refcount); + + synchronize_rcu(); + + switch (typeof_qcom_tee_object(object)) { + case QCOM_TEE_OBJECT_TYPE_TEE: + qcom_tee_release_tee_object(object); + + break; + case QCOM_TEE_OBJECT_TYPE_CB_OBJECT: + /* Copy, as after release we should not access object. */ + name = object->name; + owner = object->owner; + + if (object->ops->release) + object->ops->release(object); + + module_put(owner); + kfree_const(name); + + break; + case QCOM_TEE_OBJECT_TYPE_ROOT: + case QCOM_TEE_OBJECT_TYPE_NULL: + default: + break; + } +} + +/** + * qcom_tee_object_get() - Increase object's refcount. + * @object: object to increase the refcount + */ +int qcom_tee_object_get(struct qcom_tee_object *object) +{ + if (object != NULL_QCOM_TEE_OBJECT && + object != ROOT_QCOM_TEE_OBJECT) + return kref_get_unless_zero(&object->refcount); + + return 0; +} +EXPORT_SYMBOL_GPL(qcom_tee_object_get); + +/** + * qcom_tee_object_put() - Decrease object's refcount + * @object: object to decrease the refcount + */ +void qcom_tee_object_put(struct qcom_tee_object *object) +{ + if (object != NULL_QCOM_TEE_OBJECT && + object != ROOT_QCOM_TEE_OBJECT) + kref_put(&object->refcount, qcom_tee_object_release); +} +EXPORT_SYMBOL_GPL(qcom_tee_object_put); + +/* ''Local Object Table''. */ +/* Object from kernel that are exported to QTEE are assigned an id and stored in + * xa_qcom_local_objects (kernel object table). QTEE uses this id to reference the + * object using qcom_tee_local_object_get. + */ +static DEFINE_XARRAY_ALLOC(xa_qcom_local_objects); + +static int qcom_tee_idx_alloc(u32 *idx, struct qcom_tee_object *object) +{ + static u32 xa_last_id = QCOM_TEE_OBJECT_ID_START; + + /* Every id allocated here, has QCOM_TEE_MSG_OBJECT_NS_BIT set. */ + return xa_alloc_cyclic(&xa_qcom_local_objects, idx, object, + XA_LIMIT(QCOM_TEE_OBJECT_ID_START, QCOM_TEE_OBJECT_ID_END), + &xa_last_id, GFP_KERNEL); +} + +struct qcom_tee_object *qcom_tee_idx_erase(u32 idx) +{ + if (idx < QCOM_TEE_OBJECT_ID_START || idx > QCOM_TEE_OBJECT_ID_END) + return NULL_QCOM_TEE_OBJECT; + + return xa_erase(&xa_qcom_local_objects, idx); +} + +/** + * qcom_tee_object_id_get() - Get an id for an object to sent to QTEE. + * @object: object to get its id. + * @object_id: object id. + * + * For object hosted in REE, they are added to object table, and the idx in the + * object table is used as id. For object hosted in QTEE, use the QTEE id stored in + * @object. This is called on a path to QTEE to construct a message, see + * qcom_tee_prepare_msg() and qcom_tee_update_msg(). + * + * Return: On success return 0 or <0 on failure. + */ +static int qcom_tee_object_id_get(struct qcom_tee_object *object, unsigned int *object_id) +{ + u32 idx; + + switch (typeof_qcom_tee_object(object)) { + case QCOM_TEE_OBJECT_TYPE_CB_OBJECT: + if (qcom_tee_idx_alloc(&idx, object) < 0) + return -ENOSPC; + + *object_id = idx; + + break; + case QCOM_TEE_OBJECT_TYPE_ROOT: + case QCOM_TEE_OBJECT_TYPE_TEE: + *object_id = object->info.qtee_id; + + break; + case QCOM_TEE_OBJECT_TYPE_NULL: + *object_id = QCOM_TEE_MSG_OBJECT_NULL; + + break; + } + + return 0; +} + +/* Release object id assigned in qcom_tee_object_id_get. */ +static void qcom_tee_object_id_put(unsigned int object_id) +{ + qcom_tee_idx_erase(object_id); +} + +/** + * qcom_tee_local_object_get() - Get an object in REE referenced by the id. + * @object_id: object id. + * + * It is called on behalf of QTEE to obtain instance of object for an id. It is + * called on a path from QTEE to construct an argument of &struct qcom_tee_arg, + * see qcom_tee_update_args() and qcom_tee_prepare_args(). + * + * It increases the object's refcount on success. + * + * Return: On error returns %NULL_QCOM_TEE_OBJECT. On success, the object. + */ +static struct qcom_tee_object *qcom_tee_local_object_get(unsigned int object_id) +{ + struct qcom_tee_object *object; + + /* We trust QTEE does not mess the refcounts. + * It does not issue RELEASE request and qcom_tee_object_get(), simultaneously. + */ + + object = xa_load(&xa_qcom_local_objects, object_id); + + qcom_tee_object_get(object); + + return object; +} + +/** + * __qcom_tee_object_user_init() - Initialize an object for user. + * @object: object to initialize. + * @ot: type of object as &enum qcom_tee_object_type. + * @ops: instance of callbacks. + * @fmt: name assigned to the object. + * + * Return: On success return 0 or <0 on failure. + */ +int __qcom_tee_object_user_init(struct qcom_tee_object *object, enum qcom_tee_object_type ot, + struct qcom_tee_object_operations *ops, struct module *owner, + const char *fmt, ...) +{ + va_list ap; + int ret; + + kref_init(&object->refcount); + QCOM_TEE_OBJECT_SET(object, QCOM_TEE_OBJECT_TYPE_NULL); + + va_start(ap, fmt); + switch (ot) { + case QCOM_TEE_OBJECT_TYPE_NULL: + ret = 0; + + break; + case QCOM_TEE_OBJECT_TYPE_CB_OBJECT: + object->ops = ops; + if (!object->ops->dispatch) + return -EINVAL; + + object->owner = owner; + if (!try_module_get(object->owner)) + return -EINVAL; + + /* If failed, "no-name"; it is not really a reason to fail here. */ + object->name = kvasprintf_const(GFP_KERNEL, fmt, ap); + QCOM_TEE_OBJECT_SET(object, QCOM_TEE_OBJECT_TYPE_CB_OBJECT); + + ret = 0; + break; + case QCOM_TEE_OBJECT_TYPE_ROOT: + case QCOM_TEE_OBJECT_TYPE_TEE: + default: + ret = -EINVAL; + } + va_end(ap); + + return ret; +} +EXPORT_SYMBOL_GPL(__qcom_tee_object_user_init); + +/** + * qcom_tee_object_type() - Returns type of object represented by an object id. + * @object_id: object id for the object. + * + * This is similar to typeof_qcom_tee_object() but instead of receiving object + * as argument it receives object id. It is used internally on return path + * from QTEE. + * + * Return: Returns type of object referenced by @object_id. + */ +static enum qcom_tee_object_type qcom_tee_object_type(unsigned int object_id) +{ + if (object_id == QCOM_TEE_MSG_OBJECT_NULL) + return QCOM_TEE_OBJECT_TYPE_NULL; + + if (object_id & QCOM_TEE_MSG_OBJECT_NS_BIT) + return QCOM_TEE_OBJECT_TYPE_CB_OBJECT; + + return QCOM_TEE_OBJECT_TYPE_TEE; +} + +/** + * qcom_tee_object_init() - Initialize an object for QTEE. + * @object: return object + * @object_id: object id received form QTEE + * + * Return: On success return 0 or <0 on failure. + */ +static int qcom_tee_object_init(struct qcom_tee_object **object, unsigned int object_id) +{ + struct qcom_tee_object *qto; + int ret = 0; + + switch (qcom_tee_object_type(object_id)) { + case QCOM_TEE_OBJECT_TYPE_NULL: + *object = NULL_QCOM_TEE_OBJECT; + + break; + case QCOM_TEE_OBJECT_TYPE_CB_OBJECT: + qto = qcom_tee_local_object_get(object_id); + if (qto != NULL_QCOM_TEE_OBJECT) + *object = qto; + else + ret = -EINVAL; + + break; + case QCOM_TEE_OBJECT_TYPE_TEE: + qto = qcom_tee_object_alloc(); + if (qto) { + /* If failed, "no-name"; it is not really a reason to fail here. */ + qto->name = kasprintf(GFP_KERNEL, "qcom_tee-%u", object_id); + QCOM_TEE_OBJECT_SET(qto, QCOM_TEE_OBJECT_TYPE_TEE, object_id); + + *object = qto; + } else { + ret = -ENOMEM; + } + + break; + default: + + break; + } + + if (ret) + *object = NULL_QCOM_TEE_OBJECT; + + return ret; +} + +/* Marshaling API. */ +/* qcom_tee_prepare_msg - Prepares inbound buffer for sending to QTEE + * qcom_tee_update_args - Parses QTEE response in inbound buffer + * qcom_tee_prepare_args - Parses QTEE request from outbound buffer + * qcom_tee_update_msg - Updates outbound buffer with response for QTEE request + */ + +static int qcom_tee_prepare_msg(struct qcom_tee_object_invoke_ctx *oic, + struct qcom_tee_object *object, u32 op, struct qcom_tee_arg *u) +{ + struct qcom_tee_msg_object_invoke *msg; + unsigned int object_id; + int ib, ob, io, oo; + size_t off; + int i; + + /* Use input message buffer in 'oic'. */ + msg = (struct qcom_tee_msg_object_invoke *)oic->in_msg.addr; + + /* Start offset in a message for buffer arguments. */ + off = qcom_tee_msg_buffer_args(struct qcom_tee_msg_object_invoke, qcom_tee_args_len(u)); + + /* Get id of object being invoked. */ + if (qcom_tee_object_id_get(object, &object_id)) + return -ENOSPC; + + ib = 0; + qcom_tee_arg_for_each_input_buffer(i, u) { + void *ptr; + + /* qcom_tee_msg_buffers_alloc() already checked overflow in message! */ + msg->args[ib].b.offset = off; + msg->args[ib].b.size = u[i].b.size; + + ptr = qcom_tee_msg_offset_to_ptr(msg, off); + if (!(u[i].flags & QCOM_TEE_ARG_FLAGS_UADDR)) + memcpy(ptr, u[i].b.addr, u[i].b.size); + else if (copy_from_user(ptr, u[i].b.uaddr, u[i].b.size)) + return -EINVAL; + + off += qcom_tee_msg_offset_align(u[i].b.size); + ib++; + } + + ob = ib; + qcom_tee_arg_for_each_output_buffer(i, u) { + /* qcom_tee_msg_buffers_alloc() already checked overflow in message! */ + msg->args[ob].b.offset = off; + msg->args[ob].b.size = u[i].b.size; + + off += qcom_tee_msg_offset_align(u[i].b.size); + ob++; + } + + io = ob; + qcom_tee_arg_for_each_input_object(i, u) { + if (qcom_tee_object_id_get(u[i].o, &msg->args[io].o)) { + /* Unable to qcom_tee_object_id_get; put whatever we got. */ + qcom_tee_object_id_put(object_id); + for (--io; io >= ob; io--) + qcom_tee_object_id_put(msg->args[io].o); + + return -ENOSPC; + } + + io++; + } + + oo = io; + qcom_tee_arg_for_each_output_object(i, u) + oo++; + + /* Set object, operation, and argument counts. */ + qcom_tee_msg_init(msg, object_id, op, ib, ob, io, oo); + + return 0; +} + +static int qcom_tee_update_args(struct qcom_tee_arg *u, struct qcom_tee_object_invoke_ctx *oic) +{ + struct qcom_tee_msg_object_invoke *msg; + int ib, ob, io, oo; + int i, ret = 0; + + /* Use input message buffer in 'oic'. */ + msg = (struct qcom_tee_msg_object_invoke *)oic->in_msg.addr; + + ib = 0; + qcom_tee_arg_for_each_input_buffer(i, u) + ib++; + + ob = ib; + qcom_tee_arg_for_each_output_buffer(i, u) { + void *ptr = qcom_tee_msg_offset_to_ptr(msg, msg->args[ob].b.offset); + + if (!(u[i].flags & QCOM_TEE_ARG_FLAGS_UADDR)) { + memcpy(u[i].b.addr, ptr, msg->args[ob].b.size); + } else if (copy_to_user(u[i].b.uaddr, ptr, msg->args[ob].b.size)) { + /* On ERROR, continue to process arguments to get to output object. */ + ret = -EINVAL; + } + + u[i].b.size = msg->args[ob].b.size; + ob++; + } + + io = ob; + qcom_tee_arg_for_each_input_object(i, u) + io++; + + oo = io; + qcom_tee_arg_for_each_output_object(i, u) { + int err; + + /* On ERROR, continue to process arguments so that we can issue the RELEASE. */ + err = qcom_tee_object_init(&u[i].o, msg->args[oo].o); + if (err) + ret = err; + + oo++; + } + + return ret; +} + +static int qcom_tee_prepare_args(struct qcom_tee_object_invoke_ctx *oic) +{ + int i, ret = 0; + + /* Use output message buffer in 'oic'. */ + struct qcom_tee_msg_callback *msg = (struct qcom_tee_msg_callback *)oic->out_msg.addr; + + qcom_tee_msg_for_each_input_buffer(i, msg) { + oic->u[i].b.addr = qcom_tee_msg_offset_to_ptr(msg, msg->args[i].b.offset); + oic->u[i].b.size = msg->args[i].b.size; + oic->u[i].type = QCOM_TEE_ARG_TYPE_IB; + } + + qcom_tee_msg_for_each_output_buffer(i, msg) { + oic->u[i].b.addr = qcom_tee_msg_offset_to_ptr(msg, msg->args[i].b.offset); + oic->u[i].b.size = msg->args[i].b.size; + oic->u[i].type = QCOM_TEE_ARG_TYPE_OB; + } + + qcom_tee_msg_for_each_input_object(i, msg) { + int err; + + /* On ERROR, continue to process arguments so that we can issue the RELEASE. */ + err = qcom_tee_object_init(&oic->u[i].o, msg->args[i].o); + if (err) + ret = err; + + oic->u[i].type = QCOM_TEE_ARG_TYPE_IO; + } + + qcom_tee_msg_for_each_output_object(i, msg) + oic->u[i].type = QCOM_TEE_ARG_TYPE_OO; + + /* End of Arguments. */ + oic->u[i].type = QCOM_TEE_ARG_TYPE_INV; + + return ret; +} + +static int qcom_tee_update_msg(struct qcom_tee_object_invoke_ctx *oic) +{ + int ib, ob, io, oo; + int i; + + /* Use output message buffer in 'oic'. */ + struct qcom_tee_msg_callback *msg = (struct qcom_tee_msg_callback *)oic->out_msg.addr; + + ib = 0; + qcom_tee_arg_for_each_input_buffer(i, oic->u) + ib++; + + ob = ib; + qcom_tee_arg_for_each_output_buffer(i, oic->u) { + /* Only reduce size; never increase it. */ + if (msg->args[ob].b.size < oic->u[i].b.size) + return -EINVAL; + + msg->args[ob].b.size = oic->u[i].b.size; + ob++; + } + + io = ob; + qcom_tee_arg_for_each_input_object(i, oic->u) + io++; + + oo = io; + qcom_tee_arg_for_each_output_object(i, oic->u) { + if (qcom_tee_object_id_get(oic->u[i].o, &msg->args[oo].o)) { + /* Unable to qcom_tee_object_id_get; put whatever we got. */ + for (--oo; oo >= io; --oo) + qcom_tee_object_id_put(msg->args[oo].o); + + return -ENOSPC; + } + + oo++; + } + + return 0; +} + +/** + * define MAX_BUFFER_SIZE - Maximum size of inbound and outbound buffers. + * + * QTEE transport does not impose any restriction on these buffers. However, if size of + * buffers are larger then %MAX_BUFFER_SIZE, user should probably use some other + * form of shared memory with QTEE. + */ +#define MAX_BUFFER_SIZE SZ_8K + +/* Pool to allocate inbound and outbound buffers. */ +static struct qcom_tzmem_pool *tzmem_msg_pool; + +static int qcom_tee_msg_buffers_alloc(struct qcom_tee_object_invoke_ctx *oic, + struct qcom_tee_arg *u) +{ + size_t size; + int i; + + /* Start offset in a message for buffer arguments. */ + size = qcom_tee_msg_buffer_args(struct qcom_tee_msg_object_invoke, qcom_tee_args_len(u)); + if (size > MAX_BUFFER_SIZE) + return -EINVAL; + + /* Add size of IB arguments. */ + qcom_tee_arg_for_each_input_buffer(i, u) { + size = size_add(size, qcom_tee_msg_offset_align(u[i].b.size)); + if (size > MAX_BUFFER_SIZE) + return -EINVAL; + } + + /* Add size of OB arguments. */ + qcom_tee_arg_for_each_output_buffer(i, u) { + size = size_add(size, qcom_tee_msg_offset_align(u[i].b.size)); + if (size > MAX_BUFFER_SIZE) + return -EINVAL; + } + + /* QTEE requires inbound buffer size to be page aligned. */ + size = PAGE_ALIGN(size); + + /* Do allocations. */ + oic->in_msg.size = size; + oic->in_msg.addr = qcom_tzmem_alloc(tzmem_msg_pool, size, GFP_KERNEL); + if (!oic->in_msg.addr) + return -EINVAL; + + oic->out_msg.size = MAX_BUFFER_SIZE; + oic->out_msg.addr = qcom_tzmem_alloc(tzmem_msg_pool, MAX_BUFFER_SIZE, GFP_KERNEL); + if (!oic->out_msg.addr) { + qcom_tzmem_free(oic->in_msg.addr); + + return -EINVAL; + } + + oic->in_msg_paddr = qcom_tzmem_to_phys(oic->in_msg.addr); + oic->out_msg_paddr = qcom_tzmem_to_phys(oic->out_msg.addr); + + /* QTEE assume unused buffers are zeroed; Do it now! */ + memzero_explicit(oic->in_msg.addr, oic->in_msg.size); + memzero_explicit(oic->out_msg.addr, oic->out_msg.size); + + return 0; +} + +static void qcom_tee_msg_buffers_free(struct qcom_tee_object_invoke_ctx *oic) +{ + qcom_tzmem_free(oic->in_msg.addr); + qcom_tzmem_free(oic->out_msg.addr); +} + +static int qcom_tee_msg_buffers_init(void) +{ + struct qcom_tzmem_pool_config config = { + .policy = QCOM_TZMEM_POLICY_ON_DEMAND, + /* 4M seems enough, it is used for QTEE meg header and qcom_tee_msg_arg array. */ + .max_size = SZ_4M + }; + + tzmem_msg_pool = qcom_tzmem_pool_new(&config); + if (IS_ERR(tzmem_msg_pool)) + return PTR_ERR(tzmem_msg_pool); + + return 0; +} + +static void qcom_tee_msg_buffers_destroy(void) +{ + qcom_tzmem_pool_free(tzmem_msg_pool); +} + +/* Invoke a REE object. */ +static void qcom_tee_object_invoke(struct qcom_tee_object_invoke_ctx *oic, + struct qcom_tee_msg_callback *msg) +{ + int i, errno; + u32 op; + + /* Get object being invoked. */ + unsigned int object_id = msg->cxt; + struct qcom_tee_object *object; + + /* QTEE can not invoke NULL object or objects it hosts. */ + if (qcom_tee_object_type(object_id) == QCOM_TEE_OBJECT_TYPE_NULL || + qcom_tee_object_type(object_id) == QCOM_TEE_OBJECT_TYPE_TEE) { + errno = -EINVAL; + goto out; + } + + object = qcom_tee_local_object_get(object_id); + if (object == NULL_QCOM_TEE_OBJECT) { + errno = -EINVAL; + goto out; + } + + oic->object = object; + + /* Filter bits used by transport. */ + op = msg->op & QCOM_TEE_MSG_OBJECT_OP_MASK; + + switch (op) { + case QCOM_TEE_MSG_OBJECT_OP_RELEASE: + qcom_tee_object_id_put(object_id); + qcom_tee_object_put(object); + errno = 0; + + break; + case QCOM_TEE_MSG_OBJECT_OP_RETAIN: + qcom_tee_object_get(object); + errno = 0; + + break; + default: + errno = qcom_tee_prepare_args(oic); + if (errno) { + /* Unable to parse the message. Release any object arrived as input. */ + qcom_tee_arg_for_each_input_buffer(i, oic->u) + qcom_tee_object_put(oic->u[i].o); + + break; + } + + errno = object->ops->dispatch(oic, object, op, oic->u); + if (!errno) { + /* On SUCCESS, notify object at appropriate time. */ + oic->flags |= QCOM_TEE_OIC_FLAG_NOTIFY; + } + } + +out: + + oic->errno = errno; +} + +/** + * __qcom_tee_object_do_invoke() - Submit an invocation for an object. + * @oic: context to use for current invocation. + * @object: object being invoked. + * @op: requested operation on object. + * @u: array of argument for the current invocation. + * @result: result returned from QTEE. + * + * The caller is responsible to keep track of the refcount for each object, + * including @object. On return, the caller loses the ownership of all input + * object of type %QCOM_TEE_OBJECT_TYPE_CB_OBJECT. + * + * Return: On success return 0. On error returns -EINVAL and -ENOSPC if unable to initiate + * the invocation, -EAGAIN if invocation failed and user may retry the invocation. + * Otherwise, -ENODEV on fatal failure. + */ +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_msg_callback *cb_msg; + u64 response_type; + int i, ret, errno; + + ret = qcom_tee_msg_buffers_alloc(oic, u); + if (ret) + return ret; + + ret = qcom_tee_prepare_msg(oic, object, op, u); + if (ret) + goto out; + + cb_msg = (struct qcom_tee_msg_callback *)oic->out_msg.addr; + + while (1) { + if (oic->flags & QCOM_TEE_OIC_FLAG_BUSY) { + errno = oic->errno; + /* Update output buffer only if result is SUCCESS. */ + if (!errno) + errno = qcom_tee_update_msg(oic); + + qcom_tee_msg_translate_err(cb_msg, errno); + } + + /* Invoke remote object. */ + ret = qcom_tee_object_invoke_ctx_invoke(oic, result, &response_type); + + if (oic->flags & QCOM_TEE_OIC_FLAG_BUSY) { + struct qcom_tee_object *qto = oic->object; + + if (qto) { + if (oic->flags & QCOM_TEE_OIC_FLAG_NOTIFY) { + if (qto->ops->notify) + qto->ops->notify(oic, qto, errno || ret); + } + + /* Matching get is in qcom_tee_object_invoke. */ + qcom_tee_object_put(qto); + } + + oic->object = NULL_QCOM_TEE_OBJECT; + oic->flags &= ~(QCOM_TEE_OIC_FLAG_BUSY | QCOM_TEE_OIC_FLAG_NOTIFY); + } + + if (ret) { + if (!(oic->flags & QCOM_TEE_OIC_FLAG_SHARED)) { + /* Release QCOM_TEE_OBJECT_TYPE_CB_OBJECT input objects. */ + qcom_tee_arg_for_each_input_object(i, u) + if (typeof_qcom_tee_object(u[i].o) == + QCOM_TEE_OBJECT_TYPE_CB_OBJECT) + qcom_tee_object_put(u[i].o); + + ret = -EAGAIN; + } else { + /* On error, there is no clean way to exit. */ + /* For some reason we can not communicate with QTEE, so we can not + * notify QTEE about the failure and do further cleanup. + */ + ret = -ENODEV; + } + + goto out; + + } else { + /* QTEE obtained the ownership of QCOM_TEE_OBJECT_TYPE_CB_OBJECT + * input objects in 'u'. On further failure, QTEE is responsible + * to release them. + */ + oic->flags |= QCOM_TEE_OIC_FLAG_SHARED; + } + + /* Is it a callback request? */ +#define QCOM_TEE_RESULT_INBOUND_REQ_NEEDED 3 + if (response_type != QCOM_TEE_RESULT_INBOUND_REQ_NEEDED) { + if (!*result) { + ret = qcom_tee_update_args(u, oic); + if (ret) { + qcom_tee_arg_for_each_output_object(i, u) + qcom_tee_object_put(u[i].o); + + ret = -EAGAIN; + } + } + + break; + + } else { + oic->flags |= QCOM_TEE_OIC_FLAG_BUSY; + /* Before dispatching the request, handle any pending async requests. */ + qcom_tee_fetch_async_reqs(oic); + qcom_tee_object_invoke(oic, cb_msg); + } + } + + qcom_tee_fetch_async_reqs(oic); + +out: + qcom_tee_msg_buffers_free(oic); + + return ret; +} + +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) +{ + /* User can not set bits used by transport. */ + if (op & ~QCOM_TEE_MSG_OBJECT_OP_MASK) + return -EINVAL; + + /* User can only invoke QTEE hosted objects. */ + if (typeof_qcom_tee_object(object) != QCOM_TEE_OBJECT_TYPE_TEE && + typeof_qcom_tee_object(object) != QCOM_TEE_OBJECT_TYPE_ROOT) + return -EINVAL; + + /* User can not issue reserved operations to QTEE. */ + if (op == QCOM_TEE_MSG_OBJECT_OP_RELEASE || op == QCOM_TEE_MSG_OBJECT_OP_RETAIN) + return -EINVAL; + + return __qcom_tee_object_do_invoke(oic, object, op, u, result); +} +EXPORT_SYMBOL_GPL(qcom_tee_object_do_invoke); + +/* Dump object table. */ +static ssize_t qcom_tee_object_table_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct qcom_tee_object *object; + unsigned long idx; + size_t len = 0; + + xa_for_each_start(&xa_qcom_local_objects, idx, object, QCOM_TEE_OBJECT_ID_START) { + len += sysfs_emit_at(buf, len, "%4lx %4d %s\n", idx, + kref_read(&object->refcount), + qcom_tee_object_name(object)); + } + + return len; +} + +static struct kobj_attribute object_table = __ATTR_RO(qcom_tee_object_table); +static struct kobj_attribute release = __ATTR_RO(qcom_tee_release_wq); +static struct attribute *attrs[] = { + &object_table.attr, + &release.attr, + NULL +}; + +static struct attribute_group attr_group = { + .attrs = attrs, +}; + +static struct kobject *qcom_tee_object_invoke_kobj; +static int __init qcom_tee_object_invoke_init(void) +{ + int ret; + + ret = qcom_tee_release_init(); + if (ret) + return ret; + + ret = qcom_tee_msg_buffers_init(); + if (ret) + goto err_release_destroy; + + /* Create '/sys/firmware/qcom_tee'. */ + qcom_tee_object_invoke_kobj = kobject_create_and_add("qcom_tee", firmware_kobj); + if (!qcom_tee_object_invoke_kobj) { + ret = -ENOMEM; + + goto err_msg_buffers_destroy; + } + + /* Create 'qcom_tee_object_table' and 'qcom_tee_release_wq'. */ + ret = sysfs_create_group(qcom_tee_object_invoke_kobj, &attr_group); + if (ret) + goto err_kobject_put; + + return 0; + +err_kobject_put: + /* Remove '/sys/firmware/qcom_tee'. */ + kobject_put(qcom_tee_object_invoke_kobj); +err_msg_buffers_destroy: + qcom_tee_msg_buffers_destroy(); +err_release_destroy: + qcom_tee_release_destroy(); + + return ret; +} +module_init(qcom_tee_object_invoke_init); + +static void __exit qcom_tee_object_invoke_deinit(void) +{ + /* Wait for RELEASE operations for QTEE objects. */ + qcom_tee_release_destroy(); + qcom_tee_msg_buffers_destroy(); + sysfs_remove_group(qcom_tee_object_invoke_kobj, &attr_group); + kobject_put(qcom_tee_object_invoke_kobj); +} +module_exit(qcom_tee_object_invoke_deinit); + +MODULE_AUTHOR("Qualcomm"); +MODULE_DESCRIPTION("QTEE driver"); +MODULE_VERSION("1.0"); +MODULE_LICENSE("GPL"); diff --git a/drivers/tee/qcomtee/qcom_scm.c b/drivers/tee/qcomtee/qcom_scm.c new file mode 100644 index 000000000000..230faf249095 --- /dev/null +++ b/drivers/tee/qcomtee/qcom_scm.c @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#include <linux/firmware/qcom/qcom_scm.h> + +#include "qcomtee_private.h" + +int qcom_tee_object_invoke_ctx_invoke(struct qcom_tee_object_invoke_ctx *oic, + int *result, u64 *response_type) +{ + int ret; + u64 res; + + if (!(oic->flags & QCOM_TEE_OIC_FLAG_BUSY)) { + /* Direct QTEE object invocation. */ + ret = qcom_scm_qtee_invoke_smc(oic->in_msg_paddr, + oic->in_msg.size, + oic->out_msg_paddr, + oic->out_msg.size, + &res, response_type, NULL); + } else { + /* Submit callback response. */ + ret = qcom_scm_qtee_callback_response(oic->out_msg_paddr, + oic->out_msg.size, + &res, response_type, NULL); + } + + if (ret) + pr_err("QTEE returned with %d.\n", ret); + else + *result = (int)res; + + return ret; +} diff --git a/drivers/tee/qcomtee/qcomtee_msg.h b/drivers/tee/qcomtee/qcomtee_msg.h new file mode 100644 index 000000000000..7c968834ec9d --- /dev/null +++ b/drivers/tee/qcomtee/qcomtee_msg.h @@ -0,0 +1,217 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#ifndef QCOMTEE_MSG_H +#define QCOMTEE_MSG_H + +#include <linux/firmware/qcom/qcom_tee.h> + +/** + * DOC: ''Qualcomm TEE'' (QTEE) Transport Message + * + * There are two buffers shared with QTEE, inbound and outbound buffers. + * The inbound buffer is used for direct object invocation and the outbound buffer is + * used to make a request from QTEE to kernel, i.e. callback request. + * + * The unused tail of the outbound buffer is also used for sending and receiving + * asynchronous messages. An asynchronous message is independent from the current + * object invocation (i.e. contents of the inbound buffer) or callback request + * (i.e. the head of the outbound buffer), see qcom_tee_get_async_buffer(). It is + * used by endpoints (QTEE or kernel) as an optimization to reduce number of context + * switches between secure and non-secure world. + * + * For instance, QTEE never sends an explicit callback request to release an object in + * kernel. Instead, it sends asynchronous release messages in outbound buffer when QTEE + * returns from previous direct object invocation, or append asynchronous release + * messages after the current callback request. + * + * QTEE supports two types of arguments in a message: buffer and object arguments. + * Depending on the direction of data flow, they could be input buffer (IO) to QTEE, + * output buffer (OB) from QTEE, input object (IO) to QTEE, or output object (OO) from + * QTEE. Object arguments hold object ids. Buffer arguments hold (offset, size) pairs + * into the inbound or outbound buffers. + * + * QTEE holds an object table for objects, it hosts and exposes to kernel. An object id + * is an index to the object table in QTEE. + * + * For direct object invocation message format in inbound buffer see + * &struct qcom_tee_msg_object_invoke. For callback request message format in outbound + * buffer see &struct qcom_tee_msg_callback. For the message format for asynchronous message + * in outbound buffer see &struct qcom_tee_async_msg_hdr. + */ + +/** + * define QCOM_TEE_MSG_OBJECT_NS_BIT - Non-secure bit + * + * Object id is a globally unique 32-bit number. Ids referencing objects in kernel should + * have %QCOM_TEE_MSG_OBJECT_NS_BIT set. + */ +#define QCOM_TEE_MSG_OBJECT_NS_BIT BIT(31) + +/* Static object ids recognized by QTEE. */ +#define QCOM_TEE_MSG_OBJECT_NULL (0U) +#define QCOM_TEE_MSG_OBJECT_ROOT (1U) + +/* Definitions from QTEE as part of the transport protocol. */ + +/* qcom_tee_msg_arg is argument as recognized by QTEE. */ +union qcom_tee_msg_arg { + struct { + u32 offset; + u32 size; + } b; + u32 o; +}; + +/* BI and BO payloads in a QTEE messages should be at 64-bit boundaries. */ +#define qcom_tee_msg_offset_align(o) ALIGN((o), sizeof(u64)) + +/* Operation for objects is 32-bit. Transport uses upper 16-bits internally. */ +#define QCOM_TEE_MSG_OBJECT_OP_MASK 0x0000FFFFU + +/* Reserved Operation IDs sent to QTEE: */ +/* QCOM_TEE_MSG_OBJECT_OP_RELEASE - Reduces the refcount and releases the object. + * QCOM_TEE_MSG_OBJECT_OP_RETAIN - Increases the refcount. + * + * These operation id are valid for all objects. They are not available outside of this + * driver. Developers should use qcom_tee_object_get() and qcom_tee_object_put(), to + * achieve the same. + */ + +#define QCOM_TEE_MSG_OBJECT_OP_RELEASE (QCOM_TEE_MSG_OBJECT_OP_MASK - 0) +#define QCOM_TEE_MSG_OBJECT_OP_RETAIN (QCOM_TEE_MSG_OBJECT_OP_MASK - 1) + +/** + * struct qcom_tee_msg_object_invoke - Direct object invocation message + * @ctx: object id hosted in QTEE + * @op: operation for the object + * @counts: number of different type of arguments in @args + * @args: array of arguments + * + * @counts consists of 4 * 4-bits felids. Bits 0 - 3, is number of input buffers, + * bits 4 - 7, is number of output buffers, bits 8 - 11, is number of input objects, + * and bits 12 - 15, is number of output objects. Remaining bits should be zero. + * + * Maximum number of arguments of each type is defined by %QCOM_TEE_ARGS_PER_TYPE. + */ +struct qcom_tee_msg_object_invoke { + u32 cxt; + u32 op; + u32 counts; + union qcom_tee_msg_arg args[]; +}; + +/** + * struct qcom_tee_msg_callback - Callback request message + * @result: result of operation @op on object referenced by @cxt + * @cxt: object id hosted in kernel + * @op: operation for the object + * @counts: number of different type of arguments in @args + * @args: array of arguments + * + * For details of @counts, see &qcom_tee_msg_object_invoke.counts. + */ +struct qcom_tee_msg_callback { + u32 result; + u32 cxt; + u32 op; + u32 counts; + union qcom_tee_msg_arg args[]; +}; + +/* Offset in the message for the beginning of buffer argument's contents. */ +#define qcom_tee_msg_buffer_args(t, n) \ + qcom_tee_msg_offset_align(struct_size_t(t, args, n)) +/* Pointer to the beginning of a buffer argument's content at an offset in a message. */ +#define qcom_tee_msg_offset_to_ptr(m, off) ((void *)&((char *)(m))[(off)]) + +/* Some helpers to manage msg.counts. */ + +#define QCOM_TEE_MSG_NUM_IB(x) ((x) & 0xfU) +#define QCOM_TEE_MSG_NUM_OB(x) (((x) >> 4) & 0xfU) +#define QCOM_TEE_MSG_NUM_IO(x) (((x) >> 8) & 0xfU) +#define QCOM_TEE_MSG_NUM_OO(x) (((x) >> 12) & 0xfU) + +#define QCOM_TEE_MSG_IDX_IB(x) (0U) +#define QCOM_TEE_MSG_IDX_OB(x) (QCOM_TEE_MSG_IDX_IB(x) + QCOM_TEE_MSG_NUM_IB(x)) +#define QCOM_TEE_MSG_IDX_IO(x) (QCOM_TEE_MSG_IDX_OB(x) + QCOM_TEE_MSG_NUM_OB(x)) +#define QCOM_TEE_MSG_IDX_OO(x) (QCOM_TEE_MSG_IDX_IO(x) + QCOM_TEE_MSG_NUM_IO(x)) + +#define qcom_tee_msg_for_each(i, c, type) \ + for (i = QCOM_TEE_MSG_IDX_##type(c); \ + i < (QCOM_TEE_MSG_IDX_##type(c) + QCOM_TEE_MSG_NUM_##type(c)); \ + i++) + +#define qcom_tee_msg_for_each_input_buffer(i, m) qcom_tee_msg_for_each(i, (m)->counts, IB) +#define qcom_tee_msg_for_each_output_buffer(i, m) qcom_tee_msg_for_each(i, (m)->counts, OB) +#define qcom_tee_msg_for_each_input_object(i, m) qcom_tee_msg_for_each(i, (m)->counts, IO) +#define qcom_tee_msg_for_each_output_object(i, m) qcom_tee_msg_for_each(i, (m)->counts, OO) + +/* Sum of arguments in a message. */ +#define qcom_tee_msg_args(m) (QCOM_TEE_MSG_IDX_OO((m)->counts) + QCOM_TEE_MSG_NUM_OO((m)->counts)) + +static inline void qcom_tee_msg_init(struct qcom_tee_msg_object_invoke *msg, u32 cxt, u32 op, + int in_buffer, int out_buffer, int in_object, int out_object) +{ + msg->counts |= (in_buffer & 0xfU); + msg->counts |= ((out_buffer - in_buffer) & 0xfU) << 4; + msg->counts |= ((in_object - out_buffer) & 0xfU) << 8; + msg->counts |= ((out_object - in_object) & 0xfU) << 12; + msg->cxt = cxt; + msg->op = op; +} + +/* Generic error codes. */ +#define QCOM_TEE_MSG_OK 0 /* non-specific success code. */ +#define QCOM_TEE_MSG_ERROR 1 /* non-specific error. */ +#define QCOM_TEE_MSG_ERROR_INVALID 2 /* unsupported/unrecognized request. */ +#define QCOM_TEE_MSG_ERROR_SIZE_IN 3 /* supplied buffer/string too large. */ +#define QCOM_TEE_MSG_ERROR_SIZE_OUT 4 /* supplied output buffer too small. */ +#define QCOM_TEE_MSG_ERROR_USERBASE 10 /* start of user-defined error range. */ + +/* Transport layer error codes. */ +#define QCOM_TEE_MSG_ERROR_DEFUNCT -90 /* object no longer exists. */ +#define QCOM_TEE_MSG_ERROR_ABORT -91 /* calling thread must exit. */ +#define QCOM_TEE_MSG_ERROR_BADOBJ -92 /* invalid object context. */ +#define QCOM_TEE_MSG_ERROR_NOSLOTS -93 /* caller's object table full. */ +#define QCOM_TEE_MSG_ERROR_MAXARGS -94 /* too many args. */ +#define QCOM_TEE_MSG_ERROR_MAXDATA -95 /* buffers too large. */ +#define QCOM_TEE_MSG_ERROR_UNAVAIL -96 /* the request could not be processed. */ +#define QCOM_TEE_MSG_ERROR_KMEM -97 /* kernel out of memory. */ +#define QCOM_TEE_MSG_ERROR_REMOTE -98 /* local method sent to remote object. */ +#define QCOM_TEE_MSG_ERROR_BUSY -99 /* Object is busy. */ +#define QCOM_TEE_MSG_ERROR_TIMEOUT -103 /* Call Back Object invocation timed out. */ + +static inline void qcom_tee_msg_translate_err(struct qcom_tee_msg_callback *cb_msg, int err) +{ + if (!err) { + cb_msg->result = QCOM_TEE_MSG_OK; + } else if (err < 0) { + /* If err < 0, then it is a transport error. */ + switch (err) { + case -ENOMEM: + cb_msg->result = QCOM_TEE_MSG_ERROR_KMEM; + break; + case -ENODEV: + cb_msg->result = QCOM_TEE_MSG_ERROR_DEFUNCT; + break; + case -ENOSPC: + case -EBUSY: + cb_msg->result = QCOM_TEE_MSG_ERROR_BUSY; + break; + case -EBADF: + case -EINVAL: + cb_msg->result = QCOM_TEE_MSG_ERROR_UNAVAIL; + break; + default: + cb_msg->result = QCOM_TEE_MSG_ERROR; + } + } else { + /* If err > 0, then it is user defined error, pass it as is. */ + cb_msg->result = err; + } +} + +#endif /* QCOMTEE_MSG_H */ diff --git a/drivers/tee/qcomtee/qcomtee_private.h b/drivers/tee/qcomtee/qcomtee_private.h new file mode 100644 index 000000000000..e3e4ef51c0b2 --- /dev/null +++ b/drivers/tee/qcomtee/qcomtee_private.h @@ -0,0 +1,47 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#ifndef QCOM_TEE_PRIVATE_H +#define QCOM_TEE_PRIVATE_H + +#include <linux/firmware/qcom/qcom_tee.h> +#include <linux/kobject.h> +#include <linux/tee_core.h> + +struct qcom_tee_object *qcom_tee_idx_erase(u32 idx); +void qcom_tee_object_free(struct qcom_tee_object *object); + +/* Process async messages form QTEE. */ +void qcom_tee_fetch_async_reqs(struct qcom_tee_object_invoke_ctx *oic); + +int qcom_tee_release_init(void); +void qcom_tee_release_destroy(void); +void qcom_tee_release_tee_object(struct qcom_tee_object *object); +ssize_t qcom_tee_release_wq_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf); + +/* SCM call. */ +int qcom_tee_object_invoke_ctx_invoke(struct qcom_tee_object_invoke_ctx *oic, + int *result, u64 *response_type); + +/** + * __qcom_tee_object_do_invoke() - Submit an invocation for an object. + * @oic: context to use for current invocation. + * @object: object being invoked. + * @op: requested operation on object. + * @u: array of argument for the current invocation. + * @result: result returned from QTEE. + * + * Same as qcom_tee_object_do_invoke() without @object and @op is 32-bit, + * upper 16-bits are for internal use. + * + * Return: On success return 0. On error returns -EINVAL and -ENOSPC if unable to initiate + * the invocation, -EAGAIN if invocation failed and user can retry the invocation. + * Otherwise, -ENODEV on fatal failure. + */ +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); + +#endif /* QCOM_TEE_PRIVATE_H */ diff --git a/drivers/tee/qcomtee/release.c b/drivers/tee/qcomtee/release.c new file mode 100644 index 000000000000..f2e048418e23 --- /dev/null +++ b/drivers/tee/qcomtee/release.c @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#include "qcomtee_private.h" +#include "qcomtee_msg.h" + +static struct workqueue_struct *qcom_tee_release_wq; + +/* Number of all release requests pending for processing. */ +static atomic_t qcom_tee_pending_releases = ATOMIC_INIT(0); + +/* qcom_tee_object_do_release makes direct object invocation to release an object. */ +static void qcom_tee_destroy_user_object(struct work_struct *work) +{ + static struct qcom_tee_object_invoke_ctx oic; + static struct qcom_tee_arg args[1] = { 0 }; + struct qcom_tee_object *object; + int ret, result; + + object = container_of(work, struct qcom_tee_object, work); + + ret = __qcom_tee_object_do_invoke(&oic, object, QCOM_TEE_MSG_OBJECT_OP_RELEASE, args, + &result); + + /* Is it safe to retry the release? */ + if (ret == -EAGAIN) { + queue_work(qcom_tee_release_wq, &object->work); + } else { + if (ret || result) + pr_err("%s: %s release failed, ret = %d (%x).\n", + __func__, qcom_tee_object_name(object), ret, result); + + atomic_dec(&qcom_tee_pending_releases); + qcom_tee_object_free(object); + } +} + +/* qcom_tee_release_tee_object puts object in release work queue. */ +void qcom_tee_release_tee_object(struct qcom_tee_object *object) +{ + INIT_WORK(&object->work, qcom_tee_destroy_user_object); + atomic_inc(&qcom_tee_pending_releases); + queue_work(qcom_tee_release_wq, &object->work); +} + +ssize_t qcom_tee_release_wq_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) +{ + return sysfs_emit(buf, "%d\n", atomic_read(&qcom_tee_pending_releases)); +} + +int qcom_tee_release_init(void) +{ + qcom_tee_release_wq = alloc_ordered_workqueue("qcom_tee_release_wq", 0); + if (!qcom_tee_release_wq) + return -ENOMEM; + + return 0; +} + +void qcom_tee_release_destroy(void) +{ + /* It drains the wq. */ + destroy_workqueue(qcom_tee_release_wq); +} diff --git a/include/linux/firmware/qcom/qcom_tee.h b/include/linux/firmware/qcom/qcom_tee.h new file mode 100644 index 000000000000..90e5e10a0e62 --- /dev/null +++ b/include/linux/firmware/qcom/qcom_tee.h @@ -0,0 +1,284 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#ifndef __QCOM_TEE_H +#define __QCOM_TEE_H + +#include <linux/kref.h> +#include <linux/completion.h> +#include <linux/workqueue.h> + +struct qcom_tee_object; + +/** + * DOC: Overview + * + * qcom_tee_object provides object ref-counting, id allocation for objects hosted in + * REE, and necessary message marshaling for Qualcomm TEE (QTEE). + * + * To invoke an object in QTEE, user calls qcom_tee_object_do_invoke() while passing + * an instance of &struct qcom_tee_object and the requested operation + arguments. + * + * After the boot, QTEE provides a static object %ROOT_QCOM_TEE_OBJECT (type of + * %QCOM_TEE_OBJECT_TYPE_ROOT). The root object is invoked to pass user's credentials and + * obtain other instances of &struct qcom_tee_object (type of %QCOM_TEE_OBJECT_TYPE_TEE) + * that represents services and TAs in QTEE, see &enum qcom_tee_object_type. + * + * The object received from QTEE are refcounted. So the owner of these objects can + * issue qcom_tee_object_get(), to increase the refcount, and pass objects to other + * clients, or issue qcom_tee_object_put() to decrease the refcount, and releasing + * the resources in QTEE. + * + * REE can host services accessible to QTEE. A driver should embed an instance of + * &struct qcom_tee_object in the struct it wants to export to QTEE (it is called + * callback object). It issues qcom_tee_object_user_init() to set the dispatch() + * operation for the callback object and set its type to %QCOM_TEE_OBJECT_TYPE_CB_OBJECT. + * + * core.c holds an object table for callback objects. An object id is assigned + * to each callback object which is an index to the object table. QTEE uses these ids + * to reference or invoke callback objects. + * + * If QTEE invoke a callback object in REE, the dispatch() operation is called in the + * context of thread that called qcom_tee_object_do_invoke(), originally. + */ + +/** + * enum qcom_tee_object_typ - Object types. + * @QCOM_TEE_OBJECT_TYPE_TEE: object hosted on QTEE. + * @QCOM_TEE_OBJECT_TYPE_CB_OBJECT: object hosted on REE. + * @QCOM_TEE_OBJECT_TYPE_ROOT: 'primordial' object. + * @QCOM_TEE_OBJECT_TYPE_NULL: NULL object. + * + * Primordial object is used for bootstrapping the IPC connection between a REE + * and QTEE. It is invoked by REE when it wants to get a 'client env'. + */ +enum qcom_tee_object_type { + QCOM_TEE_OBJECT_TYPE_TEE, + QCOM_TEE_OBJECT_TYPE_CB_OBJECT, + QCOM_TEE_OBJECT_TYPE_ROOT, + QCOM_TEE_OBJECT_TYPE_NULL, +}; + +/** + * enum qcom_tee_arg_type - Type of QTEE argument. + * @QCOM_TEE_ARG_TYPE_INV: invalid type. + * @QCOM_TEE_ARG_TYPE_IB: input buffer (IO). + * @QCOM_TEE_ARG_TYPE_OO: output object (OO). + * @QCOM_TEE_ARG_TYPE_OB: output buffer (OB). + * @QCOM_TEE_ARG_TYPE_IO: input object (IO). + * + * Use invalid type to specify end of argument array. + */ +enum qcom_tee_arg_type { + QCOM_TEE_ARG_TYPE_INV = 0, + QCOM_TEE_ARG_TYPE_OB, + QCOM_TEE_ARG_TYPE_OO, + QCOM_TEE_ARG_TYPE_IB, + QCOM_TEE_ARG_TYPE_IO, + QCOM_TEE_ARG_TYPE_NR, +}; + +/** + * define QCOM_TEE_ARGS_PER_TYPE - Maximum arguments of specific type. + * + * QTEE transport protocol limits maximum number of argument of specific type + * (i.e. IB, OB, IO, and OO). + */ +#define QCOM_TEE_ARGS_PER_TYPE 16 + +/* Maximum arguments that can fit in a QTEE message, ignoring the type. */ +#define QCOM_TEE_ARGS_MAX (QCOM_TEE_ARGS_PER_TYPE * (QCOM_TEE_ARG_TYPE_NR - 1)) + +struct qcom_tee_buffer { + union { + void *addr; + void __user *uaddr; + }; + size_t size; +}; + +/** + * struct qcom_tee_arg - Argument for QTEE object invocation. + * @type: type of argument as &enum qcom_tee_arg_type. + * @flags: extra flags. + * @b: address and size if type of argument is buffer. + * @o: object instance if type of argument is object. + * + * &qcom_tee_arg.flags only accept %QCOM_TEE_ARG_FLAGS_UADDR for now which states + * that &qcom_tee_arg.b contains userspace address in uaddr. + */ +struct qcom_tee_arg { + enum qcom_tee_arg_type type; +/* 'b.uaddr' holds a __user address. */ +#define QCOM_TEE_ARG_FLAGS_UADDR 1 + unsigned int flags; + union { + struct qcom_tee_buffer b; + struct qcom_tee_object *o; + }; +}; + +static inline int qcom_tee_args_len(struct qcom_tee_arg *args) +{ + int i = 0; + + while (args[i].type != QCOM_TEE_ARG_TYPE_INV) + i++; + return i; +} + +#define QCOM_TEE_OIC_FLAG_BUSY BIT(1) /* Context is busy (callback is in progress). */ +#define QCOM_TEE_OIC_FLAG_NOTIFY BIT(2) /* Context needs to notify the current object. */ +#define QCOM_TEE_OIC_FLAG_SHARED BIT(3) /* Context has shared state with QTEE. */ + +struct qcom_tee_object_invoke_ctx { + unsigned long flags; + int errno; + + /* Current object invoked in this callback context. */ + struct qcom_tee_object *object; + + /* Arguments passed to dispatch callback (+1 for ending QCOM_TEE_ARG_TYPE_INV). */ + struct qcom_tee_arg u[QCOM_TEE_ARGS_MAX + 1]; + + /* Inbound and Outbound buffers shared with QTEE. */ + struct qcom_tee_buffer in_msg; /* Inbound Buffer. */ + phys_addr_t in_msg_paddr; /* Physical address of inbound buffer. */ + struct qcom_tee_buffer out_msg; /* Outbound Buffer. */ + phys_addr_t out_msg_paddr; /* Physical address of outbound buffer. */ + + /* Extra data attached to this context. */ + void *data; +}; + +/** + * qcom_tee_object_do_invoke() - Submit an invocation for an object. + * @oic: context to use for current invocation. + * @object: object being invoked. + * @op: requested operation on object. + * @u: array of argument for the current invocation. + * @result: result returned from QTEE. + * + * The caller is responsible to keep track of the refcount for each object, + * including @object. On return, the caller loses the ownership of all input object of + * type %QCOM_TEE_OBJECT_TYPE_CB_OBJECT. + * + * @object can be of %QCOM_TEE_OBJECT_TYPE_ROOT or %QCOM_TEE_OBJECT_TYPE_TEE types. + * + * Return: On success return 0. On error returns -EINVAL and -ENOSPC if unable to initiate + * the invocation, -EAGAIN if invocation failed and user may retry the invocation. + * Otherwise, -ENODEV on fatal failure. + */ +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_object_operations - Callback object operations. + * @release: release object if QTEE is not using it. + * @dispatch: dispatch the operation requested by QTEE. + * @notify: report status of any pending response submitted by @dispatch. + * + * Transport may fail (e.g. object table is full) even after @dispatch successfully submitted + * the response. @notify is called to do the necessary cleanup. + */ +struct qcom_tee_object_operations { + void (*release)(struct qcom_tee_object *object); + int (*dispatch)(struct qcom_tee_object_invoke_ctx *oic, + struct qcom_tee_object *object, u32 op, struct qcom_tee_arg *args); + void (*notify)(struct qcom_tee_object_invoke_ctx *oic, + struct qcom_tee_object *object, int err); +}; + +/** + * struct qcom_tee_object - QTEE or REE object. + * @name: object name. + * @refcount: reference counter. + * @object_type: object type as &enum qcom_tee_object_type. + * @info: extra information for object. + * @owner: owning module/driver. + * @ops: callback operations for object of type %QCOM_TEE_OBJECT_TYPE_CB_OBJECT. + * @work: work for async operation on object. + * + * @work is currently only used for release object of %QCOM_TEE_OBJECT_TYPE_TEE type. + */ +struct qcom_tee_object { + const char *name; + struct kref refcount; + + enum qcom_tee_object_type object_type; + union object_info { + /* QTEE object id if object_type is %QCOM_TEE_OBJECT_TYPE_TEE. */ + unsigned long qtee_id; + } info; + + struct module *owner; + struct qcom_tee_object_operations *ops; + struct work_struct work; +}; + +/* Static instances of qcom_tee_object objects. */ +#define NULL_QCOM_TEE_OBJECT ((struct qcom_tee_object *)(0)) +extern struct qcom_tee_object qcom_tee_object_root; +#define ROOT_QCOM_TEE_OBJECT (&qcom_tee_object_root) + +static inline enum qcom_tee_object_type typeof_qcom_tee_object(struct qcom_tee_object *object) +{ + if (object == NULL_QCOM_TEE_OBJECT) + return QCOM_TEE_OBJECT_TYPE_NULL; + return object->object_type; +} + +static inline const char *qcom_tee_object_name(struct qcom_tee_object *object) +{ + if (object == NULL_QCOM_TEE_OBJECT) + return "null"; + + if (!object->name) + return "no-name"; + return object->name; +} + +/** + * __qcom_tee_object_user_init() - Initialize an object for user. + * @object: object to initialize. + * @object_type: type of object as &enum qcom_tee_object_type. + * @ops: instance of callbacks. + * @owner: owning module/driver. + * @fmt: name assigned to the object. + * + * Return: On success return 0 or <0 on failure. + */ +int __qcom_tee_object_user_init(struct qcom_tee_object *object, enum qcom_tee_object_type ot, + struct qcom_tee_object_operations *ops, struct module *owner, + const char *fmt, ...); +#define qcom_tee_object_user_init(obj, ot, ops, fmt, ...) \ + __qcom_tee_object_user_init((obj), (ot), (ops), THIS_MODULE, (fmt), __VA_ARGS__) + +/* Object release is RCU protected. */ +int qcom_tee_object_get(struct qcom_tee_object *object); +void qcom_tee_object_put(struct qcom_tee_object *object); + +#define qcom_tee_arg_for_each(i, args) \ + for (i = 0; args[i].type != QCOM_TEE_ARG_TYPE_INV; i++) + +/* Next argument of type @type after index @i. */ +int qcom_tee_next_arg_type(struct qcom_tee_arg *u, int i, enum qcom_tee_arg_type type); + +/* Iterate over argument of given type. */ +#define qcom_tee_arg_for_each_type(i, args, at) \ + for (i = 0, i = qcom_tee_next_arg_type(args, i, at); \ + args[i].type != QCOM_TEE_ARG_TYPE_INV; \ + i++, i = qcom_tee_next_arg_type(args, i, at)) + +#define qcom_tee_arg_for_each_input_buffer(i, args) \ + qcom_tee_arg_for_each_type(i, args, QCOM_TEE_ARG_TYPE_IB) +#define qcom_tee_arg_for_each_output_buffer(i, args) \ + qcom_tee_arg_for_each_type(i, args, QCOM_TEE_ARG_TYPE_OB) +#define qcom_tee_arg_for_each_input_object(i, args) \ + qcom_tee_arg_for_each_type(i, args, QCOM_TEE_ARG_TYPE_IO) +#define qcom_tee_arg_for_each_output_object(i, args) \ + qcom_tee_arg_for_each_type(i, args, QCOM_TEE_ARG_TYPE_OO) + +#endif /* __QCOM_TEE_H */