123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197 |
- // SPDX-License-Identifier: GPL-2.0 OR MIT
- /******************************************************************************
- * privcmd-buf.c
- *
- * Mmap of hypercall buffers.
- *
- * Copyright (c) 2018 Juergen Gross
- */
- #define pr_fmt(fmt) "xen:" KBUILD_MODNAME ": " fmt
- #include <linux/kernel.h>
- #include <linux/module.h>
- #include <linux/list.h>
- #include <linux/miscdevice.h>
- #include <linux/mm.h>
- #include <linux/slab.h>
- #include "privcmd.h"
- MODULE_LICENSE("GPL");
- struct privcmd_buf_private {
- struct mutex lock;
- struct list_head list;
- };
- struct privcmd_buf_vma_private {
- struct privcmd_buf_private *file_priv;
- struct list_head list;
- unsigned int users;
- unsigned int n_pages;
- struct page *pages[];
- };
- static int privcmd_buf_open(struct inode *ino, struct file *file)
- {
- struct privcmd_buf_private *file_priv;
- file_priv = kzalloc(sizeof(*file_priv), GFP_KERNEL);
- if (!file_priv)
- return -ENOMEM;
- mutex_init(&file_priv->lock);
- INIT_LIST_HEAD(&file_priv->list);
- file->private_data = file_priv;
- return 0;
- }
- static void privcmd_buf_vmapriv_free(struct privcmd_buf_vma_private *vma_priv)
- {
- unsigned int i;
- list_del(&vma_priv->list);
- for (i = 0; i < vma_priv->n_pages; i++)
- __free_page(vma_priv->pages[i]);
- kfree(vma_priv);
- }
- static int privcmd_buf_release(struct inode *ino, struct file *file)
- {
- struct privcmd_buf_private *file_priv = file->private_data;
- struct privcmd_buf_vma_private *vma_priv;
- mutex_lock(&file_priv->lock);
- while (!list_empty(&file_priv->list)) {
- vma_priv = list_first_entry(&file_priv->list,
- struct privcmd_buf_vma_private,
- list);
- privcmd_buf_vmapriv_free(vma_priv);
- }
- mutex_unlock(&file_priv->lock);
- kfree(file_priv);
- return 0;
- }
- static void privcmd_buf_vma_open(struct vm_area_struct *vma)
- {
- struct privcmd_buf_vma_private *vma_priv = vma->vm_private_data;
- if (!vma_priv)
- return;
- mutex_lock(&vma_priv->file_priv->lock);
- vma_priv->users++;
- mutex_unlock(&vma_priv->file_priv->lock);
- }
- static void privcmd_buf_vma_close(struct vm_area_struct *vma)
- {
- struct privcmd_buf_vma_private *vma_priv = vma->vm_private_data;
- struct privcmd_buf_private *file_priv;
- if (!vma_priv)
- return;
- file_priv = vma_priv->file_priv;
- mutex_lock(&file_priv->lock);
- vma_priv->users--;
- if (!vma_priv->users)
- privcmd_buf_vmapriv_free(vma_priv);
- mutex_unlock(&file_priv->lock);
- }
- static vm_fault_t privcmd_buf_vma_fault(struct vm_fault *vmf)
- {
- pr_debug("fault: vma=%p %lx-%lx, pgoff=%lx, uv=%p\n",
- vmf->vma, vmf->vma->vm_start, vmf->vma->vm_end,
- vmf->pgoff, (void *)vmf->address);
- return VM_FAULT_SIGBUS;
- }
- static const struct vm_operations_struct privcmd_buf_vm_ops = {
- .open = privcmd_buf_vma_open,
- .close = privcmd_buf_vma_close,
- .fault = privcmd_buf_vma_fault,
- };
- static int privcmd_buf_mmap(struct file *file, struct vm_area_struct *vma)
- {
- struct privcmd_buf_private *file_priv = file->private_data;
- struct privcmd_buf_vma_private *vma_priv;
- unsigned long count = vma_pages(vma);
- unsigned int i;
- int ret = 0;
- if (!(vma->vm_flags & VM_SHARED))
- return -EINVAL;
- vma_priv = kzalloc(sizeof(*vma_priv) + count * sizeof(void *),
- GFP_KERNEL);
- if (!vma_priv)
- return -ENOMEM;
- for (i = 0; i < count; i++) {
- vma_priv->pages[i] = alloc_page(GFP_KERNEL | __GFP_ZERO);
- if (!vma_priv->pages[i])
- break;
- vma_priv->n_pages++;
- }
- mutex_lock(&file_priv->lock);
- vma_priv->file_priv = file_priv;
- vma_priv->users = 1;
- vma->vm_flags |= VM_IO | VM_DONTEXPAND;
- vma->vm_ops = &privcmd_buf_vm_ops;
- vma->vm_private_data = vma_priv;
- list_add(&vma_priv->list, &file_priv->list);
- if (vma_priv->n_pages != count)
- ret = -ENOMEM;
- else
- for (i = 0; i < vma_priv->n_pages; i++) {
- ret = vm_insert_page(vma, vma->vm_start + i * PAGE_SIZE,
- vma_priv->pages[i]);
- if (ret)
- break;
- }
- if (ret)
- privcmd_buf_vmapriv_free(vma_priv);
- mutex_unlock(&file_priv->lock);
- return ret;
- }
- const struct file_operations xen_privcmdbuf_fops = {
- .owner = THIS_MODULE,
- .open = privcmd_buf_open,
- .release = privcmd_buf_release,
- .mmap = privcmd_buf_mmap,
- };
- EXPORT_SYMBOL_GPL(xen_privcmdbuf_fops);
- struct miscdevice xen_privcmdbuf_dev = {
- .minor = MISC_DYNAMIC_MINOR,
- .name = "xen/hypercall",
- .fops = &xen_privcmdbuf_fops,
- };
|