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

videobuf-dma-contig.c (9809B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * helper functions for physically contiguous capture buffers
      4 *
      5 * The functions support hardware lacking scatter gather support
      6 * (i.e. the buffers must be linear in physical memory)
      7 *
      8 * Copyright (c) 2008 Magnus Damm
      9 *
     10 * Based on videobuf-vmalloc.c,
     11 * (c) 2007 Mauro Carvalho Chehab, <mchehab@kernel.org>
     12 */
     13
     14#include <linux/init.h>
     15#include <linux/module.h>
     16#include <linux/mm.h>
     17#include <linux/pagemap.h>
     18#include <linux/dma-mapping.h>
     19#include <linux/sched.h>
     20#include <linux/slab.h>
     21#include <media/videobuf-dma-contig.h>
     22
     23struct videobuf_dma_contig_memory {
     24	u32 magic;
     25	void *vaddr;
     26	dma_addr_t dma_handle;
     27	unsigned long size;
     28};
     29
     30#define MAGIC_DC_MEM 0x0733ac61
     31#define MAGIC_CHECK(is, should)						    \
     32	if (unlikely((is) != (should)))	{				    \
     33		pr_err("magic mismatch: %x expected %x\n", (is), (should)); \
     34		BUG();							    \
     35	}
     36
     37static int __videobuf_dc_alloc(struct device *dev,
     38			       struct videobuf_dma_contig_memory *mem,
     39			       unsigned long size, gfp_t flags)
     40{
     41	mem->size = size;
     42	mem->vaddr = dma_alloc_coherent(dev, mem->size,
     43					&mem->dma_handle, flags);
     44
     45	if (!mem->vaddr) {
     46		dev_err(dev, "memory alloc size %ld failed\n", mem->size);
     47		return -ENOMEM;
     48	}
     49
     50	dev_dbg(dev, "dma mapped data is at %p (%ld)\n", mem->vaddr, mem->size);
     51
     52	return 0;
     53}
     54
     55static void __videobuf_dc_free(struct device *dev,
     56			       struct videobuf_dma_contig_memory *mem)
     57{
     58	dma_free_coherent(dev, mem->size, mem->vaddr, mem->dma_handle);
     59
     60	mem->vaddr = NULL;
     61}
     62
     63static void videobuf_vm_open(struct vm_area_struct *vma)
     64{
     65	struct videobuf_mapping *map = vma->vm_private_data;
     66
     67	dev_dbg(map->q->dev, "vm_open %p [count=%u,vma=%08lx-%08lx]\n",
     68		map, map->count, vma->vm_start, vma->vm_end);
     69
     70	map->count++;
     71}
     72
     73static void videobuf_vm_close(struct vm_area_struct *vma)
     74{
     75	struct videobuf_mapping *map = vma->vm_private_data;
     76	struct videobuf_queue *q = map->q;
     77	int i;
     78
     79	dev_dbg(q->dev, "vm_close %p [count=%u,vma=%08lx-%08lx]\n",
     80		map, map->count, vma->vm_start, vma->vm_end);
     81
     82	map->count--;
     83	if (0 == map->count) {
     84		struct videobuf_dma_contig_memory *mem;
     85
     86		dev_dbg(q->dev, "munmap %p q=%p\n", map, q);
     87		videobuf_queue_lock(q);
     88
     89		/* We need first to cancel streams, before unmapping */
     90		if (q->streaming)
     91			videobuf_queue_cancel(q);
     92
     93		for (i = 0; i < VIDEO_MAX_FRAME; i++) {
     94			if (NULL == q->bufs[i])
     95				continue;
     96
     97			if (q->bufs[i]->map != map)
     98				continue;
     99
    100			mem = q->bufs[i]->priv;
    101			if (mem) {
    102				/* This callback is called only if kernel has
    103				   allocated memory and this memory is mmapped.
    104				   In this case, memory should be freed,
    105				   in order to do memory unmap.
    106				 */
    107
    108				MAGIC_CHECK(mem->magic, MAGIC_DC_MEM);
    109
    110				/* vfree is not atomic - can't be
    111				   called with IRQ's disabled
    112				 */
    113				dev_dbg(q->dev, "buf[%d] freeing %p\n",
    114					i, mem->vaddr);
    115
    116				__videobuf_dc_free(q->dev, mem);
    117				mem->vaddr = NULL;
    118			}
    119
    120			q->bufs[i]->map = NULL;
    121			q->bufs[i]->baddr = 0;
    122		}
    123
    124		kfree(map);
    125
    126		videobuf_queue_unlock(q);
    127	}
    128}
    129
    130static const struct vm_operations_struct videobuf_vm_ops = {
    131	.open	= videobuf_vm_open,
    132	.close	= videobuf_vm_close,
    133};
    134
    135/**
    136 * videobuf_dma_contig_user_put() - reset pointer to user space buffer
    137 * @mem: per-buffer private videobuf-dma-contig data
    138 *
    139 * This function resets the user space pointer
    140 */
    141static void videobuf_dma_contig_user_put(struct videobuf_dma_contig_memory *mem)
    142{
    143	mem->dma_handle = 0;
    144	mem->size = 0;
    145}
    146
    147/**
    148 * videobuf_dma_contig_user_get() - setup user space memory pointer
    149 * @mem: per-buffer private videobuf-dma-contig data
    150 * @vb: video buffer to map
    151 *
    152 * This function validates and sets up a pointer to user space memory.
    153 * Only physically contiguous pfn-mapped memory is accepted.
    154 *
    155 * Returns 0 if successful.
    156 */
    157static int videobuf_dma_contig_user_get(struct videobuf_dma_contig_memory *mem,
    158					struct videobuf_buffer *vb)
    159{
    160	unsigned long untagged_baddr = untagged_addr(vb->baddr);
    161	struct mm_struct *mm = current->mm;
    162	struct vm_area_struct *vma;
    163	unsigned long prev_pfn, this_pfn;
    164	unsigned long pages_done, user_address;
    165	unsigned int offset;
    166	int ret;
    167
    168	offset = untagged_baddr & ~PAGE_MASK;
    169	mem->size = PAGE_ALIGN(vb->size + offset);
    170	ret = -EINVAL;
    171
    172	mmap_read_lock(mm);
    173
    174	vma = find_vma(mm, untagged_baddr);
    175	if (!vma)
    176		goto out_up;
    177
    178	if ((untagged_baddr + mem->size) > vma->vm_end)
    179		goto out_up;
    180
    181	pages_done = 0;
    182	prev_pfn = 0; /* kill warning */
    183	user_address = untagged_baddr;
    184
    185	while (pages_done < (mem->size >> PAGE_SHIFT)) {
    186		ret = follow_pfn(vma, user_address, &this_pfn);
    187		if (ret)
    188			break;
    189
    190		if (pages_done == 0)
    191			mem->dma_handle = (this_pfn << PAGE_SHIFT) + offset;
    192		else if (this_pfn != (prev_pfn + 1))
    193			ret = -EFAULT;
    194
    195		if (ret)
    196			break;
    197
    198		prev_pfn = this_pfn;
    199		user_address += PAGE_SIZE;
    200		pages_done++;
    201	}
    202
    203out_up:
    204	mmap_read_unlock(current->mm);
    205
    206	return ret;
    207}
    208
    209static struct videobuf_buffer *__videobuf_alloc(size_t size)
    210{
    211	struct videobuf_dma_contig_memory *mem;
    212	struct videobuf_buffer *vb;
    213
    214	vb = kzalloc(size + sizeof(*mem), GFP_KERNEL);
    215	if (vb) {
    216		vb->priv = ((char *)vb) + size;
    217		mem = vb->priv;
    218		mem->magic = MAGIC_DC_MEM;
    219	}
    220
    221	return vb;
    222}
    223
    224static void *__videobuf_to_vaddr(struct videobuf_buffer *buf)
    225{
    226	struct videobuf_dma_contig_memory *mem = buf->priv;
    227
    228	BUG_ON(!mem);
    229	MAGIC_CHECK(mem->magic, MAGIC_DC_MEM);
    230
    231	return mem->vaddr;
    232}
    233
    234static int __videobuf_iolock(struct videobuf_queue *q,
    235			     struct videobuf_buffer *vb,
    236			     struct v4l2_framebuffer *fbuf)
    237{
    238	struct videobuf_dma_contig_memory *mem = vb->priv;
    239
    240	BUG_ON(!mem);
    241	MAGIC_CHECK(mem->magic, MAGIC_DC_MEM);
    242
    243	switch (vb->memory) {
    244	case V4L2_MEMORY_MMAP:
    245		dev_dbg(q->dev, "%s memory method MMAP\n", __func__);
    246
    247		/* All handling should be done by __videobuf_mmap_mapper() */
    248		if (!mem->vaddr) {
    249			dev_err(q->dev, "memory is not allocated/mmapped.\n");
    250			return -EINVAL;
    251		}
    252		break;
    253	case V4L2_MEMORY_USERPTR:
    254		dev_dbg(q->dev, "%s memory method USERPTR\n", __func__);
    255
    256		/* handle pointer from user space */
    257		if (vb->baddr)
    258			return videobuf_dma_contig_user_get(mem, vb);
    259
    260		/* allocate memory for the read() method */
    261		if (__videobuf_dc_alloc(q->dev, mem, PAGE_ALIGN(vb->size),
    262					GFP_KERNEL))
    263			return -ENOMEM;
    264		break;
    265	case V4L2_MEMORY_OVERLAY:
    266	default:
    267		dev_dbg(q->dev, "%s memory method OVERLAY/unknown\n", __func__);
    268		return -EINVAL;
    269	}
    270
    271	return 0;
    272}
    273
    274static int __videobuf_mmap_mapper(struct videobuf_queue *q,
    275				  struct videobuf_buffer *buf,
    276				  struct vm_area_struct *vma)
    277{
    278	struct videobuf_dma_contig_memory *mem;
    279	struct videobuf_mapping *map;
    280	int retval;
    281
    282	dev_dbg(q->dev, "%s\n", __func__);
    283
    284	/* create mapping + update buffer list */
    285	map = kzalloc(sizeof(struct videobuf_mapping), GFP_KERNEL);
    286	if (!map)
    287		return -ENOMEM;
    288
    289	buf->map = map;
    290	map->q = q;
    291
    292	buf->baddr = vma->vm_start;
    293
    294	mem = buf->priv;
    295	BUG_ON(!mem);
    296	MAGIC_CHECK(mem->magic, MAGIC_DC_MEM);
    297
    298	if (__videobuf_dc_alloc(q->dev, mem, PAGE_ALIGN(buf->bsize),
    299				GFP_KERNEL | __GFP_COMP))
    300		goto error;
    301
    302	/* Try to remap memory */
    303	vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
    304
    305	/* the "vm_pgoff" is just used in v4l2 to find the
    306	 * corresponding buffer data structure which is allocated
    307	 * earlier and it does not mean the offset from the physical
    308	 * buffer start address as usual. So set it to 0 to pass
    309	 * the sanity check in vm_iomap_memory().
    310	 */
    311	vma->vm_pgoff = 0;
    312
    313	retval = vm_iomap_memory(vma, mem->dma_handle, mem->size);
    314	if (retval) {
    315		dev_err(q->dev, "mmap: remap failed with error %d. ",
    316			retval);
    317		dma_free_coherent(q->dev, mem->size,
    318				  mem->vaddr, mem->dma_handle);
    319		goto error;
    320	}
    321
    322	vma->vm_ops = &videobuf_vm_ops;
    323	vma->vm_flags |= VM_DONTEXPAND;
    324	vma->vm_private_data = map;
    325
    326	dev_dbg(q->dev, "mmap %p: q=%p %08lx-%08lx (%lx) pgoff %08lx buf %d\n",
    327		map, q, vma->vm_start, vma->vm_end,
    328		(long int)buf->bsize, vma->vm_pgoff, buf->i);
    329
    330	videobuf_vm_open(vma);
    331
    332	return 0;
    333
    334error:
    335	kfree(map);
    336	return -ENOMEM;
    337}
    338
    339static struct videobuf_qtype_ops qops = {
    340	.magic		= MAGIC_QTYPE_OPS,
    341	.alloc_vb	= __videobuf_alloc,
    342	.iolock		= __videobuf_iolock,
    343	.mmap_mapper	= __videobuf_mmap_mapper,
    344	.vaddr		= __videobuf_to_vaddr,
    345};
    346
    347void videobuf_queue_dma_contig_init(struct videobuf_queue *q,
    348				    const struct videobuf_queue_ops *ops,
    349				    struct device *dev,
    350				    spinlock_t *irqlock,
    351				    enum v4l2_buf_type type,
    352				    enum v4l2_field field,
    353				    unsigned int msize,
    354				    void *priv,
    355				    struct mutex *ext_lock)
    356{
    357	videobuf_queue_core_init(q, ops, dev, irqlock, type, field, msize,
    358				 priv, &qops, ext_lock);
    359}
    360EXPORT_SYMBOL_GPL(videobuf_queue_dma_contig_init);
    361
    362dma_addr_t videobuf_to_dma_contig(struct videobuf_buffer *buf)
    363{
    364	struct videobuf_dma_contig_memory *mem = buf->priv;
    365
    366	BUG_ON(!mem);
    367	MAGIC_CHECK(mem->magic, MAGIC_DC_MEM);
    368
    369	return mem->dma_handle;
    370}
    371EXPORT_SYMBOL_GPL(videobuf_to_dma_contig);
    372
    373void videobuf_dma_contig_free(struct videobuf_queue *q,
    374			      struct videobuf_buffer *buf)
    375{
    376	struct videobuf_dma_contig_memory *mem = buf->priv;
    377
    378	/* mmapped memory can't be freed here, otherwise mmapped region
    379	   would be released, while still needed. In this case, the memory
    380	   release should happen inside videobuf_vm_close().
    381	   So, it should free memory only if the memory were allocated for
    382	   read() operation.
    383	 */
    384	if (buf->memory != V4L2_MEMORY_USERPTR)
    385		return;
    386
    387	if (!mem)
    388		return;
    389
    390	MAGIC_CHECK(mem->magic, MAGIC_DC_MEM);
    391
    392	/* handle user space pointer case */
    393	if (buf->baddr) {
    394		videobuf_dma_contig_user_put(mem);
    395		return;
    396	}
    397
    398	/* read() method */
    399	if (mem->vaddr) {
    400		__videobuf_dc_free(q->dev, mem);
    401		mem->vaddr = NULL;
    402	}
    403}
    404EXPORT_SYMBOL_GPL(videobuf_dma_contig_free);
    405
    406MODULE_DESCRIPTION("helper module to manage video4linux dma contig buffers");
    407MODULE_AUTHOR("Magnus Damm");
    408MODULE_LICENSE("GPL");