Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0 OR MIT
0002 
0003 /******************************************************************************
0004  * privcmd-buf.c
0005  *
0006  * Mmap of hypercall buffers.
0007  *
0008  * Copyright (c) 2018 Juergen Gross
0009  */
0010 
0011 #define pr_fmt(fmt) "xen:" KBUILD_MODNAME ": " fmt
0012 
0013 #include <linux/kernel.h>
0014 #include <linux/module.h>
0015 #include <linux/list.h>
0016 #include <linux/miscdevice.h>
0017 #include <linux/mm.h>
0018 #include <linux/slab.h>
0019 
0020 #include "privcmd.h"
0021 
0022 MODULE_LICENSE("GPL");
0023 
0024 struct privcmd_buf_private {
0025     struct mutex lock;
0026     struct list_head list;
0027 };
0028 
0029 struct privcmd_buf_vma_private {
0030     struct privcmd_buf_private *file_priv;
0031     struct list_head list;
0032     unsigned int users;
0033     unsigned int n_pages;
0034     struct page *pages[];
0035 };
0036 
0037 static int privcmd_buf_open(struct inode *ino, struct file *file)
0038 {
0039     struct privcmd_buf_private *file_priv;
0040 
0041     file_priv = kzalloc(sizeof(*file_priv), GFP_KERNEL);
0042     if (!file_priv)
0043         return -ENOMEM;
0044 
0045     mutex_init(&file_priv->lock);
0046     INIT_LIST_HEAD(&file_priv->list);
0047 
0048     file->private_data = file_priv;
0049 
0050     return 0;
0051 }
0052 
0053 static void privcmd_buf_vmapriv_free(struct privcmd_buf_vma_private *vma_priv)
0054 {
0055     unsigned int i;
0056 
0057     list_del(&vma_priv->list);
0058 
0059     for (i = 0; i < vma_priv->n_pages; i++)
0060         __free_page(vma_priv->pages[i]);
0061 
0062     kfree(vma_priv);
0063 }
0064 
0065 static int privcmd_buf_release(struct inode *ino, struct file *file)
0066 {
0067     struct privcmd_buf_private *file_priv = file->private_data;
0068     struct privcmd_buf_vma_private *vma_priv;
0069 
0070     mutex_lock(&file_priv->lock);
0071 
0072     while (!list_empty(&file_priv->list)) {
0073         vma_priv = list_first_entry(&file_priv->list,
0074                         struct privcmd_buf_vma_private,
0075                         list);
0076         privcmd_buf_vmapriv_free(vma_priv);
0077     }
0078 
0079     mutex_unlock(&file_priv->lock);
0080 
0081     kfree(file_priv);
0082 
0083     return 0;
0084 }
0085 
0086 static void privcmd_buf_vma_open(struct vm_area_struct *vma)
0087 {
0088     struct privcmd_buf_vma_private *vma_priv = vma->vm_private_data;
0089 
0090     if (!vma_priv)
0091         return;
0092 
0093     mutex_lock(&vma_priv->file_priv->lock);
0094     vma_priv->users++;
0095     mutex_unlock(&vma_priv->file_priv->lock);
0096 }
0097 
0098 static void privcmd_buf_vma_close(struct vm_area_struct *vma)
0099 {
0100     struct privcmd_buf_vma_private *vma_priv = vma->vm_private_data;
0101     struct privcmd_buf_private *file_priv;
0102 
0103     if (!vma_priv)
0104         return;
0105 
0106     file_priv = vma_priv->file_priv;
0107 
0108     mutex_lock(&file_priv->lock);
0109 
0110     vma_priv->users--;
0111     if (!vma_priv->users)
0112         privcmd_buf_vmapriv_free(vma_priv);
0113 
0114     mutex_unlock(&file_priv->lock);
0115 }
0116 
0117 static vm_fault_t privcmd_buf_vma_fault(struct vm_fault *vmf)
0118 {
0119     pr_debug("fault: vma=%p %lx-%lx, pgoff=%lx, uv=%p\n",
0120          vmf->vma, vmf->vma->vm_start, vmf->vma->vm_end,
0121          vmf->pgoff, (void *)vmf->address);
0122 
0123     return VM_FAULT_SIGBUS;
0124 }
0125 
0126 static const struct vm_operations_struct privcmd_buf_vm_ops = {
0127     .open = privcmd_buf_vma_open,
0128     .close = privcmd_buf_vma_close,
0129     .fault = privcmd_buf_vma_fault,
0130 };
0131 
0132 static int privcmd_buf_mmap(struct file *file, struct vm_area_struct *vma)
0133 {
0134     struct privcmd_buf_private *file_priv = file->private_data;
0135     struct privcmd_buf_vma_private *vma_priv;
0136     unsigned long count = vma_pages(vma);
0137     unsigned int i;
0138     int ret = 0;
0139 
0140     if (!(vma->vm_flags & VM_SHARED))
0141         return -EINVAL;
0142 
0143     vma_priv = kzalloc(struct_size(vma_priv, pages, count), GFP_KERNEL);
0144     if (!vma_priv)
0145         return -ENOMEM;
0146 
0147     for (i = 0; i < count; i++) {
0148         vma_priv->pages[i] = alloc_page(GFP_KERNEL | __GFP_ZERO);
0149         if (!vma_priv->pages[i])
0150             break;
0151         vma_priv->n_pages++;
0152     }
0153 
0154     mutex_lock(&file_priv->lock);
0155 
0156     vma_priv->file_priv = file_priv;
0157     vma_priv->users = 1;
0158 
0159     vma->vm_flags |= VM_IO | VM_DONTEXPAND;
0160     vma->vm_ops = &privcmd_buf_vm_ops;
0161     vma->vm_private_data = vma_priv;
0162 
0163     list_add(&vma_priv->list, &file_priv->list);
0164 
0165     if (vma_priv->n_pages != count)
0166         ret = -ENOMEM;
0167     else
0168         ret = vm_map_pages_zero(vma, vma_priv->pages,
0169                         vma_priv->n_pages);
0170 
0171     if (ret)
0172         privcmd_buf_vmapriv_free(vma_priv);
0173 
0174     mutex_unlock(&file_priv->lock);
0175 
0176     return ret;
0177 }
0178 
0179 const struct file_operations xen_privcmdbuf_fops = {
0180     .owner = THIS_MODULE,
0181     .open = privcmd_buf_open,
0182     .release = privcmd_buf_release,
0183     .mmap = privcmd_buf_mmap,
0184 };
0185 EXPORT_SYMBOL_GPL(xen_privcmdbuf_fops);
0186 
0187 struct miscdevice xen_privcmdbuf_dev = {
0188     .minor = MISC_DYNAMIC_MINOR,
0189     .name = "xen/hypercall",
0190     .fops = &xen_privcmdbuf_fops,
0191 };