diff --git a/drivers/tee/amdtee/amdtee_private.h b/drivers/tee/amdtee/amdtee_private.h index d7f798c3394bc7..a837676d1866ac 100644 --- a/drivers/tee/amdtee/amdtee_private.h +++ b/drivers/tee/amdtee/amdtee_private.h @@ -122,13 +122,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); diff --git a/drivers/tee/amdtee/core.c b/drivers/tee/amdtee/core.c index 27b4cd77d0db6f..4ff958b1b931a1 100644 --- a/drivers/tee/amdtee/core.c +++ b/drivers/tee/amdtee/core.c @@ -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; @@ -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; @@ -279,7 +286,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); @@ -391,12 +398,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); @@ -409,7 +423,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; } @@ -422,6 +436,7 @@ int amdtee_cancel_req(struct tee_context *ctx, u32 cancel_id, u32 session) static const struct tee_driver_ops amdtee_ops = { .get_version = amdtee_get_version, .open = amdtee_open, + .pre_release = NULL, .release = amdtee_release, .open_session = amdtee_open_session, .close_session = amdtee_close_session, diff --git a/drivers/tee/optee/call.c b/drivers/tee/optee/call.c index 49cc9b0dc13abf..24b5153db47ea6 100644 --- a/drivers/tee/optee/call.c +++ b/drivers/tee/optee/call.c @@ -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; @@ -136,6 +138,11 @@ int optee_open_session(struct tee_context *ctx, phys_addr_t msg_parg; struct optee_session *sess = NULL; + 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)) @@ -160,7 +167,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_to_msg_param(msg_arg->params + 2, arg->num_params, + normal_param); if (rc) goto out; @@ -185,7 +193,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 */ @@ -232,7 +241,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; @@ -241,6 +251,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); @@ -256,7 +271,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; @@ -265,7 +280,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; } diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c index df22fcd86092d5..c2fae61401ecb9 100644 --- a/drivers/tee/optee/core.c +++ b/drivers/tee/optee/core.c @@ -334,6 +334,7 @@ static void optee_release(struct tee_context *ctx) static const struct tee_driver_ops optee_ops = { .get_version = optee_get_version, .open = optee_open, + .pre_release = NULL, .release = optee_release, .open_session = optee_open_session, .close_session = optee_close_session, diff --git a/drivers/tee/optee/optee_private.h b/drivers/tee/optee/optee_private.h index 9c40bd776755a2..53ccc89c3e7663 100644 --- a/drivers/tee/optee/optee_private.h +++ b/drivers/tee/optee/optee_private.h @@ -155,10 +155,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); diff --git a/drivers/tee/tee_core.c b/drivers/tee/tee_core.c index 5234323de456c1..42b59375974af7 100644 --- a/drivers/tee/tee_core.c +++ b/drivers/tee/tee_core.c @@ -98,6 +98,8 @@ void teedev_ctx_put(struct tee_context *ctx) static void teedev_close_context(struct tee_context *ctx) { + if (ctx->teedev->desc->ops->pre_release) + ctx->teedev->desc->ops->pre_release(ctx); tee_device_put(ctx->teedev); teedev_ctx_put(ctx); } @@ -390,9 +392,81 @@ static int tee_ioctl_shm_register_fd(struct tee_context *ctx, return ret; } -static int params_from_user(struct tee_context *ctx, struct tee_param *params, - size_t num_params, - struct tee_ioctl_param __user *uparams) +static bool param_is_ocall_reply(struct tee_ioctl_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 && + param->a; +} + +static bool param_is_ocall_request(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 && + param->u.value.a; +} + +static bool param_is_ocall_request_safe(struct tee_param *param) +{ + return param ? param_is_ocall_request(param) : false; +} + +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 struct tee_shm *shm_from_user(struct tee_context *ctx, + struct tee_ioctl_param *ip) +{ + struct tee_shm *shm = ERR_PTR(-EINVAL); + + /* + * If a NULL pointer is passed to a TA in the TEE, + * the ip->c IOCTL parameters is set to TEE_MEMREF_NULL + * indicating a NULL memory reference. + */ + if (ip->c != TEE_MEMREF_NULL) { + /* + * If we fail to get a pointer to a shared + * memory object (and increase the ref count) + * from an identifier we return an error. All + * pointers that has been added in params have + * an increased ref count. It's the callers + * responibility to do tee_shm_put() on all + * resolved pointers. + */ + shm = tee_shm_get_from_id(ctx, ip->c); + if (IS_ERR(shm)) + return shm; + + /* + * Ensure offset + size does not overflow + * offset and does not overflow the size of + * the referred shared memory object. + */ + if ((ip->a + ip->b) < ip->a || (ip->a + ip->b) > shm->size) { + tee_shm_put(shm); + return ERR_PTR(-EINVAL); + } + } else if (ctx->cap_memref_null) { + /* Pass NULL pointer to OP-TEE */ + shm = NULL; + } + + return shm; +} + +static int params_from_user_normal(struct tee_context *ctx, + struct tee_param *params, size_t num_params, + struct tee_ioctl_param __user *uparams) { size_t n; @@ -421,41 +495,63 @@ static int params_from_user(struct tee_context *ctx, struct tee_param *params, case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT: case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT: case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT: + shm = shm_from_user(ctx, &ip); + if (IS_ERR(shm)) + return PTR_ERR(shm); + + params[n].u.memref.shm_offs = ip.a; + params[n].u.memref.size = ip.b; + params[n].u.memref.shm = shm; + break; + default: + /* Unknown attribute */ + return -EINVAL; + } + } + return 0; +} + +static int params_from_user_ocall(struct tee_context *ctx, + struct tee_param *params, size_t num_params, + struct tee_ioctl_param __user *uparams) +{ + size_t n; + + for (n = 0; n < num_params; n++) { + struct tee_shm *shm; + struct tee_ioctl_param ip; + + if (copy_from_user(&ip, uparams + n, sizeof(ip))) + return -EFAULT; + + /* All unused attribute bits has to be zero */ + if (ip.attr & ~TEE_IOCTL_PARAM_ATTR_MASK) + return -EINVAL; + + params[n].attr = ip.attr; + switch (ip.attr & TEE_IOCTL_PARAM_ATTR_TYPE_MASK) { + case TEE_IOCTL_PARAM_ATTR_TYPE_NONE: + case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT: + break; + case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT: + case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT: + params[n].u.value.a = ip.a; + params[n].u.value.b = ip.b; + params[n].u.value.c = ip.c; + 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: + shm = shm_from_user(ctx, &ip); + if (IS_ERR(shm)) + return PTR_ERR(shm); + /* - * If a NULL pointer is passed to a TA in the TEE, - * the ip.c IOCTL parameters is set to TEE_MEMREF_NULL - * indicating a NULL memory reference. + * Reference counting for OCALL memref parameters is + * handled by the TEE-specific driver as necessary. */ - if (ip.c != TEE_MEMREF_NULL) { - /* - * If we fail to get a pointer to a shared - * memory object (and increase the ref count) - * from an identifier we return an error. All - * pointers that has been added in params have - * an increased ref count. It's the callers - * responibility to do tee_shm_put() on all - * resolved pointers. - */ - shm = tee_shm_get_from_id(ctx, ip.c); - if (IS_ERR(shm)) - return PTR_ERR(shm); - - /* - * Ensure offset + size does not overflow - * offset and does not overflow the size of - * the referred shared memory object. - */ - if ((ip.a + ip.b) < ip.a || - (ip.a + ip.b) > shm->size) { - tee_shm_put(shm); - return -EINVAL; - } - } else if (ctx->cap_memref_null) { - /* Pass NULL pointer to OP-TEE */ - shm = NULL; - } else { - return -EINVAL; - } + if (shm) + tee_shm_put(shm); params[n].u.memref.shm_offs = ip.a; params[n].u.memref.size = ip.b; @@ -469,8 +565,25 @@ static int params_from_user(struct tee_context *ctx, struct tee_param *params, return 0; } -static int params_to_user(struct tee_ioctl_param __user *uparams, - size_t num_params, struct tee_param *params) +static int params_from_user(struct tee_context *ctx, struct tee_param *params, + size_t num_params, + struct tee_ioctl_param __user *uparams) +{ + struct tee_ioctl_param ip; + + if (!num_params) + return 0; + + if (copy_from_user(&ip, uparams, sizeof(ip))) + return -EFAULT; + + return param_is_ocall_reply(&ip) + ? params_from_user_ocall(ctx, params, num_params, uparams) + : params_from_user_normal(ctx, params, num_params, uparams); +} + +static int params_to_user_normal(struct tee_ioctl_param __user *uparams, + size_t num_params, struct tee_param *params) { size_t n; @@ -478,7 +591,7 @@ static int params_to_user(struct tee_ioctl_param __user *uparams, struct tee_ioctl_param __user *up = uparams + n; struct tee_param *p = params + n; - switch (p->attr) { + switch (p->attr & TEE_IOCTL_PARAM_ATTR_TYPE_MASK) { case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT: case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT: if (put_user(p->u.value.a, &up->a) || @@ -497,6 +610,87 @@ static int params_to_user(struct tee_ioctl_param __user *uparams, return 0; } +static int params_to_user_ocall(struct tee_ioctl_param __user *uparams, + size_t num_params, struct tee_param *params) +{ + size_t n; + + for (n = 0; n < num_params; n++) { + struct tee_ioctl_param __user *up = uparams + n; + struct tee_param *p = params + n; + + if (put_user(p->attr, &up->attr)) + return -EFAULT; + + switch (p->attr & TEE_IOCTL_PARAM_ATTR_TYPE_MASK) { + case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT: + case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT: + if (put_user(p->u.value.a, &up->a) || + put_user(p->u.value.b, &up->b) || + put_user(p->u.value.c, &up->c)) + return -EFAULT; + 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 (p->u.memref.shm) { + if ((put_user((u64)p->u.memref.shm_offs, + &up->a) || + put_user((u64)p->u.memref.size, &up->b) || + put_user(p->u.memref.shm->id, &up->c))) + return -EFAULT; + } else { + if (put_user(0, &up->a) || + put_user(0, &up->b) || + put_user(TEE_MEMREF_NULL, &up->c)) + return -EFAULT; + } + break; + default: + break; + } + } + return 0; +} + +static int params_to_user(struct tee_ioctl_param __user *uparams, + size_t num_params, struct tee_param *params) +{ + if (!num_params) + return 0; + + return param_is_ocall_request(params) + ? params_to_user_ocall(uparams, num_params, params) + : params_to_user_normal(uparams, num_params, params); +} + +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) { @@ -507,6 +701,9 @@ static int tee_ioctl_open_session(struct tee_context *ctx, struct tee_ioctl_open_session_arg arg; struct tee_ioctl_param __user *uparams = NULL; struct tee_param *params = NULL; + struct tee_param *ocall_param = NULL; + struct tee_param *normal_params = NULL; + u32 num_normal_params = 0; bool have_session = false; if (!ctx->teedev->desc->ops->open_session) @@ -535,6 +732,10 @@ static int tee_ioctl_open_session(struct tee_context *ctx, rc = params_from_user(ctx, params, arg.num_params, uparams); if (rc) goto out; + rc = find_ocall_param(params, arg.num_params, &normal_params, + &num_normal_params, &ocall_param); + if (rc) + goto out; } if (arg.clnt_login >= TEE_IOCTL_LOGIN_REE_KERNEL_MIN && @@ -544,7 +745,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, normal_params, + num_normal_params, + ocall_param); if (rc) goto out; have_session = true; @@ -566,10 +769,11 @@ static int tee_ioctl_open_session(struct tee_context *ctx, if (params) { /* Decrease ref count for all valid shared memory pointers */ - for (n = 0; n < arg.num_params; n++) - if (tee_param_is_memref(params + n) && - params[n].u.memref.shm) - tee_shm_put(params[n].u.memref.shm); + if (!param_is_ocall_request_safe(ocall_param)) + for (n = 0; n < arg.num_params; n++) + if (tee_param_is_memref(params + n) && + params[n].u.memref.shm) + tee_shm_put(params[n].u.memref.shm); kfree(params); } @@ -586,6 +790,9 @@ static int tee_ioctl_invoke(struct tee_context *ctx, struct tee_ioctl_invoke_arg arg; struct tee_ioctl_param __user *uparams = NULL; struct tee_param *params = NULL; + struct tee_param *ocall_param = NULL; + struct tee_param *normal_params = NULL; + u32 num_normal_params = 0; if (!ctx->teedev->desc->ops->invoke_func) return -EINVAL; @@ -613,9 +820,23 @@ static int tee_ioctl_invoke(struct tee_context *ctx, rc = params_from_user(ctx, params, arg.num_params, uparams); if (rc) goto out; + + /* + * The OCALL parameter must be first so that we know how to + * process the remainder of the parameters, or not be present at + * all. This function returns an error if the OCALL parameter is + * found in the wrong place. If it is not found, 'ocall_param' + * remains NULL. + */ + rc = find_ocall_param(params, arg.num_params, &normal_params, + &num_normal_params, &ocall_param); + if (rc) + goto out; } - rc = ctx->teedev->desc->ops->invoke_func(ctx, &arg, params); + rc = ctx->teedev->desc->ops->invoke_func(ctx, &arg, normal_params, + num_normal_params, + ocall_param); if (rc) goto out; @@ -627,11 +848,22 @@ static int tee_ioctl_invoke(struct tee_context *ctx, rc = params_to_user(uparams, arg.num_params, params); out: if (params) { - /* Decrease ref count for all valid shared memory pointers */ - for (n = 0; n < arg.num_params; n++) - if (tee_param_is_memref(params + n) && - params[n].u.memref.shm) - tee_shm_put(params[n].u.memref.shm); + /* + * Decrease the ref count for all valid shared memory pointers + * if this is a normal return. If returning with an OCALL + * request, the parameters should have been overwritten with + * those of the OCALL. The original parameters, and thus the + * memrefs carrying the SHMs whose ref count was increased on + * entry, shall be restored once the full OCALL sequence is + * finished. When that happens, we decrease the ref count on + * them. Otherwise, we leave the SHMs be; the TEE-specific + * driver should have dealt with their ref counts already. + */ + if (!param_is_ocall_request_safe(ocall_param)) + for (n = 0; n < arg.num_params; n++) + if (tee_param_is_memref(params + n) && + params[n].u.memref.shm) + tee_shm_put(params[n].u.memref.shm); kfree(params); } return rc; @@ -1220,9 +1452,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); @@ -1238,9 +1483,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); diff --git a/include/linux/tee_drv.h b/include/linux/tee_drv.h index 9ddb3010b4d2b4..ef141997c7cd39 100644 --- a/include/linux/tee_drv.h +++ b/include/linux/tee_drv.h @@ -36,20 +36,22 @@ struct tee_shm_pool; /** * struct tee_context - driver specific context on file pointer data - * @teedev: pointer to this drivers struct tee_device - * @list_shm: List of shared memory object owned by this context - * @data: driver specific context data, managed by the driver - * @refcount: reference counter for this structure - * @releasing: flag that indicates if context is being released right now. - * It is needed to break circular dependency on context during - * shared memory release. - * @supp_nowait: flag that indicates that requests in this context should not - * wait for tee-supplicant daemon to be started if not present - * and just return with an error code. It is needed for requests - * that arises from TEE based kernel drivers that should be - * non-blocking in nature. + * @teedev: pointer to this drivers struct tee_device + * @list_shm: List of shared memory object owned by this context + * @data: driver specific context data, managed by the driver + * @refcount: reference counter for this structure + * @releasing: flag that indicates if context is being released right now. + * It is needed to break circular dependency on context during + * shared memory release. + * @supp_nowait: flag that indicates that requests in this context should + * not wait for tee-supplicant daemon to be started if not + * present and just return with an error code. It is needed + * for requests that arises from TEE based kernel drivers that + * should be 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; @@ -58,6 +60,7 @@ struct tee_context { bool releasing; bool supp_nowait; bool cap_memref_null; + bool cap_ocall; }; struct tee_param_memref { @@ -84,6 +87,7 @@ struct tee_param { * struct tee_driver_ops - driver operations vtable * @get_version: returns version of driver * @open: called when the device file is opened + * @pre_release: called prior to context release, before release proper * @release: release this open file * @open_session: open a new session * @close_session: close a session @@ -98,14 +102,19 @@ struct tee_driver_ops { void (*get_version)(struct tee_device *teedev, struct tee_ioctl_version_data *vers); int (*open)(struct tee_context *ctx); + void (*pre_release)(struct tee_context *ctx); 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); diff --git a/include/uapi/linux/tee.h b/include/uapi/linux/tee.h index 083a1b81b4c62d..54faa0c23418c5 100644 --- a/include/uapi/linux/tee.h +++ b/include/uapi/linux/tee.h @@ -194,9 +194,14 @@ struct tee_ioctl_buf_data { /* Meta parameter carrying extra information about the message. */ #define TEE_IOCTL_PARAM_ATTR_META 0x100 +/* Parameter carrying information about an OCALL reply or request. */ +#define TEE_IOCTL_PARAM_ATTR_OCALL 0x200 + /* Mask of all known attr bits */ #define TEE_IOCTL_PARAM_ATTR_MASK \ - (TEE_IOCTL_PARAM_ATTR_TYPE_MASK | TEE_IOCTL_PARAM_ATTR_META) + (TEE_IOCTL_PARAM_ATTR_TYPE_MASK | \ + TEE_IOCTL_PARAM_ATTR_META | \ + TEE_IOCTL_PARAM_ATTR_OCALL) /* * Matches TEEC_LOGIN_* in GP TEE Client API @@ -289,6 +294,54 @@ struct tee_ioctl_open_session_arg { #define TEE_IOC_OPEN_SESSION _IOR(TEE_IOC_MAGIC, TEE_IOC_BASE + 2, \ struct tee_ioctl_buf_data) +/* + * Command sent to the CA to request allocation of shared memory to carry the + * parameters of an OCALL + * + * [in] param[0].u.value.b requested memory size + * [out] param[0].u.value.c SHM ID + * + * Note: [in] means from driver to CA, [out], from CA to driver. + */ +#define TEE_IOCTL_OCALL_CMD_SHM_ALLOC 1 + +/* + * Command sent to the CA to free previously allocated shared memory. + * + * [in] param[0].u.value.c SHM ID + * + * Note: [in] means from driver to CA. + */ +#define TEE_IOCTL_OCALL_CMD_SHM_FREE 2 + +/* + * Command sent to the CA to execute an OCALL by Id. + * + * [any] param[0..3].u.* carry OCALL parameters + */ +#define TEE_IOCTL_OCALL_CMD_INVOKE 3 + +/* + * Join the Id of the function that the TEE Client API must execute on behalf of + * the CA with the Id of the command that the CA must execute + * + * As an example, TEE_IOCTL_OCALL_MAKE_PAIR(TEE_IOCTL_OCALL_CMD_INVOKE, 10) + * means that the Client API must forward a function invocation to a CA-provided + * handler, and the handler must execute command Id '10', whose meaning is up to + * the user-defined contract between the CA & TA. + */ +#define TEE_IOCTL_OCALL_MAKE_PAIR(func, cmd) \ + (((__u64)(func) << 32) | (__u32)(cmd)) + +/* + * Get the Id of the function that the TEE Client API must execute on behalf of + * the CA + */ +#define TEE_IOCTL_OCALL_GET_FUNC(x) ((__u32)((x) >> 32)) + +/* Get the Id of the command that the CA must execute */ +#define TEE_IOCTL_OCALL_GET_CMD(x) ((__u32)(x)) + /** * struct tee_ioctl_invoke_func_arg - Invokes a function in a Trusted * Application