KVM: Virtualize x86 debug registers
PATCH 1/1 KVM: Virtualize x86 debug registers
[ Depends on KVM_SET_GUEST_DEBUG series! ]
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), it ensures that the registers are properly switched between host and guest, and it already lays the foundation for future debug register usage by the host (in case hardware breakpoints/watchpoints in guest code are requested).
Tested on Intel only, but SVM /should/ work. Nevertheless, this topic is a mine field, easy to get wrong with all its corner cases. So if you have nice hw-debugging scenarios that fit into a KVM guest, please give this hell!
Known limitations: Trapping on tss switch won't work - most probably on Intel.
Credits also go to Joerg Roedel - I used his debugging series as platform for this patch.
--- arch/x86/kvm/kvm_svm.h | 6 -- arch/x86/kvm/svm.c | 125 +++++++++++++++++---------------------------- arch/x86/kvm/vmx.c | 114 +++++++++++++++++++++++++++++++++++------ arch/x86/kvm/vmx.h | 2 arch/x86/kvm/x86.c | 32 +++++++++++ include/asm-x86/kvm_host.h | 22 +++++++ 6 files changed, 200 insertions(+), 101 deletions(-)
arch/x86/kvm/kvm_svm.h +0 -6
@@ -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 4struct kvm_vcpu ;
@@ -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; };
arch/x86/kvm/svm.c +48 -77
200 lines not on this page · raw message
arch/x86/kvm/vmx.c +98 -16
174 lines not on this page · raw message
arch/x86/kvm/vmx.h +1 -1
@@ -304,7 +304,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 */
arch/x86/kvm/x86.c +31 -1
@@ -2721,7 +2721,7 @@ static int __vcpu_run(struct kvm_vcpu *vpr_debug ("vcpu %d received sipi with vector # %x\n" , vcpu->vcpu_id, vcpu->arch.sipi_vector);kvm_lapic_reset (vcpu); - r = kvm_x86_ops->vcpu_reset (vcpu); + r =kvm_arch_vcpu_reset (vcpu);if (r)return r; vcpu->arch.mp_state =KVM_MP_STATE_RUNNABLE ;
@@ -2812,9 +2812,34 @@ again:if (test_and_clear_bit (KVM_REQ_TLB_FLUSH , &vcpu->requests)) kvm_x86_ops->tlb_flush (vcpu); +get_debugreg (vcpu->arch.host_dr6,6 ); +get_debugreg (vcpu->arch.host_dr7,7 ); +if (unlikely (vcpu->arch.switch_db_regs)) { +get_debugreg (vcpu->arch.host_db[0 ],0 ); +get_debugreg (vcpu->arch.host_db[1 ],1 ); +get_debugreg (vcpu->arch.host_db[2 ],2 ); +get_debugreg (vcpu->arch.host_db[3 ],3 ); + +set_debugreg (0 ,7 ); +set_debugreg (vcpu->arch.eff_db[0 ],0 ); +set_debugreg (vcpu->arch.eff_db[1 ],1 ); +set_debugreg (vcpu->arch.eff_db[2 ],2 ); +set_debugreg (vcpu->arch.eff_db[3 ],3 ); + } +KVMTRACE_0D (VMENTRY , vcpu, entryexit); kvm_x86_ops->run (vcpu, kvm_run); +if (unlikely (vcpu->arch.switch_db_regs)) { +set_debugreg (0 ,7 ); +set_debugreg (vcpu->arch.host_db[0 ],0 ); +set_debugreg (vcpu->arch.host_db[1 ],1 ); +set_debugreg (vcpu->arch.host_db[2 ],2 ); +set_debugreg (vcpu->arch.host_db[3 ],3 ); + } +set_debugreg (vcpu->arch.host_dr6,6 ); +set_debugreg (vcpu->arch.host_dr7,7 ); + vcpu->guest_mode =0 ;local_irq_enable ();
@@ -3782,6 +3807,11 @@ void kvm_arch_vcpu_destroy(struct kvm_vcint kvm_arch_vcpu_reset (struct kvm_vcpu *vcpu) { + vcpu->arch.switch_db_regs =0 ; +memset (vcpu->arch.db,0 ,sizeof (vcpu->arch.db)); + vcpu->arch.dr6 =DR6_FIXED_1 ; + vcpu->arch.dr7 =DR7_FIXED_1 ; +return kvm_x86_ops->vcpu_reset (vcpu); }
include/asm-x86/kvm_host.h +22 -0
@@ -124,6 +124,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.
@@ -284,6 +297,15 @@ struct kvm_vcpu_arch {struct page *time_page;bool nmi_pending; + +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 {