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
This commit is contained in:
Bojan Novković 2023-05-09 09:02:04 +02:00 committed by Corvin Köhne
parent b0cf48305f
commit fefac54359
No known key found for this signature in database
GPG Key ID: D854DA56315E026A
3 changed files with 13 additions and 0 deletions

View File

@ -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
};

View File

@ -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;
}

View File

@ -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);