KVM: SVM: Emulate nRIP feature when reinjecting INT3
authorJan Kiszka <jan.kiszka@siemens.com>
Tue, 23 Feb 2010 16:47:56 +0000 (17:47 +0100)
committerAvi Kivity <avi@redhat.com>
Sun, 25 Apr 2010 10:00:43 +0000 (13:00 +0300)
When in guest debugging mode, we have to reinject those #BP software
exceptions that are caused by guest-injected INT3. As older AMD
processors do not support the required nRIP VMCB field, try to emulate
it by moving RIP past the instruction on exception injection. Fix it up
again in case the injection failed and we were able to catch this. This
does not work for unintercepted faults, but it is better than doing
nothing.

Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com>
Signed-off-by: Avi Kivity <avi@redhat.com>
arch/x86/kvm/svm.c

index 908ec61895ce1b3c6955247685040b557e0be9cc..468ff6e721cedc72c82fecdac39328193a320372 100644 (file)
@@ -47,6 +47,7 @@ MODULE_LICENSE("GPL");
 #define SVM_FEATURE_NPT  (1 << 0)
 #define SVM_FEATURE_LBRV (1 << 1)
 #define SVM_FEATURE_SVML (1 << 2)
+#define SVM_FEATURE_NRIP (1 << 3)
 #define SVM_FEATURE_PAUSE_FILTER (1 << 10)
 
 #define NESTED_EXIT_HOST       0       /* Exit handled on host level */
@@ -110,6 +111,9 @@ struct vcpu_svm {
        struct nested_state nested;
 
        bool nmi_singlestep;
+
+       unsigned int3_injected;
+       unsigned long int3_rip;
 };
 
 /* enable NPT for AMD64 and X86 with PAE */
@@ -291,6 +295,22 @@ 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 && !svm_has(SVM_FEATURE_NRIP)) {
+               unsigned long rip, old_rip = kvm_rip_read(&svm->vcpu);
+
+               /*
+                * For guest debugging where we have to reinject #BP if some
+                * INT3 is guest-owned:
+                * Emulate nRIP by moving RIP forward. Will fail if injection
+                * raises a fault that is not intercepted. Still better than
+                * failing in all cases.
+                */
+               skip_emulated_instruction(&svm->vcpu);
+               rip = kvm_rip_read(&svm->vcpu);
+               svm->int3_rip = rip + svm->vmcb->save.cs.base;
+               svm->int3_injected = rip - old_rip;
+       }
+
        svm->vmcb->control.event_inj = nr
                | SVM_EVTINJ_VALID
                | (has_error_code ? SVM_EVTINJ_VALID_ERR : 0)
@@ -2701,6 +2721,9 @@ static void svm_complete_interrupts(struct vcpu_svm *svm)
        u8 vector;
        int type;
        u32 exitintinfo = svm->vmcb->control.exit_int_info;
+       unsigned int3_injected = svm->int3_injected;
+
+       svm->int3_injected = 0;
 
        if (svm->vcpu.arch.hflags & HF_IRET_MASK)
                svm->vcpu.arch.hflags &= ~(HF_NMI_MASK | HF_IRET_MASK);
@@ -2720,12 +2743,21 @@ static void svm_complete_interrupts(struct vcpu_svm *svm)
                svm->vcpu.arch.nmi_injected = true;
                break;
        case SVM_EXITINTINFO_TYPE_EXEPT:
-               /* In case of software exception do not reinject an exception
-                  vector, but re-execute and instruction instead */
                if (is_nested(svm))
                        break;
-               if (kvm_exception_is_soft(vector))
+               /*
+                * In case of software exceptions, do not reinject the vector,
+                * but re-execute the instruction instead. Rewind RIP first
+                * if we emulated INT3 before.
+                */
+               if (kvm_exception_is_soft(vector)) {
+                       if (vector == BP_VECTOR && int3_injected &&
+                           kvm_is_linear_rip(&svm->vcpu, svm->int3_rip))
+                               kvm_rip_write(&svm->vcpu,
+                                             kvm_rip_read(&svm->vcpu) -
+                                             int3_injected);
                        break;
+               }
                if (exitintinfo & SVM_EXITINTINFO_VALID_ERR) {
                        u32 err = svm->vmcb->control.exit_int_info_err;
                        kvm_queue_exception_e(&svm->vcpu, vector, err);