/* * Copyright (c) 2015, Linaro Limited * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and * may be copied, distributed, and modified under those terms. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * */ #include #include #include #include "optee_private.h" void optee_supp_init(struct optee_supp *supp) { memset(supp, 0, sizeof(*supp)); mutex_init(&supp->ctx_mutex); mutex_init(&supp->thrd_mutex); mutex_init(&supp->supp_mutex); init_completion(&supp->data_to_supp); init_completion(&supp->data_from_supp); } void optee_supp_uninit(struct optee_supp *supp) { mutex_destroy(&supp->ctx_mutex); mutex_destroy(&supp->thrd_mutex); mutex_destroy(&supp->supp_mutex); } /** * optee_supp_thrd_req() - request service from supplicant * @ctx: context doing the request * @func: function requested * @num_params: number of elements in @param array * @param: parameters for function * * Returns result of operation to be passed to secure world */ u32 optee_supp_thrd_req(struct tee_context *ctx, u32 func, size_t num_params, struct tee_param *param) { bool interruptable; struct optee *optee = tee_get_drvdata(ctx->teedev); struct optee_supp *supp = &optee->supp; u32 ret; /* * Other threads blocks here until we've copied our answer from * supplicant. */ while (mutex_lock_interruptible(&supp->thrd_mutex)) { /* See comment below on when the RPC can be interrupted. */ mutex_lock(&supp->ctx_mutex); interruptable = !supp->ctx; mutex_unlock(&supp->ctx_mutex); if (interruptable) return TEEC_ERROR_COMMUNICATION; } /* * We have exclusive access now since the supplicant at this * point is either doing a * wait_for_completion_interruptible(&supp->data_to_supp) or is in * userspace still about to do the ioctl() to enter * optee_supp_recv() below. */ supp->func = func; supp->num_params = num_params; supp->param = param; supp->req_posted = true; /* Let supplicant get the data */ complete(&supp->data_to_supp); /* * Wait for supplicant to process and return result, once we've * returned from wait_for_completion(data_from_supp) we have * exclusive access again. */ while (wait_for_completion_interruptible(&supp->data_from_supp)) { mutex_lock(&supp->ctx_mutex); interruptable = !supp->ctx; if (interruptable) { /* * There's no supplicant available and since the * supp->ctx_mutex currently is held none can * become available until the mutex released * again. * * Interrupting an RPC to supplicant is only * allowed as a way of slightly improving the user * experience in case the supplicant hasn't been * started yet. During normal operation the supplicant * will serve all requests in a timely manner and * interrupting then wouldn't make sense. */ supp->ret = TEEC_ERROR_COMMUNICATION; init_completion(&supp->data_to_supp); } mutex_unlock(&supp->ctx_mutex); if (interruptable) break; } ret = supp->ret; supp->param = NULL; supp->req_posted = false; /* We're done, let someone else talk to the supplicant now. */ mutex_unlock(&supp->thrd_mutex); return ret; } /** * optee_supp_recv() - receive request for supplicant * @ctx: context receiving the request * @func: requested function in supplicant * @num_params: number of elements allocated in @param, updated with number * used elements * @param: space for parameters for @func * * Returns 0 on success or <0 on failure */ int optee_supp_recv(struct tee_context *ctx, u32 *func, u32 *num_params, struct tee_param *param) { struct tee_device *teedev = ctx->teedev; struct optee *optee = tee_get_drvdata(teedev); struct optee_supp *supp = &optee->supp; int rc; /* * In case two threads in one supplicant is calling this function * simultaneously we need to protect the data with a mutex which * we'll release before returning. */ mutex_lock(&supp->supp_mutex); if (supp->supp_next_send) { /* * optee_supp_recv() has been called again without * a optee_supp_send() in between. Supplicant has * probably been restarted before it was able to * write back last result. Abort last request and * wait for a new. */ if (supp->req_posted) { supp->ret = TEEC_ERROR_COMMUNICATION; supp->supp_next_send = false; complete(&supp->data_from_supp); } } /* * This is where supplicant will be hanging most of the * time, let's make this interruptable so we can easily * restart supplicant if needed. */ if (wait_for_completion_interruptible(&supp->data_to_supp)) { rc = -ERESTARTSYS; goto out; } /* We have exlusive access to the data */ if (*num_params < supp->num_params) { /* * Not enough room for parameters, tell supplicant * it failed and abort last request. */ supp->ret = TEEC_ERROR_COMMUNICATION; rc = -EINVAL; complete(&supp->data_from_supp); goto out; } *func = supp->func; *num_params = supp->num_params; memcpy(param, supp->param, sizeof(struct tee_param) * supp->num_params); /* Allow optee_supp_send() below to do its work */ supp->supp_next_send = true; rc = 0; out: mutex_unlock(&supp->supp_mutex); return rc; } /** * optee_supp_send() - send result of request from supplicant * @ctx: context sending result * @ret: return value of request * @num_params: number of parameters returned * @param: returned parameters * * Returns 0 on success or <0 on failure. */ int optee_supp_send(struct tee_context *ctx, u32 ret, u32 num_params, struct tee_param *param) { struct tee_device *teedev = ctx->teedev; struct optee *optee = tee_get_drvdata(teedev); struct optee_supp *supp = &optee->supp; size_t n; int rc = 0; /* * We still have exclusive access to the data since that's how we * left it when returning from optee_supp_read(). */ /* See comment on mutex in optee_supp_read() above */ mutex_lock(&supp->supp_mutex); if (!supp->supp_next_send) { /* * Something strange is going on, supplicant shouldn't * enter optee_supp_send() in this state */ rc = -ENOENT; goto out; } if (num_params != supp->num_params) { /* * Something is wrong, let supplicant restart. Next call to * optee_supp_recv() will give an error to the requesting * thread and release it. */ rc = -EINVAL; goto out; } /* Update out and in/out parameters */ for (n = 0; n < num_params; n++) { struct tee_param *p = supp->param + n; switch (p->attr) { case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT: case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT: p->u.value.a = param[n].u.value.a; p->u.value.b = param[n].u.value.b; p->u.value.c = param[n].u.value.c; break; case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT: case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT: p->u.memref.size = param[n].u.memref.size; break; default: break; } } supp->ret = ret; /* Allow optee_supp_recv() above to do its work */ supp->supp_next_send = false; /* Let the requesting thread continue */ complete(&supp->data_from_supp); out: mutex_unlock(&supp->supp_mutex); return rc; }