Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
1// SPDX-License-Identifier: GPL-2.0-only
2/*
3 * Copyright (c) 2025, Linaro Limited
4 */
5
6#include <linux/dma-buf.h>
7#include <linux/dma-heap.h>
8#include <linux/genalloc.h>
9#include <linux/module.h>
10#include <linux/scatterlist.h>
11#include <linux/slab.h>
12#include <linux/tee_core.h>
13#include <linux/xarray.h>
14
15#include "tee_private.h"
16
17struct tee_dma_heap {
18 struct dma_heap *heap;
19 enum tee_dma_heap_id id;
20 struct kref kref;
21 struct tee_protmem_pool *pool;
22 struct tee_device *teedev;
23 bool shutting_down;
24 /* Protects pool, teedev, and shutting_down above */
25 struct mutex mu;
26};
27
28struct tee_heap_buffer {
29 struct tee_dma_heap *heap;
30 size_t size;
31 size_t offs;
32 struct sg_table table;
33};
34
35struct tee_heap_attachment {
36 struct sg_table table;
37 struct device *dev;
38};
39
40struct tee_protmem_static_pool {
41 struct tee_protmem_pool pool;
42 struct gen_pool *gen_pool;
43 phys_addr_t pa_base;
44};
45
46#if IS_ENABLED(CONFIG_TEE_DMABUF_HEAPS)
47static DEFINE_XARRAY_ALLOC(tee_dma_heap);
48
49static void tee_heap_release(struct kref *kref)
50{
51 struct tee_dma_heap *h = container_of(kref, struct tee_dma_heap, kref);
52
53 h->pool->ops->destroy_pool(h->pool);
54 tee_device_put(h->teedev);
55 h->pool = NULL;
56 h->teedev = NULL;
57}
58
59static void put_tee_heap(struct tee_dma_heap *h)
60{
61 kref_put(&h->kref, tee_heap_release);
62}
63
64static void get_tee_heap(struct tee_dma_heap *h)
65{
66 kref_get(&h->kref);
67}
68
69static int copy_sg_table(struct sg_table *dst, struct sg_table *src)
70{
71 struct scatterlist *dst_sg;
72 struct scatterlist *src_sg;
73 int ret;
74 int i;
75
76 ret = sg_alloc_table(dst, src->orig_nents, GFP_KERNEL);
77 if (ret)
78 return ret;
79
80 dst_sg = dst->sgl;
81 for_each_sgtable_sg(src, src_sg, i) {
82 sg_set_page(dst_sg, sg_page(src_sg), src_sg->length,
83 src_sg->offset);
84 dst_sg = sg_next(dst_sg);
85 }
86
87 return 0;
88}
89
90static int tee_heap_attach(struct dma_buf *dmabuf,
91 struct dma_buf_attachment *attachment)
92{
93 struct tee_heap_buffer *buf = dmabuf->priv;
94 struct tee_heap_attachment *a;
95 int ret;
96
97 a = kzalloc(sizeof(*a), GFP_KERNEL);
98 if (!a)
99 return -ENOMEM;
100
101 ret = copy_sg_table(&a->table, &buf->table);
102 if (ret) {
103 kfree(a);
104 return ret;
105 }
106
107 a->dev = attachment->dev;
108 attachment->priv = a;
109
110 return 0;
111}
112
113static void tee_heap_detach(struct dma_buf *dmabuf,
114 struct dma_buf_attachment *attachment)
115{
116 struct tee_heap_attachment *a = attachment->priv;
117
118 sg_free_table(&a->table);
119 kfree(a);
120}
121
122static struct sg_table *
123tee_heap_map_dma_buf(struct dma_buf_attachment *attachment,
124 enum dma_data_direction direction)
125{
126 struct tee_heap_attachment *a = attachment->priv;
127 int ret;
128
129 ret = dma_map_sgtable(attachment->dev, &a->table, direction,
130 DMA_ATTR_SKIP_CPU_SYNC);
131 if (ret)
132 return ERR_PTR(ret);
133
134 return &a->table;
135}
136
137static void tee_heap_unmap_dma_buf(struct dma_buf_attachment *attachment,
138 struct sg_table *table,
139 enum dma_data_direction direction)
140{
141 struct tee_heap_attachment *a = attachment->priv;
142
143 WARN_ON(&a->table != table);
144
145 dma_unmap_sgtable(attachment->dev, table, direction,
146 DMA_ATTR_SKIP_CPU_SYNC);
147}
148
149static void tee_heap_buf_free(struct dma_buf *dmabuf)
150{
151 struct tee_heap_buffer *buf = dmabuf->priv;
152
153 buf->heap->pool->ops->free(buf->heap->pool, &buf->table);
154 mutex_lock(&buf->heap->mu);
155 put_tee_heap(buf->heap);
156 mutex_unlock(&buf->heap->mu);
157 kfree(buf);
158}
159
160static const struct dma_buf_ops tee_heap_buf_ops = {
161 .attach = tee_heap_attach,
162 .detach = tee_heap_detach,
163 .map_dma_buf = tee_heap_map_dma_buf,
164 .unmap_dma_buf = tee_heap_unmap_dma_buf,
165 .release = tee_heap_buf_free,
166};
167
168static struct dma_buf *tee_dma_heap_alloc(struct dma_heap *heap,
169 unsigned long len, u32 fd_flags,
170 u64 heap_flags)
171{
172 struct tee_dma_heap *h = dma_heap_get_drvdata(heap);
173 DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
174 struct tee_device *teedev = NULL;
175 struct tee_heap_buffer *buf;
176 struct tee_protmem_pool *pool;
177 struct dma_buf *dmabuf;
178 int rc;
179
180 mutex_lock(&h->mu);
181 if (h->teedev) {
182 teedev = h->teedev;
183 pool = h->pool;
184 get_tee_heap(h);
185 }
186 mutex_unlock(&h->mu);
187
188 if (!teedev)
189 return ERR_PTR(-EINVAL);
190
191 buf = kzalloc(sizeof(*buf), GFP_KERNEL);
192 if (!buf) {
193 dmabuf = ERR_PTR(-ENOMEM);
194 goto err;
195 }
196 buf->size = len;
197 buf->heap = h;
198
199 rc = pool->ops->alloc(pool, &buf->table, len, &buf->offs);
200 if (rc) {
201 dmabuf = ERR_PTR(rc);
202 goto err_kfree;
203 }
204
205 exp_info.ops = &tee_heap_buf_ops;
206 exp_info.size = len;
207 exp_info.priv = buf;
208 exp_info.flags = fd_flags;
209 dmabuf = dma_buf_export(&exp_info);
210 if (IS_ERR(dmabuf))
211 goto err_protmem_free;
212
213 return dmabuf;
214
215err_protmem_free:
216 pool->ops->free(pool, &buf->table);
217err_kfree:
218 kfree(buf);
219err:
220 mutex_lock(&h->mu);
221 put_tee_heap(h);
222 mutex_unlock(&h->mu);
223 return dmabuf;
224}
225
226static const struct dma_heap_ops tee_dma_heap_ops = {
227 .allocate = tee_dma_heap_alloc,
228};
229
230static const char *heap_id_2_name(enum tee_dma_heap_id id)
231{
232 switch (id) {
233 case TEE_DMA_HEAP_SECURE_VIDEO_PLAY:
234 return "protected,secure-video";
235 case TEE_DMA_HEAP_TRUSTED_UI:
236 return "protected,trusted-ui";
237 case TEE_DMA_HEAP_SECURE_VIDEO_RECORD:
238 return "protected,secure-video-record";
239 default:
240 return NULL;
241 }
242}
243
244static int alloc_dma_heap(struct tee_device *teedev, enum tee_dma_heap_id id,
245 struct tee_protmem_pool *pool)
246{
247 struct dma_heap_export_info exp_info = {
248 .ops = &tee_dma_heap_ops,
249 .name = heap_id_2_name(id),
250 };
251 struct tee_dma_heap *h;
252 int rc;
253
254 if (!exp_info.name)
255 return -EINVAL;
256
257 if (xa_reserve(&tee_dma_heap, id, GFP_KERNEL)) {
258 if (!xa_load(&tee_dma_heap, id))
259 return -EEXIST;
260 return -ENOMEM;
261 }
262
263 h = kzalloc(sizeof(*h), GFP_KERNEL);
264 if (!h)
265 return -ENOMEM;
266 h->id = id;
267 kref_init(&h->kref);
268 h->teedev = teedev;
269 h->pool = pool;
270 mutex_init(&h->mu);
271
272 exp_info.priv = h;
273 h->heap = dma_heap_add(&exp_info);
274 if (IS_ERR(h->heap)) {
275 rc = PTR_ERR(h->heap);
276 kfree(h);
277
278 return rc;
279 }
280
281 /* "can't fail" due to the call to xa_reserve() above */
282 return WARN_ON(xa_is_err(xa_store(&tee_dma_heap, id, h, GFP_KERNEL)));
283}
284
285int tee_device_register_dma_heap(struct tee_device *teedev,
286 enum tee_dma_heap_id id,
287 struct tee_protmem_pool *pool)
288{
289 struct tee_dma_heap *h;
290 int rc;
291
292 if (!tee_device_get(teedev))
293 return -EINVAL;
294
295 h = xa_load(&tee_dma_heap, id);
296 if (h) {
297 mutex_lock(&h->mu);
298 if (h->teedev) {
299 rc = -EBUSY;
300 } else {
301 kref_init(&h->kref);
302 h->shutting_down = false;
303 h->teedev = teedev;
304 h->pool = pool;
305 rc = 0;
306 }
307 mutex_unlock(&h->mu);
308 } else {
309 rc = alloc_dma_heap(teedev, id, pool);
310 }
311
312 if (rc) {
313 tee_device_put(teedev);
314 dev_err(&teedev->dev, "can't register DMA heap id %d (%s)\n",
315 id, heap_id_2_name(id));
316 }
317
318 return rc;
319}
320EXPORT_SYMBOL_GPL(tee_device_register_dma_heap);
321
322void tee_device_put_all_dma_heaps(struct tee_device *teedev)
323{
324 struct tee_dma_heap *h;
325 u_long i;
326
327 xa_for_each(&tee_dma_heap, i, h) {
328 if (h) {
329 mutex_lock(&h->mu);
330 if (h->teedev == teedev && !h->shutting_down) {
331 h->shutting_down = true;
332 put_tee_heap(h);
333 }
334 mutex_unlock(&h->mu);
335 }
336 }
337}
338EXPORT_SYMBOL_GPL(tee_device_put_all_dma_heaps);
339
340int tee_heap_update_from_dma_buf(struct tee_device *teedev,
341 struct dma_buf *dmabuf, size_t *offset,
342 struct tee_shm *shm,
343 struct tee_shm **parent_shm)
344{
345 struct tee_heap_buffer *buf;
346 int rc;
347
348 /* The DMA-buf must be from our heap */
349 if (dmabuf->ops != &tee_heap_buf_ops)
350 return -EINVAL;
351
352 buf = dmabuf->priv;
353 /* The buffer must be from the same teedev */
354 if (buf->heap->teedev != teedev)
355 return -EINVAL;
356
357 shm->size = buf->size;
358
359 rc = buf->heap->pool->ops->update_shm(buf->heap->pool, &buf->table,
360 buf->offs, shm, parent_shm);
361 if (!rc && *parent_shm)
362 *offset = buf->offs;
363
364 return rc;
365}
366#else
367int tee_device_register_dma_heap(struct tee_device *teedev __always_unused,
368 enum tee_dma_heap_id id __always_unused,
369 struct tee_protmem_pool *pool __always_unused)
370{
371 return -EINVAL;
372}
373EXPORT_SYMBOL_GPL(tee_device_register_dma_heap);
374
375void
376tee_device_put_all_dma_heaps(struct tee_device *teedev __always_unused)
377{
378}
379EXPORT_SYMBOL_GPL(tee_device_put_all_dma_heaps);
380
381int tee_heap_update_from_dma_buf(struct tee_device *teedev __always_unused,
382 struct dma_buf *dmabuf __always_unused,
383 size_t *offset __always_unused,
384 struct tee_shm *shm __always_unused,
385 struct tee_shm **parent_shm __always_unused)
386{
387 return -EINVAL;
388}
389#endif
390
391static struct tee_protmem_static_pool *
392to_protmem_static_pool(struct tee_protmem_pool *pool)
393{
394 return container_of(pool, struct tee_protmem_static_pool, pool);
395}
396
397static int protmem_pool_op_static_alloc(struct tee_protmem_pool *pool,
398 struct sg_table *sgt, size_t size,
399 size_t *offs)
400{
401 struct tee_protmem_static_pool *stp = to_protmem_static_pool(pool);
402 phys_addr_t pa;
403 int ret;
404
405 pa = gen_pool_alloc(stp->gen_pool, size);
406 if (!pa)
407 return -ENOMEM;
408
409 ret = sg_alloc_table(sgt, 1, GFP_KERNEL);
410 if (ret) {
411 gen_pool_free(stp->gen_pool, pa, size);
412 return ret;
413 }
414
415 sg_set_page(sgt->sgl, phys_to_page(pa), size, 0);
416 *offs = pa - stp->pa_base;
417
418 return 0;
419}
420
421static void protmem_pool_op_static_free(struct tee_protmem_pool *pool,
422 struct sg_table *sgt)
423{
424 struct tee_protmem_static_pool *stp = to_protmem_static_pool(pool);
425 struct scatterlist *sg;
426 int i;
427
428 for_each_sgtable_sg(sgt, sg, i)
429 gen_pool_free(stp->gen_pool, sg_phys(sg), sg->length);
430 sg_free_table(sgt);
431}
432
433static int protmem_pool_op_static_update_shm(struct tee_protmem_pool *pool,
434 struct sg_table *sgt, size_t offs,
435 struct tee_shm *shm,
436 struct tee_shm **parent_shm)
437{
438 struct tee_protmem_static_pool *stp = to_protmem_static_pool(pool);
439
440 shm->paddr = stp->pa_base + offs;
441 *parent_shm = NULL;
442
443 return 0;
444}
445
446static void protmem_pool_op_static_destroy_pool(struct tee_protmem_pool *pool)
447{
448 struct tee_protmem_static_pool *stp = to_protmem_static_pool(pool);
449
450 gen_pool_destroy(stp->gen_pool);
451 kfree(stp);
452}
453
454static struct tee_protmem_pool_ops protmem_pool_ops_static = {
455 .alloc = protmem_pool_op_static_alloc,
456 .free = protmem_pool_op_static_free,
457 .update_shm = protmem_pool_op_static_update_shm,
458 .destroy_pool = protmem_pool_op_static_destroy_pool,
459};
460
461struct tee_protmem_pool *tee_protmem_static_pool_alloc(phys_addr_t paddr,
462 size_t size)
463{
464 const size_t page_mask = PAGE_SIZE - 1;
465 struct tee_protmem_static_pool *stp;
466 int rc;
467
468 /* Check it's page aligned */
469 if ((paddr | size) & page_mask)
470 return ERR_PTR(-EINVAL);
471
472 if (!pfn_valid(PHYS_PFN(paddr)))
473 return ERR_PTR(-EINVAL);
474
475 stp = kzalloc(sizeof(*stp), GFP_KERNEL);
476 if (!stp)
477 return ERR_PTR(-ENOMEM);
478
479 stp->gen_pool = gen_pool_create(PAGE_SHIFT, -1);
480 if (!stp->gen_pool) {
481 rc = -ENOMEM;
482 goto err_free;
483 }
484
485 rc = gen_pool_add(stp->gen_pool, paddr, size, -1);
486 if (rc)
487 goto err_free_pool;
488
489 stp->pool.ops = &protmem_pool_ops_static;
490 stp->pa_base = paddr;
491 return &stp->pool;
492
493err_free_pool:
494 gen_pool_destroy(stp->gen_pool);
495err_free:
496 kfree(stp);
497
498 return ERR_PTR(rc);
499}
500EXPORT_SYMBOL_GPL(tee_protmem_static_pool_alloc);