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 case VM_SUSPEND_DESTROY: 126 exit(4); 127 default: 128 fprintf(stderr, "vmexit_suspend: invalid reason %d\n", how); 129 exit(100); 130 } 131 return (0); /* NOTREACHED */ 132 } 133 134 static int 135 vmexit_debug(struct vmctx *ctx __unused, struct vcpu *vcpu, 136 struct vm_run *vmrun __unused) 137 { 138 gdb_cpu_suspend(vcpu); 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 STARTUP IPI. 142 */ 143 usleep(1000); 144 return (VMEXIT_CONTINUE); 145 } 146 147 static int 148 vmexit_bogus(struct vmctx *ctx __unused, struct vcpu *vcpu __unused, 149 struct vm_run *vmrun __unused) 150 { 151 return (VMEXIT_CONTINUE); 152 } 153 154 static uint64_t 155 smccc_affinity_info(uint64_t target_affinity, uint32_t lowest_affinity_level) 156 { 157 uint64_t mask = 0; 158 159 switch (lowest_affinity_level) { 160 case 0: 161 mask |= CPU_AFF0_MASK; 162 /* FALLTHROUGH */ 163 case 1: 164 mask |= CPU_AFF1_MASK; 165 /* FALLTHROUGH */ 166 case 2: 167 mask |= CPU_AFF2_MASK; 168 /* FALLTHROUGH */ 169 case 3: 170 mask |= CPU_AFF3_MASK; 171 break; 172 default: 173 return (PSCI_RETVAL_INVALID_PARAMS); 174 } 175 176 for (int vcpu = 0; vcpu < guest_ncpus; vcpu++) { 177 if ((cpu_to_mpidr[vcpu] & mask) == (target_affinity & mask) && 178 CPU_ISSET(vcpu, &running_cpumask)) { 179 /* Return ON if any CPUs are on */ 180 return (PSCI_AFFINITY_INFO_ON); 181 } 182 } 183 184 /* No CPUs in the affinity mask are on, return OFF */ 185 return (PSCI_AFFINITY_INFO_OFF); 186 } 187 188 static int 189 vmexit_smccc(struct vmctx *ctx, struct vcpu *vcpu, struct vm_run *vmrun) 190 { 191 struct vcpu *newvcpu; 192 struct vm_exit *vme; 193 uint64_t mpidr, smccc_rv; 194 enum vm_suspend_how how; 195 int error, newcpu; 196 197 /* Return the Unknown Function Identifier by default */ 198 smccc_rv = SMCCC_RET_NOT_SUPPORTED; 199 200 vme = vmrun->vm_exit; 201 switch (vme->u.smccc_call.func_id) { 202 case PSCI_FNID_VERSION: 203 /* We implement PSCI 1.0 */ 204 smccc_rv = PSCI_VER(1, 0); 205 break; 206 case PSCI_FNID_CPU_SUSPEND: 207 break; 208 case PSCI_FNID_CPU_OFF: 209 CPU_CLR_ATOMIC(vcpu_id(vcpu), &running_cpumask); 210 vm_suspend_cpu(vcpu); 211 break; 212 case PSCI_FNID_CPU_ON: 213 mpidr = vme->u.smccc_call.args[0]; 214 for (newcpu = 0; newcpu < guest_ncpus; newcpu++) { 215 if (cpu_to_mpidr[newcpu] == mpidr) 216 break; 217 } 218 219 if (newcpu == guest_ncpus) { 220 smccc_rv = PSCI_RETVAL_INVALID_PARAMS; 221 break; 222 } 223 224 if (CPU_TEST_SET_ATOMIC(newcpu, &running_cpumask)) { 225 smccc_rv = PSCI_RETVAL_ALREADY_ON; 226 break; 227 } 228 229 newvcpu = fbsdrun_vcpu(newcpu); 230 assert(newvcpu != NULL); 231 232 /* Set the context ID */ 233 error = vm_set_register(newvcpu, VM_REG_GUEST_X0, 234 vme->u.smccc_call.args[2]); 235 assert(error == 0); 236 237 /* Set the start program counter */ 238 error = vm_set_register(newvcpu, VM_REG_GUEST_PC, 239 vme->u.smccc_call.args[1]); 240 assert(error == 0); 241 242 vm_resume_cpu(newvcpu); 243 244 smccc_rv = PSCI_RETVAL_SUCCESS; 245 break; 246 case PSCI_FNID_AFFINITY_INFO: 247 smccc_rv = smccc_affinity_info(vme->u.smccc_call.args[0], 248 vme->u.smccc_call.args[1]); 249 break; 250 case PSCI_FNID_SYSTEM_OFF: 251 case PSCI_FNID_SYSTEM_RESET: 252 if (vme->u.smccc_call.func_id == PSCI_FNID_SYSTEM_OFF) 253 how = VM_SUSPEND_POWEROFF; 254 else 255 how = VM_SUSPEND_RESET; 256 error = vm_suspend(ctx, how); 257 assert(error == 0 || errno == EALREADY); 258 break; 259 default: 260 break; 261 } 262 263 error = vm_set_register(vcpu, VM_REG_GUEST_X0, smccc_rv); 264 assert(error == 0); 265 266 return (VMEXIT_CONTINUE); 267 } 268 269 static int 270 vmexit_hyp(struct vmctx *ctx __unused, struct vcpu *vcpu, struct vm_run *vmrun) 271 { 272 /* Raise an unknown reason exception */ 273 if (vm_inject_exception(vcpu, 274 (EXCP_UNKNOWN << ESR_ELx_EC_SHIFT) | ESR_ELx_IL, 275 vmrun->vm_exit->u.hyp.far_el2) != 0) 276 return (VMEXIT_ABORT); 277 278 return (VMEXIT_CONTINUE); 279 } 280 281 static int 282 vmexit_brk(struct vmctx *ctx __unused, struct vcpu *vcpu, struct vm_run *vmrun) 283 { 284 gdb_cpu_breakpoint(vcpu, vmrun->vm_exit); 285 return (VMEXIT_CONTINUE); 286 } 287 288 static int 289 vmexit_ss(struct vmctx *ctx __unused, struct vcpu *vcpu, struct vm_run *vmrun) 290 { 291 gdb_cpu_debug(vcpu, vmrun->vm_exit); 292 return (VMEXIT_CONTINUE); 293 } 294 295 const vmexit_handler_t vmexit_handlers[VM_EXITCODE_MAX] = { 296 [VM_EXITCODE_BOGUS] = vmexit_bogus, 297 [VM_EXITCODE_INST_EMUL] = vmexit_inst_emul, 298 [VM_EXITCODE_REG_EMUL] = vmexit_reg_emul, 299 [VM_EXITCODE_SUSPENDED] = vmexit_suspend, 300 [VM_EXITCODE_DEBUG] = vmexit_debug, 301 [VM_EXITCODE_SMCCC] = vmexit_smccc, 302 [VM_EXITCODE_HYP] = vmexit_hyp, 303 [VM_EXITCODE_BRK] = vmexit_brk, 304 [VM_EXITCODE_SS] = vmexit_ss, 305 }; 306