This patch switches both libkvm as well as the qemu pieces over to the
new guest debug interface. This means, instead of relying on hardware
breakpoints, breakpoint traps are patched into the guest code at the
required spots. Breakpoint management is done inside the qemu-kvm,
transparent to gdbstub and also avoiding that the gdb frontend takes
over. This allows for running debuggers inside the guest while guest
debugging it active, because the host can cleanly tell apart host- and
guest-originated breakpoint events.
Yet improvable are x86 corner cases when using single-step ("forgotten"
debug flags on the guest's stack). And, of courese, the yet empty
non-x86 helper functions have to be populated...
Signed-off-by: Jan Kiszka <[EMAIL PROTECTED]>
---
libkvm/kvm-common.h | 2
libkvm/libkvm-x86.c | 20 ++++++
libkvm/libkvm.c | 11 +--
libkvm/libkvm.h | 14 +++-
qemu/exec.c | 15 ++--
qemu/qemu-kvm-ia64.c | 17 +++++
qemu/qemu-kvm-powerpc.c | 17 +++++
qemu/qemu-kvm-x86.c | 32 +++++++++
qemu/qemu-kvm.c | 154 +++++++++++++++++++++++++++++++++++++++++-------
qemu/qemu-kvm.h | 27 +++++++-
10 files changed, 268 insertions(+), 41 deletions(-)
Index: b/libkvm/kvm-common.h
===================================================================
--- a/libkvm/kvm-common.h
+++ b/libkvm/kvm-common.h
@@ -85,4 +85,6 @@ int handle_io_window(kvm_context_t kvm);
int handle_debug(kvm_context_t kvm, int vcpu);
int try_push_interrupts(kvm_context_t kvm);
+int handle_debug(kvm_context_t kvm, int vcpu);
+
#endif
Index: b/libkvm/libkvm-x86.c
===================================================================
--- a/libkvm/libkvm-x86.c
+++ b/libkvm/libkvm-x86.c
@@ -661,3 +661,23 @@ int kvm_disable_tpr_access_reporting(kvm
}
#endif
+
+int handle_debug(kvm_context_t kvm, int vcpu)
+{
+#ifdef KVM_CAP_SET_GUEST_DEBUG
+ struct kvm_run *run = kvm->run[vcpu];
+ unsigned long watchpoint = 0;
+ int break_type;
+
+ if (run->debug.arch.exception == 1) {
+ /* TODO: fully process run->debug.arch */
+ break_type = KVM_GDB_BREAKPOINT_HW;
+ } else
+ break_type = KVM_GDB_BREAKPOINT_SW;
+
+ return kvm->callbacks->debug(kvm->opaque, vcpu, break_type,
+ run->debug.pc, watchpoint);
+#else
+ return -ENOSYS;
+#endif
+}
Index: b/libkvm/libkvm.c
===================================================================
--- a/libkvm/libkvm.c
+++ b/libkvm/libkvm.c
@@ -738,11 +738,6 @@ static int handle_io(kvm_context_t kvm,
return 0;
}
-int handle_debug(kvm_context_t kvm, int vcpu)
-{
- return kvm->callbacks->debug(kvm->opaque, vcpu);
-}
-
int kvm_get_regs(kvm_context_t kvm, int vcpu, struct kvm_regs *regs)
{
return ioctl(kvm->vcpu_fd[vcpu], KVM_GET_REGS, regs);
@@ -945,10 +940,12 @@ int kvm_inject_irq(kvm_context_t kvm, in
return ioctl(kvm->vcpu_fd[vcpu], KVM_INTERRUPT, &intr);
}
-int kvm_guest_debug(kvm_context_t kvm, int vcpu, struct kvm_debug_guest *dbg)
+#ifdef KVM_CAP_SET_GUEST_DEBUG
+int kvm_set_guest_debug(kvm_context_t kvm, int vcpu, struct kvm_guest_debug
*dbg)
{
- return ioctl(kvm->vcpu_fd[vcpu], KVM_DEBUG_GUEST, dbg);
+ return ioctl(kvm->vcpu_fd[vcpu], KVM_SET_GUEST_DEBUG, dbg);
}
+#endif
int kvm_set_signal_mask(kvm_context_t kvm, int vcpu, const sigset_t *sigset)
{
Index: b/qemu/exec.c
===================================================================
--- a/qemu/exec.c
+++ b/qemu/exec.c
@@ -1157,6 +1157,9 @@ int cpu_breakpoint_insert(CPUState *env,
#if defined(TARGET_HAS_ICE)
int i;
+ if (kvm_enabled())
+ return kvm_insert_breakpoint(env, pc, len, type);
+
for(i = 0; i < env->nb_breakpoints; i++) {
if (env->breakpoints[i] == pc)
return 0;
@@ -1166,9 +1169,6 @@ int cpu_breakpoint_insert(CPUState *env,
return -ENOBUFS;
env->breakpoints[env->nb_breakpoints++] = pc;
- if (kvm_enabled())
- kvm_update_debugger(env);
-
breakpoint_invalidate(env, pc);
return 0;
#else
@@ -1182,6 +1182,10 @@ int cpu_breakpoint_remove(CPUState *env,
{
#if defined(TARGET_HAS_ICE)
int i;
+
+ if (kvm_enabled())
+ return kvm_remove_breakpoint(env, pc, len, type);
+
for(i = 0; i < env->nb_breakpoints; i++) {
if (env->breakpoints[i] == pc)
goto found;
@@ -1192,9 +1196,6 @@ int cpu_breakpoint_remove(CPUState *env,
if (i < env->nb_breakpoints)
env->breakpoints[i] = env->breakpoints[env->nb_breakpoints];
- if (kvm_enabled())
- kvm_update_debugger(env);
-
breakpoint_invalidate(env, pc);
return 0;
#else
@@ -1214,7 +1215,7 @@ void cpu_single_step(CPUState *env, int
tb_flush(env);
}
if (kvm_enabled())
- kvm_update_debugger(env);
+ kvm_update_guest_debug(env, KVM_GDB_BREAKPOINT_NONE);
#endif
}
Index: b/qemu/qemu-kvm.c
===================================================================
--- a/qemu/qemu-kvm.c
+++ b/qemu/qemu-kvm.c
@@ -60,6 +60,20 @@ static int io_thread_sigfd = -1;
static int kvm_debug_stop_requested;
+struct kvm_sw_breakpoint *first_sw_breakpoint;
+
+static struct kvm_sw_breakpoint *kvm_find_sw_breakpoint(target_ulong pc)
+{
+ struct kvm_sw_breakpoint *bp = first_sw_breakpoint;
+
+ while (bp) {
+ if (bp->pc == pc)
+ break;
+ bp = bp->next;
+ }
+ return bp;
+}
+
static inline unsigned long kvm_get_thread_id(void)
{
return syscall(SYS_gettid);
@@ -546,13 +560,28 @@ int kvm_main_loop(void)
return 0;
}
-static int kvm_debug(void *opaque, int vcpu)
+static int kvm_debug(void *opaque, int vcpu, int break_type,
+ uint64_t pc, uint64_t watchpoint_addr)
{
- CPUState *env = cpu_single_env;
+ int handle = 0;
- kvm_debug_stop_requested = 1;
- vcpu_info[vcpu].stopped = 1;
- return 1;
+ switch (break_type) {
+ case KVM_GDB_BREAKPOINT_SW:
+ if (kvm_find_sw_breakpoint(pc))
+ handle = 1;
+ break;
+ case KVM_GDB_BREAKPOINT_HW:
+ if (cpu_single_env->singlestep_enabled)
+ handle = 1;
+ break;
+ }
+ if (handle) {
+ kvm_debug_stop_requested = 1;
+ vcpu_info[vcpu].stopped = 1;
+ } else
+ kvm_update_guest_debug(cpu_single_env, break_type);
+
+ return handle;
}
static int kvm_inb(void *opaque, uint16_t addr, uint8_t *data)
@@ -767,28 +796,109 @@ int kvm_qemu_init_env(CPUState *cenv)
return kvm_arch_qemu_init_env(cenv);
}
-int kvm_update_debugger(CPUState *env)
+int kvm_update_guest_debug(CPUState *env, int reinject_trap)
{
- struct kvm_debug_guest dbg;
- int i, r;
+#ifdef KVM_CAP_SET_GUEST_DEBUG
+ struct kvm_guest_debug dbg;
+ int err;
- dbg.enabled = 0;
- if (env->nb_breakpoints || env->singlestep_enabled) {
- dbg.enabled = 1;
- for (i = 0; i < 4 && i < env->nb_breakpoints; ++i) {
- dbg.breakpoints[i].enabled = 1;
- dbg.breakpoints[i].address = env->breakpoints[i];
- }
- dbg.singlestep = env->singlestep_enabled;
+ dbg.control = 0;
+ if (env->singlestep_enabled)
+ dbg.control = KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_SINGLESTEP;
+
+ kvm_arch_update_guest_debug(env, &dbg, reinject_trap);
+
+ assert(!cpu_single_env || cpu_single_env == env);
+
+ if (!cpu_single_env && vm_running) {
+ pause_vcpu_thread(env->cpu_index);
+ err = kvm_set_guest_debug(kvm_context, env->cpu_index, &dbg);
+ resume_vcpu_thread(env->cpu_index);
+ } else
+ err = kvm_set_guest_debug(kvm_context, env->cpu_index, &dbg);
+ return err;
+#else
+ return -EINVAL;
+#endif
+}
+
+int kvm_insert_breakpoint(CPUState *env, target_ulong pc,
+ target_ulong len, int type)
+{
+#ifdef KVM_CAP_SET_GUEST_DEBUG
+ struct kvm_sw_breakpoint *bp;
+ int err;
+
+ if (type != GDB_BREAKPOINT_SW)
+ return -ENOSYS;
+
+ bp = kvm_find_sw_breakpoint(pc);
+ if (bp) {
+ bp->usecount++;
+ return 0;
}
- if (vm_running)
- pause_all_threads();
- r = kvm_guest_debug(kvm_context, env->cpu_index, &dbg);
- if (vm_running)
- resume_all_threads();
- return r;
+
+ bp = malloc(sizeof(struct kvm_sw_breakpoint));
+ if (!bp)
+ return -ENOMEM;
+
+ bp->pc = pc;
+ bp->usecount = 1;
+ bp->prev = NULL;
+ bp->next = first_sw_breakpoint;
+ err = kvm_arch_insert_sw_breakpoint(env, bp);
+ if (err) {
+ free(bp);
+ return err;
+ }
+
+ first_sw_breakpoint = bp;
+ if (bp->next)
+ bp->next->prev = bp;
+
+ return kvm_update_guest_debug(env, KVM_GDB_BREAKPOINT_NONE);
+#else
+ return -EINVAL;
+#endif
}
+int kvm_remove_breakpoint(CPUState *env, target_ulong pc,
+ target_ulong len, int type)
+{
+#ifdef KVM_CAP_SET_GUEST_DEBUG
+ struct kvm_sw_breakpoint *bp;
+ int err;
+
+ if (type != GDB_BREAKPOINT_SW)
+ return -ENOSYS;
+
+ bp = kvm_find_sw_breakpoint(pc);
+ if (!bp)
+ return -ENOENT;
+
+ if (bp->usecount > 1) {
+ bp->usecount--;
+ return 0;
+ }
+
+ err = kvm_arch_remove_sw_breakpoint(env, bp);
+ if (err)
+ return err;
+
+ if (bp->prev)
+ bp->prev->next = bp->next;
+ else
+ first_sw_breakpoint = bp->next;
+ if (bp->next)
+ bp->next->prev = bp->prev;
+
+ free(bp);
+
+ return kvm_update_guest_debug(env, KVM_GDB_BREAKPOINT_NONE);
+#else
+ return -EINVAL;
+#endif
+}
/*
* dirty pages logging
Index: b/libkvm/libkvm.h
===================================================================
--- a/libkvm/libkvm.h
+++ b/libkvm/libkvm.h
@@ -25,6 +25,13 @@ int kvm_get_msrs(kvm_context_t, int vcpu
int kvm_set_msrs(kvm_context_t, int vcpu, struct kvm_msr_entry *msrs, int n);
#endif
+#define KVM_GDB_BREAKPOINT_NONE (-1)
+#define KVM_GDB_BREAKPOINT_SW 0
+#define KVM_GDB_BREAKPOINT_HW 1
+#define KVM_GDB_WATCHPOINT_WRITE 2
+#define KVM_GDB_WATCHPOINT_READ 3
+#define KVM_GDB_WATCHPOINT_ACCESS 4
+
/*!
* \brief KVM callbacks structure
*
@@ -51,7 +58,8 @@ struct kvm_callbacks {
/// generic memory writes to unmapped memory (For MMIO devices)
int (*mmio_write)(void *opaque, uint64_t addr, uint8_t *data,
int len);
- int (*debug)(void *opaque, int vcpu);
+ int (*debug)(void *opaque, int vcpu, int break_type, uint64_t pc,
+ uint64_t watchpoint);
/*!
* \brief Called when the VCPU issues an 'hlt' instruction.
*
@@ -337,7 +345,9 @@ static inline int kvm_reset_mpstate(kvm_
*/
int kvm_inject_irq(kvm_context_t kvm, int vcpu, unsigned irq);
-int kvm_guest_debug(kvm_context_t, int vcpu, struct kvm_debug_guest *dbg);
+#ifdef KVM_CAP_SET_GUEST_DEBUG
+int kvm_set_guest_debug(kvm_context_t, int vcpu, struct kvm_guest_debug *dbg);
+#endif
#if defined(__i386__) || defined(__x86_64__)
/*!
Index: b/qemu/qemu-kvm.h
===================================================================
--- a/qemu/qemu-kvm.h
+++ b/qemu/qemu-kvm.h
@@ -12,8 +12,6 @@
#include <signal.h>
-#include <signal.h>
-
int kvm_main_loop(void);
int kvm_qemu_init(void);
int kvm_qemu_create_context(void);
@@ -25,7 +23,11 @@ void kvm_save_registers(CPUState *env);
void kvm_load_mpstate(CPUState *env);
void kvm_save_mpstate(CPUState *env);
int kvm_cpu_exec(CPUState *env);
-int kvm_update_debugger(CPUState *env);
+int kvm_insert_breakpoint(CPUState *env, target_ulong pc,
+ target_ulong len, int type);
+int kvm_remove_breakpoint(CPUState *env, target_ulong pc,
+ target_ulong len, int type);
+int kvm_update_guest_debug(CPUState *env, int reinject_trap);
int kvm_qemu_init_env(CPUState *env);
int kvm_qemu_check_extension(int ext);
void kvm_apic_init(CPUState *env);
@@ -61,6 +63,25 @@ int kvm_arch_try_push_interrupts(void *o
void kvm_arch_update_regs_for_sipi(CPUState *env);
void kvm_arch_cpu_reset(CPUState *env);
+struct kvm_guest_debug;
+
+struct kvm_sw_breakpoint {
+ target_ulong pc;
+ target_ulong saved_insn;
+ int usecount;
+ struct kvm_sw_breakpoint *prev;
+ struct kvm_sw_breakpoint *next;
+};
+
+extern struct kvm_sw_breakpoint *first_sw_breakpoint;
+
+int kvm_arch_insert_sw_breakpoint(CPUState *env,
+ struct kvm_sw_breakpoint *bp);
+int kvm_arch_remove_sw_breakpoint(CPUState *env,
+ struct kvm_sw_breakpoint *bp);
+void kvm_arch_update_guest_debug(CPUState *env, struct kvm_guest_debug *dbg,
+ int reinject_trap);
+
CPUState *qemu_kvm_cpu_env(int index);
void qemu_kvm_aio_wait_start(void);
Index: b/qemu/qemu-kvm-ia64.c
===================================================================
--- a/qemu/qemu-kvm-ia64.c
+++ b/qemu/qemu-kvm-ia64.c
@@ -65,3 +65,20 @@ void kvm_arch_update_regs_for_sipi(CPUSt
void kvm_arch_cpu_reset(CPUState *env)
{
}
+
+int kvm_arch_insert_sw_breakpoint(CPUState *env,
+ struct kvm_sw_breakpoint *bp)
+{
+ return -EINVAL;
+}
+
+int kvm_arch_remove_sw_breakpoint(CPUState *env,
+ struct kvm_sw_breakpoint *bp)
+{
+ return -EINVAL;
+}
+
+void kvm_arch_update_guest_debug(CPUState *env, struct kvm_guest_debug *dbg,
+ int reinject_type)
+{
+}
Index: b/qemu/qemu-kvm-powerpc.c
===================================================================
--- a/qemu/qemu-kvm-powerpc.c
+++ b/qemu/qemu-kvm-powerpc.c
@@ -217,3 +217,20 @@ int handle_powerpc_dcr_write(int vcpu, u
void kvm_arch_cpu_reset(CPUState *env)
{
}
+
+int kvm_arch_insert_sw_breakpoint(CPUState *env,
+ struct kvm_sw_breakpoint *bp)
+{
+ return -EINVAL;
+}
+
+int kvm_arch_remove_sw_breakpoint(CPUState *env,
+ struct kvm_sw_breakpoint *bp)
+{
+ return -EINVAL;
+}
+
+void kvm_arch_update_guest_debug(CPUState *env, struct kvm_guest_debug *dbg,
+ int reinject_type)
+{
+}
Index: b/qemu/qemu-kvm-x86.c
===================================================================
--- a/qemu/qemu-kvm-x86.c
+++ b/qemu/qemu-kvm-x86.c
@@ -687,3 +687,35 @@ void kvm_arch_cpu_reset(CPUState *env)
}
}
}
+
+int kvm_arch_insert_sw_breakpoint(CPUState *env,
+ struct kvm_sw_breakpoint *bp)
+{
+ uint8_t int3 = 0xcc;
+
+ if (cpu_memory_rw_debug(env, bp->pc, (uint8_t *)&bp->saved_insn, 1, 0) ||
+ cpu_memory_rw_debug(env, bp->pc, &int3, 1, 1))
+ return -EINVAL;
+ return 0;
+}
+
+int kvm_arch_remove_sw_breakpoint(CPUState *env,
+ struct kvm_sw_breakpoint *bp)
+{
+ if (cpu_memory_rw_debug(env, bp->pc, (uint8_t *)&bp->saved_insn, 1, 1))
+ return -EINVAL;
+ return 0;
+}
+
+void kvm_arch_update_guest_debug(CPUState *env, struct kvm_guest_debug *dbg,
+ int reinject_type)
+{
+#ifdef KVM_CAP_SET_GUEST_DEBUG
+ if (first_sw_breakpoint)
+ dbg->control |= KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_USE_SW_BP;
+ if (reinject_type == KVM_GDB_BREAKPOINT_HW)
+ dbg->control |= KVM_GUESTDBG_INJECT_DB;
+ else if (reinject_type == KVM_GDB_BREAKPOINT_SW)
+ dbg->control |= KVM_GUESTDBG_INJECT_BP;
+#endif
+}
--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at http://vger.kernel.org/majordomo-info.html