aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBojan Novković <bojan.novkovic@fer.hr>2023-12-12 23:28:59 +0000
committerJohn Baldwin <jhb@FreeBSD.org>2023-12-12 23:28:59 +0000
commitca96a942cafb58476e10e887240e594e7923a6e8 (patch)
tree6249390affc4d41bf05d60d608db99300d6b9da5
parent5c7a9092713d2d076513b31b377dad59780a39da (diff)
downloadsrc-ca96a942cafb58476e10e887240e594e7923a6e8.tar.gz
src-ca96a942cafb58476e10e887240e594e7923a6e8.zip
bhyve: refactor gdbstub to enable single-stepping on AMD CPUs
This patch refactors the existing Intel-specific single-stepping mechanism in bhyve's GDB stub to work with both AMD and Intel CPUs. Reviewed by: jhb Sponsored by: Google, Inc. (GSoC 2022) Differential Revision: https://reviews.freebsd.org/D42298
-rw-r--r--usr.sbin/bhyve/amd64/vmexit.c15
-rw-r--r--usr.sbin/bhyve/gdb.c94
-rw-r--r--usr.sbin/bhyve/gdb.h1
3 files changed, 93 insertions, 17 deletions
diff --git a/usr.sbin/bhyve/amd64/vmexit.c b/usr.sbin/bhyve/amd64/vmexit.c
index 2c01c63f6454..e0b9aec2d17a 100644
--- a/usr.sbin/bhyve/amd64/vmexit.c
+++ b/usr.sbin/bhyve/amd64/vmexit.c
@@ -440,6 +440,20 @@ vmexit_debug(struct vmctx *ctx __unused, struct vcpu *vcpu,
}
static int
+vmexit_db(struct vmctx *ctx __unused, struct vcpu *vcpu, struct vm_run *vmrun)
+{
+
+#ifdef BHYVE_SNAPSHOT
+ checkpoint_cpu_suspend(vcpu_id(vcpu));
+#endif
+ gdb_cpu_debug(vcpu, vmrun->vm_exit);
+#ifdef BHYVE_SNAPSHOT
+ checkpoint_cpu_resume(vcpu_id(vcpu));
+#endif
+ return (VMEXIT_CONTINUE);
+}
+
+static int
vmexit_breakpoint(struct vmctx *ctx __unused, struct vcpu *vcpu,
struct vm_run *vmrun)
{
@@ -503,4 +517,5 @@ const vmexit_handler_t vmexit_handlers[VM_EXITCODE_MAX] = {
[VM_EXITCODE_IPI] = vmexit_ipi,
[VM_EXITCODE_HLT] = vmexit_hlt,
[VM_EXITCODE_PAUSE] = vmexit_pause,
+ [VM_EXITCODE_DB] = vmexit_db,
};
diff --git a/usr.sbin/bhyve/gdb.c b/usr.sbin/bhyve/gdb.c
index be730a75b3e6..2d49469c2e11 100644
--- a/usr.sbin/bhyve/gdb.c
+++ b/usr.sbin/bhyve/gdb.c
@@ -744,6 +744,43 @@ _gdb_cpu_suspend(struct vcpu *vcpu, bool report_stop)
}
/*
+ * Requests vCPU single-stepping using a
+ * VMEXIT suitable for the host platform.
+ */
+static int
+_gdb_set_step(struct vcpu *vcpu, int val)
+{
+ int error;
+
+ /*
+ * If the MTRAP cap fails, we are running on an AMD host.
+ * In that case, we request DB exits caused by RFLAGS.TF.
+ */
+ error = vm_set_capability(vcpu, VM_CAP_MTRAP_EXIT, val);
+ if (error != 0)
+ error = vm_set_capability(vcpu, VM_CAP_RFLAGS_TF, val);
+ if (error == 0)
+ (void)vm_set_capability(vcpu, VM_CAP_MASK_HWINTR, val);
+
+ return (error);
+}
+
+/*
+ * Checks whether single-stepping is enabled for a given vCPU.
+ */
+static int
+_gdb_check_step(struct vcpu *vcpu)
+{
+ int val;
+
+ if (vm_get_capability(vcpu, VM_CAP_MTRAP_EXIT, &val) != 0) {
+ if (vm_get_capability(vcpu, VM_CAP_RFLAGS_TF, &val) != 0)
+ return -1;
+ }
+ return 0;
+}
+
+/*
* Invoked at the start of a vCPU thread's execution to inform the
* debug server about the new thread.
*/
@@ -797,10 +834,7 @@ gdb_cpu_resume(struct vcpu *vcpu)
assert(vs->hit_swbreak == false);
assert(vs->stepped == false);
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);
+ error = _gdb_set_step(vcpu, 1);
assert(error == 0);
}
}
@@ -835,26 +869,24 @@ gdb_suspend_vcpus(void)
}
/*
- * Handler for VM_EXITCODE_MTRAP reported when a vCPU single-steps via
- * the VT-x-specific MTRAP exit.
+ * Invoked each time a vmexit handler needs to step a vCPU.
+ * Handles MTRAP and RFLAGS.TF vmexits.
*/
-void
-gdb_cpu_mtrap(struct vcpu *vcpu)
+static void
+gdb_cpu_step(struct vcpu *vcpu)
{
struct vcpu_state *vs;
- int vcpuid;
+ int vcpuid = vcpu_id(vcpu);
+ int error;
- if (!gdb_active)
- return;
- vcpuid = vcpu_id(vcpu);
- debug("$vCPU %d MTRAP\n", vcpuid);
+ debug("$vCPU %d stepped\n", vcpuid);
pthread_mutex_lock(&gdb_lock);
vs = &vcpu_state[vcpuid];
if (vs->stepping) {
vs->stepping = false;
vs->stepped = true;
- vm_set_capability(vcpu, VM_CAP_MTRAP_EXIT, 0);
- vm_set_capability(vcpu, VM_CAP_MASK_HWINTR, 0);
+ error = _gdb_set_step(vcpu, 0);
+ assert(error == 0);
while (vs->stepped) {
if (stopped_vcpu == -1) {
@@ -869,6 +901,34 @@ gdb_cpu_mtrap(struct vcpu *vcpu)
pthread_mutex_unlock(&gdb_lock);
}
+/*
+ * A general handler for VM_EXITCODE_DB.
+ * Handles RFLAGS.TF exits on AMD SVM.
+ */
+void
+gdb_cpu_debug(struct vcpu *vcpu, struct vm_exit *vmexit)
+{
+ if (!gdb_active)
+ return;
+
+ /* RFLAGS.TF exit? */
+ if (vmexit->u.dbg.trace_trap) {
+ gdb_cpu_step(vcpu);
+ }
+}
+
+/*
+ * Handler for VM_EXITCODE_MTRAP reported when a vCPU single-steps via
+ * the VT-x-specific MTRAP exit.
+ */
+void
+gdb_cpu_mtrap(struct vcpu *vcpu)
+{
+ if (!gdb_active)
+ return;
+ gdb_cpu_step(vcpu);
+}
+
static struct breakpoint *
find_breakpoint(uint64_t gpa)
{
@@ -940,11 +1000,11 @@ gdb_cpu_breakpoint(struct vcpu *vcpu, struct vm_exit *vmexit)
static bool
gdb_step_vcpu(struct vcpu *vcpu)
{
- int error, val, vcpuid;
+ int error, vcpuid;
vcpuid = vcpu_id(vcpu);
debug("$vCPU %d step\n", vcpuid);
- error = vm_get_capability(vcpu, VM_CAP_MTRAP_EXIT, &val);
+ error = _gdb_check_step(vcpu);
if (error < 0)
return (false);
diff --git a/usr.sbin/bhyve/gdb.h b/usr.sbin/bhyve/gdb.h
index f06375d0d591..98f9ece2f60c 100644
--- a/usr.sbin/bhyve/gdb.h
+++ b/usr.sbin/bhyve/gdb.h
@@ -32,6 +32,7 @@ void gdb_cpu_add(struct vcpu *vcpu);
void gdb_cpu_breakpoint(struct vcpu *vcpu, struct vm_exit *vmexit);
void gdb_cpu_mtrap(struct vcpu *vcpu);
void gdb_cpu_suspend(struct vcpu *vcpu);
+void gdb_cpu_debug(struct vcpu *vcpu, struct vm_exit *vmexit);
void init_gdb(struct vmctx *ctx);
#endif /* !__GDB_H__ */