Changeset View
Changeset View
Standalone View
Standalone View
usr.sbin/bhyve/gdb.c
Show First 20 Lines • Show All 125 Lines • ▼ Show 20 Lines | |||||
static struct io_buffer cur_comm, cur_resp; | static struct io_buffer cur_comm, cur_resp; | ||||
static uint8_t cur_csum; | static uint8_t cur_csum; | ||||
static struct vmctx *ctx; | static struct vmctx *ctx; | ||||
static int cur_fd = -1; | static int cur_fd = -1; | ||||
static TAILQ_HEAD(, breakpoint) breakpoints; | static TAILQ_HEAD(, breakpoint) breakpoints; | ||||
static struct vcpu_state *vcpu_state; | static struct vcpu_state *vcpu_state; | ||||
static int cur_vcpu, stopped_vcpu; | static int cur_vcpu, stopped_vcpu; | ||||
static bool gdb_active = false; | |||||
const int gdb_regset[] = { | const int gdb_regset[] = { | ||||
VM_REG_GUEST_RAX, | VM_REG_GUEST_RAX, | ||||
VM_REG_GUEST_RBX, | VM_REG_GUEST_RBX, | ||||
VM_REG_GUEST_RCX, | VM_REG_GUEST_RCX, | ||||
VM_REG_GUEST_RDX, | VM_REG_GUEST_RDX, | ||||
VM_REG_GUEST_RSI, | VM_REG_GUEST_RSI, | ||||
VM_REG_GUEST_RDI, | VM_REG_GUEST_RDI, | ||||
▲ Show 20 Lines • Show All 583 Lines • ▼ Show 20 Lines | |||||
* vCPU threads invoke this function whenever the vCPU enters the | * vCPU threads invoke this function whenever the vCPU enters the | ||||
* debug server to pause or report an event. vCPU threads wait here | * debug server to pause or report an event. vCPU threads wait here | ||||
* as long as the debug server keeps them suspended. | * as long as the debug server keeps them suspended. | ||||
*/ | */ | ||||
static void | static void | ||||
_gdb_cpu_suspend(int vcpu, bool report_stop) | _gdb_cpu_suspend(int vcpu, bool report_stop) | ||||
{ | { | ||||
if (!gdb_active) | |||||
return; | |||||
debug("$vCPU %d suspending\n", vcpu); | debug("$vCPU %d suspending\n", vcpu); | ||||
CPU_SET(vcpu, &vcpus_waiting); | CPU_SET(vcpu, &vcpus_waiting); | ||||
if (report_stop && CPU_CMP(&vcpus_waiting, &vcpus_suspended) == 0) | if (report_stop && CPU_CMP(&vcpus_waiting, &vcpus_suspended) == 0) | ||||
gdb_finish_suspend_vcpus(); | gdb_finish_suspend_vcpus(); | ||||
while (CPU_ISSET(vcpu, &vcpus_suspended)) | while (CPU_ISSET(vcpu, &vcpus_suspended)) | ||||
pthread_cond_wait(&idle_vcpus, &gdb_lock); | pthread_cond_wait(&idle_vcpus, &gdb_lock); | ||||
CPU_CLR(vcpu, &vcpus_waiting); | CPU_CLR(vcpu, &vcpus_waiting); | ||||
debug("$vCPU %d resuming\n", vcpu); | debug("$vCPU %d resuming\n", vcpu); | ||||
} | } | ||||
/* | /* | ||||
* 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. | ||||
*/ | */ | ||||
void | void | ||||
gdb_cpu_add(int vcpu) | gdb_cpu_add(int vcpu) | ||||
{ | { | ||||
if (!gdb_active) | |||||
return; | |||||
debug("$vCPU %d starting\n", vcpu); | debug("$vCPU %d starting\n", vcpu); | ||||
pthread_mutex_lock(&gdb_lock); | pthread_mutex_lock(&gdb_lock); | ||||
assert(vcpu < guest_ncpus); | assert(vcpu < guest_ncpus); | ||||
CPU_SET(vcpu, &vcpus_active); | CPU_SET(vcpu, &vcpus_active); | ||||
if (!TAILQ_EMPTY(&breakpoints)) { | if (!TAILQ_EMPTY(&breakpoints)) { | ||||
vm_set_capability(ctx, vcpu, VM_CAP_BPT_EXIT, 1); | vm_set_capability(ctx, vcpu, VM_CAP_BPT_EXIT, 1); | ||||
debug("$vCPU %d enabled breakpoint exits\n", vcpu); | debug("$vCPU %d enabled breakpoint exits\n", vcpu); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 65 Lines • ▼ Show 20 Lines | |||||
* Handler for VM_EXITCODE_MTRAP reported when a vCPU single-steps via | * Handler for VM_EXITCODE_MTRAP reported when a vCPU single-steps via | ||||
* the VT-x-specific MTRAP exit. | * the VT-x-specific MTRAP exit. | ||||
*/ | */ | ||||
void | void | ||||
gdb_cpu_mtrap(int vcpu) | gdb_cpu_mtrap(int vcpu) | ||||
{ | { | ||||
struct vcpu_state *vs; | struct vcpu_state *vs; | ||||
if (!gdb_active) | |||||
return; | |||||
debug("$vCPU %d MTRAP\n", vcpu); | debug("$vCPU %d MTRAP\n", vcpu); | ||||
pthread_mutex_lock(&gdb_lock); | pthread_mutex_lock(&gdb_lock); | ||||
vs = &vcpu_state[vcpu]; | vs = &vcpu_state[vcpu]; | ||||
if (vs->stepping) { | if (vs->stepping) { | ||||
vs->stepping = false; | vs->stepping = false; | ||||
vs->stepped = true; | vs->stepped = true; | ||||
vm_set_capability(ctx, vcpu, VM_CAP_MTRAP_EXIT, 0); | vm_set_capability(ctx, vcpu, VM_CAP_MTRAP_EXIT, 0); | ||||
while (vs->stepped) { | while (vs->stepped) { | ||||
Show All 24 Lines | |||||
void | void | ||||
gdb_cpu_breakpoint(int vcpu, struct vm_exit *vmexit) | gdb_cpu_breakpoint(int vcpu, struct vm_exit *vmexit) | ||||
{ | { | ||||
struct breakpoint *bp; | struct breakpoint *bp; | ||||
struct vcpu_state *vs; | struct vcpu_state *vs; | ||||
uint64_t gpa; | uint64_t gpa; | ||||
int error; | int error; | ||||
if (!gdb_active) { | |||||
fprintf(stderr, "vm_loop: unexpected VMEXIT_DEBUG\n"); | |||||
exit(4); | |||||
} | |||||
pthread_mutex_lock(&gdb_lock); | pthread_mutex_lock(&gdb_lock); | ||||
error = guest_vaddr2paddr(vcpu, vmexit->rip, &gpa); | error = guest_vaddr2paddr(vcpu, vmexit->rip, &gpa); | ||||
assert(error == 1); | assert(error == 1); | ||||
bp = find_breakpoint(gpa); | bp = find_breakpoint(gpa); | ||||
if (bp != NULL) { | if (bp != NULL) { | ||||
vs = &vcpu_state[vcpu]; | vs = &vcpu_state[vcpu]; | ||||
assert(vs->stepping == false); | assert(vs->stepping == false); | ||||
assert(vs->stepped == false); | assert(vs->stepped == false); | ||||
▲ Show 20 Lines • Show All 974 Lines • ▼ Show 20 Lines | init_gdb(struct vmctx *_ctx, int sport, bool wait) | ||||
flags = fcntl(s, F_GETFL); | flags = fcntl(s, F_GETFL); | ||||
if (fcntl(s, F_SETFL, flags | O_NONBLOCK) == -1) | if (fcntl(s, F_SETFL, flags | O_NONBLOCK) == -1) | ||||
err(1, "Failed to mark gdb socket non-blocking"); | err(1, "Failed to mark gdb socket non-blocking"); | ||||
#ifndef WITHOUT_CAPSICUM | #ifndef WITHOUT_CAPSICUM | ||||
limit_gdb_socket(s); | limit_gdb_socket(s); | ||||
#endif | #endif | ||||
mevent_add(s, EVF_READ, new_connection, NULL); | mevent_add(s, EVF_READ, new_connection, NULL); | ||||
gdb_active = true; | |||||
} | } |