xref: /freebsd/usr.sbin/bhyve/amd64/vmexit.c (revision a27328ea392714f2bc106f138191fd465157aafb)
172f9c9d8SMark Johnston /*-
272f9c9d8SMark Johnston  * SPDX-License-Identifier: BSD-2-Clause
372f9c9d8SMark Johnston  *
472f9c9d8SMark Johnston  * Copyright (c) 2011 NetApp, Inc.
572f9c9d8SMark Johnston  * All rights reserved.
672f9c9d8SMark Johnston  *
772f9c9d8SMark Johnston  * Redistribution and use in source and binary forms, with or without
872f9c9d8SMark Johnston  * modification, are permitted provided that the following conditions
972f9c9d8SMark Johnston  * are met:
1072f9c9d8SMark Johnston  * 1. Redistributions of source code must retain the above copyright
1172f9c9d8SMark Johnston  *    notice, this list of conditions and the following disclaimer.
1272f9c9d8SMark Johnston  * 2. Redistributions in binary form must reproduce the above copyright
1372f9c9d8SMark Johnston  *    notice, this list of conditions and the following disclaimer in the
1472f9c9d8SMark Johnston  *    documentation and/or other materials provided with the distribution.
1572f9c9d8SMark Johnston  *
1672f9c9d8SMark Johnston  * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND
1772f9c9d8SMark Johnston  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1872f9c9d8SMark Johnston  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1972f9c9d8SMark Johnston  * ARE DISCLAIMED.  IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE
2072f9c9d8SMark Johnston  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2172f9c9d8SMark Johnston  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2272f9c9d8SMark Johnston  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2372f9c9d8SMark Johnston  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2472f9c9d8SMark Johnston  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2572f9c9d8SMark Johnston  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2672f9c9d8SMark Johnston  * SUCH DAMAGE.
2772f9c9d8SMark Johnston  */
2872f9c9d8SMark Johnston 
2972f9c9d8SMark Johnston #include <sys/types.h>
3072f9c9d8SMark Johnston 
3172f9c9d8SMark Johnston #include <machine/vmm.h>
3272f9c9d8SMark Johnston #include <machine/vmm_dev.h>
3372f9c9d8SMark Johnston #include <machine/vmm_instruction_emul.h>
3472f9c9d8SMark Johnston #include <amd64/vmm/intel/vmcs.h>
3572f9c9d8SMark Johnston #include <x86/apicreg.h>
3672f9c9d8SMark Johnston 
3772f9c9d8SMark Johnston #include <assert.h>
3872f9c9d8SMark Johnston #include <err.h>
3972f9c9d8SMark Johnston #include <errno.h>
4072f9c9d8SMark Johnston #include <stdlib.h>
4172f9c9d8SMark Johnston #include <strings.h>
4272f9c9d8SMark Johnston #include <unistd.h>
4372f9c9d8SMark Johnston 
4472f9c9d8SMark Johnston #include <vmmapi.h>
4572f9c9d8SMark Johnston 
4672f9c9d8SMark Johnston #include "bhyverun.h"
4772f9c9d8SMark Johnston #include "config.h"
4872f9c9d8SMark Johnston #include "debug.h"
4972f9c9d8SMark Johnston #include "gdb.h"
5072f9c9d8SMark Johnston #include "inout.h"
5172f9c9d8SMark Johnston #include "mem.h"
5272f9c9d8SMark Johnston #ifdef BHYVE_SNAPSHOT
5372f9c9d8SMark Johnston #include "snapshot.h"
5472f9c9d8SMark Johnston #endif
5572f9c9d8SMark Johnston #include "spinup_ap.h"
5672f9c9d8SMark Johnston #include "vmexit.h"
5772f9c9d8SMark Johnston #include "xmsr.h"
5872f9c9d8SMark Johnston 
597228ad8dSMark Johnston void
vm_inject_fault(struct vcpu * vcpu,int vector,int errcode_valid,int errcode)607228ad8dSMark Johnston vm_inject_fault(struct vcpu *vcpu, int vector, int errcode_valid,
617228ad8dSMark Johnston     int errcode)
627228ad8dSMark Johnston {
637228ad8dSMark Johnston 	int error, restart_instruction;
647228ad8dSMark Johnston 
657228ad8dSMark Johnston 	restart_instruction = 1;
667228ad8dSMark Johnston 
677228ad8dSMark Johnston 	error = vm_inject_exception(vcpu, vector, errcode_valid, errcode,
687228ad8dSMark Johnston 	    restart_instruction);
697228ad8dSMark Johnston 	assert(error == 0);
707228ad8dSMark Johnston }
717228ad8dSMark Johnston 
7272f9c9d8SMark Johnston static int
vmexit_inout(struct vmctx * ctx,struct vcpu * vcpu,struct vm_run * vmrun)7372f9c9d8SMark Johnston vmexit_inout(struct vmctx *ctx, struct vcpu *vcpu, struct vm_run *vmrun)
7472f9c9d8SMark Johnston {
7572f9c9d8SMark Johnston 	struct vm_exit *vme;
7672f9c9d8SMark Johnston 	int error;
7772f9c9d8SMark Johnston 	int bytes, port, in;
7872f9c9d8SMark Johnston 
7972f9c9d8SMark Johnston 	vme = vmrun->vm_exit;
8072f9c9d8SMark Johnston 	port = vme->u.inout.port;
8172f9c9d8SMark Johnston 	bytes = vme->u.inout.bytes;
8272f9c9d8SMark Johnston 	in = vme->u.inout.in;
8372f9c9d8SMark Johnston 
8472f9c9d8SMark Johnston 	error = emulate_inout(ctx, vcpu, vme);
8572f9c9d8SMark Johnston 	if (error) {
86b0936440SJohn Baldwin 		EPRINTLN("Unhandled %s%c 0x%04x at 0x%lx",
8772f9c9d8SMark Johnston 		    in ? "in" : "out",
8872f9c9d8SMark Johnston 		    bytes == 1 ? 'b' : (bytes == 2 ? 'w' : 'l'),
8972f9c9d8SMark Johnston 		    port, vme->rip);
9072f9c9d8SMark Johnston 		return (VMEXIT_ABORT);
9172f9c9d8SMark Johnston 	} else {
9272f9c9d8SMark Johnston 		return (VMEXIT_CONTINUE);
9372f9c9d8SMark Johnston 	}
9472f9c9d8SMark Johnston }
9572f9c9d8SMark Johnston 
9672f9c9d8SMark Johnston static int
vmexit_rdmsr(struct vmctx * ctx __unused,struct vcpu * vcpu,struct vm_run * vmrun)9772f9c9d8SMark Johnston vmexit_rdmsr(struct vmctx *ctx __unused, struct vcpu *vcpu,
9872f9c9d8SMark Johnston     struct vm_run *vmrun)
9972f9c9d8SMark Johnston {
10072f9c9d8SMark Johnston 	struct vm_exit *vme;
10172f9c9d8SMark Johnston 	uint64_t val;
10272f9c9d8SMark Johnston 	uint32_t eax, edx;
10372f9c9d8SMark Johnston 	int error;
10472f9c9d8SMark Johnston 
10572f9c9d8SMark Johnston 	vme = vmrun->vm_exit;
10672f9c9d8SMark Johnston 
10772f9c9d8SMark Johnston 	val = 0;
10872f9c9d8SMark Johnston 	error = emulate_rdmsr(vcpu, vme->u.msr.code, &val);
10972f9c9d8SMark Johnston 	if (error != 0) {
110*a27328eaSbrendabrandy 		if (get_config_bool("x86.strictmsr") ||
111*a27328eaSbrendabrandy 		    get_config_bool("x86.verbosemsr")) {
112b0936440SJohn Baldwin 			EPRINTLN("rdmsr to register %#x on vcpu %d",
11372f9c9d8SMark Johnston 			    vme->u.msr.code, vcpu_id(vcpu));
114*a27328eaSbrendabrandy 		}
11572f9c9d8SMark Johnston 		if (get_config_bool("x86.strictmsr")) {
11672f9c9d8SMark Johnston 			vm_inject_gp(vcpu);
11772f9c9d8SMark Johnston 			return (VMEXIT_CONTINUE);
11872f9c9d8SMark Johnston 		}
11972f9c9d8SMark Johnston 	}
12072f9c9d8SMark Johnston 
12172f9c9d8SMark Johnston 	eax = val;
12272f9c9d8SMark Johnston 	error = vm_set_register(vcpu, VM_REG_GUEST_RAX, eax);
12372f9c9d8SMark Johnston 	assert(error == 0);
12472f9c9d8SMark Johnston 
12572f9c9d8SMark Johnston 	edx = val >> 32;
12672f9c9d8SMark Johnston 	error = vm_set_register(vcpu, VM_REG_GUEST_RDX, edx);
12772f9c9d8SMark Johnston 	assert(error == 0);
12872f9c9d8SMark Johnston 
12972f9c9d8SMark Johnston 	return (VMEXIT_CONTINUE);
13072f9c9d8SMark Johnston }
13172f9c9d8SMark Johnston 
13272f9c9d8SMark Johnston static int
vmexit_wrmsr(struct vmctx * ctx __unused,struct vcpu * vcpu,struct vm_run * vmrun)13372f9c9d8SMark Johnston vmexit_wrmsr(struct vmctx *ctx __unused, struct vcpu *vcpu,
13472f9c9d8SMark Johnston     struct vm_run *vmrun)
13572f9c9d8SMark Johnston {
13672f9c9d8SMark Johnston 	struct vm_exit *vme;
13772f9c9d8SMark Johnston 	int error;
13872f9c9d8SMark Johnston 
13972f9c9d8SMark Johnston 	vme = vmrun->vm_exit;
14072f9c9d8SMark Johnston 
14172f9c9d8SMark Johnston 	error = emulate_wrmsr(vcpu, vme->u.msr.code, vme->u.msr.wval);
14272f9c9d8SMark Johnston 	if (error != 0) {
143*a27328eaSbrendabrandy 		if (get_config_bool("x86.strictmsr") ||
144*a27328eaSbrendabrandy 		    get_config_bool("x86.verbosemsr")) {
145b0936440SJohn Baldwin 			EPRINTLN("wrmsr to register %#x(%#lx) on vcpu %d",
14672f9c9d8SMark Johnston 			    vme->u.msr.code, vme->u.msr.wval, vcpu_id(vcpu));
147*a27328eaSbrendabrandy 		}
14872f9c9d8SMark Johnston 		if (get_config_bool("x86.strictmsr")) {
14972f9c9d8SMark Johnston 			vm_inject_gp(vcpu);
15072f9c9d8SMark Johnston 			return (VMEXIT_CONTINUE);
15172f9c9d8SMark Johnston 		}
15272f9c9d8SMark Johnston 	}
15372f9c9d8SMark Johnston 	return (VMEXIT_CONTINUE);
15472f9c9d8SMark Johnston }
15572f9c9d8SMark Johnston 
15672f9c9d8SMark Johnston static const char * const vmx_exit_reason_desc[] = {
15772f9c9d8SMark Johnston 	[EXIT_REASON_EXCEPTION] = "Exception or non-maskable interrupt (NMI)",
15872f9c9d8SMark Johnston 	[EXIT_REASON_EXT_INTR] = "External interrupt",
15972f9c9d8SMark Johnston 	[EXIT_REASON_TRIPLE_FAULT] = "Triple fault",
16072f9c9d8SMark Johnston 	[EXIT_REASON_INIT] = "INIT signal",
16172f9c9d8SMark Johnston 	[EXIT_REASON_SIPI] = "Start-up IPI (SIPI)",
16272f9c9d8SMark Johnston 	[EXIT_REASON_IO_SMI] = "I/O system-management interrupt (SMI)",
16372f9c9d8SMark Johnston 	[EXIT_REASON_SMI] = "Other SMI",
16472f9c9d8SMark Johnston 	[EXIT_REASON_INTR_WINDOW] = "Interrupt window",
16572f9c9d8SMark Johnston 	[EXIT_REASON_NMI_WINDOW] = "NMI window",
16672f9c9d8SMark Johnston 	[EXIT_REASON_TASK_SWITCH] = "Task switch",
16772f9c9d8SMark Johnston 	[EXIT_REASON_CPUID] = "CPUID",
16872f9c9d8SMark Johnston 	[EXIT_REASON_GETSEC] = "GETSEC",
16972f9c9d8SMark Johnston 	[EXIT_REASON_HLT] = "HLT",
17072f9c9d8SMark Johnston 	[EXIT_REASON_INVD] = "INVD",
17172f9c9d8SMark Johnston 	[EXIT_REASON_INVLPG] = "INVLPG",
17272f9c9d8SMark Johnston 	[EXIT_REASON_RDPMC] = "RDPMC",
17372f9c9d8SMark Johnston 	[EXIT_REASON_RDTSC] = "RDTSC",
17472f9c9d8SMark Johnston 	[EXIT_REASON_RSM] = "RSM",
17572f9c9d8SMark Johnston 	[EXIT_REASON_VMCALL] = "VMCALL",
17672f9c9d8SMark Johnston 	[EXIT_REASON_VMCLEAR] = "VMCLEAR",
17772f9c9d8SMark Johnston 	[EXIT_REASON_VMLAUNCH] = "VMLAUNCH",
17872f9c9d8SMark Johnston 	[EXIT_REASON_VMPTRLD] = "VMPTRLD",
17972f9c9d8SMark Johnston 	[EXIT_REASON_VMPTRST] = "VMPTRST",
18072f9c9d8SMark Johnston 	[EXIT_REASON_VMREAD] = "VMREAD",
18172f9c9d8SMark Johnston 	[EXIT_REASON_VMRESUME] = "VMRESUME",
18272f9c9d8SMark Johnston 	[EXIT_REASON_VMWRITE] = "VMWRITE",
18372f9c9d8SMark Johnston 	[EXIT_REASON_VMXOFF] = "VMXOFF",
18472f9c9d8SMark Johnston 	[EXIT_REASON_VMXON] = "VMXON",
18572f9c9d8SMark Johnston 	[EXIT_REASON_CR_ACCESS] = "Control-register accesses",
18672f9c9d8SMark Johnston 	[EXIT_REASON_DR_ACCESS] = "MOV DR",
18772f9c9d8SMark Johnston 	[EXIT_REASON_INOUT] = "I/O instruction",
18872f9c9d8SMark Johnston 	[EXIT_REASON_RDMSR] = "RDMSR",
18972f9c9d8SMark Johnston 	[EXIT_REASON_WRMSR] = "WRMSR",
19072f9c9d8SMark Johnston 	[EXIT_REASON_INVAL_VMCS] =
19172f9c9d8SMark Johnston 	    "VM-entry failure due to invalid guest state",
19272f9c9d8SMark Johnston 	[EXIT_REASON_INVAL_MSR] = "VM-entry failure due to MSR loading",
19372f9c9d8SMark Johnston 	[EXIT_REASON_MWAIT] = "MWAIT",
19472f9c9d8SMark Johnston 	[EXIT_REASON_MTF] = "Monitor trap flag",
19572f9c9d8SMark Johnston 	[EXIT_REASON_MONITOR] = "MONITOR",
19672f9c9d8SMark Johnston 	[EXIT_REASON_PAUSE] = "PAUSE",
19772f9c9d8SMark Johnston 	[EXIT_REASON_MCE_DURING_ENTRY] =
19872f9c9d8SMark Johnston 	    "VM-entry failure due to machine-check event",
19972f9c9d8SMark Johnston 	[EXIT_REASON_TPR] = "TPR below threshold",
20072f9c9d8SMark Johnston 	[EXIT_REASON_APIC_ACCESS] = "APIC access",
20172f9c9d8SMark Johnston 	[EXIT_REASON_VIRTUALIZED_EOI] = "Virtualized EOI",
20272f9c9d8SMark Johnston 	[EXIT_REASON_GDTR_IDTR] = "Access to GDTR or IDTR",
20372f9c9d8SMark Johnston 	[EXIT_REASON_LDTR_TR] = "Access to LDTR or TR",
20472f9c9d8SMark Johnston 	[EXIT_REASON_EPT_FAULT] = "EPT violation",
20572f9c9d8SMark Johnston 	[EXIT_REASON_EPT_MISCONFIG] = "EPT misconfiguration",
20672f9c9d8SMark Johnston 	[EXIT_REASON_INVEPT] = "INVEPT",
20772f9c9d8SMark Johnston 	[EXIT_REASON_RDTSCP] = "RDTSCP",
20872f9c9d8SMark Johnston 	[EXIT_REASON_VMX_PREEMPT] = "VMX-preemption timer expired",
20972f9c9d8SMark Johnston 	[EXIT_REASON_INVVPID] = "INVVPID",
21072f9c9d8SMark Johnston 	[EXIT_REASON_WBINVD] = "WBINVD",
21172f9c9d8SMark Johnston 	[EXIT_REASON_XSETBV] = "XSETBV",
21272f9c9d8SMark Johnston 	[EXIT_REASON_APIC_WRITE] = "APIC write",
21372f9c9d8SMark Johnston 	[EXIT_REASON_RDRAND] = "RDRAND",
21472f9c9d8SMark Johnston 	[EXIT_REASON_INVPCID] = "INVPCID",
21572f9c9d8SMark Johnston 	[EXIT_REASON_VMFUNC] = "VMFUNC",
21672f9c9d8SMark Johnston 	[EXIT_REASON_ENCLS] = "ENCLS",
21772f9c9d8SMark Johnston 	[EXIT_REASON_RDSEED] = "RDSEED",
21872f9c9d8SMark Johnston 	[EXIT_REASON_PM_LOG_FULL] = "Page-modification log full",
21972f9c9d8SMark Johnston 	[EXIT_REASON_XSAVES] = "XSAVES",
22072f9c9d8SMark Johnston 	[EXIT_REASON_XRSTORS] = "XRSTORS"
22172f9c9d8SMark Johnston };
22272f9c9d8SMark Johnston 
22372f9c9d8SMark Johnston static const char *
vmexit_vmx_desc(uint32_t exit_reason)22472f9c9d8SMark Johnston vmexit_vmx_desc(uint32_t exit_reason)
22572f9c9d8SMark Johnston {
22672f9c9d8SMark Johnston 
22772f9c9d8SMark Johnston 	if (exit_reason >= nitems(vmx_exit_reason_desc) ||
22872f9c9d8SMark Johnston 	    vmx_exit_reason_desc[exit_reason] == NULL)
22972f9c9d8SMark Johnston 		return ("Unknown");
23072f9c9d8SMark Johnston 	return (vmx_exit_reason_desc[exit_reason]);
23172f9c9d8SMark Johnston }
23272f9c9d8SMark Johnston 
23372f9c9d8SMark Johnston #define	DEBUG_EPT_MISCONFIG
23472f9c9d8SMark Johnston #ifdef DEBUG_EPT_MISCONFIG
23572f9c9d8SMark Johnston #define	VMCS_GUEST_PHYSICAL_ADDRESS	0x00002400
23672f9c9d8SMark Johnston 
23772f9c9d8SMark Johnston static uint64_t ept_misconfig_gpa, ept_misconfig_pte[4];
23872f9c9d8SMark Johnston static int ept_misconfig_ptenum;
23972f9c9d8SMark Johnston #endif
24072f9c9d8SMark Johnston 
24172f9c9d8SMark Johnston static int
vmexit_vmx(struct vmctx * ctx,struct vcpu * vcpu,struct vm_run * vmrun)24272f9c9d8SMark Johnston vmexit_vmx(struct vmctx *ctx, struct vcpu *vcpu, struct vm_run *vmrun)
24372f9c9d8SMark Johnston {
24472f9c9d8SMark Johnston 	struct vm_exit *vme;
24572f9c9d8SMark Johnston 
24672f9c9d8SMark Johnston 	vme = vmrun->vm_exit;
24772f9c9d8SMark Johnston 
248b0936440SJohn Baldwin 	EPRINTLN("vm exit[%d]", vcpu_id(vcpu));
249b0936440SJohn Baldwin 	EPRINTLN("\treason\t\tVMX");
250b0936440SJohn Baldwin 	EPRINTLN("\trip\t\t0x%016lx", vme->rip);
251b0936440SJohn Baldwin 	EPRINTLN("\tinst_length\t%d", vme->inst_length);
252b0936440SJohn Baldwin 	EPRINTLN("\tstatus\t\t%d", vme->u.vmx.status);
253b0936440SJohn Baldwin 	EPRINTLN("\texit_reason\t%u (%s)", vme->u.vmx.exit_reason,
25472f9c9d8SMark Johnston 	    vmexit_vmx_desc(vme->u.vmx.exit_reason));
255b0936440SJohn Baldwin 	EPRINTLN("\tqualification\t0x%016lx",
25672f9c9d8SMark Johnston 	    vme->u.vmx.exit_qualification);
257b0936440SJohn Baldwin 	EPRINTLN("\tinst_type\t\t%d", vme->u.vmx.inst_type);
258b0936440SJohn Baldwin 	EPRINTLN("\tinst_error\t\t%d", vme->u.vmx.inst_error);
25972f9c9d8SMark Johnston #ifdef DEBUG_EPT_MISCONFIG
26072f9c9d8SMark Johnston 	if (vme->u.vmx.exit_reason == EXIT_REASON_EPT_MISCONFIG) {
26172f9c9d8SMark Johnston 		vm_get_register(vcpu,
26272f9c9d8SMark Johnston 		    VMCS_IDENT(VMCS_GUEST_PHYSICAL_ADDRESS),
26372f9c9d8SMark Johnston 		    &ept_misconfig_gpa);
26472f9c9d8SMark Johnston 		vm_get_gpa_pmap(ctx, ept_misconfig_gpa, ept_misconfig_pte,
26572f9c9d8SMark Johnston 		    &ept_misconfig_ptenum);
266b0936440SJohn Baldwin 		EPRINTLN("\tEPT misconfiguration:");
267b0936440SJohn Baldwin 		EPRINTLN("\t\tGPA: %#lx", ept_misconfig_gpa);
268b0936440SJohn Baldwin 		EPRINTLN("\t\tPTE(%d): %#lx %#lx %#lx %#lx",
26972f9c9d8SMark Johnston 		    ept_misconfig_ptenum, ept_misconfig_pte[0],
27072f9c9d8SMark Johnston 		    ept_misconfig_pte[1], ept_misconfig_pte[2],
27172f9c9d8SMark Johnston 		    ept_misconfig_pte[3]);
27272f9c9d8SMark Johnston 	}
27372f9c9d8SMark Johnston #endif	/* DEBUG_EPT_MISCONFIG */
27472f9c9d8SMark Johnston 	return (VMEXIT_ABORT);
27572f9c9d8SMark Johnston }
27672f9c9d8SMark Johnston 
27772f9c9d8SMark Johnston static int
vmexit_svm(struct vmctx * ctx __unused,struct vcpu * vcpu,struct vm_run * vmrun)27872f9c9d8SMark Johnston vmexit_svm(struct vmctx *ctx __unused, struct vcpu *vcpu, struct vm_run *vmrun)
27972f9c9d8SMark Johnston {
28072f9c9d8SMark Johnston 	struct vm_exit *vme;
28172f9c9d8SMark Johnston 
28272f9c9d8SMark Johnston 	vme = vmrun->vm_exit;
28372f9c9d8SMark Johnston 
284b0936440SJohn Baldwin 	EPRINTLN("vm exit[%d]", vcpu_id(vcpu));
285b0936440SJohn Baldwin 	EPRINTLN("\treason\t\tSVM");
286b0936440SJohn Baldwin 	EPRINTLN("\trip\t\t0x%016lx", vme->rip);
287b0936440SJohn Baldwin 	EPRINTLN("\tinst_length\t%d", vme->inst_length);
288b0936440SJohn Baldwin 	EPRINTLN("\texitcode\t%#lx", vme->u.svm.exitcode);
289b0936440SJohn Baldwin 	EPRINTLN("\texitinfo1\t%#lx", vme->u.svm.exitinfo1);
290b0936440SJohn Baldwin 	EPRINTLN("\texitinfo2\t%#lx", vme->u.svm.exitinfo2);
29172f9c9d8SMark Johnston 	return (VMEXIT_ABORT);
29272f9c9d8SMark Johnston }
29372f9c9d8SMark Johnston 
29472f9c9d8SMark Johnston static int
vmexit_bogus(struct vmctx * ctx __unused,struct vcpu * vcpu __unused,struct vm_run * vmrun)29572f9c9d8SMark Johnston vmexit_bogus(struct vmctx *ctx __unused, struct vcpu *vcpu __unused,
29672f9c9d8SMark Johnston     struct vm_run *vmrun)
29772f9c9d8SMark Johnston {
29872f9c9d8SMark Johnston 	assert(vmrun->vm_exit->inst_length == 0);
29972f9c9d8SMark Johnston 
30072f9c9d8SMark Johnston 	return (VMEXIT_CONTINUE);
30172f9c9d8SMark Johnston }
30272f9c9d8SMark Johnston 
30372f9c9d8SMark Johnston static int
vmexit_reqidle(struct vmctx * ctx __unused,struct vcpu * vcpu __unused,struct vm_run * vmrun)30472f9c9d8SMark Johnston vmexit_reqidle(struct vmctx *ctx __unused, struct vcpu *vcpu __unused,
30572f9c9d8SMark Johnston     struct vm_run *vmrun)
30672f9c9d8SMark Johnston {
30772f9c9d8SMark Johnston 	assert(vmrun->vm_exit->inst_length == 0);
30872f9c9d8SMark Johnston 
30972f9c9d8SMark Johnston 	return (VMEXIT_CONTINUE);
31072f9c9d8SMark Johnston }
31172f9c9d8SMark Johnston 
31272f9c9d8SMark Johnston static int
vmexit_hlt(struct vmctx * ctx __unused,struct vcpu * vcpu __unused,struct vm_run * vmrun __unused)31372f9c9d8SMark Johnston vmexit_hlt(struct vmctx *ctx __unused, struct vcpu *vcpu __unused,
31472f9c9d8SMark Johnston     struct vm_run *vmrun __unused)
31572f9c9d8SMark Johnston {
31672f9c9d8SMark Johnston 	/*
31772f9c9d8SMark Johnston 	 * Just continue execution with the next instruction. We use
31872f9c9d8SMark Johnston 	 * the HLT VM exit as a way to be friendly with the host
31972f9c9d8SMark Johnston 	 * scheduler.
32072f9c9d8SMark Johnston 	 */
32172f9c9d8SMark Johnston 	return (VMEXIT_CONTINUE);
32272f9c9d8SMark Johnston }
32372f9c9d8SMark Johnston 
32472f9c9d8SMark Johnston static int
vmexit_pause(struct vmctx * ctx __unused,struct vcpu * vcpu __unused,struct vm_run * vmrun __unused)32572f9c9d8SMark Johnston vmexit_pause(struct vmctx *ctx __unused, struct vcpu *vcpu __unused,
32672f9c9d8SMark Johnston     struct vm_run *vmrun __unused)
32772f9c9d8SMark Johnston {
32872f9c9d8SMark Johnston 	return (VMEXIT_CONTINUE);
32972f9c9d8SMark Johnston }
33072f9c9d8SMark Johnston 
33172f9c9d8SMark Johnston static int
vmexit_mtrap(struct vmctx * ctx __unused,struct vcpu * vcpu,struct vm_run * vmrun)33272f9c9d8SMark Johnston vmexit_mtrap(struct vmctx *ctx __unused, struct vcpu *vcpu,
33372f9c9d8SMark Johnston     struct vm_run *vmrun)
33472f9c9d8SMark Johnston {
33572f9c9d8SMark Johnston 	assert(vmrun->vm_exit->inst_length == 0);
33672f9c9d8SMark Johnston 
33772f9c9d8SMark Johnston #ifdef BHYVE_SNAPSHOT
33872f9c9d8SMark Johnston 	checkpoint_cpu_suspend(vcpu_id(vcpu));
33972f9c9d8SMark Johnston #endif
34072f9c9d8SMark Johnston 	gdb_cpu_mtrap(vcpu);
34172f9c9d8SMark Johnston #ifdef BHYVE_SNAPSHOT
34272f9c9d8SMark Johnston 	checkpoint_cpu_resume(vcpu_id(vcpu));
34372f9c9d8SMark Johnston #endif
34472f9c9d8SMark Johnston 
34572f9c9d8SMark Johnston 	return (VMEXIT_CONTINUE);
34672f9c9d8SMark Johnston }
34772f9c9d8SMark Johnston 
34872f9c9d8SMark Johnston static int
vmexit_inst_emul(struct vmctx * ctx __unused,struct vcpu * vcpu,struct vm_run * vmrun)34972f9c9d8SMark Johnston vmexit_inst_emul(struct vmctx *ctx __unused, struct vcpu *vcpu,
35072f9c9d8SMark Johnston     struct vm_run *vmrun)
35172f9c9d8SMark Johnston {
35272f9c9d8SMark Johnston 	struct vm_exit *vme;
35372f9c9d8SMark Johnston 	struct vie *vie;
35472f9c9d8SMark Johnston 	int err, i, cs_d;
35572f9c9d8SMark Johnston 	enum vm_cpu_mode mode;
35672f9c9d8SMark Johnston 
35772f9c9d8SMark Johnston 	vme = vmrun->vm_exit;
35872f9c9d8SMark Johnston 
35972f9c9d8SMark Johnston 	vie = &vme->u.inst_emul.vie;
36072f9c9d8SMark Johnston 	if (!vie->decoded) {
36172f9c9d8SMark Johnston 		/*
36272f9c9d8SMark Johnston 		 * Attempt to decode in userspace as a fallback.  This allows
36372f9c9d8SMark Johnston 		 * updating instruction decode in bhyve without rebooting the
36472f9c9d8SMark Johnston 		 * kernel (rapid prototyping), albeit with much slower
36572f9c9d8SMark Johnston 		 * emulation.
36672f9c9d8SMark Johnston 		 */
36772f9c9d8SMark Johnston 		vie_restart(vie);
36872f9c9d8SMark Johnston 		mode = vme->u.inst_emul.paging.cpu_mode;
36972f9c9d8SMark Johnston 		cs_d = vme->u.inst_emul.cs_d;
37072f9c9d8SMark Johnston 		if (vmm_decode_instruction(mode, cs_d, vie) != 0)
37172f9c9d8SMark Johnston 			goto fail;
37272f9c9d8SMark Johnston 		if (vm_set_register(vcpu, VM_REG_GUEST_RIP,
37372f9c9d8SMark Johnston 		    vme->rip + vie->num_processed) != 0)
37472f9c9d8SMark Johnston 			goto fail;
37572f9c9d8SMark Johnston 	}
37672f9c9d8SMark Johnston 
37772f9c9d8SMark Johnston 	err = emulate_mem(vcpu, vme->u.inst_emul.gpa, vie,
37872f9c9d8SMark Johnston 	    &vme->u.inst_emul.paging);
37972f9c9d8SMark Johnston 	if (err) {
38072f9c9d8SMark Johnston 		if (err == ESRCH) {
38172f9c9d8SMark Johnston 			EPRINTLN("Unhandled memory access to 0x%lx\n",
38272f9c9d8SMark Johnston 			    vme->u.inst_emul.gpa);
38372f9c9d8SMark Johnston 		}
38472f9c9d8SMark Johnston 		goto fail;
38572f9c9d8SMark Johnston 	}
38672f9c9d8SMark Johnston 
38772f9c9d8SMark Johnston 	return (VMEXIT_CONTINUE);
38872f9c9d8SMark Johnston 
38972f9c9d8SMark Johnston fail:
39072f9c9d8SMark Johnston 	fprintf(stderr, "Failed to emulate instruction sequence [ ");
39172f9c9d8SMark Johnston 	for (i = 0; i < vie->num_valid; i++)
39272f9c9d8SMark Johnston 		fprintf(stderr, "%02x", vie->inst[i]);
39372f9c9d8SMark Johnston 	FPRINTLN(stderr, " ] at 0x%lx", vme->rip);
39472f9c9d8SMark Johnston 	return (VMEXIT_ABORT);
39572f9c9d8SMark Johnston }
39672f9c9d8SMark Johnston 
39772f9c9d8SMark Johnston static int
vmexit_suspend(struct vmctx * ctx,struct vcpu * vcpu,struct vm_run * vmrun)39872f9c9d8SMark Johnston vmexit_suspend(struct vmctx *ctx, struct vcpu *vcpu, struct vm_run *vmrun)
39972f9c9d8SMark Johnston {
40072f9c9d8SMark Johnston 	struct vm_exit *vme;
40172f9c9d8SMark Johnston 	enum vm_suspend_how how;
40272f9c9d8SMark Johnston 	int vcpuid = vcpu_id(vcpu);
40372f9c9d8SMark Johnston 
40472f9c9d8SMark Johnston 	vme = vmrun->vm_exit;
40572f9c9d8SMark Johnston 
40672f9c9d8SMark Johnston 	how = vme->u.suspended.how;
40772f9c9d8SMark Johnston 
40872f9c9d8SMark Johnston 	fbsdrun_deletecpu(vcpuid);
40972f9c9d8SMark Johnston 
41072f9c9d8SMark Johnston 	switch (how) {
41172f9c9d8SMark Johnston 	case VM_SUSPEND_RESET:
41272f9c9d8SMark Johnston 		exit(0);
41372f9c9d8SMark Johnston 	case VM_SUSPEND_POWEROFF:
41472f9c9d8SMark Johnston 		if (get_config_bool_default("destroy_on_poweroff", false))
41572f9c9d8SMark Johnston 			vm_destroy(ctx);
41672f9c9d8SMark Johnston 		exit(1);
41772f9c9d8SMark Johnston 	case VM_SUSPEND_HALT:
41872f9c9d8SMark Johnston 		exit(2);
41972f9c9d8SMark Johnston 	case VM_SUSPEND_TRIPLEFAULT:
42072f9c9d8SMark Johnston 		exit(3);
42172f9c9d8SMark Johnston 	default:
422b0936440SJohn Baldwin 		EPRINTLN("vmexit_suspend: invalid reason %d", how);
42372f9c9d8SMark Johnston 		exit(100);
42472f9c9d8SMark Johnston 	}
42572f9c9d8SMark Johnston 	return (0);	/* NOTREACHED */
42672f9c9d8SMark Johnston }
42772f9c9d8SMark Johnston 
42872f9c9d8SMark Johnston static int
vmexit_debug(struct vmctx * ctx __unused,struct vcpu * vcpu,struct vm_run * vmrun __unused)42972f9c9d8SMark Johnston vmexit_debug(struct vmctx *ctx __unused, struct vcpu *vcpu,
43072f9c9d8SMark Johnston     struct vm_run *vmrun __unused)
43172f9c9d8SMark Johnston {
43272f9c9d8SMark Johnston 
43372f9c9d8SMark Johnston #ifdef BHYVE_SNAPSHOT
43472f9c9d8SMark Johnston 	checkpoint_cpu_suspend(vcpu_id(vcpu));
43572f9c9d8SMark Johnston #endif
43672f9c9d8SMark Johnston 	gdb_cpu_suspend(vcpu);
43772f9c9d8SMark Johnston #ifdef BHYVE_SNAPSHOT
43872f9c9d8SMark Johnston 	checkpoint_cpu_resume(vcpu_id(vcpu));
43972f9c9d8SMark Johnston #endif
44072f9c9d8SMark Johnston 	/*
44172f9c9d8SMark Johnston 	 * XXX-MJ sleep for a short period to avoid chewing up the CPU in the
44272f9c9d8SMark Johnston 	 * window between activation of the vCPU thread and the STARTUP IPI.
44372f9c9d8SMark Johnston 	 */
44472f9c9d8SMark Johnston 	usleep(1000);
44572f9c9d8SMark Johnston 	return (VMEXIT_CONTINUE);
44672f9c9d8SMark Johnston }
44772f9c9d8SMark Johnston 
44872f9c9d8SMark Johnston static int
vmexit_db(struct vmctx * ctx __unused,struct vcpu * vcpu,struct vm_run * vmrun)449ca96a942SBojan Novković vmexit_db(struct vmctx *ctx __unused, struct vcpu *vcpu, struct vm_run *vmrun)
450ca96a942SBojan Novković {
451ca96a942SBojan Novković 
452ca96a942SBojan Novković #ifdef BHYVE_SNAPSHOT
453ca96a942SBojan Novković 	checkpoint_cpu_suspend(vcpu_id(vcpu));
454ca96a942SBojan Novković #endif
455ca96a942SBojan Novković 	gdb_cpu_debug(vcpu, vmrun->vm_exit);
456ca96a942SBojan Novković #ifdef BHYVE_SNAPSHOT
457ca96a942SBojan Novković 	checkpoint_cpu_resume(vcpu_id(vcpu));
458ca96a942SBojan Novković #endif
459ca96a942SBojan Novković 	return (VMEXIT_CONTINUE);
460ca96a942SBojan Novković }
461ca96a942SBojan Novković 
462ca96a942SBojan Novković static int
vmexit_breakpoint(struct vmctx * ctx __unused,struct vcpu * vcpu,struct vm_run * vmrun)46372f9c9d8SMark Johnston vmexit_breakpoint(struct vmctx *ctx __unused, struct vcpu *vcpu,
46472f9c9d8SMark Johnston     struct vm_run *vmrun)
46572f9c9d8SMark Johnston {
46672f9c9d8SMark Johnston 	gdb_cpu_breakpoint(vcpu, vmrun->vm_exit);
46772f9c9d8SMark Johnston 	return (VMEXIT_CONTINUE);
46872f9c9d8SMark Johnston }
46972f9c9d8SMark Johnston 
47072f9c9d8SMark Johnston static int
vmexit_ipi(struct vmctx * ctx __unused,struct vcpu * vcpu __unused,struct vm_run * vmrun)47172f9c9d8SMark Johnston vmexit_ipi(struct vmctx *ctx __unused, struct vcpu *vcpu __unused,
47272f9c9d8SMark Johnston     struct vm_run *vmrun)
47372f9c9d8SMark Johnston {
47472f9c9d8SMark Johnston 	struct vm_exit *vme;
47572f9c9d8SMark Johnston 	cpuset_t *dmask;
47672f9c9d8SMark Johnston 	int error = -1;
47772f9c9d8SMark Johnston 	int i;
47872f9c9d8SMark Johnston 
47972f9c9d8SMark Johnston 	dmask = vmrun->cpuset;
48072f9c9d8SMark Johnston 	vme = vmrun->vm_exit;
48172f9c9d8SMark Johnston 
48272f9c9d8SMark Johnston 	switch (vme->u.ipi.mode) {
48372f9c9d8SMark Johnston 	case APIC_DELMODE_INIT:
48472f9c9d8SMark Johnston 		CPU_FOREACH_ISSET(i, dmask) {
48572f9c9d8SMark Johnston 			error = fbsdrun_suspendcpu(i);
48672f9c9d8SMark Johnston 			if (error) {
48772f9c9d8SMark Johnston 				warnx("failed to suspend cpu %d", i);
48872f9c9d8SMark Johnston 				break;
48972f9c9d8SMark Johnston 			}
49072f9c9d8SMark Johnston 		}
49172f9c9d8SMark Johnston 		break;
49272f9c9d8SMark Johnston 	case APIC_DELMODE_STARTUP:
49372f9c9d8SMark Johnston 		CPU_FOREACH_ISSET(i, dmask) {
49472f9c9d8SMark Johnston 			spinup_ap(fbsdrun_vcpu(i),
49572f9c9d8SMark Johnston 			    vme->u.ipi.vector << PAGE_SHIFT);
49672f9c9d8SMark Johnston 		}
49772f9c9d8SMark Johnston 		error = 0;
49872f9c9d8SMark Johnston 		break;
49972f9c9d8SMark Johnston 	default:
50072f9c9d8SMark Johnston 		break;
50172f9c9d8SMark Johnston 	}
50272f9c9d8SMark Johnston 
50372f9c9d8SMark Johnston 	return (error);
50472f9c9d8SMark Johnston }
50572f9c9d8SMark Johnston 
50672f9c9d8SMark Johnston int vmexit_task_switch(struct vmctx *, struct vcpu *, struct vm_run *);
50772f9c9d8SMark Johnston 
50872f9c9d8SMark Johnston const vmexit_handler_t vmexit_handlers[VM_EXITCODE_MAX] = {
50972f9c9d8SMark Johnston 	[VM_EXITCODE_INOUT]  = vmexit_inout,
51072f9c9d8SMark Johnston 	[VM_EXITCODE_INOUT_STR]  = vmexit_inout,
51172f9c9d8SMark Johnston 	[VM_EXITCODE_VMX]    = vmexit_vmx,
51272f9c9d8SMark Johnston 	[VM_EXITCODE_SVM]    = vmexit_svm,
51372f9c9d8SMark Johnston 	[VM_EXITCODE_BOGUS]  = vmexit_bogus,
51472f9c9d8SMark Johnston 	[VM_EXITCODE_REQIDLE] = vmexit_reqidle,
51572f9c9d8SMark Johnston 	[VM_EXITCODE_RDMSR]  = vmexit_rdmsr,
51672f9c9d8SMark Johnston 	[VM_EXITCODE_WRMSR]  = vmexit_wrmsr,
51772f9c9d8SMark Johnston 	[VM_EXITCODE_MTRAP]  = vmexit_mtrap,
51872f9c9d8SMark Johnston 	[VM_EXITCODE_INST_EMUL] = vmexit_inst_emul,
51972f9c9d8SMark Johnston 	[VM_EXITCODE_SUSPENDED] = vmexit_suspend,
52072f9c9d8SMark Johnston 	[VM_EXITCODE_TASK_SWITCH] = vmexit_task_switch,
52172f9c9d8SMark Johnston 	[VM_EXITCODE_DEBUG] = vmexit_debug,
52272f9c9d8SMark Johnston 	[VM_EXITCODE_BPT] = vmexit_breakpoint,
52372f9c9d8SMark Johnston 	[VM_EXITCODE_IPI] = vmexit_ipi,
52472f9c9d8SMark Johnston 	[VM_EXITCODE_HLT] = vmexit_hlt,
52572f9c9d8SMark Johnston 	[VM_EXITCODE_PAUSE] = vmexit_pause,
526ca96a942SBojan Novković 	[VM_EXITCODE_DB] = vmexit_db,
52772f9c9d8SMark Johnston };
528