1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2011 NetApp, Inc. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 #include <sys/types.h> 30 #include <sys/cpuset.h> 31 32 #include <dev/psci/psci.h> 33 #include <dev/psci/smccc.h> 34 35 #include <machine/armreg.h> 36 #include <machine/cpu.h> 37 #include <machine/vmm.h> 38 #include <machine/vmm_dev.h> 39 #include <machine/vmm_instruction_emul.h> 40 41 #include <assert.h> 42 #include <errno.h> 43 #include <stdbool.h> 44 #include <stdio.h> 45 #include <stdlib.h> 46 #include <unistd.h> 47 48 #include <vmmapi.h> 49 50 #include "bhyve_machdep.h" 51 #include "bhyverun.h" 52 #include "config.h" 53 #include "debug.h" 54 #include "gdb.h" 55 #include "mem.h" 56 #include "vmexit.h" 57 58 cpuset_t running_cpumask; 59 60 static int 61 vmexit_inst_emul(struct vmctx *ctx __unused, struct vcpu *vcpu, 62 struct vm_run *vmrun) 63 { 64 struct vm_exit *vme; 65 struct vie *vie; 66 int err; 67 68 vme = vmrun->vm_exit; 69 vie = &vme->u.inst_emul.vie; 70 71 err = emulate_mem(vcpu, vme->u.inst_emul.gpa, vie, 72 &vme->u.inst_emul.paging); 73 if (err) { 74 if (err == ESRCH) { 75 EPRINTLN("Unhandled memory access to 0x%lx\n", 76 vme->u.inst_emul.gpa); 77 } 78 goto fail; 79 } 80 81 return (VMEXIT_CONTINUE); 82 83 fail: 84 fprintf(stderr, "Failed to emulate instruction "); 85 FPRINTLN(stderr, "at 0x%lx", vme->pc); 86 return (VMEXIT_ABORT); 87 } 88 89 static int 90 vmexit_reg_emul(struct vmctx *ctx __unused, struct vcpu *vcpu __unused, 91 struct vm_run *vmrun) 92 { 93 struct vm_exit *vme; 94 struct vre *vre; 95 96 vme = vmrun->vm_exit; 97 vre = &vme->u.reg_emul.vre; 98 99 EPRINTLN("Unhandled register access: pc %#lx syndrome %#x reg %d\n", 100 vme->pc, vre->inst_syndrome, vre->reg); 101 return (VMEXIT_ABORT); 102 } 103 104 static int 105 vmexit_suspend(struct vmctx *ctx, struct vcpu *vcpu, struct vm_run *vmrun) 106 { 107 struct vm_exit *vme; 108 enum vm_suspend_how how; 109 int vcpuid = vcpu_id(vcpu); 110 111 vme = vmrun->vm_exit; 112 how = vme->u.suspended.how; 113 114 fbsdrun_deletecpu(vcpuid); 115 116 switch (how) { 117 case VM_SUSPEND_RESET: 118 exit(0); 119 case VM_SUSPEND_POWEROFF: 120 if (get_config_bool_default("destroy_on_poweroff", false)) 121 vm_destroy(ctx); 122 exit(1); 123 case VM_SUSPEND_HALT: 124 exit(2); 125 default: 126 fprintf(stderr, "vmexit_suspend: invalid reason %d\n", how); 127 exit(100); 128 } 129 return (0); /* NOTREACHED */ 130 } 131 132 static int 133 vmexit_debug(struct vmctx *ctx __unused, struct vcpu *vcpu, 134 struct vm_run *vmrun __unused) 135 { 136 gdb_cpu_suspend(vcpu); 137 /* 138 * XXX-MJ sleep for a short period to avoid chewing up the CPU in the 139 * window between activation of the vCPU thread and the STARTUP IPI. 140 */ 141 usleep(1000); 142 return (VMEXIT_CONTINUE); 143 } 144 145 static int 146 vmexit_bogus(struct vmctx *ctx __unused, struct vcpu *vcpu __unused, 147 struct vm_run *vmrun __unused) 148 { 149 return (VMEXIT_CONTINUE); 150 } 151 152 static uint64_t 153 smccc_affinity_info(uint64_t target_affinity, uint32_t lowest_affinity_level) 154 { 155 uint64_t mask = 0; 156 157 switch (lowest_affinity_level) { 158 case 0: 159 mask |= CPU_AFF0_MASK; 160 /* FALLTHROUGH */ 161 case 1: 162 mask |= CPU_AFF1_MASK; 163 /* FALLTHROUGH */ 164 case 2: 165 mask |= CPU_AFF2_MASK; 166 /* FALLTHROUGH */ 167 case 3: 168 mask |= CPU_AFF3_MASK; 169 break; 170 default: 171 return (PSCI_RETVAL_INVALID_PARAMS); 172 } 173 174 for (int vcpu = 0; vcpu < guest_ncpus; vcpu++) { 175 if ((cpu_to_mpidr[vcpu] & mask) == (target_affinity & mask) && 176 CPU_ISSET(vcpu, &running_cpumask)) { 177 /* Return ON if any CPUs are on */ 178 return (PSCI_AFFINITY_INFO_ON); 179 } 180 } 181 182 /* No CPUs in the affinity mask are on, return OFF */ 183 return (PSCI_AFFINITY_INFO_OFF); 184 } 185 186 static int 187 vmexit_smccc(struct vmctx *ctx, struct vcpu *vcpu, struct vm_run *vmrun) 188 { 189 struct vcpu *newvcpu; 190 struct vm_exit *vme; 191 uint64_t mpidr, smccc_rv; 192 enum vm_suspend_how how; 193 int error, newcpu; 194 195 /* Return the Unknown Function Identifier by default */ 196 smccc_rv = SMCCC_RET_NOT_SUPPORTED; 197 198 vme = vmrun->vm_exit; 199 switch (vme->u.smccc_call.func_id) { 200 case PSCI_FNID_VERSION: 201 /* We implement PSCI 1.0 */ 202 smccc_rv = PSCI_VER(1, 0); 203 break; 204 case PSCI_FNID_CPU_SUSPEND: 205 break; 206 case PSCI_FNID_CPU_OFF: 207 CPU_CLR_ATOMIC(vcpu_id(vcpu), &running_cpumask); 208 vm_suspend_cpu(vcpu); 209 break; 210 case PSCI_FNID_CPU_ON: 211 mpidr = vme->u.smccc_call.args[0]; 212 for (newcpu = 0; newcpu < guest_ncpus; newcpu++) { 213 if (cpu_to_mpidr[newcpu] == mpidr) 214 break; 215 } 216 217 if (newcpu == guest_ncpus) { 218 smccc_rv = PSCI_RETVAL_INVALID_PARAMS; 219 break; 220 } 221 222 if (CPU_TEST_SET_ATOMIC(newcpu, &running_cpumask)) { 223 smccc_rv = PSCI_RETVAL_ALREADY_ON; 224 break; 225 } 226 227 newvcpu = fbsdrun_vcpu(newcpu); 228 assert(newvcpu != NULL); 229 230 /* Set the context ID */ 231 error = vm_set_register(newvcpu, VM_REG_GUEST_X0, 232 vme->u.smccc_call.args[2]); 233 assert(error == 0); 234 235 /* Set the start program counter */ 236 error = vm_set_register(newvcpu, VM_REG_GUEST_PC, 237 vme->u.smccc_call.args[1]); 238 assert(error == 0); 239 240 vm_resume_cpu(newvcpu); 241 242 smccc_rv = PSCI_RETVAL_SUCCESS; 243 break; 244 case PSCI_FNID_AFFINITY_INFO: 245 smccc_rv = smccc_affinity_info(vme->u.smccc_call.args[0], 246 vme->u.smccc_call.args[1]); 247 break; 248 case PSCI_FNID_SYSTEM_OFF: 249 case PSCI_FNID_SYSTEM_RESET: 250 if (vme->u.smccc_call.func_id == PSCI_FNID_SYSTEM_OFF) 251 how = VM_SUSPEND_POWEROFF; 252 else 253 how = VM_SUSPEND_RESET; 254 error = vm_suspend(ctx, how); 255 assert(error == 0 || errno == EALREADY); 256 break; 257 default: 258 break; 259 } 260 261 error = vm_set_register(vcpu, VM_REG_GUEST_X0, smccc_rv); 262 assert(error == 0); 263 264 return (VMEXIT_CONTINUE); 265 } 266 267 static int 268 vmexit_hyp(struct vmctx *ctx __unused, struct vcpu *vcpu, struct vm_run *vmrun) 269 { 270 /* Raise an unknown reason exception */ 271 if (vm_inject_exception(vcpu, 272 (EXCP_UNKNOWN << ESR_ELx_EC_SHIFT) | ESR_ELx_IL, 273 vmrun->vm_exit->u.hyp.far_el2) != 0) 274 return (VMEXIT_ABORT); 275 276 return (VMEXIT_CONTINUE); 277 } 278 279 static int 280 vmexit_brk(struct vmctx *ctx __unused, struct vcpu *vcpu, struct vm_run *vmrun) 281 { 282 gdb_cpu_breakpoint(vcpu, vmrun->vm_exit); 283 return (VMEXIT_CONTINUE); 284 } 285 286 static int 287 vmexit_ss(struct vmctx *ctx __unused, struct vcpu *vcpu, struct vm_run *vmrun) 288 { 289 gdb_cpu_debug(vcpu, vmrun->vm_exit); 290 return (VMEXIT_CONTINUE); 291 } 292 293 const vmexit_handler_t vmexit_handlers[VM_EXITCODE_MAX] = { 294 [VM_EXITCODE_BOGUS] = vmexit_bogus, 295 [VM_EXITCODE_INST_EMUL] = vmexit_inst_emul, 296 [VM_EXITCODE_REG_EMUL] = vmexit_reg_emul, 297 [VM_EXITCODE_SUSPENDED] = vmexit_suspend, 298 [VM_EXITCODE_DEBUG] = vmexit_debug, 299 [VM_EXITCODE_SMCCC] = vmexit_smccc, 300 [VM_EXITCODE_HYP] = vmexit_hyp, 301 [VM_EXITCODE_BRK] = vmexit_brk, 302 [VM_EXITCODE_SS] = vmexit_ss, 303 }; 304