diff options
Diffstat (limited to 'kvm-all.c')
-rw-r--r-- | kvm-all.c | 173 |
1 files changed, 173 insertions, 0 deletions
@@ -22,6 +22,7 @@ #include "qemu-common.h" #include "sysemu.h" +#include "gdbstub.h" #include "kvm.h" /* KVM uses PAGE_SIZE in it's definition of COALESCED_MMIO_MAX */ @@ -56,6 +57,9 @@ struct KVMState int fd; int vmfd; int coalesced_mmio; +#ifdef KVM_CAP_SET_GUEST_DEBUG + struct kvm_sw_breakpoint_head kvm_sw_breakpoints; +#endif }; static KVMState *kvm_state; @@ -291,6 +295,9 @@ int kvm_init(int smp_cpus) s = qemu_mallocz(sizeof(KVMState)); +#ifdef KVM_CAP_SET_GUEST_DEBUG + TAILQ_INIT(&s->kvm_sw_breakpoints); +#endif for (i = 0; i < ARRAY_SIZE(s->slots); i++) s->slots[i].slot = i; @@ -504,6 +511,16 @@ int kvm_cpu_exec(CPUState *env) break; case KVM_EXIT_DEBUG: dprintf("kvm_exit_debug\n"); +#ifdef KVM_CAP_SET_GUEST_DEBUG + if (kvm_arch_debug(&run->debug.arch)) { + gdb_set_stop_cpu(env); + vm_stop(EXCP_DEBUG); + env->exception_index = EXCP_DEBUG; + return 0; + } + /* re-enter, this exception was guest-internal */ + ret = 1; +#endif /* KVM_CAP_SET_GUEST_DEBUG */ break; default: dprintf("kvm_arch_handle_exit\n"); @@ -656,3 +673,159 @@ int kvm_has_sync_mmu(void) return 0; } + +#ifdef KVM_CAP_SET_GUEST_DEBUG +struct kvm_sw_breakpoint *kvm_find_sw_breakpoint(CPUState *env, + target_ulong pc) +{ + struct kvm_sw_breakpoint *bp; + + TAILQ_FOREACH(bp, &env->kvm_state->kvm_sw_breakpoints, entry) { + if (bp->pc == pc) + return bp; + } + return NULL; +} + +int kvm_sw_breakpoints_active(CPUState *env) +{ + return !TAILQ_EMPTY(&env->kvm_state->kvm_sw_breakpoints); +} + +int kvm_update_guest_debug(CPUState *env, unsigned long reinject_trap) +{ + struct kvm_guest_debug dbg; + + dbg.control = 0; + if (env->singlestep_enabled) + dbg.control = KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_SINGLESTEP; + + kvm_arch_update_guest_debug(env, &dbg); + dbg.control |= reinject_trap; + + return kvm_vcpu_ioctl(env, KVM_SET_GUEST_DEBUG, &dbg); +} + +int kvm_insert_breakpoint(CPUState *current_env, target_ulong addr, + target_ulong len, int type) +{ + struct kvm_sw_breakpoint *bp; + CPUState *env; + int err; + + if (type == GDB_BREAKPOINT_SW) { + bp = kvm_find_sw_breakpoint(current_env, addr); + if (bp) { + bp->use_count++; + return 0; + } + + bp = qemu_malloc(sizeof(struct kvm_sw_breakpoint)); + if (!bp) + return -ENOMEM; + + bp->pc = addr; + bp->use_count = 1; + err = kvm_arch_insert_sw_breakpoint(current_env, bp); + if (err) { + free(bp); + return err; + } + + TAILQ_INSERT_HEAD(¤t_env->kvm_state->kvm_sw_breakpoints, + bp, entry); + } else { + err = kvm_arch_insert_hw_breakpoint(addr, len, type); + if (err) + return err; + } + + for (env = first_cpu; env != NULL; env = env->next_cpu) { + err = kvm_update_guest_debug(env, 0); + if (err) + return err; + } + return 0; +} + +int kvm_remove_breakpoint(CPUState *current_env, target_ulong addr, + target_ulong len, int type) +{ + struct kvm_sw_breakpoint *bp; + CPUState *env; + int err; + + if (type == GDB_BREAKPOINT_SW) { + bp = kvm_find_sw_breakpoint(current_env, addr); + if (!bp) + return -ENOENT; + + if (bp->use_count > 1) { + bp->use_count--; + return 0; + } + + err = kvm_arch_remove_sw_breakpoint(current_env, bp); + if (err) + return err; + + TAILQ_REMOVE(¤t_env->kvm_state->kvm_sw_breakpoints, bp, entry); + qemu_free(bp); + } else { + err = kvm_arch_remove_hw_breakpoint(addr, len, type); + if (err) + return err; + } + + for (env = first_cpu; env != NULL; env = env->next_cpu) { + err = kvm_update_guest_debug(env, 0); + if (err) + return err; + } + return 0; +} + +void kvm_remove_all_breakpoints(CPUState *current_env) +{ + struct kvm_sw_breakpoint *bp, *next; + KVMState *s = current_env->kvm_state; + CPUState *env; + + TAILQ_FOREACH_SAFE(bp, &s->kvm_sw_breakpoints, entry, next) { + if (kvm_arch_remove_sw_breakpoint(current_env, bp) != 0) { + /* Try harder to find a CPU that currently sees the breakpoint. */ + for (env = first_cpu; env != NULL; env = env->next_cpu) { + if (kvm_arch_remove_sw_breakpoint(env, bp) == 0) + break; + } + } + } + kvm_arch_remove_all_hw_breakpoints(); + + for (env = first_cpu; env != NULL; env = env->next_cpu) + kvm_update_guest_debug(env, 0); +} + +#else /* !KVM_CAP_SET_GUEST_DEBUG */ + +int kvm_update_guest_debug(CPUState *env, unsigned long reinject_trap) +{ + return -EINVAL; +} + +int kvm_insert_breakpoint(CPUState *current_env, target_ulong addr, + target_ulong len, int type) +{ + return -EINVAL; +} + +int kvm_remove_breakpoint(CPUState *current_env, target_ulong addr, + target_ulong len, int type) +{ + return -EINVAL; +} + +void kvm_remove_all_breakpoints(CPUState *current_env) +{ +} +#endif /* !KVM_CAP_SET_GUEST_DEBUG */ |