mirror of
https://github.com/freebsd/freebsd-src
synced 2024-09-06 17:18:32 +00:00
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
This commit is contained in:
parent
5c7a909271
commit
ca96a942ca
|
@ -439,6 +439,20 @@ vmexit_debug(struct vmctx *ctx __unused, struct vcpu *vcpu,
|
||||||
return (VMEXIT_CONTINUE);
|
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
|
static int
|
||||||
vmexit_breakpoint(struct vmctx *ctx __unused, struct vcpu *vcpu,
|
vmexit_breakpoint(struct vmctx *ctx __unused, struct vcpu *vcpu,
|
||||||
struct vm_run *vmrun)
|
struct vm_run *vmrun)
|
||||||
|
@ -503,4 +517,5 @@ const vmexit_handler_t vmexit_handlers[VM_EXITCODE_MAX] = {
|
||||||
[VM_EXITCODE_IPI] = vmexit_ipi,
|
[VM_EXITCODE_IPI] = vmexit_ipi,
|
||||||
[VM_EXITCODE_HLT] = vmexit_hlt,
|
[VM_EXITCODE_HLT] = vmexit_hlt,
|
||||||
[VM_EXITCODE_PAUSE] = vmexit_pause,
|
[VM_EXITCODE_PAUSE] = vmexit_pause,
|
||||||
|
[VM_EXITCODE_DB] = vmexit_db,
|
||||||
};
|
};
|
||||||
|
|
|
@ -743,6 +743,43 @@ _gdb_cpu_suspend(struct vcpu *vcpu, bool report_stop)
|
||||||
debug("$vCPU %d resuming\n", vcpuid);
|
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
|
* Invoked at the start of a vCPU thread's execution to inform the
|
||||||
* debug server about the new thread.
|
* debug server about the new thread.
|
||||||
|
@ -797,10 +834,7 @@ gdb_cpu_resume(struct vcpu *vcpu)
|
||||||
assert(vs->hit_swbreak == false);
|
assert(vs->hit_swbreak == false);
|
||||||
assert(vs->stepped == false);
|
assert(vs->stepped == false);
|
||||||
if (vs->stepping) {
|
if (vs->stepping) {
|
||||||
error = vm_set_capability(vcpu, VM_CAP_MTRAP_EXIT, 1);
|
error = _gdb_set_step(vcpu, 1);
|
||||||
assert(error == 0);
|
|
||||||
|
|
||||||
error = vm_set_capability(vcpu, VM_CAP_MASK_HWINTR, 1);
|
|
||||||
assert(error == 0);
|
assert(error == 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -835,26 +869,24 @@ gdb_suspend_vcpus(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Handler for VM_EXITCODE_MTRAP reported when a vCPU single-steps via
|
* Invoked each time a vmexit handler needs to step a vCPU.
|
||||||
* the VT-x-specific MTRAP exit.
|
* Handles MTRAP and RFLAGS.TF vmexits.
|
||||||
*/
|
*/
|
||||||
void
|
static void
|
||||||
gdb_cpu_mtrap(struct vcpu *vcpu)
|
gdb_cpu_step(struct vcpu *vcpu)
|
||||||
{
|
{
|
||||||
struct vcpu_state *vs;
|
struct vcpu_state *vs;
|
||||||
int vcpuid;
|
int vcpuid = vcpu_id(vcpu);
|
||||||
|
int error;
|
||||||
|
|
||||||
if (!gdb_active)
|
debug("$vCPU %d stepped\n", vcpuid);
|
||||||
return;
|
|
||||||
vcpuid = vcpu_id(vcpu);
|
|
||||||
debug("$vCPU %d MTRAP\n", vcpuid);
|
|
||||||
pthread_mutex_lock(&gdb_lock);
|
pthread_mutex_lock(&gdb_lock);
|
||||||
vs = &vcpu_state[vcpuid];
|
vs = &vcpu_state[vcpuid];
|
||||||
if (vs->stepping) {
|
if (vs->stepping) {
|
||||||
vs->stepping = false;
|
vs->stepping = false;
|
||||||
vs->stepped = true;
|
vs->stepped = true;
|
||||||
vm_set_capability(vcpu, VM_CAP_MTRAP_EXIT, 0);
|
error = _gdb_set_step(vcpu, 0);
|
||||||
vm_set_capability(vcpu, VM_CAP_MASK_HWINTR, 0);
|
assert(error == 0);
|
||||||
|
|
||||||
while (vs->stepped) {
|
while (vs->stepped) {
|
||||||
if (stopped_vcpu == -1) {
|
if (stopped_vcpu == -1) {
|
||||||
|
@ -869,6 +901,34 @@ gdb_cpu_mtrap(struct vcpu *vcpu)
|
||||||
pthread_mutex_unlock(&gdb_lock);
|
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 *
|
static struct breakpoint *
|
||||||
find_breakpoint(uint64_t gpa)
|
find_breakpoint(uint64_t gpa)
|
||||||
{
|
{
|
||||||
|
@ -940,11 +1000,11 @@ gdb_cpu_breakpoint(struct vcpu *vcpu, struct vm_exit *vmexit)
|
||||||
static bool
|
static bool
|
||||||
gdb_step_vcpu(struct vcpu *vcpu)
|
gdb_step_vcpu(struct vcpu *vcpu)
|
||||||
{
|
{
|
||||||
int error, val, vcpuid;
|
int error, vcpuid;
|
||||||
|
|
||||||
vcpuid = vcpu_id(vcpu);
|
vcpuid = vcpu_id(vcpu);
|
||||||
debug("$vCPU %d step\n", vcpuid);
|
debug("$vCPU %d step\n", vcpuid);
|
||||||
error = vm_get_capability(vcpu, VM_CAP_MTRAP_EXIT, &val);
|
error = _gdb_check_step(vcpu);
|
||||||
if (error < 0)
|
if (error < 0)
|
||||||
return (false);
|
return (false);
|
||||||
|
|
||||||
|
|
|
@ -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_breakpoint(struct vcpu *vcpu, struct vm_exit *vmexit);
|
||||||
void gdb_cpu_mtrap(struct vcpu *vcpu);
|
void gdb_cpu_mtrap(struct vcpu *vcpu);
|
||||||
void gdb_cpu_suspend(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);
|
void init_gdb(struct vmctx *ctx);
|
||||||
|
|
||||||
#endif /* !__GDB_H__ */
|
#endif /* !__GDB_H__ */
|
||||||
|
|
Loading…
Reference in a new issue