--- kvm-all.c | 422 +++++++++++++++----------------------------- target-i386/kvm.c | 514 ++++++++++++++++++----------------------------------- 2 files changed, 314 insertions(+), 622 deletions(-) diff --git a/kvm-all.c b/kvm-all.c index 6f92874..a364d34 100644 --- a/kvm-all.c +++ b/kvm-all.c @@ -63,6 +63,148 @@ struct KVMState static KVMState *kvm_state; kvm_context_t kvm_context; +#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_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 */ + #ifdef CONFIG_KVM /* KVM uses PAGE_SIZE in it's definition of COALESCED_MMIO_MAX */ @@ -885,24 +1027,6 @@ void kvm_setup_guest_memory(void *start, size_t size) } } -#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; @@ -917,129 +1041,6 @@ int kvm_update_guest_debug(CPUState *env, unsigned long 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 */ #endif /* CONFIG_KVM */ #ifdef USE_KVM @@ -3439,18 +3440,6 @@ int kvm_qemu_init_env(CPUState *cenv) #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; -} - struct kvm_set_guest_debug_data { struct kvm_guest_debug dbg; int err; @@ -3478,133 +3467,6 @@ int kvm_update_guest_debug(CPUState *env, unsigned long reinject_trap) on_vcpu(env, kvm_invoke_set_guest_debug, &data); return data.err; } - -int kvm_sw_breakpoints_active(CPUState *env) -{ - return !TAILQ_EMPTY(&env->kvm_state->kvm_sw_breakpoints); -} - -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; - CPUState *env; - - TAILQ_FOREACH_SAFE(bp, ¤t_env->kvm_state->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 */ /* diff --git a/target-i386/kvm.c b/target-i386/kvm.c index 66da1ba..54fc9dc 100644 --- a/target-i386/kvm.c +++ b/target-i386/kvm.c @@ -35,6 +35,178 @@ #include "libkvm-all.h" #include "libkvm.h" +#ifdef KVM_CAP_SET_GUEST_DEBUG +int kvm_arch_insert_sw_breakpoint(CPUState *env, struct kvm_sw_breakpoint *bp) +{ + const static 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, (uint8_t *)&int3, 1, 1)) + return -EINVAL; + return 0; +} + +int kvm_arch_remove_sw_breakpoint(CPUState *env, struct kvm_sw_breakpoint *bp) +{ + uint8_t int3; + + if (cpu_memory_rw_debug(env, bp->pc, &int3, 1, 0) || int3 != 0xcc || + cpu_memory_rw_debug(env, bp->pc, (uint8_t *)&bp->saved_insn, 1, 1)) + return -EINVAL; + return 0; +} + +static struct { + target_ulong addr; + int len; + int type; +} hw_breakpoint[4]; + +static int nb_hw_breakpoint; + +static int find_hw_breakpoint(target_ulong addr, int len, int type) +{ + int n; + + for (n = 0; n < nb_hw_breakpoint; n++) + if (hw_breakpoint[n].addr == addr && hw_breakpoint[n].type == type && + (hw_breakpoint[n].len == len || len == -1)) + return n; + return -1; +} + +int kvm_arch_insert_hw_breakpoint(target_ulong addr, + target_ulong len, int type) +{ + switch (type) { + case GDB_BREAKPOINT_HW: + len = 1; + break; + case GDB_WATCHPOINT_WRITE: + case GDB_WATCHPOINT_ACCESS: + switch (len) { + case 1: + break; + case 2: + case 4: + case 8: + if (addr & (len - 1)) + return -EINVAL; + break; + default: + return -EINVAL; + } + break; + default: + return -ENOSYS; + } + + if (nb_hw_breakpoint == 4) + return -ENOBUFS; + + if (find_hw_breakpoint(addr, len, type) >= 0) + return -EEXIST; + + hw_breakpoint[nb_hw_breakpoint].addr = addr; + hw_breakpoint[nb_hw_breakpoint].len = len; + hw_breakpoint[nb_hw_breakpoint].type = type; + nb_hw_breakpoint++; + + return 0; +} + +int kvm_arch_remove_hw_breakpoint(target_ulong addr, + target_ulong len, int type) +{ + int n; + + n = find_hw_breakpoint(addr, (type == GDB_BREAKPOINT_HW) ? 1 : len, type); + if (n < 0) + return -ENOENT; + + nb_hw_breakpoint--; + hw_breakpoint[n] = hw_breakpoint[nb_hw_breakpoint]; + + return 0; +} + +void kvm_arch_remove_all_hw_breakpoints(void) +{ + nb_hw_breakpoint = 0; +} + +static CPUWatchpoint hw_watchpoint; + +int kvm_arch_debug(struct kvm_debug_exit_arch *arch_info) +{ + int handle = 0; + int n; + + if (arch_info->exception == 1) { + if (arch_info->dr6 & (1 << 14)) { + if (cpu_single_env->singlestep_enabled) + handle = 1; + } else { + for (n = 0; n < 4; n++) + if (arch_info->dr6 & (1 << n)) + switch ((arch_info->dr7 >> (16 + n*4)) & 0x3) { + case 0x0: + handle = 1; + break; + case 0x1: + handle = 1; + cpu_single_env->watchpoint_hit = &hw_watchpoint; + hw_watchpoint.vaddr = hw_breakpoint[n].addr; + hw_watchpoint.flags = BP_MEM_WRITE; + break; + case 0x3: + handle = 1; + cpu_single_env->watchpoint_hit = &hw_watchpoint; + hw_watchpoint.vaddr = hw_breakpoint[n].addr; + hw_watchpoint.flags = BP_MEM_ACCESS; + break; + } + } + } else if (kvm_find_sw_breakpoint(cpu_single_env, arch_info->pc)) + handle = 1; + + if (!handle) + kvm_update_guest_debug(cpu_single_env, + (arch_info->exception == 1) ? + KVM_GUESTDBG_INJECT_DB : KVM_GUESTDBG_INJECT_BP); + + return handle; +} + +void kvm_arch_update_guest_debug(CPUState *env, struct kvm_guest_debug *dbg) +{ + const uint8_t type_code[] = { + [GDB_BREAKPOINT_HW] = 0x0, + [GDB_WATCHPOINT_WRITE] = 0x1, + [GDB_WATCHPOINT_ACCESS] = 0x3 + }; + const uint8_t len_code[] = { + [1] = 0x0, [2] = 0x1, [4] = 0x3, [8] = 0x2 + }; + int n; + + if (kvm_sw_breakpoints_active(env)) + dbg->control |= KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_USE_SW_BP; + + if (nb_hw_breakpoint > 0) { + dbg->control |= KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_USE_HW_BP; + dbg->arch.debugreg[7] = 0x0600; + for (n = 0; n < nb_hw_breakpoint; n++) { + dbg->arch.debugreg[n] = hw_breakpoint[n].addr; + dbg->arch.debugreg[7] |= (2 << (n * 2)) | + (type_code[hw_breakpoint[n].type] << (16 + n*4)) | + (len_code[hw_breakpoint[n].len] << (18 + n*4)); + } + } +} +#endif /* KVM_CAP_SET_GUEST_DEBUG */ + + #ifdef CONFIG_KVM //#define DEBUG_KVM @@ -802,177 +974,6 @@ int kvm_arch_handle_exit(CPUState *env, struct kvm_run *run) return ret; } -#ifdef KVM_CAP_SET_GUEST_DEBUG -int kvm_arch_insert_sw_breakpoint(CPUState *env, struct kvm_sw_breakpoint *bp) -{ - const static 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, (uint8_t *)&int3, 1, 1)) - return -EINVAL; - return 0; -} - -int kvm_arch_remove_sw_breakpoint(CPUState *env, struct kvm_sw_breakpoint *bp) -{ - uint8_t int3; - - if (cpu_memory_rw_debug(env, bp->pc, &int3, 1, 0) || int3 != 0xcc || - cpu_memory_rw_debug(env, bp->pc, (uint8_t *)&bp->saved_insn, 1, 1)) - return -EINVAL; - return 0; -} - -static struct { - target_ulong addr; - int len; - int type; -} hw_breakpoint[4]; - -static int nb_hw_breakpoint; - -static int find_hw_breakpoint(target_ulong addr, int len, int type) -{ - int n; - - for (n = 0; n < nb_hw_breakpoint; n++) - if (hw_breakpoint[n].addr == addr && hw_breakpoint[n].type == type && - (hw_breakpoint[n].len == len || len == -1)) - return n; - return -1; -} - -int kvm_arch_insert_hw_breakpoint(target_ulong addr, - target_ulong len, int type) -{ - switch (type) { - case GDB_BREAKPOINT_HW: - len = 1; - break; - case GDB_WATCHPOINT_WRITE: - case GDB_WATCHPOINT_ACCESS: - switch (len) { - case 1: - break; - case 2: - case 4: - case 8: - if (addr & (len - 1)) - return -EINVAL; - break; - default: - return -EINVAL; - } - break; - default: - return -ENOSYS; - } - - if (nb_hw_breakpoint == 4) - return -ENOBUFS; - - if (find_hw_breakpoint(addr, len, type) >= 0) - return -EEXIST; - - hw_breakpoint[nb_hw_breakpoint].addr = addr; - hw_breakpoint[nb_hw_breakpoint].len = len; - hw_breakpoint[nb_hw_breakpoint].type = type; - nb_hw_breakpoint++; - - return 0; -} - -int kvm_arch_remove_hw_breakpoint(target_ulong addr, - target_ulong len, int type) -{ - int n; - - n = find_hw_breakpoint(addr, (type == GDB_BREAKPOINT_HW) ? 1 : len, type); - if (n < 0) - return -ENOENT; - - nb_hw_breakpoint--; - hw_breakpoint[n] = hw_breakpoint[nb_hw_breakpoint]; - - return 0; -} - -void kvm_arch_remove_all_hw_breakpoints(void) -{ - nb_hw_breakpoint = 0; -} - -static CPUWatchpoint hw_watchpoint; - -int kvm_arch_debug(struct kvm_debug_exit_arch *arch_info) -{ - int handle = 0; - int n; - - if (arch_info->exception == 1) { - if (arch_info->dr6 & (1 << 14)) { - if (cpu_single_env->singlestep_enabled) - handle = 1; - } else { - for (n = 0; n < 4; n++) - if (arch_info->dr6 & (1 << n)) - switch ((arch_info->dr7 >> (16 + n*4)) & 0x3) { - case 0x0: - handle = 1; - break; - case 0x1: - handle = 1; - cpu_single_env->watchpoint_hit = &hw_watchpoint; - hw_watchpoint.vaddr = hw_breakpoint[n].addr; - hw_watchpoint.flags = BP_MEM_WRITE; - break; - case 0x3: - handle = 1; - cpu_single_env->watchpoint_hit = &hw_watchpoint; - hw_watchpoint.vaddr = hw_breakpoint[n].addr; - hw_watchpoint.flags = BP_MEM_ACCESS; - break; - } - } - } else if (kvm_find_sw_breakpoint(cpu_single_env, arch_info->pc)) - handle = 1; - - if (!handle) - kvm_update_guest_debug(cpu_single_env, - (arch_info->exception == 1) ? - KVM_GUESTDBG_INJECT_DB : KVM_GUESTDBG_INJECT_BP); - - return handle; -} - -void kvm_arch_update_guest_debug(CPUState *env, struct kvm_guest_debug *dbg) -{ - const uint8_t type_code[] = { - [GDB_BREAKPOINT_HW] = 0x0, - [GDB_WATCHPOINT_WRITE] = 0x1, - [GDB_WATCHPOINT_ACCESS] = 0x3 - }; - const uint8_t len_code[] = { - [1] = 0x0, [2] = 0x1, [4] = 0x3, [8] = 0x2 - }; - int n; - - if (kvm_sw_breakpoints_active(env)) - dbg->control |= KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_USE_SW_BP; - - if (nb_hw_breakpoint > 0) { - dbg->control |= KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_USE_HW_BP; - dbg->arch.debugreg[7] = 0x0600; - for (n = 0; n < nb_hw_breakpoint; n++) { - dbg->arch.debugreg[n] = hw_breakpoint[n].addr; - dbg->arch.debugreg[7] |= (2 << (n * 2)) | - (type_code[hw_breakpoint[n].type] << (16 + n*4)) | - (len_code[hw_breakpoint[n].len] << (18 + n*4)); - } - } -} -#endif /* KVM_CAP_SET_GUEST_DEBUG */ - #endif /* CONFIG_KVM */ #ifdef USE_KVM @@ -2301,177 +2302,6 @@ 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) -{ - uint8_t int3; - - if (cpu_memory_rw_debug(env, bp->pc, &int3, 1, 0) || int3 != 0xcc || - cpu_memory_rw_debug(env, bp->pc, (uint8_t *)&bp->saved_insn, 1, 1)) - return -EINVAL; - return 0; -} - -#ifdef KVM_CAP_SET_GUEST_DEBUG -static struct { - target_ulong addr; - int len; - int type; -} hw_breakpoint[4]; - -static int nb_hw_breakpoint; - -static int find_hw_breakpoint(target_ulong addr, int len, int type) -{ - int n; - - for (n = 0; n < nb_hw_breakpoint; n++) - if (hw_breakpoint[n].addr == addr && hw_breakpoint[n].type == type && - (hw_breakpoint[n].len == len || len == -1)) - return n; - return -1; -} - -int kvm_arch_insert_hw_breakpoint(target_ulong addr, - target_ulong len, int type) -{ - switch (type) { - case GDB_BREAKPOINT_HW: - len = 1; - break; - case GDB_WATCHPOINT_WRITE: - case GDB_WATCHPOINT_ACCESS: - switch (len) { - case 1: - break; - case 2: - case 4: - case 8: - if (addr & (len - 1)) - return -EINVAL; - break; - default: - return -EINVAL; - } - break; - default: - return -ENOSYS; - } - - if (nb_hw_breakpoint == 4) - return -ENOBUFS; - - if (find_hw_breakpoint(addr, len, type) >= 0) - return -EEXIST; - - hw_breakpoint[nb_hw_breakpoint].addr = addr; - hw_breakpoint[nb_hw_breakpoint].len = len; - hw_breakpoint[nb_hw_breakpoint].type = type; - nb_hw_breakpoint++; - - return 0; -} - -int kvm_arch_remove_hw_breakpoint(target_ulong addr, - target_ulong len, int type) -{ - int n; - - n = find_hw_breakpoint(addr, (type == GDB_BREAKPOINT_HW) ? 1 : len, type); - if (n < 0) - return -ENOENT; - - nb_hw_breakpoint--; - hw_breakpoint[n] = hw_breakpoint[nb_hw_breakpoint]; - - return 0; -} - -void kvm_arch_remove_all_hw_breakpoints(void) -{ - nb_hw_breakpoint = 0; -} - -static CPUWatchpoint hw_watchpoint; - -int kvm_arch_debug(struct kvm_debug_exit_arch *arch_info) -{ - int handle = 0; - int n; - - if (arch_info->exception == 1) { - if (arch_info->dr6 & (1 << 14)) { - if (cpu_single_env->singlestep_enabled) - handle = 1; - } else { - for (n = 0; n < 4; n++) - if (arch_info->dr6 & (1 << n)) - switch ((arch_info->dr7 >> (16 + n*4)) & 0x3) { - case 0x0: - handle = 1; - break; - case 0x1: - handle = 1; - cpu_single_env->watchpoint_hit = &hw_watchpoint; - hw_watchpoint.vaddr = hw_breakpoint[n].addr; - hw_watchpoint.flags = BP_MEM_WRITE; - break; - case 0x3: - handle = 1; - cpu_single_env->watchpoint_hit = &hw_watchpoint; - hw_watchpoint.vaddr = hw_breakpoint[n].addr; - hw_watchpoint.flags = BP_MEM_ACCESS; - break; - } - } - } else if (kvm_find_sw_breakpoint(cpu_single_env, arch_info->pc)) - handle = 1; - - if (!handle) - kvm_update_guest_debug(cpu_single_env, - (arch_info->exception == 1) ? - KVM_GUESTDBG_INJECT_DB : KVM_GUESTDBG_INJECT_BP); - - return handle; -} - -void kvm_arch_update_guest_debug(CPUState *env, struct kvm_guest_debug *dbg) -{ - const uint8_t type_code[] = { - [GDB_BREAKPOINT_HW] = 0x0, - [GDB_WATCHPOINT_WRITE] = 0x1, - [GDB_WATCHPOINT_ACCESS] = 0x3 - }; - const uint8_t len_code[] = { - [1] = 0x0, [2] = 0x1, [4] = 0x3, [8] = 0x2 - }; - int n; - - if (kvm_sw_breakpoints_active(env)) - dbg->control |= KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_USE_SW_BP; - - if (nb_hw_breakpoint > 0) { - dbg->control |= KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_USE_HW_BP; - dbg->arch.debugreg[7] = 0x0600; - for (n = 0; n < nb_hw_breakpoint; n++) { - dbg->arch.debugreg[n] = hw_breakpoint[n].addr; - dbg->arch.debugreg[7] |= (2 << (n * 2)) | - (type_code[hw_breakpoint[n].type] << (16 + n*4)) | - (len_code[hw_breakpoint[n].len] << (18 + n*4)); - } - } -} -#endif - void kvm_arch_do_ioperm(void *_data) { struct ioperm_data *data = _data; -- 1.6.2.2 -- To unsubscribe from this list: send the line "unsubscribe kvm" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html