Skip to content

Commit

Permalink
tee: OCALL support for kernel TEE client drivers
Browse files Browse the repository at this point in the history
Enable Trusted Applications (TAs) to invoke functions on their
corresponding client in Linux kernel driver during both both session
open and command invocation. These function invocations from TA to client
are referred to as "Out Calls", or OCALLs for short.

The fundamental mechanism is one whereby upon a function invocation from
the client to the TA, the TEE returns prematurely from the invocation
with an RPC. This RPC is generated after a TA calls a TEEC_InvokeCommand()
equivalent function in secure world. The RPC carries information
describing the OCALL as well as its parameters. When this happens, the
driver saves the state of the current call and returns to user-mode.

The TEE kernel client API has to call tee_client_open_session() or
tee_client_invoke_command() with a special parameter that carries
OCALL information. When the function returns prematurely, this parameter
includes information about what the client is expected to do on behalf
of the TA along with data to be used to reply to the request.

Once that is done, TEE kernel client API calls tee_client_open_session()
(respectively tee_client_invoke_command()) again with the modified
OCALL parameter and associated information (such as the result of the
OCALL and the output parameters as requested by the TA). The driver
notices that this invocation is in fact a resumption as opposed to a
brand-new invocation, and resumes the secure world thread that sent
the RPC in the first place.

The same mechanism applies to OCALLs during session open.

This patch also minimally updates the OP-TEE and AMD TEE drivers to match
the new signatures for session open and invoke. If an OCALL is specified
by the CA, EOPNOTSUPP is returned.

This change it based on the OCALL implementation proposal from Hernan
Gatta posted in [1] with few modifications to remove changes in shared
memory from/to sequence since OCALL is not yet available to user client
application, and to remove TEE drivers pre-release handler that are not
needed when supporting OCalls only in Linux kernel TEE client drivers.

Link: [1] linaro-swg/linux#72
Co-developed-by: Hernan Gatta <[email protected]>
Signed-off-by: Hernan Gatta <[email protected]>
Signed-off-by: Etienne Carriere <[email protected]>
Change-Id: I95b35e2447bfb24b729d7bf1d3dec4cc620100e6
  • Loading branch information
etienne-lms authored and fourmone committed Mar 25, 2022
1 parent 9061b6f commit a2264d2
Show file tree
Hide file tree
Showing 8 changed files with 165 additions and 22 deletions.
8 changes: 6 additions & 2 deletions drivers/tee/amdtee/amdtee_private.h
Original file line number Diff line number Diff line change
Expand Up @@ -135,13 +135,17 @@ static inline u32 get_session_index(u32 session)

int amdtee_open_session(struct tee_context *ctx,
struct tee_ioctl_open_session_arg *arg,
struct tee_param *param);
struct tee_param *normal_param,
u32 num_normal_params,
struct tee_param *ocall_param);

int amdtee_close_session(struct tee_context *ctx, u32 session);

int amdtee_invoke_func(struct tee_context *ctx,
struct tee_ioctl_invoke_arg *arg,
struct tee_param *param);
struct tee_param *normal_param,
u32 num_normal_params,
struct tee_param *ocall_param);

int amdtee_cancel_req(struct tee_context *ctx, u32 cancel_id, u32 session);

Expand Down
22 changes: 18 additions & 4 deletions drivers/tee/amdtee/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,9 @@ static void destroy_session(struct kref *ref)

