mirror of
https://github.com/linux-msm/laptops-kernel.git
synced 2026-08-13 14:19:53 -07:00
Merge tag 'tee-qcomtee-for-v6.18' of git://git.kernel.org/pub/scm/linux/kernel/git/jenswi/linux-tee into soc/drivers
Add Qualcomm TEE driver (QTEE) This introduces a Trusted Execution Environment (TEE) driver for Qualcomm TEE (QTEE). QTEE enables Trusted Applications (TAs) and services to run securely. It uses an object-based interface, where each service is an object with sets of operations. Kernel and userspace services are also available to QTEE through a similar approach. QTEE makes callback requests that are converted into object invocations. These objects can represent services within the kernel or userspace process. We extend the TEE subsystem to understand object parameters and an ioctl call so client can invoke objects in QTEE: - TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_* - TEE_IOC_OBJECT_INVOKE The existing ioctl calls TEE_IOC_SUPPL_RECV and TEE_IOC_SUPPL_SEND are used for invoking services in the userspace process by QTEE. The TEE backend driver uses the QTEE Transport Message to communicate with QTEE. Interactions through the object INVOKE interface are translated into QTEE messages. Likewise, object invocations from QTEE for userspace objects are converted into SEND/RECV ioctl calls to supplicants. * tag 'tee-qcomtee-for-v6.18' of git://git.kernel.org/pub/scm/linux/kernel/git/jenswi/linux-tee: Documentation: tee: Add Qualcomm TEE driver tee: qcom: enable TEE_IOC_SHM_ALLOC ioctl tee: qcom: add primordial object tee: add Qualcomm TEE driver tee: increase TEE_MAX_ARG_SIZE to 4096 tee: add TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF tee: add TEE_IOCTL_PARAM_ATTR_TYPE_UBUF tee: add close_context to TEE driver operation tee: allow a driver to allocate a tee_device without a pool Link: https://lore.kernel.org/r/20250915174957.GA2040478@rayden Signed-off-by: Arnd Bergmann <arnd@arndb.de>
This commit is contained in:
@@ -11,6 +11,7 @@ TEE Subsystem
|
||||
op-tee
|
||||
amd-tee
|
||||
ts-tee
|
||||
qtee
|
||||
|
||||
.. only:: subproject and html
|
||||
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
.. SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
=============================================
|
||||
QTEE (Qualcomm Trusted Execution Environment)
|
||||
=============================================
|
||||
|
||||
The QTEE driver handles communication with Qualcomm TEE [1].
|
||||
|
||||
The lowest level of communication with QTEE builds on the ARM SMC Calling
|
||||
Convention (SMCCC) [2], which is the foundation for QTEE's Secure Channel
|
||||
Manager (SCM) [3] used internally by the driver.
|
||||
|
||||
In a QTEE-based system, services are represented as objects with a series of
|
||||
operations that can be called to produce results, including other objects.
|
||||
|
||||
When an object is hosted within QTEE, executing its operations is referred
|
||||
to as "direct invocation". QTEE can also invoke objects hosted in the non-secure
|
||||
world using a method known as "callback request".
|
||||
|
||||
The SCM provides two functions to support direct invocation and callback requests:
|
||||
|
||||
- QCOM_SCM_SMCINVOKE_INVOKE: Used for direct invocation. It can return either
|
||||
a result or initiate a callback request.
|
||||
- QCOM_SCM_SMCINVOKE_CB_RSP: Used to submit a response to a callback request
|
||||
triggered by a previous direct invocation.
|
||||
|
||||
The QTEE Transport Message [4] is stacked on top of the SCM driver functions.
|
||||
|
||||
A message consists of two buffers shared with QTEE: inbound and outbound
|
||||
buffers. The inbound buffer is used for direct invocation, and the outbound
|
||||
buffer is used to make callback requests. This picture shows the contents of
|
||||
a QTEE transport message::
|
||||
|
||||
+---------------------+
|
||||
| v
|
||||
+-----------------+-------+-------+------+--------------------------+
|
||||
| qcomtee_msg_ |object | buffer | |
|
||||
| object_invoke | id | offset, size | | (inbound buffer)
|
||||
+-----------------+-------+--------------+--------------------------+
|
||||
<---- header -----><---- arguments ------><- in/out buffer payload ->
|
||||
|
||||
+-----------+
|
||||
| v
|
||||
+-----------------+-------+-------+------+----------------------+
|
||||
| qcomtee_msg_ |object | buffer | |
|
||||
| callback | id | offset, size | | (outbound buffer)
|
||||
+-----------------+-------+--------------+----------------------+
|
||||
|
||||
Each buffer is started with a header and array of arguments.
|
||||
|
||||
QTEE Transport Message supports four types of arguments:
|
||||
|
||||
- Input Object (IO) is an object parameter to the current invocation
|
||||
or callback request.
|
||||
- Output Object (OO) is an object parameter from the current invocation
|
||||
or callback request.
|
||||
- Input Buffer (IB) is (offset, size) pair to the inbound or outbound region
|
||||
to store parameter to the current invocation or callback request.
|
||||
- Output Buffer (OB) is (offset, size) pair to the inbound or outbound region
|
||||
to store parameter from the current invocation or callback request.
|
||||
|
||||
Picture of the relationship between the different components in the QTEE
|
||||
architecture::
|
||||
|
||||
User space Kernel Secure world
|
||||
~~~~~~~~~~ ~~~~~~ ~~~~~~~~~~~~
|
||||
+--------+ +----------+ +--------------+
|
||||
| Client | |callback | | Trusted |
|
||||
+--------+ |server | | Application |
|
||||
/\ +----------+ +--------------+
|
||||
|| +----------+ /\ /\
|
||||
|| |callback | || ||
|
||||
|| |server | || \/
|
||||
|| +----------+ || +--------------+
|
||||
|| /\ || | TEE Internal |
|
||||
|| || || | API |
|
||||
\/ \/ \/ +--------+--------+ +--------------+
|
||||
+---------------------+ | TEE | QTEE | | QTEE |
|
||||
| libqcomtee [5] | | subsys | driver | | Trusted OS |
|
||||
+-------+-------------+--+----+-------+----+-------------+--------------+
|
||||
| Generic TEE API | | QTEE MSG |
|
||||
| IOCTL (TEE_IOC_*) | | SMCCC (QCOM_SCM_SMCINVOKE_*) |
|
||||
+-----------------------------+ +---------------------------------+
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
[1] https://docs.qualcomm.com/bundle/publicresource/topics/80-70015-11/qualcomm-trusted-execution-environment.html
|
||||
|
||||
[2] http://infocenter.arm.com/help/topic/com.arm.doc.den0028a/index.html
|
||||
|
||||
[3] drivers/firmware/qcom/qcom_scm.c
|
||||
|
||||
[4] drivers/tee/qcomtee/qcomtee_msg.h
|
||||
|
||||
[5] https://github.com/quic/quic-teec
|
||||
@@ -20885,6 +20885,13 @@ F: Documentation/networking/device_drivers/cellular/qualcomm/rmnet.rst
|
||||
F: drivers/net/ethernet/qualcomm/rmnet/
|
||||
F: include/linux/if_rmnet.h
|
||||
|
||||
QUALCOMM TEE (QCOMTEE) DRIVER
|
||||
M: Amirreza Zarrabi <amirreza.zarrabi@oss.qualcomm.com>
|
||||
L: linux-arm-msm@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/tee/qtee.rst
|
||||
F: drivers/tee/qcomtee/
|
||||
|
||||
QUALCOMM TRUST ZONE MEMORY ALLOCATOR
|
||||
M: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
|
||||
L: linux-arm-msm@vger.kernel.org
|
||||
|
||||
@@ -20,5 +20,6 @@ config TEE_DMABUF_HEAPS
|
||||
source "drivers/tee/optee/Kconfig"
|
||||
source "drivers/tee/amdtee/Kconfig"
|
||||
source "drivers/tee/tstee/Kconfig"
|
||||
source "drivers/tee/qcomtee/Kconfig"
|
||||
|
||||
endif
|
||||
|
||||
@@ -7,3 +7,4 @@ tee-objs += tee_shm_pool.o
|
||||
obj-$(CONFIG_OPTEE) += optee/
|
||||
obj-$(CONFIG_AMDTEE) += amdtee/
|
||||
obj-$(CONFIG_ARM_TSTEE) += tstee/
|
||||
obj-$(CONFIG_QCOMTEE) += qcomtee/
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
# Qualcomm Trusted Execution Environment Configuration
|
||||
config QCOMTEE
|
||||
tristate "Qualcomm TEE Support"
|
||||
depends on !CPU_BIG_ENDIAN
|
||||
select QCOM_SCM
|
||||
select QCOM_TZMEM_MODE_SHMBRIDGE
|
||||
help
|
||||
This option enables the Qualcomm Trusted Execution Environment (QTEE)
|
||||
driver. It provides an API to access services offered by QTEE and
|
||||
its loaded Trusted Applications (TAs). Additionally, it facilitates
|
||||
the export of userspace services provided by supplicants to QTEE.
|
||||
@@ -0,0 +1,9 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
obj-$(CONFIG_QCOMTEE) += qcomtee.o
|
||||
qcomtee-objs += async.o
|
||||
qcomtee-objs += call.o
|
||||
qcomtee-objs += core.o
|
||||
qcomtee-objs += mem_obj.o
|
||||
qcomtee-objs += primordial_obj.o
|
||||
qcomtee-objs += shm.o
|
||||
qcomtee-objs += user_obj.o
|
||||
@@ -0,0 +1,182 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include "qcomtee.h"
|
||||
|
||||
#define QCOMTEE_ASYNC_VERSION_1_0 0x00010000U /* Maj: 0x0001, Min: 0x0000. */
|
||||
#define QCOMTEE_ASYNC_VERSION_1_1 0x00010001U /* Maj: 0x0001, Min: 0x0001. */
|
||||
#define QCOMTEE_ASYNC_VERSION_1_2 0x00010002U /* Maj: 0x0001, Min: 0x0002. */
|
||||
#define QCOMTEE_ASYNC_VERSION_CURRENT QCOMTEE_ASYNC_VERSION_1_2
|
||||
|
||||
#define QCOMTEE_ASYNC_VERSION_MAJOR(n) upper_16_bits(n)
|
||||
#define QCOMTEE_ASYNC_VERSION_MINOR(n) lower_16_bits(n)
|
||||
|
||||
#define QCOMTEE_ASYNC_VERSION_CURRENT_MAJOR \
|
||||
QCOMTEE_ASYNC_VERSION_MAJOR(QCOMTEE_ASYNC_VERSION_CURRENT)
|
||||
#define QCOMTEE_ASYNC_VERSION_CURRENT_MINOR \
|
||||
QCOMTEE_ASYNC_VERSION_MINOR(QCOMTEE_ASYNC_VERSION_CURRENT)
|
||||
|
||||
/**
|
||||
* struct qcomtee_async_msg_hdr - Asynchronous message header format.
|
||||
* @version: current async protocol version of the remote endpoint.
|
||||
* @op: async operation.
|
||||
*
|
||||
* @version specifies the endpoint's (QTEE or driver) supported async protocol.
|
||||
* For example, if QTEE sets @version to %QCOMTEE_ASYNC_VERSION_1_1, QTEE
|
||||
* handles operations supported in %QCOMTEE_ASYNC_VERSION_1_1 or
|
||||
* %QCOMTEE_ASYNC_VERSION_1_0. @op determines the message format.
|
||||
*/
|
||||
struct qcomtee_async_msg_hdr {
|
||||
u32 version;
|
||||
u32 op;
|
||||
};
|
||||
|
||||
/* Size of an empty async message. */
|
||||
#define QCOMTEE_ASYNC_MSG_ZERO sizeof(struct qcomtee_async_msg_hdr)
|
||||
|
||||
/**
|
||||
* struct qcomtee_async_release_msg - Release asynchronous message.
|
||||
* @hdr: message header as &struct qcomtee_async_msg_hdr.
|
||||
* @counts: number of objects in @object_ids.
|
||||
* @object_ids: array of object IDs that should be released.
|
||||
*
|
||||
* Available in Maj = 0x0001, Min >= 0x0000.
|
||||
*/
|
||||
struct qcomtee_async_release_msg {
|
||||
struct qcomtee_async_msg_hdr hdr;
|
||||
u32 counts;
|
||||
u32 object_ids[] __counted_by(counts);
|
||||
};
|
||||
|
||||
/**
|
||||
* qcomtee_get_async_buffer() - Get the start of the asynchronous message.
|
||||
* @oic: context used for the current invocation.
|
||||
* @async_buffer: return buffer to extract from or fill in async messages.
|
||||
*
|
||||
* If @oic is used for direct object invocation, the whole outbound buffer
|
||||
* is available for the async message. If @oic is used for a callback request,
|
||||
* the tail of the outbound buffer (after the callback request message) is
|
||||
* available for the async message.
|
||||
*
|
||||
* The start of the async buffer is aligned, see qcomtee_msg_offset_align().
|
||||
*/
|
||||
static void qcomtee_get_async_buffer(struct qcomtee_object_invoke_ctx *oic,
|
||||
struct qcomtee_buffer *async_buffer)
|
||||
{
|
||||
struct qcomtee_msg_callback *msg;
|
||||
unsigned int offset;
|
||||
int i;
|
||||
|
||||
if (!(oic->flags & QCOMTEE_OIC_FLAG_BUSY)) {
|
||||
/* The outbound buffer is empty. Using the whole buffer. */
|
||||
offset = 0;
|
||||
} else {
|
||||
msg = (struct qcomtee_msg_callback *)oic->out_msg.addr;
|
||||
|
||||
/* Start offset in a message for buffer arguments. */
|
||||
offset = qcomtee_msg_buffer_args(struct qcomtee_msg_callback,
|
||||
qcomtee_msg_args(msg));
|
||||
|
||||
/* Add size of IB arguments. */
|
||||
qcomtee_msg_for_each_input_buffer(i, msg)
|
||||
offset += qcomtee_msg_offset_align(msg->args[i].b.size);
|
||||
|
||||
/* Add size of OB arguments. */
|
||||
qcomtee_msg_for_each_output_buffer(i, msg)
|
||||
offset += qcomtee_msg_offset_align(msg->args[i].b.size);
|
||||
}
|
||||
|
||||
async_buffer->addr = oic->out_msg.addr + offset;
|
||||
async_buffer->size = oic->out_msg.size - offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* async_release() - Process QTEE async release requests.
|
||||
* @oic: context used for the current invocation.
|
||||
* @msg: async message for object release.
|
||||
* @size: size of the async buffer available.
|
||||
*
|
||||
* Return: Size of the outbound buffer used when processing @msg.
|
||||
*/
|
||||
static size_t async_release(struct qcomtee_object_invoke_ctx *oic,
|
||||
struct qcomtee_async_msg_hdr *async_msg,
|
||||
size_t size)
|
||||
{
|
||||
struct qcomtee_async_release_msg *msg;
|
||||
struct qcomtee_object *object;
|
||||
int i;
|
||||
|
||||
msg = (struct qcomtee_async_release_msg *)async_msg;
|
||||
|
||||
for (i = 0; i < msg->counts; i++) {
|
||||
object = qcomtee_idx_erase(oic, msg->object_ids[i]);
|
||||
qcomtee_object_put(object);
|
||||
}
|
||||
|
||||
return struct_size(msg, object_ids, msg->counts);
|
||||
}
|
||||
|
||||
/**
|
||||
* qcomtee_fetch_async_reqs() - Fetch and process asynchronous messages.
|
||||
* @oic: context used for the current invocation.
|
||||
*
|
||||
* Calls handlers to process the requested operations in the async message.
|
||||
* Currently, only supports async release requests.
|
||||
*/
|
||||
void qcomtee_fetch_async_reqs(struct qcomtee_object_invoke_ctx *oic)
|
||||
{
|
||||
struct qcomtee_async_msg_hdr *async_msg;
|
||||
struct qcomtee_buffer async_buffer;
|
||||
size_t consumed, used = 0;
|
||||
u16 major_ver;
|
||||
|
||||
qcomtee_get_async_buffer(oic, &async_buffer);
|
||||
|
||||
while (async_buffer.size - used > QCOMTEE_ASYNC_MSG_ZERO) {
|
||||
async_msg = (struct qcomtee_async_msg_hdr *)(async_buffer.addr +
|
||||
used);
|
||||
/*
|
||||
* QTEE assumes that the unused space of the async buffer is
|
||||
* zeroed; so if version is zero, the buffer is unused.
|
||||
*/
|
||||
if (async_msg->version == 0)
|
||||
goto out;
|
||||
|
||||
major_ver = QCOMTEE_ASYNC_VERSION_MAJOR(async_msg->version);
|
||||
/* Major version mismatch is a compatibility break. */
|
||||
if (major_ver != QCOMTEE_ASYNC_VERSION_CURRENT_MAJOR) {
|
||||
pr_err("Async message version mismatch (%u != %u)\n",
|
||||
major_ver, QCOMTEE_ASYNC_VERSION_CURRENT_MAJOR);
|
||||
|
||||
goto out;
|
||||
}
|
||||
|
||||
switch (async_msg->op) {
|
||||
case QCOMTEE_MSG_OBJECT_OP_RELEASE:
|
||||
consumed = async_release(oic, async_msg,
|
||||
async_buffer.size - used);
|
||||
break;
|
||||
default:
|
||||
pr_err("Unsupported async message %u\n", async_msg->op);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Supported operation but unable to parse the message. */
|
||||
if (!consumed) {
|
||||
pr_err("Unable to parse async message for op %u\n",
|
||||
async_msg->op);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Next async message. */
|
||||
used += qcomtee_msg_offset_align(consumed);
|
||||
}
|
||||
|
||||
out:
|
||||
/* Reset the async buffer so async requests do not loop to QTEE. */
|
||||
memzero_explicit(async_buffer.addr, async_buffer.size);
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,169 @@
|
||||
// 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/mm.h>
|
||||
|
||||
#include "qcomtee.h"
|
||||
|
||||
/**
|
||||
* DOC: Memory and Mapping Objects
|
||||
*
|
||||
* QTEE uses memory objects for memory sharing with Linux.
|
||||
* A memory object can be a standard dma_buf or a contiguous memory range,
|
||||
* e.g., tee_shm. A memory object should support one operation: map. When
|
||||
* invoked by QTEE, a mapping object is generated. A mapping object supports
|
||||
* one operation: unmap.
|
||||
*
|
||||
* (1) To map a memory object, QTEE invokes the primordial object with
|
||||
* %QCOMTEE_OBJECT_OP_MAP_REGION operation; see
|
||||
* qcomtee_primordial_obj_dispatch().
|
||||
* (2) To unmap a memory object, QTEE releases the mapping object which
|
||||
* calls qcomtee_mem_object_release().
|
||||
*
|
||||
* The map operation is implemented in the primordial object as a privileged
|
||||
* operation instead of qcomtee_mem_object_dispatch(). Otherwise, on
|
||||
* platforms without shm_bridge, a user can trick QTEE into writing to the
|
||||
* kernel memory by passing a user object as a memory object and returning a
|
||||
* random physical address as the result of the mapping request.
|
||||
*/
|
||||
|
||||
struct qcomtee_mem_object {
|
||||
struct qcomtee_object object;
|
||||
struct tee_shm *shm;
|
||||
/* QTEE requires these felids to be page aligned. */
|
||||
phys_addr_t paddr; /* Physical address of range. */
|
||||
size_t size; /* Size of the range. */
|
||||
};
|
||||
|
||||
#define to_qcomtee_mem_object(o) \
|
||||
container_of((o), struct qcomtee_mem_object, object)
|
||||
|
||||
static struct qcomtee_object_operations qcomtee_mem_object_ops;
|
||||
|
||||
/* Is it a memory object using tee_shm? */
|
||||
int is_qcomtee_memobj_object(struct qcomtee_object *object)
|
||||
{
|
||||
return object != NULL_QCOMTEE_OBJECT &&
|
||||
typeof_qcomtee_object(object) == QCOMTEE_OBJECT_TYPE_CB &&
|
||||
object->ops == &qcomtee_mem_object_ops;
|
||||
}
|
||||
|
||||
static int qcomtee_mem_object_dispatch(struct qcomtee_object_invoke_ctx *oic,
|
||||
struct qcomtee_object *object, u32 op,
|
||||
struct qcomtee_arg *args)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static void qcomtee_mem_object_release(struct qcomtee_object *object)
|
||||
{
|
||||
struct qcomtee_mem_object *mem_object = to_qcomtee_mem_object(object);
|
||||
|
||||
/* Matching get is in qcomtee_memobj_param_to_object(). */
|
||||
tee_shm_put(mem_object->shm);
|
||||
kfree(mem_object);
|
||||
}
|
||||
|
||||
static struct qcomtee_object_operations qcomtee_mem_object_ops = {
|
||||
.release = qcomtee_mem_object_release,
|
||||
.dispatch = qcomtee_mem_object_dispatch,
|
||||
};
|
||||
|
||||
/**
|
||||
* qcomtee_memobj_param_to_object() - OBJREF parameter to &struct qcomtee_object.
|
||||
* @object: object returned.
|
||||
* @param: TEE parameter.
|
||||
* @ctx: context in which the conversion should happen.
|
||||
*
|
||||
* @param is an OBJREF with %QCOMTEE_OBJREF_FLAG_MEM flags.
|
||||
*
|
||||
* Return: On success return 0 or <0 on failure.
|
||||
*/
|
||||
int qcomtee_memobj_param_to_object(struct qcomtee_object **object,
|
||||
struct tee_param *param,
|
||||
struct tee_context *ctx)
|
||||
{
|
||||
struct qcomtee_mem_object *mem_object __free(kfree) = NULL;
|
||||
struct tee_shm *shm;
|
||||
int err;
|
||||
|
||||
mem_object = kzalloc(sizeof(*mem_object), GFP_KERNEL);
|
||||
if (!mem_object)
|
||||
return -ENOMEM;
|
||||
|
||||
shm = tee_shm_get_from_id(ctx, param->u.objref.id);
|
||||
if (IS_ERR(shm))
|
||||
return PTR_ERR(shm);
|
||||
|
||||
/* mem-object wrapping the memref. */
|
||||
err = qcomtee_object_user_init(&mem_object->object,
|
||||
QCOMTEE_OBJECT_TYPE_CB,
|
||||
&qcomtee_mem_object_ops, "tee-shm-%d",
|
||||
shm->id);
|
||||
if (err) {
|
||||
tee_shm_put(shm);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
mem_object->paddr = shm->paddr;
|
||||
mem_object->size = shm->size;
|
||||
mem_object->shm = shm;
|
||||
|
||||
*object = &no_free_ptr(mem_object)->object;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Reverse what qcomtee_memobj_param_to_object() does. */
|
||||
int qcomtee_memobj_param_from_object(struct tee_param *param,
|
||||
struct qcomtee_object *object,
|
||||
struct tee_context *ctx)
|
||||
{
|
||||
struct qcomtee_mem_object *mem_object;
|
||||
|
||||
mem_object = to_qcomtee_mem_object(object);
|
||||
/* Sure if the memobj is in a same context it is originated from. */
|
||||
if (mem_object->shm->ctx != ctx)
|
||||
return -EINVAL;
|
||||
|
||||
param->u.objref.id = mem_object->shm->id;
|
||||
param->u.objref.flags = QCOMTEE_OBJREF_FLAG_MEM;
|
||||
|
||||
/* Passing shm->id to userspace; drop the reference. */
|
||||
qcomtee_object_put(object);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* qcomtee_mem_object_map() - Map a memory object.
|
||||
* @object: memory object.
|
||||
* @map_object: created mapping object.
|
||||
* @mem_paddr: physical address of the memory.
|
||||
* @mem_size: size of the memory.
|
||||
* @perms: QTEE access permissions.
|
||||
*
|
||||
* Return: On success return 0 or <0 on failure.
|
||||
*/
|
||||
int qcomtee_mem_object_map(struct qcomtee_object *object,
|
||||
struct qcomtee_object **map_object, u64 *mem_paddr,
|
||||
u64 *mem_size, u32 *perms)
|
||||
{
|
||||
struct qcomtee_mem_object *mem_object = to_qcomtee_mem_object(object);
|
||||
|
||||
/* Reuses the memory object as a mapping object by re-sharing it. */
|
||||
qcomtee_object_get(&mem_object->object);
|
||||
|
||||
*map_object = &mem_object->object;
|
||||
*mem_paddr = mem_object->paddr;
|
||||
*mem_size = mem_object->size;
|
||||
*perms = QCOM_SCM_PERM_RW;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include "qcomtee.h"
|
||||
|
||||
/**
|
||||
* DOC: Primordial Object
|
||||
*
|
||||
* After boot, the kernel provides a static object of type
|
||||
* %QCOMTEE_OBJECT_TYPE_CB called the primordial object. This object is used
|
||||
* for native kernel services or privileged operations.
|
||||
*
|
||||
* We support:
|
||||
* - %QCOMTEE_OBJECT_OP_MAP_REGION to map a memory object and return mapping
|
||||
* object and mapping information (see qcomtee_mem_object_map()).
|
||||
* - %QCOMTEE_OBJECT_OP_YIELD to yield by the thread running in QTEE.
|
||||
* - %QCOMTEE_OBJECT_OP_SLEEP to wait for a period of time.
|
||||
*/
|
||||
|
||||
#define QCOMTEE_OBJECT_OP_MAP_REGION 0
|
||||
#define QCOMTEE_OBJECT_OP_YIELD 1
|
||||
#define QCOMTEE_OBJECT_OP_SLEEP 2
|
||||
|
||||
/* Mapping information format as expected by QTEE. */
|
||||
struct qcomtee_mapping_info {
|
||||
u64 paddr;
|
||||
u64 len;
|
||||
u32 perms;
|
||||
} __packed;
|
||||
|
||||
static int
|
||||
qcomtee_primordial_obj_dispatch(struct qcomtee_object_invoke_ctx *oic,
|
||||
struct qcomtee_object *primordial_object_unused,
|
||||
u32 op, struct qcomtee_arg *args)
|
||||
{
|
||||
struct qcomtee_mapping_info *map_info;
|
||||
struct qcomtee_object *mem_object;
|
||||
struct qcomtee_object *map_object;
|
||||
int err = 0;
|
||||
|
||||
switch (op) {
|
||||
case QCOMTEE_OBJECT_OP_YIELD:
|
||||
cond_resched();
|
||||
/* No output object. */
|
||||
oic->data = NULL;
|
||||
|
||||
break;
|
||||
case QCOMTEE_OBJECT_OP_SLEEP:
|
||||
/* Check message format matched QCOMTEE_OBJECT_OP_SLEEP op. */
|
||||
if (qcomtee_args_len(args) != 1 ||
|
||||
args[0].type != QCOMTEE_ARG_TYPE_IB ||
|
||||
args[0].b.size < sizeof(u32))
|
||||
return -EINVAL;
|
||||
|
||||
msleep(*(u32 *)(args[0].b.addr));
|
||||
/* No output object. */
|
||||
oic->data = NULL;
|
||||
|
||||
break;
|
||||
case QCOMTEE_OBJECT_OP_MAP_REGION:
|
||||
if (qcomtee_args_len(args) != 3 ||
|
||||
args[0].type != QCOMTEE_ARG_TYPE_OB ||
|
||||
args[1].type != QCOMTEE_ARG_TYPE_IO ||
|
||||
args[2].type != QCOMTEE_ARG_TYPE_OO ||
|
||||
args[0].b.size < sizeof(struct qcomtee_mapping_info))
|
||||
return -EINVAL;
|
||||
|
||||
map_info = args[0].b.addr;
|
||||
mem_object = args[1].o;
|
||||
|
||||
qcomtee_mem_object_map(mem_object, &map_object,
|
||||
&map_info->paddr, &map_info->len,
|
||||
&map_info->perms);
|
||||
|
||||
args[2].o = map_object;
|
||||
/* One output object; pass it for cleanup to notify. */
|
||||
oic->data = map_object;
|
||||
|
||||
qcomtee_object_put(mem_object);
|
||||
|
||||
break;
|
||||
default:
|
||||
err = -EINVAL;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Called after submitting the callback response. */
|
||||
static void qcomtee_primordial_obj_notify(struct qcomtee_object_invoke_ctx *oic,
|
||||
struct qcomtee_object *unused,
|
||||
int err)
|
||||
{
|
||||
struct qcomtee_object *object = oic->data;
|
||||
|
||||
/* If err, QTEE did not obtain mapping object. Drop it. */
|
||||
if (object && err)
|
||||
qcomtee_object_put(object);
|
||||
}
|
||||
|
||||
static struct qcomtee_object_operations qcomtee_primordial_obj_ops = {
|
||||
.dispatch = qcomtee_primordial_obj_dispatch,
|
||||
.notify = qcomtee_primordial_obj_notify,
|
||||
};
|
||||
|
||||
struct qcomtee_object qcomtee_primordial_object = {
|
||||
.name = "primordial",
|
||||
.object_type = QCOMTEE_OBJECT_TYPE_CB,
|
||||
.ops = &qcomtee_primordial_obj_ops
|
||||
};
|
||||
@@ -0,0 +1,185 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
|
||||
*/
|
||||
|
||||
#ifndef QCOMTEE_H
|
||||
#define QCOMTEE_H
|
||||
|
||||
#include <linux/kobject.h>
|
||||
#include <linux/tee_core.h>
|
||||
|
||||
#include "qcomtee_msg.h"
|
||||
#include "qcomtee_object.h"
|
||||
|
||||
/* Flags relating to object reference. */
|
||||
#define QCOMTEE_OBJREF_FLAG_TEE BIT(0)
|
||||
#define QCOMTEE_OBJREF_FLAG_USER BIT(1)
|
||||
#define QCOMTEE_OBJREF_FLAG_MEM BIT(2)
|
||||
|
||||
/**
|
||||
* struct qcomtee - Main service struct.
|
||||
* @teedev: client device.
|
||||
* @pool: shared memory pool.
|
||||
* @ctx: driver private context.
|
||||
* @oic: context to use for the current driver invocation.
|
||||
* @wq: workqueue for QTEE async operations.
|
||||
* @xa_local_objects: array of objects exported to QTEE.
|
||||
* @xa_last_id: next ID to allocate.
|
||||
* @qtee_version: QTEE version.
|
||||
*/
|
||||
struct qcomtee {
|
||||
struct tee_device *teedev;
|
||||
struct tee_shm_pool *pool;
|
||||
struct tee_context *ctx;
|
||||
struct qcomtee_object_invoke_ctx oic;
|
||||
struct workqueue_struct *wq;
|
||||
struct xarray xa_local_objects;
|
||||
u32 xa_last_id;
|
||||
u32 qtee_version;
|
||||
};
|
||||
|
||||
void qcomtee_fetch_async_reqs(struct qcomtee_object_invoke_ctx *oic);
|
||||
struct qcomtee_object *qcomtee_idx_erase(struct qcomtee_object_invoke_ctx *oic,
|
||||
u32 idx);
|
||||
|
||||
struct tee_shm_pool *qcomtee_shm_pool_alloc(void);
|
||||
void qcomtee_msg_buffers_free(struct qcomtee_object_invoke_ctx *oic);
|
||||
int qcomtee_msg_buffers_alloc(struct qcomtee_object_invoke_ctx *oic,
|
||||
struct qcomtee_arg *u);
|
||||
|
||||
/**
|
||||
* qcomtee_object_do_invoke_internal() - Submit an invocation for an object.
|
||||
* @oic: context to use for the current invocation.
|
||||
* @object: object being invoked.
|
||||
* @op: requested operation on the object.
|
||||
* @u: array of arguments for the current invocation.
|
||||
* @result: result returned from QTEE.
|
||||
*
|
||||
* The caller is responsible for keeping track of the refcount for each
|
||||
* object, including @object. On return, the caller loses ownership of all
|
||||
* input objects of type %QCOMTEE_OBJECT_TYPE_CB.
|
||||
*
|
||||
* Return: On success, returns 0; on failure, returns < 0.
|
||||
*/
|
||||
int qcomtee_object_do_invoke_internal(struct qcomtee_object_invoke_ctx *oic,
|
||||
struct qcomtee_object *object, u32 op,
|
||||
struct qcomtee_arg *u, int *result);
|
||||
|
||||
/**
|
||||
* struct qcomtee_context_data - Clients' or supplicants' context.
|
||||
* @qtee_objects_idr: QTEE objects in this context.
|
||||
* @qtee_lock: mutex for @qtee_objects_idr.
|
||||
* @reqs_idr: requests in this context that hold ID.
|
||||
* @reqs_list: FIFO for requests in PROCESSING or QUEUED state.
|
||||
* @reqs_lock: mutex for @reqs_idr, @reqs_list and request states.
|
||||
* @req_c: completion used when the supplicant is waiting for requests.
|
||||
* @released: state of this context.
|
||||
*/
|
||||
struct qcomtee_context_data {
|
||||
struct idr qtee_objects_idr;
|
||||
/* Synchronize access to @qtee_objects_idr. */
|
||||
struct mutex qtee_lock;
|
||||
|
||||
struct idr reqs_idr;
|
||||
struct list_head reqs_list;
|
||||
/* Synchronize access to @reqs_idr, @reqs_list and updating requests states. */
|
||||
struct mutex reqs_lock;
|
||||
|
||||
struct completion req_c;
|
||||
|
||||
bool released;
|
||||
};
|
||||
|
||||
int qcomtee_context_add_qtee_object(struct tee_param *param,
|
||||
struct qcomtee_object *object,
|
||||
struct tee_context *ctx);
|
||||
int qcomtee_context_find_qtee_object(struct qcomtee_object **object,
|
||||
struct tee_param *param,
|
||||
struct tee_context *ctx);
|
||||
void qcomtee_context_del_qtee_object(struct tee_param *param,
|
||||
struct tee_context *ctx);
|
||||
|
||||
int qcomtee_objref_to_arg(struct qcomtee_arg *arg, struct tee_param *param,
|
||||
struct tee_context *ctx);
|
||||
int qcomtee_objref_from_arg(struct tee_param *param, struct qcomtee_arg *arg,
|
||||
struct tee_context *ctx);
|
||||
|
||||
/* OBJECTS: */
|
||||
|
||||
/* (1) User Object API. */
|
||||
|
||||
int is_qcomtee_user_object(struct qcomtee_object *object);
|
||||
void qcomtee_user_object_set_notify(struct qcomtee_object *object, bool notify);
|
||||
void qcomtee_requests_destroy(struct qcomtee_context_data *ctxdata);
|
||||
int qcomtee_user_param_to_object(struct qcomtee_object **object,
|
||||
struct tee_param *param,
|
||||
struct tee_context *ctx);
|
||||
int qcomtee_user_param_from_object(struct tee_param *param,
|
||||
struct qcomtee_object *object,
|
||||
struct tee_context *ctx);
|
||||
|
||||
/**
|
||||
* struct qcomtee_user_object_request_data - Data for user object request.
|
||||
* @id: ID assigned to the request.
|
||||
* @object_id: Object ID being invoked by QTEE.
|
||||
* @op: Requested operation on object.
|
||||
* @np: Number of parameters in the request.
|
||||
*/
|
||||
struct qcomtee_user_object_request_data {
|
||||
int id;
|
||||
u64 object_id;
|
||||
u32 op;
|
||||
int np;
|
||||
};
|
||||
|
||||
int qcomtee_user_object_select(struct tee_context *ctx,
|
||||
struct tee_param *params, int num_params,
|
||||
void __user *uaddr, size_t size,
|
||||
struct qcomtee_user_object_request_data *data);
|
||||
int qcomtee_user_object_submit(struct tee_context *ctx,
|
||||
struct tee_param *params, int num_params,
|
||||
int req_id, int errno);
|
||||
|
||||
/* (2) Primordial Object. */
|
||||
extern struct qcomtee_object qcomtee_primordial_object;
|
||||
|
||||
/* (3) Memory Object API. */
|
||||
|
||||
/* Is it a memory object using tee_shm? */
|
||||
int is_qcomtee_memobj_object(struct qcomtee_object *object);
|
||||
|
||||
/**
|
||||
* qcomtee_memobj_param_to_object() - OBJREF parameter to &struct qcomtee_object.
|
||||
* @object: object returned.
|
||||
* @param: TEE parameter.
|
||||
* @ctx: context in which the conversion should happen.
|
||||
*
|
||||
* @param is an OBJREF with %QCOMTEE_OBJREF_FLAG_MEM flags.
|
||||
*
|
||||
* Return: On success return 0 or <0 on failure.
|
||||
*/
|
||||
int qcomtee_memobj_param_to_object(struct qcomtee_object **object,
|
||||
struct tee_param *param,
|
||||
struct tee_context *ctx);
|
||||
|
||||
/* Reverse what qcomtee_memobj_param_to_object() does. */
|
||||
int qcomtee_memobj_param_from_object(struct tee_param *param,
|
||||
struct qcomtee_object *object,
|
||||
struct tee_context *ctx);
|
||||
|
||||
/**
|
||||
* qcomtee_mem_object_map() - Map a memory object.
|
||||
* @object: memory object.
|
||||
* @map_object: created mapping object.
|
||||
* @mem_paddr: physical address of the memory.
|
||||
* @mem_size: size of the memory.
|
||||
* @perms: QTEE access permissions.
|
||||
*
|
||||
* Return: On success return 0 or <0 on failure.
|
||||
*/
|
||||
int qcomtee_mem_object_map(struct qcomtee_object *object,
|
||||
struct qcomtee_object **map_object, u64 *mem_paddr,
|
||||
u64 *mem_size, u32 *perms);
|
||||
|
||||
#endif /* QCOMTEE_H */
|
||||
@@ -0,0 +1,304 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
|
||||
*/
|
||||
|
||||
#ifndef QCOMTEE_MSG_H
|
||||
#define QCOMTEE_MSG_H
|
||||
|
||||
#include <linux/bitfield.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 the kernel; i.e., a callback
|
||||
* request.
|
||||
*
|
||||
* The unused tail of the outbound buffer is also used for sending and
|
||||
* receiving asynchronous messages. An asynchronous message is independent of
|
||||
* the current object invocation (i.e., contents of the inbound buffer) or
|
||||
* callback request (i.e., the head of the outbound buffer); see
|
||||
* qcomtee_get_async_buffer(). It is used by endpoints (QTEE or kernel) as an
|
||||
* optimization to reduce the number of context switches between the secure and
|
||||
* non-secure worlds.
|
||||
*
|
||||
* For instance, QTEE never sends an explicit callback request to release an
|
||||
* object in the kernel. Instead, it sends asynchronous release messages in the
|
||||
* outbound buffer when QTEE returns from the previous direct object invocation,
|
||||
* or appends 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 the kernel.
|
||||
* An object ID is an index to the object table in QTEE.
|
||||
*
|
||||
* For the direct object invocation message format in the inbound buffer, see
|
||||
* &struct qcomtee_msg_object_invoke. For the callback request message format
|
||||
* in the outbound buffer, see &struct qcomtee_msg_callback. For the message
|
||||
* format for asynchronous messages in the outbound buffer, see
|
||||
* &struct qcomtee_async_msg_hdr.
|
||||
*/
|
||||
|
||||
/**
|
||||
* define QCOMTEE_MSG_OBJECT_NS_BIT - Non-secure bit
|
||||
*
|
||||
* Object ID is a globally unique 32-bit number. IDs referencing objects
|
||||
* in the kernel should have %QCOMTEE_MSG_OBJECT_NS_BIT set.
|
||||
*/
|
||||
#define QCOMTEE_MSG_OBJECT_NS_BIT BIT(31)
|
||||
|
||||
/* Static object IDs recognized by QTEE. */
|
||||
#define QCOMTEE_MSG_OBJECT_NULL (0U)
|
||||
#define QCOMTEE_MSG_OBJECT_ROOT (1U)
|
||||
|
||||
/* Definitions from QTEE as part of the transport protocol. */
|
||||
|
||||
/* qcomtee_msg_arg is an argument as recognized by QTEE. */
|
||||
union qcomtee_msg_arg {
|
||||
struct {
|
||||
u32 offset;
|
||||
u32 size;
|
||||
} b;
|
||||
u32 o;
|
||||
};
|
||||
|
||||
/* BI and BO payloads in QTEE messages should be at 64-bit boundaries. */
|
||||
#define qcomtee_msg_offset_align(o) ALIGN((o), sizeof(u64))
|
||||
|
||||
/* Operations for objects are 32-bit. Transport uses the upper 16 bits. */
|
||||
#define QCOMTEE_MSG_OBJECT_OP_MASK GENMASK(15, 0)
|
||||
|
||||
/* Reserved Operation IDs sent to QTEE: */
|
||||
/* QCOMTEE_MSG_OBJECT_OP_RELEASE - Reduces the refcount and releases the object.
|
||||
* QCOMTEE_MSG_OBJECT_OP_RETAIN - Increases the refcount.
|
||||
*
|
||||
* These operation IDs are valid for all objects.
|
||||
*/
|
||||
|
||||
#define QCOMTEE_MSG_OBJECT_OP_RELEASE (QCOMTEE_MSG_OBJECT_OP_MASK - 0)
|
||||
#define QCOMTEE_MSG_OBJECT_OP_RETAIN (QCOMTEE_MSG_OBJECT_OP_MASK - 1)
|
||||
|
||||
/* Subset of operations supported by QTEE root object. */
|
||||
|
||||
#define QCOMTEE_ROOT_OP_REG_WITH_CREDENTIALS 5
|
||||
#define QCOMTEE_ROOT_OP_NOTIFY_DOMAIN_CHANGE 4
|
||||
#define QCOMTEE_ROOT_OP_ADCI_ACCEPT 8
|
||||
#define QCOMTEE_ROOT_OP_ADCI_SHUTDOWN 9
|
||||
|
||||
/* Subset of operations supported by client_env object. */
|
||||
|
||||
#define QCOMTEE_CLIENT_ENV_OPEN 0
|
||||
|
||||
/* List of available QTEE service UIDs and subset of operations. */
|
||||
|
||||
#define QCOMTEE_FEATURE_VER_UID 2033
|
||||
#define QCOMTEE_FEATURE_VER_OP_GET 0
|
||||
/* Get QTEE version number. */
|
||||
#define QCOMTEE_FEATURE_VER_OP_GET_QTEE_ID 10
|
||||
#define QTEE_VERSION_GET_MAJOR(x) (((x) >> 22) & 0xffU)
|
||||
#define QTEE_VERSION_GET_MINOR(x) (((x) >> 12) & 0xffU)
|
||||
#define QTEE_VERSION_GET_PATCH(x) ((x) >> 0 & 0xfffU)
|
||||
|
||||
/* Response types as returned from qcomtee_object_invoke_ctx_invoke(). */
|
||||
|
||||
/* The message contains a callback request. */
|
||||
#define QCOMTEE_RESULT_INBOUND_REQ_NEEDED 3
|
||||
|
||||
/**
|
||||
* struct qcomtee_msg_object_invoke - Direct object invocation message.
|
||||
* @ctx: object ID hosted in QTEE.
|
||||
* @op: operation for the object.
|
||||
* @counts: number of different types of arguments in @args.
|
||||
* @args: array of arguments.
|
||||
*
|
||||
* @counts consists of 4 * 4-bit fields. Bits 0 - 3 represent the number of
|
||||
* input buffers, bits 4 - 7 represent the number of output buffers,
|
||||
* bits 8 - 11 represent the number of input objects, and bits 12 - 15
|
||||
* represent the number of output objects. The remaining bits should be zero.
|
||||
*
|
||||
* 15 12 11 8 7 4 3 0
|
||||
* +----------------+----------------+----------------+----------------+
|
||||
* | #OO objects | #IO objects | #OB buffers | #IB buffers |
|
||||
* +----------------+----------------+----------------+----------------+
|
||||
*
|
||||
* The maximum number of arguments of each type is defined by
|
||||
* %QCOMTEE_ARGS_PER_TYPE.
|
||||
*/
|
||||
struct qcomtee_msg_object_invoke {
|
||||
u32 cxt;
|
||||
u32 op;
|
||||
u32 counts;
|
||||
union qcomtee_msg_arg args[];
|
||||
};
|
||||
|
||||
/* Bit masks for the four 4-bit nibbles holding the counts. */
|
||||
#define QCOMTEE_MASK_IB GENMASK(3, 0)
|
||||
#define QCOMTEE_MASK_OB GENMASK(7, 4)
|
||||
#define QCOMTEE_MASK_IO GENMASK(11, 8)
|
||||
#define QCOMTEE_MASK_OO GENMASK(15, 12)
|
||||
|
||||
/**
|
||||
* struct qcomtee_msg_callback - Callback request message.
|
||||
* @result: result of operation @op on the object referenced by @cxt.
|
||||
* @cxt: object ID hosted in the kernel.
|
||||
* @op: operation for the object.
|
||||
* @counts: number of different types of arguments in @args.
|
||||
* @args: array of arguments.
|
||||
*
|
||||
* For details of @counts, see &qcomtee_msg_object_invoke.counts.
|
||||
*/
|
||||
struct qcomtee_msg_callback {
|
||||
u32 result;
|
||||
u32 cxt;
|
||||
u32 op;
|
||||
u32 counts;
|
||||
union qcomtee_msg_arg args[];
|
||||
};
|
||||
|
||||
/* Offset in the message for the beginning of the buffer argument's contents. */
|
||||
#define qcomtee_msg_buffer_args(t, n) \
|
||||
qcomtee_msg_offset_align(struct_size_t(t, args, n))
|
||||
/* Pointer to the beginning of a buffer argument's content at an offset. */
|
||||
#define qcomtee_msg_offset_to_ptr(m, off) ((void *)&((char *)(m))[(off)])
|
||||
|
||||
/* Some helpers to manage msg.counts. */
|
||||
|
||||
static inline unsigned int qcomtee_msg_num_ib(u32 counts)
|
||||
{
|
||||
return FIELD_GET(QCOMTEE_MASK_IB, counts);
|
||||
}
|
||||
|
||||
static inline unsigned int qcomtee_msg_num_ob(u32 counts)
|
||||
{
|
||||
return FIELD_GET(QCOMTEE_MASK_OB, counts);
|
||||
}
|
||||
|
||||
static inline unsigned int qcomtee_msg_num_io(u32 counts)
|
||||
{
|
||||
return FIELD_GET(QCOMTEE_MASK_IO, counts);
|
||||
}
|
||||
|
||||
static inline unsigned int qcomtee_msg_num_oo(u32 counts)
|
||||
{
|
||||
return FIELD_GET(QCOMTEE_MASK_OO, counts);
|
||||
}
|
||||
|
||||
static inline unsigned int qcomtee_msg_idx_ib(u32 counts)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline unsigned int qcomtee_msg_idx_ob(u32 counts)
|
||||
{
|
||||
return qcomtee_msg_num_ib(counts);
|
||||
}
|
||||
|
||||
static inline unsigned int qcomtee_msg_idx_io(u32 counts)
|
||||
{
|
||||
return qcomtee_msg_idx_ob(counts) + qcomtee_msg_num_ob(counts);
|
||||
}
|
||||
|
||||
static inline unsigned int qcomtee_msg_idx_oo(u32 counts)
|
||||
{
|
||||
return qcomtee_msg_idx_io(counts) + qcomtee_msg_num_io(counts);
|
||||
}
|
||||
|
||||
#define qcomtee_msg_for_each(i, first, num) \
|
||||
for ((i) = (first); (i) < (first) + (num); (i)++)
|
||||
|
||||
#define qcomtee_msg_for_each_input_buffer(i, m) \
|
||||
qcomtee_msg_for_each(i, qcomtee_msg_idx_ib((m)->counts), \
|
||||
qcomtee_msg_num_ib((m)->counts))
|
||||
|
||||
#define qcomtee_msg_for_each_output_buffer(i, m) \
|
||||
qcomtee_msg_for_each(i, qcomtee_msg_idx_ob((m)->counts), \
|
||||
qcomtee_msg_num_ob((m)->counts))
|
||||
|
||||
#define qcomtee_msg_for_each_input_object(i, m) \
|
||||
qcomtee_msg_for_each(i, qcomtee_msg_idx_io((m)->counts), \
|
||||
qcomtee_msg_num_io((m)->counts))
|
||||
|
||||
#define qcomtee_msg_for_each_output_object(i, m) \
|
||||
qcomtee_msg_for_each(i, qcomtee_msg_idx_oo((m)->counts), \
|
||||
qcomtee_msg_num_oo((m)->counts))
|
||||
|
||||
/* Sum of arguments in a message. */
|
||||
#define qcomtee_msg_args(m) \
|
||||
(qcomtee_msg_idx_oo((m)->counts) + qcomtee_msg_num_oo((m)->counts))
|
||||
|
||||
static inline void qcomtee_msg_init(struct qcomtee_msg_object_invoke *msg,
|
||||
u32 cxt, u32 op, int in_buffer,
|
||||
int out_buffer, int in_object,
|
||||
int out_object)
|
||||
{
|
||||
u32 counts = 0;
|
||||
|
||||
counts |= (in_buffer & 0xfU);
|
||||
counts |= ((out_buffer - in_buffer) & 0xfU) << 4;
|
||||
counts |= ((in_object - out_buffer) & 0xfU) << 8;
|
||||
counts |= ((out_object - in_object) & 0xfU) << 12;
|
||||
|
||||
msg->cxt = cxt;
|
||||
msg->op = op;
|
||||
msg->counts = counts;
|
||||
}
|
||||
|
||||
/* Generic error codes. */
|
||||
#define QCOMTEE_MSG_OK 0 /* non-specific success code. */
|
||||
#define QCOMTEE_MSG_ERROR 1 /* non-specific error. */
|
||||
#define QCOMTEE_MSG_ERROR_INVALID 2 /* unsupported/unrecognized request. */
|
||||
#define QCOMTEE_MSG_ERROR_SIZE_IN 3 /* supplied buffer/string too large. */
|
||||
#define QCOMTEE_MSG_ERROR_SIZE_OUT 4 /* supplied output buffer too small. */
|
||||
#define QCOMTEE_MSG_ERROR_USERBASE 10 /* start of user-defined error range. */
|
||||
|
||||
/* Transport layer error codes. */
|
||||
#define QCOMTEE_MSG_ERROR_DEFUNCT -90 /* object no longer exists. */
|
||||
#define QCOMTEE_MSG_ERROR_ABORT -91 /* calling thread must exit. */
|
||||
#define QCOMTEE_MSG_ERROR_BADOBJ -92 /* invalid object context. */
|
||||
#define QCOMTEE_MSG_ERROR_NOSLOTS -93 /* caller's object table full. */
|
||||
#define QCOMTEE_MSG_ERROR_MAXARGS -94 /* too many args. */
|
||||
#define QCOMTEE_MSG_ERROR_MAXDATA -95 /* buffers too large. */
|
||||
#define QCOMTEE_MSG_ERROR_UNAVAIL -96 /* the request could not be processed. */
|
||||
#define QCOMTEE_MSG_ERROR_KMEM -97 /* kernel out of memory. */
|
||||
#define QCOMTEE_MSG_ERROR_REMOTE -98 /* local method sent to remote object. */
|
||||
#define QCOMTEE_MSG_ERROR_BUSY -99 /* Object is busy. */
|
||||
#define QCOMTEE_MSG_ERROR_TIMEOUT -103 /* Call Back Object invocation timed out. */
|
||||
|
||||
static inline void qcomtee_msg_set_result(struct qcomtee_msg_callback *cb_msg,
|
||||
int err)
|
||||
{
|
||||
if (!err) {
|
||||
cb_msg->result = QCOMTEE_MSG_OK;
|
||||
} else if (err < 0) {
|
||||
/* If err < 0, then it is a transport error. */
|
||||
switch (err) {
|
||||
case -ENOMEM:
|
||||
cb_msg->result = QCOMTEE_MSG_ERROR_KMEM;
|
||||
break;
|
||||
case -ENODEV:
|
||||
cb_msg->result = QCOMTEE_MSG_ERROR_DEFUNCT;
|
||||
break;
|
||||
case -ENOSPC:
|
||||
case -EBUSY:
|
||||
cb_msg->result = QCOMTEE_MSG_ERROR_BUSY;
|
||||
break;
|
||||
case -EBADF:
|
||||
case -EINVAL:
|
||||
cb_msg->result = QCOMTEE_MSG_ERROR_UNAVAIL;
|
||||
break;
|
||||
default:
|
||||
cb_msg->result = QCOMTEE_MSG_ERROR;
|
||||
}
|
||||
} else {
|
||||
/* If err > 0, then it is user defined error, pass it as is. */
|
||||
cb_msg->result = err;
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* QCOMTEE_MSG_H */
|
||||
@@ -0,0 +1,316 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
|
||||
*/
|
||||
|
||||
#ifndef QCOMTEE_OBJECT_H
|
||||
#define QCOMTEE_OBJECT_H
|
||||
|
||||
#include <linux/completion.h>
|
||||
#include <linux/kref.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
struct qcomtee_object;
|
||||
|
||||
/**
|
||||
* DOC: Overview
|
||||
*
|
||||
* qcomtee_object provides object refcounting, ID allocation for objects hosted
|
||||
* in the kernel, and necessary message marshaling for Qualcomm TEE (QTEE).
|
||||
*
|
||||
* To invoke an object in QTEE, the user calls qcomtee_object_do_invoke()
|
||||
* while passing an instance of &struct qcomtee_object and the requested
|
||||
* operation + arguments.
|
||||
*
|
||||
* After boot, QTEE provides a static object %ROOT_QCOMTEE_OBJECT (type of
|
||||
* %QCOMTEE_OBJECT_TYPE_ROOT). The root object is invoked to pass the user's
|
||||
* credentials and obtain other instances of &struct qcomtee_object (type of
|
||||
* %QCOMTEE_OBJECT_TYPE_TEE) that represent services and TAs in QTEE;
|
||||
* see &enum qcomtee_object_type.
|
||||
*
|
||||
* The objects received from QTEE are refcounted. So the owner of these objects
|
||||
* can issue qcomtee_object_get() to increase the refcount and pass objects
|
||||
* to other clients, or issue qcomtee_object_put() to decrease the refcount
|
||||
* and release the resources in QTEE.
|
||||
*
|
||||
* The kernel can host services accessible to QTEE. A driver should embed
|
||||
* an instance of &struct qcomtee_object in the struct it wants to export to
|
||||
* QTEE (this is called a callback object). It issues qcomtee_object_user_init()
|
||||
* to set the dispatch() operation for the callback object and set its type
|
||||
* to %QCOMTEE_OBJECT_TYPE_CB.
|
||||
*
|
||||
* 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 invokes a callback object in the kernel, the dispatch() operation is
|
||||
* called in the context of the thread that originally called
|
||||
* qcomtee_object_do_invoke().
|
||||
*/
|
||||
|
||||
/**
|
||||
* enum qcomtee_object_type - Object types.
|
||||
* @QCOMTEE_OBJECT_TYPE_TEE: object hosted on QTEE.
|
||||
* @QCOMTEE_OBJECT_TYPE_CB: object hosted on kernel.
|
||||
* @QCOMTEE_OBJECT_TYPE_ROOT: 'primordial' object.
|
||||
* @QCOMTEE_OBJECT_TYPE_NULL: NULL object.
|
||||
*
|
||||
* The primordial object is used for bootstrapping the IPC connection between
|
||||
* the kernel and QTEE. It is invoked by the kernel when it wants to get a
|
||||
* 'client env'.
|
||||
*/
|
||||
enum qcomtee_object_type {
|
||||
QCOMTEE_OBJECT_TYPE_TEE,
|
||||
QCOMTEE_OBJECT_TYPE_CB,
|
||||
QCOMTEE_OBJECT_TYPE_ROOT,
|
||||
QCOMTEE_OBJECT_TYPE_NULL,
|
||||
};
|
||||
|
||||
/**
|
||||
* enum qcomtee_arg_type - Type of QTEE argument.
|
||||
* @QCOMTEE_ARG_TYPE_INV: invalid type.
|
||||
* @QCOMTEE_ARG_TYPE_OB: output buffer (OB).
|
||||
* @QCOMTEE_ARG_TYPE_OO: output object (OO).
|
||||
* @QCOMTEE_ARG_TYPE_IB: input buffer (IB).
|
||||
* @QCOMTEE_ARG_TYPE_IO: input object (IO).
|
||||
*
|
||||
* Use the invalid type to specify the end of the argument array.
|
||||
*/
|
||||
enum qcomtee_arg_type {
|
||||
QCOMTEE_ARG_TYPE_INV = 0,
|
||||
QCOMTEE_ARG_TYPE_OB,
|
||||
QCOMTEE_ARG_TYPE_OO,
|
||||
QCOMTEE_ARG_TYPE_IB,
|
||||
QCOMTEE_ARG_TYPE_IO,
|
||||
QCOMTEE_ARG_TYPE_NR,
|
||||
};
|
||||
|
||||
/**
|
||||
* define QCOMTEE_ARGS_PER_TYPE - Maximum arguments of a specific type.
|
||||
*
|
||||
* The QTEE transport protocol limits the maximum number of arguments of
|
||||
* a specific type (i.e., IB, OB, IO, and OO).
|
||||
*/
|
||||
#define QCOMTEE_ARGS_PER_TYPE 16
|
||||
|
||||
/* Maximum arguments that can fit in a QTEE message, ignoring the type. */
|
||||
#define QCOMTEE_ARGS_MAX (QCOMTEE_ARGS_PER_TYPE * (QCOMTEE_ARG_TYPE_NR - 1))
|
||||
|
||||
struct qcomtee_buffer {
|
||||
union {
|
||||
void *addr;
|
||||
void __user *uaddr;
|
||||
};
|
||||
size_t size;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct qcomtee_arg - Argument for QTEE object invocation.
|
||||
* @type: type of argument as &enum qcomtee_arg_type.
|
||||
* @flags: extra flags.
|
||||
* @b: address and size if the type of argument is a buffer.
|
||||
* @o: object instance if the type of argument is an object.
|
||||
*
|
||||
* &qcomtee_arg.flags only accepts %QCOMTEE_ARG_FLAGS_UADDR for now, which
|
||||
* states that &qcomtee_arg.b contains a userspace address in uaddr.
|
||||
*/
|
||||
struct qcomtee_arg {
|
||||
enum qcomtee_arg_type type;
|
||||
/* 'b.uaddr' holds a __user address. */
|
||||
#define QCOMTEE_ARG_FLAGS_UADDR BIT(0)
|
||||
unsigned int flags;
|
||||
union {
|
||||
struct qcomtee_buffer b;
|
||||
struct qcomtee_object *o;
|
||||
};
|
||||
};
|
||||
|
||||
static inline int qcomtee_args_len(struct qcomtee_arg *args)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
while (args[i].type != QCOMTEE_ARG_TYPE_INV)
|
||||
i++;
|
||||
return i;
|
||||
}
|
||||
|
||||
/* Context is busy (callback is in progress). */
|
||||
#define QCOMTEE_OIC_FLAG_BUSY BIT(1)
|
||||
/* Context needs to notify the current object. */
|
||||
#define QCOMTEE_OIC_FLAG_NOTIFY BIT(2)
|
||||
/* Context has shared state with QTEE. */
|
||||
#define QCOMTEE_OIC_FLAG_SHARED BIT(3)
|
||||
|
||||
/**
|
||||
* struct qcomtee_object_invoke_ctx - QTEE context for object invocation.
|
||||
* @ctx: TEE context for this invocation.
|
||||
* @flags: flags for the invocation context.
|
||||
* @errno: error code for the invocation.
|
||||
* @object: current object invoked in this callback context.
|
||||
* @u: array of arguments for the current invocation (+1 for ending arg).
|
||||
* @in_msg: inbound buffer shared with QTEE.
|
||||
* @out_msg: outbound buffer shared with QTEE.
|
||||
* @in_shm: TEE shm allocated for inbound buffer.
|
||||
* @out_shm: TEE shm allocated for outbound buffer.
|
||||
* @data: extra data attached to this context.
|
||||
*/
|
||||
struct qcomtee_object_invoke_ctx {
|
||||
struct tee_context *ctx;
|
||||
unsigned long flags;
|
||||
int errno;
|
||||
|
||||
struct qcomtee_object *object;
|
||||
struct qcomtee_arg u[QCOMTEE_ARGS_MAX + 1];
|
||||
|
||||
struct qcomtee_buffer in_msg;
|
||||
struct qcomtee_buffer out_msg;
|
||||
struct tee_shm *in_shm;
|
||||
struct tee_shm *out_shm;
|
||||
|
||||
void *data;
|
||||
};
|
||||
|
||||
static inline struct qcomtee_object_invoke_ctx *
|
||||
qcomtee_object_invoke_ctx_alloc(struct tee_context *ctx)
|
||||
{
|
||||
struct qcomtee_object_invoke_ctx *oic;
|
||||
|
||||
oic = kzalloc(sizeof(*oic), GFP_KERNEL);
|
||||
if (oic)
|
||||
oic->ctx = ctx;
|
||||
return oic;
|
||||
}
|
||||
|
||||
/**
|
||||
* qcomtee_object_do_invoke() - Submit an invocation for an object.
|
||||
* @oic: context to use for the current invocation.
|
||||
* @object: object being invoked.
|
||||
* @op: requested operation on the object.
|
||||
* @u: array of arguments for the current invocation.
|
||||
* @result: result returned from QTEE.
|
||||
*
|
||||
* The caller is responsible for keeping track of the refcount for each object,
|
||||
* including @object. On return, the caller loses ownership of all input
|
||||
* objects of type %QCOMTEE_OBJECT_TYPE_CB.
|
||||
*
|
||||
* @object can be of %QCOMTEE_OBJECT_TYPE_ROOT or %QCOMTEE_OBJECT_TYPE_TEE.
|
||||
*
|
||||
* Return: On success, returns 0; on failure, returns < 0.
|
||||
*/
|
||||
int qcomtee_object_do_invoke(struct qcomtee_object_invoke_ctx *oic,
|
||||
struct qcomtee_object *object, u32 op,
|
||||
struct qcomtee_arg *u, int *result);
|
||||
|
||||
/**
|
||||
* struct qcomtee_object_operations - Callback object operations.
|
||||
* @release: release the object if QTEE is not using it.
|
||||
* @dispatch: dispatch the operation requested by QTEE.
|
||||
* @notify: report the status of any pending response submitted by @dispatch.
|
||||
*/
|
||||
struct qcomtee_object_operations {
|
||||
void (*release)(struct qcomtee_object *object);
|
||||
int (*dispatch)(struct qcomtee_object_invoke_ctx *oic,
|
||||
struct qcomtee_object *object, u32 op,
|
||||
struct qcomtee_arg *args);
|
||||
void (*notify)(struct qcomtee_object_invoke_ctx *oic,
|
||||
struct qcomtee_object *object, int err);
|
||||
};
|
||||
|
||||
/**
|
||||
* struct qcomtee_object - QTEE or kernel object.
|
||||
* @name: object name.
|
||||
* @refcount: reference counter.
|
||||
* @object_type: object type as &enum qcomtee_object_type.
|
||||
* @info: extra information for the object.
|
||||
* @ops: callback operations for objects of type %QCOMTEE_OBJECT_TYPE_CB.
|
||||
* @work: work for async operations on the object.
|
||||
*
|
||||
* @work is used for releasing objects of %QCOMTEE_OBJECT_TYPE_TEE type.
|
||||
*/
|
||||
struct qcomtee_object {
|
||||
const char *name;
|
||||
struct kref refcount;
|
||||
|
||||
enum qcomtee_object_type object_type;
|
||||
struct object_info {
|
||||
unsigned long qtee_id;
|
||||
/* TEE context for QTEE object async requests. */
|
||||
struct tee_context *qcomtee_async_ctx;
|
||||
} info;
|
||||
|
||||
struct qcomtee_object_operations *ops;
|
||||
struct work_struct work;
|
||||
};
|
||||
|
||||
/* Static instances of qcomtee_object objects. */
|
||||
#define NULL_QCOMTEE_OBJECT ((struct qcomtee_object *)(0))
|
||||
extern struct qcomtee_object qcomtee_object_root;
|
||||
#define ROOT_QCOMTEE_OBJECT (&qcomtee_object_root)
|
||||
|
||||
static inline enum qcomtee_object_type
|
||||
typeof_qcomtee_object(struct qcomtee_object *object)
|
||||
{
|
||||
if (object == NULL_QCOMTEE_OBJECT)
|
||||
return QCOMTEE_OBJECT_TYPE_NULL;
|
||||
return object->object_type;
|
||||
}
|
||||
|
||||
static inline const char *qcomtee_object_name(struct qcomtee_object *object)
|
||||
{
|
||||
if (object == NULL_QCOMTEE_OBJECT)
|
||||
return "null";
|
||||
|
||||
if (!object->name)
|
||||
return "no-name";
|
||||
return object->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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, ...) __printf(4, 5);
|
||||
|
||||
/* Object release is RCU protected. */
|
||||
int qcomtee_object_get(struct qcomtee_object *object);
|
||||
void qcomtee_object_put(struct qcomtee_object *object);
|
||||
|
||||
#define qcomtee_arg_for_each(i, args) \
|
||||
for (i = 0; args[i].type != QCOMTEE_ARG_TYPE_INV; i++)
|
||||
|
||||
/* Next argument of type @type after index @i. */
|
||||
int qcomtee_next_arg_type(struct qcomtee_arg *u, int i,
|
||||
enum qcomtee_arg_type type);
|
||||
|
||||
/* Iterate over argument of given type. */
|
||||
#define qcomtee_arg_for_each_type(i, args, at) \
|
||||
for (i = qcomtee_next_arg_type(args, 0, at); \
|
||||
args[i].type != QCOMTEE_ARG_TYPE_INV; \
|
||||
i = qcomtee_next_arg_type(args, i + 1, at))
|
||||
|
||||
#define qcomtee_arg_for_each_input_buffer(i, args) \
|
||||
qcomtee_arg_for_each_type(i, args, QCOMTEE_ARG_TYPE_IB)
|
||||
#define qcomtee_arg_for_each_output_buffer(i, args) \
|
||||
qcomtee_arg_for_each_type(i, args, QCOMTEE_ARG_TYPE_OB)
|
||||
#define qcomtee_arg_for_each_input_object(i, args) \
|
||||
qcomtee_arg_for_each_type(i, args, QCOMTEE_ARG_TYPE_IO)
|
||||
#define qcomtee_arg_for_each_output_object(i, args) \
|
||||
qcomtee_arg_for_each_type(i, args, QCOMTEE_ARG_TYPE_OO)
|
||||
|
||||
struct qcomtee_object *
|
||||
qcomtee_object_get_client_env(struct qcomtee_object_invoke_ctx *oic);
|
||||
|
||||
struct qcomtee_object *
|
||||
qcomtee_object_get_service(struct qcomtee_object_invoke_ctx *oic,
|
||||
struct qcomtee_object *client_env, u32 uid);
|
||||
|
||||
#endif /* QCOMTEE_OBJECT_H */
|
||||
@@ -0,0 +1,150 @@
|
||||
// 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_tzmem.h>
|
||||
#include <linux/mm.h>
|
||||
|
||||
#include "qcomtee.h"
|
||||
|
||||
/**
|
||||
* define MAX_OUTBOUND_BUFFER_SIZE - Maximum size of outbound buffers.
|
||||
*
|
||||
* The size of outbound buffer depends on QTEE callback requests.
|
||||
*/
|
||||
#define MAX_OUTBOUND_BUFFER_SIZE SZ_4K
|
||||
|
||||
/**
|
||||
* define MAX_INBOUND_BUFFER_SIZE - Maximum size of the inbound buffer.
|
||||
*
|
||||
* The size of the inbound buffer depends on the user's requests,
|
||||
* specifically the number of IB and OB arguments. If an invocation
|
||||
* requires a size larger than %MAX_INBOUND_BUFFER_SIZE, the user should
|
||||
* consider using another form of shared memory with QTEE.
|
||||
*/
|
||||
#define MAX_INBOUND_BUFFER_SIZE SZ_4M
|
||||
|
||||
/**
|
||||
* qcomtee_msg_buffers_alloc() - Allocate inbound and outbound buffers.
|
||||
* @oic: context to use for the current invocation.
|
||||
* @u: array of arguments for the current invocation.
|
||||
*
|
||||
* It calculates the size of inbound and outbound buffers based on the
|
||||
* arguments in @u. It allocates the buffers from the teedev pool.
|
||||
*
|
||||
* Return: On success, returns 0. On error, returns < 0.
|
||||
*/
|
||||
int qcomtee_msg_buffers_alloc(struct qcomtee_object_invoke_ctx *oic,
|
||||
struct qcomtee_arg *u)
|
||||
{
|
||||
struct tee_context *ctx = oic->ctx;
|
||||
struct tee_shm *shm;
|
||||
size_t size;
|
||||
int i;
|
||||
|
||||
/* Start offset in a message for buffer arguments. */
|
||||
size = qcomtee_msg_buffer_args(struct qcomtee_msg_object_invoke,
|
||||
qcomtee_args_len(u));
|
||||
if (size > MAX_INBOUND_BUFFER_SIZE)
|
||||
return -EINVAL;
|
||||
|
||||
/* Add size of IB arguments. */
|
||||
qcomtee_arg_for_each_input_buffer(i, u) {
|
||||
size = size_add(size, qcomtee_msg_offset_align(u[i].b.size));
|
||||
if (size > MAX_INBOUND_BUFFER_SIZE)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Add size of OB arguments. */
|
||||
qcomtee_arg_for_each_output_buffer(i, u) {
|
||||
size = size_add(size, qcomtee_msg_offset_align(u[i].b.size));
|
||||
if (size > MAX_INBOUND_BUFFER_SIZE)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
shm = tee_shm_alloc_priv_buf(ctx, size);
|
||||
if (IS_ERR(shm))
|
||||
return PTR_ERR(shm);
|
||||
|
||||
/* Allocate inbound buffer. */
|
||||
oic->in_shm = shm;
|
||||
shm = tee_shm_alloc_priv_buf(ctx, MAX_OUTBOUND_BUFFER_SIZE);
|
||||
if (IS_ERR(shm)) {
|
||||
tee_shm_free(oic->in_shm);
|
||||
|
||||
return PTR_ERR(shm);
|
||||
}
|
||||
/* Allocate outbound buffer. */
|
||||
oic->out_shm = shm;
|
||||
|
||||
oic->in_msg.addr = tee_shm_get_va(oic->in_shm, 0);
|
||||
oic->in_msg.size = tee_shm_get_size(oic->in_shm);
|
||||
oic->out_msg.addr = tee_shm_get_va(oic->out_shm, 0);
|
||||
oic->out_msg.size = tee_shm_get_size(oic->out_shm);
|
||||
/* QTEE assume unused buffers are zeroed. */
|
||||
memzero_explicit(oic->in_msg.addr, oic->in_msg.size);
|
||||
memzero_explicit(oic->out_msg.addr, oic->out_msg.size);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void qcomtee_msg_buffers_free(struct qcomtee_object_invoke_ctx *oic)
|
||||
{
|
||||
tee_shm_free(oic->in_shm);
|
||||
tee_shm_free(oic->out_shm);
|
||||
}
|
||||
|
||||
/* Dynamic shared memory pool based on tee_dyn_shm_alloc_helper(). */
|
||||
|
||||
static int qcomtee_shm_register(struct tee_context *ctx, struct tee_shm *shm,
|
||||
struct page **pages, size_t num_pages,
|
||||
unsigned long start)
|
||||
{
|
||||
return qcom_tzmem_shm_bridge_create(shm->paddr, shm->size,
|
||||
&shm->sec_world_id);
|
||||
}
|
||||
|
||||
static int qcomtee_shm_unregister(struct tee_context *ctx, struct tee_shm *shm)
|
||||
{
|
||||
qcom_tzmem_shm_bridge_delete(shm->sec_world_id);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pool_op_alloc(struct tee_shm_pool *pool, struct tee_shm *shm,
|
||||
size_t size, size_t align)
|
||||
{
|
||||
return tee_dyn_shm_alloc_helper(shm, size, align, qcomtee_shm_register);
|
||||
}
|
||||
|
||||
static void pool_op_free(struct tee_shm_pool *pool, struct tee_shm *shm)
|
||||
{
|
||||
tee_dyn_shm_free_helper(shm, qcomtee_shm_unregister);
|
||||
}
|
||||
|
||||
static void pool_op_destroy_pool(struct tee_shm_pool *pool)
|
||||
{
|
||||
kfree(pool);
|
||||
}
|
||||
|
||||
static const struct tee_shm_pool_ops pool_ops = {
|
||||
.alloc = pool_op_alloc,
|
||||
.free = pool_op_free,
|
||||
.destroy_pool = pool_op_destroy_pool,
|
||||
};
|
||||
|
||||
struct tee_shm_pool *qcomtee_shm_pool_alloc(void)
|
||||
{
|
||||
struct tee_shm_pool *pool;
|
||||
|
||||
pool = kzalloc(sizeof(*pool), GFP_KERNEL);
|
||||
if (!pool)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
pool->ops = &pool_ops;
|
||||
|
||||
return pool;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
+126
-1
@@ -79,6 +79,7 @@ void teedev_ctx_get(struct tee_context *ctx)
|
||||
|
||||
kref_get(&ctx->refcount);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(teedev_ctx_get);
|
||||
|
||||
static void teedev_ctx_release(struct kref *ref)
|
||||
{
|
||||
@@ -96,11 +97,15 @@ void teedev_ctx_put(struct tee_context *ctx)
|
||||
|
||||
kref_put(&ctx->refcount, teedev_ctx_release);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(teedev_ctx_put);
|
||||
|
||||
void teedev_close_context(struct tee_context *ctx)
|
||||
{
|
||||
struct tee_device *teedev = ctx->teedev;
|
||||
|
||||
if (teedev->desc->ops->close_context)
|
||||
teedev->desc->ops->close_context(ctx);
|
||||
|
||||
teedev_ctx_put(ctx);
|
||||
tee_device_put(teedev);
|
||||
}
|
||||
@@ -445,6 +450,7 @@ static int params_from_user(struct tee_context *ctx, struct tee_param *params,
|
||||
switch (ip.attr & TEE_IOCTL_PARAM_ATTR_TYPE_MASK) {
|
||||
case TEE_IOCTL_PARAM_ATTR_TYPE_NONE:
|
||||
case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT:
|
||||
case TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_OUTPUT:
|
||||
break;
|
||||
case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT:
|
||||
case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT:
|
||||
@@ -452,6 +458,22 @@ static int params_from_user(struct tee_context *ctx, struct tee_param *params,
|
||||
params[n].u.value.b = ip.b;
|
||||
params[n].u.value.c = ip.c;
|
||||
break;
|
||||
case TEE_IOCTL_PARAM_ATTR_TYPE_UBUF_INPUT:
|
||||
case TEE_IOCTL_PARAM_ATTR_TYPE_UBUF_OUTPUT:
|
||||
case TEE_IOCTL_PARAM_ATTR_TYPE_UBUF_INOUT:
|
||||
params[n].u.ubuf.uaddr = u64_to_user_ptr(ip.a);
|
||||
params[n].u.ubuf.size = ip.b;
|
||||
|
||||
if (!access_ok(params[n].u.ubuf.uaddr,
|
||||
params[n].u.ubuf.size))
|
||||
return -EFAULT;
|
||||
|
||||
break;
|
||||
case TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_INPUT:
|
||||
case TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_INOUT:
|
||||
params[n].u.objref.id = ip.a;
|
||||
params[n].u.objref.flags = ip.b;
|
||||
break;
|
||||
case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT:
|
||||
case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT:
|
||||
case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT:
|
||||
@@ -485,6 +507,17 @@ static int params_to_user(struct tee_ioctl_param __user *uparams,
|
||||
put_user(p->u.value.c, &up->c))
|
||||
return -EFAULT;
|
||||
break;
|
||||
case TEE_IOCTL_PARAM_ATTR_TYPE_UBUF_OUTPUT:
|
||||
case TEE_IOCTL_PARAM_ATTR_TYPE_UBUF_INOUT:
|
||||
if (put_user((u64)p->u.ubuf.size, &up->b))
|
||||
return -EFAULT;
|
||||
break;
|
||||
case TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_OUTPUT:
|
||||
case TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_INOUT:
|
||||
if (put_user(p->u.objref.id, &up->a) ||
|
||||
put_user(p->u.objref.flags, &up->b))
|
||||
return -EFAULT;
|
||||
break;
|
||||
case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT:
|
||||
case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT:
|
||||
if (put_user((u64)p->u.memref.size, &up->b))
|
||||
@@ -637,6 +670,66 @@ out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int tee_ioctl_object_invoke(struct tee_context *ctx,
|
||||
struct tee_ioctl_buf_data __user *ubuf)
|
||||
{
|
||||
int rc;
|
||||
size_t n;
|
||||
struct tee_ioctl_buf_data buf;
|
||||
struct tee_ioctl_object_invoke_arg __user *uarg;
|
||||
struct tee_ioctl_object_invoke_arg arg;
|
||||
struct tee_ioctl_param __user *uparams = NULL;
|
||||
struct tee_param *params = NULL;
|
||||
|
||||
if (!ctx->teedev->desc->ops->object_invoke_func)
|
||||
return -EINVAL;
|
||||
|
||||
if (copy_from_user(&buf, ubuf, sizeof(buf)))
|
||||
return -EFAULT;
|
||||
|
||||
if (buf.buf_len > TEE_MAX_ARG_SIZE ||
|
||||
buf.buf_len < sizeof(struct tee_ioctl_object_invoke_arg))
|
||||
return -EINVAL;
|
||||
|
||||
uarg = u64_to_user_ptr(buf.buf_ptr);
|
||||
if (copy_from_user(&arg, uarg, sizeof(arg)))
|
||||
return -EFAULT;
|
||||
|
||||
if (sizeof(arg) + TEE_IOCTL_PARAM_SIZE(arg.num_params) != buf.buf_len)
|
||||
return -EINVAL;
|
||||
|
||||
if (arg.num_params) {
|
||||
params = kcalloc(arg.num_params, sizeof(struct tee_param),
|
||||
GFP_KERNEL);
|
||||
if (!params)
|
||||
return -ENOMEM;
|
||||
uparams = uarg->params;
|
||||
rc = params_from_user(ctx, params, arg.num_params, uparams);
|
||||
if (rc)
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = ctx->teedev->desc->ops->object_invoke_func(ctx, &arg, params);
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
if (put_user(arg.ret, &uarg->ret)) {
|
||||
rc = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
rc = params_to_user(uparams, arg.num_params, params);
|
||||
out:
|
||||
if (params) {
|
||||
/* Decrease ref count for all valid shared memory pointers */
|
||||
for (n = 0; n < arg.num_params; n++)
|
||||
if (tee_param_is_memref(params + n) &&
|
||||
params[n].u.memref.shm)
|
||||
tee_shm_put(params[n].u.memref.shm);
|
||||
kfree(params);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int tee_ioctl_cancel(struct tee_context *ctx,
|
||||
struct tee_ioctl_cancel_arg __user *uarg)
|
||||
{
|
||||
@@ -685,6 +778,19 @@ static int params_to_supp(struct tee_context *ctx,
|
||||
ip.b = p->u.value.b;
|
||||
ip.c = p->u.value.c;
|
||||
break;
|
||||
case TEE_IOCTL_PARAM_ATTR_TYPE_UBUF_INPUT:
|
||||
case TEE_IOCTL_PARAM_ATTR_TYPE_UBUF_OUTPUT:
|
||||
case TEE_IOCTL_PARAM_ATTR_TYPE_UBUF_INOUT:
|
||||
ip.a = (__force unsigned long)p->u.ubuf.uaddr;
|
||||
ip.b = p->u.ubuf.size;
|
||||
ip.c = 0;
|
||||
break;
|
||||
case TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_INPUT:
|
||||
case TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_INOUT:
|
||||
ip.a = p->u.objref.id;
|
||||
ip.b = p->u.objref.flags;
|
||||
ip.c = 0;
|
||||
break;
|
||||
case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT:
|
||||
case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT:
|
||||
case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT:
|
||||
@@ -787,6 +893,21 @@ static int params_from_supp(struct tee_param *params, size_t num_params,
|
||||
p->u.value.b = ip.b;
|
||||
p->u.value.c = ip.c;
|
||||
break;
|
||||
case TEE_IOCTL_PARAM_ATTR_TYPE_UBUF_OUTPUT:
|
||||
case TEE_IOCTL_PARAM_ATTR_TYPE_UBUF_INOUT:
|
||||
p->u.ubuf.uaddr = u64_to_user_ptr(ip.a);
|
||||
p->u.ubuf.size = ip.b;
|
||||
|
||||
if (!access_ok(params[n].u.ubuf.uaddr,
|
||||
params[n].u.ubuf.size))
|
||||
return -EFAULT;
|
||||
|
||||
break;
|
||||
case TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_OUTPUT:
|
||||
case TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_INOUT:
|
||||
p->u.objref.id = ip.a;
|
||||
p->u.objref.flags = ip.b;
|
||||
break;
|
||||
case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT:
|
||||
case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT:
|
||||
/*
|
||||
@@ -869,6 +990,8 @@ static long tee_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
||||
return tee_ioctl_open_session(ctx, uarg);
|
||||
case TEE_IOC_INVOKE:
|
||||
return tee_ioctl_invoke(ctx, uarg);
|
||||
case TEE_IOC_OBJECT_INVOKE:
|
||||
return tee_ioctl_object_invoke(ctx, uarg);
|
||||
case TEE_IOC_CANCEL:
|
||||
return tee_ioctl_cancel(ctx, uarg);
|
||||
case TEE_IOC_CLOSE_SESSION:
|
||||
@@ -926,7 +1049,7 @@ struct tee_device *tee_device_alloc(const struct tee_desc *teedesc,
|
||||
|
||||
if (!teedesc || !teedesc->name || !teedesc->ops ||
|
||||
!teedesc->ops->get_version || !teedesc->ops->open ||
|
||||
!teedesc->ops->release || !pool)
|
||||
!teedesc->ops->release)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
teedev = kzalloc(sizeof(*teedev), GFP_KERNEL);
|
||||
@@ -1075,6 +1198,7 @@ void tee_device_put(struct tee_device *teedev)
|
||||
}
|
||||
mutex_unlock(&teedev->mutex);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tee_device_put);
|
||||
|
||||
bool tee_device_get(struct tee_device *teedev)
|
||||
{
|
||||
@@ -1087,6 +1211,7 @@ bool tee_device_get(struct tee_device *teedev)
|
||||
mutex_unlock(&teedev->mutex);
|
||||
return true;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tee_device_get);
|
||||
|
||||
/**
|
||||
* tee_device_unregister() - Removes a TEE device
|
||||
|
||||
@@ -23,12 +23,6 @@ struct tee_shm_dmabuf_ref {
|
||||
|
||||
int tee_shm_get_fd(struct tee_shm *shm);
|
||||
|
||||
bool tee_device_get(struct tee_device *teedev);
|
||||
void tee_device_put(struct tee_device *teedev);
|
||||
|
||||
void teedev_ctx_get(struct tee_context *ctx);
|
||||
void teedev_ctx_put(struct tee_context *ctx);
|
||||
|
||||
struct tee_shm *tee_shm_alloc_user_buf(struct tee_context *ctx, size_t size);
|
||||
struct tee_shm *tee_shm_register_user_buf(struct tee_context *ctx,
|
||||
unsigned long addr, size_t length);
|
||||
|
||||
@@ -76,22 +76,30 @@ struct tee_device {
|
||||
/**
|
||||
* struct tee_driver_ops - driver operations vtable
|
||||
* @get_version: returns version of driver
|
||||
* @open: called when the device file is opened
|
||||
* @release: release this open file
|
||||
* @open: called for a context when the device file is opened
|
||||
* @close_context: called when the device file is closed
|
||||
* @release: called to release the context
|
||||
* @open_session: open a new session
|
||||
* @close_session: close a session
|
||||
* @system_session: declare session as a system session
|
||||
* @invoke_func: invoke a trusted function
|
||||
* @object_invoke_func: invoke a TEE object
|
||||
* @cancel_req: request cancel of an ongoing invoke or open
|
||||
* @supp_recv: called for supplicant to get a command
|
||||
* @supp_send: called for supplicant to send a response
|
||||
* @shm_register: register shared memory buffer in TEE
|
||||
* @shm_unregister: unregister shared memory buffer in TEE
|
||||
*
|
||||
* The context given to @open might last longer than the device file if it is
|
||||
* tied to other resources in the TEE driver. @close_context is called when the
|
||||
* client closes the device file, even if there are existing references to the
|
||||
* context. The TEE driver can use @close_context to start cleaning up.
|
||||
*/
|
||||
struct tee_driver_ops {
|
||||
void (*get_version)(struct tee_device *teedev,
|
||||
struct tee_ioctl_version_data *vers);
|
||||
int (*open)(struct tee_context *ctx);
|
||||
void (*close_context)(struct tee_context *ctx);
|
||||
void (*release)(struct tee_context *ctx);
|
||||
int (*open_session)(struct tee_context *ctx,
|
||||
struct tee_ioctl_open_session_arg *arg,
|
||||
@@ -101,6 +109,9 @@ struct tee_driver_ops {
|
||||
int (*invoke_func)(struct tee_context *ctx,
|
||||
struct tee_ioctl_invoke_arg *arg,
|
||||
struct tee_param *param);
|
||||
int (*object_invoke_func)(struct tee_context *ctx,
|
||||
struct tee_ioctl_object_invoke_arg *arg,
|
||||
struct tee_param *param);
|
||||
int (*cancel_req)(struct tee_context *ctx, u32 cancel_id, u32 session);
|
||||
int (*supp_recv)(struct tee_context *ctx, u32 *func, u32 *num_params,
|
||||
struct tee_param *param);
|
||||
@@ -200,6 +211,24 @@ int tee_device_register_dma_heap(struct tee_device *teedev,
|
||||
struct tee_protmem_pool *pool);
|
||||
void tee_device_put_all_dma_heaps(struct tee_device *teedev);
|
||||
|
||||
/**
|
||||
* tee_device_get() - Increment the user count for a tee_device
|
||||
* @teedev: Pointer to the tee_device
|
||||
*
|
||||
* If tee_device_unregister() has been called and the final user of @teedev
|
||||
* has already released the device, this function will fail to prevent new users
|
||||
* from accessing the device during the unregistration process.
|
||||
*
|
||||
* Returns: true if @teedev remains valid, otherwise false
|
||||
*/
|
||||
bool tee_device_get(struct tee_device *teedev);
|
||||
|
||||
/**
|
||||
* tee_device_put() - Decrease the user count for a tee_device
|
||||
* @teedev: pointer to the tee_device
|
||||
*/
|
||||
void tee_device_put(struct tee_device *teedev);
|
||||
|
||||
/**
|
||||
* tee_device_set_dev_groups() - Set device attribute groups
|
||||
* @teedev: Device to register
|
||||
@@ -374,4 +403,25 @@ struct tee_context *teedev_open(struct tee_device *teedev);
|
||||
*/
|
||||
void teedev_close_context(struct tee_context *ctx);
|
||||
|
||||
/**
|
||||
* teedev_ctx_get() - Increment the reference count of a context
|
||||
* @ctx: Pointer to the context
|
||||
*
|
||||
* This function increases the refcount of the context, which is tied to
|
||||
* resources shared by the same tee_device. During the unregistration process,
|
||||
* the context may remain valid even after tee_device_unregister() has returned.
|
||||
*
|
||||
* Users should ensure that the context's refcount is properly decreased before
|
||||
* calling tee_device_put(), typically within the context's release() function.
|
||||
* Alternatively, users can call tee_device_get() and teedev_ctx_get() together
|
||||
* and release them simultaneously (see shm_alloc_helper()).
|
||||
*/
|
||||
void teedev_ctx_get(struct tee_context *ctx);
|
||||
|
||||
/**
|
||||
* teedev_ctx_put() - Decrease reference count on a context
|
||||
* @ctx: pointer to the context
|
||||
*/
|
||||
void teedev_ctx_put(struct tee_context *ctx);
|
||||
|
||||
#endif /*__TEE_CORE_H*/
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user