[PATCH 2/2] KVM: SVM: Add enable_ipiv param, skip physical ID programming if disabled

From: Sean Christopherson
Date: Thu Sep 28 2023 - 20:25:48 EST


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 <mlevitsk@xxxxxxxxxx>
Signed-off-by: Sean Christopherson <seanjc@xxxxxxxxxx>
---
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


--3xcBp5v4/HRyz7Jb--