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 67 vmexit_set_bsp(int hart_id) 68 { 69 70 CPU_SET_ATOMIC(hart_id, &running_hartmask); 71 } 72 73 static int 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 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 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 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 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 170 vmexit_ecall_time(struct vmctx *ctx __unused, struct vm_exit *vme __unused) 171 { 172 173 } 174 175 static void 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 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 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 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 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