xref: /illumos-gate/usr/src/cmd/bhyve/amd64/vmexit.c (revision 5c4a5fe16715fb423db76577a6883b5bbecdbe45)
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  * This file and its contents are supplied under the terms of the
30  * Common Development and Distribution License ("CDDL"), version 1.0.
31  * You may only use this file in accordance with the terms of version
32  * 1.0 of the CDDL.
33  *
34  * A full copy of the text of the CDDL should have accompanied this
35  * source.  A copy of the CDDL is also available via the Internet at
36  * http://www.illumos.org/license/CDDL.
37  *
38  * Copyright 2015 Pluribus Networks Inc.
39  * Copyright 2018 Joyent, Inc.
40  * Copyright 2022 Oxide Computer Company
41  * Copyright 2022 OmniOS Community Edition (OmniOSce) Association.
42  */
43 
44 #include <sys/types.h>
45 
46 #ifndef __FreeBSD__
47 #include <sys/cpuset.h>
48 #include <intel/vmcs.h>
49 #endif
50 
51 #include <machine/atomic.h>
52 
53 #ifndef WITHOUT_CAPSICUM
54 #include <capsicum_helpers.h>
55 #endif
56 #include <stdio.h>
57 #include <stdlib.h>
58 #include <string.h>
59 #include <err.h>
60 #include <errno.h>
61 #include <libgen.h>
62 #include <unistd.h>
63 #include <assert.h>
64 #include <pthread.h>
65 #include <pthread_np.h>
66 #include <sysexits.h>
67 #include <stdbool.h>
68 #include <stdint.h>
69 
70 #include <machine/vmm.h>
71 #include <vmmapi.h>
72 
73 #include "bhyverun.h"
74 #include "config.h"
75 #include "debug.h"
76 #include "gdb.h"
77 #include "inout.h"
78 #include "mem.h"
79 #include "spinup_ap.h"
80 #include "vmexit.h"
81 #include "xmsr.h"
82 
83 #ifndef __FreeBSD__
84 static struct vm_entry *vmentry;
85 
86 int
vmentry_init(int ncpus)87 vmentry_init(int ncpus)
88 {
89 	vmentry = calloc(ncpus, sizeof(*vmentry));
90 	return (vmentry == NULL ? -1 : 0);
91 }
92 
93 struct vm_entry *
vmentry_vcpu(int vcpuid)94 vmentry_vcpu(int vcpuid)
95 {
96 	return (&vmentry[vcpuid]);
97 }
98 
99 static void
vmentry_mmio_read(struct vcpu * vcpu,uint64_t gpa,uint8_t bytes,uint64_t data)100 vmentry_mmio_read(struct vcpu *vcpu, uint64_t gpa, uint8_t bytes, uint64_t data)
101 {
102 	struct vm_entry *entry = &vmentry[vcpu_id(vcpu)];
103 	struct vm_mmio *mmio = &entry->u.mmio;
104 
105 	assert(entry->cmd == VEC_DEFAULT);
106 
107 	entry->cmd = VEC_FULFILL_MMIO;
108 	mmio->bytes = bytes;
109 	mmio->read = 1;
110 	mmio->gpa = gpa;
111 	mmio->data = data;
112 }
113 
114 static void
vmentry_mmio_write(struct vcpu * vcpu,uint64_t gpa,uint8_t bytes)115 vmentry_mmio_write(struct vcpu *vcpu, uint64_t gpa, uint8_t bytes)
116 {
117 	struct vm_entry *entry = &vmentry[vcpu_id(vcpu)];
118 	struct vm_mmio *mmio = &entry->u.mmio;
119 
120 	assert(entry->cmd == VEC_DEFAULT);
121 
122 	entry->cmd = VEC_FULFILL_MMIO;
123 	mmio->bytes = bytes;
124 	mmio->read = 0;
125 	mmio->gpa = gpa;
126 	mmio->data = 0;
127 }
128 
129 static void
vmentry_inout_read(struct vcpu * vcpu,uint16_t port,uint8_t bytes,uint32_t data)130 vmentry_inout_read(struct vcpu *vcpu, uint16_t port, uint8_t bytes,
131     uint32_t data)
132 {
133 	struct vm_entry *entry = &vmentry[vcpu_id(vcpu)];
134 	struct vm_inout *inout = &entry->u.inout;
135 
136 	assert(entry->cmd == VEC_DEFAULT);
137 
138 	entry->cmd = VEC_FULFILL_INOUT;
139 	inout->bytes = bytes;
140 	inout->flags = INOUT_IN;
141 	inout->port = port;
142 	inout->eax = data;
143 }
144 
145 static void
vmentry_inout_write(struct vcpu * vcpu,uint16_t port,uint8_t bytes)146 vmentry_inout_write(struct vcpu *vcpu, uint16_t port, uint8_t bytes)
147 {
148 	struct vm_entry *entry = &vmentry[vcpu_id(vcpu)];
149 	struct vm_inout *inout = &entry->u.inout;
150 
151 	assert(entry->cmd == VEC_DEFAULT);
152 
153 	entry->cmd = VEC_FULFILL_INOUT;
154 	inout->bytes = bytes;
155 	inout->flags = 0;
156 	inout->port = port;
157 	inout->eax = 0;
158 }
159 #endif
160 
161 #ifdef	__FreeBSD__
162 void
vm_inject_fault(struct vcpu * vcpu,int vector,int errcode_valid,int errcode)163 vm_inject_fault(struct vcpu *vcpu, int vector, int errcode_valid,
164     int errcode)
165 {
166 	int error, restart_instruction;
167 
168 	restart_instruction = 1;
169 
170 	error = vm_inject_exception(vcpu, vector, errcode_valid, errcode,
171 	    restart_instruction);
172 	assert(error == 0);
173 }
174 #endif
175 
176 static int
vmexit_inout(struct vmctx * ctx,struct vcpu * vcpu,struct vm_exit * vme)177 vmexit_inout(struct vmctx *ctx, struct vcpu *vcpu, struct vm_exit *vme)
178 {
179 	int error;
180 	struct vm_inout inout;
181 	bool in;
182 	uint8_t bytes;
183 
184 	inout = vme->u.inout;
185 	in = (inout.flags & INOUT_IN) != 0;
186 	bytes = inout.bytes;
187 
188 	error = emulate_inout(ctx, vcpu, &inout);
189 	if (error) {
190 		EPRINTLN("Unhandled %s%c 0x%04x at 0x%lx",
191 		    in ? "in" : "out",
192 		    bytes == 1 ? 'b' : (bytes == 2 ? 'w' : 'l'),
193 		    inout.port, vme->rip);
194 		return (VMEXIT_ABORT);
195 	} else {
196 		/*
197 		 * Communicate the status of the inout operation back to the
198 		 * in-kernel instruction emulation.
199 		 */
200 		if (in) {
201 			vmentry_inout_read(vcpu, inout.port, bytes, inout.eax);
202 		} else {
203 			vmentry_inout_write(vcpu, inout.port, bytes);
204 		}
205 		return (VMEXIT_CONTINUE);
206 	}
207 }
208 
209 static int
vmexit_rdmsr(struct vmctx * ctx __unused,struct vcpu * vcpu,struct vm_exit * vme)210 vmexit_rdmsr(struct vmctx *ctx __unused, struct vcpu *vcpu, struct vm_exit *vme)
211 {
212 	uint64_t val;
213 	uint32_t eax, edx;
214 	int error;
215 
216 	val = 0;
217 	error = emulate_rdmsr(vcpu, vme->u.msr.code, &val);
218 	if (error != 0) {
219 		EPRINTLN("rdmsr to register %#x on vcpu %d",
220 		    vme->u.msr.code, vcpu_id(vcpu));
221 		if (get_config_bool("x86.strictmsr")) {
222 			vm_inject_gp(vcpu);
223 			return (VMEXIT_CONTINUE);
224 		}
225 	}
226 
227 	eax = val;
228 	error = vm_set_register(vcpu, VM_REG_GUEST_RAX, eax);
229 	assert(error == 0);
230 
231 	edx = val >> 32;
232 	error = vm_set_register(vcpu, VM_REG_GUEST_RDX, edx);
233 	assert(error == 0);
234 
235 	return (VMEXIT_CONTINUE);
236 }
237 
238 static int
vmexit_wrmsr(struct vmctx * ctx __unused,struct vcpu * vcpu,struct vm_exit * vme)239 vmexit_wrmsr(struct vmctx *ctx __unused, struct vcpu *vcpu, struct vm_exit *vme)
240 {
241 	int error;
242 
243 	error = emulate_wrmsr(vcpu, vme->u.msr.code, vme->u.msr.wval);
244 	if (error != 0) {
245 		EPRINTLN("wrmsr to register %#x(%#lx) on vcpu %d",
246 		    vme->u.msr.code, vme->u.msr.wval, vcpu_id(vcpu));
247 		if (get_config_bool("x86.strictmsr")) {
248 			vm_inject_gp(vcpu);
249 			return (VMEXIT_CONTINUE);
250 		}
251 	}
252 	return (VMEXIT_CONTINUE);
253 }
254 
255 static const char * const vmx_exit_reason_desc[] = {
256 	[EXIT_REASON_EXCEPTION] = "Exception or non-maskable interrupt (NMI)",
257 	[EXIT_REASON_EXT_INTR] = "External interrupt",
258 	[EXIT_REASON_TRIPLE_FAULT] = "Triple fault",
259 	[EXIT_REASON_INIT] = "INIT signal",
260 	[EXIT_REASON_SIPI] = "Start-up IPI (SIPI)",
261 	[EXIT_REASON_IO_SMI] = "I/O system-management interrupt (SMI)",
262 	[EXIT_REASON_SMI] = "Other SMI",
263 	[EXIT_REASON_INTR_WINDOW] = "Interrupt window",
264 	[EXIT_REASON_NMI_WINDOW] = "NMI window",
265 	[EXIT_REASON_TASK_SWITCH] = "Task switch",
266 	[EXIT_REASON_CPUID] = "CPUID",
267 	[EXIT_REASON_GETSEC] = "GETSEC",
268 	[EXIT_REASON_HLT] = "HLT",
269 	[EXIT_REASON_INVD] = "INVD",
270 	[EXIT_REASON_INVLPG] = "INVLPG",
271 	[EXIT_REASON_RDPMC] = "RDPMC",
272 	[EXIT_REASON_RDTSC] = "RDTSC",
273 	[EXIT_REASON_RSM] = "RSM",
274 	[EXIT_REASON_VMCALL] = "VMCALL",
275 	[EXIT_REASON_VMCLEAR] = "VMCLEAR",
276 	[EXIT_REASON_VMLAUNCH] = "VMLAUNCH",
277 	[EXIT_REASON_VMPTRLD] = "VMPTRLD",
278 	[EXIT_REASON_VMPTRST] = "VMPTRST",
279 	[EXIT_REASON_VMREAD] = "VMREAD",
280 	[EXIT_REASON_VMRESUME] = "VMRESUME",
281 	[EXIT_REASON_VMWRITE] = "VMWRITE",
282 	[EXIT_REASON_VMXOFF] = "VMXOFF",
283 	[EXIT_REASON_VMXON] = "VMXON",
284 	[EXIT_REASON_CR_ACCESS] = "Control-register accesses",
285 	[EXIT_REASON_DR_ACCESS] = "MOV DR",
286 	[EXIT_REASON_INOUT] = "I/O instruction",
287 	[EXIT_REASON_RDMSR] = "RDMSR",
288 	[EXIT_REASON_WRMSR] = "WRMSR",
289 	[EXIT_REASON_INVAL_VMCS] =
290 	    "VM-entry failure due to invalid guest state",
291 	[EXIT_REASON_INVAL_MSR] = "VM-entry failure due to MSR loading",
292 	[EXIT_REASON_MWAIT] = "MWAIT",
293 	[EXIT_REASON_MTF] = "Monitor trap flag",
294 	[EXIT_REASON_MONITOR] = "MONITOR",
295 	[EXIT_REASON_PAUSE] = "PAUSE",
296 	[EXIT_REASON_MCE_DURING_ENTRY] =
297 	    "VM-entry failure due to machine-check event",
298 	[EXIT_REASON_TPR] = "TPR below threshold",
299 	[EXIT_REASON_APIC_ACCESS] = "APIC access",
300 	[EXIT_REASON_VIRTUALIZED_EOI] = "Virtualized EOI",
301 	[EXIT_REASON_GDTR_IDTR] = "Access to GDTR or IDTR",
302 	[EXIT_REASON_LDTR_TR] = "Access to LDTR or TR",
303 	[EXIT_REASON_EPT_FAULT] = "EPT violation",
304 	[EXIT_REASON_EPT_MISCONFIG] = "EPT misconfiguration",
305 	[EXIT_REASON_INVEPT] = "INVEPT",
306 	[EXIT_REASON_RDTSCP] = "RDTSCP",
307 	[EXIT_REASON_VMX_PREEMPT] = "VMX-preemption timer expired",
308 	[EXIT_REASON_INVVPID] = "INVVPID",
309 	[EXIT_REASON_WBINVD] = "WBINVD",
310 	[EXIT_REASON_XSETBV] = "XSETBV",
311 	[EXIT_REASON_APIC_WRITE] = "APIC write",
312 	[EXIT_REASON_RDRAND] = "RDRAND",
313 	[EXIT_REASON_INVPCID] = "INVPCID",
314 	[EXIT_REASON_VMFUNC] = "VMFUNC",
315 	[EXIT_REASON_ENCLS] = "ENCLS",
316 	[EXIT_REASON_RDSEED] = "RDSEED",
317 	[EXIT_REASON_PM_LOG_FULL] = "Page-modification log full",
318 	[EXIT_REASON_XSAVES] = "XSAVES",
319 	[EXIT_REASON_XRSTORS] = "XRSTORS"
320 };
321 
322 #ifndef __FreeBSD__
323 static int
vmexit_run_state(struct vmctx * ctx __unused,struct vcpu * vcpu __unused,struct vm_exit * vme __unused)324 vmexit_run_state(struct vmctx *ctx __unused, struct vcpu *vcpu __unused,
325     struct vm_exit *vme __unused)
326 {
327 	/*
328 	 * Run-state transitions (INIT, SIPI, etc) are handled in-kernel, so an
329 	 * exit to userspace with that code is not expected.
330 	 */
331 	fprintf(stderr, "unexpected run-state VM exit");
332 	return (VMEXIT_ABORT);
333 }
334 
335 static int
vmexit_paging(struct vmctx * ctx __unused,struct vcpu * vcpu,struct vm_exit * vme)336 vmexit_paging(struct vmctx *ctx __unused, struct vcpu *vcpu,
337     struct vm_exit *vme)
338 {
339 	fprintf(stderr, "vm exit[%d]\n", vcpu_id(vcpu));
340 	fprintf(stderr, "\treason\t\tPAGING\n");
341 	fprintf(stderr, "\trip\t\t0x%016lx\n", vme->rip);
342 	fprintf(stderr, "\tgpa\t\t0x%016lx\n", vme->u.paging.gpa);
343 	fprintf(stderr, "\tfault_type\t\t%d\n", vme->u.paging.fault_type);
344 
345 	return (VMEXIT_ABORT);
346 }
347 #endif /* __FreeBSD__ */
348 
349 #ifdef __FreeBSD__
350 #define	DEBUG_EPT_MISCONFIG
351 #else
352 /* EPT misconfig debugging not possible now that raw VMCS access is gone */
353 #endif
354 
355 #ifdef DEBUG_EPT_MISCONFIG
356 #define	VMCS_GUEST_PHYSICAL_ADDRESS	0x00002400
357 
358 static uint64_t ept_misconfig_gpa, ept_misconfig_pte[4];
359 static int ept_misconfig_ptenum;
360 #endif
361 
362 static const char *
vmexit_vmx_desc(uint32_t exit_reason)363 vmexit_vmx_desc(uint32_t exit_reason)
364 {
365 
366 	if (exit_reason >= nitems(vmx_exit_reason_desc) ||
367 	    vmx_exit_reason_desc[exit_reason] == NULL)
368 		return ("Unknown");
369 	return (vmx_exit_reason_desc[exit_reason]);
370 }
371 
372 static int
vmexit_vmx(struct vmctx * ctx,struct vcpu * vcpu,struct vm_exit * vme)373 vmexit_vmx(struct vmctx *ctx, struct vcpu *vcpu, struct vm_exit *vme)
374 {
375 
376 	EPRINTLN("vm exit[%d]", vcpu_id(vcpu));
377 	EPRINTLN("\treason\t\tVMX");
378 	EPRINTLN("\trip\t\t0x%016lx", vme->rip);
379 	EPRINTLN("\tinst_length\t%d", vme->inst_length);
380 	EPRINTLN("\tstatus\t\t%d", vme->u.vmx.status);
381 	EPRINTLN("\texit_reason\t%u (%s)", vme->u.vmx.exit_reason,
382 	    vmexit_vmx_desc(vme->u.vmx.exit_reason));
383 	EPRINTLN("\tqualification\t0x%016lx",
384 	    vme->u.vmx.exit_qualification);
385 	EPRINTLN("\tinst_type\t\t%d", vme->u.vmx.inst_type);
386 	EPRINTLN("\tinst_error\t\t%d", vme->u.vmx.inst_error);
387 #ifdef DEBUG_EPT_MISCONFIG
388 	if (vme->u.vmx.exit_reason == EXIT_REASON_EPT_MISCONFIG) {
389 		vm_get_register(vcpu,
390 		    VMCS_IDENT(VMCS_GUEST_PHYSICAL_ADDRESS),
391 		    &ept_misconfig_gpa);
392 		vm_get_gpa_pmap(ctx, ept_misconfig_gpa, ept_misconfig_pte,
393 		    &ept_misconfig_ptenum);
394 		EPRINTLN("\tEPT misconfiguration:");
395 		EPRINTLN("\t\tGPA: %#lx", ept_misconfig_gpa);
396 		EPRINTLN("\t\tPTE(%d): %#lx %#lx %#lx %#lx",
397 		    ept_misconfig_ptenum, ept_misconfig_pte[0],
398 		    ept_misconfig_pte[1], ept_misconfig_pte[2],
399 		    ept_misconfig_pte[3]);
400 	}
401 #endif	/* DEBUG_EPT_MISCONFIG */
402 	return (VMEXIT_ABORT);
403 }
404 
405 static int
vmexit_svm(struct vmctx * ctx __unused,struct vcpu * vcpu,struct vm_exit * vme)406 vmexit_svm(struct vmctx *ctx __unused, struct vcpu *vcpu, struct vm_exit *vme)
407 {
408 	EPRINTLN("vm exit[%d]", vcpu_id(vcpu));
409 	EPRINTLN("\treason\t\tSVM");
410 	EPRINTLN("\trip\t\t0x%016lx", vme->rip);
411 	EPRINTLN("\tinst_length\t%d", vme->inst_length);
412 	EPRINTLN("\texitcode\t%#lx", vme->u.svm.exitcode);
413 	EPRINTLN("\texitinfo1\t%#lx", vme->u.svm.exitinfo1);
414 	EPRINTLN("\texitinfo2\t%#lx", vme->u.svm.exitinfo2);
415 	return (VMEXIT_ABORT);
416 }
417 
418 static int
vmexit_bogus(struct vmctx * ctx __unused,struct vcpu * vcpu __unused,struct vm_exit * vme)419 vmexit_bogus(struct vmctx *ctx __unused, struct vcpu *vcpu __unused,
420     struct vm_exit *vme)
421 {
422 
423 	assert(vme->inst_length == 0);
424 
425 	return (VMEXIT_CONTINUE);
426 }
427 
428 static int
vmexit_hlt(struct vmctx * ctx __unused,struct vcpu * vcpu __unused,struct vm_exit * vme __unused)429 vmexit_hlt(struct vmctx *ctx __unused, struct vcpu *vcpu __unused,
430     struct vm_exit *vme __unused)
431 {
432 
433 	/*
434 	 * Just continue execution with the next instruction. We use
435 	 * the HLT VM exit as a way to be friendly with the host
436 	 * scheduler.
437 	 */
438 	return (VMEXIT_CONTINUE);
439 }
440 
441 static int
vmexit_pause(struct vmctx * ctx __unused,struct vcpu * vcpu __unused,struct vm_exit * vme __unused)442 vmexit_pause(struct vmctx *ctx __unused, struct vcpu *vcpu __unused,
443     struct vm_exit *vme __unused)
444 {
445 	return (VMEXIT_CONTINUE);
446 }
447 
448 static int
vmexit_mtrap(struct vmctx * ctx __unused,struct vcpu * vcpu,struct vm_exit * vme)449 vmexit_mtrap(struct vmctx *ctx __unused, struct vcpu *vcpu, struct vm_exit *vme)
450 {
451 
452 	assert(vme->inst_length == 0);
453 
454 	gdb_cpu_mtrap(vcpu);
455 
456 	return (VMEXIT_CONTINUE);
457 }
458 
459 static int
vmexit_inst_emul(struct vmctx * ctx __unused,struct vcpu * vcpu,struct vm_exit * vme)460 vmexit_inst_emul(struct vmctx *ctx __unused, struct vcpu *vcpu,
461     struct vm_exit *vme)
462 {
463 	uint8_t i, valid;
464 
465 	fprintf(stderr, "Failed to emulate instruction sequence ");
466 
467 	valid = vme->u.inst_emul.num_valid;
468 	if (valid != 0) {
469 		assert(valid <= sizeof (vme->u.inst_emul.inst));
470 		fprintf(stderr, "[");
471 		for (i = 0; i < valid; i++) {
472 			if (i == 0) {
473 				fprintf(stderr, "%02x",
474 				    vme->u.inst_emul.inst[i]);
475 			} else {
476 				fprintf(stderr, ", %02x",
477 				    vme->u.inst_emul.inst[i]);
478 			}
479 		}
480 		fprintf(stderr, "] ");
481 	}
482 	fprintf(stderr, "@ %rip = %x\n", vme->rip);
483 
484 	return (VMEXIT_ABORT);
485 }
486 
487 #ifndef	__FreeBSD__
488 static int
vmexit_mmio(struct vmctx * ctx __unused,struct vcpu * vcpu,struct vm_exit * vme)489 vmexit_mmio(struct vmctx *ctx __unused, struct vcpu *vcpu, struct vm_exit *vme)
490 {
491 	int err;
492 	struct vm_mmio mmio;
493 	bool is_read;
494 
495 	mmio = vme->u.mmio;
496 	is_read = (mmio.read != 0);
497 
498 	err = emulate_mem(vcpu, &mmio);
499 
500 	if (err == ESRCH) {
501 		fprintf(stderr, "Unhandled memory access to 0x%lx\n", mmio.gpa);
502 
503 		/*
504 		 * Access to non-existent physical addresses is not likely to
505 		 * result in fatal errors on hardware machines, but rather reads
506 		 * of all-ones or discarded-but-acknowledged writes.
507 		 */
508 		mmio.data = ~0UL;
509 		err = 0;
510 	}
511 
512 	if (err == 0) {
513 		if (is_read) {
514 			vmentry_mmio_read(vcpu, mmio.gpa, mmio.bytes,
515 			    mmio.data);
516 		} else {
517 			vmentry_mmio_write(vcpu, mmio.gpa, mmio.bytes);
518 		}
519 		return (VMEXIT_CONTINUE);
520 	}
521 
522 	fprintf(stderr, "Unhandled mmio error to 0x%lx: %d\n", mmio.gpa, err);
523 	return (VMEXIT_ABORT);
524 }
525 #endif /* !__FreeBSD__ */
526 
527 static int
vmexit_suspend(struct vmctx * ctx,struct vcpu * vcpu,struct vm_exit * vme)528 vmexit_suspend(struct vmctx *ctx, struct vcpu *vcpu, struct vm_exit *vme)
529 {
530 	enum vm_suspend_how how;
531 	int vcpuid = vcpu_id(vcpu);
532 
533 	how = vme->u.suspended.how;
534 
535 	fbsdrun_deletecpu(vcpuid);
536 
537 	switch (how) {
538 	case VM_SUSPEND_RESET:
539 		exit(0);
540 	case VM_SUSPEND_POWEROFF:
541 		if (get_config_bool_default("destroy_on_poweroff", false))
542 			vm_destroy(ctx);
543 		exit(1);
544 	case VM_SUSPEND_HALT:
545 		exit(2);
546 	case VM_SUSPEND_TRIPLEFAULT:
547 		exit(3);
548 	default:
549 		EPRINTLN("vmexit_suspend: invalid reason %d", how);
550 		exit(100);
551 	}
552 	return (0);	/* NOTREACHED */
553 }
554 
555 static int
vmexit_debug(struct vmctx * ctx __unused,struct vcpu * vcpu,struct vm_exit * vme __unused)556 vmexit_debug(struct vmctx *ctx __unused, struct vcpu *vcpu,
557     struct vm_exit *vme __unused)
558 {
559 	gdb_cpu_suspend(vcpu);
560 	/*
561 	 * Sleep for a short period to avoid chewing up the CPU in the
562 	 * window between activation of the vCPU thread and the STARTUP IPI.
563 	 */
564 	usleep(1000);
565 	return (VMEXIT_CONTINUE);
566 }
567 
568 static int
vmexit_breakpoint(struct vmctx * ctx __unused,struct vcpu * vcpu,struct vm_exit * vme)569 vmexit_breakpoint(struct vmctx *ctx __unused, struct vcpu *vcpu,
570     struct vm_exit *vme)
571 {
572 
573 	gdb_cpu_breakpoint(vcpu, vme);
574 	return (VMEXIT_CONTINUE);
575 }
576 
577 #ifdef	__FreeBSD__
578 static int
vmexit_ipi(struct vmctx * ctx __unused,struct vcpu * vcpu __unused,struct vm_exit * vme)579 vmexit_ipi(struct vmctx *ctx __unused, struct vcpu *vcpu __unused,
580     struct vm_exit *vme)
581 {
582 	int error = -1;
583 	int i;
584 	switch (vme->u.ipi.mode) {
585 	case APIC_DELMODE_INIT:
586 		CPU_FOREACH_ISSET(i, &vme->u.ipi.dmask) {
587 			error = vm_suspend_cpu(vcpu_info[i].vcpu);
588 			if (error) {
589 				warnx("%s: failed to suspend cpu %d\n",
590 				    __func__, i);
591 				break;
592 			}
593 		}
594 		break;
595 	case APIC_DELMODE_STARTUP:
596 		CPU_FOREACH_ISSET(i, &vme->u.ipi.dmask) {
597 			spinup_ap(vcpu_info[i].vcpu,
598 			    vme->u.ipi.vector << PAGE_SHIFT);
599 		}
600 		error = 0;
601 		break;
602 	default:
603 		break;
604 	}
605 
606 	return (error);
607 }
608 #endif
609 
610 const vmexit_handler_t vmexit_handlers[VM_EXITCODE_MAX] = {
611 	[VM_EXITCODE_INOUT]  = vmexit_inout,
612 #ifndef __FreeBSD__
613 	[VM_EXITCODE_MMIO]  = vmexit_mmio,
614 #endif
615 	[VM_EXITCODE_VMX]    = vmexit_vmx,
616 	[VM_EXITCODE_SVM]    = vmexit_svm,
617 	[VM_EXITCODE_BOGUS]  = vmexit_bogus,
618 	[VM_EXITCODE_RDMSR]  = vmexit_rdmsr,
619 	[VM_EXITCODE_WRMSR]  = vmexit_wrmsr,
620 	[VM_EXITCODE_MTRAP]  = vmexit_mtrap,
621 	[VM_EXITCODE_INST_EMUL] = vmexit_inst_emul,
622 #ifndef __FreeBSD__
623 	[VM_EXITCODE_RUN_STATE] = vmexit_run_state,
624 	[VM_EXITCODE_PAGING] = vmexit_paging,
625 #endif
626 	[VM_EXITCODE_SUSPENDED] = vmexit_suspend,
627 	[VM_EXITCODE_TASK_SWITCH] = vmexit_task_switch,
628 	[VM_EXITCODE_DEBUG] = vmexit_debug,
629 	[VM_EXITCODE_BPT] = vmexit_breakpoint,
630 #ifdef	__FreeBSD__
631 	[VM_EXITCODE_IPI] = vmexit_ipi,
632 #endif
633 	[VM_EXITCODE_HLT] = vmexit_hlt,
634 	[VM_EXITCODE_PAUSE] = vmexit_pause,
635 };
636