1d8414d3cSBastian Blank /****************************************************************************** 2d8414d3cSBastian Blank * privcmd.c 3d8414d3cSBastian Blank * 4d8414d3cSBastian Blank * Interface to privileged domain-0 commands. 5d8414d3cSBastian Blank * 6d8414d3cSBastian Blank * Copyright (c) 2002-2004, K A Fraser, B Dragovic 7d8414d3cSBastian Blank */ 8d8414d3cSBastian Blank 9283c0972SJoe Perches #define pr_fmt(fmt) "xen:" KBUILD_MODNAME ": " fmt 10283c0972SJoe Perches 11d8414d3cSBastian Blank #include <linux/kernel.h> 12d8414d3cSBastian Blank #include <linux/module.h> 13d8414d3cSBastian Blank #include <linux/sched.h> 14d8414d3cSBastian Blank #include <linux/slab.h> 15d8414d3cSBastian Blank #include <linux/string.h> 16d8414d3cSBastian Blank #include <linux/errno.h> 17d8414d3cSBastian Blank #include <linux/mm.h> 18d8414d3cSBastian Blank #include <linux/mman.h> 19d8414d3cSBastian Blank #include <linux/uaccess.h> 20d8414d3cSBastian Blank #include <linux/swap.h> 21d8414d3cSBastian Blank #include <linux/highmem.h> 22d8414d3cSBastian Blank #include <linux/pagemap.h> 23d8414d3cSBastian Blank #include <linux/seq_file.h> 24d8414d3cSBastian Blank #include <linux/miscdevice.h> 25*ab520be8SPaul Durrant #include <linux/moduleparam.h> 26d8414d3cSBastian Blank 27d8414d3cSBastian Blank #include <asm/pgalloc.h> 28d8414d3cSBastian Blank #include <asm/pgtable.h> 29d8414d3cSBastian Blank #include <asm/tlb.h> 30d8414d3cSBastian Blank #include <asm/xen/hypervisor.h> 31d8414d3cSBastian Blank #include <asm/xen/hypercall.h> 32d8414d3cSBastian Blank 33d8414d3cSBastian Blank #include <xen/xen.h> 34d8414d3cSBastian Blank #include <xen/privcmd.h> 35d8414d3cSBastian Blank #include <xen/interface/xen.h> 36*ab520be8SPaul Durrant #include <xen/interface/hvm/dm_op.h> 37d8414d3cSBastian Blank #include <xen/features.h> 38d8414d3cSBastian Blank #include <xen/page.h> 39d8414d3cSBastian Blank #include <xen/xen-ops.h> 40d71f5139SMukesh Rathor #include <xen/balloon.h> 41d8414d3cSBastian Blank 42d8414d3cSBastian Blank #include "privcmd.h" 43d8414d3cSBastian Blank 44d8414d3cSBastian Blank MODULE_LICENSE("GPL"); 45d8414d3cSBastian Blank 46d71f5139SMukesh Rathor #define PRIV_VMA_LOCKED ((void *)1) 47d71f5139SMukesh Rathor 48*ab520be8SPaul Durrant static unsigned int privcmd_dm_op_max_num = 16; 49*ab520be8SPaul Durrant module_param_named(dm_op_max_nr_bufs, privcmd_dm_op_max_num, uint, 0644); 50*ab520be8SPaul Durrant MODULE_PARM_DESC(dm_op_max_nr_bufs, 51*ab520be8SPaul Durrant "Maximum number of buffers per dm_op hypercall"); 52*ab520be8SPaul Durrant 53*ab520be8SPaul Durrant static unsigned int privcmd_dm_op_buf_max_size = 4096; 54*ab520be8SPaul Durrant module_param_named(dm_op_buf_max_size, privcmd_dm_op_buf_max_size, uint, 55*ab520be8SPaul Durrant 0644); 56*ab520be8SPaul Durrant MODULE_PARM_DESC(dm_op_buf_max_size, 57*ab520be8SPaul Durrant "Maximum size of a dm_op hypercall buffer"); 58*ab520be8SPaul Durrant 59a5deabe0SAndres Lagar-Cavilla static int privcmd_vma_range_is_mapped( 60a5deabe0SAndres Lagar-Cavilla struct vm_area_struct *vma, 61a5deabe0SAndres Lagar-Cavilla unsigned long addr, 62a5deabe0SAndres Lagar-Cavilla unsigned long nr_pages); 63d8414d3cSBastian Blank 64d8414d3cSBastian Blank static long privcmd_ioctl_hypercall(void __user *udata) 65d8414d3cSBastian Blank { 66d8414d3cSBastian Blank struct privcmd_hypercall hypercall; 67d8414d3cSBastian Blank long ret; 68d8414d3cSBastian Blank 69d8414d3cSBastian Blank if (copy_from_user(&hypercall, udata, sizeof(hypercall))) 70d8414d3cSBastian Blank return -EFAULT; 71d8414d3cSBastian Blank 72fdfd811dSDavid Vrabel xen_preemptible_hcall_begin(); 73d8414d3cSBastian Blank ret = privcmd_call(hypercall.op, 74d8414d3cSBastian Blank hypercall.arg[0], hypercall.arg[1], 75d8414d3cSBastian Blank hypercall.arg[2], hypercall.arg[3], 76d8414d3cSBastian Blank hypercall.arg[4]); 77fdfd811dSDavid Vrabel xen_preemptible_hcall_end(); 78d8414d3cSBastian Blank 79d8414d3cSBastian Blank return ret; 80d8414d3cSBastian Blank } 81d8414d3cSBastian Blank 82d8414d3cSBastian Blank static void free_page_list(struct list_head *pages) 83d8414d3cSBastian Blank { 84d8414d3cSBastian Blank struct page *p, *n; 85d8414d3cSBastian Blank 86d8414d3cSBastian Blank list_for_each_entry_safe(p, n, pages, lru) 87d8414d3cSBastian Blank __free_page(p); 88d8414d3cSBastian Blank 89d8414d3cSBastian Blank INIT_LIST_HEAD(pages); 90d8414d3cSBastian Blank } 91d8414d3cSBastian Blank 92d8414d3cSBastian Blank /* 93d8414d3cSBastian Blank * Given an array of items in userspace, return a list of pages 94d8414d3cSBastian Blank * containing the data. If copying fails, either because of memory 95d8414d3cSBastian Blank * allocation failure or a problem reading user memory, return an 96d8414d3cSBastian Blank * error code; its up to the caller to dispose of any partial list. 97d8414d3cSBastian Blank */ 98d8414d3cSBastian Blank static int gather_array(struct list_head *pagelist, 99d8414d3cSBastian Blank unsigned nelem, size_t size, 100ceb90fa0SAndres Lagar-Cavilla const void __user *data) 101d8414d3cSBastian Blank { 102d8414d3cSBastian Blank unsigned pageidx; 103d8414d3cSBastian Blank void *pagedata; 104d8414d3cSBastian Blank int ret; 105d8414d3cSBastian Blank 106d8414d3cSBastian Blank if (size > PAGE_SIZE) 107d8414d3cSBastian Blank return 0; 108d8414d3cSBastian Blank 109d8414d3cSBastian Blank pageidx = PAGE_SIZE; 110d8414d3cSBastian Blank pagedata = NULL; /* quiet, gcc */ 111d8414d3cSBastian Blank while (nelem--) { 112d8414d3cSBastian Blank if (pageidx > PAGE_SIZE-size) { 113d8414d3cSBastian Blank struct page *page = alloc_page(GFP_KERNEL); 114d8414d3cSBastian Blank 115d8414d3cSBastian Blank ret = -ENOMEM; 116d8414d3cSBastian Blank if (page == NULL) 117d8414d3cSBastian Blank goto fail; 118d8414d3cSBastian Blank 119d8414d3cSBastian Blank pagedata = page_address(page); 120d8414d3cSBastian Blank 121d8414d3cSBastian Blank list_add_tail(&page->lru, pagelist); 122d8414d3cSBastian Blank pageidx = 0; 123d8414d3cSBastian Blank } 124d8414d3cSBastian Blank 125d8414d3cSBastian Blank ret = -EFAULT; 126d8414d3cSBastian Blank if (copy_from_user(pagedata + pageidx, data, size)) 127d8414d3cSBastian Blank goto fail; 128d8414d3cSBastian Blank 129d8414d3cSBastian Blank data += size; 130d8414d3cSBastian Blank pageidx += size; 131d8414d3cSBastian Blank } 132d8414d3cSBastian Blank 133d8414d3cSBastian Blank ret = 0; 134d8414d3cSBastian Blank 135d8414d3cSBastian Blank fail: 136d8414d3cSBastian Blank return ret; 137d8414d3cSBastian Blank } 138d8414d3cSBastian Blank 139d8414d3cSBastian Blank /* 140d8414d3cSBastian Blank * Call function "fn" on each element of the array fragmented 141d8414d3cSBastian Blank * over a list of pages. 142d8414d3cSBastian Blank */ 143d8414d3cSBastian Blank static int traverse_pages(unsigned nelem, size_t size, 144d8414d3cSBastian Blank struct list_head *pos, 145d8414d3cSBastian Blank int (*fn)(void *data, void *state), 146d8414d3cSBastian Blank void *state) 147d8414d3cSBastian Blank { 148d8414d3cSBastian Blank void *pagedata; 149d8414d3cSBastian Blank unsigned pageidx; 150d8414d3cSBastian Blank int ret = 0; 151d8414d3cSBastian Blank 152d8414d3cSBastian Blank BUG_ON(size > PAGE_SIZE); 153d8414d3cSBastian Blank 154d8414d3cSBastian Blank pageidx = PAGE_SIZE; 155d8414d3cSBastian Blank pagedata = NULL; /* hush, gcc */ 156d8414d3cSBastian Blank 157d8414d3cSBastian Blank while (nelem--) { 158d8414d3cSBastian Blank if (pageidx > PAGE_SIZE-size) { 159d8414d3cSBastian Blank struct page *page; 160d8414d3cSBastian Blank pos = pos->next; 161d8414d3cSBastian Blank page = list_entry(pos, struct page, lru); 162d8414d3cSBastian Blank pagedata = page_address(page); 163d8414d3cSBastian Blank pageidx = 0; 164d8414d3cSBastian Blank } 165d8414d3cSBastian Blank 166d8414d3cSBastian Blank ret = (*fn)(pagedata + pageidx, state); 167d8414d3cSBastian Blank if (ret) 168d8414d3cSBastian Blank break; 169d8414d3cSBastian Blank pageidx += size; 170d8414d3cSBastian Blank } 171d8414d3cSBastian Blank 172d8414d3cSBastian Blank return ret; 173d8414d3cSBastian Blank } 174d8414d3cSBastian Blank 1754e8c0c8cSDavid Vrabel /* 1764e8c0c8cSDavid Vrabel * Similar to traverse_pages, but use each page as a "block" of 1774e8c0c8cSDavid Vrabel * data to be processed as one unit. 1784e8c0c8cSDavid Vrabel */ 1794e8c0c8cSDavid Vrabel static int traverse_pages_block(unsigned nelem, size_t size, 1804e8c0c8cSDavid Vrabel struct list_head *pos, 1814e8c0c8cSDavid Vrabel int (*fn)(void *data, int nr, void *state), 1824e8c0c8cSDavid Vrabel void *state) 1834e8c0c8cSDavid Vrabel { 1844e8c0c8cSDavid Vrabel void *pagedata; 1854e8c0c8cSDavid Vrabel unsigned pageidx; 1864e8c0c8cSDavid Vrabel int ret = 0; 1874e8c0c8cSDavid Vrabel 1884e8c0c8cSDavid Vrabel BUG_ON(size > PAGE_SIZE); 1894e8c0c8cSDavid Vrabel 1904e8c0c8cSDavid Vrabel pageidx = PAGE_SIZE; 1914e8c0c8cSDavid Vrabel 1924e8c0c8cSDavid Vrabel while (nelem) { 1934e8c0c8cSDavid Vrabel int nr = (PAGE_SIZE/size); 1944e8c0c8cSDavid Vrabel struct page *page; 1954e8c0c8cSDavid Vrabel if (nr > nelem) 1964e8c0c8cSDavid Vrabel nr = nelem; 1974e8c0c8cSDavid Vrabel pos = pos->next; 1984e8c0c8cSDavid Vrabel page = list_entry(pos, struct page, lru); 1994e8c0c8cSDavid Vrabel pagedata = page_address(page); 2004e8c0c8cSDavid Vrabel ret = (*fn)(pagedata, nr, state); 2014e8c0c8cSDavid Vrabel if (ret) 2024e8c0c8cSDavid Vrabel break; 2034e8c0c8cSDavid Vrabel nelem -= nr; 2044e8c0c8cSDavid Vrabel } 2054e8c0c8cSDavid Vrabel 2064e8c0c8cSDavid Vrabel return ret; 2074e8c0c8cSDavid Vrabel } 2084e8c0c8cSDavid Vrabel 209a13d7201SJulien Grall struct mmap_gfn_state { 210d8414d3cSBastian Blank unsigned long va; 211d8414d3cSBastian Blank struct vm_area_struct *vma; 212d8414d3cSBastian Blank domid_t domain; 213d8414d3cSBastian Blank }; 214d8414d3cSBastian Blank 215a13d7201SJulien Grall static int mmap_gfn_range(void *data, void *state) 216d8414d3cSBastian Blank { 217d8414d3cSBastian Blank struct privcmd_mmap_entry *msg = data; 218a13d7201SJulien Grall struct mmap_gfn_state *st = state; 219d8414d3cSBastian Blank struct vm_area_struct *vma = st->vma; 220d8414d3cSBastian Blank int rc; 221d8414d3cSBastian Blank 222d8414d3cSBastian Blank /* Do not allow range to wrap the address space. */ 223d8414d3cSBastian Blank if ((msg->npages > (LONG_MAX >> PAGE_SHIFT)) || 224d8414d3cSBastian Blank ((unsigned long)(msg->npages << PAGE_SHIFT) >= -st->va)) 225d8414d3cSBastian Blank return -EINVAL; 226d8414d3cSBastian Blank 227d8414d3cSBastian Blank /* Range chunks must be contiguous in va space. */ 228d8414d3cSBastian Blank if ((msg->va != st->va) || 229d8414d3cSBastian Blank ((msg->va+(msg->npages<<PAGE_SHIFT)) > vma->vm_end)) 230d8414d3cSBastian Blank return -EINVAL; 231d8414d3cSBastian Blank 232a13d7201SJulien Grall rc = xen_remap_domain_gfn_range(vma, 233d8414d3cSBastian Blank msg->va & PAGE_MASK, 234d8414d3cSBastian Blank msg->mfn, msg->npages, 235d8414d3cSBastian Blank vma->vm_page_prot, 2369a032e39SIan Campbell st->domain, NULL); 237d8414d3cSBastian Blank if (rc < 0) 238d8414d3cSBastian Blank return rc; 239d8414d3cSBastian Blank 240d8414d3cSBastian Blank st->va += msg->npages << PAGE_SHIFT; 241d8414d3cSBastian Blank 242d8414d3cSBastian Blank return 0; 243d8414d3cSBastian Blank } 244d8414d3cSBastian Blank 245d8414d3cSBastian Blank static long privcmd_ioctl_mmap(void __user *udata) 246d8414d3cSBastian Blank { 247d8414d3cSBastian Blank struct privcmd_mmap mmapcmd; 248d8414d3cSBastian Blank struct mm_struct *mm = current->mm; 249d8414d3cSBastian Blank struct vm_area_struct *vma; 250d8414d3cSBastian Blank int rc; 251d8414d3cSBastian Blank LIST_HEAD(pagelist); 252a13d7201SJulien Grall struct mmap_gfn_state state; 253d8414d3cSBastian Blank 254d71f5139SMukesh Rathor /* We only support privcmd_ioctl_mmap_batch for auto translated. */ 255d71f5139SMukesh Rathor if (xen_feature(XENFEAT_auto_translated_physmap)) 256d71f5139SMukesh Rathor return -ENOSYS; 257d71f5139SMukesh Rathor 258d8414d3cSBastian Blank if (copy_from_user(&mmapcmd, udata, sizeof(mmapcmd))) 259d8414d3cSBastian Blank return -EFAULT; 260d8414d3cSBastian Blank 261d8414d3cSBastian Blank rc = gather_array(&pagelist, 262d8414d3cSBastian Blank mmapcmd.num, sizeof(struct privcmd_mmap_entry), 263d8414d3cSBastian Blank mmapcmd.entry); 264d8414d3cSBastian Blank 265d8414d3cSBastian Blank if (rc || list_empty(&pagelist)) 266d8414d3cSBastian Blank goto out; 267d8414d3cSBastian Blank 268d8414d3cSBastian Blank down_write(&mm->mmap_sem); 269d8414d3cSBastian Blank 270d8414d3cSBastian Blank { 271d8414d3cSBastian Blank struct page *page = list_first_entry(&pagelist, 272d8414d3cSBastian Blank struct page, lru); 273d8414d3cSBastian Blank struct privcmd_mmap_entry *msg = page_address(page); 274d8414d3cSBastian Blank 275d8414d3cSBastian Blank vma = find_vma(mm, msg->va); 276d8414d3cSBastian Blank rc = -EINVAL; 277d8414d3cSBastian Blank 278a5deabe0SAndres Lagar-Cavilla if (!vma || (msg->va != vma->vm_start) || vma->vm_private_data) 279d8414d3cSBastian Blank goto out_up; 280a5deabe0SAndres Lagar-Cavilla vma->vm_private_data = PRIV_VMA_LOCKED; 281d8414d3cSBastian Blank } 282d8414d3cSBastian Blank 283d8414d3cSBastian Blank state.va = vma->vm_start; 284d8414d3cSBastian Blank state.vma = vma; 285d8414d3cSBastian Blank state.domain = mmapcmd.dom; 286d8414d3cSBastian Blank 287d8414d3cSBastian Blank rc = traverse_pages(mmapcmd.num, sizeof(struct privcmd_mmap_entry), 288d8414d3cSBastian Blank &pagelist, 289a13d7201SJulien Grall mmap_gfn_range, &state); 290d8414d3cSBastian Blank 291d8414d3cSBastian Blank 292d8414d3cSBastian Blank out_up: 293d8414d3cSBastian Blank up_write(&mm->mmap_sem); 294d8414d3cSBastian Blank 295d8414d3cSBastian Blank out: 296d8414d3cSBastian Blank free_page_list(&pagelist); 297d8414d3cSBastian Blank 298d8414d3cSBastian Blank return rc; 299d8414d3cSBastian Blank } 300d8414d3cSBastian Blank 301d8414d3cSBastian Blank struct mmap_batch_state { 302d8414d3cSBastian Blank domid_t domain; 303d8414d3cSBastian Blank unsigned long va; 304d8414d3cSBastian Blank struct vm_area_struct *vma; 305d71f5139SMukesh Rathor int index; 306ceb90fa0SAndres Lagar-Cavilla /* A tristate: 307ceb90fa0SAndres Lagar-Cavilla * 0 for no errors 308ceb90fa0SAndres Lagar-Cavilla * 1 if at least one error has happened (and no 309ceb90fa0SAndres Lagar-Cavilla * -ENOENT errors have happened) 310ceb90fa0SAndres Lagar-Cavilla * -ENOENT if at least 1 -ENOENT has happened. 311ceb90fa0SAndres Lagar-Cavilla */ 312ceb90fa0SAndres Lagar-Cavilla int global_error; 31399beae6cSAndres Lagar-Cavilla int version; 314d8414d3cSBastian Blank 315a13d7201SJulien Grall /* User-space gfn array to store errors in the second pass for V1. */ 316a13d7201SJulien Grall xen_pfn_t __user *user_gfn; 31799beae6cSAndres Lagar-Cavilla /* User-space int array to store errors in the second pass for V2. */ 31899beae6cSAndres Lagar-Cavilla int __user *user_err; 319d8414d3cSBastian Blank }; 320d8414d3cSBastian Blank 321a13d7201SJulien Grall /* auto translated dom0 note: if domU being created is PV, then gfn is 322a13d7201SJulien Grall * mfn(addr on bus). If it's auto xlated, then gfn is pfn (input to HAP). 323d71f5139SMukesh Rathor */ 3244e8c0c8cSDavid Vrabel static int mmap_batch_fn(void *data, int nr, void *state) 325d8414d3cSBastian Blank { 326a13d7201SJulien Grall xen_pfn_t *gfnp = data; 327d8414d3cSBastian Blank struct mmap_batch_state *st = state; 328d71f5139SMukesh Rathor struct vm_area_struct *vma = st->vma; 329d71f5139SMukesh Rathor struct page **pages = vma->vm_private_data; 3304e8c0c8cSDavid Vrabel struct page **cur_pages = NULL; 331ceb90fa0SAndres Lagar-Cavilla int ret; 332d8414d3cSBastian Blank 333d71f5139SMukesh Rathor if (xen_feature(XENFEAT_auto_translated_physmap)) 3344e8c0c8cSDavid Vrabel cur_pages = &pages[st->index]; 335d71f5139SMukesh Rathor 3364e8c0c8cSDavid Vrabel BUG_ON(nr < 0); 337a13d7201SJulien Grall ret = xen_remap_domain_gfn_array(st->vma, st->va & PAGE_MASK, gfnp, nr, 338a13d7201SJulien Grall (int *)gfnp, st->vma->vm_page_prot, 3394e8c0c8cSDavid Vrabel st->domain, cur_pages); 340ceb90fa0SAndres Lagar-Cavilla 3414e8c0c8cSDavid Vrabel /* Adjust the global_error? */ 3424e8c0c8cSDavid Vrabel if (ret != nr) { 343ceb90fa0SAndres Lagar-Cavilla if (ret == -ENOENT) 344ceb90fa0SAndres Lagar-Cavilla st->global_error = -ENOENT; 345ceb90fa0SAndres Lagar-Cavilla else { 346ceb90fa0SAndres Lagar-Cavilla /* Record that at least one error has happened. */ 347ceb90fa0SAndres Lagar-Cavilla if (st->global_error == 0) 348ceb90fa0SAndres Lagar-Cavilla st->global_error = 1; 349ceb90fa0SAndres Lagar-Cavilla } 350d8414d3cSBastian Blank } 3514e8c0c8cSDavid Vrabel st->va += PAGE_SIZE * nr; 3524e8c0c8cSDavid Vrabel st->index += nr; 353d8414d3cSBastian Blank 354d8414d3cSBastian Blank return 0; 355d8414d3cSBastian Blank } 356d8414d3cSBastian Blank 3574e8c0c8cSDavid Vrabel static int mmap_return_error(int err, struct mmap_batch_state *st) 358d8414d3cSBastian Blank { 3594e8c0c8cSDavid Vrabel int ret; 360d8414d3cSBastian Blank 36199beae6cSAndres Lagar-Cavilla if (st->version == 1) { 3624e8c0c8cSDavid Vrabel if (err) { 363a13d7201SJulien Grall xen_pfn_t gfn; 3644e8c0c8cSDavid Vrabel 365a13d7201SJulien Grall ret = get_user(gfn, st->user_gfn); 3664e8c0c8cSDavid Vrabel if (ret < 0) 3674e8c0c8cSDavid Vrabel return ret; 3684e8c0c8cSDavid Vrabel /* 3694e8c0c8cSDavid Vrabel * V1 encodes the error codes in the 32bit top 370a13d7201SJulien Grall * nibble of the gfn (with its known 3714e8c0c8cSDavid Vrabel * limitations vis-a-vis 64 bit callers). 3724e8c0c8cSDavid Vrabel */ 373a13d7201SJulien Grall gfn |= (err == -ENOENT) ? 3744e8c0c8cSDavid Vrabel PRIVCMD_MMAPBATCH_PAGED_ERROR : 3754e8c0c8cSDavid Vrabel PRIVCMD_MMAPBATCH_MFN_ERROR; 376a13d7201SJulien Grall return __put_user(gfn, st->user_gfn++); 3774e8c0c8cSDavid Vrabel } else 378a13d7201SJulien Grall st->user_gfn++; 37999beae6cSAndres Lagar-Cavilla } else { /* st->version == 2 */ 38099beae6cSAndres Lagar-Cavilla if (err) 38199beae6cSAndres Lagar-Cavilla return __put_user(err, st->user_err++); 38299beae6cSAndres Lagar-Cavilla else 38399beae6cSAndres Lagar-Cavilla st->user_err++; 38499beae6cSAndres Lagar-Cavilla } 38599beae6cSAndres Lagar-Cavilla 38699beae6cSAndres Lagar-Cavilla return 0; 387d8414d3cSBastian Blank } 388d8414d3cSBastian Blank 3894e8c0c8cSDavid Vrabel static int mmap_return_errors(void *data, int nr, void *state) 3904e8c0c8cSDavid Vrabel { 3914e8c0c8cSDavid Vrabel struct mmap_batch_state *st = state; 3924e8c0c8cSDavid Vrabel int *errs = data; 3934e8c0c8cSDavid Vrabel int i; 3944e8c0c8cSDavid Vrabel int ret; 3954e8c0c8cSDavid Vrabel 3964e8c0c8cSDavid Vrabel for (i = 0; i < nr; i++) { 3974e8c0c8cSDavid Vrabel ret = mmap_return_error(errs[i], st); 3984e8c0c8cSDavid Vrabel if (ret < 0) 3994e8c0c8cSDavid Vrabel return ret; 4004e8c0c8cSDavid Vrabel } 4014e8c0c8cSDavid Vrabel return 0; 4024e8c0c8cSDavid Vrabel } 4034e8c0c8cSDavid Vrabel 404a13d7201SJulien Grall /* Allocate pfns that are then mapped with gfns from foreign domid. Update 405d71f5139SMukesh Rathor * the vma with the page info to use later. 406d71f5139SMukesh Rathor * Returns: 0 if success, otherwise -errno 407d71f5139SMukesh Rathor */ 408d71f5139SMukesh Rathor static int alloc_empty_pages(struct vm_area_struct *vma, int numpgs) 409d71f5139SMukesh Rathor { 410d71f5139SMukesh Rathor int rc; 411d71f5139SMukesh Rathor struct page **pages; 412d71f5139SMukesh Rathor 413d71f5139SMukesh Rathor pages = kcalloc(numpgs, sizeof(pages[0]), GFP_KERNEL); 414d71f5139SMukesh Rathor if (pages == NULL) 415d71f5139SMukesh Rathor return -ENOMEM; 416d71f5139SMukesh Rathor 41781b286e0SDavid Vrabel rc = alloc_xenballooned_pages(numpgs, pages); 418d71f5139SMukesh Rathor if (rc != 0) { 419d71f5139SMukesh Rathor pr_warn("%s Could not alloc %d pfns rc:%d\n", __func__, 420d71f5139SMukesh Rathor numpgs, rc); 421d71f5139SMukesh Rathor kfree(pages); 422d71f5139SMukesh Rathor return -ENOMEM; 423d71f5139SMukesh Rathor } 424a5deabe0SAndres Lagar-Cavilla BUG_ON(vma->vm_private_data != NULL); 425d71f5139SMukesh Rathor vma->vm_private_data = pages; 426d71f5139SMukesh Rathor 427d71f5139SMukesh Rathor return 0; 428d71f5139SMukesh Rathor } 429d71f5139SMukesh Rathor 4307cbea8dcSKirill A. Shutemov static const struct vm_operations_struct privcmd_vm_ops; 431d8414d3cSBastian Blank 432ceb90fa0SAndres Lagar-Cavilla static long privcmd_ioctl_mmap_batch(void __user *udata, int version) 433d8414d3cSBastian Blank { 434d8414d3cSBastian Blank int ret; 435ceb90fa0SAndres Lagar-Cavilla struct privcmd_mmapbatch_v2 m; 436d8414d3cSBastian Blank struct mm_struct *mm = current->mm; 437d8414d3cSBastian Blank struct vm_area_struct *vma; 438d8414d3cSBastian Blank unsigned long nr_pages; 439d8414d3cSBastian Blank LIST_HEAD(pagelist); 440d8414d3cSBastian Blank struct mmap_batch_state state; 441d8414d3cSBastian Blank 442ceb90fa0SAndres Lagar-Cavilla switch (version) { 443ceb90fa0SAndres Lagar-Cavilla case 1: 444ceb90fa0SAndres Lagar-Cavilla if (copy_from_user(&m, udata, sizeof(struct privcmd_mmapbatch))) 445d8414d3cSBastian Blank return -EFAULT; 446ceb90fa0SAndres Lagar-Cavilla /* Returns per-frame error in m.arr. */ 447ceb90fa0SAndres Lagar-Cavilla m.err = NULL; 448ceb90fa0SAndres Lagar-Cavilla if (!access_ok(VERIFY_WRITE, m.arr, m.num * sizeof(*m.arr))) 449ceb90fa0SAndres Lagar-Cavilla return -EFAULT; 450ceb90fa0SAndres Lagar-Cavilla break; 451ceb90fa0SAndres Lagar-Cavilla case 2: 452ceb90fa0SAndres Lagar-Cavilla if (copy_from_user(&m, udata, sizeof(struct privcmd_mmapbatch_v2))) 453ceb90fa0SAndres Lagar-Cavilla return -EFAULT; 454ceb90fa0SAndres Lagar-Cavilla /* Returns per-frame error code in m.err. */ 455ceb90fa0SAndres Lagar-Cavilla if (!access_ok(VERIFY_WRITE, m.err, m.num * (sizeof(*m.err)))) 456ceb90fa0SAndres Lagar-Cavilla return -EFAULT; 457ceb90fa0SAndres Lagar-Cavilla break; 458ceb90fa0SAndres Lagar-Cavilla default: 459ceb90fa0SAndres Lagar-Cavilla return -EINVAL; 460ceb90fa0SAndres Lagar-Cavilla } 461d8414d3cSBastian Blank 4625995a68aSJulien Grall nr_pages = DIV_ROUND_UP(m.num, XEN_PFN_PER_PAGE); 463d8414d3cSBastian Blank if ((m.num <= 0) || (nr_pages > (LONG_MAX >> PAGE_SHIFT))) 464d8414d3cSBastian Blank return -EINVAL; 465d8414d3cSBastian Blank 466ceb90fa0SAndres Lagar-Cavilla ret = gather_array(&pagelist, m.num, sizeof(xen_pfn_t), m.arr); 467d8414d3cSBastian Blank 468ceb90fa0SAndres Lagar-Cavilla if (ret) 469d8414d3cSBastian Blank goto out; 470ceb90fa0SAndres Lagar-Cavilla if (list_empty(&pagelist)) { 471ceb90fa0SAndres Lagar-Cavilla ret = -EINVAL; 472ceb90fa0SAndres Lagar-Cavilla goto out; 473ceb90fa0SAndres Lagar-Cavilla } 474ceb90fa0SAndres Lagar-Cavilla 47599beae6cSAndres Lagar-Cavilla if (version == 2) { 47699beae6cSAndres Lagar-Cavilla /* Zero error array now to only copy back actual errors. */ 47799beae6cSAndres Lagar-Cavilla if (clear_user(m.err, sizeof(int) * m.num)) { 47899beae6cSAndres Lagar-Cavilla ret = -EFAULT; 479ceb90fa0SAndres Lagar-Cavilla goto out; 480ceb90fa0SAndres Lagar-Cavilla } 48199beae6cSAndres Lagar-Cavilla } 482d8414d3cSBastian Blank 483d8414d3cSBastian Blank down_write(&mm->mmap_sem); 484d8414d3cSBastian Blank 485d8414d3cSBastian Blank vma = find_vma(mm, m.addr); 486d8414d3cSBastian Blank if (!vma || 487a5deabe0SAndres Lagar-Cavilla vma->vm_ops != &privcmd_vm_ops) { 48868fa965dSMats Petersson ret = -EINVAL; 489a5deabe0SAndres Lagar-Cavilla goto out_unlock; 490a5deabe0SAndres Lagar-Cavilla } 491a5deabe0SAndres Lagar-Cavilla 492a5deabe0SAndres Lagar-Cavilla /* 493a5deabe0SAndres Lagar-Cavilla * Caller must either: 494a5deabe0SAndres Lagar-Cavilla * 495a5deabe0SAndres Lagar-Cavilla * Map the whole VMA range, which will also allocate all the 496a5deabe0SAndres Lagar-Cavilla * pages required for the auto_translated_physmap case. 497a5deabe0SAndres Lagar-Cavilla * 498a5deabe0SAndres Lagar-Cavilla * Or 499a5deabe0SAndres Lagar-Cavilla * 500a5deabe0SAndres Lagar-Cavilla * Map unmapped holes left from a previous map attempt (e.g., 501a5deabe0SAndres Lagar-Cavilla * because those foreign frames were previously paged out). 502a5deabe0SAndres Lagar-Cavilla */ 503a5deabe0SAndres Lagar-Cavilla if (vma->vm_private_data == NULL) { 504a5deabe0SAndres Lagar-Cavilla if (m.addr != vma->vm_start || 505a5deabe0SAndres Lagar-Cavilla m.addr + (nr_pages << PAGE_SHIFT) != vma->vm_end) { 506a5deabe0SAndres Lagar-Cavilla ret = -EINVAL; 507a5deabe0SAndres Lagar-Cavilla goto out_unlock; 508d8414d3cSBastian Blank } 509d71f5139SMukesh Rathor if (xen_feature(XENFEAT_auto_translated_physmap)) { 5105995a68aSJulien Grall ret = alloc_empty_pages(vma, nr_pages); 511a5deabe0SAndres Lagar-Cavilla if (ret < 0) 512a5deabe0SAndres Lagar-Cavilla goto out_unlock; 513a5deabe0SAndres Lagar-Cavilla } else 514a5deabe0SAndres Lagar-Cavilla vma->vm_private_data = PRIV_VMA_LOCKED; 515a5deabe0SAndres Lagar-Cavilla } else { 516a5deabe0SAndres Lagar-Cavilla if (m.addr < vma->vm_start || 517a5deabe0SAndres Lagar-Cavilla m.addr + (nr_pages << PAGE_SHIFT) > vma->vm_end) { 518a5deabe0SAndres Lagar-Cavilla ret = -EINVAL; 519a5deabe0SAndres Lagar-Cavilla goto out_unlock; 520a5deabe0SAndres Lagar-Cavilla } 521a5deabe0SAndres Lagar-Cavilla if (privcmd_vma_range_is_mapped(vma, m.addr, nr_pages)) { 522a5deabe0SAndres Lagar-Cavilla ret = -EINVAL; 523a5deabe0SAndres Lagar-Cavilla goto out_unlock; 524d71f5139SMukesh Rathor } 525d71f5139SMukesh Rathor } 526d8414d3cSBastian Blank 527d8414d3cSBastian Blank state.domain = m.dom; 528d8414d3cSBastian Blank state.vma = vma; 529d8414d3cSBastian Blank state.va = m.addr; 530d71f5139SMukesh Rathor state.index = 0; 531ceb90fa0SAndres Lagar-Cavilla state.global_error = 0; 53299beae6cSAndres Lagar-Cavilla state.version = version; 533d8414d3cSBastian Blank 5345995a68aSJulien Grall BUILD_BUG_ON(((PAGE_SIZE / sizeof(xen_pfn_t)) % XEN_PFN_PER_PAGE) != 0); 535ceb90fa0SAndres Lagar-Cavilla /* mmap_batch_fn guarantees ret == 0 */ 5364e8c0c8cSDavid Vrabel BUG_ON(traverse_pages_block(m.num, sizeof(xen_pfn_t), 537ceb90fa0SAndres Lagar-Cavilla &pagelist, mmap_batch_fn, &state)); 538d8414d3cSBastian Blank 539d8414d3cSBastian Blank up_write(&mm->mmap_sem); 540d8414d3cSBastian Blank 54168fa965dSMats Petersson if (state.global_error) { 542ceb90fa0SAndres Lagar-Cavilla /* Write back errors in second pass. */ 543a13d7201SJulien Grall state.user_gfn = (xen_pfn_t *)m.arr; 54499beae6cSAndres Lagar-Cavilla state.user_err = m.err; 5454e8c0c8cSDavid Vrabel ret = traverse_pages_block(m.num, sizeof(xen_pfn_t), 54699beae6cSAndres Lagar-Cavilla &pagelist, mmap_return_errors, &state); 54768fa965dSMats Petersson } else 54868fa965dSMats Petersson ret = 0; 54968fa965dSMats Petersson 550ceb90fa0SAndres Lagar-Cavilla /* If we have not had any EFAULT-like global errors then set the global 551ceb90fa0SAndres Lagar-Cavilla * error to -ENOENT if necessary. */ 552ceb90fa0SAndres Lagar-Cavilla if ((ret == 0) && (state.global_error == -ENOENT)) 553ceb90fa0SAndres Lagar-Cavilla ret = -ENOENT; 554d8414d3cSBastian Blank 555d8414d3cSBastian Blank out: 556d8414d3cSBastian Blank free_page_list(&pagelist); 557d8414d3cSBastian Blank return ret; 558a5deabe0SAndres Lagar-Cavilla 559a5deabe0SAndres Lagar-Cavilla out_unlock: 560a5deabe0SAndres Lagar-Cavilla up_write(&mm->mmap_sem); 561a5deabe0SAndres Lagar-Cavilla goto out; 562d8414d3cSBastian Blank } 563d8414d3cSBastian Blank 564*ab520be8SPaul Durrant static int lock_pages( 565*ab520be8SPaul Durrant struct privcmd_dm_op_buf kbufs[], unsigned int num, 566*ab520be8SPaul Durrant struct page *pages[], unsigned int nr_pages) 567*ab520be8SPaul Durrant { 568*ab520be8SPaul Durrant unsigned int i; 569*ab520be8SPaul Durrant 570*ab520be8SPaul Durrant for (i = 0; i < num; i++) { 571*ab520be8SPaul Durrant unsigned int requested; 572*ab520be8SPaul Durrant int pinned; 573*ab520be8SPaul Durrant 574*ab520be8SPaul Durrant requested = DIV_ROUND_UP( 575*ab520be8SPaul Durrant offset_in_page(kbufs[i].uptr) + kbufs[i].size, 576*ab520be8SPaul Durrant PAGE_SIZE); 577*ab520be8SPaul Durrant if (requested > nr_pages) 578*ab520be8SPaul Durrant return -ENOSPC; 579*ab520be8SPaul Durrant 580*ab520be8SPaul Durrant pinned = get_user_pages_fast( 581*ab520be8SPaul Durrant (unsigned long) kbufs[i].uptr, 582*ab520be8SPaul Durrant requested, FOLL_WRITE, pages); 583*ab520be8SPaul Durrant if (pinned < 0) 584*ab520be8SPaul Durrant return pinned; 585*ab520be8SPaul Durrant 586*ab520be8SPaul Durrant nr_pages -= pinned; 587*ab520be8SPaul Durrant pages += pinned; 588*ab520be8SPaul Durrant } 589*ab520be8SPaul Durrant 590*ab520be8SPaul Durrant return 0; 591*ab520be8SPaul Durrant } 592*ab520be8SPaul Durrant 593*ab520be8SPaul Durrant static void unlock_pages(struct page *pages[], unsigned int nr_pages) 594*ab520be8SPaul Durrant { 595*ab520be8SPaul Durrant unsigned int i; 596*ab520be8SPaul Durrant 597*ab520be8SPaul Durrant if (!pages) 598*ab520be8SPaul Durrant return; 599*ab520be8SPaul Durrant 600*ab520be8SPaul Durrant for (i = 0; i < nr_pages; i++) { 601*ab520be8SPaul Durrant if (pages[i]) 602*ab520be8SPaul Durrant put_page(pages[i]); 603*ab520be8SPaul Durrant } 604*ab520be8SPaul Durrant } 605*ab520be8SPaul Durrant 606*ab520be8SPaul Durrant static long privcmd_ioctl_dm_op(void __user *udata) 607*ab520be8SPaul Durrant { 608*ab520be8SPaul Durrant struct privcmd_dm_op kdata; 609*ab520be8SPaul Durrant struct privcmd_dm_op_buf *kbufs; 610*ab520be8SPaul Durrant unsigned int nr_pages = 0; 611*ab520be8SPaul Durrant struct page **pages = NULL; 612*ab520be8SPaul Durrant struct xen_dm_op_buf *xbufs = NULL; 613*ab520be8SPaul Durrant unsigned int i; 614*ab520be8SPaul Durrant long rc; 615*ab520be8SPaul Durrant 616*ab520be8SPaul Durrant if (copy_from_user(&kdata, udata, sizeof(kdata))) 617*ab520be8SPaul Durrant return -EFAULT; 618*ab520be8SPaul Durrant 619*ab520be8SPaul Durrant if (kdata.num == 0) 620*ab520be8SPaul Durrant return 0; 621*ab520be8SPaul Durrant 622*ab520be8SPaul Durrant if (kdata.num > privcmd_dm_op_max_num) 623*ab520be8SPaul Durrant return -E2BIG; 624*ab520be8SPaul Durrant 625*ab520be8SPaul Durrant kbufs = kcalloc(kdata.num, sizeof(*kbufs), GFP_KERNEL); 626*ab520be8SPaul Durrant if (!kbufs) 627*ab520be8SPaul Durrant return -ENOMEM; 628*ab520be8SPaul Durrant 629*ab520be8SPaul Durrant if (copy_from_user(kbufs, kdata.ubufs, 630*ab520be8SPaul Durrant sizeof(*kbufs) * kdata.num)) { 631*ab520be8SPaul Durrant rc = -EFAULT; 632*ab520be8SPaul Durrant goto out; 633*ab520be8SPaul Durrant } 634*ab520be8SPaul Durrant 635*ab520be8SPaul Durrant for (i = 0; i < kdata.num; i++) { 636*ab520be8SPaul Durrant if (kbufs[i].size > privcmd_dm_op_buf_max_size) { 637*ab520be8SPaul Durrant rc = -E2BIG; 638*ab520be8SPaul Durrant goto out; 639*ab520be8SPaul Durrant } 640*ab520be8SPaul Durrant 641*ab520be8SPaul Durrant if (!access_ok(VERIFY_WRITE, kbufs[i].uptr, 642*ab520be8SPaul Durrant kbufs[i].size)) { 643*ab520be8SPaul Durrant rc = -EFAULT; 644*ab520be8SPaul Durrant goto out; 645*ab520be8SPaul Durrant } 646*ab520be8SPaul Durrant 647*ab520be8SPaul Durrant nr_pages += DIV_ROUND_UP( 648*ab520be8SPaul Durrant offset_in_page(kbufs[i].uptr) + kbufs[i].size, 649*ab520be8SPaul Durrant PAGE_SIZE); 650*ab520be8SPaul Durrant } 651*ab520be8SPaul Durrant 652*ab520be8SPaul Durrant pages = kcalloc(nr_pages, sizeof(*pages), GFP_KERNEL); 653*ab520be8SPaul Durrant if (!pages) { 654*ab520be8SPaul Durrant rc = -ENOMEM; 655*ab520be8SPaul Durrant goto out; 656*ab520be8SPaul Durrant } 657*ab520be8SPaul Durrant 658*ab520be8SPaul Durrant xbufs = kcalloc(kdata.num, sizeof(*xbufs), GFP_KERNEL); 659*ab520be8SPaul Durrant if (!xbufs) { 660*ab520be8SPaul Durrant rc = -ENOMEM; 661*ab520be8SPaul Durrant goto out; 662*ab520be8SPaul Durrant } 663*ab520be8SPaul Durrant 664*ab520be8SPaul Durrant rc = lock_pages(kbufs, kdata.num, pages, nr_pages); 665*ab520be8SPaul Durrant if (rc) 666*ab520be8SPaul Durrant goto out; 667*ab520be8SPaul Durrant 668*ab520be8SPaul Durrant for (i = 0; i < kdata.num; i++) { 669*ab520be8SPaul Durrant set_xen_guest_handle(xbufs[i].h, kbufs[i].uptr); 670*ab520be8SPaul Durrant xbufs[i].size = kbufs[i].size; 671*ab520be8SPaul Durrant } 672*ab520be8SPaul Durrant 673*ab520be8SPaul Durrant xen_preemptible_hcall_begin(); 674*ab520be8SPaul Durrant rc = HYPERVISOR_dm_op(kdata.dom, kdata.num, xbufs); 675*ab520be8SPaul Durrant xen_preemptible_hcall_end(); 676*ab520be8SPaul Durrant 677*ab520be8SPaul Durrant out: 678*ab520be8SPaul Durrant unlock_pages(pages, nr_pages); 679*ab520be8SPaul Durrant kfree(xbufs); 680*ab520be8SPaul Durrant kfree(pages); 681*ab520be8SPaul Durrant kfree(kbufs); 682*ab520be8SPaul Durrant 683*ab520be8SPaul Durrant return rc; 684*ab520be8SPaul Durrant } 685*ab520be8SPaul Durrant 686d8414d3cSBastian Blank static long privcmd_ioctl(struct file *file, 687d8414d3cSBastian Blank unsigned int cmd, unsigned long data) 688d8414d3cSBastian Blank { 689dc9eab6fSPaul Durrant int ret = -ENOTTY; 690d8414d3cSBastian Blank void __user *udata = (void __user *) data; 691d8414d3cSBastian Blank 692d8414d3cSBastian Blank switch (cmd) { 693d8414d3cSBastian Blank case IOCTL_PRIVCMD_HYPERCALL: 694d8414d3cSBastian Blank ret = privcmd_ioctl_hypercall(udata); 695d8414d3cSBastian Blank break; 696d8414d3cSBastian Blank 697d8414d3cSBastian Blank case IOCTL_PRIVCMD_MMAP: 698d8414d3cSBastian Blank ret = privcmd_ioctl_mmap(udata); 699d8414d3cSBastian Blank break; 700d8414d3cSBastian Blank 701d8414d3cSBastian Blank case IOCTL_PRIVCMD_MMAPBATCH: 702ceb90fa0SAndres Lagar-Cavilla ret = privcmd_ioctl_mmap_batch(udata, 1); 703ceb90fa0SAndres Lagar-Cavilla break; 704ceb90fa0SAndres Lagar-Cavilla 705ceb90fa0SAndres Lagar-Cavilla case IOCTL_PRIVCMD_MMAPBATCH_V2: 706ceb90fa0SAndres Lagar-Cavilla ret = privcmd_ioctl_mmap_batch(udata, 2); 707d8414d3cSBastian Blank break; 708d8414d3cSBastian Blank 709*ab520be8SPaul Durrant case IOCTL_PRIVCMD_DM_OP: 710*ab520be8SPaul Durrant ret = privcmd_ioctl_dm_op(udata); 711*ab520be8SPaul Durrant break; 712*ab520be8SPaul Durrant 713d8414d3cSBastian Blank default: 714d8414d3cSBastian Blank break; 715d8414d3cSBastian Blank } 716d8414d3cSBastian Blank 717d8414d3cSBastian Blank return ret; 718d8414d3cSBastian Blank } 719d8414d3cSBastian Blank 720d71f5139SMukesh Rathor static void privcmd_close(struct vm_area_struct *vma) 721d71f5139SMukesh Rathor { 722d71f5139SMukesh Rathor struct page **pages = vma->vm_private_data; 723c7ebf9d9SMuhammad Falak R Wani int numpgs = vma_pages(vma); 7245995a68aSJulien Grall int numgfns = (vma->vm_end - vma->vm_start) >> XEN_PAGE_SHIFT; 725b6497b38SIan Campbell int rc; 726d71f5139SMukesh Rathor 7279eff37a8SDan Carpenter if (!xen_feature(XENFEAT_auto_translated_physmap) || !numpgs || !pages) 728d71f5139SMukesh Rathor return; 729d71f5139SMukesh Rathor 7305995a68aSJulien Grall rc = xen_unmap_domain_gfn_range(vma, numgfns, pages); 731b6497b38SIan Campbell if (rc == 0) 732d71f5139SMukesh Rathor free_xenballooned_pages(numpgs, pages); 733b6497b38SIan Campbell else 734b6497b38SIan Campbell pr_crit("unable to unmap MFN range: leaking %d pages. rc=%d\n", 735b6497b38SIan Campbell numpgs, rc); 736d71f5139SMukesh Rathor kfree(pages); 737d71f5139SMukesh Rathor } 738d71f5139SMukesh Rathor 739d8414d3cSBastian Blank static int privcmd_fault(struct vm_area_struct *vma, struct vm_fault *vmf) 740d8414d3cSBastian Blank { 741d8414d3cSBastian Blank printk(KERN_DEBUG "privcmd_fault: vma=%p %lx-%lx, pgoff=%lx, uv=%p\n", 742d8414d3cSBastian Blank vma, vma->vm_start, vma->vm_end, 7431a29d85eSJan Kara vmf->pgoff, (void *)vmf->address); 744d8414d3cSBastian Blank 745d8414d3cSBastian Blank return VM_FAULT_SIGBUS; 746d8414d3cSBastian Blank } 747d8414d3cSBastian Blank 7487cbea8dcSKirill A. Shutemov static const struct vm_operations_struct privcmd_vm_ops = { 749d71f5139SMukesh Rathor .close = privcmd_close, 750d8414d3cSBastian Blank .fault = privcmd_fault 751d8414d3cSBastian Blank }; 752d8414d3cSBastian Blank 753d8414d3cSBastian Blank static int privcmd_mmap(struct file *file, struct vm_area_struct *vma) 754d8414d3cSBastian Blank { 755d8414d3cSBastian Blank /* DONTCOPY is essential for Xen because copy_page_range doesn't know 756d8414d3cSBastian Blank * how to recreate these mappings */ 757314e51b9SKonstantin Khlebnikov vma->vm_flags |= VM_IO | VM_PFNMAP | VM_DONTCOPY | 758314e51b9SKonstantin Khlebnikov VM_DONTEXPAND | VM_DONTDUMP; 759d8414d3cSBastian Blank vma->vm_ops = &privcmd_vm_ops; 760d8414d3cSBastian Blank vma->vm_private_data = NULL; 761d8414d3cSBastian Blank 762d8414d3cSBastian Blank return 0; 763d8414d3cSBastian Blank } 764d8414d3cSBastian Blank 765a5deabe0SAndres Lagar-Cavilla /* 766a5deabe0SAndres Lagar-Cavilla * For MMAPBATCH*. This allows asserting the singleshot mapping 767a5deabe0SAndres Lagar-Cavilla * on a per pfn/pte basis. Mapping calls that fail with ENOENT 768a5deabe0SAndres Lagar-Cavilla * can be then retried until success. 769a5deabe0SAndres Lagar-Cavilla */ 770a5deabe0SAndres Lagar-Cavilla static int is_mapped_fn(pte_t *pte, struct page *pmd_page, 771a5deabe0SAndres Lagar-Cavilla unsigned long addr, void *data) 772d8414d3cSBastian Blank { 773a5deabe0SAndres Lagar-Cavilla return pte_none(*pte) ? 0 : -EBUSY; 774a5deabe0SAndres Lagar-Cavilla } 775a5deabe0SAndres Lagar-Cavilla 776a5deabe0SAndres Lagar-Cavilla static int privcmd_vma_range_is_mapped( 777a5deabe0SAndres Lagar-Cavilla struct vm_area_struct *vma, 778a5deabe0SAndres Lagar-Cavilla unsigned long addr, 779a5deabe0SAndres Lagar-Cavilla unsigned long nr_pages) 780a5deabe0SAndres Lagar-Cavilla { 781a5deabe0SAndres Lagar-Cavilla return apply_to_page_range(vma->vm_mm, addr, nr_pages << PAGE_SHIFT, 782a5deabe0SAndres Lagar-Cavilla is_mapped_fn, NULL) != 0; 783d8414d3cSBastian Blank } 784d8414d3cSBastian Blank 785d8414d3cSBastian Blank const struct file_operations xen_privcmd_fops = { 786d8414d3cSBastian Blank .owner = THIS_MODULE, 787d8414d3cSBastian Blank .unlocked_ioctl = privcmd_ioctl, 788d8414d3cSBastian Blank .mmap = privcmd_mmap, 789d8414d3cSBastian Blank }; 790d8414d3cSBastian Blank EXPORT_SYMBOL_GPL(xen_privcmd_fops); 791d8414d3cSBastian Blank 792d8414d3cSBastian Blank static struct miscdevice privcmd_dev = { 793d8414d3cSBastian Blank .minor = MISC_DYNAMIC_MINOR, 794d8414d3cSBastian Blank .name = "xen/privcmd", 795d8414d3cSBastian Blank .fops = &xen_privcmd_fops, 796d8414d3cSBastian Blank }; 797d8414d3cSBastian Blank 798d8414d3cSBastian Blank static int __init privcmd_init(void) 799d8414d3cSBastian Blank { 800d8414d3cSBastian Blank int err; 801d8414d3cSBastian Blank 802d8414d3cSBastian Blank if (!xen_domain()) 803d8414d3cSBastian Blank return -ENODEV; 804d8414d3cSBastian Blank 805d8414d3cSBastian Blank err = misc_register(&privcmd_dev); 806d8414d3cSBastian Blank if (err != 0) { 807283c0972SJoe Perches pr_err("Could not register Xen privcmd device\n"); 808d8414d3cSBastian Blank return err; 809d8414d3cSBastian Blank } 810d8414d3cSBastian Blank return 0; 811d8414d3cSBastian Blank } 812d8414d3cSBastian Blank 813d8414d3cSBastian Blank static void __exit privcmd_exit(void) 814d8414d3cSBastian Blank { 815d8414d3cSBastian Blank misc_deregister(&privcmd_dev); 816d8414d3cSBastian Blank } 817d8414d3cSBastian Blank 818d8414d3cSBastian Blank module_init(privcmd_init); 819d8414d3cSBastian Blank module_exit(privcmd_exit); 820