162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2015, Linaro Limited 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci#include <linux/device.h> 662306a36Sopenharmony_ci#include <linux/slab.h> 762306a36Sopenharmony_ci#include <linux/uaccess.h> 862306a36Sopenharmony_ci#include "optee_private.h" 962306a36Sopenharmony_ci 1062306a36Sopenharmony_cistruct optee_supp_req { 1162306a36Sopenharmony_ci struct list_head link; 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci bool in_queue; 1462306a36Sopenharmony_ci u32 func; 1562306a36Sopenharmony_ci u32 ret; 1662306a36Sopenharmony_ci size_t num_params; 1762306a36Sopenharmony_ci struct tee_param *param; 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci struct completion c; 2062306a36Sopenharmony_ci}; 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_civoid optee_supp_init(struct optee_supp *supp) 2362306a36Sopenharmony_ci{ 2462306a36Sopenharmony_ci memset(supp, 0, sizeof(*supp)); 2562306a36Sopenharmony_ci mutex_init(&supp->mutex); 2662306a36Sopenharmony_ci init_completion(&supp->reqs_c); 2762306a36Sopenharmony_ci idr_init(&supp->idr); 2862306a36Sopenharmony_ci INIT_LIST_HEAD(&supp->reqs); 2962306a36Sopenharmony_ci supp->req_id = -1; 3062306a36Sopenharmony_ci} 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_civoid optee_supp_uninit(struct optee_supp *supp) 3362306a36Sopenharmony_ci{ 3462306a36Sopenharmony_ci mutex_destroy(&supp->mutex); 3562306a36Sopenharmony_ci idr_destroy(&supp->idr); 3662306a36Sopenharmony_ci} 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_civoid optee_supp_release(struct optee_supp *supp) 3962306a36Sopenharmony_ci{ 4062306a36Sopenharmony_ci int id; 4162306a36Sopenharmony_ci struct optee_supp_req *req; 4262306a36Sopenharmony_ci struct optee_supp_req *req_tmp; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci mutex_lock(&supp->mutex); 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci /* Abort all request retrieved by supplicant */ 4762306a36Sopenharmony_ci idr_for_each_entry(&supp->idr, req, id) { 4862306a36Sopenharmony_ci idr_remove(&supp->idr, id); 4962306a36Sopenharmony_ci req->ret = TEEC_ERROR_COMMUNICATION; 5062306a36Sopenharmony_ci complete(&req->c); 5162306a36Sopenharmony_ci } 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci /* Abort all queued requests */ 5462306a36Sopenharmony_ci list_for_each_entry_safe(req, req_tmp, &supp->reqs, link) { 5562306a36Sopenharmony_ci list_del(&req->link); 5662306a36Sopenharmony_ci req->in_queue = false; 5762306a36Sopenharmony_ci req->ret = TEEC_ERROR_COMMUNICATION; 5862306a36Sopenharmony_ci complete(&req->c); 5962306a36Sopenharmony_ci } 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci supp->ctx = NULL; 6262306a36Sopenharmony_ci supp->req_id = -1; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci mutex_unlock(&supp->mutex); 6562306a36Sopenharmony_ci} 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci/** 6862306a36Sopenharmony_ci * optee_supp_thrd_req() - request service from supplicant 6962306a36Sopenharmony_ci * @ctx: context doing the request 7062306a36Sopenharmony_ci * @func: function requested 7162306a36Sopenharmony_ci * @num_params: number of elements in @param array 7262306a36Sopenharmony_ci * @param: parameters for function 7362306a36Sopenharmony_ci * 7462306a36Sopenharmony_ci * Returns result of operation to be passed to secure world 7562306a36Sopenharmony_ci */ 7662306a36Sopenharmony_ciu32 optee_supp_thrd_req(struct tee_context *ctx, u32 func, size_t num_params, 7762306a36Sopenharmony_ci struct tee_param *param) 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci{ 8062306a36Sopenharmony_ci struct optee *optee = tee_get_drvdata(ctx->teedev); 8162306a36Sopenharmony_ci struct optee_supp *supp = &optee->supp; 8262306a36Sopenharmony_ci struct optee_supp_req *req; 8362306a36Sopenharmony_ci bool interruptable; 8462306a36Sopenharmony_ci u32 ret; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci /* 8762306a36Sopenharmony_ci * Return in case there is no supplicant available and 8862306a36Sopenharmony_ci * non-blocking request. 8962306a36Sopenharmony_ci */ 9062306a36Sopenharmony_ci if (!supp->ctx && ctx->supp_nowait) 9162306a36Sopenharmony_ci return TEEC_ERROR_COMMUNICATION; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci req = kzalloc(sizeof(*req), GFP_KERNEL); 9462306a36Sopenharmony_ci if (!req) 9562306a36Sopenharmony_ci return TEEC_ERROR_OUT_OF_MEMORY; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci init_completion(&req->c); 9862306a36Sopenharmony_ci req->func = func; 9962306a36Sopenharmony_ci req->num_params = num_params; 10062306a36Sopenharmony_ci req->param = param; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci /* Insert the request in the request list */ 10362306a36Sopenharmony_ci mutex_lock(&supp->mutex); 10462306a36Sopenharmony_ci list_add_tail(&req->link, &supp->reqs); 10562306a36Sopenharmony_ci req->in_queue = true; 10662306a36Sopenharmony_ci mutex_unlock(&supp->mutex); 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci /* Tell an eventual waiter there's a new request */ 10962306a36Sopenharmony_ci complete(&supp->reqs_c); 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci /* 11262306a36Sopenharmony_ci * Wait for supplicant to process and return result, once we've 11362306a36Sopenharmony_ci * returned from wait_for_completion(&req->c) successfully we have 11462306a36Sopenharmony_ci * exclusive access again. 11562306a36Sopenharmony_ci */ 11662306a36Sopenharmony_ci while (wait_for_completion_interruptible(&req->c)) { 11762306a36Sopenharmony_ci mutex_lock(&supp->mutex); 11862306a36Sopenharmony_ci interruptable = !supp->ctx; 11962306a36Sopenharmony_ci if (interruptable) { 12062306a36Sopenharmony_ci /* 12162306a36Sopenharmony_ci * There's no supplicant available and since the 12262306a36Sopenharmony_ci * supp->mutex currently is held none can 12362306a36Sopenharmony_ci * become available until the mutex released 12462306a36Sopenharmony_ci * again. 12562306a36Sopenharmony_ci * 12662306a36Sopenharmony_ci * Interrupting an RPC to supplicant is only 12762306a36Sopenharmony_ci * allowed as a way of slightly improving the user 12862306a36Sopenharmony_ci * experience in case the supplicant hasn't been 12962306a36Sopenharmony_ci * started yet. During normal operation the supplicant 13062306a36Sopenharmony_ci * will serve all requests in a timely manner and 13162306a36Sopenharmony_ci * interrupting then wouldn't make sense. 13262306a36Sopenharmony_ci */ 13362306a36Sopenharmony_ci if (req->in_queue) { 13462306a36Sopenharmony_ci list_del(&req->link); 13562306a36Sopenharmony_ci req->in_queue = false; 13662306a36Sopenharmony_ci } 13762306a36Sopenharmony_ci } 13862306a36Sopenharmony_ci mutex_unlock(&supp->mutex); 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci if (interruptable) { 14162306a36Sopenharmony_ci req->ret = TEEC_ERROR_COMMUNICATION; 14262306a36Sopenharmony_ci break; 14362306a36Sopenharmony_ci } 14462306a36Sopenharmony_ci } 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci ret = req->ret; 14762306a36Sopenharmony_ci kfree(req); 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci return ret; 15062306a36Sopenharmony_ci} 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_cistatic struct optee_supp_req *supp_pop_entry(struct optee_supp *supp, 15362306a36Sopenharmony_ci int num_params, int *id) 15462306a36Sopenharmony_ci{ 15562306a36Sopenharmony_ci struct optee_supp_req *req; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci if (supp->req_id != -1) { 15862306a36Sopenharmony_ci /* 15962306a36Sopenharmony_ci * Supplicant should not mix synchronous and asnynchronous 16062306a36Sopenharmony_ci * requests. 16162306a36Sopenharmony_ci */ 16262306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 16362306a36Sopenharmony_ci } 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci if (list_empty(&supp->reqs)) 16662306a36Sopenharmony_ci return NULL; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci req = list_first_entry(&supp->reqs, struct optee_supp_req, link); 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci if (num_params < req->num_params) { 17162306a36Sopenharmony_ci /* Not enough room for parameters */ 17262306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 17362306a36Sopenharmony_ci } 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci *id = idr_alloc(&supp->idr, req, 1, 0, GFP_KERNEL); 17662306a36Sopenharmony_ci if (*id < 0) 17762306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci list_del(&req->link); 18062306a36Sopenharmony_ci req->in_queue = false; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci return req; 18362306a36Sopenharmony_ci} 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_cistatic int supp_check_recv_params(size_t num_params, struct tee_param *params, 18662306a36Sopenharmony_ci size_t *num_meta) 18762306a36Sopenharmony_ci{ 18862306a36Sopenharmony_ci size_t n; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci if (!num_params) 19162306a36Sopenharmony_ci return -EINVAL; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci /* 19462306a36Sopenharmony_ci * If there's memrefs we need to decrease those as they where 19562306a36Sopenharmony_ci * increased earlier and we'll even refuse to accept any below. 19662306a36Sopenharmony_ci */ 19762306a36Sopenharmony_ci for (n = 0; n < num_params; n++) 19862306a36Sopenharmony_ci if (tee_param_is_memref(params + n) && params[n].u.memref.shm) 19962306a36Sopenharmony_ci tee_shm_put(params[n].u.memref.shm); 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci /* 20262306a36Sopenharmony_ci * We only expect parameters as TEE_IOCTL_PARAM_ATTR_TYPE_NONE with 20362306a36Sopenharmony_ci * or without the TEE_IOCTL_PARAM_ATTR_META bit set. 20462306a36Sopenharmony_ci */ 20562306a36Sopenharmony_ci for (n = 0; n < num_params; n++) 20662306a36Sopenharmony_ci if (params[n].attr && 20762306a36Sopenharmony_ci params[n].attr != TEE_IOCTL_PARAM_ATTR_META) 20862306a36Sopenharmony_ci return -EINVAL; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci /* At most we'll need one meta parameter so no need to check for more */ 21162306a36Sopenharmony_ci if (params->attr == TEE_IOCTL_PARAM_ATTR_META) 21262306a36Sopenharmony_ci *num_meta = 1; 21362306a36Sopenharmony_ci else 21462306a36Sopenharmony_ci *num_meta = 0; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci return 0; 21762306a36Sopenharmony_ci} 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci/** 22062306a36Sopenharmony_ci * optee_supp_recv() - receive request for supplicant 22162306a36Sopenharmony_ci * @ctx: context receiving the request 22262306a36Sopenharmony_ci * @func: requested function in supplicant 22362306a36Sopenharmony_ci * @num_params: number of elements allocated in @param, updated with number 22462306a36Sopenharmony_ci * used elements 22562306a36Sopenharmony_ci * @param: space for parameters for @func 22662306a36Sopenharmony_ci * 22762306a36Sopenharmony_ci * Returns 0 on success or <0 on failure 22862306a36Sopenharmony_ci */ 22962306a36Sopenharmony_ciint optee_supp_recv(struct tee_context *ctx, u32 *func, u32 *num_params, 23062306a36Sopenharmony_ci struct tee_param *param) 23162306a36Sopenharmony_ci{ 23262306a36Sopenharmony_ci struct tee_device *teedev = ctx->teedev; 23362306a36Sopenharmony_ci struct optee *optee = tee_get_drvdata(teedev); 23462306a36Sopenharmony_ci struct optee_supp *supp = &optee->supp; 23562306a36Sopenharmony_ci struct optee_supp_req *req = NULL; 23662306a36Sopenharmony_ci int id; 23762306a36Sopenharmony_ci size_t num_meta; 23862306a36Sopenharmony_ci int rc; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci rc = supp_check_recv_params(*num_params, param, &num_meta); 24162306a36Sopenharmony_ci if (rc) 24262306a36Sopenharmony_ci return rc; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci while (true) { 24562306a36Sopenharmony_ci mutex_lock(&supp->mutex); 24662306a36Sopenharmony_ci req = supp_pop_entry(supp, *num_params - num_meta, &id); 24762306a36Sopenharmony_ci mutex_unlock(&supp->mutex); 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci if (req) { 25062306a36Sopenharmony_ci if (IS_ERR(req)) 25162306a36Sopenharmony_ci return PTR_ERR(req); 25262306a36Sopenharmony_ci break; 25362306a36Sopenharmony_ci } 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci /* 25662306a36Sopenharmony_ci * If we didn't get a request we'll block in 25762306a36Sopenharmony_ci * wait_for_completion() to avoid needless spinning. 25862306a36Sopenharmony_ci * 25962306a36Sopenharmony_ci * This is where supplicant will be hanging most of 26062306a36Sopenharmony_ci * the time, let's make this interruptable so we 26162306a36Sopenharmony_ci * can easily restart supplicant if needed. 26262306a36Sopenharmony_ci */ 26362306a36Sopenharmony_ci if (wait_for_completion_interruptible(&supp->reqs_c)) 26462306a36Sopenharmony_ci return -ERESTARTSYS; 26562306a36Sopenharmony_ci } 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci if (num_meta) { 26862306a36Sopenharmony_ci /* 26962306a36Sopenharmony_ci * tee-supplicant support meta parameters -> requsts can be 27062306a36Sopenharmony_ci * processed asynchronously. 27162306a36Sopenharmony_ci */ 27262306a36Sopenharmony_ci param->attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT | 27362306a36Sopenharmony_ci TEE_IOCTL_PARAM_ATTR_META; 27462306a36Sopenharmony_ci param->u.value.a = id; 27562306a36Sopenharmony_ci param->u.value.b = 0; 27662306a36Sopenharmony_ci param->u.value.c = 0; 27762306a36Sopenharmony_ci } else { 27862306a36Sopenharmony_ci mutex_lock(&supp->mutex); 27962306a36Sopenharmony_ci supp->req_id = id; 28062306a36Sopenharmony_ci mutex_unlock(&supp->mutex); 28162306a36Sopenharmony_ci } 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci *func = req->func; 28462306a36Sopenharmony_ci *num_params = req->num_params + num_meta; 28562306a36Sopenharmony_ci memcpy(param + num_meta, req->param, 28662306a36Sopenharmony_ci sizeof(struct tee_param) * req->num_params); 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci return 0; 28962306a36Sopenharmony_ci} 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_cistatic struct optee_supp_req *supp_pop_req(struct optee_supp *supp, 29262306a36Sopenharmony_ci size_t num_params, 29362306a36Sopenharmony_ci struct tee_param *param, 29462306a36Sopenharmony_ci size_t *num_meta) 29562306a36Sopenharmony_ci{ 29662306a36Sopenharmony_ci struct optee_supp_req *req; 29762306a36Sopenharmony_ci int id; 29862306a36Sopenharmony_ci size_t nm; 29962306a36Sopenharmony_ci const u32 attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT | 30062306a36Sopenharmony_ci TEE_IOCTL_PARAM_ATTR_META; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci if (!num_params) 30362306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci if (supp->req_id == -1) { 30662306a36Sopenharmony_ci if (param->attr != attr) 30762306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 30862306a36Sopenharmony_ci id = param->u.value.a; 30962306a36Sopenharmony_ci nm = 1; 31062306a36Sopenharmony_ci } else { 31162306a36Sopenharmony_ci id = supp->req_id; 31262306a36Sopenharmony_ci nm = 0; 31362306a36Sopenharmony_ci } 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci req = idr_find(&supp->idr, id); 31662306a36Sopenharmony_ci if (!req) 31762306a36Sopenharmony_ci return ERR_PTR(-ENOENT); 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci if ((num_params - nm) != req->num_params) 32062306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci idr_remove(&supp->idr, id); 32362306a36Sopenharmony_ci supp->req_id = -1; 32462306a36Sopenharmony_ci *num_meta = nm; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci return req; 32762306a36Sopenharmony_ci} 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci/** 33062306a36Sopenharmony_ci * optee_supp_send() - send result of request from supplicant 33162306a36Sopenharmony_ci * @ctx: context sending result 33262306a36Sopenharmony_ci * @ret: return value of request 33362306a36Sopenharmony_ci * @num_params: number of parameters returned 33462306a36Sopenharmony_ci * @param: returned parameters 33562306a36Sopenharmony_ci * 33662306a36Sopenharmony_ci * Returns 0 on success or <0 on failure. 33762306a36Sopenharmony_ci */ 33862306a36Sopenharmony_ciint optee_supp_send(struct tee_context *ctx, u32 ret, u32 num_params, 33962306a36Sopenharmony_ci struct tee_param *param) 34062306a36Sopenharmony_ci{ 34162306a36Sopenharmony_ci struct tee_device *teedev = ctx->teedev; 34262306a36Sopenharmony_ci struct optee *optee = tee_get_drvdata(teedev); 34362306a36Sopenharmony_ci struct optee_supp *supp = &optee->supp; 34462306a36Sopenharmony_ci struct optee_supp_req *req; 34562306a36Sopenharmony_ci size_t n; 34662306a36Sopenharmony_ci size_t num_meta; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci mutex_lock(&supp->mutex); 34962306a36Sopenharmony_ci req = supp_pop_req(supp, num_params, param, &num_meta); 35062306a36Sopenharmony_ci mutex_unlock(&supp->mutex); 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci if (IS_ERR(req)) { 35362306a36Sopenharmony_ci /* Something is wrong, let supplicant restart. */ 35462306a36Sopenharmony_ci return PTR_ERR(req); 35562306a36Sopenharmony_ci } 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci /* Update out and in/out parameters */ 35862306a36Sopenharmony_ci for (n = 0; n < req->num_params; n++) { 35962306a36Sopenharmony_ci struct tee_param *p = req->param + n; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci switch (p->attr & TEE_IOCTL_PARAM_ATTR_TYPE_MASK) { 36262306a36Sopenharmony_ci case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT: 36362306a36Sopenharmony_ci case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT: 36462306a36Sopenharmony_ci p->u.value.a = param[n + num_meta].u.value.a; 36562306a36Sopenharmony_ci p->u.value.b = param[n + num_meta].u.value.b; 36662306a36Sopenharmony_ci p->u.value.c = param[n + num_meta].u.value.c; 36762306a36Sopenharmony_ci break; 36862306a36Sopenharmony_ci case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT: 36962306a36Sopenharmony_ci case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT: 37062306a36Sopenharmony_ci p->u.memref.size = param[n + num_meta].u.memref.size; 37162306a36Sopenharmony_ci break; 37262306a36Sopenharmony_ci default: 37362306a36Sopenharmony_ci break; 37462306a36Sopenharmony_ci } 37562306a36Sopenharmony_ci } 37662306a36Sopenharmony_ci req->ret = ret; 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci /* Let the requesting thread continue */ 37962306a36Sopenharmony_ci complete(&req->c); 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci return 0; 38262306a36Sopenharmony_ci} 383