diff mbox

KVM: VMX: Update instruction length on intercepted BP

Message ID 4B77EF0D.4050303@web.de (mailing list archive)
State New, archived
Headers show

Commit Message

Jan Kiszka Feb. 14, 2010, 12:39 p.m. UTC
None
diff mbox

Patch

diff --git a/arch/x86/kvm/svm.c b/arch/x86/kvm/svm.c
index 52f78dd..450c1e7 100644
--- a/arch/x86/kvm/svm.c
+++ b/arch/x86/kvm/svm.c
@@ -109,6 +109,7 @@  struct vcpu_svm {
 	struct nested_state nested;
 
 	bool nmi_singlestep;
+	bool bp_singlestep;
 };
 
 /* enable NPT for AMD64 and X86 with PAE */
@@ -244,6 +245,19 @@  static void svm_queue_exception(struct kvm_vcpu *vcpu, unsigned nr,
 	if (nested_svm_check_exception(svm, nr, has_error_code, error_code))
 		return;
 
+	if (nr == BP_VECTOR && vcpu->guest_debug & KVM_GUESTDBG_USE_SW_BP) {
+		/*
+		 * Temporarily disable BP interception so that the trap is
+		 * properly delivered to the guest (if we left it on, SVM
+		 * would push the wrong IP on the stack).
+		 * We single-step into theexception handler in order to
+		 * reenble interception ASAP.
+		 */
+		svm->vmcb->control.intercept_exceptions &= ~(1 << BP_VECTOR);
+		svm->bp_singlestep = true;
+		svm->vmcb->save.rflags |= (X86_EFLAGS_TF | X86_EFLAGS_RF);
+	}
+
 	svm->vmcb->control.event_inj = nr
 		| SVM_EVTINJ_VALID
 		| (has_error_code ? SVM_EVTINJ_VALID_ERR : 0)
@@ -1083,7 +1097,8 @@  static void update_db_intercept(struct kvm_vcpu *vcpu)
 		    (KVM_GUESTDBG_SINGLESTEP | KVM_GUESTDBG_USE_HW_BP))
 			svm->vmcb->control.intercept_exceptions |=
 				1 << DB_VECTOR;
-		if (vcpu->guest_debug & KVM_GUESTDBG_USE_SW_BP)
+		if (vcpu->guest_debug & KVM_GUESTDBG_USE_SW_BP &&
+		    !svm->bp_singlestep)
 			svm->vmcb->control.intercept_exceptions |=
 				1 << BP_VECTOR;
 	} else
@@ -1214,7 +1229,7 @@  static int db_interception(struct vcpu_svm *svm)
 
 	if (!(svm->vcpu.guest_debug &
 	      (KVM_GUESTDBG_SINGLESTEP | KVM_GUESTDBG_USE_HW_BP)) &&
-		!svm->nmi_singlestep) {
+		!svm->nmi_singlestep && !svm->bp_singlestep) {
 		kvm_queue_exception(&svm->vcpu, DB_VECTOR);
 		return 1;
 	}
@@ -1227,6 +1242,14 @@  static int db_interception(struct vcpu_svm *svm)
 		update_db_intercept(&svm->vcpu);
 	}
 
+	if (svm->bp_singlestep) {
+		svm->bp_singlestep = false;
+		if (!(svm->vcpu.guest_debug & KVM_GUESTDBG_SINGLESTEP))
+			svm->vmcb->save.rflags &=
+				~(X86_EFLAGS_TF | X86_EFLAGS_RF);
+		update_db_intercept(&svm->vcpu);
+	}
+
 	if (svm->vcpu.guest_debug &
 	    (KVM_GUESTDBG_SINGLESTEP | KVM_GUESTDBG_USE_HW_BP)){
 		kvm_run->exit_reason = KVM_EXIT_DEBUG;