On Mon, 21 Mar 2022 at 18:33, Jens Wiklander jens.wiklander@linaro.org wrote:
Adds OPTEE_SMC_CALL_WITH_RPC_ARG and OPTEE_SMC_CALL_WITH_REGD_ARG where the struct optee_msg_arg to be used for RPC is appended in the memory following the normal argument struct optee_msg_arg. This is an optimization to avoid caching the RPC argument struct while still maintaining similar performance as if it was cached.
OPTEE_SMC_CALL_WITH_REGD_ARG optimized one step further by using a registered shared memory object instead. It's in other aspects identical to OPTEE_SMC_CALL_WITH_RPC_ARG.
The presence of OPTEE_SMC_CALL_WITH_RPC_ARG and OPTEE_SMC_CALL_WITH_REGD_ARG is indicated by the new OPTEE_SMC_SEC_CAP_RPC_ARG bit returned by OPTEE_SMC_EXCHANGE_CAPABILITIES. OPTEE_SMC_EXCHANGE_CAPABILITIES also reports the number of arguments that the RPC argument struct must have room for.
OPTEE_SMC_CALL_WITH_RPC_ARG and OPTEE_SMC_CALL_WITH_ARG can be used interleaved with difference that when OPTEE_SMC_CALL_WITH_RPC_ARG is used the RPC argument struct to be used is the one appended to the normal argument struct. The same is true for OPTEE_SMC_CALL_WITH_REGD_ARG.
Signed-off-by: Jens Wiklander jens.wiklander@linaro.org
drivers/tee/optee/call.c | 2 +- drivers/tee/optee/optee_smc.h | 47 ++++++++++++++--- drivers/tee/optee/smc_abi.c | 99 ++++++++++++++++++++++++++--------- 3 files changed, 116 insertions(+), 32 deletions(-)
Apart from minor nits below, feel free to add:
Reviewed-by: Sumit Garg sumit.garg@linaro.org
diff --git a/drivers/tee/optee/call.c b/drivers/tee/optee/call.c index a9a237d20c61..58ac15c02818 100644 --- a/drivers/tee/optee/call.c +++ b/drivers/tee/optee/call.c @@ -130,7 +130,7 @@ struct tee_shm *optee_get_msg_arg(struct tee_context *ctx, size_t num_params, return (void *)ma; }
memset(ma, 0, OPTEE_MSG_GET_ARG_SIZE(num_params));
memset(ma, 0, sz); ma->num_params = num_params; *msg_arg = ma;
diff --git a/drivers/tee/optee/optee_smc.h b/drivers/tee/optee/optee_smc.h index d44a6ae994f8..378741a459b6 100644 --- a/drivers/tee/optee/optee_smc.h +++ b/drivers/tee/optee/optee_smc.h @@ -107,14 +107,22 @@ struct optee_smc_call_get_os_revision_result { /*
- Call with struct optee_msg_arg as argument
- When calling this function normal world has a few responsibilities:
- When called with OPTEE_SMC_CALL_WITH_RPC_ARG or
- OPTEE_SMC_CALL_WITH_REGD_ARG in a0 there is one RPC struct optee_msg_arg
- following after the first struct optee_msg_arg. The RPC struct
- optee_msg_arg has reserved space for the number of RPC parameters as
- returned by OPTEE_SMC_EXCHANGE_CAPABILITIES.
- When calling these functions normal world has a few responsibilities:
s/functions normal/functions, the normal/
- It must be able to handle eventual RPCs
- Non-secure interrupts should not be masked
- If asynchronous notifications has been negotiated successfully, then
- asynchronous notifications should be unmasked during this call.
- the interrupt for asynchronous notifications should be unmasked
- during this call.
- Call register usage:
- a0 SMC Function ID, OPTEE_SMC*CALL_WITH_ARG
- Call register usage, OPTEE_SMC_CALL_WITH_ARG and
- OPTEE_SMC_CALL_WITH_RPC_ARG:
- a0 SMC Function ID, OPTEE_SMC_CALL_WITH_ARG or OPTEE_SMC_CALL_WITH_RPC_ARG
- a1 Upper 32 bits of a 64-bit physical pointer to a struct optee_msg_arg
- a2 Lower 32 bits of a 64-bit physical pointer to a struct optee_msg_arg
- a3 Cache settings, not used if physical pointer is in a predefined shared
@@ -122,6 +130,15 @@ struct optee_smc_call_get_os_revision_result {
- a4-6 Not used
- a7 Hypervisor Client ID register
- Call register usage, OPTEE_SMC_CALL_WITH_REGD_ARG:
- a0 SMC Function ID, OPTEE_SMC_CALL_WITH_REGD_ARG
- a1 Upper 32 bits of a 64-bit shared memory cookie
- a2 Lower 32 bits of a 64-bit shared memory cookie
- a3 Offset of the struct optee_msg_arg in the shared memory with the
supplied cookie
- a4-6 Not used
- a7 Hypervisor Client ID register
- Normal return register usage:
- a0 Return value, OPTEE_SMC_RETURN_*
- a1-3 Not used
@@ -154,6 +171,10 @@ struct optee_smc_call_get_os_revision_result { #define OPTEE_SMC_FUNCID_CALL_WITH_ARG OPTEE_MSG_FUNCID_CALL_WITH_ARG #define OPTEE_SMC_CALL_WITH_ARG \ OPTEE_SMC_STD_CALL_VAL(OPTEE_SMC_FUNCID_CALL_WITH_ARG) +#define OPTEE_SMC_CALL_WITH_RPC_ARG \
OPTEE_SMC_STD_CALL_VAL(OPTEE_SMC_FUNCID_CALL_WITH_RPC_ARG)
+#define OPTEE_SMC_CALL_WITH_REGD_ARG \
OPTEE_SMC_STD_CALL_VAL(OPTEE_SMC_FUNCID_CALL_WITH_REGD_ARG)
/*
- Get Shared Memory Config
@@ -202,6 +223,10 @@ struct optee_smc_get_shm_config_result {
- a0 OPTEE_SMC_RETURN_OK
- a1 bitfield of secure world capabilities OPTEE_SMC_SEC_CAP_*
- a2 The maximum secure world notification number
- a3 Bit[7:0]: Number of parameters needed for RPC to be supplied
as the second MSG arg struct for
OPTEE_SMC_CALL_WITH_ARG
Bit[31:8]: Reserved (MBZ)
- a3-7 Preserved
s/a3-7/a4-7/
- Error return register usage:
@@ -215,7 +240,6 @@ struct optee_smc_get_shm_config_result { #define OPTEE_SMC_SEC_CAP_HAVE_RESERVED_SHM BIT(0) /* Secure world can communicate via previously unregistered shared memory */ #define OPTEE_SMC_SEC_CAP_UNREGISTERED_SHM BIT(1)
nit: blank line shouldn't be removed.
/*
- Secure world supports commands "register/unregister shared memory",
- secure world accepts command buffers located in any parts of non-secure RAM
@@ -227,6 +251,8 @@ struct optee_smc_get_shm_config_result { #define OPTEE_SMC_SEC_CAP_MEMREF_NULL BIT(4) /* Secure world supports asynchronous notification of normal world */ #define OPTEE_SMC_SEC_CAP_ASYNC_NOTIF BIT(5) +/* Secure world supports pre-allocating RPC arg struct */ +#define OPTEE_SMC_SEC_CAP_RPC_ARG BIT(6)
#define OPTEE_SMC_FUNCID_EXCHANGE_CAPABILITIES 9 #define OPTEE_SMC_EXCHANGE_CAPABILITIES \ @@ -236,7 +262,7 @@ struct optee_smc_exchange_capabilities_result { unsigned long status; unsigned long capabilities; unsigned long max_notif_value;
unsigned long reserved0;
unsigned long data;
};
/* @@ -358,6 +384,9 @@ struct optee_smc_disable_shm_cache_result {
- should be called until all pended values have been retrieved. When a
- value is retrieved, it's cleared from the record in secure world.
- It is expected that this function is called from an interrupt handler
- in normal world.
- Call requests usage:
- a0 SMC Function ID, OPTEE_SMC_GET_ASYNC_NOTIF_VALUE
- a1-6 Not used
@@ -390,6 +419,12 @@ struct optee_smc_disable_shm_cache_result { #define OPTEE_SMC_GET_ASYNC_NOTIF_VALUE \ OPTEE_SMC_FAST_CALL_VAL(OPTEE_SMC_FUNCID_GET_ASYNC_NOTIF_VALUE)
+/* See OPTEE_SMC_CALL_WITH_RPC_ARG above */ +#define OPTEE_SMC_FUNCID_CALL_WITH_RPC_ARG 18
+/* See OPTEE_SMC_CALL_WITH_REGD_ARG above */ +#define OPTEE_SMC_FUNCID_CALL_WITH_REGD_ARG 19
/*
- Resume from RPC (for example after processing a foreign interrupt)
diff --git a/drivers/tee/optee/smc_abi.c b/drivers/tee/optee/smc_abi.c index 67b7f7d2ff27..b258d7306042 100644 --- a/drivers/tee/optee/smc_abi.c +++ b/drivers/tee/optee/smc_abi.c @@ -732,16 +732,9 @@ static void optee_rpc_finalize_call(struct optee_call_ctx *call_ctx) }
static void handle_rpc_func_cmd(struct tee_context *ctx, struct optee *optee,
struct tee_shm *shm,
struct optee_msg_arg *arg, struct optee_call_ctx *call_ctx)
{
struct optee_msg_arg *arg;
arg = tee_shm_get_va(shm, 0);
if (IS_ERR(arg)) {
pr_err("%s: tee_shm_get_va %p failed\n", __func__, shm);
return;
} switch (arg->cmd) { case OPTEE_RPC_CMD_SHM_ALLOC:
@@ -765,11 +758,13 @@ static void handle_rpc_func_cmd(struct tee_context *ctx, struct optee *optee,
- Result of RPC is written back into @param.
*/ static void optee_handle_rpc(struct tee_context *ctx,
struct optee_msg_arg *rpc_arg, struct optee_rpc_param *param, struct optee_call_ctx *call_ctx)
{ struct tee_device *teedev = ctx->teedev; struct optee *optee = tee_get_drvdata(teedev);
struct optee_msg_arg *arg; struct tee_shm *shm; phys_addr_t pa;
@@ -801,8 +796,19 @@ static void optee_handle_rpc(struct tee_context *ctx, */ break; case OPTEE_SMC_RPC_FUNC_CMD:
shm = reg_pair_to_ptr(param->a1, param->a2);
handle_rpc_func_cmd(ctx, optee, shm, call_ctx);
if (rpc_arg) {
arg = rpc_arg;
} else {
shm = reg_pair_to_ptr(param->a1, param->a2);
arg = tee_shm_get_va(shm, 0);
if (IS_ERR(arg)) {
pr_err("%s: tee_shm_get_va %p failed\n",
__func__, shm);
break;
}
}
handle_rpc_func_cmd(ctx, optee, arg, call_ctx); break; default: pr_warn("Unknown RPC func 0x%x\n",
@@ -816,7 +822,7 @@ static void optee_handle_rpc(struct tee_context *ctx, /**
- optee_smc_do_call_with_arg() - Do an SMC to OP-TEE in secure world
- @ctx: calling context
- @arg: shared memory holding the message to pass to secure world
- @shm: 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.
@@ -824,21 +830,46 @@ static void optee_handle_rpc(struct tee_context *ctx,
- Returns return code from secure world, 0 is OK
*/ static int optee_smc_do_call_with_arg(struct tee_context *ctx,
struct tee_shm *arg)
struct tee_shm *shm)
{ struct optee *optee = tee_get_drvdata(ctx->teedev); struct optee_call_waiter w; struct optee_rpc_param param = { }; struct optee_call_ctx call_ctx = { };
phys_addr_t parg;
struct optee_msg_arg *rpc_arg = NULL; int rc;
rc = tee_shm_get_pa(arg, 0, &parg);
if (rc)
return rc;
if (optee->rpc_param_count) {
struct optee_msg_arg *arg;
unsigned int rpc_arg_offs;
arg = tee_shm_get_va(shm, 0);
if (IS_ERR(arg))
return PTR_ERR(arg);
param.a0 = OPTEE_SMC_CALL_WITH_ARG;
reg_pair_from_64(¶m.a1, ¶m.a2, parg);
rpc_arg_offs = OPTEE_MSG_GET_ARG_SIZE(arg->num_params);
rpc_arg = tee_shm_get_va(shm, rpc_arg_offs);
if (IS_ERR(arg))
return PTR_ERR(arg);
}
if (rpc_arg && tee_shm_is_dynamic(shm)) {
param.a0 = OPTEE_SMC_CALL_WITH_REGD_ARG;
reg_pair_from_64(¶m.a1, ¶m.a2, (u_long)shm);
param.a3 = 0;
} else {
phys_addr_t parg;
rc = tee_shm_get_pa(shm, 0, &parg);
if (rc)
return rc;
if (rpc_arg)
param.a0 = OPTEE_SMC_CALL_WITH_RPC_ARG;
else
param.a0 = OPTEE_SMC_CALL_WITH_ARG;
reg_pair_from_64(¶m.a1, ¶m.a2, parg);
} /* Initialize waiter */ optee_cq_wait_init(&optee->call_queue, &w); while (true) {
@@ -862,7 +893,7 @@ static int optee_smc_do_call_with_arg(struct tee_context *ctx, param.a1 = res.a1; param.a2 = res.a2; param.a3 = res.a3;
optee_handle_rpc(ctx, ¶m, &call_ctx);
optee_handle_rpc(ctx, rpc_arg, ¶m, &call_ctx); } else { rc = res.a0; break;
@@ -1118,7 +1149,8 @@ static bool optee_msg_api_revision_is_compatible(optee_invoke_fn *invoke_fn) }
static bool optee_msg_exchange_capabilities(optee_invoke_fn *invoke_fn,
u32 *sec_caps, u32 *max_notif_value)
u32 *sec_caps, u32 *max_notif_value,
unsigned int *rpc_param_count)
{ union { struct arm_smccc_res smccc; @@ -1145,6 +1177,10 @@ static bool optee_msg_exchange_capabilities(optee_invoke_fn *invoke_fn, *max_notif_value = res.result.max_notif_value; else *max_notif_value = OPTEE_DEFAULT_MAX_NOTIF_VALUE;
if (*sec_caps & OPTEE_SMC_SEC_CAP_RPC_ARG)
*rpc_param_count = (u8)res.result.data;
else
*rpc_param_count = 0; return true;
} @@ -1251,7 +1287,8 @@ static int optee_smc_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->rpc_param_count)
optee_disable_shm_cache(optee); optee_smc_notif_uninit_irq(optee);
@@ -1274,7 +1311,10 @@ static int optee_smc_remove(struct platform_device *pdev) */ static void optee_shutdown(struct platform_device *pdev) {
optee_disable_shm_cache(platform_get_drvdata(pdev));
struct optee *optee = platform_get_drvdata(pdev);
if (!optee->rpc_param_count)
optee_disable_shm_cache(optee);
}
static int optee_probe(struct platform_device *pdev) @@ -1283,6 +1323,7 @@ static int optee_probe(struct platform_device *pdev) struct tee_shm_pool *pool = ERR_PTR(-EINVAL); struct optee *optee = NULL; void *memremaped_shm = NULL;
unsigned int rpc_param_count; struct tee_device *teedev; struct tee_context *ctx; u32 max_notif_value;
@@ -1306,7 +1347,8 @@ static int optee_probe(struct platform_device *pdev) }
if (!optee_msg_exchange_capabilities(invoke_fn, &sec_caps,
&max_notif_value)) {
&max_notif_value,
&rpc_param_count)) { pr_warn("capabilities mismatch\n"); return -EINVAL; }
@@ -1335,6 +1377,7 @@ static int optee_probe(struct platform_device *pdev) optee->ops = &optee_ops; optee->smc.invoke_fn = invoke_fn; optee->smc.sec_caps = sec_caps;
optee->rpc_param_count = rpc_param_count; teedev = tee_device_alloc(&optee_clnt_desc, NULL, pool, optee); if (IS_ERR(teedev)) {
@@ -1403,7 +1446,12 @@ static int optee_probe(struct platform_device *pdev) */ optee_disable_unmapped_shm_cache(optee);
optee_enable_shm_cache(optee);
/*
* Only enable the shm cache in case we're not able to pass the RPC
* arg struct right after the normal arg struct.
*/
if (!optee->rpc_param_count)
optee_enable_shm_cache(optee); if (optee->smc.sec_caps & OPTEE_SMC_SEC_CAP_DYNAMIC_SHM) pr_info("dynamic shared memory is enabled\n");
@@ -1416,7 +1464,8 @@ static int optee_probe(struct platform_device *pdev) return 0;
err_disable_shm_cache:
optee_disable_shm_cache(optee);
if (!optee->rpc_param_count)
optee_disable_shm_cache(optee); optee_smc_notif_uninit_irq(optee); optee_unregister_devices();
err_notif_uninit:
2.31.1