Skip to content

Commit

Permalink
tee: optee: Ocall2 implementation
Browse files Browse the repository at this point in the history
Add kernel client API function tee_client_invoke_ocall2() to invoke
a session command with support for an Ocall command from the TEE service
back to caller client. On return of that function, caller can call that
function again providing the Ocall argument to return to TEE from the
Ocall.

The interface allows Ocall command to pass 2 32bit arguments: an Ocall
command ID and a value parameter. On return, the Ocall passes 2 32bit
arguments to output value parameters upon success and an error result.

This implementation is based on the OP-TEE OCALL implementation proposed
by Hernan Gatta in linaro-swg#72 and
OP-TEE/optee_os#3673.

Change-Id: Idce8218a6f1c5d45e0a09f33eb2370c1790a571a
Co-developed-by: Hernan Gatta <[email protected]>
Signed-off-by: Hernan Gatta <[email protected]>
Signed-off-by: Etienne Carriere <[email protected]>
Reviewed-on: https://gerrit.st.com/c/mpu/oe/st/linux-stm32/+/357079
Domain-Review: Yann GAUTIER <[email protected]>
Reviewed-by: Yann GAUTIER <[email protected]>
ACI: CITOOLS <[email protected]>
  • Loading branch information
etienne-lms authored and thom24 committed Jan 21, 2025
1 parent 5f169dc commit 28d62e6
Show file tree
Hide file tree
Showing 8 changed files with 309 additions and 17 deletions.
102 changes: 96 additions & 6 deletions drivers/tee/optee/call.c
Original file line number Diff line number Diff line change
Expand Up @@ -328,7 +328,7 @@ int optee_open_session(struct tee_context *ctx,
goto out;
}

if (optee->ops->do_call_with_arg(ctx, shm, offs)) {
if (optee->ops->do_call_with_arg(ctx, shm, offs, NULL)) {
msg_arg->ret = TEEC_ERROR_COMMUNICATION;
msg_arg->ret_origin = TEEC_ORIGIN_COMMS;
}
Expand Down Expand Up @@ -374,7 +374,7 @@ int optee_close_session_helper(struct tee_context *ctx, u32 session)

msg_arg->cmd = OPTEE_MSG_CMD_CLOSE_SESSION;
msg_arg->session = session;
optee->ops->do_call_with_arg(ctx, shm, offs);
optee->ops->do_call_with_arg(ctx, shm, offs, NULL);

optee_free_msg_arg(ctx, entry, offs);

Expand All @@ -389,35 +389,76 @@ int optee_close_session(struct tee_context *ctx, u32 session)
/* Check that the session is valid and remove it from the list */
mutex_lock(&ctxdata->mutex);
sess = find_session(ctxdata, session);
if (sess)
if (sess && !sess->ocall_ctx.thread_p1)
list_del(&sess->list_node);
mutex_unlock(&ctxdata->mutex);
if (!sess)
return -EINVAL;
if (WARN_ONCE(sess->ocall_ctx.thread_p1, "Can't close, on going Ocall\n"))
return -EINVAL;

kfree(sess);

return optee_close_session_helper(ctx, session);
}

int optee_invoke_func(struct tee_context *ctx, struct tee_ioctl_invoke_arg *arg,
struct tee_param *param)
{
return optee_invoke_func_helper(ctx, arg, param, NULL);
}

