[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <2b2cfff9a2bd6bcc97b97fee7f3a3e1186c9b03c.1755609446.git.maciej.szmigiero@oracle.com>
Date: Tue, 19 Aug 2025 15:32:14 +0200
From: "Maciej S. Szmigiero" <mail@...iej.szmigiero.name>
To: Paolo Bonzini <pbonzini@...hat.com>,
Sean Christopherson <seanjc@...gle.com>
Cc: Maxim Levitsky <mlevitsk@...hat.com>,
Suravee Suthikulpanit <Suravee.Suthikulpanit@....com>,
Alejandro Jimenez <alejandro.j.jimenez@...cle.com>,
kvm@...r.kernel.org,
linux-kernel@...r.kernel.org
Subject: [PATCH 1/2] KVM: SVM: Sync TPR from LAPIC into VMCB::V_TPR when setting LAPIC regs
From: "Maciej S. Szmigiero" <maciej.szmigiero@...cle.com>
When AVIC is enabled the normal pre-VMRUN sync in sync_lapic_to_cr8() is
inhibited so any changed TPR in the LAPIC state would not get copied into
the V_TPR field of VMCB.
AVIC does sync between these two fields, however it does so only on
explicit guest writes to one of these fields, not on a bare VMRUN.
This is especially true when it is the userspace setting LAPIC state via
KVM_SET_LAPIC ioctl() since userspace does not have access to the guest
VMCB.
Practice shows that it is the V_TPR that is actually used by the AVIC to
decide whether to issue pending interrupts to the CPU (not TPR in TASKPRI),
so any leftover value in V_TPR will cause serious interrupt delivery issues
in the guest when AVIC is enabled.
Fix this issue by explicitly copying LAPIC TPR to VMCB::V_TPR in
avic_apicv_post_state_restore(), which gets called from KVM_SET_LAPIC and
similar code paths when AVIC is enabled.
Fixes: 3bbf3565f48c ("svm: Do not intercept CR8 when enable AVIC")
Signed-off-by: Maciej S. Szmigiero <maciej.szmigiero@...cle.com>
---
arch/x86/kvm/svm/avic.c | 23 +++++++++++++++++++++++
1 file changed, 23 insertions(+)
diff --git a/arch/x86/kvm/svm/avic.c b/arch/x86/kvm/svm/avic.c
index a34c5c3b164e..877bc3db2c6e 100644
--- a/arch/x86/kvm/svm/avic.c
+++ b/arch/x86/kvm/svm/avic.c
@@ -725,8 +725,31 @@ int avic_init_vcpu(struct vcpu_svm *svm)
void avic_apicv_post_state_restore(struct kvm_vcpu *vcpu)
{
+ struct vcpu_svm *svm = to_svm(vcpu);
+ u64 cr8;
+
avic_handle_dfr_update(vcpu);
avic_handle_ldr_update(vcpu);
+
+ /* Running nested should have inhibited AVIC. */
+ if (WARN_ON_ONCE(nested_svm_virtualize_tpr(vcpu)))
+ return;
+
+ /*
+ * Sync TPR from LAPIC TASKPRI into V_TPR field of the VMCB.
+ *
+ * When AVIC is enabled the normal pre-VMRUN sync in sync_lapic_to_cr8()
+ * is inhibited so any set TPR LAPIC state would not get reflected
+ * in V_TPR.
+ *
+ * Practice shows that it is the V_TPR that is actually used by the
+ * AVIC to decide whether to issue pending interrupts to the CPU, not
+ * TPR in TASKPRI.
+ */
+ cr8 = kvm_get_cr8(vcpu);
+ svm->vmcb->control.int_ctl &= ~V_TPR_MASK;
+ svm->vmcb->control.int_ctl |= cr8 & V_TPR_MASK;
+ WARN_ON_ONCE(!vmcb_is_dirty(svm->vmcb, VMCB_INTR));
}
static void svm_ir_list_del(struct kvm_kernel_irqfd *irqfd)
Powered by blists - more mailing lists