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 68 vmexit_set_bsp(int hart_id) 69 { 70 71 CPU_SET_ATOMIC(hart_id, &running_hartmask); 72 } 73 74 static int 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 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 case VM_SUSPEND_DESTROY: 125 exit(4); 126 default: 127 fprintf(stderr, "vmexit_suspend: invalid reason %d\n", how); 128 exit(100); 129 } 130 131 /* NOT REACHED. */ 132 133 return (0); 134 } 135 136 static int 137 vmexit_debug(struct vmctx *ctx __unused, struct vcpu *vcpu __unused, 138 struct vm_run *vmrun __unused) 139 { 140 141 /* 142 * XXX-MJ sleep for a short period to avoid chewing up the CPU in the 143 * window between activation of the vCPU thread and the 144 * SBI_HSM_HART_START request. 145 */ 146 usleep(1000); 147 return (VMEXIT_CONTINUE); 148 } 149 150 static int 151 vmexit_bogus(struct vmctx *ctx __unused, struct vcpu *vcpu __unused, 152 struct vm_run *vmrun __unused) 153 { 154 155 return (VMEXIT_CONTINUE); 156 } 157 158 static int 159 vmm_sbi_probe_extension(int ext_id) 160 { 161 162 switch (ext_id) { 163 case SBI_EXT_ID_HSM: 164 case SBI_EXT_ID_TIME: 165 case SBI_EXT_ID_IPI: 166 case SBI_EXT_ID_RFNC: 167 case SBI_EXT_ID_SRST: 168 case SBI_CONSOLE_PUTCHAR: 169 case SBI_CONSOLE_GETCHAR: 170 break; 171 default: 172 return (0); 173 } 174 175 return (1); 176 } 177 178 static int 179 vmexit_ecall_hsm(struct vmctx *ctx __unused, struct vcpu *vcpu __unused, 180 struct vm_exit *vme) 181 { 182 struct vcpu *newvcpu; 183 uint64_t hart_id; 184 int func_id; 185 int error; 186 187 hart_id = vme->u.ecall.args[0]; 188 func_id = vme->u.ecall.args[6]; 189 190 if (HART_TO_CPU(hart_id) >= (uint64_t)guest_ncpus) 191 return (SBI_ERR_INVALID_PARAM); 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 /* Pass private data. */ 211 error = vm_set_register(newvcpu, VM_REG_GUEST_A1, 212 vme->u.ecall.args[2]); 213 assert(error == 0); 214 215 vm_resume_cpu(newvcpu); 216 CPU_SET_ATOMIC(hart_id, &running_hartmask); 217 break; 218 case SBI_HSM_HART_STOP: 219 if (!CPU_ISSET(hart_id, &running_hartmask)) 220 break; 221 CPU_CLR_ATOMIC(hart_id, &running_hartmask); 222 vm_suspend_cpu(newvcpu); 223 break; 224 case SBI_HSM_HART_STATUS: 225 /* TODO. */ 226 break; 227 default: 228 return (SBI_ERR_NOT_SUPPORTED); 229 } 230 231 return (SBI_SUCCESS); 232 } 233 234 static int 235 vmexit_ecall_base(struct vmctx *ctx __unused, struct vcpu *vcpu, 236 struct vm_exit *vme) 237 { 238 int sbi_function_id; 239 uint32_t val; 240 int ext_id; 241 int error; 242 243 sbi_function_id = vme->u.ecall.args[6]; 244 245 switch (sbi_function_id) { 246 case SBI_BASE_GET_SPEC_VERSION: 247 val = SBI_VERS_MAJOR << SBI_SPEC_VERS_MAJOR_OFFSET; 248 val |= SBI_VERS_MINOR << SBI_SPEC_VERS_MINOR_OFFSET; 249 break; 250 case SBI_BASE_GET_IMPL_ID: 251 val = SBI_IMPL_ID_BHYVE; 252 break; 253 case SBI_BASE_GET_IMPL_VERSION: 254 val = BHYVE_VERSION; 255 break; 256 case SBI_BASE_PROBE_EXTENSION: 257 ext_id = vme->u.ecall.args[0]; 258 val = vmm_sbi_probe_extension(ext_id); 259 break; 260 case SBI_BASE_GET_MVENDORID: 261 val = MVENDORID_UNIMPL; 262 break; 263 case SBI_BASE_GET_MARCHID: 264 val = MARCHID_UNIMPL; 265 break; 266 case SBI_BASE_GET_MIMPID: 267 val = 0; 268 break; 269 default: 270 return (SBI_ERR_NOT_SUPPORTED); 271 } 272 273 error = vm_set_register(vcpu, VM_REG_GUEST_A1, val); 274 assert(error == 0); 275 276 return (SBI_SUCCESS); 277 } 278 279 static int 280 vmexit_ecall_srst(struct vmctx *ctx, struct vm_exit *vme) 281 { 282 enum vm_suspend_how how; 283 int func_id; 284 int type; 285 286 func_id = vme->u.ecall.args[6]; 287 type = vme->u.ecall.args[0]; 288 289 switch (func_id) { 290 case SBI_SRST_SYSTEM_RESET: 291 switch (type) { 292 case SBI_SRST_TYPE_SHUTDOWN: 293 case SBI_SRST_TYPE_COLD_REBOOT: 294 case SBI_SRST_TYPE_WARM_REBOOT: 295 how = VM_SUSPEND_POWEROFF; 296 vm_suspend(ctx, how); 297 break; 298 default: 299 return (SBI_ERR_NOT_SUPPORTED); 300 } 301 break; 302 default: 303 return (SBI_ERR_NOT_SUPPORTED); 304 } 305 306 return (SBI_SUCCESS); 307 } 308 309 static int 310 vmexit_ecall(struct vmctx *ctx, struct vcpu *vcpu, struct vm_run *vmrun) 311 { 312 int sbi_extension_id; 313 struct vm_exit *vme; 314 int error; 315 int ret; 316 317 vme = vmrun->vm_exit; 318 319 sbi_extension_id = vme->u.ecall.args[7]; 320 switch (sbi_extension_id) { 321 case SBI_EXT_ID_SRST: 322 ret = vmexit_ecall_srst(ctx, vme); 323 break; 324 case SBI_EXT_ID_BASE: 325 ret = vmexit_ecall_base(ctx, vcpu, vme); 326 break; 327 case SBI_EXT_ID_HSM: 328 ret = vmexit_ecall_hsm(ctx, vcpu, vme); 329 break; 330 case SBI_CONSOLE_PUTCHAR: 331 case SBI_CONSOLE_GETCHAR: 332 default: 333 /* Unknown SBI extension. */ 334 ret = SBI_ERR_NOT_SUPPORTED; 335 break; 336 } 337 338 error = vm_set_register(vcpu, VM_REG_GUEST_A0, ret); 339 assert(error == 0); 340 341 return (VMEXIT_CONTINUE); 342 } 343 344 345 static int 346 vmexit_hyp(struct vmctx *ctx __unused, struct vcpu *vcpu __unused, 347 struct vm_run *vmrun) 348 { 349 struct vm_exit *vme; 350 351 vme = vmrun->vm_exit; 352 353 printf("unhandled exception: scause %#lx\n", vme->u.hyp.scause); 354 355 return (VMEXIT_ABORT); 356 } 357 358 const vmexit_handler_t vmexit_handlers[VM_EXITCODE_MAX] = { 359 [VM_EXITCODE_BOGUS] = vmexit_bogus, 360 [VM_EXITCODE_HYP] = vmexit_hyp, 361 [VM_EXITCODE_INST_EMUL] = vmexit_inst_emul, 362 [VM_EXITCODE_SUSPENDED] = vmexit_suspend, 363 [VM_EXITCODE_DEBUG] = vmexit_debug, 364 [VM_EXITCODE_ECALL] = vmexit_ecall, 365 }; 366