xref: /freebsd/sys/arm64/vmm/vmm.c (revision c76c2a19ae3763d17aa6a60a5831ed24cbc16e83)
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>
68*c76c2a19SMark 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 */
130*c76c2a19SMark 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
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
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 *
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
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 *
30847e07394SAndrew Turner vm_exitinfo(struct vcpu *vcpu)
30947e07394SAndrew Turner {
31047e07394SAndrew Turner 	return (&vcpu->exitinfo);
31147e07394SAndrew Turner }
31247e07394SAndrew Turner 
31347e07394SAndrew Turner static int
31447e07394SAndrew Turner vmm_init(void)
31547e07394SAndrew Turner {
31647e07394SAndrew Turner 	int error;
31747e07394SAndrew Turner 
31847e07394SAndrew Turner 	vm_maxcpu = mp_ncpus;
31947e07394SAndrew Turner 	TUNABLE_INT_FETCH("hw.vmm.maxcpu", &vm_maxcpu);
32047e07394SAndrew Turner 
32147e07394SAndrew Turner 	if (vm_maxcpu > VM_MAXCPU) {
32247e07394SAndrew Turner 		printf("vmm: vm_maxcpu clamped to %u\n", VM_MAXCPU);
32347e07394SAndrew Turner 		vm_maxcpu = VM_MAXCPU;
32447e07394SAndrew Turner 	}
32547e07394SAndrew Turner 	if (vm_maxcpu == 0)
32647e07394SAndrew Turner 		vm_maxcpu = 1;
32747e07394SAndrew Turner 
32847e07394SAndrew Turner 	error = vmm_regs_init(&vmm_arch_regs, &vmm_arch_regs_masks);
32947e07394SAndrew Turner 	if (error != 0)
33047e07394SAndrew Turner 		return (error);
33147e07394SAndrew Turner 
33247e07394SAndrew Turner 	return (vmmops_modinit(0));
33347e07394SAndrew Turner }
33447e07394SAndrew Turner 
33547e07394SAndrew Turner static int
33647e07394SAndrew Turner vmm_handler(module_t mod, int what, void *arg)
33747e07394SAndrew Turner {
33847e07394SAndrew Turner 	int error;
33947e07394SAndrew Turner 
34047e07394SAndrew Turner 	switch (what) {
34147e07394SAndrew Turner 	case MOD_LOAD:
342a97f683fSMark Johnston 		error = vmmdev_init();
343a97f683fSMark Johnston 		if (error != 0)
344a97f683fSMark Johnston 			break;
34547e07394SAndrew Turner 		error = vmm_init();
34647e07394SAndrew Turner 		if (error == 0)
34747e07394SAndrew Turner 			vmm_initialized = true;
3484a46ece6SMark Johnston 		else
3494a46ece6SMark Johnston 			(void)vmmdev_cleanup();
35047e07394SAndrew Turner 		break;
35147e07394SAndrew Turner 	case MOD_UNLOAD:
35247e07394SAndrew Turner 		error = vmmdev_cleanup();
35347e07394SAndrew Turner 		if (error == 0 && vmm_initialized) {
35447e07394SAndrew Turner 			error = vmmops_modcleanup();
3554a46ece6SMark Johnston 			if (error) {
3564a46ece6SMark Johnston 				/*
3574a46ece6SMark Johnston 				 * Something bad happened - prevent new
3584a46ece6SMark Johnston 				 * VMs from being created
3594a46ece6SMark Johnston 				 */
36047e07394SAndrew Turner 				vmm_initialized = false;
36147e07394SAndrew Turner 			}
3624a46ece6SMark Johnston 		}
36347e07394SAndrew Turner 		break;
36447e07394SAndrew Turner 	default:
36547e07394SAndrew Turner 		error = 0;
36647e07394SAndrew Turner 		break;
36747e07394SAndrew Turner 	}
36847e07394SAndrew Turner 	return (error);
36947e07394SAndrew Turner }
37047e07394SAndrew Turner 
37147e07394SAndrew Turner static moduledata_t vmm_kmod = {
37247e07394SAndrew Turner 	"vmm",
37347e07394SAndrew Turner 	vmm_handler,
37447e07394SAndrew Turner 	NULL
37547e07394SAndrew Turner };
37647e07394SAndrew Turner 
37747e07394SAndrew Turner /*
37847e07394SAndrew Turner  * vmm initialization has the following dependencies:
37947e07394SAndrew Turner  *
38047e07394SAndrew Turner  * - HYP initialization requires smp_rendezvous() and therefore must happen
38147e07394SAndrew Turner  *   after SMP is fully functional (after SI_SUB_SMP).
382d7023078SMark Johnston  * - vmm device initialization requires an initialized devfs.
38347e07394SAndrew Turner  */
384d7023078SMark Johnston DECLARE_MODULE(vmm, vmm_kmod, MAX(SI_SUB_SMP, SI_SUB_DEVFS) + 1, SI_ORDER_ANY);
38547e07394SAndrew Turner MODULE_VERSION(vmm, 1);
38647e07394SAndrew Turner 
38747e07394SAndrew Turner static void
38847e07394SAndrew Turner vm_init(struct vm *vm, bool create)
38947e07394SAndrew Turner {
39047e07394SAndrew Turner 	int i;
39147e07394SAndrew Turner 
39247e07394SAndrew Turner 	vm->cookie = vmmops_init(vm, vmspace_pmap(vm->vmspace));
39347e07394SAndrew Turner 	MPASS(vm->cookie != NULL);
39447e07394SAndrew Turner 
39547e07394SAndrew Turner 	CPU_ZERO(&vm->active_cpus);
39647e07394SAndrew Turner 	CPU_ZERO(&vm->debug_cpus);
39747e07394SAndrew Turner 
39847e07394SAndrew Turner 	vm->suspend = 0;
39947e07394SAndrew Turner 	CPU_ZERO(&vm->suspended_cpus);
40047e07394SAndrew Turner 
40147e07394SAndrew Turner 	memset(vm->mmio_region, 0, sizeof(vm->mmio_region));
40247e07394SAndrew Turner 	memset(vm->special_reg, 0, sizeof(vm->special_reg));
40347e07394SAndrew Turner 
40447e07394SAndrew Turner 	if (!create) {
40547e07394SAndrew Turner 		for (i = 0; i < vm->maxcpus; i++) {
40647e07394SAndrew Turner 			if (vm->vcpu[i] != NULL)
40747e07394SAndrew Turner 				vcpu_init(vm->vcpu[i]);
40847e07394SAndrew Turner 		}
40947e07394SAndrew Turner 	}
41047e07394SAndrew Turner }
41147e07394SAndrew Turner 
412a03354b0SMark Johnston void
413a03354b0SMark Johnston vm_disable_vcpu_creation(struct vm *vm)
414a03354b0SMark Johnston {
415a03354b0SMark Johnston 	sx_xlock(&vm->vcpus_init_lock);
416a03354b0SMark Johnston 	vm->dying = true;
417a03354b0SMark Johnston 	sx_xunlock(&vm->vcpus_init_lock);
418a03354b0SMark Johnston }
419a03354b0SMark Johnston 
42047e07394SAndrew Turner struct vcpu *
42147e07394SAndrew Turner vm_alloc_vcpu(struct vm *vm, int vcpuid)
42247e07394SAndrew Turner {
42347e07394SAndrew Turner 	struct vcpu *vcpu;
42447e07394SAndrew Turner 
42547e07394SAndrew Turner 	if (vcpuid < 0 || vcpuid >= vm_get_maxcpus(vm))
42647e07394SAndrew Turner 		return (NULL);
42747e07394SAndrew Turner 
42847e07394SAndrew Turner 	/* Some interrupt controllers may have a CPU limit */
42947e07394SAndrew Turner 	if (vcpuid >= vgic_max_cpu_count(vm->cookie))
43047e07394SAndrew Turner 		return (NULL);
43147e07394SAndrew Turner 
43272ae04c7SRuslan Bukin 	vcpu = (struct vcpu *)
43372ae04c7SRuslan Bukin 	    atomic_load_acq_ptr((uintptr_t *)&vm->vcpu[vcpuid]);
43447e07394SAndrew Turner 	if (__predict_true(vcpu != NULL))
43547e07394SAndrew Turner 		return (vcpu);
43647e07394SAndrew Turner 
43747e07394SAndrew Turner 	sx_xlock(&vm->vcpus_init_lock);
43847e07394SAndrew Turner 	vcpu = vm->vcpu[vcpuid];
439a03354b0SMark Johnston 	if (vcpu == NULL && !vm->dying) {
44047e07394SAndrew Turner 		vcpu = vcpu_alloc(vm, vcpuid);
44147e07394SAndrew Turner 		vcpu_init(vcpu);
44247e07394SAndrew Turner 
44347e07394SAndrew Turner 		/*
44447e07394SAndrew Turner 		 * Ensure vCPU is fully created before updating pointer
44547e07394SAndrew Turner 		 * to permit unlocked reads above.
44647e07394SAndrew Turner 		 */
44747e07394SAndrew Turner 		atomic_store_rel_ptr((uintptr_t *)&vm->vcpu[vcpuid],
44847e07394SAndrew Turner 		    (uintptr_t)vcpu);
44947e07394SAndrew Turner 	}
45047e07394SAndrew Turner 	sx_xunlock(&vm->vcpus_init_lock);
45147e07394SAndrew Turner 	return (vcpu);
45247e07394SAndrew Turner }
45347e07394SAndrew Turner 
45447e07394SAndrew Turner void
45547e07394SAndrew Turner vm_slock_vcpus(struct vm *vm)
45647e07394SAndrew Turner {
45747e07394SAndrew Turner 	sx_slock(&vm->vcpus_init_lock);
45847e07394SAndrew Turner }
45947e07394SAndrew Turner 
46047e07394SAndrew Turner void
46147e07394SAndrew Turner vm_unlock_vcpus(struct vm *vm)
46247e07394SAndrew Turner {
46347e07394SAndrew Turner 	sx_unlock(&vm->vcpus_init_lock);
46447e07394SAndrew Turner }
46547e07394SAndrew Turner 
46647e07394SAndrew Turner int
46747e07394SAndrew Turner vm_create(const char *name, struct vm **retvm)
46847e07394SAndrew Turner {
46947e07394SAndrew Turner 	struct vm *vm;
47047e07394SAndrew Turner 	struct vmspace *vmspace;
47147e07394SAndrew Turner 
47247e07394SAndrew Turner 	/*
47347e07394SAndrew Turner 	 * If vmm.ko could not be successfully initialized then don't attempt
47447e07394SAndrew Turner 	 * to create the virtual machine.
47547e07394SAndrew Turner 	 */
47647e07394SAndrew Turner 	if (!vmm_initialized)
47747e07394SAndrew Turner 		return (ENXIO);
47847e07394SAndrew Turner 
47947e07394SAndrew Turner 	if (name == NULL || strlen(name) >= VM_MAX_NAMELEN)
48047e07394SAndrew Turner 		return (EINVAL);
48147e07394SAndrew Turner 
48247e07394SAndrew Turner 	vmspace = vmmops_vmspace_alloc(0, 1ul << 39);
48347e07394SAndrew Turner 	if (vmspace == NULL)
48447e07394SAndrew Turner 		return (ENOMEM);
48547e07394SAndrew Turner 
48647e07394SAndrew Turner 	vm = malloc(sizeof(struct vm), M_VMM, M_WAITOK | M_ZERO);
48747e07394SAndrew Turner 	strcpy(vm->name, name);
48847e07394SAndrew Turner 	vm->vmspace = vmspace;
489*c76c2a19SMark Johnston 	vm_mem_init(&vm->mem);
49047e07394SAndrew Turner 	sx_init(&vm->vcpus_init_lock, "vm vcpus");
49147e07394SAndrew Turner 
49247e07394SAndrew Turner 	vm->sockets = 1;
49347e07394SAndrew Turner 	vm->cores = 1;			/* XXX backwards compatibility */
49447e07394SAndrew Turner 	vm->threads = 1;		/* XXX backwards compatibility */
49547e07394SAndrew Turner 	vm->maxcpus = vm_maxcpu;
49647e07394SAndrew Turner 
49747e07394SAndrew Turner 	vm->vcpu = malloc(sizeof(*vm->vcpu) * vm->maxcpus, M_VMM,
49847e07394SAndrew Turner 	    M_WAITOK | M_ZERO);
49947e07394SAndrew Turner 
50047e07394SAndrew Turner 	vm_init(vm, true);
50147e07394SAndrew Turner 
50247e07394SAndrew Turner 	*retvm = vm;
50347e07394SAndrew Turner 	return (0);
50447e07394SAndrew Turner }
50547e07394SAndrew Turner 
50647e07394SAndrew Turner void
50747e07394SAndrew Turner vm_get_topology(struct vm *vm, uint16_t *sockets, uint16_t *cores,
50847e07394SAndrew Turner     uint16_t *threads, uint16_t *maxcpus)
50947e07394SAndrew Turner {
51047e07394SAndrew Turner 	*sockets = vm->sockets;
51147e07394SAndrew Turner 	*cores = vm->cores;
51247e07394SAndrew Turner 	*threads = vm->threads;
51347e07394SAndrew Turner 	*maxcpus = vm->maxcpus;
51447e07394SAndrew Turner }
51547e07394SAndrew Turner 
51647e07394SAndrew Turner uint16_t
51747e07394SAndrew Turner vm_get_maxcpus(struct vm *vm)
51847e07394SAndrew Turner {
51947e07394SAndrew Turner 	return (vm->maxcpus);
52047e07394SAndrew Turner }
52147e07394SAndrew Turner 
52247e07394SAndrew Turner int
52347e07394SAndrew Turner vm_set_topology(struct vm *vm, uint16_t sockets, uint16_t cores,
52447e07394SAndrew Turner     uint16_t threads, uint16_t maxcpus)
52547e07394SAndrew Turner {
52647e07394SAndrew Turner 	/* Ignore maxcpus. */
52747e07394SAndrew Turner 	if ((sockets * cores * threads) > vm->maxcpus)
52847e07394SAndrew Turner 		return (EINVAL);
52947e07394SAndrew Turner 	vm->sockets = sockets;
53047e07394SAndrew Turner 	vm->cores = cores;
53147e07394SAndrew Turner 	vm->threads = threads;
53247e07394SAndrew Turner 	return(0);
53347e07394SAndrew Turner }
53447e07394SAndrew Turner 
53547e07394SAndrew Turner static void
53647e07394SAndrew Turner vm_cleanup(struct vm *vm, bool destroy)
53747e07394SAndrew Turner {
53847e07394SAndrew Turner 	pmap_t pmap __diagused;
53947e07394SAndrew Turner 	int i;
54047e07394SAndrew Turner 
54147e07394SAndrew Turner 	if (destroy) {
542*c76c2a19SMark Johnston 		vm_xlock_memsegs(vm);
54347e07394SAndrew Turner 		pmap = vmspace_pmap(vm->vmspace);
54447e07394SAndrew Turner 		sched_pin();
54547e07394SAndrew Turner 		PCPU_SET(curvmpmap, NULL);
54647e07394SAndrew Turner 		sched_unpin();
54747e07394SAndrew Turner 		CPU_FOREACH(i) {
54847e07394SAndrew Turner 			MPASS(cpuid_to_pcpu[i]->pc_curvmpmap != pmap);
54947e07394SAndrew Turner 		}
550*c76c2a19SMark Johnston 	} else
551*c76c2a19SMark Johnston 		vm_assert_memseg_xlocked(vm);
552*c76c2a19SMark Johnston 
55347e07394SAndrew Turner 
55447e07394SAndrew Turner 	vgic_detach_from_vm(vm->cookie);
55547e07394SAndrew Turner 
55647e07394SAndrew Turner 	for (i = 0; i < vm->maxcpus; i++) {
55747e07394SAndrew Turner 		if (vm->vcpu[i] != NULL)
55847e07394SAndrew Turner 			vcpu_cleanup(vm->vcpu[i], destroy);
55947e07394SAndrew Turner 	}
56047e07394SAndrew Turner 
56147e07394SAndrew Turner 	vmmops_cleanup(vm->cookie);
56247e07394SAndrew Turner 
563*c76c2a19SMark Johnston 	vm_mem_cleanup(vm);
56447e07394SAndrew Turner 	if (destroy) {
565*c76c2a19SMark Johnston 		vm_mem_destroy(vm);
56647e07394SAndrew Turner 
56747e07394SAndrew Turner 		vmmops_vmspace_free(vm->vmspace);
56847e07394SAndrew Turner 		vm->vmspace = NULL;
56947e07394SAndrew Turner 
57047e07394SAndrew Turner 		for (i = 0; i < vm->maxcpus; i++)
57147e07394SAndrew Turner 			free(vm->vcpu[i], M_VMM);
57247e07394SAndrew Turner 		free(vm->vcpu, M_VMM);
57347e07394SAndrew Turner 		sx_destroy(&vm->vcpus_init_lock);
57447e07394SAndrew Turner 	}
57547e07394SAndrew Turner }
57647e07394SAndrew Turner 
57747e07394SAndrew Turner void
57847e07394SAndrew Turner vm_destroy(struct vm *vm)
57947e07394SAndrew Turner {
58047e07394SAndrew Turner 	vm_cleanup(vm, true);
58147e07394SAndrew Turner 	free(vm, M_VMM);
58247e07394SAndrew Turner }
58347e07394SAndrew Turner 
58447e07394SAndrew Turner int
58547e07394SAndrew Turner vm_reinit(struct vm *vm)
58647e07394SAndrew Turner {
58747e07394SAndrew Turner 	int error;
58847e07394SAndrew Turner 
58947e07394SAndrew Turner 	/*
59047e07394SAndrew Turner 	 * A virtual machine can be reset only if all vcpus are suspended.
59147e07394SAndrew Turner 	 */
59247e07394SAndrew Turner 	if (CPU_CMP(&vm->suspended_cpus, &vm->active_cpus) == 0) {
59347e07394SAndrew Turner 		vm_cleanup(vm, false);
59447e07394SAndrew Turner 		vm_init(vm, false);
59547e07394SAndrew Turner 		error = 0;
59647e07394SAndrew Turner 	} else {
59747e07394SAndrew Turner 		error = EBUSY;
59847e07394SAndrew Turner 	}
59947e07394SAndrew Turner 
60047e07394SAndrew Turner 	return (error);
60147e07394SAndrew Turner }
60247e07394SAndrew Turner 
60347e07394SAndrew Turner const char *
60447e07394SAndrew Turner vm_name(struct vm *vm)
60547e07394SAndrew Turner {
60647e07394SAndrew Turner 	return (vm->name);
60747e07394SAndrew Turner }
60847e07394SAndrew Turner 
60947e07394SAndrew Turner int
61047e07394SAndrew Turner vm_gla2gpa_nofault(struct vcpu *vcpu, struct vm_guest_paging *paging,
61147e07394SAndrew Turner     uint64_t gla, int prot, uint64_t *gpa, int *is_fault)
61247e07394SAndrew Turner {
613*c76c2a19SMark Johnston 	return (vmmops_gla2gpa(vcpu->cookie, paging, gla, prot, gpa, is_fault));
61447e07394SAndrew Turner }
61547e07394SAndrew Turner 
61647e07394SAndrew Turner static int
61747e07394SAndrew Turner vmm_reg_raz(struct vcpu *vcpu, uint64_t *rval, void *arg)
61847e07394SAndrew Turner {
61947e07394SAndrew Turner 	*rval = 0;
62047e07394SAndrew Turner 	return (0);
62147e07394SAndrew Turner }
62247e07394SAndrew Turner 
62347e07394SAndrew Turner static int
62447e07394SAndrew Turner vmm_reg_read_arg(struct vcpu *vcpu, uint64_t *rval, void *arg)
62547e07394SAndrew Turner {
62647e07394SAndrew Turner 	*rval = *(uint64_t *)arg;
62747e07394SAndrew Turner 	return (0);
62847e07394SAndrew Turner }
62947e07394SAndrew Turner 
63047e07394SAndrew Turner static int
63147e07394SAndrew Turner vmm_reg_wi(struct vcpu *vcpu, uint64_t wval, void *arg)
63247e07394SAndrew Turner {
63347e07394SAndrew Turner 	return (0);
63447e07394SAndrew Turner }
63547e07394SAndrew Turner 
63647e07394SAndrew Turner static const struct vmm_special_reg vmm_special_regs[] = {
63747e07394SAndrew Turner #define	SPECIAL_REG(_reg, _read, _write)				\
63847e07394SAndrew Turner 	{								\
63947e07394SAndrew Turner 		.esr_iss = ((_reg ## _op0) << ISS_MSR_OP0_SHIFT) |	\
64047e07394SAndrew Turner 		    ((_reg ## _op1) << ISS_MSR_OP1_SHIFT) |		\
64147e07394SAndrew Turner 		    ((_reg ## _CRn) << ISS_MSR_CRn_SHIFT) |		\
64247e07394SAndrew Turner 		    ((_reg ## _CRm) << ISS_MSR_CRm_SHIFT) |		\
64347e07394SAndrew Turner 		    ((_reg ## _op2) << ISS_MSR_OP2_SHIFT),		\
64447e07394SAndrew Turner 		.esr_mask = ISS_MSR_REG_MASK,				\
64547e07394SAndrew Turner 		.reg_read = (_read),					\
64647e07394SAndrew Turner 		.reg_write = (_write),					\
64747e07394SAndrew Turner 		.arg = NULL,						\
64847e07394SAndrew Turner 	}
64947e07394SAndrew Turner #define	ID_SPECIAL_REG(_reg, _name)					\
65047e07394SAndrew Turner 	{								\
65147e07394SAndrew Turner 		.esr_iss = ((_reg ## _op0) << ISS_MSR_OP0_SHIFT) |	\
65247e07394SAndrew Turner 		    ((_reg ## _op1) << ISS_MSR_OP1_SHIFT) |		\
65347e07394SAndrew Turner 		    ((_reg ## _CRn) << ISS_MSR_CRn_SHIFT) |		\
65447e07394SAndrew Turner 		    ((_reg ## _CRm) << ISS_MSR_CRm_SHIFT) |		\
65547e07394SAndrew Turner 		    ((_reg ## _op2) << ISS_MSR_OP2_SHIFT),		\
65647e07394SAndrew Turner 		.esr_mask = ISS_MSR_REG_MASK,				\
65747e07394SAndrew Turner 		.reg_read = vmm_reg_read_arg,				\
65847e07394SAndrew Turner 		.reg_write = vmm_reg_wi,				\
65947e07394SAndrew Turner 		.arg = &(vmm_arch_regs._name),				\
66047e07394SAndrew Turner 	}
66147e07394SAndrew Turner 
66247e07394SAndrew Turner 	/* ID registers */
66347e07394SAndrew Turner 	ID_SPECIAL_REG(ID_AA64PFR0_EL1, id_aa64pfr0),
66447e07394SAndrew Turner 	ID_SPECIAL_REG(ID_AA64DFR0_EL1, id_aa64dfr0),
66547e07394SAndrew Turner 	ID_SPECIAL_REG(ID_AA64ISAR0_EL1, id_aa64isar0),
66647e07394SAndrew Turner 	ID_SPECIAL_REG(ID_AA64MMFR0_EL1, id_aa64mmfr0),
66747e07394SAndrew Turner 	ID_SPECIAL_REG(ID_AA64MMFR1_EL1, id_aa64mmfr1),
66847e07394SAndrew Turner 
66947e07394SAndrew Turner 	/*
67047e07394SAndrew Turner 	 * All other ID registers are read as zero.
67147e07394SAndrew Turner 	 * They are all in the op0=3, op1=0, CRn=0, CRm={0..7} space.
67247e07394SAndrew Turner 	 */
67347e07394SAndrew Turner 	{
67447e07394SAndrew Turner 		.esr_iss = (3 << ISS_MSR_OP0_SHIFT) |
67547e07394SAndrew Turner 		    (0 << ISS_MSR_OP1_SHIFT) |
67647e07394SAndrew Turner 		    (0 << ISS_MSR_CRn_SHIFT) |
67747e07394SAndrew Turner 		    (0 << ISS_MSR_CRm_SHIFT),
67847e07394SAndrew Turner 		.esr_mask = ISS_MSR_OP0_MASK | ISS_MSR_OP1_MASK |
67947e07394SAndrew Turner 		    ISS_MSR_CRn_MASK | (0x8 << ISS_MSR_CRm_SHIFT),
68047e07394SAndrew Turner 		.reg_read = vmm_reg_raz,
68147e07394SAndrew Turner 		.reg_write = vmm_reg_wi,
68247e07394SAndrew Turner 		.arg = NULL,
68347e07394SAndrew Turner 	},
68447e07394SAndrew Turner 
68547e07394SAndrew Turner 	/* Counter physical registers */
68647e07394SAndrew Turner 	SPECIAL_REG(CNTP_CTL_EL0, vtimer_phys_ctl_read, vtimer_phys_ctl_write),
68747e07394SAndrew Turner 	SPECIAL_REG(CNTP_CVAL_EL0, vtimer_phys_cval_read,
68847e07394SAndrew Turner 	    vtimer_phys_cval_write),
68947e07394SAndrew Turner 	SPECIAL_REG(CNTP_TVAL_EL0, vtimer_phys_tval_read,
69047e07394SAndrew Turner 	    vtimer_phys_tval_write),
69147e07394SAndrew Turner 	SPECIAL_REG(CNTPCT_EL0, vtimer_phys_cnt_read, vtimer_phys_cnt_write),
69247e07394SAndrew Turner #undef SPECIAL_REG
69347e07394SAndrew Turner };
69447e07394SAndrew Turner 
69547e07394SAndrew Turner void
69647e07394SAndrew Turner vm_register_reg_handler(struct vm *vm, uint64_t iss, uint64_t mask,
69747e07394SAndrew Turner     reg_read_t reg_read, reg_write_t reg_write, void *arg)
69847e07394SAndrew Turner {
69947e07394SAndrew Turner 	int i;
70047e07394SAndrew Turner 
70147e07394SAndrew Turner 	for (i = 0; i < nitems(vm->special_reg); i++) {
70247e07394SAndrew Turner 		if (vm->special_reg[i].esr_iss == 0 &&
70347e07394SAndrew Turner 		    vm->special_reg[i].esr_mask == 0) {
70447e07394SAndrew Turner 			vm->special_reg[i].esr_iss = iss;
70547e07394SAndrew Turner 			vm->special_reg[i].esr_mask = mask;
70647e07394SAndrew Turner 			vm->special_reg[i].reg_read = reg_read;
70747e07394SAndrew Turner 			vm->special_reg[i].reg_write = reg_write;
70847e07394SAndrew Turner 			vm->special_reg[i].arg = arg;
70947e07394SAndrew Turner 			return;
71047e07394SAndrew Turner 		}
71147e07394SAndrew Turner 	}
71247e07394SAndrew Turner 
71347e07394SAndrew Turner 	panic("%s: No free special register slot", __func__);
71447e07394SAndrew Turner }
71547e07394SAndrew Turner 
71647e07394SAndrew Turner void
71747e07394SAndrew Turner vm_deregister_reg_handler(struct vm *vm, uint64_t iss, uint64_t mask)
71847e07394SAndrew Turner {
71947e07394SAndrew Turner 	int i;
72047e07394SAndrew Turner 
72147e07394SAndrew Turner 	for (i = 0; i < nitems(vm->special_reg); i++) {
72247e07394SAndrew Turner 		if (vm->special_reg[i].esr_iss == iss &&
72347e07394SAndrew Turner 		    vm->special_reg[i].esr_mask == mask) {
72447e07394SAndrew Turner 			memset(&vm->special_reg[i], 0,
72547e07394SAndrew Turner 			    sizeof(vm->special_reg[i]));
72647e07394SAndrew Turner 			return;
72747e07394SAndrew Turner 		}
72847e07394SAndrew Turner 	}
72947e07394SAndrew Turner 
73047e07394SAndrew Turner 	panic("%s: Invalid special register: iss %lx mask %lx", __func__, iss,
73147e07394SAndrew Turner 	    mask);
73247e07394SAndrew Turner }
73347e07394SAndrew Turner 
73447e07394SAndrew Turner static int
73547e07394SAndrew Turner vm_handle_reg_emul(struct vcpu *vcpu, bool *retu)
73647e07394SAndrew Turner {
73747e07394SAndrew Turner 	struct vm *vm;
73847e07394SAndrew Turner 	struct vm_exit *vme;
73947e07394SAndrew Turner 	struct vre *vre;
74047e07394SAndrew Turner 	int i, rv;
74147e07394SAndrew Turner 
74247e07394SAndrew Turner 	vm = vcpu->vm;
74347e07394SAndrew Turner 	vme = &vcpu->exitinfo;
74447e07394SAndrew Turner 	vre = &vme->u.reg_emul.vre;
74547e07394SAndrew Turner 
74647e07394SAndrew Turner 	for (i = 0; i < nitems(vm->special_reg); i++) {
74747e07394SAndrew Turner 		if (vm->special_reg[i].esr_iss == 0 &&
74847e07394SAndrew Turner 		    vm->special_reg[i].esr_mask == 0)
74947e07394SAndrew Turner 			continue;
75047e07394SAndrew Turner 
75147e07394SAndrew Turner 		if ((vre->inst_syndrome & vm->special_reg[i].esr_mask) ==
75247e07394SAndrew Turner 		    vm->special_reg[i].esr_iss) {
75347e07394SAndrew Turner 			rv = vmm_emulate_register(vcpu, vre,
75447e07394SAndrew Turner 			    vm->special_reg[i].reg_read,
75547e07394SAndrew Turner 			    vm->special_reg[i].reg_write,
75647e07394SAndrew Turner 			    vm->special_reg[i].arg);
75747e07394SAndrew Turner 			if (rv == 0) {
75847e07394SAndrew Turner 				*retu = false;
75947e07394SAndrew Turner 			}
76047e07394SAndrew Turner 			return (rv);
76147e07394SAndrew Turner 		}
76247e07394SAndrew Turner 	}
76347e07394SAndrew Turner 	for (i = 0; i < nitems(vmm_special_regs); i++) {
76447e07394SAndrew Turner 		if ((vre->inst_syndrome & vmm_special_regs[i].esr_mask) ==
76547e07394SAndrew Turner 		    vmm_special_regs[i].esr_iss) {
76647e07394SAndrew Turner 			rv = vmm_emulate_register(vcpu, vre,
76747e07394SAndrew Turner 			    vmm_special_regs[i].reg_read,
76847e07394SAndrew Turner 			    vmm_special_regs[i].reg_write,
76947e07394SAndrew Turner 			    vmm_special_regs[i].arg);
77047e07394SAndrew Turner 			if (rv == 0) {
77147e07394SAndrew Turner 				*retu = false;
77247e07394SAndrew Turner 			}
77347e07394SAndrew Turner 			return (rv);
77447e07394SAndrew Turner 		}
77547e07394SAndrew Turner 	}
77647e07394SAndrew Turner 
77747e07394SAndrew Turner 
77847e07394SAndrew Turner 	*retu = true;
77947e07394SAndrew Turner 	return (0);
78047e07394SAndrew Turner }
78147e07394SAndrew Turner 
78247e07394SAndrew Turner void
78347e07394SAndrew Turner vm_register_inst_handler(struct vm *vm, uint64_t start, uint64_t size,
78447e07394SAndrew Turner     mem_region_read_t mmio_read, mem_region_write_t mmio_write)
78547e07394SAndrew Turner {
78647e07394SAndrew Turner 	int i;
78747e07394SAndrew Turner 
78847e07394SAndrew Turner 	for (i = 0; i < nitems(vm->mmio_region); i++) {
78947e07394SAndrew Turner 		if (vm->mmio_region[i].start == 0 &&
79047e07394SAndrew Turner 		    vm->mmio_region[i].end == 0) {
79147e07394SAndrew Turner 			vm->mmio_region[i].start = start;
79247e07394SAndrew Turner 			vm->mmio_region[i].end = start + size;
79347e07394SAndrew Turner 			vm->mmio_region[i].read = mmio_read;
79447e07394SAndrew Turner 			vm->mmio_region[i].write = mmio_write;
79547e07394SAndrew Turner 			return;
79647e07394SAndrew Turner 		}
79747e07394SAndrew Turner 	}
79847e07394SAndrew Turner 
79947e07394SAndrew Turner 	panic("%s: No free MMIO region", __func__);
80047e07394SAndrew Turner }
80147e07394SAndrew Turner 
80247e07394SAndrew Turner void
80347e07394SAndrew Turner vm_deregister_inst_handler(struct vm *vm, uint64_t start, uint64_t size)
80447e07394SAndrew Turner {
80547e07394SAndrew Turner 	int i;
80647e07394SAndrew Turner 
80747e07394SAndrew Turner 	for (i = 0; i < nitems(vm->mmio_region); i++) {
80847e07394SAndrew Turner 		if (vm->mmio_region[i].start == start &&
80947e07394SAndrew Turner 		    vm->mmio_region[i].end == start + size) {
81047e07394SAndrew Turner 			memset(&vm->mmio_region[i], 0,
81147e07394SAndrew Turner 			    sizeof(vm->mmio_region[i]));
81247e07394SAndrew Turner 			return;
81347e07394SAndrew Turner 		}
81447e07394SAndrew Turner 	}
81547e07394SAndrew Turner 
81647e07394SAndrew Turner 	panic("%s: Invalid MMIO region: %lx - %lx", __func__, start,
81747e07394SAndrew Turner 	    start + size);
81847e07394SAndrew Turner }
81947e07394SAndrew Turner 
82047e07394SAndrew Turner static int
82147e07394SAndrew Turner vm_handle_inst_emul(struct vcpu *vcpu, bool *retu)
82247e07394SAndrew Turner {
82347e07394SAndrew Turner 	struct vm *vm;
82447e07394SAndrew Turner 	struct vm_exit *vme;
82547e07394SAndrew Turner 	struct vie *vie;
82647e07394SAndrew Turner 	struct hyp *hyp;
82747e07394SAndrew Turner 	uint64_t fault_ipa;
82847e07394SAndrew Turner 	struct vm_guest_paging *paging;
82947e07394SAndrew Turner 	struct vmm_mmio_region *vmr;
83047e07394SAndrew Turner 	int error, i;
83147e07394SAndrew Turner 
83247e07394SAndrew Turner 	vm = vcpu->vm;
83347e07394SAndrew Turner 	hyp = vm->cookie;
83447e07394SAndrew Turner 	if (!hyp->vgic_attached)
83547e07394SAndrew Turner 		goto out_user;
83647e07394SAndrew Turner 
83747e07394SAndrew Turner 	vme = &vcpu->exitinfo;
83847e07394SAndrew Turner 	vie = &vme->u.inst_emul.vie;
83947e07394SAndrew Turner 	paging = &vme->u.inst_emul.paging;
84047e07394SAndrew Turner 
84147e07394SAndrew Turner 	fault_ipa = vme->u.inst_emul.gpa;
84247e07394SAndrew Turner 
84347e07394SAndrew Turner 	vmr = NULL;
84447e07394SAndrew Turner 	for (i = 0; i < nitems(vm->mmio_region); i++) {
84547e07394SAndrew Turner 		if (vm->mmio_region[i].start <= fault_ipa &&
84647e07394SAndrew Turner 		    vm->mmio_region[i].end > fault_ipa) {
84747e07394SAndrew Turner 			vmr = &vm->mmio_region[i];
84847e07394SAndrew Turner 			break;
84947e07394SAndrew Turner 		}
85047e07394SAndrew Turner 	}
85147e07394SAndrew Turner 	if (vmr == NULL)
85247e07394SAndrew Turner 		goto out_user;
85347e07394SAndrew Turner 
85447e07394SAndrew Turner 	error = vmm_emulate_instruction(vcpu, fault_ipa, vie, paging,
85547e07394SAndrew Turner 	    vmr->read, vmr->write, retu);
85647e07394SAndrew Turner 	return (error);
85747e07394SAndrew Turner 
85847e07394SAndrew Turner out_user:
85947e07394SAndrew Turner 	*retu = true;
86047e07394SAndrew Turner 	return (0);
86147e07394SAndrew Turner }
86247e07394SAndrew Turner 
86347e07394SAndrew Turner int
86447e07394SAndrew Turner vm_suspend(struct vm *vm, enum vm_suspend_how how)
86547e07394SAndrew Turner {
86647e07394SAndrew Turner 	int i;
86747e07394SAndrew Turner 
86847e07394SAndrew Turner 	if (how <= VM_SUSPEND_NONE || how >= VM_SUSPEND_LAST)
86947e07394SAndrew Turner 		return (EINVAL);
87047e07394SAndrew Turner 
87147e07394SAndrew Turner 	if (atomic_cmpset_int(&vm->suspend, 0, how) == 0) {
87247e07394SAndrew Turner 		VM_CTR2(vm, "virtual machine already suspended %d/%d",
87347e07394SAndrew Turner 		    vm->suspend, how);
87447e07394SAndrew Turner 		return (EALREADY);
87547e07394SAndrew Turner 	}
87647e07394SAndrew Turner 
87747e07394SAndrew Turner 	VM_CTR1(vm, "virtual machine successfully suspended %d", how);
87847e07394SAndrew Turner 
87947e07394SAndrew Turner 	/*
88047e07394SAndrew Turner 	 * Notify all active vcpus that they are now suspended.
88147e07394SAndrew Turner 	 */
88247e07394SAndrew Turner 	for (i = 0; i < vm->maxcpus; i++) {
88347e07394SAndrew Turner 		if (CPU_ISSET(i, &vm->active_cpus))
88447e07394SAndrew Turner 			vcpu_notify_event(vm_vcpu(vm, i));
88547e07394SAndrew Turner 	}
88647e07394SAndrew Turner 
88747e07394SAndrew Turner 	return (0);
88847e07394SAndrew Turner }
88947e07394SAndrew Turner 
89047e07394SAndrew Turner void
89147e07394SAndrew Turner vm_exit_suspended(struct vcpu *vcpu, uint64_t pc)
89247e07394SAndrew Turner {
89347e07394SAndrew Turner 	struct vm *vm = vcpu->vm;
89447e07394SAndrew Turner 	struct vm_exit *vmexit;
89547e07394SAndrew Turner 
89647e07394SAndrew Turner 	KASSERT(vm->suspend > VM_SUSPEND_NONE && vm->suspend < VM_SUSPEND_LAST,
89747e07394SAndrew Turner 	    ("vm_exit_suspended: invalid suspend type %d", vm->suspend));
89847e07394SAndrew Turner 
89947e07394SAndrew Turner 	vmexit = vm_exitinfo(vcpu);
90047e07394SAndrew Turner 	vmexit->pc = pc;
90147e07394SAndrew Turner 	vmexit->inst_length = 4;
90247e07394SAndrew Turner 	vmexit->exitcode = VM_EXITCODE_SUSPENDED;
90347e07394SAndrew Turner 	vmexit->u.suspended.how = vm->suspend;
90447e07394SAndrew Turner }
90547e07394SAndrew Turner 
90647e07394SAndrew Turner void
90747e07394SAndrew Turner vm_exit_debug(struct vcpu *vcpu, uint64_t pc)
90847e07394SAndrew Turner {
90947e07394SAndrew Turner 	struct vm_exit *vmexit;
91047e07394SAndrew Turner 
91147e07394SAndrew Turner 	vmexit = vm_exitinfo(vcpu);
91247e07394SAndrew Turner 	vmexit->pc = pc;
91347e07394SAndrew Turner 	vmexit->inst_length = 4;
91447e07394SAndrew Turner 	vmexit->exitcode = VM_EXITCODE_DEBUG;
91547e07394SAndrew Turner }
91647e07394SAndrew Turner 
91747e07394SAndrew Turner int
91847e07394SAndrew Turner vm_activate_cpu(struct vcpu *vcpu)
91947e07394SAndrew Turner {
92047e07394SAndrew Turner 	struct vm *vm = vcpu->vm;
92147e07394SAndrew Turner 
92247e07394SAndrew Turner 	if (CPU_ISSET(vcpu->vcpuid, &vm->active_cpus))
92347e07394SAndrew Turner 		return (EBUSY);
92447e07394SAndrew Turner 
92547e07394SAndrew Turner 	CPU_SET_ATOMIC(vcpu->vcpuid, &vm->active_cpus);
92647e07394SAndrew Turner 	return (0);
92747e07394SAndrew Turner 
92847e07394SAndrew Turner }
92947e07394SAndrew Turner 
93047e07394SAndrew Turner int
93147e07394SAndrew Turner vm_suspend_cpu(struct vm *vm, struct vcpu *vcpu)
93247e07394SAndrew Turner {
93347e07394SAndrew Turner 	if (vcpu == NULL) {
93447e07394SAndrew Turner 		vm->debug_cpus = vm->active_cpus;
93547e07394SAndrew Turner 		for (int i = 0; i < vm->maxcpus; i++) {
93647e07394SAndrew Turner 			if (CPU_ISSET(i, &vm->active_cpus))
93747e07394SAndrew Turner 				vcpu_notify_event(vm_vcpu(vm, i));
93847e07394SAndrew Turner 		}
93947e07394SAndrew Turner 	} else {
94047e07394SAndrew Turner 		if (!CPU_ISSET(vcpu->vcpuid, &vm->active_cpus))
94147e07394SAndrew Turner 			return (EINVAL);
94247e07394SAndrew Turner 
94347e07394SAndrew Turner 		CPU_SET_ATOMIC(vcpu->vcpuid, &vm->debug_cpus);
94447e07394SAndrew Turner 		vcpu_notify_event(vcpu);
94547e07394SAndrew Turner 	}
94647e07394SAndrew Turner 	return (0);
94747e07394SAndrew Turner }
94847e07394SAndrew Turner 
94947e07394SAndrew Turner int
95047e07394SAndrew Turner vm_resume_cpu(struct vm *vm, struct vcpu *vcpu)
95147e07394SAndrew Turner {
95247e07394SAndrew Turner 
95347e07394SAndrew Turner 	if (vcpu == NULL) {
95447e07394SAndrew Turner 		CPU_ZERO(&vm->debug_cpus);
95547e07394SAndrew Turner 	} else {
95647e07394SAndrew Turner 		if (!CPU_ISSET(vcpu->vcpuid, &vm->debug_cpus))
95747e07394SAndrew Turner 			return (EINVAL);
95847e07394SAndrew Turner 
95947e07394SAndrew Turner 		CPU_CLR_ATOMIC(vcpu->vcpuid, &vm->debug_cpus);
96047e07394SAndrew Turner 	}
96147e07394SAndrew Turner 	return (0);
96247e07394SAndrew Turner }
96347e07394SAndrew Turner 
96447e07394SAndrew Turner int
96547e07394SAndrew Turner vcpu_debugged(struct vcpu *vcpu)
96647e07394SAndrew Turner {
96747e07394SAndrew Turner 
96847e07394SAndrew Turner 	return (CPU_ISSET(vcpu->vcpuid, &vcpu->vm->debug_cpus));
96947e07394SAndrew Turner }
97047e07394SAndrew Turner 
97147e07394SAndrew Turner cpuset_t
97247e07394SAndrew Turner vm_active_cpus(struct vm *vm)
97347e07394SAndrew Turner {
97447e07394SAndrew Turner 
97547e07394SAndrew Turner 	return (vm->active_cpus);
97647e07394SAndrew Turner }
97747e07394SAndrew Turner 
97847e07394SAndrew Turner cpuset_t
97947e07394SAndrew Turner vm_debug_cpus(struct vm *vm)
98047e07394SAndrew Turner {
98147e07394SAndrew Turner 
98247e07394SAndrew Turner 	return (vm->debug_cpus);
98347e07394SAndrew Turner }
98447e07394SAndrew Turner 
98547e07394SAndrew Turner cpuset_t
98647e07394SAndrew Turner vm_suspended_cpus(struct vm *vm)
98747e07394SAndrew Turner {
98847e07394SAndrew Turner 
98947e07394SAndrew Turner 	return (vm->suspended_cpus);
99047e07394SAndrew Turner }
99147e07394SAndrew Turner 
99247e07394SAndrew Turner 
99347e07394SAndrew Turner void *
99447e07394SAndrew Turner vcpu_stats(struct vcpu *vcpu)
99547e07394SAndrew Turner {
99647e07394SAndrew Turner 
99747e07394SAndrew Turner 	return (vcpu->stats);
99847e07394SAndrew Turner }
99947e07394SAndrew Turner 
100047e07394SAndrew Turner /*
100147e07394SAndrew Turner  * This function is called to ensure that a vcpu "sees" a pending event
100247e07394SAndrew Turner  * as soon as possible:
100347e07394SAndrew Turner  * - If the vcpu thread is sleeping then it is woken up.
100447e07394SAndrew Turner  * - If the vcpu is running on a different host_cpu then an IPI will be directed
100547e07394SAndrew Turner  *   to the host_cpu to cause the vcpu to trap into the hypervisor.
100647e07394SAndrew Turner  */
100747e07394SAndrew Turner static void
100847e07394SAndrew Turner vcpu_notify_event_locked(struct vcpu *vcpu)
100947e07394SAndrew Turner {
101047e07394SAndrew Turner 	int hostcpu;
101147e07394SAndrew Turner 
101247e07394SAndrew Turner 	hostcpu = vcpu->hostcpu;
101347e07394SAndrew Turner 	if (vcpu->state == VCPU_RUNNING) {
101447e07394SAndrew Turner 		KASSERT(hostcpu != NOCPU, ("vcpu running on invalid hostcpu"));
101547e07394SAndrew Turner 		if (hostcpu != curcpu) {
101647e07394SAndrew Turner 			ipi_cpu(hostcpu, vmm_ipinum);
101747e07394SAndrew Turner 		} else {
101847e07394SAndrew Turner 			/*
101947e07394SAndrew Turner 			 * If the 'vcpu' is running on 'curcpu' then it must
102047e07394SAndrew Turner 			 * be sending a notification to itself (e.g. SELF_IPI).
102147e07394SAndrew Turner 			 * The pending event will be picked up when the vcpu
102247e07394SAndrew Turner 			 * transitions back to guest context.
102347e07394SAndrew Turner 			 */
102447e07394SAndrew Turner 		}
102547e07394SAndrew Turner 	} else {
102647e07394SAndrew Turner 		KASSERT(hostcpu == NOCPU, ("vcpu state %d not consistent "
102747e07394SAndrew Turner 		    "with hostcpu %d", vcpu->state, hostcpu));
102847e07394SAndrew Turner 		if (vcpu->state == VCPU_SLEEPING)
102947e07394SAndrew Turner 			wakeup_one(vcpu);
103047e07394SAndrew Turner 	}
103147e07394SAndrew Turner }
103247e07394SAndrew Turner 
103347e07394SAndrew Turner void
103447e07394SAndrew Turner vcpu_notify_event(struct vcpu *vcpu)
103547e07394SAndrew Turner {
103647e07394SAndrew Turner 	vcpu_lock(vcpu);
103747e07394SAndrew Turner 	vcpu_notify_event_locked(vcpu);
103847e07394SAndrew Turner 	vcpu_unlock(vcpu);
103947e07394SAndrew Turner }
104047e07394SAndrew Turner 
1041*c76c2a19SMark Johnston struct vmspace *
1042*c76c2a19SMark Johnston vm_vmspace(struct vm *vm)
1043*c76c2a19SMark Johnston {
1044*c76c2a19SMark Johnston 	return (vm->vmspace);
1045*c76c2a19SMark Johnston }
1046*c76c2a19SMark Johnston 
1047*c76c2a19SMark Johnston struct vm_mem *
1048*c76c2a19SMark Johnston vm_mem(struct vm *vm)
1049*c76c2a19SMark Johnston {
1050*c76c2a19SMark Johnston 	return (&vm->mem);
1051*c76c2a19SMark Johnston }
1052*c76c2a19SMark Johnston 
105347e07394SAndrew Turner static void
105447e07394SAndrew Turner restore_guest_fpustate(struct vcpu *vcpu)
105547e07394SAndrew Turner {
105647e07394SAndrew Turner 
105747e07394SAndrew Turner 	/* flush host state to the pcb */
105847e07394SAndrew Turner 	vfp_save_state(curthread, curthread->td_pcb);
105947e07394SAndrew Turner 	/* Ensure the VFP state will be re-loaded when exiting the guest */
106047e07394SAndrew Turner 	PCPU_SET(fpcurthread, NULL);
106147e07394SAndrew Turner 
106247e07394SAndrew Turner 	/* restore guest FPU state */
106347e07394SAndrew Turner 	vfp_enable();
106447e07394SAndrew Turner 	vfp_restore(vcpu->guestfpu);
106547e07394SAndrew Turner 
106647e07394SAndrew Turner 	/*
106747e07394SAndrew Turner 	 * The FPU is now "dirty" with the guest's state so turn on emulation
106847e07394SAndrew Turner 	 * to trap any access to the FPU by the host.
106947e07394SAndrew Turner 	 */
107047e07394SAndrew Turner 	vfp_disable();
107147e07394SAndrew Turner }
107247e07394SAndrew Turner 
107347e07394SAndrew Turner static void
107447e07394SAndrew Turner save_guest_fpustate(struct vcpu *vcpu)
107547e07394SAndrew Turner {
107647e07394SAndrew Turner 	if ((READ_SPECIALREG(cpacr_el1) & CPACR_FPEN_MASK) !=
107747e07394SAndrew Turner 	    CPACR_FPEN_TRAP_ALL1)
107847e07394SAndrew Turner 		panic("VFP not enabled in host!");
107947e07394SAndrew Turner 
108047e07394SAndrew Turner 	/* save guest FPU state */
108147e07394SAndrew Turner 	vfp_enable();
108247e07394SAndrew Turner 	vfp_store(vcpu->guestfpu);
108347e07394SAndrew Turner 	vfp_disable();
108447e07394SAndrew Turner 
108547e07394SAndrew Turner 	KASSERT(PCPU_GET(fpcurthread) == NULL,
108647e07394SAndrew Turner 	    ("%s: fpcurthread set with guest registers", __func__));
108747e07394SAndrew Turner }
108847e07394SAndrew Turner static int
108947e07394SAndrew Turner vcpu_set_state_locked(struct vcpu *vcpu, enum vcpu_state newstate,
109047e07394SAndrew Turner     bool from_idle)
109147e07394SAndrew Turner {
109247e07394SAndrew Turner 	int error;
109347e07394SAndrew Turner 
109447e07394SAndrew Turner 	vcpu_assert_locked(vcpu);
109547e07394SAndrew Turner 
109647e07394SAndrew Turner 	/*
109747e07394SAndrew Turner 	 * State transitions from the vmmdev_ioctl() must always begin from
109847e07394SAndrew Turner 	 * the VCPU_IDLE state. This guarantees that there is only a single
109947e07394SAndrew Turner 	 * ioctl() operating on a vcpu at any point.
110047e07394SAndrew Turner 	 */
110147e07394SAndrew Turner 	if (from_idle) {
110247e07394SAndrew Turner 		while (vcpu->state != VCPU_IDLE) {
110347e07394SAndrew Turner 			vcpu_notify_event_locked(vcpu);
110447e07394SAndrew Turner 			msleep_spin(&vcpu->state, &vcpu->mtx, "vmstat", hz);
110547e07394SAndrew Turner 		}
110647e07394SAndrew Turner 	} else {
110747e07394SAndrew Turner 		KASSERT(vcpu->state != VCPU_IDLE, ("invalid transition from "
110847e07394SAndrew Turner 		    "vcpu idle state"));
110947e07394SAndrew Turner 	}
111047e07394SAndrew Turner 
111147e07394SAndrew Turner 	if (vcpu->state == VCPU_RUNNING) {
111247e07394SAndrew Turner 		KASSERT(vcpu->hostcpu == curcpu, ("curcpu %d and hostcpu %d "
111347e07394SAndrew Turner 		    "mismatch for running vcpu", curcpu, vcpu->hostcpu));
111447e07394SAndrew Turner 	} else {
111547e07394SAndrew Turner 		KASSERT(vcpu->hostcpu == NOCPU, ("Invalid hostcpu %d for a "
111647e07394SAndrew Turner 		    "vcpu that is not running", vcpu->hostcpu));
111747e07394SAndrew Turner 	}
111847e07394SAndrew Turner 
111947e07394SAndrew Turner 	/*
112047e07394SAndrew Turner 	 * The following state transitions are allowed:
112147e07394SAndrew Turner 	 * IDLE -> FROZEN -> IDLE
112247e07394SAndrew Turner 	 * FROZEN -> RUNNING -> FROZEN
112347e07394SAndrew Turner 	 * FROZEN -> SLEEPING -> FROZEN
112447e07394SAndrew Turner 	 */
112547e07394SAndrew Turner 	switch (vcpu->state) {
112647e07394SAndrew Turner 	case VCPU_IDLE:
112747e07394SAndrew Turner 	case VCPU_RUNNING:
112847e07394SAndrew Turner 	case VCPU_SLEEPING:
112947e07394SAndrew Turner 		error = (newstate != VCPU_FROZEN);
113047e07394SAndrew Turner 		break;
113147e07394SAndrew Turner 	case VCPU_FROZEN:
113247e07394SAndrew Turner 		error = (newstate == VCPU_FROZEN);
113347e07394SAndrew Turner 		break;
113447e07394SAndrew Turner 	default:
113547e07394SAndrew Turner 		error = 1;
113647e07394SAndrew Turner 		break;
113747e07394SAndrew Turner 	}
113847e07394SAndrew Turner 
113947e07394SAndrew Turner 	if (error)
114047e07394SAndrew Turner 		return (EBUSY);
114147e07394SAndrew Turner 
114247e07394SAndrew Turner 	vcpu->state = newstate;
114347e07394SAndrew Turner 	if (newstate == VCPU_RUNNING)
114447e07394SAndrew Turner 		vcpu->hostcpu = curcpu;
114547e07394SAndrew Turner 	else
114647e07394SAndrew Turner 		vcpu->hostcpu = NOCPU;
114747e07394SAndrew Turner 
114847e07394SAndrew Turner 	if (newstate == VCPU_IDLE)
114947e07394SAndrew Turner 		wakeup(&vcpu->state);
115047e07394SAndrew Turner 
115147e07394SAndrew Turner 	return (0);
115247e07394SAndrew Turner }
115347e07394SAndrew Turner 
115447e07394SAndrew Turner static void
115547e07394SAndrew Turner vcpu_require_state(struct vcpu *vcpu, enum vcpu_state newstate)
115647e07394SAndrew Turner {
115747e07394SAndrew Turner 	int error;
115847e07394SAndrew Turner 
115947e07394SAndrew Turner 	if ((error = vcpu_set_state(vcpu, newstate, false)) != 0)
116047e07394SAndrew Turner 		panic("Error %d setting state to %d\n", error, newstate);
116147e07394SAndrew Turner }
116247e07394SAndrew Turner 
116347e07394SAndrew Turner static void
116447e07394SAndrew Turner vcpu_require_state_locked(struct vcpu *vcpu, enum vcpu_state newstate)
116547e07394SAndrew Turner {
116647e07394SAndrew Turner 	int error;
116747e07394SAndrew Turner 
116847e07394SAndrew Turner 	if ((error = vcpu_set_state_locked(vcpu, newstate, false)) != 0)
116947e07394SAndrew Turner 		panic("Error %d setting state to %d", error, newstate);
117047e07394SAndrew Turner }
117147e07394SAndrew Turner 
117247e07394SAndrew Turner int
117347e07394SAndrew Turner vm_get_capability(struct vcpu *vcpu, int type, int *retval)
117447e07394SAndrew Turner {
117547e07394SAndrew Turner 	if (type < 0 || type >= VM_CAP_MAX)
117647e07394SAndrew Turner 		return (EINVAL);
117747e07394SAndrew Turner 
117847e07394SAndrew Turner 	return (vmmops_getcap(vcpu->cookie, type, retval));
117947e07394SAndrew Turner }
118047e07394SAndrew Turner 
118147e07394SAndrew Turner int
118247e07394SAndrew Turner vm_set_capability(struct vcpu *vcpu, int type, int val)
118347e07394SAndrew Turner {
118447e07394SAndrew Turner 	if (type < 0 || type >= VM_CAP_MAX)
118547e07394SAndrew Turner 		return (EINVAL);
118647e07394SAndrew Turner 
118747e07394SAndrew Turner 	return (vmmops_setcap(vcpu->cookie, type, val));
118847e07394SAndrew Turner }
118947e07394SAndrew Turner 
119047e07394SAndrew Turner struct vm *
119147e07394SAndrew Turner vcpu_vm(struct vcpu *vcpu)
119247e07394SAndrew Turner {
119347e07394SAndrew Turner 	return (vcpu->vm);
119447e07394SAndrew Turner }
119547e07394SAndrew Turner 
119647e07394SAndrew Turner int
119747e07394SAndrew Turner vcpu_vcpuid(struct vcpu *vcpu)
119847e07394SAndrew Turner {
119947e07394SAndrew Turner 	return (vcpu->vcpuid);
120047e07394SAndrew Turner }
120147e07394SAndrew Turner 
120247e07394SAndrew Turner void *
120347e07394SAndrew Turner vcpu_get_cookie(struct vcpu *vcpu)
120447e07394SAndrew Turner {
120547e07394SAndrew Turner 	return (vcpu->cookie);
120647e07394SAndrew Turner }
120747e07394SAndrew Turner 
120847e07394SAndrew Turner struct vcpu *
120947e07394SAndrew Turner vm_vcpu(struct vm *vm, int vcpuid)
121047e07394SAndrew Turner {
121147e07394SAndrew Turner 	return (vm->vcpu[vcpuid]);
121247e07394SAndrew Turner }
121347e07394SAndrew Turner 
121447e07394SAndrew Turner int
121547e07394SAndrew Turner vcpu_set_state(struct vcpu *vcpu, enum vcpu_state newstate, bool from_idle)
121647e07394SAndrew Turner {
121747e07394SAndrew Turner 	int error;
121847e07394SAndrew Turner 
121947e07394SAndrew Turner 	vcpu_lock(vcpu);
122047e07394SAndrew Turner 	error = vcpu_set_state_locked(vcpu, newstate, from_idle);
122147e07394SAndrew Turner 	vcpu_unlock(vcpu);
122247e07394SAndrew Turner 
122347e07394SAndrew Turner 	return (error);
122447e07394SAndrew Turner }
122547e07394SAndrew Turner 
122647e07394SAndrew Turner enum vcpu_state
122747e07394SAndrew Turner vcpu_get_state(struct vcpu *vcpu, int *hostcpu)
122847e07394SAndrew Turner {
122947e07394SAndrew Turner 	enum vcpu_state state;
123047e07394SAndrew Turner 
123147e07394SAndrew Turner 	vcpu_lock(vcpu);
123247e07394SAndrew Turner 	state = vcpu->state;
123347e07394SAndrew Turner 	if (hostcpu != NULL)
123447e07394SAndrew Turner 		*hostcpu = vcpu->hostcpu;
123547e07394SAndrew Turner 	vcpu_unlock(vcpu);
123647e07394SAndrew Turner 
123747e07394SAndrew Turner 	return (state);
123847e07394SAndrew Turner }
123947e07394SAndrew Turner 
124047e07394SAndrew Turner int
124147e07394SAndrew Turner vm_get_register(struct vcpu *vcpu, int reg, uint64_t *retval)
124247e07394SAndrew Turner {
124347e07394SAndrew Turner 
124447e07394SAndrew Turner 	if (reg >= VM_REG_LAST)
124547e07394SAndrew Turner 		return (EINVAL);
124647e07394SAndrew Turner 
124747e07394SAndrew Turner 	return (vmmops_getreg(vcpu->cookie, reg, retval));
124847e07394SAndrew Turner }
124947e07394SAndrew Turner 
125047e07394SAndrew Turner int
125147e07394SAndrew Turner vm_set_register(struct vcpu *vcpu, int reg, uint64_t val)
125247e07394SAndrew Turner {
125347e07394SAndrew Turner 	int error;
125447e07394SAndrew Turner 
125547e07394SAndrew Turner 	if (reg >= VM_REG_LAST)
125647e07394SAndrew Turner 		return (EINVAL);
125747e07394SAndrew Turner 	error = vmmops_setreg(vcpu->cookie, reg, val);
125847e07394SAndrew Turner 	if (error || reg != VM_REG_GUEST_PC)
125947e07394SAndrew Turner 		return (error);
126047e07394SAndrew Turner 
126147e07394SAndrew Turner 	vcpu->nextpc = val;
126247e07394SAndrew Turner 
126347e07394SAndrew Turner 	return (0);
126447e07394SAndrew Turner }
126547e07394SAndrew Turner 
126647e07394SAndrew Turner void *
126747e07394SAndrew Turner vm_get_cookie(struct vm *vm)
126847e07394SAndrew Turner {
126947e07394SAndrew Turner 	return (vm->cookie);
127047e07394SAndrew Turner }
127147e07394SAndrew Turner 
127247e07394SAndrew Turner int
127347e07394SAndrew Turner vm_inject_exception(struct vcpu *vcpu, uint64_t esr, uint64_t far)
127447e07394SAndrew Turner {
127547e07394SAndrew Turner 	return (vmmops_exception(vcpu->cookie, esr, far));
127647e07394SAndrew Turner }
127747e07394SAndrew Turner 
127847e07394SAndrew Turner int
127947e07394SAndrew Turner vm_attach_vgic(struct vm *vm, struct vm_vgic_descr *descr)
128047e07394SAndrew Turner {
128147e07394SAndrew Turner 	return (vgic_attach_to_vm(vm->cookie, descr));
128247e07394SAndrew Turner }
128347e07394SAndrew Turner 
128447e07394SAndrew Turner int
128547e07394SAndrew Turner vm_assert_irq(struct vm *vm, uint32_t irq)
128647e07394SAndrew Turner {
128747e07394SAndrew Turner 	return (vgic_inject_irq(vm->cookie, -1, irq, true));
128847e07394SAndrew Turner }
128947e07394SAndrew Turner 
129047e07394SAndrew Turner int
129147e07394SAndrew Turner vm_deassert_irq(struct vm *vm, uint32_t irq)
129247e07394SAndrew Turner {
129347e07394SAndrew Turner 	return (vgic_inject_irq(vm->cookie, -1, irq, false));
129447e07394SAndrew Turner }
129547e07394SAndrew Turner 
129647e07394SAndrew Turner int
129747e07394SAndrew Turner vm_raise_msi(struct vm *vm, uint64_t msg, uint64_t addr, int bus, int slot,
129847e07394SAndrew Turner     int func)
129947e07394SAndrew Turner {
130047e07394SAndrew Turner 	/* TODO: Should we raise an SError? */
130147e07394SAndrew Turner 	return (vgic_inject_msi(vm->cookie, msg, addr));
130247e07394SAndrew Turner }
130347e07394SAndrew Turner 
130447e07394SAndrew Turner static int
130547e07394SAndrew Turner vm_handle_smccc_call(struct vcpu *vcpu, struct vm_exit *vme, bool *retu)
130647e07394SAndrew Turner {
130747e07394SAndrew Turner 	struct hypctx *hypctx;
130847e07394SAndrew Turner 	int i;
130947e07394SAndrew Turner 
131047e07394SAndrew Turner 	hypctx = vcpu_get_cookie(vcpu);
131147e07394SAndrew Turner 
131247e07394SAndrew Turner 	if ((hypctx->tf.tf_esr & ESR_ELx_ISS_MASK) != 0)
131347e07394SAndrew Turner 		return (1);
131447e07394SAndrew Turner 
131547e07394SAndrew Turner 	vme->exitcode = VM_EXITCODE_SMCCC;
131647e07394SAndrew Turner 	vme->u.smccc_call.func_id = hypctx->tf.tf_x[0];
131747e07394SAndrew Turner 	for (i = 0; i < nitems(vme->u.smccc_call.args); i++)
131847e07394SAndrew Turner 		vme->u.smccc_call.args[i] = hypctx->tf.tf_x[i + 1];
131947e07394SAndrew Turner 
132047e07394SAndrew Turner 	*retu = true;
132147e07394SAndrew Turner 	return (0);
132247e07394SAndrew Turner }
132347e07394SAndrew Turner 
132447e07394SAndrew Turner static int
132547e07394SAndrew Turner vm_handle_wfi(struct vcpu *vcpu, struct vm_exit *vme, bool *retu)
132647e07394SAndrew Turner {
132747e07394SAndrew Turner 	vcpu_lock(vcpu);
132847e07394SAndrew Turner 	while (1) {
132947e07394SAndrew Turner 		if (vgic_has_pending_irq(vcpu->cookie))
133047e07394SAndrew Turner 			break;
133147e07394SAndrew Turner 
133247e07394SAndrew Turner 		if (vcpu_should_yield(vcpu))
133347e07394SAndrew Turner 			break;
133447e07394SAndrew Turner 
133547e07394SAndrew Turner 		vcpu_require_state_locked(vcpu, VCPU_SLEEPING);
133647e07394SAndrew Turner 		/*
133747e07394SAndrew Turner 		 * XXX msleep_spin() cannot be interrupted by signals so
133847e07394SAndrew Turner 		 * wake up periodically to check pending signals.
133947e07394SAndrew Turner 		 */
134047e07394SAndrew Turner 		msleep_spin(vcpu, &vcpu->mtx, "vmidle", hz);
134147e07394SAndrew Turner 		vcpu_require_state_locked(vcpu, VCPU_FROZEN);
134247e07394SAndrew Turner 	}
134347e07394SAndrew Turner 	vcpu_unlock(vcpu);
134447e07394SAndrew Turner 
134547e07394SAndrew Turner 	*retu = false;
134647e07394SAndrew Turner 	return (0);
134747e07394SAndrew Turner }
134847e07394SAndrew Turner 
134947e07394SAndrew Turner static int
135047e07394SAndrew Turner vm_handle_paging(struct vcpu *vcpu, bool *retu)
135147e07394SAndrew Turner {
135247e07394SAndrew Turner 	struct vm *vm = vcpu->vm;
135347e07394SAndrew Turner 	struct vm_exit *vme;
135447e07394SAndrew Turner 	struct vm_map *map;
135547e07394SAndrew Turner 	uint64_t addr, esr;
135647e07394SAndrew Turner 	pmap_t pmap;
135747e07394SAndrew Turner 	int ftype, rv;
135847e07394SAndrew Turner 
135947e07394SAndrew Turner 	vme = &vcpu->exitinfo;
136047e07394SAndrew Turner 
136147e07394SAndrew Turner 	pmap = vmspace_pmap(vcpu->vm->vmspace);
136247e07394SAndrew Turner 	addr = vme->u.paging.gpa;
136347e07394SAndrew Turner 	esr = vme->u.paging.esr;
136447e07394SAndrew Turner 
136547e07394SAndrew Turner 	/* The page exists, but the page table needs to be updated. */
136647e07394SAndrew Turner 	if (pmap_fault(pmap, esr, addr) == KERN_SUCCESS)
136747e07394SAndrew Turner 		return (0);
136847e07394SAndrew Turner 
136947e07394SAndrew Turner 	switch (ESR_ELx_EXCEPTION(esr)) {
137047e07394SAndrew Turner 	case EXCP_INSN_ABORT_L:
137147e07394SAndrew Turner 	case EXCP_DATA_ABORT_L:
137247e07394SAndrew Turner 		ftype = VM_PROT_EXECUTE | VM_PROT_READ | VM_PROT_WRITE;
137347e07394SAndrew Turner 		break;
137447e07394SAndrew Turner 	default:
137547e07394SAndrew Turner 		panic("%s: Invalid exception (esr = %lx)", __func__, esr);
137647e07394SAndrew Turner 	}
137747e07394SAndrew Turner 
137847e07394SAndrew Turner 	map = &vm->vmspace->vm_map;
137947e07394SAndrew Turner 	rv = vm_fault(map, vme->u.paging.gpa, ftype, VM_FAULT_NORMAL, NULL);
138047e07394SAndrew Turner 	if (rv != KERN_SUCCESS)
138147e07394SAndrew Turner 		return (EFAULT);
138247e07394SAndrew Turner 
138347e07394SAndrew Turner 	return (0);
138447e07394SAndrew Turner }
138547e07394SAndrew Turner 
13861ee7a8faSMark Johnston static int
13871ee7a8faSMark Johnston vm_handle_suspend(struct vcpu *vcpu, bool *retu)
13881ee7a8faSMark Johnston {
13891ee7a8faSMark Johnston 	struct vm *vm = vcpu->vm;
13901ee7a8faSMark Johnston 	int error, i;
13911ee7a8faSMark Johnston 	struct thread *td;
13921ee7a8faSMark Johnston 
13931ee7a8faSMark Johnston 	error = 0;
13941ee7a8faSMark Johnston 	td = curthread;
13951ee7a8faSMark Johnston 
13961ee7a8faSMark Johnston 	CPU_SET_ATOMIC(vcpu->vcpuid, &vm->suspended_cpus);
13971ee7a8faSMark Johnston 
13981ee7a8faSMark Johnston 	/*
13991ee7a8faSMark Johnston 	 * Wait until all 'active_cpus' have suspended themselves.
14001ee7a8faSMark Johnston 	 *
14011ee7a8faSMark Johnston 	 * Since a VM may be suspended at any time including when one or
14021ee7a8faSMark Johnston 	 * more vcpus are doing a rendezvous we need to call the rendezvous
14031ee7a8faSMark Johnston 	 * handler while we are waiting to prevent a deadlock.
14041ee7a8faSMark Johnston 	 */
14051ee7a8faSMark Johnston 	vcpu_lock(vcpu);
14061ee7a8faSMark Johnston 	while (error == 0) {
14071ee7a8faSMark Johnston 		if (CPU_CMP(&vm->suspended_cpus, &vm->active_cpus) == 0)
14081ee7a8faSMark Johnston 			break;
14091ee7a8faSMark Johnston 
14101ee7a8faSMark Johnston 		vcpu_require_state_locked(vcpu, VCPU_SLEEPING);
14111ee7a8faSMark Johnston 		msleep_spin(vcpu, &vcpu->mtx, "vmsusp", hz);
14121ee7a8faSMark Johnston 		vcpu_require_state_locked(vcpu, VCPU_FROZEN);
14131ee7a8faSMark Johnston 		if (td_ast_pending(td, TDA_SUSPEND)) {
14141ee7a8faSMark Johnston 			vcpu_unlock(vcpu);
14151ee7a8faSMark Johnston 			error = thread_check_susp(td, false);
14161ee7a8faSMark Johnston 			vcpu_lock(vcpu);
14171ee7a8faSMark Johnston 		}
14181ee7a8faSMark Johnston 	}
14191ee7a8faSMark Johnston 	vcpu_unlock(vcpu);
14201ee7a8faSMark Johnston 
14211ee7a8faSMark Johnston 	/*
14221ee7a8faSMark Johnston 	 * Wakeup the other sleeping vcpus and return to userspace.
14231ee7a8faSMark Johnston 	 */
14241ee7a8faSMark Johnston 	for (i = 0; i < vm->maxcpus; i++) {
14251ee7a8faSMark Johnston 		if (CPU_ISSET(i, &vm->suspended_cpus)) {
14261ee7a8faSMark Johnston 			vcpu_notify_event(vm_vcpu(vm, i));
14271ee7a8faSMark Johnston 		}
14281ee7a8faSMark Johnston 	}
14291ee7a8faSMark Johnston 
14301ee7a8faSMark Johnston 	*retu = true;
14311ee7a8faSMark Johnston 	return (error);
14321ee7a8faSMark Johnston }
14331ee7a8faSMark Johnston 
143447e07394SAndrew Turner int
143547e07394SAndrew Turner vm_run(struct vcpu *vcpu)
143647e07394SAndrew Turner {
143747e07394SAndrew Turner 	struct vm *vm = vcpu->vm;
143847e07394SAndrew Turner 	struct vm_eventinfo evinfo;
143947e07394SAndrew Turner 	int error, vcpuid;
144047e07394SAndrew Turner 	struct vm_exit *vme;
144147e07394SAndrew Turner 	bool retu;
144247e07394SAndrew Turner 	pmap_t pmap;
144347e07394SAndrew Turner 
144447e07394SAndrew Turner 	vcpuid = vcpu->vcpuid;
144547e07394SAndrew Turner 
144647e07394SAndrew Turner 	if (!CPU_ISSET(vcpuid, &vm->active_cpus))
144747e07394SAndrew Turner 		return (EINVAL);
144847e07394SAndrew Turner 
144947e07394SAndrew Turner 	if (CPU_ISSET(vcpuid, &vm->suspended_cpus))
145047e07394SAndrew Turner 		return (EINVAL);
145147e07394SAndrew Turner 
145247e07394SAndrew Turner 	pmap = vmspace_pmap(vm->vmspace);
145347e07394SAndrew Turner 	vme = &vcpu->exitinfo;
145447e07394SAndrew Turner 	evinfo.rptr = NULL;
145547e07394SAndrew Turner 	evinfo.sptr = &vm->suspend;
145647e07394SAndrew Turner 	evinfo.iptr = NULL;
145747e07394SAndrew Turner restart:
145847e07394SAndrew Turner 	critical_enter();
145947e07394SAndrew Turner 
146047e07394SAndrew Turner 	restore_guest_fpustate(vcpu);
146147e07394SAndrew Turner 
146247e07394SAndrew Turner 	vcpu_require_state(vcpu, VCPU_RUNNING);
146347e07394SAndrew Turner 	error = vmmops_run(vcpu->cookie, vcpu->nextpc, pmap, &evinfo);
146447e07394SAndrew Turner 	vcpu_require_state(vcpu, VCPU_FROZEN);
146547e07394SAndrew Turner 
146647e07394SAndrew Turner 	save_guest_fpustate(vcpu);
146747e07394SAndrew Turner 
146847e07394SAndrew Turner 	critical_exit();
146947e07394SAndrew Turner 
147047e07394SAndrew Turner 	if (error == 0) {
147147e07394SAndrew Turner 		retu = false;
147247e07394SAndrew Turner 		switch (vme->exitcode) {
147347e07394SAndrew Turner 		case VM_EXITCODE_INST_EMUL:
147447e07394SAndrew Turner 			vcpu->nextpc = vme->pc + vme->inst_length;
147547e07394SAndrew Turner 			error = vm_handle_inst_emul(vcpu, &retu);
147647e07394SAndrew Turner 			break;
147747e07394SAndrew Turner 
147847e07394SAndrew Turner 		case VM_EXITCODE_REG_EMUL:
147947e07394SAndrew Turner 			vcpu->nextpc = vme->pc + vme->inst_length;
148047e07394SAndrew Turner 			error = vm_handle_reg_emul(vcpu, &retu);
148147e07394SAndrew Turner 			break;
148247e07394SAndrew Turner 
148347e07394SAndrew Turner 		case VM_EXITCODE_HVC:
148447e07394SAndrew Turner 			/*
148547e07394SAndrew Turner 			 * The HVC instruction saves the address for the
148647e07394SAndrew Turner 			 * next instruction as the return address.
148747e07394SAndrew Turner 			 */
148847e07394SAndrew Turner 			vcpu->nextpc = vme->pc;
148947e07394SAndrew Turner 			/*
149047e07394SAndrew Turner 			 * The PSCI call can change the exit information in the
149147e07394SAndrew Turner 			 * case of suspend/reset/poweroff/cpu off/cpu on.
149247e07394SAndrew Turner 			 */
149347e07394SAndrew Turner 			error = vm_handle_smccc_call(vcpu, vme, &retu);
149447e07394SAndrew Turner 			break;
149547e07394SAndrew Turner 
149647e07394SAndrew Turner 		case VM_EXITCODE_WFI:
149747e07394SAndrew Turner 			vcpu->nextpc = vme->pc + vme->inst_length;
149847e07394SAndrew Turner 			error = vm_handle_wfi(vcpu, vme, &retu);
149947e07394SAndrew Turner 			break;
150047e07394SAndrew Turner 
150147e07394SAndrew Turner 		case VM_EXITCODE_PAGING:
150247e07394SAndrew Turner 			vcpu->nextpc = vme->pc;
150347e07394SAndrew Turner 			error = vm_handle_paging(vcpu, &retu);
150447e07394SAndrew Turner 			break;
150547e07394SAndrew Turner 
15061ee7a8faSMark Johnston 		case VM_EXITCODE_SUSPENDED:
15071ee7a8faSMark Johnston 			vcpu->nextpc = vme->pc;
15081ee7a8faSMark Johnston 			error = vm_handle_suspend(vcpu, &retu);
15091ee7a8faSMark Johnston 			break;
15101ee7a8faSMark Johnston 
151147e07394SAndrew Turner 		default:
151247e07394SAndrew Turner 			/* Handle in userland */
151347e07394SAndrew Turner 			vcpu->nextpc = vme->pc;
151447e07394SAndrew Turner 			retu = true;
151547e07394SAndrew Turner 			break;
151647e07394SAndrew Turner 		}
151747e07394SAndrew Turner 	}
151847e07394SAndrew Turner 
151947e07394SAndrew Turner 	if (error == 0 && retu == false)
152047e07394SAndrew Turner 		goto restart;
152147e07394SAndrew Turner 
152247e07394SAndrew Turner 	return (error);
152347e07394SAndrew Turner }
1524