193 lines · c
1// SPDX-License-Identifier: GPL-2.0 OR MIT2 3/******************************************************************************4 * privcmd-buf.c5 *6 * Mmap of hypercall buffers.7 *8 * Copyright (c) 2018 Juergen Gross9 */10 11#define pr_fmt(fmt) "xen:" KBUILD_MODNAME ": " fmt12 13#include <linux/kernel.h>14#include <linux/module.h>15#include <linux/list.h>16#include <linux/miscdevice.h>17#include <linux/mm.h>18#include <linux/slab.h>19 20#include "privcmd.h"21 22MODULE_DESCRIPTION("Xen Mmap of hypercall buffers");23MODULE_LICENSE("GPL");24 25struct privcmd_buf_private {26 struct mutex lock;27 struct list_head list;28};29 30struct privcmd_buf_vma_private {31 struct privcmd_buf_private *file_priv;32 struct list_head list;33 unsigned int users;34 unsigned int n_pages;35 struct page *pages[];36};37 38static int privcmd_buf_open(struct inode *ino, struct file *file)39{40 struct privcmd_buf_private *file_priv;41 42 file_priv = kzalloc(sizeof(*file_priv), GFP_KERNEL);43 if (!file_priv)44 return -ENOMEM;45 46 mutex_init(&file_priv->lock);47 INIT_LIST_HEAD(&file_priv->list);48 49 file->private_data = file_priv;50 51 return 0;52}53 54static void privcmd_buf_vmapriv_free(struct privcmd_buf_vma_private *vma_priv)55{56 unsigned int i;57 58 list_del(&vma_priv->list);59 60 for (i = 0; i < vma_priv->n_pages; i++)61 __free_page(vma_priv->pages[i]);62 63 kfree(vma_priv);64}65 66static int privcmd_buf_release(struct inode *ino, struct file *file)67{68 struct privcmd_buf_private *file_priv = file->private_data;69 struct privcmd_buf_vma_private *vma_priv;70 71 mutex_lock(&file_priv->lock);72 73 while (!list_empty(&file_priv->list)) {74 vma_priv = list_first_entry(&file_priv->list,75 struct privcmd_buf_vma_private,76 list);77 privcmd_buf_vmapriv_free(vma_priv);78 }79 80 mutex_unlock(&file_priv->lock);81 82 kfree(file_priv);83 84 return 0;85}86 87static void privcmd_buf_vma_open(struct vm_area_struct *vma)88{89 struct privcmd_buf_vma_private *vma_priv = vma->vm_private_data;90 91 if (!vma_priv)92 return;93 94 mutex_lock(&vma_priv->file_priv->lock);95 vma_priv->users++;96 mutex_unlock(&vma_priv->file_priv->lock);97}98 99static void privcmd_buf_vma_close(struct vm_area_struct *vma)100{101 struct privcmd_buf_vma_private *vma_priv = vma->vm_private_data;102 struct privcmd_buf_private *file_priv;103 104 if (!vma_priv)105 return;106 107 file_priv = vma_priv->file_priv;108 109 mutex_lock(&file_priv->lock);110 111 vma_priv->users--;112 if (!vma_priv->users)113 privcmd_buf_vmapriv_free(vma_priv);114 115 mutex_unlock(&file_priv->lock);116}117 118static vm_fault_t privcmd_buf_vma_fault(struct vm_fault *vmf)119{120 pr_debug("fault: vma=%p %lx-%lx, pgoff=%lx, uv=%p\n",121 vmf->vma, vmf->vma->vm_start, vmf->vma->vm_end,122 vmf->pgoff, (void *)vmf->address);123 124 return VM_FAULT_SIGBUS;125}126 127static const struct vm_operations_struct privcmd_buf_vm_ops = {128 .open = privcmd_buf_vma_open,129 .close = privcmd_buf_vma_close,130 .fault = privcmd_buf_vma_fault,131};132 133static int privcmd_buf_mmap(struct file *file, struct vm_area_struct *vma)134{135 struct privcmd_buf_private *file_priv = file->private_data;136 struct privcmd_buf_vma_private *vma_priv;137 unsigned long count = vma_pages(vma);138 unsigned int i;139 int ret = 0;140 141 if (!(vma->vm_flags & VM_SHARED))142 return -EINVAL;143 144 vma_priv = kzalloc(struct_size(vma_priv, pages, count), GFP_KERNEL);145 if (!vma_priv)146 return -ENOMEM;147 148 for (i = 0; i < count; i++) {149 vma_priv->pages[i] = alloc_page(GFP_KERNEL | __GFP_ZERO);150 if (!vma_priv->pages[i])151 break;152 vma_priv->n_pages++;153 }154 155 mutex_lock(&file_priv->lock);156 157 vma_priv->file_priv = file_priv;158 vma_priv->users = 1;159 160 vm_flags_set(vma, VM_IO | VM_DONTEXPAND);161 vma->vm_ops = &privcmd_buf_vm_ops;162 vma->vm_private_data = vma_priv;163 164 list_add(&vma_priv->list, &file_priv->list);165 166 if (vma_priv->n_pages != count)167 ret = -ENOMEM;168 else169 ret = vm_map_pages_zero(vma, vma_priv->pages,170 vma_priv->n_pages);171 172 if (ret)173 privcmd_buf_vmapriv_free(vma_priv);174 175 mutex_unlock(&file_priv->lock);176 177 return ret;178}179 180const struct file_operations xen_privcmdbuf_fops = {181 .owner = THIS_MODULE,182 .open = privcmd_buf_open,183 .release = privcmd_buf_release,184 .mmap = privcmd_buf_mmap,185};186EXPORT_SYMBOL_GPL(xen_privcmdbuf_fops);187 188struct miscdevice xen_privcmdbuf_dev = {189 .minor = MISC_DYNAMIC_MINOR,190 .name = "xen/hypercall",191 .fops = &xen_privcmdbuf_fops,192};193