cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

cma_heap.c (9209B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * DMABUF CMA heap exporter
      4 *
      5 * Copyright (C) 2012, 2019, 2020 Linaro Ltd.
      6 * Author: <benjamin.gaignard@linaro.org> for ST-Ericsson.
      7 *
      8 * Also utilizing parts of Andrew Davis' SRAM heap:
      9 * Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/
     10 *	Andrew F. Davis <afd@ti.com>
     11 */
     12#include <linux/cma.h>
     13#include <linux/dma-buf.h>
     14#include <linux/dma-heap.h>
     15#include <linux/dma-map-ops.h>
     16#include <linux/err.h>
     17#include <linux/highmem.h>
     18#include <linux/io.h>
     19#include <linux/mm.h>
     20#include <linux/module.h>
     21#include <linux/scatterlist.h>
     22#include <linux/slab.h>
     23#include <linux/vmalloc.h>
     24
     25
     26struct cma_heap {
     27	struct dma_heap *heap;
     28	struct cma *cma;
     29};
     30
     31struct cma_heap_buffer {
     32	struct cma_heap *heap;
     33	struct list_head attachments;
     34	struct mutex lock;
     35	unsigned long len;
     36	struct page *cma_pages;
     37	struct page **pages;
     38	pgoff_t pagecount;
     39	int vmap_cnt;
     40	void *vaddr;
     41};
     42
     43struct dma_heap_attachment {
     44	struct device *dev;
     45	struct sg_table table;
     46	struct list_head list;
     47	bool mapped;
     48};
     49
     50static int cma_heap_attach(struct dma_buf *dmabuf,
     51			   struct dma_buf_attachment *attachment)
     52{
     53	struct cma_heap_buffer *buffer = dmabuf->priv;
     54	struct dma_heap_attachment *a;
     55	int ret;
     56
     57	a = kzalloc(sizeof(*a), GFP_KERNEL);
     58	if (!a)
     59		return -ENOMEM;
     60
     61	ret = sg_alloc_table_from_pages(&a->table, buffer->pages,
     62					buffer->pagecount, 0,
     63					buffer->pagecount << PAGE_SHIFT,
     64					GFP_KERNEL);
     65	if (ret) {
     66		kfree(a);
     67		return ret;
     68	}
     69
     70	a->dev = attachment->dev;
     71	INIT_LIST_HEAD(&a->list);
     72	a->mapped = false;
     73
     74	attachment->priv = a;
     75
     76	mutex_lock(&buffer->lock);
     77	list_add(&a->list, &buffer->attachments);
     78	mutex_unlock(&buffer->lock);
     79
     80	return 0;
     81}
     82
     83static void cma_heap_detach(struct dma_buf *dmabuf,
     84			    struct dma_buf_attachment *attachment)
     85{
     86	struct cma_heap_buffer *buffer = dmabuf->priv;
     87	struct dma_heap_attachment *a = attachment->priv;
     88
     89	mutex_lock(&buffer->lock);
     90	list_del(&a->list);
     91	mutex_unlock(&buffer->lock);
     92
     93	sg_free_table(&a->table);
     94	kfree(a);
     95}
     96
     97static struct sg_table *cma_heap_map_dma_buf(struct dma_buf_attachment *attachment,
     98					     enum dma_data_direction direction)
     99{
    100	struct dma_heap_attachment *a = attachment->priv;
    101	struct sg_table *table = &a->table;
    102	int ret;
    103
    104	ret = dma_map_sgtable(attachment->dev, table, direction, 0);
    105	if (ret)
    106		return ERR_PTR(-ENOMEM);
    107	a->mapped = true;
    108	return table;
    109}
    110
    111static void cma_heap_unmap_dma_buf(struct dma_buf_attachment *attachment,
    112				   struct sg_table *table,
    113				   enum dma_data_direction direction)
    114{
    115	struct dma_heap_attachment *a = attachment->priv;
    116
    117	a->mapped = false;
    118	dma_unmap_sgtable(attachment->dev, table, direction, 0);
    119}
    120
    121static int cma_heap_dma_buf_begin_cpu_access(struct dma_buf *dmabuf,
    122					     enum dma_data_direction direction)
    123{
    124	struct cma_heap_buffer *buffer = dmabuf->priv;
    125	struct dma_heap_attachment *a;
    126
    127	mutex_lock(&buffer->lock);
    128
    129	if (buffer->vmap_cnt)
    130		invalidate_kernel_vmap_range(buffer->vaddr, buffer->len);
    131
    132	list_for_each_entry(a, &buffer->attachments, list) {
    133		if (!a->mapped)
    134			continue;
    135		dma_sync_sgtable_for_cpu(a->dev, &a->table, direction);
    136	}
    137	mutex_unlock(&buffer->lock);
    138
    139	return 0;
    140}
    141
    142static int cma_heap_dma_buf_end_cpu_access(struct dma_buf *dmabuf,
    143					   enum dma_data_direction direction)
    144{
    145	struct cma_heap_buffer *buffer = dmabuf->priv;
    146	struct dma_heap_attachment *a;
    147
    148	mutex_lock(&buffer->lock);
    149
    150	if (buffer->vmap_cnt)
    151		flush_kernel_vmap_range(buffer->vaddr, buffer->len);
    152
    153	list_for_each_entry(a, &buffer->attachments, list) {
    154		if (!a->mapped)
    155			continue;
    156		dma_sync_sgtable_for_device(a->dev, &a->table, direction);
    157	}
    158	mutex_unlock(&buffer->lock);
    159
    160	return 0;
    161}
    162
    163static vm_fault_t cma_heap_vm_fault(struct vm_fault *vmf)
    164{
    165	struct vm_area_struct *vma = vmf->vma;
    166	struct cma_heap_buffer *buffer = vma->vm_private_data;
    167
    168	if (vmf->pgoff > buffer->pagecount)
    169		return VM_FAULT_SIGBUS;
    170
    171	vmf->page = buffer->pages[vmf->pgoff];
    172	get_page(vmf->page);
    173
    174	return 0;
    175}
    176
    177static const struct vm_operations_struct dma_heap_vm_ops = {
    178	.fault = cma_heap_vm_fault,
    179};
    180
    181static int cma_heap_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma)
    182{
    183	struct cma_heap_buffer *buffer = dmabuf->priv;
    184
    185	if ((vma->vm_flags & (VM_SHARED | VM_MAYSHARE)) == 0)
    186		return -EINVAL;
    187
    188	vma->vm_ops = &dma_heap_vm_ops;
    189	vma->vm_private_data = buffer;
    190
    191	return 0;
    192}
    193
    194static void *cma_heap_do_vmap(struct cma_heap_buffer *buffer)
    195{
    196	void *vaddr;
    197
    198	vaddr = vmap(buffer->pages, buffer->pagecount, VM_MAP, PAGE_KERNEL);
    199	if (!vaddr)
    200		return ERR_PTR(-ENOMEM);
    201
    202	return vaddr;
    203}
    204
    205static int cma_heap_vmap(struct dma_buf *dmabuf, struct iosys_map *map)
    206{
    207	struct cma_heap_buffer *buffer = dmabuf->priv;
    208	void *vaddr;
    209	int ret = 0;
    210
    211	mutex_lock(&buffer->lock);
    212	if (buffer->vmap_cnt) {
    213		buffer->vmap_cnt++;
    214		iosys_map_set_vaddr(map, buffer->vaddr);
    215		goto out;
    216	}
    217
    218	vaddr = cma_heap_do_vmap(buffer);
    219	if (IS_ERR(vaddr)) {
    220		ret = PTR_ERR(vaddr);
    221		goto out;
    222	}
    223	buffer->vaddr = vaddr;
    224	buffer->vmap_cnt++;
    225	iosys_map_set_vaddr(map, buffer->vaddr);
    226out:
    227	mutex_unlock(&buffer->lock);
    228
    229	return ret;
    230}
    231
    232static void cma_heap_vunmap(struct dma_buf *dmabuf, struct iosys_map *map)
    233{
    234	struct cma_heap_buffer *buffer = dmabuf->priv;
    235
    236	mutex_lock(&buffer->lock);
    237	if (!--buffer->vmap_cnt) {
    238		vunmap(buffer->vaddr);
    239		buffer->vaddr = NULL;
    240	}
    241	mutex_unlock(&buffer->lock);
    242	iosys_map_clear(map);
    243}
    244
    245static void cma_heap_dma_buf_release(struct dma_buf *dmabuf)
    246{
    247	struct cma_heap_buffer *buffer = dmabuf->priv;
    248	struct cma_heap *cma_heap = buffer->heap;
    249
    250	if (buffer->vmap_cnt > 0) {
    251		WARN(1, "%s: buffer still mapped in the kernel\n", __func__);
    252		vunmap(buffer->vaddr);
    253		buffer->vaddr = NULL;
    254	}
    255
    256	/* free page list */
    257	kfree(buffer->pages);
    258	/* release memory */
    259	cma_release(cma_heap->cma, buffer->cma_pages, buffer->pagecount);
    260	kfree(buffer);
    261}
    262
    263static const struct dma_buf_ops cma_heap_buf_ops = {
    264	.attach = cma_heap_attach,
    265	.detach = cma_heap_detach,
    266	.map_dma_buf = cma_heap_map_dma_buf,
    267	.unmap_dma_buf = cma_heap_unmap_dma_buf,
    268	.begin_cpu_access = cma_heap_dma_buf_begin_cpu_access,
    269	.end_cpu_access = cma_heap_dma_buf_end_cpu_access,
    270	.mmap = cma_heap_mmap,
    271	.vmap = cma_heap_vmap,
    272	.vunmap = cma_heap_vunmap,
    273	.release = cma_heap_dma_buf_release,
    274};
    275
    276static struct dma_buf *cma_heap_allocate(struct dma_heap *heap,
    277					 unsigned long len,
    278					 unsigned long fd_flags,
    279					 unsigned long heap_flags)
    280{
    281	struct cma_heap *cma_heap = dma_heap_get_drvdata(heap);
    282	struct cma_heap_buffer *buffer;
    283	DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
    284	size_t size = PAGE_ALIGN(len);
    285	pgoff_t pagecount = size >> PAGE_SHIFT;
    286	unsigned long align = get_order(size);
    287	struct page *cma_pages;
    288	struct dma_buf *dmabuf;
    289	int ret = -ENOMEM;
    290	pgoff_t pg;
    291
    292	buffer = kzalloc(sizeof(*buffer), GFP_KERNEL);
    293	if (!buffer)
    294		return ERR_PTR(-ENOMEM);
    295
    296	INIT_LIST_HEAD(&buffer->attachments);
    297	mutex_init(&buffer->lock);
    298	buffer->len = size;
    299
    300	if (align > CONFIG_CMA_ALIGNMENT)
    301		align = CONFIG_CMA_ALIGNMENT;
    302
    303	cma_pages = cma_alloc(cma_heap->cma, pagecount, align, false);
    304	if (!cma_pages)
    305		goto free_buffer;
    306
    307	/* Clear the cma pages */
    308	if (PageHighMem(cma_pages)) {
    309		unsigned long nr_clear_pages = pagecount;
    310		struct page *page = cma_pages;
    311
    312		while (nr_clear_pages > 0) {
    313			void *vaddr = kmap_atomic(page);
    314
    315			memset(vaddr, 0, PAGE_SIZE);
    316			kunmap_atomic(vaddr);
    317			/*
    318			 * Avoid wasting time zeroing memory if the process
    319			 * has been killed by by SIGKILL
    320			 */
    321			if (fatal_signal_pending(current))
    322				goto free_cma;
    323			page++;
    324			nr_clear_pages--;
    325		}
    326	} else {
    327		memset(page_address(cma_pages), 0, size);
    328	}
    329
    330	buffer->pages = kmalloc_array(pagecount, sizeof(*buffer->pages), GFP_KERNEL);
    331	if (!buffer->pages) {
    332		ret = -ENOMEM;
    333		goto free_cma;
    334	}
    335
    336	for (pg = 0; pg < pagecount; pg++)
    337		buffer->pages[pg] = &cma_pages[pg];
    338
    339	buffer->cma_pages = cma_pages;
    340	buffer->heap = cma_heap;
    341	buffer->pagecount = pagecount;
    342
    343	/* create the dmabuf */
    344	exp_info.exp_name = dma_heap_get_name(heap);
    345	exp_info.ops = &cma_heap_buf_ops;
    346	exp_info.size = buffer->len;
    347	exp_info.flags = fd_flags;
    348	exp_info.priv = buffer;
    349	dmabuf = dma_buf_export(&exp_info);
    350	if (IS_ERR(dmabuf)) {
    351		ret = PTR_ERR(dmabuf);
    352		goto free_pages;
    353	}
    354	return dmabuf;
    355
    356free_pages:
    357	kfree(buffer->pages);
    358free_cma:
    359	cma_release(cma_heap->cma, cma_pages, pagecount);
    360free_buffer:
    361	kfree(buffer);
    362
    363	return ERR_PTR(ret);
    364}
    365
    366static const struct dma_heap_ops cma_heap_ops = {
    367	.allocate = cma_heap_allocate,
    368};
    369
    370static int __add_cma_heap(struct cma *cma, void *data)
    371{
    372	struct cma_heap *cma_heap;
    373	struct dma_heap_export_info exp_info;
    374
    375	cma_heap = kzalloc(sizeof(*cma_heap), GFP_KERNEL);
    376	if (!cma_heap)
    377		return -ENOMEM;
    378	cma_heap->cma = cma;
    379
    380	exp_info.name = cma_get_name(cma);
    381	exp_info.ops = &cma_heap_ops;
    382	exp_info.priv = cma_heap;
    383
    384	cma_heap->heap = dma_heap_add(&exp_info);
    385	if (IS_ERR(cma_heap->heap)) {
    386		int ret = PTR_ERR(cma_heap->heap);
    387
    388		kfree(cma_heap);
    389		return ret;
    390	}
    391
    392	return 0;
    393}
    394
    395static int add_default_cma_heap(void)
    396{
    397	struct cma *default_cma = dev_get_cma_area(NULL);
    398	int ret = 0;
    399
    400	if (default_cma)
    401		ret = __add_cma_heap(default_cma, NULL);
    402
    403	return ret;
    404}
    405module_init(add_default_cma_heap);
    406MODULE_DESCRIPTION("DMA-BUF CMA Heap");
    407MODULE_LICENSE("GPL v2");