1bf7313e3SRoger Pau Monné /*
2bf7313e3SRoger Pau Monné * Copyright (c) 2014 Roger Pau Monné <roger.pau@citrix.com>
3bf7313e3SRoger Pau Monné * All rights reserved.
4bf7313e3SRoger Pau Monné *
5bf7313e3SRoger Pau Monné * Redistribution and use in source and binary forms, with or without
6bf7313e3SRoger Pau Monné * modification, are permitted provided that the following conditions
7bf7313e3SRoger Pau Monné * are met:
8bf7313e3SRoger Pau Monné * 1. Redistributions of source code must retain the above copyright
9bf7313e3SRoger Pau Monné * notice, this list of conditions and the following disclaimer.
10bf7313e3SRoger Pau Monné * 2. Redistributions in binary form must reproduce the above copyright
11bf7313e3SRoger Pau Monné * notice, this list of conditions and the following disclaimer in the
12bf7313e3SRoger Pau Monné * documentation and/or other materials provided with the distribution.
13bf7313e3SRoger Pau Monné *
14bf7313e3SRoger Pau Monné * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS AS IS'' AND
15bf7313e3SRoger Pau Monné * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16bf7313e3SRoger Pau Monné * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17bf7313e3SRoger Pau Monné * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18bf7313e3SRoger Pau Monné * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19bf7313e3SRoger Pau Monné * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20bf7313e3SRoger Pau Monné * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21bf7313e3SRoger Pau Monné * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22bf7313e3SRoger Pau Monné * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23bf7313e3SRoger Pau Monné * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24bf7313e3SRoger Pau Monné * SUCH DAMAGE.
25bf7313e3SRoger Pau Monné */
26bf7313e3SRoger Pau Monné
27bf7313e3SRoger Pau Monné #include <sys/param.h>
28bf7313e3SRoger Pau Monné #include <sys/systm.h>
29bf7313e3SRoger Pau Monné #include <sys/uio.h>
30bf7313e3SRoger Pau Monné #include <sys/bus.h>
31bf7313e3SRoger Pau Monné #include <sys/malloc.h>
32bf7313e3SRoger Pau Monné #include <sys/kernel.h>
33bf7313e3SRoger Pau Monné #include <sys/lock.h>
34bf7313e3SRoger Pau Monné #include <sys/mutex.h>
35bf7313e3SRoger Pau Monné #include <sys/rwlock.h>
36bf7313e3SRoger Pau Monné #include <sys/selinfo.h>
37bf7313e3SRoger Pau Monné #include <sys/poll.h>
38bf7313e3SRoger Pau Monné #include <sys/conf.h>
39bf7313e3SRoger Pau Monné #include <sys/fcntl.h>
40bf7313e3SRoger Pau Monné #include <sys/ioccom.h>
41bf7313e3SRoger Pau Monné #include <sys/rman.h>
42bf7313e3SRoger Pau Monné #include <sys/tree.h>
43bf7313e3SRoger Pau Monné #include <sys/module.h>
44bf7313e3SRoger Pau Monné #include <sys/proc.h>
45288b2385SRoger Pau Monné #include <sys/bitset.h>
46bf7313e3SRoger Pau Monné
47bf7313e3SRoger Pau Monné #include <vm/vm.h>
48bf7313e3SRoger Pau Monné #include <vm/vm_param.h>
49bf7313e3SRoger Pau Monné #include <vm/vm_extern.h>
50bf7313e3SRoger Pau Monné #include <vm/vm_kern.h>
51bf7313e3SRoger Pau Monné #include <vm/vm_page.h>
52bf7313e3SRoger Pau Monné #include <vm/vm_map.h>
53bf7313e3SRoger Pau Monné #include <vm/vm_object.h>
54bf7313e3SRoger Pau Monné #include <vm/vm_pager.h>
55bf7313e3SRoger Pau Monné
56bf7313e3SRoger Pau Monné #include <machine/md_var.h>
57bf7313e3SRoger Pau Monné
58bf7313e3SRoger Pau Monné #include <xen/xen-os.h>
59bf7313e3SRoger Pau Monné #include <xen/hypervisor.h>
60bf7313e3SRoger Pau Monné #include <xen/privcmd.h>
61bf7313e3SRoger Pau Monné #include <xen/error.h>
62bf7313e3SRoger Pau Monné
63bf7313e3SRoger Pau Monné MALLOC_DEFINE(M_PRIVCMD, "privcmd_dev", "Xen privcmd user-space device");
64bf7313e3SRoger Pau Monné
65ed78016dSRoger Pau Monne #define MAX_DMOP_BUFFERS 16
66ed78016dSRoger Pau Monne
67bf7313e3SRoger Pau Monné struct privcmd_map {
68bf7313e3SRoger Pau Monné vm_object_t mem;
69bf7313e3SRoger Pau Monné vm_size_t size;
70bf7313e3SRoger Pau Monné struct resource *pseudo_phys_res;
71bf7313e3SRoger Pau Monné int pseudo_phys_res_id;
72bf7313e3SRoger Pau Monné vm_paddr_t phys_base_addr;
73bf7313e3SRoger Pau Monné boolean_t mapped;
74288b2385SRoger Pau Monné BITSET_DEFINE_VAR() *err;
75bf7313e3SRoger Pau Monné };
76bf7313e3SRoger Pau Monné
77bf7313e3SRoger Pau Monné static d_ioctl_t privcmd_ioctl;
78a7650787SRoger Pau Monne static d_open_t privcmd_open;
79bf7313e3SRoger Pau Monné static d_mmap_single_t privcmd_mmap_single;
80bf7313e3SRoger Pau Monné
81bf7313e3SRoger Pau Monné static struct cdevsw privcmd_devsw = {
82bf7313e3SRoger Pau Monné .d_version = D_VERSION,
83bf7313e3SRoger Pau Monné .d_ioctl = privcmd_ioctl,
84bf7313e3SRoger Pau Monné .d_mmap_single = privcmd_mmap_single,
85a7650787SRoger Pau Monne .d_open = privcmd_open,
86bf7313e3SRoger Pau Monné .d_name = "privcmd",
87bf7313e3SRoger Pau Monné };
88bf7313e3SRoger Pau Monné
89bf7313e3SRoger Pau Monné static int privcmd_pg_ctor(void *handle, vm_ooffset_t size, vm_prot_t prot,
90bf7313e3SRoger Pau Monné vm_ooffset_t foff, struct ucred *cred, u_short *color);
91bf7313e3SRoger Pau Monné static void privcmd_pg_dtor(void *handle);
92bf7313e3SRoger Pau Monné static int privcmd_pg_fault(vm_object_t object, vm_ooffset_t offset,
93bf7313e3SRoger Pau Monné int prot, vm_page_t *mres);
94bf7313e3SRoger Pau Monné
95bf7313e3SRoger Pau Monné static struct cdev_pager_ops privcmd_pg_ops = {
96bf7313e3SRoger Pau Monné .cdev_pg_fault = privcmd_pg_fault,
97bf7313e3SRoger Pau Monné .cdev_pg_ctor = privcmd_pg_ctor,
98bf7313e3SRoger Pau Monné .cdev_pg_dtor = privcmd_pg_dtor,
99bf7313e3SRoger Pau Monné };
100bf7313e3SRoger Pau Monné
101a7650787SRoger Pau Monne struct per_user_data {
102a7650787SRoger Pau Monne domid_t dom;
103a7650787SRoger Pau Monne };
104a7650787SRoger Pau Monne
105bf7313e3SRoger Pau Monné static device_t privcmd_dev = NULL;
106bf7313e3SRoger Pau Monné
107bf7313e3SRoger Pau Monné /*------------------------- Privcmd Pager functions --------------------------*/
108bf7313e3SRoger Pau Monné static int
privcmd_pg_ctor(void * handle,vm_ooffset_t size,vm_prot_t prot,vm_ooffset_t foff,struct ucred * cred,u_short * color)109bf7313e3SRoger Pau Monné privcmd_pg_ctor(void *handle, vm_ooffset_t size, vm_prot_t prot,
110bf7313e3SRoger Pau Monné vm_ooffset_t foff, struct ucred *cred, u_short *color)
111bf7313e3SRoger Pau Monné {
112bf7313e3SRoger Pau Monné
113bf7313e3SRoger Pau Monné return (0);
114bf7313e3SRoger Pau Monné }
115bf7313e3SRoger Pau Monné
116bf7313e3SRoger Pau Monné static void
privcmd_pg_dtor(void * handle)117bf7313e3SRoger Pau Monné privcmd_pg_dtor(void *handle)
118bf7313e3SRoger Pau Monné {
119bf7313e3SRoger Pau Monné struct xen_remove_from_physmap rm = { .domid = DOMID_SELF };
120bf7313e3SRoger Pau Monné struct privcmd_map *map = handle;
121e7236a7dSMateusz Guzik int error __diagused;
122bf7313e3SRoger Pau Monné vm_size_t i;
123bf7313e3SRoger Pau Monné vm_page_t m;
124bf7313e3SRoger Pau Monné
125bf7313e3SRoger Pau Monné /*
126bf7313e3SRoger Pau Monné * Remove the mappings from the used pages. This will remove the
127bf7313e3SRoger Pau Monné * underlying p2m bindings in Xen second stage translation.
128bf7313e3SRoger Pau Monné */
129bf7313e3SRoger Pau Monné if (map->mapped == true) {
130bf7313e3SRoger Pau Monné VM_OBJECT_WLOCK(map->mem);
131bf7313e3SRoger Pau Monné retry:
132bf7313e3SRoger Pau Monné for (i = 0; i < map->size; i++) {
133bf7313e3SRoger Pau Monné m = vm_page_lookup(map->mem, i);
134bf7313e3SRoger Pau Monné if (m == NULL)
135bf7313e3SRoger Pau Monné continue;
136c7575748SJeff Roberson if (vm_page_busy_acquire(m, VM_ALLOC_WAITFAIL) == 0)
137bf7313e3SRoger Pau Monné goto retry;
138*d48524e2SDoug Moore cdev_mgtdev_pager_free_page(map->mem, m);
139bf7313e3SRoger Pau Monné }
140bf7313e3SRoger Pau Monné VM_OBJECT_WUNLOCK(map->mem);
141bf7313e3SRoger Pau Monné
142bf7313e3SRoger Pau Monné for (i = 0; i < map->size; i++) {
143bf7313e3SRoger Pau Monné rm.gpfn = atop(map->phys_base_addr) + i;
144bf7313e3SRoger Pau Monné HYPERVISOR_memory_op(XENMEM_remove_from_physmap, &rm);
145bf7313e3SRoger Pau Monné }
146288b2385SRoger Pau Monné free(map->err, M_PRIVCMD);
147bf7313e3SRoger Pau Monné }
148bf7313e3SRoger Pau Monné
1490df8b29dSRoger Pau Monné error = xenmem_free(privcmd_dev, map->pseudo_phys_res_id,
1500df8b29dSRoger Pau Monné map->pseudo_phys_res);
151bf7313e3SRoger Pau Monné KASSERT(error == 0, ("Unable to release memory resource: %d", error));
152bf7313e3SRoger Pau Monné
153bf7313e3SRoger Pau Monné free(map, M_PRIVCMD);
154bf7313e3SRoger Pau Monné }
155bf7313e3SRoger Pau Monné
156bf7313e3SRoger Pau Monné static int
privcmd_pg_fault(vm_object_t object,vm_ooffset_t offset,int prot,vm_page_t * mres)157bf7313e3SRoger Pau Monné privcmd_pg_fault(vm_object_t object, vm_ooffset_t offset,
158bf7313e3SRoger Pau Monné int prot, vm_page_t *mres)
159bf7313e3SRoger Pau Monné {
160bf7313e3SRoger Pau Monné struct privcmd_map *map = object->handle;
161bf7313e3SRoger Pau Monné vm_pindex_t pidx;
1623cf3b4e6SJeff Roberson vm_page_t page;
163bf7313e3SRoger Pau Monné
164bf7313e3SRoger Pau Monné if (map->mapped != true)
165bf7313e3SRoger Pau Monné return (VM_PAGER_FAIL);
166bf7313e3SRoger Pau Monné
167bf7313e3SRoger Pau Monné pidx = OFF_TO_IDX(offset);
168288b2385SRoger Pau Monné if (pidx >= map->size || BIT_ISSET(map->size, pidx, map->err))
169bf7313e3SRoger Pau Monné return (VM_PAGER_FAIL);
170bf7313e3SRoger Pau Monné
171bf7313e3SRoger Pau Monné page = PHYS_TO_VM_PAGE(map->phys_base_addr + offset);
172bf7313e3SRoger Pau Monné if (page == NULL)
173bf7313e3SRoger Pau Monné return (VM_PAGER_FAIL);
174bf7313e3SRoger Pau Monné
175bf7313e3SRoger Pau Monné KASSERT((page->flags & PG_FICTITIOUS) != 0,
176bf7313e3SRoger Pau Monné ("not fictitious %p", page));
177fee2a2faSMark Johnston KASSERT(vm_page_wired(page), ("page %p not wired", page));
178fee2a2faSMark Johnston KASSERT(!vm_page_busied(page), ("page %p is busy", page));
179bf7313e3SRoger Pau Monné
18063e97555SJeff Roberson vm_page_busy_acquire(page, 0);
1810012f373SJeff Roberson vm_page_valid(page);
1823cf3b4e6SJeff Roberson
1833cf3b4e6SJeff Roberson if (*mres != NULL)
1843cf3b4e6SJeff Roberson vm_page_replace(page, object, pidx, *mres);
1853cf3b4e6SJeff Roberson else
186bf7313e3SRoger Pau Monné vm_page_insert(page, object, pidx);
187bf7313e3SRoger Pau Monné *mres = page;
188bf7313e3SRoger Pau Monné return (VM_PAGER_OK);
189bf7313e3SRoger Pau Monné }
190bf7313e3SRoger Pau Monné
191bf7313e3SRoger Pau Monné /*----------------------- Privcmd char device methods ------------------------*/
192bf7313e3SRoger Pau Monné static int
privcmd_mmap_single(struct cdev * cdev,vm_ooffset_t * offset,vm_size_t size,vm_object_t * object,int nprot)193bf7313e3SRoger Pau Monné privcmd_mmap_single(struct cdev *cdev, vm_ooffset_t *offset, vm_size_t size,
194bf7313e3SRoger Pau Monné vm_object_t *object, int nprot)
195bf7313e3SRoger Pau Monné {
196bf7313e3SRoger Pau Monné struct privcmd_map *map;
197bf7313e3SRoger Pau Monné
198bf7313e3SRoger Pau Monné map = malloc(sizeof(*map), M_PRIVCMD, M_WAITOK | M_ZERO);
199bf7313e3SRoger Pau Monné
200bf7313e3SRoger Pau Monné map->size = OFF_TO_IDX(size);
201bf7313e3SRoger Pau Monné map->pseudo_phys_res_id = 0;
202bf7313e3SRoger Pau Monné
2030df8b29dSRoger Pau Monné map->pseudo_phys_res = xenmem_alloc(privcmd_dev,
2040df8b29dSRoger Pau Monné &map->pseudo_phys_res_id, size);
205bf7313e3SRoger Pau Monné if (map->pseudo_phys_res == NULL) {
206bf7313e3SRoger Pau Monné free(map, M_PRIVCMD);
207bf7313e3SRoger Pau Monné return (ENOMEM);
208bf7313e3SRoger Pau Monné }
209bf7313e3SRoger Pau Monné
210bf7313e3SRoger Pau Monné map->phys_base_addr = rman_get_start(map->pseudo_phys_res);
211bf7313e3SRoger Pau Monné map->mem = cdev_pager_allocate(map, OBJT_MGTDEVICE, &privcmd_pg_ops,
212bf7313e3SRoger Pau Monné size, nprot, *offset, NULL);
213bf7313e3SRoger Pau Monné if (map->mem == NULL) {
2140df8b29dSRoger Pau Monné xenmem_free(privcmd_dev, map->pseudo_phys_res_id,
2150df8b29dSRoger Pau Monné map->pseudo_phys_res);
216bf7313e3SRoger Pau Monné free(map, M_PRIVCMD);
217bf7313e3SRoger Pau Monné return (ENOMEM);
218bf7313e3SRoger Pau Monné }
219bf7313e3SRoger Pau Monné
220bf7313e3SRoger Pau Monné *object = map->mem;
221bf7313e3SRoger Pau Monné
222bf7313e3SRoger Pau Monné return (0);
223bf7313e3SRoger Pau Monné }
224bf7313e3SRoger Pau Monné
225147e5939SRoger Pau Monné static struct privcmd_map *
setup_virtual_area(struct thread * td,unsigned long addr,unsigned long num)226147e5939SRoger Pau Monné setup_virtual_area(struct thread *td, unsigned long addr, unsigned long num)
227147e5939SRoger Pau Monné {
228147e5939SRoger Pau Monné vm_map_t map;
229147e5939SRoger Pau Monné vm_map_entry_t entry;
230147e5939SRoger Pau Monné vm_object_t mem;
231147e5939SRoger Pau Monné vm_pindex_t pindex;
232147e5939SRoger Pau Monné vm_prot_t prot;
233147e5939SRoger Pau Monné boolean_t wired;
234147e5939SRoger Pau Monné struct privcmd_map *umap;
235147e5939SRoger Pau Monné int error;
236147e5939SRoger Pau Monné
237147e5939SRoger Pau Monné if ((num == 0) || ((addr & PAGE_MASK) != 0))
238147e5939SRoger Pau Monné return NULL;
239147e5939SRoger Pau Monné
240147e5939SRoger Pau Monné map = &td->td_proc->p_vmspace->vm_map;
241147e5939SRoger Pau Monné error = vm_map_lookup(&map, addr, VM_PROT_NONE, &entry, &mem, &pindex,
242147e5939SRoger Pau Monné &prot, &wired);
243147e5939SRoger Pau Monné if (error != KERN_SUCCESS || (entry->start != addr) ||
244147e5939SRoger Pau Monné (entry->end != addr + (num * PAGE_SIZE)))
245147e5939SRoger Pau Monné return NULL;
246147e5939SRoger Pau Monné
247147e5939SRoger Pau Monné vm_map_lookup_done(map, entry);
248147e5939SRoger Pau Monné if ((mem->type != OBJT_MGTDEVICE) ||
249147e5939SRoger Pau Monné (mem->un_pager.devp.ops != &privcmd_pg_ops))
250147e5939SRoger Pau Monné return NULL;
251147e5939SRoger Pau Monné
252147e5939SRoger Pau Monné umap = mem->handle;
253147e5939SRoger Pau Monné /* Allocate a bitset to store broken page mappings. */
254147e5939SRoger Pau Monné umap->err = BITSET_ALLOC(num, M_PRIVCMD, M_WAITOK | M_ZERO);
255147e5939SRoger Pau Monné
256147e5939SRoger Pau Monné return umap;
257147e5939SRoger Pau Monné }
258147e5939SRoger Pau Monné
259bf7313e3SRoger Pau Monné static int
privcmd_ioctl(struct cdev * dev,unsigned long cmd,caddr_t arg,int mode,struct thread * td)260bf7313e3SRoger Pau Monné privcmd_ioctl(struct cdev *dev, unsigned long cmd, caddr_t arg,
261bf7313e3SRoger Pau Monné int mode, struct thread *td)
262bf7313e3SRoger Pau Monné {
263f713a5b3SRoger Pau Monné int error;
264f713a5b3SRoger Pau Monné unsigned int i;
265a7650787SRoger Pau Monne void *data;
266a7650787SRoger Pau Monne const struct per_user_data *u;
267a7650787SRoger Pau Monne
268a7650787SRoger Pau Monne error = devfs_get_cdevpriv(&data);
269a7650787SRoger Pau Monne if (error != 0)
270a7650787SRoger Pau Monne return (EINVAL);
271a7650787SRoger Pau Monne /*
272a7650787SRoger Pau Monne * Constify user-data to prevent unintended changes to the restriction
273a7650787SRoger Pau Monne * limits.
274a7650787SRoger Pau Monne */
275a7650787SRoger Pau Monne u = data;
276bf7313e3SRoger Pau Monné
277bf7313e3SRoger Pau Monné switch (cmd) {
278bf7313e3SRoger Pau Monné case IOCTL_PRIVCMD_HYPERCALL: {
279bf7313e3SRoger Pau Monné struct ioctl_privcmd_hypercall *hcall;
280bf7313e3SRoger Pau Monné
281bf7313e3SRoger Pau Monné hcall = (struct ioctl_privcmd_hypercall *)arg;
282a7650787SRoger Pau Monne
283a7650787SRoger Pau Monne /* Forbid hypercalls if restricted. */
284a7650787SRoger Pau Monne if (u->dom != DOMID_INVALID) {
285a7650787SRoger Pau Monne error = EPERM;
286a7650787SRoger Pau Monne break;
287a7650787SRoger Pau Monne }
288a7650787SRoger Pau Monne
2895ff6c7f3SRoger Pau Monné #ifdef __amd64__
2905ff6c7f3SRoger Pau Monné /*
2915ff6c7f3SRoger Pau Monné * The hypervisor page table walker will refuse to access
2925ff6c7f3SRoger Pau Monné * user-space pages if SMAP is enabled, so temporary disable it
2935ff6c7f3SRoger Pau Monné * while performing the hypercall.
2945ff6c7f3SRoger Pau Monné */
2955ff6c7f3SRoger Pau Monné if (cpu_stdext_feature & CPUID_STDEXT_SMAP)
2965ff6c7f3SRoger Pau Monné stac();
2975ff6c7f3SRoger Pau Monné #endif
298bf7313e3SRoger Pau Monné error = privcmd_hypercall(hcall->op, hcall->arg[0],
299bf7313e3SRoger Pau Monné hcall->arg[1], hcall->arg[2], hcall->arg[3], hcall->arg[4]);
3005ff6c7f3SRoger Pau Monné #ifdef __amd64__
3015ff6c7f3SRoger Pau Monné if (cpu_stdext_feature & CPUID_STDEXT_SMAP)
3025ff6c7f3SRoger Pau Monné clac();
3035ff6c7f3SRoger Pau Monné #endif
304bf7313e3SRoger Pau Monné if (error >= 0) {
305bf7313e3SRoger Pau Monné hcall->retval = error;
306bf7313e3SRoger Pau Monné error = 0;
307bf7313e3SRoger Pau Monné } else {
308bf7313e3SRoger Pau Monné error = xen_translate_error(error);
309bf7313e3SRoger Pau Monné hcall->retval = 0;
310bf7313e3SRoger Pau Monné }
311bf7313e3SRoger Pau Monné break;
312bf7313e3SRoger Pau Monné }
313bf7313e3SRoger Pau Monné case IOCTL_PRIVCMD_MMAPBATCH: {
314bf7313e3SRoger Pau Monné struct ioctl_privcmd_mmapbatch *mmap;
3155489d7e9SRoger Pau Monné struct xen_add_to_physmap_batch add;
316bf7313e3SRoger Pau Monné xen_ulong_t *idxs;
317bf7313e3SRoger Pau Monné xen_pfn_t *gpfns;
318f713a5b3SRoger Pau Monné int *errs;
319f713a5b3SRoger Pau Monné unsigned int index;
320bf7313e3SRoger Pau Monné struct privcmd_map *umap;
321288b2385SRoger Pau Monné uint16_t num;
322bf7313e3SRoger Pau Monné
323bf7313e3SRoger Pau Monné mmap = (struct ioctl_privcmd_mmapbatch *)arg;
324bf7313e3SRoger Pau Monné
325a7650787SRoger Pau Monne if (u->dom != DOMID_INVALID && u->dom != mmap->dom) {
326a7650787SRoger Pau Monne error = EPERM;
327a7650787SRoger Pau Monne break;
328a7650787SRoger Pau Monne }
329a7650787SRoger Pau Monne
330147e5939SRoger Pau Monné umap = setup_virtual_area(td, mmap->addr, mmap->num);
331147e5939SRoger Pau Monné if (umap == NULL) {
332bf7313e3SRoger Pau Monné error = EINVAL;
333bf7313e3SRoger Pau Monné break;
334bf7313e3SRoger Pau Monné }
335bf7313e3SRoger Pau Monné
336bf7313e3SRoger Pau Monné add.domid = DOMID_SELF;
337bf7313e3SRoger Pau Monné add.space = XENMAPSPACE_gmfn_foreign;
3385489d7e9SRoger Pau Monné add.u.foreign_domid = mmap->dom;
339bf7313e3SRoger Pau Monné
340288b2385SRoger Pau Monné /*
341288b2385SRoger Pau Monné * The 'size' field in the xen_add_to_physmap_range only
342288b2385SRoger Pau Monné * allows for UINT16_MAX mappings in a single hypercall.
343288b2385SRoger Pau Monné */
344288b2385SRoger Pau Monné num = MIN(mmap->num, UINT16_MAX);
345288b2385SRoger Pau Monné
346288b2385SRoger Pau Monné idxs = malloc(sizeof(*idxs) * num, M_PRIVCMD, M_WAITOK);
347288b2385SRoger Pau Monné gpfns = malloc(sizeof(*gpfns) * num, M_PRIVCMD, M_WAITOK);
348288b2385SRoger Pau Monné errs = malloc(sizeof(*errs) * num, M_PRIVCMD, M_WAITOK);
349bf7313e3SRoger Pau Monné
350bf7313e3SRoger Pau Monné set_xen_guest_handle(add.idxs, idxs);
351bf7313e3SRoger Pau Monné set_xen_guest_handle(add.gpfns, gpfns);
352bf7313e3SRoger Pau Monné set_xen_guest_handle(add.errs, errs);
353bf7313e3SRoger Pau Monné
354288b2385SRoger Pau Monné for (index = 0; index < mmap->num; index += num) {
355288b2385SRoger Pau Monné num = MIN(mmap->num - index, UINT16_MAX);
356288b2385SRoger Pau Monné add.size = num;
357288b2385SRoger Pau Monné
358288b2385SRoger Pau Monné error = copyin(&mmap->arr[index], idxs,
359288b2385SRoger Pau Monné sizeof(idxs[0]) * num);
360bf7313e3SRoger Pau Monné if (error != 0)
361bf7313e3SRoger Pau Monné goto mmap_out;
362bf7313e3SRoger Pau Monné
363288b2385SRoger Pau Monné for (i = 0; i < num; i++)
364288b2385SRoger Pau Monné gpfns[i] = atop(umap->phys_base_addr +
365288b2385SRoger Pau Monné (i + index) * PAGE_SIZE);
366bf7313e3SRoger Pau Monné
367288b2385SRoger Pau Monné bzero(errs, sizeof(*errs) * num);
368288b2385SRoger Pau Monné
369288b2385SRoger Pau Monné error = HYPERVISOR_memory_op(
3705489d7e9SRoger Pau Monné XENMEM_add_to_physmap_batch, &add);
371288b2385SRoger Pau Monné if (error != 0) {
372bf7313e3SRoger Pau Monné error = xen_translate_error(error);
373bf7313e3SRoger Pau Monné goto mmap_out;
374bf7313e3SRoger Pau Monné }
375bf7313e3SRoger Pau Monné
376288b2385SRoger Pau Monné for (i = 0; i < num; i++) {
377288b2385SRoger Pau Monné if (errs[i] != 0) {
378bf7313e3SRoger Pau Monné errs[i] = xen_translate_error(errs[i]);
379288b2385SRoger Pau Monné
380288b2385SRoger Pau Monné /* Mark the page as invalid. */
381288b2385SRoger Pau Monné BIT_SET(mmap->num, index + i,
382288b2385SRoger Pau Monné umap->err);
383288b2385SRoger Pau Monné }
384bf7313e3SRoger Pau Monné }
385bf7313e3SRoger Pau Monné
386288b2385SRoger Pau Monné error = copyout(errs, &mmap->err[index],
387288b2385SRoger Pau Monné sizeof(errs[0]) * num);
388288b2385SRoger Pau Monné if (error != 0)
389288b2385SRoger Pau Monné goto mmap_out;
390288b2385SRoger Pau Monné }
391bf7313e3SRoger Pau Monné
392288b2385SRoger Pau Monné umap->mapped = true;
393bf7313e3SRoger Pau Monné
394bf7313e3SRoger Pau Monné mmap_out:
395bf7313e3SRoger Pau Monné free(idxs, M_PRIVCMD);
396bf7313e3SRoger Pau Monné free(gpfns, M_PRIVCMD);
397bf7313e3SRoger Pau Monné free(errs, M_PRIVCMD);
398288b2385SRoger Pau Monné if (!umap->mapped)
399288b2385SRoger Pau Monné free(umap->err, M_PRIVCMD);
400bf7313e3SRoger Pau Monné
401bf7313e3SRoger Pau Monné break;
402bf7313e3SRoger Pau Monné }
403658860e2SRoger Pau Monne case IOCTL_PRIVCMD_MMAP_RESOURCE: {
404658860e2SRoger Pau Monne struct ioctl_privcmd_mmapresource *mmap;
405658860e2SRoger Pau Monne struct xen_mem_acquire_resource adq;
406658860e2SRoger Pau Monne xen_pfn_t *gpfns;
407658860e2SRoger Pau Monne struct privcmd_map *umap;
408bf7313e3SRoger Pau Monné
409658860e2SRoger Pau Monne mmap = (struct ioctl_privcmd_mmapresource *)arg;
410658860e2SRoger Pau Monne
411a7650787SRoger Pau Monne if (u->dom != DOMID_INVALID && u->dom != mmap->dom) {
412a7650787SRoger Pau Monne error = EPERM;
413a7650787SRoger Pau Monne break;
414a7650787SRoger Pau Monne }
415a7650787SRoger Pau Monne
416658860e2SRoger Pau Monne bzero(&adq, sizeof(adq));
417658860e2SRoger Pau Monne
418658860e2SRoger Pau Monne adq.domid = mmap->dom;
419658860e2SRoger Pau Monne adq.type = mmap->type;
420658860e2SRoger Pau Monne adq.id = mmap->id;
421658860e2SRoger Pau Monne
422658860e2SRoger Pau Monne /* Shortcut for getting the resource size. */
423658860e2SRoger Pau Monne if (mmap->addr == 0 && mmap->num == 0) {
424658860e2SRoger Pau Monne error = HYPERVISOR_memory_op(XENMEM_acquire_resource,
425658860e2SRoger Pau Monne &adq);
42650d7d967SRoger Pau Monné if (error != 0)
427658860e2SRoger Pau Monne error = xen_translate_error(error);
42850d7d967SRoger Pau Monné else
42950d7d967SRoger Pau Monné mmap->num = adq.nr_frames;
430658860e2SRoger Pau Monne break;
431658860e2SRoger Pau Monne }
432658860e2SRoger Pau Monne
433658860e2SRoger Pau Monne umap = setup_virtual_area(td, mmap->addr, mmap->num);
434658860e2SRoger Pau Monne if (umap == NULL) {
435658860e2SRoger Pau Monne error = EINVAL;
436658860e2SRoger Pau Monne break;
437658860e2SRoger Pau Monne }
438658860e2SRoger Pau Monne
439658860e2SRoger Pau Monne adq.nr_frames = mmap->num;
440658860e2SRoger Pau Monne adq.frame = mmap->idx;
441658860e2SRoger Pau Monne
442658860e2SRoger Pau Monne gpfns = malloc(sizeof(*gpfns) * mmap->num, M_PRIVCMD, M_WAITOK);
443658860e2SRoger Pau Monne for (i = 0; i < mmap->num; i++)
444658860e2SRoger Pau Monne gpfns[i] = atop(umap->phys_base_addr) + i;
445658860e2SRoger Pau Monne set_xen_guest_handle(adq.frame_list, gpfns);
446658860e2SRoger Pau Monne
447658860e2SRoger Pau Monne error = HYPERVISOR_memory_op(XENMEM_acquire_resource, &adq);
448658860e2SRoger Pau Monne if (error != 0)
449658860e2SRoger Pau Monne error = xen_translate_error(error);
450658860e2SRoger Pau Monne else
451658860e2SRoger Pau Monne umap->mapped = true;
452658860e2SRoger Pau Monne
453658860e2SRoger Pau Monne free(gpfns, M_PRIVCMD);
454658860e2SRoger Pau Monne if (!umap->mapped)
455658860e2SRoger Pau Monne free(umap->err, M_PRIVCMD);
456658860e2SRoger Pau Monne
457658860e2SRoger Pau Monne break;
458658860e2SRoger Pau Monne }
459ed78016dSRoger Pau Monne case IOCTL_PRIVCMD_DM_OP: {
460ed78016dSRoger Pau Monne const struct ioctl_privcmd_dmop *dmop;
461ed78016dSRoger Pau Monne struct privcmd_dmop_buf *bufs;
462ed78016dSRoger Pau Monne struct xen_dm_op_buf *hbufs;
463ed78016dSRoger Pau Monne
464ed78016dSRoger Pau Monne dmop = (struct ioctl_privcmd_dmop *)arg;
465ed78016dSRoger Pau Monne
466a7650787SRoger Pau Monne if (u->dom != DOMID_INVALID && u->dom != dmop->dom) {
467a7650787SRoger Pau Monne error = EPERM;
468a7650787SRoger Pau Monne break;
469a7650787SRoger Pau Monne }
470a7650787SRoger Pau Monne
471ed78016dSRoger Pau Monne if (dmop->num == 0)
472ed78016dSRoger Pau Monne break;
473ed78016dSRoger Pau Monne
474ed78016dSRoger Pau Monne if (dmop->num > MAX_DMOP_BUFFERS) {
475ed78016dSRoger Pau Monne error = E2BIG;
476ed78016dSRoger Pau Monne break;
477ed78016dSRoger Pau Monne }
478ed78016dSRoger Pau Monne
479ed78016dSRoger Pau Monne bufs = malloc(sizeof(*bufs) * dmop->num, M_PRIVCMD, M_WAITOK);
480ed78016dSRoger Pau Monne
481ed78016dSRoger Pau Monne error = copyin(dmop->ubufs, bufs, sizeof(*bufs) * dmop->num);
482ed78016dSRoger Pau Monne if (error != 0) {
483ed78016dSRoger Pau Monne free(bufs, M_PRIVCMD);
484ed78016dSRoger Pau Monne break;
485ed78016dSRoger Pau Monne }
486ed78016dSRoger Pau Monne
487ed78016dSRoger Pau Monne hbufs = malloc(sizeof(*hbufs) * dmop->num, M_PRIVCMD, M_WAITOK);
488ed78016dSRoger Pau Monne for (i = 0; i < dmop->num; i++) {
489ed78016dSRoger Pau Monne set_xen_guest_handle(hbufs[i].h, bufs[i].uptr);
490ed78016dSRoger Pau Monne hbufs[i].size = bufs[i].size;
491ed78016dSRoger Pau Monne }
492ed78016dSRoger Pau Monne
493ed78016dSRoger Pau Monne #ifdef __amd64__
494ed78016dSRoger Pau Monne if (cpu_stdext_feature & CPUID_STDEXT_SMAP)
495ed78016dSRoger Pau Monne stac();
496ed78016dSRoger Pau Monne #endif
497ed78016dSRoger Pau Monne error = HYPERVISOR_dm_op(dmop->dom, dmop->num, hbufs);
498ed78016dSRoger Pau Monne #ifdef __amd64__
499ed78016dSRoger Pau Monne if (cpu_stdext_feature & CPUID_STDEXT_SMAP)
500ed78016dSRoger Pau Monne clac();
501ed78016dSRoger Pau Monne #endif
502ed78016dSRoger Pau Monne if (error != 0)
503ed78016dSRoger Pau Monne error = xen_translate_error(error);
504ed78016dSRoger Pau Monne
505ed78016dSRoger Pau Monne free(bufs, M_PRIVCMD);
506ed78016dSRoger Pau Monne free(hbufs, M_PRIVCMD);
507ed78016dSRoger Pau Monne
508ed78016dSRoger Pau Monne
509ed78016dSRoger Pau Monne break;
510ed78016dSRoger Pau Monne }
511a7650787SRoger Pau Monne case IOCTL_PRIVCMD_RESTRICT: {
512a7650787SRoger Pau Monne struct per_user_data *u;
513a7650787SRoger Pau Monne domid_t dom;
514a7650787SRoger Pau Monne
515a7650787SRoger Pau Monne dom = *(domid_t *)arg;
516a7650787SRoger Pau Monne
517a7650787SRoger Pau Monne error = devfs_get_cdevpriv((void **)&u);
518a7650787SRoger Pau Monne if (error != 0)
519a7650787SRoger Pau Monne break;
520a7650787SRoger Pau Monne
521a7650787SRoger Pau Monne if (u->dom != DOMID_INVALID && u->dom != dom) {
522a7650787SRoger Pau Monne error = -EINVAL;
523a7650787SRoger Pau Monne break;
524a7650787SRoger Pau Monne }
525a7650787SRoger Pau Monne u->dom = dom;
526a7650787SRoger Pau Monne
527a7650787SRoger Pau Monne break;
528a7650787SRoger Pau Monne }
529bf7313e3SRoger Pau Monné default:
530bf7313e3SRoger Pau Monné error = ENOSYS;
531bf7313e3SRoger Pau Monné break;
532bf7313e3SRoger Pau Monné }
533bf7313e3SRoger Pau Monné
534bf7313e3SRoger Pau Monné return (error);
535bf7313e3SRoger Pau Monné }
536bf7313e3SRoger Pau Monné
537a7650787SRoger Pau Monne static void
user_release(void * arg)538a7650787SRoger Pau Monne user_release(void *arg)
539a7650787SRoger Pau Monne {
540a7650787SRoger Pau Monne
541a7650787SRoger Pau Monne free(arg, M_PRIVCMD);
542a7650787SRoger Pau Monne }
543a7650787SRoger Pau Monne
544a7650787SRoger Pau Monne static int
privcmd_open(struct cdev * dev,int flag,int otyp,struct thread * td)545a7650787SRoger Pau Monne privcmd_open(struct cdev *dev, int flag, int otyp, struct thread *td)
546a7650787SRoger Pau Monne {
547a7650787SRoger Pau Monne struct per_user_data *u;
548a7650787SRoger Pau Monne int error;
549a7650787SRoger Pau Monne
550a7650787SRoger Pau Monne u = malloc(sizeof(*u), M_PRIVCMD, M_WAITOK);
551a7650787SRoger Pau Monne u->dom = DOMID_INVALID;
552a7650787SRoger Pau Monne
553a7650787SRoger Pau Monne /* Assign the allocated per_user_data to this open instance. */
554a7650787SRoger Pau Monne error = devfs_set_cdevpriv(u, user_release);
555a7650787SRoger Pau Monne if (error != 0) {
556a7650787SRoger Pau Monne free(u, M_PRIVCMD);
557a7650787SRoger Pau Monne }
558a7650787SRoger Pau Monne
559a7650787SRoger Pau Monne return (error);
560a7650787SRoger Pau Monne }
561a7650787SRoger Pau Monne
562bf7313e3SRoger Pau Monné /*------------------ Private Device Attachment Functions --------------------*/
563bf7313e3SRoger Pau Monné static void
privcmd_identify(driver_t * driver,device_t parent)564bf7313e3SRoger Pau Monné privcmd_identify(driver_t *driver, device_t parent)
565bf7313e3SRoger Pau Monné {
566bf7313e3SRoger Pau Monné
567bf7313e3SRoger Pau Monné KASSERT(xen_domain(),
568bf7313e3SRoger Pau Monné ("Trying to attach privcmd device on non Xen domain"));
569bf7313e3SRoger Pau Monné
570bf7313e3SRoger Pau Monné if (BUS_ADD_CHILD(parent, 0, "privcmd", 0) == NULL)
571bf7313e3SRoger Pau Monné panic("unable to attach privcmd user-space device");
572bf7313e3SRoger Pau Monné }
573bf7313e3SRoger Pau Monné
574bf7313e3SRoger Pau Monné static int
privcmd_probe(device_t dev)575bf7313e3SRoger Pau Monné privcmd_probe(device_t dev)
576bf7313e3SRoger Pau Monné {
577bf7313e3SRoger Pau Monné
578bf7313e3SRoger Pau Monné privcmd_dev = dev;
579bf7313e3SRoger Pau Monné device_set_desc(dev, "Xen privileged interface user-space device");
580bf7313e3SRoger Pau Monné return (BUS_PROBE_NOWILDCARD);
581bf7313e3SRoger Pau Monné }
582bf7313e3SRoger Pau Monné
583bf7313e3SRoger Pau Monné static int
privcmd_attach(device_t dev)584bf7313e3SRoger Pau Monné privcmd_attach(device_t dev)
585bf7313e3SRoger Pau Monné {
586bf7313e3SRoger Pau Monné
587bf7313e3SRoger Pau Monné make_dev_credf(MAKEDEV_ETERNAL, &privcmd_devsw, 0, NULL, UID_ROOT,
588bf7313e3SRoger Pau Monné GID_WHEEL, 0600, "xen/privcmd");
589bf7313e3SRoger Pau Monné return (0);
590bf7313e3SRoger Pau Monné }
591bf7313e3SRoger Pau Monné
592bf7313e3SRoger Pau Monné /*-------------------- Private Device Attachment Data -----------------------*/
593bf7313e3SRoger Pau Monné static device_method_t privcmd_methods[] = {
594bf7313e3SRoger Pau Monné DEVMETHOD(device_identify, privcmd_identify),
595bf7313e3SRoger Pau Monné DEVMETHOD(device_probe, privcmd_probe),
596bf7313e3SRoger Pau Monné DEVMETHOD(device_attach, privcmd_attach),
597bf7313e3SRoger Pau Monné
598bf7313e3SRoger Pau Monné DEVMETHOD_END
599bf7313e3SRoger Pau Monné };
600bf7313e3SRoger Pau Monné
601bf7313e3SRoger Pau Monné static driver_t privcmd_driver = {
602bf7313e3SRoger Pau Monné "privcmd",
603bf7313e3SRoger Pau Monné privcmd_methods,
604bf7313e3SRoger Pau Monné 0,
605bf7313e3SRoger Pau Monné };
606bf7313e3SRoger Pau Monné
607f929eb1eSJohn Baldwin DRIVER_MODULE(privcmd, xenpv, privcmd_driver, 0, 0);
608bf7313e3SRoger Pau Monné MODULE_DEPEND(privcmd, xenpv, 1, 1, 1);
609