mirror of
https://github.com/Motorhead1991/qemu.git
synced 2025-08-25 11:01:52 -06:00
Guest debugging support for KVM (Jan Kiszka)
This is a backport of the guest debugging support for the KVM accelerator that is now part of the KVM tree. It implements the reworked KVM kernel API for guest debugging (KVM_CAP_SET_GUEST_DEBUG) which is not yet part of any mainline kernel but will probably be 2.6.30 stuff. So far supported is x86, but PPC is expected to catch up soon. Core features are: - unlimited soft-breakpoints via code patching - hardware-assisted x86 breakpoints and watchpoints Changes in this version: - use generic hook cpu_synchronize_state to transfer registers between user space and kvm - push kvm_sw_breakpoints into KVMState Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com> Signed-off-by: Anthony Liguori <aliguori@us.ibm.com> git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@6825 c046a42c-6fe2-441c-8c8c-71466251a162
This commit is contained in:
parent
eda578e559
commit
e22a25c936
6 changed files with 446 additions and 10 deletions
173
kvm-all.c
173
kvm-all.c
|
@ -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 */
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue