xref: /openbsd-src/sys/dev/vmm/vmm.c (revision ebaf145fc214b20f93a59c0f55728a6db3a03c40)
1*ebaf145fSbluhm /* $OpenBSD: vmm.c,v 1.3 2024/08/27 09:16:03 bluhm Exp $ */
23a0db596Smlarkin /*
33a0db596Smlarkin  * Copyright (c) 2014-2023 Mike Larkin <mlarkin@openbsd.org>
43a0db596Smlarkin  *
53a0db596Smlarkin  * Permission to use, copy, modify, and distribute this software for any
63a0db596Smlarkin  * purpose with or without fee is hereby granted, provided that the above
73a0db596Smlarkin  * copyright notice and this permission notice appear in all copies.
83a0db596Smlarkin  *
93a0db596Smlarkin  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
103a0db596Smlarkin  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
113a0db596Smlarkin  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
123a0db596Smlarkin  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
133a0db596Smlarkin  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
143a0db596Smlarkin  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
153a0db596Smlarkin  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
163a0db596Smlarkin  */
173a0db596Smlarkin 
183a0db596Smlarkin #include <sys/param.h>
193a0db596Smlarkin #include <sys/systm.h>
203a0db596Smlarkin #include <sys/device.h>
213a0db596Smlarkin #include <sys/pool.h>
223a0db596Smlarkin #include <sys/pledge.h>
233a0db596Smlarkin #include <sys/proc.h>
243a0db596Smlarkin #include <sys/ioctl.h>
253a0db596Smlarkin #include <sys/malloc.h>
263a0db596Smlarkin #include <sys/signalvar.h>
273a0db596Smlarkin 
283a0db596Smlarkin #include <machine/vmmvar.h>
293a0db596Smlarkin 
303a0db596Smlarkin #include <dev/vmm/vmm.h>
313a0db596Smlarkin 
323a0db596Smlarkin struct vmm_softc *vmm_softc;
333a0db596Smlarkin struct pool vm_pool;
343a0db596Smlarkin struct pool vcpu_pool;
353a0db596Smlarkin 
363a0db596Smlarkin struct cfdriver vmm_cd = {
373a0db596Smlarkin 	NULL, "vmm", DV_DULL, CD_SKIPHIBERNATE
383a0db596Smlarkin };
393a0db596Smlarkin 
403a0db596Smlarkin const struct cfattach vmm_ca = {
413a0db596Smlarkin 	sizeof(struct vmm_softc), vmm_probe, vmm_attach, NULL, vmm_activate
423a0db596Smlarkin };
433a0db596Smlarkin 
443a0db596Smlarkin int
453a0db596Smlarkin vmm_probe(struct device *parent, void *match, void *aux)
463a0db596Smlarkin {
473a0db596Smlarkin 	const char **busname = (const char **)aux;
483a0db596Smlarkin 
493a0db596Smlarkin 	if (strcmp(*busname, vmm_cd.cd_name) != 0)
503a0db596Smlarkin 		return (0);
513a0db596Smlarkin 	return (1);
523a0db596Smlarkin }
533a0db596Smlarkin 
543a0db596Smlarkin void
553a0db596Smlarkin vmm_attach(struct device *parent, struct device *self, void *aux)
563a0db596Smlarkin {
573a0db596Smlarkin 	struct vmm_softc *sc = (struct vmm_softc *)self;
583a0db596Smlarkin 
593a0db596Smlarkin 	rw_init(&sc->sc_slock, "vmmslk");
603a0db596Smlarkin 	sc->sc_status = VMM_ACTIVE;
613a0db596Smlarkin 	refcnt_init(&sc->sc_refcnt);
623a0db596Smlarkin 
633a0db596Smlarkin 	sc->vcpu_ct = 0;
643a0db596Smlarkin 	sc->vcpu_max = VMM_MAX_VCPUS;
653a0db596Smlarkin 	sc->vm_ct = 0;
663a0db596Smlarkin 	sc->vm_idx = 0;
673a0db596Smlarkin 
683a0db596Smlarkin 	SLIST_INIT(&sc->vm_list);
693a0db596Smlarkin 	rw_init(&sc->vm_lock, "vm_list");
703a0db596Smlarkin 
713a0db596Smlarkin 	pool_init(&vm_pool, sizeof(struct vm), 0, IPL_MPFLOOR, PR_WAITOK,
723a0db596Smlarkin 	    "vmpool", NULL);
733a0db596Smlarkin 	pool_init(&vcpu_pool, sizeof(struct vcpu), 64, IPL_MPFLOOR, PR_WAITOK,
743a0db596Smlarkin 	    "vcpupl", NULL);
753a0db596Smlarkin 
763a0db596Smlarkin 	vmm_attach_machdep(parent, self, aux);
773a0db596Smlarkin 
783a0db596Smlarkin 	vmm_softc = sc;
793a0db596Smlarkin 	printf("\n");
803a0db596Smlarkin }
813a0db596Smlarkin 
823a0db596Smlarkin int
833a0db596Smlarkin vmm_activate(struct device *self, int act)
843a0db596Smlarkin {
853a0db596Smlarkin 	switch (act) {
863a0db596Smlarkin 	case DVACT_QUIESCE:
873a0db596Smlarkin 		/* Block device users as we're suspending operation. */
883a0db596Smlarkin 		rw_enter_write(&vmm_softc->sc_slock);
893a0db596Smlarkin 		KASSERT(vmm_softc->sc_status == VMM_ACTIVE);
903a0db596Smlarkin 		vmm_softc->sc_status = VMM_SUSPENDED;
913a0db596Smlarkin 		rw_exit_write(&vmm_softc->sc_slock);
923a0db596Smlarkin 
933a0db596Smlarkin 		/* Wait for any device users to finish. */
943a0db596Smlarkin 		refcnt_finalize(&vmm_softc->sc_refcnt, "vmmsusp");
953a0db596Smlarkin 
963a0db596Smlarkin 		vmm_activate_machdep(self, act);
973a0db596Smlarkin 		break;
983a0db596Smlarkin 	case DVACT_WAKEUP:
993a0db596Smlarkin 		vmm_activate_machdep(self, act);
1003a0db596Smlarkin 
1013a0db596Smlarkin 		/* Set the device back to active. */
1023a0db596Smlarkin 		rw_enter_write(&vmm_softc->sc_slock);
1033a0db596Smlarkin 		KASSERT(vmm_softc->sc_status == VMM_SUSPENDED);
1043a0db596Smlarkin 		refcnt_init(&vmm_softc->sc_refcnt);
1053a0db596Smlarkin 		vmm_softc->sc_status = VMM_ACTIVE;
1063a0db596Smlarkin 		rw_exit_write(&vmm_softc->sc_slock);
1073a0db596Smlarkin 
1083a0db596Smlarkin 		/* Notify any waiting device users. */
1093a0db596Smlarkin 		wakeup(&vmm_softc->sc_status);
1103a0db596Smlarkin 		break;
1113a0db596Smlarkin 	}
1123a0db596Smlarkin 
1133a0db596Smlarkin 	return (0);
1143a0db596Smlarkin }
1153a0db596Smlarkin 
1163a0db596Smlarkin /*
1173a0db596Smlarkin  * vmmopen
1183a0db596Smlarkin  *
1193a0db596Smlarkin  * Called during open of /dev/vmm.
1203a0db596Smlarkin  *
1213a0db596Smlarkin  * Parameters:
1223a0db596Smlarkin  *  dev, flag, mode, p: These come from the character device and are
1233a0db596Smlarkin  *   all unused for this function
1243a0db596Smlarkin  *
1253a0db596Smlarkin  * Return values:
1263a0db596Smlarkin  *  ENODEV: if vmm(4) didn't attach or no supported CPUs detected
1273a0db596Smlarkin  *  0: successful open
1283a0db596Smlarkin  */
1293a0db596Smlarkin int
1303a0db596Smlarkin vmmopen(dev_t dev, int flag, int mode, struct proc *p)
1313a0db596Smlarkin {
1323a0db596Smlarkin 	/* Don't allow open if we didn't attach */
1333a0db596Smlarkin 	if (vmm_softc == NULL)
1343a0db596Smlarkin 		return (ENODEV);
1353a0db596Smlarkin 
1363a0db596Smlarkin 	/* Don't allow open if we didn't detect any supported CPUs */
1373a0db596Smlarkin 	if (vmm_softc->mode == VMM_MODE_UNKNOWN)
1383a0db596Smlarkin 		return (ENODEV);
1393a0db596Smlarkin 
1403a0db596Smlarkin 	return 0;
1413a0db596Smlarkin }
1423a0db596Smlarkin 
1433a0db596Smlarkin /*
1443a0db596Smlarkin  * vmmclose
1453a0db596Smlarkin  *
1463a0db596Smlarkin  * Called when /dev/vmm is closed. Presently unused.
1473a0db596Smlarkin  */
1483a0db596Smlarkin int
1493a0db596Smlarkin vmmclose(dev_t dev, int flag, int mode, struct proc *p)
1503a0db596Smlarkin {
1513a0db596Smlarkin 	return 0;
1523a0db596Smlarkin }
1533a0db596Smlarkin 
1543a0db596Smlarkin /*
1553a0db596Smlarkin  * vm_find
1563a0db596Smlarkin  *
1573a0db596Smlarkin  * Function to find an existing VM by its identifier.
1583a0db596Smlarkin  * Must be called under the global vm_lock.
1593a0db596Smlarkin  *
1603a0db596Smlarkin  * Parameters:
1613a0db596Smlarkin  *  id: The VM identifier.
1623a0db596Smlarkin  *  *res: A pointer to the VM or NULL if not found
1633a0db596Smlarkin  *
1643a0db596Smlarkin  * Return values:
1653a0db596Smlarkin  *  0: if successful
1663a0db596Smlarkin  *  ENOENT: if the VM defined by 'id' cannot be found
1673a0db596Smlarkin  *  EPERM: if the VM cannot be accessed by the current process
1683a0db596Smlarkin  */
1693a0db596Smlarkin int
1703a0db596Smlarkin vm_find(uint32_t id, struct vm **res)
1713a0db596Smlarkin {
1723a0db596Smlarkin 	struct proc *p = curproc;
1733a0db596Smlarkin 	struct vm *vm;
1743a0db596Smlarkin 	int ret = ENOENT;
1753a0db596Smlarkin 
1763a0db596Smlarkin 	*res = NULL;
1773a0db596Smlarkin 
1783a0db596Smlarkin 	rw_enter_read(&vmm_softc->vm_lock);
1793a0db596Smlarkin 	SLIST_FOREACH(vm, &vmm_softc->vm_list, vm_link) {
1803a0db596Smlarkin 		if (vm->vm_id == id) {
1813a0db596Smlarkin 			/*
1823a0db596Smlarkin 			 * In the pledged VM process, only allow to find
1833a0db596Smlarkin 			 * the VM that is running in the current process.
1843a0db596Smlarkin 			 * The managing vmm parent process can lookup all
1853a0db596Smlarkin 			 * all VMs and is indicated by PLEDGE_PROC.
1863a0db596Smlarkin 			 */
1873a0db596Smlarkin 			if (((p->p_p->ps_pledge &
1883a0db596Smlarkin 			    (PLEDGE_VMM | PLEDGE_PROC)) == PLEDGE_VMM) &&
1893a0db596Smlarkin 			    (vm->vm_creator_pid != p->p_p->ps_pid))
1903a0db596Smlarkin 				ret = EPERM;
1913a0db596Smlarkin 			else {
1923a0db596Smlarkin 				refcnt_take(&vm->vm_refcnt);
1933a0db596Smlarkin 				*res = vm;
1943a0db596Smlarkin 				ret = 0;
1953a0db596Smlarkin 			}
1963a0db596Smlarkin 			break;
1973a0db596Smlarkin 		}
1983a0db596Smlarkin 	}
1993a0db596Smlarkin 	rw_exit_read(&vmm_softc->vm_lock);
2003a0db596Smlarkin 
2013a0db596Smlarkin 	if (ret == EPERM)
2023a0db596Smlarkin 		return (pledge_fail(p, EPERM, PLEDGE_VMM));
2033a0db596Smlarkin 	return (ret);
2043a0db596Smlarkin }
2053a0db596Smlarkin 
2063a0db596Smlarkin /*
2073a0db596Smlarkin  * vmmioctl
2083a0db596Smlarkin  *
2093a0db596Smlarkin  * Main ioctl dispatch routine for /dev/vmm. Parses ioctl type and calls
2103a0db596Smlarkin  * appropriate lower level handler routine. Returns result to ioctl caller.
2113a0db596Smlarkin  */
2123a0db596Smlarkin int
2133a0db596Smlarkin vmmioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
2143a0db596Smlarkin {
2153a0db596Smlarkin 	int ret;
2163a0db596Smlarkin 
2173a0db596Smlarkin 	KERNEL_UNLOCK();
2183a0db596Smlarkin 
2193a0db596Smlarkin 	ret = rw_enter(&vmm_softc->sc_slock, RW_READ | RW_INTR);
2203a0db596Smlarkin 	if (ret != 0)
2213a0db596Smlarkin 		goto out;
2223a0db596Smlarkin 	while (vmm_softc->sc_status != VMM_ACTIVE) {
2233a0db596Smlarkin 		ret = rwsleep_nsec(&vmm_softc->sc_status, &vmm_softc->sc_slock,
2243a0db596Smlarkin 		    PWAIT | PCATCH, "vmmresume", INFSLP);
2253a0db596Smlarkin 		if (ret != 0) {
2263a0db596Smlarkin 			rw_exit(&vmm_softc->sc_slock);
2273a0db596Smlarkin 			goto out;
2283a0db596Smlarkin 		}
2293a0db596Smlarkin 	}
2303a0db596Smlarkin 	refcnt_take(&vmm_softc->sc_refcnt);
2313a0db596Smlarkin 	rw_exit(&vmm_softc->sc_slock);
2323a0db596Smlarkin 
2333a0db596Smlarkin 	switch (cmd) {
2343a0db596Smlarkin 	case VMM_IOC_CREATE:
2353a0db596Smlarkin 		if ((ret = vmm_start()) != 0) {
2363a0db596Smlarkin 			vmm_stop();
2373a0db596Smlarkin 			break;
2383a0db596Smlarkin 		}
2393a0db596Smlarkin 		ret = vm_create((struct vm_create_params *)data, p);
2403a0db596Smlarkin 		break;
2413a0db596Smlarkin 	case VMM_IOC_RUN:
2423a0db596Smlarkin 		ret = vm_run((struct vm_run_params *)data);
2433a0db596Smlarkin 		break;
2443a0db596Smlarkin 	case VMM_IOC_INFO:
2453a0db596Smlarkin 		ret = vm_get_info((struct vm_info_params *)data);
2463a0db596Smlarkin 		break;
2473a0db596Smlarkin 	case VMM_IOC_TERM:
2483a0db596Smlarkin 		ret = vm_terminate((struct vm_terminate_params *)data);
2493a0db596Smlarkin 		break;
2503a0db596Smlarkin 	case VMM_IOC_RESETCPU:
2513a0db596Smlarkin 		ret = vm_resetcpu((struct vm_resetcpu_params *)data);
2523a0db596Smlarkin 		break;
2533a0db596Smlarkin 	case VMM_IOC_READREGS:
2543a0db596Smlarkin 		ret = vm_rwregs((struct vm_rwregs_params *)data, 0);
2553a0db596Smlarkin 		break;
2563a0db596Smlarkin 	case VMM_IOC_WRITEREGS:
2573a0db596Smlarkin 		ret = vm_rwregs((struct vm_rwregs_params *)data, 1);
2583a0db596Smlarkin 		break;
2593a0db596Smlarkin 	case VMM_IOC_READVMPARAMS:
2603a0db596Smlarkin 		ret = vm_rwvmparams((struct vm_rwvmparams_params *)data, 0);
2613a0db596Smlarkin 		break;
2623a0db596Smlarkin 	case VMM_IOC_WRITEVMPARAMS:
2633a0db596Smlarkin 		ret = vm_rwvmparams((struct vm_rwvmparams_params *)data, 1);
2643a0db596Smlarkin 		break;
2653c817da7Sdv 	case VMM_IOC_SHAREMEM:
2663c817da7Sdv 		ret = vm_share_mem((struct vm_sharemem_params *)data, p);
2673c817da7Sdv 		break;
2683a0db596Smlarkin 	default:
2693a0db596Smlarkin 		ret = vmmioctl_machdep(dev, cmd, data, flag, p);
2703a0db596Smlarkin 		break;
2713a0db596Smlarkin 	}
2723a0db596Smlarkin 
2733a0db596Smlarkin 	refcnt_rele_wake(&vmm_softc->sc_refcnt);
2743a0db596Smlarkin out:
2753a0db596Smlarkin 	KERNEL_LOCK();
2763a0db596Smlarkin 
2773a0db596Smlarkin 	return (ret);
2783a0db596Smlarkin }
2793a0db596Smlarkin 
2803a0db596Smlarkin /*
2813a0db596Smlarkin  * pledge_ioctl_vmm
2823a0db596Smlarkin  *
2833a0db596Smlarkin  * Restrict the allowed ioctls in a pledged process context.
2843a0db596Smlarkin  * Is called from pledge_ioctl().
2853a0db596Smlarkin  */
2863a0db596Smlarkin int
2873a0db596Smlarkin pledge_ioctl_vmm(struct proc *p, long com)
2883a0db596Smlarkin {
2893a0db596Smlarkin 	switch (com) {
2903a0db596Smlarkin 	case VMM_IOC_CREATE:
2913a0db596Smlarkin 	case VMM_IOC_INFO:
2923c817da7Sdv 	case VMM_IOC_SHAREMEM:
2933a0db596Smlarkin 		/* The "parent" process in vmd forks and manages VMs */
2943a0db596Smlarkin 		if (p->p_p->ps_pledge & PLEDGE_PROC)
2953a0db596Smlarkin 			return (0);
2963a0db596Smlarkin 		break;
2973a0db596Smlarkin 	case VMM_IOC_TERM:
2983a0db596Smlarkin 		/* XXX VM processes should only terminate themselves */
2993a0db596Smlarkin 	case VMM_IOC_RUN:
3003a0db596Smlarkin 	case VMM_IOC_RESETCPU:
3013a0db596Smlarkin 	case VMM_IOC_READREGS:
3023a0db596Smlarkin 	case VMM_IOC_WRITEREGS:
3033a0db596Smlarkin 	case VMM_IOC_READVMPARAMS:
3043a0db596Smlarkin 	case VMM_IOC_WRITEVMPARAMS:
3053a0db596Smlarkin 		return (0);
3063a0db596Smlarkin 	default:
3073a0db596Smlarkin 		return pledge_ioctl_vmm_machdep(p, com);
3083a0db596Smlarkin 	}
3093a0db596Smlarkin 
3103a0db596Smlarkin 	return (EPERM);
3113a0db596Smlarkin }
3123a0db596Smlarkin 
3133a0db596Smlarkin /*
3143a0db596Smlarkin  * vm_find_vcpu
3153a0db596Smlarkin  *
3163a0db596Smlarkin  * Lookup VMM VCPU by ID number
3173a0db596Smlarkin  *
3183a0db596Smlarkin  * Parameters:
3193a0db596Smlarkin  *  vm: vm structure
3203a0db596Smlarkin  *  id: index id of vcpu
3213a0db596Smlarkin  *
3223a0db596Smlarkin  * Returns pointer to vcpu structure if successful, NULL otherwise
3233a0db596Smlarkin  */
3243a0db596Smlarkin struct vcpu *
3253a0db596Smlarkin vm_find_vcpu(struct vm *vm, uint32_t id)
3263a0db596Smlarkin {
3273a0db596Smlarkin 	struct vcpu *vcpu;
3283a0db596Smlarkin 
3293a0db596Smlarkin 	if (vm == NULL)
3303a0db596Smlarkin 		return (NULL);
3313a0db596Smlarkin 
3323a0db596Smlarkin 	SLIST_FOREACH(vcpu, &vm->vm_vcpu_list, vc_vcpu_link) {
3333a0db596Smlarkin 		if (vcpu->vc_id == id)
3343a0db596Smlarkin 			return (vcpu);
3353a0db596Smlarkin 	}
3363a0db596Smlarkin 
3373a0db596Smlarkin 	return (NULL);
3383a0db596Smlarkin }
3393a0db596Smlarkin 
3403a0db596Smlarkin /*
3413a0db596Smlarkin  * vm_create
3423a0db596Smlarkin  *
3433a0db596Smlarkin  * Creates the in-memory VMM structures for the VM defined by 'vcp'. The
3443a0db596Smlarkin  * parent of this VM shall be the process defined by 'p'.
3453a0db596Smlarkin  * This function does not start the VCPU(s) - see vm_start.
3463a0db596Smlarkin  *
3473a0db596Smlarkin  * Return Values:
3483a0db596Smlarkin  *  0: the create operation was successful
3493a0db596Smlarkin  *  ENOMEM: out of memory
3503a0db596Smlarkin  *  various other errors from vcpu_init/vm_impl_init
3513a0db596Smlarkin  */
3523a0db596Smlarkin int
3533a0db596Smlarkin vm_create(struct vm_create_params *vcp, struct proc *p)
3543a0db596Smlarkin {
3553a0db596Smlarkin 	int i, ret;
3563a0db596Smlarkin 	size_t memsize;
3573a0db596Smlarkin 	struct vm *vm;
3583a0db596Smlarkin 	struct vcpu *vcpu;
3593a0db596Smlarkin 
3603a0db596Smlarkin 	memsize = vm_create_check_mem_ranges(vcp);
3613a0db596Smlarkin 	if (memsize == 0)
3623a0db596Smlarkin 		return (EINVAL);
3633a0db596Smlarkin 
3643a0db596Smlarkin 	/* XXX - support UP only (for now) */
3653a0db596Smlarkin 	if (vcp->vcp_ncpus != 1)
3663a0db596Smlarkin 		return (EINVAL);
3673a0db596Smlarkin 
3683a0db596Smlarkin 	/* Bail early if we're already at vcpu capacity. */
3693a0db596Smlarkin 	rw_enter_read(&vmm_softc->vm_lock);
3703a0db596Smlarkin 	if (vmm_softc->vcpu_ct + vcp->vcp_ncpus > vmm_softc->vcpu_max) {
3713a0db596Smlarkin 		DPRINTF("%s: maximum vcpus (%lu) reached\n", __func__,
3723a0db596Smlarkin 		    vmm_softc->vcpu_max);
3733a0db596Smlarkin 		rw_exit_read(&vmm_softc->vm_lock);
3743a0db596Smlarkin 		return (ENOMEM);
3753a0db596Smlarkin 	}
3763a0db596Smlarkin 	rw_exit_read(&vmm_softc->vm_lock);
3773a0db596Smlarkin 
3783a0db596Smlarkin 	/* Instantiate and configure the new vm. */
3793a0db596Smlarkin 	vm = pool_get(&vm_pool, PR_WAITOK | PR_ZERO);
3803a0db596Smlarkin 
3813a0db596Smlarkin 	vm->vm_creator_pid = p->p_p->ps_pid;
3823a0db596Smlarkin 	vm->vm_nmemranges = vcp->vcp_nmemranges;
3833a0db596Smlarkin 	memcpy(vm->vm_memranges, vcp->vcp_memranges,
3843a0db596Smlarkin 	    vm->vm_nmemranges * sizeof(vm->vm_memranges[0]));
3853a0db596Smlarkin 	vm->vm_memory_size = memsize;
3863a0db596Smlarkin 	strncpy(vm->vm_name, vcp->vcp_name, VMM_MAX_NAME_LEN - 1);
3873a0db596Smlarkin 
3883a0db596Smlarkin 	if (vm_impl_init(vm, p)) {
3893a0db596Smlarkin 		printf("failed to init arch-specific features for vm %p\n", vm);
3903a0db596Smlarkin 		vm_teardown(&vm);
3913a0db596Smlarkin 		return (ENOMEM);
3923a0db596Smlarkin 	}
3933a0db596Smlarkin 
3943a0db596Smlarkin 	vm->vm_vcpu_ct = 0;
3953a0db596Smlarkin 
3963a0db596Smlarkin 	/* Initialize each VCPU defined in 'vcp' */
3973a0db596Smlarkin 	SLIST_INIT(&vm->vm_vcpu_list);
3983a0db596Smlarkin 	for (i = 0; i < vcp->vcp_ncpus; i++) {
3993a0db596Smlarkin 		vcpu = pool_get(&vcpu_pool, PR_WAITOK | PR_ZERO);
4003a0db596Smlarkin 
4013a0db596Smlarkin 		vcpu->vc_parent = vm;
402*ebaf145fSbluhm 		vcpu->vc_id = vm->vm_vcpu_ct;
403*ebaf145fSbluhm 		vm->vm_vcpu_ct++;
404*ebaf145fSbluhm 		if ((ret = vcpu_init(vcpu, vcp)) != 0) {
4053a0db596Smlarkin 			printf("failed to init vcpu %d for vm %p\n", i, vm);
4063a0db596Smlarkin 			vm_teardown(&vm);
4073a0db596Smlarkin 			return (ret);
4083a0db596Smlarkin 		}
4093a0db596Smlarkin 		/* Publish vcpu to list, inheriting the reference. */
4103a0db596Smlarkin 		SLIST_INSERT_HEAD(&vm->vm_vcpu_list, vcpu, vc_vcpu_link);
4113a0db596Smlarkin 	}
4123a0db596Smlarkin 
4133a0db596Smlarkin 	/* Attempt to register the vm now that it's configured. */
4143a0db596Smlarkin 	rw_enter_write(&vmm_softc->vm_lock);
4153a0db596Smlarkin 
4163a0db596Smlarkin 	if (vmm_softc->vcpu_ct + vm->vm_vcpu_ct > vmm_softc->vcpu_max) {
4173a0db596Smlarkin 		/* Someone already took our capacity. */
4183a0db596Smlarkin 		printf("%s: maximum vcpus (%lu) reached\n", __func__,
4193a0db596Smlarkin 		    vmm_softc->vcpu_max);
4203a0db596Smlarkin 		rw_exit_write(&vmm_softc->vm_lock);
4213a0db596Smlarkin 		vm_teardown(&vm);
4223a0db596Smlarkin 		return (ENOMEM);
4233a0db596Smlarkin 	}
4243a0db596Smlarkin 
4253a0db596Smlarkin 	/* Update the global index and identify the vm. */
4263a0db596Smlarkin 	vmm_softc->vm_idx++;
4273a0db596Smlarkin 	vm->vm_id = vmm_softc->vm_idx;
4283a0db596Smlarkin 	vcp->vcp_id = vm->vm_id;
4293a0db596Smlarkin 
4303a0db596Smlarkin 	/* Publish the vm into the list and update counts. */
4313a0db596Smlarkin 	refcnt_init(&vm->vm_refcnt);
4323a0db596Smlarkin 	SLIST_INSERT_HEAD(&vmm_softc->vm_list, vm, vm_link);
4333a0db596Smlarkin 	vmm_softc->vm_ct++;
4343a0db596Smlarkin 	vmm_softc->vcpu_ct += vm->vm_vcpu_ct;
4353a0db596Smlarkin 
4363a0db596Smlarkin 	rw_exit_write(&vmm_softc->vm_lock);
4373a0db596Smlarkin 
4383a0db596Smlarkin 	return (0);
4393a0db596Smlarkin }
4403a0db596Smlarkin 
4413a0db596Smlarkin /*
4423a0db596Smlarkin  * vm_create_check_mem_ranges
4433a0db596Smlarkin  *
4443a0db596Smlarkin  * Make sure that the guest physical memory ranges given by the user process
4453a0db596Smlarkin  * do not overlap and are in ascending order.
4463a0db596Smlarkin  *
4473a0db596Smlarkin  * The last physical address may not exceed VMM_MAX_VM_MEM_SIZE.
4483a0db596Smlarkin  *
4493a0db596Smlarkin  * Return Values:
4503a0db596Smlarkin  *   The total memory size in bytes if the checks were successful
4513a0db596Smlarkin  *   0: One of the memory ranges was invalid or VMM_MAX_VM_MEM_SIZE was
4523a0db596Smlarkin  *   exceeded
4533a0db596Smlarkin  */
4543a0db596Smlarkin size_t
4553a0db596Smlarkin vm_create_check_mem_ranges(struct vm_create_params *vcp)
4563a0db596Smlarkin {
4573a0db596Smlarkin 	size_t i, memsize = 0;
4583a0db596Smlarkin 	struct vm_mem_range *vmr, *pvmr;
4593a0db596Smlarkin 	const paddr_t maxgpa = VMM_MAX_VM_MEM_SIZE;
4603a0db596Smlarkin 
4613a0db596Smlarkin 	if (vcp->vcp_nmemranges == 0 ||
4623a0db596Smlarkin 	    vcp->vcp_nmemranges > VMM_MAX_MEM_RANGES) {
4633a0db596Smlarkin 		DPRINTF("invalid number of guest memory ranges\n");
4643a0db596Smlarkin 		return (0);
4653a0db596Smlarkin 	}
4663a0db596Smlarkin 
4673a0db596Smlarkin 	for (i = 0; i < vcp->vcp_nmemranges; i++) {
4683a0db596Smlarkin 		vmr = &vcp->vcp_memranges[i];
4693a0db596Smlarkin 
4703a0db596Smlarkin 		/* Only page-aligned addresses and sizes are permitted */
4713a0db596Smlarkin 		if ((vmr->vmr_gpa & PAGE_MASK) || (vmr->vmr_va & PAGE_MASK) ||
4723a0db596Smlarkin 		    (vmr->vmr_size & PAGE_MASK) || vmr->vmr_size == 0) {
4733a0db596Smlarkin 			DPRINTF("memory range %zu is not page aligned\n", i);
4743a0db596Smlarkin 			return (0);
4753a0db596Smlarkin 		}
4763a0db596Smlarkin 
4773a0db596Smlarkin 		/* Make sure that VMM_MAX_VM_MEM_SIZE is not exceeded */
4783a0db596Smlarkin 		if (vmr->vmr_gpa >= maxgpa ||
4793a0db596Smlarkin 		    vmr->vmr_size > maxgpa - vmr->vmr_gpa) {
4803a0db596Smlarkin 			DPRINTF("exceeded max memory size\n");
4813a0db596Smlarkin 			return (0);
4823a0db596Smlarkin 		}
4833a0db596Smlarkin 
4843a0db596Smlarkin 		/*
4853a0db596Smlarkin 		 * Make sure that all virtual addresses are within the address
4863a0db596Smlarkin 		 * space of the process and that they do not wrap around.
4873a0db596Smlarkin 		 * Calling uvm_share() when creating the VM will take care of
4883a0db596Smlarkin 		 * further checks.
4893a0db596Smlarkin 		 */
4903a0db596Smlarkin 		if (vmr->vmr_va < VM_MIN_ADDRESS ||
4913a0db596Smlarkin 		    vmr->vmr_va >= VM_MAXUSER_ADDRESS ||
4923a0db596Smlarkin 		    vmr->vmr_size >= VM_MAXUSER_ADDRESS - vmr->vmr_va) {
4933a0db596Smlarkin 			DPRINTF("guest va not within range or wraps\n");
4943a0db596Smlarkin 			return (0);
4953a0db596Smlarkin 		}
4963a0db596Smlarkin 
4973a0db596Smlarkin 		/*
4983a0db596Smlarkin 		 * Make sure that guest physical memory ranges do not overlap
4993a0db596Smlarkin 		 * and that they are ascending.
5003a0db596Smlarkin 		 */
5013a0db596Smlarkin 		if (i > 0 && pvmr->vmr_gpa + pvmr->vmr_size > vmr->vmr_gpa) {
5023a0db596Smlarkin 			DPRINTF("guest range %zu overlaps or !ascending\n", i);
5033a0db596Smlarkin 			return (0);
5043a0db596Smlarkin 		}
5053a0db596Smlarkin 
5063a0db596Smlarkin 		/*
5073a0db596Smlarkin 		 * No memory is mappable in MMIO ranges, so don't count towards
5083a0db596Smlarkin 		 * the total guest memory size.
5093a0db596Smlarkin 		 */
5103a0db596Smlarkin 		if (vmr->vmr_type != VM_MEM_MMIO)
5113a0db596Smlarkin 			memsize += vmr->vmr_size;
5123a0db596Smlarkin 		pvmr = vmr;
5133a0db596Smlarkin 	}
5143a0db596Smlarkin 
5153a0db596Smlarkin 	return (memsize);
5163a0db596Smlarkin }
5173a0db596Smlarkin 
5183a0db596Smlarkin /*
5193a0db596Smlarkin  * vm_teardown
5203a0db596Smlarkin  *
5213a0db596Smlarkin  * Tears down (destroys) the vm indicated by 'vm'.
5223a0db596Smlarkin  *
5233a0db596Smlarkin  * Assumes the vm is already removed from the global vm list (or was never
5243a0db596Smlarkin  * added).
5253a0db596Smlarkin  *
5263a0db596Smlarkin  * Parameters:
5273a0db596Smlarkin  *  vm: vm to be torn down
5283a0db596Smlarkin  */
5293a0db596Smlarkin void
5303a0db596Smlarkin vm_teardown(struct vm **target)
5313a0db596Smlarkin {
5323a0db596Smlarkin 	size_t nvcpu = 0;
5333a0db596Smlarkin 	struct vcpu *vcpu, *tmp;
5343a0db596Smlarkin 	struct vm *vm = *target;
5353a0db596Smlarkin 	struct vmspace *vm_vmspace;
5363a0db596Smlarkin 
5373a0db596Smlarkin 	KERNEL_ASSERT_UNLOCKED();
5383a0db596Smlarkin 
5393a0db596Smlarkin 	/* Free VCPUs */
5403a0db596Smlarkin 	SLIST_FOREACH_SAFE(vcpu, &vm->vm_vcpu_list, vc_vcpu_link, tmp) {
5413a0db596Smlarkin 		SLIST_REMOVE(&vm->vm_vcpu_list, vcpu, vcpu, vc_vcpu_link);
5423a0db596Smlarkin 		vcpu_deinit(vcpu);
5433a0db596Smlarkin 
5443a0db596Smlarkin 		pool_put(&vcpu_pool, vcpu);
5453a0db596Smlarkin 		nvcpu++;
5463a0db596Smlarkin 	}
5473a0db596Smlarkin 
5483a0db596Smlarkin 	vm_impl_deinit(vm);
5493a0db596Smlarkin 
5503a0db596Smlarkin 	/* teardown guest vmspace */
5513a0db596Smlarkin 	KERNEL_LOCK();
5523a0db596Smlarkin 	vm_vmspace = vm->vm_vmspace;
5533a0db596Smlarkin 	if (vm_vmspace != NULL) {
5543a0db596Smlarkin 		vm->vm_vmspace = NULL;
5553a0db596Smlarkin 		uvmspace_free(vm_vmspace);
5563a0db596Smlarkin 	}
5573a0db596Smlarkin 	KERNEL_UNLOCK();
5583a0db596Smlarkin 
5593a0db596Smlarkin 	pool_put(&vm_pool, vm);
5603a0db596Smlarkin 	*target = NULL;
5613a0db596Smlarkin }
5623a0db596Smlarkin 
5633a0db596Smlarkin /*
5643a0db596Smlarkin  * vm_get_info
5653a0db596Smlarkin  *
5663a0db596Smlarkin  * Returns information about the VM indicated by 'vip'. The 'vip_size' field
5673a0db596Smlarkin  * in the 'vip' parameter is used to indicate the size of the caller's buffer.
5683a0db596Smlarkin  * If insufficient space exists in that buffer, the required size needed is
5693a0db596Smlarkin  * returned in vip_size and the number of VM information structures returned
5703a0db596Smlarkin  * in vip_info_count is set to 0. The caller should then try the ioctl again
5713a0db596Smlarkin  * after allocating a sufficiently large buffer.
5723a0db596Smlarkin  *
5733a0db596Smlarkin  * Parameters:
5743a0db596Smlarkin  *  vip: information structure identifying the VM to query
5753a0db596Smlarkin  *
5763a0db596Smlarkin  * Return values:
5773a0db596Smlarkin  *  0: the operation succeeded
5783a0db596Smlarkin  *  ENOMEM: memory allocation error during processing
5793a0db596Smlarkin  *  EFAULT: error copying data to user process
5803a0db596Smlarkin  */
5813a0db596Smlarkin int
5823a0db596Smlarkin vm_get_info(struct vm_info_params *vip)
5833a0db596Smlarkin {
5843a0db596Smlarkin 	struct vm_info_result *out;
5853a0db596Smlarkin 	struct vm *vm;
5863a0db596Smlarkin 	struct vcpu *vcpu;
5873a0db596Smlarkin 	int i = 0, j;
5883a0db596Smlarkin 	size_t need, vm_ct;
5893a0db596Smlarkin 
5903a0db596Smlarkin 	rw_enter_read(&vmm_softc->vm_lock);
5913a0db596Smlarkin 	vm_ct = vmm_softc->vm_ct;
5923a0db596Smlarkin 	rw_exit_read(&vmm_softc->vm_lock);
5933a0db596Smlarkin 
5943a0db596Smlarkin 	need = vm_ct * sizeof(struct vm_info_result);
5953a0db596Smlarkin 	if (vip->vip_size < need) {
5963a0db596Smlarkin 		vip->vip_info_ct = 0;
5973a0db596Smlarkin 		vip->vip_size = need;
5983a0db596Smlarkin 		return (0);
5993a0db596Smlarkin 	}
6003a0db596Smlarkin 
6013a0db596Smlarkin 	out = malloc(need, M_DEVBUF, M_NOWAIT|M_ZERO);
6023a0db596Smlarkin 	if (out == NULL) {
6033a0db596Smlarkin 		vip->vip_info_ct = 0;
6043a0db596Smlarkin 		return (ENOMEM);
6053a0db596Smlarkin 	}
6063a0db596Smlarkin 
6073a0db596Smlarkin 	vip->vip_info_ct = vm_ct;
6083a0db596Smlarkin 
6093a0db596Smlarkin 	rw_enter_read(&vmm_softc->vm_lock);
6103a0db596Smlarkin 	SLIST_FOREACH(vm, &vmm_softc->vm_list, vm_link) {
6113a0db596Smlarkin 		refcnt_take(&vm->vm_refcnt);
6123a0db596Smlarkin 
6133a0db596Smlarkin 		out[i].vir_memory_size = vm->vm_memory_size;
6143a0db596Smlarkin 		out[i].vir_used_size =
6153a0db596Smlarkin 		    pmap_resident_count(vm->vm_map->pmap) * PAGE_SIZE;
6163a0db596Smlarkin 		out[i].vir_ncpus = vm->vm_vcpu_ct;
6173a0db596Smlarkin 		out[i].vir_id = vm->vm_id;
6183a0db596Smlarkin 		out[i].vir_creator_pid = vm->vm_creator_pid;
6193a0db596Smlarkin 		strlcpy(out[i].vir_name, vm->vm_name, VMM_MAX_NAME_LEN);
6203a0db596Smlarkin 
6213a0db596Smlarkin 		for (j = 0; j < vm->vm_vcpu_ct; j++) {
6223a0db596Smlarkin 			out[i].vir_vcpu_state[j] = VCPU_STATE_UNKNOWN;
6233a0db596Smlarkin 			SLIST_FOREACH(vcpu, &vm->vm_vcpu_list,
6243a0db596Smlarkin 			    vc_vcpu_link) {
6253a0db596Smlarkin 				if (vcpu->vc_id == j)
6263a0db596Smlarkin 					out[i].vir_vcpu_state[j] =
6273a0db596Smlarkin 					    vcpu->vc_state;
6283a0db596Smlarkin 			}
6293a0db596Smlarkin 		}
6303a0db596Smlarkin 
6313a0db596Smlarkin 		refcnt_rele_wake(&vm->vm_refcnt);
6323a0db596Smlarkin 		i++;
6333a0db596Smlarkin 		if (i == vm_ct)
6343a0db596Smlarkin 			break;	/* Truncate to keep within bounds of 'out'. */
6353a0db596Smlarkin 	}
6363a0db596Smlarkin 	rw_exit_read(&vmm_softc->vm_lock);
6373a0db596Smlarkin 
6383a0db596Smlarkin 	if (copyout(out, vip->vip_info, need) == EFAULT) {
6393a0db596Smlarkin 		free(out, M_DEVBUF, need);
6403a0db596Smlarkin 		return (EFAULT);
6413a0db596Smlarkin 	}
6423a0db596Smlarkin 
6433a0db596Smlarkin 	free(out, M_DEVBUF, need);
6443a0db596Smlarkin 	return (0);
6453a0db596Smlarkin }
6463a0db596Smlarkin 
6473a0db596Smlarkin /*
6483a0db596Smlarkin  * vm_terminate
6493a0db596Smlarkin  *
6503a0db596Smlarkin  * Terminates the VM indicated by 'vtp'.
6513a0db596Smlarkin  *
6523a0db596Smlarkin  * Parameters:
6533a0db596Smlarkin  *  vtp: structure defining the VM to terminate
6543a0db596Smlarkin  *
6553a0db596Smlarkin  * Return values:
6563a0db596Smlarkin  *  0: the VM was terminated
6573a0db596Smlarkin  *  !0: the VM could not be located
6583a0db596Smlarkin  */
6593a0db596Smlarkin int
6603a0db596Smlarkin vm_terminate(struct vm_terminate_params *vtp)
6613a0db596Smlarkin {
6623a0db596Smlarkin 	struct vm *vm;
6633a0db596Smlarkin 	int error, nvcpu, vm_id;
6643a0db596Smlarkin 
6653a0db596Smlarkin 	/*
6663a0db596Smlarkin 	 * Find desired VM
6673a0db596Smlarkin 	 */
6683a0db596Smlarkin 	error = vm_find(vtp->vtp_vm_id, &vm);
6693a0db596Smlarkin 	if (error)
6703a0db596Smlarkin 		return (error);
6713a0db596Smlarkin 
6723a0db596Smlarkin 	/* Pop the vm out of the global vm list. */
6733a0db596Smlarkin 	rw_enter_write(&vmm_softc->vm_lock);
6743a0db596Smlarkin 	SLIST_REMOVE(&vmm_softc->vm_list, vm, vm, vm_link);
6753a0db596Smlarkin 	rw_exit_write(&vmm_softc->vm_lock);
6763a0db596Smlarkin 
6773a0db596Smlarkin 	/* Drop the vm_list's reference to the vm. */
6783a0db596Smlarkin 	if (refcnt_rele(&vm->vm_refcnt))
6793a0db596Smlarkin 		panic("%s: vm %d(%p) vm_list refcnt drop was the last",
6803a0db596Smlarkin 		    __func__, vm->vm_id, vm);
6813a0db596Smlarkin 
6823a0db596Smlarkin 	/* Wait for our reference (taken from vm_find) is the last active. */
6833a0db596Smlarkin 	refcnt_finalize(&vm->vm_refcnt, __func__);
6843a0db596Smlarkin 
6853a0db596Smlarkin 	vm_id = vm->vm_id;
6863a0db596Smlarkin 	nvcpu = vm->vm_vcpu_ct;
6873a0db596Smlarkin 
6883a0db596Smlarkin 	vm_teardown(&vm);
6893a0db596Smlarkin 
6903a0db596Smlarkin 	if (vm_id > 0) {
6913a0db596Smlarkin 		rw_enter_write(&vmm_softc->vm_lock);
6923a0db596Smlarkin 		vmm_softc->vm_ct--;
6933a0db596Smlarkin 		vmm_softc->vcpu_ct -= nvcpu;
6943a0db596Smlarkin 		if (vmm_softc->vm_ct < 1)
6953a0db596Smlarkin 			vmm_stop();
6963a0db596Smlarkin 		rw_exit_write(&vmm_softc->vm_lock);
6973a0db596Smlarkin 	}
6983a0db596Smlarkin 
6993a0db596Smlarkin 	return (0);
7003a0db596Smlarkin }
7013a0db596Smlarkin 
7023a0db596Smlarkin /*
7033a0db596Smlarkin  * vm_resetcpu
7043a0db596Smlarkin  *
7053a0db596Smlarkin  * Resets the vcpu defined in 'vrp' to power-on-init register state
7063a0db596Smlarkin  *
7073a0db596Smlarkin  * Parameters:
7083a0db596Smlarkin  *  vrp: ioctl structure defining the vcpu to reset (see vmmvar.h)
7093a0db596Smlarkin  *
7103a0db596Smlarkin  * Returns 0 if successful, or various error codes on failure:
7113a0db596Smlarkin  *  ENOENT if the VM id contained in 'vrp' refers to an unknown VM or
7123a0db596Smlarkin  *      if vrp describes an unknown vcpu for this VM
7133a0db596Smlarkin  *  EBUSY if the indicated VCPU is not stopped
7143a0db596Smlarkin  *  EIO if the indicated VCPU failed to reset
7153a0db596Smlarkin  */
7163a0db596Smlarkin int
7173a0db596Smlarkin vm_resetcpu(struct vm_resetcpu_params *vrp)
7183a0db596Smlarkin {
7193a0db596Smlarkin 	struct vm *vm;
7203a0db596Smlarkin 	struct vcpu *vcpu;
7213a0db596Smlarkin 	int error, ret = 0;
7223a0db596Smlarkin 
7233a0db596Smlarkin 	/* Find the desired VM */
7243a0db596Smlarkin 	error = vm_find(vrp->vrp_vm_id, &vm);
7253a0db596Smlarkin 
7263a0db596Smlarkin 	/* Not found? exit. */
7273a0db596Smlarkin 	if (error != 0) {
7283a0db596Smlarkin 		DPRINTF("%s: vm id %u not found\n", __func__,
7293a0db596Smlarkin 		    vrp->vrp_vm_id);
7303a0db596Smlarkin 		return (error);
7313a0db596Smlarkin 	}
7323a0db596Smlarkin 
7333a0db596Smlarkin 	vcpu = vm_find_vcpu(vm, vrp->vrp_vcpu_id);
7343a0db596Smlarkin 
7353a0db596Smlarkin 	if (vcpu == NULL) {
7363a0db596Smlarkin 		DPRINTF("%s: vcpu id %u of vm %u not found\n", __func__,
7373a0db596Smlarkin 		    vrp->vrp_vcpu_id, vrp->vrp_vm_id);
7383a0db596Smlarkin 		ret = ENOENT;
7393a0db596Smlarkin 		goto out;
7403a0db596Smlarkin 	}
7413a0db596Smlarkin 
7423a0db596Smlarkin 	rw_enter_write(&vcpu->vc_lock);
7433a0db596Smlarkin 	if (vcpu->vc_state != VCPU_STATE_STOPPED)
7443a0db596Smlarkin 		ret = EBUSY;
7453a0db596Smlarkin 	else {
7463a0db596Smlarkin 		if (vcpu_reset_regs(vcpu, &vrp->vrp_init_state)) {
7473a0db596Smlarkin 			printf("%s: failed\n", __func__);
7483a0db596Smlarkin #ifdef VMM_DEBUG
7493a0db596Smlarkin 			dump_vcpu(vcpu);
7503a0db596Smlarkin #endif /* VMM_DEBUG */
7513a0db596Smlarkin 			ret = EIO;
7523a0db596Smlarkin 		}
7533a0db596Smlarkin 	}
7543a0db596Smlarkin 	rw_exit_write(&vcpu->vc_lock);
7553a0db596Smlarkin out:
7563a0db596Smlarkin 	refcnt_rele_wake(&vm->vm_refcnt);
7573a0db596Smlarkin 
7583a0db596Smlarkin 	return (ret);
7593a0db596Smlarkin }
7603a0db596Smlarkin 
7613a0db596Smlarkin /*
7623a0db596Smlarkin  * vcpu_must_stop
7633a0db596Smlarkin  *
7643a0db596Smlarkin  * Check if we need to (temporarily) stop running the VCPU for some reason,
7653a0db596Smlarkin  * such as:
7663a0db596Smlarkin  * - the VM was requested to terminate
7673a0db596Smlarkin  * - the proc running this VCPU has pending signals
7683a0db596Smlarkin  *
7693a0db596Smlarkin  * Parameters:
7703a0db596Smlarkin  *  vcpu: the VCPU to check
7713a0db596Smlarkin  *
7723a0db596Smlarkin  * Return values:
7733a0db596Smlarkin  *  1: the VM owning this VCPU should stop
7743a0db596Smlarkin  *  0: no stop is needed
7753a0db596Smlarkin  */
7763a0db596Smlarkin int
7773a0db596Smlarkin vcpu_must_stop(struct vcpu *vcpu)
7783a0db596Smlarkin {
7793a0db596Smlarkin 	struct proc *p = curproc;
7803a0db596Smlarkin 
7813a0db596Smlarkin 	if (vcpu->vc_state == VCPU_STATE_REQTERM)
7823a0db596Smlarkin 		return (1);
7833a0db596Smlarkin 	if (SIGPENDING(p) != 0)
7843a0db596Smlarkin 		return (1);
7853a0db596Smlarkin 	return (0);
7863a0db596Smlarkin }
7873c817da7Sdv 
7883c817da7Sdv /*
7893c817da7Sdv  * vm_share_mem
7903c817da7Sdv  *
7913c817da7Sdv  * Share a uvm mapping for the vm guest memory ranges into the calling process.
7923c817da7Sdv  *
7933c817da7Sdv  * Return values:
7943c817da7Sdv  *  0: if successful
7953c817da7Sdv  *  ENOENT: if the vm cannot be found by vm_find
7963c817da7Sdv  *  EPERM: if the vm cannot be accessed by the current process
7973c817da7Sdv  *  EINVAL: if the provide memory ranges fail checks
7983c817da7Sdv  *  ENOMEM: if uvm_share fails to find available memory in the destination map
7993c817da7Sdv  */
8003c817da7Sdv int
8013c817da7Sdv vm_share_mem(struct vm_sharemem_params *vsp, struct proc *p)
8023c817da7Sdv {
8033c817da7Sdv 	int ret = EINVAL;
8043c817da7Sdv 	size_t i, n;
8053c817da7Sdv 	struct vm *vm;
8063c817da7Sdv 	struct vm_mem_range *src, *dst;
8073c817da7Sdv 
8083c817da7Sdv 	ret = vm_find(vsp->vsp_vm_id, &vm);
8093c817da7Sdv 	if (ret)
8103c817da7Sdv 		return (ret);
8113c817da7Sdv 
8123c817da7Sdv 	/* Check we have the expected number of ranges. */
8133c817da7Sdv 	if (vm->vm_nmemranges != vsp->vsp_nmemranges)
8143c817da7Sdv 		goto out;
8153c817da7Sdv 	n = vm->vm_nmemranges;
8163c817da7Sdv 
8173c817da7Sdv 	/* Check their types, sizes, and gpa's (implying page alignment). */
8183c817da7Sdv 	for (i = 0; i < n; i++) {
8193c817da7Sdv 		src = &vm->vm_memranges[i];
8203c817da7Sdv 		dst = &vsp->vsp_memranges[i];
8213c817da7Sdv 
8223c817da7Sdv 		/*
8233c817da7Sdv 		 * The vm memranges were already checked during creation, so
8243c817da7Sdv 		 * compare to them to confirm validity of mapping request.
8253c817da7Sdv 		 */
8263c817da7Sdv 		if (src->vmr_type != dst->vmr_type)
8273c817da7Sdv 			goto out;
8283c817da7Sdv 		if (src->vmr_gpa != dst->vmr_gpa)
8293c817da7Sdv 			goto out;
8303c817da7Sdv 		if (src->vmr_size != dst->vmr_size)
8313c817da7Sdv 			goto out;
8323c817da7Sdv 
8333c817da7Sdv 		/* Check our intended destination is page-aligned. */
8343c817da7Sdv 		if (dst->vmr_va & PAGE_MASK)
8353c817da7Sdv 			goto out;
8363c817da7Sdv 	}
8373c817da7Sdv 
8383c817da7Sdv 	/*
8393c817da7Sdv 	 * Share each range individually with the calling process. We do
8403c817da7Sdv 	 * not need PROC_EXEC as the emulated devices do not need to execute
8413c817da7Sdv 	 * instructions from guest memory.
8423c817da7Sdv 	 */
8433c817da7Sdv 	for (i = 0; i < n; i++) {
8443c817da7Sdv 		src = &vm->vm_memranges[i];
8453c817da7Sdv 		dst = &vsp->vsp_memranges[i];
8463c817da7Sdv 
8473c817da7Sdv 		/* Skip MMIO range. */
8483c817da7Sdv 		if (src->vmr_type == VM_MEM_MMIO)
8493c817da7Sdv 			continue;
8503c817da7Sdv 
8513c817da7Sdv 		DPRINTF("sharing gpa=0x%lx for pid %d @ va=0x%lx\n",
8523c817da7Sdv 		    src->vmr_gpa, p->p_p->ps_pid, dst->vmr_va);
8533c817da7Sdv 		ret = uvm_share(&p->p_vmspace->vm_map, dst->vmr_va,
8543c817da7Sdv 		    PROT_READ | PROT_WRITE, vm->vm_map, src->vmr_gpa,
8553c817da7Sdv 		    src->vmr_size);
8563c817da7Sdv 		if (ret) {
8573c817da7Sdv 			printf("%s: uvm_share failed (%d)\n", __func__, ret);
8583c817da7Sdv 			break;
8593c817da7Sdv 		}
8603c817da7Sdv 	}
8613c817da7Sdv 	ret = 0;
8623c817da7Sdv out:
8633c817da7Sdv 	refcnt_rele_wake(&vm->vm_refcnt);
8643c817da7Sdv 	return (ret);
8653c817da7Sdv }
866