xref: /freebsd/usr.sbin/bhyve/riscv/vmexit.c (revision 48f91cacc39c3d48e8dfdb804b844e2a2dbc5157)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2011 NetApp, Inc.
5  * All rights reserved.
6  * Copyright (c) 2024 Ruslan Bukin <br@bsdpad.com>
7  *
8  * This software was developed by the University of Cambridge Computer
9  * Laboratory (Department of Computer Science and Technology) under Innovate
10  * UK project 105694, "Digital Security by Design (DSbD) Technology Platform
11  * Prototype".
12  *
13  * Redistribution and use in source and binary forms, with or without
14  * modification, are permitted provided that the following conditions
15  * are met:
16  * 1. Redistributions of source code must retain the above copyright
17  *    notice, this list of conditions and the following disclaimer.
18  * 2. Redistributions in binary form must reproduce the above copyright
19  *    notice, this list of conditions and the following disclaimer in the
20  *    documentation and/or other materials provided with the distribution.
21  *
22  * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND
23  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25  * ARE DISCLAIMED.  IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE
26  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34 
35 #include <sys/types.h>
36 #include <sys/cpuset.h>
37 
38 #include <machine/riscvreg.h>
39 #include <machine/cpu.h>
40 #include <machine/sbi.h>
41 #include <machine/vmm.h>
42 #include <machine/vmm_dev.h>
43 #include <machine/vmm_instruction_emul.h>
44 
45 #include <assert.h>
46 #include <errno.h>
47 #include <stdbool.h>
48 #include <stdio.h>
49 #include <stdlib.h>
50 #include <unistd.h>
51 
52 #include <vmmapi.h>
53 
54 #include "bhyverun.h"
55 #include "config.h"
56 #include "debug.h"
57 #include "mem.h"
58 #include "vmexit.h"
59 #include "riscv.h"
60 
61 #define	BHYVE_VERSION	((uint64_t)__FreeBSD_version)
62 #define	SBI_VERS_MAJOR	2
63 #define	SBI_VERS_MINOR	0
64 
65 static cpuset_t running_hartmask = CPUSET_T_INITIALIZER(0);
66 
67 void
vmexit_set_bsp(int hart_id)68 vmexit_set_bsp(int hart_id)
69 {
70 
71 	CPU_SET_ATOMIC(hart_id, &running_hartmask);
72 }
73 
74 static int
vmexit_inst_emul(struct vmctx * ctx __unused,struct vcpu * vcpu,struct vm_run * vmrun)75 vmexit_inst_emul(struct vmctx *ctx __unused, struct vcpu *vcpu,
76     struct vm_run *vmrun)
77 {
78 	struct vm_exit *vme;
79 	struct vie *vie;
80 	int err;
81 
82 	vme = vmrun->vm_exit;
83 	vie = &vme->u.inst_emul.vie;
84 
85 	err = emulate_mem(vcpu, vme->u.inst_emul.gpa, vie,
86 	    &vme->u.inst_emul.paging);
87 	if (err) {
88 		if (err == ESRCH) {
89 			EPRINTLN("Unhandled memory access to 0x%lx\n",
90 			    vme->u.inst_emul.gpa);
91 		}
92 		goto fail;
93 	}
94 
95 	return (VMEXIT_CONTINUE);
96 
97 fail:
98 	fprintf(stderr, "Failed to emulate instruction ");
99 	FPRINTLN(stderr, "at 0x%lx", vme->pc);
100 	return (VMEXIT_ABORT);
101 }
102 
103 static int
vmexit_suspend(struct vmctx * ctx,struct vcpu * vcpu,struct vm_run * vmrun)104 vmexit_suspend(struct vmctx *ctx, struct vcpu *vcpu, struct vm_run *vmrun)
105 {
106 	struct vm_exit *vme;
107 	enum vm_suspend_how how;
108 	int vcpuid = vcpu_id(vcpu);
109 
110 	vme = vmrun->vm_exit;
111 	how = vme->u.suspended.how;
112 
113 	fbsdrun_deletecpu(vcpuid);
114 
115 	switch (how) {
116 	case VM_SUSPEND_RESET:
117 		exit(0);
118 	case VM_SUSPEND_POWEROFF:
119 		if (get_config_bool_default("destroy_on_poweroff", false))
120 			vm_destroy(ctx);
121 		exit(1);
122 	case VM_SUSPEND_HALT:
123 		exit(2);
124 	default:
125 		fprintf(stderr, "vmexit_suspend: invalid reason %d\n", how);
126 		exit(100);
127 	}
128 
129 	/* NOT REACHED. */
130 
131 	return (0);
132 }
133 
134 static int
vmexit_debug(struct vmctx * ctx __unused,struct vcpu * vcpu __unused,struct vm_run * vmrun __unused)135 vmexit_debug(struct vmctx *ctx __unused, struct vcpu *vcpu __unused,
136     struct vm_run *vmrun __unused)
137 {
138 
139 	/*
140 	 * XXX-MJ sleep for a short period to avoid chewing up the CPU in the
141 	 * window between activation of the vCPU thread and the
142 	 * SBI_HSM_HART_START request.
143 	 */
144 	usleep(1000);
145 	return (VMEXIT_CONTINUE);
146 }
147 
148 static int
vmexit_bogus(struct vmctx * ctx __unused,struct vcpu * vcpu __unused,struct vm_run * vmrun __unused)149 vmexit_bogus(struct vmctx *ctx __unused, struct vcpu *vcpu __unused,
150     struct vm_run *vmrun __unused)
151 {
152 
153 	return (VMEXIT_CONTINUE);
154 }
155 
156 static int
vmm_sbi_probe_extension(int ext_id)157 vmm_sbi_probe_extension(int ext_id)
158 {
159 
160 	switch (ext_id) {
161 	case SBI_EXT_ID_HSM:
162 	case SBI_EXT_ID_TIME:
163 	case SBI_EXT_ID_IPI:
164 	case SBI_EXT_ID_RFNC:
165 	case SBI_EXT_ID_SRST:
166 	case SBI_CONSOLE_PUTCHAR:
167 	case SBI_CONSOLE_GETCHAR:
168 		break;
169 	default:
170 		return (0);
171 	}
172 
173 	return (1);
174 }
175 
176 static int
vmexit_ecall_hsm(struct vmctx * ctx __unused,struct vcpu * vcpu __unused,struct vm_exit * vme)177 vmexit_ecall_hsm(struct vmctx *ctx __unused, struct vcpu *vcpu __unused,
178     struct vm_exit *vme)
179 {
180 	struct vcpu *newvcpu;
181 	uint64_t hart_id;
182 	int func_id;
183 	int error;
184 
185 	hart_id = vme->u.ecall.args[0];
186 	func_id = vme->u.ecall.args[6];
187 
188 	if (HART_TO_CPU(hart_id) >= (uint64_t)guest_ncpus)
189 		return (SBI_ERR_INVALID_PARAM);
190 
191 	newvcpu = fbsdrun_vcpu(HART_TO_CPU(hart_id));
192 	assert(newvcpu != NULL);
193 
194 	switch (func_id) {
195 	case SBI_HSM_HART_START:
196 		if (CPU_ISSET(hart_id, &running_hartmask))
197 			break;
198 
199 		/* Set hart ID. */
200 		error = vm_set_register(newvcpu, VM_REG_GUEST_A0, hart_id);
201 		assert(error == 0);
202 
203 		/* Set PC. */
204 		error = vm_set_register(newvcpu, VM_REG_GUEST_SEPC,
205 		    vme->u.ecall.args[1]);
206 		assert(error == 0);
207 
208 		/* Pass private data. */
209 		error = vm_set_register(newvcpu, VM_REG_GUEST_A1,
210 		    vme->u.ecall.args[2]);
211 		assert(error == 0);
212 
213 		vm_resume_cpu(newvcpu);
214 		CPU_SET_ATOMIC(hart_id, &running_hartmask);
215 		break;
216 	case SBI_HSM_HART_STOP:
217 		if (!CPU_ISSET(hart_id, &running_hartmask))
218 			break;
219 		CPU_CLR_ATOMIC(hart_id, &running_hartmask);
220 		vm_suspend_cpu(newvcpu);
221 		break;
222 	case SBI_HSM_HART_STATUS:
223 		/* TODO. */
224 		break;
225 	default:
226 		return (SBI_ERR_NOT_SUPPORTED);
227 	}
228 
229 	return (SBI_SUCCESS);
230 }
231 
232 static int
vmexit_ecall_base(struct vmctx * ctx __unused,struct vcpu * vcpu,struct vm_exit * vme)233 vmexit_ecall_base(struct vmctx *ctx __unused, struct vcpu *vcpu,
234     struct vm_exit *vme)
235 {
236 	int sbi_function_id;
237 	uint32_t val;
238 	int ext_id;
239 	int error;
240 
241 	sbi_function_id = vme->u.ecall.args[6];
242 
243 	switch (sbi_function_id) {
244 	case SBI_BASE_GET_SPEC_VERSION:
245 		val = SBI_VERS_MAJOR << SBI_SPEC_VERS_MAJOR_OFFSET;
246 		val |= SBI_VERS_MINOR << SBI_SPEC_VERS_MINOR_OFFSET;
247 		break;
248 	case SBI_BASE_GET_IMPL_ID:
249 		val = SBI_IMPL_ID_BHYVE;
250 		break;
251 	case SBI_BASE_GET_IMPL_VERSION:
252 		val = BHYVE_VERSION;
253 		break;
254 	case SBI_BASE_PROBE_EXTENSION:
255 		ext_id = vme->u.ecall.args[0];
256 		val = vmm_sbi_probe_extension(ext_id);
257 		break;
258 	case SBI_BASE_GET_MVENDORID:
259 		val = MVENDORID_UNIMPL;
260 		break;
261 	case SBI_BASE_GET_MARCHID:
262 		val = MARCHID_UNIMPL;
263 		break;
264 	case SBI_BASE_GET_MIMPID:
265 		val = 0;
266 		break;
267 	default:
268 		return (SBI_ERR_NOT_SUPPORTED);
269 	}
270 
271 	error = vm_set_register(vcpu, VM_REG_GUEST_A1, val);
272 	assert(error == 0);
273 
274 	return (SBI_SUCCESS);
275 }
276 
277 static int
vmexit_ecall_srst(struct vmctx * ctx,struct vm_exit * vme)278 vmexit_ecall_srst(struct vmctx *ctx, struct vm_exit *vme)
279 {
280 	enum vm_suspend_how how;
281 	int func_id;
282 	int type;
283 
284 	func_id = vme->u.ecall.args[6];
285 	type = vme->u.ecall.args[0];
286 
287 	switch (func_id) {
288 	case SBI_SRST_SYSTEM_RESET:
289 		switch (type) {
290 		case SBI_SRST_TYPE_SHUTDOWN:
291 		case SBI_SRST_TYPE_COLD_REBOOT:
292 		case SBI_SRST_TYPE_WARM_REBOOT:
293 			how = VM_SUSPEND_POWEROFF;
294 			vm_suspend(ctx, how);
295 			break;
296 		default:
297 			return (SBI_ERR_NOT_SUPPORTED);
298 		}
299 		break;
300 	default:
301 		return (SBI_ERR_NOT_SUPPORTED);
302 	}
303 
304 	return (SBI_SUCCESS);
305 }
306 
307 static int
vmexit_ecall(struct vmctx * ctx,struct vcpu * vcpu,struct vm_run * vmrun)308 vmexit_ecall(struct vmctx *ctx, struct vcpu *vcpu, struct vm_run *vmrun)
309 {
310 	int sbi_extension_id;
311 	struct vm_exit *vme;
312 	int error;
313 	int ret;
314 
315 	vme = vmrun->vm_exit;
316 
317 	sbi_extension_id = vme->u.ecall.args[7];
318 	switch (sbi_extension_id) {
319 	case SBI_EXT_ID_SRST:
320 		ret = vmexit_ecall_srst(ctx, vme);
321 		break;
322 	case SBI_EXT_ID_BASE:
323 		ret = vmexit_ecall_base(ctx, vcpu, vme);
324 		break;
325 	case SBI_EXT_ID_HSM:
326 		ret = vmexit_ecall_hsm(ctx, vcpu, vme);
327 		break;
328 	case SBI_CONSOLE_PUTCHAR:
329 	case SBI_CONSOLE_GETCHAR:
330 	default:
331 		/* Unknown SBI extension. */
332 		ret = SBI_ERR_NOT_SUPPORTED;
333 		break;
334 	}
335 
336 	error = vm_set_register(vcpu, VM_REG_GUEST_A0, ret);
337 	assert(error == 0);
338 
339 	return (VMEXIT_CONTINUE);
340 }
341 
342 
343 static int
vmexit_hyp(struct vmctx * ctx __unused,struct vcpu * vcpu __unused,struct vm_run * vmrun)344 vmexit_hyp(struct vmctx *ctx __unused, struct vcpu *vcpu __unused,
345     struct vm_run *vmrun)
346 {
347 	struct vm_exit *vme;
348 
349 	vme = vmrun->vm_exit;
350 
351 	printf("unhandled exception: scause %#lx\n", vme->u.hyp.scause);
352 
353 	return (VMEXIT_ABORT);
354 }
355 
356 const vmexit_handler_t vmexit_handlers[VM_EXITCODE_MAX] = {
357 	[VM_EXITCODE_BOGUS]  = vmexit_bogus,
358 	[VM_EXITCODE_HYP] = vmexit_hyp,
359 	[VM_EXITCODE_INST_EMUL] = vmexit_inst_emul,
360 	[VM_EXITCODE_SUSPENDED] = vmexit_suspend,
361 	[VM_EXITCODE_DEBUG] = vmexit_debug,
362 	[VM_EXITCODE_ECALL] = vmexit_ecall,
363 };
364