xref: /freebsd/sys/dev/xen/privcmd/privcmd.c (revision 5489d7e93a06fbb5d06d6d7e43a4c56f1571c891)
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/cdefs.h>
28bf7313e3SRoger Pau Monné __FBSDID("$FreeBSD$");
29bf7313e3SRoger Pau Monné 
30bf7313e3SRoger Pau Monné #include <sys/param.h>
31bf7313e3SRoger Pau Monné #include <sys/systm.h>
32bf7313e3SRoger Pau Monné #include <sys/uio.h>
33bf7313e3SRoger Pau Monné #include <sys/bus.h>
34bf7313e3SRoger Pau Monné #include <sys/malloc.h>
35bf7313e3SRoger Pau Monné #include <sys/kernel.h>
36bf7313e3SRoger Pau Monné #include <sys/lock.h>
37bf7313e3SRoger Pau Monné #include <sys/mutex.h>
38bf7313e3SRoger Pau Monné #include <sys/rwlock.h>
39bf7313e3SRoger Pau Monné #include <sys/selinfo.h>
40bf7313e3SRoger Pau Monné #include <sys/poll.h>
41bf7313e3SRoger Pau Monné #include <sys/conf.h>
42bf7313e3SRoger Pau Monné #include <sys/fcntl.h>
43bf7313e3SRoger Pau Monné #include <sys/ioccom.h>
44bf7313e3SRoger Pau Monné #include <sys/rman.h>
45bf7313e3SRoger Pau Monné #include <sys/tree.h>
46bf7313e3SRoger Pau Monné #include <sys/module.h>
47bf7313e3SRoger Pau Monné #include <sys/proc.h>
48288b2385SRoger Pau Monné #include <sys/bitset.h>
49bf7313e3SRoger Pau Monné 
50bf7313e3SRoger Pau Monné #include <vm/vm.h>
51bf7313e3SRoger Pau Monné #include <vm/vm_param.h>
52bf7313e3SRoger Pau Monné #include <vm/vm_extern.h>
53bf7313e3SRoger Pau Monné #include <vm/vm_kern.h>
54bf7313e3SRoger Pau Monné #include <vm/vm_page.h>
55bf7313e3SRoger Pau Monné #include <vm/vm_map.h>
56bf7313e3SRoger Pau Monné #include <vm/vm_object.h>
57bf7313e3SRoger Pau Monné #include <vm/vm_pager.h>
58bf7313e3SRoger Pau Monné 
59bf7313e3SRoger Pau Monné #include <machine/md_var.h>
60bf7313e3SRoger Pau Monné 
61bf7313e3SRoger Pau Monné #include <xen/xen-os.h>
62bf7313e3SRoger Pau Monné #include <xen/hypervisor.h>
63bf7313e3SRoger Pau Monné #include <xen/privcmd.h>
64bf7313e3SRoger Pau Monné #include <xen/error.h>
65bf7313e3SRoger Pau Monné 
66bf7313e3SRoger Pau Monné MALLOC_DEFINE(M_PRIVCMD, "privcmd_dev", "Xen privcmd user-space device");
67bf7313e3SRoger Pau Monné 
68ed78016dSRoger Pau Monne #define MAX_DMOP_BUFFERS 16
69ed78016dSRoger Pau Monne 
70bf7313e3SRoger Pau Monné struct privcmd_map {
71bf7313e3SRoger Pau Monné 	vm_object_t mem;
72bf7313e3SRoger Pau Monné 	vm_size_t size;
73bf7313e3SRoger Pau Monné 	struct resource *pseudo_phys_res;
74bf7313e3SRoger Pau Monné 	int pseudo_phys_res_id;
75bf7313e3SRoger Pau Monné 	vm_paddr_t phys_base_addr;
76bf7313e3SRoger Pau Monné 	boolean_t mapped;
77288b2385SRoger Pau Monné 	BITSET_DEFINE_VAR() *err;
78bf7313e3SRoger Pau Monné };
79bf7313e3SRoger Pau Monné 
80bf7313e3SRoger Pau Monné static d_ioctl_t     privcmd_ioctl;
81a7650787SRoger Pau Monne static d_open_t      privcmd_open;
82bf7313e3SRoger Pau Monné static d_mmap_single_t	privcmd_mmap_single;
83bf7313e3SRoger Pau Monné 
84bf7313e3SRoger Pau Monné static struct cdevsw privcmd_devsw = {
85bf7313e3SRoger Pau Monné 	.d_version = D_VERSION,
86bf7313e3SRoger Pau Monné 	.d_ioctl = privcmd_ioctl,
87bf7313e3SRoger Pau Monné 	.d_mmap_single = privcmd_mmap_single,
88a7650787SRoger Pau Monne 	.d_open = privcmd_open,
89bf7313e3SRoger Pau Monné 	.d_name = "privcmd",
90bf7313e3SRoger Pau Monné };
91bf7313e3SRoger Pau Monné 
92bf7313e3SRoger Pau Monné static int privcmd_pg_ctor(void *handle, vm_ooffset_t size, vm_prot_t prot,
93bf7313e3SRoger Pau Monné     vm_ooffset_t foff, struct ucred *cred, u_short *color);
94bf7313e3SRoger Pau Monné static void privcmd_pg_dtor(void *handle);
95bf7313e3SRoger Pau Monné static int privcmd_pg_fault(vm_object_t object, vm_ooffset_t offset,
96bf7313e3SRoger Pau Monné     int prot, vm_page_t *mres);
97bf7313e3SRoger Pau Monné 
98bf7313e3SRoger Pau Monné static struct cdev_pager_ops privcmd_pg_ops = {
99bf7313e3SRoger Pau Monné 	.cdev_pg_fault = privcmd_pg_fault,
100bf7313e3SRoger Pau Monné 	.cdev_pg_ctor =	privcmd_pg_ctor,
101bf7313e3SRoger Pau Monné 	.cdev_pg_dtor =	privcmd_pg_dtor,
102bf7313e3SRoger Pau Monné };
103bf7313e3SRoger Pau Monné 
104a7650787SRoger Pau Monne struct per_user_data {
105a7650787SRoger Pau Monne 	domid_t dom;
106a7650787SRoger Pau Monne };
107a7650787SRoger Pau Monne 
108bf7313e3SRoger Pau Monné static device_t privcmd_dev = NULL;
109bf7313e3SRoger Pau Monné 
110bf7313e3SRoger Pau Monné /*------------------------- Privcmd Pager functions --------------------------*/
111bf7313e3SRoger Pau Monné static int
112bf7313e3SRoger Pau Monné privcmd_pg_ctor(void *handle, vm_ooffset_t size, vm_prot_t prot,
113bf7313e3SRoger Pau Monné     vm_ooffset_t foff, struct ucred *cred, u_short *color)
114bf7313e3SRoger Pau Monné {
115bf7313e3SRoger Pau Monné 
116bf7313e3SRoger Pau Monné 	return (0);
117bf7313e3SRoger Pau Monné }
118bf7313e3SRoger Pau Monné 
119bf7313e3SRoger Pau Monné static void
120bf7313e3SRoger Pau Monné privcmd_pg_dtor(void *handle)
121bf7313e3SRoger Pau Monné {
122bf7313e3SRoger Pau Monné 	struct xen_remove_from_physmap rm = { .domid = DOMID_SELF };
123bf7313e3SRoger Pau Monné 	struct privcmd_map *map = handle;
124e7236a7dSMateusz Guzik 	int error __diagused;
125bf7313e3SRoger Pau Monné 	vm_size_t i;
126bf7313e3SRoger Pau Monné 	vm_page_t m;
127bf7313e3SRoger Pau Monné 
128bf7313e3SRoger Pau Monné 	/*
129bf7313e3SRoger Pau Monné 	 * Remove the mappings from the used pages. This will remove the
130bf7313e3SRoger Pau Monné 	 * underlying p2m bindings in Xen second stage translation.
131bf7313e3SRoger Pau Monné 	 */
132bf7313e3SRoger Pau Monné 	if (map->mapped == true) {
133bf7313e3SRoger Pau Monné 		VM_OBJECT_WLOCK(map->mem);
134bf7313e3SRoger Pau Monné retry:
135bf7313e3SRoger Pau Monné 		for (i = 0; i < map->size; i++) {
136bf7313e3SRoger Pau Monné 			m = vm_page_lookup(map->mem, i);
137bf7313e3SRoger Pau Monné 			if (m == NULL)
138bf7313e3SRoger Pau Monné 				continue;
139c7575748SJeff Roberson 			if (vm_page_busy_acquire(m, VM_ALLOC_WAITFAIL) == 0)
140bf7313e3SRoger Pau Monné 				goto retry;
141bf7313e3SRoger Pau Monné 			cdev_pager_free_page(map->mem, m);
142bf7313e3SRoger Pau Monné 		}
143bf7313e3SRoger Pau Monné 		VM_OBJECT_WUNLOCK(map->mem);
144bf7313e3SRoger Pau Monné 
145bf7313e3SRoger Pau Monné 		for (i = 0; i < map->size; i++) {
146bf7313e3SRoger Pau Monné 			rm.gpfn = atop(map->phys_base_addr) + i;
147bf7313e3SRoger Pau Monné 			HYPERVISOR_memory_op(XENMEM_remove_from_physmap, &rm);
148bf7313e3SRoger Pau Monné 		}
149288b2385SRoger Pau Monné 		free(map->err, M_PRIVCMD);
150bf7313e3SRoger Pau Monné 	}
151bf7313e3SRoger Pau Monné 
1520df8b29dSRoger Pau Monné 	error = xenmem_free(privcmd_dev, map->pseudo_phys_res_id,
1530df8b29dSRoger Pau Monné 	    map->pseudo_phys_res);
154bf7313e3SRoger Pau Monné 	KASSERT(error == 0, ("Unable to release memory resource: %d", error));
155bf7313e3SRoger Pau Monné 
156bf7313e3SRoger Pau Monné 	free(map, M_PRIVCMD);
157bf7313e3SRoger Pau Monné }
158bf7313e3SRoger Pau Monné 
159bf7313e3SRoger Pau Monné static int
160bf7313e3SRoger Pau Monné privcmd_pg_fault(vm_object_t object, vm_ooffset_t offset,
161bf7313e3SRoger Pau Monné     int prot, vm_page_t *mres)
162bf7313e3SRoger Pau Monné {
163bf7313e3SRoger Pau Monné 	struct privcmd_map *map = object->handle;
164bf7313e3SRoger Pau Monné 	vm_pindex_t pidx;
1653cf3b4e6SJeff Roberson 	vm_page_t page;
166bf7313e3SRoger Pau Monné 
167bf7313e3SRoger Pau Monné 	if (map->mapped != true)
168bf7313e3SRoger Pau Monné 		return (VM_PAGER_FAIL);
169bf7313e3SRoger Pau Monné 
170bf7313e3SRoger Pau Monné 	pidx = OFF_TO_IDX(offset);
171288b2385SRoger Pau Monné 	if (pidx >= map->size || BIT_ISSET(map->size, pidx, map->err))
172bf7313e3SRoger Pau Monné 		return (VM_PAGER_FAIL);
173bf7313e3SRoger Pau Monné 
174bf7313e3SRoger Pau Monné 	page = PHYS_TO_VM_PAGE(map->phys_base_addr + offset);
175bf7313e3SRoger Pau Monné 	if (page == NULL)
176bf7313e3SRoger Pau Monné 		return (VM_PAGER_FAIL);
177bf7313e3SRoger Pau Monné 
178bf7313e3SRoger Pau Monné 	KASSERT((page->flags & PG_FICTITIOUS) != 0,
179bf7313e3SRoger Pau Monné 	    ("not fictitious %p", page));
180fee2a2faSMark Johnston 	KASSERT(vm_page_wired(page), ("page %p not wired", page));
181fee2a2faSMark Johnston 	KASSERT(!vm_page_busied(page), ("page %p is busy", page));
182bf7313e3SRoger Pau Monné 
18363e97555SJeff Roberson 	vm_page_busy_acquire(page, 0);
1840012f373SJeff Roberson 	vm_page_valid(page);
1853cf3b4e6SJeff Roberson 
1863cf3b4e6SJeff Roberson 	if (*mres != NULL)
1873cf3b4e6SJeff Roberson 		vm_page_replace(page, object, pidx, *mres);
1883cf3b4e6SJeff Roberson 	else
189bf7313e3SRoger Pau Monné 		vm_page_insert(page, object, pidx);
190bf7313e3SRoger Pau Monné 	*mres = page;
191bf7313e3SRoger Pau Monné 	return (VM_PAGER_OK);
192bf7313e3SRoger Pau Monné }
193bf7313e3SRoger Pau Monné 
194bf7313e3SRoger Pau Monné /*----------------------- Privcmd char device methods ------------------------*/
195bf7313e3SRoger Pau Monné static int
196bf7313e3SRoger Pau Monné privcmd_mmap_single(struct cdev *cdev, vm_ooffset_t *offset, vm_size_t size,
197bf7313e3SRoger Pau Monné     vm_object_t *object, int nprot)
198bf7313e3SRoger Pau Monné {
199bf7313e3SRoger Pau Monné 	struct privcmd_map *map;
200bf7313e3SRoger Pau Monné 
201bf7313e3SRoger Pau Monné 	map = malloc(sizeof(*map), M_PRIVCMD, M_WAITOK | M_ZERO);
202bf7313e3SRoger Pau Monné 
203bf7313e3SRoger Pau Monné 	map->size = OFF_TO_IDX(size);
204bf7313e3SRoger Pau Monné 	map->pseudo_phys_res_id = 0;
205bf7313e3SRoger Pau Monné 
2060df8b29dSRoger Pau Monné 	map->pseudo_phys_res = xenmem_alloc(privcmd_dev,
2070df8b29dSRoger Pau Monné 	    &map->pseudo_phys_res_id, size);
208bf7313e3SRoger Pau Monné 	if (map->pseudo_phys_res == NULL) {
209bf7313e3SRoger Pau Monné 		free(map, M_PRIVCMD);
210bf7313e3SRoger Pau Monné 		return (ENOMEM);
211bf7313e3SRoger Pau Monné 	}
212bf7313e3SRoger Pau Monné 
213bf7313e3SRoger Pau Monné 	map->phys_base_addr = rman_get_start(map->pseudo_phys_res);
214bf7313e3SRoger Pau Monné 	map->mem = cdev_pager_allocate(map, OBJT_MGTDEVICE, &privcmd_pg_ops,
215bf7313e3SRoger Pau Monné 	    size, nprot, *offset, NULL);
216bf7313e3SRoger Pau Monné 	if (map->mem == NULL) {
2170df8b29dSRoger Pau Monné 		xenmem_free(privcmd_dev, map->pseudo_phys_res_id,
2180df8b29dSRoger Pau Monné 		    map->pseudo_phys_res);
219bf7313e3SRoger Pau Monné 		free(map, M_PRIVCMD);
220bf7313e3SRoger Pau Monné 		return (ENOMEM);
221bf7313e3SRoger Pau Monné 	}
222bf7313e3SRoger Pau Monné 
223bf7313e3SRoger Pau Monné 	*object = map->mem;
224bf7313e3SRoger Pau Monné 
225bf7313e3SRoger Pau Monné 	return (0);
226bf7313e3SRoger Pau Monné }
227bf7313e3SRoger Pau Monné 
228147e5939SRoger Pau Monné static struct privcmd_map *
229147e5939SRoger Pau Monné setup_virtual_area(struct thread *td, unsigned long addr, unsigned long num)
230147e5939SRoger Pau Monné {
231147e5939SRoger Pau Monné 	vm_map_t map;
232147e5939SRoger Pau Monné 	vm_map_entry_t entry;
233147e5939SRoger Pau Monné 	vm_object_t mem;
234147e5939SRoger Pau Monné 	vm_pindex_t pindex;
235147e5939SRoger Pau Monné 	vm_prot_t prot;
236147e5939SRoger Pau Monné 	boolean_t wired;
237147e5939SRoger Pau Monné 	struct privcmd_map *umap;
238147e5939SRoger Pau Monné 	int error;
239147e5939SRoger Pau Monné 
240147e5939SRoger Pau Monné 	if ((num == 0) || ((addr & PAGE_MASK) != 0))
241147e5939SRoger Pau Monné 		return NULL;
242147e5939SRoger Pau Monné 
243147e5939SRoger Pau Monné 	map = &td->td_proc->p_vmspace->vm_map;
244147e5939SRoger Pau Monné 	error = vm_map_lookup(&map, addr, VM_PROT_NONE, &entry, &mem, &pindex,
245147e5939SRoger Pau Monné 	    &prot, &wired);
246147e5939SRoger Pau Monné 	if (error != KERN_SUCCESS || (entry->start != addr) ||
247147e5939SRoger Pau Monné 	    (entry->end != addr + (num * PAGE_SIZE)))
248147e5939SRoger Pau Monné 		return NULL;
249147e5939SRoger Pau Monné 
250147e5939SRoger Pau Monné 	vm_map_lookup_done(map, entry);
251147e5939SRoger Pau Monné 	if ((mem->type != OBJT_MGTDEVICE) ||
252147e5939SRoger Pau Monné 	    (mem->un_pager.devp.ops != &privcmd_pg_ops))
253147e5939SRoger Pau Monné 		return NULL;
254147e5939SRoger Pau Monné 
255147e5939SRoger Pau Monné 	umap = mem->handle;
256147e5939SRoger Pau Monné 	/* Allocate a bitset to store broken page mappings. */
257147e5939SRoger Pau Monné 	umap->err = BITSET_ALLOC(num, M_PRIVCMD, M_WAITOK | M_ZERO);
258147e5939SRoger Pau Monné 
259147e5939SRoger Pau Monné 	return umap;
260147e5939SRoger Pau Monné }
261147e5939SRoger Pau Monné 
262bf7313e3SRoger Pau Monné static int
263bf7313e3SRoger Pau Monné privcmd_ioctl(struct cdev *dev, unsigned long cmd, caddr_t arg,
264bf7313e3SRoger Pau Monné 	      int mode, struct thread *td)
265bf7313e3SRoger Pau Monné {
266f713a5b3SRoger Pau Monné 	int error;
267f713a5b3SRoger Pau Monné 	unsigned int i;
268a7650787SRoger Pau Monne 	void *data;
269a7650787SRoger Pau Monne 	const struct per_user_data *u;
270a7650787SRoger Pau Monne 
271a7650787SRoger Pau Monne 	error = devfs_get_cdevpriv(&data);
272a7650787SRoger Pau Monne 	if (error != 0)
273a7650787SRoger Pau Monne 		return (EINVAL);
274a7650787SRoger Pau Monne 	/*
275a7650787SRoger Pau Monne 	 * Constify user-data to prevent unintended changes to the restriction
276a7650787SRoger Pau Monne 	 * limits.
277a7650787SRoger Pau Monne 	 */
278a7650787SRoger Pau Monne 	u = data;
279bf7313e3SRoger Pau Monné 
280bf7313e3SRoger Pau Monné 	switch (cmd) {
281bf7313e3SRoger Pau Monné 	case IOCTL_PRIVCMD_HYPERCALL: {
282bf7313e3SRoger Pau Monné 		struct ioctl_privcmd_hypercall *hcall;
283bf7313e3SRoger Pau Monné 
284bf7313e3SRoger Pau Monné 		hcall = (struct ioctl_privcmd_hypercall *)arg;
285a7650787SRoger Pau Monne 
286a7650787SRoger Pau Monne 		/* Forbid hypercalls if restricted. */
287a7650787SRoger Pau Monne 		if (u->dom != DOMID_INVALID) {
288a7650787SRoger Pau Monne 			error = EPERM;
289a7650787SRoger Pau Monne 			break;
290a7650787SRoger Pau Monne 		}
291a7650787SRoger Pau Monne 
2925ff6c7f3SRoger Pau Monné #ifdef __amd64__
2935ff6c7f3SRoger Pau Monné 		/*
2945ff6c7f3SRoger Pau Monné 		 * The hypervisor page table walker will refuse to access
2955ff6c7f3SRoger Pau Monné 		 * user-space pages if SMAP is enabled, so temporary disable it
2965ff6c7f3SRoger Pau Monné 		 * while performing the hypercall.
2975ff6c7f3SRoger Pau Monné 		 */
2985ff6c7f3SRoger Pau Monné 		if (cpu_stdext_feature & CPUID_STDEXT_SMAP)
2995ff6c7f3SRoger Pau Monné 			stac();
3005ff6c7f3SRoger Pau Monné #endif
301bf7313e3SRoger Pau Monné 		error = privcmd_hypercall(hcall->op, hcall->arg[0],
302bf7313e3SRoger Pau Monné 		    hcall->arg[1], hcall->arg[2], hcall->arg[3], hcall->arg[4]);
3035ff6c7f3SRoger Pau Monné #ifdef __amd64__
3045ff6c7f3SRoger Pau Monné 		if (cpu_stdext_feature & CPUID_STDEXT_SMAP)
3055ff6c7f3SRoger Pau Monné 			clac();
3065ff6c7f3SRoger Pau Monné #endif
307bf7313e3SRoger Pau Monné 		if (error >= 0) {
308bf7313e3SRoger Pau Monné 			hcall->retval = error;
309bf7313e3SRoger Pau Monné 			error = 0;
310bf7313e3SRoger Pau Monné 		} else {
311bf7313e3SRoger Pau Monné 			error = xen_translate_error(error);
312bf7313e3SRoger Pau Monné 			hcall->retval = 0;
313bf7313e3SRoger Pau Monné 		}
314bf7313e3SRoger Pau Monné 		break;
315bf7313e3SRoger Pau Monné 	}
316bf7313e3SRoger Pau Monné 	case IOCTL_PRIVCMD_MMAPBATCH: {
317bf7313e3SRoger Pau Monné 		struct ioctl_privcmd_mmapbatch *mmap;
318*5489d7e9SRoger Pau Monné 		struct xen_add_to_physmap_batch add;
319bf7313e3SRoger Pau Monné 		xen_ulong_t *idxs;
320bf7313e3SRoger Pau Monné 		xen_pfn_t *gpfns;
321f713a5b3SRoger Pau Monné 		int *errs;
322f713a5b3SRoger Pau Monné 		unsigned int index;
323bf7313e3SRoger Pau Monné 		struct privcmd_map *umap;
324288b2385SRoger Pau Monné 		uint16_t num;
325bf7313e3SRoger Pau Monné 
326bf7313e3SRoger Pau Monné 		mmap = (struct ioctl_privcmd_mmapbatch *)arg;
327bf7313e3SRoger Pau Monné 
328a7650787SRoger Pau Monne 		if (u->dom != DOMID_INVALID && u->dom != mmap->dom) {
329a7650787SRoger Pau Monne 			error = EPERM;
330a7650787SRoger Pau Monne 			break;
331a7650787SRoger Pau Monne 		}
332a7650787SRoger Pau Monne 
333147e5939SRoger Pau Monné 		umap = setup_virtual_area(td, mmap->addr, mmap->num);
334147e5939SRoger Pau Monné 		if (umap == NULL) {
335bf7313e3SRoger Pau Monné 			error = EINVAL;
336bf7313e3SRoger Pau Monné 			break;
337bf7313e3SRoger Pau Monné 		}
338bf7313e3SRoger Pau Monné 
339bf7313e3SRoger Pau Monné 		add.domid = DOMID_SELF;
340bf7313e3SRoger Pau Monné 		add.space = XENMAPSPACE_gmfn_foreign;
341*5489d7e9SRoger Pau Monné 		add.u.foreign_domid = mmap->dom;
342bf7313e3SRoger Pau Monné 
343288b2385SRoger Pau Monné 		/*
344288b2385SRoger Pau Monné 		 * The 'size' field in the xen_add_to_physmap_range only
345288b2385SRoger Pau Monné 		 * allows for UINT16_MAX mappings in a single hypercall.
346288b2385SRoger Pau Monné 		 */
347288b2385SRoger Pau Monné 		num = MIN(mmap->num, UINT16_MAX);
348288b2385SRoger Pau Monné 
349288b2385SRoger Pau Monné 		idxs = malloc(sizeof(*idxs) * num, M_PRIVCMD, M_WAITOK);
350288b2385SRoger Pau Monné 		gpfns = malloc(sizeof(*gpfns) * num, M_PRIVCMD, M_WAITOK);
351288b2385SRoger Pau Monné 		errs = malloc(sizeof(*errs) * num, M_PRIVCMD, M_WAITOK);
352bf7313e3SRoger Pau Monné 
353bf7313e3SRoger Pau Monné 		set_xen_guest_handle(add.idxs, idxs);
354bf7313e3SRoger Pau Monné 		set_xen_guest_handle(add.gpfns, gpfns);
355bf7313e3SRoger Pau Monné 		set_xen_guest_handle(add.errs, errs);
356bf7313e3SRoger Pau Monné 
357288b2385SRoger Pau Monné 		for (index = 0; index < mmap->num; index += num) {
358288b2385SRoger Pau Monné 			num = MIN(mmap->num - index, UINT16_MAX);
359288b2385SRoger Pau Monné 			add.size = num;
360288b2385SRoger Pau Monné 
361288b2385SRoger Pau Monné 			error = copyin(&mmap->arr[index], idxs,
362288b2385SRoger Pau Monné 			    sizeof(idxs[0]) * num);
363bf7313e3SRoger Pau Monné 			if (error != 0)
364bf7313e3SRoger Pau Monné 				goto mmap_out;
365bf7313e3SRoger Pau Monné 
366288b2385SRoger Pau Monné 			for (i = 0; i < num; i++)
367288b2385SRoger Pau Monné 				gpfns[i] = atop(umap->phys_base_addr +
368288b2385SRoger Pau Monné 				    (i + index) * PAGE_SIZE);
369bf7313e3SRoger Pau Monné 
370288b2385SRoger Pau Monné 			bzero(errs, sizeof(*errs) * num);
371288b2385SRoger Pau Monné 
372288b2385SRoger Pau Monné 			error = HYPERVISOR_memory_op(
373*5489d7e9SRoger Pau Monné 			    XENMEM_add_to_physmap_batch, &add);
374288b2385SRoger Pau Monné 			if (error != 0) {
375bf7313e3SRoger Pau Monné 				error = xen_translate_error(error);
376bf7313e3SRoger Pau Monné 				goto mmap_out;
377bf7313e3SRoger Pau Monné 			}
378bf7313e3SRoger Pau Monné 
379288b2385SRoger Pau Monné 			for (i = 0; i < num; i++) {
380288b2385SRoger Pau Monné 				if (errs[i] != 0) {
381bf7313e3SRoger Pau Monné 					errs[i] = xen_translate_error(errs[i]);
382288b2385SRoger Pau Monné 
383288b2385SRoger Pau Monné 					/* Mark the page as invalid. */
384288b2385SRoger Pau Monné 					BIT_SET(mmap->num, index + i,
385288b2385SRoger Pau Monné 					    umap->err);
386288b2385SRoger Pau Monné 				}
387bf7313e3SRoger Pau Monné 			}
388bf7313e3SRoger Pau Monné 
389288b2385SRoger Pau Monné 			error = copyout(errs, &mmap->err[index],
390288b2385SRoger Pau Monné 			    sizeof(errs[0]) * num);
391288b2385SRoger Pau Monné 			if (error != 0)
392288b2385SRoger Pau Monné 				goto mmap_out;
393288b2385SRoger Pau Monné 		}
394bf7313e3SRoger Pau Monné 
395288b2385SRoger Pau Monné 		umap->mapped = true;
396bf7313e3SRoger Pau Monné 
397bf7313e3SRoger Pau Monné mmap_out:
398bf7313e3SRoger Pau Monné 		free(idxs, M_PRIVCMD);
399bf7313e3SRoger Pau Monné 		free(gpfns, M_PRIVCMD);
400bf7313e3SRoger Pau Monné 		free(errs, M_PRIVCMD);
401288b2385SRoger Pau Monné 		if (!umap->mapped)
402288b2385SRoger Pau Monné 			free(umap->err, M_PRIVCMD);
403bf7313e3SRoger Pau Monné 
404bf7313e3SRoger Pau Monné 		break;
405bf7313e3SRoger Pau Monné 	}
406658860e2SRoger Pau Monne 	case IOCTL_PRIVCMD_MMAP_RESOURCE: {
407658860e2SRoger Pau Monne 		struct ioctl_privcmd_mmapresource *mmap;
408658860e2SRoger Pau Monne 		struct xen_mem_acquire_resource adq;
409658860e2SRoger Pau Monne 		xen_pfn_t *gpfns;
410658860e2SRoger Pau Monne 		struct privcmd_map *umap;
411bf7313e3SRoger Pau Monné 
412658860e2SRoger Pau Monne 		mmap = (struct ioctl_privcmd_mmapresource *)arg;
413658860e2SRoger Pau Monne 
414a7650787SRoger Pau Monne 		if (u->dom != DOMID_INVALID && u->dom != mmap->dom) {
415a7650787SRoger Pau Monne 			error = EPERM;
416a7650787SRoger Pau Monne 			break;
417a7650787SRoger Pau Monne 		}
418a7650787SRoger Pau Monne 
419658860e2SRoger Pau Monne 		bzero(&adq, sizeof(adq));
420658860e2SRoger Pau Monne 
421658860e2SRoger Pau Monne 		adq.domid = mmap->dom;
422658860e2SRoger Pau Monne 		adq.type = mmap->type;
423658860e2SRoger Pau Monne 		adq.id = mmap->id;
424658860e2SRoger Pau Monne 
425658860e2SRoger Pau Monne 		/* Shortcut for getting the resource size. */
426658860e2SRoger Pau Monne 		if (mmap->addr == 0 && mmap->num == 0) {
427658860e2SRoger Pau Monne 			error = HYPERVISOR_memory_op(XENMEM_acquire_resource,
428658860e2SRoger Pau Monne 			    &adq);
42950d7d967SRoger Pau Monné 			if (error != 0)
430658860e2SRoger Pau Monne 				error = xen_translate_error(error);
43150d7d967SRoger Pau Monné 			else
43250d7d967SRoger Pau Monné 				mmap->num = adq.nr_frames;
433658860e2SRoger Pau Monne 			break;
434658860e2SRoger Pau Monne 		}
435658860e2SRoger Pau Monne 
436658860e2SRoger Pau Monne 		umap = setup_virtual_area(td, mmap->addr, mmap->num);
437658860e2SRoger Pau Monne 		if (umap == NULL) {
438658860e2SRoger Pau Monne 			error = EINVAL;
439658860e2SRoger Pau Monne 			break;
440658860e2SRoger Pau Monne 		}
441658860e2SRoger Pau Monne 
442658860e2SRoger Pau Monne 		adq.nr_frames = mmap->num;
443658860e2SRoger Pau Monne 		adq.frame = mmap->idx;
444658860e2SRoger Pau Monne 
445658860e2SRoger Pau Monne 		gpfns = malloc(sizeof(*gpfns) * mmap->num, M_PRIVCMD, M_WAITOK);
446658860e2SRoger Pau Monne 		for (i = 0; i < mmap->num; i++)
447658860e2SRoger Pau Monne 			gpfns[i] = atop(umap->phys_base_addr) + i;
448658860e2SRoger Pau Monne 		set_xen_guest_handle(adq.frame_list, gpfns);
449658860e2SRoger Pau Monne 
450658860e2SRoger Pau Monne 		error = HYPERVISOR_memory_op(XENMEM_acquire_resource, &adq);
451658860e2SRoger Pau Monne 		if (error != 0)
452658860e2SRoger Pau Monne 			error = xen_translate_error(error);
453658860e2SRoger Pau Monne 		else
454658860e2SRoger Pau Monne 			umap->mapped = true;
455658860e2SRoger Pau Monne 
456658860e2SRoger Pau Monne 		free(gpfns, M_PRIVCMD);
457658860e2SRoger Pau Monne 		if (!umap->mapped)
458658860e2SRoger Pau Monne 			free(umap->err, M_PRIVCMD);
459658860e2SRoger Pau Monne 
460658860e2SRoger Pau Monne 		break;
461658860e2SRoger Pau Monne 	}
462ed78016dSRoger Pau Monne 	case IOCTL_PRIVCMD_DM_OP: {
463ed78016dSRoger Pau Monne 		const struct ioctl_privcmd_dmop *dmop;
464ed78016dSRoger Pau Monne 		struct privcmd_dmop_buf *bufs;
465ed78016dSRoger Pau Monne 		struct xen_dm_op_buf *hbufs;
466ed78016dSRoger Pau Monne 
467ed78016dSRoger Pau Monne 		dmop = (struct ioctl_privcmd_dmop *)arg;
468ed78016dSRoger Pau Monne 
469a7650787SRoger Pau Monne 		if (u->dom != DOMID_INVALID && u->dom != dmop->dom) {
470a7650787SRoger Pau Monne 			error = EPERM;
471a7650787SRoger Pau Monne 			break;
472a7650787SRoger Pau Monne 		}
473a7650787SRoger Pau Monne 
474ed78016dSRoger Pau Monne 		if (dmop->num == 0)
475ed78016dSRoger Pau Monne 			break;
476ed78016dSRoger Pau Monne 
477ed78016dSRoger Pau Monne 		if (dmop->num > MAX_DMOP_BUFFERS) {
478ed78016dSRoger Pau Monne 			error = E2BIG;
479ed78016dSRoger Pau Monne 			break;
480ed78016dSRoger Pau Monne 		}
481ed78016dSRoger Pau Monne 
482ed78016dSRoger Pau Monne 		bufs = malloc(sizeof(*bufs) * dmop->num, M_PRIVCMD, M_WAITOK);
483ed78016dSRoger Pau Monne 
484ed78016dSRoger Pau Monne 		error = copyin(dmop->ubufs, bufs, sizeof(*bufs) * dmop->num);
485ed78016dSRoger Pau Monne 		if (error != 0) {
486ed78016dSRoger Pau Monne 			free(bufs, M_PRIVCMD);
487ed78016dSRoger Pau Monne 			break;
488ed78016dSRoger Pau Monne 		}
489ed78016dSRoger Pau Monne 
490ed78016dSRoger Pau Monne 		hbufs = malloc(sizeof(*hbufs) * dmop->num, M_PRIVCMD, M_WAITOK);
491ed78016dSRoger Pau Monne 		for (i = 0; i < dmop->num; i++) {
492ed78016dSRoger Pau Monne 			set_xen_guest_handle(hbufs[i].h, bufs[i].uptr);
493ed78016dSRoger Pau Monne 			hbufs[i].size = bufs[i].size;
494ed78016dSRoger Pau Monne 		}
495ed78016dSRoger Pau Monne 
496ed78016dSRoger Pau Monne #ifdef __amd64__
497ed78016dSRoger Pau Monne 		if (cpu_stdext_feature & CPUID_STDEXT_SMAP)
498ed78016dSRoger Pau Monne 			stac();
499ed78016dSRoger Pau Monne #endif
500ed78016dSRoger Pau Monne 		error = HYPERVISOR_dm_op(dmop->dom, dmop->num, hbufs);
501ed78016dSRoger Pau Monne #ifdef __amd64__
502ed78016dSRoger Pau Monne 		if (cpu_stdext_feature & CPUID_STDEXT_SMAP)
503ed78016dSRoger Pau Monne 			clac();
504ed78016dSRoger Pau Monne #endif
505ed78016dSRoger Pau Monne 		if (error != 0)
506ed78016dSRoger Pau Monne 			error = xen_translate_error(error);
507ed78016dSRoger Pau Monne 
508ed78016dSRoger Pau Monne 		free(bufs, M_PRIVCMD);
509ed78016dSRoger Pau Monne 		free(hbufs, M_PRIVCMD);
510ed78016dSRoger Pau Monne 
511ed78016dSRoger Pau Monne 
512ed78016dSRoger Pau Monne 		break;
513ed78016dSRoger Pau Monne 	}
514a7650787SRoger Pau Monne 	case IOCTL_PRIVCMD_RESTRICT: {
515a7650787SRoger Pau Monne 		struct per_user_data *u;
516a7650787SRoger Pau Monne 		domid_t dom;
517a7650787SRoger Pau Monne 
518a7650787SRoger Pau Monne 		dom = *(domid_t *)arg;
519a7650787SRoger Pau Monne 
520a7650787SRoger Pau Monne 		error = devfs_get_cdevpriv((void **)&u);
521a7650787SRoger Pau Monne 		if (error != 0)
522a7650787SRoger Pau Monne 			break;
523a7650787SRoger Pau Monne 
524a7650787SRoger Pau Monne 		if (u->dom != DOMID_INVALID && u->dom != dom) {
525a7650787SRoger Pau Monne 			error = -EINVAL;
526a7650787SRoger Pau Monne 			break;
527a7650787SRoger Pau Monne 		}
528a7650787SRoger Pau Monne 		u->dom = dom;
529a7650787SRoger Pau Monne 
530a7650787SRoger Pau Monne 		break;
531a7650787SRoger Pau Monne 	}
532bf7313e3SRoger Pau Monné 	default:
533bf7313e3SRoger Pau Monné 		error = ENOSYS;
534bf7313e3SRoger Pau Monné 		break;
535bf7313e3SRoger Pau Monné 	}
536bf7313e3SRoger Pau Monné 
537bf7313e3SRoger Pau Monné 	return (error);
538bf7313e3SRoger Pau Monné }
539bf7313e3SRoger Pau Monné 
540a7650787SRoger Pau Monne static void
541a7650787SRoger Pau Monne user_release(void *arg)
542a7650787SRoger Pau Monne {
543a7650787SRoger Pau Monne 
544a7650787SRoger Pau Monne 	free(arg, M_PRIVCMD);
545a7650787SRoger Pau Monne }
546a7650787SRoger Pau Monne 
547a7650787SRoger Pau Monne static int
548a7650787SRoger Pau Monne privcmd_open(struct cdev *dev, int flag, int otyp, struct thread *td)
549a7650787SRoger Pau Monne {
550a7650787SRoger Pau Monne 	struct per_user_data *u;
551a7650787SRoger Pau Monne 	int error;
552a7650787SRoger Pau Monne 
553a7650787SRoger Pau Monne 	u = malloc(sizeof(*u), M_PRIVCMD, M_WAITOK);
554a7650787SRoger Pau Monne 	u->dom = DOMID_INVALID;
555a7650787SRoger Pau Monne 
556a7650787SRoger Pau Monne 	/* Assign the allocated per_user_data to this open instance. */
557a7650787SRoger Pau Monne 	error = devfs_set_cdevpriv(u, user_release);
558a7650787SRoger Pau Monne 	if (error != 0) {
559a7650787SRoger Pau Monne 		free(u, M_PRIVCMD);
560a7650787SRoger Pau Monne 	}
561a7650787SRoger Pau Monne 
562a7650787SRoger Pau Monne 	return (error);
563a7650787SRoger Pau Monne }
564a7650787SRoger Pau Monne 
565bf7313e3SRoger Pau Monné /*------------------ Private Device Attachment Functions  --------------------*/
566bf7313e3SRoger Pau Monné static void
567bf7313e3SRoger Pau Monné privcmd_identify(driver_t *driver, device_t parent)
568bf7313e3SRoger Pau Monné {
569bf7313e3SRoger Pau Monné 
570bf7313e3SRoger Pau Monné 	KASSERT(xen_domain(),
571bf7313e3SRoger Pau Monné 	    ("Trying to attach privcmd device on non Xen domain"));
572bf7313e3SRoger Pau Monné 
573bf7313e3SRoger Pau Monné 	if (BUS_ADD_CHILD(parent, 0, "privcmd", 0) == NULL)
574bf7313e3SRoger Pau Monné 		panic("unable to attach privcmd user-space device");
575bf7313e3SRoger Pau Monné }
576bf7313e3SRoger Pau Monné 
577bf7313e3SRoger Pau Monné static int
578bf7313e3SRoger Pau Monné privcmd_probe(device_t dev)
579bf7313e3SRoger Pau Monné {
580bf7313e3SRoger Pau Monné 
581bf7313e3SRoger Pau Monné 	privcmd_dev = dev;
582bf7313e3SRoger Pau Monné 	device_set_desc(dev, "Xen privileged interface user-space device");
583bf7313e3SRoger Pau Monné 	return (BUS_PROBE_NOWILDCARD);
584bf7313e3SRoger Pau Monné }
585bf7313e3SRoger Pau Monné 
586bf7313e3SRoger Pau Monné static int
587bf7313e3SRoger Pau Monné privcmd_attach(device_t dev)
588bf7313e3SRoger Pau Monné {
589bf7313e3SRoger Pau Monné 
590bf7313e3SRoger Pau Monné 	make_dev_credf(MAKEDEV_ETERNAL, &privcmd_devsw, 0, NULL, UID_ROOT,
591bf7313e3SRoger Pau Monné 	    GID_WHEEL, 0600, "xen/privcmd");
592bf7313e3SRoger Pau Monné 	return (0);
593bf7313e3SRoger Pau Monné }
594bf7313e3SRoger Pau Monné 
595bf7313e3SRoger Pau Monné /*-------------------- Private Device Attachment Data  -----------------------*/
596bf7313e3SRoger Pau Monné static device_method_t privcmd_methods[] = {
597bf7313e3SRoger Pau Monné 	DEVMETHOD(device_identify,	privcmd_identify),
598bf7313e3SRoger Pau Monné 	DEVMETHOD(device_probe,		privcmd_probe),
599bf7313e3SRoger Pau Monné 	DEVMETHOD(device_attach,	privcmd_attach),
600bf7313e3SRoger Pau Monné 
601bf7313e3SRoger Pau Monné 	DEVMETHOD_END
602bf7313e3SRoger Pau Monné };
603bf7313e3SRoger Pau Monné 
604bf7313e3SRoger Pau Monné static driver_t privcmd_driver = {
605bf7313e3SRoger Pau Monné 	"privcmd",
606bf7313e3SRoger Pau Monné 	privcmd_methods,
607bf7313e3SRoger Pau Monné 	0,
608bf7313e3SRoger Pau Monné };
609bf7313e3SRoger Pau Monné 
610f929eb1eSJohn Baldwin DRIVER_MODULE(privcmd, xenpv, privcmd_driver, 0, 0);
611bf7313e3SRoger Pau Monné MODULE_DEPEND(privcmd, xenpv, 1, 1, 1);
612