xref: /freebsd/sys/riscv/vmm/vmm_riscv.c (revision d3916eace506b8ab23537223f5c92924636a1c41)
1*d3916eacSRuslan Bukin /*-
2*d3916eacSRuslan Bukin  * SPDX-License-Identifier: BSD-2-Clause
3*d3916eacSRuslan Bukin  *
4*d3916eacSRuslan Bukin  * Copyright (c) 2024 Ruslan Bukin <br@bsdpad.com>
5*d3916eacSRuslan Bukin  *
6*d3916eacSRuslan Bukin  * This software was developed by the University of Cambridge Computer
7*d3916eacSRuslan Bukin  * Laboratory (Department of Computer Science and Technology) under Innovate
8*d3916eacSRuslan Bukin  * UK project 105694, "Digital Security by Design (DSbD) Technology Platform
9*d3916eacSRuslan Bukin  * Prototype".
10*d3916eacSRuslan Bukin  *
11*d3916eacSRuslan Bukin  * Redistribution and use in source and binary forms, with or without
12*d3916eacSRuslan Bukin  * modification, are permitted provided that the following conditions
13*d3916eacSRuslan Bukin  * are met:
14*d3916eacSRuslan Bukin  * 1. Redistributions of source code must retain the above copyright
15*d3916eacSRuslan Bukin  *    notice, this list of conditions and the following disclaimer.
16*d3916eacSRuslan Bukin  * 2. Redistributions in binary form must reproduce the above copyright
17*d3916eacSRuslan Bukin  *    notice, this list of conditions and the following disclaimer in the
18*d3916eacSRuslan Bukin  *    documentation and/or other materials provided with the distribution.
19*d3916eacSRuslan Bukin  *
20*d3916eacSRuslan Bukin  * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21*d3916eacSRuslan Bukin  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22*d3916eacSRuslan Bukin  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23*d3916eacSRuslan Bukin  * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
24*d3916eacSRuslan Bukin  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25*d3916eacSRuslan Bukin  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26*d3916eacSRuslan Bukin  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27*d3916eacSRuslan Bukin  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28*d3916eacSRuslan Bukin  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29*d3916eacSRuslan Bukin  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30*d3916eacSRuslan Bukin  * SUCH DAMAGE.
31*d3916eacSRuslan Bukin  */
32*d3916eacSRuslan Bukin 
33*d3916eacSRuslan Bukin #include <sys/param.h>
34*d3916eacSRuslan Bukin #include <sys/systm.h>
35*d3916eacSRuslan Bukin #include <sys/smp.h>
36*d3916eacSRuslan Bukin #include <sys/kernel.h>
37*d3916eacSRuslan Bukin #include <sys/malloc.h>
38*d3916eacSRuslan Bukin #include <sys/mman.h>
39*d3916eacSRuslan Bukin #include <sys/pcpu.h>
40*d3916eacSRuslan Bukin #include <sys/proc.h>
41*d3916eacSRuslan Bukin #include <sys/rman.h>
42*d3916eacSRuslan Bukin #include <sys/sysctl.h>
43*d3916eacSRuslan Bukin #include <sys/lock.h>
44*d3916eacSRuslan Bukin #include <sys/mutex.h>
45*d3916eacSRuslan Bukin #include <sys/vmem.h>
46*d3916eacSRuslan Bukin #include <sys/bus.h>
47*d3916eacSRuslan Bukin 
48*d3916eacSRuslan Bukin #include <vm/vm.h>
49*d3916eacSRuslan Bukin #include <vm/pmap.h>
50*d3916eacSRuslan Bukin #include <vm/vm_extern.h>
51*d3916eacSRuslan Bukin #include <vm/vm_map.h>
52*d3916eacSRuslan Bukin #include <vm/vm_page.h>
53*d3916eacSRuslan Bukin #include <vm/vm_param.h>
54*d3916eacSRuslan Bukin 
55*d3916eacSRuslan Bukin #include <machine/md_var.h>
56*d3916eacSRuslan Bukin #include <machine/riscvreg.h>
57*d3916eacSRuslan Bukin #include <machine/vm.h>
58*d3916eacSRuslan Bukin #include <machine/cpufunc.h>
59*d3916eacSRuslan Bukin #include <machine/cpu.h>
60*d3916eacSRuslan Bukin #include <machine/machdep.h>
61*d3916eacSRuslan Bukin #include <machine/vmm.h>
62*d3916eacSRuslan Bukin #include <machine/vmm_dev.h>
63*d3916eacSRuslan Bukin #include <machine/atomic.h>
64*d3916eacSRuslan Bukin #include <machine/pmap.h>
65*d3916eacSRuslan Bukin #include <machine/intr.h>
66*d3916eacSRuslan Bukin #include <machine/encoding.h>
67*d3916eacSRuslan Bukin #include <machine/db_machdep.h>
68*d3916eacSRuslan Bukin 
69*d3916eacSRuslan Bukin #include "riscv.h"
70*d3916eacSRuslan Bukin #include "vmm_aplic.h"
71*d3916eacSRuslan Bukin #include "vmm_stat.h"
72*d3916eacSRuslan Bukin 
73*d3916eacSRuslan Bukin MALLOC_DEFINE(M_HYP, "RISC-V VMM HYP", "RISC-V VMM HYP");
74*d3916eacSRuslan Bukin 
75*d3916eacSRuslan Bukin DPCPU_DEFINE_STATIC(struct hypctx *, vcpu);
76*d3916eacSRuslan Bukin 
77*d3916eacSRuslan Bukin static int
m_op(uint32_t insn,int match,int mask)78*d3916eacSRuslan Bukin m_op(uint32_t insn, int match, int mask)
79*d3916eacSRuslan Bukin {
80*d3916eacSRuslan Bukin 
81*d3916eacSRuslan Bukin 	if (((insn ^ match) & mask) == 0)
82*d3916eacSRuslan Bukin 		return (1);
83*d3916eacSRuslan Bukin 
84*d3916eacSRuslan Bukin 	return (0);
85*d3916eacSRuslan Bukin }
86*d3916eacSRuslan Bukin 
87*d3916eacSRuslan Bukin static inline void
riscv_set_active_vcpu(struct hypctx * hypctx)88*d3916eacSRuslan Bukin riscv_set_active_vcpu(struct hypctx *hypctx)
89*d3916eacSRuslan Bukin {
90*d3916eacSRuslan Bukin 
91*d3916eacSRuslan Bukin 	DPCPU_SET(vcpu, hypctx);
92*d3916eacSRuslan Bukin }
93*d3916eacSRuslan Bukin 
94*d3916eacSRuslan Bukin struct hypctx *
riscv_get_active_vcpu(void)95*d3916eacSRuslan Bukin riscv_get_active_vcpu(void)
96*d3916eacSRuslan Bukin {
97*d3916eacSRuslan Bukin 
98*d3916eacSRuslan Bukin 	return (DPCPU_GET(vcpu));
99*d3916eacSRuslan Bukin }
100*d3916eacSRuslan Bukin 
101*d3916eacSRuslan Bukin int
vmmops_modinit(void)102*d3916eacSRuslan Bukin vmmops_modinit(void)
103*d3916eacSRuslan Bukin {
104*d3916eacSRuslan Bukin 
105*d3916eacSRuslan Bukin 	if (!has_hyp) {
106*d3916eacSRuslan Bukin 		printf("vmm: riscv hart doesn't support H-extension.\n");
107*d3916eacSRuslan Bukin 		return (ENXIO);
108*d3916eacSRuslan Bukin 	}
109*d3916eacSRuslan Bukin 
110*d3916eacSRuslan Bukin 	if (!has_sstc) {
111*d3916eacSRuslan Bukin 		printf("vmm: riscv hart doesn't support SSTC extension.\n");
112*d3916eacSRuslan Bukin 		return (ENXIO);
113*d3916eacSRuslan Bukin 	}
114*d3916eacSRuslan Bukin 
115*d3916eacSRuslan Bukin 	return (0);
116*d3916eacSRuslan Bukin }
117*d3916eacSRuslan Bukin 
118*d3916eacSRuslan Bukin int
vmmops_modcleanup(void)119*d3916eacSRuslan Bukin vmmops_modcleanup(void)
120*d3916eacSRuslan Bukin {
121*d3916eacSRuslan Bukin 
122*d3916eacSRuslan Bukin 	return (0);
123*d3916eacSRuslan Bukin }
124*d3916eacSRuslan Bukin 
125*d3916eacSRuslan Bukin void *
vmmops_init(struct vm * vm,pmap_t pmap)126*d3916eacSRuslan Bukin vmmops_init(struct vm *vm, pmap_t pmap)
127*d3916eacSRuslan Bukin {
128*d3916eacSRuslan Bukin 	struct hyp *hyp;
129*d3916eacSRuslan Bukin 	vm_size_t size;
130*d3916eacSRuslan Bukin 
131*d3916eacSRuslan Bukin 	size = round_page(sizeof(struct hyp) +
132*d3916eacSRuslan Bukin 	    sizeof(struct hypctx *) * vm_get_maxcpus(vm));
133*d3916eacSRuslan Bukin 	hyp = malloc_aligned(size, PAGE_SIZE, M_HYP, M_WAITOK | M_ZERO);
134*d3916eacSRuslan Bukin 	hyp->vm = vm;
135*d3916eacSRuslan Bukin 	hyp->aplic_attached = false;
136*d3916eacSRuslan Bukin 
137*d3916eacSRuslan Bukin 	aplic_vminit(hyp);
138*d3916eacSRuslan Bukin 
139*d3916eacSRuslan Bukin 	return (hyp);
140*d3916eacSRuslan Bukin }
141*d3916eacSRuslan Bukin 
142*d3916eacSRuslan Bukin static void
vmmops_delegate(void)143*d3916eacSRuslan Bukin vmmops_delegate(void)
144*d3916eacSRuslan Bukin {
145*d3916eacSRuslan Bukin 	uint64_t hedeleg;
146*d3916eacSRuslan Bukin 	uint64_t hideleg;
147*d3916eacSRuslan Bukin 
148*d3916eacSRuslan Bukin 	hedeleg  = (1UL << SCAUSE_INST_MISALIGNED);
149*d3916eacSRuslan Bukin 	hedeleg |= (1UL << SCAUSE_ILLEGAL_INSTRUCTION);
150*d3916eacSRuslan Bukin 	hedeleg |= (1UL << SCAUSE_BREAKPOINT);
151*d3916eacSRuslan Bukin 	hedeleg |= (1UL << SCAUSE_ECALL_USER);
152*d3916eacSRuslan Bukin 	hedeleg |= (1UL << SCAUSE_INST_PAGE_FAULT);
153*d3916eacSRuslan Bukin 	hedeleg |= (1UL << SCAUSE_LOAD_PAGE_FAULT);
154*d3916eacSRuslan Bukin 	hedeleg |= (1UL << SCAUSE_STORE_PAGE_FAULT);
155*d3916eacSRuslan Bukin 	csr_write(hedeleg, hedeleg);
156*d3916eacSRuslan Bukin 
157*d3916eacSRuslan Bukin 	hideleg  = (1UL << IRQ_SOFTWARE_HYPERVISOR);
158*d3916eacSRuslan Bukin 	hideleg |= (1UL << IRQ_TIMER_HYPERVISOR);
159*d3916eacSRuslan Bukin 	hideleg |= (1UL << IRQ_EXTERNAL_HYPERVISOR);
160*d3916eacSRuslan Bukin 	csr_write(hideleg, hideleg);
161*d3916eacSRuslan Bukin }
162*d3916eacSRuslan Bukin 
163*d3916eacSRuslan Bukin static void
vmmops_vcpu_restore_csrs(struct hypctx * hypctx)164*d3916eacSRuslan Bukin vmmops_vcpu_restore_csrs(struct hypctx *hypctx)
165*d3916eacSRuslan Bukin {
166*d3916eacSRuslan Bukin 	struct hypcsr *csrs;
167*d3916eacSRuslan Bukin 
168*d3916eacSRuslan Bukin 	csrs = &hypctx->guest_csrs;
169*d3916eacSRuslan Bukin 
170*d3916eacSRuslan Bukin 	csr_write(vsstatus, csrs->vsstatus);
171*d3916eacSRuslan Bukin 	csr_write(vsie, csrs->vsie);
172*d3916eacSRuslan Bukin 	csr_write(vstvec, csrs->vstvec);
173*d3916eacSRuslan Bukin 	csr_write(vsscratch, csrs->vsscratch);
174*d3916eacSRuslan Bukin 	csr_write(vsepc, csrs->vsepc);
175*d3916eacSRuslan Bukin 	csr_write(vscause, csrs->vscause);
176*d3916eacSRuslan Bukin 	csr_write(vstval, csrs->vstval);
177*d3916eacSRuslan Bukin 	csr_write(hvip, csrs->hvip);
178*d3916eacSRuslan Bukin 	csr_write(vsatp, csrs->vsatp);
179*d3916eacSRuslan Bukin }
180*d3916eacSRuslan Bukin 
181*d3916eacSRuslan Bukin static void
vmmops_vcpu_save_csrs(struct hypctx * hypctx)182*d3916eacSRuslan Bukin vmmops_vcpu_save_csrs(struct hypctx *hypctx)
183*d3916eacSRuslan Bukin {
184*d3916eacSRuslan Bukin 	struct hypcsr *csrs;
185*d3916eacSRuslan Bukin 
186*d3916eacSRuslan Bukin 	csrs = &hypctx->guest_csrs;
187*d3916eacSRuslan Bukin 
188*d3916eacSRuslan Bukin 	csrs->vsstatus = csr_read(vsstatus);
189*d3916eacSRuslan Bukin 	csrs->vsie = csr_read(vsie);
190*d3916eacSRuslan Bukin 	csrs->vstvec = csr_read(vstvec);
191*d3916eacSRuslan Bukin 	csrs->vsscratch = csr_read(vsscratch);
192*d3916eacSRuslan Bukin 	csrs->vsepc = csr_read(vsepc);
193*d3916eacSRuslan Bukin 	csrs->vscause = csr_read(vscause);
194*d3916eacSRuslan Bukin 	csrs->vstval = csr_read(vstval);
195*d3916eacSRuslan Bukin 	csrs->hvip = csr_read(hvip);
196*d3916eacSRuslan Bukin 	csrs->vsatp = csr_read(vsatp);
197*d3916eacSRuslan Bukin }
198*d3916eacSRuslan Bukin 
199*d3916eacSRuslan Bukin void *
vmmops_vcpu_init(void * vmi,struct vcpu * vcpu1,int vcpuid)200*d3916eacSRuslan Bukin vmmops_vcpu_init(void *vmi, struct vcpu *vcpu1, int vcpuid)
201*d3916eacSRuslan Bukin {
202*d3916eacSRuslan Bukin 	struct hypctx *hypctx;
203*d3916eacSRuslan Bukin 	struct hyp *hyp;
204*d3916eacSRuslan Bukin 	vm_size_t size;
205*d3916eacSRuslan Bukin 
206*d3916eacSRuslan Bukin 	hyp = vmi;
207*d3916eacSRuslan Bukin 
208*d3916eacSRuslan Bukin 	dprintf("%s: hyp %p\n", __func__, hyp);
209*d3916eacSRuslan Bukin 
210*d3916eacSRuslan Bukin 	KASSERT(vcpuid >= 0 && vcpuid < vm_get_maxcpus(hyp->vm),
211*d3916eacSRuslan Bukin 	    ("%s: Invalid vcpuid %d", __func__, vcpuid));
212*d3916eacSRuslan Bukin 
213*d3916eacSRuslan Bukin 	size = round_page(sizeof(struct hypctx));
214*d3916eacSRuslan Bukin 
215*d3916eacSRuslan Bukin 	hypctx = malloc_aligned(size, PAGE_SIZE, M_HYP, M_WAITOK | M_ZERO);
216*d3916eacSRuslan Bukin 	hypctx->hyp = hyp;
217*d3916eacSRuslan Bukin 	hypctx->vcpu = vcpu1;
218*d3916eacSRuslan Bukin 	hypctx->guest_scounteren = HCOUNTEREN_CY | HCOUNTEREN_TM;
219*d3916eacSRuslan Bukin 
220*d3916eacSRuslan Bukin 	/* sstatus */
221*d3916eacSRuslan Bukin 	hypctx->guest_regs.hyp_sstatus = SSTATUS_SPP | SSTATUS_SPIE;
222*d3916eacSRuslan Bukin 	hypctx->guest_regs.hyp_sstatus |= SSTATUS_FS_INITIAL;
223*d3916eacSRuslan Bukin 
224*d3916eacSRuslan Bukin 	/* hstatus */
225*d3916eacSRuslan Bukin 	hypctx->guest_regs.hyp_hstatus = HSTATUS_SPV | HSTATUS_VTW;
226*d3916eacSRuslan Bukin 	hypctx->guest_regs.hyp_hstatus |= HSTATUS_SPVP;
227*d3916eacSRuslan Bukin 
228*d3916eacSRuslan Bukin 	hypctx->cpu_id = vcpuid;
229*d3916eacSRuslan Bukin 	hyp->ctx[vcpuid] = hypctx;
230*d3916eacSRuslan Bukin 
231*d3916eacSRuslan Bukin 	aplic_cpuinit(hypctx);
232*d3916eacSRuslan Bukin 
233*d3916eacSRuslan Bukin 	return (hypctx);
234*d3916eacSRuslan Bukin }
235*d3916eacSRuslan Bukin 
236*d3916eacSRuslan Bukin static int
riscv_vmm_pinit(pmap_t pmap)237*d3916eacSRuslan Bukin riscv_vmm_pinit(pmap_t pmap)
238*d3916eacSRuslan Bukin {
239*d3916eacSRuslan Bukin 
240*d3916eacSRuslan Bukin 	dprintf("%s: pmap %p\n", __func__, pmap);
241*d3916eacSRuslan Bukin 
242*d3916eacSRuslan Bukin 	pmap_pinit_stage(pmap, PM_STAGE2);
243*d3916eacSRuslan Bukin 
244*d3916eacSRuslan Bukin 	return (1);
245*d3916eacSRuslan Bukin }
246*d3916eacSRuslan Bukin 
247*d3916eacSRuslan Bukin struct vmspace *
vmmops_vmspace_alloc(vm_offset_t min,vm_offset_t max)248*d3916eacSRuslan Bukin vmmops_vmspace_alloc(vm_offset_t min, vm_offset_t max)
249*d3916eacSRuslan Bukin {
250*d3916eacSRuslan Bukin 
251*d3916eacSRuslan Bukin 	return (vmspace_alloc(min, max, riscv_vmm_pinit));
252*d3916eacSRuslan Bukin }
253*d3916eacSRuslan Bukin 
254*d3916eacSRuslan Bukin void
vmmops_vmspace_free(struct vmspace * vmspace)255*d3916eacSRuslan Bukin vmmops_vmspace_free(struct vmspace *vmspace)
256*d3916eacSRuslan Bukin {
257*d3916eacSRuslan Bukin 
258*d3916eacSRuslan Bukin 	pmap_remove_pages(vmspace_pmap(vmspace));
259*d3916eacSRuslan Bukin 	vmspace_free(vmspace);
260*d3916eacSRuslan Bukin }
261*d3916eacSRuslan Bukin 
262*d3916eacSRuslan Bukin static void
riscv_unpriv_read(struct hypctx * hypctx,uintptr_t guest_addr,uint64_t * data,struct hyptrap * trap)263*d3916eacSRuslan Bukin riscv_unpriv_read(struct hypctx *hypctx, uintptr_t guest_addr, uint64_t *data,
264*d3916eacSRuslan Bukin     struct hyptrap *trap)
265*d3916eacSRuslan Bukin {
266*d3916eacSRuslan Bukin 	register struct hyptrap * htrap asm("a0");
267*d3916eacSRuslan Bukin 	uintptr_t old_hstatus;
268*d3916eacSRuslan Bukin 	uintptr_t old_stvec;
269*d3916eacSRuslan Bukin 	uintptr_t entry;
270*d3916eacSRuslan Bukin 	uint64_t val;
271*d3916eacSRuslan Bukin 	uint64_t tmp;
272*d3916eacSRuslan Bukin 	int intr;
273*d3916eacSRuslan Bukin 
274*d3916eacSRuslan Bukin 	entry = (uintptr_t)&vmm_unpriv_trap;
275*d3916eacSRuslan Bukin 	htrap = trap;
276*d3916eacSRuslan Bukin 
277*d3916eacSRuslan Bukin 	intr = intr_disable();
278*d3916eacSRuslan Bukin 
279*d3916eacSRuslan Bukin 	old_hstatus = csr_swap(hstatus, hypctx->guest_regs.hyp_hstatus);
280*d3916eacSRuslan Bukin 	/*
281*d3916eacSRuslan Bukin 	 * Setup a temporary exception vector, so that if hlvx.hu raises
282*d3916eacSRuslan Bukin 	 * an exception we catch it in the vmm_unpriv_trap().
283*d3916eacSRuslan Bukin 	 */
284*d3916eacSRuslan Bukin 	old_stvec = csr_swap(stvec, entry);
285*d3916eacSRuslan Bukin 
286*d3916eacSRuslan Bukin 	/*
287*d3916eacSRuslan Bukin 	 * Read first two bytes of instruction assuming it could be a
288*d3916eacSRuslan Bukin 	 * compressed one.
289*d3916eacSRuslan Bukin 	 */
290*d3916eacSRuslan Bukin 	__asm __volatile(".option push\n"
291*d3916eacSRuslan Bukin 			 ".option norvc\n"
292*d3916eacSRuslan Bukin 			"hlvx.hu %[val], (%[addr])\n"
293*d3916eacSRuslan Bukin 			".option pop\n"
294*d3916eacSRuslan Bukin 	    : [val] "=r" (val)
295*d3916eacSRuslan Bukin 	    : [addr] "r" (guest_addr), "r" (htrap)
296*d3916eacSRuslan Bukin 	    : "a1", "memory");
297*d3916eacSRuslan Bukin 
298*d3916eacSRuslan Bukin 	/*
299*d3916eacSRuslan Bukin 	 * Check if previous hlvx.hu did not raise an exception, and then
300*d3916eacSRuslan Bukin 	 * read the rest of instruction if it is a full-length one.
301*d3916eacSRuslan Bukin 	 */
302*d3916eacSRuslan Bukin 	if (trap->scause == -1 && (val & 0x3) == 0x3) {
303*d3916eacSRuslan Bukin 		guest_addr += 2;
304*d3916eacSRuslan Bukin 		__asm __volatile(".option push\n"
305*d3916eacSRuslan Bukin 				 ".option norvc\n"
306*d3916eacSRuslan Bukin 				"hlvx.hu %[tmp], (%[addr])\n"
307*d3916eacSRuslan Bukin 				".option pop\n"
308*d3916eacSRuslan Bukin 		    : [tmp] "=r" (tmp)
309*d3916eacSRuslan Bukin 		    : [addr] "r" (guest_addr), "r" (htrap)
310*d3916eacSRuslan Bukin 		    : "a1", "memory");
311*d3916eacSRuslan Bukin 		val |= (tmp << 16);
312*d3916eacSRuslan Bukin 	}
313*d3916eacSRuslan Bukin 
314*d3916eacSRuslan Bukin 	csr_write(hstatus, old_hstatus);
315*d3916eacSRuslan Bukin 	csr_write(stvec, old_stvec);
316*d3916eacSRuslan Bukin 
317*d3916eacSRuslan Bukin 	intr_restore(intr);
318*d3916eacSRuslan Bukin 
319*d3916eacSRuslan Bukin 	*data = val;
320*d3916eacSRuslan Bukin }
321*d3916eacSRuslan Bukin 
322*d3916eacSRuslan Bukin static int
riscv_gen_inst_emul_data(struct hypctx * hypctx,struct vm_exit * vme_ret,struct hyptrap * trap)323*d3916eacSRuslan Bukin riscv_gen_inst_emul_data(struct hypctx *hypctx, struct vm_exit *vme_ret,
324*d3916eacSRuslan Bukin     struct hyptrap *trap)
325*d3916eacSRuslan Bukin {
326*d3916eacSRuslan Bukin 	uintptr_t guest_addr;
327*d3916eacSRuslan Bukin 	struct vie *vie;
328*d3916eacSRuslan Bukin 	uint64_t insn;
329*d3916eacSRuslan Bukin 	int reg_num;
330*d3916eacSRuslan Bukin 	int rs2, rd;
331*d3916eacSRuslan Bukin 	int direction;
332*d3916eacSRuslan Bukin 	int sign_extend;
333*d3916eacSRuslan Bukin 	int access_size;
334*d3916eacSRuslan Bukin 
335*d3916eacSRuslan Bukin 	guest_addr = vme_ret->sepc;
336*d3916eacSRuslan Bukin 
337*d3916eacSRuslan Bukin 	KASSERT(vme_ret->scause == SCAUSE_FETCH_GUEST_PAGE_FAULT ||
338*d3916eacSRuslan Bukin 	    vme_ret->scause == SCAUSE_LOAD_GUEST_PAGE_FAULT ||
339*d3916eacSRuslan Bukin 	    vme_ret->scause == SCAUSE_STORE_GUEST_PAGE_FAULT,
340*d3916eacSRuslan Bukin 	    ("Invalid scause"));
341*d3916eacSRuslan Bukin 
342*d3916eacSRuslan Bukin 	direction = vme_ret->scause == SCAUSE_STORE_GUEST_PAGE_FAULT ?
343*d3916eacSRuslan Bukin 	    VM_DIR_WRITE : VM_DIR_READ;
344*d3916eacSRuslan Bukin 
345*d3916eacSRuslan Bukin 	sign_extend = 1;
346*d3916eacSRuslan Bukin 
347*d3916eacSRuslan Bukin 	bzero(trap, sizeof(struct hyptrap));
348*d3916eacSRuslan Bukin 	trap->scause = -1;
349*d3916eacSRuslan Bukin 	riscv_unpriv_read(hypctx, guest_addr, &insn, trap);
350*d3916eacSRuslan Bukin 	if (trap->scause != -1)
351*d3916eacSRuslan Bukin 		return (-1);
352*d3916eacSRuslan Bukin 
353*d3916eacSRuslan Bukin 	if ((insn & 0x3) == 0x3) {
354*d3916eacSRuslan Bukin 		rs2 = (insn & RS2_MASK) >> RS2_SHIFT;
355*d3916eacSRuslan Bukin 		rd = (insn & RD_MASK) >> RD_SHIFT;
356*d3916eacSRuslan Bukin 
357*d3916eacSRuslan Bukin 		if (direction == VM_DIR_WRITE) {
358*d3916eacSRuslan Bukin 			if (m_op(insn, MATCH_SB, MASK_SB))
359*d3916eacSRuslan Bukin 				access_size = 1;
360*d3916eacSRuslan Bukin 			else if (m_op(insn, MATCH_SH, MASK_SH))
361*d3916eacSRuslan Bukin 				access_size = 2;
362*d3916eacSRuslan Bukin 			else if (m_op(insn, MATCH_SW, MASK_SW))
363*d3916eacSRuslan Bukin 				access_size = 4;
364*d3916eacSRuslan Bukin 			else if (m_op(insn, MATCH_SD, MASK_SD))
365*d3916eacSRuslan Bukin 				access_size = 8;
366*d3916eacSRuslan Bukin 			else {
367*d3916eacSRuslan Bukin 				printf("unknown store instr at %lx",
368*d3916eacSRuslan Bukin 				    guest_addr);
369*d3916eacSRuslan Bukin 				return (-2);
370*d3916eacSRuslan Bukin 			}
371*d3916eacSRuslan Bukin 			reg_num = rs2;
372*d3916eacSRuslan Bukin 		} else {
373*d3916eacSRuslan Bukin 			if (m_op(insn, MATCH_LB, MASK_LB))
374*d3916eacSRuslan Bukin 				access_size = 1;
375*d3916eacSRuslan Bukin 			else if (m_op(insn, MATCH_LH, MASK_LH))
376*d3916eacSRuslan Bukin 				access_size = 2;
377*d3916eacSRuslan Bukin 			else if (m_op(insn, MATCH_LW, MASK_LW))
378*d3916eacSRuslan Bukin 				access_size = 4;
379*d3916eacSRuslan Bukin 			else if (m_op(insn, MATCH_LD, MASK_LD))
380*d3916eacSRuslan Bukin 				access_size = 8;
381*d3916eacSRuslan Bukin 			else if (m_op(insn, MATCH_LBU, MASK_LBU)) {
382*d3916eacSRuslan Bukin 				access_size = 1;
383*d3916eacSRuslan Bukin 				sign_extend = 0;
384*d3916eacSRuslan Bukin 			} else if (m_op(insn, MATCH_LHU, MASK_LHU)) {
385*d3916eacSRuslan Bukin 				access_size = 2;
386*d3916eacSRuslan Bukin 				sign_extend = 0;
387*d3916eacSRuslan Bukin 			} else if (m_op(insn, MATCH_LWU, MASK_LWU)) {
388*d3916eacSRuslan Bukin 				access_size = 4;
389*d3916eacSRuslan Bukin 				sign_extend = 0;
390*d3916eacSRuslan Bukin 			} else {
391*d3916eacSRuslan Bukin 				printf("unknown load instr at %lx",
392*d3916eacSRuslan Bukin 				    guest_addr);
393*d3916eacSRuslan Bukin 				return (-3);
394*d3916eacSRuslan Bukin 			}
395*d3916eacSRuslan Bukin 			reg_num = rd;
396*d3916eacSRuslan Bukin 		}
397*d3916eacSRuslan Bukin 		vme_ret->inst_length = 4;
398*d3916eacSRuslan Bukin 	} else {
399*d3916eacSRuslan Bukin 		rs2 = (insn >> 7) & 0x7;
400*d3916eacSRuslan Bukin 		rs2 += 0x8;
401*d3916eacSRuslan Bukin 		rd = (insn >> 2) & 0x7;
402*d3916eacSRuslan Bukin 		rd += 0x8;
403*d3916eacSRuslan Bukin 
404*d3916eacSRuslan Bukin 		if (direction == VM_DIR_WRITE) {
405*d3916eacSRuslan Bukin 			if (m_op(insn, MATCH_C_SW, MASK_C_SW))
406*d3916eacSRuslan Bukin 				access_size = 4;
407*d3916eacSRuslan Bukin 			else if (m_op(insn, MATCH_C_SD, MASK_C_SD))
408*d3916eacSRuslan Bukin 				access_size = 8;
409*d3916eacSRuslan Bukin 			else {
410*d3916eacSRuslan Bukin 				printf("unknown compressed store instr at %lx",
411*d3916eacSRuslan Bukin 				    guest_addr);
412*d3916eacSRuslan Bukin 				return (-4);
413*d3916eacSRuslan Bukin 			}
414*d3916eacSRuslan Bukin 		} else  {
415*d3916eacSRuslan Bukin 			if (m_op(insn, MATCH_C_LW, MASK_C_LW))
416*d3916eacSRuslan Bukin 				access_size = 4;
417*d3916eacSRuslan Bukin 			else if (m_op(insn, MATCH_C_LD, MASK_C_LD))
418*d3916eacSRuslan Bukin 				access_size = 8;
419*d3916eacSRuslan Bukin 			else {
420*d3916eacSRuslan Bukin 				printf("unknown load instr at %lx", guest_addr);
421*d3916eacSRuslan Bukin 				return (-5);
422*d3916eacSRuslan Bukin 			}
423*d3916eacSRuslan Bukin 		}
424*d3916eacSRuslan Bukin 		reg_num = rd;
425*d3916eacSRuslan Bukin 		vme_ret->inst_length = 2;
426*d3916eacSRuslan Bukin 	}
427*d3916eacSRuslan Bukin 
428*d3916eacSRuslan Bukin 	vme_ret->u.inst_emul.gpa = (vme_ret->htval << 2) |
429*d3916eacSRuslan Bukin 	    (vme_ret->stval & 0x3);
430*d3916eacSRuslan Bukin 
431*d3916eacSRuslan Bukin 	dprintf("guest_addr %lx insn %lx, reg %d, gpa %lx\n", guest_addr, insn,
432*d3916eacSRuslan Bukin 	    reg_num, vme_ret->u.inst_emul.gpa);
433*d3916eacSRuslan Bukin 
434*d3916eacSRuslan Bukin 	vie = &vme_ret->u.inst_emul.vie;
435*d3916eacSRuslan Bukin 	vie->dir = direction;
436*d3916eacSRuslan Bukin 	vie->reg = reg_num;
437*d3916eacSRuslan Bukin 	vie->sign_extend = sign_extend;
438*d3916eacSRuslan Bukin 	vie->access_size = access_size;
439*d3916eacSRuslan Bukin 
440*d3916eacSRuslan Bukin 	return (0);
441*d3916eacSRuslan Bukin }
442*d3916eacSRuslan Bukin 
443*d3916eacSRuslan Bukin static bool
riscv_handle_world_switch(struct hypctx * hypctx,struct vm_exit * vme,pmap_t pmap)444*d3916eacSRuslan Bukin riscv_handle_world_switch(struct hypctx *hypctx, struct vm_exit *vme,
445*d3916eacSRuslan Bukin     pmap_t pmap)
446*d3916eacSRuslan Bukin {
447*d3916eacSRuslan Bukin 	struct hyptrap trap;
448*d3916eacSRuslan Bukin 	uint64_t insn;
449*d3916eacSRuslan Bukin 	uint64_t gpa;
450*d3916eacSRuslan Bukin 	bool handled;
451*d3916eacSRuslan Bukin 	bool retu;
452*d3916eacSRuslan Bukin 	int ret;
453*d3916eacSRuslan Bukin 	int i;
454*d3916eacSRuslan Bukin 
455*d3916eacSRuslan Bukin 	handled = false;
456*d3916eacSRuslan Bukin 
457*d3916eacSRuslan Bukin 	if (vme->scause & SCAUSE_INTR) {
458*d3916eacSRuslan Bukin 		/*
459*d3916eacSRuslan Bukin 		 * Host interrupt? Leave critical section to handle.
460*d3916eacSRuslan Bukin 		 */
461*d3916eacSRuslan Bukin 		vmm_stat_incr(hypctx->vcpu, VMEXIT_IRQ, 1);
462*d3916eacSRuslan Bukin 		vme->exitcode = VM_EXITCODE_BOGUS;
463*d3916eacSRuslan Bukin 		vme->inst_length = 0;
464*d3916eacSRuslan Bukin 		return (handled);
465*d3916eacSRuslan Bukin 	}
466*d3916eacSRuslan Bukin 
467*d3916eacSRuslan Bukin 	switch (vme->scause) {
468*d3916eacSRuslan Bukin 	case SCAUSE_FETCH_GUEST_PAGE_FAULT:
469*d3916eacSRuslan Bukin 	case SCAUSE_LOAD_GUEST_PAGE_FAULT:
470*d3916eacSRuslan Bukin 	case SCAUSE_STORE_GUEST_PAGE_FAULT:
471*d3916eacSRuslan Bukin 		gpa = (vme->htval << 2) | (vme->stval & 0x3);
472*d3916eacSRuslan Bukin 		if (vm_mem_allocated(hypctx->vcpu, gpa)) {
473*d3916eacSRuslan Bukin 			vme->exitcode = VM_EXITCODE_PAGING;
474*d3916eacSRuslan Bukin 			vme->inst_length = 0;
475*d3916eacSRuslan Bukin 			vme->u.paging.gpa = gpa;
476*d3916eacSRuslan Bukin 		} else {
477*d3916eacSRuslan Bukin 			ret = riscv_gen_inst_emul_data(hypctx, vme, &trap);
478*d3916eacSRuslan Bukin 			if (ret != 0) {
479*d3916eacSRuslan Bukin 				vme->exitcode = VM_EXITCODE_HYP;
480*d3916eacSRuslan Bukin 				vme->u.hyp.scause = trap.scause;
481*d3916eacSRuslan Bukin 				break;
482*d3916eacSRuslan Bukin 			}
483*d3916eacSRuslan Bukin 			vme->exitcode = VM_EXITCODE_INST_EMUL;
484*d3916eacSRuslan Bukin 		}
485*d3916eacSRuslan Bukin 		break;
486*d3916eacSRuslan Bukin 	case SCAUSE_ILLEGAL_INSTRUCTION:
487*d3916eacSRuslan Bukin 		/*
488*d3916eacSRuslan Bukin 		 * TODO: handle illegal instruction properly.
489*d3916eacSRuslan Bukin 		 */
490*d3916eacSRuslan Bukin 		printf("%s: Illegal instruction at %lx stval 0x%lx htval "
491*d3916eacSRuslan Bukin 		    "0x%lx\n", __func__, vme->sepc, vme->stval, vme->htval);
492*d3916eacSRuslan Bukin 		vmm_stat_incr(hypctx->vcpu, VMEXIT_UNHANDLED, 1);
493*d3916eacSRuslan Bukin 		vme->exitcode = VM_EXITCODE_BOGUS;
494*d3916eacSRuslan Bukin 		handled = false;
495*d3916eacSRuslan Bukin 		break;
496*d3916eacSRuslan Bukin 	case SCAUSE_VIRTUAL_SUPERVISOR_ECALL:
497*d3916eacSRuslan Bukin 		retu = false;
498*d3916eacSRuslan Bukin 		vmm_sbi_ecall(hypctx->vcpu, &retu);
499*d3916eacSRuslan Bukin 		if (retu == false) {
500*d3916eacSRuslan Bukin 			handled = true;
501*d3916eacSRuslan Bukin 			break;
502*d3916eacSRuslan Bukin 		}
503*d3916eacSRuslan Bukin 		for (i = 0; i < nitems(vme->u.ecall.args); i++)
504*d3916eacSRuslan Bukin 			vme->u.ecall.args[i] = hypctx->guest_regs.hyp_a[i];
505*d3916eacSRuslan Bukin 		vme->exitcode = VM_EXITCODE_ECALL;
506*d3916eacSRuslan Bukin 		handled = false;
507*d3916eacSRuslan Bukin 		break;
508*d3916eacSRuslan Bukin 	case SCAUSE_VIRTUAL_INSTRUCTION:
509*d3916eacSRuslan Bukin 		insn = vme->stval;
510*d3916eacSRuslan Bukin 		if (m_op(insn, MATCH_WFI, MASK_WFI))
511*d3916eacSRuslan Bukin 			vme->exitcode = VM_EXITCODE_WFI;
512*d3916eacSRuslan Bukin 		else
513*d3916eacSRuslan Bukin 			vme->exitcode = VM_EXITCODE_BOGUS;
514*d3916eacSRuslan Bukin 		handled = false;
515*d3916eacSRuslan Bukin 		break;
516*d3916eacSRuslan Bukin 	default:
517*d3916eacSRuslan Bukin 		printf("unknown scause %lx\n", vme->scause);
518*d3916eacSRuslan Bukin 		vmm_stat_incr(hypctx->vcpu, VMEXIT_UNHANDLED, 1);
519*d3916eacSRuslan Bukin 		vme->exitcode = VM_EXITCODE_BOGUS;
520*d3916eacSRuslan Bukin 		handled = false;
521*d3916eacSRuslan Bukin 		break;
522*d3916eacSRuslan Bukin 	}
523*d3916eacSRuslan Bukin 
524*d3916eacSRuslan Bukin 	return (handled);
525*d3916eacSRuslan Bukin }
526*d3916eacSRuslan Bukin 
527*d3916eacSRuslan Bukin int
vmmops_gla2gpa(void * vcpui,struct vm_guest_paging * paging,uint64_t gla,int prot,uint64_t * gpa,int * is_fault)528*d3916eacSRuslan Bukin vmmops_gla2gpa(void *vcpui, struct vm_guest_paging *paging, uint64_t gla,
529*d3916eacSRuslan Bukin     int prot, uint64_t *gpa, int *is_fault)
530*d3916eacSRuslan Bukin {
531*d3916eacSRuslan Bukin 
532*d3916eacSRuslan Bukin 	/* Implement me. */
533*d3916eacSRuslan Bukin 
534*d3916eacSRuslan Bukin 	return (ENOSYS);
535*d3916eacSRuslan Bukin }
536*d3916eacSRuslan Bukin 
537*d3916eacSRuslan Bukin void
riscv_send_ipi(struct hypctx * hypctx,int hart_id)538*d3916eacSRuslan Bukin riscv_send_ipi(struct hypctx *hypctx, int hart_id)
539*d3916eacSRuslan Bukin {
540*d3916eacSRuslan Bukin 	struct hyp *hyp;
541*d3916eacSRuslan Bukin 	struct vm *vm;
542*d3916eacSRuslan Bukin 
543*d3916eacSRuslan Bukin 	hyp = hypctx->hyp;
544*d3916eacSRuslan Bukin 	vm = hyp->vm;
545*d3916eacSRuslan Bukin 
546*d3916eacSRuslan Bukin 	atomic_set_32(&hypctx->ipi_pending, 1);
547*d3916eacSRuslan Bukin 
548*d3916eacSRuslan Bukin 	vcpu_notify_event(vm_vcpu(vm, hart_id));
549*d3916eacSRuslan Bukin }
550*d3916eacSRuslan Bukin 
551*d3916eacSRuslan Bukin int
riscv_check_ipi(struct hypctx * hypctx,bool clear)552*d3916eacSRuslan Bukin riscv_check_ipi(struct hypctx *hypctx, bool clear)
553*d3916eacSRuslan Bukin {
554*d3916eacSRuslan Bukin 	int val;
555*d3916eacSRuslan Bukin 
556*d3916eacSRuslan Bukin 	if (clear)
557*d3916eacSRuslan Bukin 		val = atomic_swap_32(&hypctx->ipi_pending, 0);
558*d3916eacSRuslan Bukin 	else
559*d3916eacSRuslan Bukin 		val = hypctx->ipi_pending;
560*d3916eacSRuslan Bukin 
561*d3916eacSRuslan Bukin 	return (val);
562*d3916eacSRuslan Bukin }
563*d3916eacSRuslan Bukin 
564*d3916eacSRuslan Bukin static void
riscv_sync_interrupts(struct hypctx * hypctx)565*d3916eacSRuslan Bukin riscv_sync_interrupts(struct hypctx *hypctx)
566*d3916eacSRuslan Bukin {
567*d3916eacSRuslan Bukin 	int pending;
568*d3916eacSRuslan Bukin 
569*d3916eacSRuslan Bukin 	pending = aplic_check_pending(hypctx);
570*d3916eacSRuslan Bukin 
571*d3916eacSRuslan Bukin 	if (pending)
572*d3916eacSRuslan Bukin 		hypctx->guest_csrs.hvip |= HVIP_VSEIP;
573*d3916eacSRuslan Bukin 	else
574*d3916eacSRuslan Bukin 		hypctx->guest_csrs.hvip &= ~HVIP_VSEIP;
575*d3916eacSRuslan Bukin 
576*d3916eacSRuslan Bukin 	csr_write(hvip, hypctx->guest_csrs.hvip);
577*d3916eacSRuslan Bukin }
578*d3916eacSRuslan Bukin 
579*d3916eacSRuslan Bukin static void
riscv_sync_ipi(struct hypctx * hypctx)580*d3916eacSRuslan Bukin riscv_sync_ipi(struct hypctx *hypctx)
581*d3916eacSRuslan Bukin {
582*d3916eacSRuslan Bukin 
583*d3916eacSRuslan Bukin 	/* Guest clears VSSIP bit manually. */
584*d3916eacSRuslan Bukin 	if (riscv_check_ipi(hypctx, true))
585*d3916eacSRuslan Bukin 		hypctx->guest_csrs.hvip |= HVIP_VSSIP;
586*d3916eacSRuslan Bukin 
587*d3916eacSRuslan Bukin 	csr_write(hvip, hypctx->guest_csrs.hvip);
588*d3916eacSRuslan Bukin }
589*d3916eacSRuslan Bukin 
590*d3916eacSRuslan Bukin int
vmmops_run(void * vcpui,register_t pc,pmap_t pmap,struct vm_eventinfo * evinfo)591*d3916eacSRuslan Bukin vmmops_run(void *vcpui, register_t pc, pmap_t pmap, struct vm_eventinfo *evinfo)
592*d3916eacSRuslan Bukin {
593*d3916eacSRuslan Bukin 	struct hypctx *hypctx;
594*d3916eacSRuslan Bukin 	struct vm_exit *vme;
595*d3916eacSRuslan Bukin 	struct vcpu *vcpu;
596*d3916eacSRuslan Bukin 	register_t val;
597*d3916eacSRuslan Bukin 	bool handled;
598*d3916eacSRuslan Bukin 
599*d3916eacSRuslan Bukin 	hypctx = (struct hypctx *)vcpui;
600*d3916eacSRuslan Bukin 	vcpu = hypctx->vcpu;
601*d3916eacSRuslan Bukin 	vme = vm_exitinfo(vcpu);
602*d3916eacSRuslan Bukin 
603*d3916eacSRuslan Bukin 	hypctx->guest_regs.hyp_sepc = (uint64_t)pc;
604*d3916eacSRuslan Bukin 
605*d3916eacSRuslan Bukin 	vmmops_delegate();
606*d3916eacSRuslan Bukin 
607*d3916eacSRuslan Bukin 	/*
608*d3916eacSRuslan Bukin 	 * From The RISC-V Instruction Set Manual
609*d3916eacSRuslan Bukin 	 * Volume II: RISC-V Privileged Architectures
610*d3916eacSRuslan Bukin 	 *
611*d3916eacSRuslan Bukin 	 * If the new virtual machine's guest physical page tables
612*d3916eacSRuslan Bukin 	 * have been modified, it may be necessary to execute an HFENCE.GVMA
613*d3916eacSRuslan Bukin 	 * instruction (see Section 5.3.2) before or after writing hgatp.
614*d3916eacSRuslan Bukin 	 */
615*d3916eacSRuslan Bukin 	__asm __volatile("hfence.gvma" ::: "memory");
616*d3916eacSRuslan Bukin 
617*d3916eacSRuslan Bukin 	csr_write(hgatp, pmap->pm_satp);
618*d3916eacSRuslan Bukin 	csr_write(henvcfg, HENVCFG_STCE);
619*d3916eacSRuslan Bukin 	csr_write(hie, HIE_VSEIE | HIE_VSSIE | HIE_SGEIE);
620*d3916eacSRuslan Bukin 	/* TODO: should we trap rdcycle / rdtime? */
621*d3916eacSRuslan Bukin 	csr_write(hcounteren, HCOUNTEREN_CY | HCOUNTEREN_TM);
622*d3916eacSRuslan Bukin 
623*d3916eacSRuslan Bukin 	vmmops_vcpu_restore_csrs(hypctx);
624*d3916eacSRuslan Bukin 
625*d3916eacSRuslan Bukin 	for (;;) {
626*d3916eacSRuslan Bukin 		dprintf("%s: pc %lx\n", __func__, pc);
627*d3916eacSRuslan Bukin 
628*d3916eacSRuslan Bukin 		if (hypctx->has_exception) {
629*d3916eacSRuslan Bukin 			hypctx->has_exception = false;
630*d3916eacSRuslan Bukin 			/*
631*d3916eacSRuslan Bukin 			 * TODO: implement exception injection.
632*d3916eacSRuslan Bukin 			 */
633*d3916eacSRuslan Bukin 		}
634*d3916eacSRuslan Bukin 
635*d3916eacSRuslan Bukin 		val = intr_disable();
636*d3916eacSRuslan Bukin 
637*d3916eacSRuslan Bukin 		/* Check if the vcpu is suspended */
638*d3916eacSRuslan Bukin 		if (vcpu_suspended(evinfo)) {
639*d3916eacSRuslan Bukin 			intr_restore(val);
640*d3916eacSRuslan Bukin 			vm_exit_suspended(vcpu, pc);
641*d3916eacSRuslan Bukin 			break;
642*d3916eacSRuslan Bukin 		}
643*d3916eacSRuslan Bukin 
644*d3916eacSRuslan Bukin 		if (vcpu_debugged(vcpu)) {
645*d3916eacSRuslan Bukin 			intr_restore(val);
646*d3916eacSRuslan Bukin 			vm_exit_debug(vcpu, pc);
647*d3916eacSRuslan Bukin 			break;
648*d3916eacSRuslan Bukin 		}
649*d3916eacSRuslan Bukin 
650*d3916eacSRuslan Bukin 		/*
651*d3916eacSRuslan Bukin 		 * TODO: What happens if a timer interrupt is asserted exactly
652*d3916eacSRuslan Bukin 		 * here, but for the previous VM?
653*d3916eacSRuslan Bukin 		 */
654*d3916eacSRuslan Bukin 		riscv_set_active_vcpu(hypctx);
655*d3916eacSRuslan Bukin 		aplic_flush_hwstate(hypctx);
656*d3916eacSRuslan Bukin 
657*d3916eacSRuslan Bukin 		riscv_sync_interrupts(hypctx);
658*d3916eacSRuslan Bukin 		riscv_sync_ipi(hypctx);
659*d3916eacSRuslan Bukin 
660*d3916eacSRuslan Bukin 		dprintf("%s: Entering guest VM, vsatp %lx, ss %lx hs %lx\n",
661*d3916eacSRuslan Bukin 		    __func__, csr_read(vsatp), hypctx->guest_regs.hyp_sstatus,
662*d3916eacSRuslan Bukin 		    hypctx->guest_regs.hyp_hstatus);
663*d3916eacSRuslan Bukin 
664*d3916eacSRuslan Bukin 		vmm_switch(hypctx);
665*d3916eacSRuslan Bukin 
666*d3916eacSRuslan Bukin 		dprintf("%s: Leaving guest VM, hstatus %lx\n", __func__,
667*d3916eacSRuslan Bukin 		    hypctx->guest_regs.hyp_hstatus);
668*d3916eacSRuslan Bukin 
669*d3916eacSRuslan Bukin 		aplic_sync_hwstate(hypctx);
670*d3916eacSRuslan Bukin 		riscv_sync_interrupts(hypctx);
671*d3916eacSRuslan Bukin 
672*d3916eacSRuslan Bukin 		/*
673*d3916eacSRuslan Bukin 		 * TODO: deactivate stage 2 pmap here if needed.
674*d3916eacSRuslan Bukin 		 */
675*d3916eacSRuslan Bukin 
676*d3916eacSRuslan Bukin 		vme->scause = csr_read(scause);
677*d3916eacSRuslan Bukin 		vme->sepc = csr_read(sepc);
678*d3916eacSRuslan Bukin 		vme->stval = csr_read(stval);
679*d3916eacSRuslan Bukin 		vme->htval = csr_read(htval);
680*d3916eacSRuslan Bukin 		vme->htinst = csr_read(htinst);
681*d3916eacSRuslan Bukin 
682*d3916eacSRuslan Bukin 		intr_restore(val);
683*d3916eacSRuslan Bukin 
684*d3916eacSRuslan Bukin 		vmm_stat_incr(vcpu, VMEXIT_COUNT, 1);
685*d3916eacSRuslan Bukin 		vme->pc = hypctx->guest_regs.hyp_sepc;
686*d3916eacSRuslan Bukin 		vme->inst_length = INSN_SIZE;
687*d3916eacSRuslan Bukin 
688*d3916eacSRuslan Bukin 		handled = riscv_handle_world_switch(hypctx, vme, pmap);
689*d3916eacSRuslan Bukin 		if (handled == false)
690*d3916eacSRuslan Bukin 			/* Exit loop to emulate instruction. */
691*d3916eacSRuslan Bukin 			break;
692*d3916eacSRuslan Bukin 		else {
693*d3916eacSRuslan Bukin 			/* Resume guest execution from the next instruction. */
694*d3916eacSRuslan Bukin 			hypctx->guest_regs.hyp_sepc += vme->inst_length;
695*d3916eacSRuslan Bukin 		}
696*d3916eacSRuslan Bukin 	}
697*d3916eacSRuslan Bukin 
698*d3916eacSRuslan Bukin 	vmmops_vcpu_save_csrs(hypctx);
699*d3916eacSRuslan Bukin 
700*d3916eacSRuslan Bukin 	return (0);
701*d3916eacSRuslan Bukin }
702*d3916eacSRuslan Bukin 
703*d3916eacSRuslan Bukin static void
riscv_pcpu_vmcleanup(void * arg)704*d3916eacSRuslan Bukin riscv_pcpu_vmcleanup(void *arg)
705*d3916eacSRuslan Bukin {
706*d3916eacSRuslan Bukin 	struct hyp *hyp;
707*d3916eacSRuslan Bukin 	int i, maxcpus;
708*d3916eacSRuslan Bukin 
709*d3916eacSRuslan Bukin 	hyp = arg;
710*d3916eacSRuslan Bukin 	maxcpus = vm_get_maxcpus(hyp->vm);
711*d3916eacSRuslan Bukin 	for (i = 0; i < maxcpus; i++) {
712*d3916eacSRuslan Bukin 		if (riscv_get_active_vcpu() == hyp->ctx[i]) {
713*d3916eacSRuslan Bukin 			riscv_set_active_vcpu(NULL);
714*d3916eacSRuslan Bukin 			break;
715*d3916eacSRuslan Bukin 		}
716*d3916eacSRuslan Bukin 	}
717*d3916eacSRuslan Bukin }
718*d3916eacSRuslan Bukin 
719*d3916eacSRuslan Bukin void
vmmops_vcpu_cleanup(void * vcpui)720*d3916eacSRuslan Bukin vmmops_vcpu_cleanup(void *vcpui)
721*d3916eacSRuslan Bukin {
722*d3916eacSRuslan Bukin 	struct hypctx *hypctx;
723*d3916eacSRuslan Bukin 
724*d3916eacSRuslan Bukin 	hypctx = vcpui;
725*d3916eacSRuslan Bukin 
726*d3916eacSRuslan Bukin 	dprintf("%s\n", __func__);
727*d3916eacSRuslan Bukin 
728*d3916eacSRuslan Bukin 	aplic_cpucleanup(hypctx);
729*d3916eacSRuslan Bukin 
730*d3916eacSRuslan Bukin 	free(hypctx, M_HYP);
731*d3916eacSRuslan Bukin }
732*d3916eacSRuslan Bukin 
733*d3916eacSRuslan Bukin void
vmmops_cleanup(void * vmi)734*d3916eacSRuslan Bukin vmmops_cleanup(void *vmi)
735*d3916eacSRuslan Bukin {
736*d3916eacSRuslan Bukin 	struct hyp *hyp;
737*d3916eacSRuslan Bukin 
738*d3916eacSRuslan Bukin 	hyp = vmi;
739*d3916eacSRuslan Bukin 
740*d3916eacSRuslan Bukin 	dprintf("%s\n", __func__);
741*d3916eacSRuslan Bukin 
742*d3916eacSRuslan Bukin 	aplic_vmcleanup(hyp);
743*d3916eacSRuslan Bukin 
744*d3916eacSRuslan Bukin 	smp_rendezvous(NULL, riscv_pcpu_vmcleanup, NULL, hyp);
745*d3916eacSRuslan Bukin 
746*d3916eacSRuslan Bukin 	free(hyp, M_HYP);
747*d3916eacSRuslan Bukin }
748*d3916eacSRuslan Bukin 
749*d3916eacSRuslan Bukin /*
750*d3916eacSRuslan Bukin  * Return register value. Registers have different sizes and an explicit cast
751*d3916eacSRuslan Bukin  * must be made to ensure proper conversion.
752*d3916eacSRuslan Bukin  */
753*d3916eacSRuslan Bukin static uint64_t *
hypctx_regptr(struct hypctx * hypctx,int reg)754*d3916eacSRuslan Bukin hypctx_regptr(struct hypctx *hypctx, int reg)
755*d3916eacSRuslan Bukin {
756*d3916eacSRuslan Bukin 
757*d3916eacSRuslan Bukin 	switch (reg) {
758*d3916eacSRuslan Bukin 	case VM_REG_GUEST_RA:
759*d3916eacSRuslan Bukin 		return (&hypctx->guest_regs.hyp_ra);
760*d3916eacSRuslan Bukin 	case VM_REG_GUEST_SP:
761*d3916eacSRuslan Bukin 		return (&hypctx->guest_regs.hyp_sp);
762*d3916eacSRuslan Bukin 	case VM_REG_GUEST_GP:
763*d3916eacSRuslan Bukin 		return (&hypctx->guest_regs.hyp_gp);
764*d3916eacSRuslan Bukin 	case VM_REG_GUEST_TP:
765*d3916eacSRuslan Bukin 		return (&hypctx->guest_regs.hyp_tp);
766*d3916eacSRuslan Bukin 	case VM_REG_GUEST_T0:
767*d3916eacSRuslan Bukin 		return (&hypctx->guest_regs.hyp_t[0]);
768*d3916eacSRuslan Bukin 	case VM_REG_GUEST_T1:
769*d3916eacSRuslan Bukin 		return (&hypctx->guest_regs.hyp_t[1]);
770*d3916eacSRuslan Bukin 	case VM_REG_GUEST_T2:
771*d3916eacSRuslan Bukin 		return (&hypctx->guest_regs.hyp_t[2]);
772*d3916eacSRuslan Bukin 	case VM_REG_GUEST_S0:
773*d3916eacSRuslan Bukin 		return (&hypctx->guest_regs.hyp_s[0]);
774*d3916eacSRuslan Bukin 	case VM_REG_GUEST_S1:
775*d3916eacSRuslan Bukin 		return (&hypctx->guest_regs.hyp_s[1]);
776*d3916eacSRuslan Bukin 	case VM_REG_GUEST_A0:
777*d3916eacSRuslan Bukin 		return (&hypctx->guest_regs.hyp_a[0]);
778*d3916eacSRuslan Bukin 	case VM_REG_GUEST_A1:
779*d3916eacSRuslan Bukin 		return (&hypctx->guest_regs.hyp_a[1]);
780*d3916eacSRuslan Bukin 	case VM_REG_GUEST_A2:
781*d3916eacSRuslan Bukin 		return (&hypctx->guest_regs.hyp_a[2]);
782*d3916eacSRuslan Bukin 	case VM_REG_GUEST_A3:
783*d3916eacSRuslan Bukin 		return (&hypctx->guest_regs.hyp_a[3]);
784*d3916eacSRuslan Bukin 	case VM_REG_GUEST_A4:
785*d3916eacSRuslan Bukin 		return (&hypctx->guest_regs.hyp_a[4]);
786*d3916eacSRuslan Bukin 	case VM_REG_GUEST_A5:
787*d3916eacSRuslan Bukin 		return (&hypctx->guest_regs.hyp_a[5]);
788*d3916eacSRuslan Bukin 	case VM_REG_GUEST_A6:
789*d3916eacSRuslan Bukin 		return (&hypctx->guest_regs.hyp_a[6]);
790*d3916eacSRuslan Bukin 	case VM_REG_GUEST_A7:
791*d3916eacSRuslan Bukin 		return (&hypctx->guest_regs.hyp_a[7]);
792*d3916eacSRuslan Bukin 	case VM_REG_GUEST_S2:
793*d3916eacSRuslan Bukin 		return (&hypctx->guest_regs.hyp_s[2]);
794*d3916eacSRuslan Bukin 	case VM_REG_GUEST_S3:
795*d3916eacSRuslan Bukin 		return (&hypctx->guest_regs.hyp_s[3]);
796*d3916eacSRuslan Bukin 	case VM_REG_GUEST_S4:
797*d3916eacSRuslan Bukin 		return (&hypctx->guest_regs.hyp_s[4]);
798*d3916eacSRuslan Bukin 	case VM_REG_GUEST_S5:
799*d3916eacSRuslan Bukin 		return (&hypctx->guest_regs.hyp_s[5]);
800*d3916eacSRuslan Bukin 	case VM_REG_GUEST_S6:
801*d3916eacSRuslan Bukin 		return (&hypctx->guest_regs.hyp_s[6]);
802*d3916eacSRuslan Bukin 	case VM_REG_GUEST_S7:
803*d3916eacSRuslan Bukin 		return (&hypctx->guest_regs.hyp_s[7]);
804*d3916eacSRuslan Bukin 	case VM_REG_GUEST_S8:
805*d3916eacSRuslan Bukin 		return (&hypctx->guest_regs.hyp_s[8]);
806*d3916eacSRuslan Bukin 	case VM_REG_GUEST_S9:
807*d3916eacSRuslan Bukin 		return (&hypctx->guest_regs.hyp_s[9]);
808*d3916eacSRuslan Bukin 	case VM_REG_GUEST_S10:
809*d3916eacSRuslan Bukin 		return (&hypctx->guest_regs.hyp_s[10]);
810*d3916eacSRuslan Bukin 	case VM_REG_GUEST_S11:
811*d3916eacSRuslan Bukin 		return (&hypctx->guest_regs.hyp_s[11]);
812*d3916eacSRuslan Bukin 	case VM_REG_GUEST_T3:
813*d3916eacSRuslan Bukin 		return (&hypctx->guest_regs.hyp_t[3]);
814*d3916eacSRuslan Bukin 	case VM_REG_GUEST_T4:
815*d3916eacSRuslan Bukin 		return (&hypctx->guest_regs.hyp_t[4]);
816*d3916eacSRuslan Bukin 	case VM_REG_GUEST_T5:
817*d3916eacSRuslan Bukin 		return (&hypctx->guest_regs.hyp_t[5]);
818*d3916eacSRuslan Bukin 	case VM_REG_GUEST_T6:
819*d3916eacSRuslan Bukin 		return (&hypctx->guest_regs.hyp_t[6]);
820*d3916eacSRuslan Bukin 	case VM_REG_GUEST_SEPC:
821*d3916eacSRuslan Bukin 		return (&hypctx->guest_regs.hyp_sepc);
822*d3916eacSRuslan Bukin 	default:
823*d3916eacSRuslan Bukin 		break;
824*d3916eacSRuslan Bukin 	}
825*d3916eacSRuslan Bukin 
826*d3916eacSRuslan Bukin 	return (NULL);
827*d3916eacSRuslan Bukin }
828*d3916eacSRuslan Bukin 
829*d3916eacSRuslan Bukin int
vmmops_getreg(void * vcpui,int reg,uint64_t * retval)830*d3916eacSRuslan Bukin vmmops_getreg(void *vcpui, int reg, uint64_t *retval)
831*d3916eacSRuslan Bukin {
832*d3916eacSRuslan Bukin 	uint64_t *regp;
833*d3916eacSRuslan Bukin 	int running, hostcpu;
834*d3916eacSRuslan Bukin 	struct hypctx *hypctx;
835*d3916eacSRuslan Bukin 
836*d3916eacSRuslan Bukin 	hypctx = vcpui;
837*d3916eacSRuslan Bukin 
838*d3916eacSRuslan Bukin 	running = vcpu_is_running(hypctx->vcpu, &hostcpu);
839*d3916eacSRuslan Bukin 	if (running && hostcpu != curcpu)
840*d3916eacSRuslan Bukin 		panic("%s: %s%d is running", __func__, vm_name(hypctx->hyp->vm),
841*d3916eacSRuslan Bukin 		    vcpu_vcpuid(hypctx->vcpu));
842*d3916eacSRuslan Bukin 
843*d3916eacSRuslan Bukin 	if (reg == VM_REG_GUEST_ZERO) {
844*d3916eacSRuslan Bukin 		*retval = 0;
845*d3916eacSRuslan Bukin 		return (0);
846*d3916eacSRuslan Bukin 	}
847*d3916eacSRuslan Bukin 
848*d3916eacSRuslan Bukin 	regp = hypctx_regptr(hypctx, reg);
849*d3916eacSRuslan Bukin 	if (regp == NULL)
850*d3916eacSRuslan Bukin 		return (EINVAL);
851*d3916eacSRuslan Bukin 
852*d3916eacSRuslan Bukin 	*retval = *regp;
853*d3916eacSRuslan Bukin 
854*d3916eacSRuslan Bukin 	return (0);
855*d3916eacSRuslan Bukin }
856*d3916eacSRuslan Bukin 
857*d3916eacSRuslan Bukin int
vmmops_setreg(void * vcpui,int reg,uint64_t val)858*d3916eacSRuslan Bukin vmmops_setreg(void *vcpui, int reg, uint64_t val)
859*d3916eacSRuslan Bukin {
860*d3916eacSRuslan Bukin 	struct hypctx *hypctx;
861*d3916eacSRuslan Bukin 	int running, hostcpu;
862*d3916eacSRuslan Bukin 	uint64_t *regp;
863*d3916eacSRuslan Bukin 
864*d3916eacSRuslan Bukin 	hypctx = vcpui;
865*d3916eacSRuslan Bukin 
866*d3916eacSRuslan Bukin 	running = vcpu_is_running(hypctx->vcpu, &hostcpu);
867*d3916eacSRuslan Bukin 	if (running && hostcpu != curcpu)
868*d3916eacSRuslan Bukin 		panic("%s: %s%d is running", __func__, vm_name(hypctx->hyp->vm),
869*d3916eacSRuslan Bukin 		    vcpu_vcpuid(hypctx->vcpu));
870*d3916eacSRuslan Bukin 
871*d3916eacSRuslan Bukin 	regp = hypctx_regptr(hypctx, reg);
872*d3916eacSRuslan Bukin 	if (regp == NULL)
873*d3916eacSRuslan Bukin 		return (EINVAL);
874*d3916eacSRuslan Bukin 
875*d3916eacSRuslan Bukin 	*regp = val;
876*d3916eacSRuslan Bukin 
877*d3916eacSRuslan Bukin 	return (0);
878*d3916eacSRuslan Bukin }
879*d3916eacSRuslan Bukin 
880*d3916eacSRuslan Bukin int
vmmops_exception(void * vcpui,uint64_t scause)881*d3916eacSRuslan Bukin vmmops_exception(void *vcpui, uint64_t scause)
882*d3916eacSRuslan Bukin {
883*d3916eacSRuslan Bukin 	struct hypctx *hypctx;
884*d3916eacSRuslan Bukin 	int running, hostcpu;
885*d3916eacSRuslan Bukin 
886*d3916eacSRuslan Bukin 	hypctx = vcpui;
887*d3916eacSRuslan Bukin 
888*d3916eacSRuslan Bukin 	running = vcpu_is_running(hypctx->vcpu, &hostcpu);
889*d3916eacSRuslan Bukin 	if (running && hostcpu != curcpu)
890*d3916eacSRuslan Bukin 		panic("%s: %s%d is running", __func__, vm_name(hypctx->hyp->vm),
891*d3916eacSRuslan Bukin 		    vcpu_vcpuid(hypctx->vcpu));
892*d3916eacSRuslan Bukin 
893*d3916eacSRuslan Bukin 	/* TODO: implement me. */
894*d3916eacSRuslan Bukin 
895*d3916eacSRuslan Bukin 	return (ENOSYS);
896*d3916eacSRuslan Bukin }
897*d3916eacSRuslan Bukin 
898*d3916eacSRuslan Bukin int
vmmops_getcap(void * vcpui,int num,int * retval)899*d3916eacSRuslan Bukin vmmops_getcap(void *vcpui, int num, int *retval)
900*d3916eacSRuslan Bukin {
901*d3916eacSRuslan Bukin 	int ret;
902*d3916eacSRuslan Bukin 
903*d3916eacSRuslan Bukin 	ret = ENOENT;
904*d3916eacSRuslan Bukin 
905*d3916eacSRuslan Bukin 	switch (num) {
906*d3916eacSRuslan Bukin 	case VM_CAP_UNRESTRICTED_GUEST:
907*d3916eacSRuslan Bukin 		*retval = 1;
908*d3916eacSRuslan Bukin 		ret = 0;
909*d3916eacSRuslan Bukin 		break;
910*d3916eacSRuslan Bukin 	default:
911*d3916eacSRuslan Bukin 		break;
912*d3916eacSRuslan Bukin 	}
913*d3916eacSRuslan Bukin 
914*d3916eacSRuslan Bukin 	return (ret);
915*d3916eacSRuslan Bukin }
916*d3916eacSRuslan Bukin 
917*d3916eacSRuslan Bukin int
vmmops_setcap(void * vcpui,int num,int val)918*d3916eacSRuslan Bukin vmmops_setcap(void *vcpui, int num, int val)
919*d3916eacSRuslan Bukin {
920*d3916eacSRuslan Bukin 
921*d3916eacSRuslan Bukin 	return (ENOENT);
922*d3916eacSRuslan Bukin }
923