root/drivers/tee/optee/supp.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. optee_supp_init
  2. optee_supp_uninit
  3. optee_supp_release
  4. optee_supp_thrd_req
  5. supp_pop_entry
  6. supp_check_recv_params
  7. optee_supp_recv
  8. supp_pop_req
  9. optee_supp_send

   1 // SPDX-License-Identifier: GPL-2.0-only
   2 /*
   3  * Copyright (c) 2015, Linaro Limited
   4  */
   5 #include <linux/device.h>
   6 #include <linux/slab.h>
   7 #include <linux/uaccess.h>
   8 #include "optee_private.h"
   9 
  10 struct optee_supp_req {
  11         struct list_head link;
  12 
  13         bool in_queue;
  14         u32 func;
  15         u32 ret;
  16         size_t num_params;
  17         struct tee_param *param;
  18 
  19         struct completion c;
  20 };
  21 
  22 void optee_supp_init(struct optee_supp *supp)
  23 {
  24         memset(supp, 0, sizeof(*supp));
  25         mutex_init(&supp->mutex);
  26         init_completion(&supp->reqs_c);
  27         idr_init(&supp->idr);
  28         INIT_LIST_HEAD(&supp->reqs);
  29         supp->req_id = -1;
  30 }
  31 
  32 void optee_supp_uninit(struct optee_supp *supp)
  33 {
  34         mutex_destroy(&supp->mutex);
  35         idr_destroy(&supp->idr);
  36 }
  37 
  38 void optee_supp_release(struct optee_supp *supp)
  39 {
  40         int id;
  41         struct optee_supp_req *req;
  42         struct optee_supp_req *req_tmp;
  43 
  44         mutex_lock(&supp->mutex);
  45 
  46         /* Abort all request retrieved by supplicant */
  47         idr_for_each_entry(&supp->idr, req, id) {
  48                 idr_remove(&supp->idr, id);
  49                 req->ret = TEEC_ERROR_COMMUNICATION;
  50                 complete(&req->c);
  51         }
  52 
  53         /* Abort all queued requests */
  54         list_for_each_entry_safe(req, req_tmp, &supp->reqs, link) {
  55                 list_del(&req->link);
  56                 req->in_queue = false;
  57                 req->ret = TEEC_ERROR_COMMUNICATION;
  58                 complete(&req->c);
  59         }
  60 
  61         supp->ctx = NULL;
  62         supp->req_id = -1;
  63 
  64         mutex_unlock(&supp->mutex);
  65 }
  66 
  67 /**
  68  * optee_supp_thrd_req() - request service from supplicant
  69  * @ctx:        context doing the request
  70  * @func:       function requested
  71  * @num_params: number of elements in @param array
  72  * @param:      parameters for function
  73  *
  74  * Returns result of operation to be passed to secure world
  75  */
  76 u32 optee_supp_thrd_req(struct tee_context *ctx, u32 func, size_t num_params,
  77                         struct tee_param *param)
  78 
  79 {
  80         struct optee *optee = tee_get_drvdata(ctx->teedev);
  81         struct optee_supp *supp = &optee->supp;
  82         struct optee_supp_req *req;
  83         bool interruptable;
  84         u32 ret;
  85 
  86         /*
  87          * Return in case there is no supplicant available and
  88          * non-blocking request.
  89          */
  90         if (!supp->ctx && ctx->supp_nowait)
  91                 return TEEC_ERROR_COMMUNICATION;
  92 
  93         req = kzalloc(sizeof(*req), GFP_KERNEL);
  94         if (!req)
  95                 return TEEC_ERROR_OUT_OF_MEMORY;
  96 
  97         init_completion(&req->c);
  98         req->func = func;
  99         req->num_params = num_params;
 100         req->param = param;
 101 
 102         /* Insert the request in the request list */
 103         mutex_lock(&supp->mutex);
 104         list_add_tail(&req->link, &supp->reqs);
 105         req->in_queue = true;
 106         mutex_unlock(&supp->mutex);
 107 
 108         /* Tell an eventual waiter there's a new request */
 109         complete(&supp->reqs_c);
 110 
 111         /*
 112          * Wait for supplicant to process and return result, once we've
 113          * returned from wait_for_completion(&req->c) successfully we have
 114          * exclusive access again.
 115          */
 116         while (wait_for_completion_interruptible(&req->c)) {
 117                 mutex_lock(&supp->mutex);
 118                 interruptable = !supp->ctx;
 119                 if (interruptable) {
 120                         /*
 121                          * There's no supplicant available and since the
 122                          * supp->mutex currently is held none can
 123                          * become available until the mutex released
 124                          * again.
 125                          *
 126                          * Interrupting an RPC to supplicant is only
 127                          * allowed as a way of slightly improving the user
 128                          * experience in case the supplicant hasn't been
 129                          * started yet. During normal operation the supplicant
 130                          * will serve all requests in a timely manner and
 131                          * interrupting then wouldn't make sense.
 132                          */
 133                         if (req->in_queue) {
 134                                 list_del(&req->link);
 135                                 req->in_queue = false;
 136                         }
 137                 }
 138                 mutex_unlock(&supp->mutex);
 139 
 140                 if (interruptable) {
 141                         req->ret = TEEC_ERROR_COMMUNICATION;
 142                         break;
 143                 }
 144         }
 145 
 146         ret = req->ret;
 147         kfree(req);
 148 
 149         return ret;
 150 }
 151 
 152 static struct optee_supp_req  *supp_pop_entry(struct optee_supp *supp,
 153                                               int num_params, int *id)
 154 {
 155         struct optee_supp_req *req;
 156 
 157         if (supp->req_id != -1) {
 158                 /*
 159                  * Supplicant should not mix synchronous and asnynchronous
 160                  * requests.
 161                  */
 162                 return ERR_PTR(-EINVAL);
 163         }
 164 
 165         if (list_empty(&supp->reqs))
 166                 return NULL;
 167 
 168         req = list_first_entry(&supp->reqs, struct optee_supp_req, link);
 169 
 170         if (num_params < req->num_params) {
 171                 /* Not enough room for parameters */
 172                 return ERR_PTR(-EINVAL);
 173         }
 174 
 175         *id = idr_alloc(&supp->idr, req, 1, 0, GFP_KERNEL);
 176         if (*id < 0)
 177                 return ERR_PTR(-ENOMEM);
 178 
 179         list_del(&req->link);
 180         req->in_queue = false;
 181 
 182         return req;
 183 }
 184 
 185 static int supp_check_recv_params(size_t num_params, struct tee_param *params,
 186                                   size_t *num_meta)
 187 {
 188         size_t n;
 189 
 190         if (!num_params)
 191                 return -EINVAL;
 192 
 193         /*
 194          * If there's memrefs we need to decrease those as they where
 195          * increased earlier and we'll even refuse to accept any below.
 196          */
 197         for (n = 0; n < num_params; n++)
 198                 if (tee_param_is_memref(params + n) && params[n].u.memref.shm)
 199                         tee_shm_put(params[n].u.memref.shm);
 200 
 201         /*
 202          * We only expect parameters as TEE_IOCTL_PARAM_ATTR_TYPE_NONE with
 203          * or without the TEE_IOCTL_PARAM_ATTR_META bit set.
 204          */
 205         for (n = 0; n < num_params; n++)
 206                 if (params[n].attr &&
 207                     params[n].attr != TEE_IOCTL_PARAM_ATTR_META)
 208                         return -EINVAL;
 209 
 210         /* At most we'll need one meta parameter so no need to check for more */
 211         if (params->attr == TEE_IOCTL_PARAM_ATTR_META)
 212                 *num_meta = 1;
 213         else
 214                 *num_meta = 0;
 215 
 216         return 0;
 217 }
 218 
 219 /**
 220  * optee_supp_recv() - receive request for supplicant
 221  * @ctx:        context receiving the request
 222  * @func:       requested function in supplicant
 223  * @num_params: number of elements allocated in @param, updated with number
 224  *              used elements
 225  * @param:      space for parameters for @func
 226  *
 227  * Returns 0 on success or <0 on failure
 228  */
 229 int optee_supp_recv(struct tee_context *ctx, u32 *func, u32 *num_params,
 230                     struct tee_param *param)
 231 {
 232         struct tee_device *teedev = ctx->teedev;
 233         struct optee *optee = tee_get_drvdata(teedev);
 234         struct optee_supp *supp = &optee->supp;
 235         struct optee_supp_req *req = NULL;
 236         int id;
 237         size_t num_meta;
 238         int rc;
 239 
 240         rc = supp_check_recv_params(*num_params, param, &num_meta);
 241         if (rc)
 242                 return rc;
 243 
 244         while (true) {
 245                 mutex_lock(&supp->mutex);
 246                 req = supp_pop_entry(supp, *num_params - num_meta, &id);
 247                 mutex_unlock(&supp->mutex);
 248 
 249                 if (req) {
 250                         if (IS_ERR(req))
 251                                 return PTR_ERR(req);
 252                         break;
 253                 }
 254 
 255                 /*
 256                  * If we didn't get a request we'll block in
 257                  * wait_for_completion() to avoid needless spinning.
 258                  *
 259                  * This is where supplicant will be hanging most of
 260                  * the time, let's make this interruptable so we
 261                  * can easily restart supplicant if needed.
 262                  */
 263                 if (wait_for_completion_interruptible(&supp->reqs_c))
 264                         return -ERESTARTSYS;
 265         }
 266 
 267         if (num_meta) {
 268                 /*
 269                  * tee-supplicant support meta parameters -> requsts can be
 270                  * processed asynchronously.
 271                  */
 272                 param->attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT |
 273                               TEE_IOCTL_PARAM_ATTR_META;
 274                 param->u.value.a = id;
 275                 param->u.value.b = 0;
 276                 param->u.value.c = 0;
 277         } else {
 278                 mutex_lock(&supp->mutex);
 279                 supp->req_id = id;
 280                 mutex_unlock(&supp->mutex);
 281         }
 282 
 283         *func = req->func;
 284         *num_params = req->num_params + num_meta;
 285         memcpy(param + num_meta, req->param,
 286                sizeof(struct tee_param) * req->num_params);
 287 
 288         return 0;
 289 }
 290 
 291 static struct optee_supp_req *supp_pop_req(struct optee_supp *supp,
 292                                            size_t num_params,
 293                                            struct tee_param *param,
 294                                            size_t *num_meta)
 295 {
 296         struct optee_supp_req *req;
 297         int id;
 298         size_t nm;
 299         const u32 attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT |
 300                          TEE_IOCTL_PARAM_ATTR_META;
 301 
 302         if (!num_params)
 303                 return ERR_PTR(-EINVAL);
 304 
 305         if (supp->req_id == -1) {
 306                 if (param->attr != attr)
 307                         return ERR_PTR(-EINVAL);
 308                 id = param->u.value.a;
 309                 nm = 1;
 310         } else {
 311                 id = supp->req_id;
 312                 nm = 0;
 313         }
 314 
 315         req = idr_find(&supp->idr, id);
 316         if (!req)
 317                 return ERR_PTR(-ENOENT);
 318 
 319         if ((num_params - nm) != req->num_params)
 320                 return ERR_PTR(-EINVAL);
 321 
 322         idr_remove(&supp->idr, id);
 323         supp->req_id = -1;
 324         *num_meta = nm;
 325 
 326         return req;
 327 }
 328 
 329 /**
 330  * optee_supp_send() - send result of request from supplicant
 331  * @ctx:        context sending result
 332  * @ret:        return value of request
 333  * @num_params: number of parameters returned
 334  * @param:      returned parameters
 335  *
 336  * Returns 0 on success or <0 on failure.
 337  */
 338 int optee_supp_send(struct tee_context *ctx, u32 ret, u32 num_params,
 339                     struct tee_param *param)
 340 {
 341         struct tee_device *teedev = ctx->teedev;
 342         struct optee *optee = tee_get_drvdata(teedev);
 343         struct optee_supp *supp = &optee->supp;
 344         struct optee_supp_req *req;
 345         size_t n;
 346         size_t num_meta;
 347 
 348         mutex_lock(&supp->mutex);
 349         req = supp_pop_req(supp, num_params, param, &num_meta);
 350         mutex_unlock(&supp->mutex);
 351 
 352         if (IS_ERR(req)) {
 353                 /* Something is wrong, let supplicant restart. */
 354                 return PTR_ERR(req);
 355         }
 356 
 357         /* Update out and in/out parameters */
 358         for (n = 0; n < req->num_params; n++) {
 359                 struct tee_param *p = req->param + n;
 360 
 361                 switch (p->attr & TEE_IOCTL_PARAM_ATTR_TYPE_MASK) {
 362                 case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT:
 363                 case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT:
 364                         p->u.value.a = param[n + num_meta].u.value.a;
 365                         p->u.value.b = param[n + num_meta].u.value.b;
 366                         p->u.value.c = param[n + num_meta].u.value.c;
 367                         break;
 368                 case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT:
 369                 case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT:
 370                         p->u.memref.size = param[n + num_meta].u.memref.size;
 371                         break;
 372                 default:
 373                         break;
 374                 }
 375         }
 376         req->ret = ret;
 377 
 378         /* Let the requesting thread continue */
 379         complete(&req->c);
 380 
 381         return 0;
 382 }

/* [<][>][^][v][top][bottom][index][help] */