diff mbox

[1/6] KVM: x86: DR7.GD should be cleared upon any #DB exception

Message ID 1412099359-5316-2-git-send-email-namit@cs.technion.ac.il (mailing list archive)
State New, archived
Headers show

Commit Message

Nadav Amit Sept. 30, 2014, 5:49 p.m. UTC
Intel SDM 17.2.4 (Debug Control Register (DR7)) says: "The processor clears the
GD flag upon entering to the debug exception handler." This sentence may be
misunderstood as if it happens only on #DB due to debug-register protection,
but it happens regardless to the cause of the #DB.

Fix the behavior.

Signed-off-by: Nadav Amit <namit@cs.technion.ac.il>
---
 arch/x86/kvm/vmx.c | 2 --
 arch/x86/kvm/x86.c | 6 ++++++
 2 files changed, 6 insertions(+), 2 deletions(-)

Comments

Radim Krčmář Oct. 1, 2014, 3:24 p.m. UTC | #1
2014-09-30 20:49+0300, Nadav Amit:
> Intel SDM 17.2.4 (Debug Control Register (DR7)) says: "The processor clears the
> GD flag upon entering to the debug exception handler." This sentence may be
> misunderstood as if it happens only on #DB due to debug-register protection,
> but it happens regardless to the cause of the #DB.

All real hardware behaves that way?

Intel has another sentence after that

  [...], to allow the handler access to the debug registers.

I suppose that the "the" is important, but I haven't verified it.[1]
Clearing GD on every #DB would also make the stated purpose[2] harder to
achieve without adding any benefit;  it seems like a bug for Intel.


---
1: AMD [13.1.1.4 Debug-Control Register (DR7)] uses a similar wording

     General-Detect Enable (GD)—Bit 13. Software sets this bit to 1 to
     cause a debug exception to occur when an attempt is made to execute
     a MOV DRn instruction to any debug register (DR0–DR7). This bit is
     cleared to 0 by the processor when the #DB handler is entered,
     allowing the handler to read and write the DRn registers. The #DB
     exception occurs before executing the instruction, and DR6[BD] is
     set by the processor. Software debuggers can use this bit to
     prevent the currently-executing program from interfering with the
     debug operation.

2: Last sentence of [1] and also this from Intel
     This condition is provided to support in-circuit emulators.

     When the emulator needs to access the debug registers, emulator
     software can set the GD flag to prevent interference from the
     program currently executing on the processor.
--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Nadav Amit Oct. 1, 2014, 6:22 p.m. UTC | #2
On Oct 1, 2014, at 6:24 PM, Radim Kr?má? <rkrcmar@redhat.com> wrote:

> 2014-09-30 20:49+0300, Nadav Amit:
>> Intel SDM 17.2.4 (Debug Control Register (DR7)) says: "The processor clears the
>> GD flag upon entering to the debug exception handler." This sentence may be
>> misunderstood as if it happens only on #DB due to debug-register protection,
>> but it happens regardless to the cause of the #DB.
> 
> All real hardware behaves that way?
I have no way of knowing.

I know Intel’s phrasing is misleading, so I verified this behaviour in two ways:
1. I changed KVM not to trap #DB. I then changed kvm-unit-tests/debug.c to set DR7.GD prior to the watchpoint test, and printed once I entered the handler, before any DR was accessed by the handler.
The result: we entered the handler once (afterwards I printed DR7 and saw GD is indeed clear). If #DB due to watchpoint did not clear GD, we would enter the handler twice.

2. I looked at bochs: https://github.com/larsr/bochs-svn/blob/master/cpu/exception.cc :

  if (vector == BX_DB_EXCEPTION) {
    // Commit debug events to DR6: preserve DR5.BS and DR6.BD values,
    // only software can clear them
    BX_CPU_THIS_PTR dr6.val32 = (BX_CPU_THIS_PTR dr6.val32 & 0xffff6ff0) |
                          (BX_CPU_THIS_PTR debug_trap & 0x0000e00f);

    // clear GD flag in the DR7 prior entering debug exception handler
    BX_CPU_THIS_PTR dr7.set_GD(0);
  }

> 
> Intel has another sentence after that
> 
>  [...], to allow the handler access to the debug registers.
> 
> I suppose that the "the" is important, but I haven't verified it.[1]
> Clearing GD on every #DB would also make the stated purpose[2] harder to
> achieve without adding any benefit;  it seems like a bug for Intel.

The behaviour seems reasonable to me. Otherwise the CPU would re-enter the handler when the handler inspects DR6.

> 
> 
> ---
> 1: AMD [13.1.1.4 Debug-Control Register (DR7)] uses a similar wording
> 
>     General-Detect Enable (GD)—Bit 13. Software sets this bit to 1 to
>     cause a debug exception to occur when an attempt is made to execute
>     a MOV DRn instruction to any debug register (DR0–DR7). This bit is
>     cleared to 0 by the processor when the #DB handler is entered,
>     allowing the handler to read and write the DRn registers. The #DB
>     exception occurs before executing the instruction, and DR6[BD] is
>     set by the processor. Software debuggers can use this bit to
>     prevent the currently-executing program from interfering with the
>     debug operation.
> 
> 2: Last sentence of [1] and also this from Intel
>     This condition is provided to support in-circuit emulators.
> 
>     When the emulator needs to access the debug registers, emulator
>     software can set the GD flag to prevent interference from the
>     program currently executing on the processor.

--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Radim Krčmář Oct. 1, 2014, 7:22 p.m. UTC | #3
2014-10-01 21:22+0300, Nadav Amit:
> 
> On Oct 1, 2014, at 6:24 PM, Radim Kr?má? <rkrcmar@redhat.com> wrote:
> 
> > 2014-09-30 20:49+0300, Nadav Amit:
> >> Intel SDM 17.2.4 (Debug Control Register (DR7)) says: "The processor clears the
> >> GD flag upon entering to the debug exception handler." This sentence may be
> >> misunderstood as if it happens only on #DB due to debug-register protection,
> >> but it happens regardless to the cause of the #DB.
> > 
> > All real hardware behaves that way?
> I have no way of knowing.

:)

> I know Intel’s phrasing is misleading, so I verified this behaviour in two ways:
> 1. I changed KVM not to trap #DB. I then changed kvm-unit-tests/debug.c to set DR7.GD prior to the watchpoint test, and printed once I entered the handler, before any DR was accessed by the handler.
> The result: we entered the handler once (afterwards I printed DR7 and saw GD is indeed clear). If #DB due to watchpoint did not clear GD, we would enter the handler twice.

Thanks.

> 2. I looked at bochs: https://github.com/larsr/bochs-svn/blob/master/cpu/exception.cc :
> 
>   if (vector == BX_DB_EXCEPTION) {
>     // Commit debug events to DR6: preserve DR5.BS and DR6.BD values,
>     // only software can clear them
>     BX_CPU_THIS_PTR dr6.val32 = (BX_CPU_THIS_PTR dr6.val32 & 0xffff6ff0) |
>                           (BX_CPU_THIS_PTR debug_trap & 0x0000e00f);
> 
>     // clear GD flag in the DR7 prior entering debug exception handler
>     BX_CPU_THIS_PTR dr7.set_GD(0);
>   }

(Ok.)

> The behaviour seems reasonable to me. Otherwise the CPU would re-enter the handler when the handler inspects DR6.

It usually is sufficient just to read it, but yeah, re-entry for the
general usage isn't nice either.

To the patch itself:  We could use kvm_x86_ops->set_dr7() directly, but
maybe we are going to do something with on GD bit, so

Reviewed-by: Radim Kr?má? <rkrcmar@redhat.com>
--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c
index 04fa1b8..82d39f9 100644
--- a/arch/x86/kvm/vmx.c
+++ b/arch/x86/kvm/vmx.c
@@ -5176,9 +5176,7 @@  static int handle_dr(struct kvm_vcpu *vcpu)
 			vcpu->run->exit_reason = KVM_EXIT_DEBUG;
 			return 0;
 		} else {
-			vcpu->arch.dr7 &= ~DR7_GD;
 			vcpu->arch.dr6 |= DR6_BD | DR6_RTM;
-			vmcs_writel(GUEST_DR7, vcpu->arch.dr7);
 			kvm_queue_exception(vcpu, DB_VECTOR);
 			return 1;
 		}
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index 6857257..fb02371 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -5935,6 +5935,12 @@  static int inject_pending_event(struct kvm_vcpu *vcpu, bool req_int_win)
 			__kvm_set_rflags(vcpu, kvm_get_rflags(vcpu) |
 					     X86_EFLAGS_RF);
 
+		if (vcpu->arch.exception.nr == DB_VECTOR &&
+		    (vcpu->arch.dr7 & DR7_GD)) {
+			vcpu->arch.dr7 &= ~DR7_GD;
+			kvm_update_dr7(vcpu);
+		}
+
 		kvm_x86_ops->queue_exception(vcpu, vcpu->arch.exception.nr,
 					  vcpu->arch.exception.has_error_code,
 					  vcpu->arch.exception.error_code,