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