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 9*283c0972SJoe Perches #define pr_fmt(fmt) "xen:" KBUILD_MODNAME ": " fmt 10*283c0972SJoe 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> 25d8414d3cSBastian Blank 26d8414d3cSBastian Blank #include <asm/pgalloc.h> 27d8414d3cSBastian Blank #include <asm/pgtable.h> 28d8414d3cSBastian Blank #include <asm/tlb.h> 29d8414d3cSBastian Blank #include <asm/xen/hypervisor.h> 30d8414d3cSBastian Blank #include <asm/xen/hypercall.h> 31d8414d3cSBastian Blank 32d8414d3cSBastian Blank #include <xen/xen.h> 33d8414d3cSBastian Blank #include <xen/privcmd.h> 34d8414d3cSBastian Blank #include <xen/interface/xen.h> 35d8414d3cSBastian Blank #include <xen/features.h> 36d8414d3cSBastian Blank #include <xen/page.h> 37d8414d3cSBastian Blank #include <xen/xen-ops.h> 38d71f5139SMukesh Rathor #include <xen/balloon.h> 39d8414d3cSBastian Blank 40d8414d3cSBastian Blank #include "privcmd.h" 41d8414d3cSBastian Blank 42d8414d3cSBastian Blank MODULE_LICENSE("GPL"); 43d8414d3cSBastian Blank 44d71f5139SMukesh Rathor #define PRIV_VMA_LOCKED ((void *)1) 45d71f5139SMukesh Rathor 46d8414d3cSBastian Blank #ifndef HAVE_ARCH_PRIVCMD_MMAP 47d8414d3cSBastian Blank static int privcmd_enforce_singleshot_mapping(struct vm_area_struct *vma); 48d8414d3cSBastian Blank #endif 49d8414d3cSBastian Blank 50d8414d3cSBastian Blank static long privcmd_ioctl_hypercall(void __user *udata) 51d8414d3cSBastian Blank { 52d8414d3cSBastian Blank struct privcmd_hypercall hypercall; 53d8414d3cSBastian Blank long ret; 54d8414d3cSBastian Blank 55d8414d3cSBastian Blank if (copy_from_user(&hypercall, udata, sizeof(hypercall))) 56d8414d3cSBastian Blank return -EFAULT; 57d8414d3cSBastian Blank 58d8414d3cSBastian Blank ret = privcmd_call(hypercall.op, 59d8414d3cSBastian Blank hypercall.arg[0], hypercall.arg[1], 60d8414d3cSBastian Blank hypercall.arg[2], hypercall.arg[3], 61d8414d3cSBastian Blank hypercall.arg[4]); 62d8414d3cSBastian Blank 63d8414d3cSBastian Blank return ret; 64d8414d3cSBastian Blank } 65d8414d3cSBastian Blank 66d8414d3cSBastian Blank static void free_page_list(struct list_head *pages) 67d8414d3cSBastian Blank { 68d8414d3cSBastian Blank struct page *p, *n; 69d8414d3cSBastian Blank 70d8414d3cSBastian Blank list_for_each_entry_safe(p, n, pages, lru) 71d8414d3cSBastian Blank __free_page(p); 72d8414d3cSBastian Blank 73d8414d3cSBastian Blank INIT_LIST_HEAD(pages); 74d8414d3cSBastian Blank } 75d8414d3cSBastian Blank 76d8414d3cSBastian Blank /* 77d8414d3cSBastian Blank * Given an array of items in userspace, return a list of pages 78d8414d3cSBastian Blank * containing the data. If copying fails, either because of memory 79d8414d3cSBastian Blank * allocation failure or a problem reading user memory, return an 80d8414d3cSBastian Blank * error code; its up to the caller to dispose of any partial list. 81d8414d3cSBastian Blank */ 82d8414d3cSBastian Blank static int gather_array(struct list_head *pagelist, 83d8414d3cSBastian Blank unsigned nelem, size_t size, 84ceb90fa0SAndres Lagar-Cavilla const void __user *data) 85d8414d3cSBastian Blank { 86d8414d3cSBastian Blank unsigned pageidx; 87d8414d3cSBastian Blank void *pagedata; 88d8414d3cSBastian Blank int ret; 89d8414d3cSBastian Blank 90d8414d3cSBastian Blank if (size > PAGE_SIZE) 91d8414d3cSBastian Blank return 0; 92d8414d3cSBastian Blank 93d8414d3cSBastian Blank pageidx = PAGE_SIZE; 94d8414d3cSBastian Blank pagedata = NULL; /* quiet, gcc */ 95d8414d3cSBastian Blank while (nelem--) { 96d8414d3cSBastian Blank if (pageidx > PAGE_SIZE-size) { 97d8414d3cSBastian Blank struct page *page = alloc_page(GFP_KERNEL); 98d8414d3cSBastian Blank 99d8414d3cSBastian Blank ret = -ENOMEM; 100d8414d3cSBastian Blank if (page == NULL) 101d8414d3cSBastian Blank goto fail; 102d8414d3cSBastian Blank 103d8414d3cSBastian Blank pagedata = page_address(page); 104d8414d3cSBastian Blank 105d8414d3cSBastian Blank list_add_tail(&page->lru, pagelist); 106d8414d3cSBastian Blank pageidx = 0; 107d8414d3cSBastian Blank } 108d8414d3cSBastian Blank 109d8414d3cSBastian Blank ret = -EFAULT; 110d8414d3cSBastian Blank if (copy_from_user(pagedata + pageidx, data, size)) 111d8414d3cSBastian Blank goto fail; 112d8414d3cSBastian Blank 113d8414d3cSBastian Blank data += size; 114d8414d3cSBastian Blank pageidx += size; 115d8414d3cSBastian Blank } 116d8414d3cSBastian Blank 117d8414d3cSBastian Blank ret = 0; 118d8414d3cSBastian Blank 119d8414d3cSBastian Blank fail: 120d8414d3cSBastian Blank return ret; 121d8414d3cSBastian Blank } 122d8414d3cSBastian Blank 123d8414d3cSBastian Blank /* 124d8414d3cSBastian Blank * Call function "fn" on each element of the array fragmented 125d8414d3cSBastian Blank * over a list of pages. 126d8414d3cSBastian Blank */ 127d8414d3cSBastian Blank static int traverse_pages(unsigned nelem, size_t size, 128d8414d3cSBastian Blank struct list_head *pos, 129d8414d3cSBastian Blank int (*fn)(void *data, void *state), 130d8414d3cSBastian Blank void *state) 131d8414d3cSBastian Blank { 132d8414d3cSBastian Blank void *pagedata; 133d8414d3cSBastian Blank unsigned pageidx; 134d8414d3cSBastian Blank int ret = 0; 135d8414d3cSBastian Blank 136d8414d3cSBastian Blank BUG_ON(size > PAGE_SIZE); 137d8414d3cSBastian Blank 138d8414d3cSBastian Blank pageidx = PAGE_SIZE; 139d8414d3cSBastian Blank pagedata = NULL; /* hush, gcc */ 140d8414d3cSBastian Blank 141d8414d3cSBastian Blank while (nelem--) { 142d8414d3cSBastian Blank if (pageidx > PAGE_SIZE-size) { 143d8414d3cSBastian Blank struct page *page; 144d8414d3cSBastian Blank pos = pos->next; 145d8414d3cSBastian Blank page = list_entry(pos, struct page, lru); 146d8414d3cSBastian Blank pagedata = page_address(page); 147d8414d3cSBastian Blank pageidx = 0; 148d8414d3cSBastian Blank } 149d8414d3cSBastian Blank 150d8414d3cSBastian Blank ret = (*fn)(pagedata + pageidx, state); 151d8414d3cSBastian Blank if (ret) 152d8414d3cSBastian Blank break; 153d8414d3cSBastian Blank pageidx += size; 154d8414d3cSBastian Blank } 155d8414d3cSBastian Blank 156d8414d3cSBastian Blank return ret; 157d8414d3cSBastian Blank } 158d8414d3cSBastian Blank 159d8414d3cSBastian Blank struct mmap_mfn_state { 160d8414d3cSBastian Blank unsigned long va; 161d8414d3cSBastian Blank struct vm_area_struct *vma; 162d8414d3cSBastian Blank domid_t domain; 163d8414d3cSBastian Blank }; 164d8414d3cSBastian Blank 165d8414d3cSBastian Blank static int mmap_mfn_range(void *data, void *state) 166d8414d3cSBastian Blank { 167d8414d3cSBastian Blank struct privcmd_mmap_entry *msg = data; 168d8414d3cSBastian Blank struct mmap_mfn_state *st = state; 169d8414d3cSBastian Blank struct vm_area_struct *vma = st->vma; 170d8414d3cSBastian Blank int rc; 171d8414d3cSBastian Blank 172d8414d3cSBastian Blank /* Do not allow range to wrap the address space. */ 173d8414d3cSBastian Blank if ((msg->npages > (LONG_MAX >> PAGE_SHIFT)) || 174d8414d3cSBastian Blank ((unsigned long)(msg->npages << PAGE_SHIFT) >= -st->va)) 175d8414d3cSBastian Blank return -EINVAL; 176d8414d3cSBastian Blank 177d8414d3cSBastian Blank /* Range chunks must be contiguous in va space. */ 178d8414d3cSBastian Blank if ((msg->va != st->va) || 179d8414d3cSBastian Blank ((msg->va+(msg->npages<<PAGE_SHIFT)) > vma->vm_end)) 180d8414d3cSBastian Blank return -EINVAL; 181d8414d3cSBastian Blank 182d8414d3cSBastian Blank rc = xen_remap_domain_mfn_range(vma, 183d8414d3cSBastian Blank msg->va & PAGE_MASK, 184d8414d3cSBastian Blank msg->mfn, msg->npages, 185d8414d3cSBastian Blank vma->vm_page_prot, 1869a032e39SIan Campbell st->domain, NULL); 187d8414d3cSBastian Blank if (rc < 0) 188d8414d3cSBastian Blank return rc; 189d8414d3cSBastian Blank 190d8414d3cSBastian Blank st->va += msg->npages << PAGE_SHIFT; 191d8414d3cSBastian Blank 192d8414d3cSBastian Blank return 0; 193d8414d3cSBastian Blank } 194d8414d3cSBastian Blank 195d8414d3cSBastian Blank static long privcmd_ioctl_mmap(void __user *udata) 196d8414d3cSBastian Blank { 197d8414d3cSBastian Blank struct privcmd_mmap mmapcmd; 198d8414d3cSBastian Blank struct mm_struct *mm = current->mm; 199d8414d3cSBastian Blank struct vm_area_struct *vma; 200d8414d3cSBastian Blank int rc; 201d8414d3cSBastian Blank LIST_HEAD(pagelist); 202d8414d3cSBastian Blank struct mmap_mfn_state state; 203d8414d3cSBastian Blank 204d71f5139SMukesh Rathor /* We only support privcmd_ioctl_mmap_batch for auto translated. */ 205d71f5139SMukesh Rathor if (xen_feature(XENFEAT_auto_translated_physmap)) 206d71f5139SMukesh Rathor return -ENOSYS; 207d71f5139SMukesh Rathor 208d8414d3cSBastian Blank if (copy_from_user(&mmapcmd, udata, sizeof(mmapcmd))) 209d8414d3cSBastian Blank return -EFAULT; 210d8414d3cSBastian Blank 211d8414d3cSBastian Blank rc = gather_array(&pagelist, 212d8414d3cSBastian Blank mmapcmd.num, sizeof(struct privcmd_mmap_entry), 213d8414d3cSBastian Blank mmapcmd.entry); 214d8414d3cSBastian Blank 215d8414d3cSBastian Blank if (rc || list_empty(&pagelist)) 216d8414d3cSBastian Blank goto out; 217d8414d3cSBastian Blank 218d8414d3cSBastian Blank down_write(&mm->mmap_sem); 219d8414d3cSBastian Blank 220d8414d3cSBastian Blank { 221d8414d3cSBastian Blank struct page *page = list_first_entry(&pagelist, 222d8414d3cSBastian Blank struct page, lru); 223d8414d3cSBastian Blank struct privcmd_mmap_entry *msg = page_address(page); 224d8414d3cSBastian Blank 225d8414d3cSBastian Blank vma = find_vma(mm, msg->va); 226d8414d3cSBastian Blank rc = -EINVAL; 227d8414d3cSBastian Blank 228d8414d3cSBastian Blank if (!vma || (msg->va != vma->vm_start) || 229d8414d3cSBastian Blank !privcmd_enforce_singleshot_mapping(vma)) 230d8414d3cSBastian Blank goto out_up; 231d8414d3cSBastian Blank } 232d8414d3cSBastian Blank 233d8414d3cSBastian Blank state.va = vma->vm_start; 234d8414d3cSBastian Blank state.vma = vma; 235d8414d3cSBastian Blank state.domain = mmapcmd.dom; 236d8414d3cSBastian Blank 237d8414d3cSBastian Blank rc = traverse_pages(mmapcmd.num, sizeof(struct privcmd_mmap_entry), 238d8414d3cSBastian Blank &pagelist, 239d8414d3cSBastian Blank mmap_mfn_range, &state); 240d8414d3cSBastian Blank 241d8414d3cSBastian Blank 242d8414d3cSBastian Blank out_up: 243d8414d3cSBastian Blank up_write(&mm->mmap_sem); 244d8414d3cSBastian Blank 245d8414d3cSBastian Blank out: 246d8414d3cSBastian Blank free_page_list(&pagelist); 247d8414d3cSBastian Blank 248d8414d3cSBastian Blank return rc; 249d8414d3cSBastian Blank } 250d8414d3cSBastian Blank 251d8414d3cSBastian Blank struct mmap_batch_state { 252d8414d3cSBastian Blank domid_t domain; 253d8414d3cSBastian Blank unsigned long va; 254d8414d3cSBastian Blank struct vm_area_struct *vma; 255d71f5139SMukesh Rathor int index; 256ceb90fa0SAndres Lagar-Cavilla /* A tristate: 257ceb90fa0SAndres Lagar-Cavilla * 0 for no errors 258ceb90fa0SAndres Lagar-Cavilla * 1 if at least one error has happened (and no 259ceb90fa0SAndres Lagar-Cavilla * -ENOENT errors have happened) 260ceb90fa0SAndres Lagar-Cavilla * -ENOENT if at least 1 -ENOENT has happened. 261ceb90fa0SAndres Lagar-Cavilla */ 262ceb90fa0SAndres Lagar-Cavilla int global_error; 26399beae6cSAndres Lagar-Cavilla int version; 264d8414d3cSBastian Blank 265ceb90fa0SAndres Lagar-Cavilla /* User-space mfn array to store errors in the second pass for V1. */ 266ceb90fa0SAndres Lagar-Cavilla xen_pfn_t __user *user_mfn; 26799beae6cSAndres Lagar-Cavilla /* User-space int array to store errors in the second pass for V2. */ 26899beae6cSAndres Lagar-Cavilla int __user *user_err; 269d8414d3cSBastian Blank }; 270d8414d3cSBastian Blank 271d71f5139SMukesh Rathor /* auto translated dom0 note: if domU being created is PV, then mfn is 272d71f5139SMukesh Rathor * mfn(addr on bus). If it's auto xlated, then mfn is pfn (input to HAP). 273d71f5139SMukesh Rathor */ 274d8414d3cSBastian Blank static int mmap_batch_fn(void *data, void *state) 275d8414d3cSBastian Blank { 276d8414d3cSBastian Blank xen_pfn_t *mfnp = data; 277d8414d3cSBastian Blank struct mmap_batch_state *st = state; 278d71f5139SMukesh Rathor struct vm_area_struct *vma = st->vma; 279d71f5139SMukesh Rathor struct page **pages = vma->vm_private_data; 280d71f5139SMukesh Rathor struct page *cur_page = NULL; 281ceb90fa0SAndres Lagar-Cavilla int ret; 282d8414d3cSBastian Blank 283d71f5139SMukesh Rathor if (xen_feature(XENFEAT_auto_translated_physmap)) 284d71f5139SMukesh Rathor cur_page = pages[st->index++]; 285d71f5139SMukesh Rathor 286ceb90fa0SAndres Lagar-Cavilla ret = xen_remap_domain_mfn_range(st->vma, st->va & PAGE_MASK, *mfnp, 1, 2879a032e39SIan Campbell st->vma->vm_page_prot, st->domain, 288d71f5139SMukesh Rathor &cur_page); 289ceb90fa0SAndres Lagar-Cavilla 290ceb90fa0SAndres Lagar-Cavilla /* Store error code for second pass. */ 29199beae6cSAndres Lagar-Cavilla if (st->version == 1) { 29299beae6cSAndres Lagar-Cavilla if (ret < 0) { 29399beae6cSAndres Lagar-Cavilla /* 29499beae6cSAndres Lagar-Cavilla * V1 encodes the error codes in the 32bit top nibble of the 29599beae6cSAndres Lagar-Cavilla * mfn (with its known limitations vis-a-vis 64 bit callers). 29699beae6cSAndres Lagar-Cavilla */ 29799beae6cSAndres Lagar-Cavilla *mfnp |= (ret == -ENOENT) ? 29899beae6cSAndres Lagar-Cavilla PRIVCMD_MMAPBATCH_PAGED_ERROR : 29999beae6cSAndres Lagar-Cavilla PRIVCMD_MMAPBATCH_MFN_ERROR; 30099beae6cSAndres Lagar-Cavilla } 30199beae6cSAndres Lagar-Cavilla } else { /* st->version == 2 */ 30299beae6cSAndres Lagar-Cavilla *((int *) mfnp) = ret; 30399beae6cSAndres Lagar-Cavilla } 304ceb90fa0SAndres Lagar-Cavilla 305ceb90fa0SAndres Lagar-Cavilla /* And see if it affects the global_error. */ 306ceb90fa0SAndres Lagar-Cavilla if (ret < 0) { 307ceb90fa0SAndres Lagar-Cavilla if (ret == -ENOENT) 308ceb90fa0SAndres Lagar-Cavilla st->global_error = -ENOENT; 309ceb90fa0SAndres Lagar-Cavilla else { 310ceb90fa0SAndres Lagar-Cavilla /* Record that at least one error has happened. */ 311ceb90fa0SAndres Lagar-Cavilla if (st->global_error == 0) 312ceb90fa0SAndres Lagar-Cavilla st->global_error = 1; 313ceb90fa0SAndres Lagar-Cavilla } 314d8414d3cSBastian Blank } 315d8414d3cSBastian Blank st->va += PAGE_SIZE; 316d8414d3cSBastian Blank 317d8414d3cSBastian Blank return 0; 318d8414d3cSBastian Blank } 319d8414d3cSBastian Blank 32099beae6cSAndres Lagar-Cavilla static int mmap_return_errors(void *data, void *state) 321d8414d3cSBastian Blank { 322d8414d3cSBastian Blank struct mmap_batch_state *st = state; 323d8414d3cSBastian Blank 32499beae6cSAndres Lagar-Cavilla if (st->version == 1) { 32599beae6cSAndres Lagar-Cavilla xen_pfn_t mfnp = *((xen_pfn_t *) data); 32699beae6cSAndres Lagar-Cavilla if (mfnp & PRIVCMD_MMAPBATCH_MFN_ERROR) 32799beae6cSAndres Lagar-Cavilla return __put_user(mfnp, st->user_mfn++); 32899beae6cSAndres Lagar-Cavilla else 32999beae6cSAndres Lagar-Cavilla st->user_mfn++; 33099beae6cSAndres Lagar-Cavilla } else { /* st->version == 2 */ 33199beae6cSAndres Lagar-Cavilla int err = *((int *) data); 33299beae6cSAndres Lagar-Cavilla if (err) 33399beae6cSAndres Lagar-Cavilla return __put_user(err, st->user_err++); 33499beae6cSAndres Lagar-Cavilla else 33599beae6cSAndres Lagar-Cavilla st->user_err++; 33699beae6cSAndres Lagar-Cavilla } 33799beae6cSAndres Lagar-Cavilla 33899beae6cSAndres Lagar-Cavilla return 0; 339d8414d3cSBastian Blank } 340d8414d3cSBastian Blank 341d71f5139SMukesh Rathor /* Allocate pfns that are then mapped with gmfns from foreign domid. Update 342d71f5139SMukesh Rathor * the vma with the page info to use later. 343d71f5139SMukesh Rathor * Returns: 0 if success, otherwise -errno 344d71f5139SMukesh Rathor */ 345d71f5139SMukesh Rathor static int alloc_empty_pages(struct vm_area_struct *vma, int numpgs) 346d71f5139SMukesh Rathor { 347d71f5139SMukesh Rathor int rc; 348d71f5139SMukesh Rathor struct page **pages; 349d71f5139SMukesh Rathor 350d71f5139SMukesh Rathor pages = kcalloc(numpgs, sizeof(pages[0]), GFP_KERNEL); 351d71f5139SMukesh Rathor if (pages == NULL) 352d71f5139SMukesh Rathor return -ENOMEM; 353d71f5139SMukesh Rathor 354d71f5139SMukesh Rathor rc = alloc_xenballooned_pages(numpgs, pages, 0); 355d71f5139SMukesh Rathor if (rc != 0) { 356d71f5139SMukesh Rathor pr_warn("%s Could not alloc %d pfns rc:%d\n", __func__, 357d71f5139SMukesh Rathor numpgs, rc); 358d71f5139SMukesh Rathor kfree(pages); 359d71f5139SMukesh Rathor return -ENOMEM; 360d71f5139SMukesh Rathor } 361d71f5139SMukesh Rathor BUG_ON(vma->vm_private_data != PRIV_VMA_LOCKED); 362d71f5139SMukesh Rathor vma->vm_private_data = pages; 363d71f5139SMukesh Rathor 364d71f5139SMukesh Rathor return 0; 365d71f5139SMukesh Rathor } 366d71f5139SMukesh Rathor 367d8414d3cSBastian Blank static struct vm_operations_struct privcmd_vm_ops; 368d8414d3cSBastian Blank 369ceb90fa0SAndres Lagar-Cavilla static long privcmd_ioctl_mmap_batch(void __user *udata, int version) 370d8414d3cSBastian Blank { 371d8414d3cSBastian Blank int ret; 372ceb90fa0SAndres Lagar-Cavilla struct privcmd_mmapbatch_v2 m; 373d8414d3cSBastian Blank struct mm_struct *mm = current->mm; 374d8414d3cSBastian Blank struct vm_area_struct *vma; 375d8414d3cSBastian Blank unsigned long nr_pages; 376d8414d3cSBastian Blank LIST_HEAD(pagelist); 377d8414d3cSBastian Blank struct mmap_batch_state state; 378d8414d3cSBastian Blank 379ceb90fa0SAndres Lagar-Cavilla switch (version) { 380ceb90fa0SAndres Lagar-Cavilla case 1: 381ceb90fa0SAndres Lagar-Cavilla if (copy_from_user(&m, udata, sizeof(struct privcmd_mmapbatch))) 382d8414d3cSBastian Blank return -EFAULT; 383ceb90fa0SAndres Lagar-Cavilla /* Returns per-frame error in m.arr. */ 384ceb90fa0SAndres Lagar-Cavilla m.err = NULL; 385ceb90fa0SAndres Lagar-Cavilla if (!access_ok(VERIFY_WRITE, m.arr, m.num * sizeof(*m.arr))) 386ceb90fa0SAndres Lagar-Cavilla return -EFAULT; 387ceb90fa0SAndres Lagar-Cavilla break; 388ceb90fa0SAndres Lagar-Cavilla case 2: 389ceb90fa0SAndres Lagar-Cavilla if (copy_from_user(&m, udata, sizeof(struct privcmd_mmapbatch_v2))) 390ceb90fa0SAndres Lagar-Cavilla return -EFAULT; 391ceb90fa0SAndres Lagar-Cavilla /* Returns per-frame error code in m.err. */ 392ceb90fa0SAndres Lagar-Cavilla if (!access_ok(VERIFY_WRITE, m.err, m.num * (sizeof(*m.err)))) 393ceb90fa0SAndres Lagar-Cavilla return -EFAULT; 394ceb90fa0SAndres Lagar-Cavilla break; 395ceb90fa0SAndres Lagar-Cavilla default: 396ceb90fa0SAndres Lagar-Cavilla return -EINVAL; 397ceb90fa0SAndres Lagar-Cavilla } 398d8414d3cSBastian Blank 399d8414d3cSBastian Blank nr_pages = m.num; 400d8414d3cSBastian Blank if ((m.num <= 0) || (nr_pages > (LONG_MAX >> PAGE_SHIFT))) 401d8414d3cSBastian Blank return -EINVAL; 402d8414d3cSBastian Blank 403ceb90fa0SAndres Lagar-Cavilla ret = gather_array(&pagelist, m.num, sizeof(xen_pfn_t), m.arr); 404d8414d3cSBastian Blank 405ceb90fa0SAndres Lagar-Cavilla if (ret) 406d8414d3cSBastian Blank goto out; 407ceb90fa0SAndres Lagar-Cavilla if (list_empty(&pagelist)) { 408ceb90fa0SAndres Lagar-Cavilla ret = -EINVAL; 409ceb90fa0SAndres Lagar-Cavilla goto out; 410ceb90fa0SAndres Lagar-Cavilla } 411ceb90fa0SAndres Lagar-Cavilla 41299beae6cSAndres Lagar-Cavilla if (version == 2) { 41399beae6cSAndres Lagar-Cavilla /* Zero error array now to only copy back actual errors. */ 41499beae6cSAndres Lagar-Cavilla if (clear_user(m.err, sizeof(int) * m.num)) { 41599beae6cSAndres Lagar-Cavilla ret = -EFAULT; 416ceb90fa0SAndres Lagar-Cavilla goto out; 417ceb90fa0SAndres Lagar-Cavilla } 41899beae6cSAndres Lagar-Cavilla } 419d8414d3cSBastian Blank 420d8414d3cSBastian Blank down_write(&mm->mmap_sem); 421d8414d3cSBastian Blank 422d8414d3cSBastian Blank vma = find_vma(mm, m.addr); 423d8414d3cSBastian Blank if (!vma || 424d8414d3cSBastian Blank vma->vm_ops != &privcmd_vm_ops || 425d8414d3cSBastian Blank (m.addr != vma->vm_start) || 426d8414d3cSBastian Blank ((m.addr + (nr_pages << PAGE_SHIFT)) != vma->vm_end) || 427d8414d3cSBastian Blank !privcmd_enforce_singleshot_mapping(vma)) { 428d8414d3cSBastian Blank up_write(&mm->mmap_sem); 42968fa965dSMats Petersson ret = -EINVAL; 430d8414d3cSBastian Blank goto out; 431d8414d3cSBastian Blank } 432d71f5139SMukesh Rathor if (xen_feature(XENFEAT_auto_translated_physmap)) { 433d71f5139SMukesh Rathor ret = alloc_empty_pages(vma, m.num); 434d71f5139SMukesh Rathor if (ret < 0) { 435d71f5139SMukesh Rathor up_write(&mm->mmap_sem); 436d71f5139SMukesh Rathor goto out; 437d71f5139SMukesh Rathor } 438d71f5139SMukesh Rathor } 439d8414d3cSBastian Blank 440d8414d3cSBastian Blank state.domain = m.dom; 441d8414d3cSBastian Blank state.vma = vma; 442d8414d3cSBastian Blank state.va = m.addr; 443d71f5139SMukesh Rathor state.index = 0; 444ceb90fa0SAndres Lagar-Cavilla state.global_error = 0; 44599beae6cSAndres Lagar-Cavilla state.version = version; 446d8414d3cSBastian Blank 447ceb90fa0SAndres Lagar-Cavilla /* mmap_batch_fn guarantees ret == 0 */ 448ceb90fa0SAndres Lagar-Cavilla BUG_ON(traverse_pages(m.num, sizeof(xen_pfn_t), 449ceb90fa0SAndres Lagar-Cavilla &pagelist, mmap_batch_fn, &state)); 450d8414d3cSBastian Blank 451d8414d3cSBastian Blank up_write(&mm->mmap_sem); 452d8414d3cSBastian Blank 45368fa965dSMats Petersson if (state.global_error) { 454ceb90fa0SAndres Lagar-Cavilla /* Write back errors in second pass. */ 455ceb90fa0SAndres Lagar-Cavilla state.user_mfn = (xen_pfn_t *)m.arr; 45699beae6cSAndres Lagar-Cavilla state.user_err = m.err; 457d8414d3cSBastian Blank ret = traverse_pages(m.num, sizeof(xen_pfn_t), 45899beae6cSAndres Lagar-Cavilla &pagelist, mmap_return_errors, &state); 45968fa965dSMats Petersson } else 46068fa965dSMats Petersson ret = 0; 46168fa965dSMats Petersson 462ceb90fa0SAndres Lagar-Cavilla /* If we have not had any EFAULT-like global errors then set the global 463ceb90fa0SAndres Lagar-Cavilla * error to -ENOENT if necessary. */ 464ceb90fa0SAndres Lagar-Cavilla if ((ret == 0) && (state.global_error == -ENOENT)) 465ceb90fa0SAndres Lagar-Cavilla ret = -ENOENT; 466d8414d3cSBastian Blank 467d8414d3cSBastian Blank out: 468d8414d3cSBastian Blank free_page_list(&pagelist); 469d8414d3cSBastian Blank 470d8414d3cSBastian Blank return ret; 471d8414d3cSBastian Blank } 472d8414d3cSBastian Blank 473d8414d3cSBastian Blank static long privcmd_ioctl(struct file *file, 474d8414d3cSBastian Blank unsigned int cmd, unsigned long data) 475d8414d3cSBastian Blank { 476d8414d3cSBastian Blank int ret = -ENOSYS; 477d8414d3cSBastian Blank void __user *udata = (void __user *) data; 478d8414d3cSBastian Blank 479d8414d3cSBastian Blank switch (cmd) { 480d8414d3cSBastian Blank case IOCTL_PRIVCMD_HYPERCALL: 481d8414d3cSBastian Blank ret = privcmd_ioctl_hypercall(udata); 482d8414d3cSBastian Blank break; 483d8414d3cSBastian Blank 484d8414d3cSBastian Blank case IOCTL_PRIVCMD_MMAP: 485d8414d3cSBastian Blank ret = privcmd_ioctl_mmap(udata); 486d8414d3cSBastian Blank break; 487d8414d3cSBastian Blank 488d8414d3cSBastian Blank case IOCTL_PRIVCMD_MMAPBATCH: 489ceb90fa0SAndres Lagar-Cavilla ret = privcmd_ioctl_mmap_batch(udata, 1); 490ceb90fa0SAndres Lagar-Cavilla break; 491ceb90fa0SAndres Lagar-Cavilla 492ceb90fa0SAndres Lagar-Cavilla case IOCTL_PRIVCMD_MMAPBATCH_V2: 493ceb90fa0SAndres Lagar-Cavilla ret = privcmd_ioctl_mmap_batch(udata, 2); 494d8414d3cSBastian Blank break; 495d8414d3cSBastian Blank 496d8414d3cSBastian Blank default: 497d8414d3cSBastian Blank ret = -EINVAL; 498d8414d3cSBastian Blank break; 499d8414d3cSBastian Blank } 500d8414d3cSBastian Blank 501d8414d3cSBastian Blank return ret; 502d8414d3cSBastian Blank } 503d8414d3cSBastian Blank 504d71f5139SMukesh Rathor static void privcmd_close(struct vm_area_struct *vma) 505d71f5139SMukesh Rathor { 506d71f5139SMukesh Rathor struct page **pages = vma->vm_private_data; 507d71f5139SMukesh Rathor int numpgs = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT; 508d71f5139SMukesh Rathor 5099eff37a8SDan Carpenter if (!xen_feature(XENFEAT_auto_translated_physmap) || !numpgs || !pages) 510d71f5139SMukesh Rathor return; 511d71f5139SMukesh Rathor 512d71f5139SMukesh Rathor xen_unmap_domain_mfn_range(vma, numpgs, pages); 513d71f5139SMukesh Rathor free_xenballooned_pages(numpgs, pages); 514d71f5139SMukesh Rathor kfree(pages); 515d71f5139SMukesh Rathor } 516d71f5139SMukesh Rathor 517d8414d3cSBastian Blank static int privcmd_fault(struct vm_area_struct *vma, struct vm_fault *vmf) 518d8414d3cSBastian Blank { 519d8414d3cSBastian Blank printk(KERN_DEBUG "privcmd_fault: vma=%p %lx-%lx, pgoff=%lx, uv=%p\n", 520d8414d3cSBastian Blank vma, vma->vm_start, vma->vm_end, 521d8414d3cSBastian Blank vmf->pgoff, vmf->virtual_address); 522d8414d3cSBastian Blank 523d8414d3cSBastian Blank return VM_FAULT_SIGBUS; 524d8414d3cSBastian Blank } 525d8414d3cSBastian Blank 526d8414d3cSBastian Blank static struct vm_operations_struct privcmd_vm_ops = { 527d71f5139SMukesh Rathor .close = privcmd_close, 528d8414d3cSBastian Blank .fault = privcmd_fault 529d8414d3cSBastian Blank }; 530d8414d3cSBastian Blank 531d8414d3cSBastian Blank static int privcmd_mmap(struct file *file, struct vm_area_struct *vma) 532d8414d3cSBastian Blank { 533d8414d3cSBastian Blank /* DONTCOPY is essential for Xen because copy_page_range doesn't know 534d8414d3cSBastian Blank * how to recreate these mappings */ 535314e51b9SKonstantin Khlebnikov vma->vm_flags |= VM_IO | VM_PFNMAP | VM_DONTCOPY | 536314e51b9SKonstantin Khlebnikov VM_DONTEXPAND | VM_DONTDUMP; 537d8414d3cSBastian Blank vma->vm_ops = &privcmd_vm_ops; 538d8414d3cSBastian Blank vma->vm_private_data = NULL; 539d8414d3cSBastian Blank 540d8414d3cSBastian Blank return 0; 541d8414d3cSBastian Blank } 542d8414d3cSBastian Blank 543d8414d3cSBastian Blank static int privcmd_enforce_singleshot_mapping(struct vm_area_struct *vma) 544d8414d3cSBastian Blank { 545d71f5139SMukesh Rathor return !cmpxchg(&vma->vm_private_data, NULL, PRIV_VMA_LOCKED); 546d8414d3cSBastian Blank } 547d8414d3cSBastian Blank 548d8414d3cSBastian Blank const struct file_operations xen_privcmd_fops = { 549d8414d3cSBastian Blank .owner = THIS_MODULE, 550d8414d3cSBastian Blank .unlocked_ioctl = privcmd_ioctl, 551d8414d3cSBastian Blank .mmap = privcmd_mmap, 552d8414d3cSBastian Blank }; 553d8414d3cSBastian Blank EXPORT_SYMBOL_GPL(xen_privcmd_fops); 554d8414d3cSBastian Blank 555d8414d3cSBastian Blank static struct miscdevice privcmd_dev = { 556d8414d3cSBastian Blank .minor = MISC_DYNAMIC_MINOR, 557d8414d3cSBastian Blank .name = "xen/privcmd", 558d8414d3cSBastian Blank .fops = &xen_privcmd_fops, 559d8414d3cSBastian Blank }; 560d8414d3cSBastian Blank 561d8414d3cSBastian Blank static int __init privcmd_init(void) 562d8414d3cSBastian Blank { 563d8414d3cSBastian Blank int err; 564d8414d3cSBastian Blank 565d8414d3cSBastian Blank if (!xen_domain()) 566d8414d3cSBastian Blank return -ENODEV; 567d8414d3cSBastian Blank 568d8414d3cSBastian Blank err = misc_register(&privcmd_dev); 569d8414d3cSBastian Blank if (err != 0) { 570*283c0972SJoe Perches pr_err("Could not register Xen privcmd device\n"); 571d8414d3cSBastian Blank return err; 572d8414d3cSBastian Blank } 573d8414d3cSBastian Blank return 0; 574d8414d3cSBastian Blank } 575d8414d3cSBastian Blank 576d8414d3cSBastian Blank static void __exit privcmd_exit(void) 577d8414d3cSBastian Blank { 578d8414d3cSBastian Blank misc_deregister(&privcmd_dev); 579d8414d3cSBastian Blank } 580d8414d3cSBastian Blank 581d8414d3cSBastian Blank module_init(privcmd_init); 582d8414d3cSBastian Blank module_exit(privcmd_exit); 583