| 1 | |
| 2 | |
| 3 | * Copyright (c) 2015-2017, 2019-2021 Linaro Limited
|
| 4 |
|
| 5 | #include <linux/anon_inodes.h>
|
| 6 | #include <linux/device.h>
|
| 7 | #include <linux/dma-buf.h>
|
| 8 | #include <linux/dma-mapping.h>
|
| 9 | #include <linux/highmem.h>
|
| 10 | #include <linux/idr.h>
|
| 11 | #include <linux/io.h>
|
| 12 | #include <linux/mm.h>
|
| 13 | #include <linux/sched.h>
|
| 14 | #include <linux/slab.h>
|
| 15 | #include <linux/tee_core.h>
|
| 16 | #include <linux/uaccess.h>
|
| 17 | #include <linux/uio.h>
|
| 18 | #include "tee_private.h"
|
| 19 |
|
| 20 | struct tee_shm_dma_mem {
|
| 21 | struct tee_shm shm;
|
| 22 | dma_addr_t dma_addr;
|
| 23 | struct page *page;
|
| 24 | };
|
| 25 |
|
| 26 | static void release_registered_pages(struct tee_shm *shm)
|
| 27 | {
|
| 28 | if (shm->pages) {
|
| 29 | if (shm->flags & TEE_SHM_USER_MAPPED)
|
| 30 | unpin_user_pages(shm->pages, shm->num_pages);
|
| 31 |
|
| 32 | kfree(shm->pages);
|
| 33 | }
|
| 34 | }
|
| 35 |
|
| 36 | static void tee_shm_release(struct tee_device *teedev, struct tee_shm *shm)
|
| 37 | {
|
| 38 | void *p = shm;
|
| 39 |
|
| 40 | if (shm->flags & TEE_SHM_DMA_MEM) {
|
| 41 | #if IS_ENABLED(CONFIG_TEE_DMABUF_HEAPS)
|
| 42 | struct tee_shm_dma_mem *dma_mem;
|
| 43 |
|
| 44 | dma_mem = container_of(shm, struct tee_shm_dma_mem, shm);
|
| 45 | p = dma_mem;
|
| 46 | dma_free_pages(&teedev->dev, shm->size, dma_mem->page,
|
| 47 | dma_mem->dma_addr, DMA_BIDIRECTIONAL);
|
| 48 | #endif
|
| 49 | } else if (shm->flags & TEE_SHM_DMA_BUF) {
|
| 50 | struct tee_shm_dmabuf_ref *ref;
|
| 51 |
|
| 52 | ref = container_of(shm, struct tee_shm_dmabuf_ref, shm);
|
| 53 | p = ref;
|
| 54 | dma_buf_put(ref->dmabuf);
|
| 55 | } else if (shm->flags & TEE_SHM_POOL) {
|
| 56 | teedev->pool->ops->free(teedev->pool, shm);
|
| 57 | } else if (shm->flags & TEE_SHM_DYNAMIC) {
|
| 58 | int rc = teedev->desc->ops->shm_unregister(shm->ctx, shm);
|
| 59 |
|
| 60 | if (rc)
|
| 61 | dev_err(teedev->dev.parent,
|
| 62 | "unregister shm %p failed: %d", shm, rc);
|
| 63 |
|
| 64 | release_registered_pages(shm);
|
| 65 | }
|
| 66 |
|
| 67 | teedev_ctx_put(shm->ctx);
|
| 68 |
|
| 69 | kfree(p);
|
| 70 |
|
| 71 | tee_device_put(teedev);
|
| 72 | }
|
| 73 |
|
| 74 | static struct tee_shm *shm_alloc_helper(struct tee_context *ctx, size_t size,
|
| 75 | size_t align, u32 flags, int id)
|
| 76 | {
|
| 77 | struct tee_device *teedev = ctx->teedev;
|
| 78 | struct tee_shm *shm;
|
| 79 | void *ret;
|
| 80 | int rc;
|
| 81 |
|
| 82 | if (!tee_device_get(teedev))
|
| 83 | return ERR_PTR(-EINVAL);
|
| 84 |
|
| 85 | if (!teedev->pool) {
|
| 86 |
|
| 87 | ret = ERR_PTR(-EINVAL);
|
| 88 | goto err_dev_put;
|
| 89 | }
|
| 90 |
|
| 91 | shm = kzalloc_obj(*shm);
|
| 92 | if (!shm) {
|
| 93 | ret = ERR_PTR(-ENOMEM);
|
| 94 | goto err_dev_put;
|
| 95 | }
|
| 96 |
|
| 97 | refcount_set(&shm->refcount, 1);
|
| 98 | shm->flags = flags;
|
| 99 | shm->id = id;
|
| 100 |
|
| 101 | |
| 102 | * We're assigning this as it is needed if the shm is to be
|
| 103 | * registered. If this function returns OK then the caller expected
|
| 104 | * to call teedev_ctx_get() or clear shm->ctx in case it's not
|
| 105 | * needed any longer.
|
| 106 |
|
| 107 | shm->ctx = ctx;
|
| 108 |
|
| 109 | rc = teedev->pool->ops->alloc(teedev->pool, shm, size, align);
|
| 110 | if (rc) {
|
| 111 | ret = ERR_PTR(rc);
|
| 112 | goto err_kfree;
|
| 113 | }
|
| 114 |
|
| 115 | teedev_ctx_get(ctx);
|
| 116 | return shm;
|
| 117 | err_kfree:
|
| 118 | kfree(shm);
|
| 119 | err_dev_put:
|
| 120 | tee_device_put(teedev);
|
| 121 | return ret;
|
| 122 | }
|
| 123 |
|
| 124 | |
| 125 | * tee_shm_alloc_user_buf() - Allocate shared memory for user space
|
| 126 | * @ctx: Context that allocates the shared memory
|
| 127 | * @size: Requested size of shared memory
|
| 128 | *
|
| 129 | * Memory allocated as user space shared memory is automatically freed when
|
| 130 | * the TEE file pointer is closed. The primary usage of this function is
|
| 131 | * when the TEE driver doesn't support registering ordinary user space
|
| 132 | * memory.
|
| 133 | *
|
| 134 | * @returns a pointer to 'struct tee_shm'
|
| 135 |
|
| 136 | struct tee_shm *tee_shm_alloc_user_buf(struct tee_context *ctx, size_t size)
|
| 137 | {
|
| 138 | u32 flags = TEE_SHM_DYNAMIC | TEE_SHM_POOL;
|
| 139 | struct tee_device *teedev = ctx->teedev;
|
| 140 | struct tee_shm *shm;
|
| 141 | void *ret;
|
| 142 | int id;
|
| 143 |
|
| 144 | mutex_lock(&teedev->mutex);
|
| 145 | id = idr_alloc(&teedev->idr, NULL, 1, 0, GFP_KERNEL);
|
| 146 | mutex_unlock(&teedev->mutex);
|
| 147 | if (id < 0)
|
| 148 | return ERR_PTR(id);
|
| 149 |
|
| 150 | shm = shm_alloc_helper(ctx, size, PAGE_SIZE, flags, id);
|
| 151 | if (IS_ERR(shm)) {
|
| 152 | mutex_lock(&teedev->mutex);
|
| 153 | idr_remove(&teedev->idr, id);
|
| 154 | mutex_unlock(&teedev->mutex);
|
| 155 | return shm;
|
| 156 | }
|
| 157 |
|
| 158 | mutex_lock(&teedev->mutex);
|
| 159 | ret = idr_replace(&teedev->idr, shm, id);
|
| 160 | mutex_unlock(&teedev->mutex);
|
| 161 | if (IS_ERR(ret)) {
|
| 162 | tee_shm_free(shm);
|
| 163 | return ret;
|
| 164 | }
|
| 165 |
|
| 166 | return shm;
|
| 167 | }
|
| 168 |
|
| 169 | |
| 170 | * tee_shm_alloc_kernel_buf() - Allocate shared memory for kernel buffer
|
| 171 | * @ctx: Context that allocates the shared memory
|
| 172 | * @size: Requested size of shared memory
|
| 173 | *
|
| 174 | * The returned memory registered in secure world and is suitable to be
|
| 175 | * passed as a memory buffer in parameter argument to
|
| 176 | * tee_client_invoke_func(). The memory allocated is later freed with a
|
| 177 | * call to tee_shm_free().
|
| 178 | *
|
| 179 | * @returns a pointer to 'struct tee_shm' on success, and ERR_PTR on failure
|
| 180 |
|
| 181 | struct tee_shm *tee_shm_alloc_kernel_buf(struct tee_context *ctx, size_t size)
|
| 182 | {
|
| 183 | u32 flags = TEE_SHM_DYNAMIC | TEE_SHM_POOL;
|
| 184 |
|
| 185 | return shm_alloc_helper(ctx, size, PAGE_SIZE, flags, -1);
|
| 186 | }
|
| 187 | EXPORT_SYMBOL_GPL(tee_shm_alloc_kernel_buf);
|
| 188 |
|
| 189 | struct tee_shm *tee_shm_register_fd(struct tee_context *ctx, int fd)
|
| 190 | {
|
| 191 | struct tee_shm_dmabuf_ref *ref;
|
| 192 | int rc;
|
| 193 |
|
| 194 | if (!tee_device_get(ctx->teedev))
|
| 195 | return ERR_PTR(-EINVAL);
|
| 196 |
|
| 197 | teedev_ctx_get(ctx);
|
| 198 |
|
| 199 | ref = kzalloc_obj(*ref);
|
| 200 | if (!ref) {
|
| 201 | rc = -ENOMEM;
|
| 202 | goto err_put_tee;
|
| 203 | }
|
| 204 |
|
| 205 | refcount_set(&ref->shm.refcount, 1);
|
| 206 | ref->shm.ctx = ctx;
|
| 207 | ref->shm.id = -1;
|
| 208 | ref->shm.flags = TEE_SHM_DMA_BUF;
|
| 209 |
|
| 210 | ref->dmabuf = dma_buf_get(fd);
|
| 211 | if (IS_ERR(ref->dmabuf)) {
|
| 212 | rc = PTR_ERR(ref->dmabuf);
|
| 213 | goto err_kfree_ref;
|
| 214 | }
|
| 215 |
|
| 216 | rc = tee_heap_update_from_dma_buf(ctx->teedev, ref->dmabuf,
|
| 217 | &ref->offset, &ref->shm,
|
| 218 | &ref->parent_shm);
|
| 219 | if (rc)
|
| 220 | goto err_put_dmabuf;
|
| 221 |
|
| 222 | mutex_lock(&ref->shm.ctx->teedev->mutex);
|
| 223 | ref->shm.id = idr_alloc(&ref->shm.ctx->teedev->idr, &ref->shm,
|
| 224 | 1, 0, GFP_KERNEL);
|
| 225 | mutex_unlock(&ref->shm.ctx->teedev->mutex);
|
| 226 | if (ref->shm.id < 0) {
|
| 227 | rc = ref->shm.id;
|
| 228 | goto err_put_dmabuf;
|
| 229 | }
|
| 230 |
|
| 231 | return &ref->shm;
|
| 232 |
|
| 233 | err_put_dmabuf:
|
| 234 | dma_buf_put(ref->dmabuf);
|
| 235 | err_kfree_ref:
|
| 236 | kfree(ref);
|
| 237 | err_put_tee:
|
| 238 | teedev_ctx_put(ctx);
|
| 239 | tee_device_put(ctx->teedev);
|
| 240 |
|
| 241 | return ERR_PTR(rc);
|
| 242 | }
|
| 243 | EXPORT_SYMBOL_GPL(tee_shm_register_fd);
|
| 244 |
|
| 245 | |
| 246 | * tee_shm_alloc_priv_buf() - Allocate shared memory for a privately shared
|
| 247 | * kernel buffer
|
| 248 | * @ctx: Context that allocates the shared memory
|
| 249 | * @size: Requested size of shared memory
|
| 250 | *
|
| 251 | * This function returns similar shared memory as
|
| 252 | * tee_shm_alloc_kernel_buf(), but with the difference that the memory
|
| 253 | * might not be registered in secure world in case the driver supports
|
| 254 | * passing memory not registered in advance.
|
| 255 | *
|
| 256 | * This function should normally only be used internally in the TEE
|
| 257 | * drivers.
|
| 258 | *
|
| 259 | * @returns a pointer to 'struct tee_shm'
|
| 260 |
|
| 261 | struct tee_shm *tee_shm_alloc_priv_buf(struct tee_context *ctx, size_t size)
|
| 262 | {
|
| 263 | u32 flags = TEE_SHM_PRIV | TEE_SHM_POOL;
|
| 264 |
|
| 265 | return shm_alloc_helper(ctx, size, sizeof(long) * 2, flags, -1);
|
| 266 | }
|
| 267 | EXPORT_SYMBOL_GPL(tee_shm_alloc_priv_buf);
|
| 268 |
|
| 269 | #if IS_ENABLED(CONFIG_TEE_DMABUF_HEAPS)
|
| 270 | |
| 271 | * tee_shm_alloc_dma_mem() - Allocate DMA memory as shared memory object
|
| 272 | * @ctx: Context that allocates the shared memory
|
| 273 | * @page_count: Number of pages
|
| 274 | *
|
| 275 | * The allocated memory is expected to be lent (made inaccessible to the
|
| 276 | * kernel) to the TEE while it's used and returned (accessible to the
|
| 277 | * kernel again) before it's freed.
|
| 278 | *
|
| 279 | * This function should normally only be used internally in the TEE
|
| 280 | * drivers.
|
| 281 | *
|
| 282 | * @returns a pointer to 'struct tee_shm'
|
| 283 |
|
| 284 | struct tee_shm *tee_shm_alloc_dma_mem(struct tee_context *ctx,
|
| 285 | size_t page_count)
|
| 286 | {
|
| 287 | struct tee_device *teedev = ctx->teedev;
|
| 288 | struct tee_shm_dma_mem *dma_mem;
|
| 289 | dma_addr_t dma_addr;
|
| 290 | struct page *page;
|
| 291 |
|
| 292 | if (!tee_device_get(teedev))
|
| 293 | return ERR_PTR(-EINVAL);
|
| 294 |
|
| 295 | page = dma_alloc_pages(&teedev->dev, page_count * PAGE_SIZE,
|
| 296 | &dma_addr, DMA_BIDIRECTIONAL, GFP_KERNEL);
|
| 297 | if (!page)
|
| 298 | goto err_put_teedev;
|
| 299 |
|
| 300 | dma_mem = kzalloc_obj(*dma_mem);
|
| 301 | if (!dma_mem)
|
| 302 | goto err_free_pages;
|
| 303 |
|
| 304 | refcount_set(&dma_mem->shm.refcount, 1);
|
| 305 | dma_mem->shm.ctx = ctx;
|
| 306 | dma_mem->shm.paddr = page_to_phys(page);
|
| 307 | dma_mem->dma_addr = dma_addr;
|
| 308 | dma_mem->page = page;
|
| 309 | dma_mem->shm.size = page_count * PAGE_SIZE;
|
| 310 | dma_mem->shm.flags = TEE_SHM_DMA_MEM;
|
| 311 |
|
| 312 | teedev_ctx_get(ctx);
|
| 313 |
|
| 314 | return &dma_mem->shm;
|
| 315 |
|
| 316 | err_free_pages:
|
| 317 | dma_free_pages(&teedev->dev, page_count * PAGE_SIZE, page, dma_addr,
|
| 318 | DMA_BIDIRECTIONAL);
|
| 319 | err_put_teedev:
|
| 320 | tee_device_put(teedev);
|
| 321 |
|
| 322 | return ERR_PTR(-ENOMEM);
|
| 323 | }
|
| 324 | EXPORT_SYMBOL_GPL(tee_shm_alloc_dma_mem);
|
| 325 | #else
|
| 326 | struct tee_shm *tee_shm_alloc_dma_mem(struct tee_context *ctx,
|
| 327 | size_t page_count)
|
| 328 | {
|
| 329 | return ERR_PTR(-EINVAL);
|
| 330 | }
|
| 331 | EXPORT_SYMBOL_GPL(tee_shm_alloc_dma_mem);
|
| 332 | #endif
|
| 333 |
|
| 334 | int tee_dyn_shm_alloc_helper(struct tee_shm *shm, size_t size, size_t align,
|
| 335 | int (*shm_register)(struct tee_context *ctx,
|
| 336 | struct tee_shm *shm,
|
| 337 | struct page **pages,
|
| 338 | size_t num_pages,
|
| 339 | unsigned long start))
|
| 340 | {
|
| 341 | size_t nr_pages = roundup(size, PAGE_SIZE) / PAGE_SIZE;
|
| 342 | struct page **pages;
|
| 343 | unsigned int i;
|
| 344 | int rc = 0;
|
| 345 |
|
| 346 |
|
| 347 | if (!nr_pages)
|
| 348 | return -EINVAL;
|
| 349 |
|
| 350 | |
| 351 | * Ignore alignment since this is already going to be page aligned
|
| 352 | * and there's no need for any larger alignment.
|
| 353 |
|
| 354 | shm->kaddr = alloc_pages_exact(nr_pages * PAGE_SIZE,
|
| 355 | GFP_KERNEL | __GFP_ZERO);
|
| 356 | if (!shm->kaddr)
|
| 357 | return -ENOMEM;
|
| 358 |
|
| 359 | shm->paddr = virt_to_phys(shm->kaddr);
|
| 360 | shm->size = nr_pages * PAGE_SIZE;
|
| 361 |
|
| 362 | pages = kzalloc_objs(*pages, nr_pages);
|
| 363 | if (!pages) {
|
| 364 | rc = -ENOMEM;
|
| 365 | goto err_pages;
|
| 366 | }
|
| 367 |
|
| 368 | for (i = 0; i < nr_pages; i++)
|
| 369 | pages[i] = virt_to_page((u8 *)shm->kaddr + i * PAGE_SIZE);
|
| 370 |
|
| 371 | shm->pages = pages;
|
| 372 | shm->num_pages = nr_pages;
|
| 373 |
|
| 374 | if (shm_register) {
|
| 375 | rc = shm_register(shm->ctx, shm, pages, nr_pages,
|
| 376 | (unsigned long)shm->kaddr);
|
| 377 | if (rc)
|
| 378 | goto err_kfree;
|
| 379 | }
|
| 380 |
|
| 381 | return 0;
|
| 382 | err_kfree:
|
| 383 | kfree(pages);
|
| 384 | err_pages:
|
| 385 | free_pages_exact(shm->kaddr, shm->size);
|
| 386 | shm->kaddr = NULL;
|
| 387 | return rc;
|
| 388 | }
|
| 389 | EXPORT_SYMBOL_GPL(tee_dyn_shm_alloc_helper);
|
| 390 |
|
| 391 | void tee_dyn_shm_free_helper(struct tee_shm *shm,
|
| 392 | int (*shm_unregister)(struct tee_context *ctx,
|
| 393 | struct tee_shm *shm))
|
| 394 | {
|
| 395 | if (shm_unregister)
|
| 396 | shm_unregister(shm->ctx, shm);
|
| 397 | free_pages_exact(shm->kaddr, shm->size);
|
| 398 | shm->kaddr = NULL;
|
| 399 | kfree(shm->pages);
|
| 400 | shm->pages = NULL;
|
| 401 | }
|
| 402 | EXPORT_SYMBOL_GPL(tee_dyn_shm_free_helper);
|
| 403 |
|
| 404 | static struct tee_shm *
|
| 405 | register_shm_helper(struct tee_context *ctx, struct iov_iter *iter, u32 flags,
|
| 406 | int id)
|
| 407 | {
|
| 408 | struct tee_device *teedev = ctx->teedev;
|
| 409 | struct tee_shm *shm;
|
| 410 | unsigned long start, addr;
|
| 411 | size_t num_pages, off;
|
| 412 | ssize_t len;
|
| 413 | void *ret;
|
| 414 | int rc;
|
| 415 |
|
| 416 | if (!tee_device_get(teedev))
|
| 417 | return ERR_PTR(-EINVAL);
|
| 418 |
|
| 419 | if (!teedev->desc->ops->shm_register ||
|
| 420 | !teedev->desc->ops->shm_unregister) {
|
| 421 | ret = ERR_PTR(-ENOTSUPP);
|
| 422 | goto err_dev_put;
|
| 423 | }
|
| 424 |
|
| 425 | teedev_ctx_get(ctx);
|
| 426 |
|
| 427 | shm = kzalloc_obj(*shm);
|
| 428 | if (!shm) {
|
| 429 | ret = ERR_PTR(-ENOMEM);
|
| 430 | goto err_ctx_put;
|
| 431 | }
|
| 432 |
|
| 433 | refcount_set(&shm->refcount, 1);
|
| 434 | shm->flags = flags;
|
| 435 | shm->ctx = ctx;
|
| 436 | shm->id = id;
|
| 437 | addr = untagged_addr((unsigned long)iter_iov_addr(iter));
|
| 438 | start = rounddown(addr, PAGE_SIZE);
|
| 439 | num_pages = iov_iter_npages(iter, INT_MAX);
|
| 440 | if (!num_pages) {
|
| 441 | ret = ERR_PTR(-ENOMEM);
|
| 442 | goto err_free_shm;
|
| 443 | }
|
| 444 |
|
| 445 | shm->pages = kzalloc_objs(*shm->pages, num_pages);
|
| 446 | if (!shm->pages) {
|
| 447 | ret = ERR_PTR(-ENOMEM);
|
| 448 | goto err_free_shm;
|
| 449 | }
|
| 450 |
|
| 451 | len = iov_iter_extract_pages(iter, &shm->pages, LONG_MAX, num_pages, 0,
|
| 452 | &off);
|
| 453 | if (unlikely(len <= 0)) {
|
| 454 | ret = len ? ERR_PTR(len) : ERR_PTR(-ENOMEM);
|
| 455 | goto err_free_shm_pages;
|
| 456 | } else if (DIV_ROUND_UP(len + off, PAGE_SIZE) != num_pages) {
|
| 457 | |
| 458 | * If we only got a few pages, update to release the
|
| 459 | * correct amount below.
|
| 460 |
|
| 461 | shm->num_pages = len / PAGE_SIZE;
|
| 462 | ret = ERR_PTR(-ENOMEM);
|
| 463 | goto err_put_shm_pages;
|
| 464 | }
|
| 465 |
|
| 466 | shm->offset = off;
|
| 467 | shm->size = len;
|
| 468 | shm->num_pages = num_pages;
|
| 469 |
|
| 470 | rc = teedev->desc->ops->shm_register(ctx, shm, shm->pages,
|
| 471 | shm->num_pages, start);
|
| 472 | if (rc) {
|
| 473 | ret = ERR_PTR(rc);
|
| 474 | goto err_put_shm_pages;
|
| 475 | }
|
| 476 |
|
| 477 | return shm;
|
| 478 | err_put_shm_pages:
|
| 479 | if (!iov_iter_is_kvec(iter))
|
| 480 | unpin_user_pages(shm->pages, shm->num_pages);
|
| 481 | err_free_shm_pages:
|
| 482 | kfree(shm->pages);
|
| 483 | err_free_shm:
|
| 484 | kfree(shm);
|
| 485 | err_ctx_put:
|
| 486 | teedev_ctx_put(ctx);
|
| 487 | err_dev_put:
|
| 488 | tee_device_put(teedev);
|
| 489 | return ret;
|
| 490 | }
|
| 491 |
|
| 492 | |
| 493 | * tee_shm_register_user_buf() - Register a userspace shared memory buffer
|
| 494 | * @ctx: Context that registers the shared memory
|
| 495 | * @addr: The userspace address of the shared buffer
|
| 496 | * @length: Length of the shared buffer
|
| 497 | *
|
| 498 | * @returns a pointer to 'struct tee_shm'
|
| 499 |
|
| 500 | struct tee_shm *tee_shm_register_user_buf(struct tee_context *ctx,
|
| 501 | unsigned long addr, size_t length)
|
| 502 | {
|
| 503 | u32 flags = TEE_SHM_USER_MAPPED | TEE_SHM_DYNAMIC;
|
| 504 | struct tee_device *teedev = ctx->teedev;
|
| 505 | struct tee_shm *shm;
|
| 506 | struct iov_iter iter;
|
| 507 | void *ret;
|
| 508 | int id;
|
| 509 |
|
| 510 | if (!access_ok((void __user *)addr, length))
|
| 511 | return ERR_PTR(-EFAULT);
|
| 512 |
|
| 513 | mutex_lock(&teedev->mutex);
|
| 514 | id = idr_alloc(&teedev->idr, NULL, 1, 0, GFP_KERNEL);
|
| 515 | mutex_unlock(&teedev->mutex);
|
| 516 | if (id < 0)
|
| 517 | return ERR_PTR(id);
|
| 518 |
|
| 519 | iov_iter_ubuf(&iter, ITER_DEST, (void __user *)addr, length);
|
| 520 | shm = register_shm_helper(ctx, &iter, flags, id);
|
| 521 | if (IS_ERR(shm)) {
|
| 522 | mutex_lock(&teedev->mutex);
|
| 523 | idr_remove(&teedev->idr, id);
|
| 524 | mutex_unlock(&teedev->mutex);
|
| 525 | return shm;
|
| 526 | }
|
| 527 |
|
| 528 | mutex_lock(&teedev->mutex);
|
| 529 | ret = idr_replace(&teedev->idr, shm, id);
|
| 530 | mutex_unlock(&teedev->mutex);
|
| 531 | if (IS_ERR(ret)) {
|
| 532 | tee_shm_free(shm);
|
| 533 | return ret;
|
| 534 | }
|
| 535 |
|
| 536 | return shm;
|
| 537 | }
|
| 538 |
|
| 539 | |
| 540 | * tee_shm_register_kernel_buf() - Register kernel memory to be shared with
|
| 541 | * secure world
|
| 542 | * @ctx: Context that registers the shared memory
|
| 543 | * @addr: The buffer
|
| 544 | * @length: Length of the buffer
|
| 545 | *
|
| 546 | * @returns a pointer to 'struct tee_shm'
|
| 547 |
|
| 548 |
|
| 549 | struct tee_shm *tee_shm_register_kernel_buf(struct tee_context *ctx,
|
| 550 | void *addr, size_t length)
|
| 551 | {
|
| 552 | u32 flags = TEE_SHM_DYNAMIC;
|
| 553 | struct kvec kvec;
|
| 554 | struct iov_iter iter;
|
| 555 |
|
| 556 | kvec.iov_base = addr;
|
| 557 | kvec.iov_len = length;
|
| 558 | iov_iter_kvec(&iter, ITER_DEST, &kvec, 1, length);
|
| 559 |
|
| 560 | return register_shm_helper(ctx, &iter, flags, -1);
|
| 561 | }
|
| 562 | EXPORT_SYMBOL_GPL(tee_shm_register_kernel_buf);
|
| 563 |
|
| 564 | static int tee_shm_fop_release(struct inode *inode, struct file *filp)
|
| 565 | {
|
| 566 | tee_shm_put(filp->private_data);
|
| 567 | return 0;
|
| 568 | }
|
| 569 |
|
| 570 | static int tee_shm_fop_mmap(struct file *filp, struct vm_area_struct *vma)
|
| 571 | {
|
| 572 | struct tee_shm *shm = filp->private_data;
|
| 573 | size_t size = vma->vm_end - vma->vm_start;
|
| 574 |
|
| 575 |
|
| 576 | if (shm->flags & TEE_SHM_USER_MAPPED)
|
| 577 | return -EINVAL;
|
| 578 |
|
| 579 | if (shm->flags & TEE_SHM_DMA_BUF)
|
| 580 | return -EINVAL;
|
| 581 |
|
| 582 |
|
| 583 | if (vma->vm_pgoff + vma_pages(vma) > shm->size >> PAGE_SHIFT)
|
| 584 | return -EINVAL;
|
| 585 |
|
| 586 | return remap_pfn_range(vma, vma->vm_start, shm->paddr >> PAGE_SHIFT,
|
| 587 | size, vma->vm_page_prot);
|
| 588 | }
|
| 589 |
|
| 590 | static const struct file_operations tee_shm_fops = {
|
| 591 | .owner = THIS_MODULE,
|
| 592 | .release = tee_shm_fop_release,
|
| 593 | .mmap = tee_shm_fop_mmap,
|
| 594 | };
|
| 595 |
|
| 596 | |
| 597 | * tee_shm_get_fd() - Increase reference count and return file descriptor
|
| 598 | * @shm: Shared memory handle
|
| 599 | * @returns user space file descriptor to shared memory
|
| 600 |
|
| 601 | int tee_shm_get_fd(struct tee_shm *shm)
|
| 602 | {
|
| 603 | int fd;
|
| 604 |
|
| 605 | if (shm->id < 0)
|
| 606 | return -EINVAL;
|
| 607 |
|
| 608 |
|
| 609 | refcount_inc(&shm->refcount);
|
| 610 | fd = anon_inode_getfd("tee_shm", &tee_shm_fops, shm, O_RDWR);
|
| 611 | if (fd < 0)
|
| 612 | tee_shm_put(shm);
|
| 613 | return fd;
|
| 614 | }
|
| 615 |
|
| 616 | |
| 617 | * tee_shm_free() - Free shared memory
|
| 618 | * @shm: Handle to shared memory to free
|
| 619 |
|
| 620 | void tee_shm_free(struct tee_shm *shm)
|
| 621 | {
|
| 622 | tee_shm_put(shm);
|
| 623 | }
|
| 624 | EXPORT_SYMBOL_GPL(tee_shm_free);
|
| 625 |
|
| 626 | |
| 627 | * tee_shm_get_va() - Get virtual address of a shared memory plus an offset
|
| 628 | * @shm: Shared memory handle
|
| 629 | * @offs: Offset from start of this shared memory
|
| 630 | * @returns virtual address of the shared memory + offs if offs is within
|
| 631 | * the bounds of this shared memory, else an ERR_PTR
|
| 632 |
|
| 633 | void *tee_shm_get_va(struct tee_shm *shm, size_t offs)
|
| 634 | {
|
| 635 | if (!shm->kaddr)
|
| 636 | return ERR_PTR(-EINVAL);
|
| 637 | if (offs >= shm->size)
|
| 638 | return ERR_PTR(-EINVAL);
|
| 639 | return (char *)shm->kaddr + offs;
|
| 640 | }
|
| 641 | EXPORT_SYMBOL_GPL(tee_shm_get_va);
|
| 642 |
|
| 643 | |
| 644 | * tee_shm_get_pa() - Get physical address of a shared memory plus an offset
|
| 645 | * @shm: Shared memory handle
|
| 646 | * @offs: Offset from start of this shared memory
|
| 647 | * @pa: Physical address to return
|
| 648 | * @returns 0 if offs is within the bounds of this shared memory, else an
|
| 649 | * error code.
|
| 650 |
|
| 651 | int tee_shm_get_pa(struct tee_shm *shm, size_t offs, phys_addr_t *pa)
|
| 652 | {
|
| 653 | if (offs >= shm->size)
|
| 654 | return -EINVAL;
|
| 655 | if (pa)
|
| 656 | *pa = shm->paddr + offs;
|
| 657 | return 0;
|
| 658 | }
|
| 659 | EXPORT_SYMBOL_GPL(tee_shm_get_pa);
|
| 660 |
|
| 661 | |
| 662 | * tee_shm_get_from_id() - Find shared memory object and increase reference
|
| 663 | * count
|
| 664 | * @ctx: Context owning the shared memory
|
| 665 | * @id: Id of shared memory object
|
| 666 | * @returns a pointer to 'struct tee_shm' on success or an ERR_PTR on failure
|
| 667 |
|
| 668 | struct tee_shm *tee_shm_get_from_id(struct tee_context *ctx, int id)
|
| 669 | {
|
| 670 | struct tee_device *teedev;
|
| 671 | struct tee_shm *shm;
|
| 672 |
|
| 673 | if (!ctx)
|
| 674 | return ERR_PTR(-EINVAL);
|
| 675 |
|
| 676 | teedev = ctx->teedev;
|
| 677 | mutex_lock(&teedev->mutex);
|
| 678 | shm = idr_find(&teedev->idr, id);
|
| 679 | |
| 680 | * If the tee_shm was found in the IDR it must have a refcount
|
| 681 | * larger than 0 due to the guarantee in tee_shm_put() below. So
|
| 682 | * it's safe to use refcount_inc().
|
| 683 |
|
| 684 | if (!shm || shm->ctx != ctx)
|
| 685 | shm = ERR_PTR(-EINVAL);
|
| 686 | else
|
| 687 | refcount_inc(&shm->refcount);
|
| 688 | mutex_unlock(&teedev->mutex);
|
| 689 | return shm;
|
| 690 | }
|
| 691 | EXPORT_SYMBOL_GPL(tee_shm_get_from_id);
|
| 692 |
|
| 693 | |
| 694 | * tee_shm_put() - Decrease reference count on a shared memory handle
|
| 695 | * @shm: Shared memory handle
|
| 696 |
|
| 697 | void tee_shm_put(struct tee_shm *shm)
|
| 698 | {
|
| 699 | struct tee_device *teedev;
|
| 700 | bool do_release = false;
|
| 701 |
|
| 702 | if (!shm || !shm->ctx || !shm->ctx->teedev)
|
| 703 | return;
|
| 704 |
|
| 705 | teedev = shm->ctx->teedev;
|
| 706 | mutex_lock(&teedev->mutex);
|
| 707 | if (refcount_dec_and_test(&shm->refcount)) {
|
| 708 | |
| 709 | * refcount has reached 0, we must now remove it from the
|
| 710 | * IDR before releasing the mutex. This will guarantee that
|
| 711 | * the refcount_inc() in tee_shm_get_from_id() never starts
|
| 712 | * from 0.
|
| 713 |
|
| 714 | if (shm->id >= 0)
|
| 715 | idr_remove(&teedev->idr, shm->id);
|
| 716 | do_release = true;
|
| 717 | }
|
| 718 | mutex_unlock(&teedev->mutex);
|
| 719 |
|
| 720 | if (do_release)
|
| 721 | tee_shm_release(teedev, shm);
|
| 722 | }
|
| 723 | EXPORT_SYMBOL_GPL(tee_shm_put);
|