diff options
author | Bojan Novković <bojan.novkovic@fer.hr> | 2023-12-12 23:28:59 +0000 |
---|---|---|
committer | John Baldwin <jhb@FreeBSD.org> | 2023-12-12 23:28:59 +0000 |
commit | ca96a942cafb58476e10e887240e594e7923a6e8 (patch) | |
tree | 6249390affc4d41bf05d60d608db99300d6b9da5 | |
parent | 5c7a9092713d2d076513b31b377dad59780a39da (diff) | |
download | src-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.c | 15 | ||||
-rw-r--r-- | usr.sbin/bhyve/gdb.c | 94 | ||||
-rw-r--r-- | 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 @@ -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__ */ |