int optee_invoke_func_helper(struct tee_context *ctx,
struct tee_ioctl_invoke_arg *arg,
struct tee_param *param,
struct tee_ocall2_arg *ocall_arg)
{
struct optee *optee = tee_get_drvdata(ctx->teedev);
struct optee_context_data *ctxdata = ctx->data;
struct optee_call_extra call_extra;
struct optee_shm_arg_entry *entry;
struct optee_msg_arg *msg_arg;
struct optee_session *sess;
struct tee_shm *shm;
u32 session_id;
u_int offs;
int rc;

if (tee_ocall_in_progress(ocall_arg))
session_id = ocall_arg->session;
else
session_id = arg->session;

/* Check that the session is valid */
mutex_lock(&ctxdata->mutex);
sess = find_session(ctxdata, arg->session);
sess = find_session(ctxdata, session_id);
mutex_unlock(&ctxdata->mutex);
if (!sess)
return -EINVAL;

if (tee_ocall_in_progress(ocall_arg) && !sess->ocall_ctx.thread_p1) {
pr_err("Unexpected return from Ocall for the session\n");
return -EINVAL;
}
if (!tee_ocall_is_used(ocall_arg) && sess->ocall_ctx.thread_p1) {
pr_err("Session is busy with an on-going Ocall\n");
return -EINVAL;
}

/* Setup TEE call extra data */
memset(&call_extra, 0, sizeof(call_extra));

if (tee_ocall_is_used(ocall_arg)) {
call_extra.ocall_arg = ocall_arg;
call_extra.ocall_call_waiter = &sess->ocall_ctx.call_waiter;
}

if (tee_ocall_in_progress(ocall_arg)) {
call_extra.tee_thread_id = sess->ocall_ctx.thread_p1 - 1;
/* Skip shared memory buffer part, not needed with ocall2 */
goto do_call;
}

msg_arg = optee_get_msg_arg(ctx, arg->num_params,
&entry, &shm, &offs);
if (IS_ERR(msg_arg))
Expand All @@ -432,7 +473,54 @@ int optee_invoke_func(struct tee_context *ctx, struct tee_ioctl_invoke_arg *arg,
if (rc)
goto out;

if (optee->ops->do_call_with_arg(ctx, shm, offs)) {
if (tee_ocall_is_used(ocall_arg)) {
/* Save initial call context in ocall context */
memcpy(&sess->ocall_ctx.call_arg, arg, sizeof(*arg));
sess->ocall_ctx.msg_arg = msg_arg;
sess->ocall_ctx.msg_entry = entry;
sess->ocall_ctx.msg_offs = offs;
}

do_call:
rc = optee->ops->do_call_with_arg(ctx, shm, offs, &call_extra);
if (rc == -EAGAIN) {
/* We are executing an Ocall request from TEE */
if (tee_ocall_in_progress(ocall_arg)) {
mutex_lock(&ctxdata->mutex);
ocall_arg->session = session_id;
sess->ocall_ctx.thread_p1 = call_extra.tee_thread_id + 1;
mutex_unlock(&ctxdata->mutex);

return 0;
}
WARN_ONCE(1, "optee: unexpected ocall\n");
}

/* Session may be leaving an Ocall */
mutex_lock(&ctxdata->mutex);
sess->ocall_ctx.thread_p1 = 0;

if (tee_ocall_is_used(ocall_arg)) {
/* We are returning from initial call, get initial call msg */
msg_arg = sess->ocall_ctx.msg_arg;
entry = sess->ocall_ctx.msg_entry;
offs = sess->ocall_ctx.msg_offs;
if (arg) {
unsigned int num_params = arg->num_params;

memcpy(arg, &sess->ocall_ctx.call_arg, sizeof(*arg));
if (num_params < sess->ocall_ctx.call_arg.num_params) {
arg->num_params = 0;
rc = -EINVAL;
}
}

/* Wipe Ocall context deprecated information */
memset(&sess->ocall_ctx, 0, sizeof(sess->ocall_ctx));
}
mutex_unlock(&ctxdata->mutex);

if (rc) {
msg_arg->ret = TEEC_ERROR_COMMUNICATION;
msg_arg->ret_origin = TEEC_ORIGIN_COMMS;
}
Expand Down Expand Up @@ -466,6 +554,8 @@ int optee_cancel_req(struct tee_context *ctx, u32 cancel_id, u32 session)
mutex_unlock(&ctxdata->mutex);
if (!sess)
return -EINVAL;
if (WARN_ONCE(sess->ocall_ctx.thread_p1, "Can't cancel, on going Ocall\n"))
return -EINVAL;

msg_arg = optee_get_msg_arg(ctx, 0, &entry, &shm, &offs);
if (IS_ERR(msg_arg))
Expand All @@ -474,7 +564,7 @@ int optee_cancel_req(struct tee_context *ctx, u32 cancel_id, u32 session)
msg_arg->cmd = OPTEE_MSG_CMD_CANCEL;
msg_arg->session = session;
msg_arg->cancel_id = cancel_id;
optee->ops->do_call_with_arg(ctx, shm, offs);
optee->ops->do_call_with_arg(ctx, shm, offs, NULL);

optee_free_msg_arg(ctx, entry, offs);
return 0;
Expand Down
3 changes: 2 additions & 1 deletion drivers/tee/optee/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,8 @@ static void optee_release_helper(struct tee_context *ctx,
list_for_each_entry_safe(sess, sess_tmp, &ctxdata->sess_list,
list_node) {
list_del(&sess->list_node);
close_session(ctx, sess->session_id);
if (!WARN_ONCE(sess->ocall_ctx.thread_p1, "Can't close, on going Ocall\n"))
close_session(ctx, sess->session_id);
kfree(sess);
}
kfree(ctxdata);
Expand Down
3 changes: 2 additions & 1 deletion drivers/tee/optee/ffa_abi.c
Original file line number Diff line number Diff line change
Expand Up @@ -612,7 +612,8 @@ static int optee_ffa_yielding_call(struct tee_context *ctx,
*/

static int optee_ffa_do_call_with_arg(struct tee_context *ctx,
struct tee_shm *shm, u_int offs)
struct tee_shm *shm, u_int offs,
struct optee_call_extra *call_extra)
{
struct ffa_send_direct_data data = {
.data0 = OPTEE_FFA_YIELDING_CALL_WITH_ARG,
Expand Down
45 changes: 44 additions & 1 deletion drivers/tee/optee/optee_private.h
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,18 @@ struct optee_ffa {

struct optee;

/**
* struct optee_call_extra - Extra data used by calling TEE
* @ocall_arg: OCall arguments related to the call or NULL
* @tee_thread_id: TEE thread ID to use to return from an OCall, or 0
* @ocall_call_waiter: Reference to the waiter that tracks TEE entry
*/
struct optee_call_extra {
struct tee_ocall2_arg *ocall_arg;
u32 tee_thread_id;
struct optee_call_waiter *ocall_call_waiter;
};

/**
* struct optee_ops - OP-TEE driver internal operations
* @do_call_with_arg: enters OP-TEE in secure world
Expand All @@ -154,7 +166,8 @@ struct optee;
*/
struct optee_ops {
int (*do_call_with_arg)(struct tee_context *ctx,
struct tee_shm *shm_arg, u_int offs);
struct tee_shm *shm_arg, u_int offs,
struct optee_call_extra *extra);
int (*to_msg_param)(struct optee *optee,
struct optee_msg_param *msg_params,
size_t num_params, const struct tee_param *params);
Expand Down Expand Up @@ -201,9 +214,34 @@ struct optee {
struct work_struct scan_bus_work;
};

/**
* struct tee_session_ocall - Ocall context of a session
* @thread_p1: TEE thread ID plus 1 (0 means no suspended TEE thread)
* @call_waiter: Waiter for the call entry completed at Ocall return entry
* @call_arg: Invocation argument from initial call that issued the Ocall
* @msg_arg: Reference to optee msg used at initial call
* @msg_entry: Reference to optee msg allocation entry
* @msg_offs: Reference to optee msg allocation offset
*/
struct tee_session_ocall {
u32 thread_p1;
struct optee_call_waiter call_waiter;
struct tee_ioctl_invoke_arg call_arg;
struct optee_msg_arg *msg_arg;
struct optee_shm_arg_entry *msg_entry;
u_int msg_offs;
};

/**
* struct tee_session - Session between a client and a TEE service (TA)
* @list_node: User list for the session
* @session_id: Session identifier provided by the TEE
* @ocall_ctx: OCall context
*/
struct optee_session {
struct list_head list_node;
u32 session_id;
struct tee_session_ocall ocall_ctx;
};

struct optee_context_data {
Expand Down Expand Up @@ -256,6 +294,11 @@ int optee_invoke_func(struct tee_context *ctx, struct tee_ioctl_invoke_arg *arg,
struct tee_param *param);
int optee_cancel_req(struct tee_context *ctx, u32 cancel_id, u32 session);

int optee_invoke_func_helper(struct tee_context *ctx,
struct tee_ioctl_invoke_arg *arg,
struct tee_param *param,
struct tee_ocall2_arg *ocall_arg);

#define PTA_CMD_GET_DEVICES 0x0
#define PTA_CMD_GET_DEVICES_SUPP 0x1
int optee_enumerate_devices(u32 func);
Expand Down
23 changes: 23 additions & 0 deletions drivers/tee/optee/optee_smc.h
Original file line number Diff line number Diff line change
Expand Up @@ -579,6 +579,29 @@ struct optee_smc_disable_shm_cache_result {
#define OPTEE_SMC_RETURN_RPC_CMD \
OPTEE_SMC_RPC_VAL(OPTEE_SMC_RPC_FUNC_CMD)

/*
* Do an OCall RPC request to caller client. The Ocall request ABI is very
* minimal: 2 input arguments and 2 output arguments. 1st output argument
* value 0 is reserved to error management requesting OCall termination.
* When 1st output argument is 0, 2nd output argument is either 0 or can
* carry a TEEC_Result like error code.
*
* "Call" register usage:
* a0 OPTEE_SMC_RETURN_RPC_OCALL2
* a1 OCall input argument 1 (32 bit)
* a2 OCall input argument 2 (32 bit)
* a3-7 Resume information, must be preserved
*
* "Return" register usage:
* a0 SMC Function ID, OPTEE_SMC_CALL_RETURN_FROM_RPC.
* a1 OCall output argument 1 (32 bit), value 0 upon error
* a2 OCall output argument 2 (32 bit), TEEC_Result error if @a1 is 0
* a3-7 Preserved
*/
#define OPTEE_SMC_RPC_FUNC_OCALL2 2048
#define OPTEE_SMC_RETURN_RPC_OCALL2 \
OPTEE_SMC_RPC_VAL(OPTEE_SMC_RPC_FUNC_OCALL2)

/* Returned in a0 */
#define OPTEE_SMC_RETURN_UNKNOWN_FUNCTION 0xFFFFFFFF

Expand Down
Loading

0 comments on commit 28d62e6

Please sign in to comment.