xref: /freebsd/sys/arm64/vmm/vmm.c (revision 47e073941f4e7ca6e9bde3fa65abbfcfed6bfa2b)
1*47e07394SAndrew Turner /*-
2*47e07394SAndrew Turner  * SPDX-License-Identifier: BSD-2-Clause
3*47e07394SAndrew Turner  *
4*47e07394SAndrew Turner  * Copyright (C) 2015 Mihai Carabas <mihai.carabas@gmail.com>
5*47e07394SAndrew Turner  * All rights reserved.
6*47e07394SAndrew Turner  *
7*47e07394SAndrew Turner  * Redistribution and use in source and binary forms, with or without
8*47e07394SAndrew Turner  * modification, are permitted provided that the following conditions
9*47e07394SAndrew Turner  * are met:
10*47e07394SAndrew Turner  * 1. Redistributions of source code must retain the above copyright
11*47e07394SAndrew Turner  *    notice, this list of conditions and the following disclaimer.
12*47e07394SAndrew Turner  * 2. Redistributions in binary form must reproduce the above copyright
13*47e07394SAndrew Turner  *    notice, this list of conditions and the following disclaimer in the
14*47e07394SAndrew Turner  *    documentation and/or other materials provided with the distribution.
15*47e07394SAndrew Turner  *
16*47e07394SAndrew Turner  * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17*47e07394SAndrew Turner  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18*47e07394SAndrew Turner  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19*47e07394SAndrew Turner  * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
20*47e07394SAndrew Turner  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21*47e07394SAndrew Turner  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22*47e07394SAndrew Turner  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23*47e07394SAndrew Turner  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24*47e07394SAndrew Turner  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25*47e07394SAndrew Turner  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26*47e07394SAndrew Turner  * SUCH DAMAGE.
27*47e07394SAndrew Turner  */
28*47e07394SAndrew Turner 
29*47e07394SAndrew Turner #include <sys/param.h>
30*47e07394SAndrew Turner #include <sys/systm.h>
31*47e07394SAndrew Turner #include <sys/cpuset.h>
32*47e07394SAndrew Turner #include <sys/kernel.h>
33*47e07394SAndrew Turner #include <sys/linker.h>
34*47e07394SAndrew Turner #include <sys/lock.h>
35*47e07394SAndrew Turner #include <sys/malloc.h>
36*47e07394SAndrew Turner #include <sys/module.h>
37*47e07394SAndrew Turner #include <sys/mutex.h>
38*47e07394SAndrew Turner #include <sys/pcpu.h>
39*47e07394SAndrew Turner #include <sys/proc.h>
40*47e07394SAndrew Turner #include <sys/queue.h>
41*47e07394SAndrew Turner #include <sys/rwlock.h>
42*47e07394SAndrew Turner #include <sys/sched.h>
43*47e07394SAndrew Turner #include <sys/smp.h>
44*47e07394SAndrew Turner #include <sys/sysctl.h>
45*47e07394SAndrew Turner 
46*47e07394SAndrew Turner #include <vm/vm.h>
47*47e07394SAndrew Turner #include <vm/vm_object.h>
48*47e07394SAndrew Turner #include <vm/vm_page.h>
49*47e07394SAndrew Turner #include <vm/pmap.h>
50*47e07394SAndrew Turner #include <vm/vm_map.h>
51*47e07394SAndrew Turner #include <vm/vm_extern.h>
52*47e07394SAndrew Turner #include <vm/vm_param.h>
53*47e07394SAndrew Turner 
54*47e07394SAndrew Turner #include <machine/armreg.h>
55*47e07394SAndrew Turner #include <machine/cpu.h>
56*47e07394SAndrew Turner #include <machine/fpu.h>
57*47e07394SAndrew Turner #include <machine/machdep.h>
58*47e07394SAndrew Turner #include <machine/pcb.h>
59*47e07394SAndrew Turner #include <machine/smp.h>
60*47e07394SAndrew Turner #include <machine/vm.h>
61*47e07394SAndrew Turner #include <machine/vmparam.h>
62*47e07394SAndrew Turner #include <machine/vmm.h>
63*47e07394SAndrew Turner #include <machine/vmm_dev.h>
64*47e07394SAndrew Turner #include <machine/vmm_instruction_emul.h>
65*47e07394SAndrew Turner 
66*47e07394SAndrew Turner #include <dev/pci/pcireg.h>
67*47e07394SAndrew Turner 
68*47e07394SAndrew Turner #include "vmm_ktr.h"
69*47e07394SAndrew Turner #include "vmm_stat.h"
70*47e07394SAndrew Turner #include "arm64.h"
71*47e07394SAndrew Turner #include "mmu.h"
72*47e07394SAndrew Turner 
73*47e07394SAndrew Turner #include "io/vgic.h"
74*47e07394SAndrew Turner #include "io/vtimer.h"
75*47e07394SAndrew Turner 
76*47e07394SAndrew Turner struct vcpu {
77*47e07394SAndrew Turner 	int		flags;
78*47e07394SAndrew Turner 	enum vcpu_state	state;
79*47e07394SAndrew Turner 	struct mtx	mtx;
80*47e07394SAndrew Turner 	int		hostcpu;	/* host cpuid this vcpu last ran on */
81*47e07394SAndrew Turner 	int		vcpuid;
82*47e07394SAndrew Turner 	void		*stats;
83*47e07394SAndrew Turner 	struct vm_exit	exitinfo;
84*47e07394SAndrew Turner 	uint64_t	nextpc;		/* (x) next instruction to execute */
85*47e07394SAndrew Turner 	struct vm	*vm;		/* (o) */
86*47e07394SAndrew Turner 	void		*cookie;	/* (i) cpu-specific data */
87*47e07394SAndrew Turner 	struct vfpstate	*guestfpu;	/* (a,i) guest fpu state */
88*47e07394SAndrew Turner };
89*47e07394SAndrew Turner 
90*47e07394SAndrew Turner #define	vcpu_lock_initialized(v) mtx_initialized(&((v)->mtx))
91*47e07394SAndrew Turner #define	vcpu_lock_init(v)	mtx_init(&((v)->mtx), "vcpu lock", 0, MTX_SPIN)
92*47e07394SAndrew Turner #define	vcpu_lock_destroy(v)	mtx_destroy(&((v)->mtx))
93*47e07394SAndrew Turner #define	vcpu_lock(v)		mtx_lock_spin(&((v)->mtx))
94*47e07394SAndrew Turner #define	vcpu_unlock(v)		mtx_unlock_spin(&((v)->mtx))
95*47e07394SAndrew Turner #define	vcpu_assert_locked(v)	mtx_assert(&((v)->mtx), MA_OWNED)
96*47e07394SAndrew Turner 
97*47e07394SAndrew Turner struct mem_seg {
98*47e07394SAndrew Turner 	uint64_t	gpa;
99*47e07394SAndrew Turner 	size_t		len;
100*47e07394SAndrew Turner 	bool		wired;
101*47e07394SAndrew Turner 	bool		sysmem;
102*47e07394SAndrew Turner 	vm_object_t	object;
103*47e07394SAndrew Turner };
104*47e07394SAndrew Turner #define	VM_MAX_MEMSEGS	3
105*47e07394SAndrew Turner 
106*47e07394SAndrew Turner struct mem_map {
107*47e07394SAndrew Turner 	vm_paddr_t	gpa;
108*47e07394SAndrew Turner 	size_t		len;
109*47e07394SAndrew Turner 	vm_ooffset_t	segoff;
110*47e07394SAndrew Turner 	int		segid;
111*47e07394SAndrew Turner 	int		prot;
112*47e07394SAndrew Turner 	int		flags;
113*47e07394SAndrew Turner };
114*47e07394SAndrew Turner #define	VM_MAX_MEMMAPS	4
115*47e07394SAndrew Turner 
116*47e07394SAndrew Turner struct vmm_mmio_region {
117*47e07394SAndrew Turner 	uint64_t start;
118*47e07394SAndrew Turner 	uint64_t end;
119*47e07394SAndrew Turner 	mem_region_read_t read;
120*47e07394SAndrew Turner 	mem_region_write_t write;
121*47e07394SAndrew Turner };
122*47e07394SAndrew Turner #define	VM_MAX_MMIO_REGIONS	4
123*47e07394SAndrew Turner 
124*47e07394SAndrew Turner struct vmm_special_reg {
125*47e07394SAndrew Turner 	uint32_t	esr_iss;
126*47e07394SAndrew Turner 	uint32_t	esr_mask;
127*47e07394SAndrew Turner 	reg_read_t	reg_read;
128*47e07394SAndrew Turner 	reg_write_t	reg_write;
129*47e07394SAndrew Turner 	void		*arg;
130*47e07394SAndrew Turner };
131*47e07394SAndrew Turner #define	VM_MAX_SPECIAL_REGS	16
132*47e07394SAndrew Turner 
133*47e07394SAndrew Turner /*
134*47e07394SAndrew Turner  * Initialization:
135*47e07394SAndrew Turner  * (o) initialized the first time the VM is created
136*47e07394SAndrew Turner  * (i) initialized when VM is created and when it is reinitialized
137*47e07394SAndrew Turner  * (x) initialized before use
138*47e07394SAndrew Turner  */
139*47e07394SAndrew Turner struct vm {
140*47e07394SAndrew Turner 	void		*cookie;		/* (i) cpu-specific data */
141*47e07394SAndrew Turner 	volatile cpuset_t active_cpus;		/* (i) active vcpus */
142*47e07394SAndrew Turner 	volatile cpuset_t debug_cpus;		/* (i) vcpus stopped for debug */
143*47e07394SAndrew Turner 	int		suspend;		/* (i) stop VM execution */
144*47e07394SAndrew Turner 	volatile cpuset_t suspended_cpus; 	/* (i) suspended vcpus */
145*47e07394SAndrew Turner 	volatile cpuset_t halted_cpus;		/* (x) cpus in a hard halt */
146*47e07394SAndrew Turner 	struct mem_map	mem_maps[VM_MAX_MEMMAPS]; /* (i) guest address space */
147*47e07394SAndrew Turner 	struct mem_seg	mem_segs[VM_MAX_MEMSEGS]; /* (o) guest memory regions */
148*47e07394SAndrew Turner 	struct vmspace	*vmspace;		/* (o) guest's address space */
149*47e07394SAndrew Turner 	char		name[VM_MAX_NAMELEN];	/* (o) virtual machine name */
150*47e07394SAndrew Turner 	struct vcpu	**vcpu;			/* (i) guest vcpus */
151*47e07394SAndrew Turner 	struct vmm_mmio_region mmio_region[VM_MAX_MMIO_REGIONS];
152*47e07394SAndrew Turner 						/* (o) guest MMIO regions */
153*47e07394SAndrew Turner 	struct vmm_special_reg special_reg[VM_MAX_SPECIAL_REGS];
154*47e07394SAndrew Turner 	/* The following describe the vm cpu topology */
155*47e07394SAndrew Turner 	uint16_t	sockets;		/* (o) num of sockets */
156*47e07394SAndrew Turner 	uint16_t	cores;			/* (o) num of cores/socket */
157*47e07394SAndrew Turner 	uint16_t	threads;		/* (o) num of threads/core */
158*47e07394SAndrew Turner 	uint16_t	maxcpus;		/* (o) max pluggable cpus */
159*47e07394SAndrew Turner 	struct sx	mem_segs_lock;		/* (o) */
160*47e07394SAndrew Turner 	struct sx	vcpus_init_lock;	/* (o) */
161*47e07394SAndrew Turner };
162*47e07394SAndrew Turner 
163*47e07394SAndrew Turner static bool vmm_initialized = false;
164*47e07394SAndrew Turner 
165*47e07394SAndrew Turner static int vm_handle_wfi(struct vcpu *vcpu,
166*47e07394SAndrew Turner 			 struct vm_exit *vme, bool *retu);
167*47e07394SAndrew Turner 
168*47e07394SAndrew Turner static MALLOC_DEFINE(M_VMM, "vmm", "vmm");
169*47e07394SAndrew Turner 
170*47e07394SAndrew Turner /* statistics */
171*47e07394SAndrew Turner static VMM_STAT(VCPU_TOTAL_RUNTIME, "vcpu total runtime");
172*47e07394SAndrew Turner 
173*47e07394SAndrew Turner SYSCTL_NODE(_hw, OID_AUTO, vmm, CTLFLAG_RW, NULL, NULL);
174*47e07394SAndrew Turner 
175*47e07394SAndrew Turner static int vmm_ipinum;
176*47e07394SAndrew Turner SYSCTL_INT(_hw_vmm, OID_AUTO, ipinum, CTLFLAG_RD, &vmm_ipinum, 0,
177*47e07394SAndrew Turner     "IPI vector used for vcpu notifications");
178*47e07394SAndrew Turner 
179*47e07394SAndrew Turner struct vmm_regs {
180*47e07394SAndrew Turner 	uint64_t	id_aa64afr0;
181*47e07394SAndrew Turner 	uint64_t	id_aa64afr1;
182*47e07394SAndrew Turner 	uint64_t	id_aa64dfr0;
183*47e07394SAndrew Turner 	uint64_t	id_aa64dfr1;
184*47e07394SAndrew Turner 	uint64_t	id_aa64isar0;
185*47e07394SAndrew Turner 	uint64_t	id_aa64isar1;
186*47e07394SAndrew Turner 	uint64_t	id_aa64isar2;
187*47e07394SAndrew Turner 	uint64_t	id_aa64mmfr0;
188*47e07394SAndrew Turner 	uint64_t	id_aa64mmfr1;
189*47e07394SAndrew Turner 	uint64_t	id_aa64mmfr2;
190*47e07394SAndrew Turner 	uint64_t	id_aa64pfr0;
191*47e07394SAndrew Turner 	uint64_t	id_aa64pfr1;
192*47e07394SAndrew Turner };
193*47e07394SAndrew Turner 
194*47e07394SAndrew Turner static const struct vmm_regs vmm_arch_regs_masks = {
195*47e07394SAndrew Turner 	.id_aa64dfr0 =
196*47e07394SAndrew Turner 	    ID_AA64DFR0_CTX_CMPs_MASK |
197*47e07394SAndrew Turner 	    ID_AA64DFR0_WRPs_MASK |
198*47e07394SAndrew Turner 	    ID_AA64DFR0_BRPs_MASK |
199*47e07394SAndrew Turner 	    ID_AA64DFR0_PMUVer_3 |
200*47e07394SAndrew Turner 	    ID_AA64DFR0_DebugVer_8,
201*47e07394SAndrew Turner 	.id_aa64isar0 =
202*47e07394SAndrew Turner 	    ID_AA64ISAR0_TLB_TLBIOSR |
203*47e07394SAndrew Turner 	    ID_AA64ISAR0_SHA3_IMPL |
204*47e07394SAndrew Turner 	    ID_AA64ISAR0_RDM_IMPL |
205*47e07394SAndrew Turner 	    ID_AA64ISAR0_Atomic_IMPL |
206*47e07394SAndrew Turner 	    ID_AA64ISAR0_CRC32_BASE |
207*47e07394SAndrew Turner 	    ID_AA64ISAR0_SHA2_512 |
208*47e07394SAndrew Turner 	    ID_AA64ISAR0_SHA1_BASE |
209*47e07394SAndrew Turner 	    ID_AA64ISAR0_AES_PMULL,
210*47e07394SAndrew Turner 	.id_aa64mmfr0 =
211*47e07394SAndrew Turner 	    ID_AA64MMFR0_TGran4_IMPL |
212*47e07394SAndrew Turner 	    ID_AA64MMFR0_TGran64_IMPL |
213*47e07394SAndrew Turner 	    ID_AA64MMFR0_TGran16_IMPL |
214*47e07394SAndrew Turner 	    ID_AA64MMFR0_ASIDBits_16 |
215*47e07394SAndrew Turner 	    ID_AA64MMFR0_PARange_4P,
216*47e07394SAndrew Turner 	.id_aa64mmfr1 =
217*47e07394SAndrew Turner 	    ID_AA64MMFR1_SpecSEI_IMPL |
218*47e07394SAndrew Turner 	    ID_AA64MMFR1_PAN_ATS1E1 |
219*47e07394SAndrew Turner 	    ID_AA64MMFR1_HAFDBS_AF,
220*47e07394SAndrew Turner 	.id_aa64pfr0 =
221*47e07394SAndrew Turner 	    ID_AA64PFR0_GIC_CPUIF_NONE |
222*47e07394SAndrew Turner 	    ID_AA64PFR0_AdvSIMD_HP |
223*47e07394SAndrew Turner 	    ID_AA64PFR0_FP_HP |
224*47e07394SAndrew Turner 	    ID_AA64PFR0_EL3_64 |
225*47e07394SAndrew Turner 	    ID_AA64PFR0_EL2_64 |
226*47e07394SAndrew Turner 	    ID_AA64PFR0_EL1_64 |
227*47e07394SAndrew Turner 	    ID_AA64PFR0_EL0_64,
228*47e07394SAndrew Turner };
229*47e07394SAndrew Turner 
230*47e07394SAndrew Turner /* Host registers masked by vmm_arch_regs_masks. */
231*47e07394SAndrew Turner static struct vmm_regs vmm_arch_regs;
232*47e07394SAndrew Turner 
233*47e07394SAndrew Turner u_int vm_maxcpu;
234*47e07394SAndrew Turner SYSCTL_UINT(_hw_vmm, OID_AUTO, maxcpu, CTLFLAG_RDTUN | CTLFLAG_NOFETCH,
235*47e07394SAndrew Turner     &vm_maxcpu, 0, "Maximum number of vCPUs");
236*47e07394SAndrew Turner 
237*47e07394SAndrew Turner static void vm_free_memmap(struct vm *vm, int ident);
238*47e07394SAndrew Turner static bool sysmem_mapping(struct vm *vm, struct mem_map *mm);
239*47e07394SAndrew Turner static void vcpu_notify_event_locked(struct vcpu *vcpu);
240*47e07394SAndrew Turner 
241*47e07394SAndrew Turner /*
242*47e07394SAndrew Turner  * Upper limit on vm_maxcpu. We could increase this to 28 bits, but this
243*47e07394SAndrew Turner  * is a safe value for now.
244*47e07394SAndrew Turner  */
245*47e07394SAndrew Turner #define	VM_MAXCPU	MIN(0xffff - 1, CPU_SETSIZE)
246*47e07394SAndrew Turner 
247*47e07394SAndrew Turner static int
248*47e07394SAndrew Turner vmm_regs_init(struct vmm_regs *regs, const struct vmm_regs *masks)
249*47e07394SAndrew Turner {
250*47e07394SAndrew Turner #define	_FETCH_KERN_REG(reg, field) do {				\
251*47e07394SAndrew Turner 	regs->field = vmm_arch_regs_masks.field;			\
252*47e07394SAndrew Turner 	if (!get_kernel_reg_masked(reg, &regs->field, masks->field))	\
253*47e07394SAndrew Turner 		regs->field = 0;					\
254*47e07394SAndrew Turner } while (0)
255*47e07394SAndrew Turner 	_FETCH_KERN_REG(ID_AA64AFR0_EL1, id_aa64afr0);
256*47e07394SAndrew Turner 	_FETCH_KERN_REG(ID_AA64AFR1_EL1, id_aa64afr1);
257*47e07394SAndrew Turner 	_FETCH_KERN_REG(ID_AA64DFR0_EL1, id_aa64dfr0);
258*47e07394SAndrew Turner 	_FETCH_KERN_REG(ID_AA64DFR1_EL1, id_aa64dfr1);
259*47e07394SAndrew Turner 	_FETCH_KERN_REG(ID_AA64ISAR0_EL1, id_aa64isar0);
260*47e07394SAndrew Turner 	_FETCH_KERN_REG(ID_AA64ISAR1_EL1, id_aa64isar1);
261*47e07394SAndrew Turner 	_FETCH_KERN_REG(ID_AA64ISAR2_EL1, id_aa64isar2);
262*47e07394SAndrew Turner 	_FETCH_KERN_REG(ID_AA64MMFR0_EL1, id_aa64mmfr0);
263*47e07394SAndrew Turner 	_FETCH_KERN_REG(ID_AA64MMFR1_EL1, id_aa64mmfr1);
264*47e07394SAndrew Turner 	_FETCH_KERN_REG(ID_AA64MMFR2_EL1, id_aa64mmfr2);
265*47e07394SAndrew Turner 	_FETCH_KERN_REG(ID_AA64PFR0_EL1, id_aa64pfr0);
266*47e07394SAndrew Turner 	_FETCH_KERN_REG(ID_AA64PFR1_EL1, id_aa64pfr1);
267*47e07394SAndrew Turner #undef _FETCH_KERN_REG
268*47e07394SAndrew Turner 	return (0);
269*47e07394SAndrew Turner }
270*47e07394SAndrew Turner 
271*47e07394SAndrew Turner static void
272*47e07394SAndrew Turner vcpu_cleanup(struct vcpu *vcpu, bool destroy)
273*47e07394SAndrew Turner {
274*47e07394SAndrew Turner 	vmmops_vcpu_cleanup(vcpu->cookie);
275*47e07394SAndrew Turner 	vcpu->cookie = NULL;
276*47e07394SAndrew Turner 	if (destroy) {
277*47e07394SAndrew Turner 		vmm_stat_free(vcpu->stats);
278*47e07394SAndrew Turner 		fpu_save_area_free(vcpu->guestfpu);
279*47e07394SAndrew Turner 		vcpu_lock_destroy(vcpu);
280*47e07394SAndrew Turner 	}
281*47e07394SAndrew Turner }
282*47e07394SAndrew Turner 
283*47e07394SAndrew Turner static struct vcpu *
284*47e07394SAndrew Turner vcpu_alloc(struct vm *vm, int vcpu_id)
285*47e07394SAndrew Turner {
286*47e07394SAndrew Turner 	struct vcpu *vcpu;
287*47e07394SAndrew Turner 
288*47e07394SAndrew Turner 	KASSERT(vcpu_id >= 0 && vcpu_id < vm->maxcpus,
289*47e07394SAndrew Turner 	    ("vcpu_alloc: invalid vcpu %d", vcpu_id));
290*47e07394SAndrew Turner 
291*47e07394SAndrew Turner 	vcpu = malloc(sizeof(*vcpu), M_VMM, M_WAITOK | M_ZERO);
292*47e07394SAndrew Turner 	vcpu_lock_init(vcpu);
293*47e07394SAndrew Turner 	vcpu->state = VCPU_IDLE;
294*47e07394SAndrew Turner 	vcpu->hostcpu = NOCPU;
295*47e07394SAndrew Turner 	vcpu->vcpuid = vcpu_id;
296*47e07394SAndrew Turner 	vcpu->vm = vm;
297*47e07394SAndrew Turner 	vcpu->guestfpu = fpu_save_area_alloc();
298*47e07394SAndrew Turner 	vcpu->stats = vmm_stat_alloc();
299*47e07394SAndrew Turner 	return (vcpu);
300*47e07394SAndrew Turner }
301*47e07394SAndrew Turner 
302*47e07394SAndrew Turner static void
303*47e07394SAndrew Turner vcpu_init(struct vcpu *vcpu)
304*47e07394SAndrew Turner {
305*47e07394SAndrew Turner 	vcpu->cookie = vmmops_vcpu_init(vcpu->vm->cookie, vcpu, vcpu->vcpuid);
306*47e07394SAndrew Turner 	MPASS(vcpu->cookie != NULL);
307*47e07394SAndrew Turner 	fpu_save_area_reset(vcpu->guestfpu);
308*47e07394SAndrew Turner 	vmm_stat_init(vcpu->stats);
309*47e07394SAndrew Turner }
310*47e07394SAndrew Turner 
311*47e07394SAndrew Turner struct vm_exit *
312*47e07394SAndrew Turner vm_exitinfo(struct vcpu *vcpu)
313*47e07394SAndrew Turner {
314*47e07394SAndrew Turner 	return (&vcpu->exitinfo);
315*47e07394SAndrew Turner }
316*47e07394SAndrew Turner 
317*47e07394SAndrew Turner static int
318*47e07394SAndrew Turner vmm_init(void)
319*47e07394SAndrew Turner {
320*47e07394SAndrew Turner 	int error;
321*47e07394SAndrew Turner 
322*47e07394SAndrew Turner 	vm_maxcpu = mp_ncpus;
323*47e07394SAndrew Turner 	TUNABLE_INT_FETCH("hw.vmm.maxcpu", &vm_maxcpu);
324*47e07394SAndrew Turner 
325*47e07394SAndrew Turner 	if (vm_maxcpu > VM_MAXCPU) {
326*47e07394SAndrew Turner 		printf("vmm: vm_maxcpu clamped to %u\n", VM_MAXCPU);
327*47e07394SAndrew Turner 		vm_maxcpu = VM_MAXCPU;
328*47e07394SAndrew Turner 	}
329*47e07394SAndrew Turner 	if (vm_maxcpu == 0)
330*47e07394SAndrew Turner 		vm_maxcpu = 1;
331*47e07394SAndrew Turner 
332*47e07394SAndrew Turner 	error = vmm_regs_init(&vmm_arch_regs, &vmm_arch_regs_masks);
333*47e07394SAndrew Turner 	if (error != 0)
334*47e07394SAndrew Turner 		return (error);
335*47e07394SAndrew Turner 
336*47e07394SAndrew Turner 	return (vmmops_modinit(0));
337*47e07394SAndrew Turner }
338*47e07394SAndrew Turner 
339*47e07394SAndrew Turner static int
340*47e07394SAndrew Turner vmm_handler(module_t mod, int what, void *arg)
341*47e07394SAndrew Turner {
342*47e07394SAndrew Turner 	int error;
343*47e07394SAndrew Turner 
344*47e07394SAndrew Turner 	switch (what) {
345*47e07394SAndrew Turner 	case MOD_LOAD:
346*47e07394SAndrew Turner 		/* TODO: if (vmm_is_hw_supported()) { */
347*47e07394SAndrew Turner 		vmmdev_init();
348*47e07394SAndrew Turner 		error = vmm_init();
349*47e07394SAndrew Turner 		if (error == 0)
350*47e07394SAndrew Turner 			vmm_initialized = true;
351*47e07394SAndrew Turner 		break;
352*47e07394SAndrew Turner 	case MOD_UNLOAD:
353*47e07394SAndrew Turner 		/* TODO: if (vmm_is_hw_supported()) { */
354*47e07394SAndrew Turner 		error = vmmdev_cleanup();
355*47e07394SAndrew Turner 		if (error == 0 && vmm_initialized) {
356*47e07394SAndrew Turner 			error = vmmops_modcleanup();
357*47e07394SAndrew Turner 			if (error)
358*47e07394SAndrew Turner 				vmm_initialized = false;
359*47e07394SAndrew Turner 		}
360*47e07394SAndrew Turner 		break;
361*47e07394SAndrew Turner 	default:
362*47e07394SAndrew Turner 		error = 0;
363*47e07394SAndrew Turner 		break;
364*47e07394SAndrew Turner 	}
365*47e07394SAndrew Turner 	return (error);
366*47e07394SAndrew Turner }
367*47e07394SAndrew Turner 
368*47e07394SAndrew Turner static moduledata_t vmm_kmod = {
369*47e07394SAndrew Turner 	"vmm",
370*47e07394SAndrew Turner 	vmm_handler,
371*47e07394SAndrew Turner 	NULL
372*47e07394SAndrew Turner };
373*47e07394SAndrew Turner 
374*47e07394SAndrew Turner /*
375*47e07394SAndrew Turner  * vmm initialization has the following dependencies:
376*47e07394SAndrew Turner  *
377*47e07394SAndrew Turner  * - HYP initialization requires smp_rendezvous() and therefore must happen
378*47e07394SAndrew Turner  *   after SMP is fully functional (after SI_SUB_SMP).
379*47e07394SAndrew Turner  */
380*47e07394SAndrew Turner DECLARE_MODULE(vmm, vmm_kmod, SI_SUB_SMP + 1, SI_ORDER_ANY);
381*47e07394SAndrew Turner MODULE_VERSION(vmm, 1);
382*47e07394SAndrew Turner 
383*47e07394SAndrew Turner static void
384*47e07394SAndrew Turner vm_init(struct vm *vm, bool create)
385*47e07394SAndrew Turner {
386*47e07394SAndrew Turner 	int i;
387*47e07394SAndrew Turner 
388*47e07394SAndrew Turner 	vm->cookie = vmmops_init(vm, vmspace_pmap(vm->vmspace));
389*47e07394SAndrew Turner 	MPASS(vm->cookie != NULL);
390*47e07394SAndrew Turner 
391*47e07394SAndrew Turner 	CPU_ZERO(&vm->active_cpus);
392*47e07394SAndrew Turner 	CPU_ZERO(&vm->debug_cpus);
393*47e07394SAndrew Turner 
394*47e07394SAndrew Turner 	vm->suspend = 0;
395*47e07394SAndrew Turner 	CPU_ZERO(&vm->suspended_cpus);
396*47e07394SAndrew Turner 
397*47e07394SAndrew Turner 	memset(vm->mmio_region, 0, sizeof(vm->mmio_region));
398*47e07394SAndrew Turner 	memset(vm->special_reg, 0, sizeof(vm->special_reg));
399*47e07394SAndrew Turner 
400*47e07394SAndrew Turner 	if (!create) {
401*47e07394SAndrew Turner 		for (i = 0; i < vm->maxcpus; i++) {
402*47e07394SAndrew Turner 			if (vm->vcpu[i] != NULL)
403*47e07394SAndrew Turner 				vcpu_init(vm->vcpu[i]);
404*47e07394SAndrew Turner 		}
405*47e07394SAndrew Turner 	}
406*47e07394SAndrew Turner }
407*47e07394SAndrew Turner 
408*47e07394SAndrew Turner struct vcpu *
409*47e07394SAndrew Turner vm_alloc_vcpu(struct vm *vm, int vcpuid)
410*47e07394SAndrew Turner {
411*47e07394SAndrew Turner 	struct vcpu *vcpu;
412*47e07394SAndrew Turner 
413*47e07394SAndrew Turner 	if (vcpuid < 0 || vcpuid >= vm_get_maxcpus(vm))
414*47e07394SAndrew Turner 		return (NULL);
415*47e07394SAndrew Turner 
416*47e07394SAndrew Turner 	/* Some interrupt controllers may have a CPU limit */
417*47e07394SAndrew Turner 	if (vcpuid >= vgic_max_cpu_count(vm->cookie))
418*47e07394SAndrew Turner 		return (NULL);
419*47e07394SAndrew Turner 
420*47e07394SAndrew Turner 	vcpu = atomic_load_ptr(&vm->vcpu[vcpuid]);
421*47e07394SAndrew Turner 	if (__predict_true(vcpu != NULL))
422*47e07394SAndrew Turner 		return (vcpu);
423*47e07394SAndrew Turner 
424*47e07394SAndrew Turner 	sx_xlock(&vm->vcpus_init_lock);
425*47e07394SAndrew Turner 	vcpu = vm->vcpu[vcpuid];
426*47e07394SAndrew Turner 	if (vcpu == NULL/* && !vm->dying*/) {
427*47e07394SAndrew Turner 		vcpu = vcpu_alloc(vm, vcpuid);
428*47e07394SAndrew Turner 		vcpu_init(vcpu);
429*47e07394SAndrew Turner 
430*47e07394SAndrew Turner 		/*
431*47e07394SAndrew Turner 		 * Ensure vCPU is fully created before updating pointer
432*47e07394SAndrew Turner 		 * to permit unlocked reads above.
433*47e07394SAndrew Turner 		 */
434*47e07394SAndrew Turner 		atomic_store_rel_ptr((uintptr_t *)&vm->vcpu[vcpuid],
435*47e07394SAndrew Turner 		    (uintptr_t)vcpu);
436*47e07394SAndrew Turner 	}
437*47e07394SAndrew Turner 	sx_xunlock(&vm->vcpus_init_lock);
438*47e07394SAndrew Turner 	return (vcpu);
439*47e07394SAndrew Turner }
440*47e07394SAndrew Turner 
441*47e07394SAndrew Turner void
442*47e07394SAndrew Turner vm_slock_vcpus(struct vm *vm)
443*47e07394SAndrew Turner {
444*47e07394SAndrew Turner 	sx_slock(&vm->vcpus_init_lock);
445*47e07394SAndrew Turner }
446*47e07394SAndrew Turner 
447*47e07394SAndrew Turner void
448*47e07394SAndrew Turner vm_unlock_vcpus(struct vm *vm)
449*47e07394SAndrew Turner {
450*47e07394SAndrew Turner 	sx_unlock(&vm->vcpus_init_lock);
451*47e07394SAndrew Turner }
452*47e07394SAndrew Turner 
453*47e07394SAndrew Turner int
454*47e07394SAndrew Turner vm_create(const char *name, struct vm **retvm)
455*47e07394SAndrew Turner {
456*47e07394SAndrew Turner 	struct vm *vm;
457*47e07394SAndrew Turner 	struct vmspace *vmspace;
458*47e07394SAndrew Turner 
459*47e07394SAndrew Turner 	/*
460*47e07394SAndrew Turner 	 * If vmm.ko could not be successfully initialized then don't attempt
461*47e07394SAndrew Turner 	 * to create the virtual machine.
462*47e07394SAndrew Turner 	 */
463*47e07394SAndrew Turner 	if (!vmm_initialized)
464*47e07394SAndrew Turner 		return (ENXIO);
465*47e07394SAndrew Turner 
466*47e07394SAndrew Turner 	if (name == NULL || strlen(name) >= VM_MAX_NAMELEN)
467*47e07394SAndrew Turner 		return (EINVAL);
468*47e07394SAndrew Turner 
469*47e07394SAndrew Turner 	vmspace = vmmops_vmspace_alloc(0, 1ul << 39);
470*47e07394SAndrew Turner 	if (vmspace == NULL)
471*47e07394SAndrew Turner 		return (ENOMEM);
472*47e07394SAndrew Turner 
473*47e07394SAndrew Turner 	vm = malloc(sizeof(struct vm), M_VMM, M_WAITOK | M_ZERO);
474*47e07394SAndrew Turner 	strcpy(vm->name, name);
475*47e07394SAndrew Turner 	vm->vmspace = vmspace;
476*47e07394SAndrew Turner 	sx_init(&vm->mem_segs_lock, "vm mem_segs");
477*47e07394SAndrew Turner 	sx_init(&vm->vcpus_init_lock, "vm vcpus");
478*47e07394SAndrew Turner 
479*47e07394SAndrew Turner 	vm->sockets = 1;
480*47e07394SAndrew Turner 	vm->cores = 1;			/* XXX backwards compatibility */
481*47e07394SAndrew Turner 	vm->threads = 1;		/* XXX backwards compatibility */
482*47e07394SAndrew Turner 	vm->maxcpus = vm_maxcpu;
483*47e07394SAndrew Turner 
484*47e07394SAndrew Turner 	vm->vcpu = malloc(sizeof(*vm->vcpu) * vm->maxcpus, M_VMM,
485*47e07394SAndrew Turner 	    M_WAITOK | M_ZERO);
486*47e07394SAndrew Turner 
487*47e07394SAndrew Turner 	vm_init(vm, true);
488*47e07394SAndrew Turner 
489*47e07394SAndrew Turner 	*retvm = vm;
490*47e07394SAndrew Turner 	return (0);
491*47e07394SAndrew Turner }
492*47e07394SAndrew Turner 
493*47e07394SAndrew Turner void
494*47e07394SAndrew Turner vm_get_topology(struct vm *vm, uint16_t *sockets, uint16_t *cores,
495*47e07394SAndrew Turner     uint16_t *threads, uint16_t *maxcpus)
496*47e07394SAndrew Turner {
497*47e07394SAndrew Turner 	*sockets = vm->sockets;
498*47e07394SAndrew Turner 	*cores = vm->cores;
499*47e07394SAndrew Turner 	*threads = vm->threads;
500*47e07394SAndrew Turner 	*maxcpus = vm->maxcpus;
501*47e07394SAndrew Turner }
502*47e07394SAndrew Turner 
503*47e07394SAndrew Turner uint16_t
504*47e07394SAndrew Turner vm_get_maxcpus(struct vm *vm)
505*47e07394SAndrew Turner {
506*47e07394SAndrew Turner 	return (vm->maxcpus);
507*47e07394SAndrew Turner }
508*47e07394SAndrew Turner 
509*47e07394SAndrew Turner int
510*47e07394SAndrew Turner vm_set_topology(struct vm *vm, uint16_t sockets, uint16_t cores,
511*47e07394SAndrew Turner     uint16_t threads, uint16_t maxcpus)
512*47e07394SAndrew Turner {
513*47e07394SAndrew Turner 	/* Ignore maxcpus. */
514*47e07394SAndrew Turner 	if ((sockets * cores * threads) > vm->maxcpus)
515*47e07394SAndrew Turner 		return (EINVAL);
516*47e07394SAndrew Turner 	vm->sockets = sockets;
517*47e07394SAndrew Turner 	vm->cores = cores;
518*47e07394SAndrew Turner 	vm->threads = threads;
519*47e07394SAndrew Turner 	return(0);
520*47e07394SAndrew Turner }
521*47e07394SAndrew Turner 
522*47e07394SAndrew Turner static void
523*47e07394SAndrew Turner vm_cleanup(struct vm *vm, bool destroy)
524*47e07394SAndrew Turner {
525*47e07394SAndrew Turner 	struct mem_map *mm;
526*47e07394SAndrew Turner 	pmap_t pmap __diagused;
527*47e07394SAndrew Turner 	int i;
528*47e07394SAndrew Turner 
529*47e07394SAndrew Turner 	if (destroy) {
530*47e07394SAndrew Turner 		pmap = vmspace_pmap(vm->vmspace);
531*47e07394SAndrew Turner 		sched_pin();
532*47e07394SAndrew Turner 		PCPU_SET(curvmpmap, NULL);
533*47e07394SAndrew Turner 		sched_unpin();
534*47e07394SAndrew Turner 		CPU_FOREACH(i) {
535*47e07394SAndrew Turner 			MPASS(cpuid_to_pcpu[i]->pc_curvmpmap != pmap);
536*47e07394SAndrew Turner 		}
537*47e07394SAndrew Turner 	}
538*47e07394SAndrew Turner 
539*47e07394SAndrew Turner 	vgic_detach_from_vm(vm->cookie);
540*47e07394SAndrew Turner 
541*47e07394SAndrew Turner 	for (i = 0; i < vm->maxcpus; i++) {
542*47e07394SAndrew Turner 		if (vm->vcpu[i] != NULL)
543*47e07394SAndrew Turner 			vcpu_cleanup(vm->vcpu[i], destroy);
544*47e07394SAndrew Turner 	}
545*47e07394SAndrew Turner 
546*47e07394SAndrew Turner 	vmmops_cleanup(vm->cookie);
547*47e07394SAndrew Turner 
548*47e07394SAndrew Turner 	/*
549*47e07394SAndrew Turner 	 * System memory is removed from the guest address space only when
550*47e07394SAndrew Turner 	 * the VM is destroyed. This is because the mapping remains the same
551*47e07394SAndrew Turner 	 * across VM reset.
552*47e07394SAndrew Turner 	 *
553*47e07394SAndrew Turner 	 * Device memory can be relocated by the guest (e.g. using PCI BARs)
554*47e07394SAndrew Turner 	 * so those mappings are removed on a VM reset.
555*47e07394SAndrew Turner 	 */
556*47e07394SAndrew Turner 	if (!destroy) {
557*47e07394SAndrew Turner 		for (i = 0; i < VM_MAX_MEMMAPS; i++) {
558*47e07394SAndrew Turner 			mm = &vm->mem_maps[i];
559*47e07394SAndrew Turner 			if (destroy || !sysmem_mapping(vm, mm))
560*47e07394SAndrew Turner 				vm_free_memmap(vm, i);
561*47e07394SAndrew Turner 		}
562*47e07394SAndrew Turner 	}
563*47e07394SAndrew Turner 
564*47e07394SAndrew Turner 	if (destroy) {
565*47e07394SAndrew Turner 		for (i = 0; i < VM_MAX_MEMSEGS; i++)
566*47e07394SAndrew Turner 			vm_free_memseg(vm, i);
567*47e07394SAndrew Turner 
568*47e07394SAndrew Turner 		vmmops_vmspace_free(vm->vmspace);
569*47e07394SAndrew Turner 		vm->vmspace = NULL;
570*47e07394SAndrew Turner 
571*47e07394SAndrew Turner 		for (i = 0; i < vm->maxcpus; i++)
572*47e07394SAndrew Turner 			free(vm->vcpu[i], M_VMM);
573*47e07394SAndrew Turner 		free(vm->vcpu, M_VMM);
574*47e07394SAndrew Turner 		sx_destroy(&vm->vcpus_init_lock);
575*47e07394SAndrew Turner 		sx_destroy(&vm->mem_segs_lock);
576*47e07394SAndrew Turner 	}
577*47e07394SAndrew Turner }
578*47e07394SAndrew Turner 
579*47e07394SAndrew Turner void
580*47e07394SAndrew Turner vm_destroy(struct vm *vm)
581*47e07394SAndrew Turner {
582*47e07394SAndrew Turner 	vm_cleanup(vm, true);
583*47e07394SAndrew Turner 	free(vm, M_VMM);
584*47e07394SAndrew Turner }
585*47e07394SAndrew Turner 
586*47e07394SAndrew Turner int
587*47e07394SAndrew Turner vm_reinit(struct vm *vm)
588*47e07394SAndrew Turner {
589*47e07394SAndrew Turner 	int error;
590*47e07394SAndrew Turner 
591*47e07394SAndrew Turner 	/*
592*47e07394SAndrew Turner 	 * A virtual machine can be reset only if all vcpus are suspended.
593*47e07394SAndrew Turner 	 */
594*47e07394SAndrew Turner 	if (CPU_CMP(&vm->suspended_cpus, &vm->active_cpus) == 0) {
595*47e07394SAndrew Turner 		vm_cleanup(vm, false);
596*47e07394SAndrew Turner 		vm_init(vm, false);
597*47e07394SAndrew Turner 		error = 0;
598*47e07394SAndrew Turner 	} else {
599*47e07394SAndrew Turner 		error = EBUSY;
600*47e07394SAndrew Turner 	}
601*47e07394SAndrew Turner 
602*47e07394SAndrew Turner 	return (error);
603*47e07394SAndrew Turner }
604*47e07394SAndrew Turner 
605*47e07394SAndrew Turner const char *
606*47e07394SAndrew Turner vm_name(struct vm *vm)
607*47e07394SAndrew Turner {
608*47e07394SAndrew Turner 	return (vm->name);
609*47e07394SAndrew Turner }
610*47e07394SAndrew Turner 
611*47e07394SAndrew Turner void
612*47e07394SAndrew Turner vm_slock_memsegs(struct vm *vm)
613*47e07394SAndrew Turner {
614*47e07394SAndrew Turner 	sx_slock(&vm->mem_segs_lock);
615*47e07394SAndrew Turner }
616*47e07394SAndrew Turner 
617*47e07394SAndrew Turner void
618*47e07394SAndrew Turner vm_xlock_memsegs(struct vm *vm)
619*47e07394SAndrew Turner {
620*47e07394SAndrew Turner 	sx_xlock(&vm->mem_segs_lock);
621*47e07394SAndrew Turner }
622*47e07394SAndrew Turner 
623*47e07394SAndrew Turner void
624*47e07394SAndrew Turner vm_unlock_memsegs(struct vm *vm)
625*47e07394SAndrew Turner {
626*47e07394SAndrew Turner 	sx_unlock(&vm->mem_segs_lock);
627*47e07394SAndrew Turner }
628*47e07394SAndrew Turner 
629*47e07394SAndrew Turner /*
630*47e07394SAndrew Turner  * Return 'true' if 'gpa' is allocated in the guest address space.
631*47e07394SAndrew Turner  *
632*47e07394SAndrew Turner  * This function is called in the context of a running vcpu which acts as
633*47e07394SAndrew Turner  * an implicit lock on 'vm->mem_maps[]'.
634*47e07394SAndrew Turner  */
635*47e07394SAndrew Turner bool
636*47e07394SAndrew Turner vm_mem_allocated(struct vcpu *vcpu, vm_paddr_t gpa)
637*47e07394SAndrew Turner {
638*47e07394SAndrew Turner 	struct vm *vm = vcpu->vm;
639*47e07394SAndrew Turner 	struct mem_map *mm;
640*47e07394SAndrew Turner 	int i;
641*47e07394SAndrew Turner 
642*47e07394SAndrew Turner #ifdef INVARIANTS
643*47e07394SAndrew Turner 	int hostcpu, state;
644*47e07394SAndrew Turner 	state = vcpu_get_state(vcpu, &hostcpu);
645*47e07394SAndrew Turner 	KASSERT(state == VCPU_RUNNING && hostcpu == curcpu,
646*47e07394SAndrew Turner 	    ("%s: invalid vcpu state %d/%d", __func__, state, hostcpu));
647*47e07394SAndrew Turner #endif
648*47e07394SAndrew Turner 
649*47e07394SAndrew Turner 	for (i = 0; i < VM_MAX_MEMMAPS; i++) {
650*47e07394SAndrew Turner 		mm = &vm->mem_maps[i];
651*47e07394SAndrew Turner 		if (mm->len != 0 && gpa >= mm->gpa && gpa < mm->gpa + mm->len)
652*47e07394SAndrew Turner 			return (true);		/* 'gpa' is sysmem or devmem */
653*47e07394SAndrew Turner 	}
654*47e07394SAndrew Turner 
655*47e07394SAndrew Turner 	return (false);
656*47e07394SAndrew Turner }
657*47e07394SAndrew Turner 
658*47e07394SAndrew Turner int
659*47e07394SAndrew Turner vm_alloc_memseg(struct vm *vm, int ident, size_t len, bool sysmem)
660*47e07394SAndrew Turner {
661*47e07394SAndrew Turner 	struct mem_seg *seg;
662*47e07394SAndrew Turner 	vm_object_t obj;
663*47e07394SAndrew Turner 
664*47e07394SAndrew Turner 	sx_assert(&vm->mem_segs_lock, SX_XLOCKED);
665*47e07394SAndrew Turner 
666*47e07394SAndrew Turner 	if (ident < 0 || ident >= VM_MAX_MEMSEGS)
667*47e07394SAndrew Turner 		return (EINVAL);
668*47e07394SAndrew Turner 
669*47e07394SAndrew Turner 	if (len == 0 || (len & PAGE_MASK))
670*47e07394SAndrew Turner 		return (EINVAL);
671*47e07394SAndrew Turner 
672*47e07394SAndrew Turner 	seg = &vm->mem_segs[ident];
673*47e07394SAndrew Turner 	if (seg->object != NULL) {
674*47e07394SAndrew Turner 		if (seg->len == len && seg->sysmem == sysmem)
675*47e07394SAndrew Turner 			return (EEXIST);
676*47e07394SAndrew Turner 		else
677*47e07394SAndrew Turner 			return (EINVAL);
678*47e07394SAndrew Turner 	}
679*47e07394SAndrew Turner 
680*47e07394SAndrew Turner 	obj = vm_object_allocate(OBJT_DEFAULT, len >> PAGE_SHIFT);
681*47e07394SAndrew Turner 	if (obj == NULL)
682*47e07394SAndrew Turner 		return (ENOMEM);
683*47e07394SAndrew Turner 
684*47e07394SAndrew Turner 	seg->len = len;
685*47e07394SAndrew Turner 	seg->object = obj;
686*47e07394SAndrew Turner 	seg->sysmem = sysmem;
687*47e07394SAndrew Turner 	return (0);
688*47e07394SAndrew Turner }
689*47e07394SAndrew Turner 
690*47e07394SAndrew Turner int
691*47e07394SAndrew Turner vm_get_memseg(struct vm *vm, int ident, size_t *len, bool *sysmem,
692*47e07394SAndrew Turner     vm_object_t *objptr)
693*47e07394SAndrew Turner {
694*47e07394SAndrew Turner 	struct mem_seg *seg;
695*47e07394SAndrew Turner 
696*47e07394SAndrew Turner 	sx_assert(&vm->mem_segs_lock, SX_LOCKED);
697*47e07394SAndrew Turner 
698*47e07394SAndrew Turner 	if (ident < 0 || ident >= VM_MAX_MEMSEGS)
699*47e07394SAndrew Turner 		return (EINVAL);
700*47e07394SAndrew Turner 
701*47e07394SAndrew Turner 	seg = &vm->mem_segs[ident];
702*47e07394SAndrew Turner 	if (len)
703*47e07394SAndrew Turner 		*len = seg->len;
704*47e07394SAndrew Turner 	if (sysmem)
705*47e07394SAndrew Turner 		*sysmem = seg->sysmem;
706*47e07394SAndrew Turner 	if (objptr)
707*47e07394SAndrew Turner 		*objptr = seg->object;
708*47e07394SAndrew Turner 	return (0);
709*47e07394SAndrew Turner }
710*47e07394SAndrew Turner 
711*47e07394SAndrew Turner void
712*47e07394SAndrew Turner vm_free_memseg(struct vm *vm, int ident)
713*47e07394SAndrew Turner {
714*47e07394SAndrew Turner 	struct mem_seg *seg;
715*47e07394SAndrew Turner 
716*47e07394SAndrew Turner 	KASSERT(ident >= 0 && ident < VM_MAX_MEMSEGS,
717*47e07394SAndrew Turner 	    ("%s: invalid memseg ident %d", __func__, ident));
718*47e07394SAndrew Turner 
719*47e07394SAndrew Turner 	seg = &vm->mem_segs[ident];
720*47e07394SAndrew Turner 	if (seg->object != NULL) {
721*47e07394SAndrew Turner 		vm_object_deallocate(seg->object);
722*47e07394SAndrew Turner 		bzero(seg, sizeof(struct mem_seg));
723*47e07394SAndrew Turner 	}
724*47e07394SAndrew Turner }
725*47e07394SAndrew Turner 
726*47e07394SAndrew Turner int
727*47e07394SAndrew Turner vm_mmap_memseg(struct vm *vm, vm_paddr_t gpa, int segid, vm_ooffset_t first,
728*47e07394SAndrew Turner     size_t len, int prot, int flags)
729*47e07394SAndrew Turner {
730*47e07394SAndrew Turner 	struct mem_seg *seg;
731*47e07394SAndrew Turner 	struct mem_map *m, *map;
732*47e07394SAndrew Turner 	vm_ooffset_t last;
733*47e07394SAndrew Turner 	int i, error;
734*47e07394SAndrew Turner 
735*47e07394SAndrew Turner 	if (prot == 0 || (prot & ~(VM_PROT_ALL)) != 0)
736*47e07394SAndrew Turner 		return (EINVAL);
737*47e07394SAndrew Turner 
738*47e07394SAndrew Turner 	if (flags & ~VM_MEMMAP_F_WIRED)
739*47e07394SAndrew Turner 		return (EINVAL);
740*47e07394SAndrew Turner 
741*47e07394SAndrew Turner 	if (segid < 0 || segid >= VM_MAX_MEMSEGS)
742*47e07394SAndrew Turner 		return (EINVAL);
743*47e07394SAndrew Turner 
744*47e07394SAndrew Turner 	seg = &vm->mem_segs[segid];
745*47e07394SAndrew Turner 	if (seg->object == NULL)
746*47e07394SAndrew Turner 		return (EINVAL);
747*47e07394SAndrew Turner 
748*47e07394SAndrew Turner 	last = first + len;
749*47e07394SAndrew Turner 	if (first < 0 || first >= last || last > seg->len)
750*47e07394SAndrew Turner 		return (EINVAL);
751*47e07394SAndrew Turner 
752*47e07394SAndrew Turner 	if ((gpa | first | last) & PAGE_MASK)
753*47e07394SAndrew Turner 		return (EINVAL);
754*47e07394SAndrew Turner 
755*47e07394SAndrew Turner 	map = NULL;
756*47e07394SAndrew Turner 	for (i = 0; i < VM_MAX_MEMMAPS; i++) {
757*47e07394SAndrew Turner 		m = &vm->mem_maps[i];
758*47e07394SAndrew Turner 		if (m->len == 0) {
759*47e07394SAndrew Turner 			map = m;
760*47e07394SAndrew Turner 			break;
761*47e07394SAndrew Turner 		}
762*47e07394SAndrew Turner 	}
763*47e07394SAndrew Turner 
764*47e07394SAndrew Turner 	if (map == NULL)
765*47e07394SAndrew Turner 		return (ENOSPC);
766*47e07394SAndrew Turner 
767*47e07394SAndrew Turner 	error = vm_map_find(&vm->vmspace->vm_map, seg->object, first, &gpa,
768*47e07394SAndrew Turner 	    len, 0, VMFS_NO_SPACE, prot, prot, 0);
769*47e07394SAndrew Turner 	if (error != KERN_SUCCESS)
770*47e07394SAndrew Turner 		return (EFAULT);
771*47e07394SAndrew Turner 
772*47e07394SAndrew Turner 	vm_object_reference(seg->object);
773*47e07394SAndrew Turner 
774*47e07394SAndrew Turner 	if (flags & VM_MEMMAP_F_WIRED) {
775*47e07394SAndrew Turner 		error = vm_map_wire(&vm->vmspace->vm_map, gpa, gpa + len,
776*47e07394SAndrew Turner 		    VM_MAP_WIRE_USER | VM_MAP_WIRE_NOHOLES);
777*47e07394SAndrew Turner 		if (error != KERN_SUCCESS) {
778*47e07394SAndrew Turner 			vm_map_remove(&vm->vmspace->vm_map, gpa, gpa + len);
779*47e07394SAndrew Turner 			return (error == KERN_RESOURCE_SHORTAGE ? ENOMEM :
780*47e07394SAndrew Turner 			    EFAULT);
781*47e07394SAndrew Turner 		}
782*47e07394SAndrew Turner 	}
783*47e07394SAndrew Turner 
784*47e07394SAndrew Turner 	map->gpa = gpa;
785*47e07394SAndrew Turner 	map->len = len;
786*47e07394SAndrew Turner 	map->segoff = first;
787*47e07394SAndrew Turner 	map->segid = segid;
788*47e07394SAndrew Turner 	map->prot = prot;
789*47e07394SAndrew Turner 	map->flags = flags;
790*47e07394SAndrew Turner 	return (0);
791*47e07394SAndrew Turner }
792*47e07394SAndrew Turner 
793*47e07394SAndrew Turner int
794*47e07394SAndrew Turner vm_munmap_memseg(struct vm *vm, vm_paddr_t gpa, size_t len)
795*47e07394SAndrew Turner {
796*47e07394SAndrew Turner 	struct mem_map *m;
797*47e07394SAndrew Turner 	int i;
798*47e07394SAndrew Turner 
799*47e07394SAndrew Turner 	for (i = 0; i < VM_MAX_MEMMAPS; i++) {
800*47e07394SAndrew Turner 		m = &vm->mem_maps[i];
801*47e07394SAndrew Turner 		if (m->gpa == gpa && m->len == len) {
802*47e07394SAndrew Turner 			vm_free_memmap(vm, i);
803*47e07394SAndrew Turner 			return (0);
804*47e07394SAndrew Turner 		}
805*47e07394SAndrew Turner 	}
806*47e07394SAndrew Turner 
807*47e07394SAndrew Turner 	return (EINVAL);
808*47e07394SAndrew Turner }
809*47e07394SAndrew Turner 
810*47e07394SAndrew Turner int
811*47e07394SAndrew Turner vm_mmap_getnext(struct vm *vm, vm_paddr_t *gpa, int *segid,
812*47e07394SAndrew Turner     vm_ooffset_t *segoff, size_t *len, int *prot, int *flags)
813*47e07394SAndrew Turner {
814*47e07394SAndrew Turner 	struct mem_map *mm, *mmnext;
815*47e07394SAndrew Turner 	int i;
816*47e07394SAndrew Turner 
817*47e07394SAndrew Turner 	mmnext = NULL;
818*47e07394SAndrew Turner 	for (i = 0; i < VM_MAX_MEMMAPS; i++) {
819*47e07394SAndrew Turner 		mm = &vm->mem_maps[i];
820*47e07394SAndrew Turner 		if (mm->len == 0 || mm->gpa < *gpa)
821*47e07394SAndrew Turner 			continue;
822*47e07394SAndrew Turner 		if (mmnext == NULL || mm->gpa < mmnext->gpa)
823*47e07394SAndrew Turner 			mmnext = mm;
824*47e07394SAndrew Turner 	}
825*47e07394SAndrew Turner 
826*47e07394SAndrew Turner 	if (mmnext != NULL) {
827*47e07394SAndrew Turner 		*gpa = mmnext->gpa;
828*47e07394SAndrew Turner 		if (segid)
829*47e07394SAndrew Turner 			*segid = mmnext->segid;
830*47e07394SAndrew Turner 		if (segoff)
831*47e07394SAndrew Turner 			*segoff = mmnext->segoff;
832*47e07394SAndrew Turner 		if (len)
833*47e07394SAndrew Turner 			*len = mmnext->len;
834*47e07394SAndrew Turner 		if (prot)
835*47e07394SAndrew Turner 			*prot = mmnext->prot;
836*47e07394SAndrew Turner 		if (flags)
837*47e07394SAndrew Turner 			*flags = mmnext->flags;
838*47e07394SAndrew Turner 		return (0);
839*47e07394SAndrew Turner 	} else {
840*47e07394SAndrew Turner 		return (ENOENT);
841*47e07394SAndrew Turner 	}
842*47e07394SAndrew Turner }
843*47e07394SAndrew Turner 
844*47e07394SAndrew Turner static void
845*47e07394SAndrew Turner vm_free_memmap(struct vm *vm, int ident)
846*47e07394SAndrew Turner {
847*47e07394SAndrew Turner 	struct mem_map *mm;
848*47e07394SAndrew Turner 	int error __diagused;
849*47e07394SAndrew Turner 
850*47e07394SAndrew Turner 	mm = &vm->mem_maps[ident];
851*47e07394SAndrew Turner 	if (mm->len) {
852*47e07394SAndrew Turner 		error = vm_map_remove(&vm->vmspace->vm_map, mm->gpa,
853*47e07394SAndrew Turner 		    mm->gpa + mm->len);
854*47e07394SAndrew Turner 		KASSERT(error == KERN_SUCCESS, ("%s: vm_map_remove error %d",
855*47e07394SAndrew Turner 		    __func__, error));
856*47e07394SAndrew Turner 		bzero(mm, sizeof(struct mem_map));
857*47e07394SAndrew Turner 	}
858*47e07394SAndrew Turner }
859*47e07394SAndrew Turner 
860*47e07394SAndrew Turner static __inline bool
861*47e07394SAndrew Turner sysmem_mapping(struct vm *vm, struct mem_map *mm)
862*47e07394SAndrew Turner {
863*47e07394SAndrew Turner 
864*47e07394SAndrew Turner 	if (mm->len != 0 && vm->mem_segs[mm->segid].sysmem)
865*47e07394SAndrew Turner 		return (true);
866*47e07394SAndrew Turner 	else
867*47e07394SAndrew Turner 		return (false);
868*47e07394SAndrew Turner }
869*47e07394SAndrew Turner 
870*47e07394SAndrew Turner vm_paddr_t
871*47e07394SAndrew Turner vmm_sysmem_maxaddr(struct vm *vm)
872*47e07394SAndrew Turner {
873*47e07394SAndrew Turner 	struct mem_map *mm;
874*47e07394SAndrew Turner 	vm_paddr_t maxaddr;
875*47e07394SAndrew Turner 	int i;
876*47e07394SAndrew Turner 
877*47e07394SAndrew Turner 	maxaddr = 0;
878*47e07394SAndrew Turner 	for (i = 0; i < VM_MAX_MEMMAPS; i++) {
879*47e07394SAndrew Turner 		mm = &vm->mem_maps[i];
880*47e07394SAndrew Turner 		if (sysmem_mapping(vm, mm)) {
881*47e07394SAndrew Turner 			if (maxaddr < mm->gpa + mm->len)
882*47e07394SAndrew Turner 				maxaddr = mm->gpa + mm->len;
883*47e07394SAndrew Turner 		}
884*47e07394SAndrew Turner 	}
885*47e07394SAndrew Turner 	return (maxaddr);
886*47e07394SAndrew Turner }
887*47e07394SAndrew Turner 
888*47e07394SAndrew Turner int
889*47e07394SAndrew Turner vm_gla2gpa_nofault(struct vcpu *vcpu, struct vm_guest_paging *paging,
890*47e07394SAndrew Turner     uint64_t gla, int prot, uint64_t *gpa, int *is_fault)
891*47e07394SAndrew Turner {
892*47e07394SAndrew Turner 
893*47e07394SAndrew Turner 	vmmops_gla2gpa(vcpu->cookie, paging, gla, prot, gpa, is_fault);
894*47e07394SAndrew Turner 	return (0);
895*47e07394SAndrew Turner }
896*47e07394SAndrew Turner 
897*47e07394SAndrew Turner static int
898*47e07394SAndrew Turner vmm_reg_raz(struct vcpu *vcpu, uint64_t *rval, void *arg)
899*47e07394SAndrew Turner {
900*47e07394SAndrew Turner 	*rval = 0;
901*47e07394SAndrew Turner 	return (0);
902*47e07394SAndrew Turner }
903*47e07394SAndrew Turner 
904*47e07394SAndrew Turner static int
905*47e07394SAndrew Turner vmm_reg_read_arg(struct vcpu *vcpu, uint64_t *rval, void *arg)
906*47e07394SAndrew Turner {
907*47e07394SAndrew Turner 	*rval = *(uint64_t *)arg;
908*47e07394SAndrew Turner 	return (0);
909*47e07394SAndrew Turner }
910*47e07394SAndrew Turner 
911*47e07394SAndrew Turner static int
912*47e07394SAndrew Turner vmm_reg_wi(struct vcpu *vcpu, uint64_t wval, void *arg)
913*47e07394SAndrew Turner {
914*47e07394SAndrew Turner 	return (0);
915*47e07394SAndrew Turner }
916*47e07394SAndrew Turner 
917*47e07394SAndrew Turner static const struct vmm_special_reg vmm_special_regs[] = {
918*47e07394SAndrew Turner #define	SPECIAL_REG(_reg, _read, _write)				\
919*47e07394SAndrew Turner 	{								\
920*47e07394SAndrew Turner 		.esr_iss = ((_reg ## _op0) << ISS_MSR_OP0_SHIFT) |	\
921*47e07394SAndrew Turner 		    ((_reg ## _op1) << ISS_MSR_OP1_SHIFT) |		\
922*47e07394SAndrew Turner 		    ((_reg ## _CRn) << ISS_MSR_CRn_SHIFT) |		\
923*47e07394SAndrew Turner 		    ((_reg ## _CRm) << ISS_MSR_CRm_SHIFT) |		\
924*47e07394SAndrew Turner 		    ((_reg ## _op2) << ISS_MSR_OP2_SHIFT),		\
925*47e07394SAndrew Turner 		.esr_mask = ISS_MSR_REG_MASK,				\
926*47e07394SAndrew Turner 		.reg_read = (_read),					\
927*47e07394SAndrew Turner 		.reg_write = (_write),					\
928*47e07394SAndrew Turner 		.arg = NULL,						\
929*47e07394SAndrew Turner 	}
930*47e07394SAndrew Turner #define	ID_SPECIAL_REG(_reg, _name)					\
931*47e07394SAndrew Turner 	{								\
932*47e07394SAndrew Turner 		.esr_iss = ((_reg ## _op0) << ISS_MSR_OP0_SHIFT) |	\
933*47e07394SAndrew Turner 		    ((_reg ## _op1) << ISS_MSR_OP1_SHIFT) |		\
934*47e07394SAndrew Turner 		    ((_reg ## _CRn) << ISS_MSR_CRn_SHIFT) |		\
935*47e07394SAndrew Turner 		    ((_reg ## _CRm) << ISS_MSR_CRm_SHIFT) |		\
936*47e07394SAndrew Turner 		    ((_reg ## _op2) << ISS_MSR_OP2_SHIFT),		\
937*47e07394SAndrew Turner 		.esr_mask = ISS_MSR_REG_MASK,				\
938*47e07394SAndrew Turner 		.reg_read = vmm_reg_read_arg,				\
939*47e07394SAndrew Turner 		.reg_write = vmm_reg_wi,				\
940*47e07394SAndrew Turner 		.arg = &(vmm_arch_regs._name),				\
941*47e07394SAndrew Turner 	}
942*47e07394SAndrew Turner 
943*47e07394SAndrew Turner 	/* ID registers */
944*47e07394SAndrew Turner 	ID_SPECIAL_REG(ID_AA64PFR0_EL1, id_aa64pfr0),
945*47e07394SAndrew Turner 	ID_SPECIAL_REG(ID_AA64DFR0_EL1, id_aa64dfr0),
946*47e07394SAndrew Turner 	ID_SPECIAL_REG(ID_AA64ISAR0_EL1, id_aa64isar0),
947*47e07394SAndrew Turner 	ID_SPECIAL_REG(ID_AA64MMFR0_EL1, id_aa64mmfr0),
948*47e07394SAndrew Turner 	ID_SPECIAL_REG(ID_AA64MMFR1_EL1, id_aa64mmfr1),
949*47e07394SAndrew Turner 
950*47e07394SAndrew Turner 	/*
951*47e07394SAndrew Turner 	 * All other ID registers are read as zero.
952*47e07394SAndrew Turner 	 * They are all in the op0=3, op1=0, CRn=0, CRm={0..7} space.
953*47e07394SAndrew Turner 	 */
954*47e07394SAndrew Turner 	{
955*47e07394SAndrew Turner 		.esr_iss = (3 << ISS_MSR_OP0_SHIFT) |
956*47e07394SAndrew Turner 		    (0 << ISS_MSR_OP1_SHIFT) |
957*47e07394SAndrew Turner 		    (0 << ISS_MSR_CRn_SHIFT) |
958*47e07394SAndrew Turner 		    (0 << ISS_MSR_CRm_SHIFT),
959*47e07394SAndrew Turner 		.esr_mask = ISS_MSR_OP0_MASK | ISS_MSR_OP1_MASK |
960*47e07394SAndrew Turner 		    ISS_MSR_CRn_MASK | (0x8 << ISS_MSR_CRm_SHIFT),
961*47e07394SAndrew Turner 		.reg_read = vmm_reg_raz,
962*47e07394SAndrew Turner 		.reg_write = vmm_reg_wi,
963*47e07394SAndrew Turner 		.arg = NULL,
964*47e07394SAndrew Turner 	},
965*47e07394SAndrew Turner 
966*47e07394SAndrew Turner 	/* Counter physical registers */
967*47e07394SAndrew Turner 	SPECIAL_REG(CNTP_CTL_EL0, vtimer_phys_ctl_read, vtimer_phys_ctl_write),
968*47e07394SAndrew Turner 	SPECIAL_REG(CNTP_CVAL_EL0, vtimer_phys_cval_read,
969*47e07394SAndrew Turner 	    vtimer_phys_cval_write),
970*47e07394SAndrew Turner 	SPECIAL_REG(CNTP_TVAL_EL0, vtimer_phys_tval_read,
971*47e07394SAndrew Turner 	    vtimer_phys_tval_write),
972*47e07394SAndrew Turner 	SPECIAL_REG(CNTPCT_EL0, vtimer_phys_cnt_read, vtimer_phys_cnt_write),
973*47e07394SAndrew Turner #undef SPECIAL_REG
974*47e07394SAndrew Turner };
975*47e07394SAndrew Turner 
976*47e07394SAndrew Turner void
977*47e07394SAndrew Turner vm_register_reg_handler(struct vm *vm, uint64_t iss, uint64_t mask,
978*47e07394SAndrew Turner     reg_read_t reg_read, reg_write_t reg_write, void *arg)
979*47e07394SAndrew Turner {
980*47e07394SAndrew Turner 	int i;
981*47e07394SAndrew Turner 
982*47e07394SAndrew Turner 	for (i = 0; i < nitems(vm->special_reg); i++) {
983*47e07394SAndrew Turner 		if (vm->special_reg[i].esr_iss == 0 &&
984*47e07394SAndrew Turner 		    vm->special_reg[i].esr_mask == 0) {
985*47e07394SAndrew Turner 			vm->special_reg[i].esr_iss = iss;
986*47e07394SAndrew Turner 			vm->special_reg[i].esr_mask = mask;
987*47e07394SAndrew Turner 			vm->special_reg[i].reg_read = reg_read;
988*47e07394SAndrew Turner 			vm->special_reg[i].reg_write = reg_write;
989*47e07394SAndrew Turner 			vm->special_reg[i].arg = arg;
990*47e07394SAndrew Turner 			return;
991*47e07394SAndrew Turner 		}
992*47e07394SAndrew Turner 	}
993*47e07394SAndrew Turner 
994*47e07394SAndrew Turner 	panic("%s: No free special register slot", __func__);
995*47e07394SAndrew Turner }
996*47e07394SAndrew Turner 
997*47e07394SAndrew Turner void
998*47e07394SAndrew Turner vm_deregister_reg_handler(struct vm *vm, uint64_t iss, uint64_t mask)
999*47e07394SAndrew Turner {
1000*47e07394SAndrew Turner 	int i;
1001*47e07394SAndrew Turner 
1002*47e07394SAndrew Turner 	for (i = 0; i < nitems(vm->special_reg); i++) {
1003*47e07394SAndrew Turner 		if (vm->special_reg[i].esr_iss == iss &&
1004*47e07394SAndrew Turner 		    vm->special_reg[i].esr_mask == mask) {
1005*47e07394SAndrew Turner 			memset(&vm->special_reg[i], 0,
1006*47e07394SAndrew Turner 			    sizeof(vm->special_reg[i]));
1007*47e07394SAndrew Turner 			return;
1008*47e07394SAndrew Turner 		}
1009*47e07394SAndrew Turner 	}
1010*47e07394SAndrew Turner 
1011*47e07394SAndrew Turner 	panic("%s: Invalid special register: iss %lx mask %lx", __func__, iss,
1012*47e07394SAndrew Turner 	    mask);
1013*47e07394SAndrew Turner }
1014*47e07394SAndrew Turner 
1015*47e07394SAndrew Turner static int
1016*47e07394SAndrew Turner vm_handle_reg_emul(struct vcpu *vcpu, bool *retu)
1017*47e07394SAndrew Turner {
1018*47e07394SAndrew Turner 	struct vm *vm;
1019*47e07394SAndrew Turner 	struct vm_exit *vme;
1020*47e07394SAndrew Turner 	struct vre *vre;
1021*47e07394SAndrew Turner 	int i, rv;
1022*47e07394SAndrew Turner 
1023*47e07394SAndrew Turner 	vm = vcpu->vm;
1024*47e07394SAndrew Turner 	vme = &vcpu->exitinfo;
1025*47e07394SAndrew Turner 	vre = &vme->u.reg_emul.vre;
1026*47e07394SAndrew Turner 
1027*47e07394SAndrew Turner 	for (i = 0; i < nitems(vm->special_reg); i++) {
1028*47e07394SAndrew Turner 		if (vm->special_reg[i].esr_iss == 0 &&
1029*47e07394SAndrew Turner 		    vm->special_reg[i].esr_mask == 0)
1030*47e07394SAndrew Turner 			continue;
1031*47e07394SAndrew Turner 
1032*47e07394SAndrew Turner 		if ((vre->inst_syndrome & vm->special_reg[i].esr_mask) ==
1033*47e07394SAndrew Turner 		    vm->special_reg[i].esr_iss) {
1034*47e07394SAndrew Turner 			rv = vmm_emulate_register(vcpu, vre,
1035*47e07394SAndrew Turner 			    vm->special_reg[i].reg_read,
1036*47e07394SAndrew Turner 			    vm->special_reg[i].reg_write,
1037*47e07394SAndrew Turner 			    vm->special_reg[i].arg);
1038*47e07394SAndrew Turner 			if (rv == 0) {
1039*47e07394SAndrew Turner 				*retu = false;
1040*47e07394SAndrew Turner 			}
1041*47e07394SAndrew Turner 			return (rv);
1042*47e07394SAndrew Turner 		}
1043*47e07394SAndrew Turner 	}
1044*47e07394SAndrew Turner 	for (i = 0; i < nitems(vmm_special_regs); i++) {
1045*47e07394SAndrew Turner 		if ((vre->inst_syndrome & vmm_special_regs[i].esr_mask) ==
1046*47e07394SAndrew Turner 		    vmm_special_regs[i].esr_iss) {
1047*47e07394SAndrew Turner 			rv = vmm_emulate_register(vcpu, vre,
1048*47e07394SAndrew Turner 			    vmm_special_regs[i].reg_read,
1049*47e07394SAndrew Turner 			    vmm_special_regs[i].reg_write,
1050*47e07394SAndrew Turner 			    vmm_special_regs[i].arg);
1051*47e07394SAndrew Turner 			if (rv == 0) {
1052*47e07394SAndrew Turner 				*retu = false;
1053*47e07394SAndrew Turner 			}
1054*47e07394SAndrew Turner 			return (rv);
1055*47e07394SAndrew Turner 		}
1056*47e07394SAndrew Turner 	}
1057*47e07394SAndrew Turner 
1058*47e07394SAndrew Turner 
1059*47e07394SAndrew Turner 	*retu = true;
1060*47e07394SAndrew Turner 	return (0);
1061*47e07394SAndrew Turner }
1062*47e07394SAndrew Turner 
1063*47e07394SAndrew Turner void
1064*47e07394SAndrew Turner vm_register_inst_handler(struct vm *vm, uint64_t start, uint64_t size,
1065*47e07394SAndrew Turner     mem_region_read_t mmio_read, mem_region_write_t mmio_write)
1066*47e07394SAndrew Turner {
1067*47e07394SAndrew Turner 	int i;
1068*47e07394SAndrew Turner 
1069*47e07394SAndrew Turner 	for (i = 0; i < nitems(vm->mmio_region); i++) {
1070*47e07394SAndrew Turner 		if (vm->mmio_region[i].start == 0 &&
1071*47e07394SAndrew Turner 		    vm->mmio_region[i].end == 0) {
1072*47e07394SAndrew Turner 			vm->mmio_region[i].start = start;
1073*47e07394SAndrew Turner 			vm->mmio_region[i].end = start + size;
1074*47e07394SAndrew Turner 			vm->mmio_region[i].read = mmio_read;
1075*47e07394SAndrew Turner 			vm->mmio_region[i].write = mmio_write;
1076*47e07394SAndrew Turner 			return;
1077*47e07394SAndrew Turner 		}
1078*47e07394SAndrew Turner 	}
1079*47e07394SAndrew Turner 
1080*47e07394SAndrew Turner 	panic("%s: No free MMIO region", __func__);
1081*47e07394SAndrew Turner }
1082*47e07394SAndrew Turner 
1083*47e07394SAndrew Turner void
1084*47e07394SAndrew Turner vm_deregister_inst_handler(struct vm *vm, uint64_t start, uint64_t size)
1085*47e07394SAndrew Turner {
1086*47e07394SAndrew Turner 	int i;
1087*47e07394SAndrew Turner 
1088*47e07394SAndrew Turner 	for (i = 0; i < nitems(vm->mmio_region); i++) {
1089*47e07394SAndrew Turner 		if (vm->mmio_region[i].start == start &&
1090*47e07394SAndrew Turner 		    vm->mmio_region[i].end == start + size) {
1091*47e07394SAndrew Turner 			memset(&vm->mmio_region[i], 0,
1092*47e07394SAndrew Turner 			    sizeof(vm->mmio_region[i]));
1093*47e07394SAndrew Turner 			return;
1094*47e07394SAndrew Turner 		}
1095*47e07394SAndrew Turner 	}
1096*47e07394SAndrew Turner 
1097*47e07394SAndrew Turner 	panic("%s: Invalid MMIO region: %lx - %lx", __func__, start,
1098*47e07394SAndrew Turner 	    start + size);
1099*47e07394SAndrew Turner }
1100*47e07394SAndrew Turner 
1101*47e07394SAndrew Turner static int
1102*47e07394SAndrew Turner vm_handle_inst_emul(struct vcpu *vcpu, bool *retu)
1103*47e07394SAndrew Turner {
1104*47e07394SAndrew Turner 	struct vm *vm;
1105*47e07394SAndrew Turner 	struct vm_exit *vme;
1106*47e07394SAndrew Turner 	struct vie *vie;
1107*47e07394SAndrew Turner 	struct hyp *hyp;
1108*47e07394SAndrew Turner 	uint64_t fault_ipa;
1109*47e07394SAndrew Turner 	struct vm_guest_paging *paging;
1110*47e07394SAndrew Turner 	struct vmm_mmio_region *vmr;
1111*47e07394SAndrew Turner 	int error, i;
1112*47e07394SAndrew Turner 
1113*47e07394SAndrew Turner 	vm = vcpu->vm;
1114*47e07394SAndrew Turner 	hyp = vm->cookie;
1115*47e07394SAndrew Turner 	if (!hyp->vgic_attached)
1116*47e07394SAndrew Turner 		goto out_user;
1117*47e07394SAndrew Turner 
1118*47e07394SAndrew Turner 	vme = &vcpu->exitinfo;
1119*47e07394SAndrew Turner 	vie = &vme->u.inst_emul.vie;
1120*47e07394SAndrew Turner 	paging = &vme->u.inst_emul.paging;
1121*47e07394SAndrew Turner 
1122*47e07394SAndrew Turner 	fault_ipa = vme->u.inst_emul.gpa;
1123*47e07394SAndrew Turner 
1124*47e07394SAndrew Turner 	vmr = NULL;
1125*47e07394SAndrew Turner 	for (i = 0; i < nitems(vm->mmio_region); i++) {
1126*47e07394SAndrew Turner 		if (vm->mmio_region[i].start <= fault_ipa &&
1127*47e07394SAndrew Turner 		    vm->mmio_region[i].end > fault_ipa) {
1128*47e07394SAndrew Turner 			vmr = &vm->mmio_region[i];
1129*47e07394SAndrew Turner 			break;
1130*47e07394SAndrew Turner 		}
1131*47e07394SAndrew Turner 	}
1132*47e07394SAndrew Turner 	if (vmr == NULL)
1133*47e07394SAndrew Turner 		goto out_user;
1134*47e07394SAndrew Turner 
1135*47e07394SAndrew Turner 	error = vmm_emulate_instruction(vcpu, fault_ipa, vie, paging,
1136*47e07394SAndrew Turner 	    vmr->read, vmr->write, retu);
1137*47e07394SAndrew Turner 	return (error);
1138*47e07394SAndrew Turner 
1139*47e07394SAndrew Turner out_user:
1140*47e07394SAndrew Turner 	*retu = true;
1141*47e07394SAndrew Turner 	return (0);
1142*47e07394SAndrew Turner }
1143*47e07394SAndrew Turner 
1144*47e07394SAndrew Turner int
1145*47e07394SAndrew Turner vm_suspend(struct vm *vm, enum vm_suspend_how how)
1146*47e07394SAndrew Turner {
1147*47e07394SAndrew Turner 	int i;
1148*47e07394SAndrew Turner 
1149*47e07394SAndrew Turner 	if (how <= VM_SUSPEND_NONE || how >= VM_SUSPEND_LAST)
1150*47e07394SAndrew Turner 		return (EINVAL);
1151*47e07394SAndrew Turner 
1152*47e07394SAndrew Turner 	if (atomic_cmpset_int(&vm->suspend, 0, how) == 0) {
1153*47e07394SAndrew Turner 		VM_CTR2(vm, "virtual machine already suspended %d/%d",
1154*47e07394SAndrew Turner 		    vm->suspend, how);
1155*47e07394SAndrew Turner 		return (EALREADY);
1156*47e07394SAndrew Turner 	}
1157*47e07394SAndrew Turner 
1158*47e07394SAndrew Turner 	VM_CTR1(vm, "virtual machine successfully suspended %d", how);
1159*47e07394SAndrew Turner 
1160*47e07394SAndrew Turner 	/*
1161*47e07394SAndrew Turner 	 * Notify all active vcpus that they are now suspended.
1162*47e07394SAndrew Turner 	 */
1163*47e07394SAndrew Turner 	for (i = 0; i < vm->maxcpus; i++) {
1164*47e07394SAndrew Turner 		if (CPU_ISSET(i, &vm->active_cpus))
1165*47e07394SAndrew Turner 			vcpu_notify_event(vm_vcpu(vm, i));
1166*47e07394SAndrew Turner 	}
1167*47e07394SAndrew Turner 
1168*47e07394SAndrew Turner 	return (0);
1169*47e07394SAndrew Turner }
1170*47e07394SAndrew Turner 
1171*47e07394SAndrew Turner void
1172*47e07394SAndrew Turner vm_exit_suspended(struct vcpu *vcpu, uint64_t pc)
1173*47e07394SAndrew Turner {
1174*47e07394SAndrew Turner 	struct vm *vm = vcpu->vm;
1175*47e07394SAndrew Turner 	struct vm_exit *vmexit;
1176*47e07394SAndrew Turner 
1177*47e07394SAndrew Turner 	KASSERT(vm->suspend > VM_SUSPEND_NONE && vm->suspend < VM_SUSPEND_LAST,
1178*47e07394SAndrew Turner 	    ("vm_exit_suspended: invalid suspend type %d", vm->suspend));
1179*47e07394SAndrew Turner 
1180*47e07394SAndrew Turner 	vmexit = vm_exitinfo(vcpu);
1181*47e07394SAndrew Turner 	vmexit->pc = pc;
1182*47e07394SAndrew Turner 	vmexit->inst_length = 4;
1183*47e07394SAndrew Turner 	vmexit->exitcode = VM_EXITCODE_SUSPENDED;
1184*47e07394SAndrew Turner 	vmexit->u.suspended.how = vm->suspend;
1185*47e07394SAndrew Turner }
1186*47e07394SAndrew Turner 
1187*47e07394SAndrew Turner void
1188*47e07394SAndrew Turner vm_exit_debug(struct vcpu *vcpu, uint64_t pc)
1189*47e07394SAndrew Turner {
1190*47e07394SAndrew Turner 	struct vm_exit *vmexit;
1191*47e07394SAndrew Turner 
1192*47e07394SAndrew Turner 	vmexit = vm_exitinfo(vcpu);
1193*47e07394SAndrew Turner 	vmexit->pc = pc;
1194*47e07394SAndrew Turner 	vmexit->inst_length = 4;
1195*47e07394SAndrew Turner 	vmexit->exitcode = VM_EXITCODE_DEBUG;
1196*47e07394SAndrew Turner }
1197*47e07394SAndrew Turner 
1198*47e07394SAndrew Turner int
1199*47e07394SAndrew Turner vm_activate_cpu(struct vcpu *vcpu)
1200*47e07394SAndrew Turner {
1201*47e07394SAndrew Turner 	struct vm *vm = vcpu->vm;
1202*47e07394SAndrew Turner 
1203*47e07394SAndrew Turner 	if (CPU_ISSET(vcpu->vcpuid, &vm->active_cpus))
1204*47e07394SAndrew Turner 		return (EBUSY);
1205*47e07394SAndrew Turner 
1206*47e07394SAndrew Turner 	CPU_SET_ATOMIC(vcpu->vcpuid, &vm->active_cpus);
1207*47e07394SAndrew Turner 	return (0);
1208*47e07394SAndrew Turner 
1209*47e07394SAndrew Turner }
1210*47e07394SAndrew Turner 
1211*47e07394SAndrew Turner int
1212*47e07394SAndrew Turner vm_suspend_cpu(struct vm *vm, struct vcpu *vcpu)
1213*47e07394SAndrew Turner {
1214*47e07394SAndrew Turner 	if (vcpu == NULL) {
1215*47e07394SAndrew Turner 		vm->debug_cpus = vm->active_cpus;
1216*47e07394SAndrew Turner 		for (int i = 0; i < vm->maxcpus; i++) {
1217*47e07394SAndrew Turner 			if (CPU_ISSET(i, &vm->active_cpus))
1218*47e07394SAndrew Turner 				vcpu_notify_event(vm_vcpu(vm, i));
1219*47e07394SAndrew Turner 		}
1220*47e07394SAndrew Turner 	} else {
1221*47e07394SAndrew Turner 		if (!CPU_ISSET(vcpu->vcpuid, &vm->active_cpus))
1222*47e07394SAndrew Turner 			return (EINVAL);
1223*47e07394SAndrew Turner 
1224*47e07394SAndrew Turner 		CPU_SET_ATOMIC(vcpu->vcpuid, &vm->debug_cpus);
1225*47e07394SAndrew Turner 		vcpu_notify_event(vcpu);
1226*47e07394SAndrew Turner 	}
1227*47e07394SAndrew Turner 	return (0);
1228*47e07394SAndrew Turner }
1229*47e07394SAndrew Turner 
1230*47e07394SAndrew Turner int
1231*47e07394SAndrew Turner vm_resume_cpu(struct vm *vm, struct vcpu *vcpu)
1232*47e07394SAndrew Turner {
1233*47e07394SAndrew Turner 
1234*47e07394SAndrew Turner 	if (vcpu == NULL) {
1235*47e07394SAndrew Turner 		CPU_ZERO(&vm->debug_cpus);
1236*47e07394SAndrew Turner 	} else {
1237*47e07394SAndrew Turner 		if (!CPU_ISSET(vcpu->vcpuid, &vm->debug_cpus))
1238*47e07394SAndrew Turner 			return (EINVAL);
1239*47e07394SAndrew Turner 
1240*47e07394SAndrew Turner 		CPU_CLR_ATOMIC(vcpu->vcpuid, &vm->debug_cpus);
1241*47e07394SAndrew Turner 	}
1242*47e07394SAndrew Turner 	return (0);
1243*47e07394SAndrew Turner }
1244*47e07394SAndrew Turner 
1245*47e07394SAndrew Turner int
1246*47e07394SAndrew Turner vcpu_debugged(struct vcpu *vcpu)
1247*47e07394SAndrew Turner {
1248*47e07394SAndrew Turner 
1249*47e07394SAndrew Turner 	return (CPU_ISSET(vcpu->vcpuid, &vcpu->vm->debug_cpus));
1250*47e07394SAndrew Turner }
1251*47e07394SAndrew Turner 
1252*47e07394SAndrew Turner cpuset_t
1253*47e07394SAndrew Turner vm_active_cpus(struct vm *vm)
1254*47e07394SAndrew Turner {
1255*47e07394SAndrew Turner 
1256*47e07394SAndrew Turner 	return (vm->active_cpus);
1257*47e07394SAndrew Turner }
1258*47e07394SAndrew Turner 
1259*47e07394SAndrew Turner cpuset_t
1260*47e07394SAndrew Turner vm_debug_cpus(struct vm *vm)
1261*47e07394SAndrew Turner {
1262*47e07394SAndrew Turner 
1263*47e07394SAndrew Turner 	return (vm->debug_cpus);
1264*47e07394SAndrew Turner }
1265*47e07394SAndrew Turner 
1266*47e07394SAndrew Turner cpuset_t
1267*47e07394SAndrew Turner vm_suspended_cpus(struct vm *vm)
1268*47e07394SAndrew Turner {
1269*47e07394SAndrew Turner 
1270*47e07394SAndrew Turner 	return (vm->suspended_cpus);
1271*47e07394SAndrew Turner }
1272*47e07394SAndrew Turner 
1273*47e07394SAndrew Turner 
1274*47e07394SAndrew Turner void *
1275*47e07394SAndrew Turner vcpu_stats(struct vcpu *vcpu)
1276*47e07394SAndrew Turner {
1277*47e07394SAndrew Turner 
1278*47e07394SAndrew Turner 	return (vcpu->stats);
1279*47e07394SAndrew Turner }
1280*47e07394SAndrew Turner 
1281*47e07394SAndrew Turner /*
1282*47e07394SAndrew Turner  * This function is called to ensure that a vcpu "sees" a pending event
1283*47e07394SAndrew Turner  * as soon as possible:
1284*47e07394SAndrew Turner  * - If the vcpu thread is sleeping then it is woken up.
1285*47e07394SAndrew Turner  * - If the vcpu is running on a different host_cpu then an IPI will be directed
1286*47e07394SAndrew Turner  *   to the host_cpu to cause the vcpu to trap into the hypervisor.
1287*47e07394SAndrew Turner  */
1288*47e07394SAndrew Turner static void
1289*47e07394SAndrew Turner vcpu_notify_event_locked(struct vcpu *vcpu)
1290*47e07394SAndrew Turner {
1291*47e07394SAndrew Turner 	int hostcpu;
1292*47e07394SAndrew Turner 
1293*47e07394SAndrew Turner 	hostcpu = vcpu->hostcpu;
1294*47e07394SAndrew Turner 	if (vcpu->state == VCPU_RUNNING) {
1295*47e07394SAndrew Turner 		KASSERT(hostcpu != NOCPU, ("vcpu running on invalid hostcpu"));
1296*47e07394SAndrew Turner 		if (hostcpu != curcpu) {
1297*47e07394SAndrew Turner 			ipi_cpu(hostcpu, vmm_ipinum);
1298*47e07394SAndrew Turner 		} else {
1299*47e07394SAndrew Turner 			/*
1300*47e07394SAndrew Turner 			 * If the 'vcpu' is running on 'curcpu' then it must
1301*47e07394SAndrew Turner 			 * be sending a notification to itself (e.g. SELF_IPI).
1302*47e07394SAndrew Turner 			 * The pending event will be picked up when the vcpu
1303*47e07394SAndrew Turner 			 * transitions back to guest context.
1304*47e07394SAndrew Turner 			 */
1305*47e07394SAndrew Turner 		}
1306*47e07394SAndrew Turner 	} else {
1307*47e07394SAndrew Turner 		KASSERT(hostcpu == NOCPU, ("vcpu state %d not consistent "
1308*47e07394SAndrew Turner 		    "with hostcpu %d", vcpu->state, hostcpu));
1309*47e07394SAndrew Turner 		if (vcpu->state == VCPU_SLEEPING)
1310*47e07394SAndrew Turner 			wakeup_one(vcpu);
1311*47e07394SAndrew Turner 	}
1312*47e07394SAndrew Turner }
1313*47e07394SAndrew Turner 
1314*47e07394SAndrew Turner void
1315*47e07394SAndrew Turner vcpu_notify_event(struct vcpu *vcpu)
1316*47e07394SAndrew Turner {
1317*47e07394SAndrew Turner 	vcpu_lock(vcpu);
1318*47e07394SAndrew Turner 	vcpu_notify_event_locked(vcpu);
1319*47e07394SAndrew Turner 	vcpu_unlock(vcpu);
1320*47e07394SAndrew Turner }
1321*47e07394SAndrew Turner 
1322*47e07394SAndrew Turner static void
1323*47e07394SAndrew Turner restore_guest_fpustate(struct vcpu *vcpu)
1324*47e07394SAndrew Turner {
1325*47e07394SAndrew Turner 
1326*47e07394SAndrew Turner 	/* flush host state to the pcb */
1327*47e07394SAndrew Turner 	vfp_save_state(curthread, curthread->td_pcb);
1328*47e07394SAndrew Turner 	/* Ensure the VFP state will be re-loaded when exiting the guest */
1329*47e07394SAndrew Turner 	PCPU_SET(fpcurthread, NULL);
1330*47e07394SAndrew Turner 
1331*47e07394SAndrew Turner 	/* restore guest FPU state */
1332*47e07394SAndrew Turner 	vfp_enable();
1333*47e07394SAndrew Turner 	vfp_restore(vcpu->guestfpu);
1334*47e07394SAndrew Turner 
1335*47e07394SAndrew Turner 	/*
1336*47e07394SAndrew Turner 	 * The FPU is now "dirty" with the guest's state so turn on emulation
1337*47e07394SAndrew Turner 	 * to trap any access to the FPU by the host.
1338*47e07394SAndrew Turner 	 */
1339*47e07394SAndrew Turner 	vfp_disable();
1340*47e07394SAndrew Turner }
1341*47e07394SAndrew Turner 
1342*47e07394SAndrew Turner static void
1343*47e07394SAndrew Turner save_guest_fpustate(struct vcpu *vcpu)
1344*47e07394SAndrew Turner {
1345*47e07394SAndrew Turner 	if ((READ_SPECIALREG(cpacr_el1) & CPACR_FPEN_MASK) !=
1346*47e07394SAndrew Turner 	    CPACR_FPEN_TRAP_ALL1)
1347*47e07394SAndrew Turner 		panic("VFP not enabled in host!");
1348*47e07394SAndrew Turner 
1349*47e07394SAndrew Turner 	/* save guest FPU state */
1350*47e07394SAndrew Turner 	vfp_enable();
1351*47e07394SAndrew Turner 	vfp_store(vcpu->guestfpu);
1352*47e07394SAndrew Turner 	vfp_disable();
1353*47e07394SAndrew Turner 
1354*47e07394SAndrew Turner 	KASSERT(PCPU_GET(fpcurthread) == NULL,
1355*47e07394SAndrew Turner 	    ("%s: fpcurthread set with guest registers", __func__));
1356*47e07394SAndrew Turner }
1357*47e07394SAndrew Turner static int
1358*47e07394SAndrew Turner vcpu_set_state_locked(struct vcpu *vcpu, enum vcpu_state newstate,
1359*47e07394SAndrew Turner     bool from_idle)
1360*47e07394SAndrew Turner {
1361*47e07394SAndrew Turner 	int error;
1362*47e07394SAndrew Turner 
1363*47e07394SAndrew Turner 	vcpu_assert_locked(vcpu);
1364*47e07394SAndrew Turner 
1365*47e07394SAndrew Turner 	/*
1366*47e07394SAndrew Turner 	 * State transitions from the vmmdev_ioctl() must always begin from
1367*47e07394SAndrew Turner 	 * the VCPU_IDLE state. This guarantees that there is only a single
1368*47e07394SAndrew Turner 	 * ioctl() operating on a vcpu at any point.
1369*47e07394SAndrew Turner 	 */
1370*47e07394SAndrew Turner 	if (from_idle) {
1371*47e07394SAndrew Turner 		while (vcpu->state != VCPU_IDLE) {
1372*47e07394SAndrew Turner 			vcpu_notify_event_locked(vcpu);
1373*47e07394SAndrew Turner 			msleep_spin(&vcpu->state, &vcpu->mtx, "vmstat", hz);
1374*47e07394SAndrew Turner 		}
1375*47e07394SAndrew Turner 	} else {
1376*47e07394SAndrew Turner 		KASSERT(vcpu->state != VCPU_IDLE, ("invalid transition from "
1377*47e07394SAndrew Turner 		    "vcpu idle state"));
1378*47e07394SAndrew Turner 	}
1379*47e07394SAndrew Turner 
1380*47e07394SAndrew Turner 	if (vcpu->state == VCPU_RUNNING) {
1381*47e07394SAndrew Turner 		KASSERT(vcpu->hostcpu == curcpu, ("curcpu %d and hostcpu %d "
1382*47e07394SAndrew Turner 		    "mismatch for running vcpu", curcpu, vcpu->hostcpu));
1383*47e07394SAndrew Turner 	} else {
1384*47e07394SAndrew Turner 		KASSERT(vcpu->hostcpu == NOCPU, ("Invalid hostcpu %d for a "
1385*47e07394SAndrew Turner 		    "vcpu that is not running", vcpu->hostcpu));
1386*47e07394SAndrew Turner 	}
1387*47e07394SAndrew Turner 
1388*47e07394SAndrew Turner 	/*
1389*47e07394SAndrew Turner 	 * The following state transitions are allowed:
1390*47e07394SAndrew Turner 	 * IDLE -> FROZEN -> IDLE
1391*47e07394SAndrew Turner 	 * FROZEN -> RUNNING -> FROZEN
1392*47e07394SAndrew Turner 	 * FROZEN -> SLEEPING -> FROZEN
1393*47e07394SAndrew Turner 	 */
1394*47e07394SAndrew Turner 	switch (vcpu->state) {
1395*47e07394SAndrew Turner 	case VCPU_IDLE:
1396*47e07394SAndrew Turner 	case VCPU_RUNNING:
1397*47e07394SAndrew Turner 	case VCPU_SLEEPING:
1398*47e07394SAndrew Turner 		error = (newstate != VCPU_FROZEN);
1399*47e07394SAndrew Turner 		break;
1400*47e07394SAndrew Turner 	case VCPU_FROZEN:
1401*47e07394SAndrew Turner 		error = (newstate == VCPU_FROZEN);
1402*47e07394SAndrew Turner 		break;
1403*47e07394SAndrew Turner 	default:
1404*47e07394SAndrew Turner 		error = 1;
1405*47e07394SAndrew Turner 		break;
1406*47e07394SAndrew Turner 	}
1407*47e07394SAndrew Turner 
1408*47e07394SAndrew Turner 	if (error)
1409*47e07394SAndrew Turner 		return (EBUSY);
1410*47e07394SAndrew Turner 
1411*47e07394SAndrew Turner 	vcpu->state = newstate;
1412*47e07394SAndrew Turner 	if (newstate == VCPU_RUNNING)
1413*47e07394SAndrew Turner 		vcpu->hostcpu = curcpu;
1414*47e07394SAndrew Turner 	else
1415*47e07394SAndrew Turner 		vcpu->hostcpu = NOCPU;
1416*47e07394SAndrew Turner 
1417*47e07394SAndrew Turner 	if (newstate == VCPU_IDLE)
1418*47e07394SAndrew Turner 		wakeup(&vcpu->state);
1419*47e07394SAndrew Turner 
1420*47e07394SAndrew Turner 	return (0);
1421*47e07394SAndrew Turner }
1422*47e07394SAndrew Turner 
1423*47e07394SAndrew Turner static void
1424*47e07394SAndrew Turner vcpu_require_state(struct vcpu *vcpu, enum vcpu_state newstate)
1425*47e07394SAndrew Turner {
1426*47e07394SAndrew Turner 	int error;
1427*47e07394SAndrew Turner 
1428*47e07394SAndrew Turner 	if ((error = vcpu_set_state(vcpu, newstate, false)) != 0)
1429*47e07394SAndrew Turner 		panic("Error %d setting state to %d\n", error, newstate);
1430*47e07394SAndrew Turner }
1431*47e07394SAndrew Turner 
1432*47e07394SAndrew Turner static void
1433*47e07394SAndrew Turner vcpu_require_state_locked(struct vcpu *vcpu, enum vcpu_state newstate)
1434*47e07394SAndrew Turner {
1435*47e07394SAndrew Turner 	int error;
1436*47e07394SAndrew Turner 
1437*47e07394SAndrew Turner 	if ((error = vcpu_set_state_locked(vcpu, newstate, false)) != 0)
1438*47e07394SAndrew Turner 		panic("Error %d setting state to %d", error, newstate);
1439*47e07394SAndrew Turner }
1440*47e07394SAndrew Turner 
1441*47e07394SAndrew Turner int
1442*47e07394SAndrew Turner vm_get_capability(struct vcpu *vcpu, int type, int *retval)
1443*47e07394SAndrew Turner {
1444*47e07394SAndrew Turner 	if (type < 0 || type >= VM_CAP_MAX)
1445*47e07394SAndrew Turner 		return (EINVAL);
1446*47e07394SAndrew Turner 
1447*47e07394SAndrew Turner 	return (vmmops_getcap(vcpu->cookie, type, retval));
1448*47e07394SAndrew Turner }
1449*47e07394SAndrew Turner 
1450*47e07394SAndrew Turner int
1451*47e07394SAndrew Turner vm_set_capability(struct vcpu *vcpu, int type, int val)
1452*47e07394SAndrew Turner {
1453*47e07394SAndrew Turner 	if (type < 0 || type >= VM_CAP_MAX)
1454*47e07394SAndrew Turner 		return (EINVAL);
1455*47e07394SAndrew Turner 
1456*47e07394SAndrew Turner 	return (vmmops_setcap(vcpu->cookie, type, val));
1457*47e07394SAndrew Turner }
1458*47e07394SAndrew Turner 
1459*47e07394SAndrew Turner struct vm *
1460*47e07394SAndrew Turner vcpu_vm(struct vcpu *vcpu)
1461*47e07394SAndrew Turner {
1462*47e07394SAndrew Turner 	return (vcpu->vm);
1463*47e07394SAndrew Turner }
1464*47e07394SAndrew Turner 
1465*47e07394SAndrew Turner int
1466*47e07394SAndrew Turner vcpu_vcpuid(struct vcpu *vcpu)
1467*47e07394SAndrew Turner {
1468*47e07394SAndrew Turner 	return (vcpu->vcpuid);
1469*47e07394SAndrew Turner }
1470*47e07394SAndrew Turner 
1471*47e07394SAndrew Turner void *
1472*47e07394SAndrew Turner vcpu_get_cookie(struct vcpu *vcpu)
1473*47e07394SAndrew Turner {
1474*47e07394SAndrew Turner 	return (vcpu->cookie);
1475*47e07394SAndrew Turner }
1476*47e07394SAndrew Turner 
1477*47e07394SAndrew Turner struct vcpu *
1478*47e07394SAndrew Turner vm_vcpu(struct vm *vm, int vcpuid)
1479*47e07394SAndrew Turner {
1480*47e07394SAndrew Turner 	return (vm->vcpu[vcpuid]);
1481*47e07394SAndrew Turner }
1482*47e07394SAndrew Turner 
1483*47e07394SAndrew Turner int
1484*47e07394SAndrew Turner vcpu_set_state(struct vcpu *vcpu, enum vcpu_state newstate, bool from_idle)
1485*47e07394SAndrew Turner {
1486*47e07394SAndrew Turner 	int error;
1487*47e07394SAndrew Turner 
1488*47e07394SAndrew Turner 	vcpu_lock(vcpu);
1489*47e07394SAndrew Turner 	error = vcpu_set_state_locked(vcpu, newstate, from_idle);
1490*47e07394SAndrew Turner 	vcpu_unlock(vcpu);
1491*47e07394SAndrew Turner 
1492*47e07394SAndrew Turner 	return (error);
1493*47e07394SAndrew Turner }
1494*47e07394SAndrew Turner 
1495*47e07394SAndrew Turner enum vcpu_state
1496*47e07394SAndrew Turner vcpu_get_state(struct vcpu *vcpu, int *hostcpu)
1497*47e07394SAndrew Turner {
1498*47e07394SAndrew Turner 	enum vcpu_state state;
1499*47e07394SAndrew Turner 
1500*47e07394SAndrew Turner 	vcpu_lock(vcpu);
1501*47e07394SAndrew Turner 	state = vcpu->state;
1502*47e07394SAndrew Turner 	if (hostcpu != NULL)
1503*47e07394SAndrew Turner 		*hostcpu = vcpu->hostcpu;
1504*47e07394SAndrew Turner 	vcpu_unlock(vcpu);
1505*47e07394SAndrew Turner 
1506*47e07394SAndrew Turner 	return (state);
1507*47e07394SAndrew Turner }
1508*47e07394SAndrew Turner 
1509*47e07394SAndrew Turner static void *
1510*47e07394SAndrew Turner _vm_gpa_hold(struct vm *vm, vm_paddr_t gpa, size_t len, int reqprot,
1511*47e07394SAndrew Turner     void **cookie)
1512*47e07394SAndrew Turner {
1513*47e07394SAndrew Turner 	int i, count, pageoff;
1514*47e07394SAndrew Turner 	struct mem_map *mm;
1515*47e07394SAndrew Turner 	vm_page_t m;
1516*47e07394SAndrew Turner 
1517*47e07394SAndrew Turner 	pageoff = gpa & PAGE_MASK;
1518*47e07394SAndrew Turner 	if (len > PAGE_SIZE - pageoff)
1519*47e07394SAndrew Turner 		panic("vm_gpa_hold: invalid gpa/len: 0x%016lx/%lu", gpa, len);
1520*47e07394SAndrew Turner 
1521*47e07394SAndrew Turner 	count = 0;
1522*47e07394SAndrew Turner 	for (i = 0; i < VM_MAX_MEMMAPS; i++) {
1523*47e07394SAndrew Turner 		mm = &vm->mem_maps[i];
1524*47e07394SAndrew Turner 		if (sysmem_mapping(vm, mm) && gpa >= mm->gpa &&
1525*47e07394SAndrew Turner 		    gpa < mm->gpa + mm->len) {
1526*47e07394SAndrew Turner 			count = vm_fault_quick_hold_pages(&vm->vmspace->vm_map,
1527*47e07394SAndrew Turner 			    trunc_page(gpa), PAGE_SIZE, reqprot, &m, 1);
1528*47e07394SAndrew Turner 			break;
1529*47e07394SAndrew Turner 		}
1530*47e07394SAndrew Turner 	}
1531*47e07394SAndrew Turner 
1532*47e07394SAndrew Turner 	if (count == 1) {
1533*47e07394SAndrew Turner 		*cookie = m;
1534*47e07394SAndrew Turner 		return ((void *)(PHYS_TO_DMAP(VM_PAGE_TO_PHYS(m)) + pageoff));
1535*47e07394SAndrew Turner 	} else {
1536*47e07394SAndrew Turner 		*cookie = NULL;
1537*47e07394SAndrew Turner 		return (NULL);
1538*47e07394SAndrew Turner 	}
1539*47e07394SAndrew Turner }
1540*47e07394SAndrew Turner 
1541*47e07394SAndrew Turner void *
1542*47e07394SAndrew Turner vm_gpa_hold(struct vcpu *vcpu, vm_paddr_t gpa, size_t len, int reqprot,
1543*47e07394SAndrew Turner 	    void **cookie)
1544*47e07394SAndrew Turner {
1545*47e07394SAndrew Turner #ifdef INVARIANTS
1546*47e07394SAndrew Turner 	/*
1547*47e07394SAndrew Turner 	 * The current vcpu should be frozen to ensure 'vm_memmap[]'
1548*47e07394SAndrew Turner 	 * stability.
1549*47e07394SAndrew Turner 	 */
1550*47e07394SAndrew Turner 	int state = vcpu_get_state(vcpu, NULL);
1551*47e07394SAndrew Turner 	KASSERT(state == VCPU_FROZEN, ("%s: invalid vcpu state %d",
1552*47e07394SAndrew Turner 	    __func__, state));
1553*47e07394SAndrew Turner #endif
1554*47e07394SAndrew Turner 	return (_vm_gpa_hold(vcpu->vm, gpa, len, reqprot, cookie));
1555*47e07394SAndrew Turner }
1556*47e07394SAndrew Turner 
1557*47e07394SAndrew Turner void *
1558*47e07394SAndrew Turner vm_gpa_hold_global(struct vm *vm, vm_paddr_t gpa, size_t len, int reqprot,
1559*47e07394SAndrew Turner     void **cookie)
1560*47e07394SAndrew Turner {
1561*47e07394SAndrew Turner 	sx_assert(&vm->mem_segs_lock, SX_LOCKED);
1562*47e07394SAndrew Turner 	return (_vm_gpa_hold(vm, gpa, len, reqprot, cookie));
1563*47e07394SAndrew Turner }
1564*47e07394SAndrew Turner 
1565*47e07394SAndrew Turner void
1566*47e07394SAndrew Turner vm_gpa_release(void *cookie)
1567*47e07394SAndrew Turner {
1568*47e07394SAndrew Turner 	vm_page_t m = cookie;
1569*47e07394SAndrew Turner 
1570*47e07394SAndrew Turner 	vm_page_unwire(m, PQ_ACTIVE);
1571*47e07394SAndrew Turner }
1572*47e07394SAndrew Turner 
1573*47e07394SAndrew Turner int
1574*47e07394SAndrew Turner vm_get_register(struct vcpu *vcpu, int reg, uint64_t *retval)
1575*47e07394SAndrew Turner {
1576*47e07394SAndrew Turner 
1577*47e07394SAndrew Turner 	if (reg >= VM_REG_LAST)
1578*47e07394SAndrew Turner 		return (EINVAL);
1579*47e07394SAndrew Turner 
1580*47e07394SAndrew Turner 	return (vmmops_getreg(vcpu->cookie, reg, retval));
1581*47e07394SAndrew Turner }
1582*47e07394SAndrew Turner 
1583*47e07394SAndrew Turner int
1584*47e07394SAndrew Turner vm_set_register(struct vcpu *vcpu, int reg, uint64_t val)
1585*47e07394SAndrew Turner {
1586*47e07394SAndrew Turner 	int error;
1587*47e07394SAndrew Turner 
1588*47e07394SAndrew Turner 	if (reg >= VM_REG_LAST)
1589*47e07394SAndrew Turner 		return (EINVAL);
1590*47e07394SAndrew Turner 	error = vmmops_setreg(vcpu->cookie, reg, val);
1591*47e07394SAndrew Turner 	if (error || reg != VM_REG_GUEST_PC)
1592*47e07394SAndrew Turner 		return (error);
1593*47e07394SAndrew Turner 
1594*47e07394SAndrew Turner 	vcpu->nextpc = val;
1595*47e07394SAndrew Turner 
1596*47e07394SAndrew Turner 	return (0);
1597*47e07394SAndrew Turner }
1598*47e07394SAndrew Turner 
1599*47e07394SAndrew Turner void *
1600*47e07394SAndrew Turner vm_get_cookie(struct vm *vm)
1601*47e07394SAndrew Turner {
1602*47e07394SAndrew Turner 	return (vm->cookie);
1603*47e07394SAndrew Turner }
1604*47e07394SAndrew Turner 
1605*47e07394SAndrew Turner int
1606*47e07394SAndrew Turner vm_inject_exception(struct vcpu *vcpu, uint64_t esr, uint64_t far)
1607*47e07394SAndrew Turner {
1608*47e07394SAndrew Turner 	return (vmmops_exception(vcpu->cookie, esr, far));
1609*47e07394SAndrew Turner }
1610*47e07394SAndrew Turner 
1611*47e07394SAndrew Turner int
1612*47e07394SAndrew Turner vm_attach_vgic(struct vm *vm, struct vm_vgic_descr *descr)
1613*47e07394SAndrew Turner {
1614*47e07394SAndrew Turner 	return (vgic_attach_to_vm(vm->cookie, descr));
1615*47e07394SAndrew Turner }
1616*47e07394SAndrew Turner 
1617*47e07394SAndrew Turner int
1618*47e07394SAndrew Turner vm_assert_irq(struct vm *vm, uint32_t irq)
1619*47e07394SAndrew Turner {
1620*47e07394SAndrew Turner 	return (vgic_inject_irq(vm->cookie, -1, irq, true));
1621*47e07394SAndrew Turner }
1622*47e07394SAndrew Turner 
1623*47e07394SAndrew Turner int
1624*47e07394SAndrew Turner vm_deassert_irq(struct vm *vm, uint32_t irq)
1625*47e07394SAndrew Turner {
1626*47e07394SAndrew Turner 	return (vgic_inject_irq(vm->cookie, -1, irq, false));
1627*47e07394SAndrew Turner }
1628*47e07394SAndrew Turner 
1629*47e07394SAndrew Turner int
1630*47e07394SAndrew Turner vm_raise_msi(struct vm *vm, uint64_t msg, uint64_t addr, int bus, int slot,
1631*47e07394SAndrew Turner     int func)
1632*47e07394SAndrew Turner {
1633*47e07394SAndrew Turner 	/* TODO: Should we raise an SError? */
1634*47e07394SAndrew Turner 	return (vgic_inject_msi(vm->cookie, msg, addr));
1635*47e07394SAndrew Turner }
1636*47e07394SAndrew Turner 
1637*47e07394SAndrew Turner static int
1638*47e07394SAndrew Turner vm_handle_smccc_call(struct vcpu *vcpu, struct vm_exit *vme, bool *retu)
1639*47e07394SAndrew Turner {
1640*47e07394SAndrew Turner 	struct hypctx *hypctx;
1641*47e07394SAndrew Turner 	int i;
1642*47e07394SAndrew Turner 
1643*47e07394SAndrew Turner 	hypctx = vcpu_get_cookie(vcpu);
1644*47e07394SAndrew Turner 
1645*47e07394SAndrew Turner 	if ((hypctx->tf.tf_esr & ESR_ELx_ISS_MASK) != 0)
1646*47e07394SAndrew Turner 		return (1);
1647*47e07394SAndrew Turner 
1648*47e07394SAndrew Turner 	vme->exitcode = VM_EXITCODE_SMCCC;
1649*47e07394SAndrew Turner 	vme->u.smccc_call.func_id = hypctx->tf.tf_x[0];
1650*47e07394SAndrew Turner 	for (i = 0; i < nitems(vme->u.smccc_call.args); i++)
1651*47e07394SAndrew Turner 		vme->u.smccc_call.args[i] = hypctx->tf.tf_x[i + 1];
1652*47e07394SAndrew Turner 
1653*47e07394SAndrew Turner 	*retu = true;
1654*47e07394SAndrew Turner 	return (0);
1655*47e07394SAndrew Turner }
1656*47e07394SAndrew Turner 
1657*47e07394SAndrew Turner static int
1658*47e07394SAndrew Turner vm_handle_wfi(struct vcpu *vcpu, struct vm_exit *vme, bool *retu)
1659*47e07394SAndrew Turner {
1660*47e07394SAndrew Turner 	vcpu_lock(vcpu);
1661*47e07394SAndrew Turner 	while (1) {
1662*47e07394SAndrew Turner 		if (vgic_has_pending_irq(vcpu->cookie))
1663*47e07394SAndrew Turner 			break;
1664*47e07394SAndrew Turner 
1665*47e07394SAndrew Turner 		if (vcpu_should_yield(vcpu))
1666*47e07394SAndrew Turner 			break;
1667*47e07394SAndrew Turner 
1668*47e07394SAndrew Turner 		vcpu_require_state_locked(vcpu, VCPU_SLEEPING);
1669*47e07394SAndrew Turner 		/*
1670*47e07394SAndrew Turner 		 * XXX msleep_spin() cannot be interrupted by signals so
1671*47e07394SAndrew Turner 		 * wake up periodically to check pending signals.
1672*47e07394SAndrew Turner 		 */
1673*47e07394SAndrew Turner 		msleep_spin(vcpu, &vcpu->mtx, "vmidle", hz);
1674*47e07394SAndrew Turner 		vcpu_require_state_locked(vcpu, VCPU_FROZEN);
1675*47e07394SAndrew Turner 	}
1676*47e07394SAndrew Turner 	vcpu_unlock(vcpu);
1677*47e07394SAndrew Turner 
1678*47e07394SAndrew Turner 	*retu = false;
1679*47e07394SAndrew Turner 	return (0);
1680*47e07394SAndrew Turner }
1681*47e07394SAndrew Turner 
1682*47e07394SAndrew Turner static int
1683*47e07394SAndrew Turner vm_handle_paging(struct vcpu *vcpu, bool *retu)
1684*47e07394SAndrew Turner {
1685*47e07394SAndrew Turner 	struct vm *vm = vcpu->vm;
1686*47e07394SAndrew Turner 	struct vm_exit *vme;
1687*47e07394SAndrew Turner 	struct vm_map *map;
1688*47e07394SAndrew Turner 	uint64_t addr, esr;
1689*47e07394SAndrew Turner 	pmap_t pmap;
1690*47e07394SAndrew Turner 	int ftype, rv;
1691*47e07394SAndrew Turner 
1692*47e07394SAndrew Turner 	vme = &vcpu->exitinfo;
1693*47e07394SAndrew Turner 
1694*47e07394SAndrew Turner 	pmap = vmspace_pmap(vcpu->vm->vmspace);
1695*47e07394SAndrew Turner 	addr = vme->u.paging.gpa;
1696*47e07394SAndrew Turner 	esr = vme->u.paging.esr;
1697*47e07394SAndrew Turner 
1698*47e07394SAndrew Turner 	/* The page exists, but the page table needs to be updated. */
1699*47e07394SAndrew Turner 	if (pmap_fault(pmap, esr, addr) == KERN_SUCCESS)
1700*47e07394SAndrew Turner 		return (0);
1701*47e07394SAndrew Turner 
1702*47e07394SAndrew Turner 	switch (ESR_ELx_EXCEPTION(esr)) {
1703*47e07394SAndrew Turner 	case EXCP_INSN_ABORT_L:
1704*47e07394SAndrew Turner 	case EXCP_DATA_ABORT_L:
1705*47e07394SAndrew Turner 		ftype = VM_PROT_EXECUTE | VM_PROT_READ | VM_PROT_WRITE;
1706*47e07394SAndrew Turner 		break;
1707*47e07394SAndrew Turner 	default:
1708*47e07394SAndrew Turner 		panic("%s: Invalid exception (esr = %lx)", __func__, esr);
1709*47e07394SAndrew Turner 	}
1710*47e07394SAndrew Turner 
1711*47e07394SAndrew Turner 	map = &vm->vmspace->vm_map;
1712*47e07394SAndrew Turner 	rv = vm_fault(map, vme->u.paging.gpa, ftype, VM_FAULT_NORMAL, NULL);
1713*47e07394SAndrew Turner 	if (rv != KERN_SUCCESS)
1714*47e07394SAndrew Turner 		return (EFAULT);
1715*47e07394SAndrew Turner 
1716*47e07394SAndrew Turner 	return (0);
1717*47e07394SAndrew Turner }
1718*47e07394SAndrew Turner 
1719*47e07394SAndrew Turner int
1720*47e07394SAndrew Turner vm_run(struct vcpu *vcpu)
1721*47e07394SAndrew Turner {
1722*47e07394SAndrew Turner 	struct vm *vm = vcpu->vm;
1723*47e07394SAndrew Turner 	struct vm_eventinfo evinfo;
1724*47e07394SAndrew Turner 	int error, vcpuid;
1725*47e07394SAndrew Turner 	struct vm_exit *vme;
1726*47e07394SAndrew Turner 	bool retu;
1727*47e07394SAndrew Turner 	pmap_t pmap;
1728*47e07394SAndrew Turner 
1729*47e07394SAndrew Turner 	vcpuid = vcpu->vcpuid;
1730*47e07394SAndrew Turner 
1731*47e07394SAndrew Turner 	if (!CPU_ISSET(vcpuid, &vm->active_cpus))
1732*47e07394SAndrew Turner 		return (EINVAL);
1733*47e07394SAndrew Turner 
1734*47e07394SAndrew Turner 	if (CPU_ISSET(vcpuid, &vm->suspended_cpus))
1735*47e07394SAndrew Turner 		return (EINVAL);
1736*47e07394SAndrew Turner 
1737*47e07394SAndrew Turner 	pmap = vmspace_pmap(vm->vmspace);
1738*47e07394SAndrew Turner 	vme = &vcpu->exitinfo;
1739*47e07394SAndrew Turner 	evinfo.rptr = NULL;
1740*47e07394SAndrew Turner 	evinfo.sptr = &vm->suspend;
1741*47e07394SAndrew Turner 	evinfo.iptr = NULL;
1742*47e07394SAndrew Turner restart:
1743*47e07394SAndrew Turner 	critical_enter();
1744*47e07394SAndrew Turner 
1745*47e07394SAndrew Turner 	restore_guest_fpustate(vcpu);
1746*47e07394SAndrew Turner 
1747*47e07394SAndrew Turner 	vcpu_require_state(vcpu, VCPU_RUNNING);
1748*47e07394SAndrew Turner 	error = vmmops_run(vcpu->cookie, vcpu->nextpc, pmap, &evinfo);
1749*47e07394SAndrew Turner 	vcpu_require_state(vcpu, VCPU_FROZEN);
1750*47e07394SAndrew Turner 
1751*47e07394SAndrew Turner 	save_guest_fpustate(vcpu);
1752*47e07394SAndrew Turner 
1753*47e07394SAndrew Turner 	critical_exit();
1754*47e07394SAndrew Turner 
1755*47e07394SAndrew Turner 	if (error == 0) {
1756*47e07394SAndrew Turner 		retu = false;
1757*47e07394SAndrew Turner 		switch (vme->exitcode) {
1758*47e07394SAndrew Turner 		case VM_EXITCODE_INST_EMUL:
1759*47e07394SAndrew Turner 			vcpu->nextpc = vme->pc + vme->inst_length;
1760*47e07394SAndrew Turner 			error = vm_handle_inst_emul(vcpu, &retu);
1761*47e07394SAndrew Turner 			break;
1762*47e07394SAndrew Turner 
1763*47e07394SAndrew Turner 		case VM_EXITCODE_REG_EMUL:
1764*47e07394SAndrew Turner 			vcpu->nextpc = vme->pc + vme->inst_length;
1765*47e07394SAndrew Turner 			error = vm_handle_reg_emul(vcpu, &retu);
1766*47e07394SAndrew Turner 			break;
1767*47e07394SAndrew Turner 
1768*47e07394SAndrew Turner 		case VM_EXITCODE_HVC:
1769*47e07394SAndrew Turner 			/*
1770*47e07394SAndrew Turner 			 * The HVC instruction saves the address for the
1771*47e07394SAndrew Turner 			 * next instruction as the return address.
1772*47e07394SAndrew Turner 			 */
1773*47e07394SAndrew Turner 			vcpu->nextpc = vme->pc;
1774*47e07394SAndrew Turner 			/*
1775*47e07394SAndrew Turner 			 * The PSCI call can change the exit information in the
1776*47e07394SAndrew Turner 			 * case of suspend/reset/poweroff/cpu off/cpu on.
1777*47e07394SAndrew Turner 			 */
1778*47e07394SAndrew Turner 			error = vm_handle_smccc_call(vcpu, vme, &retu);
1779*47e07394SAndrew Turner 			break;
1780*47e07394SAndrew Turner 
1781*47e07394SAndrew Turner 		case VM_EXITCODE_WFI:
1782*47e07394SAndrew Turner 			vcpu->nextpc = vme->pc + vme->inst_length;
1783*47e07394SAndrew Turner 			error = vm_handle_wfi(vcpu, vme, &retu);
1784*47e07394SAndrew Turner 			break;
1785*47e07394SAndrew Turner 
1786*47e07394SAndrew Turner 		case VM_EXITCODE_PAGING:
1787*47e07394SAndrew Turner 			vcpu->nextpc = vme->pc;
1788*47e07394SAndrew Turner 			error = vm_handle_paging(vcpu, &retu);
1789*47e07394SAndrew Turner 			break;
1790*47e07394SAndrew Turner 
1791*47e07394SAndrew Turner 		default:
1792*47e07394SAndrew Turner 			/* Handle in userland */
1793*47e07394SAndrew Turner 			vcpu->nextpc = vme->pc;
1794*47e07394SAndrew Turner 			retu = true;
1795*47e07394SAndrew Turner 			break;
1796*47e07394SAndrew Turner 		}
1797*47e07394SAndrew Turner 	}
1798*47e07394SAndrew Turner 
1799*47e07394SAndrew Turner 	if (error == 0 && retu == false)
1800*47e07394SAndrew Turner 		goto restart;
1801*47e07394SAndrew Turner 
1802*47e07394SAndrew Turner 	return (error);
1803*47e07394SAndrew Turner }
1804