File Index
Files and directories
Sign in
FILE

tee_heap.c

File preview with syntax highlighting.

linux-6.19.14/drivers/tee/tee_heap.c C
  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);
~/ Read-only file preview.