>From fb86a56d11eac07626ffd9defeff39b88dbf6406 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Thu, 28 Sep 2023 17:25:48 -0700 Subject: [PATCH 2/2] KVM: SVM: Add enable_ipiv param, skip physical ID programming if disabled Let userspace "disable" IPI virtualization via an enable_ipiv module param by programming a dummy entry instead of the vCPU's actual backing entry in the physical ID table. SVM doesn't provide a way to actually disable IPI virtualization in hardware, but by leaving all entries blank, every IPI in the guest (except for self-IPIs) will generate a VM-Exit. Providing a way to effectively disable IPI virtualization will allow KVM to safely enable AVIC on hardware that is suseptible to erratum #1235, which causes hardware to sometimes fail to detect that the IsRunning bit has been cleared by software. All credit goes to Maxim for the idea! Suggested-by: Maxim Levitsky Signed-off-by: Sean Christopherson --- arch/x86/kvm/svm/avic.c | 15 ++++++++++++++- arch/x86/kvm/svm/svm.c | 3 +++ arch/x86/kvm/svm/svm.h | 1 + 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/arch/x86/kvm/svm/avic.c b/arch/x86/kvm/svm/avic.c index fa87b6853f1d..fc804bb84394 100644 --- a/arch/x86/kvm/svm/avic.c +++ b/arch/x86/kvm/svm/avic.c @@ -310,7 +310,20 @@ static int avic_init_backing_page(struct kvm_vcpu *vcpu) AVIC_PHYSICAL_ID_ENTRY_VALID_MASK; WRITE_ONCE(table[id], new_entry); - svm->avic_physical_id_entry = &table[id]; + /* + * IPI virtualization is bundled with AVIC, but effectively can be + * disabled simply by never marking vCPUs as running in the physical ID + * table. Use a dummy entry to avoid conditionals in the runtime code, + * and to keep the IOMMU coordination logic as simple as possible. The + * entry in the table also needs to be valid (see above), otherwise KVM + * will ignore IPIs due to thinking the target doesn't exist. + */ + if (enable_ipiv) { + svm->avic_physical_id_entry = &table[id]; + } else { + svm->ipiv_disabled_backing_entry = table[id]; + svm->avic_physical_id_entry = &svm->ipiv_disabled_backing_entry; + } return 0; } diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c index acdd0b89e471..bc40ffb5c47c 100644 --- a/arch/x86/kvm/svm/svm.c +++ b/arch/x86/kvm/svm/svm.c @@ -227,6 +227,8 @@ module_param(tsc_scaling, int, 0444); static bool avic; module_param(avic, bool, 0444); +module_param(enable_ipiv, bool, 0444); + bool __read_mostly dump_invalid_vmcb; module_param(dump_invalid_vmcb, bool, 0644); @@ -5252,6 +5254,7 @@ static __init int svm_hardware_setup(void) enable_apicv = avic = avic && avic_hardware_setup(); if (!enable_apicv) { + enable_ipiv = false; svm_x86_ops.vcpu_blocking = NULL; svm_x86_ops.vcpu_unblocking = NULL; svm_x86_ops.vcpu_get_apicv_inhibit_reasons = NULL; diff --git a/arch/x86/kvm/svm/svm.h b/arch/x86/kvm/svm/svm.h index 147516617f88..7a1fc9325d74 100644 --- a/arch/x86/kvm/svm/svm.h +++ b/arch/x86/kvm/svm/svm.h @@ -264,6 +264,7 @@ struct vcpu_svm { u32 ldr_reg; u32 dfr_reg; + u64 ipiv_disabled_backing_entry; u64 *avic_physical_id_entry; /* -- 2.42.0.582.g8ccd20d70d-goog