~vcs-imports/qemu/git

« back to all changes in this revision

Viewing changes to target-i386/kvm.c

  • Committer: aliguori
  • Date: 2009-03-12 20:12:48 UTC
  • Revision ID: git-v1:e22a25c9361c44995c9241c24df0e1e2c47a56c8
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

Show diffs side-by-side

added added

removed removed

Lines of Context:
22
22
#include "sysemu.h"
23
23
#include "kvm.h"
24
24
#include "cpu.h"
 
25
#include "gdbstub.h"
25
26
 
26
27
//#define DEBUG_KVM
27
28
 
683
684
 
684
685
    return ret;
685
686
}
 
687
 
 
688
#ifdef KVM_CAP_SET_GUEST_DEBUG
 
689
static int kvm_patch_opcode_byte(CPUState *env, target_ulong addr, uint8_t val)
 
690
{
 
691
    target_phys_addr_t phys_page_addr;
 
692
    unsigned long pd;
 
693
    uint8_t *ptr;
 
694
 
 
695
    phys_page_addr = cpu_get_phys_page_debug(env, addr & TARGET_PAGE_MASK);
 
696
    if (phys_page_addr == -1)
 
697
        return -EINVAL;
 
698
 
 
699
    pd = cpu_get_physical_page_desc(phys_page_addr);
 
700
    if ((pd & ~TARGET_PAGE_MASK) != IO_MEM_RAM &&
 
701
        (pd & ~TARGET_PAGE_MASK) != IO_MEM_ROM && !(pd & IO_MEM_ROMD))
 
702
        return -EINVAL;
 
703
 
 
704
    ptr = phys_ram_base + (pd & TARGET_PAGE_MASK)
 
705
                        + (addr & ~TARGET_PAGE_MASK);
 
706
    *ptr = val;
 
707
    return 0;
 
708
}
 
709
 
 
710
int kvm_arch_insert_sw_breakpoint(CPUState *env, struct kvm_sw_breakpoint *bp)
 
711
{
 
712
    if (cpu_memory_rw_debug(env, bp->pc, (uint8_t *)&bp->saved_insn, 1, 0) ||
 
713
        kvm_patch_opcode_byte(env, bp->pc, 0xcc))
 
714
        return -EINVAL;
 
715
    return 0;
 
716
}
 
717
 
 
718
int kvm_arch_remove_sw_breakpoint(CPUState *env, struct kvm_sw_breakpoint *bp)
 
719
{
 
720
    uint8_t int3;
 
721
 
 
722
    if (cpu_memory_rw_debug(env, bp->pc, &int3, 1, 0) || int3 != 0xcc ||
 
723
        kvm_patch_opcode_byte(env, bp->pc, bp->saved_insn))
 
724
        return -EINVAL;
 
725
    return 0;
 
726
}
 
727
 
 
728
static struct {
 
729
    target_ulong addr;
 
730
    int len;
 
731
    int type;
 
732
} hw_breakpoint[4];
 
733
 
 
734
static int nb_hw_breakpoint;
 
735
 
 
736
static int find_hw_breakpoint(target_ulong addr, int len, int type)
 
737
{
 
738
    int n;
 
739
 
 
740
    for (n = 0; n < nb_hw_breakpoint; n++)
 
741
        if (hw_breakpoint[n].addr == addr && hw_breakpoint[n].type == type &&
 
742
            (hw_breakpoint[n].len == len || len == -1))
 
743
            return n;
 
744
    return -1;
 
745
}
 
746
 
 
747
int kvm_arch_insert_hw_breakpoint(target_ulong addr,
 
748
                                  target_ulong len, int type)
 
749
{
 
750
    switch (type) {
 
751
    case GDB_BREAKPOINT_HW:
 
752
        len = 1;
 
753
        break;
 
754
    case GDB_WATCHPOINT_WRITE:
 
755
    case GDB_WATCHPOINT_ACCESS:
 
756
        switch (len) {
 
757
        case 1:
 
758
            break;
 
759
        case 2:
 
760
        case 4:
 
761
        case 8:
 
762
            if (addr & (len - 1))
 
763
                return -EINVAL;
 
764
            break;
 
765
        default:
 
766
            return -EINVAL;
 
767
        }
 
768
        break;
 
769
    default:
 
770
        return -ENOSYS;
 
771
    }
 
772
 
 
773
    if (nb_hw_breakpoint == 4)
 
774
        return -ENOBUFS;
 
775
 
 
776
    if (find_hw_breakpoint(addr, len, type) >= 0)
 
777
        return -EEXIST;
 
778
 
 
779
    hw_breakpoint[nb_hw_breakpoint].addr = addr;
 
780
    hw_breakpoint[nb_hw_breakpoint].len = len;
 
781
    hw_breakpoint[nb_hw_breakpoint].type = type;
 
782
    nb_hw_breakpoint++;
 
783
 
 
784
    return 0;
 
785
}
 
