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 47 #include <vmmapi.h> 48 49 #include "bhyverun.h" 50 #include "config.h" 51 #include "debug.h" 52 #include "gdb.h" 53 #include "mem.h" 54 #include "vmexit.h" 55 56 static cpuset_t running_cpumask; 57 58 static int 59 vmexit_inst_emul(struct vmctx *ctx __unused, struct vcpu *vcpu, 60 struct vm_run *vmrun) 61 { 62 struct vm_exit *vme; 63 struct vie *vie; 64 int err; 65 66 vme = vmrun->vm_exit; 67 vie = &vme->u.inst_emul.vie; 68 69 err = emulate_mem(vcpu, vme->u.inst_emul.gpa, vie, 70 &vme->u.inst_emul.paging); 71 if (err) { 72 if (err == ESRCH) { 73 EPRINTLN("Unhandled memory access to 0x%lx\n", 74 vme->u.inst_emul.gpa); 75 } 76 goto fail; 77 } 78 79 return (VMEXIT_CONTINUE); 80 81 fail: 82 fprintf(stderr, "Failed to emulate instruction "); 83 FPRINTLN(stderr, "at 0x%lx", vme->pc); 84 return (VMEXIT_ABORT); 85 } 86 87 static int 88 vmexit_reg_emul(struct vmctx *ctx __unused, struct vcpu *vcpu __unused, 89 struct vm_run *vmrun) 90 { 91 struct vm_exit *vme; 92 struct vre *vre; 93 94 vme = vmrun->vm_exit; 95 vre = &vme->u.reg_emul.vre; 96 97 EPRINTLN("Unhandled register access: pc %#lx syndrome %#x reg %d\n", 98 vme->pc, vre->inst_syndrome, vre->reg); 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 return (0); /* NOTREACHED */ 128 } 129 130 static int 131 vmexit_debug(struct vmctx *ctx __unused, struct vcpu *vcpu, 132 struct vm_run *vmrun __unused) 133 { 134 gdb_cpu_suspend(vcpu); 135 return (VMEXIT_CONTINUE); 136 } 137 138 static int 139 vmexit_bogus(struct vmctx *ctx __unused, struct vcpu *vcpu __unused, 140 struct vm_run *vmrun __unused) 141 { 142 return (VMEXIT_CONTINUE); 143 } 144 145 static uint64_t 146 smccc_affinity_info(uint64_t target_affinity, uint32_t lowest_affinity_level) 147 { 148 uint64_t cpu_aff, mask = 0; 149 150 switch (lowest_affinity_level) { 151 case 0: 152 mask |= CPU_AFF0_MASK; 153 /* FALLTHROUGH */ 154 case 1: 155 mask |= CPU_AFF1_MASK; 156 /* FALLTHROUGH */ 157 case 2: 158 mask |= CPU_AFF2_MASK; 159 /* FALLTHROUGH */ 160 case 3: 161 mask |= CPU_AFF3_MASK; 162 break; 163 default: 164 return (PSCI_RETVAL_INVALID_PARAMS); 165 } 166 167 for (int vcpu = 0; vcpu < guest_ncpus; vcpu++) { 168 /* TODO: We should get this from the kernel */ 169 cpu_aff = (vcpu & 0xf) << MPIDR_AFF0_SHIFT | 170 ((vcpu >> 4) & 0xff) << MPIDR_AFF1_SHIFT | 171 ((vcpu >> 12) & 0xff) << MPIDR_AFF2_SHIFT | 172 (uint64_t)((vcpu >> 20) & 0xff) << MPIDR_AFF3_SHIFT; 173 174 if ((cpu_aff & mask) == (target_affinity & mask) && 175 CPU_ISSET(vcpu, &running_cpumask)) { 176 /* Return ON if any CPUs are on */ 177 return (PSCI_AFFINITY_INFO_ON); 178 } 179 } 180 181 /* No CPUs in the affinity mask are on, return OFF */ 182 return (PSCI_AFFINITY_INFO_OFF); 183 } 184 185 static int 186 vmexit_smccc(struct vmctx *ctx, struct vcpu *vcpu, struct vm_run *vmrun) 187 { 188 struct vcpu *newvcpu; 189 struct vm_exit *vme; 190 uint64_t newcpu, smccc_rv; 191 enum vm_suspend_how how; 192 int error; 193 194 /* Return the Unknown Function Identifier by default */ 195 smccc_rv = SMCCC_RET_NOT_SUPPORTED; 196 197 vme = vmrun->vm_exit; 198 switch (vme->u.smccc_call.func_id) { 199 case PSCI_FNID_VERSION: 200 /* We implement PSCI 1.0 */ 201 smccc_rv = PSCI_VER(1, 0); 202 break; 203 case PSCI_FNID_CPU_SUSPEND: 204 case PSCI_FNID_CPU_OFF: 205 break; 206 case PSCI_FNID_CPU_ON: 207 newcpu = vme->u.smccc_call.args[0]; 208 if (newcpu > (uint64_t)guest_ncpus) { 209 smccc_rv = PSCI_RETVAL_INVALID_PARAMS; 210 break; 211 } 212 213 if (CPU_ISSET(newcpu, &running_cpumask)) { 214 smccc_rv = PSCI_RETVAL_ALREADY_ON; 215 break; 216 } 217 218 newvcpu = fbsdrun_vcpu(newcpu); 219 assert(newvcpu != NULL); 220 221 /* Set the context ID */ 222 error = vm_set_register(newvcpu, VM_REG_GUEST_X0, 223 vme->u.smccc_call.args[2]); 224 assert(error == 0); 225 226 /* Set the start program counter */ 227 error = vm_set_register(newvcpu, VM_REG_GUEST_PC, 228 vme->u.smccc_call.args[1]); 229 assert(error == 0); 230 231 vm_resume_cpu(newvcpu); 232 CPU_SET_ATOMIC(newcpu, &running_cpumask); 233 234 smccc_rv = PSCI_RETVAL_SUCCESS; 235 break; 236 case PSCI_FNID_AFFINITY_INFO: 237 smccc_rv = smccc_affinity_info(vme->u.smccc_call.args[0], 238 vme->u.smccc_call.args[1]); 239 break; 240 case PSCI_FNID_SYSTEM_OFF: 241 case PSCI_FNID_SYSTEM_RESET: 242 if (vme->u.smccc_call.func_id == PSCI_FNID_SYSTEM_OFF) 243 how = VM_SUSPEND_POWEROFF; 244 else 245 how = VM_SUSPEND_RESET; 246 vm_suspend(ctx, how); 247 break; 248 default: 249 break; 250 } 251 252 error = vm_set_register(vcpu, VM_REG_GUEST_X0, smccc_rv); 253 assert(error == 0); 254 255 return (VMEXIT_CONTINUE); 256 } 257 258 static int 259 vmexit_hyp(struct vmctx *ctx __unused, struct vcpu *vcpu __unused, 260 struct vm_run *vmrun) 261 { 262 struct vm_exit *vme; 263 264 vme = vmrun->vm_exit; 265 printf("unhandled exception: esr %#lx, far %#lx\n", 266 vme->u.hyp.esr_el2, vme->u.hyp.far_el2); 267 return (VMEXIT_ABORT); 268 } 269 270 static int 271 vmexit_brk(struct vmctx *ctx __unused, struct vcpu *vcpu, struct vm_run *vmrun) 272 { 273 gdb_cpu_breakpoint(vcpu, vmrun->vm_exit); 274 return (VMEXIT_CONTINUE); 275 } 276 277 static int 278 vmexit_ss(struct vmctx *ctx __unused, struct vcpu *vcpu, struct vm_run *vmrun) 279 { 280 gdb_cpu_debug(vcpu, vmrun->vm_exit); 281 return (VMEXIT_CONTINUE); 282 } 283 284 const vmexit_handler_t vmexit_handlers[VM_EXITCODE_MAX] = { 285 [VM_EXITCODE_BOGUS] = vmexit_bogus, 286 [VM_EXITCODE_INST_EMUL] = vmexit_inst_emul, 287 [VM_EXITCODE_REG_EMUL] = vmexit_reg_emul, 288 [VM_EXITCODE_SUSPENDED] = vmexit_suspend, 289 [VM_EXITCODE_DEBUG] = vmexit_debug, 290 [VM_EXITCODE_SMCCC] = vmexit_smccc, 291 [VM_EXITCODE_HYP] = vmexit_hyp, 292 [VM_EXITCODE_BRK] = vmexit_brk, 293 [VM_EXITCODE_SS] = vmexit_ss, 294 }; 295