.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
---|
1 | 2 | /* |
---|
2 | 3 | * Copyright (c) 2015-2017, 2019-2021 Linaro Limited |
---|
3 | | - * |
---|
4 | | - * This software is licensed under the terms of the GNU General Public |
---|
5 | | - * License version 2, as published by the Free Software Foundation, and |
---|
6 | | - * may be copied, distributed, and modified under those terms. |
---|
7 | | - * |
---|
8 | | - * This program is distributed in the hope that it will be useful, |
---|
9 | | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
10 | | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
---|
11 | | - * GNU General Public License for more details. |
---|
12 | | - * |
---|
13 | 4 | */ |
---|
14 | 5 | #include <linux/anon_inodes.h> |
---|
15 | 6 | #include <linux/device.h> |
---|
.. | .. |
---|
18 | 9 | #include <linux/sched.h> |
---|
19 | 10 | #include <linux/slab.h> |
---|
20 | 11 | #include <linux/tee_drv.h> |
---|
| 12 | +#include <linux/uaccess.h> |
---|
| 13 | +#include <linux/uio.h> |
---|
21 | 14 | #include "tee_private.h" |
---|
| 15 | + |
---|
| 16 | +static void release_registered_pages(struct tee_shm *shm) |
---|
| 17 | +{ |
---|
| 18 | + if (shm->pages) { |
---|
| 19 | + if (shm->flags & TEE_SHM_USER_MAPPED) { |
---|
| 20 | + unpin_user_pages(shm->pages, shm->num_pages); |
---|
| 21 | + } else { |
---|
| 22 | + size_t n; |
---|
| 23 | + |
---|
| 24 | + for (n = 0; n < shm->num_pages; n++) |
---|
| 25 | + put_page(shm->pages[n]); |
---|
| 26 | + } |
---|
| 27 | + |
---|
| 28 | + kfree(shm->pages); |
---|
| 29 | + } |
---|
| 30 | +} |
---|
22 | 31 | |
---|
23 | 32 | static void tee_shm_release(struct tee_device *teedev, struct tee_shm *shm) |
---|
24 | 33 | { |
---|
.. | .. |
---|
32 | 41 | |
---|
33 | 42 | poolm->ops->free(poolm, shm); |
---|
34 | 43 | } else if (shm->flags & TEE_SHM_REGISTER) { |
---|
35 | | - size_t n; |
---|
36 | 44 | int rc = teedev->desc->ops->shm_unregister(shm->ctx, shm); |
---|
37 | 45 | |
---|
38 | 46 | if (rc) |
---|
39 | 47 | dev_err(teedev->dev.parent, |
---|
40 | 48 | "unregister shm %p failed: %d", shm, rc); |
---|
41 | 49 | |
---|
42 | | - for (n = 0; n < shm->num_pages; n++) |
---|
43 | | - put_page(shm->pages[n]); |
---|
44 | | - |
---|
45 | | - kfree(shm->pages); |
---|
| 50 | + release_registered_pages(shm); |
---|
46 | 51 | } |
---|
47 | 52 | |
---|
48 | | - if (shm->ctx) |
---|
49 | | - teedev_ctx_put(shm->ctx); |
---|
| 53 | + teedev_ctx_put(shm->ctx); |
---|
50 | 54 | |
---|
51 | 55 | kfree(shm); |
---|
52 | 56 | |
---|
53 | 57 | tee_device_put(teedev); |
---|
54 | 58 | } |
---|
55 | 59 | |
---|
56 | | -static struct tee_shm *__tee_shm_alloc(struct tee_context *ctx, |
---|
57 | | - struct tee_device *teedev, |
---|
58 | | - size_t size, u32 flags) |
---|
| 60 | +struct tee_shm *tee_shm_alloc(struct tee_context *ctx, size_t size, u32 flags) |
---|
59 | 61 | { |
---|
| 62 | + struct tee_device *teedev = ctx->teedev; |
---|
60 | 63 | struct tee_shm_pool_mgr *poolm = NULL; |
---|
61 | 64 | struct tee_shm *shm; |
---|
62 | 65 | void *ret; |
---|
63 | 66 | int rc; |
---|
64 | | - |
---|
65 | | - if (ctx && ctx->teedev != teedev) { |
---|
66 | | - dev_err(teedev->dev.parent, "ctx and teedev mismatch\n"); |
---|
67 | | - return ERR_PTR(-EINVAL); |
---|
68 | | - } |
---|
69 | 67 | |
---|
70 | 68 | if (!(flags & TEE_SHM_MAPPED)) { |
---|
71 | 69 | dev_err(teedev->dev.parent, |
---|
.. | .. |
---|
73 | 71 | return ERR_PTR(-EINVAL); |
---|
74 | 72 | } |
---|
75 | 73 | |
---|
76 | | - if ((flags & ~(TEE_SHM_MAPPED | TEE_SHM_DMA_BUF))) { |
---|
| 74 | + if ((flags & ~(TEE_SHM_MAPPED | TEE_SHM_DMA_BUF | TEE_SHM_PRIV))) { |
---|
77 | 75 | dev_err(teedev->dev.parent, "invalid shm flags 0x%x", flags); |
---|
78 | 76 | return ERR_PTR(-EINVAL); |
---|
79 | 77 | } |
---|
.. | .. |
---|
95 | 93 | |
---|
96 | 94 | refcount_set(&shm->refcount, 1); |
---|
97 | 95 | shm->flags = flags | TEE_SHM_POOL; |
---|
98 | | - shm->teedev = teedev; |
---|
99 | 96 | shm->ctx = ctx; |
---|
100 | 97 | if (flags & TEE_SHM_DMA_BUF) |
---|
101 | 98 | poolm = teedev->pool->dma_buf_mgr; |
---|
.. | .. |
---|
108 | 105 | goto err_kfree; |
---|
109 | 106 | } |
---|
110 | 107 | |
---|
111 | | - mutex_lock(&teedev->mutex); |
---|
112 | | - shm->id = idr_alloc(&teedev->idr, shm, 1, 0, GFP_KERNEL); |
---|
113 | | - mutex_unlock(&teedev->mutex); |
---|
114 | | - if (shm->id < 0) { |
---|
115 | | - ret = ERR_PTR(shm->id); |
---|
116 | | - goto err_pool_free; |
---|
| 108 | + if (flags & TEE_SHM_DMA_BUF) { |
---|
| 109 | + mutex_lock(&teedev->mutex); |
---|
| 110 | + shm->id = idr_alloc(&teedev->idr, shm, 1, 0, GFP_KERNEL); |
---|
| 111 | + mutex_unlock(&teedev->mutex); |
---|
| 112 | + if (shm->id < 0) { |
---|
| 113 | + ret = ERR_PTR(shm->id); |
---|
| 114 | + goto err_pool_free; |
---|
| 115 | + } |
---|
117 | 116 | } |
---|
118 | 117 | |
---|
119 | | - if (ctx) { |
---|
120 | | - teedev_ctx_get(ctx); |
---|
121 | | - mutex_lock(&teedev->mutex); |
---|
122 | | - list_add_tail(&shm->link, &ctx->list_shm); |
---|
123 | | - mutex_unlock(&teedev->mutex); |
---|
124 | | - } |
---|
| 118 | + teedev_ctx_get(ctx); |
---|
125 | 119 | |
---|
126 | 120 | return shm; |
---|
127 | 121 | err_pool_free: |
---|
.. | .. |
---|
132 | 126 | tee_device_put(teedev); |
---|
133 | 127 | return ret; |
---|
134 | 128 | } |
---|
135 | | - |
---|
136 | | -/** |
---|
137 | | - * tee_shm_alloc() - Allocate shared memory |
---|
138 | | - * @ctx: Context that allocates the shared memory |
---|
139 | | - * @size: Requested size of shared memory |
---|
140 | | - * @flags: Flags setting properties for the requested shared memory. |
---|
141 | | - * |
---|
142 | | - * Memory allocated as global shared memory is automatically freed when the |
---|
143 | | - * TEE file pointer is closed. The @flags field uses the bits defined by |
---|
144 | | - * TEE_SHM_* in <linux/tee_drv.h>. TEE_SHM_MAPPED must currently always be |
---|
145 | | - * set. If TEE_SHM_DMA_BUF global shared memory will be allocated and |
---|
146 | | - * associated with a dma-buf handle, else driver private memory. |
---|
147 | | - */ |
---|
148 | | -struct tee_shm *tee_shm_alloc(struct tee_context *ctx, size_t size, u32 flags) |
---|
149 | | -{ |
---|
150 | | - return __tee_shm_alloc(ctx, ctx->teedev, size, flags); |
---|
151 | | -} |
---|
152 | 129 | EXPORT_SYMBOL_GPL(tee_shm_alloc); |
---|
153 | 130 | |
---|
154 | | -struct tee_shm *tee_shm_priv_alloc(struct tee_device *teedev, size_t size) |
---|
| 131 | +/** |
---|
| 132 | + * tee_shm_alloc_kernel_buf() - Allocate shared memory for kernel buffer |
---|
| 133 | + * @ctx: Context that allocates the shared memory |
---|
| 134 | + * @size: Requested size of shared memory |
---|
| 135 | + * |
---|
| 136 | + * The returned memory registered in secure world and is suitable to be |
---|
| 137 | + * passed as a memory buffer in parameter argument to |
---|
| 138 | + * tee_client_invoke_func(). The memory allocated is later freed with a |
---|
| 139 | + * call to tee_shm_free(). |
---|
| 140 | + * |
---|
| 141 | + * @returns a pointer to 'struct tee_shm' |
---|
| 142 | + */ |
---|
| 143 | +struct tee_shm *tee_shm_alloc_kernel_buf(struct tee_context *ctx, size_t size) |
---|
155 | 144 | { |
---|
156 | | - return __tee_shm_alloc(NULL, teedev, size, TEE_SHM_MAPPED); |
---|
| 145 | + return tee_shm_alloc(ctx, size, TEE_SHM_MAPPED); |
---|
157 | 146 | } |
---|
158 | | -EXPORT_SYMBOL_GPL(tee_shm_priv_alloc); |
---|
| 147 | +EXPORT_SYMBOL_GPL(tee_shm_alloc_kernel_buf); |
---|
159 | 148 | |
---|
160 | 149 | struct tee_shm *tee_shm_register(struct tee_context *ctx, unsigned long addr, |
---|
161 | 150 | size_t length, u32 flags) |
---|
162 | 151 | { |
---|
163 | 152 | struct tee_device *teedev = ctx->teedev; |
---|
164 | | - const u32 req_flags = TEE_SHM_DMA_BUF | TEE_SHM_USER_MAPPED; |
---|
| 153 | + const u32 req_user_flags = TEE_SHM_DMA_BUF | TEE_SHM_USER_MAPPED; |
---|
| 154 | + const u32 req_kernel_flags = TEE_SHM_DMA_BUF | TEE_SHM_KERNEL_MAPPED; |
---|
165 | 155 | struct tee_shm *shm; |
---|
166 | 156 | void *ret; |
---|
167 | 157 | int rc; |
---|
168 | 158 | int num_pages; |
---|
169 | 159 | unsigned long start; |
---|
170 | 160 | |
---|
171 | | - if (flags != req_flags) |
---|
| 161 | + if (flags != req_user_flags && flags != req_kernel_flags) |
---|
172 | 162 | return ERR_PTR(-ENOTSUPP); |
---|
173 | 163 | |
---|
174 | 164 | if (!tee_device_get(teedev)) |
---|
.. | .. |
---|
190 | 180 | |
---|
191 | 181 | refcount_set(&shm->refcount, 1); |
---|
192 | 182 | shm->flags = flags | TEE_SHM_REGISTER; |
---|
193 | | - shm->teedev = teedev; |
---|
194 | 183 | shm->ctx = ctx; |
---|
195 | 184 | shm->id = -1; |
---|
196 | 185 | addr = untagged_addr(addr); |
---|
.. | .. |
---|
204 | 193 | goto err; |
---|
205 | 194 | } |
---|
206 | 195 | |
---|
207 | | - rc = get_user_pages_fast(start, num_pages, 1, shm->pages); |
---|
| 196 | + if (flags & TEE_SHM_USER_MAPPED) { |
---|
| 197 | + rc = pin_user_pages_fast(start, num_pages, FOLL_WRITE, |
---|
| 198 | + shm->pages); |
---|
| 199 | + } else { |
---|
| 200 | + struct kvec *kiov; |
---|
| 201 | + int i; |
---|
| 202 | + |
---|
| 203 | + kiov = kcalloc(num_pages, sizeof(*kiov), GFP_KERNEL); |
---|
| 204 | + if (!kiov) { |
---|
| 205 | + ret = ERR_PTR(-ENOMEM); |
---|
| 206 | + goto err; |
---|
| 207 | + } |
---|
| 208 | + |
---|
| 209 | + for (i = 0; i < num_pages; i++) { |
---|
| 210 | + kiov[i].iov_base = (void *)(start + i * PAGE_SIZE); |
---|
| 211 | + kiov[i].iov_len = PAGE_SIZE; |
---|
| 212 | + } |
---|
| 213 | + |
---|
| 214 | + rc = get_kernel_pages(kiov, num_pages, 0, shm->pages); |
---|
| 215 | + kfree(kiov); |
---|
| 216 | + } |
---|
208 | 217 | if (rc > 0) |
---|
209 | 218 | shm->num_pages = rc; |
---|
210 | 219 | if (rc != num_pages) { |
---|
.. | .. |
---|
230 | 239 | goto err; |
---|
231 | 240 | } |
---|
232 | 241 | |
---|
233 | | - mutex_lock(&teedev->mutex); |
---|
234 | | - list_add_tail(&shm->link, &ctx->list_shm); |
---|
235 | | - mutex_unlock(&teedev->mutex); |
---|
236 | | - |
---|
237 | 242 | return shm; |
---|
238 | 243 | err: |
---|
239 | 244 | if (shm) { |
---|
240 | | - size_t n; |
---|
241 | | - |
---|
242 | 245 | if (shm->id >= 0) { |
---|
243 | 246 | mutex_lock(&teedev->mutex); |
---|
244 | 247 | idr_remove(&teedev->idr, shm->id); |
---|
245 | 248 | mutex_unlock(&teedev->mutex); |
---|
246 | 249 | } |
---|
247 | | - if (shm->pages) { |
---|
248 | | - for (n = 0; n < shm->num_pages; n++) |
---|
249 | | - put_page(shm->pages[n]); |
---|
250 | | - kfree(shm->pages); |
---|
251 | | - } |
---|
| 250 | + release_registered_pages(shm); |
---|
252 | 251 | } |
---|
253 | 252 | kfree(shm); |
---|
254 | 253 | teedev_ctx_put(ctx); |
---|
.. | .. |
---|
439 | 438 | */ |
---|
440 | 439 | void tee_shm_put(struct tee_shm *shm) |
---|
441 | 440 | { |
---|
442 | | - struct tee_device *teedev = shm->teedev; |
---|
| 441 | + struct tee_device *teedev = shm->ctx->teedev; |
---|
443 | 442 | bool do_release = false; |
---|
444 | 443 | |
---|
445 | 444 | mutex_lock(&teedev->mutex); |
---|
.. | .. |
---|
450 | 449 | * the refcount_inc() in tee_shm_get_from_id() never starts |
---|
451 | 450 | * from 0. |
---|
452 | 451 | */ |
---|
453 | | - idr_remove(&teedev->idr, shm->id); |
---|
454 | | - if (shm->ctx) |
---|
455 | | - list_del(&shm->link); |
---|
| 452 | + if (shm->flags & TEE_SHM_DMA_BUF) |
---|
| 453 | + idr_remove(&teedev->idr, shm->id); |
---|
456 | 454 | do_release = true; |
---|
457 | 455 | } |
---|
458 | 456 | mutex_unlock(&teedev->mutex); |
---|