1 // SPDX-License-Identifier: GPL-2.0 OR MIT 2 3 /****************************************************************************** 4 * privcmd-buf.c 5 * 6 * Mmap of hypercall buffers. 7 * 8 * Copyright (c) 2018 Juergen Gross 9 */ 10 11 #define pr_fmt(fmt) "xen:" KBUILD_MODNAME ": " fmt 12 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 22 MODULE_DESCRIPTION("Xen Mmap of hypercall buffers"); 23 MODULE_LICENSE("GPL"); 24 25 struct privcmd_buf_private { 26 struct mutex lock; 27 struct list_head list; 28 }; 29 30 struct 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 38 static 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 54 static 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 66 static 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 87 static 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 99 static 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 118 static 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 127 static 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 133 static 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 else 169 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 180 const 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 }; 186 EXPORT_SYMBOL_GPL(xen_privcmdbuf_fops); 187 188 struct miscdevice xen_privcmdbuf_dev = { 189 .minor = MISC_DYNAMIC_MINOR, 190 .name = "xen/hypercall", 191 .fops = &xen_privcmdbuf_fops, 192 }; 193