xref: /freebsd/usr.sbin/bhyve/aarch64/vmexit.c (revision 6580f5c38dd5b01aeeaed16b370f1a12423437f0)
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