KVM: nSVM: If VMRUN is single-stepped, queue the #DB intercept in nested_svm_vmexit()
authorKrish Sadhukhan <krish.sadhukhan@oracle.com>
Tue, 23 Mar 2021 17:50:03 +0000 (13:50 -0400)
committerPaolo Bonzini <pbonzini@redhat.com>
Sat, 17 Apr 2021 12:30:52 +0000 (08:30 -0400)
According to APM, the #DB intercept for a single-stepped VMRUN must happen
after the completion of that instruction, when the guest does #VMEXIT to
the host. However, in the current implementation of KVM, the #DB intercept
for a single-stepped VMRUN happens after the completion of the instruction
that follows the VMRUN instruction. When the #DB intercept handler is
invoked, it shows the RIP of the instruction that follows VMRUN, instead of
of VMRUN itself. This is an incorrect RIP as far as single-stepping VMRUN
is concerned.

This patch fixes the problem by checking, in nested_svm_vmexit(), for the
condition that the VMRUN instruction is being single-stepped and if so,
queues the pending #DB intercept so that the #DB is accounted for before
we execute L1's next instruction.

Suggested-by: Paolo Bonzini <pbonzini@redhat.com>
Signed-off-by: Krish Sadhukhan <krish.sadhukhan@oraacle.com>
Message-Id: <20210323175006.73249-2-krish.sadhukhan@oracle.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
arch/x86/kvm/svm/nested.c

index 9bed484..b974bd9 100644 (file)
@@ -764,6 +764,7 @@ int nested_svm_vmexit(struct vcpu_svm *svm)
        nested_svm_copy_common_state(svm->nested.vmcb02.ptr, svm->vmcb01.ptr);
 
        svm_switch_vmcb(svm, &svm->vmcb01);
+       WARN_ON_ONCE(svm->vmcb->control.exit_code != SVM_EXIT_VMRUN);
 
        /*
         * On vmexit the  GIF is set to false and
@@ -817,6 +818,15 @@ int nested_svm_vmexit(struct vcpu_svm *svm)
        kvm_clear_exception_queue(vcpu);
        kvm_clear_interrupt_queue(vcpu);
 
+       /*
+        * If we are here following the completion of a VMRUN that
+        * is being single-stepped, queue the pending #DB intercept
+        * right now so that it an be accounted for before we execute
+        * L1's next instruction.
+        */
+       if (unlikely(svm->vmcb->save.rflags & X86_EFLAGS_TF))
+               kvm_queue_exception(&(svm->vcpu), DB_VECTOR);
+
        return 0;
 }