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> 25ab520be8SPaul 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> 36ab520be8SPaul 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 48ab520be8SPaul Durrant static unsigned int privcmd_dm_op_max_num = 16; 49ab520be8SPaul Durrant module_param_named(dm_op_max_nr_bufs, privcmd_dm_op_max_num, uint, 0644); 50ab520be8SPaul Durrant MODULE_PARM_DESC(dm_op_max_nr_bufs, 51ab520be8SPaul Durrant "Maximum number of buffers per dm_op hypercall"); 52ab520be8SPaul Durrant 53ab520be8SPaul Durrant static unsigned int privcmd_dm_op_buf_max_size = 4096; 54ab520be8SPaul Durrant module_param_named(dm_op_buf_max_size, privcmd_dm_op_buf_max_size, uint, 55ab520be8SPaul Durrant 0644); 56ab520be8SPaul Durrant MODULE_PARM_DESC(dm_op_buf_max_size, 57ab520be8SPaul Durrant "Maximum size of a dm_op hypercall buffer"); 58ab520be8SPaul Durrant 594610d240SPaul Durrant struct privcmd_data { 604610d240SPaul Durrant domid_t domid; 614610d240SPaul Durrant }; 624610d240SPaul Durrant 63a5deabe0SAndres Lagar-Cavilla static int privcmd_vma_range_is_mapped( 64a5deabe0SAndres Lagar-Cavilla struct vm_area_struct *vma, 65a5deabe0SAndres Lagar-Cavilla unsigned long addr, 66a5deabe0SAndres Lagar-Cavilla unsigned long nr_pages); 67d8414d3cSBastian Blank 684610d240SPaul Durrant static long privcmd_ioctl_hypercall(struct file *file, void __user *udata) 69d8414d3cSBastian Blank { 704610d240SPaul Durrant struct privcmd_data *data = file->private_data; 71d8414d3cSBastian Blank struct privcmd_hypercall hypercall; 72d8414d3cSBastian Blank long ret; 73d8414d3cSBastian Blank 744610d240SPaul Durrant /* Disallow arbitrary hypercalls if restricted */ 754610d240SPaul Durrant if (data->domid != DOMID_INVALID) 764610d240SPaul Durrant return -EPERM; 774610d240SPaul Durrant 78d8414d3cSBastian Blank if (copy_from_user(&hypercall, udata, sizeof(hypercall))) 79d8414d3cSBastian Blank return -EFAULT; 80d8414d3cSBastian Blank 81fdfd811dSDavid Vrabel xen_preemptible_hcall_begin(); 82d8414d3cSBastian Blank ret = privcmd_call(hypercall.op, 83d8414d3cSBastian Blank hypercall.arg[0], hypercall.arg[1], 84d8414d3cSBastian Blank hypercall.arg[2], hypercall.arg[3], 85d8414d3cSBastian Blank hypercall.arg[4]); 86fdfd811dSDavid Vrabel xen_preemptible_hcall_end(); 87d8414d3cSBastian Blank 88d8414d3cSBastian Blank return ret; 89d8414d3cSBastian Blank } 90d8414d3cSBastian Blank 91d8414d3cSBastian Blank static void free_page_list(struct list_head *pages) 92d8414d3cSBastian Blank { 93d8414d3cSBastian Blank struct page *p, *n; 94d8414d3cSBastian Blank 95d8414d3cSBastian Blank list_for_each_entry_safe(p, n, pages, lru) 96d8414d3cSBastian Blank __free_page(p); 97d8414d3cSBastian Blank 98d8414d3cSBastian Blank INIT_LIST_HEAD(pages); 99d8414d3cSBastian Blank } 100d8414d3cSBastian Blank 101d8414d3cSBastian Blank /* 102d8414d3cSBastian Blank * Given an array of items in userspace, return a list of pages 103d8414d3cSBastian Blank * containing the data. If copying fails, either because of memory 104d8414d3cSBastian Blank * allocation failure or a problem reading user memory, return an 105d8414d3cSBastian Blank * error code; its up to the caller to dispose of any partial list. 106d8414d3cSBastian Blank */ 107d8414d3cSBastian Blank static int gather_array(struct list_head *pagelist, 108d8414d3cSBastian Blank unsigned nelem, size_t size, 109ceb90fa0SAndres Lagar-Cavilla const void __user *data) 110d8414d3cSBastian Blank { 111d8414d3cSBastian Blank unsigned pageidx; 112d8414d3cSBastian Blank void *pagedata; 113d8414d3cSBastian Blank int ret; 114d8414d3cSBastian Blank 115d8414d3cSBastian Blank if (size > PAGE_SIZE) 116d8414d3cSBastian Blank return 0; 117d8414d3cSBastian Blank 118d8414d3cSBastian Blank pageidx = PAGE_SIZE; 119d8414d3cSBastian Blank pagedata = NULL; /* quiet, gcc */ 120d8414d3cSBastian Blank while (nelem--) { 121d8414d3cSBastian Blank if (pageidx > PAGE_SIZE-size) { 122d8414d3cSBastian Blank struct page *page = alloc_page(GFP_KERNEL); 123d8414d3cSBastian Blank 124d8414d3cSBastian Blank ret = -ENOMEM; 125d8414d3cSBastian Blank if (page == NULL) 126d8414d3cSBastian Blank goto fail; 127d8414d3cSBastian Blank 128d8414d3cSBastian Blank pagedata = page_address(page); 129d8414d3cSBastian Blank 130d8414d3cSBastian Blank list_add_tail(&page->lru, pagelist); 131d8414d3cSBastian Blank pageidx = 0; 132d8414d3cSBastian Blank } 133d8414d3cSBastian Blank 134d8414d3cSBastian Blank ret = -EFAULT; 135d8414d3cSBastian Blank if (copy_from_user(pagedata + pageidx, data, size)) 136d8414d3cSBastian Blank goto fail; 137d8414d3cSBastian Blank 138d8414d3cSBastian Blank data += size; 139d8414d3cSBastian Blank pageidx += size; 140d8414d3cSBastian Blank } 141d8414d3cSBastian Blank 142d8414d3cSBastian Blank ret = 0; 143d8414d3cSBastian Blank 144d8414d3cSBastian Blank fail: 145d8414d3cSBastian Blank return ret; 146d8414d3cSBastian Blank } 147d8414d3cSBastian Blank 148d8414d3cSBastian Blank /* 149d8414d3cSBastian Blank * Call function "fn" on each element of the array fragmented 150d8414d3cSBastian Blank * over a list of pages. 151d8414d3cSBastian Blank */ 152d8414d3cSBastian Blank static int traverse_pages(unsigned nelem, size_t size, 153d8414d3cSBastian Blank struct list_head *pos, 154d8414d3cSBastian Blank int (*fn)(void *data, void *state), 155d8414d3cSBastian Blank void *state) 156d8414d3cSBastian Blank { 157d8414d3cSBastian Blank void *pagedata; 158d8414d3cSBastian Blank unsigned pageidx; 159d8414d3cSBastian Blank int ret = 0; 160d8414d3cSBastian Blank 161d8414d3cSBastian Blank BUG_ON(size > PAGE_SIZE); 162d8414d3cSBastian Blank 163d8414d3cSBastian Blank pageidx = PAGE_SIZE; 164d8414d3cSBastian Blank pagedata = NULL; /* hush, gcc */ 165d8414d3cSBastian Blank 166d8414d3cSBastian Blank while (nelem--) { 167d8414d3cSBastian Blank if (pageidx > PAGE_SIZE-size) { 168d8414d3cSBastian Blank struct page *page; 169d8414d3cSBastian Blank pos = pos->next; 170d8414d3cSBastian Blank page = list_entry(pos, struct page, lru); 171d8414d3cSBastian Blank pagedata = page_address(page); 172d8414d3cSBastian Blank pageidx = 0; 173d8414d3cSBastian Blank } 174d8414d3cSBastian Blank 175d8414d3cSBastian Blank ret = (*fn)(pagedata + pageidx, state); 176d8414d3cSBastian Blank if (ret) 177d8414d3cSBastian Blank break; 178d8414d3cSBastian Blank pageidx += size; 179d8414d3cSBastian Blank } 180d8414d3cSBastian Blank 181d8414d3cSBastian Blank return ret; 182d8414d3cSBastian Blank } 183d8414d3cSBastian Blank 1844e8c0c8cSDavid Vrabel /* 1854e8c0c8cSDavid Vrabel * Similar to traverse_pages, but use each page as a "block" of 1864e8c0c8cSDavid Vrabel * data to be processed as one unit. 1874e8c0c8cSDavid Vrabel */ 1884e8c0c8cSDavid Vrabel static int traverse_pages_block(unsigned nelem, size_t size, 1894e8c0c8cSDavid Vrabel struct list_head *pos, 1904e8c0c8cSDavid Vrabel int (*fn)(void *data, int nr, void *state), 1914e8c0c8cSDavid Vrabel void *state) 1924e8c0c8cSDavid Vrabel { 1934e8c0c8cSDavid Vrabel void *pagedata; 1944e8c0c8cSDavid Vrabel unsigned pageidx; 1954e8c0c8cSDavid Vrabel int ret = 0; 1964e8c0c8cSDavid Vrabel 1974e8c0c8cSDavid Vrabel BUG_ON(size > PAGE_SIZE); 1984e8c0c8cSDavid Vrabel 1994e8c0c8cSDavid Vrabel pageidx = PAGE_SIZE; 2004e8c0c8cSDavid Vrabel 2014e8c0c8cSDavid Vrabel while (nelem) { 2024e8c0c8cSDavid Vrabel int nr = (PAGE_SIZE/size); 2034e8c0c8cSDavid Vrabel struct page *page; 2044e8c0c8cSDavid Vrabel if (nr > nelem) 2054e8c0c8cSDavid Vrabel nr = nelem; 2064e8c0c8cSDavid Vrabel pos = pos->next; 2074e8c0c8cSDavid Vrabel page = list_entry(pos, struct page, lru); 2084e8c0c8cSDavid Vrabel pagedata = page_address(page); 2094e8c0c8cSDavid Vrabel ret = (*fn)(pagedata, nr, state); 2104e8c0c8cSDavid Vrabel if (ret) 2114e8c0c8cSDavid Vrabel break; 2124e8c0c8cSDavid Vrabel nelem -= nr; 2134e8c0c8cSDavid Vrabel } 2144e8c0c8cSDavid Vrabel 2154e8c0c8cSDavid Vrabel return ret; 2164e8c0c8cSDavid Vrabel } 2174e8c0c8cSDavid Vrabel 218a13d7201SJulien Grall struct mmap_gfn_state { 219d8414d3cSBastian Blank unsigned long va; 220d8414d3cSBastian Blank struct vm_area_struct *vma; 221d8414d3cSBastian Blank domid_t domain; 222d8414d3cSBastian Blank }; 223d8414d3cSBastian Blank 224a13d7201SJulien Grall static int mmap_gfn_range(void *data, void *state) 225d8414d3cSBastian Blank { 226d8414d3cSBastian Blank struct privcmd_mmap_entry *msg = data; 227a13d7201SJulien Grall struct mmap_gfn_state *st = state; 228d8414d3cSBastian Blank struct vm_area_struct *vma = st->vma; 229d8414d3cSBastian Blank int rc; 230d8414d3cSBastian Blank 231d8414d3cSBastian Blank /* Do not allow range to wrap the address space. */ 232d8414d3cSBastian Blank if ((msg->npages > (LONG_MAX >> PAGE_SHIFT)) || 233d8414d3cSBastian Blank ((unsigned long)(msg->npages << PAGE_SHIFT) >= -st->va)) 234d8414d3cSBastian Blank return -EINVAL; 235d8414d3cSBastian Blank 236d8414d3cSBastian Blank /* Range chunks must be contiguous in va space. */ 237d8414d3cSBastian Blank if ((msg->va != st->va) || 238d8414d3cSBastian Blank ((msg->va+(msg->npages<<PAGE_SHIFT)) > vma->vm_end)) 239d8414d3cSBastian Blank return -EINVAL; 240d8414d3cSBastian Blank 241a13d7201SJulien Grall rc = xen_remap_domain_gfn_range(vma, 242d8414d3cSBastian Blank msg->va & PAGE_MASK, 243d8414d3cSBastian Blank msg->mfn, msg->npages, 244d8414d3cSBastian Blank vma->vm_page_prot, 2459a032e39SIan Campbell st->domain, NULL); 246d8414d3cSBastian Blank if (rc < 0) 247d8414d3cSBastian Blank return rc; 248d8414d3cSBastian Blank 249d8414d3cSBastian Blank st->va += msg->npages << PAGE_SHIFT; 250d8414d3cSBastian Blank 251d8414d3cSBastian Blank return 0; 252d8414d3cSBastian Blank } 253d8414d3cSBastian Blank 2544610d240SPaul Durrant static long privcmd_ioctl_mmap(struct file *file, void __user *udata) 255d8414d3cSBastian Blank { 2564610d240SPaul Durrant struct privcmd_data *data = file->private_data; 257d8414d3cSBastian Blank struct privcmd_mmap mmapcmd; 258d8414d3cSBastian Blank struct mm_struct *mm = current->mm; 259d8414d3cSBastian Blank struct vm_area_struct *vma; 260d8414d3cSBastian Blank int rc; 261d8414d3cSBastian Blank LIST_HEAD(pagelist); 262a13d7201SJulien Grall struct mmap_gfn_state state; 263d8414d3cSBastian Blank 264d71f5139SMukesh Rathor /* We only support privcmd_ioctl_mmap_batch for auto translated. */ 265d71f5139SMukesh Rathor if (xen_feature(XENFEAT_auto_translated_physmap)) 266d71f5139SMukesh Rathor return -ENOSYS; 267d71f5139SMukesh Rathor 268d8414d3cSBastian Blank if (copy_from_user(&mmapcmd, udata, sizeof(mmapcmd))) 269d8414d3cSBastian Blank return -EFAULT; 270d8414d3cSBastian Blank 2714610d240SPaul Durrant /* If restriction is in place, check the domid matches */ 2724610d240SPaul Durrant if (data->domid != DOMID_INVALID && data->domid != mmapcmd.dom) 2734610d240SPaul Durrant return -EPERM; 2744610d240SPaul Durrant 275d8414d3cSBastian Blank rc = gather_array(&pagelist, 276d8414d3cSBastian Blank mmapcmd.num, sizeof(struct privcmd_mmap_entry), 277d8414d3cSBastian Blank mmapcmd.entry); 278d8414d3cSBastian Blank 279d8414d3cSBastian Blank if (rc || list_empty(&pagelist)) 280d8414d3cSBastian Blank goto out; 281d8414d3cSBastian Blank 282d8414d3cSBastian Blank down_write(&mm->mmap_sem); 283d8414d3cSBastian Blank 284d8414d3cSBastian Blank { 285d8414d3cSBastian Blank struct page *page = list_first_entry(&pagelist, 286d8414d3cSBastian Blank struct page, lru); 287d8414d3cSBastian Blank struct privcmd_mmap_entry *msg = page_address(page); 288d8414d3cSBastian Blank 289d8414d3cSBastian Blank vma = find_vma(mm, msg->va); 290d8414d3cSBastian Blank rc = -EINVAL; 291d8414d3cSBastian Blank 292a5deabe0SAndres Lagar-Cavilla if (!vma || (msg->va != vma->vm_start) || vma->vm_private_data) 293d8414d3cSBastian Blank goto out_up; 294a5deabe0SAndres Lagar-Cavilla vma->vm_private_data = PRIV_VMA_LOCKED; 295d8414d3cSBastian Blank } 296d8414d3cSBastian Blank 297d8414d3cSBastian Blank state.va = vma->vm_start; 298d8414d3cSBastian Blank state.vma = vma; 299d8414d3cSBastian Blank state.domain = mmapcmd.dom; 300d8414d3cSBastian Blank 301d8414d3cSBastian Blank rc = traverse_pages(mmapcmd.num, sizeof(struct privcmd_mmap_entry), 302d8414d3cSBastian Blank &pagelist, 303a13d7201SJulien Grall mmap_gfn_range, &state); 304d8414d3cSBastian Blank 305d8414d3cSBastian Blank 306d8414d3cSBastian Blank out_up: 307d8414d3cSBastian Blank up_write(&mm->mmap_sem); 308d8414d3cSBastian Blank 309d8414d3cSBastian Blank out: 310d8414d3cSBastian Blank free_page_list(&pagelist); 311d8414d3cSBastian Blank 312d8414d3cSBastian Blank return rc; 313d8414d3cSBastian Blank } 314d8414d3cSBastian Blank 315d8414d3cSBastian Blank struct mmap_batch_state { 316d8414d3cSBastian Blank domid_t domain; 317d8414d3cSBastian Blank unsigned long va; 318d8414d3cSBastian Blank struct vm_area_struct *vma; 319d71f5139SMukesh Rathor int index; 320ceb90fa0SAndres Lagar-Cavilla /* A tristate: 321ceb90fa0SAndres Lagar-Cavilla * 0 for no errors 322ceb90fa0SAndres Lagar-Cavilla * 1 if at least one error has happened (and no 323ceb90fa0SAndres Lagar-Cavilla * -ENOENT errors have happened) 324ceb90fa0SAndres Lagar-Cavilla * -ENOENT if at least 1 -ENOENT has happened. 325ceb90fa0SAndres Lagar-Cavilla */ 326ceb90fa0SAndres Lagar-Cavilla int global_error; 32799beae6cSAndres Lagar-Cavilla int version; 328d8414d3cSBastian Blank 329a13d7201SJulien Grall /* User-space gfn array to store errors in the second pass for V1. */ 330a13d7201SJulien Grall xen_pfn_t __user *user_gfn; 33199beae6cSAndres Lagar-Cavilla /* User-space int array to store errors in the second pass for V2. */ 33299beae6cSAndres Lagar-Cavilla int __user *user_err; 333d8414d3cSBastian Blank }; 334d8414d3cSBastian Blank 335a13d7201SJulien Grall /* auto translated dom0 note: if domU being created is PV, then gfn is 336a13d7201SJulien Grall * mfn(addr on bus). If it's auto xlated, then gfn is pfn (input to HAP). 337d71f5139SMukesh Rathor */ 3384e8c0c8cSDavid Vrabel static int mmap_batch_fn(void *data, int nr, void *state) 339d8414d3cSBastian Blank { 340a13d7201SJulien Grall xen_pfn_t *gfnp = data; 341d8414d3cSBastian Blank struct mmap_batch_state *st = state; 342d71f5139SMukesh Rathor struct vm_area_struct *vma = st->vma; 343d71f5139SMukesh Rathor struct page **pages = vma->vm_private_data; 3444e8c0c8cSDavid Vrabel struct page **cur_pages = NULL; 345ceb90fa0SAndres Lagar-Cavilla int ret; 346d8414d3cSBastian Blank 347d71f5139SMukesh Rathor if (xen_feature(XENFEAT_auto_translated_physmap)) 3484e8c0c8cSDavid Vrabel cur_pages = &pages[st->index]; 349d71f5139SMukesh Rathor 3504e8c0c8cSDavid Vrabel BUG_ON(nr < 0); 351a13d7201SJulien Grall ret = xen_remap_domain_gfn_array(st->vma, st->va & PAGE_MASK, gfnp, nr, 352a13d7201SJulien Grall (int *)gfnp, st->vma->vm_page_prot, 3534e8c0c8cSDavid Vrabel st->domain, cur_pages); 354ceb90fa0SAndres Lagar-Cavilla 3554e8c0c8cSDavid Vrabel /* Adjust the global_error? */ 3564e8c0c8cSDavid Vrabel if (ret != nr) { 357ceb90fa0SAndres Lagar-Cavilla if (ret == -ENOENT) 358ceb90fa0SAndres Lagar-Cavilla st->global_error = -ENOENT; 359ceb90fa0SAndres Lagar-Cavilla else { 360ceb90fa0SAndres Lagar-Cavilla /* Record that at least one error has happened. */ 361ceb90fa0SAndres Lagar-Cavilla if (st->global_error == 0) 362ceb90fa0SAndres Lagar-Cavilla st->global_error = 1; 363ceb90fa0SAndres Lagar-Cavilla } 364d8414d3cSBastian Blank } 365*753c09b5SJulien Grall st->va += XEN_PAGE_SIZE * nr; 366*753c09b5SJulien Grall st->index += nr / XEN_PFN_PER_PAGE; 367d8414d3cSBastian Blank 368d8414d3cSBastian Blank return 0; 369d8414d3cSBastian Blank } 370d8414d3cSBastian Blank 3714e8c0c8cSDavid Vrabel static int mmap_return_error(int err, struct mmap_batch_state *st) 372d8414d3cSBastian Blank { 3734e8c0c8cSDavid Vrabel int ret; 374d8414d3cSBastian Blank 37599beae6cSAndres Lagar-Cavilla if (st->version == 1) { 3764e8c0c8cSDavid Vrabel if (err) { 377a13d7201SJulien Grall xen_pfn_t gfn; 3784e8c0c8cSDavid Vrabel 379a13d7201SJulien Grall ret = get_user(gfn, st->user_gfn); 3804e8c0c8cSDavid Vrabel if (ret < 0) 3814e8c0c8cSDavid Vrabel return ret; 3824e8c0c8cSDavid Vrabel /* 3834e8c0c8cSDavid Vrabel * V1 encodes the error codes in the 32bit top 384a13d7201SJulien Grall * nibble of the gfn (with its known 3854e8c0c8cSDavid Vrabel * limitations vis-a-vis 64 bit callers). 3864e8c0c8cSDavid Vrabel */ 387a13d7201SJulien Grall gfn |= (err == -ENOENT) ? 3884e8c0c8cSDavid Vrabel PRIVCMD_MMAPBATCH_PAGED_ERROR : 3894e8c0c8cSDavid Vrabel PRIVCMD_MMAPBATCH_MFN_ERROR; 390a13d7201SJulien Grall return __put_user(gfn, st->user_gfn++); 3914e8c0c8cSDavid Vrabel } else 392a13d7201SJulien Grall st->user_gfn++; 39399beae6cSAndres Lagar-Cavilla } else { /* st->version == 2 */ 39499beae6cSAndres Lagar-Cavilla if (err) 39599beae6cSAndres Lagar-Cavilla return __put_user(err, st->user_err++); 39699beae6cSAndres Lagar-Cavilla else 39799beae6cSAndres Lagar-Cavilla st->user_err++; 39899beae6cSAndres Lagar-Cavilla } 39999beae6cSAndres Lagar-Cavilla 40099beae6cSAndres Lagar-Cavilla return 0; 401d8414d3cSBastian Blank } 402d8414d3cSBastian Blank 4034e8c0c8cSDavid Vrabel static int mmap_return_errors(void *data, int nr, void *state) 4044e8c0c8cSDavid Vrabel { 4054e8c0c8cSDavid Vrabel struct mmap_batch_state *st = state; 4064e8c0c8cSDavid Vrabel int *errs = data; 4074e8c0c8cSDavid Vrabel int i; 4084e8c0c8cSDavid Vrabel int ret; 4094e8c0c8cSDavid Vrabel 4104e8c0c8cSDavid Vrabel for (i = 0; i < nr; i++) { 4114e8c0c8cSDavid Vrabel ret = mmap_return_error(errs[i], st); 4124e8c0c8cSDavid Vrabel if (ret < 0) 4134e8c0c8cSDavid Vrabel return ret; 4144e8c0c8cSDavid Vrabel } 4154e8c0c8cSDavid Vrabel return 0; 4164e8c0c8cSDavid Vrabel } 4174e8c0c8cSDavid Vrabel 418a13d7201SJulien Grall /* Allocate pfns that are then mapped with gfns from foreign domid. Update 419d71f5139SMukesh Rathor * the vma with the page info to use later. 420d71f5139SMukesh Rathor * Returns: 0 if success, otherwise -errno 421d71f5139SMukesh Rathor */ 422d71f5139SMukesh Rathor static int alloc_empty_pages(struct vm_area_struct *vma, int numpgs) 423d71f5139SMukesh Rathor { 424d71f5139SMukesh Rathor int rc; 425d71f5139SMukesh Rathor struct page **pages; 426d71f5139SMukesh Rathor 427d71f5139SMukesh Rathor pages = kcalloc(numpgs, sizeof(pages[0]), GFP_KERNEL); 428d71f5139SMukesh Rathor if (pages == NULL) 429d71f5139SMukesh Rathor return -ENOMEM; 430d71f5139SMukesh Rathor 43181b286e0SDavid Vrabel rc = alloc_xenballooned_pages(numpgs, pages); 432d71f5139SMukesh Rathor if (rc != 0) { 433d71f5139SMukesh Rathor pr_warn("%s Could not alloc %d pfns rc:%d\n", __func__, 434d71f5139SMukesh Rathor numpgs, rc); 435d71f5139SMukesh Rathor kfree(pages); 436d71f5139SMukesh Rathor return -ENOMEM; 437d71f5139SMukesh Rathor } 438a5deabe0SAndres Lagar-Cavilla BUG_ON(vma->vm_private_data != NULL); 439d71f5139SMukesh Rathor vma->vm_private_data = pages; 440d71f5139SMukesh Rathor 441d71f5139SMukesh Rathor return 0; 442d71f5139SMukesh Rathor } 443d71f5139SMukesh Rathor 4447cbea8dcSKirill A. Shutemov static const struct vm_operations_struct privcmd_vm_ops; 445d8414d3cSBastian Blank 4464610d240SPaul Durrant static long privcmd_ioctl_mmap_batch( 4474610d240SPaul Durrant struct file *file, void __user *udata, int version) 448d8414d3cSBastian Blank { 4494610d240SPaul Durrant struct privcmd_data *data = file->private_data; 450d8414d3cSBastian Blank int ret; 451ceb90fa0SAndres Lagar-Cavilla struct privcmd_mmapbatch_v2 m; 452d8414d3cSBastian Blank struct mm_struct *mm = current->mm; 453d8414d3cSBastian Blank struct vm_area_struct *vma; 454d8414d3cSBastian Blank unsigned long nr_pages; 455d8414d3cSBastian Blank LIST_HEAD(pagelist); 456d8414d3cSBastian Blank struct mmap_batch_state state; 457d8414d3cSBastian Blank 458ceb90fa0SAndres Lagar-Cavilla switch (version) { 459ceb90fa0SAndres Lagar-Cavilla case 1: 460ceb90fa0SAndres Lagar-Cavilla if (copy_from_user(&m, udata, sizeof(struct privcmd_mmapbatch))) 461d8414d3cSBastian Blank return -EFAULT; 462ceb90fa0SAndres Lagar-Cavilla /* Returns per-frame error in m.arr. */ 463ceb90fa0SAndres Lagar-Cavilla m.err = NULL; 464ceb90fa0SAndres Lagar-Cavilla if (!access_ok(VERIFY_WRITE, m.arr, m.num * sizeof(*m.arr))) 465ceb90fa0SAndres Lagar-Cavilla return -EFAULT; 466ceb90fa0SAndres Lagar-Cavilla break; 467ceb90fa0SAndres Lagar-Cavilla case 2: 468ceb90fa0SAndres Lagar-Cavilla if (copy_from_user(&m, udata, sizeof(struct privcmd_mmapbatch_v2))) 469ceb90fa0SAndres Lagar-Cavilla return -EFAULT; 470ceb90fa0SAndres Lagar-Cavilla /* Returns per-frame error code in m.err. */ 471ceb90fa0SAndres Lagar-Cavilla if (!access_ok(VERIFY_WRITE, m.err, m.num * (sizeof(*m.err)))) 472ceb90fa0SAndres Lagar-Cavilla return -EFAULT; 473ceb90fa0SAndres Lagar-Cavilla break; 474ceb90fa0SAndres Lagar-Cavilla default: 475ceb90fa0SAndres Lagar-Cavilla return -EINVAL; 476ceb90fa0SAndres Lagar-Cavilla } 477d8414d3cSBastian Blank 4784610d240SPaul Durrant /* If restriction is in place, check the domid matches */ 4794610d240SPaul Durrant if (data->domid != DOMID_INVALID && data->domid != m.dom) 4804610d240SPaul Durrant return -EPERM; 4814610d240SPaul Durrant 4825995a68aSJulien Grall nr_pages = DIV_ROUND_UP(m.num, XEN_PFN_PER_PAGE); 483d8414d3cSBastian Blank if ((m.num <= 0) || (nr_pages > (LONG_MAX >> PAGE_SHIFT))) 484d8414d3cSBastian Blank return -EINVAL; 485d8414d3cSBastian Blank 486ceb90fa0SAndres Lagar-Cavilla ret = gather_array(&pagelist, m.num, sizeof(xen_pfn_t), m.arr); 487d8414d3cSBastian Blank 488ceb90fa0SAndres Lagar-Cavilla if (ret) 489d8414d3cSBastian Blank goto out; 490ceb90fa0SAndres Lagar-Cavilla if (list_empty(&pagelist)) { 491ceb90fa0SAndres Lagar-Cavilla ret = -EINVAL; 492ceb90fa0SAndres Lagar-Cavilla goto out; 493ceb90fa0SAndres Lagar-Cavilla } 494ceb90fa0SAndres Lagar-Cavilla 49599beae6cSAndres Lagar-Cavilla if (version == 2) { 49699beae6cSAndres Lagar-Cavilla /* Zero error array now to only copy back actual errors. */ 49799beae6cSAndres Lagar-Cavilla if (clear_user(m.err, sizeof(int) * m.num)) { 49899beae6cSAndres Lagar-Cavilla ret = -EFAULT; 499ceb90fa0SAndres Lagar-Cavilla goto out; 500ceb90fa0SAndres Lagar-Cavilla } 50199beae6cSAndres Lagar-Cavilla } 502d8414d3cSBastian Blank 503d8414d3cSBastian Blank down_write(&mm->mmap_sem); 504d8414d3cSBastian Blank 505d8414d3cSBastian Blank vma = find_vma(mm, m.addr); 506d8414d3cSBastian Blank if (!vma || 507a5deabe0SAndres Lagar-Cavilla vma->vm_ops != &privcmd_vm_ops) { 50868fa965dSMats Petersson ret = -EINVAL; 509a5deabe0SAndres Lagar-Cavilla goto out_unlock; 510a5deabe0SAndres Lagar-Cavilla } 511a5deabe0SAndres Lagar-Cavilla 512a5deabe0SAndres Lagar-Cavilla /* 513a5deabe0SAndres Lagar-Cavilla * Caller must either: 514a5deabe0SAndres Lagar-Cavilla * 515a5deabe0SAndres Lagar-Cavilla * Map the whole VMA range, which will also allocate all the 516a5deabe0SAndres Lagar-Cavilla * pages required for the auto_translated_physmap case. 517a5deabe0SAndres Lagar-Cavilla * 518a5deabe0SAndres Lagar-Cavilla * Or 519a5deabe0SAndres Lagar-Cavilla * 520a5deabe0SAndres Lagar-Cavilla * Map unmapped holes left from a previous map attempt (e.g., 521a5deabe0SAndres Lagar-Cavilla * because those foreign frames were previously paged out). 522a5deabe0SAndres Lagar-Cavilla */ 523a5deabe0SAndres Lagar-Cavilla if (vma->vm_private_data == NULL) { 524a5deabe0SAndres Lagar-Cavilla if (m.addr != vma->vm_start || 525a5deabe0SAndres Lagar-Cavilla m.addr + (nr_pages << PAGE_SHIFT) != vma->vm_end) { 526a5deabe0SAndres Lagar-Cavilla ret = -EINVAL; 527a5deabe0SAndres Lagar-Cavilla goto out_unlock; 528d8414d3cSBastian Blank } 529d71f5139SMukesh Rathor if (xen_feature(XENFEAT_auto_translated_physmap)) { 5305995a68aSJulien Grall ret = alloc_empty_pages(vma, nr_pages); 531a5deabe0SAndres Lagar-Cavilla if (ret < 0) 532a5deabe0SAndres Lagar-Cavilla goto out_unlock; 533a5deabe0SAndres Lagar-Cavilla } else 534a5deabe0SAndres Lagar-Cavilla vma->vm_private_data = PRIV_VMA_LOCKED; 535a5deabe0SAndres Lagar-Cavilla } else { 536a5deabe0SAndres Lagar-Cavilla if (m.addr < vma->vm_start || 537a5deabe0SAndres Lagar-Cavilla m.addr + (nr_pages << PAGE_SHIFT) > vma->vm_end) { 538a5deabe0SAndres Lagar-Cavilla ret = -EINVAL; 539a5deabe0SAndres Lagar-Cavilla goto out_unlock; 540a5deabe0SAndres Lagar-Cavilla } 541a5deabe0SAndres Lagar-Cavilla if (privcmd_vma_range_is_mapped(vma, m.addr, nr_pages)) { 542a5deabe0SAndres Lagar-Cavilla ret = -EINVAL; 543a5deabe0SAndres Lagar-Cavilla goto out_unlock; 544d71f5139SMukesh Rathor } 545d71f5139SMukesh Rathor } 546d8414d3cSBastian Blank 547d8414d3cSBastian Blank state.domain = m.dom; 548d8414d3cSBastian Blank state.vma = vma; 549d8414d3cSBastian Blank state.va = m.addr; 550d71f5139SMukesh Rathor state.index = 0; 551ceb90fa0SAndres Lagar-Cavilla state.global_error = 0; 55299beae6cSAndres Lagar-Cavilla state.version = version; 553d8414d3cSBastian Blank 5545995a68aSJulien Grall BUILD_BUG_ON(((PAGE_SIZE / sizeof(xen_pfn_t)) % XEN_PFN_PER_PAGE) != 0); 555ceb90fa0SAndres Lagar-Cavilla /* mmap_batch_fn guarantees ret == 0 */ 5564e8c0c8cSDavid Vrabel BUG_ON(traverse_pages_block(m.num, sizeof(xen_pfn_t), 557ceb90fa0SAndres Lagar-Cavilla &pagelist, mmap_batch_fn, &state)); 558d8414d3cSBastian Blank 559d8414d3cSBastian Blank up_write(&mm->mmap_sem); 560d8414d3cSBastian Blank 56168fa965dSMats Petersson if (state.global_error) { 562ceb90fa0SAndres Lagar-Cavilla /* Write back errors in second pass. */ 563a13d7201SJulien Grall state.user_gfn = (xen_pfn_t *)m.arr; 56499beae6cSAndres Lagar-Cavilla state.user_err = m.err; 5654e8c0c8cSDavid Vrabel ret = traverse_pages_block(m.num, sizeof(xen_pfn_t), 56699beae6cSAndres Lagar-Cavilla &pagelist, mmap_return_errors, &state); 56768fa965dSMats Petersson } else 56868fa965dSMats Petersson ret = 0; 56968fa965dSMats Petersson 570ceb90fa0SAndres Lagar-Cavilla /* If we have not had any EFAULT-like global errors then set the global 571ceb90fa0SAndres Lagar-Cavilla * error to -ENOENT if necessary. */ 572ceb90fa0SAndres Lagar-Cavilla if ((ret == 0) && (state.global_error == -ENOENT)) 573ceb90fa0SAndres Lagar-Cavilla ret = -ENOENT; 574d8414d3cSBastian Blank 575d8414d3cSBastian Blank out: 576d8414d3cSBastian Blank free_page_list(&pagelist); 577d8414d3cSBastian Blank return ret; 578a5deabe0SAndres Lagar-Cavilla 579a5deabe0SAndres Lagar-Cavilla out_unlock: 580a5deabe0SAndres Lagar-Cavilla up_write(&mm->mmap_sem); 581a5deabe0SAndres Lagar-Cavilla goto out; 582d8414d3cSBastian Blank } 583d8414d3cSBastian Blank 584ab520be8SPaul Durrant static int lock_pages( 585ab520be8SPaul Durrant struct privcmd_dm_op_buf kbufs[], unsigned int num, 586ab520be8SPaul Durrant struct page *pages[], unsigned int nr_pages) 587ab520be8SPaul Durrant { 588ab520be8SPaul Durrant unsigned int i; 589ab520be8SPaul Durrant 590ab520be8SPaul Durrant for (i = 0; i < num; i++) { 591ab520be8SPaul Durrant unsigned int requested; 592ab520be8SPaul Durrant int pinned; 593ab520be8SPaul Durrant 594ab520be8SPaul Durrant requested = DIV_ROUND_UP( 595ab520be8SPaul Durrant offset_in_page(kbufs[i].uptr) + kbufs[i].size, 596ab520be8SPaul Durrant PAGE_SIZE); 597ab520be8SPaul Durrant if (requested > nr_pages) 598ab520be8SPaul Durrant return -ENOSPC; 599ab520be8SPaul Durrant 600ab520be8SPaul Durrant pinned = get_user_pages_fast( 601ab520be8SPaul Durrant (unsigned long) kbufs[i].uptr, 602ab520be8SPaul Durrant requested, FOLL_WRITE, pages); 603ab520be8SPaul Durrant if (pinned < 0) 604ab520be8SPaul Durrant return pinned; 605ab520be8SPaul Durrant 606ab520be8SPaul Durrant nr_pages -= pinned; 607ab520be8SPaul Durrant pages += pinned; 608ab520be8SPaul Durrant } 609ab520be8SPaul Durrant 610ab520be8SPaul Durrant return 0; 611ab520be8SPaul Durrant } 612ab520be8SPaul Durrant 613ab520be8SPaul Durrant static void unlock_pages(struct page *pages[], unsigned int nr_pages) 614ab520be8SPaul Durrant { 615ab520be8SPaul Durrant unsigned int i; 616ab520be8SPaul Durrant 617ab520be8SPaul Durrant if (!pages) 618ab520be8SPaul Durrant return; 619ab520be8SPaul Durrant 620ab520be8SPaul Durrant for (i = 0; i < nr_pages; i++) { 621ab520be8SPaul Durrant if (pages[i]) 622ab520be8SPaul Durrant put_page(pages[i]); 623ab520be8SPaul Durrant } 624ab520be8SPaul Durrant } 625ab520be8SPaul Durrant 6264610d240SPaul Durrant static long privcmd_ioctl_dm_op(struct file *file, void __user *udata) 627ab520be8SPaul Durrant { 6284610d240SPaul Durrant struct privcmd_data *data = file->private_data; 629ab520be8SPaul Durrant struct privcmd_dm_op kdata; 630ab520be8SPaul Durrant struct privcmd_dm_op_buf *kbufs; 631ab520be8SPaul Durrant unsigned int nr_pages = 0; 632ab520be8SPaul Durrant struct page **pages = NULL; 633ab520be8SPaul Durrant struct xen_dm_op_buf *xbufs = NULL; 634ab520be8SPaul Durrant unsigned int i; 635ab520be8SPaul Durrant long rc; 636ab520be8SPaul Durrant 637ab520be8SPaul Durrant if (copy_from_user(&kdata, udata, sizeof(kdata))) 638ab520be8SPaul Durrant return -EFAULT; 639ab520be8SPaul Durrant 6404610d240SPaul Durrant /* If restriction is in place, check the domid matches */ 6414610d240SPaul Durrant if (data->domid != DOMID_INVALID && data->domid != kdata.dom) 6424610d240SPaul Durrant return -EPERM; 6434610d240SPaul Durrant 644ab520be8SPaul Durrant if (kdata.num == 0) 645ab520be8SPaul Durrant return 0; 646ab520be8SPaul Durrant 647ab520be8SPaul Durrant if (kdata.num > privcmd_dm_op_max_num) 648ab520be8SPaul Durrant return -E2BIG; 649ab520be8SPaul Durrant 650ab520be8SPaul Durrant kbufs = kcalloc(kdata.num, sizeof(*kbufs), GFP_KERNEL); 651ab520be8SPaul Durrant if (!kbufs) 652ab520be8SPaul Durrant return -ENOMEM; 653ab520be8SPaul Durrant 654ab520be8SPaul Durrant if (copy_from_user(kbufs, kdata.ubufs, 655ab520be8SPaul Durrant sizeof(*kbufs) * kdata.num)) { 656ab520be8SPaul Durrant rc = -EFAULT; 657ab520be8SPaul Durrant goto out; 658ab520be8SPaul Durrant } 659ab520be8SPaul Durrant 660ab520be8SPaul Durrant for (i = 0; i < kdata.num; i++) { 661ab520be8SPaul Durrant if (kbufs[i].size > privcmd_dm_op_buf_max_size) { 662ab520be8SPaul Durrant rc = -E2BIG; 663ab520be8SPaul Durrant goto out; 664ab520be8SPaul Durrant } 665ab520be8SPaul Durrant 666ab520be8SPaul Durrant if (!access_ok(VERIFY_WRITE, kbufs[i].uptr, 667ab520be8SPaul Durrant kbufs[i].size)) { 668ab520be8SPaul Durrant rc = -EFAULT; 669ab520be8SPaul Durrant goto out; 670ab520be8SPaul Durrant } 671ab520be8SPaul Durrant 672ab520be8SPaul Durrant nr_pages += DIV_ROUND_UP( 673ab520be8SPaul Durrant offset_in_page(kbufs[i].uptr) + kbufs[i].size, 674ab520be8SPaul Durrant PAGE_SIZE); 675ab520be8SPaul Durrant } 676ab520be8SPaul Durrant 677ab520be8SPaul Durrant pages = kcalloc(nr_pages, sizeof(*pages), GFP_KERNEL); 678ab520be8SPaul Durrant if (!pages) { 679ab520be8SPaul Durrant rc = -ENOMEM; 680ab520be8SPaul Durrant goto out; 681ab520be8SPaul Durrant } 682ab520be8SPaul Durrant 683ab520be8SPaul Durrant xbufs = kcalloc(kdata.num, sizeof(*xbufs), GFP_KERNEL); 684ab520be8SPaul Durrant if (!xbufs) { 685ab520be8SPaul Durrant rc = -ENOMEM; 686ab520be8SPaul Durrant goto out; 687ab520be8SPaul Durrant } 688ab520be8SPaul Durrant 689ab520be8SPaul Durrant rc = lock_pages(kbufs, kdata.num, pages, nr_pages); 690ab520be8SPaul Durrant if (rc) 691ab520be8SPaul Durrant goto out; 692ab520be8SPaul Durrant 693ab520be8SPaul Durrant for (i = 0; i < kdata.num; i++) { 694ab520be8SPaul Durrant set_xen_guest_handle(xbufs[i].h, kbufs[i].uptr); 695ab520be8SPaul Durrant xbufs[i].size = kbufs[i].size; 696ab520be8SPaul Durrant } 697ab520be8SPaul Durrant 698ab520be8SPaul Durrant xen_preemptible_hcall_begin(); 699ab520be8SPaul Durrant rc = HYPERVISOR_dm_op(kdata.dom, kdata.num, xbufs); 700ab520be8SPaul Durrant xen_preemptible_hcall_end(); 701ab520be8SPaul Durrant 702ab520be8SPaul Durrant out: 703ab520be8SPaul Durrant unlock_pages(pages, nr_pages); 704ab520be8SPaul Durrant kfree(xbufs); 705ab520be8SPaul Durrant kfree(pages); 706ab520be8SPaul Durrant kfree(kbufs); 707ab520be8SPaul Durrant 708ab520be8SPaul Durrant return rc; 709ab520be8SPaul Durrant } 710ab520be8SPaul Durrant 7114610d240SPaul Durrant static long privcmd_ioctl_restrict(struct file *file, void __user *udata) 7124610d240SPaul Durrant { 7134610d240SPaul Durrant struct privcmd_data *data = file->private_data; 7144610d240SPaul Durrant domid_t dom; 7154610d240SPaul Durrant 7164610d240SPaul Durrant if (copy_from_user(&dom, udata, sizeof(dom))) 7174610d240SPaul Durrant return -EFAULT; 7184610d240SPaul Durrant 7194610d240SPaul Durrant /* Set restriction to the specified domain, or check it matches */ 7204610d240SPaul Durrant if (data->domid == DOMID_INVALID) 7214610d240SPaul Durrant data->domid = dom; 7224610d240SPaul Durrant else if (data->domid != dom) 7234610d240SPaul Durrant return -EINVAL; 7244610d240SPaul Durrant 7254610d240SPaul Durrant return 0; 7264610d240SPaul Durrant } 7274610d240SPaul Durrant 728d8414d3cSBastian Blank static long privcmd_ioctl(struct file *file, 729d8414d3cSBastian Blank unsigned int cmd, unsigned long data) 730d8414d3cSBastian Blank { 731dc9eab6fSPaul Durrant int ret = -ENOTTY; 732d8414d3cSBastian Blank void __user *udata = (void __user *) data; 733d8414d3cSBastian Blank 734d8414d3cSBastian Blank switch (cmd) { 735d8414d3cSBastian Blank case IOCTL_PRIVCMD_HYPERCALL: 7364610d240SPaul Durrant ret = privcmd_ioctl_hypercall(file, udata); 737d8414d3cSBastian Blank break; 738d8414d3cSBastian Blank 739d8414d3cSBastian Blank case IOCTL_PRIVCMD_MMAP: 7404610d240SPaul Durrant ret = privcmd_ioctl_mmap(file, udata); 741d8414d3cSBastian Blank break; 742d8414d3cSBastian Blank 743d8414d3cSBastian Blank case IOCTL_PRIVCMD_MMAPBATCH: 7444610d240SPaul Durrant ret = privcmd_ioctl_mmap_batch(file, udata, 1); 745ceb90fa0SAndres Lagar-Cavilla break; 746ceb90fa0SAndres Lagar-Cavilla 747ceb90fa0SAndres Lagar-Cavilla case IOCTL_PRIVCMD_MMAPBATCH_V2: 7484610d240SPaul Durrant ret = privcmd_ioctl_mmap_batch(file, udata, 2); 749d8414d3cSBastian Blank break; 750d8414d3cSBastian Blank 751ab520be8SPaul Durrant case IOCTL_PRIVCMD_DM_OP: 7524610d240SPaul Durrant ret = privcmd_ioctl_dm_op(file, udata); 7534610d240SPaul Durrant break; 7544610d240SPaul Durrant 7554610d240SPaul Durrant case IOCTL_PRIVCMD_RESTRICT: 7564610d240SPaul Durrant ret = privcmd_ioctl_restrict(file, udata); 757ab520be8SPaul Durrant break; 758ab520be8SPaul Durrant 759d8414d3cSBastian Blank default: 760d8414d3cSBastian Blank break; 761d8414d3cSBastian Blank } 762d8414d3cSBastian Blank 763d8414d3cSBastian Blank return ret; 764d8414d3cSBastian Blank } 765d8414d3cSBastian Blank 7664610d240SPaul Durrant static int privcmd_open(struct inode *ino, struct file *file) 7674610d240SPaul Durrant { 7684610d240SPaul Durrant struct privcmd_data *data = kzalloc(sizeof(*data), GFP_KERNEL); 7694610d240SPaul Durrant 7704610d240SPaul Durrant if (!data) 7714610d240SPaul Durrant return -ENOMEM; 7724610d240SPaul Durrant 7734610d240SPaul Durrant /* DOMID_INVALID implies no restriction */ 7744610d240SPaul Durrant data->domid = DOMID_INVALID; 7754610d240SPaul Durrant 7764610d240SPaul Durrant file->private_data = data; 7774610d240SPaul Durrant return 0; 7784610d240SPaul Durrant } 7794610d240SPaul Durrant 7804610d240SPaul Durrant static int privcmd_release(struct inode *ino, struct file *file) 7814610d240SPaul Durrant { 7824610d240SPaul Durrant struct privcmd_data *data = file->private_data; 7834610d240SPaul Durrant 7844610d240SPaul Durrant kfree(data); 7854610d240SPaul Durrant return 0; 7864610d240SPaul Durrant } 7874610d240SPaul Durrant 788d71f5139SMukesh Rathor static void privcmd_close(struct vm_area_struct *vma) 789d71f5139SMukesh Rathor { 790d71f5139SMukesh Rathor struct page **pages = vma->vm_private_data; 791c7ebf9d9SMuhammad Falak R Wani int numpgs = vma_pages(vma); 7925995a68aSJulien Grall int numgfns = (vma->vm_end - vma->vm_start) >> XEN_PAGE_SHIFT; 793b6497b38SIan Campbell int rc; 794d71f5139SMukesh Rathor 7959eff37a8SDan Carpenter if (!xen_feature(XENFEAT_auto_translated_physmap) || !numpgs || !pages) 796d71f5139SMukesh Rathor return; 797d71f5139SMukesh Rathor 7985995a68aSJulien Grall rc = xen_unmap_domain_gfn_range(vma, numgfns, pages); 799b6497b38SIan Campbell if (rc == 0) 800d71f5139SMukesh Rathor free_xenballooned_pages(numpgs, pages); 801b6497b38SIan Campbell else 802b6497b38SIan Campbell pr_crit("unable to unmap MFN range: leaking %d pages. rc=%d\n", 803b6497b38SIan Campbell numpgs, rc); 804d71f5139SMukesh Rathor kfree(pages); 805d71f5139SMukesh Rathor } 806d71f5139SMukesh Rathor 80711bac800SDave Jiang static int privcmd_fault(struct vm_fault *vmf) 808d8414d3cSBastian Blank { 809d8414d3cSBastian Blank printk(KERN_DEBUG "privcmd_fault: vma=%p %lx-%lx, pgoff=%lx, uv=%p\n", 81011bac800SDave Jiang vmf->vma, vmf->vma->vm_start, vmf->vma->vm_end, 8111a29d85eSJan Kara vmf->pgoff, (void *)vmf->address); 812d8414d3cSBastian Blank 813d8414d3cSBastian Blank return VM_FAULT_SIGBUS; 814d8414d3cSBastian Blank } 815d8414d3cSBastian Blank 8167cbea8dcSKirill A. Shutemov static const struct vm_operations_struct privcmd_vm_ops = { 817d71f5139SMukesh Rathor .close = privcmd_close, 818d8414d3cSBastian Blank .fault = privcmd_fault 819d8414d3cSBastian Blank }; 820d8414d3cSBastian Blank 821d8414d3cSBastian Blank static int privcmd_mmap(struct file *file, struct vm_area_struct *vma) 822d8414d3cSBastian Blank { 823d8414d3cSBastian Blank /* DONTCOPY is essential for Xen because copy_page_range doesn't know 824d8414d3cSBastian Blank * how to recreate these mappings */ 825314e51b9SKonstantin Khlebnikov vma->vm_flags |= VM_IO | VM_PFNMAP | VM_DONTCOPY | 826314e51b9SKonstantin Khlebnikov VM_DONTEXPAND | VM_DONTDUMP; 827d8414d3cSBastian Blank vma->vm_ops = &privcmd_vm_ops; 828d8414d3cSBastian Blank vma->vm_private_data = NULL; 829d8414d3cSBastian Blank 830d8414d3cSBastian Blank return 0; 831d8414d3cSBastian Blank } 832d8414d3cSBastian Blank 833a5deabe0SAndres Lagar-Cavilla /* 834a5deabe0SAndres Lagar-Cavilla * For MMAPBATCH*. This allows asserting the singleshot mapping 835a5deabe0SAndres Lagar-Cavilla * on a per pfn/pte basis. Mapping calls that fail with ENOENT 836a5deabe0SAndres Lagar-Cavilla * can be then retried until success. 837a5deabe0SAndres Lagar-Cavilla */ 838a5deabe0SAndres Lagar-Cavilla static int is_mapped_fn(pte_t *pte, struct page *pmd_page, 839a5deabe0SAndres Lagar-Cavilla unsigned long addr, void *data) 840d8414d3cSBastian Blank { 841a5deabe0SAndres Lagar-Cavilla return pte_none(*pte) ? 0 : -EBUSY; 842a5deabe0SAndres Lagar-Cavilla } 843a5deabe0SAndres Lagar-Cavilla 844a5deabe0SAndres Lagar-Cavilla static int privcmd_vma_range_is_mapped( 845a5deabe0SAndres Lagar-Cavilla struct vm_area_struct *vma, 846a5deabe0SAndres Lagar-Cavilla unsigned long addr, 847a5deabe0SAndres Lagar-Cavilla unsigned long nr_pages) 848a5deabe0SAndres Lagar-Cavilla { 849a5deabe0SAndres Lagar-Cavilla return apply_to_page_range(vma->vm_mm, addr, nr_pages << PAGE_SHIFT, 850a5deabe0SAndres Lagar-Cavilla is_mapped_fn, NULL) != 0; 851d8414d3cSBastian Blank } 852d8414d3cSBastian Blank 853d8414d3cSBastian Blank const struct file_operations xen_privcmd_fops = { 854d8414d3cSBastian Blank .owner = THIS_MODULE, 855d8414d3cSBastian Blank .unlocked_ioctl = privcmd_ioctl, 8564610d240SPaul Durrant .open = privcmd_open, 8574610d240SPaul Durrant .release = privcmd_release, 858d8414d3cSBastian Blank .mmap = privcmd_mmap, 859d8414d3cSBastian Blank }; 860d8414d3cSBastian Blank EXPORT_SYMBOL_GPL(xen_privcmd_fops); 861d8414d3cSBastian Blank 862d8414d3cSBastian Blank static struct miscdevice privcmd_dev = { 863d8414d3cSBastian Blank .minor = MISC_DYNAMIC_MINOR, 864d8414d3cSBastian Blank .name = "xen/privcmd", 865d8414d3cSBastian Blank .fops = &xen_privcmd_fops, 866d8414d3cSBastian Blank }; 867d8414d3cSBastian Blank 868d8414d3cSBastian Blank static int __init privcmd_init(void) 869d8414d3cSBastian Blank { 870d8414d3cSBastian Blank int err; 871d8414d3cSBastian Blank 872d8414d3cSBastian Blank if (!xen_domain()) 873d8414d3cSBastian Blank return -ENODEV; 874d8414d3cSBastian Blank 875d8414d3cSBastian Blank err = misc_register(&privcmd_dev); 876d8414d3cSBastian Blank if (err != 0) { 877283c0972SJoe Perches pr_err("Could not register Xen privcmd device\n"); 878d8414d3cSBastian Blank return err; 879d8414d3cSBastian Blank } 880d8414d3cSBastian Blank return 0; 881d8414d3cSBastian Blank } 882d8414d3cSBastian Blank 883d8414d3cSBastian Blank static void __exit privcmd_exit(void) 884d8414d3cSBastian Blank { 885d8414d3cSBastian Blank misc_deregister(&privcmd_dev); 886d8414d3cSBastian Blank } 887d8414d3cSBastian Blank 888d8414d3cSBastian Blank module_init(privcmd_init); 889d8414d3cSBastian Blank module_exit(privcmd_exit); 890