The branch main has been updated by jhb:

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

commit ca96a942cafb58476e10e887240e594e7923a6e8
Author:     Bojan Novković <[email protected]>
AuthorDate: 2023-12-12 23:28:59 +0000
Commit:     John Baldwin <[email protected]>
CommitDate: 2023-12-12 23:28:59 +0000

    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
---
 usr.sbin/bhyve/amd64/vmexit.c | 15 +++++++
 usr.sbin/bhyve/gdb.c          | 94 +++++++++++++++++++++++++++++++++++--------
 usr.sbin/bhyve/gdb.h          |  1 +
 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
@@ -439,6 +439,20 @@ vmexit_debug(struct vmctx *ctx __unused, struct vcpu *vcpu,
        return (VMEXIT_CONTINUE);
 }
 
+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
@@ -743,6 +743,43 @@ _gdb_cpu_suspend(struct vcpu *vcpu, bool report_stop)
        debug("$vCPU %d resuming\n", vcpuid);
 }
 
+/*
+ * 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__ */

Reply via email to