Skip to content

Commit

Permalink
KVM: x86: Virtualize debug registers
Browse files Browse the repository at this point in the history
So far KVM only had basic x86 debug register support, once introduced to
realize guest debugging that way. The guest itself was not able to use
those registers.

This patch now adds (almost) full support for guest self-debugging via
hardware registers. It refactors the code, moving generic parts out of
SVM (VMX was already cleaned up by the KVM_SET_GUEST_DEBUG patches), and
it ensures that the registers are properly switched between host and
guest.

This patch also prepares debug register usage by the host. The latter
will (once wired-up by the following patch) allow for hardware
breakpoints/watchpoints in guest code. If this is enabled, the guest
will only see faked debug registers without functionality, but with
content reflecting the guest's modifications.

Tested on Intel only, but SVM /should/ work as well, but who knows...

Known limitations: Trapping on tss switch won't work - most probably on
Intel.

Credits also go to Joerg Roedel - I used his once posted debugging
series as platform for this patch.

Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com>
Signed-off-by: Avi Kivity <avi@redhat.com>
  • Loading branch information
Jan Kiszka authored and Avi Kivity committed Mar 24, 2009
1 parent 55934c0 commit 42dbaa5
Show file tree
Hide file tree
Showing 6 changed files with 193 additions and 96 deletions.
22 changes: 22 additions & 0 deletions arch/x86/include/asm/kvm_host.h
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,19 @@ enum {

#define KVM_NR_MEM_OBJS 40

#define KVM_NR_DB_REGS 4

#define DR6_BD (1 << 13)
#define DR6_BS (1 << 14)
#define DR6_FIXED_1 0xffff0ff0
#define DR6_VOLATILE 0x0000e00f

#define DR7_BP_EN_MASK 0x000000ff
#define DR7_GE (1 << 9)
#define DR7_GD (1 << 13)
#define DR7_FIXED_1 0x00000400
#define DR7_VOLATILE 0xffff23ff

/*
* We don't want allocation failures within the mmu code, so we preallocate
* enough memory for a single page fault in a cache.
Expand Down Expand Up @@ -334,6 +347,15 @@ struct kvm_vcpu_arch {

struct mtrr_state_type mtrr_state;
u32 pat;

int switch_db_regs;
unsigned long host_db[KVM_NR_DB_REGS];
unsigned long host_dr6;
unsigned long host_dr7;
unsigned long db[KVM_NR_DB_REGS];
unsigned long dr6;
unsigned long dr7;
unsigned long eff_db[KVM_NR_DB_REGS];
};

struct kvm_mem_alias {
Expand Down
2 changes: 1 addition & 1 deletion arch/x86/include/asm/vmx.h
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,7 @@ enum vmcs_field {
#define DEBUG_REG_ACCESS_TYPE 0x10 /* 4, direction of access */
#define TYPE_MOV_TO_DR (0 << 4)
#define TYPE_MOV_FROM_DR (1 << 4)
#define DEBUG_REG_ACCESS_REG 0xf00 /* 11:8, general purpose reg. */
#define DEBUG_REG_ACCESS_REG(eq) (((eq) >> 8) & 0xf) /* 11:8, general purpose reg. */


/* segment AR */
Expand Down
6 changes: 0 additions & 6 deletions arch/x86/kvm/kvm_svm.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ static const u32 host_save_user_msrs[] = {
};

#define NR_HOST_SAVE_USER_MSRS ARRAY_SIZE(host_save_user_msrs)
#define NUM_DB_REGS 4

struct kvm_vcpu;

Expand All @@ -29,16 +28,11 @@ struct vcpu_svm {
struct svm_cpu_data *svm_data;
uint64_t asid_generation;

unsigned long db_regs[NUM_DB_REGS];

u64 next_rip;

u64 host_user_msrs[NR_HOST_SAVE_USER_MSRS];
u64 host_gs_base;
unsigned long host_cr2;
unsigned long host_db_regs[NUM_DB_REGS];
unsigned long host_dr6;
unsigned long host_dr7;

u32 *msrpm;
struct vmcb *hsave;
Expand Down
116 changes: 43 additions & 73 deletions arch/x86/kvm/svm.c
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,6 @@ MODULE_LICENSE("GPL");
#define IOPM_ALLOC_ORDER 2
#define MSRPM_ALLOC_ORDER 1

#define DR7_GD_MASK (1 << 13)
#define DR6_BD_MASK (1 << 13)

#define SEG_TYPE_LDT 2
#define SEG_TYPE_BUSY_TSS16 3

Expand Down Expand Up @@ -181,32 +178,6 @@ static inline void kvm_write_cr2(unsigned long val)
asm volatile ("mov %0, %%cr2" :: "r" (val));
}

static inline unsigned long read_dr6(void)
{
unsigned long dr6;

asm volatile ("mov %%dr6, %0" : "=r" (dr6));
return dr6;
}

static inline void write_dr6(unsigned long val)
{
asm volatile ("mov %0, %%dr6" :: "r" (val));
}

static inline unsigned long read_dr7(void)
{
unsigned long dr7;

asm volatile ("mov %%dr7, %0" : "=r" (dr7));
return dr7;
}

static inline void write_dr7(unsigned long val)
{
asm volatile ("mov %0, %%dr7" :: "r" (val));
}

static inline void force_new_asid(struct kvm_vcpu *vcpu)
{
to_svm(vcpu)->asid_generation--;
Expand Down Expand Up @@ -695,7 +666,6 @@ static struct kvm_vcpu *svm_create_vcpu(struct kvm *kvm, unsigned int id)
clear_page(svm->vmcb);
svm->vmcb_pa = page_to_pfn(page) << PAGE_SHIFT;
svm->asid_generation = 0;
memset(svm->db_regs, 0, sizeof(svm->db_regs));
init_vmcb(svm);

fx_init(&svm->vcpu);
Expand Down Expand Up @@ -1035,7 +1005,29 @@ static void new_asid(struct vcpu_svm *svm, struct svm_cpu_data *svm_data)

static unsigned long svm_get_dr(struct kvm_vcpu *vcpu, int dr)
{
unsigned long val = to_svm(vcpu)->db_regs[dr];
struct vcpu_svm *svm = to_svm(vcpu);
unsigned long val;

switch (dr) {
case 0 ... 3:
val = vcpu->arch.db[dr];
break;
case 6:
if (vcpu->guest_debug & KVM_GUESTDBG_USE_HW_BP)
val = vcpu->arch.dr6;
else
val = svm->vmcb->save.dr6;
break;
case 7:
if (vcpu->guest_debug & KVM_GUESTDBG_USE_HW_BP)
val = vcpu->arch.dr7;
else
val = svm->vmcb->save.dr7;
break;
default:
val = 0;
}

KVMTRACE_2D(DR_READ, vcpu, (u32)dr, (u32)val, handler);
return val;
}
Expand All @@ -1045,33 +1037,40 @@ static void svm_set_dr(struct kvm_vcpu *vcpu, int dr, unsigned long value,
{
struct vcpu_svm *svm = to_svm(vcpu);

*exception = 0;
KVMTRACE_2D(DR_WRITE, vcpu, (u32)dr, (u32)value, handler);

if (svm->vmcb->save.dr7 & DR7_GD_MASK) {
svm->vmcb->save.dr7 &= ~DR7_GD_MASK;
svm->vmcb->save.dr6 |= DR6_BD_MASK;
*exception = DB_VECTOR;
return;
}
*exception = 0;

switch (dr) {
case 0 ... 3:
svm->db_regs[dr] = value;
vcpu->arch.db[dr] = value;
if (!(vcpu->guest_debug & KVM_GUESTDBG_USE_HW_BP))
vcpu->arch.eff_db[dr] = value;
return;
case 4 ... 5:
if (vcpu->arch.cr4 & X86_CR4_DE) {
if (vcpu->arch.cr4 & X86_CR4_DE)
*exception = UD_VECTOR;
return;
case 6:
if (value & 0xffffffff00000000ULL) {
*exception = GP_VECTOR;
return;
}
case 7: {
if (value & ~((1ULL << 32) - 1)) {
vcpu->arch.dr6 = (value & DR6_VOLATILE) | DR6_FIXED_1;
return;
case 7:
if (value & 0xffffffff00000000ULL) {
*exception = GP_VECTOR;
return;
}
svm->vmcb->save.dr7 = value;
vcpu->arch.dr7 = (value & DR7_VOLATILE) | DR7_FIXED_1;
if (!(vcpu->guest_debug & KVM_GUESTDBG_USE_HW_BP)) {
svm->vmcb->save.dr7 = vcpu->arch.dr7;
vcpu->arch.switch_db_regs = (value & DR7_BP_EN_MASK);
}
return;
}
default:
/* FIXME: Possible case? */
printk(KERN_DEBUG "%s: unexpected dr %u\n",
__func__, dr);
*exception = UD_VECTOR;
Expand Down Expand Up @@ -2365,22 +2364,6 @@ static int svm_set_tss_addr(struct kvm *kvm, unsigned int addr)
return 0;
}

static void save_db_regs(unsigned long *db_regs)
{
asm volatile ("mov %%dr0, %0" : "=r"(db_regs[0]));
asm volatile ("mov %%dr1, %0" : "=r"(db_regs[1]));
asm volatile ("mov %%dr2, %0" : "=r"(db_regs[2]));
asm volatile ("mov %%dr3, %0" : "=r"(db_regs[3]));
}

static void load_db_regs(unsigned long *db_regs)
{
asm volatile ("mov %0, %%dr0" : : "r"(db_regs[0]));
asm volatile ("mov %0, %%dr1" : : "r"(db_regs[1]));
asm volatile ("mov %0, %%dr2" : : "r"(db_regs[2]));
asm volatile ("mov %0, %%dr3" : : "r"(db_regs[3]));
}

static void svm_flush_tlb(struct kvm_vcpu *vcpu)
{
force_new_asid(vcpu);
Expand Down Expand Up @@ -2439,20 +2422,12 @@ static void svm_vcpu_run(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
gs_selector = kvm_read_gs();
ldt_selector = kvm_read_ldt();
svm->host_cr2 = kvm_read_cr2();
svm->host_dr6 = read_dr6();
svm->host_dr7 = read_dr7();
if (!is_nested(svm))
svm->vmcb->save.cr2 = vcpu->arch.cr2;
/* required for live migration with NPT */
if (npt_enabled)
svm->vmcb->save.cr3 = vcpu->arch.cr3;

if (svm->vmcb->save.dr7 & 0xff) {
write_dr7(0);
save_db_regs(svm->host_db_regs);
load_db_regs(svm->db_regs);
}

clgi();

local_irq_enable();
Expand Down Expand Up @@ -2528,16 +2503,11 @@ static void svm_vcpu_run(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
#endif
);

if ((svm->vmcb->save.dr7 & 0xff))
load_db_regs(svm->host_db_regs);

vcpu->arch.cr2 = svm->vmcb->save.cr2;
vcpu->arch.regs[VCPU_REGS_RAX] = svm->vmcb->save.rax;
vcpu->arch.regs[VCPU_REGS_RSP] = svm->vmcb->save.rsp;
vcpu->arch.regs[VCPU_REGS_RIP] = svm->vmcb->save.rip;

write_dr6(svm->host_dr6);
write_dr7(svm->host_dr7);
kvm_write_cr2(svm->host_cr2);

kvm_load_fs(fs_selector);
Expand Down
Loading

0 comments on commit 42dbaa5

Please sign in to comment.