xref: /illumos-gate/usr/src/uts/intel/io/vmm/vmm_sol_dev.c (revision 4bd36be41e0f25c6061bb4934a8c1048dbbd938e)
17c8c0b82SPatrick Mooney /*
27c8c0b82SPatrick Mooney  * This file and its contents are supplied under the terms of the
37c8c0b82SPatrick Mooney  * Common Development and Distribution License ("CDDL"), version 1.0.
47c8c0b82SPatrick Mooney  * You may only use this file in accordance with the terms of version
57c8c0b82SPatrick Mooney  * 1.0 of the CDDL.
67c8c0b82SPatrick Mooney  *
77c8c0b82SPatrick Mooney  * A full copy of the text of the CDDL should have accompanied this
87c8c0b82SPatrick Mooney  * source.  A copy of the CDDL is also available via the Internet at
97c8c0b82SPatrick Mooney  * http://www.illumos.org/license/CDDL.
107c8c0b82SPatrick Mooney  */
117c8c0b82SPatrick Mooney /* This file is dual-licensed; see usr/src/contrib/bhyve/LICENSE */
127c8c0b82SPatrick Mooney 
137c8c0b82SPatrick Mooney /*
147c8c0b82SPatrick Mooney  * Copyright 2015 Pluribus Networks Inc.
157c8c0b82SPatrick Mooney  * Copyright 2019 Joyent, Inc.
167c8c0b82SPatrick Mooney  * Copyright 2020 OmniOS Community Edition (OmniOSce) Association.
176bba8b59SPatrick Mooney  * Copyright 2023 Oxide Computer Company
187c8c0b82SPatrick Mooney  */
197c8c0b82SPatrick Mooney 
207c8c0b82SPatrick Mooney #include <sys/types.h>
217c8c0b82SPatrick Mooney #include <sys/conf.h>
227c8c0b82SPatrick Mooney #include <sys/cpuvar.h>
237c8c0b82SPatrick Mooney #include <sys/ioccom.h>
247c8c0b82SPatrick Mooney #include <sys/stat.h>
257c8c0b82SPatrick Mooney #include <sys/vmsystm.h>
267c8c0b82SPatrick Mooney #include <sys/ddi.h>
277c8c0b82SPatrick Mooney #include <sys/mkdev.h>
287c8c0b82SPatrick Mooney #include <sys/sunddi.h>
297c8c0b82SPatrick Mooney #include <sys/fs/dv_node.h>
307c8c0b82SPatrick Mooney #include <sys/cpuset.h>
317c8c0b82SPatrick Mooney #include <sys/id_space.h>
327c8c0b82SPatrick Mooney #include <sys/fs/sdev_plugin.h>
337c8c0b82SPatrick Mooney #include <sys/smt.h>
347c8c0b82SPatrick Mooney #include <sys/kstat.h>
357c8c0b82SPatrick Mooney 
367c8c0b82SPatrick Mooney #include <sys/kernel.h>
377c8c0b82SPatrick Mooney #include <sys/hma.h>
387c8c0b82SPatrick Mooney #include <sys/x86_archext.h>
397c8c0b82SPatrick Mooney #include <x86/apicreg.h>
407c8c0b82SPatrick Mooney 
417c8c0b82SPatrick Mooney #include <sys/vmm.h>
427c8c0b82SPatrick Mooney #include <sys/vmm_kernel.h>
437c8c0b82SPatrick Mooney #include <sys/vmm_instruction_emul.h>
447c8c0b82SPatrick Mooney #include <sys/vmm_dev.h>
457c8c0b82SPatrick Mooney #include <sys/vmm_impl.h>
467c8c0b82SPatrick Mooney #include <sys/vmm_drv.h>
477c8c0b82SPatrick Mooney #include <sys/vmm_vm.h>
487c8c0b82SPatrick Mooney #include <sys/vmm_reservoir.h>
497c8c0b82SPatrick Mooney 
507c8c0b82SPatrick Mooney #include <vm/seg_dev.h>
517c8c0b82SPatrick Mooney 
527c8c0b82SPatrick Mooney #include "io/ppt.h"
537c8c0b82SPatrick Mooney #include "io/vatpic.h"
547c8c0b82SPatrick Mooney #include "io/vioapic.h"
557c8c0b82SPatrick Mooney #include "io/vrtc.h"
567c8c0b82SPatrick Mooney #include "io/vhpet.h"
577c8c0b82SPatrick Mooney #include "io/vpmtmr.h"
587c8c0b82SPatrick Mooney #include "vmm_lapic.h"
597c8c0b82SPatrick Mooney #include "vmm_stat.h"
607c8c0b82SPatrick Mooney #include "vmm_util.h"
617c8c0b82SPatrick Mooney 
627c8c0b82SPatrick Mooney /*
637c8c0b82SPatrick Mooney  * Locking details:
647c8c0b82SPatrick Mooney  *
657c8c0b82SPatrick Mooney  * Driver-wide data (vmmdev_*) , including HMA and sdev registration, is
667c8c0b82SPatrick Mooney  * protected by vmmdev_mtx.  The list of vmm_softc_t instances and related data
677c8c0b82SPatrick Mooney  * (vmm_*) are protected by vmm_mtx.  Actions requiring both locks must acquire
687c8c0b82SPatrick Mooney  * vmmdev_mtx before vmm_mtx.  The sdev plugin functions must not attempt to
697c8c0b82SPatrick Mooney  * acquire vmmdev_mtx, as they could deadlock with plugin unregistration.
707c8c0b82SPatrick Mooney  */
717c8c0b82SPatrick Mooney 
727c8c0b82SPatrick Mooney static kmutex_t		vmmdev_mtx;
737c8c0b82SPatrick Mooney static dev_info_t	*vmmdev_dip;
747c8c0b82SPatrick Mooney static hma_reg_t	*vmmdev_hma_reg;
757c8c0b82SPatrick Mooney static uint_t		vmmdev_hma_ref;
767c8c0b82SPatrick Mooney static sdev_plugin_hdl_t vmmdev_sdev_hdl;
777c8c0b82SPatrick Mooney 
787c8c0b82SPatrick Mooney static kmutex_t		vmm_mtx;
797c8c0b82SPatrick Mooney static list_t		vmm_list;
807c8c0b82SPatrick Mooney static id_space_t	*vmm_minors;
817c8c0b82SPatrick Mooney static void		*vmm_statep;
827c8c0b82SPatrick Mooney 
831ab0d30fSPatrick Mooney /*
841ab0d30fSPatrick Mooney  * Until device emulation in bhyve had been adequately scrutinized and tested,
851ab0d30fSPatrick Mooney  * there was (justified) concern that unusual or corrupt device state payloads
861ab0d30fSPatrick Mooney  * could crash the host when loaded via the vmm-data interface.
871ab0d30fSPatrick Mooney  *
881ab0d30fSPatrick Mooney  * Now that those concerns have been mitigated, this protection is loosened to
891ab0d30fSPatrick Mooney  * default-allow, but the switch is left in place, in case there is a need to
901ab0d30fSPatrick Mooney  * once again clamp down on vmm-data writes.
911ab0d30fSPatrick Mooney  */
921ab0d30fSPatrick Mooney int		vmm_allow_state_writes = 1;
93d515dd77SPatrick Mooney 
947c8c0b82SPatrick Mooney static const char *vmmdev_hvm_name = "bhyve";
957c8c0b82SPatrick Mooney 
967c8c0b82SPatrick Mooney /* For sdev plugin (/dev) */
977c8c0b82SPatrick Mooney #define	VMM_SDEV_ROOT "/dev/vmm"
987c8c0b82SPatrick Mooney 
997c8c0b82SPatrick Mooney /* From uts/intel/io/vmm/intel/vmx.c */
1007c8c0b82SPatrick Mooney extern int vmx_x86_supported(const char **);
1017c8c0b82SPatrick Mooney 
1027c8c0b82SPatrick Mooney /* Holds and hooks from drivers external to vmm */
1037c8c0b82SPatrick Mooney struct vmm_hold {
1047c8c0b82SPatrick Mooney 	list_node_t	vmh_node;
1057c8c0b82SPatrick Mooney 	vmm_softc_t	*vmh_sc;
1067c8c0b82SPatrick Mooney 	boolean_t	vmh_release_req;
1077c8c0b82SPatrick Mooney 	uint_t		vmh_ioport_hook_cnt;
1087c8c0b82SPatrick Mooney };
1097c8c0b82SPatrick Mooney 
1107c8c0b82SPatrick Mooney struct vmm_lease {
1117c8c0b82SPatrick Mooney 	list_node_t		vml_node;
1127c8c0b82SPatrick Mooney 	struct vm		*vml_vm;
1137c8c0b82SPatrick Mooney 	vm_client_t		*vml_vmclient;
1147c8c0b82SPatrick Mooney 	boolean_t		vml_expired;
1157c8c0b82SPatrick Mooney 	boolean_t		vml_break_deferred;
1167c8c0b82SPatrick Mooney 	boolean_t		(*vml_expire_func)(void *);
1177c8c0b82SPatrick Mooney 	void			*vml_expire_arg;
1187c8c0b82SPatrick Mooney 	struct vmm_hold		*vml_hold;
1197c8c0b82SPatrick Mooney };
1207c8c0b82SPatrick Mooney 
121aa39f6d0SPatrick Mooney /* Options for vmm_destroy_locked */
122aa39f6d0SPatrick Mooney typedef enum vmm_destroy_opts {
123aa39f6d0SPatrick Mooney 	VDO_DEFAULT		= 0,
124aa39f6d0SPatrick Mooney 	/*
12542640e49SPatrick Mooney 	 * Indicate that zone-specific-data associated with this VM not be
126aa39f6d0SPatrick Mooney 	 * cleaned up as part of the destroy.  Skipping ZSD clean-up is
127aa39f6d0SPatrick Mooney 	 * necessary when VM is being destroyed as part of zone destruction,
128aa39f6d0SPatrick Mooney 	 * when said ZSD is already being cleaned up.
129aa39f6d0SPatrick Mooney 	 */
130aa39f6d0SPatrick Mooney 	VDO_NO_CLEAN_ZSD	= (1 << 0),
131aa39f6d0SPatrick Mooney 	/*
13242640e49SPatrick Mooney 	 * Attempt to wait for VM destruction to complete.  This is opt-in,
13342640e49SPatrick Mooney 	 * since there are many normal conditions which could lead to
13442640e49SPatrick Mooney 	 * destruction being stalled pending other clean-up.
135aa39f6d0SPatrick Mooney 	 */
13642640e49SPatrick Mooney 	VDO_ATTEMPT_WAIT	= (1 << 1),
137aa39f6d0SPatrick Mooney } vmm_destroy_opts_t;
138aa39f6d0SPatrick Mooney 
13942640e49SPatrick Mooney static void vmm_hma_release(void);
14042640e49SPatrick Mooney static int vmm_destroy_locked(vmm_softc_t *, vmm_destroy_opts_t, bool *);
1417c8c0b82SPatrick Mooney static int vmm_drv_block_hook(vmm_softc_t *, boolean_t);
1427c8c0b82SPatrick Mooney static void vmm_lease_block(vmm_softc_t *);
1437c8c0b82SPatrick Mooney static void vmm_lease_unblock(vmm_softc_t *);
1447c8c0b82SPatrick Mooney static int vmm_kstat_alloc(vmm_softc_t *, minor_t, const cred_t *);
1457c8c0b82SPatrick Mooney static void vmm_kstat_init(vmm_softc_t *);
1467c8c0b82SPatrick Mooney static void vmm_kstat_fini(vmm_softc_t *);
1477c8c0b82SPatrick Mooney 
1487c8c0b82SPatrick Mooney /*
1497c8c0b82SPatrick Mooney  * The 'devmem' hack:
1507c8c0b82SPatrick Mooney  *
1517c8c0b82SPatrick Mooney  * On native FreeBSD, bhyve consumers are allowed to create 'devmem' segments
1527c8c0b82SPatrick Mooney  * in the vm which appear with their own name related to the vm under /dev.
1537c8c0b82SPatrick Mooney  * Since this would be a hassle from an sdev perspective and would require a
1547c8c0b82SPatrick Mooney  * new cdev interface (or complicate the existing one), we choose to implement
1557c8c0b82SPatrick Mooney  * this in a different manner.  Direct access to the underlying vm memory
1567c8c0b82SPatrick Mooney  * segments is exposed by placing them in a range of offsets beyond the normal
1577c8c0b82SPatrick Mooney  * guest memory space.  Userspace can query the appropriate offset to mmap()
1587c8c0b82SPatrick Mooney  * for a given segment-id with the VM_DEVMEM_GETOFFSET ioctl.
1597c8c0b82SPatrick Mooney  */
1607c8c0b82SPatrick Mooney 
1617c8c0b82SPatrick Mooney static vmm_devmem_entry_t *
vmmdev_devmem_find(vmm_softc_t * sc,int segid)1627c8c0b82SPatrick Mooney vmmdev_devmem_find(vmm_softc_t *sc, int segid)
1637c8c0b82SPatrick Mooney {
1647c8c0b82SPatrick Mooney 	vmm_devmem_entry_t *ent = NULL;
1657c8c0b82SPatrick Mooney 	list_t *dl = &sc->vmm_devmem_list;
1667c8c0b82SPatrick Mooney 
1677c8c0b82SPatrick Mooney 	for (ent = list_head(dl); ent != NULL; ent = list_next(dl, ent)) {
1687c8c0b82SPatrick Mooney 		if (ent->vde_segid == segid) {
1697c8c0b82SPatrick Mooney 			return (ent);
1707c8c0b82SPatrick Mooney 		}
1717c8c0b82SPatrick Mooney 	}
1727c8c0b82SPatrick Mooney 	return (NULL);
1737c8c0b82SPatrick Mooney }
1747c8c0b82SPatrick Mooney 
1757c8c0b82SPatrick Mooney static int
vmmdev_get_memseg(vmm_softc_t * sc,struct vm_memseg * mseg)1767c8c0b82SPatrick Mooney vmmdev_get_memseg(vmm_softc_t *sc, struct vm_memseg *mseg)
1777c8c0b82SPatrick Mooney {
1787c8c0b82SPatrick Mooney 	int error;
1797c8c0b82SPatrick Mooney 	bool sysmem;
1807c8c0b82SPatrick Mooney 
1817c8c0b82SPatrick Mooney 	error = vm_get_memseg(sc->vmm_vm, mseg->segid, &mseg->len, &sysmem,
1827c8c0b82SPatrick Mooney 	    NULL);
1837c8c0b82SPatrick Mooney 	if (error || mseg->len == 0)
1847c8c0b82SPatrick Mooney 		return (error);
1857c8c0b82SPatrick Mooney 
1867c8c0b82SPatrick Mooney 	if (!sysmem) {
1877c8c0b82SPatrick Mooney 		vmm_devmem_entry_t *de;
1887c8c0b82SPatrick Mooney 
1897c8c0b82SPatrick Mooney 		de = vmmdev_devmem_find(sc, mseg->segid);
1907c8c0b82SPatrick Mooney 		if (de != NULL) {
1917c8c0b82SPatrick Mooney 			(void) strlcpy(mseg->name, de->vde_name,
1927c8c0b82SPatrick Mooney 			    sizeof (mseg->name));
1937c8c0b82SPatrick Mooney 		}
1947c8c0b82SPatrick Mooney 	} else {
1957c8c0b82SPatrick Mooney 		bzero(mseg->name, sizeof (mseg->name));
1967c8c0b82SPatrick Mooney 	}
1977c8c0b82SPatrick Mooney 
1987c8c0b82SPatrick Mooney 	return (error);
1997c8c0b82SPatrick Mooney }
2007c8c0b82SPatrick Mooney 
2017c8c0b82SPatrick Mooney static int
vmmdev_devmem_create(vmm_softc_t * sc,struct vm_memseg * mseg,const char * name)2027c8c0b82SPatrick Mooney vmmdev_devmem_create(vmm_softc_t *sc, struct vm_memseg *mseg, const char *name)
2037c8c0b82SPatrick Mooney {
2047c8c0b82SPatrick Mooney 	off_t map_offset;
2057c8c0b82SPatrick Mooney 	vmm_devmem_entry_t *entry;
2067c8c0b82SPatrick Mooney 
2077c8c0b82SPatrick Mooney 	if (list_is_empty(&sc->vmm_devmem_list)) {
2087c8c0b82SPatrick Mooney 		map_offset = VM_DEVMEM_START;
2097c8c0b82SPatrick Mooney 	} else {
2107c8c0b82SPatrick Mooney 		entry = list_tail(&sc->vmm_devmem_list);
2117c8c0b82SPatrick Mooney 		map_offset = entry->vde_off + entry->vde_len;
2127c8c0b82SPatrick Mooney 		if (map_offset < entry->vde_off) {
2137c8c0b82SPatrick Mooney 			/* Do not tolerate overflow */
2147c8c0b82SPatrick Mooney 			return (ERANGE);
2157c8c0b82SPatrick Mooney 		}
2167c8c0b82SPatrick Mooney 		/*
2177c8c0b82SPatrick Mooney 		 * XXXJOY: We could choose to search the list for duplicate
2187c8c0b82SPatrick Mooney 		 * names and toss an error.  Since we're using the offset
2197c8c0b82SPatrick Mooney 		 * method for now, it does not make much of a difference.
2207c8c0b82SPatrick Mooney 		 */
2217c8c0b82SPatrick Mooney 	}
2227c8c0b82SPatrick Mooney 
2237c8c0b82SPatrick Mooney 	entry = kmem_zalloc(sizeof (*entry), KM_SLEEP);
2247c8c0b82SPatrick Mooney 	entry->vde_segid = mseg->segid;
2257c8c0b82SPatrick Mooney 	entry->vde_len = mseg->len;
2267c8c0b82SPatrick Mooney 	entry->vde_off = map_offset;
2277c8c0b82SPatrick Mooney 	(void) strlcpy(entry->vde_name, name, sizeof (entry->vde_name));
2287c8c0b82SPatrick Mooney 	list_insert_tail(&sc->vmm_devmem_list, entry);
2297c8c0b82SPatrick Mooney 
2307c8c0b82SPatrick Mooney 	return (0);
2317c8c0b82SPatrick Mooney }
2327c8c0b82SPatrick Mooney 
2337c8c0b82SPatrick Mooney static boolean_t
vmmdev_devmem_segid(vmm_softc_t * sc,off_t off,off_t len,int * segidp,off_t * map_offp)2347c8c0b82SPatrick Mooney vmmdev_devmem_segid(vmm_softc_t *sc, off_t off, off_t len, int *segidp,
2357c8c0b82SPatrick Mooney     off_t *map_offp)
2367c8c0b82SPatrick Mooney {
2377c8c0b82SPatrick Mooney 	list_t *dl = &sc->vmm_devmem_list;
2387c8c0b82SPatrick Mooney 	vmm_devmem_entry_t *de = NULL;
2397c8c0b82SPatrick Mooney 	const off_t map_end = off + len;
2407c8c0b82SPatrick Mooney 
2417c8c0b82SPatrick Mooney 	VERIFY(off >= VM_DEVMEM_START);
2427c8c0b82SPatrick Mooney 
2437c8c0b82SPatrick Mooney 	if (map_end < off) {
2447c8c0b82SPatrick Mooney 		/* No match on overflow */
2457c8c0b82SPatrick Mooney 		return (B_FALSE);
2467c8c0b82SPatrick Mooney 	}
2477c8c0b82SPatrick Mooney 
2487c8c0b82SPatrick Mooney 	for (de = list_head(dl); de != NULL; de = list_next(dl, de)) {
2497c8c0b82SPatrick Mooney 		const off_t item_end = de->vde_off + de->vde_len;
2507c8c0b82SPatrick Mooney 
2517c8c0b82SPatrick Mooney 		if (de->vde_off <= off && item_end >= map_end) {
2527c8c0b82SPatrick Mooney 			*segidp = de->vde_segid;
2537c8c0b82SPatrick Mooney 			*map_offp = off - de->vde_off;
2547c8c0b82SPatrick Mooney 			return (B_TRUE);
2557c8c0b82SPatrick Mooney 		}
2567c8c0b82SPatrick Mooney 	}
2577c8c0b82SPatrick Mooney 	return (B_FALSE);
2587c8c0b82SPatrick Mooney }
2597c8c0b82SPatrick Mooney 
26042640e49SPatrick Mooney /*
26142640e49SPatrick Mooney  * When an instance is being destroyed, the devmem list of named memory objects
26242640e49SPatrick Mooney  * can be torn down, as no new mappings are allowed.
26342640e49SPatrick Mooney  */
2647c8c0b82SPatrick Mooney static void
vmmdev_devmem_purge(vmm_softc_t * sc)2657c8c0b82SPatrick Mooney vmmdev_devmem_purge(vmm_softc_t *sc)
2667c8c0b82SPatrick Mooney {
2677c8c0b82SPatrick Mooney 	vmm_devmem_entry_t *entry;
2687c8c0b82SPatrick Mooney 
2697c8c0b82SPatrick Mooney 	while ((entry = list_remove_head(&sc->vmm_devmem_list)) != NULL) {
2707c8c0b82SPatrick Mooney 		kmem_free(entry, sizeof (*entry));
2717c8c0b82SPatrick Mooney 	}
2727c8c0b82SPatrick Mooney }
2737c8c0b82SPatrick Mooney 
2747c8c0b82SPatrick Mooney static int
vmmdev_alloc_memseg(vmm_softc_t * sc,struct vm_memseg * mseg)2757c8c0b82SPatrick Mooney vmmdev_alloc_memseg(vmm_softc_t *sc, struct vm_memseg *mseg)
2767c8c0b82SPatrick Mooney {
2777c8c0b82SPatrick Mooney 	int error;
2787c8c0b82SPatrick Mooney 	bool sysmem = true;
2797c8c0b82SPatrick Mooney 
2807c8c0b82SPatrick Mooney 	if (VM_MEMSEG_NAME(mseg)) {
2817c8c0b82SPatrick Mooney 		sysmem = false;
2827c8c0b82SPatrick Mooney 	}
2837c8c0b82SPatrick Mooney 	error = vm_alloc_memseg(sc->vmm_vm, mseg->segid, mseg->len, sysmem);
2847c8c0b82SPatrick Mooney 
2857c8c0b82SPatrick Mooney 	if (error == 0) {
2867c8c0b82SPatrick Mooney 		/*
2877c8c0b82SPatrick Mooney 		 * Rather than create a whole fresh device from which userspace
2887c8c0b82SPatrick Mooney 		 * can mmap this segment, instead make it available at an
2897c8c0b82SPatrick Mooney 		 * offset above where the main guest memory resides.
2907c8c0b82SPatrick Mooney 		 */
2917c8c0b82SPatrick Mooney 		error = vmmdev_devmem_create(sc, mseg, mseg->name);
2927c8c0b82SPatrick Mooney 		if (error != 0) {
2937c8c0b82SPatrick Mooney 			vm_free_memseg(sc->vmm_vm, mseg->segid);
2947c8c0b82SPatrick Mooney 		}
2957c8c0b82SPatrick Mooney 	}
2967c8c0b82SPatrick Mooney 	return (error);
2977c8c0b82SPatrick Mooney }
2987c8c0b82SPatrick Mooney 
2997c8c0b82SPatrick Mooney /*
3007c8c0b82SPatrick Mooney  * Resource Locking and Exclusion
3017c8c0b82SPatrick Mooney  *
3027c8c0b82SPatrick Mooney  * Much of bhyve depends on key portions of VM state, such as the guest memory
3037c8c0b82SPatrick Mooney  * map, to remain unchanged while the guest is running.  As ported from
3047c8c0b82SPatrick Mooney  * FreeBSD, the initial strategy for this resource exclusion hinged on gating
3057c8c0b82SPatrick Mooney  * access to the instance vCPUs.  Threads acting on a single vCPU, like those
3067c8c0b82SPatrick Mooney  * performing the work of actually running the guest in VMX/SVM, would lock
3077c8c0b82SPatrick Mooney  * only that vCPU during ioctl() entry.  For ioctls which would change VM-wide
3087c8c0b82SPatrick Mooney  * state, all of the vCPUs would be first locked, ensuring that the
3097c8c0b82SPatrick Mooney  * operation(s) could complete without any other threads stumbling into
3107c8c0b82SPatrick Mooney  * intermediate states.
3117c8c0b82SPatrick Mooney  *
3127c8c0b82SPatrick Mooney  * This approach is largely effective for bhyve.  Common operations, such as
3137c8c0b82SPatrick Mooney  * running the vCPUs, steer clear of lock contention.  The model begins to
3147c8c0b82SPatrick Mooney  * break down for operations which do not occur in the context of a specific
3157c8c0b82SPatrick Mooney  * vCPU.  LAPIC MSI delivery, for example, may be initiated from a worker
3167c8c0b82SPatrick Mooney  * thread in the bhyve process.  In order to properly protect those vCPU-less
3177c8c0b82SPatrick Mooney  * operations from encountering invalid states, additional locking is required.
3187c8c0b82SPatrick Mooney  * This was solved by forcing those operations to lock the VM_MAXCPU-1 vCPU.
3197c8c0b82SPatrick Mooney  * It does mean that class of operations will be serialized on locking the
3207c8c0b82SPatrick Mooney  * specific vCPU and that instances sized at VM_MAXCPU will potentially see
3217c8c0b82SPatrick Mooney  * undue contention on the VM_MAXCPU-1 vCPU.
3227c8c0b82SPatrick Mooney  *
3237c8c0b82SPatrick Mooney  * In order to address the shortcomings of this model, the concept of a
3247c8c0b82SPatrick Mooney  * read/write lock has been added to bhyve.  Operations which change
3257c8c0b82SPatrick Mooney  * fundamental aspects of a VM (such as the memory map) must acquire the write
3267c8c0b82SPatrick Mooney  * lock, which also implies locking all of the vCPUs and waiting for all read
3277c8c0b82SPatrick Mooney  * lock holders to release.  While it increases the cost and waiting time for
3287c8c0b82SPatrick Mooney  * those few operations, it allows most hot-path operations on the VM (which
3297c8c0b82SPatrick Mooney  * depend on its configuration remaining stable) to occur with minimal locking.
3307c8c0b82SPatrick Mooney  *
3317c8c0b82SPatrick Mooney  * Consumers of the Driver API (see below) are a special case when it comes to
3327c8c0b82SPatrick Mooney  * this locking, since they may hold a read lock via the drv_lease mechanism
3337c8c0b82SPatrick Mooney  * for an extended period of time.  Rather than forcing those consumers to
3347c8c0b82SPatrick Mooney  * continuously poll for a write lock attempt, the lease system forces them to
3357c8c0b82SPatrick Mooney  * provide a release callback to trigger their clean-up (and potential later
3367c8c0b82SPatrick Mooney  * reacquisition) of the read lock.
3377c8c0b82SPatrick Mooney  */
3387c8c0b82SPatrick Mooney 
3397c8c0b82SPatrick Mooney static void
vcpu_lock_one(vmm_softc_t * sc,int vcpu)3407c8c0b82SPatrick Mooney vcpu_lock_one(vmm_softc_t *sc, int vcpu)
3417c8c0b82SPatrick Mooney {
3427c8c0b82SPatrick Mooney 	ASSERT(vcpu >= 0 && vcpu < VM_MAXCPU);
3437c8c0b82SPatrick Mooney 
3447c8c0b82SPatrick Mooney 	/*
3457c8c0b82SPatrick Mooney 	 * Since this state transition is utilizing from_idle=true, it should
3467c8c0b82SPatrick Mooney 	 * not fail, but rather block until it can be successful.
3477c8c0b82SPatrick Mooney 	 */
3487c8c0b82SPatrick Mooney 	VERIFY0(vcpu_set_state(sc->vmm_vm, vcpu, VCPU_FROZEN, true));
3497c8c0b82SPatrick Mooney }
3507c8c0b82SPatrick Mooney 
3517c8c0b82SPatrick Mooney static void
vcpu_unlock_one(vmm_softc_t * sc,int vcpu)3527c8c0b82SPatrick Mooney vcpu_unlock_one(vmm_softc_t *sc, int vcpu)
3537c8c0b82SPatrick Mooney {
3547c8c0b82SPatrick Mooney 	ASSERT(vcpu >= 0 && vcpu < VM_MAXCPU);
3557c8c0b82SPatrick Mooney 
3567c8c0b82SPatrick Mooney 	VERIFY3U(vcpu_get_state(sc->vmm_vm, vcpu, NULL), ==, VCPU_FROZEN);
357e0994bd2SPatrick Mooney 	VERIFY0(vcpu_set_state(sc->vmm_vm, vcpu, VCPU_IDLE, false));
3587c8c0b82SPatrick Mooney }
3597c8c0b82SPatrick Mooney 
3607c8c0b82SPatrick Mooney static void
vmm_read_lock(vmm_softc_t * sc)3617c8c0b82SPatrick Mooney vmm_read_lock(vmm_softc_t *sc)
3627c8c0b82SPatrick Mooney {
3637c8c0b82SPatrick Mooney 	rw_enter(&sc->vmm_rwlock, RW_READER);
3647c8c0b82SPatrick Mooney }
3657c8c0b82SPatrick Mooney 
3667c8c0b82SPatrick Mooney static void
vmm_read_unlock(vmm_softc_t * sc)3677c8c0b82SPatrick Mooney vmm_read_unlock(vmm_softc_t *sc)
3687c8c0b82SPatrick Mooney {
3697c8c0b82SPatrick Mooney 	rw_exit(&sc->vmm_rwlock);
3707c8c0b82SPatrick Mooney }
3717c8c0b82SPatrick Mooney 
3727c8c0b82SPatrick Mooney static void
vmm_write_lock(vmm_softc_t * sc)3737c8c0b82SPatrick Mooney vmm_write_lock(vmm_softc_t *sc)
3747c8c0b82SPatrick Mooney {
3757c8c0b82SPatrick Mooney 	int maxcpus;
3767c8c0b82SPatrick Mooney 
3777c8c0b82SPatrick Mooney 	/* First lock all the vCPUs */
3787c8c0b82SPatrick Mooney 	maxcpus = vm_get_maxcpus(sc->vmm_vm);
3797c8c0b82SPatrick Mooney 	for (int vcpu = 0; vcpu < maxcpus; vcpu++) {
3807c8c0b82SPatrick Mooney 		vcpu_lock_one(sc, vcpu);
3817c8c0b82SPatrick Mooney 	}
3827c8c0b82SPatrick Mooney 
3837c8c0b82SPatrick Mooney 	/*
3847c8c0b82SPatrick Mooney 	 * Block vmm_drv leases from being acquired or held while the VM write
3857c8c0b82SPatrick Mooney 	 * lock is held.
3867c8c0b82SPatrick Mooney 	 */
3877c8c0b82SPatrick Mooney 	vmm_lease_block(sc);
3887c8c0b82SPatrick Mooney 
3897c8c0b82SPatrick Mooney 	rw_enter(&sc->vmm_rwlock, RW_WRITER);
3907c8c0b82SPatrick Mooney 	/*
3917c8c0b82SPatrick Mooney 	 * For now, the 'maxcpus' value for an instance is fixed at the
3927c8c0b82SPatrick Mooney 	 * compile-time constant of VM_MAXCPU at creation.  If this changes in
3937c8c0b82SPatrick Mooney 	 * the future, allowing for dynamic vCPU resource sizing, acquisition
3947c8c0b82SPatrick Mooney 	 * of the write lock will need to be wary of such changes.
3957c8c0b82SPatrick Mooney 	 */
3967c8c0b82SPatrick Mooney 	VERIFY(maxcpus == vm_get_maxcpus(sc->vmm_vm));
3977c8c0b82SPatrick Mooney }
3987c8c0b82SPatrick Mooney 
3997c8c0b82SPatrick Mooney static void
vmm_write_unlock(vmm_softc_t * sc)4007c8c0b82SPatrick Mooney vmm_write_unlock(vmm_softc_t *sc)
4017c8c0b82SPatrick Mooney {
4027c8c0b82SPatrick Mooney 	int maxcpus;
4037c8c0b82SPatrick Mooney 
4047c8c0b82SPatrick Mooney 	/* Allow vmm_drv leases to be acquired once write lock is dropped */
4057c8c0b82SPatrick Mooney 	vmm_lease_unblock(sc);
4067c8c0b82SPatrick Mooney 
4077c8c0b82SPatrick Mooney 	/*
4087c8c0b82SPatrick Mooney 	 * The VM write lock _must_ be released from the same thread it was
4097c8c0b82SPatrick Mooney 	 * acquired in, unlike the read lock.
4107c8c0b82SPatrick Mooney 	 */
4117c8c0b82SPatrick Mooney 	VERIFY(rw_write_held(&sc->vmm_rwlock));
4127c8c0b82SPatrick Mooney 	rw_exit(&sc->vmm_rwlock);
4137c8c0b82SPatrick Mooney 
4147c8c0b82SPatrick Mooney 	/* Unlock all the vCPUs */
4157c8c0b82SPatrick Mooney 	maxcpus = vm_get_maxcpus(sc->vmm_vm);
4167c8c0b82SPatrick Mooney 	for (int vcpu = 0; vcpu < maxcpus; vcpu++) {
4177c8c0b82SPatrick Mooney 		vcpu_unlock_one(sc, vcpu);
4187c8c0b82SPatrick Mooney 	}
4197c8c0b82SPatrick Mooney }
4207c8c0b82SPatrick Mooney 
4217c8c0b82SPatrick Mooney static int
vmmdev_do_ioctl(vmm_softc_t * sc,int cmd,intptr_t arg,int md,cred_t * credp,int * rvalp)4227c8c0b82SPatrick Mooney vmmdev_do_ioctl(vmm_softc_t *sc, int cmd, intptr_t arg, int md,
4237c8c0b82SPatrick Mooney     cred_t *credp, int *rvalp)
4247c8c0b82SPatrick Mooney {
4257c8c0b82SPatrick Mooney 	int error = 0, vcpu = -1;
4267c8c0b82SPatrick Mooney 	void *datap = (void *)arg;
4277c8c0b82SPatrick Mooney 	enum vm_lock_type {
4287c8c0b82SPatrick Mooney 		LOCK_NONE = 0,
4297c8c0b82SPatrick Mooney 		LOCK_VCPU,
4307c8c0b82SPatrick Mooney 		LOCK_READ_HOLD,
4317c8c0b82SPatrick Mooney 		LOCK_WRITE_HOLD
4327c8c0b82SPatrick Mooney 	} lock_type = LOCK_NONE;
4337c8c0b82SPatrick Mooney 
4347c8c0b82SPatrick Mooney 	/* Acquire any exclusion resources needed for the operation. */
4357c8c0b82SPatrick Mooney 	switch (cmd) {
4367c8c0b82SPatrick Mooney 	case VM_RUN:
4377c8c0b82SPatrick Mooney 	case VM_GET_REGISTER:
4387c8c0b82SPatrick Mooney 	case VM_SET_REGISTER:
4397c8c0b82SPatrick Mooney 	case VM_GET_SEGMENT_DESCRIPTOR:
4407c8c0b82SPatrick Mooney 	case VM_SET_SEGMENT_DESCRIPTOR:
4417c8c0b82SPatrick Mooney 	case VM_GET_REGISTER_SET:
4427c8c0b82SPatrick Mooney 	case VM_SET_REGISTER_SET:
4437c8c0b82SPatrick Mooney 	case VM_INJECT_EXCEPTION:
4447c8c0b82SPatrick Mooney 	case VM_GET_CAPABILITY:
4457c8c0b82SPatrick Mooney 	case VM_SET_CAPABILITY:
4467c8c0b82SPatrick Mooney 	case VM_PPTDEV_MSI:
4477c8c0b82SPatrick Mooney 	case VM_PPTDEV_MSIX:
4487c8c0b82SPatrick Mooney 	case VM_SET_X2APIC_STATE:
4497c8c0b82SPatrick Mooney 	case VM_GLA2GPA:
4507c8c0b82SPatrick Mooney 	case VM_GLA2GPA_NOFAULT:
4517c8c0b82SPatrick Mooney 	case VM_ACTIVATE_CPU:
4527c8c0b82SPatrick Mooney 	case VM_SET_INTINFO:
4537c8c0b82SPatrick Mooney 	case VM_GET_INTINFO:
4547c8c0b82SPatrick Mooney 	case VM_RESTART_INSTRUCTION:
4557c8c0b82SPatrick Mooney 	case VM_SET_KERNEMU_DEV:
4567c8c0b82SPatrick Mooney 	case VM_GET_KERNEMU_DEV:
4577c8c0b82SPatrick Mooney 	case VM_RESET_CPU:
4587c8c0b82SPatrick Mooney 	case VM_GET_RUN_STATE:
4597c8c0b82SPatrick Mooney 	case VM_SET_RUN_STATE:
4607c8c0b82SPatrick Mooney 	case VM_GET_FPU:
4617c8c0b82SPatrick Mooney 	case VM_SET_FPU:
462578d9a56SPatrick Mooney 	case VM_GET_CPUID:
463578d9a56SPatrick Mooney 	case VM_SET_CPUID:
464578d9a56SPatrick Mooney 	case VM_LEGACY_CPUID:
4657c8c0b82SPatrick Mooney 		/*
4667c8c0b82SPatrick Mooney 		 * Copy in the ID of the vCPU chosen for this operation.
4677c8c0b82SPatrick Mooney 		 * Since a nefarious caller could update their struct between
4687c8c0b82SPatrick Mooney 		 * this locking and when the rest of the ioctl data is copied
4697c8c0b82SPatrick Mooney 		 * in, it is _critical_ that this local 'vcpu' variable be used
4707c8c0b82SPatrick Mooney 		 * rather than the in-struct one when performing the ioctl.
4717c8c0b82SPatrick Mooney 		 */
4727c8c0b82SPatrick Mooney 		if (ddi_copyin(datap, &vcpu, sizeof (vcpu), md)) {
4737c8c0b82SPatrick Mooney 			return (EFAULT);
4747c8c0b82SPatrick Mooney 		}
4752cef7ad9SPatrick Mooney 		if (vcpu < 0 || vcpu >= vm_get_maxcpus(sc->vmm_vm)) {
4767c8c0b82SPatrick Mooney 			return (EINVAL);
4777c8c0b82SPatrick Mooney 		}
4787c8c0b82SPatrick Mooney 		vcpu_lock_one(sc, vcpu);
4797c8c0b82SPatrick Mooney 		lock_type = LOCK_VCPU;
4807c8c0b82SPatrick Mooney 		break;
4817c8c0b82SPatrick Mooney 
4827c8c0b82SPatrick Mooney 	case VM_REINIT:
4837c8c0b82SPatrick Mooney 	case VM_BIND_PPTDEV:
4847c8c0b82SPatrick Mooney 	case VM_UNBIND_PPTDEV:
4857c8c0b82SPatrick Mooney 	case VM_MAP_PPTDEV_MMIO:
4867c8c0b82SPatrick Mooney 	case VM_UNMAP_PPTDEV_MMIO:
4877c8c0b82SPatrick Mooney 	case VM_ALLOC_MEMSEG:
4887c8c0b82SPatrick Mooney 	case VM_MMAP_MEMSEG:
4897c8c0b82SPatrick Mooney 	case VM_MUNMAP_MEMSEG:
4907c8c0b82SPatrick Mooney 	case VM_WRLOCK_CYCLE:
4917c8c0b82SPatrick Mooney 	case VM_PMTMR_LOCATE:
4922cac0506SPatrick Mooney 	case VM_PAUSE:
4932cac0506SPatrick Mooney 	case VM_RESUME:
4947c8c0b82SPatrick Mooney 		vmm_write_lock(sc);
4957c8c0b82SPatrick Mooney 		lock_type = LOCK_WRITE_HOLD;
4967c8c0b82SPatrick Mooney 		break;
4977c8c0b82SPatrick Mooney 
4987c8c0b82SPatrick Mooney 	case VM_GET_MEMSEG:
4997c8c0b82SPatrick Mooney 	case VM_MMAP_GETNEXT:
5007c8c0b82SPatrick Mooney 	case VM_LAPIC_IRQ:
5017c8c0b82SPatrick Mooney 	case VM_INJECT_NMI:
5027c8c0b82SPatrick Mooney 	case VM_IOAPIC_ASSERT_IRQ:
5037c8c0b82SPatrick Mooney 	case VM_IOAPIC_DEASSERT_IRQ:
5047c8c0b82SPatrick Mooney 	case VM_IOAPIC_PULSE_IRQ:
5057c8c0b82SPatrick Mooney 	case VM_LAPIC_MSI:
5067c8c0b82SPatrick Mooney 	case VM_LAPIC_LOCAL_IRQ:
5077c8c0b82SPatrick Mooney 	case VM_GET_X2APIC_STATE:
5087c8c0b82SPatrick Mooney 	case VM_RTC_READ:
5097c8c0b82SPatrick Mooney 	case VM_RTC_WRITE:
5107c8c0b82SPatrick Mooney 	case VM_RTC_SETTIME:
5117c8c0b82SPatrick Mooney 	case VM_RTC_GETTIME:
5127c8c0b82SPatrick Mooney 	case VM_PPTDEV_DISABLE_MSIX:
5137c8c0b82SPatrick Mooney 	case VM_DEVMEM_GETOFFSET:
5147c8c0b82SPatrick Mooney 	case VM_TRACK_DIRTY_PAGES:
515b9b43e84SPatrick Mooney 	case VM_NPT_OPERATION:
5167c8c0b82SPatrick Mooney 		vmm_read_lock(sc);
5177c8c0b82SPatrick Mooney 		lock_type = LOCK_READ_HOLD;
5187c8c0b82SPatrick Mooney 		break;
5197c8c0b82SPatrick Mooney 
520d515dd77SPatrick Mooney 	case VM_DATA_READ:
521d515dd77SPatrick Mooney 	case VM_DATA_WRITE:
522d515dd77SPatrick Mooney 		if (ddi_copyin(datap, &vcpu, sizeof (vcpu), md)) {
523d515dd77SPatrick Mooney 			return (EFAULT);
524d515dd77SPatrick Mooney 		}
525d515dd77SPatrick Mooney 		if (vcpu == -1) {
526d515dd77SPatrick Mooney 			/* Access data for VM-wide devices */
527d515dd77SPatrick Mooney 			vmm_write_lock(sc);
528d515dd77SPatrick Mooney 			lock_type = LOCK_WRITE_HOLD;
529d515dd77SPatrick Mooney 		} else if (vcpu >= 0 && vcpu < vm_get_maxcpus(sc->vmm_vm)) {
530d515dd77SPatrick Mooney 			/* Access data associated with a specific vCPU */
531d515dd77SPatrick Mooney 			vcpu_lock_one(sc, vcpu);
532d515dd77SPatrick Mooney 			lock_type = LOCK_VCPU;
533d515dd77SPatrick Mooney 		} else {
534d515dd77SPatrick Mooney 			return (EINVAL);
535d515dd77SPatrick Mooney 		}
536d515dd77SPatrick Mooney 		break;
537d515dd77SPatrick Mooney 
5387c8c0b82SPatrick Mooney 	case VM_GET_GPA_PMAP:
5397c8c0b82SPatrick Mooney 	case VM_IOAPIC_PINCOUNT:
5407c8c0b82SPatrick Mooney 	case VM_SUSPEND:
5417c8c0b82SPatrick Mooney 	case VM_DESC_FPU_AREA:
542aa39f6d0SPatrick Mooney 	case VM_SET_AUTODESTRUCT:
54342640e49SPatrick Mooney 	case VM_DESTROY_SELF:
54442640e49SPatrick Mooney 	case VM_DESTROY_PENDING:
54572473353SPatrick Mooney 	case VM_VCPU_BARRIER:
5467c8c0b82SPatrick Mooney 	default:
5477c8c0b82SPatrick Mooney 		break;
5487c8c0b82SPatrick Mooney 	}
5497c8c0b82SPatrick Mooney 
5507c8c0b82SPatrick Mooney 	/* Execute the primary logic for the ioctl. */
5517c8c0b82SPatrick Mooney 	switch (cmd) {
5527c8c0b82SPatrick Mooney 	case VM_RUN: {
5537c8c0b82SPatrick Mooney 		struct vm_entry entry;
5547c8c0b82SPatrick Mooney 
5557c8c0b82SPatrick Mooney 		if (ddi_copyin(datap, &entry, sizeof (entry), md)) {
5567c8c0b82SPatrick Mooney 			error = EFAULT;
5577c8c0b82SPatrick Mooney 			break;
5587c8c0b82SPatrick Mooney 		}
5597c8c0b82SPatrick Mooney 
5607c8c0b82SPatrick Mooney 		if (!(curthread->t_schedflag & TS_VCPU))
5617c8c0b82SPatrick Mooney 			smt_mark_as_vcpu();
5627c8c0b82SPatrick Mooney 
5637c8c0b82SPatrick Mooney 		error = vm_run(sc->vmm_vm, vcpu, &entry);
5647c8c0b82SPatrick Mooney 
5657c8c0b82SPatrick Mooney 		/*
5667c8c0b82SPatrick Mooney 		 * Unexpected states in vm_run() are expressed through positive
5677c8c0b82SPatrick Mooney 		 * errno-oriented return values.  VM states which expect further
5687c8c0b82SPatrick Mooney 		 * processing in userspace (necessary context via exitinfo) are
5697c8c0b82SPatrick Mooney 		 * expressed through negative return values.  For the time being
5707c8c0b82SPatrick Mooney 		 * a return value of 0 is not expected from vm_run().
5717c8c0b82SPatrick Mooney 		 */
5727c8c0b82SPatrick Mooney 		ASSERT(error != 0);
5737c8c0b82SPatrick Mooney 		if (error < 0) {
5747c8c0b82SPatrick Mooney 			const struct vm_exit *vme;
5757c8c0b82SPatrick Mooney 			void *outp = entry.exit_data;
5767c8c0b82SPatrick Mooney 
5777c8c0b82SPatrick Mooney 			error = 0;
5787c8c0b82SPatrick Mooney 			vme = vm_exitinfo(sc->vmm_vm, vcpu);
5797c8c0b82SPatrick Mooney 			if (ddi_copyout(vme, outp, sizeof (*vme), md)) {
5807c8c0b82SPatrick Mooney 				error = EFAULT;
5817c8c0b82SPatrick Mooney 			}
5827c8c0b82SPatrick Mooney 		}
5837c8c0b82SPatrick Mooney 		break;
5847c8c0b82SPatrick Mooney 	}
5857c8c0b82SPatrick Mooney 	case VM_SUSPEND: {
5867c8c0b82SPatrick Mooney 		struct vm_suspend vmsuspend;
5877c8c0b82SPatrick Mooney 
5887c8c0b82SPatrick Mooney 		if (ddi_copyin(datap, &vmsuspend, sizeof (vmsuspend), md)) {
5897c8c0b82SPatrick Mooney 			error = EFAULT;
5907c8c0b82SPatrick Mooney 			break;
5917c8c0b82SPatrick Mooney 		}
59272473353SPatrick Mooney 		error = vm_suspend(sc->vmm_vm, vmsuspend.how, vmsuspend.source);
5937c8c0b82SPatrick Mooney 		break;
5947c8c0b82SPatrick Mooney 	}
5957c8c0b82SPatrick Mooney 	case VM_REINIT: {
5967c8c0b82SPatrick Mooney 		struct vm_reinit reinit;
5977c8c0b82SPatrick Mooney 
5987c8c0b82SPatrick Mooney 		if (ddi_copyin(datap, &reinit, sizeof (reinit), md)) {
5997c8c0b82SPatrick Mooney 			error = EFAULT;
6007c8c0b82SPatrick Mooney 			break;
6017c8c0b82SPatrick Mooney 		}
6027c8c0b82SPatrick Mooney 		if ((error = vmm_drv_block_hook(sc, B_TRUE)) != 0) {
6037c8c0b82SPatrick Mooney 			/*
6047c8c0b82SPatrick Mooney 			 * The VM instance should be free of driver-attached
6057c8c0b82SPatrick Mooney 			 * hooks during the reinitialization process.
6067c8c0b82SPatrick Mooney 			 */
6077c8c0b82SPatrick Mooney 			break;
6087c8c0b82SPatrick Mooney 		}
6097c8c0b82SPatrick Mooney 		error = vm_reinit(sc->vmm_vm, reinit.flags);
6107c8c0b82SPatrick Mooney 		(void) vmm_drv_block_hook(sc, B_FALSE);
6117c8c0b82SPatrick Mooney 		break;
6127c8c0b82SPatrick Mooney 	}
6137c8c0b82SPatrick Mooney 	case VM_STAT_DESC: {
6147c8c0b82SPatrick Mooney 		struct vm_stat_desc statdesc;
6157c8c0b82SPatrick Mooney 
6167c8c0b82SPatrick Mooney 		if (ddi_copyin(datap, &statdesc, sizeof (statdesc), md)) {
6177c8c0b82SPatrick Mooney 			error = EFAULT;
6187c8c0b82SPatrick Mooney 			break;
6197c8c0b82SPatrick Mooney 		}
6207c8c0b82SPatrick Mooney 		error = vmm_stat_desc_copy(statdesc.index, statdesc.desc,
6217c8c0b82SPatrick Mooney 		    sizeof (statdesc.desc));
6227c8c0b82SPatrick Mooney 		if (error == 0 &&
6237c8c0b82SPatrick Mooney 		    ddi_copyout(&statdesc, datap, sizeof (statdesc), md)) {
6247c8c0b82SPatrick Mooney 			error = EFAULT;
6257c8c0b82SPatrick Mooney 			break;
6267c8c0b82SPatrick Mooney 		}
6277c8c0b82SPatrick Mooney 		break;
6287c8c0b82SPatrick Mooney 	}
6297c8c0b82SPatrick Mooney 	case VM_STATS_IOC: {
6307c8c0b82SPatrick Mooney 		struct vm_stats vmstats;
6317c8c0b82SPatrick Mooney 
6327c8c0b82SPatrick Mooney 		if (ddi_copyin(datap, &vmstats, sizeof (vmstats), md)) {
6337c8c0b82SPatrick Mooney 			error = EFAULT;
6347c8c0b82SPatrick Mooney 			break;
6357c8c0b82SPatrick Mooney 		}
6367c8c0b82SPatrick Mooney 		hrt2tv(gethrtime(), &vmstats.tv);
637d7b72f7bSAndy Fiddaman 		error = vmm_stat_copy(sc->vmm_vm, vmstats.cpuid, vmstats.index,
638d7b72f7bSAndy Fiddaman 		    nitems(vmstats.statbuf),
6397c8c0b82SPatrick Mooney 		    &vmstats.num_entries, vmstats.statbuf);
6407c8c0b82SPatrick Mooney 		if (error == 0 &&
6417c8c0b82SPatrick Mooney 		    ddi_copyout(&vmstats, datap, sizeof (vmstats), md)) {
6427c8c0b82SPatrick Mooney 			error = EFAULT;
6437c8c0b82SPatrick Mooney 			break;
6447c8c0b82SPatrick Mooney 		}
6457c8c0b82SPatrick Mooney 		break;
6467c8c0b82SPatrick Mooney 	}
6477c8c0b82SPatrick Mooney 
6487c8c0b82SPatrick Mooney 	case VM_PPTDEV_MSI: {
6497c8c0b82SPatrick Mooney 		struct vm_pptdev_msi pptmsi;
6507c8c0b82SPatrick Mooney 
6517c8c0b82SPatrick Mooney 		if (ddi_copyin(datap, &pptmsi, sizeof (pptmsi), md)) {
6527c8c0b82SPatrick Mooney 			error = EFAULT;
6537c8c0b82SPatrick Mooney 			break;
6547c8c0b82SPatrick Mooney 		}
6557c8c0b82SPatrick Mooney 		error = ppt_setup_msi(sc->vmm_vm, pptmsi.vcpu, pptmsi.pptfd,
6567c8c0b82SPatrick Mooney 		    pptmsi.addr, pptmsi.msg, pptmsi.numvec);
6577c8c0b82SPatrick Mooney 		break;
6587c8c0b82SPatrick Mooney 	}
6597c8c0b82SPatrick Mooney 	case VM_PPTDEV_MSIX: {
6607c8c0b82SPatrick Mooney 		struct vm_pptdev_msix pptmsix;
6617c8c0b82SPatrick Mooney 
6627c8c0b82SPatrick Mooney 		if (ddi_copyin(datap, &pptmsix, sizeof (pptmsix), md)) {
6637c8c0b82SPatrick Mooney 			error = EFAULT;
6647c8c0b82SPatrick Mooney 			break;
6657c8c0b82SPatrick Mooney 		}
6667c8c0b82SPatrick Mooney 		error = ppt_setup_msix(sc->vmm_vm, pptmsix.vcpu, pptmsix.pptfd,
6677c8c0b82SPatrick Mooney 		    pptmsix.idx, pptmsix.addr, pptmsix.msg,
6687c8c0b82SPatrick Mooney 		    pptmsix.vector_control);
6697c8c0b82SPatrick Mooney 		break;
6707c8c0b82SPatrick Mooney 	}
6717c8c0b82SPatrick Mooney 	case VM_PPTDEV_DISABLE_MSIX: {
6727c8c0b82SPatrick Mooney 		struct vm_pptdev pptdev;
6737c8c0b82SPatrick Mooney 
6747c8c0b82SPatrick Mooney 		if (ddi_copyin(datap, &pptdev, sizeof (pptdev), md)) {
6757c8c0b82SPatrick Mooney 			error = EFAULT;
6767c8c0b82SPatrick Mooney 			break;
6777c8c0b82SPatrick Mooney 		}
6787c8c0b82SPatrick Mooney 		error = ppt_disable_msix(sc->vmm_vm, pptdev.pptfd);
6797c8c0b82SPatrick Mooney 		break;
6807c8c0b82SPatrick Mooney 	}
6817c8c0b82SPatrick Mooney 	case VM_MAP_PPTDEV_MMIO: {
6827c8c0b82SPatrick Mooney 		struct vm_pptdev_mmio pptmmio;
6837c8c0b82SPatrick Mooney 
6847c8c0b82SPatrick Mooney 		if (ddi_copyin(datap, &pptmmio, sizeof (pptmmio), md)) {
6857c8c0b82SPatrick Mooney 			error = EFAULT;
6867c8c0b82SPatrick Mooney 			break;
6877c8c0b82SPatrick Mooney 		}
6887c8c0b82SPatrick Mooney 		error = ppt_map_mmio(sc->vmm_vm, pptmmio.pptfd, pptmmio.gpa,
6897c8c0b82SPatrick Mooney 		    pptmmio.len, pptmmio.hpa);
6907c8c0b82SPatrick Mooney 		break;
6917c8c0b82SPatrick Mooney 	}
6927c8c0b82SPatrick Mooney 	case VM_UNMAP_PPTDEV_MMIO: {
6937c8c0b82SPatrick Mooney 		struct vm_pptdev_mmio pptmmio;
6947c8c0b82SPatrick Mooney 
6957c8c0b82SPatrick Mooney 		if (ddi_copyin(datap, &pptmmio, sizeof (pptmmio), md)) {
6967c8c0b82SPatrick Mooney 			error = EFAULT;
6977c8c0b82SPatrick Mooney 			break;
6987c8c0b82SPatrick Mooney 		}
6997c8c0b82SPatrick Mooney 		error = ppt_unmap_mmio(sc->vmm_vm, pptmmio.pptfd, pptmmio.gpa,
7007c8c0b82SPatrick Mooney 		    pptmmio.len);
7017c8c0b82SPatrick Mooney 		break;
7027c8c0b82SPatrick Mooney 	}
7037c8c0b82SPatrick Mooney 	case VM_BIND_PPTDEV: {
7047c8c0b82SPatrick Mooney 		struct vm_pptdev pptdev;
7057c8c0b82SPatrick Mooney 
7067c8c0b82SPatrick Mooney 		if (ddi_copyin(datap, &pptdev, sizeof (pptdev), md)) {
7077c8c0b82SPatrick Mooney 			error = EFAULT;
7087c8c0b82SPatrick Mooney 			break;
7097c8c0b82SPatrick Mooney 		}
7107c8c0b82SPatrick Mooney 		error = vm_assign_pptdev(sc->vmm_vm, pptdev.pptfd);
7117c8c0b82SPatrick Mooney 		break;
7127c8c0b82SPatrick Mooney 	}
7137c8c0b82SPatrick Mooney 	case VM_UNBIND_PPTDEV: {
7147c8c0b82SPatrick Mooney 		struct vm_pptdev pptdev;
7157c8c0b82SPatrick Mooney 
7167c8c0b82SPatrick Mooney 		if (ddi_copyin(datap, &pptdev, sizeof (pptdev), md)) {
7177c8c0b82SPatrick Mooney 			error = EFAULT;
7187c8c0b82SPatrick Mooney 			break;
7197c8c0b82SPatrick Mooney 		}
7207c8c0b82SPatrick Mooney 		error = vm_unassign_pptdev(sc->vmm_vm, pptdev.pptfd);
7217c8c0b82SPatrick Mooney 		break;
7227c8c0b82SPatrick Mooney 	}
7237c8c0b82SPatrick Mooney 	case VM_GET_PPTDEV_LIMITS: {
7247c8c0b82SPatrick Mooney 		struct vm_pptdev_limits pptlimits;
7257c8c0b82SPatrick Mooney 
7267c8c0b82SPatrick Mooney 		if (ddi_copyin(datap, &pptlimits, sizeof (pptlimits), md)) {
7277c8c0b82SPatrick Mooney 			error = EFAULT;
7287c8c0b82SPatrick Mooney 			break;
7297c8c0b82SPatrick Mooney 		}
7307c8c0b82SPatrick Mooney 		error = ppt_get_limits(sc->vmm_vm, pptlimits.pptfd,
7317c8c0b82SPatrick Mooney 		    &pptlimits.msi_limit, &pptlimits.msix_limit);
7327c8c0b82SPatrick Mooney 		if (error == 0 &&
7337c8c0b82SPatrick Mooney 		    ddi_copyout(&pptlimits, datap, sizeof (pptlimits), md)) {
7347c8c0b82SPatrick Mooney 			error = EFAULT;
7357c8c0b82SPatrick Mooney 			break;
7367c8c0b82SPatrick Mooney 		}
7377c8c0b82SPatrick Mooney 		break;
7387c8c0b82SPatrick Mooney 	}
7397c8c0b82SPatrick Mooney 	case VM_INJECT_EXCEPTION: {
7407c8c0b82SPatrick Mooney 		struct vm_exception vmexc;
7417c8c0b82SPatrick Mooney 		if (ddi_copyin(datap, &vmexc, sizeof (vmexc), md)) {
7427c8c0b82SPatrick Mooney 			error = EFAULT;
7437c8c0b82SPatrick Mooney 			break;
7447c8c0b82SPatrick Mooney 		}
7457c8c0b82SPatrick Mooney 		error = vm_inject_exception(sc->vmm_vm, vcpu, vmexc.vector,
7463d097f7dSPatrick Mooney 		    vmexc.error_code_valid != 0, vmexc.error_code,
7473d097f7dSPatrick Mooney 		    vmexc.restart_instruction != 0);
7487c8c0b82SPatrick Mooney 		break;
7497c8c0b82SPatrick Mooney 	}
7507c8c0b82SPatrick Mooney 	case VM_INJECT_NMI: {
7517c8c0b82SPatrick Mooney 		struct vm_nmi vmnmi;
7527c8c0b82SPatrick Mooney 
7537c8c0b82SPatrick Mooney 		if (ddi_copyin(datap, &vmnmi, sizeof (vmnmi), md)) {
7547c8c0b82SPatrick Mooney 			error = EFAULT;
7557c8c0b82SPatrick Mooney 			break;
7567c8c0b82SPatrick Mooney 		}
7577c8c0b82SPatrick Mooney 		error = vm_inject_nmi(sc->vmm_vm, vmnmi.cpuid);
7587c8c0b82SPatrick Mooney 		break;
7597c8c0b82SPatrick Mooney 	}
7607c8c0b82SPatrick Mooney 	case VM_LAPIC_IRQ: {
7617c8c0b82SPatrick Mooney 		struct vm_lapic_irq vmirq;
7627c8c0b82SPatrick Mooney 
7637c8c0b82SPatrick Mooney 		if (ddi_copyin(datap, &vmirq, sizeof (vmirq), md)) {
7647c8c0b82SPatrick Mooney 			error = EFAULT;
7657c8c0b82SPatrick Mooney 			break;
7667c8c0b82SPatrick Mooney 		}
7677c8c0b82SPatrick Mooney 		error = lapic_intr_edge(sc->vmm_vm, vmirq.cpuid, vmirq.vector);
7687c8c0b82SPatrick Mooney 		break;
7697c8c0b82SPatrick Mooney 	}
7707c8c0b82SPatrick Mooney 	case VM_LAPIC_LOCAL_IRQ: {
7717c8c0b82SPatrick Mooney 		struct vm_lapic_irq vmirq;
7727c8c0b82SPatrick Mooney 
7737c8c0b82SPatrick Mooney 		if (ddi_copyin(datap, &vmirq, sizeof (vmirq), md)) {
7747c8c0b82SPatrick Mooney 			error = EFAULT;
7757c8c0b82SPatrick Mooney 			break;
7767c8c0b82SPatrick Mooney 		}
7777c8c0b82SPatrick Mooney 		error = lapic_set_local_intr(sc->vmm_vm, vmirq.cpuid,
7787c8c0b82SPatrick Mooney 		    vmirq.vector);
7797c8c0b82SPatrick Mooney 		break;
7807c8c0b82SPatrick Mooney 	}
7817c8c0b82SPatrick Mooney 	case VM_LAPIC_MSI: {
7827c8c0b82SPatrick Mooney 		struct vm_lapic_msi vmmsi;
7837c8c0b82SPatrick Mooney 
7847c8c0b82SPatrick Mooney 		if (ddi_copyin(datap, &vmmsi, sizeof (vmmsi), md)) {
7857c8c0b82SPatrick Mooney 			error = EFAULT;
7867c8c0b82SPatrick Mooney 			break;
7877c8c0b82SPatrick Mooney 		}
7887c8c0b82SPatrick Mooney 		error = lapic_intr_msi(sc->vmm_vm, vmmsi.addr, vmmsi.msg);
7897c8c0b82SPatrick Mooney 		break;
7907c8c0b82SPatrick Mooney 	}
7917c8c0b82SPatrick Mooney 
7927c8c0b82SPatrick Mooney 	case VM_IOAPIC_ASSERT_IRQ: {
7937c8c0b82SPatrick Mooney 		struct vm_ioapic_irq ioapic_irq;
7947c8c0b82SPatrick Mooney 
7957c8c0b82SPatrick Mooney 		if (ddi_copyin(datap, &ioapic_irq, sizeof (ioapic_irq), md)) {
7967c8c0b82SPatrick Mooney 			error = EFAULT;
7977c8c0b82SPatrick Mooney 			break;
7987c8c0b82SPatrick Mooney 		}
7997c8c0b82SPatrick Mooney 		error = vioapic_assert_irq(sc->vmm_vm, ioapic_irq.irq);
8007c8c0b82SPatrick Mooney 		break;
8017c8c0b82SPatrick Mooney 	}
8027c8c0b82SPatrick Mooney 	case VM_IOAPIC_DEASSERT_IRQ: {
8037c8c0b82SPatrick Mooney 		struct vm_ioapic_irq ioapic_irq;
8047c8c0b82SPatrick Mooney 
8057c8c0b82SPatrick Mooney 		if (ddi_copyin(datap, &ioapic_irq, sizeof (ioapic_irq), md)) {
8067c8c0b82SPatrick Mooney 			error = EFAULT;
8077c8c0b82SPatrick Mooney 			break;
8087c8c0b82SPatrick Mooney 		}
8097c8c0b82SPatrick Mooney 		error = vioapic_deassert_irq(sc->vmm_vm, ioapic_irq.irq);
8107c8c0b82SPatrick Mooney 		break;
8117c8c0b82SPatrick Mooney 	}
8127c8c0b82SPatrick Mooney 	case VM_IOAPIC_PULSE_IRQ: {
8137c8c0b82SPatrick Mooney 		struct vm_ioapic_irq ioapic_irq;
8147c8c0b82SPatrick Mooney 
8157c8c0b82SPatrick Mooney 		if (ddi_copyin(datap, &ioapic_irq, sizeof (ioapic_irq), md)) {
8167c8c0b82SPatrick Mooney 			error = EFAULT;
8177c8c0b82SPatrick Mooney 			break;
8187c8c0b82SPatrick Mooney 		}
8197c8c0b82SPatrick Mooney 		error = vioapic_pulse_irq(sc->vmm_vm, ioapic_irq.irq);
8207c8c0b82SPatrick Mooney 		break;
8217c8c0b82SPatrick Mooney 	}
8227c8c0b82SPatrick Mooney 	case VM_IOAPIC_PINCOUNT: {
8237c8c0b82SPatrick Mooney 		int pincount;
8247c8c0b82SPatrick Mooney 
8257c8c0b82SPatrick Mooney 		pincount = vioapic_pincount(sc->vmm_vm);
8267c8c0b82SPatrick Mooney 		if (ddi_copyout(&pincount, datap, sizeof (int), md)) {
8277c8c0b82SPatrick Mooney 			error = EFAULT;
8287c8c0b82SPatrick Mooney 			break;
8297c8c0b82SPatrick Mooney 		}
8307c8c0b82SPatrick Mooney 		break;
8317c8c0b82SPatrick Mooney 	}
8327c8c0b82SPatrick Mooney 	case VM_DESC_FPU_AREA: {
8337c8c0b82SPatrick Mooney 		struct vm_fpu_desc desc;
8347c8c0b82SPatrick Mooney 		void *buf = NULL;
8357c8c0b82SPatrick Mooney 
8367c8c0b82SPatrick Mooney 		if (ddi_copyin(datap, &desc, sizeof (desc), md)) {
8377c8c0b82SPatrick Mooney 			error = EFAULT;
8387c8c0b82SPatrick Mooney 			break;
8397c8c0b82SPatrick Mooney 		}
8407c8c0b82SPatrick Mooney 		if (desc.vfd_num_entries > 64) {
8417c8c0b82SPatrick Mooney 			error = EINVAL;
8427c8c0b82SPatrick Mooney 			break;
8437c8c0b82SPatrick Mooney 		}
8447c8c0b82SPatrick Mooney 		const size_t buf_sz = sizeof (struct vm_fpu_desc_entry) *
8457c8c0b82SPatrick Mooney 		    desc.vfd_num_entries;
8467c8c0b82SPatrick Mooney 		if (buf_sz != 0) {
8477c8c0b82SPatrick Mooney 			buf = kmem_zalloc(buf_sz, KM_SLEEP);
8487c8c0b82SPatrick Mooney 		}
8497c8c0b82SPatrick Mooney 
8507c8c0b82SPatrick Mooney 		/*
8517c8c0b82SPatrick Mooney 		 * For now, we are depending on vm_fpu_desc_entry and
8527c8c0b82SPatrick Mooney 		 * hma_xsave_state_desc_t having the same format.
8537c8c0b82SPatrick Mooney 		 */
8547c8c0b82SPatrick Mooney 		CTASSERT(sizeof (struct vm_fpu_desc_entry) ==
8557c8c0b82SPatrick Mooney 		    sizeof (hma_xsave_state_desc_t));
8567c8c0b82SPatrick Mooney 
8577c8c0b82SPatrick Mooney 		size_t req_size;
8587c8c0b82SPatrick Mooney 		const uint_t max_entries = hma_fpu_describe_xsave_state(
8597c8c0b82SPatrick Mooney 		    (hma_xsave_state_desc_t *)buf,
8607c8c0b82SPatrick Mooney 		    desc.vfd_num_entries,
8617c8c0b82SPatrick Mooney 		    &req_size);
8627c8c0b82SPatrick Mooney 
8637c8c0b82SPatrick Mooney 		desc.vfd_req_size = req_size;
8647c8c0b82SPatrick Mooney 		desc.vfd_num_entries = max_entries;
8657c8c0b82SPatrick Mooney 		if (buf_sz != 0) {
8667c8c0b82SPatrick Mooney 			if (ddi_copyout(buf, desc.vfd_entry_data, buf_sz, md)) {
8677c8c0b82SPatrick Mooney 				error = EFAULT;
8687c8c0b82SPatrick Mooney 			}
8697c8c0b82SPatrick Mooney 			kmem_free(buf, buf_sz);
8707c8c0b82SPatrick Mooney 		}
8717c8c0b82SPatrick Mooney 
8727c8c0b82SPatrick Mooney 		if (error == 0) {
8737c8c0b82SPatrick Mooney 			if (ddi_copyout(&desc, datap, sizeof (desc), md)) {
8747c8c0b82SPatrick Mooney 				error = EFAULT;
8757c8c0b82SPatrick Mooney 			}
8767c8c0b82SPatrick Mooney 		}
8777c8c0b82SPatrick Mooney 		break;
8787c8c0b82SPatrick Mooney 	}
879aa39f6d0SPatrick Mooney 	case VM_SET_AUTODESTRUCT: {
880aa39f6d0SPatrick Mooney 		/*
881aa39f6d0SPatrick Mooney 		 * Since this has to do with controlling the lifetime of the
882aa39f6d0SPatrick Mooney 		 * greater vmm_softc_t, the flag is protected by vmm_mtx, rather
883aa39f6d0SPatrick Mooney 		 * than the vcpu-centric or rwlock exclusion mechanisms.
884aa39f6d0SPatrick Mooney 		 */
885aa39f6d0SPatrick Mooney 		mutex_enter(&vmm_mtx);
88642640e49SPatrick Mooney 		if (arg != 0) {
88742640e49SPatrick Mooney 			sc->vmm_flags |= VMM_AUTODESTROY;
88842640e49SPatrick Mooney 		} else {
88942640e49SPatrick Mooney 			sc->vmm_flags &= ~VMM_AUTODESTROY;
89042640e49SPatrick Mooney 		}
891aa39f6d0SPatrick Mooney 		mutex_exit(&vmm_mtx);
892aa39f6d0SPatrick Mooney 		break;
893aa39f6d0SPatrick Mooney 	}
89442640e49SPatrick Mooney 	case VM_DESTROY_SELF: {
89542640e49SPatrick Mooney 		bool hma_release = false;
89642640e49SPatrick Mooney 
89742640e49SPatrick Mooney 		/*
89842640e49SPatrick Mooney 		 * Just like VMM_DESTROY_VM, but on the instance file descriptor
89942640e49SPatrick Mooney 		 * itself, rather than having to perform a racy name lookup as
90042640e49SPatrick Mooney 		 * part of the destroy process.
90142640e49SPatrick Mooney 		 *
90242640e49SPatrick Mooney 		 * Since vmm_destroy_locked() performs vCPU lock acquisition in
90342640e49SPatrick Mooney 		 * order to kick the vCPUs out of guest context as part of any
90442640e49SPatrick Mooney 		 * destruction, we do not need to worry about it ourself using
90542640e49SPatrick Mooney 		 * the `lock_type` logic here.
90642640e49SPatrick Mooney 		 */
90742640e49SPatrick Mooney 		mutex_enter(&vmm_mtx);
90842640e49SPatrick Mooney 		VERIFY0(vmm_destroy_locked(sc, VDO_DEFAULT, &hma_release));
90942640e49SPatrick Mooney 		mutex_exit(&vmm_mtx);
91042640e49SPatrick Mooney 		if (hma_release) {
91142640e49SPatrick Mooney 			vmm_hma_release();
91242640e49SPatrick Mooney 		}
91342640e49SPatrick Mooney 		break;
91442640e49SPatrick Mooney 	}
91542640e49SPatrick Mooney 	case VM_DESTROY_PENDING: {
91642640e49SPatrick Mooney 		/*
91742640e49SPatrick Mooney 		 * If we have made it this far, then destruction of the instance
91842640e49SPatrick Mooney 		 * has not been initiated.
91942640e49SPatrick Mooney 		 */
92042640e49SPatrick Mooney 		*rvalp = 0;
92142640e49SPatrick Mooney 		break;
92242640e49SPatrick Mooney 	}
9237c8c0b82SPatrick Mooney 
9247c8c0b82SPatrick Mooney 	case VM_ISA_ASSERT_IRQ: {
9257c8c0b82SPatrick Mooney 		struct vm_isa_irq isa_irq;
9267c8c0b82SPatrick Mooney 
9277c8c0b82SPatrick Mooney 		if (ddi_copyin(datap, &isa_irq, sizeof (isa_irq), md)) {
9287c8c0b82SPatrick Mooney 			error = EFAULT;
9297c8c0b82SPatrick Mooney 			break;
9307c8c0b82SPatrick Mooney 		}
9317c8c0b82SPatrick Mooney 		error = vatpic_assert_irq(sc->vmm_vm, isa_irq.atpic_irq);
9327c8c0b82SPatrick Mooney 		if (error == 0 && isa_irq.ioapic_irq != -1) {
9337c8c0b82SPatrick Mooney 			error = vioapic_assert_irq(sc->vmm_vm,
9347c8c0b82SPatrick Mooney 			    isa_irq.ioapic_irq);
9357c8c0b82SPatrick Mooney 		}
9367c8c0b82SPatrick Mooney 		break;
9377c8c0b82SPatrick Mooney 	}
9387c8c0b82SPatrick Mooney 	case VM_ISA_DEASSERT_IRQ: {
9397c8c0b82SPatrick Mooney 		struct vm_isa_irq isa_irq;
9407c8c0b82SPatrick Mooney 
9417c8c0b82SPatrick Mooney 		if (ddi_copyin(datap, &isa_irq, sizeof (isa_irq), md)) {
9427c8c0b82SPatrick Mooney 			error = EFAULT;
9437c8c0b82SPatrick Mooney 			break;
9447c8c0b82SPatrick Mooney 		}
9457c8c0b82SPatrick Mooney 		error = vatpic_deassert_irq(sc->vmm_vm, isa_irq.atpic_irq);
9467c8c0b82SPatrick Mooney 		if (error == 0 && isa_irq.ioapic_irq != -1) {
9477c8c0b82SPatrick Mooney 			error = vioapic_deassert_irq(sc->vmm_vm,
9487c8c0b82SPatrick Mooney 			    isa_irq.ioapic_irq);
9497c8c0b82SPatrick Mooney 		}
9507c8c0b82SPatrick Mooney 		break;
9517c8c0b82SPatrick Mooney 	}
9527c8c0b82SPatrick Mooney 	case VM_ISA_PULSE_IRQ: {
9537c8c0b82SPatrick Mooney 		struct vm_isa_irq isa_irq;
9547c8c0b82SPatrick Mooney 
9557c8c0b82SPatrick Mooney 		if (ddi_copyin(datap, &isa_irq, sizeof (isa_irq), md)) {
9567c8c0b82SPatrick Mooney 			error = EFAULT;
9577c8c0b82SPatrick Mooney 			break;
9587c8c0b82SPatrick Mooney 		}
9597c8c0b82SPatrick Mooney 		error = vatpic_pulse_irq(sc->vmm_vm, isa_irq.atpic_irq);
9607c8c0b82SPatrick Mooney 		if (error == 0 && isa_irq.ioapic_irq != -1) {
9617c8c0b82SPatrick Mooney 			error = vioapic_pulse_irq(sc->vmm_vm,
9627c8c0b82SPatrick Mooney 			    isa_irq.ioapic_irq);
9637c8c0b82SPatrick Mooney 		}
9647c8c0b82SPatrick Mooney 		break;
9657c8c0b82SPatrick Mooney 	}
9667c8c0b82SPatrick Mooney 	case VM_ISA_SET_IRQ_TRIGGER: {
9677c8c0b82SPatrick Mooney 		struct vm_isa_irq_trigger isa_irq_trigger;
9687c8c0b82SPatrick Mooney 
9697c8c0b82SPatrick Mooney 		if (ddi_copyin(datap, &isa_irq_trigger,
9707c8c0b82SPatrick Mooney 		    sizeof (isa_irq_trigger), md)) {
9717c8c0b82SPatrick Mooney 			error = EFAULT;
9727c8c0b82SPatrick Mooney 			break;
9737c8c0b82SPatrick Mooney 		}
9747c8c0b82SPatrick Mooney 		error = vatpic_set_irq_trigger(sc->vmm_vm,
9757c8c0b82SPatrick Mooney 		    isa_irq_trigger.atpic_irq, isa_irq_trigger.trigger);
9767c8c0b82SPatrick Mooney 		break;
9777c8c0b82SPatrick Mooney 	}
9787c8c0b82SPatrick Mooney 
9797c8c0b82SPatrick Mooney 	case VM_MMAP_GETNEXT: {
9807c8c0b82SPatrick Mooney 		struct vm_memmap mm;
9817c8c0b82SPatrick Mooney 
9827c8c0b82SPatrick Mooney 		if (ddi_copyin(datap, &mm, sizeof (mm), md)) {
9837c8c0b82SPatrick Mooney 			error = EFAULT;
9847c8c0b82SPatrick Mooney 			break;
9857c8c0b82SPatrick Mooney 		}
9867c8c0b82SPatrick Mooney 		error = vm_mmap_getnext(sc->vmm_vm, &mm.gpa, &mm.segid,
9877c8c0b82SPatrick Mooney 		    &mm.segoff, &mm.len, &mm.prot, &mm.flags);
9887c8c0b82SPatrick Mooney 		if (error == 0 && ddi_copyout(&mm, datap, sizeof (mm), md)) {
9897c8c0b82SPatrick Mooney 			error = EFAULT;
9907c8c0b82SPatrick Mooney 			break;
9917c8c0b82SPatrick Mooney 		}
9927c8c0b82SPatrick Mooney 		break;
9937c8c0b82SPatrick Mooney 	}
9947c8c0b82SPatrick Mooney 	case VM_MMAP_MEMSEG: {
9957c8c0b82SPatrick Mooney 		struct vm_memmap mm;
9967c8c0b82SPatrick Mooney 
9977c8c0b82SPatrick Mooney 		if (ddi_copyin(datap, &mm, sizeof (mm), md)) {
9987c8c0b82SPatrick Mooney 			error = EFAULT;
9997c8c0b82SPatrick Mooney 			break;
10007c8c0b82SPatrick Mooney 		}
10017c8c0b82SPatrick Mooney 		error = vm_mmap_memseg(sc->vmm_vm, mm.gpa, mm.segid, mm.segoff,
10027c8c0b82SPatrick Mooney 		    mm.len, mm.prot, mm.flags);
10037c8c0b82SPatrick Mooney 		break;
10047c8c0b82SPatrick Mooney 	}
10057c8c0b82SPatrick Mooney 	case VM_MUNMAP_MEMSEG: {
10067c8c0b82SPatrick Mooney 		struct vm_munmap mu;
10077c8c0b82SPatrick Mooney 
10087c8c0b82SPatrick Mooney 		if (ddi_copyin(datap, &mu, sizeof (mu), md)) {
10097c8c0b82SPatrick Mooney 			error = EFAULT;
10107c8c0b82SPatrick Mooney 			break;
10117c8c0b82SPatrick Mooney 		}
10127c8c0b82SPatrick Mooney 		error = vm_munmap_memseg(sc->vmm_vm, mu.gpa, mu.len);
10137c8c0b82SPatrick Mooney 		break;
10147c8c0b82SPatrick Mooney 	}
10157c8c0b82SPatrick Mooney 	case VM_ALLOC_MEMSEG: {
10167c8c0b82SPatrick Mooney 		struct vm_memseg vmseg;
10177c8c0b82SPatrick Mooney 
10187c8c0b82SPatrick Mooney 		if (ddi_copyin(datap, &vmseg, sizeof (vmseg), md)) {
10197c8c0b82SPatrick Mooney 			error = EFAULT;
10207c8c0b82SPatrick Mooney 			break;
10217c8c0b82SPatrick Mooney 		}
10227c8c0b82SPatrick Mooney 		error = vmmdev_alloc_memseg(sc, &vmseg);
10237c8c0b82SPatrick Mooney 		break;
10247c8c0b82SPatrick Mooney 	}
10257c8c0b82SPatrick Mooney 	case VM_GET_MEMSEG: {
10267c8c0b82SPatrick Mooney 		struct vm_memseg vmseg;
10277c8c0b82SPatrick Mooney 
10287c8c0b82SPatrick Mooney 		if (ddi_copyin(datap, &vmseg, sizeof (vmseg), md)) {
10297c8c0b82SPatrick Mooney 			error = EFAULT;
10307c8c0b82SPatrick Mooney 			break;
10317c8c0b82SPatrick Mooney 		}
10327c8c0b82SPatrick Mooney 		error = vmmdev_get_memseg(sc, &vmseg);
10337c8c0b82SPatrick Mooney 		if (error == 0 &&
10347c8c0b82SPatrick Mooney 		    ddi_copyout(&vmseg, datap, sizeof (vmseg), md)) {
10357c8c0b82SPatrick Mooney 			error = EFAULT;
10367c8c0b82SPatrick Mooney 			break;
10377c8c0b82SPatrick Mooney 		}
10387c8c0b82SPatrick Mooney 		break;
10397c8c0b82SPatrick Mooney 	}
10407c8c0b82SPatrick Mooney 	case VM_GET_REGISTER: {
10417c8c0b82SPatrick Mooney 		struct vm_register vmreg;
10427c8c0b82SPatrick Mooney 
10437c8c0b82SPatrick Mooney 		if (ddi_copyin(datap, &vmreg, sizeof (vmreg), md)) {
10447c8c0b82SPatrick Mooney 			error = EFAULT;
10457c8c0b82SPatrick Mooney 			break;
10467c8c0b82SPatrick Mooney 		}
10477c8c0b82SPatrick Mooney 		error = vm_get_register(sc->vmm_vm, vcpu, vmreg.regnum,
10487c8c0b82SPatrick Mooney 		    &vmreg.regval);
10497c8c0b82SPatrick Mooney 		if (error == 0 &&
10507c8c0b82SPatrick Mooney 		    ddi_copyout(&vmreg, datap, sizeof (vmreg), md)) {
10517c8c0b82SPatrick Mooney 			error = EFAULT;
10527c8c0b82SPatrick Mooney 			break;
10537c8c0b82SPatrick Mooney 		}
10547c8c0b82SPatrick Mooney 		break;
10557c8c0b82SPatrick Mooney 	}
10567c8c0b82SPatrick Mooney 	case VM_SET_REGISTER: {
10577c8c0b82SPatrick Mooney 		struct vm_register vmreg;
10587c8c0b82SPatrick Mooney 
10597c8c0b82SPatrick Mooney 		if (ddi_copyin(datap, &vmreg, sizeof (vmreg), md)) {
10607c8c0b82SPatrick Mooney 			error = EFAULT;
10617c8c0b82SPatrick Mooney 			break;
10627c8c0b82SPatrick Mooney 		}
10637c8c0b82SPatrick Mooney 		error = vm_set_register(sc->vmm_vm, vcpu, vmreg.regnum,
10647c8c0b82SPatrick Mooney 		    vmreg.regval);
10657c8c0b82SPatrick Mooney 		break;
10667c8c0b82SPatrick Mooney 	}
10677c8c0b82SPatrick Mooney 	case VM_SET_SEGMENT_DESCRIPTOR: {
10687c8c0b82SPatrick Mooney 		struct vm_seg_desc vmsegd;
10697c8c0b82SPatrick Mooney 
10707c8c0b82SPatrick Mooney 		if (ddi_copyin(datap, &vmsegd, sizeof (vmsegd), md)) {
10717c8c0b82SPatrick Mooney 			error = EFAULT;
10727c8c0b82SPatrick Mooney 			break;
10737c8c0b82SPatrick Mooney 		}
10747c8c0b82SPatrick Mooney 		error = vm_set_seg_desc(sc->vmm_vm, vcpu, vmsegd.regnum,
10757c8c0b82SPatrick Mooney 		    &vmsegd.desc);
10767c8c0b82SPatrick Mooney 		break;
10777c8c0b82SPatrick Mooney 	}
10787c8c0b82SPatrick Mooney 	case VM_GET_SEGMENT_DESCRIPTOR: {
10797c8c0b82SPatrick Mooney 		struct vm_seg_desc vmsegd;
10807c8c0b82SPatrick Mooney 
10817c8c0b82SPatrick Mooney 		if (ddi_copyin(datap, &vmsegd, sizeof (vmsegd), md)) {
10827c8c0b82SPatrick Mooney 			error = EFAULT;
10837c8c0b82SPatrick Mooney 			break;
10847c8c0b82SPatrick Mooney 		}
10857c8c0b82SPatrick Mooney 		error = vm_get_seg_desc(sc->vmm_vm, vcpu, vmsegd.regnum,
10867c8c0b82SPatrick Mooney 		    &vmsegd.desc);
10877c8c0b82SPatrick Mooney 		if (error == 0 &&
10887c8c0b82SPatrick Mooney 		    ddi_copyout(&vmsegd, datap, sizeof (vmsegd), md)) {
10897c8c0b82SPatrick Mooney 			error = EFAULT;
10907c8c0b82SPatrick Mooney 			break;
10917c8c0b82SPatrick Mooney 		}
10927c8c0b82SPatrick Mooney 		break;
10937c8c0b82SPatrick Mooney 	}
10947c8c0b82SPatrick Mooney 	case VM_GET_REGISTER_SET: {
10957c8c0b82SPatrick Mooney 		struct vm_register_set vrs;
10967c8c0b82SPatrick Mooney 		int regnums[VM_REG_LAST];
10977c8c0b82SPatrick Mooney 		uint64_t regvals[VM_REG_LAST];
10987c8c0b82SPatrick Mooney 
10997c8c0b82SPatrick Mooney 		if (ddi_copyin(datap, &vrs, sizeof (vrs), md)) {
11007c8c0b82SPatrick Mooney 			error = EFAULT;
11017c8c0b82SPatrick Mooney 			break;
11027c8c0b82SPatrick Mooney 		}
11037c8c0b82SPatrick Mooney 		if (vrs.count > VM_REG_LAST || vrs.count == 0) {
11047c8c0b82SPatrick Mooney 			error = EINVAL;
11057c8c0b82SPatrick Mooney 			break;
11067c8c0b82SPatrick Mooney 		}
11077c8c0b82SPatrick Mooney 		if (ddi_copyin(vrs.regnums, regnums,
11087c8c0b82SPatrick Mooney 		    sizeof (int) * vrs.count, md)) {
11097c8c0b82SPatrick Mooney 			error = EFAULT;
11107c8c0b82SPatrick Mooney 			break;
11117c8c0b82SPatrick Mooney 		}
11127c8c0b82SPatrick Mooney 
11137c8c0b82SPatrick Mooney 		error = 0;
11147c8c0b82SPatrick Mooney 		for (uint_t i = 0; i < vrs.count && error == 0; i++) {
11157c8c0b82SPatrick Mooney 			if (regnums[i] < 0) {
11167c8c0b82SPatrick Mooney 				error = EINVAL;
11177c8c0b82SPatrick Mooney 				break;
11187c8c0b82SPatrick Mooney 			}
11197c8c0b82SPatrick Mooney 			error = vm_get_register(sc->vmm_vm, vcpu, regnums[i],
11207c8c0b82SPatrick Mooney 			    &regvals[i]);
11217c8c0b82SPatrick Mooney 		}
11227c8c0b82SPatrick Mooney 		if (error == 0 && ddi_copyout(regvals, vrs.regvals,
11237c8c0b82SPatrick Mooney 		    sizeof (uint64_t) * vrs.count, md)) {
11247c8c0b82SPatrick Mooney 			error = EFAULT;
11257c8c0b82SPatrick Mooney 		}
11267c8c0b82SPatrick Mooney 		break;
11277c8c0b82SPatrick Mooney 	}
11287c8c0b82SPatrick Mooney 	case VM_SET_REGISTER_SET: {
11297c8c0b82SPatrick Mooney 		struct vm_register_set vrs;
11307c8c0b82SPatrick Mooney 		int regnums[VM_REG_LAST];
11317c8c0b82SPatrick Mooney 		uint64_t regvals[VM_REG_LAST];
11327c8c0b82SPatrick Mooney 
11337c8c0b82SPatrick Mooney 		if (ddi_copyin(datap, &vrs, sizeof (vrs), md)) {
11347c8c0b82SPatrick Mooney 			error = EFAULT;
11357c8c0b82SPatrick Mooney 			break;
11367c8c0b82SPatrick Mooney 		}
11377c8c0b82SPatrick Mooney 		if (vrs.count > VM_REG_LAST || vrs.count == 0) {
11387c8c0b82SPatrick Mooney 			error = EINVAL;
11397c8c0b82SPatrick Mooney 			break;
11407c8c0b82SPatrick Mooney 		}
11417c8c0b82SPatrick Mooney 		if (ddi_copyin(vrs.regnums, regnums,
11427c8c0b82SPatrick Mooney 		    sizeof (int) * vrs.count, md)) {
11437c8c0b82SPatrick Mooney 			error = EFAULT;
11447c8c0b82SPatrick Mooney 			break;
11457c8c0b82SPatrick Mooney 		}
11467c8c0b82SPatrick Mooney 		if (ddi_copyin(vrs.regvals, regvals,
11477c8c0b82SPatrick Mooney 		    sizeof (uint64_t) * vrs.count, md)) {
11487c8c0b82SPatrick Mooney 			error = EFAULT;
11497c8c0b82SPatrick Mooney 			break;
11507c8c0b82SPatrick Mooney 		}
11517c8c0b82SPatrick Mooney 
11527c8c0b82SPatrick Mooney 		error = 0;
11537c8c0b82SPatrick Mooney 		for (uint_t i = 0; i < vrs.count && error == 0; i++) {
11547c8c0b82SPatrick Mooney 			/*
11557c8c0b82SPatrick Mooney 			 * Setting registers in a set is not atomic, since a
11567c8c0b82SPatrick Mooney 			 * failure in the middle of the set will cause a
11577c8c0b82SPatrick Mooney 			 * bail-out and inconsistent register state.  Callers
11587c8c0b82SPatrick Mooney 			 * should be wary of this.
11597c8c0b82SPatrick Mooney 			 */
11607c8c0b82SPatrick Mooney 			if (regnums[i] < 0) {
11617c8c0b82SPatrick Mooney 				error = EINVAL;
11627c8c0b82SPatrick Mooney 				break;
11637c8c0b82SPatrick Mooney 			}
11647c8c0b82SPatrick Mooney 			error = vm_set_register(sc->vmm_vm, vcpu, regnums[i],
11657c8c0b82SPatrick Mooney 			    regvals[i]);
11667c8c0b82SPatrick Mooney 		}
11677c8c0b82SPatrick Mooney 		break;
11687c8c0b82SPatrick Mooney 	}
11697c8c0b82SPatrick Mooney 	case VM_RESET_CPU: {
11707c8c0b82SPatrick Mooney 		struct vm_vcpu_reset vvr;
11717c8c0b82SPatrick Mooney 
11727c8c0b82SPatrick Mooney 		if (ddi_copyin(datap, &vvr, sizeof (vvr), md)) {
11737c8c0b82SPatrick Mooney 			error = EFAULT;
11747c8c0b82SPatrick Mooney 			break;
11757c8c0b82SPatrick Mooney 		}
11767c8c0b82SPatrick Mooney 		if (vvr.kind != VRK_RESET && vvr.kind != VRK_INIT) {
11777c8c0b82SPatrick Mooney 			error = EINVAL;
11787c8c0b82SPatrick Mooney 		}
11797c8c0b82SPatrick Mooney 
11807c8c0b82SPatrick Mooney 		error = vcpu_arch_reset(sc->vmm_vm, vcpu, vvr.kind == VRK_INIT);
11817c8c0b82SPatrick Mooney 		break;
11827c8c0b82SPatrick Mooney 	}
11837c8c0b82SPatrick Mooney 	case VM_GET_RUN_STATE: {
11847c8c0b82SPatrick Mooney 		struct vm_run_state vrs;
11857c8c0b82SPatrick Mooney 
11867c8c0b82SPatrick Mooney 		bzero(&vrs, sizeof (vrs));
11877c8c0b82SPatrick Mooney 		error = vm_get_run_state(sc->vmm_vm, vcpu, &vrs.state,
11887c8c0b82SPatrick Mooney 		    &vrs.sipi_vector);
11897c8c0b82SPatrick Mooney 		if (error == 0) {
11907c8c0b82SPatrick Mooney 			if (ddi_copyout(&vrs, datap, sizeof (vrs), md)) {
11917c8c0b82SPatrick Mooney 				error = EFAULT;
11927c8c0b82SPatrick Mooney 				break;
11937c8c0b82SPatrick Mooney 			}
11947c8c0b82SPatrick Mooney 		}
11957c8c0b82SPatrick Mooney 		break;
11967c8c0b82SPatrick Mooney 	}
11977c8c0b82SPatrick Mooney 	case VM_SET_RUN_STATE: {
11987c8c0b82SPatrick Mooney 		struct vm_run_state vrs;
11997c8c0b82SPatrick Mooney 
12007c8c0b82SPatrick Mooney 		if (ddi_copyin(datap, &vrs, sizeof (vrs), md)) {
12017c8c0b82SPatrick Mooney 			error = EFAULT;
12027c8c0b82SPatrick Mooney 			break;
12037c8c0b82SPatrick Mooney 		}
12047c8c0b82SPatrick Mooney 		error = vm_set_run_state(sc->vmm_vm, vcpu, vrs.state,
12057c8c0b82SPatrick Mooney 		    vrs.sipi_vector);
12067c8c0b82SPatrick Mooney 		break;
12077c8c0b82SPatrick Mooney 	}
12087c8c0b82SPatrick Mooney 	case VM_GET_FPU: {
12097c8c0b82SPatrick Mooney 		struct vm_fpu_state req;
12107c8c0b82SPatrick Mooney 		const size_t max_len = (PAGESIZE * 2);
12117c8c0b82SPatrick Mooney 		void *kbuf;
12127c8c0b82SPatrick Mooney 
12137c8c0b82SPatrick Mooney 		if (ddi_copyin(datap, &req, sizeof (req), md)) {
12147c8c0b82SPatrick Mooney 			error = EFAULT;
12157c8c0b82SPatrick Mooney 			break;
12167c8c0b82SPatrick Mooney 		}
12177c8c0b82SPatrick Mooney 		if (req.len > max_len || req.len == 0) {
12187c8c0b82SPatrick Mooney 			error = EINVAL;
12197c8c0b82SPatrick Mooney 			break;
12207c8c0b82SPatrick Mooney 		}
12217c8c0b82SPatrick Mooney 		kbuf = kmem_zalloc(req.len, KM_SLEEP);
12227c8c0b82SPatrick Mooney 		error = vm_get_fpu(sc->vmm_vm, vcpu, kbuf, req.len);
12237c8c0b82SPatrick Mooney 		if (error == 0) {
12247c8c0b82SPatrick Mooney 			if (ddi_copyout(kbuf, req.buf, req.len, md)) {
12257c8c0b82SPatrick Mooney 				error = EFAULT;
12267c8c0b82SPatrick Mooney 			}
12277c8c0b82SPatrick Mooney 		}
12287c8c0b82SPatrick Mooney 		kmem_free(kbuf, req.len);
12297c8c0b82SPatrick Mooney 		break;
12307c8c0b82SPatrick Mooney 	}
12317c8c0b82SPatrick Mooney 	case VM_SET_FPU: {
12327c8c0b82SPatrick Mooney 		struct vm_fpu_state req;
12337c8c0b82SPatrick Mooney 		const size_t max_len = (PAGESIZE * 2);
12347c8c0b82SPatrick Mooney 		void *kbuf;
12357c8c0b82SPatrick Mooney 
12367c8c0b82SPatrick Mooney 		if (ddi_copyin(datap, &req, sizeof (req), md)) {
12377c8c0b82SPatrick Mooney 			error = EFAULT;
12387c8c0b82SPatrick Mooney 			break;
12397c8c0b82SPatrick Mooney 		}
12407c8c0b82SPatrick Mooney 		if (req.len > max_len || req.len == 0) {
12417c8c0b82SPatrick Mooney 			error = EINVAL;
12427c8c0b82SPatrick Mooney 			break;
12437c8c0b82SPatrick Mooney 		}
12447c8c0b82SPatrick Mooney 		kbuf = kmem_alloc(req.len, KM_SLEEP);
12457c8c0b82SPatrick Mooney 		if (ddi_copyin(req.buf, kbuf, req.len, md)) {
12467c8c0b82SPatrick Mooney 			error = EFAULT;
12477c8c0b82SPatrick Mooney 		} else {
12487c8c0b82SPatrick Mooney 			error = vm_set_fpu(sc->vmm_vm, vcpu, kbuf, req.len);
12497c8c0b82SPatrick Mooney 		}
12507c8c0b82SPatrick Mooney 		kmem_free(kbuf, req.len);
12517c8c0b82SPatrick Mooney 		break;
12527c8c0b82SPatrick Mooney 	}
1253578d9a56SPatrick Mooney 	case VM_GET_CPUID: {
1254578d9a56SPatrick Mooney 		struct vm_vcpu_cpuid_config cfg;
1255578d9a56SPatrick Mooney 		struct vcpu_cpuid_entry *entries = NULL;
1256578d9a56SPatrick Mooney 
1257578d9a56SPatrick Mooney 		if (ddi_copyin(datap, &cfg, sizeof (cfg), md)) {
1258578d9a56SPatrick Mooney 			error = EFAULT;
1259578d9a56SPatrick Mooney 			break;
1260578d9a56SPatrick Mooney 		}
1261578d9a56SPatrick Mooney 		if (cfg.vvcc_nent > VMM_MAX_CPUID_ENTRIES) {
1262578d9a56SPatrick Mooney 			error = EINVAL;
1263578d9a56SPatrick Mooney 			break;
1264578d9a56SPatrick Mooney 		}
1265578d9a56SPatrick Mooney 
1266578d9a56SPatrick Mooney 		const size_t entries_size =
1267578d9a56SPatrick Mooney 		    cfg.vvcc_nent * sizeof (struct vcpu_cpuid_entry);
1268578d9a56SPatrick Mooney 		if (entries_size != 0) {
1269578d9a56SPatrick Mooney 			entries = kmem_zalloc(entries_size, KM_SLEEP);
1270578d9a56SPatrick Mooney 		}
1271578d9a56SPatrick Mooney 
1272578d9a56SPatrick Mooney 		vcpu_cpuid_config_t vm_cfg = {
1273578d9a56SPatrick Mooney 			.vcc_nent = cfg.vvcc_nent,
1274578d9a56SPatrick Mooney 			.vcc_entries = entries,
1275578d9a56SPatrick Mooney 		};
1276578d9a56SPatrick Mooney 		error = vm_get_cpuid(sc->vmm_vm, vcpu, &vm_cfg);
1277578d9a56SPatrick Mooney 
1278578d9a56SPatrick Mooney 		/*
1279578d9a56SPatrick Mooney 		 * Only attempt to copy out the resultant entries if we were
1280578d9a56SPatrick Mooney 		 * able to query them from the instance.  The flags and number
1281578d9a56SPatrick Mooney 		 * of entries are emitted regardless.
1282578d9a56SPatrick Mooney 		 */
1283578d9a56SPatrick Mooney 		cfg.vvcc_flags = vm_cfg.vcc_flags;
1284578d9a56SPatrick Mooney 		cfg.vvcc_nent = vm_cfg.vcc_nent;
1285578d9a56SPatrick Mooney 		if (entries != NULL) {
1286578d9a56SPatrick Mooney 			if (error == 0 && ddi_copyout(entries, cfg.vvcc_entries,
1287578d9a56SPatrick Mooney 			    entries_size, md) != 0) {
1288578d9a56SPatrick Mooney 				error = EFAULT;
1289578d9a56SPatrick Mooney 			}
1290578d9a56SPatrick Mooney 
1291578d9a56SPatrick Mooney 			kmem_free(entries, entries_size);
1292578d9a56SPatrick Mooney 		}
1293578d9a56SPatrick Mooney 
1294578d9a56SPatrick Mooney 		if (ddi_copyout(&cfg, datap, sizeof (cfg), md) != 0) {
1295578d9a56SPatrick Mooney 			error = EFAULT;
1296578d9a56SPatrick Mooney 		}
1297578d9a56SPatrick Mooney 		break;
1298578d9a56SPatrick Mooney 	}
1299578d9a56SPatrick Mooney 	case VM_SET_CPUID: {
1300578d9a56SPatrick Mooney 		struct vm_vcpu_cpuid_config cfg;
1301578d9a56SPatrick Mooney 		struct vcpu_cpuid_entry *entries = NULL;
1302578d9a56SPatrick Mooney 		size_t entries_size = 0;
1303578d9a56SPatrick Mooney 
1304578d9a56SPatrick Mooney 		if (ddi_copyin(datap, &cfg, sizeof (cfg), md)) {
1305578d9a56SPatrick Mooney 			error = EFAULT;
1306578d9a56SPatrick Mooney 			break;
1307578d9a56SPatrick Mooney 		}
1308578d9a56SPatrick Mooney 		if (cfg.vvcc_nent > VMM_MAX_CPUID_ENTRIES) {
1309578d9a56SPatrick Mooney 			error = EFBIG;
1310578d9a56SPatrick Mooney 			break;
1311578d9a56SPatrick Mooney 		}
1312578d9a56SPatrick Mooney 		if ((cfg.vvcc_flags & VCC_FLAG_LEGACY_HANDLING) != 0) {
1313578d9a56SPatrick Mooney 			/*
1314578d9a56SPatrick Mooney 			 * If we are being instructed to use "legacy" handling,
1315578d9a56SPatrick Mooney 			 * then no entries should be provided, since the static
1316578d9a56SPatrick Mooney 			 * in-kernel masking will be used.
1317578d9a56SPatrick Mooney 			 */
1318578d9a56SPatrick Mooney 			if (cfg.vvcc_nent != 0) {
1319578d9a56SPatrick Mooney 				error = EINVAL;
1320578d9a56SPatrick Mooney 				break;
1321578d9a56SPatrick Mooney 			}
1322578d9a56SPatrick Mooney 		} else if (cfg.vvcc_nent != 0) {
1323578d9a56SPatrick Mooney 			entries_size =
1324578d9a56SPatrick Mooney 			    cfg.vvcc_nent * sizeof (struct vcpu_cpuid_entry);
1325578d9a56SPatrick Mooney 			entries = kmem_alloc(entries_size, KM_SLEEP);
1326578d9a56SPatrick Mooney 
1327578d9a56SPatrick Mooney 			if (ddi_copyin(cfg.vvcc_entries, entries, entries_size,
1328578d9a56SPatrick Mooney 			    md) != 0) {
1329578d9a56SPatrick Mooney 				error = EFAULT;
1330578d9a56SPatrick Mooney 				kmem_free(entries, entries_size);
1331578d9a56SPatrick Mooney 				break;
1332578d9a56SPatrick Mooney 			}
1333578d9a56SPatrick Mooney 		}
1334578d9a56SPatrick Mooney 
1335578d9a56SPatrick Mooney 		vcpu_cpuid_config_t vm_cfg = {
1336578d9a56SPatrick Mooney 			.vcc_flags = cfg.vvcc_flags,
1337578d9a56SPatrick Mooney 			.vcc_nent = cfg.vvcc_nent,
1338578d9a56SPatrick Mooney 			.vcc_entries = entries,
1339578d9a56SPatrick Mooney 		};
1340578d9a56SPatrick Mooney 		error = vm_set_cpuid(sc->vmm_vm, vcpu, &vm_cfg);
1341578d9a56SPatrick Mooney 
1342578d9a56SPatrick Mooney 		if (entries != NULL) {
1343578d9a56SPatrick Mooney 			kmem_free(entries, entries_size);
1344578d9a56SPatrick Mooney 		}
1345578d9a56SPatrick Mooney 		break;
1346578d9a56SPatrick Mooney 	}
1347578d9a56SPatrick Mooney 	case VM_LEGACY_CPUID: {
1348578d9a56SPatrick Mooney 		struct vm_legacy_cpuid vlc;
1349578d9a56SPatrick Mooney 		if (ddi_copyin(datap, &vlc, sizeof (vlc), md)) {
1350578d9a56SPatrick Mooney 			error = EFAULT;
1351578d9a56SPatrick Mooney 			break;
1352578d9a56SPatrick Mooney 		}
1353578d9a56SPatrick Mooney 		vlc.vlc_vcpuid = vcpu;
1354578d9a56SPatrick Mooney 
1355578d9a56SPatrick Mooney 		legacy_emulate_cpuid(sc->vmm_vm, vcpu, &vlc.vlc_eax,
1356578d9a56SPatrick Mooney 		    &vlc.vlc_ebx, &vlc.vlc_ecx, &vlc.vlc_edx);
1357578d9a56SPatrick Mooney 
1358578d9a56SPatrick Mooney 		if (ddi_copyout(&vlc, datap, sizeof (vlc), md)) {
1359578d9a56SPatrick Mooney 			error = EFAULT;
1360578d9a56SPatrick Mooney 			break;
1361578d9a56SPatrick Mooney 		}
1362578d9a56SPatrick Mooney 		break;
1363578d9a56SPatrick Mooney 	}
13647c8c0b82SPatrick Mooney 
13657c8c0b82SPatrick Mooney 	case VM_SET_KERNEMU_DEV:
13667c8c0b82SPatrick Mooney 	case VM_GET_KERNEMU_DEV: {
13677c8c0b82SPatrick Mooney 		struct vm_readwrite_kernemu_device kemu;
13687c8c0b82SPatrick Mooney 		size_t size = 0;
13697c8c0b82SPatrick Mooney 
13707c8c0b82SPatrick Mooney 		if (ddi_copyin(datap, &kemu, sizeof (kemu), md)) {
13717c8c0b82SPatrick Mooney 			error = EFAULT;
13727c8c0b82SPatrick Mooney 			break;
13737c8c0b82SPatrick Mooney 		}
13747c8c0b82SPatrick Mooney 
13757c8c0b82SPatrick Mooney 		if (kemu.access_width > 3) {
13767c8c0b82SPatrick Mooney 			error = EINVAL;
13777c8c0b82SPatrick Mooney 			break;
13787c8c0b82SPatrick Mooney 		}
13797c8c0b82SPatrick Mooney 		size = (1 << kemu.access_width);
13807c8c0b82SPatrick Mooney 		ASSERT(size >= 1 && size <= 8);
13817c8c0b82SPatrick Mooney 
13827c8c0b82SPatrick Mooney 		if (cmd == VM_SET_KERNEMU_DEV) {
13837c8c0b82SPatrick Mooney 			error = vm_service_mmio_write(sc->vmm_vm, vcpu,
13847c8c0b82SPatrick Mooney 			    kemu.gpa, kemu.value, size);
13857c8c0b82SPatrick Mooney 		} else {
13867c8c0b82SPatrick Mooney 			error = vm_service_mmio_read(sc->vmm_vm, vcpu,
13877c8c0b82SPatrick Mooney 			    kemu.gpa, &kemu.value, size);
13887c8c0b82SPatrick Mooney 		}
13897c8c0b82SPatrick Mooney 
13907c8c0b82SPatrick Mooney 		if (error == 0) {
13917c8c0b82SPatrick Mooney 			if (ddi_copyout(&kemu, datap, sizeof (kemu), md)) {
13927c8c0b82SPatrick Mooney 				error = EFAULT;
13937c8c0b82SPatrick Mooney 				break;
13947c8c0b82SPatrick Mooney 			}
13957c8c0b82SPatrick Mooney 		}
13967c8c0b82SPatrick Mooney 		break;
13977c8c0b82SPatrick Mooney 	}
13987c8c0b82SPatrick Mooney 
13997c8c0b82SPatrick Mooney 	case VM_GET_CAPABILITY: {
14007c8c0b82SPatrick Mooney 		struct vm_capability vmcap;
14017c8c0b82SPatrick Mooney 
14027c8c0b82SPatrick Mooney 		if (ddi_copyin(datap, &vmcap, sizeof (vmcap), md)) {
14037c8c0b82SPatrick Mooney 			error = EFAULT;
14047c8c0b82SPatrick Mooney 			break;
14057c8c0b82SPatrick Mooney 		}
14067c8c0b82SPatrick Mooney 		error = vm_get_capability(sc->vmm_vm, vcpu, vmcap.captype,
14077c8c0b82SPatrick Mooney 		    &vmcap.capval);
14087c8c0b82SPatrick Mooney 		if (error == 0 &&
14097c8c0b82SPatrick Mooney 		    ddi_copyout(&vmcap, datap, sizeof (vmcap), md)) {
14107c8c0b82SPatrick Mooney 			error = EFAULT;
14117c8c0b82SPatrick Mooney 			break;
14127c8c0b82SPatrick Mooney 		}
14137c8c0b82SPatrick Mooney 		break;
14147c8c0b82SPatrick Mooney 	}
14157c8c0b82SPatrick Mooney 	case VM_SET_CAPABILITY: {
14167c8c0b82SPatrick Mooney 		struct vm_capability vmcap;
14177c8c0b82SPatrick Mooney 
14187c8c0b82SPatrick Mooney 		if (ddi_copyin(datap, &vmcap, sizeof (vmcap), md)) {
14197c8c0b82SPatrick Mooney 			error = EFAULT;
14207c8c0b82SPatrick Mooney 			break;
14217c8c0b82SPatrick Mooney 		}
14227c8c0b82SPatrick Mooney 		error = vm_set_capability(sc->vmm_vm, vcpu, vmcap.captype,
14237c8c0b82SPatrick Mooney 		    vmcap.capval);
14247c8c0b82SPatrick Mooney 		break;
14257c8c0b82SPatrick Mooney 	}
14267c8c0b82SPatrick Mooney 	case VM_SET_X2APIC_STATE: {
14277c8c0b82SPatrick Mooney 		struct vm_x2apic x2apic;
14287c8c0b82SPatrick Mooney 
14297c8c0b82SPatrick Mooney 		if (ddi_copyin(datap, &x2apic, sizeof (x2apic), md)) {
14307c8c0b82SPatrick Mooney 			error = EFAULT;
14317c8c0b82SPatrick Mooney 			break;
14327c8c0b82SPatrick Mooney 		}
14337c8c0b82SPatrick Mooney 		error = vm_set_x2apic_state(sc->vmm_vm, vcpu, x2apic.state);
14347c8c0b82SPatrick Mooney 		break;
14357c8c0b82SPatrick Mooney 	}
14367c8c0b82SPatrick Mooney 	case VM_GET_X2APIC_STATE: {
14377c8c0b82SPatrick Mooney 		struct vm_x2apic x2apic;
14387c8c0b82SPatrick Mooney 
14397c8c0b82SPatrick Mooney 		if (ddi_copyin(datap, &x2apic, sizeof (x2apic), md)) {
14407c8c0b82SPatrick Mooney 			error = EFAULT;
14417c8c0b82SPatrick Mooney 			break;
14427c8c0b82SPatrick Mooney 		}
14437c8c0b82SPatrick Mooney 		error = vm_get_x2apic_state(sc->vmm_vm, x2apic.cpuid,
14447c8c0b82SPatrick Mooney 		    &x2apic.state);
14457c8c0b82SPatrick Mooney 		if (error == 0 &&
14467c8c0b82SPatrick Mooney 		    ddi_copyout(&x2apic, datap, sizeof (x2apic), md)) {
14477c8c0b82SPatrick Mooney 			error = EFAULT;
14487c8c0b82SPatrick Mooney 			break;
14497c8c0b82SPatrick Mooney 		}
14507c8c0b82SPatrick Mooney 		break;
14517c8c0b82SPatrick Mooney 	}
14527c8c0b82SPatrick Mooney 	case VM_GET_GPA_PMAP: {
14537c8c0b82SPatrick Mooney 		/*
14547c8c0b82SPatrick Mooney 		 * Until there is a necessity to leak EPT/RVI PTE values to
14557c8c0b82SPatrick Mooney 		 * userspace, this will remain unimplemented
14567c8c0b82SPatrick Mooney 		 */
14577c8c0b82SPatrick Mooney 		error = EINVAL;
14587c8c0b82SPatrick Mooney 		break;
14597c8c0b82SPatrick Mooney 	}
14607c8c0b82SPatrick Mooney 	case VM_GET_HPET_CAPABILITIES: {
14617c8c0b82SPatrick Mooney 		struct vm_hpet_cap hpetcap;
14627c8c0b82SPatrick Mooney 
14637c8c0b82SPatrick Mooney 		error = vhpet_getcap(&hpetcap);
14647c8c0b82SPatrick Mooney 		if (error == 0 &&
14657c8c0b82SPatrick Mooney 		    ddi_copyout(&hpetcap, datap, sizeof (hpetcap), md)) {
14667c8c0b82SPatrick Mooney 			error = EFAULT;
14677c8c0b82SPatrick Mooney 			break;
14687c8c0b82SPatrick Mooney 		}
14697c8c0b82SPatrick Mooney 		break;
14707c8c0b82SPatrick Mooney 	}
14717c8c0b82SPatrick Mooney 	case VM_GLA2GPA: {
14727c8c0b82SPatrick Mooney 		struct vm_gla2gpa gg;
14737c8c0b82SPatrick Mooney 
14747c8c0b82SPatrick Mooney 		if (ddi_copyin(datap, &gg, sizeof (gg), md)) {
14757c8c0b82SPatrick Mooney 			error = EFAULT;
14767c8c0b82SPatrick Mooney 			break;
14777c8c0b82SPatrick Mooney 		}
14787c8c0b82SPatrick Mooney 		gg.vcpuid = vcpu;
14797c8c0b82SPatrick Mooney 		error = vm_gla2gpa(sc->vmm_vm, vcpu, &gg.paging, gg.gla,
14807c8c0b82SPatrick Mooney 		    gg.prot, &gg.gpa, &gg.fault);
14817c8c0b82SPatrick Mooney 		if (error == 0 && ddi_copyout(&gg, datap, sizeof (gg), md)) {
14827c8c0b82SPatrick Mooney 			error = EFAULT;
14837c8c0b82SPatrick Mooney 			break;
14847c8c0b82SPatrick Mooney 		}
14857c8c0b82SPatrick Mooney 		break;
14867c8c0b82SPatrick Mooney 	}
14877c8c0b82SPatrick Mooney 	case VM_GLA2GPA_NOFAULT: {
14887c8c0b82SPatrick Mooney 		struct vm_gla2gpa gg;
14897c8c0b82SPatrick Mooney 
14907c8c0b82SPatrick Mooney 		if (ddi_copyin(datap, &gg, sizeof (gg), md)) {
14917c8c0b82SPatrick Mooney 			error = EFAULT;
14927c8c0b82SPatrick Mooney 			break;
14937c8c0b82SPatrick Mooney 		}
14947c8c0b82SPatrick Mooney 		gg.vcpuid = vcpu;
14957c8c0b82SPatrick Mooney 		error = vm_gla2gpa_nofault(sc->vmm_vm, vcpu, &gg.paging,
14967c8c0b82SPatrick Mooney 		    gg.gla, gg.prot, &gg.gpa, &gg.fault);
14977c8c0b82SPatrick Mooney 		if (error == 0 && ddi_copyout(&gg, datap, sizeof (gg), md)) {
14987c8c0b82SPatrick Mooney 			error = EFAULT;
14997c8c0b82SPatrick Mooney 			break;
15007c8c0b82SPatrick Mooney 		}
15017c8c0b82SPatrick Mooney 		break;
15027c8c0b82SPatrick Mooney 	}
15037c8c0b82SPatrick Mooney 
15047c8c0b82SPatrick Mooney 	case VM_ACTIVATE_CPU:
15057c8c0b82SPatrick Mooney 		error = vm_activate_cpu(sc->vmm_vm, vcpu);
15067c8c0b82SPatrick Mooney 		break;
15077c8c0b82SPatrick Mooney 
15087c8c0b82SPatrick Mooney 	case VM_SUSPEND_CPU:
15097c8c0b82SPatrick Mooney 		if (ddi_copyin(datap, &vcpu, sizeof (vcpu), md)) {
15107c8c0b82SPatrick Mooney 			error = EFAULT;
15117c8c0b82SPatrick Mooney 		} else {
15127c8c0b82SPatrick Mooney 			error = vm_suspend_cpu(sc->vmm_vm, vcpu);
15137c8c0b82SPatrick Mooney 		}
15147c8c0b82SPatrick Mooney 		break;
15157c8c0b82SPatrick Mooney 
15167c8c0b82SPatrick Mooney 	case VM_RESUME_CPU:
15177c8c0b82SPatrick Mooney 		if (ddi_copyin(datap, &vcpu, sizeof (vcpu), md)) {
15187c8c0b82SPatrick Mooney 			error = EFAULT;
15197c8c0b82SPatrick Mooney 		} else {
15207c8c0b82SPatrick Mooney 			error = vm_resume_cpu(sc->vmm_vm, vcpu);
15217c8c0b82SPatrick Mooney 		}
15227c8c0b82SPatrick Mooney 		break;
15237c8c0b82SPatrick Mooney 
152472473353SPatrick Mooney 	case VM_VCPU_BARRIER:
152572473353SPatrick Mooney 		vcpu = arg;
152672473353SPatrick Mooney 		error = vm_vcpu_barrier(sc->vmm_vm, vcpu);
152772473353SPatrick Mooney 		break;
152872473353SPatrick Mooney 
15297c8c0b82SPatrick Mooney 	case VM_GET_CPUS: {
15307c8c0b82SPatrick Mooney 		struct vm_cpuset vm_cpuset;
15317c8c0b82SPatrick Mooney 		cpuset_t tempset;
15327c8c0b82SPatrick Mooney 		void *srcp = &tempset;
15337c8c0b82SPatrick Mooney 		int size;
15347c8c0b82SPatrick Mooney 
15357c8c0b82SPatrick Mooney 		if (ddi_copyin(datap, &vm_cpuset, sizeof (vm_cpuset), md)) {
15367c8c0b82SPatrick Mooney 			error = EFAULT;
15377c8c0b82SPatrick Mooney 			break;
15387c8c0b82SPatrick Mooney 		}
15397c8c0b82SPatrick Mooney 
15407c8c0b82SPatrick Mooney 		/* Be more generous about sizing since our cpuset_t is large. */
15417c8c0b82SPatrick Mooney 		size = vm_cpuset.cpusetsize;
15427c8c0b82SPatrick Mooney 		if (size <= 0 || size > sizeof (cpuset_t)) {
15437c8c0b82SPatrick Mooney 			error = ERANGE;
15447c8c0b82SPatrick Mooney 		}
15457c8c0b82SPatrick Mooney 		/*
15467c8c0b82SPatrick Mooney 		 * If they want a ulong_t or less, make sure they receive the
15477c8c0b82SPatrick Mooney 		 * low bits with all the useful information.
15487c8c0b82SPatrick Mooney 		 */
15497c8c0b82SPatrick Mooney 		if (size <= sizeof (tempset.cpub[0])) {
15507c8c0b82SPatrick Mooney 			srcp = &tempset.cpub[0];
15517c8c0b82SPatrick Mooney 		}
15527c8c0b82SPatrick Mooney 
15537c8c0b82SPatrick Mooney 		if (vm_cpuset.which == VM_ACTIVE_CPUS) {
15547c8c0b82SPatrick Mooney 			tempset = vm_active_cpus(sc->vmm_vm);
15557c8c0b82SPatrick Mooney 		} else if (vm_cpuset.which == VM_DEBUG_CPUS) {
15567c8c0b82SPatrick Mooney 			tempset = vm_debug_cpus(sc->vmm_vm);
15577c8c0b82SPatrick Mooney 		} else {
15587c8c0b82SPatrick Mooney 			error = EINVAL;
15597c8c0b82SPatrick Mooney 		}
15607c8c0b82SPatrick Mooney 
15617c8c0b82SPatrick Mooney 		ASSERT(size > 0 && size <= sizeof (tempset));
15627c8c0b82SPatrick Mooney 		if (error == 0 &&
15637c8c0b82SPatrick Mooney 		    ddi_copyout(srcp, vm_cpuset.cpus, size, md)) {
15647c8c0b82SPatrick Mooney 			error = EFAULT;
15657c8c0b82SPatrick Mooney 			break;
15667c8c0b82SPatrick Mooney 		}
15677c8c0b82SPatrick Mooney 		break;
15687c8c0b82SPatrick Mooney 	}
15697c8c0b82SPatrick Mooney 	case VM_SET_INTINFO: {
15707c8c0b82SPatrick Mooney 		struct vm_intinfo vmii;
15717c8c0b82SPatrick Mooney 
15727c8c0b82SPatrick Mooney 		if (ddi_copyin(datap, &vmii, sizeof (vmii), md)) {
15737c8c0b82SPatrick Mooney 			error = EFAULT;
15747c8c0b82SPatrick Mooney 			break;
15757c8c0b82SPatrick Mooney 		}
15767c8c0b82SPatrick Mooney 		error = vm_exit_intinfo(sc->vmm_vm, vcpu, vmii.info1);
15777c8c0b82SPatrick Mooney 		break;
15787c8c0b82SPatrick Mooney 	}
15797c8c0b82SPatrick Mooney 	case VM_GET_INTINFO: {
15807c8c0b82SPatrick Mooney 		struct vm_intinfo vmii;
15817c8c0b82SPatrick Mooney 
15827c8c0b82SPatrick Mooney 		vmii.vcpuid = vcpu;
15837c8c0b82SPatrick Mooney 		error = vm_get_intinfo(sc->vmm_vm, vcpu, &vmii.info1,
15847c8c0b82SPatrick Mooney 		    &vmii.info2);
15857c8c0b82SPatrick Mooney 		if (error == 0 &&
15867c8c0b82SPatrick Mooney 		    ddi_copyout(&vmii, datap, sizeof (vmii), md)) {
15877c8c0b82SPatrick Mooney 			error = EFAULT;
15887c8c0b82SPatrick Mooney 			break;
15897c8c0b82SPatrick Mooney 		}
15907c8c0b82SPatrick Mooney 		break;
15917c8c0b82SPatrick Mooney 	}
15927c8c0b82SPatrick Mooney 	case VM_RTC_WRITE: {
15937c8c0b82SPatrick Mooney 		struct vm_rtc_data rtcdata;
15947c8c0b82SPatrick Mooney 
15957c8c0b82SPatrick Mooney 		if (ddi_copyin(datap, &rtcdata, sizeof (rtcdata), md)) {
15967c8c0b82SPatrick Mooney 			error = EFAULT;
15977c8c0b82SPatrick Mooney 			break;
15987c8c0b82SPatrick Mooney 		}
15997c8c0b82SPatrick Mooney 		error = vrtc_nvram_write(sc->vmm_vm, rtcdata.offset,
16007c8c0b82SPatrick Mooney 		    rtcdata.value);
16017c8c0b82SPatrick Mooney 		break;
16027c8c0b82SPatrick Mooney 	}
16037c8c0b82SPatrick Mooney 	case VM_RTC_READ: {
16047c8c0b82SPatrick Mooney 		struct vm_rtc_data rtcdata;
16057c8c0b82SPatrick Mooney 
16067c8c0b82SPatrick Mooney 		if (ddi_copyin(datap, &rtcdata, sizeof (rtcdata), md)) {
16077c8c0b82SPatrick Mooney 			error = EFAULT;
16087c8c0b82SPatrick Mooney 			break;
16097c8c0b82SPatrick Mooney 		}
16107c8c0b82SPatrick Mooney 		error = vrtc_nvram_read(sc->vmm_vm, rtcdata.offset,
16117c8c0b82SPatrick Mooney 		    &rtcdata.value);
16127c8c0b82SPatrick Mooney 		if (error == 0 &&
16137c8c0b82SPatrick Mooney 		    ddi_copyout(&rtcdata, datap, sizeof (rtcdata), md)) {
16147c8c0b82SPatrick Mooney 			error = EFAULT;
16157c8c0b82SPatrick Mooney 			break;
16167c8c0b82SPatrick Mooney 		}
16177c8c0b82SPatrick Mooney 		break;
16187c8c0b82SPatrick Mooney 	}
16197c8c0b82SPatrick Mooney 	case VM_RTC_SETTIME: {
1620a1d41cf9SPatrick Mooney 		timespec_t ts;
16217c8c0b82SPatrick Mooney 
1622a1d41cf9SPatrick Mooney 		if (ddi_copyin(datap, &ts, sizeof (ts), md)) {
16237c8c0b82SPatrick Mooney 			error = EFAULT;
16247c8c0b82SPatrick Mooney 			break;
16257c8c0b82SPatrick Mooney 		}
1626a1d41cf9SPatrick Mooney 		error = vrtc_set_time(sc->vmm_vm, &ts);
16277c8c0b82SPatrick Mooney 		break;
16287c8c0b82SPatrick Mooney 	}
16297c8c0b82SPatrick Mooney 	case VM_RTC_GETTIME: {
1630a1d41cf9SPatrick Mooney 		timespec_t ts;
16317c8c0b82SPatrick Mooney 
1632a1d41cf9SPatrick Mooney 		vrtc_get_time(sc->vmm_vm, &ts);
1633a1d41cf9SPatrick Mooney 		if (ddi_copyout(&ts, datap, sizeof (ts), md)) {
16347c8c0b82SPatrick Mooney 			error = EFAULT;
16357c8c0b82SPatrick Mooney 			break;
16367c8c0b82SPatrick Mooney 		}
16377c8c0b82SPatrick Mooney 		break;
16387c8c0b82SPatrick Mooney 	}
16397c8c0b82SPatrick Mooney 
16407c8c0b82SPatrick Mooney 	case VM_PMTMR_LOCATE: {
16417c8c0b82SPatrick Mooney 		uint16_t port = arg;
16427c8c0b82SPatrick Mooney 		error = vpmtmr_set_location(sc->vmm_vm, port);
16437c8c0b82SPatrick Mooney 		break;
16447c8c0b82SPatrick Mooney 	}
16457c8c0b82SPatrick Mooney 
16467c8c0b82SPatrick Mooney 	case VM_RESTART_INSTRUCTION:
16477c8c0b82SPatrick Mooney 		error = vm_restart_instruction(sc->vmm_vm, vcpu);
16487c8c0b82SPatrick Mooney 		break;
16497c8c0b82SPatrick Mooney 
16507c8c0b82SPatrick Mooney 	case VM_SET_TOPOLOGY: {
16517c8c0b82SPatrick Mooney 		struct vm_cpu_topology topo;
16527c8c0b82SPatrick Mooney 
16537c8c0b82SPatrick Mooney 		if (ddi_copyin(datap, &topo, sizeof (topo), md) != 0) {
16547c8c0b82SPatrick Mooney 			error = EFAULT;
16557c8c0b82SPatrick Mooney 			break;
16567c8c0b82SPatrick Mooney 		}
16577c8c0b82SPatrick Mooney 		error = vm_set_topology(sc->vmm_vm, topo.sockets, topo.cores,
16587c8c0b82SPatrick Mooney 		    topo.threads, topo.maxcpus);
16597c8c0b82SPatrick Mooney 		break;
16607c8c0b82SPatrick Mooney 	}
16617c8c0b82SPatrick Mooney 	case VM_GET_TOPOLOGY: {
16627c8c0b82SPatrick Mooney 		struct vm_cpu_topology topo;
16637c8c0b82SPatrick Mooney 
16647c8c0b82SPatrick Mooney 		vm_get_topology(sc->vmm_vm, &topo.sockets, &topo.cores,
16657c8c0b82SPatrick Mooney 		    &topo.threads, &topo.maxcpus);
16667c8c0b82SPatrick Mooney 		if (ddi_copyout(&topo, datap, sizeof (topo), md) != 0) {
16677c8c0b82SPatrick Mooney 			error = EFAULT;
16687c8c0b82SPatrick Mooney 			break;
16697c8c0b82SPatrick Mooney 		}
16707c8c0b82SPatrick Mooney 		break;
16717c8c0b82SPatrick Mooney 	}
16727c8c0b82SPatrick Mooney 	case VM_DEVMEM_GETOFFSET: {
16737c8c0b82SPatrick Mooney 		struct vm_devmem_offset vdo;
16747c8c0b82SPatrick Mooney 		vmm_devmem_entry_t *de;
16757c8c0b82SPatrick Mooney 
16767c8c0b82SPatrick Mooney 		if (ddi_copyin(datap, &vdo, sizeof (vdo), md) != 0) {
16777c8c0b82SPatrick Mooney 			error = EFAULT;
16787c8c0b82SPatrick Mooney 			break;
16797c8c0b82SPatrick Mooney 		}
16807c8c0b82SPatrick Mooney 
16817c8c0b82SPatrick Mooney 		de = vmmdev_devmem_find(sc, vdo.segid);
16827c8c0b82SPatrick Mooney 		if (de != NULL) {
16837c8c0b82SPatrick Mooney 			vdo.offset = de->vde_off;
16847c8c0b82SPatrick Mooney 			if (ddi_copyout(&vdo, datap, sizeof (vdo), md) != 0) {
16857c8c0b82SPatrick Mooney 				error = EFAULT;
16867c8c0b82SPatrick Mooney 			}
16877c8c0b82SPatrick Mooney 		} else {
16887c8c0b82SPatrick Mooney 			error = ENOENT;
16897c8c0b82SPatrick Mooney 		}
16907c8c0b82SPatrick Mooney 		break;
16917c8c0b82SPatrick Mooney 	}
16927c8c0b82SPatrick Mooney 	case VM_TRACK_DIRTY_PAGES: {
16937c8c0b82SPatrick Mooney 		const size_t max_track_region_len = 8 * PAGESIZE * 8 * PAGESIZE;
16947c8c0b82SPatrick Mooney 		struct vmm_dirty_tracker tracker;
16957c8c0b82SPatrick Mooney 		uint8_t *bitmap;
16967c8c0b82SPatrick Mooney 		size_t len;
16977c8c0b82SPatrick Mooney 
16987c8c0b82SPatrick Mooney 		if (ddi_copyin(datap, &tracker, sizeof (tracker), md) != 0) {
16997c8c0b82SPatrick Mooney 			error = EFAULT;
17007c8c0b82SPatrick Mooney 			break;
17017c8c0b82SPatrick Mooney 		}
17027c8c0b82SPatrick Mooney 		if ((tracker.vdt_start_gpa & PAGEOFFSET) != 0) {
17037c8c0b82SPatrick Mooney 			error = EINVAL;
17047c8c0b82SPatrick Mooney 			break;
17057c8c0b82SPatrick Mooney 		}
17067c8c0b82SPatrick Mooney 		if (tracker.vdt_len == 0) {
17077c8c0b82SPatrick Mooney 			break;
17087c8c0b82SPatrick Mooney 		}
17097c8c0b82SPatrick Mooney 		if ((tracker.vdt_len & PAGEOFFSET) != 0) {
17107c8c0b82SPatrick Mooney 			error = EINVAL;
17117c8c0b82SPatrick Mooney 			break;
17127c8c0b82SPatrick Mooney 		}
17137c8c0b82SPatrick Mooney 		if (tracker.vdt_len > max_track_region_len) {
17147c8c0b82SPatrick Mooney 			error = EINVAL;
17157c8c0b82SPatrick Mooney 			break;
17167c8c0b82SPatrick Mooney 		}
17177c8c0b82SPatrick Mooney 		len = roundup(tracker.vdt_len / PAGESIZE, 8) / 8;
17187c8c0b82SPatrick Mooney 		bitmap = kmem_zalloc(len, KM_SLEEP);
17194ac713daSLuqman Aden 		error = vm_track_dirty_pages(sc->vmm_vm, tracker.vdt_start_gpa,
17207c8c0b82SPatrick Mooney 		    tracker.vdt_len, bitmap);
17214ac713daSLuqman Aden 		if (error == 0 &&
17224ac713daSLuqman Aden 		    ddi_copyout(bitmap, tracker.vdt_pfns, len, md) != 0) {
17237c8c0b82SPatrick Mooney 			error = EFAULT;
17247c8c0b82SPatrick Mooney 		}
17257c8c0b82SPatrick Mooney 		kmem_free(bitmap, len);
17267c8c0b82SPatrick Mooney 
17277c8c0b82SPatrick Mooney 		break;
17287c8c0b82SPatrick Mooney 	}
1729b9b43e84SPatrick Mooney 	case VM_NPT_OPERATION: {
1730b9b43e84SPatrick Mooney 		struct vm_npt_operation vno;
1731b9b43e84SPatrick Mooney 		uint8_t *bitmap = NULL;
1732b9b43e84SPatrick Mooney 		uint64_t bitmap_size = 0;
1733b9b43e84SPatrick Mooney 
1734b9b43e84SPatrick Mooney 		if (ddi_copyin(datap, &vno, sizeof (vno), md) != 0) {
1735b9b43e84SPatrick Mooney 			error = EFAULT;
1736b9b43e84SPatrick Mooney 			break;
1737b9b43e84SPatrick Mooney 		}
1738b9b43e84SPatrick Mooney 		if ((vno.vno_gpa & PAGEOFFSET) != 0 ||
1739b9b43e84SPatrick Mooney 		    (vno.vno_len & PAGEOFFSET) != 0) {
1740b9b43e84SPatrick Mooney 			error = EINVAL;
1741b9b43e84SPatrick Mooney 			break;
1742b9b43e84SPatrick Mooney 		}
1743b9b43e84SPatrick Mooney 		if ((UINT64_MAX - vno.vno_len) < vno.vno_gpa) {
1744b9b43e84SPatrick Mooney 			error = EOVERFLOW;
1745b9b43e84SPatrick Mooney 			break;
1746b9b43e84SPatrick Mooney 		}
1747b9b43e84SPatrick Mooney 
1748b9b43e84SPatrick Mooney 		/*
1749b9b43e84SPatrick Mooney 		 * Allocate a bitmap for the operation if it is specified as
1750b9b43e84SPatrick Mooney 		 * part of the input or output.
1751b9b43e84SPatrick Mooney 		 */
1752b9b43e84SPatrick Mooney 		if ((vno.vno_operation &
1753b9b43e84SPatrick Mooney 		    (VNO_FLAG_BITMAP_IN | VNO_FLAG_BITMAP_OUT)) != 0) {
1754b9b43e84SPatrick Mooney 			/*
1755b9b43e84SPatrick Mooney 			 * Operations expecting data to be copied in or out
1756b9b43e84SPatrick Mooney 			 * should not have zero length.
1757b9b43e84SPatrick Mooney 			 */
1758b9b43e84SPatrick Mooney 			if (vno.vno_len == 0) {
1759b9b43e84SPatrick Mooney 				error = EINVAL;
1760b9b43e84SPatrick Mooney 				break;
1761b9b43e84SPatrick Mooney 			}
1762b9b43e84SPatrick Mooney 
1763b9b43e84SPatrick Mooney 			/*
1764b9b43e84SPatrick Mooney 			 * Maximum bitmap size of 8 pages results in 1 GiB of
1765b9b43e84SPatrick Mooney 			 * coverage.
1766b9b43e84SPatrick Mooney 			 */
1767b9b43e84SPatrick Mooney 			const uint64_t max_bitmap_size = 8 * PAGESIZE;
1768b9b43e84SPatrick Mooney 
1769b9b43e84SPatrick Mooney 			bitmap_size = roundup(vno.vno_len / PAGESIZE, 8) / 8;
1770b9b43e84SPatrick Mooney 			if (bitmap_size > max_bitmap_size) {
1771b9b43e84SPatrick Mooney 				error = E2BIG;
1772b9b43e84SPatrick Mooney 				break;
1773b9b43e84SPatrick Mooney 			}
1774b9b43e84SPatrick Mooney 			bitmap = kmem_zalloc(bitmap_size, KM_SLEEP);
1775b9b43e84SPatrick Mooney 		}
1776b9b43e84SPatrick Mooney 
1777b9b43e84SPatrick Mooney 		if ((vno.vno_operation & VNO_FLAG_BITMAP_IN) != 0) {
1778b9b43e84SPatrick Mooney 			ASSERT(bitmap != NULL);
1779b9b43e84SPatrick Mooney 			if (ddi_copyin(vno.vno_bitmap, bitmap, bitmap_size,
1780b9b43e84SPatrick Mooney 			    md) != 0) {
1781b9b43e84SPatrick Mooney 				error = EFAULT;
1782b9b43e84SPatrick Mooney 			}
1783b9b43e84SPatrick Mooney 		}
1784b9b43e84SPatrick Mooney 
1785b9b43e84SPatrick Mooney 		if (error == 0) {
1786b9b43e84SPatrick Mooney 			error = vm_npt_do_operation(sc->vmm_vm, vno.vno_gpa,
1787b9b43e84SPatrick Mooney 			    vno.vno_len, vno.vno_operation, bitmap, rvalp);
1788b9b43e84SPatrick Mooney 		}
1789b9b43e84SPatrick Mooney 
1790b9b43e84SPatrick Mooney 		if ((vno.vno_operation & VNO_FLAG_BITMAP_OUT) != 0 &&
1791b9b43e84SPatrick Mooney 		    error == 0) {
1792b9b43e84SPatrick Mooney 			ASSERT(bitmap != NULL);
1793b9b43e84SPatrick Mooney 			if (ddi_copyout(bitmap, vno.vno_bitmap, bitmap_size,
1794b9b43e84SPatrick Mooney 			    md) != 0) {
1795b9b43e84SPatrick Mooney 				error = EFAULT;
1796b9b43e84SPatrick Mooney 			}
1797b9b43e84SPatrick Mooney 		}
1798b9b43e84SPatrick Mooney 
1799b9b43e84SPatrick Mooney 		if (bitmap != NULL) {
1800b9b43e84SPatrick Mooney 			kmem_free(bitmap, bitmap_size);
1801b9b43e84SPatrick Mooney 		}
1802b9b43e84SPatrick Mooney 
1803b9b43e84SPatrick Mooney 		break;
1804b9b43e84SPatrick Mooney 	}
18057c8c0b82SPatrick Mooney 	case VM_WRLOCK_CYCLE: {
18067c8c0b82SPatrick Mooney 		/*
18077c8c0b82SPatrick Mooney 		 * Present a test mechanism to acquire/release the write lock
18087c8c0b82SPatrick Mooney 		 * on the VM without any other effects.
18097c8c0b82SPatrick Mooney 		 */
18107c8c0b82SPatrick Mooney 		break;
18117c8c0b82SPatrick Mooney 	}
1812d515dd77SPatrick Mooney 	case VM_DATA_READ: {
1813d515dd77SPatrick Mooney 		struct vm_data_xfer vdx;
1814d515dd77SPatrick Mooney 
1815d515dd77SPatrick Mooney 		if (ddi_copyin(datap, &vdx, sizeof (vdx), md) != 0) {
1816d515dd77SPatrick Mooney 			error = EFAULT;
1817d515dd77SPatrick Mooney 			break;
1818d515dd77SPatrick Mooney 		}
1819d515dd77SPatrick Mooney 		if ((vdx.vdx_flags & ~VDX_FLAGS_VALID) != 0) {
1820d515dd77SPatrick Mooney 			error = EINVAL;
1821d515dd77SPatrick Mooney 			break;
1822d515dd77SPatrick Mooney 		}
1823d515dd77SPatrick Mooney 		if (vdx.vdx_len > VM_DATA_XFER_LIMIT) {
1824d515dd77SPatrick Mooney 			error = EFBIG;
1825d515dd77SPatrick Mooney 			break;
1826d515dd77SPatrick Mooney 		}
1827d515dd77SPatrick Mooney 
1828d515dd77SPatrick Mooney 		const size_t len = vdx.vdx_len;
182954cf5b63SPatrick Mooney 		void *buf = NULL;
183054cf5b63SPatrick Mooney 		if (len != 0) {
1831ad4335f7SPatrick Mooney 			const void *udata = vdx.vdx_data;
1832ad4335f7SPatrick Mooney 
183354cf5b63SPatrick Mooney 			buf = kmem_alloc(len, KM_SLEEP);
1834ad4335f7SPatrick Mooney 			if ((vdx.vdx_flags & VDX_FLAG_READ_COPYIN) == 0) {
1835ad4335f7SPatrick Mooney 				bzero(buf, len);
1836ad4335f7SPatrick Mooney 			} else if (ddi_copyin(udata, buf, len, md) != 0) {
1837d515dd77SPatrick Mooney 				kmem_free(buf, len);
1838d515dd77SPatrick Mooney 				error = EFAULT;
1839d515dd77SPatrick Mooney 				break;
1840d515dd77SPatrick Mooney 			}
184154cf5b63SPatrick Mooney 		}
1842d515dd77SPatrick Mooney 
184354cf5b63SPatrick Mooney 		vdx.vdx_result_len = 0;
1844d515dd77SPatrick Mooney 		vmm_data_req_t req = {
1845d515dd77SPatrick Mooney 			.vdr_class = vdx.vdx_class,
1846d515dd77SPatrick Mooney 			.vdr_version = vdx.vdx_version,
1847d515dd77SPatrick Mooney 			.vdr_flags = vdx.vdx_flags,
184854cf5b63SPatrick Mooney 			.vdr_len = len,
1849d515dd77SPatrick Mooney 			.vdr_data = buf,
185054cf5b63SPatrick Mooney 			.vdr_result_len = &vdx.vdx_result_len,
1851*4bd36be4SPatrick Mooney 			.vdr_vcpuid = vdx.vdx_vcpuid,
1852d515dd77SPatrick Mooney 		};
1853*4bd36be4SPatrick Mooney 		error = vmm_data_read(sc->vmm_vm, &req);
1854d515dd77SPatrick Mooney 
185554cf5b63SPatrick Mooney 		if (error == 0 && buf != NULL) {
1856d515dd77SPatrick Mooney 			if (ddi_copyout(buf, vdx.vdx_data, len, md) != 0) {
1857d515dd77SPatrick Mooney 				error = EFAULT;
1858d515dd77SPatrick Mooney 			}
1859d515dd77SPatrick Mooney 		}
186054cf5b63SPatrick Mooney 
186154cf5b63SPatrick Mooney 		/*
186254cf5b63SPatrick Mooney 		 * Copy out the transfer request so that the value of
186354cf5b63SPatrick Mooney 		 * vdx_result_len can be made available, regardless of any
186454cf5b63SPatrick Mooney 		 * error(s) which may have occurred.
186554cf5b63SPatrick Mooney 		 */
186654cf5b63SPatrick Mooney 		if (ddi_copyout(&vdx, datap, sizeof (vdx), md) != 0) {
186754cf5b63SPatrick Mooney 			error = (error != 0) ? error : EFAULT;
186854cf5b63SPatrick Mooney 		}
186954cf5b63SPatrick Mooney 
187054cf5b63SPatrick Mooney 		if (buf != NULL) {
1871d515dd77SPatrick Mooney 			kmem_free(buf, len);
187254cf5b63SPatrick Mooney 		}
1873d515dd77SPatrick Mooney 		break;
1874d515dd77SPatrick Mooney 	}
1875d515dd77SPatrick Mooney 	case VM_DATA_WRITE: {
1876d515dd77SPatrick Mooney 		struct vm_data_xfer vdx;
1877d515dd77SPatrick Mooney 
1878d515dd77SPatrick Mooney 		if (ddi_copyin(datap, &vdx, sizeof (vdx), md) != 0) {
1879d515dd77SPatrick Mooney 			error = EFAULT;
1880d515dd77SPatrick Mooney 			break;
1881d515dd77SPatrick Mooney 		}
1882d515dd77SPatrick Mooney 		if ((vdx.vdx_flags & ~VDX_FLAGS_VALID) != 0) {
1883d515dd77SPatrick Mooney 			error = EINVAL;
1884d515dd77SPatrick Mooney 			break;
1885d515dd77SPatrick Mooney 		}
1886d515dd77SPatrick Mooney 		if (vdx.vdx_len > VM_DATA_XFER_LIMIT) {
1887d515dd77SPatrick Mooney 			error = EFBIG;
1888d515dd77SPatrick Mooney 			break;
1889d515dd77SPatrick Mooney 		}
1890d515dd77SPatrick Mooney 
1891d515dd77SPatrick Mooney 		const size_t len = vdx.vdx_len;
189254cf5b63SPatrick Mooney 		void *buf = NULL;
189354cf5b63SPatrick Mooney 		if (len != 0) {
189454cf5b63SPatrick Mooney 			buf = kmem_alloc(len, KM_SLEEP);
1895d515dd77SPatrick Mooney 			if (ddi_copyin(vdx.vdx_data, buf, len, md) != 0) {
1896d515dd77SPatrick Mooney 				kmem_free(buf, len);
1897d515dd77SPatrick Mooney 				error = EFAULT;
1898d515dd77SPatrick Mooney 				break;
1899d515dd77SPatrick Mooney 			}
190054cf5b63SPatrick Mooney 		}
1901d515dd77SPatrick Mooney 
190254cf5b63SPatrick Mooney 		vdx.vdx_result_len = 0;
1903d515dd77SPatrick Mooney 		vmm_data_req_t req = {
1904d515dd77SPatrick Mooney 			.vdr_class = vdx.vdx_class,
1905d515dd77SPatrick Mooney 			.vdr_version = vdx.vdx_version,
1906d515dd77SPatrick Mooney 			.vdr_flags = vdx.vdx_flags,
190754cf5b63SPatrick Mooney 			.vdr_len = len,
1908d515dd77SPatrick Mooney 			.vdr_data = buf,
190954cf5b63SPatrick Mooney 			.vdr_result_len = &vdx.vdx_result_len,
1910*4bd36be4SPatrick Mooney 			.vdr_vcpuid = vdx.vdx_vcpuid,
1911d515dd77SPatrick Mooney 		};
19121ab0d30fSPatrick Mooney 		if (vmm_allow_state_writes != 0) {
1913*4bd36be4SPatrick Mooney 			error = vmm_data_write(sc->vmm_vm, &req);
19141ab0d30fSPatrick Mooney 		} else {
19151ab0d30fSPatrick Mooney 			/*
19161ab0d30fSPatrick Mooney 			 * Reject the write if somone has thrown the switch back
19171ab0d30fSPatrick Mooney 			 * into the "disallow" position.
19181ab0d30fSPatrick Mooney 			 */
19191ab0d30fSPatrick Mooney 			error = EPERM;
1920d515dd77SPatrick Mooney 		}
1921d515dd77SPatrick Mooney 
192254cf5b63SPatrick Mooney 		if (error == 0 && buf != NULL &&
1923d515dd77SPatrick Mooney 		    (vdx.vdx_flags & VDX_FLAG_WRITE_COPYOUT) != 0) {
1924d515dd77SPatrick Mooney 			if (ddi_copyout(buf, vdx.vdx_data, len, md) != 0) {
1925d515dd77SPatrick Mooney 				error = EFAULT;
1926d515dd77SPatrick Mooney 			}
1927d515dd77SPatrick Mooney 		}
192854cf5b63SPatrick Mooney 
192954cf5b63SPatrick Mooney 		/*
193054cf5b63SPatrick Mooney 		 * Copy out the transfer request so that the value of
193154cf5b63SPatrick Mooney 		 * vdx_result_len can be made available, regardless of any
193254cf5b63SPatrick Mooney 		 * error(s) which may have occurred.
193354cf5b63SPatrick Mooney 		 */
193454cf5b63SPatrick Mooney 		if (ddi_copyout(&vdx, datap, sizeof (vdx), md) != 0) {
193554cf5b63SPatrick Mooney 			error = (error != 0) ? error : EFAULT;
193654cf5b63SPatrick Mooney 		}
193754cf5b63SPatrick Mooney 
193854cf5b63SPatrick Mooney 		if (buf != NULL) {
1939d515dd77SPatrick Mooney 			kmem_free(buf, len);
194054cf5b63SPatrick Mooney 		}
1941d515dd77SPatrick Mooney 		break;
1942d515dd77SPatrick Mooney 	}
19437c8c0b82SPatrick Mooney 
19442cac0506SPatrick Mooney 	case VM_PAUSE: {
19452cac0506SPatrick Mooney 		error = vm_pause_instance(sc->vmm_vm);
19462cac0506SPatrick Mooney 		break;
19472cac0506SPatrick Mooney 	}
19482cac0506SPatrick Mooney 	case VM_RESUME: {
19492cac0506SPatrick Mooney 		error = vm_resume_instance(sc->vmm_vm);
19502cac0506SPatrick Mooney 		break;
19512cac0506SPatrick Mooney 	}
19522cac0506SPatrick Mooney 
19537c8c0b82SPatrick Mooney 	default:
19547c8c0b82SPatrick Mooney 		error = ENOTTY;
19557c8c0b82SPatrick Mooney 		break;
19567c8c0b82SPatrick Mooney 	}
19577c8c0b82SPatrick Mooney 
19587c8c0b82SPatrick Mooney 	/* Release exclusion resources */
19597c8c0b82SPatrick Mooney 	switch (lock_type) {
19607c8c0b82SPatrick Mooney 	case LOCK_NONE:
19617c8c0b82SPatrick Mooney 		break;
19627c8c0b82SPatrick Mooney 	case LOCK_VCPU:
19637c8c0b82SPatrick Mooney 		vcpu_unlock_one(sc, vcpu);
19647c8c0b82SPatrick Mooney 		break;
19657c8c0b82SPatrick Mooney 	case LOCK_READ_HOLD:
19667c8c0b82SPatrick Mooney 		vmm_read_unlock(sc);
19677c8c0b82SPatrick Mooney 		break;
19687c8c0b82SPatrick Mooney 	case LOCK_WRITE_HOLD:
19697c8c0b82SPatrick Mooney 		vmm_write_unlock(sc);
19707c8c0b82SPatrick Mooney 		break;
19717c8c0b82SPatrick Mooney 	default:
19727c8c0b82SPatrick Mooney 		panic("unexpected lock type");
19737c8c0b82SPatrick Mooney 		break;
19747c8c0b82SPatrick Mooney 	}
19757c8c0b82SPatrick Mooney 
19767c8c0b82SPatrick Mooney 	return (error);
19777c8c0b82SPatrick Mooney }
19787c8c0b82SPatrick Mooney 
19797c8c0b82SPatrick Mooney static vmm_softc_t *
vmm_lookup(const char * name)19807c8c0b82SPatrick Mooney vmm_lookup(const char *name)
19817c8c0b82SPatrick Mooney {
19827c8c0b82SPatrick Mooney 	list_t *vml = &vmm_list;
19837c8c0b82SPatrick Mooney 	vmm_softc_t *sc;
19847c8c0b82SPatrick Mooney 
19857c8c0b82SPatrick Mooney 	ASSERT(MUTEX_HELD(&vmm_mtx));
19867c8c0b82SPatrick Mooney 
19877c8c0b82SPatrick Mooney 	for (sc = list_head(vml); sc != NULL; sc = list_next(vml, sc)) {
19887c8c0b82SPatrick Mooney 		if (strcmp(sc->vmm_name, name) == 0) {
19897c8c0b82SPatrick Mooney 			break;
19907c8c0b82SPatrick Mooney 		}
19917c8c0b82SPatrick Mooney 	}
19927c8c0b82SPatrick Mooney 
19937c8c0b82SPatrick Mooney 	return (sc);
19947c8c0b82SPatrick Mooney }
19957c8c0b82SPatrick Mooney 
19967c8c0b82SPatrick Mooney /*
19977c8c0b82SPatrick Mooney  * Acquire an HMA registration if not already held.
19987c8c0b82SPatrick Mooney  */
19997c8c0b82SPatrick Mooney static boolean_t
vmm_hma_acquire(void)20007c8c0b82SPatrick Mooney vmm_hma_acquire(void)
20017c8c0b82SPatrick Mooney {
20027c8c0b82SPatrick Mooney 	ASSERT(MUTEX_NOT_HELD(&vmm_mtx));
20037c8c0b82SPatrick Mooney 
20047c8c0b82SPatrick Mooney 	mutex_enter(&vmmdev_mtx);
20057c8c0b82SPatrick Mooney 
20067c8c0b82SPatrick Mooney 	if (vmmdev_hma_reg == NULL) {
20077c8c0b82SPatrick Mooney 		VERIFY3U(vmmdev_hma_ref, ==, 0);
20087c8c0b82SPatrick Mooney 		vmmdev_hma_reg = hma_register(vmmdev_hvm_name);
20097c8c0b82SPatrick Mooney 		if (vmmdev_hma_reg == NULL) {
20107c8c0b82SPatrick Mooney 			cmn_err(CE_WARN, "%s HMA registration failed.",
20117c8c0b82SPatrick Mooney 			    vmmdev_hvm_name);
20127c8c0b82SPatrick Mooney 			mutex_exit(&vmmdev_mtx);
20137c8c0b82SPatrick Mooney 			return (B_FALSE);
20147c8c0b82SPatrick Mooney 		}
20157c8c0b82SPatrick Mooney 	}
20167c8c0b82SPatrick Mooney 
20177c8c0b82SPatrick Mooney 	vmmdev_hma_ref++;
20187c8c0b82SPatrick Mooney 
20197c8c0b82SPatrick Mooney 	mutex_exit(&vmmdev_mtx);
20207c8c0b82SPatrick Mooney 
20217c8c0b82SPatrick Mooney 	return (B_TRUE);
20227c8c0b82SPatrick Mooney }
20237c8c0b82SPatrick Mooney 
20247c8c0b82SPatrick Mooney /*
20257c8c0b82SPatrick Mooney  * Release the HMA registration if held and there are no remaining VMs.
20267c8c0b82SPatrick Mooney  */
20277c8c0b82SPatrick Mooney static void
vmm_hma_release(void)20287c8c0b82SPatrick Mooney vmm_hma_release(void)
20297c8c0b82SPatrick Mooney {
20307c8c0b82SPatrick Mooney 	ASSERT(MUTEX_NOT_HELD(&vmm_mtx));
20317c8c0b82SPatrick Mooney 
20327c8c0b82SPatrick Mooney 	mutex_enter(&vmmdev_mtx);
20337c8c0b82SPatrick Mooney 
20347c8c0b82SPatrick Mooney 	VERIFY3U(vmmdev_hma_ref, !=, 0);
20357c8c0b82SPatrick Mooney 
20367c8c0b82SPatrick Mooney 	vmmdev_hma_ref--;
20377c8c0b82SPatrick Mooney 
20387c8c0b82SPatrick Mooney 	if (vmmdev_hma_ref == 0) {
20397c8c0b82SPatrick Mooney 		VERIFY(vmmdev_hma_reg != NULL);
20407c8c0b82SPatrick Mooney 		hma_unregister(vmmdev_hma_reg);
20417c8c0b82SPatrick Mooney 		vmmdev_hma_reg = NULL;
20427c8c0b82SPatrick Mooney 	}
20437c8c0b82SPatrick Mooney 	mutex_exit(&vmmdev_mtx);
20447c8c0b82SPatrick Mooney }
20457c8c0b82SPatrick Mooney 
20467c8c0b82SPatrick Mooney static int
vmmdev_do_vm_create(const struct vm_create_req * req,cred_t * cr)20477c8c0b82SPatrick Mooney vmmdev_do_vm_create(const struct vm_create_req *req, cred_t *cr)
20487c8c0b82SPatrick Mooney {
20497c8c0b82SPatrick Mooney 	vmm_softc_t	*sc = NULL;
20507c8c0b82SPatrick Mooney 	minor_t		minor;
20517c8c0b82SPatrick Mooney 	int		error = ENOMEM;
20527c8c0b82SPatrick Mooney 	size_t		len;
20537c8c0b82SPatrick Mooney 	const char	*name = req->name;
20547c8c0b82SPatrick Mooney 
20557c8c0b82SPatrick Mooney 	len = strnlen(name, VM_MAX_NAMELEN);
20567c8c0b82SPatrick Mooney 	if (len == 0) {
20577c8c0b82SPatrick Mooney 		return (EINVAL);
20587c8c0b82SPatrick Mooney 	}
20597c8c0b82SPatrick Mooney 	if (len >= VM_MAX_NAMELEN) {
20607c8c0b82SPatrick Mooney 		return (ENAMETOOLONG);
20617c8c0b82SPatrick Mooney 	}
20627c8c0b82SPatrick Mooney 	if (strchr(name, '/') != NULL) {
20637c8c0b82SPatrick Mooney 		return (EINVAL);
20647c8c0b82SPatrick Mooney 	}
20657c8c0b82SPatrick Mooney 
20667c8c0b82SPatrick Mooney 	if (!vmm_hma_acquire())
20677c8c0b82SPatrick Mooney 		return (ENXIO);
20687c8c0b82SPatrick Mooney 
20697c8c0b82SPatrick Mooney 	mutex_enter(&vmm_mtx);
20707c8c0b82SPatrick Mooney 
20717c8c0b82SPatrick Mooney 	/* Look for duplicate names */
20727c8c0b82SPatrick Mooney 	if (vmm_lookup(name) != NULL) {
20737c8c0b82SPatrick Mooney 		mutex_exit(&vmm_mtx);
20747c8c0b82SPatrick Mooney 		vmm_hma_release();
20757c8c0b82SPatrick Mooney 		return (EEXIST);
20767c8c0b82SPatrick Mooney 	}
20777c8c0b82SPatrick Mooney 
20787c8c0b82SPatrick Mooney 	/* Allow only one instance per non-global zone. */
20797c8c0b82SPatrick Mooney 	if (!INGLOBALZONE(curproc)) {
20807c8c0b82SPatrick Mooney 		for (sc = list_head(&vmm_list); sc != NULL;
20817c8c0b82SPatrick Mooney 		    sc = list_next(&vmm_list, sc)) {
20827c8c0b82SPatrick Mooney 			if (sc->vmm_zone == curzone) {
20837c8c0b82SPatrick Mooney 				mutex_exit(&vmm_mtx);
20847c8c0b82SPatrick Mooney 				vmm_hma_release();
20857c8c0b82SPatrick Mooney 				return (EINVAL);
20867c8c0b82SPatrick Mooney 			}
20877c8c0b82SPatrick Mooney 		}
20887c8c0b82SPatrick Mooney 	}
20897c8c0b82SPatrick Mooney 
20907c8c0b82SPatrick Mooney 	minor = id_alloc(vmm_minors);
20917c8c0b82SPatrick Mooney 	if (ddi_soft_state_zalloc(vmm_statep, minor) != DDI_SUCCESS) {
20927c8c0b82SPatrick Mooney 		goto fail;
20937c8c0b82SPatrick Mooney 	} else if ((sc = ddi_get_soft_state(vmm_statep, minor)) == NULL) {
20947c8c0b82SPatrick Mooney 		ddi_soft_state_free(vmm_statep, minor);
20957c8c0b82SPatrick Mooney 		goto fail;
20967c8c0b82SPatrick Mooney 	} else if (ddi_create_minor_node(vmmdev_dip, name, S_IFCHR, minor,
20977c8c0b82SPatrick Mooney 	    DDI_PSEUDO, 0) != DDI_SUCCESS) {
20987c8c0b82SPatrick Mooney 		goto fail;
20997c8c0b82SPatrick Mooney 	}
21007c8c0b82SPatrick Mooney 
21017c8c0b82SPatrick Mooney 	if (vmm_kstat_alloc(sc, minor, cr) != 0) {
21027c8c0b82SPatrick Mooney 		goto fail;
21037c8c0b82SPatrick Mooney 	}
21047c8c0b82SPatrick Mooney 
2105d4f59ae5SPatrick Mooney 	error = vm_create(req->flags, &sc->vmm_vm);
21067c8c0b82SPatrick Mooney 	if (error == 0) {
21077c8c0b82SPatrick Mooney 		/* Complete VM intialization and report success. */
21087c8c0b82SPatrick Mooney 		(void) strlcpy(sc->vmm_name, name, sizeof (sc->vmm_name));
21097c8c0b82SPatrick Mooney 		sc->vmm_minor = minor;
21107c8c0b82SPatrick Mooney 		list_create(&sc->vmm_devmem_list, sizeof (vmm_devmem_entry_t),
21117c8c0b82SPatrick Mooney 		    offsetof(vmm_devmem_entry_t, vde_node));
21127c8c0b82SPatrick Mooney 
21137c8c0b82SPatrick Mooney 		list_create(&sc->vmm_holds, sizeof (vmm_hold_t),
21147c8c0b82SPatrick Mooney 		    offsetof(vmm_hold_t, vmh_node));
21157c8c0b82SPatrick Mooney 		cv_init(&sc->vmm_cv, NULL, CV_DEFAULT, NULL);
21167c8c0b82SPatrick Mooney 
21177c8c0b82SPatrick Mooney 		mutex_init(&sc->vmm_lease_lock, NULL, MUTEX_DEFAULT, NULL);
21187c8c0b82SPatrick Mooney 		list_create(&sc->vmm_lease_list, sizeof (vmm_lease_t),
21197c8c0b82SPatrick Mooney 		    offsetof(vmm_lease_t, vml_node));
21207c8c0b82SPatrick Mooney 		cv_init(&sc->vmm_lease_cv, NULL, CV_DEFAULT, NULL);
21217c8c0b82SPatrick Mooney 		rw_init(&sc->vmm_rwlock, NULL, RW_DEFAULT, NULL);
21227c8c0b82SPatrick Mooney 
21237c8c0b82SPatrick Mooney 		sc->vmm_zone = crgetzone(cr);
21247c8c0b82SPatrick Mooney 		zone_hold(sc->vmm_zone);
21257c8c0b82SPatrick Mooney 		vmm_zsd_add_vm(sc);
21267c8c0b82SPatrick Mooney 		vmm_kstat_init(sc);
21277c8c0b82SPatrick Mooney 
21287c8c0b82SPatrick Mooney 		list_insert_tail(&vmm_list, sc);
21297c8c0b82SPatrick Mooney 		mutex_exit(&vmm_mtx);
21307c8c0b82SPatrick Mooney 		return (0);
21317c8c0b82SPatrick Mooney 	}
21327c8c0b82SPatrick Mooney 
21337c8c0b82SPatrick Mooney 	vmm_kstat_fini(sc);
21347c8c0b82SPatrick Mooney 	ddi_remove_minor_node(vmmdev_dip, name);
21357c8c0b82SPatrick Mooney fail:
21367c8c0b82SPatrick Mooney 	id_free(vmm_minors, minor);
21377c8c0b82SPatrick Mooney 	if (sc != NULL) {
21387c8c0b82SPatrick Mooney 		ddi_soft_state_free(vmm_statep, minor);
21397c8c0b82SPatrick Mooney 	}
21407c8c0b82SPatrick Mooney 	mutex_exit(&vmm_mtx);
21417c8c0b82SPatrick Mooney 	vmm_hma_release();
21427c8c0b82SPatrick Mooney 
21437c8c0b82SPatrick Mooney 	return (error);
21447c8c0b82SPatrick Mooney }
21457c8c0b82SPatrick Mooney 
21467c8c0b82SPatrick Mooney /*
21477c8c0b82SPatrick Mooney  * Bhyve 'Driver' Interface
21487c8c0b82SPatrick Mooney  *
21497c8c0b82SPatrick Mooney  * While many devices are emulated in the bhyve userspace process, there are
21507c8c0b82SPatrick Mooney  * others with performance constraints which require that they run mostly or
21517c8c0b82SPatrick Mooney  * entirely in-kernel.  For those not integrated directly into bhyve, an API is
21527c8c0b82SPatrick Mooney  * needed so they can query/manipulate the portions of VM state needed to
21537c8c0b82SPatrick Mooney  * fulfill their purpose.
21547c8c0b82SPatrick Mooney  *
21557c8c0b82SPatrick Mooney  * This includes:
21567c8c0b82SPatrick Mooney  * - Translating guest-physical addresses to host-virtual pointers
21577c8c0b82SPatrick Mooney  * - Injecting MSIs
21587c8c0b82SPatrick Mooney  * - Hooking IO port addresses
21597c8c0b82SPatrick Mooney  *
21607c8c0b82SPatrick Mooney  * The vmm_drv interface exists to provide that functionality to its consumers.
21617c8c0b82SPatrick Mooney  * (At this time, 'viona' is the only user)
21627c8c0b82SPatrick Mooney  */
21637c8c0b82SPatrick Mooney int
vmm_drv_hold(file_t * fp,cred_t * cr,vmm_hold_t ** holdp)21647c8c0b82SPatrick Mooney vmm_drv_hold(file_t *fp, cred_t *cr, vmm_hold_t **holdp)
21657c8c0b82SPatrick Mooney {
21667c8c0b82SPatrick Mooney 	vnode_t *vp = fp->f_vnode;
21677c8c0b82SPatrick Mooney 	const dev_t dev = vp->v_rdev;
21687c8c0b82SPatrick Mooney 	vmm_softc_t *sc;
21697c8c0b82SPatrick Mooney 	vmm_hold_t *hold;
21707c8c0b82SPatrick Mooney 	int err = 0;
21717c8c0b82SPatrick Mooney 
21727c8c0b82SPatrick Mooney 	if (vp->v_type != VCHR) {
21737c8c0b82SPatrick Mooney 		return (ENXIO);
21747c8c0b82SPatrick Mooney 	}
21757c8c0b82SPatrick Mooney 	const major_t major = getmajor(dev);
21767c8c0b82SPatrick Mooney 	const minor_t minor = getminor(dev);
21777c8c0b82SPatrick Mooney 
21787c8c0b82SPatrick Mooney 	mutex_enter(&vmmdev_mtx);
21797c8c0b82SPatrick Mooney 	if (vmmdev_dip == NULL || major != ddi_driver_major(vmmdev_dip)) {
21807c8c0b82SPatrick Mooney 		mutex_exit(&vmmdev_mtx);
21817c8c0b82SPatrick Mooney 		return (ENOENT);
21827c8c0b82SPatrick Mooney 	}
21837c8c0b82SPatrick Mooney 	mutex_enter(&vmm_mtx);
21847c8c0b82SPatrick Mooney 	mutex_exit(&vmmdev_mtx);
21857c8c0b82SPatrick Mooney 
21867c8c0b82SPatrick Mooney 	if ((sc = ddi_get_soft_state(vmm_statep, minor)) == NULL) {
21877c8c0b82SPatrick Mooney 		err = ENOENT;
21887c8c0b82SPatrick Mooney 		goto out;
21897c8c0b82SPatrick Mooney 	}
21907c8c0b82SPatrick Mooney 	/* XXXJOY: check cred permissions against instance */
21917c8c0b82SPatrick Mooney 
219242640e49SPatrick Mooney 	if ((sc->vmm_flags & VMM_DESTROY) != 0) {
21937c8c0b82SPatrick Mooney 		err = EBUSY;
21947c8c0b82SPatrick Mooney 		goto out;
21957c8c0b82SPatrick Mooney 	}
21967c8c0b82SPatrick Mooney 
21977c8c0b82SPatrick Mooney 	hold = kmem_zalloc(sizeof (*hold), KM_SLEEP);
21987c8c0b82SPatrick Mooney 	hold->vmh_sc = sc;
21997c8c0b82SPatrick Mooney 	hold->vmh_release_req = B_FALSE;
22007c8c0b82SPatrick Mooney 
22017c8c0b82SPatrick Mooney 	list_insert_tail(&sc->vmm_holds, hold);
22027c8c0b82SPatrick Mooney 	sc->vmm_flags |= VMM_HELD;
22037c8c0b82SPatrick Mooney 	*holdp = hold;
22047c8c0b82SPatrick Mooney 
22057c8c0b82SPatrick Mooney out:
22067c8c0b82SPatrick Mooney 	mutex_exit(&vmm_mtx);
22077c8c0b82SPatrick Mooney 	return (err);
22087c8c0b82SPatrick Mooney }
22097c8c0b82SPatrick Mooney 
22107c8c0b82SPatrick Mooney void
vmm_drv_rele(vmm_hold_t * hold)22117c8c0b82SPatrick Mooney vmm_drv_rele(vmm_hold_t *hold)
22127c8c0b82SPatrick Mooney {
22137c8c0b82SPatrick Mooney 	vmm_softc_t *sc;
221442640e49SPatrick Mooney 	bool hma_release = false;
22157c8c0b82SPatrick Mooney 
22167c8c0b82SPatrick Mooney 	ASSERT(hold != NULL);
22177c8c0b82SPatrick Mooney 	ASSERT(hold->vmh_sc != NULL);
22187c8c0b82SPatrick Mooney 	VERIFY(hold->vmh_ioport_hook_cnt == 0);
22197c8c0b82SPatrick Mooney 
22207c8c0b82SPatrick Mooney 	mutex_enter(&vmm_mtx);
22217c8c0b82SPatrick Mooney 	sc = hold->vmh_sc;
22227c8c0b82SPatrick Mooney 	list_remove(&sc->vmm_holds, hold);
222342640e49SPatrick Mooney 	kmem_free(hold, sizeof (*hold));
222442640e49SPatrick Mooney 
22257c8c0b82SPatrick Mooney 	if (list_is_empty(&sc->vmm_holds)) {
22267c8c0b82SPatrick Mooney 		sc->vmm_flags &= ~VMM_HELD;
2227aa39f6d0SPatrick Mooney 
2228aa39f6d0SPatrick Mooney 		/*
222942640e49SPatrick Mooney 		 * Since outstanding holds would prevent instance destruction
223042640e49SPatrick Mooney 		 * from completing, attempt to finish it now if it was already
223142640e49SPatrick Mooney 		 * set in motion.
2232aa39f6d0SPatrick Mooney 		 */
223342640e49SPatrick Mooney 		if ((sc->vmm_flags & VMM_DESTROY) != 0) {
223442640e49SPatrick Mooney 			VERIFY0(vmm_destroy_locked(sc, VDO_DEFAULT,
223542640e49SPatrick Mooney 			    &hma_release));
2236aa39f6d0SPatrick Mooney 		}
22377c8c0b82SPatrick Mooney 	}
22387c8c0b82SPatrick Mooney 	mutex_exit(&vmm_mtx);
2239aa39f6d0SPatrick Mooney 
2240aa39f6d0SPatrick Mooney 	if (hma_release) {
2241aa39f6d0SPatrick Mooney 		vmm_hma_release();
2242aa39f6d0SPatrick Mooney 	}
22437c8c0b82SPatrick Mooney }
22447c8c0b82SPatrick Mooney 
22457c8c0b82SPatrick Mooney boolean_t
vmm_drv_release_reqd(vmm_hold_t * hold)22467c8c0b82SPatrick Mooney vmm_drv_release_reqd(vmm_hold_t *hold)
22477c8c0b82SPatrick Mooney {
22487c8c0b82SPatrick Mooney 	ASSERT(hold != NULL);
22497c8c0b82SPatrick Mooney 
22507c8c0b82SPatrick Mooney 	return (hold->vmh_release_req);
22517c8c0b82SPatrick Mooney }
22527c8c0b82SPatrick Mooney 
22537c8c0b82SPatrick Mooney vmm_lease_t *
vmm_drv_lease_sign(vmm_hold_t * hold,boolean_t (* expiref)(void *),void * arg)22547c8c0b82SPatrick Mooney vmm_drv_lease_sign(vmm_hold_t *hold, boolean_t (*expiref)(void *), void *arg)
22557c8c0b82SPatrick Mooney {
22567c8c0b82SPatrick Mooney 	vmm_softc_t *sc = hold->vmh_sc;
22577c8c0b82SPatrick Mooney 	vmm_lease_t *lease;
22587c8c0b82SPatrick Mooney 
22597c8c0b82SPatrick Mooney 	ASSERT3P(expiref, !=, NULL);
22607c8c0b82SPatrick Mooney 
22617c8c0b82SPatrick Mooney 	if (hold->vmh_release_req) {
22627c8c0b82SPatrick Mooney 		return (NULL);
22637c8c0b82SPatrick Mooney 	}
22647c8c0b82SPatrick Mooney 
22657c8c0b82SPatrick Mooney 	lease = kmem_alloc(sizeof (*lease), KM_SLEEP);
22667c8c0b82SPatrick Mooney 	list_link_init(&lease->vml_node);
22677c8c0b82SPatrick Mooney 	lease->vml_expire_func = expiref;
22687c8c0b82SPatrick Mooney 	lease->vml_expire_arg = arg;
22697c8c0b82SPatrick Mooney 	lease->vml_expired = B_FALSE;
22707c8c0b82SPatrick Mooney 	lease->vml_break_deferred = B_FALSE;
22717c8c0b82SPatrick Mooney 	lease->vml_hold = hold;
22727c8c0b82SPatrick Mooney 	/* cache the VM pointer for one less pointer chase */
22737c8c0b82SPatrick Mooney 	lease->vml_vm = sc->vmm_vm;
22747c8c0b82SPatrick Mooney 	lease->vml_vmclient = vmspace_client_alloc(vm_get_vmspace(sc->vmm_vm));
22757c8c0b82SPatrick Mooney 
22767c8c0b82SPatrick Mooney 	mutex_enter(&sc->vmm_lease_lock);
22777c8c0b82SPatrick Mooney 	while (sc->vmm_lease_blocker != 0) {
22787c8c0b82SPatrick Mooney 		cv_wait(&sc->vmm_lease_cv, &sc->vmm_lease_lock);
22797c8c0b82SPatrick Mooney 	}
22807c8c0b82SPatrick Mooney 	list_insert_tail(&sc->vmm_lease_list, lease);
22817c8c0b82SPatrick Mooney 	vmm_read_lock(sc);
22827c8c0b82SPatrick Mooney 	mutex_exit(&sc->vmm_lease_lock);
22837c8c0b82SPatrick Mooney 
22847c8c0b82SPatrick Mooney 	return (lease);
22857c8c0b82SPatrick Mooney }
22867c8c0b82SPatrick Mooney 
22877c8c0b82SPatrick Mooney static void
vmm_lease_break_locked(vmm_softc_t * sc,vmm_lease_t * lease)22887c8c0b82SPatrick Mooney vmm_lease_break_locked(vmm_softc_t *sc, vmm_lease_t *lease)
22897c8c0b82SPatrick Mooney {
22907c8c0b82SPatrick Mooney 	ASSERT(MUTEX_HELD(&sc->vmm_lease_lock));
22917c8c0b82SPatrick Mooney 
22927c8c0b82SPatrick Mooney 	list_remove(&sc->vmm_lease_list, lease);
22937c8c0b82SPatrick Mooney 	vmm_read_unlock(sc);
22947c8c0b82SPatrick Mooney 	vmc_destroy(lease->vml_vmclient);
22957c8c0b82SPatrick Mooney 	kmem_free(lease, sizeof (*lease));
22967c8c0b82SPatrick Mooney }
22977c8c0b82SPatrick Mooney 
22987c8c0b82SPatrick Mooney static void
vmm_lease_block(vmm_softc_t * sc)22997c8c0b82SPatrick Mooney vmm_lease_block(vmm_softc_t *sc)
23007c8c0b82SPatrick Mooney {
23017c8c0b82SPatrick Mooney 	mutex_enter(&sc->vmm_lease_lock);
23027c8c0b82SPatrick Mooney 	VERIFY3U(sc->vmm_lease_blocker, !=, UINT_MAX);
23037c8c0b82SPatrick Mooney 	sc->vmm_lease_blocker++;
23047c8c0b82SPatrick Mooney 	if (sc->vmm_lease_blocker == 1) {
23057c8c0b82SPatrick Mooney 		list_t *list = &sc->vmm_lease_list;
23067c8c0b82SPatrick Mooney 		vmm_lease_t *lease = list_head(list);
23077c8c0b82SPatrick Mooney 
23087c8c0b82SPatrick Mooney 		while (lease != NULL) {
23097c8c0b82SPatrick Mooney 			void *arg = lease->vml_expire_arg;
23107c8c0b82SPatrick Mooney 			boolean_t (*expiref)(void *) = lease->vml_expire_func;
23117c8c0b82SPatrick Mooney 			boolean_t sync_break = B_FALSE;
23127c8c0b82SPatrick Mooney 
23137c8c0b82SPatrick Mooney 			/*
23147c8c0b82SPatrick Mooney 			 * Since the lease expiration notification may
23157c8c0b82SPatrick Mooney 			 * need to take locks which would deadlock with
23167c8c0b82SPatrick Mooney 			 * vmm_lease_lock, drop it across the call.
23177c8c0b82SPatrick Mooney 			 *
23187c8c0b82SPatrick Mooney 			 * We are the only one allowed to manipulate
23197c8c0b82SPatrick Mooney 			 * vmm_lease_list right now, so it is safe to
23207c8c0b82SPatrick Mooney 			 * continue iterating through it after
23217c8c0b82SPatrick Mooney 			 * reacquiring the lock.
23227c8c0b82SPatrick Mooney 			 */
23237c8c0b82SPatrick Mooney 			lease->vml_expired = B_TRUE;
23247c8c0b82SPatrick Mooney 			mutex_exit(&sc->vmm_lease_lock);
23257c8c0b82SPatrick Mooney 			sync_break = expiref(arg);
23267c8c0b82SPatrick Mooney 			mutex_enter(&sc->vmm_lease_lock);
23277c8c0b82SPatrick Mooney 
23287c8c0b82SPatrick Mooney 			if (sync_break) {
23297c8c0b82SPatrick Mooney 				vmm_lease_t *next;
23307c8c0b82SPatrick Mooney 
23317c8c0b82SPatrick Mooney 				/*
23327c8c0b82SPatrick Mooney 				 * These leases which are synchronously broken
23337c8c0b82SPatrick Mooney 				 * result in vmm_read_unlock() calls from a
23347c8c0b82SPatrick Mooney 				 * different thread than the corresponding
23357c8c0b82SPatrick Mooney 				 * vmm_read_lock().  This is acceptable, given
23367c8c0b82SPatrick Mooney 				 * that the rwlock underpinning the whole
23377c8c0b82SPatrick Mooney 				 * mechanism tolerates the behavior.  This
23387c8c0b82SPatrick Mooney 				 * flexibility is _only_ afforded to VM read
23397c8c0b82SPatrick Mooney 				 * lock (RW_READER) holders.
23407c8c0b82SPatrick Mooney 				 */
23417c8c0b82SPatrick Mooney 				next = list_next(list, lease);
23427c8c0b82SPatrick Mooney 				vmm_lease_break_locked(sc, lease);
23437c8c0b82SPatrick Mooney 				lease = next;
23447c8c0b82SPatrick Mooney 			} else {
23457c8c0b82SPatrick Mooney 				lease = list_next(list, lease);
23467c8c0b82SPatrick Mooney 			}
23477c8c0b82SPatrick Mooney 		}
23487c8c0b82SPatrick Mooney 
23497c8c0b82SPatrick Mooney 		/* Process leases which were not broken synchronously. */
23507c8c0b82SPatrick Mooney 		while (!list_is_empty(list)) {
23517c8c0b82SPatrick Mooney 			/*
23527c8c0b82SPatrick Mooney 			 * Although the nested loops are quadratic, the number
23537c8c0b82SPatrick Mooney 			 * of leases is small.
23547c8c0b82SPatrick Mooney 			 */
23557c8c0b82SPatrick Mooney 			lease = list_head(list);
23567c8c0b82SPatrick Mooney 			while (lease != NULL) {
23577c8c0b82SPatrick Mooney 				vmm_lease_t *next = list_next(list, lease);
23587c8c0b82SPatrick Mooney 				if (lease->vml_break_deferred) {
23597c8c0b82SPatrick Mooney 					vmm_lease_break_locked(sc, lease);
23607c8c0b82SPatrick Mooney 				}
23617c8c0b82SPatrick Mooney 				lease = next;
23627c8c0b82SPatrick Mooney 			}
23637c8c0b82SPatrick Mooney 			if (list_is_empty(list)) {
23647c8c0b82SPatrick Mooney 				break;
23657c8c0b82SPatrick Mooney 			}
23667c8c0b82SPatrick Mooney 			cv_wait(&sc->vmm_lease_cv, &sc->vmm_lease_lock);
23677c8c0b82SPatrick Mooney 		}
23687c8c0b82SPatrick Mooney 		/* Wake anyone else waiting for the lease list to be empty  */
23697c8c0b82SPatrick Mooney 		cv_broadcast(&sc->vmm_lease_cv);
23707c8c0b82SPatrick Mooney 	} else {
23717c8c0b82SPatrick Mooney 		list_t *list = &sc->vmm_lease_list;
23727c8c0b82SPatrick Mooney 
23737c8c0b82SPatrick Mooney 		/*
23747c8c0b82SPatrick Mooney 		 * Some other thread beat us to the duty of lease cleanup.
23757c8c0b82SPatrick Mooney 		 * Wait until that is complete.
23767c8c0b82SPatrick Mooney 		 */
23777c8c0b82SPatrick Mooney 		while (!list_is_empty(list)) {
23787c8c0b82SPatrick Mooney 			cv_wait(&sc->vmm_lease_cv, &sc->vmm_lease_lock);
23797c8c0b82SPatrick Mooney 		}
23807c8c0b82SPatrick Mooney 	}
23817c8c0b82SPatrick Mooney 	mutex_exit(&sc->vmm_lease_lock);
23827c8c0b82SPatrick Mooney }
23837c8c0b82SPatrick Mooney 
23847c8c0b82SPatrick Mooney static void
vmm_lease_unblock(vmm_softc_t * sc)23857c8c0b82SPatrick Mooney vmm_lease_unblock(vmm_softc_t *sc)
23867c8c0b82SPatrick Mooney {
23877c8c0b82SPatrick Mooney 	mutex_enter(&sc->vmm_lease_lock);
23887c8c0b82SPatrick Mooney 	VERIFY3U(sc->vmm_lease_blocker, !=, 0);
23897c8c0b82SPatrick Mooney 	sc->vmm_lease_blocker--;
23907c8c0b82SPatrick Mooney 	if (sc->vmm_lease_blocker == 0) {
23917c8c0b82SPatrick Mooney 		cv_broadcast(&sc->vmm_lease_cv);
23927c8c0b82SPatrick Mooney 	}
23937c8c0b82SPatrick Mooney 	mutex_exit(&sc->vmm_lease_lock);
23947c8c0b82SPatrick Mooney }
23957c8c0b82SPatrick Mooney 
23967c8c0b82SPatrick Mooney void
vmm_drv_lease_break(vmm_hold_t * hold,vmm_lease_t * lease)23977c8c0b82SPatrick Mooney vmm_drv_lease_break(vmm_hold_t *hold, vmm_lease_t *lease)
23987c8c0b82SPatrick Mooney {
23997c8c0b82SPatrick Mooney 	vmm_softc_t *sc = hold->vmh_sc;
24007c8c0b82SPatrick Mooney 
24017c8c0b82SPatrick Mooney 	VERIFY3P(hold, ==, lease->vml_hold);
24027c8c0b82SPatrick Mooney 	VERIFY(!lease->vml_break_deferred);
24037c8c0b82SPatrick Mooney 
24047c8c0b82SPatrick Mooney 	mutex_enter(&sc->vmm_lease_lock);
24057c8c0b82SPatrick Mooney 	if (sc->vmm_lease_blocker == 0) {
24067c8c0b82SPatrick Mooney 		vmm_lease_break_locked(sc, lease);
24077c8c0b82SPatrick Mooney 	} else {
24087c8c0b82SPatrick Mooney 		/*
24097c8c0b82SPatrick Mooney 		 * Defer the lease-breaking to whichever thread is currently
24107c8c0b82SPatrick Mooney 		 * cleaning up all leases as part of a vmm_lease_block() call.
24117c8c0b82SPatrick Mooney 		 */
24127c8c0b82SPatrick Mooney 		lease->vml_break_deferred = B_TRUE;
24137c8c0b82SPatrick Mooney 		cv_broadcast(&sc->vmm_lease_cv);
24147c8c0b82SPatrick Mooney 	}
24157c8c0b82SPatrick Mooney 	mutex_exit(&sc->vmm_lease_lock);
24167c8c0b82SPatrick Mooney }
24177c8c0b82SPatrick Mooney 
24187c8c0b82SPatrick Mooney boolean_t
vmm_drv_lease_expired(vmm_lease_t * lease)24197c8c0b82SPatrick Mooney vmm_drv_lease_expired(vmm_lease_t *lease)
24207c8c0b82SPatrick Mooney {
24217c8c0b82SPatrick Mooney 	return (lease->vml_expired);
24227c8c0b82SPatrick Mooney }
24237c8c0b82SPatrick Mooney 
24247c8c0b82SPatrick Mooney vmm_page_t *
vmm_drv_page_hold(vmm_lease_t * lease,uintptr_t gpa,int prot)24257c8c0b82SPatrick Mooney vmm_drv_page_hold(vmm_lease_t *lease, uintptr_t gpa, int prot)
24267c8c0b82SPatrick Mooney {
24277c8c0b82SPatrick Mooney 	ASSERT(lease != NULL);
24287c8c0b82SPatrick Mooney 	ASSERT0(gpa & PAGEOFFSET);
24297c8c0b82SPatrick Mooney 
24307c8c0b82SPatrick Mooney 	return ((vmm_page_t *)vmc_hold(lease->vml_vmclient, gpa, prot));
24317c8c0b82SPatrick Mooney }
24327c8c0b82SPatrick Mooney 
2433f2357d97SPatrick Mooney 
2434f2357d97SPatrick Mooney /* Ensure that flags mirrored by vmm_drv interface properly match up */
2435f2357d97SPatrick Mooney CTASSERT(VMPF_DEFER_DIRTY == VPF_DEFER_DIRTY);
2436f2357d97SPatrick Mooney 
2437f2357d97SPatrick Mooney vmm_page_t *
vmm_drv_page_hold_ext(vmm_lease_t * lease,uintptr_t gpa,int prot,int flags)2438f2357d97SPatrick Mooney vmm_drv_page_hold_ext(vmm_lease_t *lease, uintptr_t gpa, int prot, int flags)
2439f2357d97SPatrick Mooney {
2440f2357d97SPatrick Mooney 	ASSERT(lease != NULL);
2441f2357d97SPatrick Mooney 	ASSERT0(gpa & PAGEOFFSET);
2442f2357d97SPatrick Mooney 
2443f2357d97SPatrick Mooney 	vmm_page_t *page =
2444f2357d97SPatrick Mooney 	    (vmm_page_t *)vmc_hold_ext(lease->vml_vmclient, gpa, prot, flags);
2445f2357d97SPatrick Mooney 	return (page);
2446f2357d97SPatrick Mooney }
2447f2357d97SPatrick Mooney 
24487c8c0b82SPatrick Mooney void
vmm_drv_page_release(vmm_page_t * vmmp)24497c8c0b82SPatrick Mooney vmm_drv_page_release(vmm_page_t *vmmp)
24507c8c0b82SPatrick Mooney {
2451e0994bd2SPatrick Mooney 	(void) vmp_release((vm_page_t *)vmmp);
24527c8c0b82SPatrick Mooney }
24537c8c0b82SPatrick Mooney 
24547c8c0b82SPatrick Mooney void
vmm_drv_page_release_chain(vmm_page_t * vmmp)24557c8c0b82SPatrick Mooney vmm_drv_page_release_chain(vmm_page_t *vmmp)
24567c8c0b82SPatrick Mooney {
2457e0994bd2SPatrick Mooney 	(void) vmp_release_chain((vm_page_t *)vmmp);
24587c8c0b82SPatrick Mooney }
24597c8c0b82SPatrick Mooney 
24607c8c0b82SPatrick Mooney const void *
vmm_drv_page_readable(const vmm_page_t * vmmp)24617c8c0b82SPatrick Mooney vmm_drv_page_readable(const vmm_page_t *vmmp)
24627c8c0b82SPatrick Mooney {
24637c8c0b82SPatrick Mooney 	return (vmp_get_readable((const vm_page_t *)vmmp));
24647c8c0b82SPatrick Mooney }
24657c8c0b82SPatrick Mooney 
24667c8c0b82SPatrick Mooney void *
vmm_drv_page_writable(const vmm_page_t * vmmp)24677c8c0b82SPatrick Mooney vmm_drv_page_writable(const vmm_page_t *vmmp)
24687c8c0b82SPatrick Mooney {
24697c8c0b82SPatrick Mooney 	return (vmp_get_writable((const vm_page_t *)vmmp));
24707c8c0b82SPatrick Mooney }
24717c8c0b82SPatrick Mooney 
24727c8c0b82SPatrick Mooney void
vmm_drv_page_mark_dirty(vmm_page_t * vmmp)2473f2357d97SPatrick Mooney vmm_drv_page_mark_dirty(vmm_page_t *vmmp)
2474f2357d97SPatrick Mooney {
2475f2357d97SPatrick Mooney 	return (vmp_mark_dirty((vm_page_t *)vmmp));
2476f2357d97SPatrick Mooney }
2477f2357d97SPatrick Mooney 
2478f2357d97SPatrick Mooney void
vmm_drv_page_chain(vmm_page_t * vmmp,vmm_page_t * to_chain)24797c8c0b82SPatrick Mooney vmm_drv_page_chain(vmm_page_t *vmmp, vmm_page_t *to_chain)
24807c8c0b82SPatrick Mooney {
24817c8c0b82SPatrick Mooney 	vmp_chain((vm_page_t *)vmmp, (vm_page_t *)to_chain);
24827c8c0b82SPatrick Mooney }
24837c8c0b82SPatrick Mooney 
24847c8c0b82SPatrick Mooney vmm_page_t *
vmm_drv_page_next(const vmm_page_t * vmmp)24857c8c0b82SPatrick Mooney vmm_drv_page_next(const vmm_page_t *vmmp)
24867c8c0b82SPatrick Mooney {
24877c8c0b82SPatrick Mooney 	return ((vmm_page_t *)vmp_next((vm_page_t *)vmmp));
24887c8c0b82SPatrick Mooney }
24897c8c0b82SPatrick Mooney 
24907c8c0b82SPatrick Mooney int
vmm_drv_msi(vmm_lease_t * lease,uint64_t addr,uint64_t msg)24917c8c0b82SPatrick Mooney vmm_drv_msi(vmm_lease_t *lease, uint64_t addr, uint64_t msg)
24927c8c0b82SPatrick Mooney {
24937c8c0b82SPatrick Mooney 	ASSERT(lease != NULL);
24947c8c0b82SPatrick Mooney 
24957c8c0b82SPatrick Mooney 	return (lapic_intr_msi(lease->vml_vm, addr, msg));
24967c8c0b82SPatrick Mooney }
24977c8c0b82SPatrick Mooney 
24987c8c0b82SPatrick Mooney int
vmm_drv_ioport_hook(vmm_hold_t * hold,uint16_t ioport,vmm_drv_iop_cb_t func,void * arg,void ** cookie)24997c8c0b82SPatrick Mooney vmm_drv_ioport_hook(vmm_hold_t *hold, uint16_t ioport, vmm_drv_iop_cb_t func,
25007c8c0b82SPatrick Mooney     void *arg, void **cookie)
25017c8c0b82SPatrick Mooney {
25027c8c0b82SPatrick Mooney 	vmm_softc_t *sc;
25037c8c0b82SPatrick Mooney 	int err;
25047c8c0b82SPatrick Mooney 
25057c8c0b82SPatrick Mooney 	ASSERT(hold != NULL);
25067c8c0b82SPatrick Mooney 	ASSERT(cookie != NULL);
25077c8c0b82SPatrick Mooney 
25087c8c0b82SPatrick Mooney 	sc = hold->vmh_sc;
25097c8c0b82SPatrick Mooney 	mutex_enter(&vmm_mtx);
25107c8c0b82SPatrick Mooney 	/* Confirm that hook installation is not blocked */
25117c8c0b82SPatrick Mooney 	if ((sc->vmm_flags & VMM_BLOCK_HOOK) != 0) {
25127c8c0b82SPatrick Mooney 		mutex_exit(&vmm_mtx);
25137c8c0b82SPatrick Mooney 		return (EBUSY);
25147c8c0b82SPatrick Mooney 	}
25157c8c0b82SPatrick Mooney 	/*
25167c8c0b82SPatrick Mooney 	 * Optimistically record an installed hook which will prevent a block
25177c8c0b82SPatrick Mooney 	 * from being asserted while the mutex is dropped.
25187c8c0b82SPatrick Mooney 	 */
25197c8c0b82SPatrick Mooney 	hold->vmh_ioport_hook_cnt++;
25207c8c0b82SPatrick Mooney 	mutex_exit(&vmm_mtx);
25217c8c0b82SPatrick Mooney 
25227c8c0b82SPatrick Mooney 	vmm_write_lock(sc);
25237c8c0b82SPatrick Mooney 	err = vm_ioport_hook(sc->vmm_vm, ioport, (ioport_handler_t)func,
25247c8c0b82SPatrick Mooney 	    arg, cookie);
25257c8c0b82SPatrick Mooney 	vmm_write_unlock(sc);
25267c8c0b82SPatrick Mooney 
25277c8c0b82SPatrick Mooney 	if (err != 0) {
25287c8c0b82SPatrick Mooney 		mutex_enter(&vmm_mtx);
25297c8c0b82SPatrick Mooney 		/* Walk back optimism about the hook installation */
25307c8c0b82SPatrick Mooney 		hold->vmh_ioport_hook_cnt--;
25317c8c0b82SPatrick Mooney 		mutex_exit(&vmm_mtx);
25327c8c0b82SPatrick Mooney 	}
25337c8c0b82SPatrick Mooney 	return (err);
25347c8c0b82SPatrick Mooney }
25357c8c0b82SPatrick Mooney 
25367c8c0b82SPatrick Mooney void
vmm_drv_ioport_unhook(vmm_hold_t * hold,void ** cookie)25377c8c0b82SPatrick Mooney vmm_drv_ioport_unhook(vmm_hold_t *hold, void **cookie)
25387c8c0b82SPatrick Mooney {
25397c8c0b82SPatrick Mooney 	vmm_softc_t *sc;
25407c8c0b82SPatrick Mooney 
25417c8c0b82SPatrick Mooney 	ASSERT(hold != NULL);
25427c8c0b82SPatrick Mooney 	ASSERT(cookie != NULL);
25437c8c0b82SPatrick Mooney 	ASSERT(hold->vmh_ioport_hook_cnt != 0);
25447c8c0b82SPatrick Mooney 
25457c8c0b82SPatrick Mooney 	sc = hold->vmh_sc;
25467c8c0b82SPatrick Mooney 	vmm_write_lock(sc);
25477c8c0b82SPatrick Mooney 	vm_ioport_unhook(sc->vmm_vm, cookie);
25487c8c0b82SPatrick Mooney 	vmm_write_unlock(sc);
25497c8c0b82SPatrick Mooney 
25507c8c0b82SPatrick Mooney 	mutex_enter(&vmm_mtx);
25517c8c0b82SPatrick Mooney 	hold->vmh_ioport_hook_cnt--;
25527c8c0b82SPatrick Mooney 	mutex_exit(&vmm_mtx);
25537c8c0b82SPatrick Mooney }
25547c8c0b82SPatrick Mooney 
255542640e49SPatrick Mooney static void
vmm_drv_purge(vmm_softc_t * sc)255642640e49SPatrick Mooney vmm_drv_purge(vmm_softc_t *sc)
25577c8c0b82SPatrick Mooney {
25587c8c0b82SPatrick Mooney 	ASSERT(MUTEX_HELD(&vmm_mtx));
25597c8c0b82SPatrick Mooney 
25607c8c0b82SPatrick Mooney 	if ((sc->vmm_flags & VMM_HELD) != 0) {
25617c8c0b82SPatrick Mooney 		vmm_hold_t *hold;
25627c8c0b82SPatrick Mooney 
25637c8c0b82SPatrick Mooney 		for (hold = list_head(&sc->vmm_holds); hold != NULL;
25647c8c0b82SPatrick Mooney 		    hold = list_next(&sc->vmm_holds, hold)) {
25657c8c0b82SPatrick Mooney 			hold->vmh_release_req = B_TRUE;
25667c8c0b82SPatrick Mooney 		}
2567a26f9c14SPatrick Mooney 
2568a26f9c14SPatrick Mooney 		/*
2569a26f9c14SPatrick Mooney 		 * Require that all leases on the instance be broken, now that
2570a26f9c14SPatrick Mooney 		 * all associated holds have been marked as needing release.
2571a26f9c14SPatrick Mooney 		 *
2572a26f9c14SPatrick Mooney 		 * Dropping vmm_mtx is not strictly necessary, but if any of the
2573a26f9c14SPatrick Mooney 		 * lessees are slow to respond, it would be nice to leave it
2574a26f9c14SPatrick Mooney 		 * available for other parties.
2575a26f9c14SPatrick Mooney 		 */
2576a26f9c14SPatrick Mooney 		mutex_exit(&vmm_mtx);
2577a26f9c14SPatrick Mooney 		vmm_lease_block(sc);
2578a26f9c14SPatrick Mooney 		vmm_lease_unblock(sc);
2579a26f9c14SPatrick Mooney 		mutex_enter(&vmm_mtx);
25807c8c0b82SPatrick Mooney 	}
25817c8c0b82SPatrick Mooney }
25827c8c0b82SPatrick Mooney 
25837c8c0b82SPatrick Mooney static int
vmm_drv_block_hook(vmm_softc_t * sc,boolean_t enable_block)25847c8c0b82SPatrick Mooney vmm_drv_block_hook(vmm_softc_t *sc, boolean_t enable_block)
25857c8c0b82SPatrick Mooney {
25867c8c0b82SPatrick Mooney 	int err = 0;
25877c8c0b82SPatrick Mooney 
25887c8c0b82SPatrick Mooney 	mutex_enter(&vmm_mtx);
25897c8c0b82SPatrick Mooney 	if (!enable_block) {
25907c8c0b82SPatrick Mooney 		VERIFY((sc->vmm_flags & VMM_BLOCK_HOOK) != 0);
25917c8c0b82SPatrick Mooney 
25927c8c0b82SPatrick Mooney 		sc->vmm_flags &= ~VMM_BLOCK_HOOK;
25937c8c0b82SPatrick Mooney 		goto done;
25947c8c0b82SPatrick Mooney 	}
25957c8c0b82SPatrick Mooney 
25967c8c0b82SPatrick Mooney 	/* If any holds have hooks installed, the block is a failure */
25977c8c0b82SPatrick Mooney 	if (!list_is_empty(&sc->vmm_holds)) {
25987c8c0b82SPatrick Mooney 		vmm_hold_t *hold;
25997c8c0b82SPatrick Mooney 
26007c8c0b82SPatrick Mooney 		for (hold = list_head(&sc->vmm_holds); hold != NULL;
26017c8c0b82SPatrick Mooney 		    hold = list_next(&sc->vmm_holds, hold)) {
26027c8c0b82SPatrick Mooney 			if (hold->vmh_ioport_hook_cnt != 0) {
26037c8c0b82SPatrick Mooney 				err = EBUSY;
26047c8c0b82SPatrick Mooney 				goto done;
26057c8c0b82SPatrick Mooney 			}
26067c8c0b82SPatrick Mooney 		}
26077c8c0b82SPatrick Mooney 	}
26087c8c0b82SPatrick Mooney 	sc->vmm_flags |= VMM_BLOCK_HOOK;
26097c8c0b82SPatrick Mooney 
26107c8c0b82SPatrick Mooney done:
26117c8c0b82SPatrick Mooney 	mutex_exit(&vmm_mtx);
26127c8c0b82SPatrick Mooney 	return (err);
26137c8c0b82SPatrick Mooney }
26147c8c0b82SPatrick Mooney 
261542640e49SPatrick Mooney 
261642640e49SPatrick Mooney static void
vmm_destroy_begin(vmm_softc_t * sc,vmm_destroy_opts_t opts)261742640e49SPatrick Mooney vmm_destroy_begin(vmm_softc_t *sc, vmm_destroy_opts_t opts)
26187c8c0b82SPatrick Mooney {
26197c8c0b82SPatrick Mooney 	ASSERT(MUTEX_HELD(&vmm_mtx));
262042640e49SPatrick Mooney 	ASSERT0(sc->vmm_flags & VMM_DESTROY);
26217c8c0b82SPatrick Mooney 
262242640e49SPatrick Mooney 	sc->vmm_flags |= VMM_DESTROY;
26237c8c0b82SPatrick Mooney 
262442640e49SPatrick Mooney 	/*
262542640e49SPatrick Mooney 	 * Lock and unlock all of the vCPUs to ensure that they are kicked out
262642640e49SPatrick Mooney 	 * of guest context, being unable to return now that the instance is
262742640e49SPatrick Mooney 	 * marked for destruction.
262842640e49SPatrick Mooney 	 */
262942640e49SPatrick Mooney 	const int maxcpus = vm_get_maxcpus(sc->vmm_vm);
263042640e49SPatrick Mooney 	for (int vcpu = 0; vcpu < maxcpus; vcpu++) {
263142640e49SPatrick Mooney 		vcpu_lock_one(sc, vcpu);
263242640e49SPatrick Mooney 		vcpu_unlock_one(sc, vcpu);
26337c8c0b82SPatrick Mooney 	}
26347c8c0b82SPatrick Mooney 
263542640e49SPatrick Mooney 	vmmdev_devmem_purge(sc);
2636aa39f6d0SPatrick Mooney 	if ((opts & VDO_NO_CLEAN_ZSD) == 0) {
263742640e49SPatrick Mooney 		/*
263842640e49SPatrick Mooney 		 * The ZSD should be cleaned up now, unless destruction of the
263942640e49SPatrick Mooney 		 * instance was initated by destruction of the containing zone,
264042640e49SPatrick Mooney 		 * in which case the ZSD has already been removed.
264142640e49SPatrick Mooney 		 */
26427c8c0b82SPatrick Mooney 		vmm_zsd_rem_vm(sc);
26437c8c0b82SPatrick Mooney 	}
264442640e49SPatrick Mooney 	zone_rele(sc->vmm_zone);
26457c8c0b82SPatrick Mooney 
264642640e49SPatrick Mooney 	vmm_drv_purge(sc);
264742640e49SPatrick Mooney }
264842640e49SPatrick Mooney 
264942640e49SPatrick Mooney static bool
vmm_destroy_ready(vmm_softc_t * sc)265042640e49SPatrick Mooney vmm_destroy_ready(vmm_softc_t *sc)
265142640e49SPatrick Mooney {
265242640e49SPatrick Mooney 	ASSERT(MUTEX_HELD(&vmm_mtx));
265342640e49SPatrick Mooney 
265442640e49SPatrick Mooney 	if ((sc->vmm_flags & (VMM_HELD | VMM_IS_OPEN)) == 0) {
265542640e49SPatrick Mooney 		VERIFY(list_is_empty(&sc->vmm_holds));
265642640e49SPatrick Mooney 		return (true);
265742640e49SPatrick Mooney 	}
265842640e49SPatrick Mooney 
265942640e49SPatrick Mooney 	return (false);
266042640e49SPatrick Mooney }
266142640e49SPatrick Mooney 
266242640e49SPatrick Mooney static void
vmm_destroy_finish(vmm_softc_t * sc)266342640e49SPatrick Mooney vmm_destroy_finish(vmm_softc_t *sc)
266442640e49SPatrick Mooney {
266542640e49SPatrick Mooney 	ASSERT(MUTEX_HELD(&vmm_mtx));
266642640e49SPatrick Mooney 	ASSERT(vmm_destroy_ready(sc));
26677c8c0b82SPatrick Mooney 
26687c8c0b82SPatrick Mooney 	list_remove(&vmm_list, sc);
26697c8c0b82SPatrick Mooney 	vmm_kstat_fini(sc);
26707c8c0b82SPatrick Mooney 	vm_destroy(sc->vmm_vm);
267142640e49SPatrick Mooney 	ddi_remove_minor_node(vmmdev_dip, sc->vmm_name);
267242640e49SPatrick Mooney 	(void) devfs_clean(ddi_get_parent(vmmdev_dip), NULL, DV_CLEAN_FORCE);
267342640e49SPatrick Mooney 
267442640e49SPatrick Mooney 	const minor_t minor = sc->vmm_minor;
26757c8c0b82SPatrick Mooney 	ddi_soft_state_free(vmm_statep, minor);
26767c8c0b82SPatrick Mooney 	id_free(vmm_minors, minor);
26777c8c0b82SPatrick Mooney }
26787c8c0b82SPatrick Mooney 
267942640e49SPatrick Mooney /*
268042640e49SPatrick Mooney  * Initiate or attempt to finish destruction of a VMM instance.
268142640e49SPatrick Mooney  *
268242640e49SPatrick Mooney  * This is called from several contexts:
268342640e49SPatrick Mooney  * - An explicit destroy ioctl is made
268442640e49SPatrick Mooney  * - A vmm_drv consumer releases its hold (being the last on the instance)
268542640e49SPatrick Mooney  * - The vmm device is closed, and auto-destruct is enabled
268642640e49SPatrick Mooney  */
268742640e49SPatrick Mooney static int
vmm_destroy_locked(vmm_softc_t * sc,vmm_destroy_opts_t opts,bool * hma_release)268842640e49SPatrick Mooney vmm_destroy_locked(vmm_softc_t *sc, vmm_destroy_opts_t opts,
268942640e49SPatrick Mooney     bool *hma_release)
269042640e49SPatrick Mooney {
269142640e49SPatrick Mooney 	ASSERT(MUTEX_HELD(&vmm_mtx));
269242640e49SPatrick Mooney 
269342640e49SPatrick Mooney 	*hma_release = false;
269442640e49SPatrick Mooney 
269542640e49SPatrick Mooney 	/*
269642640e49SPatrick Mooney 	 * When instance destruction begins, it is so marked such that any
269742640e49SPatrick Mooney 	 * further requests to operate the instance will fail.
269842640e49SPatrick Mooney 	 */
269942640e49SPatrick Mooney 	if ((sc->vmm_flags & VMM_DESTROY) == 0) {
270042640e49SPatrick Mooney 		vmm_destroy_begin(sc, opts);
270142640e49SPatrick Mooney 	}
270242640e49SPatrick Mooney 
270342640e49SPatrick Mooney 	if (vmm_destroy_ready(sc)) {
270442640e49SPatrick Mooney 
270542640e49SPatrick Mooney 		/*
270642640e49SPatrick Mooney 		 * Notify anyone waiting for the destruction to finish.  They
270742640e49SPatrick Mooney 		 * must be clear before we can safely tear down the softc.
270842640e49SPatrick Mooney 		 */
270942640e49SPatrick Mooney 		if (sc->vmm_destroy_waiters != 0) {
271042640e49SPatrick Mooney 			cv_broadcast(&sc->vmm_cv);
271142640e49SPatrick Mooney 			while (sc->vmm_destroy_waiters != 0) {
271242640e49SPatrick Mooney 				cv_wait(&sc->vmm_cv, &vmm_mtx);
271342640e49SPatrick Mooney 			}
271442640e49SPatrick Mooney 		}
271542640e49SPatrick Mooney 
271642640e49SPatrick Mooney 		/*
271742640e49SPatrick Mooney 		 * Finish destruction of instance.  After this point, the softc
271842640e49SPatrick Mooney 		 * is freed and cannot be accessed again.
271942640e49SPatrick Mooney 		 *
272042640e49SPatrick Mooney 		 * With destruction complete, the HMA hold can be released
272142640e49SPatrick Mooney 		 */
272242640e49SPatrick Mooney 		vmm_destroy_finish(sc);
272342640e49SPatrick Mooney 		*hma_release = true;
272442640e49SPatrick Mooney 		return (0);
272542640e49SPatrick Mooney 	} else if ((opts & VDO_ATTEMPT_WAIT) != 0) {
272642640e49SPatrick Mooney 		int err = 0;
272742640e49SPatrick Mooney 
272842640e49SPatrick Mooney 		sc->vmm_destroy_waiters++;
272942640e49SPatrick Mooney 		while (!vmm_destroy_ready(sc) && err == 0) {
273042640e49SPatrick Mooney 			if (cv_wait_sig(&sc->vmm_cv, &vmm_mtx) <= 0) {
273142640e49SPatrick Mooney 				err = EINTR;
273242640e49SPatrick Mooney 			}
273342640e49SPatrick Mooney 		}
273442640e49SPatrick Mooney 		sc->vmm_destroy_waiters--;
273542640e49SPatrick Mooney 
273642640e49SPatrick Mooney 		if (sc->vmm_destroy_waiters == 0) {
273742640e49SPatrick Mooney 			/*
273842640e49SPatrick Mooney 			 * If we were the last waiter, it could be that VM
273942640e49SPatrick Mooney 			 * destruction is waiting on _us_ to proceed with the
274042640e49SPatrick Mooney 			 * final clean-up.
274142640e49SPatrick Mooney 			 */
274242640e49SPatrick Mooney 			cv_signal(&sc->vmm_cv);
274342640e49SPatrick Mooney 		}
274442640e49SPatrick Mooney 		return (err);
274542640e49SPatrick Mooney 	} else {
274642640e49SPatrick Mooney 		/*
274742640e49SPatrick Mooney 		 * Since the instance is not ready for destruction, and the
274842640e49SPatrick Mooney 		 * caller did not ask to wait, consider it a success for now.
274942640e49SPatrick Mooney 		 */
27507c8c0b82SPatrick Mooney 		return (0);
27517c8c0b82SPatrick Mooney 	}
275242640e49SPatrick Mooney }
27537c8c0b82SPatrick Mooney 
275442640e49SPatrick Mooney void
vmm_zone_vm_destroy(vmm_softc_t * sc)2755aa39f6d0SPatrick Mooney vmm_zone_vm_destroy(vmm_softc_t *sc)
27567c8c0b82SPatrick Mooney {
275742640e49SPatrick Mooney 	bool hma_release = false;
27587c8c0b82SPatrick Mooney 	int err;
27597c8c0b82SPatrick Mooney 
27607c8c0b82SPatrick Mooney 	mutex_enter(&vmm_mtx);
2761aa39f6d0SPatrick Mooney 	err = vmm_destroy_locked(sc, VDO_NO_CLEAN_ZSD, &hma_release);
27627c8c0b82SPatrick Mooney 	mutex_exit(&vmm_mtx);
27637c8c0b82SPatrick Mooney 
276442640e49SPatrick Mooney 	VERIFY0(err);
27657c8c0b82SPatrick Mooney 
276642640e49SPatrick Mooney 	if (hma_release) {
276742640e49SPatrick Mooney 		vmm_hma_release();
276842640e49SPatrick Mooney 	}
27697c8c0b82SPatrick Mooney }
27707c8c0b82SPatrick Mooney 
27717c8c0b82SPatrick Mooney static int
vmmdev_do_vm_destroy(const struct vm_destroy_req * req,cred_t * cr)27727c8c0b82SPatrick Mooney vmmdev_do_vm_destroy(const struct vm_destroy_req *req, cred_t *cr)
27737c8c0b82SPatrick Mooney {
27747c8c0b82SPatrick Mooney 	vmm_softc_t *sc;
277542640e49SPatrick Mooney 	bool hma_release = false;
27767c8c0b82SPatrick Mooney 	int err;
27777c8c0b82SPatrick Mooney 
277842640e49SPatrick Mooney 	if (crgetuid(cr) != 0) {
27797c8c0b82SPatrick Mooney 		return (EPERM);
278042640e49SPatrick Mooney 	}
27817c8c0b82SPatrick Mooney 
27827c8c0b82SPatrick Mooney 	mutex_enter(&vmm_mtx);
278342640e49SPatrick Mooney 	sc = vmm_lookup(req->name);
278442640e49SPatrick Mooney 	if (sc == NULL) {
27857c8c0b82SPatrick Mooney 		mutex_exit(&vmm_mtx);
27867c8c0b82SPatrick Mooney 		return (ENOENT);
27877c8c0b82SPatrick Mooney 	}
27887c8c0b82SPatrick Mooney 	/*
27897c8c0b82SPatrick Mooney 	 * We don't check this in vmm_lookup() since that function is also used
27907c8c0b82SPatrick Mooney 	 * for validation during create and currently vmm names must be unique.
27917c8c0b82SPatrick Mooney 	 */
27927c8c0b82SPatrick Mooney 	if (!INGLOBALZONE(curproc) && sc->vmm_zone != curzone) {
27937c8c0b82SPatrick Mooney 		mutex_exit(&vmm_mtx);
27947c8c0b82SPatrick Mooney 		return (EPERM);
27957c8c0b82SPatrick Mooney 	}
27967c8c0b82SPatrick Mooney 
279742640e49SPatrick Mooney 	err = vmm_destroy_locked(sc, VDO_ATTEMPT_WAIT, &hma_release);
27987c8c0b82SPatrick Mooney 	mutex_exit(&vmm_mtx);
27997c8c0b82SPatrick Mooney 
280042640e49SPatrick Mooney 	if (hma_release) {
28017c8c0b82SPatrick Mooney 		vmm_hma_release();
280242640e49SPatrick Mooney 	}
28037c8c0b82SPatrick Mooney 
28047c8c0b82SPatrick Mooney 	return (err);
28057c8c0b82SPatrick Mooney }
28067c8c0b82SPatrick Mooney 
28077c8c0b82SPatrick Mooney #define	VCPU_NAME_BUFLEN	32
28087c8c0b82SPatrick Mooney 
28097c8c0b82SPatrick Mooney static int
vmm_kstat_alloc(vmm_softc_t * sc,minor_t minor,const cred_t * cr)28107c8c0b82SPatrick Mooney vmm_kstat_alloc(vmm_softc_t *sc, minor_t minor, const cred_t *cr)
28117c8c0b82SPatrick Mooney {
28127c8c0b82SPatrick Mooney 	zoneid_t zid = crgetzoneid(cr);
28137c8c0b82SPatrick Mooney 	int instance = minor;
28147c8c0b82SPatrick Mooney 	kstat_t *ksp;
28157c8c0b82SPatrick Mooney 
28167c8c0b82SPatrick Mooney 	ASSERT3P(sc->vmm_kstat_vm, ==, NULL);
28177c8c0b82SPatrick Mooney 
28187c8c0b82SPatrick Mooney 	ksp = kstat_create_zone(VMM_MODULE_NAME, instance, "vm",
28197c8c0b82SPatrick Mooney 	    VMM_KSTAT_CLASS, KSTAT_TYPE_NAMED,
28207c8c0b82SPatrick Mooney 	    sizeof (vmm_kstats_t) / sizeof (kstat_named_t), 0, zid);
28217c8c0b82SPatrick Mooney 
28227c8c0b82SPatrick Mooney 	if (ksp == NULL) {
28237c8c0b82SPatrick Mooney 		return (-1);
28247c8c0b82SPatrick Mooney 	}
28257c8c0b82SPatrick Mooney 	sc->vmm_kstat_vm = ksp;
28267c8c0b82SPatrick Mooney 
28277c8c0b82SPatrick Mooney 	for (uint_t i = 0; i < VM_MAXCPU; i++) {
28287c8c0b82SPatrick Mooney 		char namebuf[VCPU_NAME_BUFLEN];
28297c8c0b82SPatrick Mooney 
28307c8c0b82SPatrick Mooney 		ASSERT3P(sc->vmm_kstat_vcpu[i], ==, NULL);
28317c8c0b82SPatrick Mooney 
28327c8c0b82SPatrick Mooney 		(void) snprintf(namebuf, VCPU_NAME_BUFLEN, "vcpu%u", i);
28337c8c0b82SPatrick Mooney 		ksp = kstat_create_zone(VMM_MODULE_NAME, instance, namebuf,
28347c8c0b82SPatrick Mooney 		    VMM_KSTAT_CLASS, KSTAT_TYPE_NAMED,
28357c8c0b82SPatrick Mooney 		    sizeof (vmm_vcpu_kstats_t) / sizeof (kstat_named_t),
28367c8c0b82SPatrick Mooney 		    0, zid);
28377c8c0b82SPatrick Mooney 		if (ksp == NULL) {
28387c8c0b82SPatrick Mooney 			goto fail;
28397c8c0b82SPatrick Mooney 		}
28407c8c0b82SPatrick Mooney 
28417c8c0b82SPatrick Mooney 		sc->vmm_kstat_vcpu[i] = ksp;
28427c8c0b82SPatrick Mooney 	}
28437c8c0b82SPatrick Mooney 
28447c8c0b82SPatrick Mooney 	/*
28457c8c0b82SPatrick Mooney 	 * If this instance is associated with a non-global zone, make its
28467c8c0b82SPatrick Mooney 	 * kstats visible from the GZ.
28477c8c0b82SPatrick Mooney 	 */
28487c8c0b82SPatrick Mooney 	if (zid != GLOBAL_ZONEID) {
28497c8c0b82SPatrick Mooney 		kstat_zone_add(sc->vmm_kstat_vm, GLOBAL_ZONEID);
28507c8c0b82SPatrick Mooney 		for (uint_t i = 0; i < VM_MAXCPU; i++) {
28517c8c0b82SPatrick Mooney 			kstat_zone_add(sc->vmm_kstat_vcpu[i], GLOBAL_ZONEID);
28527c8c0b82SPatrick Mooney 		}
28537c8c0b82SPatrick Mooney 	}
28547c8c0b82SPatrick Mooney 
28557c8c0b82SPatrick Mooney 	return (0);
28567c8c0b82SPatrick Mooney 
28577c8c0b82SPatrick Mooney fail:
28587c8c0b82SPatrick Mooney 	for (uint_t i = 0; i < VM_MAXCPU; i++) {
28597c8c0b82SPatrick Mooney 		if (sc->vmm_kstat_vcpu[i] != NULL) {
28607c8c0b82SPatrick Mooney 			kstat_delete(sc->vmm_kstat_vcpu[i]);
28617c8c0b82SPatrick Mooney 			sc->vmm_kstat_vcpu[i] = NULL;
28627c8c0b82SPatrick Mooney 		} else {
28637c8c0b82SPatrick Mooney 			break;
28647c8c0b82SPatrick Mooney 		}
28657c8c0b82SPatrick Mooney 	}
28667c8c0b82SPatrick Mooney 	kstat_delete(sc->vmm_kstat_vm);
28677c8c0b82SPatrick Mooney 	sc->vmm_kstat_vm = NULL;
28687c8c0b82SPatrick Mooney 	return (-1);
28697c8c0b82SPatrick Mooney }
28707c8c0b82SPatrick Mooney 
28717c8c0b82SPatrick Mooney static void
vmm_kstat_init(vmm_softc_t * sc)28727c8c0b82SPatrick Mooney vmm_kstat_init(vmm_softc_t *sc)
28737c8c0b82SPatrick Mooney {
28747c8c0b82SPatrick Mooney 	kstat_t *ksp;
28757c8c0b82SPatrick Mooney 
28767c8c0b82SPatrick Mooney 	ASSERT3P(sc->vmm_vm, !=, NULL);
28777c8c0b82SPatrick Mooney 	ASSERT3P(sc->vmm_kstat_vm, !=, NULL);
28787c8c0b82SPatrick Mooney 
28797c8c0b82SPatrick Mooney 	ksp = sc->vmm_kstat_vm;
28807c8c0b82SPatrick Mooney 	vmm_kstats_t *vk = ksp->ks_data;
28817c8c0b82SPatrick Mooney 	ksp->ks_private = sc->vmm_vm;
28827c8c0b82SPatrick Mooney 	kstat_named_init(&vk->vk_name, "vm_name", KSTAT_DATA_STRING);
28837c8c0b82SPatrick Mooney 	kstat_named_setstr(&vk->vk_name, sc->vmm_name);
28847c8c0b82SPatrick Mooney 
28857c8c0b82SPatrick Mooney 	for (uint_t i = 0; i < VM_MAXCPU; i++) {
28867c8c0b82SPatrick Mooney 		ASSERT3P(sc->vmm_kstat_vcpu[i], !=, NULL);
28877c8c0b82SPatrick Mooney 
28887c8c0b82SPatrick Mooney 		ksp = sc->vmm_kstat_vcpu[i];
28897c8c0b82SPatrick Mooney 		vmm_vcpu_kstats_t *vvk = ksp->ks_data;
28907c8c0b82SPatrick Mooney 
28917c8c0b82SPatrick Mooney 		kstat_named_init(&vvk->vvk_vcpu, "vcpu", KSTAT_DATA_UINT32);
28927c8c0b82SPatrick Mooney 		vvk->vvk_vcpu.value.ui32 = i;
28937c8c0b82SPatrick Mooney 		kstat_named_init(&vvk->vvk_time_init, "time_init",
28947c8c0b82SPatrick Mooney 		    KSTAT_DATA_UINT64);
28957c8c0b82SPatrick Mooney 		kstat_named_init(&vvk->vvk_time_run, "time_run",
28967c8c0b82SPatrick Mooney 		    KSTAT_DATA_UINT64);
28977c8c0b82SPatrick Mooney 		kstat_named_init(&vvk->vvk_time_idle, "time_idle",
28987c8c0b82SPatrick Mooney 		    KSTAT_DATA_UINT64);
28997c8c0b82SPatrick Mooney 		kstat_named_init(&vvk->vvk_time_emu_kern, "time_emu_kern",
29007c8c0b82SPatrick Mooney 		    KSTAT_DATA_UINT64);
29017c8c0b82SPatrick Mooney 		kstat_named_init(&vvk->vvk_time_emu_user, "time_emu_user",
29027c8c0b82SPatrick Mooney 		    KSTAT_DATA_UINT64);
29037c8c0b82SPatrick Mooney 		kstat_named_init(&vvk->vvk_time_sched, "time_sched",
29047c8c0b82SPatrick Mooney 		    KSTAT_DATA_UINT64);
29057c8c0b82SPatrick Mooney 		ksp->ks_private = sc->vmm_vm;
29067c8c0b82SPatrick Mooney 		ksp->ks_update = vmm_kstat_update_vcpu;
29077c8c0b82SPatrick Mooney 	}
29087c8c0b82SPatrick Mooney 
29097c8c0b82SPatrick Mooney 	kstat_install(sc->vmm_kstat_vm);
29107c8c0b82SPatrick Mooney 	for (uint_t i = 0; i < VM_MAXCPU; i++) {
29117c8c0b82SPatrick Mooney 		kstat_install(sc->vmm_kstat_vcpu[i]);
29127c8c0b82SPatrick Mooney 	}
29137c8c0b82SPatrick Mooney }
29147c8c0b82SPatrick Mooney 
29157c8c0b82SPatrick Mooney static void
vmm_kstat_fini(vmm_softc_t * sc)29167c8c0b82SPatrick Mooney vmm_kstat_fini(vmm_softc_t *sc)
29177c8c0b82SPatrick Mooney {
29187c8c0b82SPatrick Mooney 	ASSERT(sc->vmm_kstat_vm != NULL);
29197c8c0b82SPatrick Mooney 
29207c8c0b82SPatrick Mooney 	kstat_delete(sc->vmm_kstat_vm);
29217c8c0b82SPatrick Mooney 	sc->vmm_kstat_vm = NULL;
29227c8c0b82SPatrick Mooney 
29237c8c0b82SPatrick Mooney 	for (uint_t i = 0; i < VM_MAXCPU; i++) {
29247c8c0b82SPatrick Mooney 		ASSERT3P(sc->vmm_kstat_vcpu[i], !=, NULL);
29257c8c0b82SPatrick Mooney 
29267c8c0b82SPatrick Mooney 		kstat_delete(sc->vmm_kstat_vcpu[i]);
29277c8c0b82SPatrick Mooney 		sc->vmm_kstat_vcpu[i] = NULL;
29287c8c0b82SPatrick Mooney 	}
29297c8c0b82SPatrick Mooney }
29307c8c0b82SPatrick Mooney 
29317c8c0b82SPatrick Mooney static int
vmm_open(dev_t * devp,int flag,int otyp,cred_t * credp)29327c8c0b82SPatrick Mooney vmm_open(dev_t *devp, int flag, int otyp, cred_t *credp)
29337c8c0b82SPatrick Mooney {
29347c8c0b82SPatrick Mooney 	minor_t		minor;
29357c8c0b82SPatrick Mooney 	vmm_softc_t	*sc;
29367c8c0b82SPatrick Mooney 
29377c8c0b82SPatrick Mooney 	/*
29387c8c0b82SPatrick Mooney 	 * Forbid running bhyve in a 32-bit process until it has been tested and
29397c8c0b82SPatrick Mooney 	 * verified to be safe.
29407c8c0b82SPatrick Mooney 	 */
29417c8c0b82SPatrick Mooney 	if (curproc->p_model != DATAMODEL_LP64) {
29427c8c0b82SPatrick Mooney 		return (EFBIG);
29437c8c0b82SPatrick Mooney 	}
29447c8c0b82SPatrick Mooney 
29457c8c0b82SPatrick Mooney 	minor = getminor(*devp);
29467c8c0b82SPatrick Mooney 	if (minor == VMM_CTL_MINOR) {
29477c8c0b82SPatrick Mooney 		/*
29487c8c0b82SPatrick Mooney 		 * Master control device must be opened exclusively.
29497c8c0b82SPatrick Mooney 		 */
29507c8c0b82SPatrick Mooney 		if ((flag & FEXCL) != FEXCL || otyp != OTYP_CHR) {
29517c8c0b82SPatrick Mooney 			return (EINVAL);
29527c8c0b82SPatrick Mooney 		}
29537c8c0b82SPatrick Mooney 
29547c8c0b82SPatrick Mooney 		return (0);
29557c8c0b82SPatrick Mooney 	}
29567c8c0b82SPatrick Mooney 
29577c8c0b82SPatrick Mooney 	mutex_enter(&vmm_mtx);
29587c8c0b82SPatrick Mooney 	sc = ddi_get_soft_state(vmm_statep, minor);
29597c8c0b82SPatrick Mooney 	if (sc == NULL) {
29607c8c0b82SPatrick Mooney 		mutex_exit(&vmm_mtx);
29617c8c0b82SPatrick Mooney 		return (ENXIO);
29627c8c0b82SPatrick Mooney 	}
29637c8c0b82SPatrick Mooney 
296442640e49SPatrick Mooney 	sc->vmm_flags |= VMM_IS_OPEN;
29657c8c0b82SPatrick Mooney 	mutex_exit(&vmm_mtx);
29667c8c0b82SPatrick Mooney 
29677c8c0b82SPatrick Mooney 	return (0);
29687c8c0b82SPatrick Mooney }
29697c8c0b82SPatrick Mooney 
29707c8c0b82SPatrick Mooney static int
vmm_close(dev_t dev,int flag,int otyp,cred_t * credp)29717c8c0b82SPatrick Mooney vmm_close(dev_t dev, int flag, int otyp, cred_t *credp)
29727c8c0b82SPatrick Mooney {
297342640e49SPatrick Mooney 	const minor_t minor = getminor(dev);
29747c8c0b82SPatrick Mooney 	vmm_softc_t *sc;
297542640e49SPatrick Mooney 	bool hma_release = false;
29767c8c0b82SPatrick Mooney 
297742640e49SPatrick Mooney 	if (minor == VMM_CTL_MINOR) {
29787c8c0b82SPatrick Mooney 		return (0);
297942640e49SPatrick Mooney 	}
29807c8c0b82SPatrick Mooney 
29817c8c0b82SPatrick Mooney 	mutex_enter(&vmm_mtx);
29827c8c0b82SPatrick Mooney 	sc = ddi_get_soft_state(vmm_statep, minor);
29837c8c0b82SPatrick Mooney 	if (sc == NULL) {
29847c8c0b82SPatrick Mooney 		mutex_exit(&vmm_mtx);
29857c8c0b82SPatrick Mooney 		return (ENXIO);
29867c8c0b82SPatrick Mooney 	}
29877c8c0b82SPatrick Mooney 
298842640e49SPatrick Mooney 	VERIFY3U(sc->vmm_flags & VMM_IS_OPEN, !=, 0);
298942640e49SPatrick Mooney 	sc->vmm_flags &= ~VMM_IS_OPEN;
29907c8c0b82SPatrick Mooney 
29917c8c0b82SPatrick Mooney 	/*
299242640e49SPatrick Mooney 	 * If instance was marked for auto-destruction begin that now.  Instance
299342640e49SPatrick Mooney 	 * destruction may have been initated already, so try to make progress
299442640e49SPatrick Mooney 	 * in that case, since closure of the device is one of its requirements.
29957c8c0b82SPatrick Mooney 	 */
299642640e49SPatrick Mooney 	if ((sc->vmm_flags & VMM_DESTROY) != 0 ||
299742640e49SPatrick Mooney 	    (sc->vmm_flags & VMM_AUTODESTROY) != 0) {
299842640e49SPatrick Mooney 		VERIFY0(vmm_destroy_locked(sc, VDO_DEFAULT, &hma_release));
29997c8c0b82SPatrick Mooney 	}
30007c8c0b82SPatrick Mooney 	mutex_exit(&vmm_mtx);
30017c8c0b82SPatrick Mooney 
300242640e49SPatrick Mooney 	if (hma_release) {
30037c8c0b82SPatrick Mooney 		vmm_hma_release();
300442640e49SPatrick Mooney 	}
30057c8c0b82SPatrick Mooney 
30067c8c0b82SPatrick Mooney 	return (0);
30077c8c0b82SPatrick Mooney }
30087c8c0b82SPatrick Mooney 
30097c8c0b82SPatrick Mooney static int
vmm_is_supported(intptr_t arg)30107c8c0b82SPatrick Mooney vmm_is_supported(intptr_t arg)
30117c8c0b82SPatrick Mooney {
30127c8c0b82SPatrick Mooney 	int r;
30137c8c0b82SPatrick Mooney 	const char *msg;
30147c8c0b82SPatrick Mooney 
30157c8c0b82SPatrick Mooney 	if (vmm_is_intel()) {
30167c8c0b82SPatrick Mooney 		r = vmx_x86_supported(&msg);
30177c8c0b82SPatrick Mooney 	} else if (vmm_is_svm()) {
30187c8c0b82SPatrick Mooney 		/*
30197c8c0b82SPatrick Mooney 		 * HMA already ensured that the features necessary for SVM
30207c8c0b82SPatrick Mooney 		 * operation were present and online during vmm_attach().
30217c8c0b82SPatrick Mooney 		 */
30227c8c0b82SPatrick Mooney 		r = 0;
30237c8c0b82SPatrick Mooney 	} else {
30247c8c0b82SPatrick Mooney 		r = ENXIO;
30257c8c0b82SPatrick Mooney 		msg = "Unsupported CPU vendor";
30267c8c0b82SPatrick Mooney 	}
30277c8c0b82SPatrick Mooney 
30287c8c0b82SPatrick Mooney 	if (r != 0 && arg != (intptr_t)NULL) {
30297c8c0b82SPatrick Mooney 		if (copyoutstr(msg, (char *)arg, strlen(msg) + 1, NULL) != 0)
30307c8c0b82SPatrick Mooney 			return (EFAULT);
30317c8c0b82SPatrick Mooney 	}
30327c8c0b82SPatrick Mooney 	return (r);
30337c8c0b82SPatrick Mooney }
30347c8c0b82SPatrick Mooney 
30357c8c0b82SPatrick Mooney static int
vmm_ctl_ioctl(int cmd,intptr_t arg,int md,cred_t * cr,int * rvalp)30367c8c0b82SPatrick Mooney vmm_ctl_ioctl(int cmd, intptr_t arg, int md, cred_t *cr, int *rvalp)
30377c8c0b82SPatrick Mooney {
30387c8c0b82SPatrick Mooney 	void *argp = (void *)arg;
30397c8c0b82SPatrick Mooney 
30407c8c0b82SPatrick Mooney 	switch (cmd) {
30417c8c0b82SPatrick Mooney 	case VMM_CREATE_VM: {
30427c8c0b82SPatrick Mooney 		struct vm_create_req req;
30437c8c0b82SPatrick Mooney 
30447c8c0b82SPatrick Mooney 		if ((md & FWRITE) == 0) {
30457c8c0b82SPatrick Mooney 			return (EPERM);
30467c8c0b82SPatrick Mooney 		}
30477c8c0b82SPatrick Mooney 		if (ddi_copyin(argp, &req, sizeof (req), md) != 0) {
30487c8c0b82SPatrick Mooney 			return (EFAULT);
30497c8c0b82SPatrick Mooney 		}
30507c8c0b82SPatrick Mooney 		return (vmmdev_do_vm_create(&req, cr));
30517c8c0b82SPatrick Mooney 	}
30527c8c0b82SPatrick Mooney 	case VMM_DESTROY_VM: {
30537c8c0b82SPatrick Mooney 		struct vm_destroy_req req;
30547c8c0b82SPatrick Mooney 
30557c8c0b82SPatrick Mooney 		if ((md & FWRITE) == 0) {
30567c8c0b82SPatrick Mooney 			return (EPERM);
30577c8c0b82SPatrick Mooney 		}
30587c8c0b82SPatrick Mooney 		if (ddi_copyin(argp, &req, sizeof (req), md) != 0) {
30597c8c0b82SPatrick Mooney 			return (EFAULT);
30607c8c0b82SPatrick Mooney 		}
30617c8c0b82SPatrick Mooney 		return (vmmdev_do_vm_destroy(&req, cr));
30627c8c0b82SPatrick Mooney 	}
30637c8c0b82SPatrick Mooney 	case VMM_VM_SUPPORTED:
30647c8c0b82SPatrick Mooney 		return (vmm_is_supported(arg));
3065e760f150SPatrick Mooney 	case VMM_CHECK_IOMMU:
3066e760f150SPatrick Mooney 		if (!vmm_check_iommu()) {
3067e760f150SPatrick Mooney 			return (ENXIO);
3068e760f150SPatrick Mooney 		}
3069e760f150SPatrick Mooney 		return (0);
30707c8c0b82SPatrick Mooney 	case VMM_RESV_QUERY:
30716bba8b59SPatrick Mooney 	case VMM_RESV_SET_TARGET:
30727c8c0b82SPatrick Mooney 		return (vmmr_ioctl(cmd, arg, md, cr, rvalp));
30737c8c0b82SPatrick Mooney 	default:
30747c8c0b82SPatrick Mooney 		break;
30757c8c0b82SPatrick Mooney 	}
30767c8c0b82SPatrick Mooney 	/* No other actions are legal on ctl device */
30777c8c0b82SPatrick Mooney 	return (ENOTTY);
30787c8c0b82SPatrick Mooney }
30797c8c0b82SPatrick Mooney 
30807c8c0b82SPatrick Mooney static int
vmm_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * credp,int * rvalp)30817c8c0b82SPatrick Mooney vmm_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
30827c8c0b82SPatrick Mooney     int *rvalp)
30837c8c0b82SPatrick Mooney {
30847c8c0b82SPatrick Mooney 	vmm_softc_t	*sc;
30857c8c0b82SPatrick Mooney 	minor_t		minor;
30867c8c0b82SPatrick Mooney 
30877c8c0b82SPatrick Mooney 	/*
30887c8c0b82SPatrick Mooney 	 * Forbid running bhyve in a 32-bit process until it has been tested and
30897c8c0b82SPatrick Mooney 	 * verified to be safe.
30907c8c0b82SPatrick Mooney 	 */
30917c8c0b82SPatrick Mooney 	if (curproc->p_model != DATAMODEL_LP64) {
30927c8c0b82SPatrick Mooney 		return (EFBIG);
30937c8c0b82SPatrick Mooney 	}
30947c8c0b82SPatrick Mooney 
30957c8c0b82SPatrick Mooney 	/* The structs in bhyve ioctls assume a 64-bit datamodel */
30967c8c0b82SPatrick Mooney 	if (ddi_model_convert_from(mode & FMODELS) != DDI_MODEL_NONE) {
30977c8c0b82SPatrick Mooney 		return (ENOTSUP);
30987c8c0b82SPatrick Mooney 	}
30997c8c0b82SPatrick Mooney 
310017e9e0aeSPatrick Mooney 	/*
310117e9e0aeSPatrick Mooney 	 * Regardless of minor (vmmctl or instance), we respond to queries of
310217e9e0aeSPatrick Mooney 	 * the interface version.
310317e9e0aeSPatrick Mooney 	 */
310417e9e0aeSPatrick Mooney 	if (cmd == VMM_INTERFACE_VERSION) {
310517e9e0aeSPatrick Mooney 		*rvalp = VMM_CURRENT_INTERFACE_VERSION;
310617e9e0aeSPatrick Mooney 		return (0);
310717e9e0aeSPatrick Mooney 	}
310817e9e0aeSPatrick Mooney 
31097c8c0b82SPatrick Mooney 	minor = getminor(dev);
31107c8c0b82SPatrick Mooney 
31117c8c0b82SPatrick Mooney 	if (minor == VMM_CTL_MINOR) {
31127c8c0b82SPatrick Mooney 		return (vmm_ctl_ioctl(cmd, arg, mode, credp, rvalp));
31137c8c0b82SPatrick Mooney 	}
31147c8c0b82SPatrick Mooney 
31157c8c0b82SPatrick Mooney 	sc = ddi_get_soft_state(vmm_statep, minor);
311642640e49SPatrick Mooney 	ASSERT(sc != NULL);
31177c8c0b82SPatrick Mooney 
311842640e49SPatrick Mooney 	/*
311942640e49SPatrick Mooney 	 * Turn away any ioctls against an instance when it is being destroyed.
312042640e49SPatrick Mooney 	 * (Except for the ioctl inquiring about that destroy-in-progress.)
312142640e49SPatrick Mooney 	 */
312242640e49SPatrick Mooney 	if ((sc->vmm_flags & VMM_DESTROY) != 0) {
312342640e49SPatrick Mooney 		if (cmd == VM_DESTROY_PENDING) {
312442640e49SPatrick Mooney 			*rvalp = 1;
312542640e49SPatrick Mooney 			return (0);
312642640e49SPatrick Mooney 		}
31277c8c0b82SPatrick Mooney 		return (ENXIO);
312842640e49SPatrick Mooney 	}
31297c8c0b82SPatrick Mooney 
31307c8c0b82SPatrick Mooney 	return (vmmdev_do_ioctl(sc, cmd, arg, mode, credp, rvalp));
31317c8c0b82SPatrick Mooney }
31327c8c0b82SPatrick Mooney 
31337c8c0b82SPatrick Mooney static int
vmm_segmap(dev_t dev,off_t off,struct as * as,caddr_t * addrp,off_t len,unsigned int prot,unsigned int maxprot,unsigned int flags,cred_t * credp)31347c8c0b82SPatrick Mooney vmm_segmap(dev_t dev, off_t off, struct as *as, caddr_t *addrp, off_t len,
31357c8c0b82SPatrick Mooney     unsigned int prot, unsigned int maxprot, unsigned int flags, cred_t *credp)
31367c8c0b82SPatrick Mooney {
31377c8c0b82SPatrick Mooney 	vmm_softc_t *sc;
31387c8c0b82SPatrick Mooney 	const minor_t minor = getminor(dev);
31397c8c0b82SPatrick Mooney 	int err;
31407c8c0b82SPatrick Mooney 
31417c8c0b82SPatrick Mooney 	if (minor == VMM_CTL_MINOR) {
31427c8c0b82SPatrick Mooney 		return (ENODEV);
31437c8c0b82SPatrick Mooney 	}
31447c8c0b82SPatrick Mooney 	if (off < 0 || (off + len) <= 0) {
31457c8c0b82SPatrick Mooney 		return (EINVAL);
31467c8c0b82SPatrick Mooney 	}
31477c8c0b82SPatrick Mooney 	if ((prot & PROT_USER) == 0) {
31487c8c0b82SPatrick Mooney 		return (EACCES);
31497c8c0b82SPatrick Mooney 	}
31507c8c0b82SPatrick Mooney 
31517c8c0b82SPatrick Mooney 	sc = ddi_get_soft_state(vmm_statep, minor);
31527c8c0b82SPatrick Mooney 	ASSERT(sc);
31537c8c0b82SPatrick Mooney 
31547c8c0b82SPatrick Mooney 	if (sc->vmm_flags & VMM_DESTROY)
31557c8c0b82SPatrick Mooney 		return (ENXIO);
31567c8c0b82SPatrick Mooney 
31577c8c0b82SPatrick Mooney 	/* Grab read lock on the VM to prevent any changes to the memory map */
31587c8c0b82SPatrick Mooney 	vmm_read_lock(sc);
31597c8c0b82SPatrick Mooney 
31607c8c0b82SPatrick Mooney 	if (off >= VM_DEVMEM_START) {
31617c8c0b82SPatrick Mooney 		int segid;
31627c8c0b82SPatrick Mooney 		off_t segoff;
31637c8c0b82SPatrick Mooney 
31647c8c0b82SPatrick Mooney 		/* Mapping a devmem "device" */
31657c8c0b82SPatrick Mooney 		if (!vmmdev_devmem_segid(sc, off, len, &segid, &segoff)) {
31667c8c0b82SPatrick Mooney 			err = ENODEV;
31677c8c0b82SPatrick Mooney 		} else {
31687c8c0b82SPatrick Mooney 			err = vm_segmap_obj(sc->vmm_vm, segid, segoff, len, as,
31697c8c0b82SPatrick Mooney 			    addrp, prot, maxprot, flags);
31707c8c0b82SPatrick Mooney 		}
31717c8c0b82SPatrick Mooney 	} else {
31727c8c0b82SPatrick Mooney 		/* Mapping a part of the guest physical space */
31737c8c0b82SPatrick Mooney 		err = vm_segmap_space(sc->vmm_vm, off, as, addrp, len, prot,
31747c8c0b82SPatrick Mooney 		    maxprot, flags);
31757c8c0b82SPatrick Mooney 	}
31767c8c0b82SPatrick Mooney 
31777c8c0b82SPatrick Mooney 	vmm_read_unlock(sc);
31787c8c0b82SPatrick Mooney 	return (err);
31797c8c0b82SPatrick Mooney }
31807c8c0b82SPatrick Mooney 
31817c8c0b82SPatrick Mooney static sdev_plugin_validate_t
vmm_sdev_validate(sdev_ctx_t ctx)31827c8c0b82SPatrick Mooney vmm_sdev_validate(sdev_ctx_t ctx)
31837c8c0b82SPatrick Mooney {
31847c8c0b82SPatrick Mooney 	const char *name = sdev_ctx_name(ctx);
31857c8c0b82SPatrick Mooney 	vmm_softc_t *sc;
31867c8c0b82SPatrick Mooney 	sdev_plugin_validate_t ret;
31877c8c0b82SPatrick Mooney 	minor_t minor;
31887c8c0b82SPatrick Mooney 
31897c8c0b82SPatrick Mooney 	if (sdev_ctx_vtype(ctx) != VCHR)
31907c8c0b82SPatrick Mooney 		return (SDEV_VTOR_INVALID);
31917c8c0b82SPatrick Mooney 
31927c8c0b82SPatrick Mooney 	VERIFY3S(sdev_ctx_minor(ctx, &minor), ==, 0);
31937c8c0b82SPatrick Mooney 
31947c8c0b82SPatrick Mooney 	mutex_enter(&vmm_mtx);
31957c8c0b82SPatrick Mooney 	if ((sc = vmm_lookup(name)) == NULL)
31967c8c0b82SPatrick Mooney 		ret = SDEV_VTOR_INVALID;
31977c8c0b82SPatrick Mooney 	else if (sc->vmm_minor != minor)
31987c8c0b82SPatrick Mooney 		ret = SDEV_VTOR_STALE;
31997c8c0b82SPatrick Mooney 	else
32007c8c0b82SPatrick Mooney 		ret = SDEV_VTOR_VALID;
32017c8c0b82SPatrick Mooney 	mutex_exit(&vmm_mtx);
32027c8c0b82SPatrick Mooney 
32037c8c0b82SPatrick Mooney 	return (ret);
32047c8c0b82SPatrick Mooney }
32057c8c0b82SPatrick Mooney 
32067c8c0b82SPatrick Mooney static int
vmm_sdev_filldir(sdev_ctx_t ctx)32077c8c0b82SPatrick Mooney vmm_sdev_filldir(sdev_ctx_t ctx)
32087c8c0b82SPatrick Mooney {
32097c8c0b82SPatrick Mooney 	vmm_softc_t *sc;
32107c8c0b82SPatrick Mooney 	int ret;
32117c8c0b82SPatrick Mooney 
32127c8c0b82SPatrick Mooney 	if (strcmp(sdev_ctx_path(ctx), VMM_SDEV_ROOT) != 0) {
32137c8c0b82SPatrick Mooney 		cmn_err(CE_WARN, "%s: bad path '%s' != '%s'\n", __func__,
32147c8c0b82SPatrick Mooney 		    sdev_ctx_path(ctx), VMM_SDEV_ROOT);
32157c8c0b82SPatrick Mooney 		return (EINVAL);
32167c8c0b82SPatrick Mooney 	}
32177c8c0b82SPatrick Mooney 
32187c8c0b82SPatrick Mooney 	mutex_enter(&vmm_mtx);
32197c8c0b82SPatrick Mooney 	ASSERT(vmmdev_dip != NULL);
32207c8c0b82SPatrick Mooney 	for (sc = list_head(&vmm_list); sc != NULL;
32217c8c0b82SPatrick Mooney 	    sc = list_next(&vmm_list, sc)) {
32227c8c0b82SPatrick Mooney 		if (INGLOBALZONE(curproc) || sc->vmm_zone == curzone) {
32237c8c0b82SPatrick Mooney 			ret = sdev_plugin_mknod(ctx, sc->vmm_name,
32247c8c0b82SPatrick Mooney 			    S_IFCHR | 0600,
32257c8c0b82SPatrick Mooney 			    makedevice(ddi_driver_major(vmmdev_dip),
32267c8c0b82SPatrick Mooney 			    sc->vmm_minor));
32277c8c0b82SPatrick Mooney 		} else {
32287c8c0b82SPatrick Mooney 			continue;
32297c8c0b82SPatrick Mooney 		}
32307c8c0b82SPatrick Mooney 		if (ret != 0 && ret != EEXIST)
32317c8c0b82SPatrick Mooney 			goto out;
32327c8c0b82SPatrick Mooney 	}
32337c8c0b82SPatrick Mooney 
32347c8c0b82SPatrick Mooney 	ret = 0;
32357c8c0b82SPatrick Mooney 
32367c8c0b82SPatrick Mooney out:
32377c8c0b82SPatrick Mooney 	mutex_exit(&vmm_mtx);
32387c8c0b82SPatrick Mooney 	return (ret);
32397c8c0b82SPatrick Mooney }
32407c8c0b82SPatrick Mooney 
32417c8c0b82SPatrick Mooney /* ARGSUSED */
32427c8c0b82SPatrick Mooney static void
vmm_sdev_inactive(sdev_ctx_t ctx)32437c8c0b82SPatrick Mooney vmm_sdev_inactive(sdev_ctx_t ctx)
32447c8c0b82SPatrick Mooney {
32457c8c0b82SPatrick Mooney }
32467c8c0b82SPatrick Mooney 
32477c8c0b82SPatrick Mooney static sdev_plugin_ops_t vmm_sdev_ops = {
32487c8c0b82SPatrick Mooney 	.spo_version = SDEV_PLUGIN_VERSION,
32497c8c0b82SPatrick Mooney 	.spo_flags = SDEV_PLUGIN_SUBDIR,
32507c8c0b82SPatrick Mooney 	.spo_validate = vmm_sdev_validate,
32517c8c0b82SPatrick Mooney 	.spo_filldir = vmm_sdev_filldir,
32527c8c0b82SPatrick Mooney 	.spo_inactive = vmm_sdev_inactive
32537c8c0b82SPatrick Mooney };
32547c8c0b82SPatrick Mooney 
32557c8c0b82SPatrick Mooney /* ARGSUSED */
32567c8c0b82SPatrick Mooney static int
vmm_info(dev_info_t * dip,ddi_info_cmd_t cmd,void * arg,void ** result)32577c8c0b82SPatrick Mooney vmm_info(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
32587c8c0b82SPatrick Mooney {
32597c8c0b82SPatrick Mooney 	int error;
32607c8c0b82SPatrick Mooney 
32617c8c0b82SPatrick Mooney 	switch (cmd) {
32627c8c0b82SPatrick Mooney 	case DDI_INFO_DEVT2DEVINFO:
32637c8c0b82SPatrick Mooney 		*result = (void *)vmmdev_dip;
32647c8c0b82SPatrick Mooney 		error = DDI_SUCCESS;
32657c8c0b82SPatrick Mooney 		break;
32667c8c0b82SPatrick Mooney 	case DDI_INFO_DEVT2INSTANCE:
32677c8c0b82SPatrick Mooney 		*result = (void *)0;
32687c8c0b82SPatrick Mooney 		error = DDI_SUCCESS;
32697c8c0b82SPatrick Mooney 		break;
32707c8c0b82SPatrick Mooney 	default:
32717c8c0b82SPatrick Mooney 		error = DDI_FAILURE;
32727c8c0b82SPatrick Mooney 		break;
32737c8c0b82SPatrick Mooney 	}
32747c8c0b82SPatrick Mooney 	return (error);
32757c8c0b82SPatrick Mooney }
32767c8c0b82SPatrick Mooney 
32777c8c0b82SPatrick Mooney static int
vmm_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)32787c8c0b82SPatrick Mooney vmm_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
32797c8c0b82SPatrick Mooney {
32807c8c0b82SPatrick Mooney 	sdev_plugin_hdl_t sph;
32817c8c0b82SPatrick Mooney 	hma_reg_t *reg = NULL;
32827c8c0b82SPatrick Mooney 	boolean_t vmm_loaded = B_FALSE;
32837c8c0b82SPatrick Mooney 
32847c8c0b82SPatrick Mooney 	if (cmd != DDI_ATTACH) {
32857c8c0b82SPatrick Mooney 		return (DDI_FAILURE);
32867c8c0b82SPatrick Mooney 	}
32877c8c0b82SPatrick Mooney 
32887c8c0b82SPatrick Mooney 	mutex_enter(&vmmdev_mtx);
32897c8c0b82SPatrick Mooney 	/* Ensure we are not already attached. */
32907c8c0b82SPatrick Mooney 	if (vmmdev_dip != NULL) {
32917c8c0b82SPatrick Mooney 		mutex_exit(&vmmdev_mtx);
32927c8c0b82SPatrick Mooney 		return (DDI_FAILURE);
32937c8c0b82SPatrick Mooney 	}
32947c8c0b82SPatrick Mooney 
32957c8c0b82SPatrick Mooney 	vmm_sol_glue_init();
32967c8c0b82SPatrick Mooney 
32977c8c0b82SPatrick Mooney 	/*
32987c8c0b82SPatrick Mooney 	 * Perform temporary HMA registration to determine if the system
32997c8c0b82SPatrick Mooney 	 * is capable.
33007c8c0b82SPatrick Mooney 	 */
33017c8c0b82SPatrick Mooney 	if ((reg = hma_register(vmmdev_hvm_name)) == NULL) {
33027c8c0b82SPatrick Mooney 		goto fail;
33037c8c0b82SPatrick Mooney 	} else if (vmm_mod_load() != 0) {
33047c8c0b82SPatrick Mooney 		goto fail;
33057c8c0b82SPatrick Mooney 	}
33067c8c0b82SPatrick Mooney 	vmm_loaded = B_TRUE;
33077c8c0b82SPatrick Mooney 	hma_unregister(reg);
33087c8c0b82SPatrick Mooney 	reg = NULL;
33097c8c0b82SPatrick Mooney 
33107c8c0b82SPatrick Mooney 	/* Create control node.  Other nodes will be created on demand. */
33117c8c0b82SPatrick Mooney 	if (ddi_create_minor_node(dip, "ctl", S_IFCHR,
33127c8c0b82SPatrick Mooney 	    VMM_CTL_MINOR, DDI_PSEUDO, 0) != 0) {
33137c8c0b82SPatrick Mooney 		goto fail;
33147c8c0b82SPatrick Mooney 	}
33157c8c0b82SPatrick Mooney 
33167c8c0b82SPatrick Mooney 	sph = sdev_plugin_register(VMM_MODULE_NAME, &vmm_sdev_ops, NULL);
33177c8c0b82SPatrick Mooney 	if (sph == (sdev_plugin_hdl_t)NULL) {
33187c8c0b82SPatrick Mooney 		ddi_remove_minor_node(dip, NULL);
33197c8c0b82SPatrick Mooney 		goto fail;
33207c8c0b82SPatrick Mooney 	}
33217c8c0b82SPatrick Mooney 
33227c8c0b82SPatrick Mooney 	ddi_report_dev(dip);
33237c8c0b82SPatrick Mooney 	vmmdev_sdev_hdl = sph;
33247c8c0b82SPatrick Mooney 	vmmdev_dip = dip;
33257c8c0b82SPatrick Mooney 	mutex_exit(&vmmdev_mtx);
33267c8c0b82SPatrick Mooney 	return (DDI_SUCCESS);
33277c8c0b82SPatrick Mooney 
33287c8c0b82SPatrick Mooney fail:
33297c8c0b82SPatrick Mooney 	if (vmm_loaded) {
33307c8c0b82SPatrick Mooney 		VERIFY0(vmm_mod_unload());
33317c8c0b82SPatrick Mooney 	}
33327c8c0b82SPatrick Mooney 	if (reg != NULL) {
33337c8c0b82SPatrick Mooney 		hma_unregister(reg);
33347c8c0b82SPatrick Mooney 	}
33357c8c0b82SPatrick Mooney 	vmm_sol_glue_cleanup();
33367c8c0b82SPatrick Mooney 	mutex_exit(&vmmdev_mtx);
33377c8c0b82SPatrick Mooney 	return (DDI_FAILURE);
33387c8c0b82SPatrick Mooney }
33397c8c0b82SPatrick Mooney 
33407c8c0b82SPatrick Mooney static int
vmm_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)33417c8c0b82SPatrick Mooney vmm_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
33427c8c0b82SPatrick Mooney {
33437c8c0b82SPatrick Mooney 	if (cmd != DDI_DETACH) {
33447c8c0b82SPatrick Mooney 		return (DDI_FAILURE);
33457c8c0b82SPatrick Mooney 	}
33467c8c0b82SPatrick Mooney 
33477c8c0b82SPatrick Mooney 	/*
33487c8c0b82SPatrick Mooney 	 * Ensure that all resources have been cleaned up.
33497c8c0b82SPatrick Mooney 	 *
33507c8c0b82SPatrick Mooney 	 * To prevent a deadlock with iommu_cleanup() we'll fail the detach if
33517c8c0b82SPatrick Mooney 	 * vmmdev_mtx is already held. We can't wait for vmmdev_mtx with our
33527c8c0b82SPatrick Mooney 	 * devinfo locked as iommu_cleanup() tries to recursively lock each
33537c8c0b82SPatrick Mooney 	 * devinfo, including our own, while holding vmmdev_mtx.
33547c8c0b82SPatrick Mooney 	 */
33557c8c0b82SPatrick Mooney 	if (mutex_tryenter(&vmmdev_mtx) == 0)
33567c8c0b82SPatrick Mooney 		return (DDI_FAILURE);
33577c8c0b82SPatrick Mooney 
33587c8c0b82SPatrick Mooney 	mutex_enter(&vmm_mtx);
335942640e49SPatrick Mooney 	if (!list_is_empty(&vmm_list)) {
33607c8c0b82SPatrick Mooney 		mutex_exit(&vmm_mtx);
33617c8c0b82SPatrick Mooney 		mutex_exit(&vmmdev_mtx);
33627c8c0b82SPatrick Mooney 		return (DDI_FAILURE);
33637c8c0b82SPatrick Mooney 	}
33647c8c0b82SPatrick Mooney 	mutex_exit(&vmm_mtx);
33657c8c0b82SPatrick Mooney 
33667c8c0b82SPatrick Mooney 	if (!vmmr_is_empty()) {
33677c8c0b82SPatrick Mooney 		mutex_exit(&vmmdev_mtx);
33687c8c0b82SPatrick Mooney 		return (DDI_FAILURE);
33697c8c0b82SPatrick Mooney 	}
33707c8c0b82SPatrick Mooney 
33717c8c0b82SPatrick Mooney 	VERIFY(vmmdev_sdev_hdl != (sdev_plugin_hdl_t)NULL);
33727c8c0b82SPatrick Mooney 	if (sdev_plugin_unregister(vmmdev_sdev_hdl) != 0) {
33737c8c0b82SPatrick Mooney 		mutex_exit(&vmmdev_mtx);
33747c8c0b82SPatrick Mooney 		return (DDI_FAILURE);
33757c8c0b82SPatrick Mooney 	}
33767c8c0b82SPatrick Mooney 	vmmdev_sdev_hdl = (sdev_plugin_hdl_t)NULL;
33777c8c0b82SPatrick Mooney 
33787c8c0b82SPatrick Mooney 	/* Remove the control node. */
33797c8c0b82SPatrick Mooney 	ddi_remove_minor_node(dip, "ctl");
33807c8c0b82SPatrick Mooney 	vmmdev_dip = NULL;
33817c8c0b82SPatrick Mooney 
33827c8c0b82SPatrick Mooney 	VERIFY0(vmm_mod_unload());
33837c8c0b82SPatrick Mooney 	VERIFY3U(vmmdev_hma_reg, ==, NULL);
33847c8c0b82SPatrick Mooney 	vmm_sol_glue_cleanup();
33857c8c0b82SPatrick Mooney 
33867c8c0b82SPatrick Mooney 	mutex_exit(&vmmdev_mtx);
33877c8c0b82SPatrick Mooney 
33887c8c0b82SPatrick Mooney 	return (DDI_SUCCESS);
33897c8c0b82SPatrick Mooney }
33907c8c0b82SPatrick Mooney 
33917c8c0b82SPatrick Mooney static struct cb_ops vmm_cb_ops = {
33927c8c0b82SPatrick Mooney 	vmm_open,
33937c8c0b82SPatrick Mooney 	vmm_close,
33947c8c0b82SPatrick Mooney 	nodev,		/* strategy */
33957c8c0b82SPatrick Mooney 	nodev,		/* print */
33967c8c0b82SPatrick Mooney 	nodev,		/* dump */
33977c8c0b82SPatrick Mooney 	nodev,		/* read */
33987c8c0b82SPatrick Mooney 	nodev,		/* write */
33997c8c0b82SPatrick Mooney 	vmm_ioctl,
34007c8c0b82SPatrick Mooney 	nodev,		/* devmap */
34017c8c0b82SPatrick Mooney 	nodev,		/* mmap */
34027c8c0b82SPatrick Mooney 	vmm_segmap,
34037c8c0b82SPatrick Mooney 	nochpoll,	/* poll */
34047c8c0b82SPatrick Mooney 	ddi_prop_op,
34057c8c0b82SPatrick Mooney 	NULL,
34067c8c0b82SPatrick Mooney 	D_NEW | D_MP | D_DEVMAP
34077c8c0b82SPatrick Mooney };
34087c8c0b82SPatrick Mooney 
34097c8c0b82SPatrick Mooney static struct dev_ops vmm_ops = {
34107c8c0b82SPatrick Mooney 	DEVO_REV,
34117c8c0b82SPatrick Mooney 	0,
34127c8c0b82SPatrick Mooney 	vmm_info,
34137c8c0b82SPatrick Mooney 	nulldev,	/* identify */
34147c8c0b82SPatrick Mooney 	nulldev,	/* probe */
34157c8c0b82SPatrick Mooney 	vmm_attach,
34167c8c0b82SPatrick Mooney 	vmm_detach,
34177c8c0b82SPatrick Mooney 	nodev,		/* reset */
34187c8c0b82SPatrick Mooney 	&vmm_cb_ops,
34197c8c0b82SPatrick Mooney 	(struct bus_ops *)NULL
34207c8c0b82SPatrick Mooney };
34217c8c0b82SPatrick Mooney 
34227c8c0b82SPatrick Mooney static struct modldrv modldrv = {
34237c8c0b82SPatrick Mooney 	&mod_driverops,
34247c8c0b82SPatrick Mooney 	"bhyve vmm",
34257c8c0b82SPatrick Mooney 	&vmm_ops
34267c8c0b82SPatrick Mooney };
34277c8c0b82SPatrick Mooney 
34287c8c0b82SPatrick Mooney static struct modlinkage modlinkage = {
34297c8c0b82SPatrick Mooney 	MODREV_1,
34307c8c0b82SPatrick Mooney 	&modldrv,
34317c8c0b82SPatrick Mooney 	NULL
34327c8c0b82SPatrick Mooney };
34337c8c0b82SPatrick Mooney 
34347c8c0b82SPatrick Mooney int
_init(void)34357c8c0b82SPatrick Mooney _init(void)
34367c8c0b82SPatrick Mooney {
34377c8c0b82SPatrick Mooney 	int	error;
34387c8c0b82SPatrick Mooney 
34397c8c0b82SPatrick Mooney 	sysinit();
34407c8c0b82SPatrick Mooney 
34417c8c0b82SPatrick Mooney 	mutex_init(&vmmdev_mtx, NULL, MUTEX_DRIVER, NULL);
34427c8c0b82SPatrick Mooney 	mutex_init(&vmm_mtx, NULL, MUTEX_DRIVER, NULL);
34437c8c0b82SPatrick Mooney 	list_create(&vmm_list, sizeof (vmm_softc_t),
34447c8c0b82SPatrick Mooney 	    offsetof(vmm_softc_t, vmm_node));
34457c8c0b82SPatrick Mooney 	vmm_minors = id_space_create("vmm_minors", VMM_CTL_MINOR + 1, MAXMIN32);
34467c8c0b82SPatrick Mooney 
34477c8c0b82SPatrick Mooney 	error = ddi_soft_state_init(&vmm_statep, sizeof (vmm_softc_t), 0);
34487c8c0b82SPatrick Mooney 	if (error) {
34497c8c0b82SPatrick Mooney 		return (error);
34507c8c0b82SPatrick Mooney 	}
34517c8c0b82SPatrick Mooney 
34526bba8b59SPatrick Mooney 	error = vmmr_init();
34536bba8b59SPatrick Mooney 	if (error) {
34546bba8b59SPatrick Mooney 		ddi_soft_state_fini(&vmm_statep);
34556bba8b59SPatrick Mooney 		return (error);
34566bba8b59SPatrick Mooney 	}
34576bba8b59SPatrick Mooney 
34587c8c0b82SPatrick Mooney 	vmm_zsd_init();
34597c8c0b82SPatrick Mooney 
34607c8c0b82SPatrick Mooney 	error = mod_install(&modlinkage);
34617c8c0b82SPatrick Mooney 	if (error) {
34627c8c0b82SPatrick Mooney 		ddi_soft_state_fini(&vmm_statep);
34637c8c0b82SPatrick Mooney 		vmm_zsd_fini();
34647c8c0b82SPatrick Mooney 		vmmr_fini();
34657c8c0b82SPatrick Mooney 	}
34667c8c0b82SPatrick Mooney 
34677c8c0b82SPatrick Mooney 	return (error);
34687c8c0b82SPatrick Mooney }
34697c8c0b82SPatrick Mooney 
34707c8c0b82SPatrick Mooney int
_fini(void)34717c8c0b82SPatrick Mooney _fini(void)
34727c8c0b82SPatrick Mooney {
34737c8c0b82SPatrick Mooney 	int	error;
34747c8c0b82SPatrick Mooney 
34757c8c0b82SPatrick Mooney 	error = mod_remove(&modlinkage);
34767c8c0b82SPatrick Mooney 	if (error) {
34777c8c0b82SPatrick Mooney 		return (error);
34787c8c0b82SPatrick Mooney 	}
34797c8c0b82SPatrick Mooney 
34807c8c0b82SPatrick Mooney 	vmm_zsd_fini();
34817c8c0b82SPatrick Mooney 	vmmr_fini();
34827c8c0b82SPatrick Mooney 
34837c8c0b82SPatrick Mooney 	ddi_soft_state_fini(&vmm_statep);
34847c8c0b82SPatrick Mooney 
34857c8c0b82SPatrick Mooney 	return (0);
34867c8c0b82SPatrick Mooney }
34877c8c0b82SPatrick Mooney 
34887c8c0b82SPatrick Mooney int
_info(struct modinfo * modinfop)34897c8c0b82SPatrick Mooney _info(struct modinfo *modinfop)
34907c8c0b82SPatrick Mooney {
34917c8c0b82SPatrick Mooney 	return (mod_info(&modlinkage, modinfop));
34927c8c0b82SPatrick Mooney }
3493