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 "mem.h" 53 #include "vmexit.h" 54 55 static cpuset_t running_cpumask; 56 57 static int 58 vmexit_inst_emul(struct vmctx *ctx __unused, struct vcpu *vcpu, 59 struct vm_run *vmrun) 60 { 61 struct vm_exit *vme; 62 struct vie *vie; 63 int err; 64 65 vme = vmrun->vm_exit; 66 vie = &vme->u.inst_emul.vie; 67 68 err = emulate_mem(vcpu, vme->u.inst_emul.gpa, vie, 69 &vme->u.inst_emul.paging); 70 if (err) { 71 if (err == ESRCH) { 72 EPRINTLN("Unhandled memory access to 0x%lx\n", 73 vme->u.inst_emul.gpa); 74 } 75 goto fail; 76 } 77 78 return (VMEXIT_CONTINUE); 79 80 fail: 81 fprintf(stderr, "Failed to emulate instruction "); 82 FPRINTLN(stderr, "at 0x%lx", vme->pc); 83 return (VMEXIT_ABORT); 84 } 85 86 static int 87 vmexit_suspend(struct vmctx *ctx, struct vcpu *vcpu, struct vm_run *vmrun) 88 { 89 struct vm_exit *vme; 90 enum vm_suspend_how how; 91 int vcpuid = vcpu_id(vcpu); 92 93 vme = vmrun->vm_exit; 94 how = vme->u.suspended.how; 95 96 fbsdrun_deletecpu(vcpuid); 97 98 switch (how) { 99 case VM_SUSPEND_RESET: 100 exit(0); 101 case VM_SUSPEND_POWEROFF: 102 if (get_config_bool_default("destroy_on_poweroff", false)) 103 vm_destroy(ctx); 104 exit(1); 105 case VM_SUSPEND_HALT: 106 exit(2); 107 default: 108 fprintf(stderr, "vmexit_suspend: invalid reason %d\n", how); 109 exit(100); 110 } 111 return (0); /* NOTREACHED */ 112 } 113 114 static int 115 vmexit_debug(struct vmctx *ctx __unused, struct vcpu *vcpu __unused, 116 struct vm_run *vmrun __unused) 117 { 118 return (VMEXIT_CONTINUE); 119 } 120 121 static int 122 vmexit_bogus(struct vmctx *ctx __unused, struct vcpu *vcpu __unused, 123 struct vm_run *vmrun __unused) 124 { 125 return (VMEXIT_CONTINUE); 126 } 127 128 static uint64_t 129 smccc_affinity_info(uint64_t target_affinity, uint32_t lowest_affinity_level) 130 { 131 uint64_t cpu_aff, mask = 0; 132 133 switch (lowest_affinity_level) { 134 case 0: 135 mask |= CPU_AFF0_MASK; 136 /* FALLTHROUGH */ 137 case 1: 138 mask |= CPU_AFF1_MASK; 139 /* FALLTHROUGH */ 140 case 2: 141 mask |= CPU_AFF2_MASK; 142 /* FALLTHROUGH */ 143 case 3: 144 mask |= CPU_AFF3_MASK; 145 break; 146 default: 147 return (PSCI_RETVAL_INVALID_PARAMS); 148 } 149 150 for (int vcpu = 0; vcpu < guest_ncpus; vcpu++) { 151 /* TODO: We should get this from the kernel */ 152 cpu_aff = (vcpu & 0xf) << MPIDR_AFF0_SHIFT | 153 ((vcpu >> 4) & 0xff) << MPIDR_AFF1_SHIFT | 154 ((vcpu >> 12) & 0xff) << MPIDR_AFF2_SHIFT | 155 (uint64_t)((vcpu >> 20) & 0xff) << MPIDR_AFF3_SHIFT; 156 157 if ((cpu_aff & mask) == (target_affinity & mask) && 158 CPU_ISSET(vcpu, &running_cpumask)) { 159 /* Return ON if any CPUs are on */ 160 return (PSCI_AFFINITY_INFO_ON); 161 } 162 } 163 164 /* No CPUs in the affinity mask are on, return OFF */ 165 return (PSCI_AFFINITY_INFO_OFF); 166 } 167 168 static int 169 vmexit_smccc(struct vmctx *ctx, struct vcpu *vcpu, struct vm_run *vmrun) 170 { 171 struct vcpu *newvcpu; 172 struct vm_exit *vme; 173 uint64_t newcpu, smccc_rv; 174 enum vm_suspend_how how; 175 int error; 176 177 /* Return the Unknown Function Identifier by default */ 178 smccc_rv = SMCCC_RET_NOT_SUPPORTED; 179 180 vme = vmrun->vm_exit; 181 switch (vme->u.smccc_call.func_id) { 182 case PSCI_FNID_VERSION: 183 /* We implement PSCI 1.0 */ 184 smccc_rv = PSCI_VER(1, 0); 185 break; 186 case PSCI_FNID_CPU_SUSPEND: 187 case PSCI_FNID_CPU_OFF: 188 break; 189 case PSCI_FNID_CPU_ON: 190 newcpu = vme->u.smccc_call.args[0]; 191 if (newcpu > (uint64_t)guest_ncpus) { 192 smccc_rv = PSCI_RETVAL_INVALID_PARAMS; 193 break; 194 } 195 196 if (CPU_ISSET(newcpu, &running_cpumask)) { 197 smccc_rv = PSCI_RETVAL_ALREADY_ON; 198 break; 199 } 200 201 newvcpu = fbsdrun_vcpu(newcpu); 202 assert(newvcpu != NULL); 203 204 /* Set the context ID */ 205 error = vm_set_register(newvcpu, VM_REG_GUEST_X0, 206 vme->u.smccc_call.args[2]); 207 assert(error == 0); 208 209 /* Set the start program counter */ 210 error = vm_set_register(newvcpu, VM_REG_GUEST_PC, 211 vme->u.smccc_call.args[1]); 212 assert(error == 0); 213 214 vm_resume_cpu(newvcpu); 215 CPU_SET_ATOMIC(newcpu, &running_cpumask); 216 217 smccc_rv = PSCI_RETVAL_SUCCESS; 218 break; 219 case PSCI_FNID_AFFINITY_INFO: 220 smccc_rv = smccc_affinity_info(vme->u.smccc_call.args[0], 221 vme->u.smccc_call.args[1]); 222 break; 223 case PSCI_FNID_SYSTEM_OFF: 224 case PSCI_FNID_SYSTEM_RESET: 225 if (vme->u.smccc_call.func_id == PSCI_FNID_SYSTEM_OFF) 226 how = VM_SUSPEND_POWEROFF; 227 else 228 how = VM_SUSPEND_RESET; 229 vm_suspend(ctx, how); 230 break; 231 default: 232 break; 233 } 234 235 error = vm_set_register(vcpu, VM_REG_GUEST_X0, smccc_rv); 236 assert(error == 0); 237 238 return (VMEXIT_CONTINUE); 239 } 240 241 static int 242 vmexit_hyp(struct vmctx *ctx __unused, struct vcpu *vcpu __unused, 243 struct vm_run *vmrun) 244 { 245 struct vm_exit *vme; 246 247 vme = vmrun->vm_exit; 248 printf("unhandled exception: esr %#lx, far %#lx\n", 249 vme->u.hyp.esr_el2, vme->u.hyp.far_el2); 250 return (VMEXIT_ABORT); 251 } 252 253 const vmexit_handler_t vmexit_handlers[VM_EXITCODE_MAX] = { 254 [VM_EXITCODE_BOGUS] = vmexit_bogus, 255 [VM_EXITCODE_INST_EMUL] = vmexit_inst_emul, 256 [VM_EXITCODE_SUSPENDED] = vmexit_suspend, 257 [VM_EXITCODE_DEBUG] = vmexit_debug, 258 [VM_EXITCODE_SMCCC] = vmexit_smccc, 259 [VM_EXITCODE_HYP] = vmexit_hyp, 260 }; 261