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-vmalloc.c (7870B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * helper functions for vmalloc video4linux capture buffers
      4 *
      5 * The functions expect the hardware being able to scatter gather
      6 * (i.e. the buffers are not linear in physical memory, but fragmented
      7 * into PAGE_SIZE chunks).  They also assume the driver does not need
      8 * to touch the video data.
      9 *
     10 * (c) 2007 Mauro Carvalho Chehab <mchehab@kernel.org>
     11 */
     12
     13#include <linux/init.h>
     14#include <linux/module.h>
     15#include <linux/moduleparam.h>
     16#include <linux/slab.h>
     17#include <linux/interrupt.h>
     18#include <linux/pgtable.h>
     19
     20#include <linux/pci.h>
     21#include <linux/vmalloc.h>
     22#include <linux/pagemap.h>
     23#include <asm/page.h>
     24
     25#include <media/videobuf-vmalloc.h>
     26
     27#define MAGIC_DMABUF   0x17760309
     28#define MAGIC_VMAL_MEM 0x18221223
     29
     30#define MAGIC_CHECK(is, should)						\
     31	if (unlikely((is) != (should))) {				\
     32		printk(KERN_ERR "magic mismatch: %x (expected %x)\n",	\
     33				is, should);				\
     34		BUG();							\
     35	}
     36
     37static int debug;
     38module_param(debug, int, 0644);
     39
     40MODULE_DESCRIPTION("helper module to manage video4linux vmalloc buffers");
     41MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@kernel.org>");
     42MODULE_LICENSE("GPL");
     43
     44#define dprintk(level, fmt, arg...)					\
     45	if (debug >= level)						\
     46		printk(KERN_DEBUG "vbuf-vmalloc: " fmt , ## arg)
     47
     48
     49/***************************************************************************/
     50
     51static void videobuf_vm_open(struct vm_area_struct *vma)
     52{
     53	struct videobuf_mapping *map = vma->vm_private_data;
     54
     55	dprintk(2, "vm_open %p [count=%u,vma=%08lx-%08lx]\n", map,
     56		map->count, vma->vm_start, vma->vm_end);
     57
     58	map->count++;
     59}
     60
     61static void videobuf_vm_close(struct vm_area_struct *vma)
     62{
     63	struct videobuf_mapping *map = vma->vm_private_data;
     64	struct videobuf_queue *q = map->q;
     65	int i;
     66
     67	dprintk(2, "vm_close %p [count=%u,vma=%08lx-%08lx]\n", map,
     68		map->count, vma->vm_start, vma->vm_end);
     69
     70	map->count--;
     71	if (0 == map->count) {
     72		struct videobuf_vmalloc_memory *mem;
     73
     74		dprintk(1, "munmap %p q=%p\n", map, q);
     75		videobuf_queue_lock(q);
     76
     77		/* We need first to cancel streams, before unmapping */
     78		if (q->streaming)
     79			videobuf_queue_cancel(q);
     80
     81		for (i = 0; i < VIDEO_MAX_FRAME; i++) {
     82			if (NULL == q->bufs[i])
     83				continue;
     84
     85			if (q->bufs[i]->map != map)
     86				continue;
     87
     88			mem = q->bufs[i]->priv;
     89			if (mem) {
     90				/* This callback is called only if kernel has
     91				   allocated memory and this memory is mmapped.
     92				   In this case, memory should be freed,
     93				   in order to do memory unmap.
     94				 */
     95
     96				MAGIC_CHECK(mem->magic, MAGIC_VMAL_MEM);
     97
     98				/* vfree is not atomic - can't be
     99				   called with IRQ's disabled
    100				 */
    101				dprintk(1, "%s: buf[%d] freeing (%p)\n",
    102					__func__, i, mem->vaddr);
    103
    104				vfree(mem->vaddr);
    105				mem->vaddr = NULL;
    106			}
    107
    108			q->bufs[i]->map   = NULL;
    109			q->bufs[i]->baddr = 0;
    110		}
    111
    112		kfree(map);
    113
    114		videobuf_queue_unlock(q);
    115	}
    116
    117	return;
    118}
    119
    120static const struct vm_operations_struct videobuf_vm_ops = {
    121	.open     = videobuf_vm_open,
    122	.close    = videobuf_vm_close,
    123};
    124
    125/* ---------------------------------------------------------------------
    126 * vmalloc handlers for the generic methods
    127 */
    128
    129/* Allocated area consists on 3 parts:
    130	struct video_buffer
    131	struct <driver>_buffer (cx88_buffer, saa7134_buf, ...)
    132	struct videobuf_dma_sg_memory
    133 */
    134
    135static struct videobuf_buffer *__videobuf_alloc_vb(size_t size)
    136{
    137	struct videobuf_vmalloc_memory *mem;
    138	struct videobuf_buffer *vb;
    139
    140	vb = kzalloc(size + sizeof(*mem), GFP_KERNEL);
    141	if (!vb)
    142		return vb;
    143
    144	mem = vb->priv = ((char *)vb) + size;
    145	mem->magic = MAGIC_VMAL_MEM;
    146
    147	dprintk(1, "%s: allocated at %p(%ld+%ld) & %p(%ld)\n",
    148		__func__, vb, (long)sizeof(*vb), (long)size - sizeof(*vb),
    149		mem, (long)sizeof(*mem));
    150
    151	return vb;
    152}
    153
    154static int __videobuf_iolock(struct videobuf_queue *q,
    155			     struct videobuf_buffer *vb,
    156			     struct v4l2_framebuffer *fbuf)
    157{
    158	struct videobuf_vmalloc_memory *mem = vb->priv;
    159	int pages;
    160
    161	BUG_ON(!mem);
    162
    163	MAGIC_CHECK(mem->magic, MAGIC_VMAL_MEM);
    164
    165	switch (vb->memory) {
    166	case V4L2_MEMORY_MMAP:
    167		dprintk(1, "%s memory method MMAP\n", __func__);
    168
    169		/* All handling should be done by __videobuf_mmap_mapper() */
    170		if (!mem->vaddr) {
    171			printk(KERN_ERR "memory is not allocated/mmapped.\n");
    172			return -EINVAL;
    173		}
    174		break;
    175	case V4L2_MEMORY_USERPTR:
    176		pages = PAGE_ALIGN(vb->size);
    177
    178		dprintk(1, "%s memory method USERPTR\n", __func__);
    179
    180		if (vb->baddr) {
    181			printk(KERN_ERR "USERPTR is currently not supported\n");
    182			return -EINVAL;
    183		}
    184
    185		/* The only USERPTR currently supported is the one needed for
    186		 * read() method.
    187		 */
    188
    189		mem->vaddr = vmalloc_user(pages);
    190		if (!mem->vaddr) {
    191			printk(KERN_ERR "vmalloc (%d pages) failed\n", pages);
    192			return -ENOMEM;
    193		}
    194		dprintk(1, "vmalloc is at addr %p (%d pages)\n",
    195			mem->vaddr, pages);
    196		break;
    197	case V4L2_MEMORY_OVERLAY:
    198	default:
    199		dprintk(1, "%s memory method OVERLAY/unknown\n", __func__);
    200
    201		/* Currently, doesn't support V4L2_MEMORY_OVERLAY */
    202		printk(KERN_ERR "Memory method currently unsupported.\n");
    203		return -EINVAL;
    204	}
    205
    206	return 0;
    207}
    208
    209static int __videobuf_mmap_mapper(struct videobuf_queue *q,
    210				  struct videobuf_buffer *buf,
    211				  struct vm_area_struct *vma)
    212{
    213	struct videobuf_vmalloc_memory *mem;
    214	struct videobuf_mapping *map;
    215	int retval, pages;
    216
    217	dprintk(1, "%s\n", __func__);
    218
    219	/* create mapping + update buffer list */
    220	map = kzalloc(sizeof(struct videobuf_mapping), GFP_KERNEL);
    221	if (NULL == map)
    222		return -ENOMEM;
    223
    224	buf->map = map;
    225	map->q     = q;
    226
    227	buf->baddr = vma->vm_start;
    228
    229	mem = buf->priv;
    230	BUG_ON(!mem);
    231	MAGIC_CHECK(mem->magic, MAGIC_VMAL_MEM);
    232
    233	pages = PAGE_ALIGN(vma->vm_end - vma->vm_start);
    234	mem->vaddr = vmalloc_user(pages);
    235	if (!mem->vaddr) {
    236		printk(KERN_ERR "vmalloc (%d pages) failed\n", pages);
    237		goto error;
    238	}
    239	dprintk(1, "vmalloc is at addr %p (%d pages)\n", mem->vaddr, pages);
    240
    241	/* Try to remap memory */
    242	retval = remap_vmalloc_range(vma, mem->vaddr, 0);
    243	if (retval < 0) {
    244		printk(KERN_ERR "mmap: remap failed with error %d. ", retval);
    245		vfree(mem->vaddr);
    246		goto error;
    247	}
    248
    249	vma->vm_ops          = &videobuf_vm_ops;
    250	vma->vm_flags       |= VM_DONTEXPAND | VM_DONTDUMP;
    251	vma->vm_private_data = map;
    252
    253	dprintk(1, "mmap %p: q=%p %08lx-%08lx (%lx) pgoff %08lx buf %d\n",
    254		map, q, vma->vm_start, vma->vm_end,
    255		(long int)buf->bsize,
    256		vma->vm_pgoff, buf->i);
    257
    258	videobuf_vm_open(vma);
    259
    260	return 0;
    261
    262error:
    263	mem = NULL;
    264	kfree(map);
    265	return -ENOMEM;
    266}
    267
    268static struct videobuf_qtype_ops qops = {
    269	.magic        = MAGIC_QTYPE_OPS,
    270
    271	.alloc_vb     = __videobuf_alloc_vb,
    272	.iolock       = __videobuf_iolock,
    273	.mmap_mapper  = __videobuf_mmap_mapper,
    274	.vaddr        = videobuf_to_vmalloc,
    275};
    276
    277void videobuf_queue_vmalloc_init(struct videobuf_queue *q,
    278			 const struct videobuf_queue_ops *ops,
    279			 struct device *dev,
    280			 spinlock_t *irqlock,
    281			 enum v4l2_buf_type type,
    282			 enum v4l2_field field,
    283			 unsigned int msize,
    284			 void *priv,
    285			 struct mutex *ext_lock)
    286{
    287	videobuf_queue_core_init(q, ops, dev, irqlock, type, field, msize,
    288				 priv, &qops, ext_lock);
    289}
    290EXPORT_SYMBOL_GPL(videobuf_queue_vmalloc_init);
    291
    292void *videobuf_to_vmalloc(struct videobuf_buffer *buf)
    293{
    294	struct videobuf_vmalloc_memory *mem = buf->priv;
    295	BUG_ON(!mem);
    296	MAGIC_CHECK(mem->magic, MAGIC_VMAL_MEM);
    297
    298	return mem->vaddr;
    299}
    300EXPORT_SYMBOL_GPL(videobuf_to_vmalloc);
    301
    302void videobuf_vmalloc_free(struct videobuf_buffer *buf)
    303{
    304	struct videobuf_vmalloc_memory *mem = buf->priv;
    305
    306	/* mmapped memory can't be freed here, otherwise mmapped region
    307	   would be released, while still needed. In this case, the memory
    308	   release should happen inside videobuf_vm_close().
    309	   So, it should free memory only if the memory were allocated for
    310	   read() operation.
    311	 */
    312	if ((buf->memory != V4L2_MEMORY_USERPTR) || buf->baddr)
    313		return;
    314
    315	if (!mem)
    316		return;
    317
    318	MAGIC_CHECK(mem->magic, MAGIC_VMAL_MEM);
    319
    320	vfree(mem->vaddr);
    321	mem->vaddr = NULL;
    322
    323	return;
    324}
    325EXPORT_SYMBOL_GPL(videobuf_vmalloc_free);
    326