Commit ed8faf6c authored by Jens Wiklander's avatar Jens Wiklander

optee: add OPTEE_SMC_CALL_WITH_RPC_ARG and OPTEE_SMC_CALL_WITH_REGD_ARG

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.
Reviewed-by: default avatarSumit Garg <sumit.garg@linaro.org>
Signed-off-by: default avatarJens Wiklander <jens.wiklander@linaro.org>
parent 30c375a7
...@@ -130,7 +130,7 @@ struct tee_shm *optee_get_msg_arg(struct tee_context *ctx, size_t num_params, ...@@ -130,7 +130,7 @@ struct tee_shm *optee_get_msg_arg(struct tee_context *ctx, size_t num_params,
return (void *)ma; return (void *)ma;
} }
memset(ma, 0, OPTEE_MSG_GET_ARG_SIZE(num_params)); memset(ma, 0, sz);
ma->num_params = num_params; ma->num_params = num_params;
*msg_arg = ma; *msg_arg = ma;
......
...@@ -107,14 +107,22 @@ struct optee_smc_call_get_os_revision_result { ...@@ -107,14 +107,22 @@ struct optee_smc_call_get_os_revision_result {
/* /*
* Call with struct optee_msg_arg as argument * 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:
* 1. It must be able to handle eventual RPCs * 1. It must be able to handle eventual RPCs
* 2. Non-secure interrupts should not be masked * 2. Non-secure interrupts should not be masked
* 3. If asynchronous notifications has been negotiated successfully, then * 3. 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: * Call register usage, OPTEE_SMC_CALL_WITH_ARG and
* a0 SMC Function ID, OPTEE_SMC*CALL_WITH_ARG * 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 * 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 * 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 * 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 { ...@@ -122,6 +130,15 @@ struct optee_smc_call_get_os_revision_result {
* a4-6 Not used * a4-6 Not used
* a7 Hypervisor Client ID register * 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: * Normal return register usage:
* a0 Return value, OPTEE_SMC_RETURN_* * a0 Return value, OPTEE_SMC_RETURN_*
* a1-3 Not used * a1-3 Not used
...@@ -154,6 +171,10 @@ struct optee_smc_call_get_os_revision_result { ...@@ -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_FUNCID_CALL_WITH_ARG OPTEE_MSG_FUNCID_CALL_WITH_ARG
#define OPTEE_SMC_CALL_WITH_ARG \ #define OPTEE_SMC_CALL_WITH_ARG \
OPTEE_SMC_STD_CALL_VAL(OPTEE_SMC_FUNCID_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 * Get Shared Memory Config
...@@ -202,7 +223,11 @@ struct optee_smc_get_shm_config_result { ...@@ -202,7 +223,11 @@ struct optee_smc_get_shm_config_result {
* a0 OPTEE_SMC_RETURN_OK * a0 OPTEE_SMC_RETURN_OK
* a1 bitfield of secure world capabilities OPTEE_SMC_SEC_CAP_* * a1 bitfield of secure world capabilities OPTEE_SMC_SEC_CAP_*
* a2 The maximum secure world notification number * a2 The maximum secure world notification number
* a3-7 Preserved * 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)
* a4-7 Preserved
* *
* Error return register usage: * Error return register usage:
* a0 OPTEE_SMC_RETURN_ENOTAVAIL, can't use the capabilities from normal world * a0 OPTEE_SMC_RETURN_ENOTAVAIL, can't use the capabilities from normal world
...@@ -227,6 +252,8 @@ struct optee_smc_get_shm_config_result { ...@@ -227,6 +252,8 @@ struct optee_smc_get_shm_config_result {
#define OPTEE_SMC_SEC_CAP_MEMREF_NULL BIT(4) #define OPTEE_SMC_SEC_CAP_MEMREF_NULL BIT(4)
/* Secure world supports asynchronous notification of normal world */ /* Secure world supports asynchronous notification of normal world */
#define OPTEE_SMC_SEC_CAP_ASYNC_NOTIF BIT(5) #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_FUNCID_EXCHANGE_CAPABILITIES 9
#define OPTEE_SMC_EXCHANGE_CAPABILITIES \ #define OPTEE_SMC_EXCHANGE_CAPABILITIES \
...@@ -236,7 +263,7 @@ struct optee_smc_exchange_capabilities_result { ...@@ -236,7 +263,7 @@ struct optee_smc_exchange_capabilities_result {
unsigned long status; unsigned long status;
unsigned long capabilities; unsigned long capabilities;
unsigned long max_notif_value; unsigned long max_notif_value;
unsigned long reserved0; unsigned long data;
}; };
/* /*
...@@ -358,6 +385,9 @@ struct optee_smc_disable_shm_cache_result { ...@@ -358,6 +385,9 @@ struct optee_smc_disable_shm_cache_result {
* should be called until all pended values have been retrieved. When a * should be called until all pended values have been retrieved. When a
* value is retrieved, it's cleared from the record in secure world. * 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: * Call requests usage:
* a0 SMC Function ID, OPTEE_SMC_GET_ASYNC_NOTIF_VALUE * a0 SMC Function ID, OPTEE_SMC_GET_ASYNC_NOTIF_VALUE
* a1-6 Not used * a1-6 Not used
...@@ -390,6 +420,12 @@ struct optee_smc_disable_shm_cache_result { ...@@ -390,6 +420,12 @@ struct optee_smc_disable_shm_cache_result {
#define OPTEE_SMC_GET_ASYNC_NOTIF_VALUE \ #define OPTEE_SMC_GET_ASYNC_NOTIF_VALUE \
OPTEE_SMC_FAST_CALL_VAL(OPTEE_SMC_FUNCID_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) * Resume from RPC (for example after processing a foreign interrupt)
* *
......
...@@ -732,16 +732,9 @@ static void optee_rpc_finalize_call(struct optee_call_ctx *call_ctx) ...@@ -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, 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_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) { switch (arg->cmd) {
case OPTEE_RPC_CMD_SHM_ALLOC: case OPTEE_RPC_CMD_SHM_ALLOC:
...@@ -765,11 +758,13 @@ static void handle_rpc_func_cmd(struct tee_context *ctx, struct optee *optee, ...@@ -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. * Result of RPC is written back into @param.
*/ */
static void optee_handle_rpc(struct tee_context *ctx, static void optee_handle_rpc(struct tee_context *ctx,
struct optee_msg_arg *rpc_arg,
struct optee_rpc_param *param, struct optee_rpc_param *param,
struct optee_call_ctx *call_ctx) struct optee_call_ctx *call_ctx)
{ {
struct tee_device *teedev = ctx->teedev; struct tee_device *teedev = ctx->teedev;
struct optee *optee = tee_get_drvdata(teedev); struct optee *optee = tee_get_drvdata(teedev);
struct optee_msg_arg *arg;
struct tee_shm *shm; struct tee_shm *shm;
phys_addr_t pa; phys_addr_t pa;
...@@ -801,8 +796,19 @@ static void optee_handle_rpc(struct tee_context *ctx, ...@@ -801,8 +796,19 @@ static void optee_handle_rpc(struct tee_context *ctx,
*/ */
break; break;
case OPTEE_SMC_RPC_FUNC_CMD: case OPTEE_SMC_RPC_FUNC_CMD:
shm = reg_pair_to_ptr(param->a1, param->a2); if (rpc_arg) {
handle_rpc_func_cmd(ctx, optee, shm, call_ctx); 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; break;
default: default:
pr_warn("Unknown RPC func 0x%x\n", pr_warn("Unknown RPC func 0x%x\n",
...@@ -816,7 +822,7 @@ static void optee_handle_rpc(struct tee_context *ctx, ...@@ -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 * optee_smc_do_call_with_arg() - Do an SMC to OP-TEE in secure world
* @ctx: calling context * @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 * Does and SMC to OP-TEE in secure world and handles eventual resulting
* Remote Procedure Calls (RPC) from OP-TEE. * Remote Procedure Calls (RPC) from OP-TEE.
...@@ -824,21 +830,46 @@ static void optee_handle_rpc(struct tee_context *ctx, ...@@ -824,21 +830,46 @@ static void optee_handle_rpc(struct tee_context *ctx,
* Returns return code from secure world, 0 is OK * Returns return code from secure world, 0 is OK
*/ */
static int optee_smc_do_call_with_arg(struct tee_context *ctx, 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 *optee = tee_get_drvdata(ctx->teedev);
struct optee_call_waiter w; struct optee_call_waiter w;
struct optee_rpc_param param = { }; struct optee_rpc_param param = { };
struct optee_call_ctx call_ctx = { }; struct optee_call_ctx call_ctx = { };
phys_addr_t parg; struct optee_msg_arg *rpc_arg = NULL;
int rc; int rc;
rc = tee_shm_get_pa(arg, 0, &parg); if (optee->rpc_param_count) {
if (rc) struct optee_msg_arg *arg;
return rc; 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; rpc_arg_offs = OPTEE_MSG_GET_ARG_SIZE(arg->num_params);
reg_pair_from_64(&param.a1, &param.a2, parg); 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(&param.a1, &param.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(&param.a1, &param.a2, parg);
}
/* Initialize waiter */ /* Initialize waiter */
optee_cq_wait_init(&optee->call_queue, &w); optee_cq_wait_init(&optee->call_queue, &w);
while (true) { while (true) {
...@@ -862,7 +893,7 @@ static int optee_smc_do_call_with_arg(struct tee_context *ctx, ...@@ -862,7 +893,7 @@ static int optee_smc_do_call_with_arg(struct tee_context *ctx,
param.a1 = res.a1; param.a1 = res.a1;
param.a2 = res.a2; param.a2 = res.a2;
param.a3 = res.a3; param.a3 = res.a3;
optee_handle_rpc(ctx, &param, &call_ctx); optee_handle_rpc(ctx, rpc_arg, &param, &call_ctx);
} else { } else {
rc = res.a0; rc = res.a0;
break; break;
...@@ -1118,7 +1149,8 @@ static bool optee_msg_api_revision_is_compatible(optee_invoke_fn *invoke_fn) ...@@ -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, 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 { union {
struct arm_smccc_res smccc; struct arm_smccc_res smccc;
...@@ -1145,6 +1177,10 @@ static bool optee_msg_exchange_capabilities(optee_invoke_fn *invoke_fn, ...@@ -1145,6 +1177,10 @@ static bool optee_msg_exchange_capabilities(optee_invoke_fn *invoke_fn,
*max_notif_value = res.result.max_notif_value; *max_notif_value = res.result.max_notif_value;
else else
*max_notif_value = OPTEE_DEFAULT_MAX_NOTIF_VALUE; *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; return true;
} }
...@@ -1251,7 +1287,8 @@ static int optee_smc_remove(struct platform_device *pdev) ...@@ -1251,7 +1287,8 @@ static int optee_smc_remove(struct platform_device *pdev)
* reference counters and also avoid wild pointers in secure world * reference counters and also avoid wild pointers in secure world
* into the old shared memory range. * 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); optee_smc_notif_uninit_irq(optee);
...@@ -1274,7 +1311,10 @@ static int optee_smc_remove(struct platform_device *pdev) ...@@ -1274,7 +1311,10 @@ static int optee_smc_remove(struct platform_device *pdev)
*/ */
static void optee_shutdown(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) static int optee_probe(struct platform_device *pdev)
...@@ -1283,6 +1323,7 @@ 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 tee_shm_pool *pool = ERR_PTR(-EINVAL);
struct optee *optee = NULL; struct optee *optee = NULL;
void *memremaped_shm = NULL; void *memremaped_shm = NULL;
unsigned int rpc_param_count;
struct tee_device *teedev; struct tee_device *teedev;
struct tee_context *ctx; struct tee_context *ctx;
u32 max_notif_value; u32 max_notif_value;
...@@ -1306,7 +1347,8 @@ static int optee_probe(struct platform_device *pdev) ...@@ -1306,7 +1347,8 @@ static int optee_probe(struct platform_device *pdev)
} }
if (!optee_msg_exchange_capabilities(invoke_fn, &sec_caps, if (!optee_msg_exchange_capabilities(invoke_fn, &sec_caps,
&max_notif_value)) { &max_notif_value,
&rpc_param_count)) {
pr_warn("capabilities mismatch\n"); pr_warn("capabilities mismatch\n");
return -EINVAL; return -EINVAL;
} }
...@@ -1335,6 +1377,7 @@ static int optee_probe(struct platform_device *pdev) ...@@ -1335,6 +1377,7 @@ static int optee_probe(struct platform_device *pdev)
optee->ops = &optee_ops; optee->ops = &optee_ops;
optee->smc.invoke_fn = invoke_fn; optee->smc.invoke_fn = invoke_fn;
optee->smc.sec_caps = sec_caps; optee->smc.sec_caps = sec_caps;
optee->rpc_param_count = rpc_param_count;
teedev = tee_device_alloc(&optee_clnt_desc, NULL, pool, optee); teedev = tee_device_alloc(&optee_clnt_desc, NULL, pool, optee);
if (IS_ERR(teedev)) { if (IS_ERR(teedev)) {
...@@ -1403,7 +1446,12 @@ static int optee_probe(struct platform_device *pdev) ...@@ -1403,7 +1446,12 @@ static int optee_probe(struct platform_device *pdev)
*/ */
optee_disable_unmapped_shm_cache(optee); 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) if (optee->smc.sec_caps & OPTEE_SMC_SEC_CAP_DYNAMIC_SHM)
pr_info("dynamic shared memory is enabled\n"); pr_info("dynamic shared memory is enabled\n");
...@@ -1416,7 +1464,8 @@ static int optee_probe(struct platform_device *pdev) ...@@ -1416,7 +1464,8 @@ static int optee_probe(struct platform_device *pdev)
return 0; return 0;
err_disable_shm_cache: 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_smc_notif_uninit_irq(optee);
optee_unregister_devices(); optee_unregister_devices();
err_notif_uninit: err_notif_uninit:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment