KVM: nSVM: Raise event on nested VM exit if L1 doesn't intercept IRQs
authorMaxim Levitsky <mlevitsk@redhat.com>
Mon, 27 Feb 2023 08:40:08 +0000 (14:10 +0530)
committerSean Christopherson <seanjc@google.com>
Wed, 22 Mar 2023 19:33:58 +0000 (12:33 -0700)
If L1 doesn't intercept interrupts, then KVM will use vmcb02's V_IRQ
to detect an interrupt window for L1 IRQs.  On a subsequent nested
VM-Exit, KVM might need to copy the current V_IRQ from vmcb02 to vmcb01
to continue waiting for an interrupt window, i.e. if there is still a
pending IRQ for L1.

Raise KVM_REQ_EVENT on nested exit if L1 isn't intercepting IRQs to ensure
that KVM will re-enable interrupt window detection if needed.

Note that this is a theoretical bug because KVM already raises
KVM_REQ_EVENT on each nested VM exit, because the nested VM exit resets
RFLAGS and kvm_set_rflags() raises the KVM_REQ_EVENT unconditionally.

Explicitly raise KVM_REQ_EVENT for the interrupt window case to avoid
having an unnecessary dependency on kvm_set_rflags(), and to document
the scenario.

Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
[santosh: reworded description as per Sean's v2 comment]
Signed-off-by: Santosh Shukla <Santosh.Shukla@amd.com>
Link: https://lore.kernel.org/r/20230227084016.3368-4-santosh.shukla@amd.com
[sean: further massage changelog and comment]
Signed-off-by: Sean Christopherson <seanjc@google.com>
arch/x86/kvm/svm/nested.c

index a0a525758ed9d36176a68bcb9b04d702bc29ea5e..8300fd31878df214bcf83ecceb5e2002d519ce01 100644 (file)
@@ -1025,6 +1025,28 @@ int nested_svm_vmexit(struct vcpu_svm *svm)
 
        svm_switch_vmcb(svm, &svm->vmcb01);
 
+       /*
+        * Rules for synchronizing int_ctl bits from vmcb02 to vmcb01:
+        *
+        * V_IRQ, V_IRQ_VECTOR, V_INTR_PRIO_MASK, V_IGN_TPR:  If L1 doesn't
+        * intercept interrupts, then KVM will use vmcb02's V_IRQ (and related
+        * flags) to detect interrupt windows for L1 IRQs (even if L1 uses
+        * virtual interrupt masking).  Raise KVM_REQ_EVENT to ensure that
+        * KVM re-requests an interrupt window if necessary, which implicitly
+        * copies this bits from vmcb02 to vmcb01.
+        *
+        * V_TPR: If L1 doesn't use virtual interrupt masking, then L1's vTPR
+        * is stored in vmcb02, but its value doesn't need to be copied from/to
+        * vmcb01 because it is copied from/to the virtual APIC's TPR register
+        * on each VM entry/exit.
+        *
+        * V_GIF: If nested vGIF is not used, KVM uses vmcb02's V_GIF for L1's
+        * V_GIF.  However, GIF is architecturally clear on each VM exit, thus
+        * there is no need to copy V_GIF from vmcb02 to vmcb01.
+        */
+       if (!nested_exit_on_intr(svm))
+               kvm_make_request(KVM_REQ_EVENT, &svm->vcpu);
+
        if (unlikely(svm->lbrv_enabled && (svm->nested.ctl.virt_ext & LBR_CTL_ENABLE_MASK))) {
                svm_copy_lbrs(vmcb12, vmcb02);
                svm_update_lbrv(vcpu);