Hi all,
This adds supports for the OP-TEE driver to communicate with secure world using FF-A [1] as transport.
These patches are based on the FF-A v4 patch set by Sudeep Holla [2] [3].
There is one change to the TEE subsystem with "tee: add sec_world_id to struct tee_shm" to add support for holding globally unique handle assigned by the FF-A. This is a field that I believe could useful for the AMDTEE driver too.
For communication the OP-TEE message protocol is still used, but with a new type of memory reference, struct optee_msg_param_fmem, to carry the information needed by FF-A. The OP-TEE driver is refactored internally with to sets of callbacks, one for the old SMC based communication and another set with FF-A as transport.
There is also a difference in how the drivers are instantiated. With the SMC based transport we have a platform driver, module_platform_driver(), today which we're keeping as is for this configuration. In a FF-A system we have a FF-A driver, module_ffa_driver(), instead.
The OP-TEE driver can be compiled for both targets at the same time and it's up to runtime configuration (device tree or ACPI) to decide how it's initialized.
Thanks, Jens
[1] https://developer.arm.com/documentation/den0077/latest [2] https://lore.kernel.org/linux-arm-kernel/20210212154614.38604-1-sudeep.holla... [3] git://git.kernel.org/pub/scm/linux/kernel/git/sudeep.holla/linux.git v5.11/ffa
Jens Wiklander (6): tee: add sec_world_id to struct tee_shm optee: simplify optee_release() optee: sync optee_msg.h and optee_rpc_cmd.h optee: refactor driver with internal callbacks optee: add a FF-A memory pool optee: add FF-A support
drivers/tee/optee/call.c | 327 +++++++++++--- drivers/tee/optee/core.c | 698 +++++++++++++++++++++++++----- drivers/tee/optee/optee_ffa.h | 153 +++++++ drivers/tee/optee/optee_msg.h | 168 ++----- drivers/tee/optee/optee_private.h | 88 +++- drivers/tee/optee/optee_rpc_cmd.h | 333 ++++++++++++++ drivers/tee/optee/rpc.c | 169 +++++++- drivers/tee/optee/shm_pool.c | 65 ++- drivers/tee/optee/shm_pool.h | 1 + include/linux/tee_drv.h | 7 +- 10 files changed, 1685 insertions(+), 324 deletions(-) create mode 100644 drivers/tee/optee/optee_ffa.h create mode 100644 drivers/tee/optee/optee_rpc_cmd.h
base-commit: 31ef391700953fb59ea8755ea38c6085bdec380e
Adds sec_world_id to struct tee_shm which describes a shared memory object. sec_world_id can be used by a driver to store an id assigned by secure world.
Signed-off-by: Jens Wiklander jens.wiklander@linaro.org --- include/linux/tee_drv.h | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/include/linux/tee_drv.h b/include/linux/tee_drv.h index cdd049a724b1..93d836fded8b 100644 --- a/include/linux/tee_drv.h +++ b/include/linux/tee_drv.h @@ -196,7 +196,11 @@ int tee_session_calc_client_uuid(uuid_t *uuid, u32 connection_method, * @num_pages: number of locked pages * @dmabuf: dmabuf used to for exporting to user space * @flags: defined by TEE_SHM_* in tee_drv.h - * @id: unique id of a shared memory object on this device + * @id: unique id of a shared memory object on this device, shared + * with user space + * @sec_world_id: + * secure world assigned id of this shared memory object, not + * used by all drivers * * This pool is only supposed to be accessed directly from the TEE * subsystem and from drivers that implements their own shm pool manager. @@ -212,6 +216,7 @@ struct tee_shm { struct dma_buf *dmabuf; u32 flags; int id; + u64 sec_world_id; };
/**
Simplifies optee_release() with a new helper function, optee_close_session_helper() which has been factored out from optee_close_session().
A separate optee_release_supp() is added for the supplicant device.
Signed-off-by: Jens Wiklander jens.wiklander@linaro.org --- drivers/tee/optee/call.c | 31 ++++++++++------- drivers/tee/optee/core.c | 55 +++++++++++-------------------- drivers/tee/optee/optee_private.h | 1 + 3 files changed, 39 insertions(+), 48 deletions(-)
diff --git a/drivers/tee/optee/call.c b/drivers/tee/optee/call.c index 780d7c4fd756..c0eca4d7d7f2 100644 --- a/drivers/tee/optee/call.c +++ b/drivers/tee/optee/call.c @@ -283,12 +283,28 @@ int optee_open_session(struct tee_context *ctx, return rc; }
-int optee_close_session(struct tee_context *ctx, u32 session) +int optee_close_session_helper(struct tee_context *ctx, u32 session) { - struct optee_context_data *ctxdata = ctx->data; struct tee_shm *shm; struct optee_msg_arg *msg_arg; phys_addr_t msg_parg; + + shm = get_msg_arg(ctx, 0, &msg_arg, &msg_parg); + if (IS_ERR(shm)) + return PTR_ERR(shm); + + msg_arg->cmd = OPTEE_MSG_CMD_CLOSE_SESSION; + msg_arg->session = session; + optee_do_call_with_arg(ctx, msg_parg); + + tee_shm_free(shm); + + return 0; +} + +int optee_close_session(struct tee_context *ctx, u32 session) +{ + struct optee_context_data *ctxdata = ctx->data; struct optee_session *sess;
/* Check that the session is valid and remove it from the list */ @@ -301,16 +317,7 @@ int optee_close_session(struct tee_context *ctx, u32 session) return -EINVAL; kfree(sess);
- shm = get_msg_arg(ctx, 0, &msg_arg, &msg_parg); - if (IS_ERR(shm)) - return PTR_ERR(shm); - - msg_arg->cmd = OPTEE_MSG_CMD_CLOSE_SESSION; - msg_arg->session = session; - optee_do_call_with_arg(ctx, msg_parg); - - tee_shm_free(shm); - return 0; + return optee_close_session_helper(ctx, session); }
int optee_invoke_func(struct tee_context *ctx, struct tee_ioctl_invoke_arg *arg, diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c index cf4718c6d35d..0ffe0c7d68ff 100644 --- a/drivers/tee/optee/core.c +++ b/drivers/tee/optee/core.c @@ -273,59 +273,42 @@ static int optee_open(struct tee_context *ctx) return 0; }
-static void optee_release(struct tee_context *ctx) +static void optee_release_helper(struct tee_context *ctx, + int (*close_session)(struct tee_context *ctx, + u32 session)) { struct optee_context_data *ctxdata = ctx->data; - struct tee_device *teedev = ctx->teedev; - struct optee *optee = tee_get_drvdata(teedev); - struct tee_shm *shm; - struct optee_msg_arg *arg = NULL; - phys_addr_t parg; struct optee_session *sess; struct optee_session *sess_tmp;
if (!ctxdata) return;
- shm = tee_shm_alloc(ctx, sizeof(struct optee_msg_arg), TEE_SHM_MAPPED); - if (!IS_ERR(shm)) { - arg = tee_shm_get_va(shm, 0); - /* - * If va2pa fails for some reason, we can't call into - * secure world, only free the memory. Secure OS will leak - * sessions and finally refuse more sessions, but we will - * at least let normal world reclaim its memory. - */ - if (!IS_ERR(arg)) - if (tee_shm_va2pa(shm, arg, &parg)) - arg = NULL; /* prevent usage of parg below */ - } - list_for_each_entry_safe(sess, sess_tmp, &ctxdata->sess_list, list_node) { list_del(&sess->list_node); - if (!IS_ERR_OR_NULL(arg)) { - memset(arg, 0, sizeof(*arg)); - arg->cmd = OPTEE_MSG_CMD_CLOSE_SESSION; - arg->session = sess->session_id; - optee_do_call_with_arg(ctx, parg); - } + close_session(ctx, sess->session_id); kfree(sess); } kfree(ctxdata); + ctx->data = NULL; +}
- if (!IS_ERR(shm)) - tee_shm_free(shm); +static void optee_release(struct tee_context *ctx) +{ + optee_release_helper(ctx, optee_close_session_helper); +}
- ctx->data = NULL; +static void optee_release_supp(struct tee_context *ctx) +{ + struct optee *optee = tee_get_drvdata(ctx->teedev);
- if (teedev == optee->supp_teedev) { - if (optee->scan_bus_wq) { - destroy_workqueue(optee->scan_bus_wq); - optee->scan_bus_wq = NULL; - } - optee_supp_release(&optee->supp); + optee_release_helper(ctx, optee_close_session_helper); + if (optee->scan_bus_wq) { + destroy_workqueue(optee->scan_bus_wq); + optee->scan_bus_wq = NULL; } + optee_supp_release(&optee->supp); }
static const struct tee_driver_ops optee_ops = { @@ -349,7 +332,7 @@ static const struct tee_desc optee_desc = { static const struct tee_driver_ops optee_supp_ops = { .get_version = optee_get_version, .open = optee_open, - .release = optee_release, + .release = optee_release_supp, .supp_recv = optee_supp_recv, .supp_send = optee_supp_send, .shm_register = optee_shm_register_supp, diff --git a/drivers/tee/optee/optee_private.h b/drivers/tee/optee/optee_private.h index e25b216a14ef..2b63b796645e 100644 --- a/drivers/tee/optee/optee_private.h +++ b/drivers/tee/optee/optee_private.h @@ -152,6 +152,7 @@ u32 optee_do_call_with_arg(struct tee_context *ctx, phys_addr_t parg); int optee_open_session(struct tee_context *ctx, struct tee_ioctl_open_session_arg *arg, struct tee_param *param); +int optee_close_session_helper(struct tee_context *ctx, u32 session); int optee_close_session(struct tee_context *ctx, u32 session); int optee_invoke_func(struct tee_context *ctx, struct tee_ioctl_invoke_arg *arg, struct tee_param *param);
Updates to latest optee_msg.h and optee_rpc_cmd.h. There's no changes in the ABI. Only some clarifications and a complete specification of RPC requests where the latter is now in a separate file, optee_rpc_cmd.h.
Most of the RPC requests are not served by the OP-TEE driver instead they are forwarded as opaque requests to tee-supplicant.
Signed-off-by: Jens Wiklander jens.wiklander@linaro.org --- drivers/tee/optee/optee_msg.h | 143 +------------ drivers/tee/optee/optee_rpc_cmd.h | 333 ++++++++++++++++++++++++++++++ drivers/tee/optee/rpc.c | 32 +-- 3 files changed, 360 insertions(+), 148 deletions(-) create mode 100644 drivers/tee/optee/optee_rpc_cmd.h
diff --git a/drivers/tee/optee/optee_msg.h b/drivers/tee/optee/optee_msg.h index 7b2d919da2ac..de96882f7b9b 100644 --- a/drivers/tee/optee/optee_msg.h +++ b/drivers/tee/optee/optee_msg.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) */ /* - * Copyright (c) 2015-2019, Linaro Limited + * Copyright (c) 2015-2021, Linaro Limited */ #ifndef _OPTEE_MSG_H #define _OPTEE_MSG_H @@ -11,12 +11,6 @@ /* * This file defines the OP-TEE message protocol used to communicate * with an instance of OP-TEE running in secure world. - * - * This file is divided into three sections. - * 1. Formatting of messages. - * 2. Requests from normal world - * 3. Requests from secure world, Remote Procedure Call (RPC), handled by - * tee-supplicant. */
/***************************************************************************** @@ -54,14 +48,14 @@ * Every entry in buffer should point to a 4k page beginning (12 least * significant bits must be equal to zero). * - * 12 least significant bints of optee_msg_param.u.tmem.buf_ptr should hold page - * offset of the user buffer. + * 12 least significant bits of optee_msg_param.u.tmem.buf_ptr should hold + * page offset of user buffer. * * So, entries should be placed like members of this structure: * * struct page_data { - * uint64_t pages_array[OPTEE_MSG_NONCONTIG_PAGE_SIZE/sizeof(uint64_t) - 1]; - * uint64_t next_page_data; + * u64 pages_array[OPTEE_MSG_NONCONTIG_PAGE_SIZE/sizeof(u64) - 1]; + * u64 next_page_data; * }; * * Structure is designed to exactly fit into the page size @@ -150,7 +144,7 @@ struct optee_msg_param_value { * @attr & OPTEE_MSG_ATTR_TYPE_MASK indicates if tmem, rmem or value is used in * the union. OPTEE_MSG_ATTR_TYPE_VALUE_* indicates value, * OPTEE_MSG_ATTR_TYPE_TMEM_* indicates @tmem and - * OPTEE_MSG_ATTR_TYPE_RMEM_* indicates @rmem, + * OPTEE_MSG_ATTR_TYPE_RMEM_* indicates @rmem. * OPTEE_MSG_ATTR_TYPE_NONE indicates that none of the members are used. */ struct optee_msg_param { @@ -176,17 +170,9 @@ struct optee_msg_param { * @params: the parameters supplied to the OS Command * * All normal calls to Trusted OS uses this struct. If cmd requires further - * information than what these field holds it can be passed as a parameter + * information than what these fields hold it can be passed as a parameter * tagged as meta (setting the OPTEE_MSG_ATTR_META bit in corresponding - * attrs field). All parameters tagged as meta has to come first. - * - * Temp memref parameters can be fragmented if supported by the Trusted OS - * (when optee_smc.h is bearer of this protocol this is indicated with - * OPTEE_SMC_SEC_CAP_UNREGISTERED_SHM). If a logical memref parameter is - * fragmented then has all but the last fragment the - * OPTEE_MSG_ATTR_FRAGMENT bit set in attrs. Even if a memref is fragmented - * it will still be presented as a single logical memref to the Trusted - * Application. + * attrs field). All parameters tagged as meta have to come first. */ struct optee_msg_arg { u32 cmd; @@ -199,7 +185,7 @@ struct optee_msg_arg { u32 num_params;
/* num_params tells the actual number of element in params */ - struct optee_msg_param params[0]; + struct optee_msg_param params[]; };
/** @@ -290,13 +276,10 @@ struct optee_msg_arg { * OPTEE_MSG_CMD_REGISTER_SHM registers a shared memory reference. The * information is passed as: * [in] param[0].attr OPTEE_MSG_ATTR_TYPE_TMEM_INPUT - * [| OPTEE_MSG_ATTR_FRAGMENT] + * [| OPTEE_MSG_ATTR_NONCONTIG] * [in] param[0].u.tmem.buf_ptr physical address (of first fragment) * [in] param[0].u.tmem.size size (of first fragment) * [in] param[0].u.tmem.shm_ref holds shared memory reference - * ... - * The shared memory can optionally be fragmented, temp memrefs can follow - * each other with all but the last with the OPTEE_MSG_ATTR_FRAGMENT bit set. * * OPTEE_MSG_CMD_UNREGISTER_SHM unregisteres a previously registered shared * memory reference. The information is passed as: @@ -313,112 +296,6 @@ struct optee_msg_arg { #define OPTEE_MSG_CMD_UNREGISTER_SHM 5 #define OPTEE_MSG_FUNCID_CALL_WITH_ARG 0x0004
-/***************************************************************************** - * Part 3 - Requests from secure world, RPC - *****************************************************************************/ - -/* - * All RPC is done with a struct optee_msg_arg as bearer of information, - * struct optee_msg_arg::arg holds values defined by OPTEE_MSG_RPC_CMD_* below - * - * RPC communication with tee-supplicant is reversed compared to normal - * client communication desribed above. The supplicant receives requests - * and sends responses. - */ - -/* - * Load a TA into memory, defined in tee-supplicant - */ -#define OPTEE_MSG_RPC_CMD_LOAD_TA 0 - -/* - * Reserved - */ -#define OPTEE_MSG_RPC_CMD_RPMB 1 - -/* - * File system access, defined in tee-supplicant - */ -#define OPTEE_MSG_RPC_CMD_FS 2 - -/* - * Get time - * - * Returns number of seconds and nano seconds since the Epoch, - * 1970-01-01 00:00:00 +0000 (UTC). - * - * [out] param[0].u.value.a Number of seconds - * [out] param[0].u.value.b Number of nano seconds. - */ -#define OPTEE_MSG_RPC_CMD_GET_TIME 3 - -/* - * Wait queue primitive, helper for secure world to implement a wait queue. - * - * If secure world need to wait for a secure world mutex it issues a sleep - * request instead of spinning in secure world. Conversely is a wakeup - * request issued when a secure world mutex with a thread waiting thread is - * unlocked. - * - * Waiting on a key - * [in] param[0].u.value.a OPTEE_MSG_RPC_WAIT_QUEUE_SLEEP - * [in] param[0].u.value.b wait key - * - * Waking up a key - * [in] param[0].u.value.a OPTEE_MSG_RPC_WAIT_QUEUE_WAKEUP - * [in] param[0].u.value.b wakeup key - */ -#define OPTEE_MSG_RPC_CMD_WAIT_QUEUE 4 -#define OPTEE_MSG_RPC_WAIT_QUEUE_SLEEP 0 -#define OPTEE_MSG_RPC_WAIT_QUEUE_WAKEUP 1 - -/* - * Suspend execution - * - * [in] param[0].value .a number of milliseconds to suspend - */ -#define OPTEE_MSG_RPC_CMD_SUSPEND 5 - -/* - * Allocate a piece of shared memory - * - * Shared memory can optionally be fragmented, to support that additional - * spare param entries are allocated to make room for eventual fragments. - * The spare param entries has .attr = OPTEE_MSG_ATTR_TYPE_NONE when - * unused. All returned temp memrefs except the last should have the - * OPTEE_MSG_ATTR_FRAGMENT bit set in the attr field. - * - * [in] param[0].u.value.a type of memory one of - * OPTEE_MSG_RPC_SHM_TYPE_* below - * [in] param[0].u.value.b requested size - * [in] param[0].u.value.c required alignment - * - * [out] param[0].u.tmem.buf_ptr physical address (of first fragment) - * [out] param[0].u.tmem.size size (of first fragment) - * [out] param[0].u.tmem.shm_ref shared memory reference - * ... - * [out] param[n].u.tmem.buf_ptr physical address - * [out] param[n].u.tmem.size size - * [out] param[n].u.tmem.shm_ref shared memory reference (same value - * as in param[n-1].u.tmem.shm_ref) - */ -#define OPTEE_MSG_RPC_CMD_SHM_ALLOC 6 -/* Memory that can be shared with a non-secure user space application */ -#define OPTEE_MSG_RPC_SHM_TYPE_APPL 0 -/* Memory only shared with non-secure kernel */ -#define OPTEE_MSG_RPC_SHM_TYPE_KERNEL 1 - -/* - * Free shared memory previously allocated with OPTEE_MSG_RPC_CMD_SHM_ALLOC - * - * [in] param[0].u.value.a type of memory one of - * OPTEE_MSG_RPC_SHM_TYPE_* above - * [in] param[0].u.value.b value of shared memory reference - * returned in param[0].u.tmem.shm_ref - * above - */ -#define OPTEE_MSG_RPC_CMD_SHM_FREE 7 - /* * Access a device on an i2c bus * diff --git a/drivers/tee/optee/optee_rpc_cmd.h b/drivers/tee/optee/optee_rpc_cmd.h new file mode 100644 index 000000000000..712aa32513d7 --- /dev/null +++ b/drivers/tee/optee/optee_rpc_cmd.h @@ -0,0 +1,333 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) 2016-2018, Linaro Limited + */ + +#ifndef __OPTEE_RPC_CMD_H +#define __OPTEE_RPC_CMD_H + +/* + * All RPC is done with a struct optee_msg_arg as bearer of information, + * struct optee_msg_arg::arg holds values defined by OPTEE_RPC_CMD_* below. + * Only the commands handled by the kernel driver are defined here. + * + * RPC communication with tee-supplicant is reversed compared to normal + * client communication described above. The supplicant receives requests + * and sends responses. + */ + +/* + * Load a TA into memory + * + * Since the size of the TA isn't known in advance the size of the TA is + * can be queried with a NULL buffer. + * + * [in] value[0].a-b UUID + * [out] memref[1] Buffer with TA + */ +#define OPTEE_RPC_CMD_LOAD_TA 0 + +/* + * Replay Protected Memory Block access + * + * [in] memref[0] Frames to device + * [out] memref[1] Frames from device + */ +#define OPTEE_RPC_CMD_RPMB 1 + +/* + * File system access, see definition of protocol below + */ +#define OPTEE_RPC_CMD_FS 2 + +/* + * Get time + * + * Returns number of seconds and nano seconds since the Epoch, + * 1970-01-01 00:00:00 +0000 (UTC). + * + * [out] value[0].a Number of seconds + * [out] value[0].b Number of nano seconds. + */ +#define OPTEE_RPC_CMD_GET_TIME 3 + +/* + * Wait queue primitive, helper for secure world to implement a wait queue. + * + * If secure world needs to wait for a secure world mutex it issues a sleep + * request instead of spinning in secure world. Conversely is a wakeup + * request issued when a secure world mutex with a thread waiting thread is + * unlocked. + * + * Waiting on a key + * [in] value[0].a OPTEE_RPC_WAIT_QUEUE_SLEEP + * [in] value[0].b Wait key + * + * Waking up a key + * [in] value[0].a OPTEE_RPC_WAIT_QUEUE_WAKEUP + * [in] value[0].b Wakeup key + */ +#define OPTEE_RPC_CMD_WAIT_QUEUE 4 +#define OPTEE_RPC_WAIT_QUEUE_SLEEP 0 +#define OPTEE_RPC_WAIT_QUEUE_WAKEUP 1 + +/* + * Suspend execution + * + * [in] value[0].a Number of milliseconds to suspend + */ +#define OPTEE_RPC_CMD_SUSPEND 5 + +/* + * Allocate a piece of shared memory + * + * [in] value[0].a Type of memory one of + * OPTEE_RPC_SHM_TYPE_* below + * [in] value[0].b Requested size + * [in] value[0].c Required alignment + * [out] memref[0] Buffer + */ +#define OPTEE_RPC_CMD_SHM_ALLOC 6 +/* Memory that can be shared with a non-secure user space application */ +#define OPTEE_RPC_SHM_TYPE_APPL 0 +/* Memory only shared with non-secure kernel */ +#define OPTEE_RPC_SHM_TYPE_KERNEL 1 +/* + * Memory shared with non-secure kernel and exported to a non-secure user + * space application + */ +#define OPTEE_RPC_SHM_TYPE_GLOBAL 2 + +/* + * Free shared memory previously allocated with OPTEE_RPC_CMD_SHM_ALLOC + * + * [in] value[0].a Type of memory one of + * OPTEE_RPC_SHM_TYPE_* above + * [in] value[0].b Value of shared memory reference or cookie + */ +#define OPTEE_RPC_CMD_SHM_FREE 7 + +/* Was OPTEE_RPC_CMD_SQL_FS, which isn't supported any longer */ +#define OPTEE_RPC_CMD_SQL_FS_RESERVED 8 + +/* + * Send TA profiling information to normal world + * + * [in/out] value[0].a File identifier. Must be set to 0 on + * first call. A value >= 1 will be + * returned on success. Re-use this value + * to append data to the same file. + * [in] memref[1] TA UUID + * [in] memref[2] Profile data + */ +#define OPTEE_RPC_CMD_GPROF 9 + +/* + * Socket command, see definition of protocol below + */ +#define OPTEE_RPC_CMD_SOCKET 10 + +/* + * Send TA function graph data to normal world + * + * [in/out] value[0].a File identifier. Must be set to 0 on + * first call. A value >= 1 will be + * returned on success. Re-use this value + * to append data to the same file. + * [in] memref[1] TA UUID + * [in] memref[2] function graph data + */ +#define OPTEE_RPC_CMD_FTRACE 11 + +/* + * Register timestamp buffer in the linux kernel optee driver + * + * [in] value[0].a Subcommand (register buffer, unregister buffer) + * OPTEE_RPC_CMD_BENCH_REG_* below + * [in] value[0].b Physical address of timestamp buffer + * [in] value[0].c Size of buffer + */ +#define OPTEE_RPC_CMD_BENCH_REG_NEW 0 +#define OPTEE_RPC_CMD_BENCH_REG_DEL 1 +#define OPTEE_RPC_CMD_BENCH_REG 20 + +/* + * Definition of protocol for command OPTEE_RPC_CMD_FS + */ + +/* + * Open a file + * + * [in] value[0].a OPTEE_RPC_FS_OPEN + * [in] memref[1] A string holding the file name + * [out] value[2].a File descriptor of open file + */ +#define OPTEE_RPC_FS_OPEN 0 + +/* + * Create a file + * + * [in] value[0].a OPTEE_RPC_FS_CREATE + * [in] memref[1] A string holding the file name + * [out] value[2].a File descriptor of open file + */ +#define OPTEE_RPC_FS_CREATE 1 + +/* + * Close a file + * + * [in] value[0].a OPTEE_RPC_FS_CLOSE + * [in] value[0].b File descriptor of open file. + */ +#define OPTEE_RPC_FS_CLOSE 2 + +/* + * Read from a file + * + * [in] value[0].a OPTEE_RPC_FS_READ + * [in] value[0].b File descriptor of open file + * [in] value[0].c Offset into file + * [out] memref[1] Buffer to hold returned data + */ +#define OPTEE_RPC_FS_READ 3 + +/* + * Write to a file + * + * [in] value[0].a OPTEE_RPC_FS_WRITE + * [in] value[0].b File descriptor of open file + * [in] value[0].c Offset into file + * [in] memref[1] Buffer holding data to be written + */ +#define OPTEE_RPC_FS_WRITE 4 + +/* + * Truncate a file + * + * [in] value[0].a OPTEE_RPC_FS_TRUNCATE + * [in] value[0].b File descriptor of open file + * [in] value[0].c Length of file. + */ +#define OPTEE_RPC_FS_TRUNCATE 5 + +/* + * Remove a file + * + * [in] value[0].a OPTEE_RPC_FS_REMOVE + * [in] memref[1] A string holding the file name + */ +#define OPTEE_RPC_FS_REMOVE 6 + +/* + * Rename a file + * + * [in] value[0].a OPTEE_RPC_FS_RENAME + * [in] value[0].b True if existing target should be removed + * [in] memref[1] A string holding the old file name + * [in] memref[2] A string holding the new file name + */ +#define OPTEE_RPC_FS_RENAME 7 + +/* + * Opens a directory for file listing + * + * [in] value[0].a OPTEE_RPC_FS_OPENDIR + * [in] memref[1] A string holding the name of the directory + * [out] value[2].a Handle to open directory + */ +#define OPTEE_RPC_FS_OPENDIR 8 + +/* + * Closes a directory handle + * + * [in] value[0].a OPTEE_RPC_FS_CLOSEDIR + * [in] value[0].b Handle to open directory + */ +#define OPTEE_RPC_FS_CLOSEDIR 9 + +/* + * Read next file name of directory + * + * + * [in] value[0].a OPTEE_RPC_FS_READDIR + * [in] value[0].b Handle to open directory + * [out] memref[1] A string holding the file name + */ +#define OPTEE_RPC_FS_READDIR 10 + +/* End of definition of protocol for command OPTEE_RPC_CMD_FS */ + +/* + * Definition of protocol for command OPTEE_RPC_CMD_SOCKET + */ + +#define OPTEE_RPC_SOCKET_TIMEOUT_NONBLOCKING 0 +#define OPTEE_RPC_SOCKET_TIMEOUT_BLOCKING 0xffffffff + +/* + * Open socket + * + * [in] value[0].a OPTEE_RPC_SOCKET_OPEN + * [in] value[0].b TA instance id + * [in] value[1].a Server port number + * [in] value[1].b Protocol, TEE_ISOCKET_PROTOCOLID_* + * [in] value[1].c Ip version TEE_IP_VERSION_* from tee_ipsocket.h + * [in] memref[2] Server address + * [out] value[3].a Socket handle (32-bit) + */ +#define OPTEE_RPC_SOCKET_OPEN 0 + +/* + * Close socket + * + * [in] value[0].a OPTEE_RPC_SOCKET_CLOSE + * [in] value[0].b TA instance id + * [in] value[0].c Socket handle + */ +#define OPTEE_RPC_SOCKET_CLOSE 1 + +/* + * Close all sockets + * + * [in] value[0].a OPTEE_RPC_SOCKET_CLOSE_ALL + * [in] value[0].b TA instance id + */ +#define OPTEE_RPC_SOCKET_CLOSE_ALL 2 + +/* + * Send data on socket + * + * [in] value[0].a OPTEE_RPC_SOCKET_SEND + * [in] value[0].b TA instance id + * [in] value[0].c Socket handle + * [in] memref[1] Buffer to transmit + * [in] value[2].a Timeout ms or OPTEE_RPC_SOCKET_TIMEOUT_* + * [out] value[2].b Number of transmitted bytes + */ +#define OPTEE_RPC_SOCKET_SEND 3 + +/* + * Receive data on socket + * + * [in] value[0].a OPTEE_RPC_SOCKET_RECV + * [in] value[0].b TA instance id + * [in] value[0].c Socket handle + * [out] memref[1] Buffer to receive + * [in] value[2].a Timeout ms or OPTEE_RPC_SOCKET_TIMEOUT_* + */ +#define OPTEE_RPC_SOCKET_RECV 4 + +/* + * Perform IOCTL on socket + * + * [in] value[0].a OPTEE_RPC_SOCKET_IOCTL + * [in] value[0].b TA instance id + * [in] value[0].c Socket handle + * [in/out] memref[1] Buffer + * [in] value[2].a Ioctl command + */ +#define OPTEE_RPC_SOCKET_IOCTL 5 + +/* End of definition of protocol for command OPTEE_RPC_CMD_SOCKET */ + +#endif /*__OPTEE_RPC_CMD_H*/ diff --git a/drivers/tee/optee/rpc.c b/drivers/tee/optee/rpc.c index 1e3614e4798f..a3a1aa59f639 100644 --- a/drivers/tee/optee/rpc.c +++ b/drivers/tee/optee/rpc.c @@ -10,7 +10,9 @@ #include <linux/i2c.h> #include <linux/slab.h> #include <linux/tee_drv.h> +#include "optee_msg.h" #include "optee_private.h" +#include "optee_rpc_cmd.h" #include "optee_smc.h"
struct wq_entry { @@ -194,10 +196,10 @@ static void handle_rpc_func_cmd_wq(struct optee *optee, goto bad;
switch (arg->params[0].u.value.a) { - case OPTEE_MSG_RPC_WAIT_QUEUE_SLEEP: + case OPTEE_RPC_WAIT_QUEUE_SLEEP: wq_sleep(&optee->wait_queue, arg->params[0].u.value.b); break; - case OPTEE_MSG_RPC_WAIT_QUEUE_WAKEUP: + case OPTEE_RPC_WAIT_QUEUE_WAKEUP: wq_wakeup(&optee->wait_queue, arg->params[0].u.value.b); break; default: @@ -267,11 +269,11 @@ static struct tee_shm *cmd_alloc_suppl(struct tee_context *ctx, size_t sz) struct tee_shm *shm;
param.attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT; - param.u.value.a = OPTEE_MSG_RPC_SHM_TYPE_APPL; + param.u.value.a = OPTEE_RPC_SHM_TYPE_APPL; param.u.value.b = sz; param.u.value.c = 0;
- ret = optee_supp_thrd_req(ctx, OPTEE_MSG_RPC_CMD_SHM_ALLOC, 1, ¶m); + ret = optee_supp_thrd_req(ctx, OPTEE_RPC_CMD_SHM_ALLOC, 1, ¶m); if (ret) return ERR_PTR(-ENOMEM);
@@ -308,10 +310,10 @@ static void handle_rpc_func_cmd_shm_alloc(struct tee_context *ctx,
sz = arg->params[0].u.value.b; switch (arg->params[0].u.value.a) { - case OPTEE_MSG_RPC_SHM_TYPE_APPL: + case OPTEE_RPC_SHM_TYPE_APPL: shm = cmd_alloc_suppl(ctx, sz); break; - case OPTEE_MSG_RPC_SHM_TYPE_KERNEL: + case OPTEE_RPC_SHM_TYPE_KERNEL: shm = tee_shm_alloc(ctx, sz, TEE_SHM_MAPPED); break; default: @@ -383,7 +385,7 @@ static void cmd_free_suppl(struct tee_context *ctx, struct tee_shm *shm) struct tee_param param;
param.attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT; - param.u.value.a = OPTEE_MSG_RPC_SHM_TYPE_APPL; + param.u.value.a = OPTEE_RPC_SHM_TYPE_APPL; param.u.value.b = tee_shm_get_id(shm); param.u.value.c = 0;
@@ -400,7 +402,7 @@ static void cmd_free_suppl(struct tee_context *ctx, struct tee_shm *shm) */ tee_shm_put(shm);
- optee_supp_thrd_req(ctx, OPTEE_MSG_RPC_CMD_SHM_FREE, 1, ¶m); + optee_supp_thrd_req(ctx, OPTEE_RPC_CMD_SHM_FREE, 1, ¶m); }
static void handle_rpc_func_cmd_shm_free(struct tee_context *ctx, @@ -418,10 +420,10 @@ static void handle_rpc_func_cmd_shm_free(struct tee_context *ctx,
shm = (struct tee_shm *)(unsigned long)arg->params[0].u.value.b; switch (arg->params[0].u.value.a) { - case OPTEE_MSG_RPC_SHM_TYPE_APPL: + case OPTEE_RPC_SHM_TYPE_APPL: cmd_free_suppl(ctx, shm); break; - case OPTEE_MSG_RPC_SHM_TYPE_KERNEL: + case OPTEE_RPC_SHM_TYPE_KERNEL: tee_shm_free(shm); break; default: @@ -458,20 +460,20 @@ static void handle_rpc_func_cmd(struct tee_context *ctx, struct optee *optee, }
switch (arg->cmd) { - case OPTEE_MSG_RPC_CMD_GET_TIME: + case OPTEE_RPC_CMD_GET_TIME: handle_rpc_func_cmd_get_time(arg); break; - case OPTEE_MSG_RPC_CMD_WAIT_QUEUE: + case OPTEE_RPC_CMD_WAIT_QUEUE: handle_rpc_func_cmd_wq(optee, arg); break; - case OPTEE_MSG_RPC_CMD_SUSPEND: + case OPTEE_RPC_CMD_SUSPEND: handle_rpc_func_cmd_wait(arg); break; - case OPTEE_MSG_RPC_CMD_SHM_ALLOC: + case OPTEE_RPC_CMD_SHM_ALLOC: free_pages_list(call_ctx); handle_rpc_func_cmd_shm_alloc(ctx, arg, call_ctx); break; - case OPTEE_MSG_RPC_CMD_SHM_FREE: + case OPTEE_RPC_CMD_SHM_FREE: handle_rpc_func_cmd_shm_free(ctx, arg); break; case OPTEE_MSG_RPC_CMD_I2C_TRANSFER:
The OP-TEE driver is refactored with three internal callbacks replacing direct calls to optee_from_msg_param(), optee_to_msg_param() and optee_do_call_with_arg().
These functions a central to communicating with OP-TEE in secure world by using the SMC Calling Convention directly.
This refactoring makes room for using other primitives to communicate with OP-TEE in secure world while being able to reuse as much as possible from the present driver.
Signed-off-by: Jens Wiklander jens.wiklander@linaro.org --- drivers/tee/optee/call.c | 78 ++++++++------- drivers/tee/optee/core.c | 158 +++++++++++++++++++----------- drivers/tee/optee/optee_private.h | 35 +++++-- drivers/tee/optee/rpc.c | 19 ++-- 4 files changed, 181 insertions(+), 109 deletions(-)
diff --git a/drivers/tee/optee/call.c b/drivers/tee/optee/call.c index c0eca4d7d7f2..ef049c0dee50 100644 --- a/drivers/tee/optee/call.c +++ b/drivers/tee/optee/call.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2015, Linaro Limited + * Copyright (c) 2015-2021, Linaro Limited */ #include <linux/arm-smccc.h> #include <linux/device.h> @@ -116,20 +116,25 @@ static struct optee_session *find_session(struct optee_context_data *ctxdata, /** * optee_do_call_with_arg() - Do an SMC to OP-TEE in secure world * @ctx: calling context - * @parg: physical address of message to pass to secure world + * @arg: shared memory holding the message to pass to secure world * * Does and SMC to OP-TEE in secure world and handles eventual resulting * Remote Procedure Calls (RPC) from OP-TEE. * * Returns return code from secure world, 0 is OK */ -u32 optee_do_call_with_arg(struct tee_context *ctx, phys_addr_t parg) +int optee_do_call_with_arg(struct tee_context *ctx, struct tee_shm *arg) { struct optee *optee = tee_get_drvdata(ctx->teedev); struct optee_call_waiter w; struct optee_rpc_param param = { }; struct optee_call_ctx call_ctx = { }; - u32 ret; + phys_addr_t parg; + int rc; + + rc = tee_shm_get_pa(arg, 0, &parg); + if (rc) + return rc;
param.a0 = OPTEE_SMC_CALL_WITH_ARG; reg_pair_from_64(¶m.a1, ¶m.a2, parg); @@ -157,7 +162,7 @@ u32 optee_do_call_with_arg(struct tee_context *ctx, phys_addr_t parg) param.a3 = res.a3; optee_handle_rpc(ctx, ¶m, &call_ctx); } else { - ret = res.a0; + rc = res.a0; break; } } @@ -169,12 +174,11 @@ u32 optee_do_call_with_arg(struct tee_context *ctx, phys_addr_t parg) */ optee_cq_wait_final(&optee->call_queue, &w);
- return ret; + return rc; }
static struct tee_shm *get_msg_arg(struct tee_context *ctx, size_t num_params, - struct optee_msg_arg **msg_arg, - phys_addr_t *msg_parg) + struct optee_msg_arg **msg_arg) { int rc; struct tee_shm *shm; @@ -191,10 +195,6 @@ static struct tee_shm *get_msg_arg(struct tee_context *ctx, size_t num_params, goto out; }
- rc = tee_shm_get_pa(shm, 0, msg_parg); - if (rc) - goto out; - memset(ma, 0, OPTEE_MSG_GET_ARG_SIZE(num_params)); ma->num_params = num_params; *msg_arg = ma; @@ -211,15 +211,15 @@ int optee_open_session(struct tee_context *ctx, struct tee_ioctl_open_session_arg *arg, struct tee_param *param) { + struct optee *optee = tee_get_drvdata(ctx->teedev); struct optee_context_data *ctxdata = ctx->data; int rc; struct tee_shm *shm; struct optee_msg_arg *msg_arg; - phys_addr_t msg_parg; struct optee_session *sess = NULL;
/* +2 for the meta parameters added below */ - shm = get_msg_arg(ctx, arg->num_params + 2, &msg_arg, &msg_parg); + shm = get_msg_arg(ctx, arg->num_params + 2, &msg_arg); if (IS_ERR(shm)) return PTR_ERR(shm);
@@ -242,7 +242,8 @@ int optee_open_session(struct tee_context *ctx, if (rc) goto out;
- rc = optee_to_msg_param(msg_arg->params + 2, arg->num_params, param); + rc = optee->ops->to_msg_param(optee, msg_arg->params + 2, + arg->num_params, param); if (rc) goto out;
@@ -252,7 +253,7 @@ int optee_open_session(struct tee_context *ctx, goto out; }
- if (optee_do_call_with_arg(ctx, msg_parg)) { + if (optee->ops->do_call_with_arg(ctx, shm)) { msg_arg->ret = TEEC_ERROR_COMMUNICATION; msg_arg->ret_origin = TEEC_ORIGIN_COMMS; } @@ -267,7 +268,8 @@ int optee_open_session(struct tee_context *ctx, kfree(sess); }
- if (optee_from_msg_param(param, arg->num_params, msg_arg->params + 2)) { + if (optee->ops->from_msg_param(optee, param, arg->num_params, + msg_arg->params + 2)) { arg->ret = TEEC_ERROR_COMMUNICATION; arg->ret_origin = TEEC_ORIGIN_COMMS; /* Close session again to avoid leakage */ @@ -286,16 +288,16 @@ int optee_open_session(struct tee_context *ctx, int optee_close_session_helper(struct tee_context *ctx, u32 session) { struct tee_shm *shm; + struct optee *optee = tee_get_drvdata(ctx->teedev); struct optee_msg_arg *msg_arg; - phys_addr_t msg_parg;
- shm = get_msg_arg(ctx, 0, &msg_arg, &msg_parg); + shm = get_msg_arg(ctx, 0, &msg_arg); if (IS_ERR(shm)) return PTR_ERR(shm);
msg_arg->cmd = OPTEE_MSG_CMD_CLOSE_SESSION; msg_arg->session = session; - optee_do_call_with_arg(ctx, msg_parg); + optee->ops->do_call_with_arg(ctx, shm);
tee_shm_free(shm);
@@ -323,10 +325,10 @@ int optee_close_session(struct tee_context *ctx, u32 session) int optee_invoke_func(struct tee_context *ctx, struct tee_ioctl_invoke_arg *arg, struct tee_param *param) { + struct optee *optee = tee_get_drvdata(ctx->teedev); struct optee_context_data *ctxdata = ctx->data; struct tee_shm *shm; struct optee_msg_arg *msg_arg; - phys_addr_t msg_parg; struct optee_session *sess; int rc;
@@ -337,7 +339,7 @@ int optee_invoke_func(struct tee_context *ctx, struct tee_ioctl_invoke_arg *arg, if (!sess) return -EINVAL;
- shm = get_msg_arg(ctx, arg->num_params, &msg_arg, &msg_parg); + shm = get_msg_arg(ctx, arg->num_params, &msg_arg); if (IS_ERR(shm)) return PTR_ERR(shm); msg_arg->cmd = OPTEE_MSG_CMD_INVOKE_COMMAND; @@ -345,16 +347,18 @@ int optee_invoke_func(struct tee_context *ctx, struct tee_ioctl_invoke_arg *arg, msg_arg->session = arg->session; msg_arg->cancel_id = arg->cancel_id;
- rc = optee_to_msg_param(msg_arg->params, arg->num_params, param); + rc = optee->ops->to_msg_param(optee, msg_arg->params, arg->num_params, + param); if (rc) goto out;
- if (optee_do_call_with_arg(ctx, msg_parg)) { + if (optee->ops->do_call_with_arg(ctx, shm)) { msg_arg->ret = TEEC_ERROR_COMMUNICATION; msg_arg->ret_origin = TEEC_ORIGIN_COMMS; }
- if (optee_from_msg_param(param, arg->num_params, msg_arg->params)) { + if (optee->ops->from_msg_param(optee, param, arg->num_params, + msg_arg->params)) { msg_arg->ret = TEEC_ERROR_COMMUNICATION; msg_arg->ret_origin = TEEC_ORIGIN_COMMS; } @@ -368,10 +372,10 @@ int optee_invoke_func(struct tee_context *ctx, struct tee_ioctl_invoke_arg *arg,
int optee_cancel_req(struct tee_context *ctx, u32 cancel_id, u32 session) { + struct optee *optee = tee_get_drvdata(ctx->teedev); struct optee_context_data *ctxdata = ctx->data; struct tee_shm *shm; struct optee_msg_arg *msg_arg; - phys_addr_t msg_parg; struct optee_session *sess;
/* Check that the session is valid */ @@ -381,14 +385,14 @@ int optee_cancel_req(struct tee_context *ctx, u32 cancel_id, u32 session) if (!sess) return -EINVAL;
- shm = get_msg_arg(ctx, 0, &msg_arg, &msg_parg); + shm = get_msg_arg(ctx, 0, &msg_arg); if (IS_ERR(shm)) return PTR_ERR(shm);
msg_arg->cmd = OPTEE_MSG_CMD_CANCEL; msg_arg->session = session; msg_arg->cancel_id = cancel_id; - optee_do_call_with_arg(ctx, msg_parg); + optee->ops->do_call_with_arg(ctx, shm);
tee_shm_free(shm); return 0; @@ -587,10 +591,10 @@ int optee_shm_register(struct tee_context *ctx, struct tee_shm *shm, struct page **pages, size_t num_pages, unsigned long start) { - struct tee_shm *shm_arg = NULL; + struct optee *optee = tee_get_drvdata(ctx->teedev); struct optee_msg_arg *msg_arg; + struct tee_shm *shm_arg; u64 *pages_list; - phys_addr_t msg_parg; int rc;
if (!num_pages) @@ -604,7 +608,7 @@ int optee_shm_register(struct tee_context *ctx, struct tee_shm *shm, if (!pages_list) return -ENOMEM;
- shm_arg = get_msg_arg(ctx, 1, &msg_arg, &msg_parg); + shm_arg = get_msg_arg(ctx, 1, &msg_arg); if (IS_ERR(shm_arg)) { rc = PTR_ERR(shm_arg); goto out; @@ -625,7 +629,7 @@ int optee_shm_register(struct tee_context *ctx, struct tee_shm *shm, msg_arg->params->u.tmem.buf_ptr = virt_to_phys(pages_list) | (tee_shm_get_page_offset(shm) & (OPTEE_MSG_NONCONTIG_PAGE_SIZE - 1));
- if (optee_do_call_with_arg(ctx, msg_parg) || + if (optee->ops->do_call_with_arg(ctx, shm) || msg_arg->ret != TEEC_SUCCESS) rc = -EINVAL;
@@ -637,12 +641,12 @@ int optee_shm_register(struct tee_context *ctx, struct tee_shm *shm,
int optee_shm_unregister(struct tee_context *ctx, struct tee_shm *shm) { - struct tee_shm *shm_arg; + struct optee *optee = tee_get_drvdata(ctx->teedev); struct optee_msg_arg *msg_arg; - phys_addr_t msg_parg; - int rc = 0; + struct tee_shm *shm_arg; + int rc;
- shm_arg = get_msg_arg(ctx, 1, &msg_arg, &msg_parg); + shm_arg = get_msg_arg(ctx, 1, &msg_arg); if (IS_ERR(shm_arg)) return PTR_ERR(shm_arg);
@@ -651,7 +655,7 @@ int optee_shm_unregister(struct tee_context *ctx, struct tee_shm *shm) msg_arg->params[0].attr = OPTEE_MSG_ATTR_TYPE_RMEM_INPUT; msg_arg->params[0].u.rmem.shm_ref = (unsigned long)shm;
- if (optee_do_call_with_arg(ctx, msg_parg) || + if (optee->ops->do_call_with_arg(ctx, shm) || msg_arg->ret != TEEC_SUCCESS) rc = -EINVAL; tee_shm_free(shm_arg); diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c index 0ffe0c7d68ff..ab602bb8e14a 100644 --- a/drivers/tee/optee/core.c +++ b/drivers/tee/optee/core.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2015, Linaro Limited + * Copyright (c) 2015-2021, Linaro Limited */
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt @@ -26,21 +26,87 @@
#define OPTEE_SHM_NUM_PRIV_PAGES CONFIG_OPTEE_SHM_NUM_PRIV_PAGES
+static void from_msg_param_value(struct tee_param *p, u32 attr, + const struct optee_msg_param *mp) +{ + p->attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT + + attr - OPTEE_MSG_ATTR_TYPE_VALUE_INPUT; + p->u.value.a = mp->u.value.a; + p->u.value.b = mp->u.value.b; + p->u.value.c = mp->u.value.c; +} + +static int from_msg_param_tmp_mem(struct tee_param *p, u32 attr, + const struct optee_msg_param *mp) +{ + struct tee_shm *shm; + phys_addr_t pa; + int rc; + + p->attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT + + attr - OPTEE_MSG_ATTR_TYPE_TMEM_INPUT; + p->u.memref.size = mp->u.tmem.size; + shm = (struct tee_shm *)(unsigned long)mp->u.tmem.shm_ref; + if (!shm) { + p->u.memref.shm_offs = 0; + p->u.memref.shm = NULL; + return 0; + } + + rc = tee_shm_get_pa(shm, 0, &pa); + if (rc) + return rc; + + p->u.memref.shm_offs = mp->u.tmem.buf_ptr - pa; + p->u.memref.shm = shm; + + /* Check that the memref is covered by the shm object */ + if (p->u.memref.size) { + size_t o = p->u.memref.shm_offs + + p->u.memref.size - 1; + + rc = tee_shm_get_pa(shm, o, NULL); + if (rc) + return rc; + } + + return 0; +} + +static void from_msg_param_reg_mem(struct tee_param *p, u32 attr, + const struct optee_msg_param *mp) +{ + struct tee_shm *shm; + + p->attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT + + attr - OPTEE_MSG_ATTR_TYPE_RMEM_INPUT; + p->u.memref.size = mp->u.rmem.size; + shm = (struct tee_shm *)(unsigned long)mp->u.rmem.shm_ref; + + if (shm) { + p->u.memref.shm_offs = mp->u.rmem.offs; + p->u.memref.shm = shm; + } else { + p->u.memref.shm_offs = 0; + p->u.memref.shm = NULL; + } +} + /** * optee_from_msg_param() - convert from OPTEE_MSG parameters to * struct tee_param + * @optee: main service struct * @params: subsystem internal parameter representation * @num_params: number of elements in the parameter arrays * @msg_params: OPTEE_MSG parameters * Returns 0 on success or <0 on failure */ -int optee_from_msg_param(struct tee_param *params, size_t num_params, - const struct optee_msg_param *msg_params) +static int optee_from_msg_param(struct optee *optee, struct tee_param *params, + size_t num_params, + const struct optee_msg_param *msg_params) { int rc; size_t n; - struct tee_shm *shm; - phys_addr_t pa;
for (n = 0; n < num_params; n++) { struct tee_param *p = params + n; @@ -55,58 +121,19 @@ int optee_from_msg_param(struct tee_param *params, size_t num_params, case OPTEE_MSG_ATTR_TYPE_VALUE_INPUT: case OPTEE_MSG_ATTR_TYPE_VALUE_OUTPUT: case OPTEE_MSG_ATTR_TYPE_VALUE_INOUT: - p->attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT + - attr - OPTEE_MSG_ATTR_TYPE_VALUE_INPUT; - p->u.value.a = mp->u.value.a; - p->u.value.b = mp->u.value.b; - p->u.value.c = mp->u.value.c; + from_msg_param_value(p, attr, mp); break; case OPTEE_MSG_ATTR_TYPE_TMEM_INPUT: case OPTEE_MSG_ATTR_TYPE_TMEM_OUTPUT: case OPTEE_MSG_ATTR_TYPE_TMEM_INOUT: - p->attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT + - attr - OPTEE_MSG_ATTR_TYPE_TMEM_INPUT; - p->u.memref.size = mp->u.tmem.size; - shm = (struct tee_shm *)(unsigned long) - mp->u.tmem.shm_ref; - if (!shm) { - p->u.memref.shm_offs = 0; - p->u.memref.shm = NULL; - break; - } - rc = tee_shm_get_pa(shm, 0, &pa); + rc = from_msg_param_tmp_mem(p, attr, mp); if (rc) return rc; - p->u.memref.shm_offs = mp->u.tmem.buf_ptr - pa; - p->u.memref.shm = shm; - - /* Check that the memref is covered by the shm object */ - if (p->u.memref.size) { - size_t o = p->u.memref.shm_offs + - p->u.memref.size - 1; - - rc = tee_shm_get_pa(shm, o, NULL); - if (rc) - return rc; - } break; case OPTEE_MSG_ATTR_TYPE_RMEM_INPUT: case OPTEE_MSG_ATTR_TYPE_RMEM_OUTPUT: case OPTEE_MSG_ATTR_TYPE_RMEM_INOUT: - p->attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT + - attr - OPTEE_MSG_ATTR_TYPE_RMEM_INPUT; - p->u.memref.size = mp->u.rmem.size; - shm = (struct tee_shm *)(unsigned long) - mp->u.rmem.shm_ref; - - if (!shm) { - p->u.memref.shm_offs = 0; - p->u.memref.shm = NULL; - break; - } - p->u.memref.shm_offs = mp->u.rmem.offs; - p->u.memref.shm = shm; - + from_msg_param_reg_mem(p, attr, mp); break;
default: @@ -116,6 +143,16 @@ int optee_from_msg_param(struct tee_param *params, size_t num_params, return 0; }
+static void to_msg_param_value(struct optee_msg_param *mp, + const struct tee_param *p) +{ + mp->attr = OPTEE_MSG_ATTR_TYPE_VALUE_INPUT + p->attr - + TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT; + mp->u.value.a = p->u.value.a; + mp->u.value.b = p->u.value.b; + mp->u.value.c = p->u.value.c; +} + static int to_msg_param_tmp_mem(struct optee_msg_param *mp, const struct tee_param *p) { @@ -158,13 +195,15 @@ static int to_msg_param_reg_mem(struct optee_msg_param *mp,
/** * optee_to_msg_param() - convert from struct tee_params to OPTEE_MSG parameters + * @optee: main service struct * @msg_params: OPTEE_MSG parameters * @num_params: number of elements in the parameter arrays * @params: subsystem itnernal parameter representation * Returns 0 on success or <0 on failure */ -int optee_to_msg_param(struct optee_msg_param *msg_params, size_t num_params, - const struct tee_param *params) +static int optee_to_msg_param(struct optee *optee, + struct optee_msg_param *msg_params, + size_t num_params, const struct tee_param *params) { int rc; size_t n; @@ -181,11 +220,7 @@ int optee_to_msg_param(struct optee_msg_param *msg_params, size_t num_params, case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT: case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT: case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT: - mp->attr = OPTEE_MSG_ATTR_TYPE_VALUE_INPUT + p->attr - - TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT; - mp->u.value.a = p->u.value.a; - mp->u.value.b = p->u.value.b; - mp->u.value.c = p->u.value.c; + to_msg_param_value(mp, p); break; case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT: case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT: @@ -311,7 +346,7 @@ static void optee_release_supp(struct tee_context *ctx) optee_supp_release(&optee->supp); }
-static const struct tee_driver_ops optee_ops = { +static const struct tee_driver_ops optee_clnt_ops = { .get_version = optee_get_version, .open = optee_open, .release = optee_release, @@ -323,9 +358,9 @@ static const struct tee_driver_ops optee_ops = { .shm_unregister = optee_shm_unregister, };
-static const struct tee_desc optee_desc = { +static const struct tee_desc optee_clnt_desc = { .name = DRIVER_NAME "-clnt", - .ops = &optee_ops, + .ops = &optee_clnt_ops, .owner = THIS_MODULE, };
@@ -346,6 +381,12 @@ static const struct tee_desc optee_supp_desc = { .flags = TEE_DESC_PRIVILEGED, };
+static const struct optee_ops optee_ops = { + .do_call_with_arg = optee_do_call_with_arg, + .to_msg_param = optee_to_msg_param, + .from_msg_param = optee_from_msg_param, +}; + static bool optee_msg_api_uid_is_optee_api(optee_invoke_fn *invoke_fn) { struct arm_smccc_res res; @@ -647,10 +688,11 @@ static int optee_probe(struct platform_device *pdev) goto err; }
+ optee->ops = &optee_ops; optee->invoke_fn = invoke_fn; optee->sec_caps = sec_caps;
- teedev = tee_device_alloc(&optee_desc, NULL, pool, optee); + teedev = tee_device_alloc(&optee_clnt_desc, NULL, pool, optee); if (IS_ERR(teedev)) { rc = PTR_ERR(teedev); goto err; diff --git a/drivers/tee/optee/optee_private.h b/drivers/tee/optee/optee_private.h index 2b63b796645e..c5741e96e967 100644 --- a/drivers/tee/optee/optee_private.h +++ b/drivers/tee/optee/optee_private.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* - * Copyright (c) 2015, Linaro Limited + * Copyright (c) 2015-2021, Linaro Limited */
#ifndef OPTEE_PRIVATE_H @@ -66,9 +66,34 @@ struct optee_supp { struct completion reqs_c; };
+struct optee; + +/** + * struct optee_ops - OP-TEE driver internal operations + * @do_call_with_arg: enters OP-TEE in secure world + * @to_msg_param: converts from struct tee_param to OPTEE_MSG parameters + * @from_msg_param: converts from OPTEE_MSG parameters to struct tee_param + * + * These OPs are only supposed to be used internally in the OP-TEE driver + * as a way of abstracting the different methogs of entering OP-TEE in + * secure world. + */ +struct optee_ops { + int (*do_call_with_arg)(struct tee_context *ctx, + struct tee_shm *shm_arg); + int (*to_msg_param)(struct optee *optee, + struct optee_msg_param *msg_params, + size_t num_params, const struct tee_param *params); + int (*from_msg_param)(struct optee *optee, struct tee_param *params, + size_t num_params, + const struct optee_msg_param *msg_params); +}; + /** * struct optee - main service struct * @supp_teedev: supplicant device + * @ops: internal callbacks for different ways to reach secure + * world * @teedev: client device * @invoke_fn: function to issue smc or hvc * @call_queue: queue of threads waiting to call @invoke_fn @@ -86,6 +111,7 @@ struct optee_supp { struct optee { struct tee_device *supp_teedev; struct tee_device *teedev; + const struct optee_ops *ops; optee_invoke_fn *invoke_fn; struct optee_call_queue call_queue; struct optee_wait_queue wait_queue; @@ -148,7 +174,7 @@ int optee_supp_recv(struct tee_context *ctx, u32 *func, u32 *num_params, int optee_supp_send(struct tee_context *ctx, u32 ret, u32 num_params, struct tee_param *param);
-u32 optee_do_call_with_arg(struct tee_context *ctx, phys_addr_t parg); +int optee_do_call_with_arg(struct tee_context *ctx, struct tee_shm *arg); int optee_open_session(struct tee_context *ctx, struct tee_ioctl_open_session_arg *arg, struct tee_param *param); @@ -171,11 +197,6 @@ int optee_shm_register_supp(struct tee_context *ctx, struct tee_shm *shm, unsigned long start); int optee_shm_unregister_supp(struct tee_context *ctx, struct tee_shm *shm);
-int optee_from_msg_param(struct tee_param *params, size_t num_params, - const struct optee_msg_param *msg_params); -int optee_to_msg_param(struct optee_msg_param *msg_params, size_t num_params, - const struct tee_param *params); - u64 *optee_allocate_pages_list(size_t num_entries); void optee_free_pages_list(void *array, size_t num_entries); void optee_fill_pages_list(u64 *dst, struct page **pages, int num_pages, diff --git a/drivers/tee/optee/rpc.c b/drivers/tee/optee/rpc.c index a3a1aa59f639..f64a0ec472e0 100644 --- a/drivers/tee/optee/rpc.c +++ b/drivers/tee/optee/rpc.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2015-2016, Linaro Limited + * Copyright (c) 2015-2021, Linaro Limited */
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt @@ -56,6 +56,7 @@ static void handle_rpc_func_cmd_get_time(struct optee_msg_arg *arg) static void handle_rpc_func_cmd_i2c_transfer(struct tee_context *ctx, struct optee_msg_arg *arg) { + struct optee *optee = tee_get_drvdata(ctx->teedev); struct i2c_client client = { 0 }; struct tee_param *params; size_t i; @@ -79,7 +80,8 @@ static void handle_rpc_func_cmd_i2c_transfer(struct tee_context *ctx, return; }
- if (optee_from_msg_param(params, arg->num_params, arg->params)) + if (optee->ops->from_msg_param(optee, params, arg->num_params, + arg->params)) goto bad;
for (i = 0; i < arg->num_params; i++) { @@ -122,7 +124,8 @@ static void handle_rpc_func_cmd_i2c_transfer(struct tee_context *ctx, arg->ret = TEEC_ERROR_COMMUNICATION; } else { params[3].u.value.a = ret; - if (optee_to_msg_param(arg->params, arg->num_params, params)) + if (optee->ops->to_msg_param(optee, arg->params, + arg->num_params, params)) arg->ret = TEEC_ERROR_BAD_PARAMETERS; else arg->ret = TEEC_SUCCESS; @@ -234,7 +237,7 @@ static void handle_rpc_func_cmd_wait(struct optee_msg_arg *arg) arg->ret = TEEC_ERROR_BAD_PARAMETERS; }
-static void handle_rpc_supp_cmd(struct tee_context *ctx, +static void handle_rpc_supp_cmd(struct tee_context *ctx, struct optee *optee, struct optee_msg_arg *arg) { struct tee_param *params; @@ -248,14 +251,16 @@ static void handle_rpc_supp_cmd(struct tee_context *ctx, return; }
- if (optee_from_msg_param(params, arg->num_params, arg->params)) { + if (optee->ops->from_msg_param(optee, params, arg->num_params, + arg->params)) { arg->ret = TEEC_ERROR_BAD_PARAMETERS; goto out; }
arg->ret = optee_supp_thrd_req(ctx, arg->cmd, arg->num_params, params);
- if (optee_to_msg_param(arg->params, arg->num_params, params)) + if (optee->ops->to_msg_param(optee, arg->params, arg->num_params, + params)) arg->ret = TEEC_ERROR_BAD_PARAMETERS; out: kfree(params); @@ -480,7 +485,7 @@ static void handle_rpc_func_cmd(struct tee_context *ctx, struct optee *optee, handle_rpc_func_cmd_i2c_transfer(ctx, arg); break; default: - handle_rpc_supp_cmd(ctx, arg); + handle_rpc_supp_cmd(ctx, optee, arg); } }
Adds a memory pool to be used when the driver uses FF-A [1] as transport layer.
[1] https://developer.arm.com/documentation/den0077/latest Signed-off-by: Jens Wiklander jens.wiklander@linaro.org --- drivers/tee/optee/shm_pool.c | 65 +++++++++++++++++++++++++++++++++--- drivers/tee/optee/shm_pool.h | 1 + 2 files changed, 61 insertions(+), 5 deletions(-)
diff --git a/drivers/tee/optee/shm_pool.c b/drivers/tee/optee/shm_pool.c index d767eebf30bd..d2116cb39c8b 100644 --- a/drivers/tee/optee/shm_pool.c +++ b/drivers/tee/optee/shm_pool.c @@ -12,8 +12,14 @@ #include "optee_smc.h" #include "shm_pool.h"
-static int pool_op_alloc(struct tee_shm_pool_mgr *poolm, - struct tee_shm *shm, size_t size) +static int +pool_op_alloc_helper(struct tee_shm_pool_mgr *poolm, + struct tee_shm *shm, size_t size, + int (*shm_register)(struct tee_context *ctx, + struct tee_shm *shm, + struct page **pages, + size_t num_pages, + unsigned long start)) { unsigned int order = get_order(size); struct page *page; @@ -27,7 +33,7 @@ static int pool_op_alloc(struct tee_shm_pool_mgr *poolm, shm->paddr = page_to_phys(page); shm->size = PAGE_SIZE << order;
- if (shm->flags & TEE_SHM_DMA_BUF) { + if (shm_register) { unsigned int nr_pages = 1 << order, i; struct page **pages;
@@ -41,14 +47,23 @@ static int pool_op_alloc(struct tee_shm_pool_mgr *poolm, }
shm->flags |= TEE_SHM_REGISTER; - rc = optee_shm_register(shm->ctx, shm, pages, nr_pages, - (unsigned long)shm->kaddr); + rc = shm_register(shm->ctx, shm, pages, nr_pages, + (unsigned long)shm->kaddr); kfree(pages); }
return rc; }
+static int pool_op_alloc(struct tee_shm_pool_mgr *poolm, + struct tee_shm *shm, size_t size) +{ + if (!(shm->flags & TEE_SHM_DMA_BUF)) + return pool_op_alloc_helper(poolm, shm, size, NULL); + + return pool_op_alloc_helper(poolm, shm, size, optee_shm_register); +} + static void pool_op_free(struct tee_shm_pool_mgr *poolm, struct tee_shm *shm) { @@ -87,3 +102,43 @@ struct tee_shm_pool_mgr *optee_shm_pool_alloc_pages(void)
return mgr; } + +#ifdef CONFIG_ARM_FFA_TRANSPORT +static int pool_ffa_op_alloc(struct tee_shm_pool_mgr *poolm, + struct tee_shm *shm, size_t size) +{ + return pool_op_alloc_helper(poolm, shm, size, optee_ffa_shm_register); +} + +static void pool_ffa_op_free(struct tee_shm_pool_mgr *poolm, + struct tee_shm *shm) +{ + optee_ffa_shm_unregister(shm->ctx, shm); + free_pages((unsigned long)shm->kaddr, get_order(shm->size)); + shm->kaddr = NULL; +} + +static const struct tee_shm_pool_mgr_ops pool_ffa_ops = { + .alloc = pool_ffa_op_alloc, + .free = pool_ffa_op_free, + .destroy_poolmgr = pool_op_destroy_poolmgr, +}; + +/** + * optee_ffa_shm_pool_alloc_pages() - create page-based allocator pool + * + * This pool is used with OP-TEE over FF-A. In this case command buffers + * and such are allocated from kernel's own memory. + */ +struct tee_shm_pool_mgr *optee_ffa_shm_pool_alloc_pages(void) +{ + struct tee_shm_pool_mgr *mgr = kzalloc(sizeof(*mgr), GFP_KERNEL); + + if (!mgr) + return ERR_PTR(-ENOMEM); + + mgr->ops = &pool_ffa_ops; + + return mgr; +} +#endif /*CONFIG_ARM_FFA_TRANSPORT*/ diff --git a/drivers/tee/optee/shm_pool.h b/drivers/tee/optee/shm_pool.h index 28109d991c4b..34c5fd74a3ff 100644 --- a/drivers/tee/optee/shm_pool.h +++ b/drivers/tee/optee/shm_pool.h @@ -10,5 +10,6 @@ #include <linux/tee_drv.h>
struct tee_shm_pool_mgr *optee_shm_pool_alloc_pages(void); +struct tee_shm_pool_mgr *optee_ffa_shm_pool_alloc_pages(void);
#endif
Adds support for using FF-A [1] as transport to the OP-TEE driver.
Introduces struct optee_msg_param_fmem which carries all information needed when OP-TEE is calling FFA_MEM_RETRIEVE_REQ to get the shared memory reference mapped by the hypervisor in S-EL2. Register usage is also updated to include the information needed.
The FF-A part of this driver is enabled if CONFIG_ARM_FFA_TRANSPORT is enabled.
[1] https://developer.arm.com/documentation/den0077/latest Signed-off-by: Jens Wiklander jens.wiklander@linaro.org --- drivers/tee/optee/call.c | 222 +++++++++++++- drivers/tee/optee/core.c | 485 +++++++++++++++++++++++++++++- drivers/tee/optee/optee_ffa.h | 153 ++++++++++ drivers/tee/optee/optee_msg.h | 27 +- drivers/tee/optee/optee_private.h | 52 ++++ drivers/tee/optee/rpc.c | 118 ++++++++ 6 files changed, 1041 insertions(+), 16 deletions(-) create mode 100644 drivers/tee/optee/optee_ffa.h
diff --git a/drivers/tee/optee/call.c b/drivers/tee/optee/call.c index ef049c0dee50..c559f82e1343 100644 --- a/drivers/tee/optee/call.c +++ b/drivers/tee/optee/call.c @@ -3,17 +3,20 @@ * Copyright (c) 2015-2021, Linaro Limited */ #include <linux/arm-smccc.h> +#include <linux/arm_ffa.h> #include <linux/device.h> #include <linux/err.h> #include <linux/errno.h> #include <linux/mm.h> #include <linux/sched.h> +#include <linux/scatterlist.h> #include <linux/slab.h> #include <linux/tee_drv.h> #include <linux/types.h> #include <linux/uaccess.h> #include "optee_private.h" #include "optee_smc.h" +#include "optee_ffa.h"
struct optee_call_waiter { struct list_head list_node; @@ -180,29 +183,33 @@ int optee_do_call_with_arg(struct tee_context *ctx, struct tee_shm *arg) static struct tee_shm *get_msg_arg(struct tee_context *ctx, size_t num_params, struct optee_msg_arg **msg_arg) { - int rc; + struct optee *optee = tee_get_drvdata(ctx->teedev); + size_t sz = OPTEE_MSG_GET_ARG_SIZE(num_params); struct tee_shm *shm; struct optee_msg_arg *ma;
- shm = tee_shm_alloc(ctx, OPTEE_MSG_GET_ARG_SIZE(num_params), - TEE_SHM_MAPPED); + /* + * rpc_arg_count is set to the number of allocated parameters in + * the RPC argument struct if a second MSG arg struct is expected. + * The second arg struct will then be used for RPC. So far only + * enabled when using FF-A as transport layer. + */ + if (optee->rpc_arg_count) + sz += OPTEE_MSG_GET_ARG_SIZE(optee->rpc_arg_count); + + shm = tee_shm_alloc(ctx, sz, TEE_SHM_MAPPED); if (IS_ERR(shm)) return shm;
ma = tee_shm_get_va(shm, 0); if (IS_ERR(ma)) { - rc = PTR_ERR(ma); - goto out; + tee_shm_free(shm); + return (void *)ma; }
memset(ma, 0, OPTEE_MSG_GET_ARG_SIZE(num_params)); ma->num_params = num_params; *msg_arg = ma; -out: - if (rc) { - tee_shm_free(shm); - return ERR_PTR(rc); - }
return shm; } @@ -677,3 +684,198 @@ int optee_shm_unregister_supp(struct tee_context *ctx, struct tee_shm *shm) { return 0; } + +#ifdef CONFIG_ARM_FFA_TRANSPORT +static int optee_ffa_yielding_call(struct tee_context *ctx, + struct ffa_send_direct_data *data, + struct optee_msg_arg *rpc_arg) +{ + struct optee *optee = tee_get_drvdata(ctx->teedev); + const struct ffa_dev_ops *ffa_ops = optee->ffa.ffa_ops; + struct ffa_device *ffa_dev = optee->ffa.ffa_dev; + struct optee_call_waiter w; + u32 cmd = data->data0; + u32 w4 = data->data1; + u32 w5 = data->data2; + u32 w6 = data->data3; + int rc; + + /* Initialize waiter */ + optee_cq_wait_init(&optee->call_queue, &w); + while (true) { + rc = ffa_ops->sync_send_receive(ffa_dev, data); + if (rc) + goto done; + + switch ((int)data->data0) { + case TEEC_SUCCESS: + break; + case TEEC_ERROR_BUSY: + if (cmd == OPTEE_FFA_YIELDING_CALL_RESUME) { + rc = -EIO; + goto done; + } + + /* + * Out of threads in secure world, wait for a thread + * become available. + */ + optee_cq_wait_for_completion(&optee->call_queue, &w); + data->data0 = cmd; + data->data1 = w4; + data->data2 = w5; + data->data3 = w6; + continue; + default: + rc = -EIO; + goto done; + } + + if (data->data1 == OPTEE_FFA_YIELDING_CALL_RETURN_DONE) + goto done; + + /* + * OP-TEE has returned with a RPC request. + * + * Note that data->data4 (passed in register w7) is already + * filled in by ffa_ops->sync_send_receive() returning + * above. + */ + cond_resched(); + optee_handle_ffa_rpc(ctx, data->data1, rpc_arg); + cmd = OPTEE_FFA_YIELDING_CALL_RESUME; + data->data0 = cmd; + data->data1 = 0; + data->data2 = 0; + data->data3 = 0; + } +done: + /* + * We're done with our thread in secure world, if there's any + * thread waiters wake up one. + */ + optee_cq_wait_final(&optee->call_queue, &w); + + return rc; +} + +/** + * optee_ffa_do_call_with_arg() - Do a FF-A call to enter OP-TEE in secure world + * @ctx: calling context + * @shm: shared memory holding the message to pass to secure world + * + * Does a FF-A call to OP-TEE in secure world and handles eventual resulting + * Remote Procedure Calls (RPC) from OP-TEE. + * + * Returns return code from FF-A, 0 is OK + */ + +int optee_ffa_do_call_with_arg(struct tee_context *ctx, struct tee_shm *shm) +{ + struct ffa_send_direct_data data = { + .data0 = OPTEE_FFA_YIELDING_CALL_WITH_ARG, + .data1 = (u32)shm->sec_world_id, + .data2 = (u32)(shm->sec_world_id >> 32), + .data3 = shm->offset, + }; + struct optee_msg_arg *arg = tee_shm_get_va(shm, 0); + unsigned int rpc_arg_offs = OPTEE_MSG_GET_ARG_SIZE(arg->num_params); + struct optee_msg_arg *rpc_arg = tee_shm_get_va(shm, rpc_arg_offs); + + return optee_ffa_yielding_call(ctx, &data, rpc_arg); +} + +int optee_ffa_shm_register(struct tee_context *ctx, struct tee_shm *shm, + struct page **pages, size_t num_pages, + unsigned long start) +{ + struct optee *optee = tee_get_drvdata(ctx->teedev); + const struct ffa_dev_ops *ffa_ops = optee->ffa.ffa_ops; + struct ffa_device *ffa_dev = optee->ffa.ffa_dev; + struct ffa_mem_region_attributes mem_attr = { + .receiver = ffa_dev->vm_id, + .attrs = FFA_MEM_RW, + }; + struct ffa_mem_ops_args args = { + .use_txbuf = true, + .attrs = &mem_attr, + .nattrs = 1, + }; + struct sg_table sgt; + int rc; + + rc = check_mem_type(start, num_pages); + if (rc) + return rc; + + rc = sg_alloc_table_from_pages(&sgt, pages, num_pages, 0, + num_pages * PAGE_SIZE, GFP_KERNEL); + if (rc) + return rc; + args.sg = sgt.sgl; + rc = ffa_ops->memory_share(ffa_dev, &args); + sg_free_table(&sgt); + if (rc) + return rc; + + rc = optee_shm_add_ffa_handle(optee, shm, args.g_handle); + if (rc) { + ffa_ops->memory_reclaim(args.g_handle, 0); + return rc; + } + + shm->sec_world_id = args.g_handle; + + return 0; +} + +int optee_ffa_shm_unregister(struct tee_context *ctx, struct tee_shm *shm) +{ + struct optee *optee = tee_get_drvdata(ctx->teedev); + const struct ffa_dev_ops *ffa_ops = optee->ffa.ffa_ops; + struct ffa_device *ffa_dev = optee->ffa.ffa_dev; + u64 global_handle = shm->sec_world_id; + struct ffa_send_direct_data data = { + .data0 = OPTEE_FFA_UNREGISTER_SHM, + .data1 = (u32)global_handle, + .data2 = (u32)(global_handle >> 32) + }; + int rc; + + optee_shm_rem_ffa_handle(optee, global_handle); + shm->sec_world_id = 0; + + rc = ffa_ops->sync_send_receive(ffa_dev, &data); + if (rc) + pr_err("Unregister SHM id 0x%llx rc %d\n", global_handle, rc); + + rc = ffa_ops->memory_reclaim(global_handle, 0); + if (rc) + pr_err("mem_reclain: 0x%llx %d", global_handle, rc); + + return rc; +} + +int optee_ffa_shm_unregister_supp(struct tee_context *ctx, struct tee_shm *shm) +{ + struct optee *optee = tee_get_drvdata(ctx->teedev); + const struct ffa_dev_ops *ffa_ops = optee->ffa.ffa_ops; + u64 global_handle = shm->sec_world_id; + int rc; + + /* + * We're skipping the OPTEE_FFA_YIELDING_CALL_UNREGISTER_SHM call + * since this is OP-TEE freeing via RPC so it has already retired + * this ID. + */ + + optee_shm_rem_ffa_handle(optee, global_handle); + rc = ffa_ops->memory_reclaim(global_handle, 0); + if (rc) + pr_err("mem_reclain: 0x%llx %d", global_handle, rc); + + shm->sec_world_id = 0; + + return rc; +} +#endif /*CONFIG_ARM_FFA_TRANSPORT*/ diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c index ab602bb8e14a..224d402bac98 100644 --- a/drivers/tee/optee/core.c +++ b/drivers/tee/optee/core.c @@ -6,6 +6,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/arm-smccc.h> +#include <linux/arm_ffa.h> #include <linux/errno.h> #include <linux/io.h> #include <linux/module.h> @@ -20,6 +21,7 @@ #include <linux/workqueue.h> #include "optee_private.h" #include "optee_smc.h" +#include "optee_ffa.h" #include "shm_pool.h"
#define DRIVER_NAME "optee" @@ -299,10 +301,9 @@ static int optee_open(struct tee_context *ctx) mutex_init(&ctxdata->mutex); INIT_LIST_HEAD(&ctxdata->sess_list);
- if (optee->sec_caps & OPTEE_SMC_SEC_CAP_MEMREF_NULL) - ctx->cap_memref_null = true; - else - ctx->cap_memref_null = false; + ctx->cap_memref_null = optee_is_ffa_based(optee) || + (optee->sec_caps & + OPTEE_SMC_SEC_CAP_MEMREF_NULL);
ctx->data = ctxdata; return 0; @@ -567,6 +568,471 @@ optee_config_shm_memremap(optee_invoke_fn *invoke_fn, void **memremaped_shm) return rc; }
+#ifdef CONFIG_ARM_FFA_TRANSPORT +static void optee_ffa_get_version(struct tee_device *teedev, + struct tee_ioctl_version_data *vers) +{ + struct tee_ioctl_version_data v = { + .impl_id = TEE_IMPL_ID_OPTEE, + .impl_caps = TEE_OPTEE_CAP_TZ, + .gen_caps = TEE_GEN_CAP_GP | TEE_GEN_CAP_REG_MEM | + TEE_GEN_CAP_MEMREF_NULL, + }; + + *vers = v; +} + +struct shm_rhash { + struct tee_shm *shm; + u64 global_id; + struct rhash_head linkage; +}; + +static void rh_free_fn(void *ptr, void *arg) +{ + kfree(ptr); +} + +static const struct rhashtable_params shm_rhash_params = { + .head_offset = offsetof(struct shm_rhash, linkage), + .key_len = sizeof(u64), + .key_offset = offsetof(struct shm_rhash, global_id), + .automatic_shrinking = true, +}; + +struct tee_shm *optee_shm_from_ffa_handle(struct optee *optee, u64 global_id) +{ + struct tee_shm *shm = NULL; + struct shm_rhash *r; + + mutex_lock(&optee->ffa.mutex); + r = rhashtable_lookup_fast(&optee->ffa.global_ids, &global_id, + shm_rhash_params); + if (r) + shm = r->shm; + mutex_unlock(&optee->ffa.mutex); + + return shm; +} + +int optee_shm_add_ffa_handle(struct optee *optee, struct tee_shm *shm, + u64 global_id) +{ + struct shm_rhash *r; + int rc; + + r = kmalloc(sizeof(*r), GFP_KERNEL); + if (!r) + return -ENOMEM; + r->shm = shm; + r->global_id = global_id; + + mutex_lock(&optee->ffa.mutex); + rc = rhashtable_lookup_insert_fast(&optee->ffa.global_ids, &r->linkage, + shm_rhash_params); + mutex_unlock(&optee->ffa.mutex); + + if (rc) + kfree(r); + + return rc; +} + +int optee_shm_rem_ffa_handle(struct optee *optee, u64 global_id) +{ + struct shm_rhash *r; + int rc = -ENOENT; + + mutex_lock(&optee->ffa.mutex); + r = rhashtable_lookup_fast(&optee->ffa.global_ids, &global_id, + shm_rhash_params); + if (r) + rc = rhashtable_remove_fast(&optee->ffa.global_ids, + &r->linkage, shm_rhash_params); + mutex_unlock(&optee->ffa.mutex); + + if (!rc) + kfree(r); + + return rc; +} + +static void from_msg_param_ffa_mem(struct optee *optee, struct tee_param *p, + u32 attr, const struct optee_msg_param *mp) +{ + struct tee_shm *shm = NULL; + u64 offs_high = 0; + u64 offs_low = 0; + + p->attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT + + attr - OPTEE_MSG_ATTR_TYPE_FMEM_INPUT; + p->u.memref.size = mp->u.fmem.size; + + if (mp->u.fmem.global_id != OPTEE_MSG_FMEM_INVALID_GLOBAL_ID) + shm = optee_shm_from_ffa_handle(optee, mp->u.fmem.global_id); + p->u.memref.shm = shm; + + if (shm) { + offs_low = mp->u.fmem.offs_low; + offs_high = mp->u.fmem.offs_high; + } + p->u.memref.shm_offs = offs_low | offs_high << 32; +} + +/** + * optee_ffa_from_msg_param() - convert from OPTEE_MSG parameters to + * struct tee_param + * @optee: main service struct + * @params: subsystem internal parameter representation + * @num_params: number of elements in the parameter arrays + * @msg_params: OPTEE_MSG parameters + * + * Returns 0 on success or <0 on failure + */ +static int optee_ffa_from_msg_param(struct optee *optee, + struct tee_param *params, size_t num_params, + const struct optee_msg_param *msg_params) +{ + size_t n; + + for (n = 0; n < num_params; n++) { + struct tee_param *p = params + n; + const struct optee_msg_param *mp = msg_params + n; + u32 attr = mp->attr & OPTEE_MSG_ATTR_TYPE_MASK; + + switch (attr) { + case OPTEE_MSG_ATTR_TYPE_NONE: + p->attr = TEE_IOCTL_PARAM_ATTR_TYPE_NONE; + memset(&p->u, 0, sizeof(p->u)); + break; + case OPTEE_MSG_ATTR_TYPE_VALUE_INPUT: + case OPTEE_MSG_ATTR_TYPE_VALUE_OUTPUT: + case OPTEE_MSG_ATTR_TYPE_VALUE_INOUT: + from_msg_param_value(p, attr, mp); + break; + case OPTEE_MSG_ATTR_TYPE_FMEM_INPUT: + case OPTEE_MSG_ATTR_TYPE_FMEM_OUTPUT: + case OPTEE_MSG_ATTR_TYPE_FMEM_INOUT: + from_msg_param_ffa_mem(optee, p, attr, mp); + break; + default: + return -EINVAL; + } + } + + return 0; +} + +static int to_msg_param_ffa_mem(struct optee_msg_param *mp, + const struct tee_param *p) +{ + struct tee_shm *shm = p->u.memref.shm; + + mp->attr = OPTEE_MSG_ATTR_TYPE_FMEM_INPUT + p->attr - + TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT; + + if (shm) { + u64 shm_offs = p->u.memref.shm_offs; + + mp->u.fmem.internal_offs = shm->offset; + + mp->u.fmem.offs_low = shm_offs; + mp->u.fmem.offs_high = shm_offs >> 32; + /* Check that the entire offset could be stored. */ + if (mp->u.fmem.offs_high != shm_offs >> 32) + return -EINVAL; + + mp->u.fmem.global_id = shm->sec_world_id; + } else { + memset(&mp->u, 0, sizeof(mp->u)); + mp->u.fmem.global_id = OPTEE_MSG_FMEM_INVALID_GLOBAL_ID; + } + mp->u.fmem.size = p->u.memref.size; + + return 0; +} + +/** + * optee_to_msg_param() - convert from struct tee_params to OPTEE_MSG parameters + * @optee: main service struct + * @msg_params: OPTEE_MSG parameters + * @num_params: number of elements in the parameter arrays + * @params: subsystem itnernal parameter representation + * Returns 0 on success or <0 on failure + */ +static int optee_ffa_to_msg_param(struct optee *optee, + struct optee_msg_param *msg_params, + size_t num_params, + const struct tee_param *params) +{ + size_t n; + + for (n = 0; n < num_params; n++) { + const struct tee_param *p = params + n; + struct optee_msg_param *mp = msg_params + n; + + switch (p->attr) { + case TEE_IOCTL_PARAM_ATTR_TYPE_NONE: + mp->attr = TEE_IOCTL_PARAM_ATTR_TYPE_NONE; + memset(&mp->u, 0, sizeof(mp->u)); + break; + case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT: + case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT: + case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT: + to_msg_param_value(mp, p); + 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: + if (to_msg_param_ffa_mem(mp, p)) + return -EINVAL; + break; + default: + return -EINVAL; + } + } + + return 0; +} + +static bool optee_ffa_api_is_compatbile(struct ffa_device *ffa_dev, + const struct ffa_dev_ops *ops) +{ + struct ffa_send_direct_data data = { OPTEE_FFA_GET_API_VERSION }; + int rc; + + ops->mode_32bit_set(ffa_dev); + + rc = ops->sync_send_receive(ffa_dev, &data); + if (rc) { + pr_err("Unexpected error %d\n", rc); + return false; + } + if (data.data0 != OPTEE_FFA_VERSION_MAJOR || + data.data1 < OPTEE_FFA_VERSION_MINOR) { + pr_err("Incompatible OP-TEE API version %lu.%lu", + data.data0, data.data1); + return false; + } + + data = (struct ffa_send_direct_data){ OPTEE_FFA_GET_OS_VERSION }; + rc = ops->sync_send_receive(ffa_dev, &data); + if (rc) { + pr_err("Unexpected error %d\n", rc); + return false; + } + if (data.data2) + pr_info("revision %lu.%lu (%08lx)", + data.data0, data.data1, data.data2); + else + pr_info("revision %lu.%lu", data.data0, data.data1); + + return true; +} + +static bool optee_ffa_exchange_caps(struct ffa_device *ffa_dev, + const struct ffa_dev_ops *ops, + u32 *sec_caps, unsigned int *rpc_arg_count) +{ + struct ffa_send_direct_data data = { OPTEE_FFA_EXCHANGE_CAPABILITIES }; + int rc; + + rc = ops->sync_send_receive(ffa_dev, &data); + if (rc) { + pr_err("Unexpected error %d", rc); + return false; + } + if (data.data0) { + pr_err("Unexpected exchange error %lu", data.data0); + return false; + } + + *sec_caps = 0; + *rpc_arg_count = (u8)data.data1; + + return true; +} + +static struct tee_shm_pool *optee_ffa_config_dyn_shm(void) +{ + struct tee_shm_pool_mgr *priv_mgr; + struct tee_shm_pool_mgr *dmabuf_mgr; + void *rc; + + rc = optee_ffa_shm_pool_alloc_pages(); + if (IS_ERR(rc)) + return rc; + priv_mgr = rc; + + rc = optee_ffa_shm_pool_alloc_pages(); + if (IS_ERR(rc)) { + tee_shm_pool_mgr_destroy(priv_mgr); + return rc; + } + dmabuf_mgr = rc; + + rc = tee_shm_pool_alloc(priv_mgr, dmabuf_mgr); + if (IS_ERR(rc)) { + tee_shm_pool_mgr_destroy(priv_mgr); + tee_shm_pool_mgr_destroy(dmabuf_mgr); + } + + return rc; +} + +static const struct tee_driver_ops optee_ffa_clnt_ops = { + .get_version = optee_ffa_get_version, + .open = optee_open, + .release = optee_release, + .open_session = optee_open_session, + .close_session = optee_close_session, + .invoke_func = optee_invoke_func, + .cancel_req = optee_cancel_req, + .shm_register = optee_ffa_shm_register, + .shm_unregister = optee_ffa_shm_unregister, +}; + +static const struct tee_desc optee_ffa_clnt_desc = { + .name = DRIVER_NAME "ffa-clnt", + .ops = &optee_ffa_clnt_ops, + .owner = THIS_MODULE, +}; + +static const struct tee_driver_ops optee_ffa_supp_ops = { + .get_version = optee_ffa_get_version, + .open = optee_open, + .release = optee_release_supp, + .supp_recv = optee_supp_recv, + .supp_send = optee_supp_send, + .shm_register = optee_ffa_shm_register, /* same as for clnt ops */ + .shm_unregister = optee_ffa_shm_unregister_supp, +}; + +static const struct tee_desc optee_ffa_supp_desc = { + .name = DRIVER_NAME "ffa-supp", + .ops = &optee_ffa_supp_ops, + .owner = THIS_MODULE, + .flags = TEE_DESC_PRIVILEGED, +}; + +static const struct optee_ops optee_ffa_ops = { + .do_call_with_arg = optee_ffa_do_call_with_arg, + .to_msg_param = optee_ffa_to_msg_param, + .from_msg_param = optee_ffa_from_msg_param, +}; + +static void optee_ffa_remove(struct ffa_device *ffa_dev) +{ + (void)ffa_dev; +} + +static int optee_ffa_probe(struct ffa_device *ffa_dev) +{ + const struct ffa_dev_ops *ffa_ops; + unsigned int rpc_arg_count; + struct tee_device *teedev; + struct optee *optee; + u32 sec_caps; + int rc; + + ffa_ops = ffa_dev_ops_get(ffa_dev); + if (!ffa_ops) { + pr_warn("failed "method" init: ffa\n"); + return -ENOENT; + } + + if (!optee_ffa_api_is_compatbile(ffa_dev, ffa_ops)) + return -EINVAL; + + if (!optee_ffa_exchange_caps(ffa_dev, ffa_ops, &sec_caps, + &rpc_arg_count)) + return -EINVAL; + + optee = kzalloc(sizeof(*optee), GFP_KERNEL); + if (!optee) { + rc = -ENOMEM; + goto err; + } + optee->pool = optee_ffa_config_dyn_shm(); + if (IS_ERR(optee->pool)) { + rc = PTR_ERR(optee->pool); + optee->pool = NULL; + goto err; + } + + optee->ops = &optee_ffa_ops; + optee->ffa.ffa_dev = ffa_dev; + optee->ffa.ffa_ops = ffa_ops; + optee->sec_caps = sec_caps; + optee->rpc_arg_count = rpc_arg_count; + + teedev = tee_device_alloc(&optee_ffa_clnt_desc, NULL, optee->pool, + optee); + if (IS_ERR(teedev)) { + rc = PTR_ERR(teedev); + goto err; + } + optee->teedev = teedev; + + teedev = tee_device_alloc(&optee_ffa_supp_desc, NULL, optee->pool, + optee); + if (IS_ERR(teedev)) { + rc = PTR_ERR(teedev); + goto err; + } + optee->supp_teedev = teedev; + + rc = tee_device_register(optee->teedev); + if (rc) + goto err; + + rc = tee_device_register(optee->supp_teedev); + if (rc) + goto err; + + rc = rhashtable_init(&optee->ffa.global_ids, &shm_rhash_params); + if (rc) + goto err; + mutex_init(&optee->ffa.mutex); + mutex_init(&optee->call_queue.mutex); + INIT_LIST_HEAD(&optee->call_queue.waiters); + optee_wait_queue_init(&optee->wait_queue); + optee_supp_init(&optee->supp); + ffa_dev_set_drvdata(ffa_dev, optee); + + pr_info("initialized driver\n"); + return 0; +err: + /* + * tee_device_unregister() is safe to call even if the + * devices hasn't been registered with + * tee_device_register() yet. + */ + tee_device_unregister(optee->supp_teedev); + tee_device_unregister(optee->teedev); + if (optee->pool) + tee_shm_pool_free(optee->pool); + kfree(optee); + return rc; +} + +static const struct ffa_device_id optee_ffa_device_id[] = { + /* 486178e0-e7f8-11e3-bc5e0002a5d5c51b */ + { UUID_INIT(0x486178e0, 0xe7f8, 0x11e3, + 0xbc, 0x5e, 0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b) }, + {} +}; + +static struct ffa_driver optee_ffa_driver = { + .name = "optee", + .probe = optee_ffa_probe, + .remove = optee_ffa_remove, + .id_table = optee_ffa_device_id, +}; + +module_ffa_driver(optee_ffa_driver); +#endif /*CONFIG_ARM_FFA_TRANSPORT*/ + /* Simple wrapper functions to be able to use a function pointer */ static void optee_smccc_smc(unsigned long a0, unsigned long a1, unsigned long a2, unsigned long a3, @@ -615,7 +1081,8 @@ static int optee_remove(struct platform_device *pdev) * reference counters and also avoid wild pointers in secure world * into the old shared memory range. */ - optee_disable_shm_cache(optee); + if (!optee_is_ffa_based(optee)) + optee_disable_shm_cache(optee);
/* * The two devices have to be unregistered before we can free the @@ -631,6 +1098,14 @@ static int optee_remove(struct platform_device *pdev) optee_supp_uninit(&optee->supp); mutex_destroy(&optee->call_queue.mutex);
+#ifdef CONFIG_ARM_FFA_TRANSPORT + if (optee->ffa.ffa_ops) { + mutex_destroy(&optee->ffa.mutex); + rhashtable_free_and_destroy(&optee->ffa.global_ids, + rh_free_fn, NULL); + } +#endif /*CONFIG_ARM_FFA_TRANSPORT*/ + kfree(optee);
return 0; diff --git a/drivers/tee/optee/optee_ffa.h b/drivers/tee/optee/optee_ffa.h new file mode 100644 index 000000000000..ee3a03fc392c --- /dev/null +++ b/drivers/tee/optee/optee_ffa.h @@ -0,0 +1,153 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) 2019-2021, Linaro Limited + */ + +/* + * This file is exported by OP-TEE and is kept in sync between secure world + * and normal world drivers. We're using ARM FF-A 1.0 specification. + */ + +#ifndef __OPTEE_FFA_H +#define __OPTEE_FFA_H + +#include <linux/arm_ffa.h> + +/* + * Normal world sends requests with FFA_MSG_SEND_DIRECT_REQ and + * responses are returned with FFA_MSG_SEND_DIRECT_RESP for normal + * messages. + * + * All requests with FFA_MSG_SEND_DIRECT_REQ and FFA_MSG_SEND_DIRECT_RESP + * are using the AArch32 SMC calling convention with register usage as + * defined in FF-A specification: + * w0: Function ID (0x8400006F or 0x84000070) + * w1: Source/Destination IDs + * w2: Reserved (MBZ) + * w3-w7: Implementation defined, free to be used below + */ + +#define OPTEE_FFA_VERSION_MAJOR 1 +#define OPTEE_FFA_VERSION_MINOR 0 + +#define OPTEE_FFA_BLOCKING_CALL(id) (id) +#define OPTEE_FFA_YIELDING_CALL_BIT 31 +#define OPTEE_FFA_YIELDING_CALL(id) ((id) | BIT(OPTEE_FFA_YIELDING_CALL_BIT)) + +/* + * Returns the API version implemented, currently follows the FF-A version. + * Call register usage: + * w3: Service ID, OPTEE_FFA_GET_API_VERSION + * w4-w7: Not used (MBZ) + * + * Return register usage: + * w3: OPTEE_FFA_VERSION_MAJOR + * w4: OPTEE_FFA_VERSION_MINOR + * w5-w7: Not used (MBZ) + */ +#define OPTEE_FFA_GET_API_VERSION OPTEE_FFA_BLOCKING_CALL(0) + +/* + * Returns the revision of OP-TEE. + * + * Used by non-secure world to figure out which version of the Trusted OS + * is installed. Note that the returned revision is the revision of the + * Trusted OS, not of the API. + * + * Call register usage: + * w3: Service ID, OPTEE_FFA_GET_OS_VERSION + * w4-w7: Unused (MBZ) + * + * Return register usage: + * w3: CFG_OPTEE_REVISION_MAJOR + * w4: CFG_OPTEE_REVISION_MINOR + * w5: TEE_IMPL_GIT_SHA1 (or zero if not supported) + */ +#define OPTEE_FFA_GET_OS_VERSION OPTEE_FFA_BLOCKING_CALL(1) + +/* + * Exchange capabilities between normal world and secure world. + * + * Currently there are no defined capabilities. When features are added new + * capabilities may be added. + * + * Call register usage: + * w3: Service ID, OPTEE_FFA_EXCHANGE_CAPABILITIES + * w4-w7: Note used (MBZ) + * + * Return register usage: + * w3: Error code, 0 on success + * w4: Bit[7:0]: Number of parameters needed for RPC to be supplied + * as the second MSG arg struct for + * OPTEE_FFA_YIELDING_CALL_WITH_ARG. + * Bit[31:8]: Reserved (MBZ) + * w5-w7: Note used (MBZ) + */ +#define OPTEE_FFA_EXCHANGE_CAPABILITIES OPTEE_FFA_BLOCKING_CALL(2) + +/* + * Unregister shared memory + * + * Call register usage: + * w3: Service ID, OPTEE_FFA_YIELDING_CALL_UNREGISTER_SHM + * w4: Shared memory handle, lower bits + * w5: Shared memory handle, higher bits + * w6-w7: Not used (MBZ) + * + * Return register usage: + * w3: Error code, 0 on success + * w4-w7: Note used (MBZ) + */ +#define OPTEE_FFA_UNREGISTER_SHM OPTEE_FFA_BLOCKING_CALL(3) + +/* + * Call with struct optee_msg_arg as argument in the supplied shared memory + * with a zero internal offset and normal cached memory attributes. + * Register usage: + * w3: Service ID, OPTEE_FFA_YIELDING_CALL_WITH_ARG + * w4: Lower 32 bits of a 64-bit Shared memory handle + * w5: Upper 32 bits of a 64-bit Shared memory handle + * w6: Offset into shared memory pointing to a struct optee_msg_arg + * right after the parameters of this struct (at offset + * OPTEE_MSG_GET_ARG_SIZE(num_params) follows a struct optee_msg_arg + * for RPC, this struct has reserved space for the number of RPC + * parameters as returned by OPTEE_FFA_EXCHANGE_CAPABILITIES. + * w7: Not used (MBZ) + * Resume from RPC. Register usage: + * w3: Service ID, OPTEE_FFA_YIELDING_CALL_RESUME + * w4-w6: Not used (MBZ) + * w7: Resume info + * + * Normal return (yielding call is completed). Register usage: + * w3: Error code, 0 on success + * w4: OPTEE_FFA_YIELDING_CALL_RETURN_DONE + * w5-w7: Not used (MBZ) + * + * RPC interrupt return (RPC from secure world). Register usage: + * w3: Error code == 0 + * w4: Any defined RPC code but OPTEE_FFA_YIELDING_CALL_RETURN_DONE + * w5-w6: Not used (MBZ) + * w7: Resume info + * + * Possible error codes in register w3: + * 0: Success + * FFA_DENIED: w4 isn't one of OPTEE_FFA_YIELDING_CALL_START + * OPTEE_FFA_YIELDING_CALL_RESUME + * + * Possible error codes for OPTEE_FFA_YIELDING_CALL_START, + * FFA_BUSY: Number of OP-TEE OS threads exceeded, + * try again later + * FFA_DENIED: RPC shared memory object not found + * FFA_INVALID_PARAMETER: Bad shared memory handle or offset into the memory + * + * Possible error codes for OPTEE_FFA_YIELDING_CALL_RESUME + * FFA_INVALID_PARAMETER: Bad resume info + */ +#define OPTEE_FFA_YIELDING_CALL_WITH_ARG OPTEE_FFA_YIELDING_CALL(0) +#define OPTEE_FFA_YIELDING_CALL_RESUME OPTEE_FFA_YIELDING_CALL(1) + +#define OPTEE_FFA_YIELDING_CALL_RETURN_DONE 0 +#define OPTEE_FFA_YIELDING_CALL_RETURN_RPC_CMD 1 +#define OPTEE_FFA_YIELDING_CALL_RETURN_INTERRUPT 2 + +#endif /*__OPTEE_FFA_H*/ diff --git a/drivers/tee/optee/optee_msg.h b/drivers/tee/optee/optee_msg.h index de96882f7b9b..fa03daffbfb3 100644 --- a/drivers/tee/optee/optee_msg.h +++ b/drivers/tee/optee/optee_msg.h @@ -24,6 +24,9 @@ #define OPTEE_MSG_ATTR_TYPE_RMEM_INPUT 0x5 #define OPTEE_MSG_ATTR_TYPE_RMEM_OUTPUT 0x6 #define OPTEE_MSG_ATTR_TYPE_RMEM_INOUT 0x7 +#define OPTEE_MSG_ATTR_TYPE_FMEM_INPUT OPTEE_MSG_ATTR_TYPE_RMEM_INPUT +#define OPTEE_MSG_ATTR_TYPE_FMEM_OUTPUT OPTEE_MSG_ATTR_TYPE_RMEM_OUTPUT +#define OPTEE_MSG_ATTR_TYPE_FMEM_INOUT OPTEE_MSG_ATTR_TYPE_RMEM_INOUT #define OPTEE_MSG_ATTR_TYPE_TMEM_INPUT 0x9 #define OPTEE_MSG_ATTR_TYPE_TMEM_OUTPUT 0xa #define OPTEE_MSG_ATTR_TYPE_TMEM_INOUT 0xb @@ -92,6 +95,8 @@ */ #define OPTEE_MSG_NONCONTIG_PAGE_SIZE 4096
+#define OPTEE_MSG_FMEM_INVALID_GLOBAL_ID 0xffffffffffffffff + /** * struct optee_msg_param_tmem - temporary memory reference parameter * @buf_ptr: Address of the buffer @@ -123,6 +128,23 @@ struct optee_msg_param_rmem { u64 shm_ref; };
+/** + * struct optee_msg_param_fmem - ffa memory reference parameter + * @offs_lower: Lower bits of offset into shared memory reference + * @offs_upper: Upper bits of offset into shared memory reference + * @internal_offs: Internal offset into the first page of shared memory + * reference + * @size: Size of the buffer + * @global_id: Global identifier of Shared memory + */ +struct optee_msg_param_fmem { + u32 offs_low; + u16 offs_high; + u16 internal_offs; + u64 size; + u64 global_id; +}; + /** * struct optee_msg_param_value - opaque value parameter * @@ -139,12 +161,14 @@ struct optee_msg_param_value { * @attr: attributes * @tmem: parameter by temporary memory reference * @rmem: parameter by registered memory reference + * @fmem: parameter by ffa registered memory reference * @value: parameter by opaque value * * @attr & OPTEE_MSG_ATTR_TYPE_MASK indicates if tmem, rmem or value is used in * the union. OPTEE_MSG_ATTR_TYPE_VALUE_* indicates value, * OPTEE_MSG_ATTR_TYPE_TMEM_* indicates @tmem and - * OPTEE_MSG_ATTR_TYPE_RMEM_* indicates @rmem. + * OPTEE_MSG_ATTR_TYPE_RMEM_* or the alias PTEE_MSG_ATTR_TYPE_SMEM_* indicates + * @rmem or @smem depending on the conduit. * OPTEE_MSG_ATTR_TYPE_NONE indicates that none of the members are used. */ struct optee_msg_param { @@ -152,6 +176,7 @@ struct optee_msg_param { union { struct optee_msg_param_tmem tmem; struct optee_msg_param_rmem rmem; + struct optee_msg_param_fmem fmem; struct optee_msg_param_value value; } u; }; diff --git a/drivers/tee/optee/optee_private.h b/drivers/tee/optee/optee_private.h index c5741e96e967..1ffe74e66d15 100644 --- a/drivers/tee/optee/optee_private.h +++ b/drivers/tee/optee/optee_private.h @@ -7,6 +7,7 @@ #define OPTEE_PRIVATE_H
#include <linux/arm-smccc.h> +#include <linux/rhashtable.h> #include <linux/semaphore.h> #include <linux/tee_drv.h> #include <linux/types.h> @@ -20,6 +21,7 @@ #define TEEC_ERROR_NOT_SUPPORTED 0xFFFF000A #define TEEC_ERROR_COMMUNICATION 0xFFFF000E #define TEEC_ERROR_OUT_OF_MEMORY 0xFFFF000C +#define TEEC_ERROR_BUSY 0xFFFF000D #define TEEC_ERROR_SHORT_BUFFER 0xFFFF0010
#define TEEC_ORIGIN_COMMS 0x00000002 @@ -66,6 +68,22 @@ struct optee_supp { struct completion reqs_c; };
+/** + * struct optee_ffa_data - FFA communication struct + * @ffa_dev FFA device, contains the destination id, the id of + * OP-TEE in secure world + * @ffa_ops FFA operations + * @mutex Serializes access to @global_ids + * @global_ids FF-A shared memory global handle translation + */ +struct optee_ffa { + struct ffa_device *ffa_dev; + const struct ffa_dev_ops *ffa_ops; + /* Serializes access to @global_ids */ + struct mutex mutex; + struct rhashtable global_ids; +}; + struct optee;
/** @@ -113,11 +131,15 @@ struct optee { struct tee_device *teedev; const struct optee_ops *ops; optee_invoke_fn *invoke_fn; +#ifdef CONFIG_ARM_FFA_TRANSPORT + struct optee_ffa ffa; +#endif struct optee_call_queue call_queue; struct optee_wait_queue wait_queue; struct optee_supp supp; struct tee_shm_pool *pool; void *memremaped_shm; + unsigned int rpc_arg_count; u32 sec_caps; bool scan_bus_done; struct workqueue_struct *scan_bus_wq; @@ -206,6 +228,36 @@ void optee_fill_pages_list(u64 *dst, struct page **pages, int num_pages, #define PTA_CMD_GET_DEVICES_SUPP 0x1 int optee_enumerate_devices(u32 func);
+int optee_shm_add_ffa_handle(struct optee *optee, struct tee_shm *shm, + u64 global_id); +int optee_shm_rem_ffa_handle(struct optee *optee, u64 global_id); + +struct tee_shm *optee_shm_from_ffa_handle(struct optee *optee, u64 global_id); + +int optee_ffa_shm_register(struct tee_context *ctx, struct tee_shm *shm, + struct page **pages, size_t num_pages, + unsigned long start); +int optee_ffa_shm_unregister(struct tee_context *ctx, struct tee_shm *shm); +int optee_ffa_shm_register_supp(struct tee_context *ctx, struct tee_shm *shm, + struct page **pages, size_t num_pages, + unsigned long start); +int optee_ffa_shm_unregister_supp(struct tee_context *ctx, + struct tee_shm *shm); + +int optee_ffa_do_call_with_arg(struct tee_context *ctx, struct tee_shm *arg); +int optee_ffa_rpc_shm_register(struct tee_context *ctx, struct tee_shm *shm); +void optee_handle_ffa_rpc(struct tee_context *ctx, u32 cmd, + struct optee_msg_arg *arg); + +static inline bool optee_is_ffa_based(struct optee *optee) +{ +#ifdef CONFIG_ARM_FFA_TRANSPORT + return optee->ffa.ffa_ops; +#else + return false; +#endif +} + /* * Small helpers */ diff --git a/drivers/tee/optee/rpc.c b/drivers/tee/optee/rpc.c index f64a0ec472e0..416b50a23f68 100644 --- a/drivers/tee/optee/rpc.c +++ b/drivers/tee/optee/rpc.c @@ -10,6 +10,7 @@ #include <linux/i2c.h> #include <linux/slab.h> #include <linux/tee_drv.h> +#include "optee_ffa.h" #include "optee_msg.h" #include "optee_private.h" #include "optee_rpc_cmd.h" @@ -543,3 +544,120 @@ void optee_handle_rpc(struct tee_context *ctx, struct optee_rpc_param *param,
param->a0 = OPTEE_SMC_CALL_RETURN_FROM_RPC; } + +#ifdef CONFIG_ARM_FFA_TRANSPORT +static void handle_ffa_rpc_func_cmd_shm_alloc(struct tee_context *ctx, + struct optee_msg_arg *arg) +{ + struct tee_shm *shm; + + if (arg->num_params != 1 || + arg->params[0].attr != OPTEE_MSG_ATTR_TYPE_VALUE_INPUT) { + arg->ret = TEEC_ERROR_BAD_PARAMETERS; + return; + } + + switch (arg->params[0].u.value.a) { + case OPTEE_RPC_SHM_TYPE_APPL: + shm = cmd_alloc_suppl(ctx, arg->params[0].u.value.b); + break; + case OPTEE_RPC_SHM_TYPE_KERNEL: + shm = tee_shm_alloc(ctx, arg->params[0].u.value.b, + TEE_SHM_MAPPED); + break; + default: + arg->ret = TEEC_ERROR_BAD_PARAMETERS; + return; + } + + if (IS_ERR(shm)) { + arg->ret = TEEC_ERROR_OUT_OF_MEMORY; + return; + } + + arg->params[0] = (struct optee_msg_param){ + .attr = OPTEE_MSG_ATTR_TYPE_FMEM_OUTPUT, + .u.fmem.size = tee_shm_get_size(shm), + .u.fmem.global_id = shm->sec_world_id, + .u.fmem.internal_offs = shm->offset, + }; + + arg->ret = TEEC_SUCCESS; +} + +static void handle_ffa_rpc_func_cmd_shm_free(struct tee_context *ctx, + struct optee *optee, + struct optee_msg_arg *arg) +{ + struct tee_shm *shm; + + if (arg->num_params != 1 || + arg->params[0].attr != OPTEE_MSG_ATTR_TYPE_VALUE_INPUT) + goto err_bad_param; + + shm = optee_shm_from_ffa_handle(optee, arg->params[0].u.value.b); + if (!shm) + goto err_bad_param; + switch (arg->params[0].u.value.a) { + case OPTEE_RPC_SHM_TYPE_APPL: + cmd_free_suppl(ctx, shm); + break; + case OPTEE_RPC_SHM_TYPE_KERNEL: + tee_shm_free(shm); + break; + default: + goto err_bad_param; + } + arg->ret = TEEC_SUCCESS; + return; + +err_bad_param: + arg->ret = TEEC_ERROR_BAD_PARAMETERS; +} + +static void handle_ffa_rpc_func_cmd(struct tee_context *ctx, + struct optee_msg_arg *arg) +{ + struct optee *optee = tee_get_drvdata(ctx->teedev); + + arg->ret_origin = TEEC_ORIGIN_COMMS; + switch (arg->cmd) { + case OPTEE_RPC_CMD_GET_TIME: + handle_rpc_func_cmd_get_time(arg); + break; + case OPTEE_RPC_CMD_WAIT_QUEUE: + handle_rpc_func_cmd_wq(optee, arg); + break; + case OPTEE_RPC_CMD_SUSPEND: + handle_rpc_func_cmd_wait(arg); + break; + case OPTEE_RPC_CMD_SHM_ALLOC: + handle_ffa_rpc_func_cmd_shm_alloc(ctx, arg); + break; + case OPTEE_RPC_CMD_SHM_FREE: + handle_ffa_rpc_func_cmd_shm_free(ctx, optee, arg); + break; + case OPTEE_MSG_RPC_CMD_I2C_TRANSFER: + handle_rpc_func_cmd_i2c_transfer(ctx, arg); + break; + default: + handle_rpc_supp_cmd(ctx, optee, arg); + } +} + +void optee_handle_ffa_rpc(struct tee_context *ctx, u32 cmd, + struct optee_msg_arg *arg) +{ + switch (cmd) { + case OPTEE_FFA_YIELDING_CALL_RETURN_RPC_CMD: + handle_ffa_rpc_func_cmd(ctx, arg); + break; + case OPTEE_FFA_YIELDING_CALL_RETURN_INTERRUPT: + /* Interrupt delivered by now */ + break; + default: + pr_warn("Unknown RPC func 0x%x\n", cmd); + break; + } +} +#endif /*CONFIG_ARM_FFA_TRANSPORT*/
op-tee@lists.trustedfirmware.org