xref: /freebsd/sys/arm64/vmm/vmm.c (revision a97f683fe3c425b425cf8cc466319f54ea957c20)
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>
6893e81baaSMark Johnston #include <dev/vmm/vmm_stat.h>
6947e07394SAndrew Turner 
7047e07394SAndrew Turner #include "arm64.h"
7147e07394SAndrew Turner #include "mmu.h"
7247e07394SAndrew Turner 
7347e07394SAndrew Turner #include "io/vgic.h"
7447e07394SAndrew Turner #include "io/vtimer.h"
7547e07394SAndrew Turner 
7647e07394SAndrew Turner struct vcpu {
7747e07394SAndrew Turner 	int		flags;
7847e07394SAndrew Turner 	enum vcpu_state	state;
7947e07394SAndrew Turner 	struct mtx	mtx;
8047e07394SAndrew Turner 	int		hostcpu;	/* host cpuid this vcpu last ran on */
8147e07394SAndrew Turner 	int		vcpuid;
8247e07394SAndrew Turner 	void		*stats;
8347e07394SAndrew Turner 	struct vm_exit	exitinfo;
8447e07394SAndrew Turner 	uint64_t	nextpc;		/* (x) next instruction to execute */
8547e07394SAndrew Turner 	struct vm	*vm;		/* (o) */
8647e07394SAndrew Turner 	void		*cookie;	/* (i) cpu-specific data */
8747e07394SAndrew Turner 	struct vfpstate	*guestfpu;	/* (a,i) guest fpu state */
8847e07394SAndrew Turner };
8947e07394SAndrew Turner 
9047e07394SAndrew Turner #define	vcpu_lock_initialized(v) mtx_initialized(&((v)->mtx))
9147e07394SAndrew Turner #define	vcpu_lock_init(v)	mtx_init(&((v)->mtx), "vcpu lock", 0, MTX_SPIN)
9247e07394SAndrew Turner #define	vcpu_lock_destroy(v)	mtx_destroy(&((v)->mtx))
9347e07394SAndrew Turner #define	vcpu_lock(v)		mtx_lock_spin(&((v)->mtx))
9447e07394SAndrew Turner #define	vcpu_unlock(v)		mtx_unlock_spin(&((v)->mtx))
9547e07394SAndrew Turner #define	vcpu_assert_locked(v)	mtx_assert(&((v)->mtx), MA_OWNED)
9647e07394SAndrew Turner 
9747e07394SAndrew Turner struct mem_seg {
9847e07394SAndrew Turner 	uint64_t	gpa;
9947e07394SAndrew Turner 	size_t		len;
10047e07394SAndrew Turner 	bool		wired;
10147e07394SAndrew Turner 	bool		sysmem;
10247e07394SAndrew Turner 	vm_object_t	object;
10347e07394SAndrew Turner };
10447e07394SAndrew Turner #define	VM_MAX_MEMSEGS	3
10547e07394SAndrew Turner 
10647e07394SAndrew Turner struct mem_map {
10747e07394SAndrew Turner 	vm_paddr_t	gpa;
10847e07394SAndrew Turner 	size_t		len;
10947e07394SAndrew Turner 	vm_ooffset_t	segoff;
11047e07394SAndrew Turner 	int		segid;
11147e07394SAndrew Turner 	int		prot;
11247e07394SAndrew Turner 	int		flags;
11347e07394SAndrew Turner };
11447e07394SAndrew Turner #define	VM_MAX_MEMMAPS	4
11547e07394SAndrew Turner 
11647e07394SAndrew Turner struct vmm_mmio_region {
11747e07394SAndrew Turner 	uint64_t start;
11847e07394SAndrew Turner 	uint64_t end;
11947e07394SAndrew Turner 	mem_region_read_t read;
12047e07394SAndrew Turner 	mem_region_write_t write;
12147e07394SAndrew Turner };
12247e07394SAndrew Turner #define	VM_MAX_MMIO_REGIONS	4
12347e07394SAndrew Turner 
12447e07394SAndrew Turner struct vmm_special_reg {
12547e07394SAndrew Turner 	uint32_t	esr_iss;
12647e07394SAndrew Turner 	uint32_t	esr_mask;
12747e07394SAndrew Turner 	reg_read_t	reg_read;
12847e07394SAndrew Turner 	reg_write_t	reg_write;
12947e07394SAndrew Turner 	void		*arg;
13047e07394SAndrew Turner };
13147e07394SAndrew Turner #define	VM_MAX_SPECIAL_REGS	16
13247e07394SAndrew Turner 
13347e07394SAndrew Turner /*
13447e07394SAndrew Turner  * Initialization:
13547e07394SAndrew Turner  * (o) initialized the first time the VM is created
13647e07394SAndrew Turner  * (i) initialized when VM is created and when it is reinitialized
13747e07394SAndrew Turner  * (x) initialized before use
13847e07394SAndrew Turner  */
13947e07394SAndrew Turner struct vm {
14047e07394SAndrew Turner 	void		*cookie;		/* (i) cpu-specific data */
14147e07394SAndrew Turner 	volatile cpuset_t active_cpus;		/* (i) active vcpus */
14247e07394SAndrew Turner 	volatile cpuset_t debug_cpus;		/* (i) vcpus stopped for debug */
14347e07394SAndrew Turner 	int		suspend;		/* (i) stop VM execution */
144a03354b0SMark Johnston 	bool		dying;			/* (o) is dying */
14547e07394SAndrew Turner 	volatile cpuset_t suspended_cpus; 	/* (i) suspended vcpus */
14647e07394SAndrew Turner 	volatile cpuset_t halted_cpus;		/* (x) cpus in a hard halt */
14747e07394SAndrew Turner 	struct mem_map	mem_maps[VM_MAX_MEMMAPS]; /* (i) guest address space */
14847e07394SAndrew Turner 	struct mem_seg	mem_segs[VM_MAX_MEMSEGS]; /* (o) guest memory regions */
14947e07394SAndrew Turner 	struct vmspace	*vmspace;		/* (o) guest's address space */
15047e07394SAndrew Turner 	char		name[VM_MAX_NAMELEN];	/* (o) virtual machine name */
15147e07394SAndrew Turner 	struct vcpu	**vcpu;			/* (i) guest vcpus */
15247e07394SAndrew Turner 	struct vmm_mmio_region mmio_region[VM_MAX_MMIO_REGIONS];
15347e07394SAndrew Turner 						/* (o) guest MMIO regions */
15447e07394SAndrew Turner 	struct vmm_special_reg special_reg[VM_MAX_SPECIAL_REGS];
15547e07394SAndrew Turner 	/* The following describe the vm cpu topology */
15647e07394SAndrew Turner 	uint16_t	sockets;		/* (o) num of sockets */
15747e07394SAndrew Turner 	uint16_t	cores;			/* (o) num of cores/socket */
15847e07394SAndrew Turner 	uint16_t	threads;		/* (o) num of threads/core */
15947e07394SAndrew Turner 	uint16_t	maxcpus;		/* (o) max pluggable cpus */
16047e07394SAndrew Turner 	struct sx	mem_segs_lock;		/* (o) */
16147e07394SAndrew Turner 	struct sx	vcpus_init_lock;	/* (o) */
16247e07394SAndrew Turner };
16347e07394SAndrew Turner 
16447e07394SAndrew Turner static bool vmm_initialized = false;
16547e07394SAndrew Turner 
16647e07394SAndrew Turner static int vm_handle_wfi(struct vcpu *vcpu,
16747e07394SAndrew Turner 			 struct vm_exit *vme, bool *retu);
16847e07394SAndrew Turner 
16947e07394SAndrew Turner static MALLOC_DEFINE(M_VMM, "vmm", "vmm");
17047e07394SAndrew Turner 
17147e07394SAndrew Turner /* statistics */
17247e07394SAndrew Turner static VMM_STAT(VCPU_TOTAL_RUNTIME, "vcpu total runtime");
17347e07394SAndrew Turner 
17447e07394SAndrew Turner SYSCTL_NODE(_hw, OID_AUTO, vmm, CTLFLAG_RW, NULL, NULL);
17547e07394SAndrew Turner 
17647e07394SAndrew Turner static int vmm_ipinum;
17747e07394SAndrew Turner SYSCTL_INT(_hw_vmm, OID_AUTO, ipinum, CTLFLAG_RD, &vmm_ipinum, 0,
17847e07394SAndrew Turner     "IPI vector used for vcpu notifications");
17947e07394SAndrew Turner 
18047e07394SAndrew Turner struct vmm_regs {
18147e07394SAndrew Turner 	uint64_t	id_aa64afr0;
18247e07394SAndrew Turner 	uint64_t	id_aa64afr1;
18347e07394SAndrew Turner 	uint64_t	id_aa64dfr0;
18447e07394SAndrew Turner 	uint64_t	id_aa64dfr1;
18547e07394SAndrew Turner 	uint64_t	id_aa64isar0;
18647e07394SAndrew Turner 	uint64_t	id_aa64isar1;
18747e07394SAndrew Turner 	uint64_t	id_aa64isar2;
18847e07394SAndrew Turner 	uint64_t	id_aa64mmfr0;
18947e07394SAndrew Turner 	uint64_t	id_aa64mmfr1;
19047e07394SAndrew Turner 	uint64_t	id_aa64mmfr2;
19147e07394SAndrew Turner 	uint64_t	id_aa64pfr0;
19247e07394SAndrew Turner 	uint64_t	id_aa64pfr1;
19347e07394SAndrew Turner };
19447e07394SAndrew Turner 
19547e07394SAndrew Turner static const struct vmm_regs vmm_arch_regs_masks = {
19647e07394SAndrew Turner 	.id_aa64dfr0 =
19747e07394SAndrew Turner 	    ID_AA64DFR0_CTX_CMPs_MASK |
19847e07394SAndrew Turner 	    ID_AA64DFR0_WRPs_MASK |
19947e07394SAndrew Turner 	    ID_AA64DFR0_BRPs_MASK |
20047e07394SAndrew Turner 	    ID_AA64DFR0_PMUVer_3 |
20147e07394SAndrew Turner 	    ID_AA64DFR0_DebugVer_8,
20247e07394SAndrew Turner 	.id_aa64isar0 =
20347e07394SAndrew Turner 	    ID_AA64ISAR0_TLB_TLBIOSR |
20447e07394SAndrew Turner 	    ID_AA64ISAR0_SHA3_IMPL |
20547e07394SAndrew Turner 	    ID_AA64ISAR0_RDM_IMPL |
20647e07394SAndrew Turner 	    ID_AA64ISAR0_Atomic_IMPL |
20747e07394SAndrew Turner 	    ID_AA64ISAR0_CRC32_BASE |
20847e07394SAndrew Turner 	    ID_AA64ISAR0_SHA2_512 |
20947e07394SAndrew Turner 	    ID_AA64ISAR0_SHA1_BASE |
21047e07394SAndrew Turner 	    ID_AA64ISAR0_AES_PMULL,
21147e07394SAndrew Turner 	.id_aa64mmfr0 =
21247e07394SAndrew Turner 	    ID_AA64MMFR0_TGran4_IMPL |
21347e07394SAndrew Turner 	    ID_AA64MMFR0_TGran64_IMPL |
21447e07394SAndrew Turner 	    ID_AA64MMFR0_TGran16_IMPL |
21547e07394SAndrew Turner 	    ID_AA64MMFR0_ASIDBits_16 |
21647e07394SAndrew Turner 	    ID_AA64MMFR0_PARange_4P,
21747e07394SAndrew Turner 	.id_aa64mmfr1 =
21847e07394SAndrew Turner 	    ID_AA64MMFR1_SpecSEI_IMPL |
21947e07394SAndrew Turner 	    ID_AA64MMFR1_PAN_ATS1E1 |
22047e07394SAndrew Turner 	    ID_AA64MMFR1_HAFDBS_AF,
22147e07394SAndrew Turner 	.id_aa64pfr0 =
22247e07394SAndrew Turner 	    ID_AA64PFR0_GIC_CPUIF_NONE |
22347e07394SAndrew Turner 	    ID_AA64PFR0_AdvSIMD_HP |
22447e07394SAndrew Turner 	    ID_AA64PFR0_FP_HP |
22547e07394SAndrew Turner 	    ID_AA64PFR0_EL3_64 |
22647e07394SAndrew Turner 	    ID_AA64PFR0_EL2_64 |
22747e07394SAndrew Turner 	    ID_AA64PFR0_EL1_64 |
22847e07394SAndrew Turner 	    ID_AA64PFR0_EL0_64,
22947e07394SAndrew Turner };
23047e07394SAndrew Turner 
23147e07394SAndrew Turner /* Host registers masked by vmm_arch_regs_masks. */
23247e07394SAndrew Turner static struct vmm_regs vmm_arch_regs;
23347e07394SAndrew Turner 
23447e07394SAndrew Turner u_int vm_maxcpu;
23547e07394SAndrew Turner SYSCTL_UINT(_hw_vmm, OID_AUTO, maxcpu, CTLFLAG_RDTUN | CTLFLAG_NOFETCH,
23647e07394SAndrew Turner     &vm_maxcpu, 0, "Maximum number of vCPUs");
23747e07394SAndrew Turner 
23847e07394SAndrew Turner static void vm_free_memmap(struct vm *vm, int ident);
23947e07394SAndrew Turner static bool sysmem_mapping(struct vm *vm, struct mem_map *mm);
24047e07394SAndrew Turner static void vcpu_notify_event_locked(struct vcpu *vcpu);
24147e07394SAndrew Turner 
24293e81baaSMark Johnston /* global statistics */
24393e81baaSMark Johnston VMM_STAT(VMEXIT_COUNT, "total number of vm exits");
24493e81baaSMark Johnston VMM_STAT(VMEXIT_UNKNOWN, "number of vmexits for the unknown exception");
24593e81baaSMark Johnston VMM_STAT(VMEXIT_WFI, "number of times wfi was intercepted");
24693e81baaSMark Johnston VMM_STAT(VMEXIT_WFE, "number of times wfe was intercepted");
24793e81baaSMark Johnston VMM_STAT(VMEXIT_HVC, "number of times hvc was intercepted");
24893e81baaSMark Johnston VMM_STAT(VMEXIT_MSR, "number of times msr/mrs was intercepted");
24993e81baaSMark Johnston VMM_STAT(VMEXIT_DATA_ABORT, "number of vmexits for a data abort");
25093e81baaSMark Johnston VMM_STAT(VMEXIT_INSN_ABORT, "number of vmexits for an instruction abort");
25193e81baaSMark Johnston VMM_STAT(VMEXIT_UNHANDLED_SYNC, "number of vmexits for an unhandled synchronous exception");
25293e81baaSMark Johnston VMM_STAT(VMEXIT_IRQ, "number of vmexits for an irq");
25393e81baaSMark Johnston VMM_STAT(VMEXIT_FIQ, "number of vmexits for an interrupt");
25493e81baaSMark Johnston VMM_STAT(VMEXIT_BRK, "number of vmexits for a breakpoint exception");
25593e81baaSMark Johnston VMM_STAT(VMEXIT_SS, "number of vmexits for a single-step exception");
25693e81baaSMark Johnston VMM_STAT(VMEXIT_UNHANDLED_EL2, "number of vmexits for an unhandled EL2 exception");
25793e81baaSMark Johnston VMM_STAT(VMEXIT_UNHANDLED, "number of vmexits for an unhandled exception");
25893e81baaSMark Johnston 
25947e07394SAndrew Turner /*
26047e07394SAndrew Turner  * Upper limit on vm_maxcpu. We could increase this to 28 bits, but this
26147e07394SAndrew Turner  * is a safe value for now.
26247e07394SAndrew Turner  */
26347e07394SAndrew Turner #define	VM_MAXCPU	MIN(0xffff - 1, CPU_SETSIZE)
26447e07394SAndrew Turner 
26547e07394SAndrew Turner static int
26647e07394SAndrew Turner vmm_regs_init(struct vmm_regs *regs, const struct vmm_regs *masks)
26747e07394SAndrew Turner {
26847e07394SAndrew Turner #define	_FETCH_KERN_REG(reg, field) do {				\
26947e07394SAndrew Turner 	regs->field = vmm_arch_regs_masks.field;			\
27047e07394SAndrew Turner 	if (!get_kernel_reg_masked(reg, &regs->field, masks->field))	\
27147e07394SAndrew Turner 		regs->field = 0;					\
27247e07394SAndrew Turner } while (0)
27347e07394SAndrew Turner 	_FETCH_KERN_REG(ID_AA64AFR0_EL1, id_aa64afr0);
27447e07394SAndrew Turner 	_FETCH_KERN_REG(ID_AA64AFR1_EL1, id_aa64afr1);
27547e07394SAndrew Turner 	_FETCH_KERN_REG(ID_AA64DFR0_EL1, id_aa64dfr0);
27647e07394SAndrew Turner 	_FETCH_KERN_REG(ID_AA64DFR1_EL1, id_aa64dfr1);
27747e07394SAndrew Turner 	_FETCH_KERN_REG(ID_AA64ISAR0_EL1, id_aa64isar0);
27847e07394SAndrew Turner 	_FETCH_KERN_REG(ID_AA64ISAR1_EL1, id_aa64isar1);
27947e07394SAndrew Turner 	_FETCH_KERN_REG(ID_AA64ISAR2_EL1, id_aa64isar2);
28047e07394SAndrew Turner 	_FETCH_KERN_REG(ID_AA64MMFR0_EL1, id_aa64mmfr0);
28147e07394SAndrew Turner 	_FETCH_KERN_REG(ID_AA64MMFR1_EL1, id_aa64mmfr1);
28247e07394SAndrew Turner 	_FETCH_KERN_REG(ID_AA64MMFR2_EL1, id_aa64mmfr2);
28347e07394SAndrew Turner 	_FETCH_KERN_REG(ID_AA64PFR0_EL1, id_aa64pfr0);
28447e07394SAndrew Turner 	_FETCH_KERN_REG(ID_AA64PFR1_EL1, id_aa64pfr1);
28547e07394SAndrew Turner #undef _FETCH_KERN_REG
28647e07394SAndrew Turner 	return (0);
28747e07394SAndrew Turner }
28847e07394SAndrew Turner 
28947e07394SAndrew Turner static void
29047e07394SAndrew Turner vcpu_cleanup(struct vcpu *vcpu, bool destroy)
29147e07394SAndrew Turner {
29247e07394SAndrew Turner 	vmmops_vcpu_cleanup(vcpu->cookie);
29347e07394SAndrew Turner 	vcpu->cookie = NULL;
29447e07394SAndrew Turner 	if (destroy) {
29547e07394SAndrew Turner 		vmm_stat_free(vcpu->stats);
29647e07394SAndrew Turner 		fpu_save_area_free(vcpu->guestfpu);
29747e07394SAndrew Turner 		vcpu_lock_destroy(vcpu);
29847e07394SAndrew Turner 	}
29947e07394SAndrew Turner }
30047e07394SAndrew Turner 
30147e07394SAndrew Turner static struct vcpu *
30247e07394SAndrew Turner vcpu_alloc(struct vm *vm, int vcpu_id)
30347e07394SAndrew Turner {
30447e07394SAndrew Turner 	struct vcpu *vcpu;
30547e07394SAndrew Turner 
30647e07394SAndrew Turner 	KASSERT(vcpu_id >= 0 && vcpu_id < vm->maxcpus,
30747e07394SAndrew Turner 	    ("vcpu_alloc: invalid vcpu %d", vcpu_id));
30847e07394SAndrew Turner 
30947e07394SAndrew Turner 	vcpu = malloc(sizeof(*vcpu), M_VMM, M_WAITOK | M_ZERO);
31047e07394SAndrew Turner 	vcpu_lock_init(vcpu);
31147e07394SAndrew Turner 	vcpu->state = VCPU_IDLE;
31247e07394SAndrew Turner 	vcpu->hostcpu = NOCPU;
31347e07394SAndrew Turner 	vcpu->vcpuid = vcpu_id;
31447e07394SAndrew Turner 	vcpu->vm = vm;
31547e07394SAndrew Turner 	vcpu->guestfpu = fpu_save_area_alloc();
31647e07394SAndrew Turner 	vcpu->stats = vmm_stat_alloc();
31747e07394SAndrew Turner 	return (vcpu);
31847e07394SAndrew Turner }
31947e07394SAndrew Turner 
32047e07394SAndrew Turner static void
32147e07394SAndrew Turner vcpu_init(struct vcpu *vcpu)
32247e07394SAndrew Turner {
32347e07394SAndrew Turner 	vcpu->cookie = vmmops_vcpu_init(vcpu->vm->cookie, vcpu, vcpu->vcpuid);
32447e07394SAndrew Turner 	MPASS(vcpu->cookie != NULL);
32547e07394SAndrew Turner 	fpu_save_area_reset(vcpu->guestfpu);
32647e07394SAndrew Turner 	vmm_stat_init(vcpu->stats);
32747e07394SAndrew Turner }
32847e07394SAndrew Turner 
32947e07394SAndrew Turner struct vm_exit *
33047e07394SAndrew Turner vm_exitinfo(struct vcpu *vcpu)
33147e07394SAndrew Turner {
33247e07394SAndrew Turner 	return (&vcpu->exitinfo);
33347e07394SAndrew Turner }
33447e07394SAndrew Turner 
33547e07394SAndrew Turner static int
33647e07394SAndrew Turner vmm_init(void)
33747e07394SAndrew Turner {
33847e07394SAndrew Turner 	int error;
33947e07394SAndrew Turner 
34047e07394SAndrew Turner 	vm_maxcpu = mp_ncpus;
34147e07394SAndrew Turner 	TUNABLE_INT_FETCH("hw.vmm.maxcpu", &vm_maxcpu);
34247e07394SAndrew Turner 
34347e07394SAndrew Turner 	if (vm_maxcpu > VM_MAXCPU) {
34447e07394SAndrew Turner 		printf("vmm: vm_maxcpu clamped to %u\n", VM_MAXCPU);
34547e07394SAndrew Turner 		vm_maxcpu = VM_MAXCPU;
34647e07394SAndrew Turner 	}
34747e07394SAndrew Turner 	if (vm_maxcpu == 0)
34847e07394SAndrew Turner 		vm_maxcpu = 1;
34947e07394SAndrew Turner 
35047e07394SAndrew Turner 	error = vmm_regs_init(&vmm_arch_regs, &vmm_arch_regs_masks);
35147e07394SAndrew Turner 	if (error != 0)
35247e07394SAndrew Turner 		return (error);
35347e07394SAndrew Turner 
35447e07394SAndrew Turner 	return (vmmops_modinit(0));
35547e07394SAndrew Turner }
35647e07394SAndrew Turner 
35747e07394SAndrew Turner static int
35847e07394SAndrew Turner vmm_handler(module_t mod, int what, void *arg)
35947e07394SAndrew Turner {
36047e07394SAndrew Turner 	int error;
36147e07394SAndrew Turner 
36247e07394SAndrew Turner 	switch (what) {
36347e07394SAndrew Turner 	case MOD_LOAD:
36447e07394SAndrew Turner 		/* TODO: if (vmm_is_hw_supported()) { */
365*a97f683fSMark Johnston 		error = vmmdev_init();
366*a97f683fSMark Johnston 		if (error != 0)
367*a97f683fSMark Johnston 			break;
36847e07394SAndrew Turner 		error = vmm_init();
36947e07394SAndrew Turner 		if (error == 0)
37047e07394SAndrew Turner 			vmm_initialized = true;
37147e07394SAndrew Turner 		break;
37247e07394SAndrew Turner 	case MOD_UNLOAD:
37347e07394SAndrew Turner 		/* TODO: if (vmm_is_hw_supported()) { */
37447e07394SAndrew Turner 		error = vmmdev_cleanup();
37547e07394SAndrew Turner 		if (error == 0 && vmm_initialized) {
37647e07394SAndrew Turner 			error = vmmops_modcleanup();
37747e07394SAndrew Turner 			if (error)
37847e07394SAndrew Turner 				vmm_initialized = false;
37947e07394SAndrew Turner 		}
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).
39947e07394SAndrew Turner  */
40047e07394SAndrew Turner DECLARE_MODULE(vmm, vmm_kmod, SI_SUB_SMP + 1, SI_ORDER_ANY);
40147e07394SAndrew Turner MODULE_VERSION(vmm, 1);
40247e07394SAndrew Turner 
40347e07394SAndrew Turner static void
40447e07394SAndrew Turner vm_init(struct vm *vm, bool create)
40547e07394SAndrew Turner {
40647e07394SAndrew Turner 	int i;
40747e07394SAndrew Turner 
40847e07394SAndrew Turner 	vm->cookie = vmmops_init(vm, vmspace_pmap(vm->vmspace));
40947e07394SAndrew Turner 	MPASS(vm->cookie != NULL);
41047e07394SAndrew Turner 
41147e07394SAndrew Turner 	CPU_ZERO(&vm->active_cpus);
41247e07394SAndrew Turner 	CPU_ZERO(&vm->debug_cpus);
41347e07394SAndrew Turner 
41447e07394SAndrew Turner 	vm->suspend = 0;
41547e07394SAndrew Turner 	CPU_ZERO(&vm->suspended_cpus);
41647e07394SAndrew Turner 
41747e07394SAndrew Turner 	memset(vm->mmio_region, 0, sizeof(vm->mmio_region));
41847e07394SAndrew Turner 	memset(vm->special_reg, 0, sizeof(vm->special_reg));
41947e07394SAndrew Turner 
42047e07394SAndrew Turner 	if (!create) {
42147e07394SAndrew Turner 		for (i = 0; i < vm->maxcpus; i++) {
42247e07394SAndrew Turner 			if (vm->vcpu[i] != NULL)
42347e07394SAndrew Turner 				vcpu_init(vm->vcpu[i]);
42447e07394SAndrew Turner 		}
42547e07394SAndrew Turner 	}
42647e07394SAndrew Turner }
42747e07394SAndrew Turner 
428a03354b0SMark Johnston void
429a03354b0SMark Johnston vm_disable_vcpu_creation(struct vm *vm)
430a03354b0SMark Johnston {
431a03354b0SMark Johnston 	sx_xlock(&vm->vcpus_init_lock);
432a03354b0SMark Johnston 	vm->dying = true;
433a03354b0SMark Johnston 	sx_xunlock(&vm->vcpus_init_lock);
434a03354b0SMark Johnston }
435a03354b0SMark Johnston 
43647e07394SAndrew Turner struct vcpu *
43747e07394SAndrew Turner vm_alloc_vcpu(struct vm *vm, int vcpuid)
43847e07394SAndrew Turner {
43947e07394SAndrew Turner 	struct vcpu *vcpu;
44047e07394SAndrew Turner 
44147e07394SAndrew Turner 	if (vcpuid < 0 || vcpuid >= vm_get_maxcpus(vm))
44247e07394SAndrew Turner 		return (NULL);
44347e07394SAndrew Turner 
44447e07394SAndrew Turner 	/* Some interrupt controllers may have a CPU limit */
44547e07394SAndrew Turner 	if (vcpuid >= vgic_max_cpu_count(vm->cookie))
44647e07394SAndrew Turner 		return (NULL);
44747e07394SAndrew Turner 
44872ae04c7SRuslan Bukin 	vcpu = (struct vcpu *)
44972ae04c7SRuslan Bukin 	    atomic_load_acq_ptr((uintptr_t *)&vm->vcpu[vcpuid]);
45047e07394SAndrew Turner 	if (__predict_true(vcpu != NULL))
45147e07394SAndrew Turner 		return (vcpu);
45247e07394SAndrew Turner 
45347e07394SAndrew Turner 	sx_xlock(&vm->vcpus_init_lock);
45447e07394SAndrew Turner 	vcpu = vm->vcpu[vcpuid];
455a03354b0SMark Johnston 	if (vcpu == NULL && !vm->dying) {
45647e07394SAndrew Turner 		vcpu = vcpu_alloc(vm, vcpuid);
45747e07394SAndrew Turner 		vcpu_init(vcpu);
45847e07394SAndrew Turner 
45947e07394SAndrew Turner 		/*
46047e07394SAndrew Turner 		 * Ensure vCPU is fully created before updating pointer
46147e07394SAndrew Turner 		 * to permit unlocked reads above.
46247e07394SAndrew Turner 		 */
46347e07394SAndrew Turner 		atomic_store_rel_ptr((uintptr_t *)&vm->vcpu[vcpuid],
46447e07394SAndrew Turner 		    (uintptr_t)vcpu);
46547e07394SAndrew Turner 	}
46647e07394SAndrew Turner 	sx_xunlock(&vm->vcpus_init_lock);
46747e07394SAndrew Turner 	return (vcpu);
46847e07394SAndrew Turner }
46947e07394SAndrew Turner 
47047e07394SAndrew Turner void
47147e07394SAndrew Turner vm_slock_vcpus(struct vm *vm)
47247e07394SAndrew Turner {
47347e07394SAndrew Turner 	sx_slock(&vm->vcpus_init_lock);
47447e07394SAndrew Turner }
47547e07394SAndrew Turner 
47647e07394SAndrew Turner void
47747e07394SAndrew Turner vm_unlock_vcpus(struct vm *vm)
47847e07394SAndrew Turner {
47947e07394SAndrew Turner 	sx_unlock(&vm->vcpus_init_lock);
48047e07394SAndrew Turner }
48147e07394SAndrew Turner 
48247e07394SAndrew Turner int
48347e07394SAndrew Turner vm_create(const char *name, struct vm **retvm)
48447e07394SAndrew Turner {
48547e07394SAndrew Turner 	struct vm *vm;
48647e07394SAndrew Turner 	struct vmspace *vmspace;
48747e07394SAndrew Turner 
48847e07394SAndrew Turner 	/*
48947e07394SAndrew Turner 	 * If vmm.ko could not be successfully initialized then don't attempt
49047e07394SAndrew Turner 	 * to create the virtual machine.
49147e07394SAndrew Turner 	 */
49247e07394SAndrew Turner 	if (!vmm_initialized)
49347e07394SAndrew Turner 		return (ENXIO);
49447e07394SAndrew Turner 
49547e07394SAndrew Turner 	if (name == NULL || strlen(name) >= VM_MAX_NAMELEN)
49647e07394SAndrew Turner 		return (EINVAL);
49747e07394SAndrew Turner 
49847e07394SAndrew Turner 	vmspace = vmmops_vmspace_alloc(0, 1ul << 39);
49947e07394SAndrew Turner 	if (vmspace == NULL)
50047e07394SAndrew Turner 		return (ENOMEM);
50147e07394SAndrew Turner 
50247e07394SAndrew Turner 	vm = malloc(sizeof(struct vm), M_VMM, M_WAITOK | M_ZERO);
50347e07394SAndrew Turner 	strcpy(vm->name, name);
50447e07394SAndrew Turner 	vm->vmspace = vmspace;
50547e07394SAndrew Turner 	sx_init(&vm->mem_segs_lock, "vm mem_segs");
50647e07394SAndrew Turner 	sx_init(&vm->vcpus_init_lock, "vm vcpus");
50747e07394SAndrew Turner 
50847e07394SAndrew Turner 	vm->sockets = 1;
50947e07394SAndrew Turner 	vm->cores = 1;			/* XXX backwards compatibility */
51047e07394SAndrew Turner 	vm->threads = 1;		/* XXX backwards compatibility */
51147e07394SAndrew Turner 	vm->maxcpus = vm_maxcpu;
51247e07394SAndrew Turner 
51347e07394SAndrew Turner 	vm->vcpu = malloc(sizeof(*vm->vcpu) * vm->maxcpus, M_VMM,
51447e07394SAndrew Turner 	    M_WAITOK | M_ZERO);
51547e07394SAndrew Turner 
51647e07394SAndrew Turner 	vm_init(vm, true);
51747e07394SAndrew Turner 
51847e07394SAndrew Turner 	*retvm = vm;
51947e07394SAndrew Turner 	return (0);
52047e07394SAndrew Turner }
52147e07394SAndrew Turner 
52247e07394SAndrew Turner void
52347e07394SAndrew Turner vm_get_topology(struct vm *vm, uint16_t *sockets, uint16_t *cores,
52447e07394SAndrew Turner     uint16_t *threads, uint16_t *maxcpus)
52547e07394SAndrew Turner {
52647e07394SAndrew Turner 	*sockets = vm->sockets;
52747e07394SAndrew Turner 	*cores = vm->cores;
52847e07394SAndrew Turner 	*threads = vm->threads;
52947e07394SAndrew Turner 	*maxcpus = vm->maxcpus;
53047e07394SAndrew Turner }
53147e07394SAndrew Turner 
53247e07394SAndrew Turner uint16_t
53347e07394SAndrew Turner vm_get_maxcpus(struct vm *vm)
53447e07394SAndrew Turner {
53547e07394SAndrew Turner 	return (vm->maxcpus);
53647e07394SAndrew Turner }
53747e07394SAndrew Turner 
53847e07394SAndrew Turner int
53947e07394SAndrew Turner vm_set_topology(struct vm *vm, uint16_t sockets, uint16_t cores,
54047e07394SAndrew Turner     uint16_t threads, uint16_t maxcpus)
54147e07394SAndrew Turner {
54247e07394SAndrew Turner 	/* Ignore maxcpus. */
54347e07394SAndrew Turner 	if ((sockets * cores * threads) > vm->maxcpus)
54447e07394SAndrew Turner 		return (EINVAL);
54547e07394SAndrew Turner 	vm->sockets = sockets;
54647e07394SAndrew Turner 	vm->cores = cores;
54747e07394SAndrew Turner 	vm->threads = threads;
54847e07394SAndrew Turner 	return(0);
54947e07394SAndrew Turner }
55047e07394SAndrew Turner 
55147e07394SAndrew Turner static void
55247e07394SAndrew Turner vm_cleanup(struct vm *vm, bool destroy)
55347e07394SAndrew Turner {
55447e07394SAndrew Turner 	struct mem_map *mm;
55547e07394SAndrew Turner 	pmap_t pmap __diagused;
55647e07394SAndrew Turner 	int i;
55747e07394SAndrew Turner 
55847e07394SAndrew Turner 	if (destroy) {
55947e07394SAndrew Turner 		pmap = vmspace_pmap(vm->vmspace);
56047e07394SAndrew Turner 		sched_pin();
56147e07394SAndrew Turner 		PCPU_SET(curvmpmap, NULL);
56247e07394SAndrew Turner 		sched_unpin();
56347e07394SAndrew Turner 		CPU_FOREACH(i) {
56447e07394SAndrew Turner 			MPASS(cpuid_to_pcpu[i]->pc_curvmpmap != pmap);
56547e07394SAndrew Turner 		}
56647e07394SAndrew Turner 	}
56747e07394SAndrew Turner 
56847e07394SAndrew Turner 	vgic_detach_from_vm(vm->cookie);
56947e07394SAndrew Turner 
57047e07394SAndrew Turner 	for (i = 0; i < vm->maxcpus; i++) {
57147e07394SAndrew Turner 		if (vm->vcpu[i] != NULL)
57247e07394SAndrew Turner 			vcpu_cleanup(vm->vcpu[i], destroy);
57347e07394SAndrew Turner 	}
57447e07394SAndrew Turner 
57547e07394SAndrew Turner 	vmmops_cleanup(vm->cookie);
57647e07394SAndrew Turner 
57747e07394SAndrew Turner 	/*
57847e07394SAndrew Turner 	 * System memory is removed from the guest address space only when
57947e07394SAndrew Turner 	 * the VM is destroyed. This is because the mapping remains the same
58047e07394SAndrew Turner 	 * across VM reset.
58147e07394SAndrew Turner 	 *
58247e07394SAndrew Turner 	 * Device memory can be relocated by the guest (e.g. using PCI BARs)
58347e07394SAndrew Turner 	 * so those mappings are removed on a VM reset.
58447e07394SAndrew Turner 	 */
58547e07394SAndrew Turner 	if (!destroy) {
58647e07394SAndrew Turner 		for (i = 0; i < VM_MAX_MEMMAPS; i++) {
58747e07394SAndrew Turner 			mm = &vm->mem_maps[i];
58847e07394SAndrew Turner 			if (destroy || !sysmem_mapping(vm, mm))
58947e07394SAndrew Turner 				vm_free_memmap(vm, i);
59047e07394SAndrew Turner 		}
59147e07394SAndrew Turner 	}
59247e07394SAndrew Turner 
59347e07394SAndrew Turner 	if (destroy) {
59447e07394SAndrew Turner 		for (i = 0; i < VM_MAX_MEMSEGS; i++)
59547e07394SAndrew Turner 			vm_free_memseg(vm, i);
59647e07394SAndrew Turner 
59747e07394SAndrew Turner 		vmmops_vmspace_free(vm->vmspace);
59847e07394SAndrew Turner 		vm->vmspace = NULL;
59947e07394SAndrew Turner 
60047e07394SAndrew Turner 		for (i = 0; i < vm->maxcpus; i++)
60147e07394SAndrew Turner 			free(vm->vcpu[i], M_VMM);
60247e07394SAndrew Turner 		free(vm->vcpu, M_VMM);
60347e07394SAndrew Turner 		sx_destroy(&vm->vcpus_init_lock);
60447e07394SAndrew Turner 		sx_destroy(&vm->mem_segs_lock);
60547e07394SAndrew Turner 	}
60647e07394SAndrew Turner }
60747e07394SAndrew Turner 
60847e07394SAndrew Turner void
60947e07394SAndrew Turner vm_destroy(struct vm *vm)
61047e07394SAndrew Turner {
61147e07394SAndrew Turner 	vm_cleanup(vm, true);
61247e07394SAndrew Turner 	free(vm, M_VMM);
61347e07394SAndrew Turner }
61447e07394SAndrew Turner 
61547e07394SAndrew Turner int
61647e07394SAndrew Turner vm_reinit(struct vm *vm)
61747e07394SAndrew Turner {
61847e07394SAndrew Turner 	int error;
61947e07394SAndrew Turner 
62047e07394SAndrew Turner 	/*
62147e07394SAndrew Turner 	 * A virtual machine can be reset only if all vcpus are suspended.
62247e07394SAndrew Turner 	 */
62347e07394SAndrew Turner 	if (CPU_CMP(&vm->suspended_cpus, &vm->active_cpus) == 0) {
62447e07394SAndrew Turner 		vm_cleanup(vm, false);
62547e07394SAndrew Turner 		vm_init(vm, false);
62647e07394SAndrew Turner 		error = 0;
62747e07394SAndrew Turner 	} else {
62847e07394SAndrew Turner 		error = EBUSY;
62947e07394SAndrew Turner 	}
63047e07394SAndrew Turner 
63147e07394SAndrew Turner 	return (error);
63247e07394SAndrew Turner }
63347e07394SAndrew Turner 
63447e07394SAndrew Turner const char *
63547e07394SAndrew Turner vm_name(struct vm *vm)
63647e07394SAndrew Turner {
63747e07394SAndrew Turner 	return (vm->name);
63847e07394SAndrew Turner }
63947e07394SAndrew Turner 
64047e07394SAndrew Turner void
64147e07394SAndrew Turner vm_slock_memsegs(struct vm *vm)
64247e07394SAndrew Turner {
64347e07394SAndrew Turner 	sx_slock(&vm->mem_segs_lock);
64447e07394SAndrew Turner }
64547e07394SAndrew Turner 
64647e07394SAndrew Turner void
64747e07394SAndrew Turner vm_xlock_memsegs(struct vm *vm)
64847e07394SAndrew Turner {
64947e07394SAndrew Turner 	sx_xlock(&vm->mem_segs_lock);
65047e07394SAndrew Turner }
65147e07394SAndrew Turner 
65247e07394SAndrew Turner void
65347e07394SAndrew Turner vm_unlock_memsegs(struct vm *vm)
65447e07394SAndrew Turner {
65547e07394SAndrew Turner 	sx_unlock(&vm->mem_segs_lock);
65647e07394SAndrew Turner }
65747e07394SAndrew Turner 
65847e07394SAndrew Turner /*
65947e07394SAndrew Turner  * Return 'true' if 'gpa' is allocated in the guest address space.
66047e07394SAndrew Turner  *
66147e07394SAndrew Turner  * This function is called in the context of a running vcpu which acts as
66247e07394SAndrew Turner  * an implicit lock on 'vm->mem_maps[]'.
66347e07394SAndrew Turner  */
66447e07394SAndrew Turner bool
66547e07394SAndrew Turner vm_mem_allocated(struct vcpu *vcpu, vm_paddr_t gpa)
66647e07394SAndrew Turner {
66747e07394SAndrew Turner 	struct vm *vm = vcpu->vm;
66847e07394SAndrew Turner 	struct mem_map *mm;
66947e07394SAndrew Turner 	int i;
67047e07394SAndrew Turner 
67147e07394SAndrew Turner #ifdef INVARIANTS
67247e07394SAndrew Turner 	int hostcpu, state;
67347e07394SAndrew Turner 	state = vcpu_get_state(vcpu, &hostcpu);
67447e07394SAndrew Turner 	KASSERT(state == VCPU_RUNNING && hostcpu == curcpu,
67547e07394SAndrew Turner 	    ("%s: invalid vcpu state %d/%d", __func__, state, hostcpu));
67647e07394SAndrew Turner #endif
67747e07394SAndrew Turner 
67847e07394SAndrew Turner 	for (i = 0; i < VM_MAX_MEMMAPS; i++) {
67947e07394SAndrew Turner 		mm = &vm->mem_maps[i];
68047e07394SAndrew Turner 		if (mm->len != 0 && gpa >= mm->gpa && gpa < mm->gpa + mm->len)
68147e07394SAndrew Turner 			return (true);		/* 'gpa' is sysmem or devmem */
68247e07394SAndrew Turner 	}
68347e07394SAndrew Turner 
68447e07394SAndrew Turner 	return (false);
68547e07394SAndrew Turner }
68647e07394SAndrew Turner 
68747e07394SAndrew Turner int
68847e07394SAndrew Turner vm_alloc_memseg(struct vm *vm, int ident, size_t len, bool sysmem)
68947e07394SAndrew Turner {
69047e07394SAndrew Turner 	struct mem_seg *seg;
69147e07394SAndrew Turner 	vm_object_t obj;
69247e07394SAndrew Turner 
69347e07394SAndrew Turner 	sx_assert(&vm->mem_segs_lock, SX_XLOCKED);
69447e07394SAndrew Turner 
69547e07394SAndrew Turner 	if (ident < 0 || ident >= VM_MAX_MEMSEGS)
69647e07394SAndrew Turner 		return (EINVAL);
69747e07394SAndrew Turner 
69847e07394SAndrew Turner 	if (len == 0 || (len & PAGE_MASK))
69947e07394SAndrew Turner 		return (EINVAL);
70047e07394SAndrew Turner 
70147e07394SAndrew Turner 	seg = &vm->mem_segs[ident];
70247e07394SAndrew Turner 	if (seg->object != NULL) {
70347e07394SAndrew Turner 		if (seg->len == len && seg->sysmem == sysmem)
70447e07394SAndrew Turner 			return (EEXIST);
70547e07394SAndrew Turner 		else
70647e07394SAndrew Turner 			return (EINVAL);
70747e07394SAndrew Turner 	}
70847e07394SAndrew Turner 
70947e07394SAndrew Turner 	obj = vm_object_allocate(OBJT_DEFAULT, len >> PAGE_SHIFT);
71047e07394SAndrew Turner 	if (obj == NULL)
71147e07394SAndrew Turner 		return (ENOMEM);
71247e07394SAndrew Turner 
71347e07394SAndrew Turner 	seg->len = len;
71447e07394SAndrew Turner 	seg->object = obj;
71547e07394SAndrew Turner 	seg->sysmem = sysmem;
71647e07394SAndrew Turner 	return (0);
71747e07394SAndrew Turner }
71847e07394SAndrew Turner 
71947e07394SAndrew Turner int
72047e07394SAndrew Turner vm_get_memseg(struct vm *vm, int ident, size_t *len, bool *sysmem,
72147e07394SAndrew Turner     vm_object_t *objptr)
72247e07394SAndrew Turner {
72347e07394SAndrew Turner 	struct mem_seg *seg;
72447e07394SAndrew Turner 
72547e07394SAndrew Turner 	sx_assert(&vm->mem_segs_lock, SX_LOCKED);
72647e07394SAndrew Turner 
72747e07394SAndrew Turner 	if (ident < 0 || ident >= VM_MAX_MEMSEGS)
72847e07394SAndrew Turner 		return (EINVAL);
72947e07394SAndrew Turner 
73047e07394SAndrew Turner 	seg = &vm->mem_segs[ident];
73147e07394SAndrew Turner 	if (len)
73247e07394SAndrew Turner 		*len = seg->len;
73347e07394SAndrew Turner 	if (sysmem)
73447e07394SAndrew Turner 		*sysmem = seg->sysmem;
73547e07394SAndrew Turner 	if (objptr)
73647e07394SAndrew Turner 		*objptr = seg->object;
73747e07394SAndrew Turner 	return (0);
73847e07394SAndrew Turner }
73947e07394SAndrew Turner 
74047e07394SAndrew Turner void
74147e07394SAndrew Turner vm_free_memseg(struct vm *vm, int ident)
74247e07394SAndrew Turner {
74347e07394SAndrew Turner 	struct mem_seg *seg;
74447e07394SAndrew Turner 
74547e07394SAndrew Turner 	KASSERT(ident >= 0 && ident < VM_MAX_MEMSEGS,
74647e07394SAndrew Turner 	    ("%s: invalid memseg ident %d", __func__, ident));
74747e07394SAndrew Turner 
74847e07394SAndrew Turner 	seg = &vm->mem_segs[ident];
74947e07394SAndrew Turner 	if (seg->object != NULL) {
75047e07394SAndrew Turner 		vm_object_deallocate(seg->object);
75147e07394SAndrew Turner 		bzero(seg, sizeof(struct mem_seg));
75247e07394SAndrew Turner 	}
75347e07394SAndrew Turner }
75447e07394SAndrew Turner 
75547e07394SAndrew Turner int
75647e07394SAndrew Turner vm_mmap_memseg(struct vm *vm, vm_paddr_t gpa, int segid, vm_ooffset_t first,
75747e07394SAndrew Turner     size_t len, int prot, int flags)
75847e07394SAndrew Turner {
75947e07394SAndrew Turner 	struct mem_seg *seg;
76047e07394SAndrew Turner 	struct mem_map *m, *map;
76147e07394SAndrew Turner 	vm_ooffset_t last;
76247e07394SAndrew Turner 	int i, error;
76347e07394SAndrew Turner 
76447e07394SAndrew Turner 	if (prot == 0 || (prot & ~(VM_PROT_ALL)) != 0)
76547e07394SAndrew Turner 		return (EINVAL);
76647e07394SAndrew Turner 
76747e07394SAndrew Turner 	if (flags & ~VM_MEMMAP_F_WIRED)
76847e07394SAndrew Turner 		return (EINVAL);
76947e07394SAndrew Turner 
77047e07394SAndrew Turner 	if (segid < 0 || segid >= VM_MAX_MEMSEGS)
77147e07394SAndrew Turner 		return (EINVAL);
77247e07394SAndrew Turner 
77347e07394SAndrew Turner 	seg = &vm->mem_segs[segid];
77447e07394SAndrew Turner 	if (seg->object == NULL)
77547e07394SAndrew Turner 		return (EINVAL);
77647e07394SAndrew Turner 
77747e07394SAndrew Turner 	last = first + len;
77847e07394SAndrew Turner 	if (first < 0 || first >= last || last > seg->len)
77947e07394SAndrew Turner 		return (EINVAL);
78047e07394SAndrew Turner 
78147e07394SAndrew Turner 	if ((gpa | first | last) & PAGE_MASK)
78247e07394SAndrew Turner 		return (EINVAL);
78347e07394SAndrew Turner 
78447e07394SAndrew Turner 	map = NULL;
78547e07394SAndrew Turner 	for (i = 0; i < VM_MAX_MEMMAPS; i++) {
78647e07394SAndrew Turner 		m = &vm->mem_maps[i];
78747e07394SAndrew Turner 		if (m->len == 0) {
78847e07394SAndrew Turner 			map = m;
78947e07394SAndrew Turner 			break;
79047e07394SAndrew Turner 		}
79147e07394SAndrew Turner 	}
79247e07394SAndrew Turner 
79347e07394SAndrew Turner 	if (map == NULL)
79447e07394SAndrew Turner 		return (ENOSPC);
79547e07394SAndrew Turner 
79647e07394SAndrew Turner 	error = vm_map_find(&vm->vmspace->vm_map, seg->object, first, &gpa,
79747e07394SAndrew Turner 	    len, 0, VMFS_NO_SPACE, prot, prot, 0);
79847e07394SAndrew Turner 	if (error != KERN_SUCCESS)
79947e07394SAndrew Turner 		return (EFAULT);
80047e07394SAndrew Turner 
80147e07394SAndrew Turner 	vm_object_reference(seg->object);
80247e07394SAndrew Turner 
80347e07394SAndrew Turner 	if (flags & VM_MEMMAP_F_WIRED) {
80447e07394SAndrew Turner 		error = vm_map_wire(&vm->vmspace->vm_map, gpa, gpa + len,
80547e07394SAndrew Turner 		    VM_MAP_WIRE_USER | VM_MAP_WIRE_NOHOLES);
80647e07394SAndrew Turner 		if (error != KERN_SUCCESS) {
80747e07394SAndrew Turner 			vm_map_remove(&vm->vmspace->vm_map, gpa, gpa + len);
80847e07394SAndrew Turner 			return (error == KERN_RESOURCE_SHORTAGE ? ENOMEM :
80947e07394SAndrew Turner 			    EFAULT);
81047e07394SAndrew Turner 		}
81147e07394SAndrew Turner 	}
81247e07394SAndrew Turner 
81347e07394SAndrew Turner 	map->gpa = gpa;
81447e07394SAndrew Turner 	map->len = len;
81547e07394SAndrew Turner 	map->segoff = first;
81647e07394SAndrew Turner 	map->segid = segid;
81747e07394SAndrew Turner 	map->prot = prot;
81847e07394SAndrew Turner 	map->flags = flags;
81947e07394SAndrew Turner 	return (0);
82047e07394SAndrew Turner }
82147e07394SAndrew Turner 
82247e07394SAndrew Turner int
82347e07394SAndrew Turner vm_munmap_memseg(struct vm *vm, vm_paddr_t gpa, size_t len)
82447e07394SAndrew Turner {
82547e07394SAndrew Turner 	struct mem_map *m;
82647e07394SAndrew Turner 	int i;
82747e07394SAndrew Turner 
82847e07394SAndrew Turner 	for (i = 0; i < VM_MAX_MEMMAPS; i++) {
82947e07394SAndrew Turner 		m = &vm->mem_maps[i];
83047e07394SAndrew Turner 		if (m->gpa == gpa && m->len == len) {
83147e07394SAndrew Turner 			vm_free_memmap(vm, i);
83247e07394SAndrew Turner 			return (0);
83347e07394SAndrew Turner 		}
83447e07394SAndrew Turner 	}
83547e07394SAndrew Turner 
83647e07394SAndrew Turner 	return (EINVAL);
83747e07394SAndrew Turner }
83847e07394SAndrew Turner 
83947e07394SAndrew Turner int
84047e07394SAndrew Turner vm_mmap_getnext(struct vm *vm, vm_paddr_t *gpa, int *segid,
84147e07394SAndrew Turner     vm_ooffset_t *segoff, size_t *len, int *prot, int *flags)
84247e07394SAndrew Turner {
84347e07394SAndrew Turner 	struct mem_map *mm, *mmnext;
84447e07394SAndrew Turner 	int i;
84547e07394SAndrew Turner 
84647e07394SAndrew Turner 	mmnext = NULL;
84747e07394SAndrew Turner 	for (i = 0; i < VM_MAX_MEMMAPS; i++) {
84847e07394SAndrew Turner 		mm = &vm->mem_maps[i];
84947e07394SAndrew Turner 		if (mm->len == 0 || mm->gpa < *gpa)
85047e07394SAndrew Turner 			continue;
85147e07394SAndrew Turner 		if (mmnext == NULL || mm->gpa < mmnext->gpa)
85247e07394SAndrew Turner 			mmnext = mm;
85347e07394SAndrew Turner 	}
85447e07394SAndrew Turner 
85547e07394SAndrew Turner 	if (mmnext != NULL) {
85647e07394SAndrew Turner 		*gpa = mmnext->gpa;
85747e07394SAndrew Turner 		if (segid)
85847e07394SAndrew Turner 			*segid = mmnext->segid;
85947e07394SAndrew Turner 		if (segoff)
86047e07394SAndrew Turner 			*segoff = mmnext->segoff;
86147e07394SAndrew Turner 		if (len)
86247e07394SAndrew Turner 			*len = mmnext->len;
86347e07394SAndrew Turner 		if (prot)
86447e07394SAndrew Turner 			*prot = mmnext->prot;
86547e07394SAndrew Turner 		if (flags)
86647e07394SAndrew Turner 			*flags = mmnext->flags;
86747e07394SAndrew Turner 		return (0);
86847e07394SAndrew Turner 	} else {
86947e07394SAndrew Turner 		return (ENOENT);
87047e07394SAndrew Turner 	}
87147e07394SAndrew Turner }
87247e07394SAndrew Turner 
87347e07394SAndrew Turner static void
87447e07394SAndrew Turner vm_free_memmap(struct vm *vm, int ident)
87547e07394SAndrew Turner {
87647e07394SAndrew Turner 	struct mem_map *mm;
87747e07394SAndrew Turner 	int error __diagused;
87847e07394SAndrew Turner 
87947e07394SAndrew Turner 	mm = &vm->mem_maps[ident];
88047e07394SAndrew Turner 	if (mm->len) {
88147e07394SAndrew Turner 		error = vm_map_remove(&vm->vmspace->vm_map, mm->gpa,
88247e07394SAndrew Turner 		    mm->gpa + mm->len);
88347e07394SAndrew Turner 		KASSERT(error == KERN_SUCCESS, ("%s: vm_map_remove error %d",
88447e07394SAndrew Turner 		    __func__, error));
88547e07394SAndrew Turner 		bzero(mm, sizeof(struct mem_map));
88647e07394SAndrew Turner 	}
88747e07394SAndrew Turner }
88847e07394SAndrew Turner 
88947e07394SAndrew Turner static __inline bool
89047e07394SAndrew Turner sysmem_mapping(struct vm *vm, struct mem_map *mm)
89147e07394SAndrew Turner {
89247e07394SAndrew Turner 
89347e07394SAndrew Turner 	if (mm->len != 0 && vm->mem_segs[mm->segid].sysmem)
89447e07394SAndrew Turner 		return (true);
89547e07394SAndrew Turner 	else
89647e07394SAndrew Turner 		return (false);
89747e07394SAndrew Turner }
89847e07394SAndrew Turner 
89947e07394SAndrew Turner vm_paddr_t
90047e07394SAndrew Turner vmm_sysmem_maxaddr(struct vm *vm)
90147e07394SAndrew Turner {
90247e07394SAndrew Turner 	struct mem_map *mm;
90347e07394SAndrew Turner 	vm_paddr_t maxaddr;
90447e07394SAndrew Turner 	int i;
90547e07394SAndrew Turner 
90647e07394SAndrew Turner 	maxaddr = 0;
90747e07394SAndrew Turner 	for (i = 0; i < VM_MAX_MEMMAPS; i++) {
90847e07394SAndrew Turner 		mm = &vm->mem_maps[i];
90947e07394SAndrew Turner 		if (sysmem_mapping(vm, mm)) {
91047e07394SAndrew Turner 			if (maxaddr < mm->gpa + mm->len)
91147e07394SAndrew Turner 				maxaddr = mm->gpa + mm->len;
91247e07394SAndrew Turner 		}
91347e07394SAndrew Turner 	}
91447e07394SAndrew Turner 	return (maxaddr);
91547e07394SAndrew Turner }
91647e07394SAndrew Turner 
91747e07394SAndrew Turner int
91847e07394SAndrew Turner vm_gla2gpa_nofault(struct vcpu *vcpu, struct vm_guest_paging *paging,
91947e07394SAndrew Turner     uint64_t gla, int prot, uint64_t *gpa, int *is_fault)
92047e07394SAndrew Turner {
92147e07394SAndrew Turner 
92247e07394SAndrew Turner 	vmmops_gla2gpa(vcpu->cookie, paging, gla, prot, gpa, is_fault);
92347e07394SAndrew Turner 	return (0);
92447e07394SAndrew Turner }
92547e07394SAndrew Turner 
92647e07394SAndrew Turner static int
92747e07394SAndrew Turner vmm_reg_raz(struct vcpu *vcpu, uint64_t *rval, void *arg)
92847e07394SAndrew Turner {
92947e07394SAndrew Turner 	*rval = 0;
93047e07394SAndrew Turner 	return (0);
93147e07394SAndrew Turner }
93247e07394SAndrew Turner 
93347e07394SAndrew Turner static int
93447e07394SAndrew Turner vmm_reg_read_arg(struct vcpu *vcpu, uint64_t *rval, void *arg)
93547e07394SAndrew Turner {
93647e07394SAndrew Turner 	*rval = *(uint64_t *)arg;
93747e07394SAndrew Turner 	return (0);
93847e07394SAndrew Turner }
93947e07394SAndrew Turner 
94047e07394SAndrew Turner static int
94147e07394SAndrew Turner vmm_reg_wi(struct vcpu *vcpu, uint64_t wval, void *arg)
94247e07394SAndrew Turner {
94347e07394SAndrew Turner 	return (0);
94447e07394SAndrew Turner }
94547e07394SAndrew Turner 
94647e07394SAndrew Turner static const struct vmm_special_reg vmm_special_regs[] = {
94747e07394SAndrew Turner #define	SPECIAL_REG(_reg, _read, _write)				\
94847e07394SAndrew Turner 	{								\
94947e07394SAndrew Turner 		.esr_iss = ((_reg ## _op0) << ISS_MSR_OP0_SHIFT) |	\
95047e07394SAndrew Turner 		    ((_reg ## _op1) << ISS_MSR_OP1_SHIFT) |		\
95147e07394SAndrew Turner 		    ((_reg ## _CRn) << ISS_MSR_CRn_SHIFT) |		\
95247e07394SAndrew Turner 		    ((_reg ## _CRm) << ISS_MSR_CRm_SHIFT) |		\
95347e07394SAndrew Turner 		    ((_reg ## _op2) << ISS_MSR_OP2_SHIFT),		\
95447e07394SAndrew Turner 		.esr_mask = ISS_MSR_REG_MASK,				\
95547e07394SAndrew Turner 		.reg_read = (_read),					\
95647e07394SAndrew Turner 		.reg_write = (_write),					\
95747e07394SAndrew Turner 		.arg = NULL,						\
95847e07394SAndrew Turner 	}
95947e07394SAndrew Turner #define	ID_SPECIAL_REG(_reg, _name)					\
96047e07394SAndrew Turner 	{								\
96147e07394SAndrew Turner 		.esr_iss = ((_reg ## _op0) << ISS_MSR_OP0_SHIFT) |	\
96247e07394SAndrew Turner 		    ((_reg ## _op1) << ISS_MSR_OP1_SHIFT) |		\
96347e07394SAndrew Turner 		    ((_reg ## _CRn) << ISS_MSR_CRn_SHIFT) |		\
96447e07394SAndrew Turner 		    ((_reg ## _CRm) << ISS_MSR_CRm_SHIFT) |		\
96547e07394SAndrew Turner 		    ((_reg ## _op2) << ISS_MSR_OP2_SHIFT),		\
96647e07394SAndrew Turner 		.esr_mask = ISS_MSR_REG_MASK,				\
96747e07394SAndrew Turner 		.reg_read = vmm_reg_read_arg,				\
96847e07394SAndrew Turner 		.reg_write = vmm_reg_wi,				\
96947e07394SAndrew Turner 		.arg = &(vmm_arch_regs._name),				\
97047e07394SAndrew Turner 	}
97147e07394SAndrew Turner 
97247e07394SAndrew Turner 	/* ID registers */
97347e07394SAndrew Turner 	ID_SPECIAL_REG(ID_AA64PFR0_EL1, id_aa64pfr0),
97447e07394SAndrew Turner 	ID_SPECIAL_REG(ID_AA64DFR0_EL1, id_aa64dfr0),
97547e07394SAndrew Turner 	ID_SPECIAL_REG(ID_AA64ISAR0_EL1, id_aa64isar0),
97647e07394SAndrew Turner 	ID_SPECIAL_REG(ID_AA64MMFR0_EL1, id_aa64mmfr0),
97747e07394SAndrew Turner 	ID_SPECIAL_REG(ID_AA64MMFR1_EL1, id_aa64mmfr1),
97847e07394SAndrew Turner 
97947e07394SAndrew Turner 	/*
98047e07394SAndrew Turner 	 * All other ID registers are read as zero.
98147e07394SAndrew Turner 	 * They are all in the op0=3, op1=0, CRn=0, CRm={0..7} space.
98247e07394SAndrew Turner 	 */
98347e07394SAndrew Turner 	{
98447e07394SAndrew Turner 		.esr_iss = (3 << ISS_MSR_OP0_SHIFT) |
98547e07394SAndrew Turner 		    (0 << ISS_MSR_OP1_SHIFT) |
98647e07394SAndrew Turner 		    (0 << ISS_MSR_CRn_SHIFT) |
98747e07394SAndrew Turner 		    (0 << ISS_MSR_CRm_SHIFT),
98847e07394SAndrew Turner 		.esr_mask = ISS_MSR_OP0_MASK | ISS_MSR_OP1_MASK |
98947e07394SAndrew Turner 		    ISS_MSR_CRn_MASK | (0x8 << ISS_MSR_CRm_SHIFT),
99047e07394SAndrew Turner 		.reg_read = vmm_reg_raz,
99147e07394SAndrew Turner 		.reg_write = vmm_reg_wi,
99247e07394SAndrew Turner 		.arg = NULL,
99347e07394SAndrew Turner 	},
99447e07394SAndrew Turner 
99547e07394SAndrew Turner 	/* Counter physical registers */
99647e07394SAndrew Turner 	SPECIAL_REG(CNTP_CTL_EL0, vtimer_phys_ctl_read, vtimer_phys_ctl_write),
99747e07394SAndrew Turner 	SPECIAL_REG(CNTP_CVAL_EL0, vtimer_phys_cval_read,
99847e07394SAndrew Turner 	    vtimer_phys_cval_write),
99947e07394SAndrew Turner 	SPECIAL_REG(CNTP_TVAL_EL0, vtimer_phys_tval_read,
100047e07394SAndrew Turner 	    vtimer_phys_tval_write),
100147e07394SAndrew Turner 	SPECIAL_REG(CNTPCT_EL0, vtimer_phys_cnt_read, vtimer_phys_cnt_write),
100247e07394SAndrew Turner #undef SPECIAL_REG
100347e07394SAndrew Turner };
100447e07394SAndrew Turner 
100547e07394SAndrew Turner void
100647e07394SAndrew Turner vm_register_reg_handler(struct vm *vm, uint64_t iss, uint64_t mask,
100747e07394SAndrew Turner     reg_read_t reg_read, reg_write_t reg_write, void *arg)
100847e07394SAndrew Turner {
100947e07394SAndrew Turner 	int i;
101047e07394SAndrew Turner 
101147e07394SAndrew Turner 	for (i = 0; i < nitems(vm->special_reg); i++) {
101247e07394SAndrew Turner 		if (vm->special_reg[i].esr_iss == 0 &&
101347e07394SAndrew Turner 		    vm->special_reg[i].esr_mask == 0) {
101447e07394SAndrew Turner 			vm->special_reg[i].esr_iss = iss;
101547e07394SAndrew Turner 			vm->special_reg[i].esr_mask = mask;
101647e07394SAndrew Turner 			vm->special_reg[i].reg_read = reg_read;
101747e07394SAndrew Turner 			vm->special_reg[i].reg_write = reg_write;
101847e07394SAndrew Turner 			vm->special_reg[i].arg = arg;
101947e07394SAndrew Turner 			return;
102047e07394SAndrew Turner 		}
102147e07394SAndrew Turner 	}
102247e07394SAndrew Turner 
102347e07394SAndrew Turner 	panic("%s: No free special register slot", __func__);
102447e07394SAndrew Turner }
102547e07394SAndrew Turner 
102647e07394SAndrew Turner void
102747e07394SAndrew Turner vm_deregister_reg_handler(struct vm *vm, uint64_t iss, uint64_t mask)
102847e07394SAndrew Turner {
102947e07394SAndrew Turner 	int i;
103047e07394SAndrew Turner 
103147e07394SAndrew Turner 	for (i = 0; i < nitems(vm->special_reg); i++) {
103247e07394SAndrew Turner 		if (vm->special_reg[i].esr_iss == iss &&
103347e07394SAndrew Turner 		    vm->special_reg[i].esr_mask == mask) {
103447e07394SAndrew Turner 			memset(&vm->special_reg[i], 0,
103547e07394SAndrew Turner 			    sizeof(vm->special_reg[i]));
103647e07394SAndrew Turner 			return;
103747e07394SAndrew Turner 		}
103847e07394SAndrew Turner 	}
103947e07394SAndrew Turner 
104047e07394SAndrew Turner 	panic("%s: Invalid special register: iss %lx mask %lx", __func__, iss,
104147e07394SAndrew Turner 	    mask);
104247e07394SAndrew Turner }
104347e07394SAndrew Turner 
104447e07394SAndrew Turner static int
104547e07394SAndrew Turner vm_handle_reg_emul(struct vcpu *vcpu, bool *retu)
104647e07394SAndrew Turner {
104747e07394SAndrew Turner 	struct vm *vm;
104847e07394SAndrew Turner 	struct vm_exit *vme;
104947e07394SAndrew Turner 	struct vre *vre;
105047e07394SAndrew Turner 	int i, rv;
105147e07394SAndrew Turner 
105247e07394SAndrew Turner 	vm = vcpu->vm;
105347e07394SAndrew Turner 	vme = &vcpu->exitinfo;
105447e07394SAndrew Turner 	vre = &vme->u.reg_emul.vre;
105547e07394SAndrew Turner 
105647e07394SAndrew Turner 	for (i = 0; i < nitems(vm->special_reg); i++) {
105747e07394SAndrew Turner 		if (vm->special_reg[i].esr_iss == 0 &&
105847e07394SAndrew Turner 		    vm->special_reg[i].esr_mask == 0)
105947e07394SAndrew Turner 			continue;
106047e07394SAndrew Turner 
106147e07394SAndrew Turner 		if ((vre->inst_syndrome & vm->special_reg[i].esr_mask) ==
106247e07394SAndrew Turner 		    vm->special_reg[i].esr_iss) {
106347e07394SAndrew Turner 			rv = vmm_emulate_register(vcpu, vre,
106447e07394SAndrew Turner 			    vm->special_reg[i].reg_read,
106547e07394SAndrew Turner 			    vm->special_reg[i].reg_write,
106647e07394SAndrew Turner 			    vm->special_reg[i].arg);
106747e07394SAndrew Turner 			if (rv == 0) {
106847e07394SAndrew Turner 				*retu = false;
106947e07394SAndrew Turner 			}
107047e07394SAndrew Turner 			return (rv);
107147e07394SAndrew Turner 		}
107247e07394SAndrew Turner 	}
107347e07394SAndrew Turner 	for (i = 0; i < nitems(vmm_special_regs); i++) {
107447e07394SAndrew Turner 		if ((vre->inst_syndrome & vmm_special_regs[i].esr_mask) ==
107547e07394SAndrew Turner 		    vmm_special_regs[i].esr_iss) {
107647e07394SAndrew Turner 			rv = vmm_emulate_register(vcpu, vre,
107747e07394SAndrew Turner 			    vmm_special_regs[i].reg_read,
107847e07394SAndrew Turner 			    vmm_special_regs[i].reg_write,
107947e07394SAndrew Turner 			    vmm_special_regs[i].arg);
108047e07394SAndrew Turner 			if (rv == 0) {
108147e07394SAndrew Turner 				*retu = false;
108247e07394SAndrew Turner 			}
108347e07394SAndrew Turner 			return (rv);
108447e07394SAndrew Turner 		}
108547e07394SAndrew Turner 	}
108647e07394SAndrew Turner 
108747e07394SAndrew Turner 
108847e07394SAndrew Turner 	*retu = true;
108947e07394SAndrew Turner 	return (0);
109047e07394SAndrew Turner }
109147e07394SAndrew Turner 
109247e07394SAndrew Turner void
109347e07394SAndrew Turner vm_register_inst_handler(struct vm *vm, uint64_t start, uint64_t size,
109447e07394SAndrew Turner     mem_region_read_t mmio_read, mem_region_write_t mmio_write)
109547e07394SAndrew Turner {
109647e07394SAndrew Turner 	int i;
109747e07394SAndrew Turner 
109847e07394SAndrew Turner 	for (i = 0; i < nitems(vm->mmio_region); i++) {
109947e07394SAndrew Turner 		if (vm->mmio_region[i].start == 0 &&
110047e07394SAndrew Turner 		    vm->mmio_region[i].end == 0) {
110147e07394SAndrew Turner 			vm->mmio_region[i].start = start;
110247e07394SAndrew Turner 			vm->mmio_region[i].end = start + size;
110347e07394SAndrew Turner 			vm->mmio_region[i].read = mmio_read;
110447e07394SAndrew Turner 			vm->mmio_region[i].write = mmio_write;
110547e07394SAndrew Turner 			return;
110647e07394SAndrew Turner 		}
110747e07394SAndrew Turner 	}
110847e07394SAndrew Turner 
110947e07394SAndrew Turner 	panic("%s: No free MMIO region", __func__);
111047e07394SAndrew Turner }
111147e07394SAndrew Turner 
111247e07394SAndrew Turner void
111347e07394SAndrew Turner vm_deregister_inst_handler(struct vm *vm, uint64_t start, uint64_t size)
111447e07394SAndrew Turner {
111547e07394SAndrew Turner 	int i;
111647e07394SAndrew Turner 
111747e07394SAndrew Turner 	for (i = 0; i < nitems(vm->mmio_region); i++) {
111847e07394SAndrew Turner 		if (vm->mmio_region[i].start == start &&
111947e07394SAndrew Turner 		    vm->mmio_region[i].end == start + size) {
112047e07394SAndrew Turner 			memset(&vm->mmio_region[i], 0,
112147e07394SAndrew Turner 			    sizeof(vm->mmio_region[i]));
112247e07394SAndrew Turner 			return;
112347e07394SAndrew Turner 		}
112447e07394SAndrew Turner 	}
112547e07394SAndrew Turner 
112647e07394SAndrew Turner 	panic("%s: Invalid MMIO region: %lx - %lx", __func__, start,
112747e07394SAndrew Turner 	    start + size);
112847e07394SAndrew Turner }
112947e07394SAndrew Turner 
113047e07394SAndrew Turner static int
113147e07394SAndrew Turner vm_handle_inst_emul(struct vcpu *vcpu, bool *retu)
113247e07394SAndrew Turner {
113347e07394SAndrew Turner 	struct vm *vm;
113447e07394SAndrew Turner 	struct vm_exit *vme;
113547e07394SAndrew Turner 	struct vie *vie;
113647e07394SAndrew Turner 	struct hyp *hyp;
113747e07394SAndrew Turner 	uint64_t fault_ipa;
113847e07394SAndrew Turner 	struct vm_guest_paging *paging;
113947e07394SAndrew Turner 	struct vmm_mmio_region *vmr;
114047e07394SAndrew Turner 	int error, i;
114147e07394SAndrew Turner 
114247e07394SAndrew Turner 	vm = vcpu->vm;
114347e07394SAndrew Turner 	hyp = vm->cookie;
114447e07394SAndrew Turner 	if (!hyp->vgic_attached)
114547e07394SAndrew Turner 		goto out_user;
114647e07394SAndrew Turner 
114747e07394SAndrew Turner 	vme = &vcpu->exitinfo;
114847e07394SAndrew Turner 	vie = &vme->u.inst_emul.vie;
114947e07394SAndrew Turner 	paging = &vme->u.inst_emul.paging;
115047e07394SAndrew Turner 
115147e07394SAndrew Turner 	fault_ipa = vme->u.inst_emul.gpa;
115247e07394SAndrew Turner 
115347e07394SAndrew Turner 	vmr = NULL;
115447e07394SAndrew Turner 	for (i = 0; i < nitems(vm->mmio_region); i++) {
115547e07394SAndrew Turner 		if (vm->mmio_region[i].start <= fault_ipa &&
115647e07394SAndrew Turner 		    vm->mmio_region[i].end > fault_ipa) {
115747e07394SAndrew Turner 			vmr = &vm->mmio_region[i];
115847e07394SAndrew Turner 			break;
115947e07394SAndrew Turner 		}
116047e07394SAndrew Turner 	}
116147e07394SAndrew Turner 	if (vmr == NULL)
116247e07394SAndrew Turner 		goto out_user;
116347e07394SAndrew Turner 
116447e07394SAndrew Turner 	error = vmm_emulate_instruction(vcpu, fault_ipa, vie, paging,
116547e07394SAndrew Turner 	    vmr->read, vmr->write, retu);
116647e07394SAndrew Turner 	return (error);
116747e07394SAndrew Turner 
116847e07394SAndrew Turner out_user:
116947e07394SAndrew Turner 	*retu = true;
117047e07394SAndrew Turner 	return (0);
117147e07394SAndrew Turner }
117247e07394SAndrew Turner 
117347e07394SAndrew Turner int
117447e07394SAndrew Turner vm_suspend(struct vm *vm, enum vm_suspend_how how)
117547e07394SAndrew Turner {
117647e07394SAndrew Turner 	int i;
117747e07394SAndrew Turner 
117847e07394SAndrew Turner 	if (how <= VM_SUSPEND_NONE || how >= VM_SUSPEND_LAST)
117947e07394SAndrew Turner 		return (EINVAL);
118047e07394SAndrew Turner 
118147e07394SAndrew Turner 	if (atomic_cmpset_int(&vm->suspend, 0, how) == 0) {
118247e07394SAndrew Turner 		VM_CTR2(vm, "virtual machine already suspended %d/%d",
118347e07394SAndrew Turner 		    vm->suspend, how);
118447e07394SAndrew Turner 		return (EALREADY);
118547e07394SAndrew Turner 	}
118647e07394SAndrew Turner 
118747e07394SAndrew Turner 	VM_CTR1(vm, "virtual machine successfully suspended %d", how);
118847e07394SAndrew Turner 
118947e07394SAndrew Turner 	/*
119047e07394SAndrew Turner 	 * Notify all active vcpus that they are now suspended.
119147e07394SAndrew Turner 	 */
119247e07394SAndrew Turner 	for (i = 0; i < vm->maxcpus; i++) {
119347e07394SAndrew Turner 		if (CPU_ISSET(i, &vm->active_cpus))
119447e07394SAndrew Turner 			vcpu_notify_event(vm_vcpu(vm, i));
119547e07394SAndrew Turner 	}
119647e07394SAndrew Turner 
119747e07394SAndrew Turner 	return (0);
119847e07394SAndrew Turner }
119947e07394SAndrew Turner 
120047e07394SAndrew Turner void
120147e07394SAndrew Turner vm_exit_suspended(struct vcpu *vcpu, uint64_t pc)
120247e07394SAndrew Turner {
120347e07394SAndrew Turner 	struct vm *vm = vcpu->vm;
120447e07394SAndrew Turner 	struct vm_exit *vmexit;
120547e07394SAndrew Turner 
120647e07394SAndrew Turner 	KASSERT(vm->suspend > VM_SUSPEND_NONE && vm->suspend < VM_SUSPEND_LAST,
120747e07394SAndrew Turner 	    ("vm_exit_suspended: invalid suspend type %d", vm->suspend));
120847e07394SAndrew Turner 
120947e07394SAndrew Turner 	vmexit = vm_exitinfo(vcpu);
121047e07394SAndrew Turner 	vmexit->pc = pc;
121147e07394SAndrew Turner 	vmexit->inst_length = 4;
121247e07394SAndrew Turner 	vmexit->exitcode = VM_EXITCODE_SUSPENDED;
121347e07394SAndrew Turner 	vmexit->u.suspended.how = vm->suspend;
121447e07394SAndrew Turner }
121547e07394SAndrew Turner 
121647e07394SAndrew Turner void
121747e07394SAndrew Turner vm_exit_debug(struct vcpu *vcpu, uint64_t pc)
121847e07394SAndrew Turner {
121947e07394SAndrew Turner 	struct vm_exit *vmexit;
122047e07394SAndrew Turner 
122147e07394SAndrew Turner 	vmexit = vm_exitinfo(vcpu);
122247e07394SAndrew Turner 	vmexit->pc = pc;
122347e07394SAndrew Turner 	vmexit->inst_length = 4;
122447e07394SAndrew Turner 	vmexit->exitcode = VM_EXITCODE_DEBUG;
122547e07394SAndrew Turner }
122647e07394SAndrew Turner 
122747e07394SAndrew Turner int
122847e07394SAndrew Turner vm_activate_cpu(struct vcpu *vcpu)
122947e07394SAndrew Turner {
123047e07394SAndrew Turner 	struct vm *vm = vcpu->vm;
123147e07394SAndrew Turner 
123247e07394SAndrew Turner 	if (CPU_ISSET(vcpu->vcpuid, &vm->active_cpus))
123347e07394SAndrew Turner 		return (EBUSY);
123447e07394SAndrew Turner 
123547e07394SAndrew Turner 	CPU_SET_ATOMIC(vcpu->vcpuid, &vm->active_cpus);
123647e07394SAndrew Turner 	return (0);
123747e07394SAndrew Turner 
123847e07394SAndrew Turner }
123947e07394SAndrew Turner 
124047e07394SAndrew Turner int
124147e07394SAndrew Turner vm_suspend_cpu(struct vm *vm, struct vcpu *vcpu)
124247e07394SAndrew Turner {
124347e07394SAndrew Turner 	if (vcpu == NULL) {
124447e07394SAndrew Turner 		vm->debug_cpus = vm->active_cpus;
124547e07394SAndrew Turner 		for (int i = 0; i < vm->maxcpus; i++) {
124647e07394SAndrew Turner 			if (CPU_ISSET(i, &vm->active_cpus))
124747e07394SAndrew Turner 				vcpu_notify_event(vm_vcpu(vm, i));
124847e07394SAndrew Turner 		}
124947e07394SAndrew Turner 	} else {
125047e07394SAndrew Turner 		if (!CPU_ISSET(vcpu->vcpuid, &vm->active_cpus))
125147e07394SAndrew Turner 			return (EINVAL);
125247e07394SAndrew Turner 
125347e07394SAndrew Turner 		CPU_SET_ATOMIC(vcpu->vcpuid, &vm->debug_cpus);
125447e07394SAndrew Turner 		vcpu_notify_event(vcpu);
125547e07394SAndrew Turner 	}
125647e07394SAndrew Turner 	return (0);
125747e07394SAndrew Turner }
125847e07394SAndrew Turner 
125947e07394SAndrew Turner int
126047e07394SAndrew Turner vm_resume_cpu(struct vm *vm, struct vcpu *vcpu)
126147e07394SAndrew Turner {
126247e07394SAndrew Turner 
126347e07394SAndrew Turner 	if (vcpu == NULL) {
126447e07394SAndrew Turner 		CPU_ZERO(&vm->debug_cpus);
126547e07394SAndrew Turner 	} else {
126647e07394SAndrew Turner 		if (!CPU_ISSET(vcpu->vcpuid, &vm->debug_cpus))
126747e07394SAndrew Turner 			return (EINVAL);
126847e07394SAndrew Turner 
126947e07394SAndrew Turner 		CPU_CLR_ATOMIC(vcpu->vcpuid, &vm->debug_cpus);
127047e07394SAndrew Turner 	}
127147e07394SAndrew Turner 	return (0);
127247e07394SAndrew Turner }
127347e07394SAndrew Turner 
127447e07394SAndrew Turner int
127547e07394SAndrew Turner vcpu_debugged(struct vcpu *vcpu)
127647e07394SAndrew Turner {
127747e07394SAndrew Turner 
127847e07394SAndrew Turner 	return (CPU_ISSET(vcpu->vcpuid, &vcpu->vm->debug_cpus));
127947e07394SAndrew Turner }
128047e07394SAndrew Turner 
128147e07394SAndrew Turner cpuset_t
128247e07394SAndrew Turner vm_active_cpus(struct vm *vm)
128347e07394SAndrew Turner {
128447e07394SAndrew Turner 
128547e07394SAndrew Turner 	return (vm->active_cpus);
128647e07394SAndrew Turner }
128747e07394SAndrew Turner 
128847e07394SAndrew Turner cpuset_t
128947e07394SAndrew Turner vm_debug_cpus(struct vm *vm)
129047e07394SAndrew Turner {
129147e07394SAndrew Turner 
129247e07394SAndrew Turner 	return (vm->debug_cpus);
129347e07394SAndrew Turner }
129447e07394SAndrew Turner 
129547e07394SAndrew Turner cpuset_t
129647e07394SAndrew Turner vm_suspended_cpus(struct vm *vm)
129747e07394SAndrew Turner {
129847e07394SAndrew Turner 
129947e07394SAndrew Turner 	return (vm->suspended_cpus);
130047e07394SAndrew Turner }
130147e07394SAndrew Turner 
130247e07394SAndrew Turner 
130347e07394SAndrew Turner void *
130447e07394SAndrew Turner vcpu_stats(struct vcpu *vcpu)
130547e07394SAndrew Turner {
130647e07394SAndrew Turner 
130747e07394SAndrew Turner 	return (vcpu->stats);
130847e07394SAndrew Turner }
130947e07394SAndrew Turner 
131047e07394SAndrew Turner /*
131147e07394SAndrew Turner  * This function is called to ensure that a vcpu "sees" a pending event
131247e07394SAndrew Turner  * as soon as possible:
131347e07394SAndrew Turner  * - If the vcpu thread is sleeping then it is woken up.
131447e07394SAndrew Turner  * - If the vcpu is running on a different host_cpu then an IPI will be directed
131547e07394SAndrew Turner  *   to the host_cpu to cause the vcpu to trap into the hypervisor.
131647e07394SAndrew Turner  */
131747e07394SAndrew Turner static void
131847e07394SAndrew Turner vcpu_notify_event_locked(struct vcpu *vcpu)
131947e07394SAndrew Turner {
132047e07394SAndrew Turner 	int hostcpu;
132147e07394SAndrew Turner 
132247e07394SAndrew Turner 	hostcpu = vcpu->hostcpu;
132347e07394SAndrew Turner 	if (vcpu->state == VCPU_RUNNING) {
132447e07394SAndrew Turner 		KASSERT(hostcpu != NOCPU, ("vcpu running on invalid hostcpu"));
132547e07394SAndrew Turner 		if (hostcpu != curcpu) {
132647e07394SAndrew Turner 			ipi_cpu(hostcpu, vmm_ipinum);
132747e07394SAndrew Turner 		} else {
132847e07394SAndrew Turner 			/*
132947e07394SAndrew Turner 			 * If the 'vcpu' is running on 'curcpu' then it must
133047e07394SAndrew Turner 			 * be sending a notification to itself (e.g. SELF_IPI).
133147e07394SAndrew Turner 			 * The pending event will be picked up when the vcpu
133247e07394SAndrew Turner 			 * transitions back to guest context.
133347e07394SAndrew Turner 			 */
133447e07394SAndrew Turner 		}
133547e07394SAndrew Turner 	} else {
133647e07394SAndrew Turner 		KASSERT(hostcpu == NOCPU, ("vcpu state %d not consistent "
133747e07394SAndrew Turner 		    "with hostcpu %d", vcpu->state, hostcpu));
133847e07394SAndrew Turner 		if (vcpu->state == VCPU_SLEEPING)
133947e07394SAndrew Turner 			wakeup_one(vcpu);
134047e07394SAndrew Turner 	}
134147e07394SAndrew Turner }
134247e07394SAndrew Turner 
134347e07394SAndrew Turner void
134447e07394SAndrew Turner vcpu_notify_event(struct vcpu *vcpu)
134547e07394SAndrew Turner {
134647e07394SAndrew Turner 	vcpu_lock(vcpu);
134747e07394SAndrew Turner 	vcpu_notify_event_locked(vcpu);
134847e07394SAndrew Turner 	vcpu_unlock(vcpu);
134947e07394SAndrew Turner }
135047e07394SAndrew Turner 
135147e07394SAndrew Turner static void
135247e07394SAndrew Turner restore_guest_fpustate(struct vcpu *vcpu)
135347e07394SAndrew Turner {
135447e07394SAndrew Turner 
135547e07394SAndrew Turner 	/* flush host state to the pcb */
135647e07394SAndrew Turner 	vfp_save_state(curthread, curthread->td_pcb);
135747e07394SAndrew Turner 	/* Ensure the VFP state will be re-loaded when exiting the guest */
135847e07394SAndrew Turner 	PCPU_SET(fpcurthread, NULL);
135947e07394SAndrew Turner 
136047e07394SAndrew Turner 	/* restore guest FPU state */
136147e07394SAndrew Turner 	vfp_enable();
136247e07394SAndrew Turner 	vfp_restore(vcpu->guestfpu);
136347e07394SAndrew Turner 
136447e07394SAndrew Turner 	/*
136547e07394SAndrew Turner 	 * The FPU is now "dirty" with the guest's state so turn on emulation
136647e07394SAndrew Turner 	 * to trap any access to the FPU by the host.
136747e07394SAndrew Turner 	 */
136847e07394SAndrew Turner 	vfp_disable();
136947e07394SAndrew Turner }
137047e07394SAndrew Turner 
137147e07394SAndrew Turner static void
137247e07394SAndrew Turner save_guest_fpustate(struct vcpu *vcpu)
137347e07394SAndrew Turner {
137447e07394SAndrew Turner 	if ((READ_SPECIALREG(cpacr_el1) & CPACR_FPEN_MASK) !=
137547e07394SAndrew Turner 	    CPACR_FPEN_TRAP_ALL1)
137647e07394SAndrew Turner 		panic("VFP not enabled in host!");
137747e07394SAndrew Turner 
137847e07394SAndrew Turner 	/* save guest FPU state */
137947e07394SAndrew Turner 	vfp_enable();
138047e07394SAndrew Turner 	vfp_store(vcpu->guestfpu);
138147e07394SAndrew Turner 	vfp_disable();
138247e07394SAndrew Turner 
138347e07394SAndrew Turner 	KASSERT(PCPU_GET(fpcurthread) == NULL,
138447e07394SAndrew Turner 	    ("%s: fpcurthread set with guest registers", __func__));
138547e07394SAndrew Turner }
138647e07394SAndrew Turner static int
138747e07394SAndrew Turner vcpu_set_state_locked(struct vcpu *vcpu, enum vcpu_state newstate,
138847e07394SAndrew Turner     bool from_idle)
138947e07394SAndrew Turner {
139047e07394SAndrew Turner 	int error;
139147e07394SAndrew Turner 
139247e07394SAndrew Turner 	vcpu_assert_locked(vcpu);
139347e07394SAndrew Turner 
139447e07394SAndrew Turner 	/*
139547e07394SAndrew Turner 	 * State transitions from the vmmdev_ioctl() must always begin from
139647e07394SAndrew Turner 	 * the VCPU_IDLE state. This guarantees that there is only a single
139747e07394SAndrew Turner 	 * ioctl() operating on a vcpu at any point.
139847e07394SAndrew Turner 	 */
139947e07394SAndrew Turner 	if (from_idle) {
140047e07394SAndrew Turner 		while (vcpu->state != VCPU_IDLE) {
140147e07394SAndrew Turner 			vcpu_notify_event_locked(vcpu);
140247e07394SAndrew Turner 			msleep_spin(&vcpu->state, &vcpu->mtx, "vmstat", hz);
140347e07394SAndrew Turner 		}
140447e07394SAndrew Turner 	} else {
140547e07394SAndrew Turner 		KASSERT(vcpu->state != VCPU_IDLE, ("invalid transition from "
140647e07394SAndrew Turner 		    "vcpu idle state"));
140747e07394SAndrew Turner 	}
140847e07394SAndrew Turner 
140947e07394SAndrew Turner 	if (vcpu->state == VCPU_RUNNING) {
141047e07394SAndrew Turner 		KASSERT(vcpu->hostcpu == curcpu, ("curcpu %d and hostcpu %d "
141147e07394SAndrew Turner 		    "mismatch for running vcpu", curcpu, vcpu->hostcpu));
141247e07394SAndrew Turner 	} else {
141347e07394SAndrew Turner 		KASSERT(vcpu->hostcpu == NOCPU, ("Invalid hostcpu %d for a "
141447e07394SAndrew Turner 		    "vcpu that is not running", vcpu->hostcpu));
141547e07394SAndrew Turner 	}
141647e07394SAndrew Turner 
141747e07394SAndrew Turner 	/*
141847e07394SAndrew Turner 	 * The following state transitions are allowed:
141947e07394SAndrew Turner 	 * IDLE -> FROZEN -> IDLE
142047e07394SAndrew Turner 	 * FROZEN -> RUNNING -> FROZEN
142147e07394SAndrew Turner 	 * FROZEN -> SLEEPING -> FROZEN
142247e07394SAndrew Turner 	 */
142347e07394SAndrew Turner 	switch (vcpu->state) {
142447e07394SAndrew Turner 	case VCPU_IDLE:
142547e07394SAndrew Turner 	case VCPU_RUNNING:
142647e07394SAndrew Turner 	case VCPU_SLEEPING:
142747e07394SAndrew Turner 		error = (newstate != VCPU_FROZEN);
142847e07394SAndrew Turner 		break;
142947e07394SAndrew Turner 	case VCPU_FROZEN:
143047e07394SAndrew Turner 		error = (newstate == VCPU_FROZEN);
143147e07394SAndrew Turner 		break;
143247e07394SAndrew Turner 	default:
143347e07394SAndrew Turner 		error = 1;
143447e07394SAndrew Turner 		break;
143547e07394SAndrew Turner 	}
143647e07394SAndrew Turner 
143747e07394SAndrew Turner 	if (error)
143847e07394SAndrew Turner 		return (EBUSY);
143947e07394SAndrew Turner 
144047e07394SAndrew Turner 	vcpu->state = newstate;
144147e07394SAndrew Turner 	if (newstate == VCPU_RUNNING)
144247e07394SAndrew Turner 		vcpu->hostcpu = curcpu;
144347e07394SAndrew Turner 	else
144447e07394SAndrew Turner 		vcpu->hostcpu = NOCPU;
144547e07394SAndrew Turner 
144647e07394SAndrew Turner 	if (newstate == VCPU_IDLE)
144747e07394SAndrew Turner 		wakeup(&vcpu->state);
144847e07394SAndrew Turner 
144947e07394SAndrew Turner 	return (0);
145047e07394SAndrew Turner }
145147e07394SAndrew Turner 
145247e07394SAndrew Turner static void
145347e07394SAndrew Turner vcpu_require_state(struct vcpu *vcpu, enum vcpu_state newstate)
145447e07394SAndrew Turner {
145547e07394SAndrew Turner 	int error;
145647e07394SAndrew Turner 
145747e07394SAndrew Turner 	if ((error = vcpu_set_state(vcpu, newstate, false)) != 0)
145847e07394SAndrew Turner 		panic("Error %d setting state to %d\n", error, newstate);
145947e07394SAndrew Turner }
146047e07394SAndrew Turner 
146147e07394SAndrew Turner static void
146247e07394SAndrew Turner vcpu_require_state_locked(struct vcpu *vcpu, enum vcpu_state newstate)
146347e07394SAndrew Turner {
146447e07394SAndrew Turner 	int error;
146547e07394SAndrew Turner 
146647e07394SAndrew Turner 	if ((error = vcpu_set_state_locked(vcpu, newstate, false)) != 0)
146747e07394SAndrew Turner 		panic("Error %d setting state to %d", error, newstate);
146847e07394SAndrew Turner }
146947e07394SAndrew Turner 
147047e07394SAndrew Turner int
147147e07394SAndrew Turner vm_get_capability(struct vcpu *vcpu, int type, int *retval)
147247e07394SAndrew Turner {
147347e07394SAndrew Turner 	if (type < 0 || type >= VM_CAP_MAX)
147447e07394SAndrew Turner 		return (EINVAL);
147547e07394SAndrew Turner 
147647e07394SAndrew Turner 	return (vmmops_getcap(vcpu->cookie, type, retval));
147747e07394SAndrew Turner }
147847e07394SAndrew Turner 
147947e07394SAndrew Turner int
148047e07394SAndrew Turner vm_set_capability(struct vcpu *vcpu, int type, int val)
148147e07394SAndrew Turner {
148247e07394SAndrew Turner 	if (type < 0 || type >= VM_CAP_MAX)
148347e07394SAndrew Turner 		return (EINVAL);
148447e07394SAndrew Turner 
148547e07394SAndrew Turner 	return (vmmops_setcap(vcpu->cookie, type, val));
148647e07394SAndrew Turner }
148747e07394SAndrew Turner 
148847e07394SAndrew Turner struct vm *
148947e07394SAndrew Turner vcpu_vm(struct vcpu *vcpu)
149047e07394SAndrew Turner {
149147e07394SAndrew Turner 	return (vcpu->vm);
149247e07394SAndrew Turner }
149347e07394SAndrew Turner 
149447e07394SAndrew Turner int
149547e07394SAndrew Turner vcpu_vcpuid(struct vcpu *vcpu)
149647e07394SAndrew Turner {
149747e07394SAndrew Turner 	return (vcpu->vcpuid);
149847e07394SAndrew Turner }
149947e07394SAndrew Turner 
150047e07394SAndrew Turner void *
150147e07394SAndrew Turner vcpu_get_cookie(struct vcpu *vcpu)
150247e07394SAndrew Turner {
150347e07394SAndrew Turner 	return (vcpu->cookie);
150447e07394SAndrew Turner }
150547e07394SAndrew Turner 
150647e07394SAndrew Turner struct vcpu *
150747e07394SAndrew Turner vm_vcpu(struct vm *vm, int vcpuid)
150847e07394SAndrew Turner {
150947e07394SAndrew Turner 	return (vm->vcpu[vcpuid]);
151047e07394SAndrew Turner }
151147e07394SAndrew Turner 
151247e07394SAndrew Turner int
151347e07394SAndrew Turner vcpu_set_state(struct vcpu *vcpu, enum vcpu_state newstate, bool from_idle)
151447e07394SAndrew Turner {
151547e07394SAndrew Turner 	int error;
151647e07394SAndrew Turner 
151747e07394SAndrew Turner 	vcpu_lock(vcpu);
151847e07394SAndrew Turner 	error = vcpu_set_state_locked(vcpu, newstate, from_idle);
151947e07394SAndrew Turner 	vcpu_unlock(vcpu);
152047e07394SAndrew Turner 
152147e07394SAndrew Turner 	return (error);
152247e07394SAndrew Turner }
152347e07394SAndrew Turner 
152447e07394SAndrew Turner enum vcpu_state
152547e07394SAndrew Turner vcpu_get_state(struct vcpu *vcpu, int *hostcpu)
152647e07394SAndrew Turner {
152747e07394SAndrew Turner 	enum vcpu_state state;
152847e07394SAndrew Turner 
152947e07394SAndrew Turner 	vcpu_lock(vcpu);
153047e07394SAndrew Turner 	state = vcpu->state;
153147e07394SAndrew Turner 	if (hostcpu != NULL)
153247e07394SAndrew Turner 		*hostcpu = vcpu->hostcpu;
153347e07394SAndrew Turner 	vcpu_unlock(vcpu);
153447e07394SAndrew Turner 
153547e07394SAndrew Turner 	return (state);
153647e07394SAndrew Turner }
153747e07394SAndrew Turner 
153847e07394SAndrew Turner static void *
153947e07394SAndrew Turner _vm_gpa_hold(struct vm *vm, vm_paddr_t gpa, size_t len, int reqprot,
154047e07394SAndrew Turner     void **cookie)
154147e07394SAndrew Turner {
154247e07394SAndrew Turner 	int i, count, pageoff;
154347e07394SAndrew Turner 	struct mem_map *mm;
154447e07394SAndrew Turner 	vm_page_t m;
154547e07394SAndrew Turner 
154647e07394SAndrew Turner 	pageoff = gpa & PAGE_MASK;
154747e07394SAndrew Turner 	if (len > PAGE_SIZE - pageoff)
154847e07394SAndrew Turner 		panic("vm_gpa_hold: invalid gpa/len: 0x%016lx/%lu", gpa, len);
154947e07394SAndrew Turner 
155047e07394SAndrew Turner 	count = 0;
155147e07394SAndrew Turner 	for (i = 0; i < VM_MAX_MEMMAPS; i++) {
155247e07394SAndrew Turner 		mm = &vm->mem_maps[i];
155347e07394SAndrew Turner 		if (sysmem_mapping(vm, mm) && gpa >= mm->gpa &&
155447e07394SAndrew Turner 		    gpa < mm->gpa + mm->len) {
155547e07394SAndrew Turner 			count = vm_fault_quick_hold_pages(&vm->vmspace->vm_map,
155647e07394SAndrew Turner 			    trunc_page(gpa), PAGE_SIZE, reqprot, &m, 1);
155747e07394SAndrew Turner 			break;
155847e07394SAndrew Turner 		}
155947e07394SAndrew Turner 	}
156047e07394SAndrew Turner 
156147e07394SAndrew Turner 	if (count == 1) {
156247e07394SAndrew Turner 		*cookie = m;
156347e07394SAndrew Turner 		return ((void *)(PHYS_TO_DMAP(VM_PAGE_TO_PHYS(m)) + pageoff));
156447e07394SAndrew Turner 	} else {
156547e07394SAndrew Turner 		*cookie = NULL;
156647e07394SAndrew Turner 		return (NULL);
156747e07394SAndrew Turner 	}
156847e07394SAndrew Turner }
156947e07394SAndrew Turner 
157047e07394SAndrew Turner void *
157147e07394SAndrew Turner vm_gpa_hold(struct vcpu *vcpu, vm_paddr_t gpa, size_t len, int reqprot,
157247e07394SAndrew Turner 	    void **cookie)
157347e07394SAndrew Turner {
157447e07394SAndrew Turner #ifdef INVARIANTS
157547e07394SAndrew Turner 	/*
157647e07394SAndrew Turner 	 * The current vcpu should be frozen to ensure 'vm_memmap[]'
157747e07394SAndrew Turner 	 * stability.
157847e07394SAndrew Turner 	 */
157947e07394SAndrew Turner 	int state = vcpu_get_state(vcpu, NULL);
158047e07394SAndrew Turner 	KASSERT(state == VCPU_FROZEN, ("%s: invalid vcpu state %d",
158147e07394SAndrew Turner 	    __func__, state));
158247e07394SAndrew Turner #endif
158347e07394SAndrew Turner 	return (_vm_gpa_hold(vcpu->vm, gpa, len, reqprot, cookie));
158447e07394SAndrew Turner }
158547e07394SAndrew Turner 
158647e07394SAndrew Turner void *
158747e07394SAndrew Turner vm_gpa_hold_global(struct vm *vm, vm_paddr_t gpa, size_t len, int reqprot,
158847e07394SAndrew Turner     void **cookie)
158947e07394SAndrew Turner {
159047e07394SAndrew Turner 	sx_assert(&vm->mem_segs_lock, SX_LOCKED);
159147e07394SAndrew Turner 	return (_vm_gpa_hold(vm, gpa, len, reqprot, cookie));
159247e07394SAndrew Turner }
159347e07394SAndrew Turner 
159447e07394SAndrew Turner void
159547e07394SAndrew Turner vm_gpa_release(void *cookie)
159647e07394SAndrew Turner {
159747e07394SAndrew Turner 	vm_page_t m = cookie;
159847e07394SAndrew Turner 
159947e07394SAndrew Turner 	vm_page_unwire(m, PQ_ACTIVE);
160047e07394SAndrew Turner }
160147e07394SAndrew Turner 
160247e07394SAndrew Turner int
160347e07394SAndrew Turner vm_get_register(struct vcpu *vcpu, int reg, uint64_t *retval)
160447e07394SAndrew Turner {
160547e07394SAndrew Turner 
160647e07394SAndrew Turner 	if (reg >= VM_REG_LAST)
160747e07394SAndrew Turner 		return (EINVAL);
160847e07394SAndrew Turner 
160947e07394SAndrew Turner 	return (vmmops_getreg(vcpu->cookie, reg, retval));
161047e07394SAndrew Turner }
161147e07394SAndrew Turner 
161247e07394SAndrew Turner int
161347e07394SAndrew Turner vm_set_register(struct vcpu *vcpu, int reg, uint64_t val)
161447e07394SAndrew Turner {
161547e07394SAndrew Turner 	int error;
161647e07394SAndrew Turner 
161747e07394SAndrew Turner 	if (reg >= VM_REG_LAST)
161847e07394SAndrew Turner 		return (EINVAL);
161947e07394SAndrew Turner 	error = vmmops_setreg(vcpu->cookie, reg, val);
162047e07394SAndrew Turner 	if (error || reg != VM_REG_GUEST_PC)
162147e07394SAndrew Turner 		return (error);
162247e07394SAndrew Turner 
162347e07394SAndrew Turner 	vcpu->nextpc = val;
162447e07394SAndrew Turner 
162547e07394SAndrew Turner 	return (0);
162647e07394SAndrew Turner }
162747e07394SAndrew Turner 
162847e07394SAndrew Turner void *
162947e07394SAndrew Turner vm_get_cookie(struct vm *vm)
163047e07394SAndrew Turner {
163147e07394SAndrew Turner 	return (vm->cookie);
163247e07394SAndrew Turner }
163347e07394SAndrew Turner 
163447e07394SAndrew Turner int
163547e07394SAndrew Turner vm_inject_exception(struct vcpu *vcpu, uint64_t esr, uint64_t far)
163647e07394SAndrew Turner {
163747e07394SAndrew Turner 	return (vmmops_exception(vcpu->cookie, esr, far));
163847e07394SAndrew Turner }
163947e07394SAndrew Turner 
164047e07394SAndrew Turner int
164147e07394SAndrew Turner vm_attach_vgic(struct vm *vm, struct vm_vgic_descr *descr)
164247e07394SAndrew Turner {
164347e07394SAndrew Turner 	return (vgic_attach_to_vm(vm->cookie, descr));
164447e07394SAndrew Turner }
164547e07394SAndrew Turner 
164647e07394SAndrew Turner int
164747e07394SAndrew Turner vm_assert_irq(struct vm *vm, uint32_t irq)
164847e07394SAndrew Turner {
164947e07394SAndrew Turner 	return (vgic_inject_irq(vm->cookie, -1, irq, true));
165047e07394SAndrew Turner }
165147e07394SAndrew Turner 
165247e07394SAndrew Turner int
165347e07394SAndrew Turner vm_deassert_irq(struct vm *vm, uint32_t irq)
165447e07394SAndrew Turner {
165547e07394SAndrew Turner 	return (vgic_inject_irq(vm->cookie, -1, irq, false));
165647e07394SAndrew Turner }
165747e07394SAndrew Turner 
165847e07394SAndrew Turner int
165947e07394SAndrew Turner vm_raise_msi(struct vm *vm, uint64_t msg, uint64_t addr, int bus, int slot,
166047e07394SAndrew Turner     int func)
166147e07394SAndrew Turner {
166247e07394SAndrew Turner 	/* TODO: Should we raise an SError? */
166347e07394SAndrew Turner 	return (vgic_inject_msi(vm->cookie, msg, addr));
166447e07394SAndrew Turner }
166547e07394SAndrew Turner 
166647e07394SAndrew Turner static int
166747e07394SAndrew Turner vm_handle_smccc_call(struct vcpu *vcpu, struct vm_exit *vme, bool *retu)
166847e07394SAndrew Turner {
166947e07394SAndrew Turner 	struct hypctx *hypctx;
167047e07394SAndrew Turner 	int i;
167147e07394SAndrew Turner 
167247e07394SAndrew Turner 	hypctx = vcpu_get_cookie(vcpu);
167347e07394SAndrew Turner 
167447e07394SAndrew Turner 	if ((hypctx->tf.tf_esr & ESR_ELx_ISS_MASK) != 0)
167547e07394SAndrew Turner 		return (1);
167647e07394SAndrew Turner 
167747e07394SAndrew Turner 	vme->exitcode = VM_EXITCODE_SMCCC;
167847e07394SAndrew Turner 	vme->u.smccc_call.func_id = hypctx->tf.tf_x[0];
167947e07394SAndrew Turner 	for (i = 0; i < nitems(vme->u.smccc_call.args); i++)
168047e07394SAndrew Turner 		vme->u.smccc_call.args[i] = hypctx->tf.tf_x[i + 1];
168147e07394SAndrew Turner 
168247e07394SAndrew Turner 	*retu = true;
168347e07394SAndrew Turner 	return (0);
168447e07394SAndrew Turner }
168547e07394SAndrew Turner 
168647e07394SAndrew Turner static int
168747e07394SAndrew Turner vm_handle_wfi(struct vcpu *vcpu, struct vm_exit *vme, bool *retu)
168847e07394SAndrew Turner {
168947e07394SAndrew Turner 	vcpu_lock(vcpu);
169047e07394SAndrew Turner 	while (1) {
169147e07394SAndrew Turner 		if (vgic_has_pending_irq(vcpu->cookie))
169247e07394SAndrew Turner 			break;
169347e07394SAndrew Turner 
169447e07394SAndrew Turner 		if (vcpu_should_yield(vcpu))
169547e07394SAndrew Turner 			break;
169647e07394SAndrew Turner 
169747e07394SAndrew Turner 		vcpu_require_state_locked(vcpu, VCPU_SLEEPING);
169847e07394SAndrew Turner 		/*
169947e07394SAndrew Turner 		 * XXX msleep_spin() cannot be interrupted by signals so
170047e07394SAndrew Turner 		 * wake up periodically to check pending signals.
170147e07394SAndrew Turner 		 */
170247e07394SAndrew Turner 		msleep_spin(vcpu, &vcpu->mtx, "vmidle", hz);
170347e07394SAndrew Turner 		vcpu_require_state_locked(vcpu, VCPU_FROZEN);
170447e07394SAndrew Turner 	}
170547e07394SAndrew Turner 	vcpu_unlock(vcpu);
170647e07394SAndrew Turner 
170747e07394SAndrew Turner 	*retu = false;
170847e07394SAndrew Turner 	return (0);
170947e07394SAndrew Turner }
171047e07394SAndrew Turner 
171147e07394SAndrew Turner static int
171247e07394SAndrew Turner vm_handle_paging(struct vcpu *vcpu, bool *retu)
171347e07394SAndrew Turner {
171447e07394SAndrew Turner 	struct vm *vm = vcpu->vm;
171547e07394SAndrew Turner 	struct vm_exit *vme;
171647e07394SAndrew Turner 	struct vm_map *map;
171747e07394SAndrew Turner 	uint64_t addr, esr;
171847e07394SAndrew Turner 	pmap_t pmap;
171947e07394SAndrew Turner 	int ftype, rv;
172047e07394SAndrew Turner 
172147e07394SAndrew Turner 	vme = &vcpu->exitinfo;
172247e07394SAndrew Turner 
172347e07394SAndrew Turner 	pmap = vmspace_pmap(vcpu->vm->vmspace);
172447e07394SAndrew Turner 	addr = vme->u.paging.gpa;
172547e07394SAndrew Turner 	esr = vme->u.paging.esr;
172647e07394SAndrew Turner 
172747e07394SAndrew Turner 	/* The page exists, but the page table needs to be updated. */
172847e07394SAndrew Turner 	if (pmap_fault(pmap, esr, addr) == KERN_SUCCESS)
172947e07394SAndrew Turner 		return (0);
173047e07394SAndrew Turner 
173147e07394SAndrew Turner 	switch (ESR_ELx_EXCEPTION(esr)) {
173247e07394SAndrew Turner 	case EXCP_INSN_ABORT_L:
173347e07394SAndrew Turner 	case EXCP_DATA_ABORT_L:
173447e07394SAndrew Turner 		ftype = VM_PROT_EXECUTE | VM_PROT_READ | VM_PROT_WRITE;
173547e07394SAndrew Turner 		break;
173647e07394SAndrew Turner 	default:
173747e07394SAndrew Turner 		panic("%s: Invalid exception (esr = %lx)", __func__, esr);
173847e07394SAndrew Turner 	}
173947e07394SAndrew Turner 
174047e07394SAndrew Turner 	map = &vm->vmspace->vm_map;
174147e07394SAndrew Turner 	rv = vm_fault(map, vme->u.paging.gpa, ftype, VM_FAULT_NORMAL, NULL);
174247e07394SAndrew Turner 	if (rv != KERN_SUCCESS)
174347e07394SAndrew Turner 		return (EFAULT);
174447e07394SAndrew Turner 
174547e07394SAndrew Turner 	return (0);
174647e07394SAndrew Turner }
174747e07394SAndrew Turner 
17481ee7a8faSMark Johnston static int
17491ee7a8faSMark Johnston vm_handle_suspend(struct vcpu *vcpu, bool *retu)
17501ee7a8faSMark Johnston {
17511ee7a8faSMark Johnston 	struct vm *vm = vcpu->vm;
17521ee7a8faSMark Johnston 	int error, i;
17531ee7a8faSMark Johnston 	struct thread *td;
17541ee7a8faSMark Johnston 
17551ee7a8faSMark Johnston 	error = 0;
17561ee7a8faSMark Johnston 	td = curthread;
17571ee7a8faSMark Johnston 
17581ee7a8faSMark Johnston 	CPU_SET_ATOMIC(vcpu->vcpuid, &vm->suspended_cpus);
17591ee7a8faSMark Johnston 
17601ee7a8faSMark Johnston 	/*
17611ee7a8faSMark Johnston 	 * Wait until all 'active_cpus' have suspended themselves.
17621ee7a8faSMark Johnston 	 *
17631ee7a8faSMark Johnston 	 * Since a VM may be suspended at any time including when one or
17641ee7a8faSMark Johnston 	 * more vcpus are doing a rendezvous we need to call the rendezvous
17651ee7a8faSMark Johnston 	 * handler while we are waiting to prevent a deadlock.
17661ee7a8faSMark Johnston 	 */
17671ee7a8faSMark Johnston 	vcpu_lock(vcpu);
17681ee7a8faSMark Johnston 	while (error == 0) {
17691ee7a8faSMark Johnston 		if (CPU_CMP(&vm->suspended_cpus, &vm->active_cpus) == 0)
17701ee7a8faSMark Johnston 			break;
17711ee7a8faSMark Johnston 
17721ee7a8faSMark Johnston 		vcpu_require_state_locked(vcpu, VCPU_SLEEPING);
17731ee7a8faSMark Johnston 		msleep_spin(vcpu, &vcpu->mtx, "vmsusp", hz);
17741ee7a8faSMark Johnston 		vcpu_require_state_locked(vcpu, VCPU_FROZEN);
17751ee7a8faSMark Johnston 		if (td_ast_pending(td, TDA_SUSPEND)) {
17761ee7a8faSMark Johnston 			vcpu_unlock(vcpu);
17771ee7a8faSMark Johnston 			error = thread_check_susp(td, false);
17781ee7a8faSMark Johnston 			vcpu_lock(vcpu);
17791ee7a8faSMark Johnston 		}
17801ee7a8faSMark Johnston 	}
17811ee7a8faSMark Johnston 	vcpu_unlock(vcpu);
17821ee7a8faSMark Johnston 
17831ee7a8faSMark Johnston 	/*
17841ee7a8faSMark Johnston 	 * Wakeup the other sleeping vcpus and return to userspace.
17851ee7a8faSMark Johnston 	 */
17861ee7a8faSMark Johnston 	for (i = 0; i < vm->maxcpus; i++) {
17871ee7a8faSMark Johnston 		if (CPU_ISSET(i, &vm->suspended_cpus)) {
17881ee7a8faSMark Johnston 			vcpu_notify_event(vm_vcpu(vm, i));
17891ee7a8faSMark Johnston 		}
17901ee7a8faSMark Johnston 	}
17911ee7a8faSMark Johnston 
17921ee7a8faSMark Johnston 	*retu = true;
17931ee7a8faSMark Johnston 	return (error);
17941ee7a8faSMark Johnston }
17951ee7a8faSMark Johnston 
179647e07394SAndrew Turner int
179747e07394SAndrew Turner vm_run(struct vcpu *vcpu)
179847e07394SAndrew Turner {
179947e07394SAndrew Turner 	struct vm *vm = vcpu->vm;
180047e07394SAndrew Turner 	struct vm_eventinfo evinfo;
180147e07394SAndrew Turner 	int error, vcpuid;
180247e07394SAndrew Turner 	struct vm_exit *vme;
180347e07394SAndrew Turner 	bool retu;
180447e07394SAndrew Turner 	pmap_t pmap;
180547e07394SAndrew Turner 
180647e07394SAndrew Turner 	vcpuid = vcpu->vcpuid;
180747e07394SAndrew Turner 
180847e07394SAndrew Turner 	if (!CPU_ISSET(vcpuid, &vm->active_cpus))
180947e07394SAndrew Turner 		return (EINVAL);
181047e07394SAndrew Turner 
181147e07394SAndrew Turner 	if (CPU_ISSET(vcpuid, &vm->suspended_cpus))
181247e07394SAndrew Turner 		return (EINVAL);
181347e07394SAndrew Turner 
181447e07394SAndrew Turner 	pmap = vmspace_pmap(vm->vmspace);
181547e07394SAndrew Turner 	vme = &vcpu->exitinfo;
181647e07394SAndrew Turner 	evinfo.rptr = NULL;
181747e07394SAndrew Turner 	evinfo.sptr = &vm->suspend;
181847e07394SAndrew Turner 	evinfo.iptr = NULL;
181947e07394SAndrew Turner restart:
182047e07394SAndrew Turner 	critical_enter();
182147e07394SAndrew Turner 
182247e07394SAndrew Turner 	restore_guest_fpustate(vcpu);
182347e07394SAndrew Turner 
182447e07394SAndrew Turner 	vcpu_require_state(vcpu, VCPU_RUNNING);
182547e07394SAndrew Turner 	error = vmmops_run(vcpu->cookie, vcpu->nextpc, pmap, &evinfo);
182647e07394SAndrew Turner 	vcpu_require_state(vcpu, VCPU_FROZEN);
182747e07394SAndrew Turner 
182847e07394SAndrew Turner 	save_guest_fpustate(vcpu);
182947e07394SAndrew Turner 
183047e07394SAndrew Turner 	critical_exit();
183147e07394SAndrew Turner 
183247e07394SAndrew Turner 	if (error == 0) {
183347e07394SAndrew Turner 		retu = false;
183447e07394SAndrew Turner 		switch (vme->exitcode) {
183547e07394SAndrew Turner 		case VM_EXITCODE_INST_EMUL:
183647e07394SAndrew Turner 			vcpu->nextpc = vme->pc + vme->inst_length;
183747e07394SAndrew Turner 			error = vm_handle_inst_emul(vcpu, &retu);
183847e07394SAndrew Turner 			break;
183947e07394SAndrew Turner 
184047e07394SAndrew Turner 		case VM_EXITCODE_REG_EMUL:
184147e07394SAndrew Turner 			vcpu->nextpc = vme->pc + vme->inst_length;
184247e07394SAndrew Turner 			error = vm_handle_reg_emul(vcpu, &retu);
184347e07394SAndrew Turner 			break;
184447e07394SAndrew Turner 
184547e07394SAndrew Turner 		case VM_EXITCODE_HVC:
184647e07394SAndrew Turner 			/*
184747e07394SAndrew Turner 			 * The HVC instruction saves the address for the
184847e07394SAndrew Turner 			 * next instruction as the return address.
184947e07394SAndrew Turner 			 */
185047e07394SAndrew Turner 			vcpu->nextpc = vme->pc;
185147e07394SAndrew Turner 			/*
185247e07394SAndrew Turner 			 * The PSCI call can change the exit information in the
185347e07394SAndrew Turner 			 * case of suspend/reset/poweroff/cpu off/cpu on.
185447e07394SAndrew Turner 			 */
185547e07394SAndrew Turner 			error = vm_handle_smccc_call(vcpu, vme, &retu);
185647e07394SAndrew Turner 			break;
185747e07394SAndrew Turner 
185847e07394SAndrew Turner 		case VM_EXITCODE_WFI:
185947e07394SAndrew Turner 			vcpu->nextpc = vme->pc + vme->inst_length;
186047e07394SAndrew Turner 			error = vm_handle_wfi(vcpu, vme, &retu);
186147e07394SAndrew Turner 			break;
186247e07394SAndrew Turner 
186347e07394SAndrew Turner 		case VM_EXITCODE_PAGING:
186447e07394SAndrew Turner 			vcpu->nextpc = vme->pc;
186547e07394SAndrew Turner 			error = vm_handle_paging(vcpu, &retu);
186647e07394SAndrew Turner 			break;
186747e07394SAndrew Turner 
18681ee7a8faSMark Johnston 		case VM_EXITCODE_SUSPENDED:
18691ee7a8faSMark Johnston 			vcpu->nextpc = vme->pc;
18701ee7a8faSMark Johnston 			error = vm_handle_suspend(vcpu, &retu);
18711ee7a8faSMark Johnston 			break;
18721ee7a8faSMark Johnston 
187347e07394SAndrew Turner 		default:
187447e07394SAndrew Turner 			/* Handle in userland */
187547e07394SAndrew Turner 			vcpu->nextpc = vme->pc;
187647e07394SAndrew Turner 			retu = true;
187747e07394SAndrew Turner 			break;
187847e07394SAndrew Turner 		}
187947e07394SAndrew Turner 	}
188047e07394SAndrew Turner 
188147e07394SAndrew Turner 	if (error == 0 && retu == false)
188247e07394SAndrew Turner 		goto restart;
188347e07394SAndrew Turner 
188447e07394SAndrew Turner 	return (error);
188547e07394SAndrew Turner }
1886