git: fefac543590d - main - bhyve: fix vCPU single-stepping on VMX

From: Corvin Köhne <corvink_at_FreeBSD.org>
Date: Tue, 09 May 2023 08:05:09 UTC
The branch main has been updated by corvink:

URL: https://cgit.FreeBSD.org/src/commit/?id=fefac543590db4e1461235b7c936f46026d0f318

commit fefac543590db4e1461235b7c936f46026d0f318
Author:     Bojan Novković <bojan.novkovic@fer.hr>
AuthorDate: 2023-05-09 07:02:04 +0000
Commit:     Corvin Köhne <corvink@FreeBSD.org>
CommitDate: 2023-05-09 08:04:55 +0000

    bhyve: fix vCPU single-stepping on VMX
    
    This patch fixes virtual machine single stepping on VMX hosts.
    
    Currently, when using bhyve's gdb stub, each attempt at single-stepping
    a vCPU lands in a timer interrupt. The current single-stepping mechanism
    uses the Monitor Trap Flag feature to cause VMEXIT after a single
    instruction is executed. Unfortunately, the SDM states that MTF causes
    VMEXITs for the next instruction that gets executed, which is often not
    what the person using the debugger expects. [1]
    
    This patch adds a new VM capability that masks interrupts on a vCPU by
    blocking interrupt injection and modifies the gdb stub to use the newly
    added capability while single-stepping a vCPU.
    
    [1] Intel SDM 26.5.2 Vol. 3C
    
    Reviewed by:            corvink, jbh
    MFC after:              1 week
    Differential Revision:  https://reviews.freebsd.org/D39949
---
 sys/amd64/include/vmm.h   | 1 +
 sys/amd64/vmm/intel/vmx.c | 7 +++++++
 usr.sbin/bhyve/gdb.c      | 5 +++++
 3 files changed, 13 insertions(+)

diff --git a/sys/amd64/include/vmm.h b/sys/amd64/include/vmm.h
index c3d1aa4f3866..a328404a89d5 100644
--- a/sys/amd64/include/vmm.h
+++ b/sys/amd64/include/vmm.h
@@ -497,6 +497,7 @@ enum vm_cap_type {
 	VM_CAP_RDPID,
 	VM_CAP_RDTSCP,
 	VM_CAP_IPI_EXIT,
+	VM_CAP_MASK_HWINTR,
 	VM_CAP_MAX
 };
 
diff --git a/sys/amd64/vmm/intel/vmx.c b/sys/amd64/vmm/intel/vmx.c
index 8b8f8fe6cb25..69391f5b77de 100644
--- a/sys/amd64/vmm/intel/vmx.c
+++ b/sys/amd64/vmm/intel/vmx.c
@@ -1439,6 +1439,10 @@ vmx_inject_interrupts(struct vmx_vcpu *vcpu, struct vlapic *vlapic,
 	uint64_t rflags, entryinfo;
 	uint32_t gi, info;
 
+	if (vcpu->cap.set & (1 << VM_CAP_MASK_HWINTR)) {
+		return;
+	}
+
 	if (vcpu->state.nextrip != guestrip) {
 		gi = vmcs_read(VMCS_GUEST_INTERRUPTIBILITY);
 		if (gi & HWINTR_BLOCKING) {
@@ -3634,6 +3638,9 @@ vmx_setcap(void *vcpui, int type, int val)
 		vlapic = vm_lapic(vcpu->vcpu);
 		vlapic->ipi_exit = val;
 		break;
+	case VM_CAP_MASK_HWINTR:		
+		retval = 0;
+		break;
 	default:
 		break;
 	}
diff --git a/usr.sbin/bhyve/gdb.c b/usr.sbin/bhyve/gdb.c
index 9e50602e92be..7a2c5aec0d86 100644
--- a/usr.sbin/bhyve/gdb.c
+++ b/usr.sbin/bhyve/gdb.c
@@ -801,6 +801,9 @@ gdb_cpu_resume(struct vcpu *vcpu)
 	if (vs->stepping) {
 		error = vm_set_capability(vcpu, VM_CAP_MTRAP_EXIT, 1);
 		assert(error == 0);
+
+		error = vm_set_capability(vcpu, VM_CAP_MASK_HWINTR, 1);
+		assert(error == 0);
 	}
 }
 
@@ -853,6 +856,8 @@ gdb_cpu_mtrap(struct vcpu *vcpu)
 		vs->stepping = false;
 		vs->stepped = true;
 		vm_set_capability(vcpu, VM_CAP_MTRAP_EXIT, 0);
+		vm_set_capability(vcpu, VM_CAP_MASK_HWINTR, 0);
+
 		while (vs->stepped) {
 			if (stopped_vcpu == -1) {
 				debug("$vCPU %d reporting step\n", vcpuid);