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 87 vmentry_init(int ncpus) 88 { 89 vmentry = calloc(ncpus, sizeof(*vmentry)); 90 return (vmentry == NULL ? -1 : 0); 91 } 92 93 struct vm_entry * 94 vmentry_vcpu(int vcpuid) 95 { 96 return (&vmentry[vcpuid]); 97 } 98 99 static void 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 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 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 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 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 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 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 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 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 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 * 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 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 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 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 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 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 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 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 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 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 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 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 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