tee: add Qualcomm TEE driver

Introduce qcomtee_object, which represents an object in both QTEE and
the kernel. QTEE clients can invoke an instance of qcomtee_object to
access QTEE services. If this invocation produces a new object in QTEE,
an instance of qcomtee_object will be returned.

Similarly, QTEE can request services from by issuing a callback
request, which invokes an instance of qcomtee_object.

Implement initial support for exporting qcomtee_object to userspace
and QTEE, enabling the invocation of objects hosted in QTEE and userspace
through the TEE subsystem.

Tested-by: Neil Armstrong <neil.armstrong@linaro.org>
Tested-by: Harshal Dev <quic_hdev@quicinc.com>
Acked-by: Sumit Garg <sumit.garg@oss.qualcomm.com>
Signed-off-by: Amirreza Zarrabi <amirreza.zarrabi@oss.qualcomm.com>
Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
This commit is contained in:
Amirreza Zarrabi
2025-09-15 17:34:06 +02:00
committed by Jens Wiklander
parent bd51393068
commit d6e290837e
14 changed files with 3537 additions and 0 deletions
+6
View File
@@ -20856,6 +20856,12 @@ 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: drivers/tee/qcomtee/
QUALCOMM TRUST ZONE MEMORY ALLOCATOR
M: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
L: linux-arm-msm@vger.kernel.org
+1
View File
@@ -21,5 +21,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
+1
View File
@@ -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/
+12
View File
@@ -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.
+7
View File
@@ -0,0 +1,7 @@
# 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 += shm.o
qcomtee-objs += user_obj.o
+182
View File
@@ -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
+143
View File
@@ -0,0 +1,143 @@
/* 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)
/**
* 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);
#endif /* QCOMTEE_H */
+304
View File
@@ -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 */
+316
View File
@@ -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 */
+153
View File
@@ -0,0 +1,153 @@
// 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)
{
if (!(shm->flags & TEE_SHM_PRIV))
return -ENOMEM;
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
+1
View File
@@ -59,6 +59,7 @@
#define TEE_IMPL_ID_OPTEE 1
#define TEE_IMPL_ID_AMDTEE 2
#define TEE_IMPL_ID_TSTEE 3
#define TEE_IMPL_ID_QTEE 4
/*
* OP-TEE specific capabilities