xref: /freebsd/sys/dev/vmm/vmm_dev.c (revision 4a46ece6c6a90f18effbfae7ddef79b41ef43eec)
1b9ef152bSMark Johnston /*-
2b9ef152bSMark Johnston  * SPDX-License-Identifier: BSD-2-Clause
3b9ef152bSMark Johnston  *
4b9ef152bSMark Johnston  * Copyright (c) 2011 NetApp, Inc.
5b9ef152bSMark Johnston  * Copyright (C) 2015 Mihai Carabas <mihai.carabas@gmail.com>
6b9ef152bSMark Johnston  * All rights reserved.
7b9ef152bSMark Johnston  */
8b9ef152bSMark Johnston 
9b9ef152bSMark Johnston #include <sys/param.h>
10b9ef152bSMark Johnston #include <sys/conf.h>
11a97f683fSMark Johnston #include <sys/fcntl.h>
12b9ef152bSMark Johnston #include <sys/ioccom.h>
13b9ef152bSMark Johnston #include <sys/jail.h>
14b9ef152bSMark Johnston #include <sys/kernel.h>
15b9ef152bSMark Johnston #include <sys/malloc.h>
16b9ef152bSMark Johnston #include <sys/mman.h>
17b9ef152bSMark Johnston #include <sys/proc.h>
18b9ef152bSMark Johnston #include <sys/queue.h>
19887c0877SMark Johnston #include <sys/sx.h>
20b9ef152bSMark Johnston #include <sys/sysctl.h>
21b9ef152bSMark Johnston #include <sys/ucred.h>
22b9ef152bSMark Johnston #include <sys/uio.h>
23b9ef152bSMark Johnston 
24b9ef152bSMark Johnston #include <machine/vmm.h>
25b9ef152bSMark Johnston 
26b9ef152bSMark Johnston #include <vm/vm.h>
27b9ef152bSMark Johnston #include <vm/vm_object.h>
28b9ef152bSMark Johnston 
29b9ef152bSMark Johnston #include <dev/vmm/vmm_dev.h>
30b9ef152bSMark Johnston #include <dev/vmm/vmm_stat.h>
31b9ef152bSMark Johnston 
32e12b6aafSMark Johnston #if defined(__amd64__) && defined(COMPAT_FREEBSD12)
33a852dc58SMark Johnston struct vm_memseg_12 {
34e12b6aafSMark Johnston 	int		segid;
35e12b6aafSMark Johnston 	size_t		len;
36e12b6aafSMark Johnston 	char		name[64];
37e12b6aafSMark Johnston };
38a852dc58SMark Johnston _Static_assert(sizeof(struct vm_memseg_12) == 80, "COMPAT_FREEBSD12 ABI");
39e12b6aafSMark Johnston 
40a852dc58SMark Johnston #define	VM_ALLOC_MEMSEG_12	\
41a852dc58SMark Johnston 	_IOW('v', IOCNUM_ALLOC_MEMSEG, struct vm_memseg_12)
42a852dc58SMark Johnston #define	VM_GET_MEMSEG_12	\
43a852dc58SMark Johnston 	_IOWR('v', IOCNUM_GET_MEMSEG, struct vm_memseg_12)
44e12b6aafSMark Johnston #endif
45e12b6aafSMark Johnston 
46b9ef152bSMark Johnston struct devmem_softc {
47b9ef152bSMark Johnston 	int	segid;
48b9ef152bSMark Johnston 	char	*name;
49b9ef152bSMark Johnston 	struct cdev *cdev;
50b9ef152bSMark Johnston 	struct vmmdev_softc *sc;
51b9ef152bSMark Johnston 	SLIST_ENTRY(devmem_softc) link;
52b9ef152bSMark Johnston };
53b9ef152bSMark Johnston 
54b9ef152bSMark Johnston struct vmmdev_softc {
55b9ef152bSMark Johnston 	struct vm	*vm;		/* vm instance cookie */
56b9ef152bSMark Johnston 	struct cdev	*cdev;
57b9ef152bSMark Johnston 	struct ucred	*ucred;
58b9ef152bSMark Johnston 	SLIST_ENTRY(vmmdev_softc) link;
59b9ef152bSMark Johnston 	SLIST_HEAD(, devmem_softc) devmem;
60b9ef152bSMark Johnston 	int		flags;
61b9ef152bSMark Johnston };
62b9ef152bSMark Johnston 
63b9ef152bSMark Johnston static SLIST_HEAD(, vmmdev_softc) head;
64b9ef152bSMark Johnston 
65b9ef152bSMark Johnston static unsigned pr_allow_flag;
66887c0877SMark Johnston static struct sx vmmdev_mtx;
67887c0877SMark Johnston SX_SYSINIT(vmmdev_mtx, &vmmdev_mtx, "vmm device mutex");
68b9ef152bSMark Johnston 
69b9ef152bSMark Johnston static MALLOC_DEFINE(M_VMMDEV, "vmmdev", "vmmdev");
70b9ef152bSMark Johnston 
71b9ef152bSMark Johnston SYSCTL_DECL(_hw_vmm);
72b9ef152bSMark Johnston 
73b9ef152bSMark Johnston static void devmem_destroy(void *arg);
74f4002135SMark Johnston static int devmem_create_cdev(struct vmmdev_softc *sc, int id, char *devmem);
75b9ef152bSMark Johnston 
76b9ef152bSMark Johnston static int
77b9ef152bSMark Johnston vmm_priv_check(struct ucred *ucred)
78b9ef152bSMark Johnston {
79b9ef152bSMark Johnston 	if (jailed(ucred) &&
80b9ef152bSMark Johnston 	    !(ucred->cr_prison->pr_allow & pr_allow_flag))
81b9ef152bSMark Johnston 		return (EPERM);
82b9ef152bSMark Johnston 
83b9ef152bSMark Johnston 	return (0);
84b9ef152bSMark Johnston }
85b9ef152bSMark Johnston 
86b9ef152bSMark Johnston static int
87b9ef152bSMark Johnston vcpu_lock_one(struct vcpu *vcpu)
88b9ef152bSMark Johnston {
89b9ef152bSMark Johnston 	return (vcpu_set_state(vcpu, VCPU_FROZEN, true));
90b9ef152bSMark Johnston }
91b9ef152bSMark Johnston 
92b9ef152bSMark Johnston static void
93b9ef152bSMark Johnston vcpu_unlock_one(struct vcpu *vcpu)
94b9ef152bSMark Johnston {
95b9ef152bSMark Johnston 	enum vcpu_state state;
96b9ef152bSMark Johnston 
97b9ef152bSMark Johnston 	state = vcpu_get_state(vcpu, NULL);
98b9ef152bSMark Johnston 	if (state != VCPU_FROZEN) {
99b9ef152bSMark Johnston 		panic("vcpu %s(%d) has invalid state %d",
100b9ef152bSMark Johnston 		    vm_name(vcpu_vm(vcpu)), vcpu_vcpuid(vcpu), state);
101b9ef152bSMark Johnston 	}
102b9ef152bSMark Johnston 
103b9ef152bSMark Johnston 	vcpu_set_state(vcpu, VCPU_IDLE, false);
104b9ef152bSMark Johnston }
105b9ef152bSMark Johnston 
106b9ef152bSMark Johnston static int
107b9ef152bSMark Johnston vcpu_lock_all(struct vmmdev_softc *sc)
108b9ef152bSMark Johnston {
109b9ef152bSMark Johnston 	struct vcpu *vcpu;
110b9ef152bSMark Johnston 	int error;
111b9ef152bSMark Johnston 	uint16_t i, j, maxcpus;
112b9ef152bSMark Johnston 
113b9ef152bSMark Johnston 	error = 0;
114b9ef152bSMark Johnston 	vm_slock_vcpus(sc->vm);
115b9ef152bSMark Johnston 	maxcpus = vm_get_maxcpus(sc->vm);
116b9ef152bSMark Johnston 	for (i = 0; i < maxcpus; i++) {
117b9ef152bSMark Johnston 		vcpu = vm_vcpu(sc->vm, i);
118b9ef152bSMark Johnston 		if (vcpu == NULL)
119b9ef152bSMark Johnston 			continue;
120b9ef152bSMark Johnston 		error = vcpu_lock_one(vcpu);
121b9ef152bSMark Johnston 		if (error)
122b9ef152bSMark Johnston 			break;
123b9ef152bSMark Johnston 	}
124b9ef152bSMark Johnston 
125b9ef152bSMark Johnston 	if (error) {
126b9ef152bSMark Johnston 		for (j = 0; j < i; j++) {
127b9ef152bSMark Johnston 			vcpu = vm_vcpu(sc->vm, j);
128b9ef152bSMark Johnston 			if (vcpu == NULL)
129b9ef152bSMark Johnston 				continue;
130b9ef152bSMark Johnston 			vcpu_unlock_one(vcpu);
131b9ef152bSMark Johnston 		}
132b9ef152bSMark Johnston 		vm_unlock_vcpus(sc->vm);
133b9ef152bSMark Johnston 	}
134b9ef152bSMark Johnston 
135b9ef152bSMark Johnston 	return (error);
136b9ef152bSMark Johnston }
137b9ef152bSMark Johnston 
138b9ef152bSMark Johnston static void
139b9ef152bSMark Johnston vcpu_unlock_all(struct vmmdev_softc *sc)
140b9ef152bSMark Johnston {
141b9ef152bSMark Johnston 	struct vcpu *vcpu;
142b9ef152bSMark Johnston 	uint16_t i, maxcpus;
143b9ef152bSMark Johnston 
144b9ef152bSMark Johnston 	maxcpus = vm_get_maxcpus(sc->vm);
145b9ef152bSMark Johnston 	for (i = 0; i < maxcpus; i++) {
146b9ef152bSMark Johnston 		vcpu = vm_vcpu(sc->vm, i);
147b9ef152bSMark Johnston 		if (vcpu == NULL)
148b9ef152bSMark Johnston 			continue;
149b9ef152bSMark Johnston 		vcpu_unlock_one(vcpu);
150b9ef152bSMark Johnston 	}
151b9ef152bSMark Johnston 	vm_unlock_vcpus(sc->vm);
152b9ef152bSMark Johnston }
153b9ef152bSMark Johnston 
154b9ef152bSMark Johnston static struct vmmdev_softc *
155c23da668SMark Johnston vmmdev_lookup(const char *name, struct ucred *cred)
156b9ef152bSMark Johnston {
157b9ef152bSMark Johnston 	struct vmmdev_softc *sc;
158b9ef152bSMark Johnston 
159887c0877SMark Johnston 	sx_assert(&vmmdev_mtx, SA_XLOCKED);
160b9ef152bSMark Johnston 
161b9ef152bSMark Johnston 	SLIST_FOREACH(sc, &head, link) {
162b9ef152bSMark Johnston 		if (strcmp(name, vm_name(sc->vm)) == 0)
163b9ef152bSMark Johnston 			break;
164b9ef152bSMark Johnston 	}
165b9ef152bSMark Johnston 
166b9ef152bSMark Johnston 	if (sc == NULL)
167b9ef152bSMark Johnston 		return (NULL);
168b9ef152bSMark Johnston 
169c23da668SMark Johnston 	if (cr_cansee(cred, sc->ucred))
170b9ef152bSMark Johnston 		return (NULL);
171b9ef152bSMark Johnston 
172b9ef152bSMark Johnston 	return (sc);
173b9ef152bSMark Johnston }
174b9ef152bSMark Johnston 
175b9ef152bSMark Johnston static struct vmmdev_softc *
176b9ef152bSMark Johnston vmmdev_lookup2(struct cdev *cdev)
177b9ef152bSMark Johnston {
178b9ef152bSMark Johnston 	return (cdev->si_drv1);
179b9ef152bSMark Johnston }
180b9ef152bSMark Johnston 
181b9ef152bSMark Johnston static int
182b9ef152bSMark Johnston vmmdev_rw(struct cdev *cdev, struct uio *uio, int flags)
183b9ef152bSMark Johnston {
184b9ef152bSMark Johnston 	int error, off, c, prot;
185b9ef152bSMark Johnston 	vm_paddr_t gpa, maxaddr;
186b9ef152bSMark Johnston 	void *hpa, *cookie;
187b9ef152bSMark Johnston 	struct vmmdev_softc *sc;
188b9ef152bSMark Johnston 
189b9ef152bSMark Johnston 	sc = vmmdev_lookup2(cdev);
190b9ef152bSMark Johnston 	if (sc == NULL)
191b9ef152bSMark Johnston 		return (ENXIO);
192b9ef152bSMark Johnston 
193b9ef152bSMark Johnston 	/*
194b9ef152bSMark Johnston 	 * Get a read lock on the guest memory map.
195b9ef152bSMark Johnston 	 */
196b9ef152bSMark Johnston 	vm_slock_memsegs(sc->vm);
197b9ef152bSMark Johnston 
1987c89253bSJohn Baldwin 	error = 0;
199b9ef152bSMark Johnston 	prot = (uio->uio_rw == UIO_WRITE ? VM_PROT_WRITE : VM_PROT_READ);
200b9ef152bSMark Johnston 	maxaddr = vmm_sysmem_maxaddr(sc->vm);
201b9ef152bSMark Johnston 	while (uio->uio_resid > 0 && error == 0) {
202b9ef152bSMark Johnston 		gpa = uio->uio_offset;
203b9ef152bSMark Johnston 		off = gpa & PAGE_MASK;
204b9ef152bSMark Johnston 		c = min(uio->uio_resid, PAGE_SIZE - off);
205b9ef152bSMark Johnston 
206b9ef152bSMark Johnston 		/*
207b9ef152bSMark Johnston 		 * The VM has a hole in its physical memory map. If we want to
208b9ef152bSMark Johnston 		 * use 'dd' to inspect memory beyond the hole we need to
209b9ef152bSMark Johnston 		 * provide bogus data for memory that lies in the hole.
210b9ef152bSMark Johnston 		 *
211b9ef152bSMark Johnston 		 * Since this device does not support lseek(2), dd(1) will
212b9ef152bSMark Johnston 		 * read(2) blocks of data to simulate the lseek(2).
213b9ef152bSMark Johnston 		 */
214b9ef152bSMark Johnston 		hpa = vm_gpa_hold_global(sc->vm, gpa, c, prot, &cookie);
215b9ef152bSMark Johnston 		if (hpa == NULL) {
216b9ef152bSMark Johnston 			if (uio->uio_rw == UIO_READ && gpa < maxaddr)
217b9ef152bSMark Johnston 				error = uiomove(__DECONST(void *, zero_region),
218b9ef152bSMark Johnston 				    c, uio);
219b9ef152bSMark Johnston 			else
220b9ef152bSMark Johnston 				error = EFAULT;
221b9ef152bSMark Johnston 		} else {
222b9ef152bSMark Johnston 			error = uiomove(hpa, c, uio);
223b9ef152bSMark Johnston 			vm_gpa_release(cookie);
224b9ef152bSMark Johnston 		}
225b9ef152bSMark Johnston 	}
226b9ef152bSMark Johnston 	vm_unlock_memsegs(sc->vm);
227b9ef152bSMark Johnston 	return (error);
228b9ef152bSMark Johnston }
229b9ef152bSMark Johnston 
230b9ef152bSMark Johnston CTASSERT(sizeof(((struct vm_memseg *)0)->name) >= VM_MAX_SUFFIXLEN + 1);
231b9ef152bSMark Johnston 
232b9ef152bSMark Johnston static int
233b9ef152bSMark Johnston get_memseg(struct vmmdev_softc *sc, struct vm_memseg *mseg, size_t len)
234b9ef152bSMark Johnston {
235b9ef152bSMark Johnston 	struct devmem_softc *dsc;
236b9ef152bSMark Johnston 	int error;
237b9ef152bSMark Johnston 	bool sysmem;
238b9ef152bSMark Johnston 
239b9ef152bSMark Johnston 	error = vm_get_memseg(sc->vm, mseg->segid, &mseg->len, &sysmem, NULL);
240b9ef152bSMark Johnston 	if (error || mseg->len == 0)
241b9ef152bSMark Johnston 		return (error);
242b9ef152bSMark Johnston 
243b9ef152bSMark Johnston 	if (!sysmem) {
244b9ef152bSMark Johnston 		SLIST_FOREACH(dsc, &sc->devmem, link) {
245b9ef152bSMark Johnston 			if (dsc->segid == mseg->segid)
246b9ef152bSMark Johnston 				break;
247b9ef152bSMark Johnston 		}
248b9ef152bSMark Johnston 		KASSERT(dsc != NULL, ("%s: devmem segment %d not found",
249b9ef152bSMark Johnston 		    __func__, mseg->segid));
250b9ef152bSMark Johnston 		error = copystr(dsc->name, mseg->name, len, NULL);
251b9ef152bSMark Johnston 	} else {
252b9ef152bSMark Johnston 		bzero(mseg->name, len);
253b9ef152bSMark Johnston 	}
254b9ef152bSMark Johnston 
255b9ef152bSMark Johnston 	return (error);
256b9ef152bSMark Johnston }
257b9ef152bSMark Johnston 
258b9ef152bSMark Johnston static int
259b9ef152bSMark Johnston alloc_memseg(struct vmmdev_softc *sc, struct vm_memseg *mseg, size_t len)
260b9ef152bSMark Johnston {
261b9ef152bSMark Johnston 	char *name;
262b9ef152bSMark Johnston 	int error;
263b9ef152bSMark Johnston 	bool sysmem;
264b9ef152bSMark Johnston 
265b9ef152bSMark Johnston 	error = 0;
266b9ef152bSMark Johnston 	name = NULL;
267b9ef152bSMark Johnston 	sysmem = true;
268b9ef152bSMark Johnston 
269b9ef152bSMark Johnston 	/*
270b9ef152bSMark Johnston 	 * The allocation is lengthened by 1 to hold a terminating NUL.  It'll
271b9ef152bSMark Johnston 	 * by stripped off when devfs processes the full string.
272b9ef152bSMark Johnston 	 */
273b9ef152bSMark Johnston 	if (VM_MEMSEG_NAME(mseg)) {
274b9ef152bSMark Johnston 		sysmem = false;
275b9ef152bSMark Johnston 		name = malloc(len, M_VMMDEV, M_WAITOK);
276b9ef152bSMark Johnston 		error = copystr(mseg->name, name, len, NULL);
277b9ef152bSMark Johnston 		if (error)
278b9ef152bSMark Johnston 			goto done;
279b9ef152bSMark Johnston 	}
280b9ef152bSMark Johnston 
281b9ef152bSMark Johnston 	error = vm_alloc_memseg(sc->vm, mseg->segid, mseg->len, sysmem);
282b9ef152bSMark Johnston 	if (error)
283b9ef152bSMark Johnston 		goto done;
284b9ef152bSMark Johnston 
285b9ef152bSMark Johnston 	if (VM_MEMSEG_NAME(mseg)) {
286f4002135SMark Johnston 		error = devmem_create_cdev(sc, mseg->segid, name);
287b9ef152bSMark Johnston 		if (error)
288b9ef152bSMark Johnston 			vm_free_memseg(sc->vm, mseg->segid);
289b9ef152bSMark Johnston 		else
290b9ef152bSMark Johnston 			name = NULL;	/* freed when 'cdev' is destroyed */
291b9ef152bSMark Johnston 	}
292b9ef152bSMark Johnston done:
293b9ef152bSMark Johnston 	free(name, M_VMMDEV);
294b9ef152bSMark Johnston 	return (error);
295b9ef152bSMark Johnston }
296b9ef152bSMark Johnston 
297b9ef152bSMark Johnston static int
298b9ef152bSMark Johnston vm_get_register_set(struct vcpu *vcpu, unsigned int count, int *regnum,
299b9ef152bSMark Johnston     uint64_t *regval)
300b9ef152bSMark Johnston {
301b9ef152bSMark Johnston 	int error, i;
302b9ef152bSMark Johnston 
303b9ef152bSMark Johnston 	error = 0;
304b9ef152bSMark Johnston 	for (i = 0; i < count; i++) {
305b9ef152bSMark Johnston 		error = vm_get_register(vcpu, regnum[i], &regval[i]);
306b9ef152bSMark Johnston 		if (error)
307b9ef152bSMark Johnston 			break;
308b9ef152bSMark Johnston 	}
309b9ef152bSMark Johnston 	return (error);
310b9ef152bSMark Johnston }
311b9ef152bSMark Johnston 
312b9ef152bSMark Johnston static int
313b9ef152bSMark Johnston vm_set_register_set(struct vcpu *vcpu, unsigned int count, int *regnum,
314b9ef152bSMark Johnston     uint64_t *regval)
315b9ef152bSMark Johnston {
316b9ef152bSMark Johnston 	int error, i;
317b9ef152bSMark Johnston 
318b9ef152bSMark Johnston 	error = 0;
319b9ef152bSMark Johnston 	for (i = 0; i < count; i++) {
320b9ef152bSMark Johnston 		error = vm_set_register(vcpu, regnum[i], regval[i]);
321b9ef152bSMark Johnston 		if (error)
322b9ef152bSMark Johnston 			break;
323b9ef152bSMark Johnston 	}
324b9ef152bSMark Johnston 	return (error);
325b9ef152bSMark Johnston }
326b9ef152bSMark Johnston 
32740087581SMark Johnston static int
32840087581SMark Johnston vmmdev_open(struct cdev *dev, int flags, int fmt, struct thread *td)
32940087581SMark Johnston {
33040087581SMark Johnston 	int error;
33140087581SMark Johnston 
33240087581SMark Johnston 	/*
33340087581SMark Johnston 	 * A jail without vmm access shouldn't be able to access vmm device
33440087581SMark Johnston 	 * files at all, but check here just to be thorough.
33540087581SMark Johnston 	 */
33640087581SMark Johnston 	error = vmm_priv_check(td->td_ucred);
33740087581SMark Johnston 	if (error != 0)
33840087581SMark Johnston 		return (error);
33940087581SMark Johnston 
34040087581SMark Johnston 	return (0);
34140087581SMark Johnston }
34240087581SMark Johnston 
343b9ef152bSMark Johnston static const struct vmmdev_ioctl vmmdev_ioctls[] = {
344b9ef152bSMark Johnston 	VMMDEV_IOCTL(VM_GET_REGISTER, VMMDEV_IOCTL_LOCK_ONE_VCPU),
345b9ef152bSMark Johnston 	VMMDEV_IOCTL(VM_SET_REGISTER, VMMDEV_IOCTL_LOCK_ONE_VCPU),
346b9ef152bSMark Johnston 	VMMDEV_IOCTL(VM_GET_REGISTER_SET, VMMDEV_IOCTL_LOCK_ONE_VCPU),
347b9ef152bSMark Johnston 	VMMDEV_IOCTL(VM_SET_REGISTER_SET, VMMDEV_IOCTL_LOCK_ONE_VCPU),
348b9ef152bSMark Johnston 	VMMDEV_IOCTL(VM_GET_CAPABILITY, VMMDEV_IOCTL_LOCK_ONE_VCPU),
349b9ef152bSMark Johnston 	VMMDEV_IOCTL(VM_SET_CAPABILITY, VMMDEV_IOCTL_LOCK_ONE_VCPU),
350b9ef152bSMark Johnston 	VMMDEV_IOCTL(VM_ACTIVATE_CPU, VMMDEV_IOCTL_LOCK_ONE_VCPU),
351b9ef152bSMark Johnston 	VMMDEV_IOCTL(VM_INJECT_EXCEPTION, VMMDEV_IOCTL_LOCK_ONE_VCPU),
352b9ef152bSMark Johnston 	VMMDEV_IOCTL(VM_STATS, VMMDEV_IOCTL_LOCK_ONE_VCPU),
353b9ef152bSMark Johnston 
354b9ef152bSMark Johnston #if defined(__amd64__) && defined(COMPAT_FREEBSD12)
355a852dc58SMark Johnston 	VMMDEV_IOCTL(VM_ALLOC_MEMSEG_12,
356b9ef152bSMark Johnston 	    VMMDEV_IOCTL_XLOCK_MEMSEGS | VMMDEV_IOCTL_LOCK_ALL_VCPUS),
357b9ef152bSMark Johnston #endif
358b9ef152bSMark Johnston 	VMMDEV_IOCTL(VM_ALLOC_MEMSEG,
359b9ef152bSMark Johnston 	    VMMDEV_IOCTL_XLOCK_MEMSEGS | VMMDEV_IOCTL_LOCK_ALL_VCPUS),
360b9ef152bSMark Johnston 	VMMDEV_IOCTL(VM_MMAP_MEMSEG,
361b9ef152bSMark Johnston 	    VMMDEV_IOCTL_XLOCK_MEMSEGS | VMMDEV_IOCTL_LOCK_ALL_VCPUS),
362b9ef152bSMark Johnston 	VMMDEV_IOCTL(VM_MUNMAP_MEMSEG,
363b9ef152bSMark Johnston 	    VMMDEV_IOCTL_XLOCK_MEMSEGS | VMMDEV_IOCTL_LOCK_ALL_VCPUS),
364b9ef152bSMark Johnston 	VMMDEV_IOCTL(VM_REINIT,
365b9ef152bSMark Johnston 	    VMMDEV_IOCTL_XLOCK_MEMSEGS | VMMDEV_IOCTL_LOCK_ALL_VCPUS),
366b9ef152bSMark Johnston 
367b9ef152bSMark Johnston #if defined(__amd64__) && defined(COMPAT_FREEBSD12)
368a852dc58SMark Johnston 	VMMDEV_IOCTL(VM_GET_MEMSEG_12, VMMDEV_IOCTL_SLOCK_MEMSEGS),
369b9ef152bSMark Johnston #endif
370b9ef152bSMark Johnston 	VMMDEV_IOCTL(VM_GET_MEMSEG, VMMDEV_IOCTL_SLOCK_MEMSEGS),
371b9ef152bSMark Johnston 	VMMDEV_IOCTL(VM_MMAP_GETNEXT, VMMDEV_IOCTL_SLOCK_MEMSEGS),
372b9ef152bSMark Johnston 
373b9ef152bSMark Johnston 	VMMDEV_IOCTL(VM_SUSPEND_CPU, VMMDEV_IOCTL_MAYBE_ALLOC_VCPU),
374b9ef152bSMark Johnston 	VMMDEV_IOCTL(VM_RESUME_CPU, VMMDEV_IOCTL_MAYBE_ALLOC_VCPU),
375b9ef152bSMark Johnston 
376b9ef152bSMark Johnston 	VMMDEV_IOCTL(VM_SUSPEND, 0),
377b9ef152bSMark Johnston 	VMMDEV_IOCTL(VM_GET_CPUS, 0),
378b9ef152bSMark Johnston 	VMMDEV_IOCTL(VM_GET_TOPOLOGY, 0),
379b9ef152bSMark Johnston 	VMMDEV_IOCTL(VM_SET_TOPOLOGY, 0),
380b9ef152bSMark Johnston };
381b9ef152bSMark Johnston 
382b9ef152bSMark Johnston static int
383b9ef152bSMark Johnston vmmdev_ioctl(struct cdev *cdev, u_long cmd, caddr_t data, int fflag,
384b9ef152bSMark Johnston     struct thread *td)
385b9ef152bSMark Johnston {
386b9ef152bSMark Johnston 	struct vmmdev_softc *sc;
387b9ef152bSMark Johnston 	struct vcpu *vcpu;
388b9ef152bSMark Johnston 	const struct vmmdev_ioctl *ioctl;
389b9ef152bSMark Johnston 	int error, vcpuid;
390b9ef152bSMark Johnston 
391b9ef152bSMark Johnston 	sc = vmmdev_lookup2(cdev);
392b9ef152bSMark Johnston 	if (sc == NULL)
393b9ef152bSMark Johnston 		return (ENXIO);
394b9ef152bSMark Johnston 
395b9ef152bSMark Johnston 	ioctl = NULL;
396b9ef152bSMark Johnston 	for (size_t i = 0; i < nitems(vmmdev_ioctls); i++) {
397b9ef152bSMark Johnston 		if (vmmdev_ioctls[i].cmd == cmd) {
398b9ef152bSMark Johnston 			ioctl = &vmmdev_ioctls[i];
399b9ef152bSMark Johnston 			break;
400b9ef152bSMark Johnston 		}
401b9ef152bSMark Johnston 	}
402b9ef152bSMark Johnston 	if (ioctl == NULL) {
403b9ef152bSMark Johnston 		for (size_t i = 0; i < vmmdev_machdep_ioctl_count; i++) {
404b9ef152bSMark Johnston 			if (vmmdev_machdep_ioctls[i].cmd == cmd) {
405b9ef152bSMark Johnston 				ioctl = &vmmdev_machdep_ioctls[i];
406b9ef152bSMark Johnston 				break;
407b9ef152bSMark Johnston 			}
408b9ef152bSMark Johnston 		}
409b9ef152bSMark Johnston 	}
410b9ef152bSMark Johnston 	if (ioctl == NULL)
411b9ef152bSMark Johnston 		return (ENOTTY);
412b9ef152bSMark Johnston 
413b9ef152bSMark Johnston 	if ((ioctl->flags & VMMDEV_IOCTL_XLOCK_MEMSEGS) != 0)
414b9ef152bSMark Johnston 		vm_xlock_memsegs(sc->vm);
415b9ef152bSMark Johnston 	else if ((ioctl->flags & VMMDEV_IOCTL_SLOCK_MEMSEGS) != 0)
416b9ef152bSMark Johnston 		vm_slock_memsegs(sc->vm);
417b9ef152bSMark Johnston 
418b9ef152bSMark Johnston 	vcpu = NULL;
419b9ef152bSMark Johnston 	vcpuid = -1;
420b9ef152bSMark Johnston 	if ((ioctl->flags & (VMMDEV_IOCTL_LOCK_ONE_VCPU |
421b9ef152bSMark Johnston 	    VMMDEV_IOCTL_ALLOC_VCPU | VMMDEV_IOCTL_MAYBE_ALLOC_VCPU)) != 0) {
422b9ef152bSMark Johnston 		vcpuid = *(int *)data;
423b9ef152bSMark Johnston 		if (vcpuid == -1) {
424b9ef152bSMark Johnston 			if ((ioctl->flags &
425b9ef152bSMark Johnston 			    VMMDEV_IOCTL_MAYBE_ALLOC_VCPU) == 0) {
426b9ef152bSMark Johnston 				error = EINVAL;
427b9ef152bSMark Johnston 				goto lockfail;
428b9ef152bSMark Johnston 			}
429b9ef152bSMark Johnston 		} else {
430b9ef152bSMark Johnston 			vcpu = vm_alloc_vcpu(sc->vm, vcpuid);
431b9ef152bSMark Johnston 			if (vcpu == NULL) {
432b9ef152bSMark Johnston 				error = EINVAL;
433b9ef152bSMark Johnston 				goto lockfail;
434b9ef152bSMark Johnston 			}
435b9ef152bSMark Johnston 			if ((ioctl->flags & VMMDEV_IOCTL_LOCK_ONE_VCPU) != 0) {
436b9ef152bSMark Johnston 				error = vcpu_lock_one(vcpu);
437b9ef152bSMark Johnston 				if (error)
438b9ef152bSMark Johnston 					goto lockfail;
439b9ef152bSMark Johnston 			}
440b9ef152bSMark Johnston 		}
441b9ef152bSMark Johnston 	}
442b9ef152bSMark Johnston 	if ((ioctl->flags & VMMDEV_IOCTL_LOCK_ALL_VCPUS) != 0) {
443b9ef152bSMark Johnston 		error = vcpu_lock_all(sc);
444b9ef152bSMark Johnston 		if (error)
445b9ef152bSMark Johnston 			goto lockfail;
446b9ef152bSMark Johnston 	}
447b9ef152bSMark Johnston 
448b9ef152bSMark Johnston 	switch (cmd) {
449b9ef152bSMark Johnston 	case VM_SUSPEND: {
450b9ef152bSMark Johnston 		struct vm_suspend *vmsuspend;
451b9ef152bSMark Johnston 
452b9ef152bSMark Johnston 		vmsuspend = (struct vm_suspend *)data;
453b9ef152bSMark Johnston 		error = vm_suspend(sc->vm, vmsuspend->how);
454b9ef152bSMark Johnston 		break;
455b9ef152bSMark Johnston 	}
456b9ef152bSMark Johnston 	case VM_REINIT:
457b9ef152bSMark Johnston 		error = vm_reinit(sc->vm);
458b9ef152bSMark Johnston 		break;
459b9ef152bSMark Johnston 	case VM_STAT_DESC: {
460b9ef152bSMark Johnston 		struct vm_stat_desc *statdesc;
461b9ef152bSMark Johnston 
462b9ef152bSMark Johnston 		statdesc = (struct vm_stat_desc *)data;
463b9ef152bSMark Johnston 		error = vmm_stat_desc_copy(statdesc->index, statdesc->desc,
464b9ef152bSMark Johnston 		    sizeof(statdesc->desc));
465b9ef152bSMark Johnston 		break;
466b9ef152bSMark Johnston 	}
467b9ef152bSMark Johnston 	case VM_STATS: {
468b9ef152bSMark Johnston 		struct vm_stats *vmstats;
469b9ef152bSMark Johnston 
470b9ef152bSMark Johnston 		vmstats = (struct vm_stats *)data;
471b9ef152bSMark Johnston 		getmicrotime(&vmstats->tv);
472b9ef152bSMark Johnston 		error = vmm_stat_copy(vcpu, vmstats->index,
473b9ef152bSMark Johnston 		    nitems(vmstats->statbuf), &vmstats->num_entries,
474b9ef152bSMark Johnston 		    vmstats->statbuf);
475b9ef152bSMark Johnston 		break;
476b9ef152bSMark Johnston 	}
477b9ef152bSMark Johnston 	case VM_MMAP_GETNEXT: {
478b9ef152bSMark Johnston 		struct vm_memmap *mm;
479b9ef152bSMark Johnston 
480b9ef152bSMark Johnston 		mm = (struct vm_memmap *)data;
481b9ef152bSMark Johnston 		error = vm_mmap_getnext(sc->vm, &mm->gpa, &mm->segid,
482b9ef152bSMark Johnston 		    &mm->segoff, &mm->len, &mm->prot, &mm->flags);
483b9ef152bSMark Johnston 		break;
484b9ef152bSMark Johnston 	}
485b9ef152bSMark Johnston 	case VM_MMAP_MEMSEG: {
486b9ef152bSMark Johnston 		struct vm_memmap *mm;
487b9ef152bSMark Johnston 
488b9ef152bSMark Johnston 		mm = (struct vm_memmap *)data;
489b9ef152bSMark Johnston 		error = vm_mmap_memseg(sc->vm, mm->gpa, mm->segid, mm->segoff,
490b9ef152bSMark Johnston 		    mm->len, mm->prot, mm->flags);
491b9ef152bSMark Johnston 		break;
492b9ef152bSMark Johnston 	}
493b9ef152bSMark Johnston 	case VM_MUNMAP_MEMSEG: {
494b9ef152bSMark Johnston 		struct vm_munmap *mu;
495b9ef152bSMark Johnston 
496b9ef152bSMark Johnston 		mu = (struct vm_munmap *)data;
497b9ef152bSMark Johnston 		error = vm_munmap_memseg(sc->vm, mu->gpa, mu->len);
498b9ef152bSMark Johnston 		break;
499b9ef152bSMark Johnston 	}
500b9ef152bSMark Johnston #if defined(__amd64__) && defined(COMPAT_FREEBSD12)
501a852dc58SMark Johnston 	case VM_ALLOC_MEMSEG_12:
502b9ef152bSMark Johnston 		error = alloc_memseg(sc, (struct vm_memseg *)data,
503a852dc58SMark Johnston 		    sizeof(((struct vm_memseg_12 *)0)->name));
504b9ef152bSMark Johnston 		break;
505a852dc58SMark Johnston 	case VM_GET_MEMSEG_12:
506b9ef152bSMark Johnston 		error = get_memseg(sc, (struct vm_memseg *)data,
507a852dc58SMark Johnston 		    sizeof(((struct vm_memseg_12 *)0)->name));
508b9ef152bSMark Johnston 		break;
509b9ef152bSMark Johnston #endif
510b9ef152bSMark Johnston 	case VM_ALLOC_MEMSEG:
511b9ef152bSMark Johnston 		error = alloc_memseg(sc, (struct vm_memseg *)data,
512b9ef152bSMark Johnston 		    sizeof(((struct vm_memseg *)0)->name));
513b9ef152bSMark Johnston 		break;
514b9ef152bSMark Johnston 	case VM_GET_MEMSEG:
515b9ef152bSMark Johnston 		error = get_memseg(sc, (struct vm_memseg *)data,
516b9ef152bSMark Johnston 		    sizeof(((struct vm_memseg *)0)->name));
517b9ef152bSMark Johnston 		break;
518b9ef152bSMark Johnston 	case VM_GET_REGISTER: {
519b9ef152bSMark Johnston 		struct vm_register *vmreg;
520b9ef152bSMark Johnston 
521b9ef152bSMark Johnston 		vmreg = (struct vm_register *)data;
522b9ef152bSMark Johnston 		error = vm_get_register(vcpu, vmreg->regnum, &vmreg->regval);
523b9ef152bSMark Johnston 		break;
524b9ef152bSMark Johnston 	}
525b9ef152bSMark Johnston 	case VM_SET_REGISTER: {
526b9ef152bSMark Johnston 		struct vm_register *vmreg;
527b9ef152bSMark Johnston 
528b9ef152bSMark Johnston 		vmreg = (struct vm_register *)data;
529b9ef152bSMark Johnston 		error = vm_set_register(vcpu, vmreg->regnum, vmreg->regval);
530b9ef152bSMark Johnston 		break;
531b9ef152bSMark Johnston 	}
532b9ef152bSMark Johnston 	case VM_GET_REGISTER_SET: {
533b9ef152bSMark Johnston 		struct vm_register_set *vmregset;
534b9ef152bSMark Johnston 		uint64_t *regvals;
535b9ef152bSMark Johnston 		int *regnums;
536b9ef152bSMark Johnston 
537b9ef152bSMark Johnston 		vmregset = (struct vm_register_set *)data;
538b9ef152bSMark Johnston 		if (vmregset->count > VM_REG_LAST) {
539b9ef152bSMark Johnston 			error = EINVAL;
540b9ef152bSMark Johnston 			break;
541b9ef152bSMark Johnston 		}
542b9ef152bSMark Johnston 		regvals = malloc(sizeof(regvals[0]) * vmregset->count, M_VMMDEV,
543b9ef152bSMark Johnston 		    M_WAITOK);
544b9ef152bSMark Johnston 		regnums = malloc(sizeof(regnums[0]) * vmregset->count, M_VMMDEV,
545b9ef152bSMark Johnston 		    M_WAITOK);
546b9ef152bSMark Johnston 		error = copyin(vmregset->regnums, regnums, sizeof(regnums[0]) *
547b9ef152bSMark Johnston 		    vmregset->count);
548b9ef152bSMark Johnston 		if (error == 0)
549b9ef152bSMark Johnston 			error = vm_get_register_set(vcpu,
550b9ef152bSMark Johnston 			    vmregset->count, regnums, regvals);
551b9ef152bSMark Johnston 		if (error == 0)
552b9ef152bSMark Johnston 			error = copyout(regvals, vmregset->regvals,
553b9ef152bSMark Johnston 			    sizeof(regvals[0]) * vmregset->count);
554b9ef152bSMark Johnston 		free(regvals, M_VMMDEV);
555b9ef152bSMark Johnston 		free(regnums, M_VMMDEV);
556b9ef152bSMark Johnston 		break;
557b9ef152bSMark Johnston 	}
558b9ef152bSMark Johnston 	case VM_SET_REGISTER_SET: {
559b9ef152bSMark Johnston 		struct vm_register_set *vmregset;
560b9ef152bSMark Johnston 		uint64_t *regvals;
561b9ef152bSMark Johnston 		int *regnums;
562b9ef152bSMark Johnston 
563b9ef152bSMark Johnston 		vmregset = (struct vm_register_set *)data;
564b9ef152bSMark Johnston 		if (vmregset->count > VM_REG_LAST) {
565b9ef152bSMark Johnston 			error = EINVAL;
566b9ef152bSMark Johnston 			break;
567b9ef152bSMark Johnston 		}
568b9ef152bSMark Johnston 		regvals = malloc(sizeof(regvals[0]) * vmregset->count, M_VMMDEV,
569b9ef152bSMark Johnston 		    M_WAITOK);
570b9ef152bSMark Johnston 		regnums = malloc(sizeof(regnums[0]) * vmregset->count, M_VMMDEV,
571b9ef152bSMark Johnston 		    M_WAITOK);
572b9ef152bSMark Johnston 		error = copyin(vmregset->regnums, regnums, sizeof(regnums[0]) *
573b9ef152bSMark Johnston 		    vmregset->count);
574b9ef152bSMark Johnston 		if (error == 0)
575b9ef152bSMark Johnston 			error = copyin(vmregset->regvals, regvals,
576b9ef152bSMark Johnston 			    sizeof(regvals[0]) * vmregset->count);
577b9ef152bSMark Johnston 		if (error == 0)
578b9ef152bSMark Johnston 			error = vm_set_register_set(vcpu,
579b9ef152bSMark Johnston 			    vmregset->count, regnums, regvals);
580b9ef152bSMark Johnston 		free(regvals, M_VMMDEV);
581b9ef152bSMark Johnston 		free(regnums, M_VMMDEV);
582b9ef152bSMark Johnston 		break;
583b9ef152bSMark Johnston 	}
584b9ef152bSMark Johnston 	case VM_GET_CAPABILITY: {
585b9ef152bSMark Johnston 		struct vm_capability *vmcap;
586b9ef152bSMark Johnston 
587b9ef152bSMark Johnston 		vmcap = (struct vm_capability *)data;
588b9ef152bSMark Johnston 		error = vm_get_capability(vcpu, vmcap->captype, &vmcap->capval);
589b9ef152bSMark Johnston 		break;
590b9ef152bSMark Johnston 	}
591b9ef152bSMark Johnston 	case VM_SET_CAPABILITY: {
592b9ef152bSMark Johnston 		struct vm_capability *vmcap;
593b9ef152bSMark Johnston 
594b9ef152bSMark Johnston 		vmcap = (struct vm_capability *)data;
595b9ef152bSMark Johnston 		error = vm_set_capability(vcpu, vmcap->captype, vmcap->capval);
596b9ef152bSMark Johnston 		break;
597b9ef152bSMark Johnston 	}
598b9ef152bSMark Johnston 	case VM_ACTIVATE_CPU:
599b9ef152bSMark Johnston 		error = vm_activate_cpu(vcpu);
600b9ef152bSMark Johnston 		break;
601b9ef152bSMark Johnston 	case VM_GET_CPUS: {
602b9ef152bSMark Johnston 		struct vm_cpuset *vm_cpuset;
603b9ef152bSMark Johnston 		cpuset_t *cpuset;
604b9ef152bSMark Johnston 		int size;
605b9ef152bSMark Johnston 
606b9ef152bSMark Johnston 		error = 0;
607b9ef152bSMark Johnston 		vm_cpuset = (struct vm_cpuset *)data;
608b9ef152bSMark Johnston 		size = vm_cpuset->cpusetsize;
609b9ef152bSMark Johnston 		if (size < 1 || size > CPU_MAXSIZE / NBBY) {
610b9ef152bSMark Johnston 			error = ERANGE;
611b9ef152bSMark Johnston 			break;
612b9ef152bSMark Johnston 		}
613b9ef152bSMark Johnston 		cpuset = malloc(max(size, sizeof(cpuset_t)), M_TEMP,
614b9ef152bSMark Johnston 		    M_WAITOK | M_ZERO);
615b9ef152bSMark Johnston 		if (vm_cpuset->which == VM_ACTIVE_CPUS)
616b9ef152bSMark Johnston 			*cpuset = vm_active_cpus(sc->vm);
617b9ef152bSMark Johnston 		else if (vm_cpuset->which == VM_SUSPENDED_CPUS)
618b9ef152bSMark Johnston 			*cpuset = vm_suspended_cpus(sc->vm);
619b9ef152bSMark Johnston 		else if (vm_cpuset->which == VM_DEBUG_CPUS)
620b9ef152bSMark Johnston 			*cpuset = vm_debug_cpus(sc->vm);
621b9ef152bSMark Johnston 		else
622b9ef152bSMark Johnston 			error = EINVAL;
623b9ef152bSMark Johnston 		if (error == 0 && size < howmany(CPU_FLS(cpuset), NBBY))
624b9ef152bSMark Johnston 			error = ERANGE;
625b9ef152bSMark Johnston 		if (error == 0)
626b9ef152bSMark Johnston 			error = copyout(cpuset, vm_cpuset->cpus, size);
627b9ef152bSMark Johnston 		free(cpuset, M_TEMP);
628b9ef152bSMark Johnston 		break;
629b9ef152bSMark Johnston 	}
630b9ef152bSMark Johnston 	case VM_SUSPEND_CPU:
631b9ef152bSMark Johnston 		error = vm_suspend_cpu(sc->vm, vcpu);
632b9ef152bSMark Johnston 		break;
633b9ef152bSMark Johnston 	case VM_RESUME_CPU:
634b9ef152bSMark Johnston 		error = vm_resume_cpu(sc->vm, vcpu);
635b9ef152bSMark Johnston 		break;
636b9ef152bSMark Johnston 	case VM_SET_TOPOLOGY: {
637b9ef152bSMark Johnston 		struct vm_cpu_topology *topology;
638b9ef152bSMark Johnston 
639b9ef152bSMark Johnston 		topology = (struct vm_cpu_topology *)data;
640b9ef152bSMark Johnston 		error = vm_set_topology(sc->vm, topology->sockets,
641b9ef152bSMark Johnston 		    topology->cores, topology->threads, topology->maxcpus);
642b9ef152bSMark Johnston 		break;
643b9ef152bSMark Johnston 	}
644b9ef152bSMark Johnston 	case VM_GET_TOPOLOGY: {
645b9ef152bSMark Johnston 		struct vm_cpu_topology *topology;
646b9ef152bSMark Johnston 
647b9ef152bSMark Johnston 		topology = (struct vm_cpu_topology *)data;
648b9ef152bSMark Johnston 		vm_get_topology(sc->vm, &topology->sockets, &topology->cores,
649b9ef152bSMark Johnston 		    &topology->threads, &topology->maxcpus);
650b9ef152bSMark Johnston 		error = 0;
651b9ef152bSMark Johnston 		break;
652b9ef152bSMark Johnston 	}
653b9ef152bSMark Johnston 	default:
654b9ef152bSMark Johnston 		error = vmmdev_machdep_ioctl(sc->vm, vcpu, cmd, data, fflag,
655b9ef152bSMark Johnston 		    td);
656b9ef152bSMark Johnston 		break;
657b9ef152bSMark Johnston 	}
658b9ef152bSMark Johnston 
659b9ef152bSMark Johnston 	if ((ioctl->flags &
660b9ef152bSMark Johnston 	    (VMMDEV_IOCTL_XLOCK_MEMSEGS | VMMDEV_IOCTL_SLOCK_MEMSEGS)) != 0)
661b9ef152bSMark Johnston 		vm_unlock_memsegs(sc->vm);
662b9ef152bSMark Johnston 	if ((ioctl->flags & VMMDEV_IOCTL_LOCK_ALL_VCPUS) != 0)
663b9ef152bSMark Johnston 		vcpu_unlock_all(sc);
664b9ef152bSMark Johnston 	else if ((ioctl->flags & VMMDEV_IOCTL_LOCK_ONE_VCPU) != 0)
665b9ef152bSMark Johnston 		vcpu_unlock_one(vcpu);
666b9ef152bSMark Johnston 
667b9ef152bSMark Johnston 	/*
668b9ef152bSMark Johnston 	 * Make sure that no handler returns a kernel-internal
669b9ef152bSMark Johnston 	 * error value to userspace.
670b9ef152bSMark Johnston 	 */
671b9ef152bSMark Johnston 	KASSERT(error == ERESTART || error >= 0,
672b9ef152bSMark Johnston 	    ("vmmdev_ioctl: invalid error return %d", error));
673b9ef152bSMark Johnston 	return (error);
674b9ef152bSMark Johnston 
675b9ef152bSMark Johnston lockfail:
676b9ef152bSMark Johnston 	if ((ioctl->flags &
677b9ef152bSMark Johnston 	    (VMMDEV_IOCTL_XLOCK_MEMSEGS | VMMDEV_IOCTL_SLOCK_MEMSEGS)) != 0)
678b9ef152bSMark Johnston 		vm_unlock_memsegs(sc->vm);
679b9ef152bSMark Johnston 	return (error);
680b9ef152bSMark Johnston }
681b9ef152bSMark Johnston 
682b9ef152bSMark Johnston static int
683b9ef152bSMark Johnston vmmdev_mmap_single(struct cdev *cdev, vm_ooffset_t *offset, vm_size_t mapsize,
684b9ef152bSMark Johnston     struct vm_object **objp, int nprot)
685b9ef152bSMark Johnston {
686b9ef152bSMark Johnston 	struct vmmdev_softc *sc;
687b9ef152bSMark Johnston 	vm_paddr_t gpa;
688b9ef152bSMark Johnston 	size_t len;
689b9ef152bSMark Johnston 	vm_ooffset_t segoff, first, last;
690b9ef152bSMark Johnston 	int error, found, segid;
691b9ef152bSMark Johnston 	bool sysmem;
692b9ef152bSMark Johnston 
693b9ef152bSMark Johnston 	first = *offset;
694b9ef152bSMark Johnston 	last = first + mapsize;
695b9ef152bSMark Johnston 	if ((nprot & PROT_EXEC) || first < 0 || first >= last)
696b9ef152bSMark Johnston 		return (EINVAL);
697b9ef152bSMark Johnston 
698b9ef152bSMark Johnston 	sc = vmmdev_lookup2(cdev);
699b9ef152bSMark Johnston 	if (sc == NULL) {
700b9ef152bSMark Johnston 		/* virtual machine is in the process of being created */
701b9ef152bSMark Johnston 		return (EINVAL);
702b9ef152bSMark Johnston 	}
703b9ef152bSMark Johnston 
704b9ef152bSMark Johnston 	/*
705b9ef152bSMark Johnston 	 * Get a read lock on the guest memory map.
706b9ef152bSMark Johnston 	 */
707b9ef152bSMark Johnston 	vm_slock_memsegs(sc->vm);
708b9ef152bSMark Johnston 
709b9ef152bSMark Johnston 	gpa = 0;
710b9ef152bSMark Johnston 	found = 0;
711b9ef152bSMark Johnston 	while (!found) {
712b9ef152bSMark Johnston 		error = vm_mmap_getnext(sc->vm, &gpa, &segid, &segoff, &len,
713b9ef152bSMark Johnston 		    NULL, NULL);
714b9ef152bSMark Johnston 		if (error)
715b9ef152bSMark Johnston 			break;
716b9ef152bSMark Johnston 
717b9ef152bSMark Johnston 		if (first >= gpa && last <= gpa + len)
718b9ef152bSMark Johnston 			found = 1;
719b9ef152bSMark Johnston 		else
720b9ef152bSMark Johnston 			gpa += len;
721b9ef152bSMark Johnston 	}
722b9ef152bSMark Johnston 
723b9ef152bSMark Johnston 	if (found) {
724b9ef152bSMark Johnston 		error = vm_get_memseg(sc->vm, segid, &len, &sysmem, objp);
725b9ef152bSMark Johnston 		KASSERT(error == 0 && *objp != NULL,
726b9ef152bSMark Johnston 		    ("%s: invalid memory segment %d", __func__, segid));
727b9ef152bSMark Johnston 		if (sysmem) {
728b9ef152bSMark Johnston 			vm_object_reference(*objp);
729b9ef152bSMark Johnston 			*offset = segoff + (first - gpa);
730b9ef152bSMark Johnston 		} else {
731b9ef152bSMark Johnston 			error = EINVAL;
732b9ef152bSMark Johnston 		}
733b9ef152bSMark Johnston 	}
734b9ef152bSMark Johnston 	vm_unlock_memsegs(sc->vm);
735b9ef152bSMark Johnston 	return (error);
736b9ef152bSMark Johnston }
737b9ef152bSMark Johnston 
738b9ef152bSMark Johnston static void
739063a8bd9SMark Johnston vmmdev_destroy(struct vmmdev_softc *sc)
740b9ef152bSMark Johnston {
741b9ef152bSMark Johnston 	struct devmem_softc *dsc;
742b9ef152bSMark Johnston 	int error __diagused;
743b9ef152bSMark Johnston 
744cef5f43fSMark Johnston 	KASSERT(sc->cdev == NULL, ("%s: cdev not free", __func__));
745cef5f43fSMark Johnston 
746063a8bd9SMark Johnston 	/*
747063a8bd9SMark Johnston 	 * Destroy all cdevs:
748063a8bd9SMark Johnston 	 *
749063a8bd9SMark Johnston 	 * - any new operations on the 'cdev' will return an error (ENXIO).
750063a8bd9SMark Johnston 	 *
751063a8bd9SMark Johnston 	 * - the 'devmem' cdevs are destroyed before the virtual machine 'cdev'
752063a8bd9SMark Johnston 	 */
753063a8bd9SMark Johnston 	SLIST_FOREACH(dsc, &sc->devmem, link) {
754063a8bd9SMark Johnston 		KASSERT(dsc->cdev != NULL, ("devmem cdev already destroyed"));
755063a8bd9SMark Johnston 		devmem_destroy(dsc);
756063a8bd9SMark Johnston 	}
757063a8bd9SMark Johnston 
758b9ef152bSMark Johnston 	vm_disable_vcpu_creation(sc->vm);
759b9ef152bSMark Johnston 	error = vcpu_lock_all(sc);
760b9ef152bSMark Johnston 	KASSERT(error == 0, ("%s: error %d freezing vcpus", __func__, error));
761b9ef152bSMark Johnston 	vm_unlock_vcpus(sc->vm);
762b9ef152bSMark Johnston 
763b9ef152bSMark Johnston 	while ((dsc = SLIST_FIRST(&sc->devmem)) != NULL) {
764b9ef152bSMark Johnston 		KASSERT(dsc->cdev == NULL, ("%s: devmem not free", __func__));
765b9ef152bSMark Johnston 		SLIST_REMOVE_HEAD(&sc->devmem, link);
766b9ef152bSMark Johnston 		free(dsc->name, M_VMMDEV);
767b9ef152bSMark Johnston 		free(dsc, M_VMMDEV);
768b9ef152bSMark Johnston 	}
769b9ef152bSMark Johnston 
770b9ef152bSMark Johnston 	if (sc->vm != NULL)
771b9ef152bSMark Johnston 		vm_destroy(sc->vm);
772b9ef152bSMark Johnston 
773b9ef152bSMark Johnston 	if (sc->ucred != NULL)
774b9ef152bSMark Johnston 		crfree(sc->ucred);
775b9ef152bSMark Johnston 
776887c0877SMark Johnston 	sx_xlock(&vmmdev_mtx);
777b9ef152bSMark Johnston 	SLIST_REMOVE(&head, sc, vmmdev_softc, link);
778887c0877SMark Johnston 	sx_xunlock(&vmmdev_mtx);
779b9ef152bSMark Johnston 	free(sc, M_VMMDEV);
780b9ef152bSMark Johnston }
781b9ef152bSMark Johnston 
782b9ef152bSMark Johnston static int
783063a8bd9SMark Johnston vmmdev_lookup_and_destroy(const char *name, struct ucred *cred)
784063a8bd9SMark Johnston {
785063a8bd9SMark Johnston 	struct cdev *cdev;
786063a8bd9SMark Johnston 	struct vmmdev_softc *sc;
787063a8bd9SMark Johnston 
788887c0877SMark Johnston 	sx_xlock(&vmmdev_mtx);
789c23da668SMark Johnston 	sc = vmmdev_lookup(name, cred);
790063a8bd9SMark Johnston 	if (sc == NULL || sc->cdev == NULL) {
791887c0877SMark Johnston 		sx_xunlock(&vmmdev_mtx);
792063a8bd9SMark Johnston 		return (EINVAL);
793063a8bd9SMark Johnston 	}
794063a8bd9SMark Johnston 
795063a8bd9SMark Johnston 	/*
796063a8bd9SMark Johnston 	 * Setting 'sc->cdev' to NULL is used to indicate that the VM
797063a8bd9SMark Johnston 	 * is scheduled for destruction.
798063a8bd9SMark Johnston 	 */
799063a8bd9SMark Johnston 	cdev = sc->cdev;
800063a8bd9SMark Johnston 	sc->cdev = NULL;
801887c0877SMark Johnston 	sx_xunlock(&vmmdev_mtx);
802063a8bd9SMark Johnston 
803063a8bd9SMark Johnston 	destroy_dev(cdev);
804063a8bd9SMark Johnston 	vmmdev_destroy(sc);
805063a8bd9SMark Johnston 
806063a8bd9SMark Johnston 	return (0);
807063a8bd9SMark Johnston }
808063a8bd9SMark Johnston 
809063a8bd9SMark Johnston static int
810b9ef152bSMark Johnston sysctl_vmm_destroy(SYSCTL_HANDLER_ARGS)
811b9ef152bSMark Johnston {
812b9ef152bSMark Johnston 	char *buf;
813b9ef152bSMark Johnston 	int error, buflen;
814b9ef152bSMark Johnston 
815b9ef152bSMark Johnston 	error = vmm_priv_check(req->td->td_ucred);
816b9ef152bSMark Johnston 	if (error)
817b9ef152bSMark Johnston 		return (error);
818b9ef152bSMark Johnston 
819b9ef152bSMark Johnston 	buflen = VM_MAX_NAMELEN + 1;
820b9ef152bSMark Johnston 	buf = malloc(buflen, M_VMMDEV, M_WAITOK | M_ZERO);
821b9ef152bSMark Johnston 	strlcpy(buf, "beavis", buflen);
822b9ef152bSMark Johnston 	error = sysctl_handle_string(oidp, buf, buflen, req);
823063a8bd9SMark Johnston 	if (error == 0 && req->newptr != NULL)
824063a8bd9SMark Johnston 		error = vmmdev_lookup_and_destroy(buf, req->td->td_ucred);
825b9ef152bSMark Johnston 	free(buf, M_VMMDEV);
826b9ef152bSMark Johnston 	return (error);
827b9ef152bSMark Johnston }
828b9ef152bSMark Johnston SYSCTL_PROC(_hw_vmm, OID_AUTO, destroy,
829b9ef152bSMark Johnston     CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_PRISON | CTLFLAG_MPSAFE,
830b9ef152bSMark Johnston     NULL, 0, sysctl_vmm_destroy, "A",
831b9ef152bSMark Johnston     NULL);
832b9ef152bSMark Johnston 
833b9ef152bSMark Johnston static struct cdevsw vmmdevsw = {
834b9ef152bSMark Johnston 	.d_name		= "vmmdev",
835b9ef152bSMark Johnston 	.d_version	= D_VERSION,
83640087581SMark Johnston 	.d_open		= vmmdev_open,
837b9ef152bSMark Johnston 	.d_ioctl	= vmmdev_ioctl,
838b9ef152bSMark Johnston 	.d_mmap_single	= vmmdev_mmap_single,
839b9ef152bSMark Johnston 	.d_read		= vmmdev_rw,
840b9ef152bSMark Johnston 	.d_write	= vmmdev_rw,
841b9ef152bSMark Johnston };
842b9ef152bSMark Johnston 
843d5819709SMark Johnston static struct vmmdev_softc *
844d5819709SMark Johnston vmmdev_alloc(struct vm *vm, struct ucred *cred)
845b9ef152bSMark Johnston {
846d5819709SMark Johnston 	struct vmmdev_softc *sc;
847b9ef152bSMark Johnston 
848d5819709SMark Johnston 	sc = malloc(sizeof(*sc), M_VMMDEV, M_WAITOK | M_ZERO);
849d5819709SMark Johnston 	SLIST_INIT(&sc->devmem);
850d5819709SMark Johnston 	sc->vm = vm;
851d5819709SMark Johnston 	sc->ucred = crhold(cred);
852d5819709SMark Johnston 	return (sc);
853b9ef152bSMark Johnston }
854b9ef152bSMark Johnston 
855d5819709SMark Johnston static int
856d5819709SMark Johnston vmmdev_create(const char *name, struct ucred *cred)
857d5819709SMark Johnston {
858cef5f43fSMark Johnston 	struct make_dev_args mda;
859d5819709SMark Johnston 	struct cdev *cdev;
860cef5f43fSMark Johnston 	struct vmmdev_softc *sc;
861d5819709SMark Johnston 	struct vm *vm;
862d5819709SMark Johnston 	int error;
863b9ef152bSMark Johnston 
864887c0877SMark Johnston 	sx_xlock(&vmmdev_mtx);
865c23da668SMark Johnston 	sc = vmmdev_lookup(name, cred);
866cef5f43fSMark Johnston 	if (sc != NULL) {
867887c0877SMark Johnston 		sx_xunlock(&vmmdev_mtx);
868d5819709SMark Johnston 		return (EEXIST);
869cef5f43fSMark Johnston 	}
870d5819709SMark Johnston 
871d5819709SMark Johnston 	error = vm_create(name, &vm);
872b9ef152bSMark Johnston 	if (error != 0) {
873cef5f43fSMark Johnston 		sx_xunlock(&vmmdev_mtx);
874cef5f43fSMark Johnston 		return (error);
875cef5f43fSMark Johnston 	}
876cef5f43fSMark Johnston 	sc = vmmdev_alloc(vm, cred);
877cef5f43fSMark Johnston 	SLIST_INSERT_HEAD(&head, sc, link);
878cef5f43fSMark Johnston 
879cef5f43fSMark Johnston 	make_dev_args_init(&mda);
880cef5f43fSMark Johnston 	mda.mda_devsw = &vmmdevsw;
881cef5f43fSMark Johnston 	mda.mda_cr = sc->ucred;
882cef5f43fSMark Johnston 	mda.mda_uid = UID_ROOT;
883cef5f43fSMark Johnston 	mda.mda_gid = GID_WHEEL;
884cef5f43fSMark Johnston 	mda.mda_mode = 0600;
885cef5f43fSMark Johnston 	mda.mda_si_drv1 = sc;
886cef5f43fSMark Johnston 	mda.mda_flags = MAKEDEV_CHECKNAME | MAKEDEV_WAITOK;
887cef5f43fSMark Johnston 	error = make_dev_s(&mda, &cdev, "vmm/%s", name);
888cef5f43fSMark Johnston 	if (error != 0) {
889cef5f43fSMark Johnston 		sx_xunlock(&vmmdev_mtx);
890b9ef152bSMark Johnston 		vmmdev_destroy(sc);
891d5819709SMark Johnston 		return (error);
892b9ef152bSMark Johnston 	}
893b9ef152bSMark Johnston 	sc->cdev = cdev;
894887c0877SMark Johnston 	sx_xunlock(&vmmdev_mtx);
895d5819709SMark Johnston 	return (0);
896d5819709SMark Johnston }
897d5819709SMark Johnston 
898d5819709SMark Johnston static int
899d5819709SMark Johnston sysctl_vmm_create(SYSCTL_HANDLER_ARGS)
900d5819709SMark Johnston {
901d5819709SMark Johnston 	char *buf;
902d5819709SMark Johnston 	int error, buflen;
903d5819709SMark Johnston 
904d5819709SMark Johnston 	error = vmm_priv_check(req->td->td_ucred);
905d5819709SMark Johnston 	if (error != 0)
906d5819709SMark Johnston 		return (error);
907d5819709SMark Johnston 
908d5819709SMark Johnston 	buflen = VM_MAX_NAMELEN + 1;
909d5819709SMark Johnston 	buf = malloc(buflen, M_VMMDEV, M_WAITOK | M_ZERO);
910d5819709SMark Johnston 	strlcpy(buf, "beavis", buflen);
911d5819709SMark Johnston 	error = sysctl_handle_string(oidp, buf, buflen, req);
912d5819709SMark Johnston 	if (error == 0 && req->newptr != NULL)
913d5819709SMark Johnston 		error = vmmdev_create(buf, req->td->td_ucred);
914b9ef152bSMark Johnston 	free(buf, M_VMMDEV);
915b9ef152bSMark Johnston 	return (error);
916b9ef152bSMark Johnston }
917b9ef152bSMark Johnston SYSCTL_PROC(_hw_vmm, OID_AUTO, create,
918b9ef152bSMark Johnston     CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_PRISON | CTLFLAG_MPSAFE,
919b9ef152bSMark Johnston     NULL, 0, sysctl_vmm_create, "A",
920b9ef152bSMark Johnston     NULL);
921b9ef152bSMark Johnston 
922a97f683fSMark Johnston static int
923a97f683fSMark Johnston vmmctl_open(struct cdev *cdev, int flags, int fmt, struct thread *td)
924a97f683fSMark Johnston {
925a97f683fSMark Johnston 	int error;
926a97f683fSMark Johnston 
927a97f683fSMark Johnston 	error = vmm_priv_check(td->td_ucred);
928a97f683fSMark Johnston 	if (error != 0)
929a97f683fSMark Johnston 		return (error);
930a97f683fSMark Johnston 
931a97f683fSMark Johnston 	if ((flags & FWRITE) == 0)
932a97f683fSMark Johnston 		return (EPERM);
933a97f683fSMark Johnston 
934a97f683fSMark Johnston 	return (0);
935a97f683fSMark Johnston }
936a97f683fSMark Johnston 
937a97f683fSMark Johnston static int
938a97f683fSMark Johnston vmmctl_ioctl(struct cdev *cdev, u_long cmd, caddr_t data, int fflag,
939a97f683fSMark Johnston     struct thread *td)
940a97f683fSMark Johnston {
941a97f683fSMark Johnston 	int error;
942a97f683fSMark Johnston 
943a97f683fSMark Johnston 	switch (cmd) {
944a97f683fSMark Johnston 	case VMMCTL_VM_CREATE: {
945a97f683fSMark Johnston 		struct vmmctl_vm_create *vmc;
946a97f683fSMark Johnston 
947a97f683fSMark Johnston 		vmc = (struct vmmctl_vm_create *)data;
948a97f683fSMark Johnston 		vmc->name[VM_MAX_NAMELEN] = '\0';
949a97f683fSMark Johnston 		for (size_t i = 0; i < nitems(vmc->reserved); i++) {
950a97f683fSMark Johnston 			if (vmc->reserved[i] != 0) {
951a97f683fSMark Johnston 				error = EINVAL;
952a97f683fSMark Johnston 				return (error);
953a97f683fSMark Johnston 			}
954a97f683fSMark Johnston 		}
955a97f683fSMark Johnston 
956a97f683fSMark Johnston 		error = vmmdev_create(vmc->name, td->td_ucred);
957a97f683fSMark Johnston 		break;
958a97f683fSMark Johnston 	}
959a97f683fSMark Johnston 	case VMMCTL_VM_DESTROY: {
960a97f683fSMark Johnston 		struct vmmctl_vm_destroy *vmd;
961a97f683fSMark Johnston 
962a97f683fSMark Johnston 		vmd = (struct vmmctl_vm_destroy *)data;
963a97f683fSMark Johnston 		vmd->name[VM_MAX_NAMELEN] = '\0';
964a97f683fSMark Johnston 		for (size_t i = 0; i < nitems(vmd->reserved); i++) {
965a97f683fSMark Johnston 			if (vmd->reserved[i] != 0) {
966a97f683fSMark Johnston 				error = EINVAL;
967a97f683fSMark Johnston 				return (error);
968a97f683fSMark Johnston 			}
969a97f683fSMark Johnston 		}
970a97f683fSMark Johnston 
971a97f683fSMark Johnston 		error = vmmdev_lookup_and_destroy(vmd->name, td->td_ucred);
972a97f683fSMark Johnston 		break;
973a97f683fSMark Johnston 	}
974a97f683fSMark Johnston 	default:
975a97f683fSMark Johnston 		error = ENOTTY;
976a97f683fSMark Johnston 		break;
977a97f683fSMark Johnston 	}
978a97f683fSMark Johnston 
979a97f683fSMark Johnston 	return (error);
980a97f683fSMark Johnston }
981a97f683fSMark Johnston 
982*4a46ece6SMark Johnston static struct cdev *vmmctl_cdev;
983a97f683fSMark Johnston static struct cdevsw vmmctlsw = {
984a97f683fSMark Johnston 	.d_name		= "vmmctl",
985a97f683fSMark Johnston 	.d_version	= D_VERSION,
986a97f683fSMark Johnston 	.d_open		= vmmctl_open,
987a97f683fSMark Johnston 	.d_ioctl	= vmmctl_ioctl,
988a97f683fSMark Johnston };
989a97f683fSMark Johnston 
990a97f683fSMark Johnston int
991b9ef152bSMark Johnston vmmdev_init(void)
992b9ef152bSMark Johnston {
993a97f683fSMark Johnston 	int error;
994a97f683fSMark Johnston 
995*4a46ece6SMark Johnston 	sx_xlock(&vmmdev_mtx);
996*4a46ece6SMark Johnston 	error = make_dev_p(MAKEDEV_CHECKNAME, &vmmctl_cdev, &vmmctlsw, NULL,
997a97f683fSMark Johnston 	    UID_ROOT, GID_WHEEL, 0600, "vmmctl");
998*4a46ece6SMark Johnston 	if (error == 0)
999b9ef152bSMark Johnston 		pr_allow_flag = prison_add_allow(NULL, "vmm", NULL,
1000b9ef152bSMark Johnston 		    "Allow use of vmm in a jail.");
1001*4a46ece6SMark Johnston 	sx_xunlock(&vmmdev_mtx);
1002a97f683fSMark Johnston 
1003*4a46ece6SMark Johnston 	return (error);
1004b9ef152bSMark Johnston }
1005b9ef152bSMark Johnston 
1006b9ef152bSMark Johnston int
1007b9ef152bSMark Johnston vmmdev_cleanup(void)
1008b9ef152bSMark Johnston {
1009*4a46ece6SMark Johnston 	sx_xlock(&vmmdev_mtx);
1010*4a46ece6SMark Johnston 	if (!SLIST_EMPTY(&head)) {
1011*4a46ece6SMark Johnston 		sx_xunlock(&vmmdev_mtx);
1012*4a46ece6SMark Johnston 		return (EBUSY);
1013*4a46ece6SMark Johnston 	}
1014*4a46ece6SMark Johnston 	if (vmmctl_cdev != NULL) {
1015*4a46ece6SMark Johnston 		destroy_dev(vmmctl_cdev);
1016*4a46ece6SMark Johnston 		vmmctl_cdev = NULL;
1017*4a46ece6SMark Johnston 	}
1018*4a46ece6SMark Johnston 	sx_xunlock(&vmmdev_mtx);
1019b9ef152bSMark Johnston 
1020*4a46ece6SMark Johnston 	return (0);
1021b9ef152bSMark Johnston }
1022b9ef152bSMark Johnston 
1023b9ef152bSMark Johnston static int
1024b9ef152bSMark Johnston devmem_mmap_single(struct cdev *cdev, vm_ooffset_t *offset, vm_size_t len,
1025b9ef152bSMark Johnston     struct vm_object **objp, int nprot)
1026b9ef152bSMark Johnston {
1027b9ef152bSMark Johnston 	struct devmem_softc *dsc;
1028b9ef152bSMark Johnston 	vm_ooffset_t first, last;
1029b9ef152bSMark Johnston 	size_t seglen;
1030b9ef152bSMark Johnston 	int error;
1031b9ef152bSMark Johnston 	bool sysmem;
1032b9ef152bSMark Johnston 
1033b9ef152bSMark Johnston 	dsc = cdev->si_drv1;
1034b9ef152bSMark Johnston 	if (dsc == NULL) {
1035b9ef152bSMark Johnston 		/* 'cdev' has been created but is not ready for use */
1036b9ef152bSMark Johnston 		return (ENXIO);
1037b9ef152bSMark Johnston 	}
1038b9ef152bSMark Johnston 
1039b9ef152bSMark Johnston 	first = *offset;
1040b9ef152bSMark Johnston 	last = *offset + len;
1041b9ef152bSMark Johnston 	if ((nprot & PROT_EXEC) || first < 0 || first >= last)
1042b9ef152bSMark Johnston 		return (EINVAL);
1043b9ef152bSMark Johnston 
1044b9ef152bSMark Johnston 	vm_slock_memsegs(dsc->sc->vm);
1045b9ef152bSMark Johnston 
1046b9ef152bSMark Johnston 	error = vm_get_memseg(dsc->sc->vm, dsc->segid, &seglen, &sysmem, objp);
1047b9ef152bSMark Johnston 	KASSERT(error == 0 && !sysmem && *objp != NULL,
1048b9ef152bSMark Johnston 	    ("%s: invalid devmem segment %d", __func__, dsc->segid));
1049b9ef152bSMark Johnston 
1050b9ef152bSMark Johnston 	if (seglen >= last)
1051b9ef152bSMark Johnston 		vm_object_reference(*objp);
1052b9ef152bSMark Johnston 	else
1053b9ef152bSMark Johnston 		error = EINVAL;
1054b9ef152bSMark Johnston 
1055b9ef152bSMark Johnston 	vm_unlock_memsegs(dsc->sc->vm);
1056b9ef152bSMark Johnston 	return (error);
1057b9ef152bSMark Johnston }
1058b9ef152bSMark Johnston 
1059b9ef152bSMark Johnston static struct cdevsw devmemsw = {
1060b9ef152bSMark Johnston 	.d_name		= "devmem",
1061b9ef152bSMark Johnston 	.d_version	= D_VERSION,
1062b9ef152bSMark Johnston 	.d_mmap_single	= devmem_mmap_single,
1063b9ef152bSMark Johnston };
1064b9ef152bSMark Johnston 
1065b9ef152bSMark Johnston static int
1066f4002135SMark Johnston devmem_create_cdev(struct vmmdev_softc *sc, int segid, char *devname)
1067b9ef152bSMark Johnston {
1068cef5f43fSMark Johnston 	struct make_dev_args mda;
1069b9ef152bSMark Johnston 	struct devmem_softc *dsc;
1070b9ef152bSMark Johnston 	int error;
1071b9ef152bSMark Johnston 
1072cef5f43fSMark Johnston 	sx_xlock(&vmmdev_mtx);
1073b9ef152bSMark Johnston 
1074b9ef152bSMark Johnston 	dsc = malloc(sizeof(struct devmem_softc), M_VMMDEV, M_WAITOK | M_ZERO);
1075b9ef152bSMark Johnston 	dsc->segid = segid;
1076b9ef152bSMark Johnston 	dsc->name = devname;
1077b9ef152bSMark Johnston 	dsc->sc = sc;
1078b9ef152bSMark Johnston 	SLIST_INSERT_HEAD(&sc->devmem, dsc, link);
1079cef5f43fSMark Johnston 
1080cef5f43fSMark Johnston 	make_dev_args_init(&mda);
1081cef5f43fSMark Johnston 	mda.mda_devsw = &devmemsw;
1082cef5f43fSMark Johnston 	mda.mda_cr = sc->ucred;
1083cef5f43fSMark Johnston 	mda.mda_uid = UID_ROOT;
1084cef5f43fSMark Johnston 	mda.mda_gid = GID_WHEEL;
1085cef5f43fSMark Johnston 	mda.mda_mode = 0600;
1086cef5f43fSMark Johnston 	mda.mda_si_drv1 = dsc;
1087cef5f43fSMark Johnston 	mda.mda_flags = MAKEDEV_CHECKNAME | MAKEDEV_WAITOK;
1088cef5f43fSMark Johnston 	error = make_dev_s(&mda, &dsc->cdev, "vmm.io/%s.%s", vm_name(sc->vm),
1089cef5f43fSMark Johnston 	    devname);
1090cef5f43fSMark Johnston 	if (error != 0) {
1091cef5f43fSMark Johnston 		SLIST_REMOVE(&sc->devmem, dsc, devmem_softc, link);
1092cef5f43fSMark Johnston 		free(dsc->name, M_VMMDEV);
1093cef5f43fSMark Johnston 		free(dsc, M_VMMDEV);
1094cef5f43fSMark Johnston 	}
1095cef5f43fSMark Johnston 
1096887c0877SMark Johnston 	sx_xunlock(&vmmdev_mtx);
1097b9ef152bSMark Johnston 
1098cef5f43fSMark Johnston 	return (error);
1099b9ef152bSMark Johnston }
1100b9ef152bSMark Johnston 
1101b9ef152bSMark Johnston static void
1102b9ef152bSMark Johnston devmem_destroy(void *arg)
1103b9ef152bSMark Johnston {
1104b9ef152bSMark Johnston 	struct devmem_softc *dsc = arg;
1105b9ef152bSMark Johnston 
1106cef5f43fSMark Johnston 	destroy_dev(dsc->cdev);
1107b9ef152bSMark Johnston 	dsc->cdev = NULL;
1108b9ef152bSMark Johnston 	dsc->sc = NULL;
1109b9ef152bSMark Johnston }
1110