int amdtee_open_session(struct tee_context *ctx,
struct tee_ioctl_open_session_arg *arg,
struct tee_param *param)
struct tee_param *normal_param,
u32 num_normal_params,
struct tee_param *ocall_param)
{
struct amdtee_context_data *ctxdata = ctx->data;
struct amdtee_session *sess = NULL;
Expand All @@ -239,6 +241,11 @@ int amdtee_open_session(struct tee_context *ctx,
int rc, i;
void *ta;

if (ocall_param) {
pr_err("OCALLs not supported\n");
return -EOPNOTSUPP;
}

if (arg->clnt_login != TEE_IOCTL_LOGIN_PUBLIC) {
pr_err("unsupported client login method\n");
return -EINVAL;
Expand Down Expand Up @@ -283,7 +290,7 @@ int amdtee_open_session(struct tee_context *ctx,
}

/* Open session with loaded TA */
handle_open_session(arg, &session_info, param);
handle_open_session(arg, &session_info, normal_param);
if (arg->ret != TEEC_SUCCESS) {
pr_err("open_session failed %d\n", arg->ret);
spin_lock(&sess->lock);
Expand Down Expand Up @@ -405,12 +412,19 @@ void amdtee_unmap_shmem(struct tee_shm *shm)

int amdtee_invoke_func(struct tee_context *ctx,
struct tee_ioctl_invoke_arg *arg,
struct tee_param *param)
struct tee_param *normal_param,
u32 num_normal_params,
struct tee_param *ocall_param)
{
struct amdtee_context_data *ctxdata = ctx->data;
struct amdtee_session *sess;
u32 i, session_info;

if (ocall_param) {
pr_err("OCALLs not supported\n");
return -EOPNOTSUPP;
}

/* Check that the session is valid */
mutex_lock(&session_list_mutex);
sess = find_session(ctxdata, arg->session);
Expand All @@ -423,7 +437,7 @@ int amdtee_invoke_func(struct tee_context *ctx,
if (!sess)
return -EINVAL;

handle_invoke_cmd(arg, session_info, param);
handle_invoke_cmd(arg, session_info, normal_param);

return 0;
}
Expand Down
28 changes: 22 additions & 6 deletions drivers/tee/optee/call.c
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,9 @@ static struct tee_shm *get_msg_arg(struct tee_context *ctx, size_t num_params,

int optee_open_session(struct tee_context *ctx,
struct tee_ioctl_open_session_arg *arg,
struct tee_param *param)
struct tee_param *normal_param,
u32 num_normal_params,
struct tee_param *ocall_param)
{
struct optee_context_data *ctxdata = ctx->data;
int rc;
Expand All @@ -137,6 +139,11 @@ int optee_open_session(struct tee_context *ctx,
struct optee_session *sess = NULL;
uuid_t client_uuid;

if (ocall_param) {
pr_err("OCALLs not supported\n");
return -EOPNOTSUPP;
}

/* +2 for the meta parameters added below */
shm = get_msg_arg(ctx, arg->num_params + 2, &msg_arg, &msg_parg);
if (IS_ERR(shm))
Expand All @@ -162,7 +169,8 @@ int optee_open_session(struct tee_context *ctx,
goto out;
export_uuid(msg_arg->params[1].u.octets, &client_uuid);

rc = optee_to_msg_param(msg_arg->params + 2, arg->num_params, param);
rc = optee_to_msg_param(msg_arg->params + 2, arg->num_params,
normal_param);
if (rc)
goto out;

Expand All @@ -187,7 +195,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_from_msg_param(normal_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 */
Expand Down Expand Up @@ -234,7 +243,8 @@ 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 tee_param *normal_param, u32 num_normal_params,
struct tee_param *ocall_param)
{
struct optee_context_data *ctxdata = ctx->data;
struct tee_shm *shm;
Expand All @@ -243,6 +253,11 @@ int optee_invoke_func(struct tee_context *ctx, struct tee_ioctl_invoke_arg *arg,
struct optee_session *sess;
int rc;

if (ocall_param) {
pr_err("OCALLs not supported\n");
return -EOPNOTSUPP;
}

/* Check that the session is valid */
mutex_lock(&ctxdata->mutex);
sess = find_session(ctxdata, arg->session);
Expand All @@ -258,7 +273,7 @@ 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_to_msg_param(msg_arg->params, arg->num_params, normal_param);
if (rc)
goto out;

Expand All @@ -267,7 +282,8 @@ int optee_invoke_func(struct tee_context *ctx, struct tee_ioctl_invoke_arg *arg,
msg_arg->ret_origin = TEEC_ORIGIN_COMMS;
}

if (optee_from_msg_param(param, arg->num_params, msg_arg->params)) {
if (optee_from_msg_param(normal_param, arg->num_params,
msg_arg->params)) {
msg_arg->ret = TEEC_ERROR_COMMUNICATION;
msg_arg->ret_origin = TEEC_ORIGIN_COMMS;
}
Expand Down
6 changes: 4 additions & 2 deletions drivers/tee/optee/optee_private.h
Original file line number Diff line number Diff line change
Expand Up @@ -156,10 +156,12 @@ int optee_supp_send(struct tee_context *ctx, u32 ret, u32 num_params,
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);
struct tee_param *normal_param, u32 num_normal_params,
struct tee_param *ocall_param);
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 tee_param *normal_param, u32 num_normal_params,
struct tee_param *ocall_param);
int optee_cancel_req(struct tee_context *ctx, u32 cancel_id, u32 session);

void optee_enable_shm_cache(struct optee *optee);
Expand Down
73 changes: 69 additions & 4 deletions drivers/tee/tee_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,14 @@ tee_ioctl_shm_register(struct tee_context *ctx,
return ret;
}

static bool param_is_ocall(struct tee_param *param)
{
u64 type = param->attr & TEE_IOCTL_PARAM_ATTR_TYPE_MASK;

return param->attr & TEE_IOCTL_PARAM_ATTR_OCALL &&
type == TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT;
}

static int params_from_user(struct tee_context *ctx, struct tee_param *params,
size_t num_params,
struct tee_ioctl_param __user *uparams)
Expand Down Expand Up @@ -460,6 +468,33 @@ static int params_to_user(struct tee_ioctl_param __user *uparams,
return 0;
}

static inline int find_ocall_param(struct tee_param *params, u32 num_params,
struct tee_param **normal_params,
u32 *num_normal_params,
struct tee_param **ocall_param)
{
size_t n;

for (n = 0; n < num_params; n++) {
if (param_is_ocall(params + n)) {
if (n == 0) {
*normal_params = params + 1;
*num_normal_params = num_params - 1;
*ocall_param = params;
return 0;
} else {
return -EINVAL;
}
}
}

*normal_params = params;
*num_normal_params = num_params;
*ocall_param = NULL;

return 0;
}

static int tee_ioctl_open_session(struct tee_context *ctx,
struct tee_ioctl_buf_data __user *ubuf)
{
Expand Down Expand Up @@ -507,7 +542,9 @@ static int tee_ioctl_open_session(struct tee_context *ctx,
goto out;
}

rc = ctx->teedev->desc->ops->open_session(ctx, &arg, params);
rc = ctx->teedev->desc->ops->open_session(ctx, &arg, params,
arg.num_params,
NULL /*ocall_param*/);
if (rc)
goto out;
have_session = true;
Expand Down Expand Up @@ -578,7 +615,9 @@ static int tee_ioctl_invoke(struct tee_context *ctx,
goto out;
}

rc = ctx->teedev->desc->ops->invoke_func(ctx, &arg, params);
rc = ctx->teedev->desc->ops->invoke_func(ctx, &arg, params,
arg.num_params,
NULL /*ocall_param*/);
if (rc)
goto out;

Expand Down Expand Up @@ -1155,9 +1194,22 @@ int tee_client_open_session(struct tee_context *ctx,
struct tee_ioctl_open_session_arg *arg,
struct tee_param *param)
{
struct tee_param *ocall_param = NULL;
struct tee_param *normal_params = NULL;
u32 num_normal_params = 0;
int rc;

if (!ctx->teedev->desc->ops->open_session)
return -EINVAL;
return ctx->teedev->desc->ops->open_session(ctx, arg, param);

rc = find_ocall_param(param, arg->num_params, &normal_params,
&num_normal_params, &ocall_param);
if (rc)
return rc;

return ctx->teedev->desc->ops->open_session(ctx, arg, normal_params,
num_normal_params,
ocall_param);
}
EXPORT_SYMBOL_GPL(tee_client_open_session);

Expand All @@ -1173,9 +1225,22 @@ int tee_client_invoke_func(struct tee_context *ctx,
struct tee_ioctl_invoke_arg *arg,
struct tee_param *param)
{
struct tee_param *ocall_param = NULL;
struct tee_param *normal_params = NULL;
u32 num_normal_params = 0;
int rc;

if (!ctx->teedev->desc->ops->invoke_func)
return -EINVAL;
return ctx->teedev->desc->ops->invoke_func(ctx, arg, param);

rc = find_ocall_param(param, arg->num_params, &normal_params,
&num_normal_params, &ocall_param);
if (rc)
return rc;

return ctx->teedev->desc->ops->invoke_func(ctx, arg, normal_params,
num_normal_params,
ocall_param);
}
EXPORT_SYMBOL_GPL(tee_client_invoke_func);

Expand Down
3 changes: 2 additions & 1 deletion drivers/tee/tee_shm.c
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,8 @@ struct tee_shm *tee_shm_register(struct tee_context *ctx, unsigned long addr,
int num_pages;
unsigned long start;

if (flags != req_user_flags && flags != req_kernel_flags)
if (((flags & req_user_flags) != req_user_flags) &&
((flags & req_kernel_flags) != req_kernel_flags))
return ERR_PTR(-ENOTSUPP);

if (!tee_device_get(teedev))
Expand Down
11 changes: 9 additions & 2 deletions include/linux/tee_drv.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ struct tee_shm_pool;
* non-blocking in nature.
* @cap_memref_null: flag indicating if the TEE Client support shared
* memory buffer with a NULL pointer.
* @cap_ocall: flag indicating that OP-TEE supports OCALLs, allowing TAs
* to invoke commands on their CA.
*/
struct tee_context {
struct tee_device *teedev;
Expand All @@ -58,6 +60,7 @@ struct tee_context {
bool releasing;
bool supp_nowait;
bool cap_memref_null;
bool cap_ocall;
};

struct tee_param_memref {
Expand Down Expand Up @@ -101,11 +104,15 @@ struct tee_driver_ops {
void (*release)(struct tee_context *ctx);
int (*open_session)(struct tee_context *ctx,
struct tee_ioctl_open_session_arg *arg,
struct tee_param *param);
struct tee_param *normal_param,
u32 num_normal_params,
struct tee_param *ocall_param);
int (*close_session)(struct tee_context *ctx, u32 session);
int (*invoke_func)(struct tee_context *ctx,
struct tee_ioctl_invoke_arg *arg,
struct tee_param *param);
struct tee_param *normal_param,
u32 num_normal_params,
struct tee_param *ocall_param);
int (*cancel_req)(struct tee_context *ctx, u32 cancel_id, u32 session);
int (*supp_recv)(struct tee_context *ctx, u32 *func, u32 *num_params,
struct tee_param *param);
Expand Down
Loading

0 comments on commit a2264d2

Please sign in to comment.