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 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 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 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 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 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 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 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 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 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