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
51 #include <vmmapi.h>
52
53 #include "bhyverun.h"
54 #include "config.h"
55 #include "debug.h"
56 #include "mem.h"
57 #include "vmexit.h"
58 #include "riscv.h"
59
60 #define BHYVE_VERSION ((uint64_t)__FreeBSD_version)
61 #define SBI_VERS_MAJOR 2
62 #define SBI_VERS_MINOR 0
63
64 static cpuset_t running_hartmask = CPUSET_T_INITIALIZER(0);
65
66 void
vmexit_set_bsp(int hart_id)67 vmexit_set_bsp(int hart_id)
68 {
69
70 CPU_SET_ATOMIC(hart_id, &running_hartmask);
71 }
72
73 static int
vmexit_inst_emul(struct vmctx * ctx __unused,struct vcpu * vcpu,struct vm_run * vmrun)74 vmexit_inst_emul(struct vmctx *ctx __unused, struct vcpu *vcpu,
75 struct vm_run *vmrun)
76 {
77 struct vm_exit *vme;
78 struct vie *vie;
79 int err;
80
81 vme = vmrun->vm_exit;
82 vie = &vme->u.inst_emul.vie;
83
84 err = emulate_mem(vcpu, vme->u.inst_emul.gpa, vie,
85 &vme->u.inst_emul.paging);
86 if (err) {
87 if (err == ESRCH) {
88 EPRINTLN("Unhandled memory access to 0x%lx\n",
89 vme->u.inst_emul.gpa);
90 }
91 goto fail;
92 }
93
94 return (VMEXIT_CONTINUE);
95
96 fail:
97 fprintf(stderr, "Failed to emulate instruction ");
98 FPRINTLN(stderr, "at 0x%lx", vme->pc);
99 return (VMEXIT_ABORT);
100 }
101
102 static int
vmexit_suspend(struct vmctx * ctx,struct vcpu * vcpu,struct vm_run * vmrun)103 vmexit_suspend(struct vmctx *ctx, struct vcpu *vcpu, struct vm_run *vmrun)
104 {
105 struct vm_exit *vme;
106 enum vm_suspend_how how;
107 int vcpuid = vcpu_id(vcpu);
108
109 vme = vmrun->vm_exit;
110 how = vme->u.suspended.how;
111
112 fbsdrun_deletecpu(vcpuid);
113
114 switch (how) {
115 case VM_SUSPEND_RESET:
116 exit(0);
117 case VM_SUSPEND_POWEROFF:
118 if (get_config_bool_default("destroy_on_poweroff", false))
119 vm_destroy(ctx);
120 exit(1);
121 case VM_SUSPEND_HALT:
122 exit(2);
123 default:
124 fprintf(stderr, "vmexit_suspend: invalid reason %d\n", how);
125 exit(100);
126 }
127
128 /* NOT REACHED. */
129
130 return (0);
131 }
132
133 static int
vmexit_debug(struct vmctx * ctx __unused,struct vcpu * vcpu __unused,struct vm_run * vmrun __unused)134 vmexit_debug(struct vmctx *ctx __unused, struct vcpu *vcpu __unused,
135 struct vm_run *vmrun __unused)
136 {
137
138 return (VMEXIT_CONTINUE);
139 }
140
141 static int
vmexit_bogus(struct vmctx * ctx __unused,struct vcpu * vcpu __unused,struct vm_run * vmrun __unused)142 vmexit_bogus(struct vmctx *ctx __unused, struct vcpu *vcpu __unused,
143 struct vm_run *vmrun __unused)
144 {
145
146 return (VMEXIT_CONTINUE);
147 }
148
149 static int
vmm_sbi_probe_extension(int ext_id)150 vmm_sbi_probe_extension(int ext_id)
151 {
152
153 switch (ext_id) {
154 case SBI_EXT_ID_HSM:
155 case SBI_EXT_ID_TIME:
156 case SBI_EXT_ID_IPI:
157 case SBI_EXT_ID_RFNC:
158 case SBI_EXT_ID_SRST:
159 case SBI_CONSOLE_PUTCHAR:
160 case SBI_CONSOLE_GETCHAR:
161 break;
162 default:
163 return (0);
164 }
165
166 return (1);
167 }
168
169 static void
vmexit_ecall_time(struct vmctx * ctx __unused,struct vm_exit * vme __unused)170 vmexit_ecall_time(struct vmctx *ctx __unused, struct vm_exit *vme __unused)
171 {
172
173 }
174
175 static void
vmexit_ecall_hsm(struct vmctx * ctx __unused,struct vcpu * vcpu __unused,struct vm_exit * vme)176 vmexit_ecall_hsm(struct vmctx *ctx __unused, struct vcpu *vcpu __unused,
177 struct vm_exit *vme)
178 {
179 struct vcpu *newvcpu;
180 uint64_t hart_id;
181 int func_id;
182 int error;
183 int ret;
184
185 hart_id = vme->u.ecall.args[0];
186 func_id = vme->u.ecall.args[6];
187
188 ret = -1;
189
190 if (HART_TO_CPU(hart_id) >= (uint64_t)guest_ncpus)
191 goto done;
192
193 newvcpu = fbsdrun_vcpu(HART_TO_CPU(hart_id));
194 assert(newvcpu != NULL);
195
196 switch (func_id) {
197 case SBI_HSM_HART_START:
198 if (CPU_ISSET(hart_id, &running_hartmask))
199 break;
200
201 /* Set hart ID. */
202 error = vm_set_register(newvcpu, VM_REG_GUEST_A0, hart_id);
203 assert(error == 0);
204
205 /* Set PC. */
206 error = vm_set_register(newvcpu, VM_REG_GUEST_SEPC,
207 vme->u.ecall.args[1]);
208 assert(error == 0);
209
210 vm_resume_cpu(newvcpu);
211 CPU_SET_ATOMIC(hart_id, &running_hartmask);
212
213 ret = 0;
214 break;
215 case SBI_HSM_HART_STOP:
216 if (!CPU_ISSET(hart_id, &running_hartmask))
217 break;
218 CPU_CLR_ATOMIC(hart_id, &running_hartmask);
219 vm_suspend_cpu(newvcpu);
220 ret = 0;
221 break;
222 case SBI_HSM_HART_STATUS:
223 /* TODO. */
224 break;
225 default:
226 break;
227 }
228
229 done:
230 error = vm_set_register(vcpu, VM_REG_GUEST_A0, ret);
231 assert(error == 0);
232 }
233
234 static void
vmexit_ecall_base(struct vmctx * ctx __unused,struct vcpu * vcpu,struct vm_exit * vme)235 vmexit_ecall_base(struct vmctx *ctx __unused, struct vcpu *vcpu,
236 struct vm_exit *vme)
237 {
238 int sbi_function_id;
239 int ext_id;
240 int error;
241 uint32_t val;
242 int ret;
243
244 sbi_function_id = vme->u.ecall.args[6];
245
246 ret = 0;
247
248 switch (sbi_function_id) {
249 case SBI_BASE_GET_SPEC_VERSION:
250 val = SBI_VERS_MAJOR << SBI_SPEC_VERS_MAJOR_OFFSET;
251 val |= SBI_VERS_MINOR << SBI_SPEC_VERS_MINOR_OFFSET;
252 break;
253 case SBI_BASE_GET_IMPL_ID:
254 val = SBI_IMPL_ID_BHYVE;
255 break;
256 case SBI_BASE_GET_IMPL_VERSION:
257 val = BHYVE_VERSION;
258 break;
259 case SBI_BASE_PROBE_EXTENSION:
260 ext_id = vme->u.ecall.args[0];
261 val = vmm_sbi_probe_extension(ext_id);
262 break;
263 case SBI_BASE_GET_MVENDORID:
264 val = MVENDORID_UNIMPL;
265 break;
266 case SBI_BASE_GET_MARCHID:
267 val = MARCHID_UNIMPL;
268 break;
269 case SBI_BASE_GET_MIMPID:
270 val = 0;
271 break;
272 default:
273 ret = 1;
274 break;
275 }
276
277 error = vm_set_register(vcpu, VM_REG_GUEST_A0, ret);
278 assert(error == 0);
279
280 if (ret == 0) {
281 error = vm_set_register(vcpu, VM_REG_GUEST_A1, val);
282 assert(error == 0);
283 }
284 }
285
286 static void
vmexit_ecall_srst(struct vmctx * ctx,struct vm_exit * vme)287 vmexit_ecall_srst(struct vmctx *ctx, struct vm_exit *vme)
288 {
289 enum vm_suspend_how how;
290 int func_id;
291 int type;
292
293 func_id = vme->u.ecall.args[6];
294 type = vme->u.ecall.args[0];
295
296 switch (func_id) {
297 case SBI_SRST_SYSTEM_RESET:
298 switch (type) {
299 case SBI_SRST_TYPE_SHUTDOWN:
300 case SBI_SRST_TYPE_COLD_REBOOT:
301 case SBI_SRST_TYPE_WARM_REBOOT:
302 how = VM_SUSPEND_POWEROFF;
303 vm_suspend(ctx, how);
304 break;
305 default:
306 break;
307 }
308 default:
309 break;
310 }
311 }
312
313 static int
vmexit_ecall(struct vmctx * ctx,struct vcpu * vcpu,struct vm_run * vmrun)314 vmexit_ecall(struct vmctx *ctx, struct vcpu *vcpu, struct vm_run *vmrun)
315 {
316 int sbi_extension_id;
317 struct vm_exit *vme;
318
319 vme = vmrun->vm_exit;
320
321 sbi_extension_id = vme->u.ecall.args[7];
322 switch (sbi_extension_id) {
323 case SBI_EXT_ID_SRST:
324 vmexit_ecall_srst(ctx, vme);
325 break;
326 case SBI_EXT_ID_BASE:
327 vmexit_ecall_base(ctx, vcpu, vme);
328 break;
329 case SBI_EXT_ID_TIME:
330 vmexit_ecall_time(ctx, vme);
331 break;
332 case SBI_EXT_ID_HSM:
333 vmexit_ecall_hsm(ctx, vcpu, vme);
334 break;
335 case SBI_CONSOLE_PUTCHAR:
336 case SBI_CONSOLE_GETCHAR:
337 default:
338 /* Unknown SBI extension. */
339 break;
340 }
341
342 return (VMEXIT_CONTINUE);
343 }
344
345
346 static int
vmexit_hyp(struct vmctx * ctx __unused,struct vcpu * vcpu __unused,struct vm_run * vmrun)347 vmexit_hyp(struct vmctx *ctx __unused, struct vcpu *vcpu __unused,
348 struct vm_run *vmrun)
349 {
350 struct vm_exit *vme;
351
352 vme = vmrun->vm_exit;
353
354 printf("unhandled exception: scause %#lx\n", vme->u.hyp.scause);
355
356 return (VMEXIT_ABORT);
357 }
358
359 const vmexit_handler_t vmexit_handlers[VM_EXITCODE_MAX] = {
360 [VM_EXITCODE_BOGUS] = vmexit_bogus,
361 [VM_EXITCODE_HYP] = vmexit_hyp,
362 [VM_EXITCODE_INST_EMUL] = vmexit_inst_emul,
363 [VM_EXITCODE_SUSPENDED] = vmexit_suspend,
364 [VM_EXITCODE_DEBUG] = vmexit_debug,
365 [VM_EXITCODE_ECALL] = vmexit_ecall,
366 };
367