786
 
 
787
int kvm_arch_remove_hw_breakpoint(target_ulong addr,
 
788
                                  target_ulong len, int type)
 
789
{
 
790
    int n;
 
791
 
 
792
    n = find_hw_breakpoint(addr, (type == GDB_BREAKPOINT_HW) ? 1 : len, type);
 
793
    if (n < 0)
 
794
        return -ENOENT;
 
795
 
 
796
    nb_hw_breakpoint--;
 
797
    hw_breakpoint[n] = hw_breakpoint[nb_hw_breakpoint];
 
798
 
 
799
    return 0;
 
800
}
 
801
 
 
802
void kvm_arch_remove_all_hw_breakpoints(void)
 
803
{
 
804
    nb_hw_breakpoint = 0;
 
805
}
 
806
 
 
807
static CPUWatchpoint hw_watchpoint;
 
808
 
 
809
int kvm_arch_debug(struct kvm_debug_exit_arch *arch_info)
 
810
{
 
811
    int handle = 0;
 
812
    int n;
 
813
 
 
814
    if (arch_info->exception == 1) {
 
815
        if (arch_info->dr6 & (1 << 14)) {
 
816
            if (cpu_single_env->singlestep_enabled)
 
817
                handle = 1;
 
818
        } else {
 
819
            for (n = 0; n < 4; n++)
 
820
                if (arch_info->dr6 & (1 << n))
 
821
                    switch ((arch_info->dr7 >> (16 + n*4)) & 0x3) {
 
822
                    case 0x0:
 
823
                        handle = 1;
 
824
                        break;
 
825
                    case 0x1:
 
826
                        handle = 1;
 
827
                        cpu_single_env->watchpoint_hit = &hw_watchpoint;
 
828
                        hw_watchpoint.vaddr = hw_breakpoint[n].addr;
 
829
                        hw_watchpoint.flags = BP_MEM_WRITE;
 
830
                        break;
 
831
                    case 0x3:
 
832
                        handle = 1;
 
833
                        cpu_single_env->watchpoint_hit = &hw_watchpoint;
 
834
                        hw_watchpoint.vaddr = hw_breakpoint[n].addr;
 
835
                        hw_watchpoint.flags = BP_MEM_ACCESS;
 
836
                        break;
 
837
                    }
 
838
        }
 
839
    } else if (kvm_find_sw_breakpoint(cpu_single_env, arch_info->pc))
 
840
        handle = 1;
 
841
 
 
842
    if (!handle)
 
843
        kvm_update_guest_debug(cpu_single_env,
 
844
                        (arch_info->exception == 1) ?
 
845
                        KVM_GUESTDBG_INJECT_DB : KVM_GUESTDBG_INJECT_BP);
 
846
 
 
847
    return handle;
 
848
}
 
849
 
 
850
void kvm_arch_update_guest_debug(CPUState *env, struct kvm_guest_debug *dbg)
 
851
{
 
852
    const uint8_t type_code[] = {
 
853
        [GDB_BREAKPOINT_HW] = 0x0,
 
854
        [GDB_WATCHPOINT_WRITE] = 0x1,
 
855
        [GDB_WATCHPOINT_ACCESS] = 0x3
 
856
    };
 
857
    const uint8_t len_code[] = {
 
858
        [1] = 0x0, [2] = 0x1, [4] = 0x3, [8] = 0x2
 
859
    };
 
860
    int n;
 
861
 
 
862
    if (kvm_sw_breakpoints_active(env))
 
863
        dbg->control |= KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_USE_SW_BP;
 
864
 
 
865
    if (nb_hw_breakpoint > 0) {
 
866
        dbg->control |= KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_USE_HW_BP;
 
867
        dbg->arch.debugreg[7] = 0x0600;
 
868
        for (n = 0; n < nb_hw_breakpoint; n++) {
 
869
            dbg->arch.debugreg[n] = hw_breakpoint[n].addr;
 
870
            dbg->arch.debugreg[7] |= (2 << (n * 2)) |
 
871
                (type_code[hw_breakpoint[n].type] << (16 + n*4)) |
 
872
                (len_code[hw_breakpoint[n].len] << (18 + n*4));
 
873
        }
 
874
    }
 
875
}
 
876
#endif /* KVM_CAP_SET_GUEST_DEBUG */