Commit 6f43ed01 authored by Nadav Amit's avatar Nadav Amit Committed by Paolo Bonzini

KVM: x86: DR6/7.RTM cannot be written

Haswell and newer Intel CPUs have support for RTM, and in that case DR6.RTM is
not fixed to 1 and DR7.RTM is not fixed to zero. That is not the case in the
current KVM implementation. This bug is apparent only if the MOV-DR instruction
is emulated or the host also debugs the guest.

This patch is a partial fix which enables DR6.RTM and DR7.RTM to be cleared and
set respectively. It also sets DR6.RTM upon every debug exception. Obviously,
it is not a complete fix, as debugging of RTM is still unsupported.
Signed-off-by: default avatarNadav Amit <namit@cs.technion.ac.il>
Signed-off-by: default avatarPaolo Bonzini <pbonzini@redhat.com>
parent 9a2a05b9
...@@ -152,14 +152,16 @@ enum { ...@@ -152,14 +152,16 @@ enum {
#define DR6_BD (1 << 13) #define DR6_BD (1 << 13)
#define DR6_BS (1 << 14) #define DR6_BS (1 << 14)
#define DR6_FIXED_1 0xffff0ff0 #define DR6_RTM (1 << 16)
#define DR6_VOLATILE 0x0000e00f #define DR6_FIXED_1 0xfffe0ff0
#define DR6_INIT 0xffff0ff0
#define DR6_VOLATILE 0x0001e00f
#define DR7_BP_EN_MASK 0x000000ff #define DR7_BP_EN_MASK 0x000000ff
#define DR7_GE (1 << 9) #define DR7_GE (1 << 9)
#define DR7_GD (1 << 13) #define DR7_GD (1 << 13)
#define DR7_FIXED_1 0x00000400 #define DR7_FIXED_1 0x00000400
#define DR7_VOLATILE 0xffff23ff #define DR7_VOLATILE 0xffff2bff
/* apic attention bits */ /* apic attention bits */
#define KVM_APIC_CHECK_VAPIC 0 #define KVM_APIC_CHECK_VAPIC 0
......
...@@ -95,4 +95,12 @@ static inline bool guest_cpuid_has_gbpages(struct kvm_vcpu *vcpu) ...@@ -95,4 +95,12 @@ static inline bool guest_cpuid_has_gbpages(struct kvm_vcpu *vcpu)
best = kvm_find_cpuid_entry(vcpu, 0x80000001, 0); best = kvm_find_cpuid_entry(vcpu, 0x80000001, 0);
return best && (best->edx & bit(X86_FEATURE_GBPAGES)); return best && (best->edx & bit(X86_FEATURE_GBPAGES));
} }
static inline bool guest_cpuid_has_rtm(struct kvm_vcpu *vcpu)
{
struct kvm_cpuid_entry2 *best;
best = kvm_find_cpuid_entry(vcpu, 7, 0);
return best && (best->ebx & bit(X86_FEATURE_RTM));
}
#endif #endif
...@@ -4892,7 +4892,7 @@ static int handle_exception(struct kvm_vcpu *vcpu) ...@@ -4892,7 +4892,7 @@ static int handle_exception(struct kvm_vcpu *vcpu)
if (!(vcpu->guest_debug & if (!(vcpu->guest_debug &
(KVM_GUESTDBG_SINGLESTEP | KVM_GUESTDBG_USE_HW_BP))) { (KVM_GUESTDBG_SINGLESTEP | KVM_GUESTDBG_USE_HW_BP))) {
vcpu->arch.dr6 &= ~15; vcpu->arch.dr6 &= ~15;
vcpu->arch.dr6 |= dr6; vcpu->arch.dr6 |= dr6 | DR6_RTM;
if (!(dr6 & ~DR6_RESERVED)) /* icebp */ if (!(dr6 & ~DR6_RESERVED)) /* icebp */
skip_emulated_instruction(vcpu); skip_emulated_instruction(vcpu);
...@@ -5151,7 +5151,7 @@ static int handle_dr(struct kvm_vcpu *vcpu) ...@@ -5151,7 +5151,7 @@ static int handle_dr(struct kvm_vcpu *vcpu)
return 0; return 0;
} else { } else {
vcpu->arch.dr7 &= ~DR7_GD; vcpu->arch.dr7 &= ~DR7_GD;
vcpu->arch.dr6 |= DR6_BD; vcpu->arch.dr6 |= DR6_BD | DR6_RTM;
vmcs_writel(GUEST_DR7, vcpu->arch.dr7); vmcs_writel(GUEST_DR7, vcpu->arch.dr7);
kvm_queue_exception(vcpu, DB_VECTOR); kvm_queue_exception(vcpu, DB_VECTOR);
return 1; return 1;
......
...@@ -759,6 +759,15 @@ static void kvm_update_dr7(struct kvm_vcpu *vcpu) ...@@ -759,6 +759,15 @@ static void kvm_update_dr7(struct kvm_vcpu *vcpu)
vcpu->arch.switch_db_regs |= KVM_DEBUGREG_BP_ENABLED; vcpu->arch.switch_db_regs |= KVM_DEBUGREG_BP_ENABLED;
} }
static u64 kvm_dr6_fixed(struct kvm_vcpu *vcpu)
{
u64 fixed = DR6_FIXED_1;
if (!guest_cpuid_has_rtm(vcpu))
fixed |= DR6_RTM;
return fixed;
}
static int __kvm_set_dr(struct kvm_vcpu *vcpu, int dr, unsigned long val) static int __kvm_set_dr(struct kvm_vcpu *vcpu, int dr, unsigned long val)
{ {
switch (dr) { switch (dr) {
...@@ -774,7 +783,7 @@ static int __kvm_set_dr(struct kvm_vcpu *vcpu, int dr, unsigned long val) ...@@ -774,7 +783,7 @@ static int __kvm_set_dr(struct kvm_vcpu *vcpu, int dr, unsigned long val)
case 6: case 6:
if (val & 0xffffffff00000000ULL) if (val & 0xffffffff00000000ULL)
return -1; /* #GP */ return -1; /* #GP */
vcpu->arch.dr6 = (val & DR6_VOLATILE) | DR6_FIXED_1; vcpu->arch.dr6 = (val & DR6_VOLATILE) | kvm_dr6_fixed(vcpu);
kvm_update_dr6(vcpu); kvm_update_dr6(vcpu);
break; break;
case 5: case 5:
...@@ -5115,7 +5124,8 @@ static void kvm_vcpu_check_singlestep(struct kvm_vcpu *vcpu, unsigned long rflag ...@@ -5115,7 +5124,8 @@ static void kvm_vcpu_check_singlestep(struct kvm_vcpu *vcpu, unsigned long rflag
*/ */
if (unlikely(rflags & X86_EFLAGS_TF)) { if (unlikely(rflags & X86_EFLAGS_TF)) {
if (vcpu->guest_debug & KVM_GUESTDBG_SINGLESTEP) { if (vcpu->guest_debug & KVM_GUESTDBG_SINGLESTEP) {
kvm_run->debug.arch.dr6 = DR6_BS | DR6_FIXED_1; kvm_run->debug.arch.dr6 = DR6_BS | DR6_FIXED_1 |
DR6_RTM;
kvm_run->debug.arch.pc = vcpu->arch.singlestep_rip; kvm_run->debug.arch.pc = vcpu->arch.singlestep_rip;
kvm_run->debug.arch.exception = DB_VECTOR; kvm_run->debug.arch.exception = DB_VECTOR;
kvm_run->exit_reason = KVM_EXIT_DEBUG; kvm_run->exit_reason = KVM_EXIT_DEBUG;
...@@ -5128,7 +5138,7 @@ static void kvm_vcpu_check_singlestep(struct kvm_vcpu *vcpu, unsigned long rflag ...@@ -5128,7 +5138,7 @@ static void kvm_vcpu_check_singlestep(struct kvm_vcpu *vcpu, unsigned long rflag
* cleared by the processor". * cleared by the processor".
*/ */
vcpu->arch.dr6 &= ~15; vcpu->arch.dr6 &= ~15;
vcpu->arch.dr6 |= DR6_BS; vcpu->arch.dr6 |= DR6_BS | DR6_RTM;
kvm_queue_exception(vcpu, DB_VECTOR); kvm_queue_exception(vcpu, DB_VECTOR);
} }
} }
...@@ -5147,7 +5157,7 @@ static bool kvm_vcpu_check_breakpoint(struct kvm_vcpu *vcpu, int *r) ...@@ -5147,7 +5157,7 @@ static bool kvm_vcpu_check_breakpoint(struct kvm_vcpu *vcpu, int *r)
vcpu->arch.eff_db); vcpu->arch.eff_db);
if (dr6 != 0) { if (dr6 != 0) {
kvm_run->debug.arch.dr6 = dr6 | DR6_FIXED_1; kvm_run->debug.arch.dr6 = dr6 | DR6_FIXED_1 | DR6_RTM;
kvm_run->debug.arch.pc = kvm_rip_read(vcpu) + kvm_run->debug.arch.pc = kvm_rip_read(vcpu) +
get_segment_base(vcpu, VCPU_SREG_CS); get_segment_base(vcpu, VCPU_SREG_CS);
...@@ -5166,7 +5176,7 @@ static bool kvm_vcpu_check_breakpoint(struct kvm_vcpu *vcpu, int *r) ...@@ -5166,7 +5176,7 @@ static bool kvm_vcpu_check_breakpoint(struct kvm_vcpu *vcpu, int *r)
if (dr6 != 0) { if (dr6 != 0) {
vcpu->arch.dr6 &= ~15; vcpu->arch.dr6 &= ~15;
vcpu->arch.dr6 |= dr6; vcpu->arch.dr6 |= dr6 | DR6_RTM;
kvm_queue_exception(vcpu, DB_VECTOR); kvm_queue_exception(vcpu, DB_VECTOR);
*r = EMULATE_DONE; *r = EMULATE_DONE;
return true; return true;
...@@ -6866,7 +6876,7 @@ void kvm_vcpu_reset(struct kvm_vcpu *vcpu) ...@@ -6866,7 +6876,7 @@ void kvm_vcpu_reset(struct kvm_vcpu *vcpu)
kvm_clear_exception_queue(vcpu); kvm_clear_exception_queue(vcpu);
memset(vcpu->arch.db, 0, sizeof(vcpu->arch.db)); memset(vcpu->arch.db, 0, sizeof(vcpu->arch.db));
vcpu->arch.dr6 = DR6_FIXED_1; vcpu->arch.dr6 = DR6_INIT;
kvm_update_dr6(vcpu); kvm_update_dr6(vcpu);
vcpu->arch.dr7 = DR7_FIXED_1; vcpu->arch.dr7 = DR7_FIXED_1;
kvm_update_dr7(vcpu); kvm_update_dr7(vcpu);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment