xref: /freebsd/sys/arm64/vmm/vmm.c (revision 872fc1b8de1ac2a10465a315457cd69b4268cbe9)
147e07394SAndrew Turner /*-
247e07394SAndrew Turner  * SPDX-License-Identifier: BSD-2-Clause
347e07394SAndrew Turner  *
447e07394SAndrew Turner  * Copyright (C) 2015 Mihai Carabas <mihai.carabas@gmail.com>
547e07394SAndrew Turner  * All rights reserved.
647e07394SAndrew Turner  *
747e07394SAndrew Turner  * Redistribution and use in source and binary forms, with or without
847e07394SAndrew Turner  * modification, are permitted provided that the following conditions
947e07394SAndrew Turner  * are met:
1047e07394SAndrew Turner  * 1. Redistributions of source code must retain the above copyright
1147e07394SAndrew Turner  *    notice, this list of conditions and the following disclaimer.
1247e07394SAndrew Turner  * 2. Redistributions in binary form must reproduce the above copyright
1347e07394SAndrew Turner  *    notice, this list of conditions and the following disclaimer in the
1447e07394SAndrew Turner  *    documentation and/or other materials provided with the distribution.
1547e07394SAndrew Turner  *
1647e07394SAndrew Turner  * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1747e07394SAndrew Turner  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1847e07394SAndrew Turner  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1947e07394SAndrew Turner  * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
2047e07394SAndrew Turner  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2147e07394SAndrew Turner  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2247e07394SAndrew Turner  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2347e07394SAndrew Turner  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2447e07394SAndrew Turner  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2547e07394SAndrew Turner  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2647e07394SAndrew Turner  * SUCH DAMAGE.
2747e07394SAndrew Turner  */
2847e07394SAndrew Turner 
2947e07394SAndrew Turner #include <sys/param.h>
3047e07394SAndrew Turner #include <sys/systm.h>
3147e07394SAndrew Turner #include <sys/cpuset.h>
3247e07394SAndrew Turner #include <sys/kernel.h>
3347e07394SAndrew Turner #include <sys/linker.h>
3447e07394SAndrew Turner #include <sys/lock.h>
3547e07394SAndrew Turner #include <sys/malloc.h>
3647e07394SAndrew Turner #include <sys/module.h>
3747e07394SAndrew Turner #include <sys/mutex.h>
3847e07394SAndrew Turner #include <sys/pcpu.h>
3947e07394SAndrew Turner #include <sys/proc.h>
4047e07394SAndrew Turner #include <sys/queue.h>
4147e07394SAndrew Turner #include <sys/rwlock.h>
4247e07394SAndrew Turner #include <sys/sched.h>
4347e07394SAndrew Turner #include <sys/smp.h>
4447e07394SAndrew Turner #include <sys/sysctl.h>
4547e07394SAndrew Turner 
4647e07394SAndrew Turner #include <vm/vm.h>
4747e07394SAndrew Turner #include <vm/vm_object.h>
4847e07394SAndrew Turner #include <vm/vm_page.h>
4947e07394SAndrew Turner #include <vm/pmap.h>
5047e07394SAndrew Turner #include <vm/vm_map.h>
5147e07394SAndrew Turner #include <vm/vm_extern.h>
5247e07394SAndrew Turner #include <vm/vm_param.h>
5347e07394SAndrew Turner 
5447e07394SAndrew Turner #include <machine/armreg.h>
5547e07394SAndrew Turner #include <machine/cpu.h>
5647e07394SAndrew Turner #include <machine/fpu.h>
5747e07394SAndrew Turner #include <machine/machdep.h>
5847e07394SAndrew Turner #include <machine/pcb.h>
5947e07394SAndrew Turner #include <machine/smp.h>
6047e07394SAndrew Turner #include <machine/vm.h>
6147e07394SAndrew Turner #include <machine/vmparam.h>
6247e07394SAndrew Turner #include <machine/vmm.h>
6347e07394SAndrew Turner #include <machine/vmm_instruction_emul.h>
6447e07394SAndrew Turner 
6547e07394SAndrew Turner #include <dev/pci/pcireg.h>
66b9ef152bSMark Johnston #include <dev/vmm/vmm_dev.h>
673ccb0233SMark Johnston #include <dev/vmm/vmm_ktr.h>
68c76c2a19SMark Johnston #include <dev/vmm/vmm_mem.h>
6993e81baaSMark Johnston #include <dev/vmm/vmm_stat.h>
7047e07394SAndrew Turner 
7147e07394SAndrew Turner #include "arm64.h"
7247e07394SAndrew Turner #include "mmu.h"
7347e07394SAndrew Turner 
7447e07394SAndrew Turner #include "io/vgic.h"
7547e07394SAndrew Turner #include "io/vtimer.h"
7647e07394SAndrew Turner 
7747e07394SAndrew Turner struct vcpu {
7847e07394SAndrew Turner 	int		flags;
7947e07394SAndrew Turner 	enum vcpu_state	state;
8047e07394SAndrew Turner 	struct mtx	mtx;
8147e07394SAndrew Turner 	int		hostcpu;	/* host cpuid this vcpu last ran on */
8247e07394SAndrew Turner 	int		vcpuid;
8347e07394SAndrew Turner 	void		*stats;
8447e07394SAndrew Turner 	struct vm_exit	exitinfo;
8547e07394SAndrew Turner 	uint64_t	nextpc;		/* (x) next instruction to execute */
8647e07394SAndrew Turner 	struct vm	*vm;		/* (o) */
8747e07394SAndrew Turner 	void		*cookie;	/* (i) cpu-specific data */
8847e07394SAndrew Turner 	struct vfpstate	*guestfpu;	/* (a,i) guest fpu state */
8947e07394SAndrew Turner };
9047e07394SAndrew Turner 
9147e07394SAndrew Turner #define	vcpu_lock_initialized(v) mtx_initialized(&((v)->mtx))
9247e07394SAndrew Turner #define	vcpu_lock_init(v)	mtx_init(&((v)->mtx), "vcpu lock", 0, MTX_SPIN)
9347e07394SAndrew Turner #define	vcpu_lock_destroy(v)	mtx_destroy(&((v)->mtx))
9447e07394SAndrew Turner #define	vcpu_lock(v)		mtx_lock_spin(&((v)->mtx))
9547e07394SAndrew Turner #define	vcpu_unlock(v)		mtx_unlock_spin(&((v)->mtx))
9647e07394SAndrew Turner #define	vcpu_assert_locked(v)	mtx_assert(&((v)->mtx), MA_OWNED)
9747e07394SAndrew Turner 
9847e07394SAndrew Turner struct vmm_mmio_region {
9947e07394SAndrew Turner 	uint64_t start;
10047e07394SAndrew Turner 	uint64_t end;
10147e07394SAndrew Turner 	mem_region_read_t read;
10247e07394SAndrew Turner 	mem_region_write_t write;
10347e07394SAndrew Turner };
10447e07394SAndrew Turner #define	VM_MAX_MMIO_REGIONS	4
10547e07394SAndrew Turner 
10647e07394SAndrew Turner struct vmm_special_reg {
10747e07394SAndrew Turner 	uint32_t	esr_iss;
10847e07394SAndrew Turner 	uint32_t	esr_mask;
10947e07394SAndrew Turner 	reg_read_t	reg_read;
11047e07394SAndrew Turner 	reg_write_t	reg_write;
11147e07394SAndrew Turner 	void		*arg;
11247e07394SAndrew Turner };
11347e07394SAndrew Turner #define	VM_MAX_SPECIAL_REGS	16
11447e07394SAndrew Turner 
11547e07394SAndrew Turner /*
11647e07394SAndrew Turner  * Initialization:
11747e07394SAndrew Turner  * (o) initialized the first time the VM is created
11847e07394SAndrew Turner  * (i) initialized when VM is created and when it is reinitialized
11947e07394SAndrew Turner  * (x) initialized before use
12047e07394SAndrew Turner  */
12147e07394SAndrew Turner struct vm {
12247e07394SAndrew Turner 	void		*cookie;		/* (i) cpu-specific data */
12347e07394SAndrew Turner 	volatile cpuset_t active_cpus;		/* (i) active vcpus */
12447e07394SAndrew Turner 	volatile cpuset_t debug_cpus;		/* (i) vcpus stopped for debug */
12547e07394SAndrew Turner 	int		suspend;		/* (i) stop VM execution */
126a03354b0SMark Johnston 	bool		dying;			/* (o) is dying */
12747e07394SAndrew Turner 	volatile cpuset_t suspended_cpus; 	/* (i) suspended vcpus */
12847e07394SAndrew Turner 	volatile cpuset_t halted_cpus;		/* (x) cpus in a hard halt */
12947e07394SAndrew Turner 	struct vmspace	*vmspace;		/* (o) guest's address space */
130c76c2a19SMark Johnston 	struct vm_mem	mem;			/* (i) guest memory */
13147e07394SAndrew Turner 	char		name[VM_MAX_NAMELEN];	/* (o) virtual machine name */
13247e07394SAndrew Turner 	struct vcpu	**vcpu;			/* (i) guest vcpus */
13347e07394SAndrew Turner 	struct vmm_mmio_region mmio_region[VM_MAX_MMIO_REGIONS];
13447e07394SAndrew Turner 						/* (o) guest MMIO regions */
13547e07394SAndrew Turner 	struct vmm_special_reg special_reg[VM_MAX_SPECIAL_REGS];
13647e07394SAndrew Turner 	/* The following describe the vm cpu topology */
13747e07394SAndrew Turner 	uint16_t	sockets;		/* (o) num of sockets */
13847e07394SAndrew Turner 	uint16_t	cores;			/* (o) num of cores/socket */
13947e07394SAndrew Turner 	uint16_t	threads;		/* (o) num of threads/core */
14047e07394SAndrew Turner 	uint16_t	maxcpus;		/* (o) max pluggable cpus */
14147e07394SAndrew Turner 	struct sx	vcpus_init_lock;	/* (o) */
14247e07394SAndrew Turner };
14347e07394SAndrew Turner 
14447e07394SAndrew Turner static bool vmm_initialized = false;
14547e07394SAndrew Turner 
14647e07394SAndrew Turner static int vm_handle_wfi(struct vcpu *vcpu,
14747e07394SAndrew Turner 			 struct vm_exit *vme, bool *retu);
14847e07394SAndrew Turner 
14947e07394SAndrew Turner static MALLOC_DEFINE(M_VMM, "vmm", "vmm");
15047e07394SAndrew Turner 
15147e07394SAndrew Turner /* statistics */
15247e07394SAndrew Turner static VMM_STAT(VCPU_TOTAL_RUNTIME, "vcpu total runtime");
15347e07394SAndrew Turner 
15447e07394SAndrew Turner SYSCTL_NODE(_hw, OID_AUTO, vmm, CTLFLAG_RW, NULL, NULL);
15547e07394SAndrew Turner 
15647e07394SAndrew Turner static int vmm_ipinum;
15747e07394SAndrew Turner SYSCTL_INT(_hw_vmm, OID_AUTO, ipinum, CTLFLAG_RD, &vmm_ipinum, 0,
15847e07394SAndrew Turner     "IPI vector used for vcpu notifications");
15947e07394SAndrew Turner 
16047e07394SAndrew Turner struct vmm_regs {
16147e07394SAndrew Turner 	uint64_t	id_aa64afr0;
16247e07394SAndrew Turner 	uint64_t	id_aa64afr1;
16347e07394SAndrew Turner 	uint64_t	id_aa64dfr0;
16447e07394SAndrew Turner 	uint64_t	id_aa64dfr1;
16547e07394SAndrew Turner 	uint64_t	id_aa64isar0;
16647e07394SAndrew Turner 	uint64_t	id_aa64isar1;
16747e07394SAndrew Turner 	uint64_t	id_aa64isar2;
16847e07394SAndrew Turner 	uint64_t	id_aa64mmfr0;
16947e07394SAndrew Turner 	uint64_t	id_aa64mmfr1;
17047e07394SAndrew Turner 	uint64_t	id_aa64mmfr2;
17147e07394SAndrew Turner 	uint64_t	id_aa64pfr0;
17247e07394SAndrew Turner 	uint64_t	id_aa64pfr1;
17347e07394SAndrew Turner };
17447e07394SAndrew Turner 
17547e07394SAndrew Turner static const struct vmm_regs vmm_arch_regs_masks = {
17647e07394SAndrew Turner 	.id_aa64dfr0 =
17747e07394SAndrew Turner 	    ID_AA64DFR0_CTX_CMPs_MASK |
17847e07394SAndrew Turner 	    ID_AA64DFR0_WRPs_MASK |
17947e07394SAndrew Turner 	    ID_AA64DFR0_BRPs_MASK |
18047e07394SAndrew Turner 	    ID_AA64DFR0_PMUVer_3 |
18147e07394SAndrew Turner 	    ID_AA64DFR0_DebugVer_8,
18247e07394SAndrew Turner 	.id_aa64isar0 =
18347e07394SAndrew Turner 	    ID_AA64ISAR0_TLB_TLBIOSR |
18447e07394SAndrew Turner 	    ID_AA64ISAR0_SHA3_IMPL |
18547e07394SAndrew Turner 	    ID_AA64ISAR0_RDM_IMPL |
18647e07394SAndrew Turner 	    ID_AA64ISAR0_Atomic_IMPL |
18747e07394SAndrew Turner 	    ID_AA64ISAR0_CRC32_BASE |
18847e07394SAndrew Turner 	    ID_AA64ISAR0_SHA2_512 |
18947e07394SAndrew Turner 	    ID_AA64ISAR0_SHA1_BASE |
19047e07394SAndrew Turner 	    ID_AA64ISAR0_AES_PMULL,
19147e07394SAndrew Turner 	.id_aa64mmfr0 =
19247e07394SAndrew Turner 	    ID_AA64MMFR0_TGran4_IMPL |
19347e07394SAndrew Turner 	    ID_AA64MMFR0_TGran64_IMPL |
19447e07394SAndrew Turner 	    ID_AA64MMFR0_TGran16_IMPL |
19547e07394SAndrew Turner 	    ID_AA64MMFR0_ASIDBits_16 |
19647e07394SAndrew Turner 	    ID_AA64MMFR0_PARange_4P,
19747e07394SAndrew Turner 	.id_aa64mmfr1 =
19847e07394SAndrew Turner 	    ID_AA64MMFR1_SpecSEI_IMPL |
19947e07394SAndrew Turner 	    ID_AA64MMFR1_PAN_ATS1E1 |
20047e07394SAndrew Turner 	    ID_AA64MMFR1_HAFDBS_AF,
20147e07394SAndrew Turner 	.id_aa64pfr0 =
20247e07394SAndrew Turner 	    ID_AA64PFR0_GIC_CPUIF_NONE |
20347e07394SAndrew Turner 	    ID_AA64PFR0_AdvSIMD_HP |
20447e07394SAndrew Turner 	    ID_AA64PFR0_FP_HP |
20547e07394SAndrew Turner 	    ID_AA64PFR0_EL3_64 |
20647e07394SAndrew Turner 	    ID_AA64PFR0_EL2_64 |
20747e07394SAndrew Turner 	    ID_AA64PFR0_EL1_64 |
20847e07394SAndrew Turner 	    ID_AA64PFR0_EL0_64,
20947e07394SAndrew Turner };
21047e07394SAndrew Turner 
21147e07394SAndrew Turner /* Host registers masked by vmm_arch_regs_masks. */
21247e07394SAndrew Turner static struct vmm_regs vmm_arch_regs;
21347e07394SAndrew Turner 
21447e07394SAndrew Turner u_int vm_maxcpu;
21547e07394SAndrew Turner SYSCTL_UINT(_hw_vmm, OID_AUTO, maxcpu, CTLFLAG_RDTUN | CTLFLAG_NOFETCH,
21647e07394SAndrew Turner     &vm_maxcpu, 0, "Maximum number of vCPUs");
21747e07394SAndrew Turner 
21847e07394SAndrew Turner static void vcpu_notify_event_locked(struct vcpu *vcpu);
21947e07394SAndrew Turner 
22093e81baaSMark Johnston /* global statistics */
22193e81baaSMark Johnston VMM_STAT(VMEXIT_COUNT, "total number of vm exits");
22293e81baaSMark Johnston VMM_STAT(VMEXIT_UNKNOWN, "number of vmexits for the unknown exception");
22393e81baaSMark Johnston VMM_STAT(VMEXIT_WFI, "number of times wfi was intercepted");
22493e81baaSMark Johnston VMM_STAT(VMEXIT_WFE, "number of times wfe was intercepted");
22593e81baaSMark Johnston VMM_STAT(VMEXIT_HVC, "number of times hvc was intercepted");
22693e81baaSMark Johnston VMM_STAT(VMEXIT_MSR, "number of times msr/mrs was intercepted");
22793e81baaSMark Johnston VMM_STAT(VMEXIT_DATA_ABORT, "number of vmexits for a data abort");
22893e81baaSMark Johnston VMM_STAT(VMEXIT_INSN_ABORT, "number of vmexits for an instruction abort");
22993e81baaSMark Johnston VMM_STAT(VMEXIT_UNHANDLED_SYNC, "number of vmexits for an unhandled synchronous exception");
23093e81baaSMark Johnston VMM_STAT(VMEXIT_IRQ, "number of vmexits for an irq");
23193e81baaSMark Johnston VMM_STAT(VMEXIT_FIQ, "number of vmexits for an interrupt");
23293e81baaSMark Johnston VMM_STAT(VMEXIT_BRK, "number of vmexits for a breakpoint exception");
23393e81baaSMark Johnston VMM_STAT(VMEXIT_SS, "number of vmexits for a single-step exception");
23493e81baaSMark Johnston VMM_STAT(VMEXIT_UNHANDLED_EL2, "number of vmexits for an unhandled EL2 exception");
23593e81baaSMark Johnston VMM_STAT(VMEXIT_UNHANDLED, "number of vmexits for an unhandled exception");
23693e81baaSMark Johnston 
23747e07394SAndrew Turner /*
23847e07394SAndrew Turner  * Upper limit on vm_maxcpu. We could increase this to 28 bits, but this
23947e07394SAndrew Turner  * is a safe value for now.
24047e07394SAndrew Turner  */
24147e07394SAndrew Turner #define	VM_MAXCPU	MIN(0xffff - 1, CPU_SETSIZE)
24247e07394SAndrew Turner 
24347e07394SAndrew Turner static int
vmm_regs_init(struct vmm_regs * regs,const struct vmm_regs * masks)24447e07394SAndrew Turner vmm_regs_init(struct vmm_regs *regs, const struct vmm_regs *masks)
24547e07394SAndrew Turner {
24647e07394SAndrew Turner #define	_FETCH_KERN_REG(reg, field) do {				\
24747e07394SAndrew Turner 	regs->field = vmm_arch_regs_masks.field;			\
24847e07394SAndrew Turner 	if (!get_kernel_reg_masked(reg, &regs->field, masks->field))	\
24947e07394SAndrew Turner 		regs->field = 0;					\
25047e07394SAndrew Turner } while (0)
25147e07394SAndrew Turner 	_FETCH_KERN_REG(ID_AA64AFR0_EL1, id_aa64afr0);
25247e07394SAndrew Turner 	_FETCH_KERN_REG(ID_AA64AFR1_EL1, id_aa64afr1);
25347e07394SAndrew Turner 	_FETCH_KERN_REG(ID_AA64DFR0_EL1, id_aa64dfr0);
25447e07394SAndrew Turner 	_FETCH_KERN_REG(ID_AA64DFR1_EL1, id_aa64dfr1);
25547e07394SAndrew Turner 	_FETCH_KERN_REG(ID_AA64ISAR0_EL1, id_aa64isar0);
25647e07394SAndrew Turner 	_FETCH_KERN_REG(ID_AA64ISAR1_EL1, id_aa64isar1);
25747e07394SAndrew Turner 	_FETCH_KERN_REG(ID_AA64ISAR2_EL1, id_aa64isar2);
25847e07394SAndrew Turner 	_FETCH_KERN_REG(ID_AA64MMFR0_EL1, id_aa64mmfr0);
25947e07394SAndrew Turner 	_FETCH_KERN_REG(ID_AA64MMFR1_EL1, id_aa64mmfr1);
26047e07394SAndrew Turner 	_FETCH_KERN_REG(ID_AA64MMFR2_EL1, id_aa64mmfr2);
26147e07394SAndrew Turner 	_FETCH_KERN_REG(ID_AA64PFR0_EL1, id_aa64pfr0);
26247e07394SAndrew Turner 	_FETCH_KERN_REG(ID_AA64PFR1_EL1, id_aa64pfr1);
26347e07394SAndrew Turner #undef _FETCH_KERN_REG
26447e07394SAndrew Turner 	return (0);
26547e07394SAndrew Turner }
26647e07394SAndrew Turner 
26747e07394SAndrew Turner static void
vcpu_cleanup(struct vcpu * vcpu,bool destroy)26847e07394SAndrew Turner vcpu_cleanup(struct vcpu *vcpu, bool destroy)
26947e07394SAndrew Turner {
27047e07394SAndrew Turner 	vmmops_vcpu_cleanup(vcpu->cookie);
27147e07394SAndrew Turner 	vcpu->cookie = NULL;
27247e07394SAndrew Turner 	if (destroy) {
27347e07394SAndrew Turner 		vmm_stat_free(vcpu->stats);
27447e07394SAndrew Turner 		fpu_save_area_free(vcpu->guestfpu);
27547e07394SAndrew Turner 		vcpu_lock_destroy(vcpu);
27647e07394SAndrew Turner 	}
27747e07394SAndrew Turner }
27847e07394SAndrew Turner 
27947e07394SAndrew Turner static struct vcpu *
vcpu_alloc(struct vm * vm,int vcpu_id)28047e07394SAndrew Turner vcpu_alloc(struct vm *vm, int vcpu_id)
28147e07394SAndrew Turner {
28247e07394SAndrew Turner 	struct vcpu *vcpu;
28347e07394SAndrew Turner 
28447e07394SAndrew Turner 	KASSERT(vcpu_id >= 0 && vcpu_id < vm->maxcpus,
28547e07394SAndrew Turner 	    ("vcpu_alloc: invalid vcpu %d", vcpu_id));
28647e07394SAndrew Turner 
28747e07394SAndrew Turner 	vcpu = malloc(sizeof(*vcpu), M_VMM, M_WAITOK | M_ZERO);
28847e07394SAndrew Turner 	vcpu_lock_init(vcpu);
28947e07394SAndrew Turner 	vcpu->state = VCPU_IDLE;
29047e07394SAndrew Turner 	vcpu->hostcpu = NOCPU;
29147e07394SAndrew Turner 	vcpu->vcpuid = vcpu_id;
29247e07394SAndrew Turner 	vcpu->vm = vm;
29347e07394SAndrew Turner 	vcpu->guestfpu = fpu_save_area_alloc();
29447e07394SAndrew Turner 	vcpu->stats = vmm_stat_alloc();
29547e07394SAndrew Turner 	return (vcpu);
29647e07394SAndrew Turner }
29747e07394SAndrew Turner 
29847e07394SAndrew Turner static void
vcpu_init(struct vcpu * vcpu)29947e07394SAndrew Turner vcpu_init(struct vcpu *vcpu)
30047e07394SAndrew Turner {
30147e07394SAndrew Turner 	vcpu->cookie = vmmops_vcpu_init(vcpu->vm->cookie, vcpu, vcpu->vcpuid);
30247e07394SAndrew Turner 	MPASS(vcpu->cookie != NULL);
30347e07394SAndrew Turner 	fpu_save_area_reset(vcpu->guestfpu);
30447e07394SAndrew Turner 	vmm_stat_init(vcpu->stats);
30547e07394SAndrew Turner }
30647e07394SAndrew Turner 
30747e07394SAndrew Turner struct vm_exit *
vm_exitinfo(struct vcpu * vcpu)30847e07394SAndrew Turner vm_exitinfo(struct vcpu *vcpu)
30947e07394SAndrew Turner {
31047e07394SAndrew Turner 	return (&vcpu->exitinfo);
31147e07394SAndrew Turner }
31247e07394SAndrew Turner 
31347e07394SAndrew Turner static int
vmm_unsupported_quirk(void)314*872fc1b8SAndrew Turner vmm_unsupported_quirk(void)
315*872fc1b8SAndrew Turner {
316*872fc1b8SAndrew Turner 	/*
317*872fc1b8SAndrew Turner 	 * Known to not load on Ampere eMAG
318*872fc1b8SAndrew Turner 	 * https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=285051
319*872fc1b8SAndrew Turner 	 */
320*872fc1b8SAndrew Turner 	if (CPU_MATCH(CPU_IMPL_MASK | CPU_PART_MASK, CPU_IMPL_APM,
321*872fc1b8SAndrew Turner 	    CPU_PART_EMAG8180, 0, 0))
322*872fc1b8SAndrew Turner 		return (ENXIO);
323*872fc1b8SAndrew Turner 
324*872fc1b8SAndrew Turner 	return (0);
325*872fc1b8SAndrew Turner }
326*872fc1b8SAndrew Turner 
327*872fc1b8SAndrew Turner static int
vmm_init(void)32847e07394SAndrew Turner vmm_init(void)
32947e07394SAndrew Turner {
33047e07394SAndrew Turner 	int error;
33147e07394SAndrew Turner 
33247e07394SAndrew Turner 	vm_maxcpu = mp_ncpus;
33347e07394SAndrew Turner 	TUNABLE_INT_FETCH("hw.vmm.maxcpu", &vm_maxcpu);
33447e07394SAndrew Turner 
33547e07394SAndrew Turner 	if (vm_maxcpu > VM_MAXCPU) {
33647e07394SAndrew Turner 		printf("vmm: vm_maxcpu clamped to %u\n", VM_MAXCPU);
33747e07394SAndrew Turner 		vm_maxcpu = VM_MAXCPU;
33847e07394SAndrew Turner 	}
33947e07394SAndrew Turner 	if (vm_maxcpu == 0)
34047e07394SAndrew Turner 		vm_maxcpu = 1;
34147e07394SAndrew Turner 
34247e07394SAndrew Turner 	error = vmm_regs_init(&vmm_arch_regs, &vmm_arch_regs_masks);
34347e07394SAndrew Turner 	if (error != 0)
34447e07394SAndrew Turner 		return (error);
34547e07394SAndrew Turner 
34647e07394SAndrew Turner 	return (vmmops_modinit(0));
34747e07394SAndrew Turner }
34847e07394SAndrew Turner 
34947e07394SAndrew Turner static int
vmm_handler(module_t mod,int what,void * arg)35047e07394SAndrew Turner vmm_handler(module_t mod, int what, void *arg)
35147e07394SAndrew Turner {
35247e07394SAndrew Turner 	int error;
35347e07394SAndrew Turner 
35447e07394SAndrew Turner 	switch (what) {
35547e07394SAndrew Turner 	case MOD_LOAD:
356*872fc1b8SAndrew Turner 		error = vmm_unsupported_quirk();
357*872fc1b8SAndrew Turner 		if (error != 0)
358*872fc1b8SAndrew Turner 			break;
359a97f683fSMark Johnston 		error = vmmdev_init();
360a97f683fSMark Johnston 		if (error != 0)
361a97f683fSMark Johnston 			break;
36247e07394SAndrew Turner 		error = vmm_init();
36347e07394SAndrew Turner 		if (error == 0)
36447e07394SAndrew Turner 			vmm_initialized = true;
3654a46ece6SMark Johnston 		else
3664a46ece6SMark Johnston 			(void)vmmdev_cleanup();
36747e07394SAndrew Turner 		break;
36847e07394SAndrew Turner 	case MOD_UNLOAD:
36947e07394SAndrew Turner 		error = vmmdev_cleanup();
37047e07394SAndrew Turner 		if (error == 0 && vmm_initialized) {
37147e07394SAndrew Turner 			error = vmmops_modcleanup();
3724a46ece6SMark Johnston 			if (error) {
3734a46ece6SMark Johnston 				/*
3744a46ece6SMark Johnston 				 * Something bad happened - prevent new
3754a46ece6SMark Johnston 				 * VMs from being created
3764a46ece6SMark Johnston 				 */
37747e07394SAndrew Turner 				vmm_initialized = false;
37847e07394SAndrew Turner 			}
3794a46ece6SMark Johnston 		}
38047e07394SAndrew Turner 		break;
38147e07394SAndrew Turner 	default:
38247e07394SAndrew Turner 		error = 0;
38347e07394SAndrew Turner 		break;
38447e07394SAndrew Turner 	}
38547e07394SAndrew Turner 	return (error);
38647e07394SAndrew Turner }
38747e07394SAndrew Turner 
38847e07394SAndrew Turner static moduledata_t vmm_kmod = {
38947e07394SAndrew Turner 	"vmm",
39047e07394SAndrew Turner 	vmm_handler,
39147e07394SAndrew Turner 	NULL
39247e07394SAndrew Turner };
39347e07394SAndrew Turner 
39447e07394SAndrew Turner /*
39547e07394SAndrew Turner  * vmm initialization has the following dependencies:
39647e07394SAndrew Turner  *
39747e07394SAndrew Turner  * - HYP initialization requires smp_rendezvous() and therefore must happen
39847e07394SAndrew Turner  *   after SMP is fully functional (after SI_SUB_SMP).
399d7023078SMark Johnston  * - vmm device initialization requires an initialized devfs.
40047e07394SAndrew Turner  */
401d7023078SMark Johnston DECLARE_MODULE(vmm, vmm_kmod, MAX(SI_SUB_SMP, SI_SUB_DEVFS) + 1, SI_ORDER_ANY);
40247e07394SAndrew Turner MODULE_VERSION(vmm, 1);
40347e07394SAndrew Turner 
40447e07394SAndrew Turner static void
vm_init(struct vm * vm,bool create)40547e07394SAndrew Turner vm_init(struct vm *vm, bool create)
40647e07394SAndrew Turner {
40747e07394SAndrew Turner 	int i;
40847e07394SAndrew Turner 
40947e07394SAndrew Turner 	vm->cookie = vmmops_init(vm, vmspace_pmap(vm->vmspace));
41047e07394SAndrew Turner 	MPASS(vm->cookie != NULL);
41147e07394SAndrew Turner 
41247e07394SAndrew Turner 	CPU_ZERO(&vm->active_cpus);
41347e07394SAndrew Turner 	CPU_ZERO(&vm->debug_cpus);
41447e07394SAndrew Turner 
41547e07394SAndrew Turner 	vm->suspend = 0;
41647e07394SAndrew Turner 	CPU_ZERO(&vm->suspended_cpus);
41747e07394SAndrew Turner 
41847e07394SAndrew Turner 	memset(vm->mmio_region, 0, sizeof(vm->mmio_region));
41947e07394SAndrew Turner 	memset(vm->special_reg, 0, sizeof(vm->special_reg));
42047e07394SAndrew Turner 
42147e07394SAndrew Turner 	if (!create) {
42247e07394SAndrew Turner 		for (i = 0; i < vm->maxcpus; i++) {
42347e07394SAndrew Turner 			if (vm->vcpu[i] != NULL)
42447e07394SAndrew Turner 				vcpu_init(vm->vcpu[i]);
42547e07394SAndrew Turner 		}
42647e07394SAndrew Turner 	}
42747e07394SAndrew Turner }
42847e07394SAndrew Turner 
429a03354b0SMark Johnston void
vm_disable_vcpu_creation(struct vm * vm)430a03354b0SMark Johnston vm_disable_vcpu_creation(struct vm *vm)
431a03354b0SMark Johnston {
432a03354b0SMark Johnston 	sx_xlock(&vm->vcpus_init_lock);
433a03354b0SMark Johnston 	vm->dying = true;
434a03354b0SMark Johnston 	sx_xunlock(&vm->vcpus_init_lock);
435a03354b0SMark Johnston }
436a03354b0SMark Johnston 
43747e07394SAndrew Turner struct vcpu *
vm_alloc_vcpu(struct vm * vm,int vcpuid)43847e07394SAndrew Turner vm_alloc_vcpu(struct vm *vm, int vcpuid)
43947e07394SAndrew Turner {
44047e07394SAndrew Turner 	struct vcpu *vcpu;
44147e07394SAndrew Turner 
44247e07394SAndrew Turner 	if (vcpuid < 0 || vcpuid >= vm_get_maxcpus(vm))
44347e07394SAndrew Turner 		return (NULL);
44447e07394SAndrew Turner 
44547e07394SAndrew Turner 	/* Some interrupt controllers may have a CPU limit */
44647e07394SAndrew Turner 	if (vcpuid >= vgic_max_cpu_count(vm->cookie))
44747e07394SAndrew Turner 		return (NULL);
44847e07394SAndrew Turner 
44972ae04c7SRuslan Bukin 	vcpu = (struct vcpu *)
45072ae04c7SRuslan Bukin 	    atomic_load_acq_ptr((uintptr_t *)&vm->vcpu[vcpuid]);
45147e07394SAndrew Turner 	if (__predict_true(vcpu != NULL))
45247e07394SAndrew Turner 		return (vcpu);
45347e07394SAndrew Turner 
45447e07394SAndrew Turner 	sx_xlock(&vm->vcpus_init_lock);
45547e07394SAndrew Turner 	vcpu = vm->vcpu[vcpuid];
456a03354b0SMark Johnston 	if (vcpu == NULL && !vm->dying) {
45747e07394SAndrew Turner 		vcpu = vcpu_alloc(vm, vcpuid);
45847e07394SAndrew Turner 		vcpu_init(vcpu);
45947e07394SAndrew Turner 
46047e07394SAndrew Turner 		/*
46147e07394SAndrew Turner 		 * Ensure vCPU is fully created before updating pointer
46247e07394SAndrew Turner 		 * to permit unlocked reads above.
46347e07394SAndrew Turner 		 */
46447e07394SAndrew Turner 		atomic_store_rel_ptr((uintptr_t *)&vm->vcpu[vcpuid],
46547e07394SAndrew Turner 		    (uintptr_t)vcpu);
46647e07394SAndrew Turner 	}
46747e07394SAndrew Turner 	sx_xunlock(&vm->vcpus_init_lock);
46847e07394SAndrew Turner 	return (vcpu);
46947e07394SAndrew Turner }
47047e07394SAndrew Turner 
47147e07394SAndrew Turner void
vm_slock_vcpus(struct vm * vm)47247e07394SAndrew Turner vm_slock_vcpus(struct vm *vm)
47347e07394SAndrew Turner {
47447e07394SAndrew Turner 	sx_slock(&vm->vcpus_init_lock);
47547e07394SAndrew Turner }
47647e07394SAndrew Turner 
47747e07394SAndrew Turner void
vm_unlock_vcpus(struct vm * vm)47847e07394SAndrew Turner vm_unlock_vcpus(struct vm *vm)
47947e07394SAndrew Turner {
48047e07394SAndrew Turner 	sx_unlock(&vm->vcpus_init_lock);
48147e07394SAndrew Turner }
48247e07394SAndrew Turner 
48347e07394SAndrew Turner int
vm_create(const char * name,struct vm ** retvm)48447e07394SAndrew Turner vm_create(const char *name, struct vm **retvm)
48547e07394SAndrew Turner {
48647e07394SAndrew Turner 	struct vm *vm;
48747e07394SAndrew Turner 	struct vmspace *vmspace;
48847e07394SAndrew Turner 
48947e07394SAndrew Turner 	/*
49047e07394SAndrew Turner 	 * If vmm.ko could not be successfully initialized then don't attempt
49147e07394SAndrew Turner 	 * to create the virtual machine.
49247e07394SAndrew Turner 	 */
49347e07394SAndrew Turner 	if (!vmm_initialized)
49447e07394SAndrew Turner 		return (ENXIO);
49547e07394SAndrew Turner 
49647e07394SAndrew Turner 	if (name == NULL || strlen(name) >= VM_MAX_NAMELEN)
49747e07394SAndrew Turner 		return (EINVAL);
49847e07394SAndrew Turner 
49947e07394SAndrew Turner 	vmspace = vmmops_vmspace_alloc(0, 1ul << 39);
50047e07394SAndrew Turner 	if (vmspace == NULL)
50147e07394SAndrew Turner 		return (ENOMEM);
50247e07394SAndrew Turner 
50347e07394SAndrew Turner 	vm = malloc(sizeof(struct vm), M_VMM, M_WAITOK | M_ZERO);
50447e07394SAndrew Turner 	strcpy(vm->name, name);
50547e07394SAndrew Turner 	vm->vmspace = vmspace;
506c76c2a19SMark Johnston 	vm_mem_init(&vm->mem);
50747e07394SAndrew Turner 	sx_init(&vm->vcpus_init_lock, "vm vcpus");
50847e07394SAndrew Turner 
50947e07394SAndrew Turner 	vm->sockets = 1;
51047e07394SAndrew Turner 	vm->cores = 1;			/* XXX backwards compatibility */
51147e07394SAndrew Turner 	vm->threads = 1;		/* XXX backwards compatibility */
51247e07394SAndrew Turner 	vm->maxcpus = vm_maxcpu;
51347e07394SAndrew Turner 
51447e07394SAndrew Turner 	vm->vcpu = malloc(sizeof(*vm->vcpu) * vm->maxcpus, M_VMM,
51547e07394SAndrew Turner 	    M_WAITOK | M_ZERO);
51647e07394SAndrew Turner 
51747e07394SAndrew Turner 	vm_init(vm, true);
51847e07394SAndrew Turner 
51947e07394SAndrew Turner 	*retvm = vm;
52047e07394SAndrew Turner 	return (0);
52147e07394SAndrew Turner }
52247e07394SAndrew Turner 
52347e07394SAndrew Turner void
vm_get_topology(struct vm * vm,uint16_t * sockets,uint16_t * cores,uint16_t * threads,uint16_t * maxcpus)52447e07394SAndrew Turner vm_get_topology(struct vm *vm, uint16_t *sockets, uint16_t *cores,
52547e07394SAndrew Turner     uint16_t *threads, uint16_t *maxcpus)
52647e07394SAndrew Turner {
52747e07394SAndrew Turner 	*sockets = vm->sockets;
52847e07394SAndrew Turner 	*cores = vm->cores;
52947e07394SAndrew Turner 	*threads = vm->threads;
53047e07394SAndrew Turner 	*maxcpus = vm->maxcpus;
53147e07394SAndrew Turner }
53247e07394SAndrew Turner 
53347e07394SAndrew Turner uint16_t
vm_get_maxcpus(struct vm * vm)53447e07394SAndrew Turner vm_get_maxcpus(struct vm *vm)
53547e07394SAndrew Turner {
53647e07394SAndrew Turner 	return (vm->maxcpus);
53747e07394SAndrew Turner }
53847e07394SAndrew Turner 
53947e07394SAndrew Turner int
vm_set_topology(struct vm * vm,uint16_t sockets,uint16_t cores,uint16_t threads,uint16_t maxcpus)54047e07394SAndrew Turner vm_set_topology(struct vm *vm, uint16_t sockets, uint16_t cores,
54147e07394SAndrew Turner     uint16_t threads, uint16_t maxcpus)
54247e07394SAndrew Turner {
54347e07394SAndrew Turner 	/* Ignore maxcpus. */
54447e07394SAndrew Turner 	if ((sockets * cores * threads) > vm->maxcpus)
54547e07394SAndrew Turner 		return (EINVAL);
54647e07394SAndrew Turner 	vm->sockets = sockets;
54747e07394SAndrew Turner 	vm->cores = cores;
54847e07394SAndrew Turner 	vm->threads = threads;
54947e07394SAndrew Turner 	return(0);
55047e07394SAndrew Turner }
55147e07394SAndrew Turner 
55247e07394SAndrew Turner static void
vm_cleanup(struct vm * vm,bool destroy)55347e07394SAndrew Turner vm_cleanup(struct vm *vm, bool destroy)
55447e07394SAndrew Turner {
55547e07394SAndrew Turner 	pmap_t pmap __diagused;
55647e07394SAndrew Turner 	int i;
55747e07394SAndrew Turner 
55847e07394SAndrew Turner 	if (destroy) {
559c76c2a19SMark Johnston 		vm_xlock_memsegs(vm);
56047e07394SAndrew Turner 		pmap = vmspace_pmap(vm->vmspace);
56147e07394SAndrew Turner 		sched_pin();
56247e07394SAndrew Turner 		PCPU_SET(curvmpmap, NULL);
56347e07394SAndrew Turner 		sched_unpin();
56447e07394SAndrew Turner 		CPU_FOREACH(i) {
56547e07394SAndrew Turner 			MPASS(cpuid_to_pcpu[i]->pc_curvmpmap != pmap);
56647e07394SAndrew Turner 		}
567c76c2a19SMark Johnston 	} else
568c76c2a19SMark Johnston 		vm_assert_memseg_xlocked(vm);
569c76c2a19SMark Johnston 
57047e07394SAndrew Turner 
57147e07394SAndrew Turner 	vgic_detach_from_vm(vm->cookie);
57247e07394SAndrew Turner 
57347e07394SAndrew Turner 	for (i = 0; i < vm->maxcpus; i++) {
57447e07394SAndrew Turner 		if (vm->vcpu[i] != NULL)
57547e07394SAndrew Turner 			vcpu_cleanup(vm->vcpu[i], destroy);
57647e07394SAndrew Turner 	}
57747e07394SAndrew Turner 
57847e07394SAndrew Turner 	vmmops_cleanup(vm->cookie);
57947e07394SAndrew Turner 
580c76c2a19SMark Johnston 	vm_mem_cleanup(vm);
58147e07394SAndrew Turner 	if (destroy) {
582c76c2a19SMark Johnston 		vm_mem_destroy(vm);
58347e07394SAndrew Turner 
58447e07394SAndrew Turner 		vmmops_vmspace_free(vm->vmspace);
58547e07394SAndrew Turner 		vm->vmspace = NULL;
58647e07394SAndrew Turner 
58747e07394SAndrew Turner 		for (i = 0; i < vm->maxcpus; i++)
58847e07394SAndrew Turner 			free(vm->vcpu[i], M_VMM);
58947e07394SAndrew Turner 		free(vm->vcpu, M_VMM);
59047e07394SAndrew Turner 		sx_destroy(&vm->vcpus_init_lock);
59147e07394SAndrew Turner 	}
59247e07394SAndrew Turner }
59347e07394SAndrew Turner 
59447e07394SAndrew Turner void
vm_destroy(struct vm * vm)59547e07394SAndrew Turner vm_destroy(struct vm *vm)
59647e07394SAndrew Turner {
59747e07394SAndrew Turner 	vm_cleanup(vm, true);
59847e07394SAndrew Turner 	free(vm, M_VMM);
59947e07394SAndrew Turner }
60047e07394SAndrew Turner 
60147e07394SAndrew Turner int
vm_reinit(struct vm * vm)60247e07394SAndrew Turner vm_reinit(struct vm *vm)
60347e07394SAndrew Turner {
60447e07394SAndrew Turner 	int error;
60547e07394SAndrew Turner 
60647e07394SAndrew Turner 	/*
60747e07394SAndrew Turner 	 * A virtual machine can be reset only if all vcpus are suspended.
60847e07394SAndrew Turner 	 */
60947e07394SAndrew Turner 	if (CPU_CMP(&vm->suspended_cpus, &vm->active_cpus) == 0) {
61047e07394SAndrew Turner 		vm_cleanup(vm, false);
61147e07394SAndrew Turner 		vm_init(vm, false);
61247e07394SAndrew Turner 		error = 0;
61347e07394SAndrew Turner 	} else {
61447e07394SAndrew Turner 		error = EBUSY;
61547e07394SAndrew Turner 	}
61647e07394SAndrew Turner 
61747e07394SAndrew Turner 	return (error);
61847e07394SAndrew Turner }
61947e07394SAndrew Turner 
62047e07394SAndrew Turner const char *
vm_name(struct vm * vm)62147e07394SAndrew Turner vm_name(struct vm *vm)
62247e07394SAndrew Turner {
62347e07394SAndrew Turner 	return (vm->name);
62447e07394SAndrew Turner }
62547e07394SAndrew Turner 
62647e07394SAndrew Turner int
vm_gla2gpa_nofault(struct vcpu * vcpu,struct vm_guest_paging * paging,uint64_t gla,int prot,uint64_t * gpa,int * is_fault)62747e07394SAndrew Turner vm_gla2gpa_nofault(struct vcpu *vcpu, struct vm_guest_paging *paging,
62847e07394SAndrew Turner     uint64_t gla, int prot, uint64_t *gpa, int *is_fault)
62947e07394SAndrew Turner {
630c76c2a19SMark Johnston 	return (vmmops_gla2gpa(vcpu->cookie, paging, gla, prot, gpa, is_fault));
63147e07394SAndrew Turner }
63247e07394SAndrew Turner 
63347e07394SAndrew Turner static int
vmm_reg_raz(struct vcpu * vcpu,uint64_t * rval,void * arg)63447e07394SAndrew Turner vmm_reg_raz(struct vcpu *vcpu, uint64_t *rval, void *arg)
63547e07394SAndrew Turner {
63647e07394SAndrew Turner 	*rval = 0;
63747e07394SAndrew Turner 	return (0);
63847e07394SAndrew Turner }
63947e07394SAndrew Turner 
64047e07394SAndrew Turner static int
vmm_reg_read_arg(struct vcpu * vcpu,uint64_t * rval,void * arg)64147e07394SAndrew Turner vmm_reg_read_arg(struct vcpu *vcpu, uint64_t *rval, void *arg)
64247e07394SAndrew Turner {
64347e07394SAndrew Turner 	*rval = *(uint64_t *)arg;
64447e07394SAndrew Turner 	return (0);
64547e07394SAndrew Turner }
64647e07394SAndrew Turner 
64747e07394SAndrew Turner static int
vmm_reg_wi(struct vcpu * vcpu,uint64_t wval,void * arg)64847e07394SAndrew Turner vmm_reg_wi(struct vcpu *vcpu, uint64_t wval, void *arg)
64947e07394SAndrew Turner {
65047e07394SAndrew Turner 	return (0);
65147e07394SAndrew Turner }
65247e07394SAndrew Turner 
65347e07394SAndrew Turner static const struct vmm_special_reg vmm_special_regs[] = {
65447e07394SAndrew Turner #define	SPECIAL_REG(_reg, _read, _write)				\
65547e07394SAndrew Turner 	{								\
65647e07394SAndrew Turner 		.esr_iss = ((_reg ## _op0) << ISS_MSR_OP0_SHIFT) |	\
65747e07394SAndrew Turner 		    ((_reg ## _op1) << ISS_MSR_OP1_SHIFT) |		\
65847e07394SAndrew Turner 		    ((_reg ## _CRn) << ISS_MSR_CRn_SHIFT) |		\
65947e07394SAndrew Turner 		    ((_reg ## _CRm) << ISS_MSR_CRm_SHIFT) |		\
66047e07394SAndrew Turner 		    ((_reg ## _op2) << ISS_MSR_OP2_SHIFT),		\
66147e07394SAndrew Turner 		.esr_mask = ISS_MSR_REG_MASK,				\
66247e07394SAndrew Turner 		.reg_read = (_read),					\
66347e07394SAndrew Turner 		.reg_write = (_write),					\
66447e07394SAndrew Turner 		.arg = NULL,						\
66547e07394SAndrew Turner 	}
66647e07394SAndrew Turner #define	ID_SPECIAL_REG(_reg, _name)					\
66747e07394SAndrew Turner 	{								\
66847e07394SAndrew Turner 		.esr_iss = ((_reg ## _op0) << ISS_MSR_OP0_SHIFT) |	\
66947e07394SAndrew Turner 		    ((_reg ## _op1) << ISS_MSR_OP1_SHIFT) |		\
67047e07394SAndrew Turner 		    ((_reg ## _CRn) << ISS_MSR_CRn_SHIFT) |		\
67147e07394SAndrew Turner 		    ((_reg ## _CRm) << ISS_MSR_CRm_SHIFT) |		\
67247e07394SAndrew Turner 		    ((_reg ## _op2) << ISS_MSR_OP2_SHIFT),		\
67347e07394SAndrew Turner 		.esr_mask = ISS_MSR_REG_MASK,				\
67447e07394SAndrew Turner 		.reg_read = vmm_reg_read_arg,				\
67547e07394SAndrew Turner 		.reg_write = vmm_reg_wi,				\
67647e07394SAndrew Turner 		.arg = &(vmm_arch_regs._name),				\
67747e07394SAndrew Turner 	}
67847e07394SAndrew Turner 
67947e07394SAndrew Turner 	/* ID registers */
68047e07394SAndrew Turner 	ID_SPECIAL_REG(ID_AA64PFR0_EL1, id_aa64pfr0),
68147e07394SAndrew Turner 	ID_SPECIAL_REG(ID_AA64DFR0_EL1, id_aa64dfr0),
68247e07394SAndrew Turner 	ID_SPECIAL_REG(ID_AA64ISAR0_EL1, id_aa64isar0),
68347e07394SAndrew Turner 	ID_SPECIAL_REG(ID_AA64MMFR0_EL1, id_aa64mmfr0),
68447e07394SAndrew Turner 	ID_SPECIAL_REG(ID_AA64MMFR1_EL1, id_aa64mmfr1),
68547e07394SAndrew Turner 
68647e07394SAndrew Turner 	/*
68747e07394SAndrew Turner 	 * All other ID registers are read as zero.
68847e07394SAndrew Turner 	 * They are all in the op0=3, op1=0, CRn=0, CRm={0..7} space.
68947e07394SAndrew Turner 	 */
69047e07394SAndrew Turner 	{
69147e07394SAndrew Turner 		.esr_iss = (3 << ISS_MSR_OP0_SHIFT) |
69247e07394SAndrew Turner 		    (0 << ISS_MSR_OP1_SHIFT) |
69347e07394SAndrew Turner 		    (0 << ISS_MSR_CRn_SHIFT) |
69447e07394SAndrew Turner 		    (0 << ISS_MSR_CRm_SHIFT),
69547e07394SAndrew Turner 		.esr_mask = ISS_MSR_OP0_MASK | ISS_MSR_OP1_MASK |
69647e07394SAndrew Turner 		    ISS_MSR_CRn_MASK | (0x8 << ISS_MSR_CRm_SHIFT),
69747e07394SAndrew Turner 		.reg_read = vmm_reg_raz,
69847e07394SAndrew Turner 		.reg_write = vmm_reg_wi,
69947e07394SAndrew Turner 		.arg = NULL,
70047e07394SAndrew Turner 	},
70147e07394SAndrew Turner 
70247e07394SAndrew Turner 	/* Counter physical registers */
70347e07394SAndrew Turner 	SPECIAL_REG(CNTP_CTL_EL0, vtimer_phys_ctl_read, vtimer_phys_ctl_write),
70447e07394SAndrew Turner 	SPECIAL_REG(CNTP_CVAL_EL0, vtimer_phys_cval_read,
70547e07394SAndrew Turner 	    vtimer_phys_cval_write),
70647e07394SAndrew Turner 	SPECIAL_REG(CNTP_TVAL_EL0, vtimer_phys_tval_read,
70747e07394SAndrew Turner 	    vtimer_phys_tval_write),
70847e07394SAndrew Turner 	SPECIAL_REG(CNTPCT_EL0, vtimer_phys_cnt_read, vtimer_phys_cnt_write),
70947e07394SAndrew Turner #undef SPECIAL_REG
71047e07394SAndrew Turner };
71147e07394SAndrew Turner 
71247e07394SAndrew Turner void
vm_register_reg_handler(struct vm * vm,uint64_t iss,uint64_t mask,reg_read_t reg_read,reg_write_t reg_write,void * arg)71347e07394SAndrew Turner vm_register_reg_handler(struct vm *vm, uint64_t iss, uint64_t mask,
71447e07394SAndrew Turner     reg_read_t reg_read, reg_write_t reg_write, void *arg)
71547e07394SAndrew Turner {
71647e07394SAndrew Turner 	int i;
71747e07394SAndrew Turner 
71847e07394SAndrew Turner 	for (i = 0; i < nitems(vm->special_reg); i++) {
71947e07394SAndrew Turner 		if (vm->special_reg[i].esr_iss == 0 &&
72047e07394SAndrew Turner 		    vm->special_reg[i].esr_mask == 0) {
72147e07394SAndrew Turner 			vm->special_reg[i].esr_iss = iss;
72247e07394SAndrew Turner 			vm->special_reg[i].esr_mask = mask;
72347e07394SAndrew Turner 			vm->special_reg[i].reg_read = reg_read;
72447e07394SAndrew Turner 			vm->special_reg[i].reg_write = reg_write;
72547e07394SAndrew Turner 			vm->special_reg[i].arg = arg;
72647e07394SAndrew Turner 			return;
72747e07394SAndrew Turner 		}
72847e07394SAndrew Turner 	}
72947e07394SAndrew Turner 
73047e07394SAndrew Turner 	panic("%s: No free special register slot", __func__);
73147e07394SAndrew Turner }
73247e07394SAndrew Turner 
73347e07394SAndrew Turner void
vm_deregister_reg_handler(struct vm * vm,uint64_t iss,uint64_t mask)73447e07394SAndrew Turner vm_deregister_reg_handler(struct vm *vm, uint64_t iss, uint64_t mask)
73547e07394SAndrew Turner {
73647e07394SAndrew Turner 	int i;
73747e07394SAndrew Turner 
73847e07394SAndrew Turner 	for (i = 0; i < nitems(vm->special_reg); i++) {
73947e07394SAndrew Turner 		if (vm->special_reg[i].esr_iss == iss &&
74047e07394SAndrew Turner 		    vm->special_reg[i].esr_mask == mask) {
74147e07394SAndrew Turner 			memset(&vm->special_reg[i], 0,
74247e07394SAndrew Turner 			    sizeof(vm->special_reg[i]));
74347e07394SAndrew Turner 			return;
74447e07394SAndrew Turner 		}
74547e07394SAndrew Turner 	}
74647e07394SAndrew Turner 
74747e07394SAndrew Turner 	panic("%s: Invalid special register: iss %lx mask %lx", __func__, iss,
74847e07394SAndrew Turner 	    mask);
74947e07394SAndrew Turner }
75047e07394SAndrew Turner 
75147e07394SAndrew Turner static int
vm_handle_reg_emul(struct vcpu * vcpu,bool * retu)75247e07394SAndrew Turner vm_handle_reg_emul(struct vcpu *vcpu, bool *retu)
75347e07394SAndrew Turner {
75447e07394SAndrew Turner 	struct vm *vm;
75547e07394SAndrew Turner 	struct vm_exit *vme;
75647e07394SAndrew Turner 	struct vre *vre;
75747e07394SAndrew Turner 	int i, rv;
75847e07394SAndrew Turner 
75947e07394SAndrew Turner 	vm = vcpu->vm;
76047e07394SAndrew Turner 	vme = &vcpu->exitinfo;
76147e07394SAndrew Turner 	vre = &vme->u.reg_emul.vre;
76247e07394SAndrew Turner 
76347e07394SAndrew Turner 	for (i = 0; i < nitems(vm->special_reg); i++) {
76447e07394SAndrew Turner 		if (vm->special_reg[i].esr_iss == 0 &&
76547e07394SAndrew Turner 		    vm->special_reg[i].esr_mask == 0)
76647e07394SAndrew Turner 			continue;
76747e07394SAndrew Turner 
76847e07394SAndrew Turner 		if ((vre->inst_syndrome & vm->special_reg[i].esr_mask) ==
76947e07394SAndrew Turner 		    vm->special_reg[i].esr_iss) {
77047e07394SAndrew Turner 			rv = vmm_emulate_register(vcpu, vre,
77147e07394SAndrew Turner 			    vm->special_reg[i].reg_read,
77247e07394SAndrew Turner 			    vm->special_reg[i].reg_write,
77347e07394SAndrew Turner 			    vm->special_reg[i].arg);
77447e07394SAndrew Turner 			if (rv == 0) {
77547e07394SAndrew Turner 				*retu = false;
77647e07394SAndrew Turner 			}
77747e07394SAndrew Turner 			return (rv);
77847e07394SAndrew Turner 		}
77947e07394SAndrew Turner 	}
78047e07394SAndrew Turner 	for (i = 0; i < nitems(vmm_special_regs); i++) {
78147e07394SAndrew Turner 		if ((vre->inst_syndrome & vmm_special_regs[i].esr_mask) ==
78247e07394SAndrew Turner 		    vmm_special_regs[i].esr_iss) {
78347e07394SAndrew Turner 			rv = vmm_emulate_register(vcpu, vre,
78447e07394SAndrew Turner 			    vmm_special_regs[i].reg_read,
78547e07394SAndrew Turner 			    vmm_special_regs[i].reg_write,
78647e07394SAndrew Turner 			    vmm_special_regs[i].arg);
78747e07394SAndrew Turner 			if (rv == 0) {
78847e07394SAndrew Turner 				*retu = false;
78947e07394SAndrew Turner 			}
79047e07394SAndrew Turner 			return (rv);
79147e07394SAndrew Turner 		}
79247e07394SAndrew Turner 	}
79347e07394SAndrew Turner 
79447e07394SAndrew Turner 
79547e07394SAndrew Turner 	*retu = true;
79647e07394SAndrew Turner 	return (0);
79747e07394SAndrew Turner }
79847e07394SAndrew Turner 
79947e07394SAndrew Turner void
vm_register_inst_handler(struct vm * vm,uint64_t start,uint64_t size,mem_region_read_t mmio_read,mem_region_write_t mmio_write)80047e07394SAndrew Turner vm_register_inst_handler(struct vm *vm, uint64_t start, uint64_t size,
80147e07394SAndrew Turner     mem_region_read_t mmio_read, mem_region_write_t mmio_write)
80247e07394SAndrew Turner {
80347e07394SAndrew Turner 	int i;
80447e07394SAndrew Turner 
80547e07394SAndrew Turner 	for (i = 0; i < nitems(vm->mmio_region); i++) {
80647e07394SAndrew Turner 		if (vm->mmio_region[i].start == 0 &&
80747e07394SAndrew Turner 		    vm->mmio_region[i].end == 0) {
80847e07394SAndrew Turner 			vm->mmio_region[i].start = start;
80947e07394SAndrew Turner 			vm->mmio_region[i].end = start + size;
81047e07394SAndrew Turner 			vm->mmio_region[i].read = mmio_read;
81147e07394SAndrew Turner 			vm->mmio_region[i].write = mmio_write;
81247e07394SAndrew Turner 			return;
81347e07394SAndrew Turner 		}
81447e07394SAndrew Turner 	}
81547e07394SAndrew Turner 
81647e07394SAndrew Turner 	panic("%s: No free MMIO region", __func__);
81747e07394SAndrew Turner }
81847e07394SAndrew Turner 
81947e07394SAndrew Turner void
vm_deregister_inst_handler(struct vm * vm,uint64_t start,uint64_t size)82047e07394SAndrew Turner vm_deregister_inst_handler(struct vm *vm, uint64_t start, uint64_t size)
82147e07394SAndrew Turner {
82247e07394SAndrew Turner 	int i;
82347e07394SAndrew Turner 
82447e07394SAndrew Turner 	for (i = 0; i < nitems(vm->mmio_region); i++) {
82547e07394SAndrew Turner 		if (vm->mmio_region[i].start == start &&
82647e07394SAndrew Turner 		    vm->mmio_region[i].end == start + size) {
82747e07394SAndrew Turner 			memset(&vm->mmio_region[i], 0,
82847e07394SAndrew Turner 			    sizeof(vm->mmio_region[i]));
82947e07394SAndrew Turner 			return;
83047e07394SAndrew Turner 		}
83147e07394SAndrew Turner 	}
83247e07394SAndrew Turner 
83347e07394SAndrew Turner 	panic("%s: Invalid MMIO region: %lx - %lx", __func__, start,
83447e07394SAndrew Turner 	    start + size);
83547e07394SAndrew Turner }
83647e07394SAndrew Turner 
83747e07394SAndrew Turner static int
vm_handle_inst_emul(struct vcpu * vcpu,bool * retu)83847e07394SAndrew Turner vm_handle_inst_emul(struct vcpu *vcpu, bool *retu)
83947e07394SAndrew Turner {
84047e07394SAndrew Turner 	struct vm *vm;
84147e07394SAndrew Turner 	struct vm_exit *vme;
84247e07394SAndrew Turner 	struct vie *vie;
84347e07394SAndrew Turner 	struct hyp *hyp;
84447e07394SAndrew Turner 	uint64_t fault_ipa;
84547e07394SAndrew Turner 	struct vm_guest_paging *paging;
84647e07394SAndrew Turner 	struct vmm_mmio_region *vmr;
84747e07394SAndrew Turner 	int error, i;
84847e07394SAndrew Turner 
84947e07394SAndrew Turner 	vm = vcpu->vm;
85047e07394SAndrew Turner 	hyp = vm->cookie;
85147e07394SAndrew Turner 	if (!hyp->vgic_attached)
85247e07394SAndrew Turner 		goto out_user;
85347e07394SAndrew Turner 
85447e07394SAndrew Turner 	vme = &vcpu->exitinfo;
85547e07394SAndrew Turner 	vie = &vme->u.inst_emul.vie;
85647e07394SAndrew Turner 	paging = &vme->u.inst_emul.paging;
85747e07394SAndrew Turner 
85847e07394SAndrew Turner 	fault_ipa = vme->u.inst_emul.gpa;
85947e07394SAndrew Turner 
86047e07394SAndrew Turner 	vmr = NULL;
86147e07394SAndrew Turner 	for (i = 0; i < nitems(vm->mmio_region); i++) {
86247e07394SAndrew Turner 		if (vm->mmio_region[i].start <= fault_ipa &&
86347e07394SAndrew Turner 		    vm->mmio_region[i].end > fault_ipa) {
86447e07394SAndrew Turner 			vmr = &vm->mmio_region[i];
86547e07394SAndrew Turner 			break;
86647e07394SAndrew Turner 		}
86747e07394SAndrew Turner 	}
86847e07394SAndrew Turner 	if (vmr == NULL)
86947e07394SAndrew Turner 		goto out_user;
87047e07394SAndrew Turner 
87147e07394SAndrew Turner 	error = vmm_emulate_instruction(vcpu, fault_ipa, vie, paging,
87247e07394SAndrew Turner 	    vmr->read, vmr->write, retu);
87347e07394SAndrew Turner 	return (error);
87447e07394SAndrew Turner 
87547e07394SAndrew Turner out_user:
87647e07394SAndrew Turner 	*retu = true;
87747e07394SAndrew Turner 	return (0);
87847e07394SAndrew Turner }
87947e07394SAndrew Turner 
88047e07394SAndrew Turner int
vm_suspend(struct vm * vm,enum vm_suspend_how how)88147e07394SAndrew Turner vm_suspend(struct vm *vm, enum vm_suspend_how how)
88247e07394SAndrew Turner {
88347e07394SAndrew Turner 	int i;
88447e07394SAndrew Turner 
88547e07394SAndrew Turner 	if (how <= VM_SUSPEND_NONE || how >= VM_SUSPEND_LAST)
88647e07394SAndrew Turner 		return (EINVAL);
88747e07394SAndrew Turner 
88847e07394SAndrew Turner 	if (atomic_cmpset_int(&vm->suspend, 0, how) == 0) {
88947e07394SAndrew Turner 		VM_CTR2(vm, "virtual machine already suspended %d/%d",
89047e07394SAndrew Turner 		    vm->suspend, how);
89147e07394SAndrew Turner 		return (EALREADY);
89247e07394SAndrew Turner 	}
89347e07394SAndrew Turner 
89447e07394SAndrew Turner 	VM_CTR1(vm, "virtual machine successfully suspended %d", how);
89547e07394SAndrew Turner 
89647e07394SAndrew Turner 	/*
89747e07394SAndrew Turner 	 * Notify all active vcpus that they are now suspended.
89847e07394SAndrew Turner 	 */
89947e07394SAndrew Turner 	for (i = 0; i < vm->maxcpus; i++) {
90047e07394SAndrew Turner 		if (CPU_ISSET(i, &vm->active_cpus))
90147e07394SAndrew Turner 			vcpu_notify_event(vm_vcpu(vm, i));
90247e07394SAndrew Turner 	}
90347e07394SAndrew Turner 
90447e07394SAndrew Turner 	return (0);
90547e07394SAndrew Turner }
90647e07394SAndrew Turner 
90747e07394SAndrew Turner void
vm_exit_suspended(struct vcpu * vcpu,uint64_t pc)90847e07394SAndrew Turner vm_exit_suspended(struct vcpu *vcpu, uint64_t pc)
90947e07394SAndrew Turner {
91047e07394SAndrew Turner 	struct vm *vm = vcpu->vm;
91147e07394SAndrew Turner 	struct vm_exit *vmexit;
91247e07394SAndrew Turner 
91347e07394SAndrew Turner 	KASSERT(vm->suspend > VM_SUSPEND_NONE && vm->suspend < VM_SUSPEND_LAST,
91447e07394SAndrew Turner 	    ("vm_exit_suspended: invalid suspend type %d", vm->suspend));
91547e07394SAndrew Turner 
91647e07394SAndrew Turner 	vmexit = vm_exitinfo(vcpu);
91747e07394SAndrew Turner 	vmexit->pc = pc;
91847e07394SAndrew Turner 	vmexit->inst_length = 4;
91947e07394SAndrew Turner 	vmexit->exitcode = VM_EXITCODE_SUSPENDED;
92047e07394SAndrew Turner 	vmexit->u.suspended.how = vm->suspend;
92147e07394SAndrew Turner }
92247e07394SAndrew Turner 
92347e07394SAndrew Turner void
vm_exit_debug(struct vcpu * vcpu,uint64_t pc)92447e07394SAndrew Turner vm_exit_debug(struct vcpu *vcpu, uint64_t pc)
92547e07394SAndrew Turner {
92647e07394SAndrew Turner 	struct vm_exit *vmexit;
92747e07394SAndrew Turner 
92847e07394SAndrew Turner 	vmexit = vm_exitinfo(vcpu);
92947e07394SAndrew Turner 	vmexit->pc = pc;
93047e07394SAndrew Turner 	vmexit->inst_length = 4;
93147e07394SAndrew Turner 	vmexit->exitcode = VM_EXITCODE_DEBUG;
93247e07394SAndrew Turner }
93347e07394SAndrew Turner 
93447e07394SAndrew Turner int
vm_activate_cpu(struct vcpu * vcpu)93547e07394SAndrew Turner vm_activate_cpu(struct vcpu *vcpu)
93647e07394SAndrew Turner {
93747e07394SAndrew Turner 	struct vm *vm = vcpu->vm;
93847e07394SAndrew Turner 
93947e07394SAndrew Turner 	if (CPU_ISSET(vcpu->vcpuid, &vm->active_cpus))
94047e07394SAndrew Turner 		return (EBUSY);
94147e07394SAndrew Turner 
94247e07394SAndrew Turner 	CPU_SET_ATOMIC(vcpu->vcpuid, &vm->active_cpus);
94347e07394SAndrew Turner 	return (0);
94447e07394SAndrew Turner 
94547e07394SAndrew Turner }
94647e07394SAndrew Turner 
94747e07394SAndrew Turner int
vm_suspend_cpu(struct vm * vm,struct vcpu * vcpu)94847e07394SAndrew Turner vm_suspend_cpu(struct vm *vm, struct vcpu *vcpu)
94947e07394SAndrew Turner {
95047e07394SAndrew Turner 	if (vcpu == NULL) {
95147e07394SAndrew Turner 		vm->debug_cpus = vm->active_cpus;
95247e07394SAndrew Turner 		for (int i = 0; i < vm->maxcpus; i++) {
95347e07394SAndrew Turner 			if (CPU_ISSET(i, &vm->active_cpus))
95447e07394SAndrew Turner 				vcpu_notify_event(vm_vcpu(vm, i));
95547e07394SAndrew Turner 		}
95647e07394SAndrew Turner 	} else {
95747e07394SAndrew Turner 		if (!CPU_ISSET(vcpu->vcpuid, &vm->active_cpus))
95847e07394SAndrew Turner 			return (EINVAL);
95947e07394SAndrew Turner 
96047e07394SAndrew Turner 		CPU_SET_ATOMIC(vcpu->vcpuid, &vm->debug_cpus);
96147e07394SAndrew Turner 		vcpu_notify_event(vcpu);
96247e07394SAndrew Turner 	}
96347e07394SAndrew Turner 	return (0);
96447e07394SAndrew Turner }
96547e07394SAndrew Turner 
96647e07394SAndrew Turner int
vm_resume_cpu(struct vm * vm,struct vcpu * vcpu)96747e07394SAndrew Turner vm_resume_cpu(struct vm *vm, struct vcpu *vcpu)
96847e07394SAndrew Turner {
96947e07394SAndrew Turner 
97047e07394SAndrew Turner 	if (vcpu == NULL) {
97147e07394SAndrew Turner 		CPU_ZERO(&vm->debug_cpus);
97247e07394SAndrew Turner 	} else {
97347e07394SAndrew Turner 		if (!CPU_ISSET(vcpu->vcpuid, &vm->debug_cpus))
97447e07394SAndrew Turner 			return (EINVAL);
97547e07394SAndrew Turner 
97647e07394SAndrew Turner 		CPU_CLR_ATOMIC(vcpu->vcpuid, &vm->debug_cpus);
97747e07394SAndrew Turner 	}
97847e07394SAndrew Turner 	return (0);
97947e07394SAndrew Turner }
98047e07394SAndrew Turner 
98147e07394SAndrew Turner int
vcpu_debugged(struct vcpu * vcpu)98247e07394SAndrew Turner vcpu_debugged(struct vcpu *vcpu)
98347e07394SAndrew Turner {
98447e07394SAndrew Turner 
98547e07394SAndrew Turner 	return (CPU_ISSET(vcpu->vcpuid, &vcpu->vm->debug_cpus));
98647e07394SAndrew Turner }
98747e07394SAndrew Turner 
98847e07394SAndrew Turner cpuset_t
vm_active_cpus(struct vm * vm)98947e07394SAndrew Turner vm_active_cpus(struct vm *vm)
99047e07394SAndrew Turner {
99147e07394SAndrew Turner 
99247e07394SAndrew Turner 	return (vm->active_cpus);
99347e07394SAndrew Turner }
99447e07394SAndrew Turner 
99547e07394SAndrew Turner cpuset_t
vm_debug_cpus(struct vm * vm)99647e07394SAndrew Turner vm_debug_cpus(struct vm *vm)
99747e07394SAndrew Turner {
99847e07394SAndrew Turner 
99947e07394SAndrew Turner 	return (vm->debug_cpus);
100047e07394SAndrew Turner }
100147e07394SAndrew Turner 
100247e07394SAndrew Turner cpuset_t
vm_suspended_cpus(struct vm * vm)100347e07394SAndrew Turner vm_suspended_cpus(struct vm *vm)
100447e07394SAndrew Turner {
100547e07394SAndrew Turner 
100647e07394SAndrew Turner 	return (vm->suspended_cpus);
100747e07394SAndrew Turner }
100847e07394SAndrew Turner 
100947e07394SAndrew Turner 
101047e07394SAndrew Turner void *
vcpu_stats(struct vcpu * vcpu)101147e07394SAndrew Turner vcpu_stats(struct vcpu *vcpu)
101247e07394SAndrew Turner {
101347e07394SAndrew Turner 
101447e07394SAndrew Turner 	return (vcpu->stats);
101547e07394SAndrew Turner }
101647e07394SAndrew Turner 
101747e07394SAndrew Turner /*
101847e07394SAndrew Turner  * This function is called to ensure that a vcpu "sees" a pending event
101947e07394SAndrew Turner  * as soon as possible:
102047e07394SAndrew Turner  * - If the vcpu thread is sleeping then it is woken up.
102147e07394SAndrew Turner  * - If the vcpu is running on a different host_cpu then an IPI will be directed
102247e07394SAndrew Turner  *   to the host_cpu to cause the vcpu to trap into the hypervisor.
102347e07394SAndrew Turner  */
102447e07394SAndrew Turner static void
vcpu_notify_event_locked(struct vcpu * vcpu)102547e07394SAndrew Turner vcpu_notify_event_locked(struct vcpu *vcpu)
102647e07394SAndrew Turner {
102747e07394SAndrew Turner 	int hostcpu;
102847e07394SAndrew Turner 
102947e07394SAndrew Turner 	hostcpu = vcpu->hostcpu;
103047e07394SAndrew Turner 	if (vcpu->state == VCPU_RUNNING) {
103147e07394SAndrew Turner 		KASSERT(hostcpu != NOCPU, ("vcpu running on invalid hostcpu"));
103247e07394SAndrew Turner 		if (hostcpu != curcpu) {
103347e07394SAndrew Turner 			ipi_cpu(hostcpu, vmm_ipinum);
103447e07394SAndrew Turner 		} else {
103547e07394SAndrew Turner 			/*
103647e07394SAndrew Turner 			 * If the 'vcpu' is running on 'curcpu' then it must
103747e07394SAndrew Turner 			 * be sending a notification to itself (e.g. SELF_IPI).
103847e07394SAndrew Turner 			 * The pending event will be picked up when the vcpu
103947e07394SAndrew Turner 			 * transitions back to guest context.
104047e07394SAndrew Turner 			 */
104147e07394SAndrew Turner 		}
104247e07394SAndrew Turner 	} else {
104347e07394SAndrew Turner 		KASSERT(hostcpu == NOCPU, ("vcpu state %d not consistent "
104447e07394SAndrew Turner 		    "with hostcpu %d", vcpu->state, hostcpu));
104547e07394SAndrew Turner 		if (vcpu->state == VCPU_SLEEPING)
104647e07394SAndrew Turner 			wakeup_one(vcpu);
104747e07394SAndrew Turner 	}
104847e07394SAndrew Turner }
104947e07394SAndrew Turner 
105047e07394SAndrew Turner void
vcpu_notify_event(struct vcpu * vcpu)105147e07394SAndrew Turner vcpu_notify_event(struct vcpu *vcpu)
105247e07394SAndrew Turner {
105347e07394SAndrew Turner 	vcpu_lock(vcpu);
105447e07394SAndrew Turner 	vcpu_notify_event_locked(vcpu);
105547e07394SAndrew Turner 	vcpu_unlock(vcpu);
105647e07394SAndrew Turner }
105747e07394SAndrew Turner 
1058c76c2a19SMark Johnston struct vmspace *
vm_vmspace(struct vm * vm)1059c76c2a19SMark Johnston vm_vmspace(struct vm *vm)
1060c76c2a19SMark Johnston {
1061c76c2a19SMark Johnston 	return (vm->vmspace);
1062c76c2a19SMark Johnston }
1063c76c2a19SMark Johnston 
1064c76c2a19SMark Johnston struct vm_mem *
vm_mem(struct vm * vm)1065c76c2a19SMark Johnston vm_mem(struct vm *vm)
1066c76c2a19SMark Johnston {
1067c76c2a19SMark Johnston 	return (&vm->mem);
1068c76c2a19SMark Johnston }
1069c76c2a19SMark Johnston 
107047e07394SAndrew Turner static void
restore_guest_fpustate(struct vcpu * vcpu)107147e07394SAndrew Turner restore_guest_fpustate(struct vcpu *vcpu)
107247e07394SAndrew Turner {
107347e07394SAndrew Turner 
107447e07394SAndrew Turner 	/* flush host state to the pcb */
107547e07394SAndrew Turner 	vfp_save_state(curthread, curthread->td_pcb);
107647e07394SAndrew Turner 	/* Ensure the VFP state will be re-loaded when exiting the guest */
107747e07394SAndrew Turner 	PCPU_SET(fpcurthread, NULL);
107847e07394SAndrew Turner 
107947e07394SAndrew Turner 	/* restore guest FPU state */
108047e07394SAndrew Turner 	vfp_enable();
108147e07394SAndrew Turner 	vfp_restore(vcpu->guestfpu);
108247e07394SAndrew Turner 
108347e07394SAndrew Turner 	/*
108447e07394SAndrew Turner 	 * The FPU is now "dirty" with the guest's state so turn on emulation
108547e07394SAndrew Turner 	 * to trap any access to the FPU by the host.
108647e07394SAndrew Turner 	 */
108747e07394SAndrew Turner 	vfp_disable();
108847e07394SAndrew Turner }
108947e07394SAndrew Turner 
109047e07394SAndrew Turner static void
save_guest_fpustate(struct vcpu * vcpu)109147e07394SAndrew Turner save_guest_fpustate(struct vcpu *vcpu)
109247e07394SAndrew Turner {
109347e07394SAndrew Turner 	if ((READ_SPECIALREG(cpacr_el1) & CPACR_FPEN_MASK) !=
109447e07394SAndrew Turner 	    CPACR_FPEN_TRAP_ALL1)
109547e07394SAndrew Turner 		panic("VFP not enabled in host!");
109647e07394SAndrew Turner 
109747e07394SAndrew Turner 	/* save guest FPU state */
109847e07394SAndrew Turner 	vfp_enable();
109947e07394SAndrew Turner 	vfp_store(vcpu->guestfpu);
110047e07394SAndrew Turner 	vfp_disable();
110147e07394SAndrew Turner 
110247e07394SAndrew Turner 	KASSERT(PCPU_GET(fpcurthread) == NULL,
110347e07394SAndrew Turner 	    ("%s: fpcurthread set with guest registers", __func__));
110447e07394SAndrew Turner }
110547e07394SAndrew Turner static int
vcpu_set_state_locked(struct vcpu * vcpu,enum vcpu_state newstate,bool from_idle)110647e07394SAndrew Turner vcpu_set_state_locked(struct vcpu *vcpu, enum vcpu_state newstate,
110747e07394SAndrew Turner     bool from_idle)
110847e07394SAndrew Turner {
110947e07394SAndrew Turner 	int error;
111047e07394SAndrew Turner 
111147e07394SAndrew Turner 	vcpu_assert_locked(vcpu);
111247e07394SAndrew Turner 
111347e07394SAndrew Turner 	/*
111447e07394SAndrew Turner 	 * State transitions from the vmmdev_ioctl() must always begin from
111547e07394SAndrew Turner 	 * the VCPU_IDLE state. This guarantees that there is only a single
111647e07394SAndrew Turner 	 * ioctl() operating on a vcpu at any point.
111747e07394SAndrew Turner 	 */
111847e07394SAndrew Turner 	if (from_idle) {
111947e07394SAndrew Turner 		while (vcpu->state != VCPU_IDLE) {
112047e07394SAndrew Turner 			vcpu_notify_event_locked(vcpu);
112147e07394SAndrew Turner 			msleep_spin(&vcpu->state, &vcpu->mtx, "vmstat", hz);
112247e07394SAndrew Turner 		}
112347e07394SAndrew Turner 	} else {
112447e07394SAndrew Turner 		KASSERT(vcpu->state != VCPU_IDLE, ("invalid transition from "
112547e07394SAndrew Turner 		    "vcpu idle state"));
112647e07394SAndrew Turner 	}
112747e07394SAndrew Turner 
112847e07394SAndrew Turner 	if (vcpu->state == VCPU_RUNNING) {
112947e07394SAndrew Turner 		KASSERT(vcpu->hostcpu == curcpu, ("curcpu %d and hostcpu %d "
113047e07394SAndrew Turner 		    "mismatch for running vcpu", curcpu, vcpu->hostcpu));
113147e07394SAndrew Turner 	} else {
113247e07394SAndrew Turner 		KASSERT(vcpu->hostcpu == NOCPU, ("Invalid hostcpu %d for a "
113347e07394SAndrew Turner 		    "vcpu that is not running", vcpu->hostcpu));
113447e07394SAndrew Turner 	}
113547e07394SAndrew Turner 
113647e07394SAndrew Turner 	/*
113747e07394SAndrew Turner 	 * The following state transitions are allowed:
113847e07394SAndrew Turner 	 * IDLE -> FROZEN -> IDLE
113947e07394SAndrew Turner 	 * FROZEN -> RUNNING -> FROZEN
114047e07394SAndrew Turner 	 * FROZEN -> SLEEPING -> FROZEN
114147e07394SAndrew Turner 	 */
114247e07394SAndrew Turner 	switch (vcpu->state) {
114347e07394SAndrew Turner 	case VCPU_IDLE:
114447e07394SAndrew Turner 	case VCPU_RUNNING:
114547e07394SAndrew Turner 	case VCPU_SLEEPING:
114647e07394SAndrew Turner 		error = (newstate != VCPU_FROZEN);
114747e07394SAndrew Turner 		break;
114847e07394SAndrew Turner 	case VCPU_FROZEN:
114947e07394SAndrew Turner 		error = (newstate == VCPU_FROZEN);
115047e07394SAndrew Turner 		break;
115147e07394SAndrew Turner 	default:
115247e07394SAndrew Turner 		error = 1;
115347e07394SAndrew Turner 		break;
115447e07394SAndrew Turner 	}
115547e07394SAndrew Turner 
115647e07394SAndrew Turner 	if (error)
115747e07394SAndrew Turner 		return (EBUSY);
115847e07394SAndrew Turner 
115947e07394SAndrew Turner 	vcpu->state = newstate;
116047e07394SAndrew Turner 	if (newstate == VCPU_RUNNING)
116147e07394SAndrew Turner 		vcpu->hostcpu = curcpu;
116247e07394SAndrew Turner 	else
116347e07394SAndrew Turner 		vcpu->hostcpu = NOCPU;
116447e07394SAndrew Turner 
116547e07394SAndrew Turner 	if (newstate == VCPU_IDLE)
116647e07394SAndrew Turner 		wakeup(&vcpu->state);
116747e07394SAndrew Turner 
116847e07394SAndrew Turner 	return (0);
116947e07394SAndrew Turner }
117047e07394SAndrew Turner 
117147e07394SAndrew Turner static void
vcpu_require_state(struct vcpu * vcpu,enum vcpu_state newstate)117247e07394SAndrew Turner vcpu_require_state(struct vcpu *vcpu, enum vcpu_state newstate)
117347e07394SAndrew Turner {
117447e07394SAndrew Turner 	int error;
117547e07394SAndrew Turner 
117647e07394SAndrew Turner 	if ((error = vcpu_set_state(vcpu, newstate, false)) != 0)
117747e07394SAndrew Turner 		panic("Error %d setting state to %d\n", error, newstate);
117847e07394SAndrew Turner }
117947e07394SAndrew Turner 
118047e07394SAndrew Turner static void
vcpu_require_state_locked(struct vcpu * vcpu,enum vcpu_state newstate)118147e07394SAndrew Turner vcpu_require_state_locked(struct vcpu *vcpu, enum vcpu_state newstate)
118247e07394SAndrew Turner {
118347e07394SAndrew Turner 	int error;
118447e07394SAndrew Turner 
118547e07394SAndrew Turner 	if ((error = vcpu_set_state_locked(vcpu, newstate, false)) != 0)
118647e07394SAndrew Turner 		panic("Error %d setting state to %d", error, newstate);
118747e07394SAndrew Turner }
118847e07394SAndrew Turner 
118947e07394SAndrew Turner int
vm_get_capability(struct vcpu * vcpu,int type,int * retval)119047e07394SAndrew Turner vm_get_capability(struct vcpu *vcpu, int type, int *retval)
119147e07394SAndrew Turner {
119247e07394SAndrew Turner 	if (type < 0 || type >= VM_CAP_MAX)
119347e07394SAndrew Turner 		return (EINVAL);
119447e07394SAndrew Turner 
119547e07394SAndrew Turner 	return (vmmops_getcap(vcpu->cookie, type, retval));
119647e07394SAndrew Turner }
119747e07394SAndrew Turner 
119847e07394SAndrew Turner int
vm_set_capability(struct vcpu * vcpu,int type,int val)119947e07394SAndrew Turner vm_set_capability(struct vcpu *vcpu, int type, int val)
120047e07394SAndrew Turner {
120147e07394SAndrew Turner 	if (type < 0 || type >= VM_CAP_MAX)
120247e07394SAndrew Turner 		return (EINVAL);
120347e07394SAndrew Turner 
120447e07394SAndrew Turner 	return (vmmops_setcap(vcpu->cookie, type, val));
120547e07394SAndrew Turner }
120647e07394SAndrew Turner 
120747e07394SAndrew Turner struct vm *
vcpu_vm(struct vcpu * vcpu)120847e07394SAndrew Turner vcpu_vm(struct vcpu *vcpu)
120947e07394SAndrew Turner {
121047e07394SAndrew Turner 	return (vcpu->vm);
121147e07394SAndrew Turner }
121247e07394SAndrew Turner 
121347e07394SAndrew Turner int
vcpu_vcpuid(struct vcpu * vcpu)121447e07394SAndrew Turner vcpu_vcpuid(struct vcpu *vcpu)
121547e07394SAndrew Turner {
121647e07394SAndrew Turner 	return (vcpu->vcpuid);
121747e07394SAndrew Turner }
121847e07394SAndrew Turner 
121947e07394SAndrew Turner void *
vcpu_get_cookie(struct vcpu * vcpu)122047e07394SAndrew Turner vcpu_get_cookie(struct vcpu *vcpu)
122147e07394SAndrew Turner {
122247e07394SAndrew Turner 	return (vcpu->cookie);
122347e07394SAndrew Turner }
122447e07394SAndrew Turner 
122547e07394SAndrew Turner struct vcpu *
vm_vcpu(struct vm * vm,int vcpuid)122647e07394SAndrew Turner vm_vcpu(struct vm *vm, int vcpuid)
122747e07394SAndrew Turner {
122847e07394SAndrew Turner 	return (vm->vcpu[vcpuid]);
122947e07394SAndrew Turner }
123047e07394SAndrew Turner 
123147e07394SAndrew Turner int
vcpu_set_state(struct vcpu * vcpu,enum vcpu_state newstate,bool from_idle)123247e07394SAndrew Turner vcpu_set_state(struct vcpu *vcpu, enum vcpu_state newstate, bool from_idle)
123347e07394SAndrew Turner {
123447e07394SAndrew Turner 	int error;
123547e07394SAndrew Turner 
123647e07394SAndrew Turner 	vcpu_lock(vcpu);
123747e07394SAndrew Turner 	error = vcpu_set_state_locked(vcpu, newstate, from_idle);
123847e07394SAndrew Turner 	vcpu_unlock(vcpu);
123947e07394SAndrew Turner 
124047e07394SAndrew Turner 	return (error);
124147e07394SAndrew Turner }
124247e07394SAndrew Turner 
124347e07394SAndrew Turner enum vcpu_state
vcpu_get_state(struct vcpu * vcpu,int * hostcpu)124447e07394SAndrew Turner vcpu_get_state(struct vcpu *vcpu, int *hostcpu)
124547e07394SAndrew Turner {
124647e07394SAndrew Turner 	enum vcpu_state state;
124747e07394SAndrew Turner 
124847e07394SAndrew Turner 	vcpu_lock(vcpu);
124947e07394SAndrew Turner 	state = vcpu->state;
125047e07394SAndrew Turner 	if (hostcpu != NULL)
125147e07394SAndrew Turner 		*hostcpu = vcpu->hostcpu;
125247e07394SAndrew Turner 	vcpu_unlock(vcpu);
125347e07394SAndrew Turner 
125447e07394SAndrew Turner 	return (state);
125547e07394SAndrew Turner }
125647e07394SAndrew Turner 
125747e07394SAndrew Turner int
vm_get_register(struct vcpu * vcpu,int reg,uint64_t * retval)125847e07394SAndrew Turner vm_get_register(struct vcpu *vcpu, int reg, uint64_t *retval)
125947e07394SAndrew Turner {
126047e07394SAndrew Turner 
126147e07394SAndrew Turner 	if (reg >= VM_REG_LAST)
126247e07394SAndrew Turner 		return (EINVAL);
126347e07394SAndrew Turner 
126447e07394SAndrew Turner 	return (vmmops_getreg(vcpu->cookie, reg, retval));
126547e07394SAndrew Turner }
126647e07394SAndrew Turner 
126747e07394SAndrew Turner int
vm_set_register(struct vcpu * vcpu,int reg,uint64_t val)126847e07394SAndrew Turner vm_set_register(struct vcpu *vcpu, int reg, uint64_t val)
126947e07394SAndrew Turner {
127047e07394SAndrew Turner 	int error;
127147e07394SAndrew Turner 
127247e07394SAndrew Turner 	if (reg >= VM_REG_LAST)
127347e07394SAndrew Turner 		return (EINVAL);
127447e07394SAndrew Turner 	error = vmmops_setreg(vcpu->cookie, reg, val);
127547e07394SAndrew Turner 	if (error || reg != VM_REG_GUEST_PC)
127647e07394SAndrew Turner 		return (error);
127747e07394SAndrew Turner 
127847e07394SAndrew Turner 	vcpu->nextpc = val;
127947e07394SAndrew Turner 
128047e07394SAndrew Turner 	return (0);
128147e07394SAndrew Turner }
128247e07394SAndrew Turner 
128347e07394SAndrew Turner void *
vm_get_cookie(struct vm * vm)128447e07394SAndrew Turner vm_get_cookie(struct vm *vm)
128547e07394SAndrew Turner {
128647e07394SAndrew Turner 	return (vm->cookie);
128747e07394SAndrew Turner }
128847e07394SAndrew Turner 
128947e07394SAndrew Turner int
vm_inject_exception(struct vcpu * vcpu,uint64_t esr,uint64_t far)129047e07394SAndrew Turner vm_inject_exception(struct vcpu *vcpu, uint64_t esr, uint64_t far)
129147e07394SAndrew Turner {
129247e07394SAndrew Turner 	return (vmmops_exception(vcpu->cookie, esr, far));
129347e07394SAndrew Turner }
129447e07394SAndrew Turner 
129547e07394SAndrew Turner int
vm_attach_vgic(struct vm * vm,struct vm_vgic_descr * descr)129647e07394SAndrew Turner vm_attach_vgic(struct vm *vm, struct vm_vgic_descr *descr)
129747e07394SAndrew Turner {
129847e07394SAndrew Turner 	return (vgic_attach_to_vm(vm->cookie, descr));
129947e07394SAndrew Turner }
130047e07394SAndrew Turner 
130147e07394SAndrew Turner int
vm_assert_irq(struct vm * vm,uint32_t irq)130247e07394SAndrew Turner vm_assert_irq(struct vm *vm, uint32_t irq)
130347e07394SAndrew Turner {
130447e07394SAndrew Turner 	return (vgic_inject_irq(vm->cookie, -1, irq, true));
130547e07394SAndrew Turner }
130647e07394SAndrew Turner 
130747e07394SAndrew Turner int
vm_deassert_irq(struct vm * vm,uint32_t irq)130847e07394SAndrew Turner vm_deassert_irq(struct vm *vm, uint32_t irq)
130947e07394SAndrew Turner {
131047e07394SAndrew Turner 	return (vgic_inject_irq(vm->cookie, -1, irq, false));
131147e07394SAndrew Turner }
131247e07394SAndrew Turner 
131347e07394SAndrew Turner int
vm_raise_msi(struct vm * vm,uint64_t msg,uint64_t addr,int bus,int slot,int func)131447e07394SAndrew Turner vm_raise_msi(struct vm *vm, uint64_t msg, uint64_t addr, int bus, int slot,
131547e07394SAndrew Turner     int func)
131647e07394SAndrew Turner {
131747e07394SAndrew Turner 	/* TODO: Should we raise an SError? */
131847e07394SAndrew Turner 	return (vgic_inject_msi(vm->cookie, msg, addr));
131947e07394SAndrew Turner }
132047e07394SAndrew Turner 
132147e07394SAndrew Turner static int
vm_handle_smccc_call(struct vcpu * vcpu,struct vm_exit * vme,bool * retu)132247e07394SAndrew Turner vm_handle_smccc_call(struct vcpu *vcpu, struct vm_exit *vme, bool *retu)
132347e07394SAndrew Turner {
132447e07394SAndrew Turner 	struct hypctx *hypctx;
132547e07394SAndrew Turner 	int i;
132647e07394SAndrew Turner 
132747e07394SAndrew Turner 	hypctx = vcpu_get_cookie(vcpu);
132847e07394SAndrew Turner 
132947e07394SAndrew Turner 	if ((hypctx->tf.tf_esr & ESR_ELx_ISS_MASK) != 0)
133047e07394SAndrew Turner 		return (1);
133147e07394SAndrew Turner 
133247e07394SAndrew Turner 	vme->exitcode = VM_EXITCODE_SMCCC;
133347e07394SAndrew Turner 	vme->u.smccc_call.func_id = hypctx->tf.tf_x[0];
133447e07394SAndrew Turner 	for (i = 0; i < nitems(vme->u.smccc_call.args); i++)
133547e07394SAndrew Turner 		vme->u.smccc_call.args[i] = hypctx->tf.tf_x[i + 1];
133647e07394SAndrew Turner 
133747e07394SAndrew Turner 	*retu = true;
133847e07394SAndrew Turner 	return (0);
133947e07394SAndrew Turner }
134047e07394SAndrew Turner 
134147e07394SAndrew Turner static int
vm_handle_wfi(struct vcpu * vcpu,struct vm_exit * vme,bool * retu)134247e07394SAndrew Turner vm_handle_wfi(struct vcpu *vcpu, struct vm_exit *vme, bool *retu)
134347e07394SAndrew Turner {
134447e07394SAndrew Turner 	vcpu_lock(vcpu);
134547e07394SAndrew Turner 	while (1) {
134647e07394SAndrew Turner 		if (vgic_has_pending_irq(vcpu->cookie))
134747e07394SAndrew Turner 			break;
134847e07394SAndrew Turner 
134947e07394SAndrew Turner 		if (vcpu_should_yield(vcpu))
135047e07394SAndrew Turner 			break;
135147e07394SAndrew Turner 
135247e07394SAndrew Turner 		vcpu_require_state_locked(vcpu, VCPU_SLEEPING);
135347e07394SAndrew Turner 		/*
135447e07394SAndrew Turner 		 * XXX msleep_spin() cannot be interrupted by signals so
135547e07394SAndrew Turner 		 * wake up periodically to check pending signals.
135647e07394SAndrew Turner 		 */
135747e07394SAndrew Turner 		msleep_spin(vcpu, &vcpu->mtx, "vmidle", hz);
135847e07394SAndrew Turner 		vcpu_require_state_locked(vcpu, VCPU_FROZEN);
135947e07394SAndrew Turner 	}
136047e07394SAndrew Turner 	vcpu_unlock(vcpu);
136147e07394SAndrew Turner 
136247e07394SAndrew Turner 	*retu = false;
136347e07394SAndrew Turner 	return (0);
136447e07394SAndrew Turner }
136547e07394SAndrew Turner 
136647e07394SAndrew Turner static int
vm_handle_paging(struct vcpu * vcpu,bool * retu)136747e07394SAndrew Turner vm_handle_paging(struct vcpu *vcpu, bool *retu)
136847e07394SAndrew Turner {
136947e07394SAndrew Turner 	struct vm *vm = vcpu->vm;
137047e07394SAndrew Turner 	struct vm_exit *vme;
137147e07394SAndrew Turner 	struct vm_map *map;
137247e07394SAndrew Turner 	uint64_t addr, esr;
137347e07394SAndrew Turner 	pmap_t pmap;
137447e07394SAndrew Turner 	int ftype, rv;
137547e07394SAndrew Turner 
137647e07394SAndrew Turner 	vme = &vcpu->exitinfo;
137747e07394SAndrew Turner 
137847e07394SAndrew Turner 	pmap = vmspace_pmap(vcpu->vm->vmspace);
137947e07394SAndrew Turner 	addr = vme->u.paging.gpa;
138047e07394SAndrew Turner 	esr = vme->u.paging.esr;
138147e07394SAndrew Turner 
138247e07394SAndrew Turner 	/* The page exists, but the page table needs to be updated. */
138347e07394SAndrew Turner 	if (pmap_fault(pmap, esr, addr) == KERN_SUCCESS)
138447e07394SAndrew Turner 		return (0);
138547e07394SAndrew Turner 
138647e07394SAndrew Turner 	switch (ESR_ELx_EXCEPTION(esr)) {
138747e07394SAndrew Turner 	case EXCP_INSN_ABORT_L:
138847e07394SAndrew Turner 	case EXCP_DATA_ABORT_L:
138947e07394SAndrew Turner 		ftype = VM_PROT_EXECUTE | VM_PROT_READ | VM_PROT_WRITE;
139047e07394SAndrew Turner 		break;
139147e07394SAndrew Turner 	default:
139247e07394SAndrew Turner 		panic("%s: Invalid exception (esr = %lx)", __func__, esr);
139347e07394SAndrew Turner 	}
139447e07394SAndrew Turner 
139547e07394SAndrew Turner 	map = &vm->vmspace->vm_map;
139647e07394SAndrew Turner 	rv = vm_fault(map, vme->u.paging.gpa, ftype, VM_FAULT_NORMAL, NULL);
139747e07394SAndrew Turner 	if (rv != KERN_SUCCESS)
139847e07394SAndrew Turner 		return (EFAULT);
139947e07394SAndrew Turner 
140047e07394SAndrew Turner 	return (0);
140147e07394SAndrew Turner }
140247e07394SAndrew Turner 
14031ee7a8faSMark Johnston static int
vm_handle_suspend(struct vcpu * vcpu,bool * retu)14041ee7a8faSMark Johnston vm_handle_suspend(struct vcpu *vcpu, bool *retu)
14051ee7a8faSMark Johnston {
14061ee7a8faSMark Johnston 	struct vm *vm = vcpu->vm;
14071ee7a8faSMark Johnston 	int error, i;
14081ee7a8faSMark Johnston 	struct thread *td;
14091ee7a8faSMark Johnston 
14101ee7a8faSMark Johnston 	error = 0;
14111ee7a8faSMark Johnston 	td = curthread;
14121ee7a8faSMark Johnston 
14131ee7a8faSMark Johnston 	CPU_SET_ATOMIC(vcpu->vcpuid, &vm->suspended_cpus);
14141ee7a8faSMark Johnston 
14151ee7a8faSMark Johnston 	/*
14161ee7a8faSMark Johnston 	 * Wait until all 'active_cpus' have suspended themselves.
14171ee7a8faSMark Johnston 	 *
14181ee7a8faSMark Johnston 	 * Since a VM may be suspended at any time including when one or
14191ee7a8faSMark Johnston 	 * more vcpus are doing a rendezvous we need to call the rendezvous
14201ee7a8faSMark Johnston 	 * handler while we are waiting to prevent a deadlock.
14211ee7a8faSMark Johnston 	 */
14221ee7a8faSMark Johnston 	vcpu_lock(vcpu);
14231ee7a8faSMark Johnston 	while (error == 0) {
14241ee7a8faSMark Johnston 		if (CPU_CMP(&vm->suspended_cpus, &vm->active_cpus) == 0)
14251ee7a8faSMark Johnston 			break;
14261ee7a8faSMark Johnston 
14271ee7a8faSMark Johnston 		vcpu_require_state_locked(vcpu, VCPU_SLEEPING);
14281ee7a8faSMark Johnston 		msleep_spin(vcpu, &vcpu->mtx, "vmsusp", hz);
14291ee7a8faSMark Johnston 		vcpu_require_state_locked(vcpu, VCPU_FROZEN);
14301ee7a8faSMark Johnston 		if (td_ast_pending(td, TDA_SUSPEND)) {
14311ee7a8faSMark Johnston 			vcpu_unlock(vcpu);
14321ee7a8faSMark Johnston 			error = thread_check_susp(td, false);
14331ee7a8faSMark Johnston 			vcpu_lock(vcpu);
14341ee7a8faSMark Johnston 		}
14351ee7a8faSMark Johnston 	}
14361ee7a8faSMark Johnston 	vcpu_unlock(vcpu);
14371ee7a8faSMark Johnston 
14381ee7a8faSMark Johnston 	/*
14391ee7a8faSMark Johnston 	 * Wakeup the other sleeping vcpus and return to userspace.
14401ee7a8faSMark Johnston 	 */
14411ee7a8faSMark Johnston 	for (i = 0; i < vm->maxcpus; i++) {
14421ee7a8faSMark Johnston 		if (CPU_ISSET(i, &vm->suspended_cpus)) {
14431ee7a8faSMark Johnston 			vcpu_notify_event(vm_vcpu(vm, i));
14441ee7a8faSMark Johnston 		}
14451ee7a8faSMark Johnston 	}
14461ee7a8faSMark Johnston 
14471ee7a8faSMark Johnston 	*retu = true;
14481ee7a8faSMark Johnston 	return (error);
14491ee7a8faSMark Johnston }
14501ee7a8faSMark Johnston 
145147e07394SAndrew Turner int
vm_run(struct vcpu * vcpu)145247e07394SAndrew Turner vm_run(struct vcpu *vcpu)
145347e07394SAndrew Turner {
145447e07394SAndrew Turner 	struct vm *vm = vcpu->vm;
145547e07394SAndrew Turner 	struct vm_eventinfo evinfo;
145647e07394SAndrew Turner 	int error, vcpuid;
145747e07394SAndrew Turner 	struct vm_exit *vme;
145847e07394SAndrew Turner 	bool retu;
145947e07394SAndrew Turner 	pmap_t pmap;
146047e07394SAndrew Turner 
146147e07394SAndrew Turner 	vcpuid = vcpu->vcpuid;
146247e07394SAndrew Turner 
146347e07394SAndrew Turner 	if (!CPU_ISSET(vcpuid, &vm->active_cpus))
146447e07394SAndrew Turner 		return (EINVAL);
146547e07394SAndrew Turner 
146647e07394SAndrew Turner 	if (CPU_ISSET(vcpuid, &vm->suspended_cpus))
146747e07394SAndrew Turner 		return (EINVAL);
146847e07394SAndrew Turner 
146947e07394SAndrew Turner 	pmap = vmspace_pmap(vm->vmspace);
147047e07394SAndrew Turner 	vme = &vcpu->exitinfo;
147147e07394SAndrew Turner 	evinfo.rptr = NULL;
147247e07394SAndrew Turner 	evinfo.sptr = &vm->suspend;
147347e07394SAndrew Turner 	evinfo.iptr = NULL;
147447e07394SAndrew Turner restart:
147547e07394SAndrew Turner 	critical_enter();
147647e07394SAndrew Turner 
147747e07394SAndrew Turner 	restore_guest_fpustate(vcpu);
147847e07394SAndrew Turner 
147947e07394SAndrew Turner 	vcpu_require_state(vcpu, VCPU_RUNNING);
148047e07394SAndrew Turner 	error = vmmops_run(vcpu->cookie, vcpu->nextpc, pmap, &evinfo);
148147e07394SAndrew Turner 	vcpu_require_state(vcpu, VCPU_FROZEN);
148247e07394SAndrew Turner 
148347e07394SAndrew Turner 	save_guest_fpustate(vcpu);
148447e07394SAndrew Turner 
148547e07394SAndrew Turner 	critical_exit();
148647e07394SAndrew Turner 
148747e07394SAndrew Turner 	if (error == 0) {
148847e07394SAndrew Turner 		retu = false;
148947e07394SAndrew Turner 		switch (vme->exitcode) {
149047e07394SAndrew Turner 		case VM_EXITCODE_INST_EMUL:
149147e07394SAndrew Turner 			vcpu->nextpc = vme->pc + vme->inst_length;
149247e07394SAndrew Turner 			error = vm_handle_inst_emul(vcpu, &retu);
149347e07394SAndrew Turner 			break;
149447e07394SAndrew Turner 
149547e07394SAndrew Turner 		case VM_EXITCODE_REG_EMUL:
149647e07394SAndrew Turner 			vcpu->nextpc = vme->pc + vme->inst_length;
149747e07394SAndrew Turner 			error = vm_handle_reg_emul(vcpu, &retu);
149847e07394SAndrew Turner 			break;
149947e07394SAndrew Turner 
150047e07394SAndrew Turner 		case VM_EXITCODE_HVC:
150147e07394SAndrew Turner 			/*
150247e07394SAndrew Turner 			 * The HVC instruction saves the address for the
150347e07394SAndrew Turner 			 * next instruction as the return address.
150447e07394SAndrew Turner 			 */
150547e07394SAndrew Turner 			vcpu->nextpc = vme->pc;
150647e07394SAndrew Turner 			/*
150747e07394SAndrew Turner 			 * The PSCI call can change the exit information in the
150847e07394SAndrew Turner 			 * case of suspend/reset/poweroff/cpu off/cpu on.
150947e07394SAndrew Turner 			 */
151047e07394SAndrew Turner 			error = vm_handle_smccc_call(vcpu, vme, &retu);
151147e07394SAndrew Turner 			break;
151247e07394SAndrew Turner 
151347e07394SAndrew Turner 		case VM_EXITCODE_WFI:
151447e07394SAndrew Turner 			vcpu->nextpc = vme->pc + vme->inst_length;
151547e07394SAndrew Turner 			error = vm_handle_wfi(vcpu, vme, &retu);
151647e07394SAndrew Turner 			break;
151747e07394SAndrew Turner 
151847e07394SAndrew Turner 		case VM_EXITCODE_PAGING:
151947e07394SAndrew Turner 			vcpu->nextpc = vme->pc;
152047e07394SAndrew Turner 			error = vm_handle_paging(vcpu, &retu);
152147e07394SAndrew Turner 			break;
152247e07394SAndrew Turner 
15231ee7a8faSMark Johnston 		case VM_EXITCODE_SUSPENDED:
15241ee7a8faSMark Johnston 			vcpu->nextpc = vme->pc;
15251ee7a8faSMark Johnston 			error = vm_handle_suspend(vcpu, &retu);
15261ee7a8faSMark Johnston 			break;
15271ee7a8faSMark Johnston 
152847e07394SAndrew Turner 		default:
152947e07394SAndrew Turner 			/* Handle in userland */
153047e07394SAndrew Turner 			vcpu->nextpc = vme->pc;
153147e07394SAndrew Turner 			retu = true;
153247e07394SAndrew Turner 			break;
153347e07394SAndrew Turner 		}
153447e07394SAndrew Turner 	}
153547e07394SAndrew Turner 
153647e07394SAndrew Turner 	if (error == 0 && retu == false)
153747e07394SAndrew Turner 		goto restart;
153847e07394SAndrew Turner 
153947e07394SAndrew Turner 	return (error);
154047e07394SAndrew Turner }
1541