109c434b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 2d8414d3cSBastian Blank /****************************************************************************** 3d8414d3cSBastian Blank * privcmd.c 4d8414d3cSBastian Blank * 5d8414d3cSBastian Blank * Interface to privileged domain-0 commands. 6d8414d3cSBastian Blank * 7d8414d3cSBastian Blank * Copyright (c) 2002-2004, K A Fraser, B Dragovic 8d8414d3cSBastian Blank */ 9d8414d3cSBastian Blank 10283c0972SJoe Perches #define pr_fmt(fmt) "xen:" KBUILD_MODNAME ": " fmt 11283c0972SJoe Perches 12d8414d3cSBastian Blank #include <linux/kernel.h> 13d8414d3cSBastian Blank #include <linux/module.h> 14d8414d3cSBastian Blank #include <linux/sched.h> 15d8414d3cSBastian Blank #include <linux/slab.h> 16d8414d3cSBastian Blank #include <linux/string.h> 17d8414d3cSBastian Blank #include <linux/errno.h> 18d8414d3cSBastian Blank #include <linux/mm.h> 19d8414d3cSBastian Blank #include <linux/mman.h> 20d8414d3cSBastian Blank #include <linux/uaccess.h> 21d8414d3cSBastian Blank #include <linux/swap.h> 22d8414d3cSBastian Blank #include <linux/highmem.h> 23d8414d3cSBastian Blank #include <linux/pagemap.h> 24d8414d3cSBastian Blank #include <linux/seq_file.h> 25d8414d3cSBastian Blank #include <linux/miscdevice.h> 26ab520be8SPaul Durrant #include <linux/moduleparam.h> 27d8414d3cSBastian Blank 28d8414d3cSBastian Blank #include <asm/xen/hypervisor.h> 29d8414d3cSBastian Blank #include <asm/xen/hypercall.h> 30d8414d3cSBastian Blank 31d8414d3cSBastian Blank #include <xen/xen.h> 32d8414d3cSBastian Blank #include <xen/privcmd.h> 33d8414d3cSBastian Blank #include <xen/interface/xen.h> 343ad08765SPaul Durrant #include <xen/interface/memory.h> 35ab520be8SPaul Durrant #include <xen/interface/hvm/dm_op.h> 36d8414d3cSBastian Blank #include <xen/features.h> 37d8414d3cSBastian Blank #include <xen/page.h> 38d8414d3cSBastian Blank #include <xen/xen-ops.h> 39d71f5139SMukesh Rathor #include <xen/balloon.h> 40d8414d3cSBastian Blank 41d8414d3cSBastian Blank #include "privcmd.h" 42d8414d3cSBastian Blank 43d8414d3cSBastian Blank MODULE_LICENSE("GPL"); 44d8414d3cSBastian Blank 45d71f5139SMukesh Rathor #define PRIV_VMA_LOCKED ((void *)1) 46d71f5139SMukesh Rathor 47ab520be8SPaul Durrant static unsigned int privcmd_dm_op_max_num = 16; 48ab520be8SPaul Durrant module_param_named(dm_op_max_nr_bufs, privcmd_dm_op_max_num, uint, 0644); 49ab520be8SPaul Durrant MODULE_PARM_DESC(dm_op_max_nr_bufs, 50ab520be8SPaul Durrant "Maximum number of buffers per dm_op hypercall"); 51ab520be8SPaul Durrant 52ab520be8SPaul Durrant static unsigned int privcmd_dm_op_buf_max_size = 4096; 53ab520be8SPaul Durrant module_param_named(dm_op_buf_max_size, privcmd_dm_op_buf_max_size, uint, 54ab520be8SPaul Durrant 0644); 55ab520be8SPaul Durrant MODULE_PARM_DESC(dm_op_buf_max_size, 56ab520be8SPaul Durrant "Maximum size of a dm_op hypercall buffer"); 57ab520be8SPaul Durrant 584610d240SPaul Durrant struct privcmd_data { 594610d240SPaul Durrant domid_t domid; 604610d240SPaul Durrant }; 614610d240SPaul Durrant 62a5deabe0SAndres Lagar-Cavilla static int privcmd_vma_range_is_mapped( 63a5deabe0SAndres Lagar-Cavilla struct vm_area_struct *vma, 64a5deabe0SAndres Lagar-Cavilla unsigned long addr, 65a5deabe0SAndres Lagar-Cavilla unsigned long nr_pages); 66d8414d3cSBastian Blank 674610d240SPaul Durrant static long privcmd_ioctl_hypercall(struct file *file, void __user *udata) 68d8414d3cSBastian Blank { 694610d240SPaul Durrant struct privcmd_data *data = file->private_data; 70d8414d3cSBastian Blank struct privcmd_hypercall hypercall; 71d8414d3cSBastian Blank long ret; 72d8414d3cSBastian Blank 734610d240SPaul Durrant /* Disallow arbitrary hypercalls if restricted */ 744610d240SPaul Durrant if (data->domid != DOMID_INVALID) 754610d240SPaul Durrant return -EPERM; 764610d240SPaul Durrant 77d8414d3cSBastian Blank if (copy_from_user(&hypercall, udata, sizeof(hypercall))) 78d8414d3cSBastian Blank return -EFAULT; 79d8414d3cSBastian Blank 80fdfd811dSDavid Vrabel xen_preemptible_hcall_begin(); 81d8414d3cSBastian Blank ret = privcmd_call(hypercall.op, 82d8414d3cSBastian Blank hypercall.arg[0], hypercall.arg[1], 83d8414d3cSBastian Blank hypercall.arg[2], hypercall.arg[3], 84d8414d3cSBastian Blank hypercall.arg[4]); 85fdfd811dSDavid Vrabel xen_preemptible_hcall_end(); 86d8414d3cSBastian Blank 87d8414d3cSBastian Blank return ret; 88d8414d3cSBastian Blank } 89d8414d3cSBastian Blank 90d8414d3cSBastian Blank static void free_page_list(struct list_head *pages) 91d8414d3cSBastian Blank { 92d8414d3cSBastian Blank struct page *p, *n; 93d8414d3cSBastian Blank 94d8414d3cSBastian Blank list_for_each_entry_safe(p, n, pages, lru) 95d8414d3cSBastian Blank __free_page(p); 96d8414d3cSBastian Blank 97d8414d3cSBastian Blank INIT_LIST_HEAD(pages); 98d8414d3cSBastian Blank } 99d8414d3cSBastian Blank 100d8414d3cSBastian Blank /* 101d8414d3cSBastian Blank * Given an array of items in userspace, return a list of pages 102d8414d3cSBastian Blank * containing the data. If copying fails, either because of memory 103d8414d3cSBastian Blank * allocation failure or a problem reading user memory, return an 104d8414d3cSBastian Blank * error code; its up to the caller to dispose of any partial list. 105d8414d3cSBastian Blank */ 106d8414d3cSBastian Blank static int gather_array(struct list_head *pagelist, 107d8414d3cSBastian Blank unsigned nelem, size_t size, 108ceb90fa0SAndres Lagar-Cavilla const void __user *data) 109d8414d3cSBastian Blank { 110d8414d3cSBastian Blank unsigned pageidx; 111d8414d3cSBastian Blank void *pagedata; 112d8414d3cSBastian Blank int ret; 113d8414d3cSBastian Blank 114d8414d3cSBastian Blank if (size > PAGE_SIZE) 115d8414d3cSBastian Blank return 0; 116d8414d3cSBastian Blank 117d8414d3cSBastian Blank pageidx = PAGE_SIZE; 118d8414d3cSBastian Blank pagedata = NULL; /* quiet, gcc */ 119d8414d3cSBastian Blank while (nelem--) { 120d8414d3cSBastian Blank if (pageidx > PAGE_SIZE-size) { 121d8414d3cSBastian Blank struct page *page = alloc_page(GFP_KERNEL); 122d8414d3cSBastian Blank 123d8414d3cSBastian Blank ret = -ENOMEM; 124d8414d3cSBastian Blank if (page == NULL) 125d8414d3cSBastian Blank goto fail; 126d8414d3cSBastian Blank 127d8414d3cSBastian Blank pagedata = page_address(page); 128d8414d3cSBastian Blank 129d8414d3cSBastian Blank list_add_tail(&page->lru, pagelist); 130d8414d3cSBastian Blank pageidx = 0; 131d8414d3cSBastian Blank } 132d8414d3cSBastian Blank 133d8414d3cSBastian Blank ret = -EFAULT; 134d8414d3cSBastian Blank if (copy_from_user(pagedata + pageidx, data, size)) 135d8414d3cSBastian Blank goto fail; 136d8414d3cSBastian Blank 137d8414d3cSBastian Blank data += size; 138d8414d3cSBastian Blank pageidx += size; 139d8414d3cSBastian Blank } 140d8414d3cSBastian Blank 141d8414d3cSBastian Blank ret = 0; 142d8414d3cSBastian Blank 143d8414d3cSBastian Blank fail: 144d8414d3cSBastian Blank return ret; 145d8414d3cSBastian Blank } 146d8414d3cSBastian Blank 147d8414d3cSBastian Blank /* 148d8414d3cSBastian Blank * Call function "fn" on each element of the array fragmented 149d8414d3cSBastian Blank * over a list of pages. 150d8414d3cSBastian Blank */ 151d8414d3cSBastian Blank static int traverse_pages(unsigned nelem, size_t size, 152d8414d3cSBastian Blank struct list_head *pos, 153d8414d3cSBastian Blank int (*fn)(void *data, void *state), 154d8414d3cSBastian Blank void *state) 155d8414d3cSBastian Blank { 156d8414d3cSBastian Blank void *pagedata; 157d8414d3cSBastian Blank unsigned pageidx; 158d8414d3cSBastian Blank int ret = 0; 159d8414d3cSBastian Blank 160d8414d3cSBastian Blank BUG_ON(size > PAGE_SIZE); 161d8414d3cSBastian Blank 162d8414d3cSBastian Blank pageidx = PAGE_SIZE; 163d8414d3cSBastian Blank pagedata = NULL; /* hush, gcc */ 164d8414d3cSBastian Blank 165d8414d3cSBastian Blank while (nelem--) { 166d8414d3cSBastian Blank if (pageidx > PAGE_SIZE-size) { 167d8414d3cSBastian Blank struct page *page; 168d8414d3cSBastian Blank pos = pos->next; 169d8414d3cSBastian Blank page = list_entry(pos, struct page, lru); 170d8414d3cSBastian Blank pagedata = page_address(page); 171d8414d3cSBastian Blank pageidx = 0; 172d8414d3cSBastian Blank } 173d8414d3cSBastian Blank 174d8414d3cSBastian Blank ret = (*fn)(pagedata + pageidx, state); 175d8414d3cSBastian Blank if (ret) 176d8414d3cSBastian Blank break; 177d8414d3cSBastian Blank pageidx += size; 178d8414d3cSBastian Blank } 179d8414d3cSBastian Blank 180d8414d3cSBastian Blank return ret; 181d8414d3cSBastian Blank } 182d8414d3cSBastian Blank 1834e8c0c8cSDavid Vrabel /* 1844e8c0c8cSDavid Vrabel * Similar to traverse_pages, but use each page as a "block" of 1854e8c0c8cSDavid Vrabel * data to be processed as one unit. 1864e8c0c8cSDavid Vrabel */ 1874e8c0c8cSDavid Vrabel static int traverse_pages_block(unsigned nelem, size_t size, 1884e8c0c8cSDavid Vrabel struct list_head *pos, 1894e8c0c8cSDavid Vrabel int (*fn)(void *data, int nr, void *state), 1904e8c0c8cSDavid Vrabel void *state) 1914e8c0c8cSDavid Vrabel { 1924e8c0c8cSDavid Vrabel void *pagedata; 1934e8c0c8cSDavid Vrabel int ret = 0; 1944e8c0c8cSDavid Vrabel 1954e8c0c8cSDavid Vrabel BUG_ON(size > PAGE_SIZE); 1964e8c0c8cSDavid Vrabel 1974e8c0c8cSDavid Vrabel while (nelem) { 1984e8c0c8cSDavid Vrabel int nr = (PAGE_SIZE/size); 1994e8c0c8cSDavid Vrabel struct page *page; 2004e8c0c8cSDavid Vrabel if (nr > nelem) 2014e8c0c8cSDavid Vrabel nr = nelem; 2024e8c0c8cSDavid Vrabel pos = pos->next; 2034e8c0c8cSDavid Vrabel page = list_entry(pos, struct page, lru); 2044e8c0c8cSDavid Vrabel pagedata = page_address(page); 2054e8c0c8cSDavid Vrabel ret = (*fn)(pagedata, nr, state); 2064e8c0c8cSDavid Vrabel if (ret) 2074e8c0c8cSDavid Vrabel break; 2084e8c0c8cSDavid Vrabel nelem -= nr; 2094e8c0c8cSDavid Vrabel } 2104e8c0c8cSDavid Vrabel 2114e8c0c8cSDavid Vrabel return ret; 2124e8c0c8cSDavid Vrabel } 2134e8c0c8cSDavid Vrabel 214a13d7201SJulien Grall struct mmap_gfn_state { 215d8414d3cSBastian Blank unsigned long va; 216d8414d3cSBastian Blank struct vm_area_struct *vma; 217d8414d3cSBastian Blank domid_t domain; 218d8414d3cSBastian Blank }; 219d8414d3cSBastian Blank 220a13d7201SJulien Grall static int mmap_gfn_range(void *data, void *state) 221d8414d3cSBastian Blank { 222d8414d3cSBastian Blank struct privcmd_mmap_entry *msg = data; 223a13d7201SJulien Grall struct mmap_gfn_state *st = state; 224d8414d3cSBastian Blank struct vm_area_struct *vma = st->vma; 225d8414d3cSBastian Blank int rc; 226d8414d3cSBastian Blank 227d8414d3cSBastian Blank /* Do not allow range to wrap the address space. */ 228d8414d3cSBastian Blank if ((msg->npages > (LONG_MAX >> PAGE_SHIFT)) || 229d8414d3cSBastian Blank ((unsigned long)(msg->npages << PAGE_SHIFT) >= -st->va)) 230d8414d3cSBastian Blank return -EINVAL; 231d8414d3cSBastian Blank 232d8414d3cSBastian Blank /* Range chunks must be contiguous in va space. */ 233d8414d3cSBastian Blank if ((msg->va != st->va) || 234d8414d3cSBastian Blank ((msg->va+(msg->npages<<PAGE_SHIFT)) > vma->vm_end)) 235d8414d3cSBastian Blank return -EINVAL; 236d8414d3cSBastian Blank 237a13d7201SJulien Grall rc = xen_remap_domain_gfn_range(vma, 238d8414d3cSBastian Blank msg->va & PAGE_MASK, 239d8414d3cSBastian Blank msg->mfn, msg->npages, 240d8414d3cSBastian Blank vma->vm_page_prot, 2419a032e39SIan Campbell st->domain, NULL); 242d8414d3cSBastian Blank if (rc < 0) 243d8414d3cSBastian Blank return rc; 244d8414d3cSBastian Blank 245d8414d3cSBastian Blank st->va += msg->npages << PAGE_SHIFT; 246d8414d3cSBastian Blank 247d8414d3cSBastian Blank return 0; 248d8414d3cSBastian Blank } 249d8414d3cSBastian Blank 2504610d240SPaul Durrant static long privcmd_ioctl_mmap(struct file *file, void __user *udata) 251d8414d3cSBastian Blank { 2524610d240SPaul Durrant struct privcmd_data *data = file->private_data; 253d8414d3cSBastian Blank struct privcmd_mmap mmapcmd; 254d8414d3cSBastian Blank struct mm_struct *mm = current->mm; 255d8414d3cSBastian Blank struct vm_area_struct *vma; 256d8414d3cSBastian Blank int rc; 257d8414d3cSBastian Blank LIST_HEAD(pagelist); 258a13d7201SJulien Grall struct mmap_gfn_state state; 259d8414d3cSBastian Blank 260d71f5139SMukesh Rathor /* We only support privcmd_ioctl_mmap_batch for auto translated. */ 261d71f5139SMukesh Rathor if (xen_feature(XENFEAT_auto_translated_physmap)) 262d71f5139SMukesh Rathor return -ENOSYS; 263d71f5139SMukesh Rathor 264d8414d3cSBastian Blank if (copy_from_user(&mmapcmd, udata, sizeof(mmapcmd))) 265d8414d3cSBastian Blank return -EFAULT; 266d8414d3cSBastian Blank 2674610d240SPaul Durrant /* If restriction is in place, check the domid matches */ 2684610d240SPaul Durrant if (data->domid != DOMID_INVALID && data->domid != mmapcmd.dom) 2694610d240SPaul Durrant return -EPERM; 2704610d240SPaul Durrant 271d8414d3cSBastian Blank rc = gather_array(&pagelist, 272d8414d3cSBastian Blank mmapcmd.num, sizeof(struct privcmd_mmap_entry), 273d8414d3cSBastian Blank mmapcmd.entry); 274d8414d3cSBastian Blank 275d8414d3cSBastian Blank if (rc || list_empty(&pagelist)) 276d8414d3cSBastian Blank goto out; 277d8414d3cSBastian Blank 278d8ed45c5SMichel Lespinasse mmap_write_lock(mm); 279d8414d3cSBastian Blank 280d8414d3cSBastian Blank { 281d8414d3cSBastian Blank struct page *page = list_first_entry(&pagelist, 282d8414d3cSBastian Blank struct page, lru); 283d8414d3cSBastian Blank struct privcmd_mmap_entry *msg = page_address(page); 284d8414d3cSBastian Blank 285d8414d3cSBastian Blank vma = find_vma(mm, msg->va); 286d8414d3cSBastian Blank rc = -EINVAL; 287d8414d3cSBastian Blank 288a5deabe0SAndres Lagar-Cavilla if (!vma || (msg->va != vma->vm_start) || vma->vm_private_data) 289d8414d3cSBastian Blank goto out_up; 290a5deabe0SAndres Lagar-Cavilla vma->vm_private_data = PRIV_VMA_LOCKED; 291d8414d3cSBastian Blank } 292d8414d3cSBastian Blank 293d8414d3cSBastian Blank state.va = vma->vm_start; 294d8414d3cSBastian Blank state.vma = vma; 295d8414d3cSBastian Blank state.domain = mmapcmd.dom; 296d8414d3cSBastian Blank 297d8414d3cSBastian Blank rc = traverse_pages(mmapcmd.num, sizeof(struct privcmd_mmap_entry), 298d8414d3cSBastian Blank &pagelist, 299a13d7201SJulien Grall mmap_gfn_range, &state); 300d8414d3cSBastian Blank 301d8414d3cSBastian Blank 302d8414d3cSBastian Blank out_up: 303d8ed45c5SMichel Lespinasse mmap_write_unlock(mm); 304d8414d3cSBastian Blank 305d8414d3cSBastian Blank out: 306d8414d3cSBastian Blank free_page_list(&pagelist); 307d8414d3cSBastian Blank 308d8414d3cSBastian Blank return rc; 309d8414d3cSBastian Blank } 310d8414d3cSBastian Blank 311d8414d3cSBastian Blank struct mmap_batch_state { 312d8414d3cSBastian Blank domid_t domain; 313d8414d3cSBastian Blank unsigned long va; 314d8414d3cSBastian Blank struct vm_area_struct *vma; 315d71f5139SMukesh Rathor int index; 316ceb90fa0SAndres Lagar-Cavilla /* A tristate: 317ceb90fa0SAndres Lagar-Cavilla * 0 for no errors 318ceb90fa0SAndres Lagar-Cavilla * 1 if at least one error has happened (and no 319ceb90fa0SAndres Lagar-Cavilla * -ENOENT errors have happened) 320ceb90fa0SAndres Lagar-Cavilla * -ENOENT if at least 1 -ENOENT has happened. 321ceb90fa0SAndres Lagar-Cavilla */ 322ceb90fa0SAndres Lagar-Cavilla int global_error; 32399beae6cSAndres Lagar-Cavilla int version; 324d8414d3cSBastian Blank 325a13d7201SJulien Grall /* User-space gfn array to store errors in the second pass for V1. */ 326a13d7201SJulien Grall xen_pfn_t __user *user_gfn; 32799beae6cSAndres Lagar-Cavilla /* User-space int array to store errors in the second pass for V2. */ 32899beae6cSAndres Lagar-Cavilla int __user *user_err; 329d8414d3cSBastian Blank }; 330d8414d3cSBastian Blank 331a13d7201SJulien Grall /* auto translated dom0 note: if domU being created is PV, then gfn is 332a13d7201SJulien Grall * mfn(addr on bus). If it's auto xlated, then gfn is pfn (input to HAP). 333d71f5139SMukesh Rathor */ 3344e8c0c8cSDavid Vrabel static int mmap_batch_fn(void *data, int nr, void *state) 335d8414d3cSBastian Blank { 336a13d7201SJulien Grall xen_pfn_t *gfnp = data; 337d8414d3cSBastian Blank struct mmap_batch_state *st = state; 338d71f5139SMukesh Rathor struct vm_area_struct *vma = st->vma; 339d71f5139SMukesh Rathor struct page **pages = vma->vm_private_data; 3404e8c0c8cSDavid Vrabel struct page **cur_pages = NULL; 341ceb90fa0SAndres Lagar-Cavilla int ret; 342d8414d3cSBastian Blank 343d71f5139SMukesh Rathor if (xen_feature(XENFEAT_auto_translated_physmap)) 3444e8c0c8cSDavid Vrabel cur_pages = &pages[st->index]; 345d71f5139SMukesh Rathor 3464e8c0c8cSDavid Vrabel BUG_ON(nr < 0); 347a13d7201SJulien Grall ret = xen_remap_domain_gfn_array(st->vma, st->va & PAGE_MASK, gfnp, nr, 348a13d7201SJulien Grall (int *)gfnp, st->vma->vm_page_prot, 3494e8c0c8cSDavid Vrabel st->domain, cur_pages); 350ceb90fa0SAndres Lagar-Cavilla 3514e8c0c8cSDavid Vrabel /* Adjust the global_error? */ 3524e8c0c8cSDavid Vrabel if (ret != nr) { 353ceb90fa0SAndres Lagar-Cavilla if (ret == -ENOENT) 354ceb90fa0SAndres Lagar-Cavilla st->global_error = -ENOENT; 355ceb90fa0SAndres Lagar-Cavilla else { 356ceb90fa0SAndres Lagar-Cavilla /* Record that at least one error has happened. */ 357ceb90fa0SAndres Lagar-Cavilla if (st->global_error == 0) 358ceb90fa0SAndres Lagar-Cavilla st->global_error = 1; 359ceb90fa0SAndres Lagar-Cavilla } 360d8414d3cSBastian Blank } 361753c09b5SJulien Grall st->va += XEN_PAGE_SIZE * nr; 362753c09b5SJulien Grall st->index += nr / XEN_PFN_PER_PAGE; 363d8414d3cSBastian Blank 364d8414d3cSBastian Blank return 0; 365d8414d3cSBastian Blank } 366d8414d3cSBastian Blank 3674e8c0c8cSDavid Vrabel static int mmap_return_error(int err, struct mmap_batch_state *st) 368d8414d3cSBastian Blank { 3694e8c0c8cSDavid Vrabel int ret; 370d8414d3cSBastian Blank 37199beae6cSAndres Lagar-Cavilla if (st->version == 1) { 3724e8c0c8cSDavid Vrabel if (err) { 373a13d7201SJulien Grall xen_pfn_t gfn; 3744e8c0c8cSDavid Vrabel 375a13d7201SJulien Grall ret = get_user(gfn, st->user_gfn); 3764e8c0c8cSDavid Vrabel if (ret < 0) 3774e8c0c8cSDavid Vrabel return ret; 3784e8c0c8cSDavid Vrabel /* 3794e8c0c8cSDavid Vrabel * V1 encodes the error codes in the 32bit top 380a13d7201SJulien Grall * nibble of the gfn (with its known 3814e8c0c8cSDavid Vrabel * limitations vis-a-vis 64 bit callers). 3824e8c0c8cSDavid Vrabel */ 383a13d7201SJulien Grall gfn |= (err == -ENOENT) ? 3844e8c0c8cSDavid Vrabel PRIVCMD_MMAPBATCH_PAGED_ERROR : 3854e8c0c8cSDavid Vrabel PRIVCMD_MMAPBATCH_MFN_ERROR; 386a13d7201SJulien Grall return __put_user(gfn, st->user_gfn++); 3874e8c0c8cSDavid Vrabel } else 388a13d7201SJulien Grall st->user_gfn++; 38999beae6cSAndres Lagar-Cavilla } else { /* st->version == 2 */ 39099beae6cSAndres Lagar-Cavilla if (err) 39199beae6cSAndres Lagar-Cavilla return __put_user(err, st->user_err++); 39299beae6cSAndres Lagar-Cavilla else 39399beae6cSAndres Lagar-Cavilla st->user_err++; 39499beae6cSAndres Lagar-Cavilla } 39599beae6cSAndres Lagar-Cavilla 39699beae6cSAndres Lagar-Cavilla return 0; 397d8414d3cSBastian Blank } 398d8414d3cSBastian Blank 3994e8c0c8cSDavid Vrabel static int mmap_return_errors(void *data, int nr, void *state) 4004e8c0c8cSDavid Vrabel { 4014e8c0c8cSDavid Vrabel struct mmap_batch_state *st = state; 4024e8c0c8cSDavid Vrabel int *errs = data; 4034e8c0c8cSDavid Vrabel int i; 4044e8c0c8cSDavid Vrabel int ret; 4054e8c0c8cSDavid Vrabel 4064e8c0c8cSDavid Vrabel for (i = 0; i < nr; i++) { 4074e8c0c8cSDavid Vrabel ret = mmap_return_error(errs[i], st); 4084e8c0c8cSDavid Vrabel if (ret < 0) 4094e8c0c8cSDavid Vrabel return ret; 4104e8c0c8cSDavid Vrabel } 4114e8c0c8cSDavid Vrabel return 0; 4124e8c0c8cSDavid Vrabel } 4134e8c0c8cSDavid Vrabel 414a13d7201SJulien Grall /* Allocate pfns that are then mapped with gfns from foreign domid. Update 415d71f5139SMukesh Rathor * the vma with the page info to use later. 416d71f5139SMukesh Rathor * Returns: 0 if success, otherwise -errno 417d71f5139SMukesh Rathor */ 418d71f5139SMukesh Rathor static int alloc_empty_pages(struct vm_area_struct *vma, int numpgs) 419d71f5139SMukesh Rathor { 420d71f5139SMukesh Rathor int rc; 421d71f5139SMukesh Rathor struct page **pages; 422d71f5139SMukesh Rathor 423d71f5139SMukesh Rathor pages = kcalloc(numpgs, sizeof(pages[0]), GFP_KERNEL); 424d71f5139SMukesh Rathor if (pages == NULL) 425d71f5139SMukesh Rathor return -ENOMEM; 426d71f5139SMukesh Rathor 4279e2369c0SRoger Pau Monne rc = xen_alloc_unpopulated_pages(numpgs, pages); 428d71f5139SMukesh Rathor if (rc != 0) { 429d71f5139SMukesh Rathor pr_warn("%s Could not alloc %d pfns rc:%d\n", __func__, 430d71f5139SMukesh Rathor numpgs, rc); 431d71f5139SMukesh Rathor kfree(pages); 432d71f5139SMukesh Rathor return -ENOMEM; 433d71f5139SMukesh Rathor } 434a5deabe0SAndres Lagar-Cavilla BUG_ON(vma->vm_private_data != NULL); 435d71f5139SMukesh Rathor vma->vm_private_data = pages; 436d71f5139SMukesh Rathor 437d71f5139SMukesh Rathor return 0; 438d71f5139SMukesh Rathor } 439d71f5139SMukesh Rathor 4407cbea8dcSKirill A. Shutemov static const struct vm_operations_struct privcmd_vm_ops; 441d8414d3cSBastian Blank 4424610d240SPaul Durrant static long privcmd_ioctl_mmap_batch( 4434610d240SPaul Durrant struct file *file, void __user *udata, int version) 444d8414d3cSBastian Blank { 4454610d240SPaul Durrant struct privcmd_data *data = file->private_data; 446d8414d3cSBastian Blank int ret; 447ceb90fa0SAndres Lagar-Cavilla struct privcmd_mmapbatch_v2 m; 448d8414d3cSBastian Blank struct mm_struct *mm = current->mm; 449d8414d3cSBastian Blank struct vm_area_struct *vma; 450d8414d3cSBastian Blank unsigned long nr_pages; 451d8414d3cSBastian Blank LIST_HEAD(pagelist); 452d8414d3cSBastian Blank struct mmap_batch_state state; 453d8414d3cSBastian Blank 454ceb90fa0SAndres Lagar-Cavilla switch (version) { 455ceb90fa0SAndres Lagar-Cavilla case 1: 456ceb90fa0SAndres Lagar-Cavilla if (copy_from_user(&m, udata, sizeof(struct privcmd_mmapbatch))) 457d8414d3cSBastian Blank return -EFAULT; 458ceb90fa0SAndres Lagar-Cavilla /* Returns per-frame error in m.arr. */ 459ceb90fa0SAndres Lagar-Cavilla m.err = NULL; 46096d4f267SLinus Torvalds if (!access_ok(m.arr, m.num * sizeof(*m.arr))) 461ceb90fa0SAndres Lagar-Cavilla return -EFAULT; 462ceb90fa0SAndres Lagar-Cavilla break; 463ceb90fa0SAndres Lagar-Cavilla case 2: 464ceb90fa0SAndres Lagar-Cavilla if (copy_from_user(&m, udata, sizeof(struct privcmd_mmapbatch_v2))) 465ceb90fa0SAndres Lagar-Cavilla return -EFAULT; 466ceb90fa0SAndres Lagar-Cavilla /* Returns per-frame error code in m.err. */ 46796d4f267SLinus Torvalds if (!access_ok(m.err, m.num * (sizeof(*m.err)))) 468ceb90fa0SAndres Lagar-Cavilla return -EFAULT; 469ceb90fa0SAndres Lagar-Cavilla break; 470ceb90fa0SAndres Lagar-Cavilla default: 471ceb90fa0SAndres Lagar-Cavilla return -EINVAL; 472ceb90fa0SAndres Lagar-Cavilla } 473d8414d3cSBastian Blank 4744610d240SPaul Durrant /* If restriction is in place, check the domid matches */ 4754610d240SPaul Durrant if (data->domid != DOMID_INVALID && data->domid != m.dom) 4764610d240SPaul Durrant return -EPERM; 4774610d240SPaul Durrant 4785995a68aSJulien Grall nr_pages = DIV_ROUND_UP(m.num, XEN_PFN_PER_PAGE); 479d8414d3cSBastian Blank if ((m.num <= 0) || (nr_pages > (LONG_MAX >> PAGE_SHIFT))) 480d8414d3cSBastian Blank return -EINVAL; 481d8414d3cSBastian Blank 482ceb90fa0SAndres Lagar-Cavilla ret = gather_array(&pagelist, m.num, sizeof(xen_pfn_t), m.arr); 483d8414d3cSBastian Blank 484ceb90fa0SAndres Lagar-Cavilla if (ret) 485d8414d3cSBastian Blank goto out; 486ceb90fa0SAndres Lagar-Cavilla if (list_empty(&pagelist)) { 487ceb90fa0SAndres Lagar-Cavilla ret = -EINVAL; 488ceb90fa0SAndres Lagar-Cavilla goto out; 489ceb90fa0SAndres Lagar-Cavilla } 490ceb90fa0SAndres Lagar-Cavilla 49199beae6cSAndres Lagar-Cavilla if (version == 2) { 49299beae6cSAndres Lagar-Cavilla /* Zero error array now to only copy back actual errors. */ 49399beae6cSAndres Lagar-Cavilla if (clear_user(m.err, sizeof(int) * m.num)) { 49499beae6cSAndres Lagar-Cavilla ret = -EFAULT; 495ceb90fa0SAndres Lagar-Cavilla goto out; 496ceb90fa0SAndres Lagar-Cavilla } 49799beae6cSAndres Lagar-Cavilla } 498d8414d3cSBastian Blank 499d8ed45c5SMichel Lespinasse mmap_write_lock(mm); 500d8414d3cSBastian Blank 501d8414d3cSBastian Blank vma = find_vma(mm, m.addr); 502d8414d3cSBastian Blank if (!vma || 503a5deabe0SAndres Lagar-Cavilla vma->vm_ops != &privcmd_vm_ops) { 50468fa965dSMats Petersson ret = -EINVAL; 505a5deabe0SAndres Lagar-Cavilla goto out_unlock; 506a5deabe0SAndres Lagar-Cavilla } 507a5deabe0SAndres Lagar-Cavilla 508a5deabe0SAndres Lagar-Cavilla /* 509a5deabe0SAndres Lagar-Cavilla * Caller must either: 510a5deabe0SAndres Lagar-Cavilla * 511a5deabe0SAndres Lagar-Cavilla * Map the whole VMA range, which will also allocate all the 512a5deabe0SAndres Lagar-Cavilla * pages required for the auto_translated_physmap case. 513a5deabe0SAndres Lagar-Cavilla * 514a5deabe0SAndres Lagar-Cavilla * Or 515a5deabe0SAndres Lagar-Cavilla * 516a5deabe0SAndres Lagar-Cavilla * Map unmapped holes left from a previous map attempt (e.g., 517a5deabe0SAndres Lagar-Cavilla * because those foreign frames were previously paged out). 518a5deabe0SAndres Lagar-Cavilla */ 519a5deabe0SAndres Lagar-Cavilla if (vma->vm_private_data == NULL) { 520a5deabe0SAndres Lagar-Cavilla if (m.addr != vma->vm_start || 521a5deabe0SAndres Lagar-Cavilla m.addr + (nr_pages << PAGE_SHIFT) != vma->vm_end) { 522a5deabe0SAndres Lagar-Cavilla ret = -EINVAL; 523a5deabe0SAndres Lagar-Cavilla goto out_unlock; 524d8414d3cSBastian Blank } 525d71f5139SMukesh Rathor if (xen_feature(XENFEAT_auto_translated_physmap)) { 5265995a68aSJulien Grall ret = alloc_empty_pages(vma, nr_pages); 527a5deabe0SAndres Lagar-Cavilla if (ret < 0) 528a5deabe0SAndres Lagar-Cavilla goto out_unlock; 529a5deabe0SAndres Lagar-Cavilla } else 530a5deabe0SAndres Lagar-Cavilla vma->vm_private_data = PRIV_VMA_LOCKED; 531a5deabe0SAndres Lagar-Cavilla } else { 532a5deabe0SAndres Lagar-Cavilla if (m.addr < vma->vm_start || 533a5deabe0SAndres Lagar-Cavilla m.addr + (nr_pages << PAGE_SHIFT) > vma->vm_end) { 534a5deabe0SAndres Lagar-Cavilla ret = -EINVAL; 535a5deabe0SAndres Lagar-Cavilla goto out_unlock; 536a5deabe0SAndres Lagar-Cavilla } 537a5deabe0SAndres Lagar-Cavilla if (privcmd_vma_range_is_mapped(vma, m.addr, nr_pages)) { 538a5deabe0SAndres Lagar-Cavilla ret = -EINVAL; 539a5deabe0SAndres Lagar-Cavilla goto out_unlock; 540d71f5139SMukesh Rathor } 541d71f5139SMukesh Rathor } 542d8414d3cSBastian Blank 543d8414d3cSBastian Blank state.domain = m.dom; 544d8414d3cSBastian Blank state.vma = vma; 545d8414d3cSBastian Blank state.va = m.addr; 546d71f5139SMukesh Rathor state.index = 0; 547ceb90fa0SAndres Lagar-Cavilla state.global_error = 0; 54899beae6cSAndres Lagar-Cavilla state.version = version; 549d8414d3cSBastian Blank 5505995a68aSJulien Grall BUILD_BUG_ON(((PAGE_SIZE / sizeof(xen_pfn_t)) % XEN_PFN_PER_PAGE) != 0); 551ceb90fa0SAndres Lagar-Cavilla /* mmap_batch_fn guarantees ret == 0 */ 5524e8c0c8cSDavid Vrabel BUG_ON(traverse_pages_block(m.num, sizeof(xen_pfn_t), 553ceb90fa0SAndres Lagar-Cavilla &pagelist, mmap_batch_fn, &state)); 554d8414d3cSBastian Blank 555d8ed45c5SMichel Lespinasse mmap_write_unlock(mm); 556d8414d3cSBastian Blank 55768fa965dSMats Petersson if (state.global_error) { 558ceb90fa0SAndres Lagar-Cavilla /* Write back errors in second pass. */ 559a13d7201SJulien Grall state.user_gfn = (xen_pfn_t *)m.arr; 56099beae6cSAndres Lagar-Cavilla state.user_err = m.err; 5614e8c0c8cSDavid Vrabel ret = traverse_pages_block(m.num, sizeof(xen_pfn_t), 56299beae6cSAndres Lagar-Cavilla &pagelist, mmap_return_errors, &state); 56368fa965dSMats Petersson } else 56468fa965dSMats Petersson ret = 0; 56568fa965dSMats Petersson 566ceb90fa0SAndres Lagar-Cavilla /* If we have not had any EFAULT-like global errors then set the global 567ceb90fa0SAndres Lagar-Cavilla * error to -ENOENT if necessary. */ 568ceb90fa0SAndres Lagar-Cavilla if ((ret == 0) && (state.global_error == -ENOENT)) 569ceb90fa0SAndres Lagar-Cavilla ret = -ENOENT; 570d8414d3cSBastian Blank 571d8414d3cSBastian Blank out: 572d8414d3cSBastian Blank free_page_list(&pagelist); 573d8414d3cSBastian Blank return ret; 574a5deabe0SAndres Lagar-Cavilla 575a5deabe0SAndres Lagar-Cavilla out_unlock: 576d8ed45c5SMichel Lespinasse mmap_write_unlock(mm); 577a5deabe0SAndres Lagar-Cavilla goto out; 578d8414d3cSBastian Blank } 579d8414d3cSBastian Blank 580ab520be8SPaul Durrant static int lock_pages( 581ab520be8SPaul Durrant struct privcmd_dm_op_buf kbufs[], unsigned int num, 582e398fb4bSSouptick Joarder struct page *pages[], unsigned int nr_pages, unsigned int *pinned) 583ab520be8SPaul Durrant { 584ab520be8SPaul Durrant unsigned int i; 585ab520be8SPaul Durrant 586ab520be8SPaul Durrant for (i = 0; i < num; i++) { 587ab520be8SPaul Durrant unsigned int requested; 588e398fb4bSSouptick Joarder int page_count; 589ab520be8SPaul Durrant 590ab520be8SPaul Durrant requested = DIV_ROUND_UP( 591ab520be8SPaul Durrant offset_in_page(kbufs[i].uptr) + kbufs[i].size, 592ab520be8SPaul Durrant PAGE_SIZE); 593ab520be8SPaul Durrant if (requested > nr_pages) 594ab520be8SPaul Durrant return -ENOSPC; 595ab520be8SPaul Durrant 596ff669aa8SSouptick Joarder page_count = pin_user_pages_fast( 597ab520be8SPaul Durrant (unsigned long) kbufs[i].uptr, 598ab520be8SPaul Durrant requested, FOLL_WRITE, pages); 599e398fb4bSSouptick Joarder if (page_count < 0) 600e398fb4bSSouptick Joarder return page_count; 601ab520be8SPaul Durrant 602e398fb4bSSouptick Joarder *pinned += page_count; 603e398fb4bSSouptick Joarder nr_pages -= page_count; 604e398fb4bSSouptick Joarder pages += page_count; 605ab520be8SPaul Durrant } 606ab520be8SPaul Durrant 607ab520be8SPaul Durrant return 0; 608ab520be8SPaul Durrant } 609ab520be8SPaul Durrant 610ab520be8SPaul Durrant static void unlock_pages(struct page *pages[], unsigned int nr_pages) 611ab520be8SPaul Durrant { 612ff669aa8SSouptick Joarder unpin_user_pages_dirty_lock(pages, nr_pages, true); 613a0c34d22SSouptick Joarder } 614ab520be8SPaul Durrant 6154610d240SPaul Durrant static long privcmd_ioctl_dm_op(struct file *file, void __user *udata) 616ab520be8SPaul Durrant { 6174610d240SPaul Durrant struct privcmd_data *data = file->private_data; 618ab520be8SPaul Durrant struct privcmd_dm_op kdata; 619ab520be8SPaul Durrant struct privcmd_dm_op_buf *kbufs; 620ab520be8SPaul Durrant unsigned int nr_pages = 0; 621ab520be8SPaul Durrant struct page **pages = NULL; 622ab520be8SPaul Durrant struct xen_dm_op_buf *xbufs = NULL; 623ab520be8SPaul Durrant unsigned int i; 624ab520be8SPaul Durrant long rc; 625e398fb4bSSouptick Joarder unsigned int pinned = 0; 626ab520be8SPaul Durrant 627ab520be8SPaul Durrant if (copy_from_user(&kdata, udata, sizeof(kdata))) 628ab520be8SPaul Durrant return -EFAULT; 629ab520be8SPaul Durrant 6304610d240SPaul Durrant /* If restriction is in place, check the domid matches */ 6314610d240SPaul Durrant if (data->domid != DOMID_INVALID && data->domid != kdata.dom) 6324610d240SPaul Durrant return -EPERM; 6334610d240SPaul Durrant 634ab520be8SPaul Durrant if (kdata.num == 0) 635ab520be8SPaul Durrant return 0; 636ab520be8SPaul Durrant 637ab520be8SPaul Durrant if (kdata.num > privcmd_dm_op_max_num) 638ab520be8SPaul Durrant return -E2BIG; 639ab520be8SPaul Durrant 640ab520be8SPaul Durrant kbufs = kcalloc(kdata.num, sizeof(*kbufs), GFP_KERNEL); 641ab520be8SPaul Durrant if (!kbufs) 642ab520be8SPaul Durrant return -ENOMEM; 643ab520be8SPaul Durrant 644ab520be8SPaul Durrant if (copy_from_user(kbufs, kdata.ubufs, 645ab520be8SPaul Durrant sizeof(*kbufs) * kdata.num)) { 646ab520be8SPaul Durrant rc = -EFAULT; 647ab520be8SPaul Durrant goto out; 648ab520be8SPaul Durrant } 649ab520be8SPaul Durrant 650ab520be8SPaul Durrant for (i = 0; i < kdata.num; i++) { 651ab520be8SPaul Durrant if (kbufs[i].size > privcmd_dm_op_buf_max_size) { 652ab520be8SPaul Durrant rc = -E2BIG; 653ab520be8SPaul Durrant goto out; 654ab520be8SPaul Durrant } 655ab520be8SPaul Durrant 65696d4f267SLinus Torvalds if (!access_ok(kbufs[i].uptr, 657ab520be8SPaul Durrant kbufs[i].size)) { 658ab520be8SPaul Durrant rc = -EFAULT; 659ab520be8SPaul Durrant goto out; 660ab520be8SPaul Durrant } 661ab520be8SPaul Durrant 662ab520be8SPaul Durrant nr_pages += DIV_ROUND_UP( 663ab520be8SPaul Durrant offset_in_page(kbufs[i].uptr) + kbufs[i].size, 664ab520be8SPaul Durrant PAGE_SIZE); 665ab520be8SPaul Durrant } 666ab520be8SPaul Durrant 667ab520be8SPaul Durrant pages = kcalloc(nr_pages, sizeof(*pages), GFP_KERNEL); 668ab520be8SPaul Durrant if (!pages) { 669ab520be8SPaul Durrant rc = -ENOMEM; 670ab520be8SPaul Durrant goto out; 671ab520be8SPaul Durrant } 672ab520be8SPaul Durrant 673ab520be8SPaul Durrant xbufs = kcalloc(kdata.num, sizeof(*xbufs), GFP_KERNEL); 674ab520be8SPaul Durrant if (!xbufs) { 675ab520be8SPaul Durrant rc = -ENOMEM; 676ab520be8SPaul Durrant goto out; 677ab520be8SPaul Durrant } 678ab520be8SPaul Durrant 679e398fb4bSSouptick Joarder rc = lock_pages(kbufs, kdata.num, pages, nr_pages, &pinned); 680e398fb4bSSouptick Joarder if (rc < 0) { 681e398fb4bSSouptick Joarder nr_pages = pinned; 682ab520be8SPaul Durrant goto out; 683e398fb4bSSouptick Joarder } 684ab520be8SPaul Durrant 685ab520be8SPaul Durrant for (i = 0; i < kdata.num; i++) { 686ab520be8SPaul Durrant set_xen_guest_handle(xbufs[i].h, kbufs[i].uptr); 687ab520be8SPaul Durrant xbufs[i].size = kbufs[i].size; 688ab520be8SPaul Durrant } 689ab520be8SPaul Durrant 690ab520be8SPaul Durrant xen_preemptible_hcall_begin(); 691ab520be8SPaul Durrant rc = HYPERVISOR_dm_op(kdata.dom, kdata.num, xbufs); 692ab520be8SPaul Durrant xen_preemptible_hcall_end(); 693ab520be8SPaul Durrant 694ab520be8SPaul Durrant out: 695ab520be8SPaul Durrant unlock_pages(pages, nr_pages); 696ab520be8SPaul Durrant kfree(xbufs); 697ab520be8SPaul Durrant kfree(pages); 698ab520be8SPaul Durrant kfree(kbufs); 699ab520be8SPaul Durrant 700ab520be8SPaul Durrant return rc; 701ab520be8SPaul Durrant } 702ab520be8SPaul Durrant 7034610d240SPaul Durrant static long privcmd_ioctl_restrict(struct file *file, void __user *udata) 7044610d240SPaul Durrant { 7054610d240SPaul Durrant struct privcmd_data *data = file->private_data; 7064610d240SPaul Durrant domid_t dom; 7074610d240SPaul Durrant 7084610d240SPaul Durrant if (copy_from_user(&dom, udata, sizeof(dom))) 7094610d240SPaul Durrant return -EFAULT; 7104610d240SPaul Durrant 7114610d240SPaul Durrant /* Set restriction to the specified domain, or check it matches */ 7124610d240SPaul Durrant if (data->domid == DOMID_INVALID) 7134610d240SPaul Durrant data->domid = dom; 7144610d240SPaul Durrant else if (data->domid != dom) 7154610d240SPaul Durrant return -EINVAL; 7164610d240SPaul Durrant 7174610d240SPaul Durrant return 0; 7184610d240SPaul Durrant } 7194610d240SPaul Durrant 720*ef3a575bSRoger Pau Monne static long privcmd_ioctl_mmap_resource(struct file *file, 721*ef3a575bSRoger Pau Monne struct privcmd_mmap_resource __user *udata) 7223ad08765SPaul Durrant { 7233ad08765SPaul Durrant struct privcmd_data *data = file->private_data; 7243ad08765SPaul Durrant struct mm_struct *mm = current->mm; 7253ad08765SPaul Durrant struct vm_area_struct *vma; 7263ad08765SPaul Durrant struct privcmd_mmap_resource kdata; 7273ad08765SPaul Durrant xen_pfn_t *pfns = NULL; 728*ef3a575bSRoger Pau Monne struct xen_mem_acquire_resource xdata = { }; 7293ad08765SPaul Durrant int rc; 7303ad08765SPaul Durrant 7313ad08765SPaul Durrant if (copy_from_user(&kdata, udata, sizeof(kdata))) 7323ad08765SPaul Durrant return -EFAULT; 7333ad08765SPaul Durrant 7343ad08765SPaul Durrant /* If restriction is in place, check the domid matches */ 7353ad08765SPaul Durrant if (data->domid != DOMID_INVALID && data->domid != kdata.dom) 7363ad08765SPaul Durrant return -EPERM; 7373ad08765SPaul Durrant 738*ef3a575bSRoger Pau Monne /* Both fields must be set or unset */ 739*ef3a575bSRoger Pau Monne if (!!kdata.addr != !!kdata.num) 740*ef3a575bSRoger Pau Monne return -EINVAL; 741*ef3a575bSRoger Pau Monne 742*ef3a575bSRoger Pau Monne xdata.domid = kdata.dom; 743*ef3a575bSRoger Pau Monne xdata.type = kdata.type; 744*ef3a575bSRoger Pau Monne xdata.id = kdata.id; 745*ef3a575bSRoger Pau Monne 746*ef3a575bSRoger Pau Monne if (!kdata.addr && !kdata.num) { 747*ef3a575bSRoger Pau Monne /* Query the size of the resource. */ 748*ef3a575bSRoger Pau Monne rc = HYPERVISOR_memory_op(XENMEM_acquire_resource, &xdata); 749*ef3a575bSRoger Pau Monne if (rc) 750*ef3a575bSRoger Pau Monne return rc; 751*ef3a575bSRoger Pau Monne return __put_user(xdata.nr_frames, &udata->num); 752*ef3a575bSRoger Pau Monne } 753*ef3a575bSRoger Pau Monne 754d8ed45c5SMichel Lespinasse mmap_write_lock(mm); 7553ad08765SPaul Durrant 7563ad08765SPaul Durrant vma = find_vma(mm, kdata.addr); 7573ad08765SPaul Durrant if (!vma || vma->vm_ops != &privcmd_vm_ops) { 7583ad08765SPaul Durrant rc = -EINVAL; 7593ad08765SPaul Durrant goto out; 7603ad08765SPaul Durrant } 7613ad08765SPaul Durrant 7623ad08765SPaul Durrant pfns = kcalloc(kdata.num, sizeof(*pfns), GFP_KERNEL); 7633ad08765SPaul Durrant if (!pfns) { 7643ad08765SPaul Durrant rc = -ENOMEM; 7653ad08765SPaul Durrant goto out; 7663ad08765SPaul Durrant } 7673ad08765SPaul Durrant 768a78d14a3SArnd Bergmann if (IS_ENABLED(CONFIG_XEN_AUTO_XLATE) && 769a78d14a3SArnd Bergmann xen_feature(XENFEAT_auto_translated_physmap)) { 7703ad08765SPaul Durrant unsigned int nr = DIV_ROUND_UP(kdata.num, XEN_PFN_PER_PAGE); 7713ad08765SPaul Durrant struct page **pages; 7723ad08765SPaul Durrant unsigned int i; 7733ad08765SPaul Durrant 7743ad08765SPaul Durrant rc = alloc_empty_pages(vma, nr); 7753ad08765SPaul Durrant if (rc < 0) 7763ad08765SPaul Durrant goto out; 7773ad08765SPaul Durrant 7783ad08765SPaul Durrant pages = vma->vm_private_data; 7793ad08765SPaul Durrant for (i = 0; i < kdata.num; i++) { 7803ad08765SPaul Durrant xen_pfn_t pfn = 7813ad08765SPaul Durrant page_to_xen_pfn(pages[i / XEN_PFN_PER_PAGE]); 7823ad08765SPaul Durrant 7833ad08765SPaul Durrant pfns[i] = pfn + (i % XEN_PFN_PER_PAGE); 7843ad08765SPaul Durrant } 7853ad08765SPaul Durrant } else 7863ad08765SPaul Durrant vma->vm_private_data = PRIV_VMA_LOCKED; 7873ad08765SPaul Durrant 7883ad08765SPaul Durrant xdata.frame = kdata.idx; 7893ad08765SPaul Durrant xdata.nr_frames = kdata.num; 7903ad08765SPaul Durrant set_xen_guest_handle(xdata.frame_list, pfns); 7913ad08765SPaul Durrant 7923ad08765SPaul Durrant xen_preemptible_hcall_begin(); 7933ad08765SPaul Durrant rc = HYPERVISOR_memory_op(XENMEM_acquire_resource, &xdata); 7943ad08765SPaul Durrant xen_preemptible_hcall_end(); 7953ad08765SPaul Durrant 7963ad08765SPaul Durrant if (rc) 7973ad08765SPaul Durrant goto out; 7983ad08765SPaul Durrant 799a78d14a3SArnd Bergmann if (IS_ENABLED(CONFIG_XEN_AUTO_XLATE) && 800a78d14a3SArnd Bergmann xen_feature(XENFEAT_auto_translated_physmap)) { 801a78d14a3SArnd Bergmann rc = xen_remap_vma_range(vma, kdata.addr, kdata.num << PAGE_SHIFT); 8023ad08765SPaul Durrant } else { 8033ad08765SPaul Durrant unsigned int domid = 8043ad08765SPaul Durrant (xdata.flags & XENMEM_rsrc_acq_caller_owned) ? 8053ad08765SPaul Durrant DOMID_SELF : kdata.dom; 8063ad08765SPaul Durrant int num; 8073ad08765SPaul Durrant 8083ad08765SPaul Durrant num = xen_remap_domain_mfn_array(vma, 8093ad08765SPaul Durrant kdata.addr & PAGE_MASK, 8103ad08765SPaul Durrant pfns, kdata.num, (int *)pfns, 8113ad08765SPaul Durrant vma->vm_page_prot, 8123ad08765SPaul Durrant domid, 8133ad08765SPaul Durrant vma->vm_private_data); 8143ad08765SPaul Durrant if (num < 0) 8153ad08765SPaul Durrant rc = num; 8163ad08765SPaul Durrant else if (num != kdata.num) { 8173ad08765SPaul Durrant unsigned int i; 8183ad08765SPaul Durrant 8193ad08765SPaul Durrant for (i = 0; i < num; i++) { 8203ad08765SPaul Durrant rc = pfns[i]; 8213ad08765SPaul Durrant if (rc < 0) 8223ad08765SPaul Durrant break; 8233ad08765SPaul Durrant } 8243ad08765SPaul Durrant } else 8253ad08765SPaul Durrant rc = 0; 8263ad08765SPaul Durrant } 8273ad08765SPaul Durrant 8283ad08765SPaul Durrant out: 829d8ed45c5SMichel Lespinasse mmap_write_unlock(mm); 8303ad08765SPaul Durrant kfree(pfns); 8313ad08765SPaul Durrant 8323ad08765SPaul Durrant return rc; 8333ad08765SPaul Durrant } 8343ad08765SPaul Durrant 835d8414d3cSBastian Blank static long privcmd_ioctl(struct file *file, 836d8414d3cSBastian Blank unsigned int cmd, unsigned long data) 837d8414d3cSBastian Blank { 838dc9eab6fSPaul Durrant int ret = -ENOTTY; 839d8414d3cSBastian Blank void __user *udata = (void __user *) data; 840d8414d3cSBastian Blank 841d8414d3cSBastian Blank switch (cmd) { 842d8414d3cSBastian Blank case IOCTL_PRIVCMD_HYPERCALL: 8434610d240SPaul Durrant ret = privcmd_ioctl_hypercall(file, udata); 844d8414d3cSBastian Blank break; 845d8414d3cSBastian Blank 846d8414d3cSBastian Blank case IOCTL_PRIVCMD_MMAP: 8474610d240SPaul Durrant ret = privcmd_ioctl_mmap(file, udata); 848d8414d3cSBastian Blank break; 849d8414d3cSBastian Blank 850d8414d3cSBastian Blank case IOCTL_PRIVCMD_MMAPBATCH: 8514610d240SPaul Durrant ret = privcmd_ioctl_mmap_batch(file, udata, 1); 852ceb90fa0SAndres Lagar-Cavilla break; 853ceb90fa0SAndres Lagar-Cavilla 854ceb90fa0SAndres Lagar-Cavilla case IOCTL_PRIVCMD_MMAPBATCH_V2: 8554610d240SPaul Durrant ret = privcmd_ioctl_mmap_batch(file, udata, 2); 856d8414d3cSBastian Blank break; 857d8414d3cSBastian Blank 858ab520be8SPaul Durrant case IOCTL_PRIVCMD_DM_OP: 8594610d240SPaul Durrant ret = privcmd_ioctl_dm_op(file, udata); 8604610d240SPaul Durrant break; 8614610d240SPaul Durrant 8624610d240SPaul Durrant case IOCTL_PRIVCMD_RESTRICT: 8634610d240SPaul Durrant ret = privcmd_ioctl_restrict(file, udata); 864ab520be8SPaul Durrant break; 865ab520be8SPaul Durrant 8663ad08765SPaul Durrant case IOCTL_PRIVCMD_MMAP_RESOURCE: 8673ad08765SPaul Durrant ret = privcmd_ioctl_mmap_resource(file, udata); 8683ad08765SPaul Durrant break; 8693ad08765SPaul Durrant 870d8414d3cSBastian Blank default: 871d8414d3cSBastian Blank break; 872d8414d3cSBastian Blank } 873d8414d3cSBastian Blank 874d8414d3cSBastian Blank return ret; 875d8414d3cSBastian Blank } 876d8414d3cSBastian Blank 8774610d240SPaul Durrant static int privcmd_open(struct inode *ino, struct file *file) 8784610d240SPaul Durrant { 8794610d240SPaul Durrant struct privcmd_data *data = kzalloc(sizeof(*data), GFP_KERNEL); 8804610d240SPaul Durrant 8814610d240SPaul Durrant if (!data) 8824610d240SPaul Durrant return -ENOMEM; 8834610d240SPaul Durrant 8844610d240SPaul Durrant /* DOMID_INVALID implies no restriction */ 8854610d240SPaul Durrant data->domid = DOMID_INVALID; 8864610d240SPaul Durrant 8874610d240SPaul Durrant file->private_data = data; 8884610d240SPaul Durrant return 0; 8894610d240SPaul Durrant } 8904610d240SPaul Durrant 8914610d240SPaul Durrant static int privcmd_release(struct inode *ino, struct file *file) 8924610d240SPaul Durrant { 8934610d240SPaul Durrant struct privcmd_data *data = file->private_data; 8944610d240SPaul Durrant 8954610d240SPaul Durrant kfree(data); 8964610d240SPaul Durrant return 0; 8974610d240SPaul Durrant } 8984610d240SPaul Durrant 899d71f5139SMukesh Rathor static void privcmd_close(struct vm_area_struct *vma) 900d71f5139SMukesh Rathor { 901d71f5139SMukesh Rathor struct page **pages = vma->vm_private_data; 902c7ebf9d9SMuhammad Falak R Wani int numpgs = vma_pages(vma); 9035995a68aSJulien Grall int numgfns = (vma->vm_end - vma->vm_start) >> XEN_PAGE_SHIFT; 904b6497b38SIan Campbell int rc; 905d71f5139SMukesh Rathor 9069eff37a8SDan Carpenter if (!xen_feature(XENFEAT_auto_translated_physmap) || !numpgs || !pages) 907d71f5139SMukesh Rathor return; 908d71f5139SMukesh Rathor 9095995a68aSJulien Grall rc = xen_unmap_domain_gfn_range(vma, numgfns, pages); 910b6497b38SIan Campbell if (rc == 0) 9119e2369c0SRoger Pau Monne xen_free_unpopulated_pages(numpgs, pages); 912b6497b38SIan Campbell else 913b6497b38SIan Campbell pr_crit("unable to unmap MFN range: leaking %d pages. rc=%d\n", 914b6497b38SIan Campbell numpgs, rc); 915d71f5139SMukesh Rathor kfree(pages); 916d71f5139SMukesh Rathor } 917d71f5139SMukesh Rathor 9184bf2cc96SSouptick Joarder static vm_fault_t privcmd_fault(struct vm_fault *vmf) 919d8414d3cSBastian Blank { 920d8414d3cSBastian Blank printk(KERN_DEBUG "privcmd_fault: vma=%p %lx-%lx, pgoff=%lx, uv=%p\n", 92111bac800SDave Jiang vmf->vma, vmf->vma->vm_start, vmf->vma->vm_end, 9221a29d85eSJan Kara vmf->pgoff, (void *)vmf->address); 923d8414d3cSBastian Blank 924d8414d3cSBastian Blank return VM_FAULT_SIGBUS; 925d8414d3cSBastian Blank } 926d8414d3cSBastian Blank 9277cbea8dcSKirill A. Shutemov static const struct vm_operations_struct privcmd_vm_ops = { 928d71f5139SMukesh Rathor .close = privcmd_close, 929d8414d3cSBastian Blank .fault = privcmd_fault 930d8414d3cSBastian Blank }; 931d8414d3cSBastian Blank 932d8414d3cSBastian Blank static int privcmd_mmap(struct file *file, struct vm_area_struct *vma) 933d8414d3cSBastian Blank { 934d8414d3cSBastian Blank /* DONTCOPY is essential for Xen because copy_page_range doesn't know 935d8414d3cSBastian Blank * how to recreate these mappings */ 936314e51b9SKonstantin Khlebnikov vma->vm_flags |= VM_IO | VM_PFNMAP | VM_DONTCOPY | 937314e51b9SKonstantin Khlebnikov VM_DONTEXPAND | VM_DONTDUMP; 938d8414d3cSBastian Blank vma->vm_ops = &privcmd_vm_ops; 939d8414d3cSBastian Blank vma->vm_private_data = NULL; 940d8414d3cSBastian Blank 941d8414d3cSBastian Blank return 0; 942d8414d3cSBastian Blank } 943d8414d3cSBastian Blank 944a5deabe0SAndres Lagar-Cavilla /* 945a5deabe0SAndres Lagar-Cavilla * For MMAPBATCH*. This allows asserting the singleshot mapping 946a5deabe0SAndres Lagar-Cavilla * on a per pfn/pte basis. Mapping calls that fail with ENOENT 947a5deabe0SAndres Lagar-Cavilla * can be then retried until success. 948a5deabe0SAndres Lagar-Cavilla */ 9498b1e0f81SAnshuman Khandual static int is_mapped_fn(pte_t *pte, unsigned long addr, void *data) 950d8414d3cSBastian Blank { 951a5deabe0SAndres Lagar-Cavilla return pte_none(*pte) ? 0 : -EBUSY; 952a5deabe0SAndres Lagar-Cavilla } 953a5deabe0SAndres Lagar-Cavilla 954a5deabe0SAndres Lagar-Cavilla static int privcmd_vma_range_is_mapped( 955a5deabe0SAndres Lagar-Cavilla struct vm_area_struct *vma, 956a5deabe0SAndres Lagar-Cavilla unsigned long addr, 957a5deabe0SAndres Lagar-Cavilla unsigned long nr_pages) 958a5deabe0SAndres Lagar-Cavilla { 959a5deabe0SAndres Lagar-Cavilla return apply_to_page_range(vma->vm_mm, addr, nr_pages << PAGE_SHIFT, 960a5deabe0SAndres Lagar-Cavilla is_mapped_fn, NULL) != 0; 961d8414d3cSBastian Blank } 962d8414d3cSBastian Blank 963d8414d3cSBastian Blank const struct file_operations xen_privcmd_fops = { 964d8414d3cSBastian Blank .owner = THIS_MODULE, 965d8414d3cSBastian Blank .unlocked_ioctl = privcmd_ioctl, 9664610d240SPaul Durrant .open = privcmd_open, 9674610d240SPaul Durrant .release = privcmd_release, 968d8414d3cSBastian Blank .mmap = privcmd_mmap, 969d8414d3cSBastian Blank }; 970d8414d3cSBastian Blank EXPORT_SYMBOL_GPL(xen_privcmd_fops); 971d8414d3cSBastian Blank 972d8414d3cSBastian Blank static struct miscdevice privcmd_dev = { 973d8414d3cSBastian Blank .minor = MISC_DYNAMIC_MINOR, 974d8414d3cSBastian Blank .name = "xen/privcmd", 975d8414d3cSBastian Blank .fops = &xen_privcmd_fops, 976d8414d3cSBastian Blank }; 977d8414d3cSBastian Blank 978d8414d3cSBastian Blank static int __init privcmd_init(void) 979d8414d3cSBastian Blank { 980d8414d3cSBastian Blank int err; 981d8414d3cSBastian Blank 982d8414d3cSBastian Blank if (!xen_domain()) 983d8414d3cSBastian Blank return -ENODEV; 984d8414d3cSBastian Blank 985d8414d3cSBastian Blank err = misc_register(&privcmd_dev); 986d8414d3cSBastian Blank if (err != 0) { 987283c0972SJoe Perches pr_err("Could not register Xen privcmd device\n"); 988d8414d3cSBastian Blank return err; 989d8414d3cSBastian Blank } 990c51b3c63SJuergen Gross 991c51b3c63SJuergen Gross err = misc_register(&xen_privcmdbuf_dev); 992c51b3c63SJuergen Gross if (err != 0) { 993c51b3c63SJuergen Gross pr_err("Could not register Xen hypercall-buf device\n"); 994c51b3c63SJuergen Gross misc_deregister(&privcmd_dev); 995c51b3c63SJuergen Gross return err; 996c51b3c63SJuergen Gross } 997c51b3c63SJuergen Gross 998d8414d3cSBastian Blank return 0; 999d8414d3cSBastian Blank } 1000d8414d3cSBastian Blank 1001d8414d3cSBastian Blank static void __exit privcmd_exit(void) 1002d8414d3cSBastian Blank { 1003d8414d3cSBastian Blank misc_deregister(&privcmd_dev); 1004c51b3c63SJuergen Gross misc_deregister(&xen_privcmdbuf_dev); 1005d8414d3cSBastian Blank } 1006d8414d3cSBastian Blank 1007d8414d3cSBastian Blank module_init(privcmd_init); 1008d8414d3cSBastian Blank module_exit(privcmd_exit); 1009