From: Jan K. <jan...@we...> - 2008-05-16 16:02:04
|
[ Should apply against vanilla QEMU, but not ATM due to ongoing constructions in gdbstub. ] This patch prepares the QEMU cpu_watchpoint/breakpoint API to allow us hooking in with KVM and doing guest debugging differently (maybe QEMUAccel should provide appropriate callbacks for this, too). But it also allows to extend QEMU's debugging features one day, specifically /wrt different watchpoint types. --- qemu/cpu-all.h | 14 ++++++++++---- qemu/exec.c | 30 ++++++++++++++++++++---------- qemu/gdbstub.c | 50 +++++++++++++++++++++++++++++--------------------- 3 files changed, 59 insertions(+), 35 deletions(-) Index: b/qemu/cpu-all.h =================================================================== --- a/qemu/cpu-all.h +++ b/qemu/cpu-all.h @@ -758,10 +758,16 @@ extern int code_copy_enabled; void cpu_interrupt(CPUState *s, int mask); void cpu_reset_interrupt(CPUState *env, int mask); -int cpu_watchpoint_insert(CPUState *env, target_ulong addr); -int cpu_watchpoint_remove(CPUState *env, target_ulong addr); -int cpu_breakpoint_insert(CPUState *env, target_ulong pc); -int cpu_breakpoint_remove(CPUState *env, target_ulong pc); +#define GDB_BREAKPOINT_SW 0 +#define GDB_BREAKPOINT_HW 1 +#define GDB_WATCHPOINT_WRITE 2 +#define GDB_WATCHPOINT_READ 3 +#define GDB_WATCHPOINT_ACCESS 4 + +int cpu_watchpoint_insert(CPUState *env, target_ulong addr, target_ulong len, int type); +int cpu_watchpoint_remove(CPUState *env, target_ulong addr, target_ulong len, int type); +int cpu_breakpoint_insert(CPUState *env, target_ulong pc, target_ulong len, int type); +int cpu_breakpoint_remove(CPUState *env, target_ulong pc, target_ulong len, int type); void cpu_single_step(CPUState *env, int enabled); void cpu_reset(CPUState *s); Index: b/qemu/exec.c =================================================================== --- a/qemu/exec.c +++ b/qemu/exec.c @@ -1104,16 +1104,20 @@ static void breakpoint_invalidate(CPUSta #endif /* Add a watchpoint. */ -int cpu_watchpoint_insert(CPUState *env, target_ulong addr) +int cpu_watchpoint_insert(CPUState *env, target_ulong addr, target_ulong len, + int type) { int i; + if (type != GDB_WATCHPOINT_WRITE) + return -ENOSYS; + for (i = 0; i < env->nb_watchpoints; i++) { if (addr == env->watchpoint[i].vaddr) return 0; } if (env->nb_watchpoints >= MAX_WATCHPOINTS) - return -1; + return -ENOBUFS; i = env->nb_watchpoints++; env->watchpoint[i].vaddr = addr; @@ -1126,10 +1130,14 @@ int cpu_watchpoint_insert(CPUState *env } /* Remove a watchpoint. */ -int cpu_watchpoint_remove(CPUState *env, target_ulong addr) +int cpu_watchpoint_remove(CPUState *env, target_ulong addr, target_ulong len, + int type) { int i; + if (type != GDB_WATCHPOINT_WRITE) + return -ENOSYS; + for (i = 0; i < env->nb_watchpoints; i++) { if (addr == env->watchpoint[i].vaddr) { env->nb_watchpoints--; @@ -1138,12 +1146,13 @@ int cpu_watchpoint_remove(CPUState *env, return 0; } } - return -1; + return -ENOENT; } /* add a breakpoint. EXCP_DEBUG is returned by the CPU loop if a breakpoint is reached */ -int cpu_breakpoint_insert(CPUState *env, target_ulong pc) +int cpu_breakpoint_insert(CPUState *env, target_ulong pc, target_ulong len, + int type) { #if defined(TARGET_HAS_ICE) int i; @@ -1154,7 +1163,7 @@ int cpu_breakpoint_insert(CPUState *env, } if (env->nb_breakpoints >= MAX_BREAKPOINTS) - return -1; + return -ENOBUFS; env->breakpoints[env->nb_breakpoints++] = pc; if (kvm_enabled()) @@ -1163,12 +1172,13 @@ int cpu_breakpoint_insert(CPUState *env, breakpoint_invalidate(env, pc); return 0; #else - return -1; + return -ENOSYS; #endif } /* remove a breakpoint */ -int cpu_breakpoint_remove(CPUState *env, target_ulong pc) +int cpu_breakpoint_remove(CPUState *env, target_ulong pc, target_ulong len, + int type) { #if defined(TARGET_HAS_ICE) int i; @@ -1176,7 +1186,7 @@ int cpu_breakpoint_remove(CPUState *env, if (env->breakpoints[i] == pc) goto found; } - return -1; + return -ENOENT; found: env->nb_breakpoints--; if (i < env->nb_breakpoints) @@ -1188,7 +1198,7 @@ int cpu_breakpoint_remove(CPUState *env, breakpoint_invalidate(env, pc); return 0; #else - return -1; + return -ENOSYS; #endif } Index: b/qemu/gdbstub.c =================================================================== --- a/qemu/gdbstub.c +++ b/qemu/gdbstub.c @@ -882,7 +882,7 @@ static void cpu_gdb_write_registers(CPUS static int gdb_handle_packet(GDBState *s, CPUState *env, const char *line_buf) { const char *p; - int ch, reg_size, type; + int ch, reg_size, type, res; char buf[4096]; uint8_t mem_buf[4096]; uint32_t *registers; @@ -1017,21 +1017,20 @@ static int gdb_handle_packet(GDBState *s if (*p == ',') p++; len = strtoull(p, (char **)&p, 16); - if (type == 0 || type == 1) { - if (cpu_breakpoint_insert(env, addr) < 0) - goto breakpoint_error; - put_packet(s, "OK"); + switch (type) { + case GDB_BREAKPOINT_SW ... GDB_BREAKPOINT_HW: + res = cpu_breakpoint_insert(env, addr, len, type); + break; #ifndef CONFIG_USER_ONLY - } else if (type == 2) { - if (cpu_watchpoint_insert(env, addr) < 0) - goto breakpoint_error; - put_packet(s, "OK"); + case GDB_WATCHPOINT_WRITE ... GDB_WATCHPOINT_ACCESS: + res = cpu_watchpoint_insert(env, addr, len, type); + break; #endif - } else { - breakpoint_error: - put_packet(s, "E22"); + default: + res = -ENOSYS; + break; } - break; + goto answer_bp_packet; case 'z': type = strtoul(p, (char **)&p, 16); if (*p == ',') @@ -1040,17 +1039,26 @@ static int gdb_handle_packet(GDBState *s if (*p == ',') p++; len = strtoull(p, (char **)&p, 16); - if (type == 0 || type == 1) { - cpu_breakpoint_remove(env, addr); - put_packet(s, "OK"); + switch (type) { + case GDB_BREAKPOINT_SW ... GDB_BREAKPOINT_HW: + res = cpu_breakpoint_remove(env, addr, len, type); + break; #ifndef CONFIG_USER_ONLY - } else if (type == 2) { - cpu_watchpoint_remove(env, addr); - put_packet(s, "OK"); + case GDB_WATCHPOINT_WRITE ... GDB_WATCHPOINT_ACCESS: + res = cpu_watchpoint_remove(env, addr, len, type); + break; #endif - } else { - goto breakpoint_error; + default: + res = -ENOSYS; + break; } + answer_bp_packet: + if (res >= 0) + put_packet(s, "OK"); + else if (res == -ENOSYS) + put_packet(s, ""); + else + put_packet(s, "E22"); break; #ifdef CONFIG_LINUX_USER case 'q': |
From: Jan K. <jan...@we...> - 2008-05-16 16:02:21
|
In order to allow the gdbstub of QEMU to push (soft) breakpoint handling completely into the gdb frontend, this patch enables guest exits also for #BP exceptions - in case guest debugging was turned on. Along this enhancement, this patch also fixes the flag manipulation for the singlestep mode. --- arch/x86/kvm/vmx.c | 38 +++++++++++++++----------------------- 1 file changed, 15 insertions(+), 23 deletions(-) Index: b/arch/x86/kvm/vmx.c =================================================================== --- a/arch/x86/kvm/vmx.c +++ b/arch/x86/kvm/vmx.c @@ -461,7 +461,7 @@ static void update_exception_bitmap(stru if (!vcpu->fpu_active) eb |= 1u << NM_VECTOR; if (vcpu->guest_debug.enabled) - eb |= 1u << 1; + eb |= (1u << 1) | (1u << 3); if (vcpu->arch.rmode.active) eb = ~0; if (vm_need_ept()) @@ -949,6 +949,7 @@ static int set_guest_debug(struct kvm_vc { unsigned long dr7 = 0x400; int old_singlestep; + unsigned long flags; old_singlestep = vcpu->guest_debug.singlestep; @@ -969,13 +970,12 @@ static int set_guest_debug(struct kvm_vc } else vcpu->guest_debug.singlestep = 0; - if (old_singlestep && !vcpu->guest_debug.singlestep) { - unsigned long flags; - - flags = vmcs_readl(GUEST_RFLAGS); + flags = vmcs_readl(GUEST_RFLAGS); + if (vcpu->guest_debug.singlestep) + flags |= X86_EFLAGS_TF | X86_EFLAGS_RF; + else if (old_singlestep && !vcpu->guest_debug.singlestep) flags &= ~(X86_EFLAGS_TF | X86_EFLAGS_RF); - vmcs_writel(GUEST_RFLAGS, flags); - } + vmcs_writel(GUEST_RFLAGS, flags); update_exception_bitmap(vcpu); vmcs_writel(GUEST_DR7, dr7); @@ -2192,14 +2192,6 @@ static void kvm_guest_debug_pre(struct k set_debugreg(dbg->bp[1], 1); set_debugreg(dbg->bp[2], 2); set_debugreg(dbg->bp[3], 3); - - if (dbg->singlestep) { - unsigned long flags; - - flags = vmcs_readl(GUEST_RFLAGS); - flags |= X86_EFLAGS_TF | X86_EFLAGS_RF; - vmcs_writel(GUEST_RFLAGS, flags); - } } static int handle_rmode_exception(struct kvm_vcpu *vcpu, @@ -2221,7 +2213,7 @@ static int handle_rmode_exception(struct static int handle_exception(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run) { struct vcpu_vmx *vmx = to_vmx(vcpu); - u32 intr_info, error_code; + u32 intr_info, ex_no, error_code; unsigned long cr2, rip; u32 vect_info; enum emulation_result er; @@ -2279,15 +2271,15 @@ static int handle_exception(struct kvm_v return 1; } - if ((intr_info & (INTR_INFO_INTR_TYPE_MASK | INTR_INFO_VECTOR_MASK)) == - (INTR_TYPE_EXCEPTION | 1)) { + ex_no = intr_info & INTR_INFO_VECTOR_MASK; + if (ex_no == 1 || ex_no == 3) { kvm_run->exit_reason = KVM_EXIT_DEBUG; - kvm_run->debug.arch.exception = 1; - return 0; + kvm_run->debug.arch.exception = ex_no; + } else { + kvm_run->exit_reason = KVM_EXIT_EXCEPTION; + kvm_run->ex.exception = ex_no; + kvm_run->ex.error_code = error_code; } - kvm_run->exit_reason = KVM_EXIT_EXCEPTION; - kvm_run->ex.exception = intr_info & INTR_INFO_VECTOR_MASK; - kvm_run->ex.error_code = error_code; return 0; } |
From: Jan K. <jan...@we...> - 2008-05-16 16:02:29
|
This adds an arch field to kvm_run.debug, the payload that is returned to user space on KVM_EXIT_DEBUG guest exits. For x86, this field is now supposed to report the precise debug exception (#DB or #BP) and the current state of the debug registers (the latter is not yet implemented). --- arch/x86/kvm/vmx.c | 1 + include/asm-x86/kvm.h | 5 +++++ include/linux/kvm.h | 1 + 3 files changed, 7 insertions(+) Index: b/arch/x86/kvm/vmx.c =================================================================== --- a/arch/x86/kvm/vmx.c +++ b/arch/x86/kvm/vmx.c @@ -2282,6 +2282,7 @@ static int handle_exception(struct kvm_v if ((intr_info & (INTR_INFO_INTR_TYPE_MASK | INTR_INFO_VECTOR_MASK)) == (INTR_TYPE_EXCEPTION | 1)) { kvm_run->exit_reason = KVM_EXIT_DEBUG; + kvm_run->debug.arch.exception = 1; return 0; } kvm_run->exit_reason = KVM_EXIT_EXCEPTION; Index: b/include/asm-x86/kvm.h =================================================================== --- a/include/asm-x86/kvm.h +++ b/include/asm-x86/kvm.h @@ -230,4 +230,9 @@ struct kvm_pit_state { #define KVM_TRC_APIC_ACCESS (KVM_TRC_HANDLER + 0x14) #define KVM_TRC_TDP_FAULT (KVM_TRC_HANDLER + 0x15) +struct kvm_debug_exit_arch { + __u32 exception; + __u64 dr[8]; +}; + #endif Index: b/include/linux/kvm.h =================================================================== --- a/include/linux/kvm.h +++ b/include/linux/kvm.h @@ -125,6 +125,7 @@ struct kvm_run { __u64 data_offset; /* relative to kvm_run start */ } io; struct { + struct kvm_debug_exit_arch arch; } debug; /* KVM_EXIT_MMIO */ struct { |
From: Jan K. <jan...@we...> - 2008-05-16 16:03:33
|
With this patch applied, kvm is able to ignore breakpoint requests of an attached gdb frontend so that the latter is motivated to insert soft breakpoints into the guest code. All we need to do for this is to catch and forward #BP exceptions which are now provided by the kernel module. Along this, the patch makes vm_stop-on-breakpoint start to work. Limitations: - only takes care of x86 so far - gdbstub state tracking is broken (artificially incrementing nb_breakpoints won't fly, as we'll have no chance to decrement it), we need an enhanced interface to the stub --- libkvm/kvm-common.h | 2 ++ libkvm/libkvm-x86.c | 16 ++++++++++++++++ libkvm/libkvm.c | 5 ----- libkvm/libkvm.h | 8 +++++++- qemu/exec.c | 19 +++++++++++++------ qemu/qemu-kvm.c | 22 +++++++++++++--------- 6 files changed, 51 insertions(+), 21 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,19 @@ int kvm_disable_tpr_access_reporting(kvm } #endif + +int handle_debug(kvm_context_t kvm, int vcpu) +{ + 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, + watchpoint); +} 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); Index: b/qemu/exec.c =================================================================== --- a/qemu/exec.c +++ b/qemu/exec.c @@ -1157,6 +1157,12 @@ int cpu_breakpoint_insert(CPUState *env, #if defined(TARGET_HAS_ICE) int i; + if (kvm_enabled()) { + env->nb_breakpoints++; + kvm_update_debugger(env); + return -ENOSYS; + } + for(i = 0; i < env->nb_breakpoints; i++) { if (env->breakpoints[i] == pc) return 0; @@ -1166,9 +1172,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 +1185,13 @@ int cpu_breakpoint_remove(CPUState *env, { #if defined(TARGET_HAS_ICE) int i; + + if (kvm_enabled()) { + env->nb_breakpoints--; + kvm_update_debugger(env); + return -ENOSYS; + } + for(i = 0; i < env->nb_breakpoints; i++) { if (env->breakpoints[i] == pc) goto found; @@ -1192,9 +1202,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 Index: b/qemu/qemu-kvm.c =================================================================== --- a/qemu/qemu-kvm.c +++ b/qemu/qemu-kvm.c @@ -58,6 +58,8 @@ pthread_t io_thread; static int io_thread_fd = -1; static int io_thread_sigfd = -1; +static int kvm_debug_stop_requested; + static inline unsigned long kvm_get_thread_id(void) { return syscall(SYS_gettid); @@ -517,6 +519,10 @@ int kvm_main_loop(void) qemu_system_powerdown(); else if (qemu_reset_requested()) qemu_kvm_system_reset(); + else if (kvm_debug_stop_requested) { + kvm_debug_stop_requested = 0; + vm_stop(EXCP_DEBUG); + } } pause_all_threads(); @@ -525,11 +531,12 @@ 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 watchpoint_addr) { - CPUState *env = cpu_single_env; - - env->exception_index = EXCP_DEBUG; + /* TODO: process break_type */ + kvm_debug_stop_requested = 1; + vcpu_info[vcpu].stopped = 1; return 1; } @@ -748,15 +755,12 @@ int kvm_qemu_init_env(CPUState *cenv) int kvm_update_debugger(CPUState *env) { struct kvm_debug_guest dbg; - int i, r; + int r; 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]; - } + memset(dbg.breakpoints, 0, sizeof(dbg.breakpoints)); dbg.singlestep = env->singlestep_enabled; } if (vm_running) Index: b/libkvm/libkvm.h =================================================================== --- a/libkvm/libkvm.h +++ b/libkvm/libkvm.h @@ -25,6 +25,12 @@ 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_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 +57,7 @@ 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 watchpoint); /*! * \brief Called when the VCPU issues an 'hlt' instruction. * |