]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
KVM: SVM: Inhibit AVIC if ID is too big instead of rejecting vCPU creation
authorSean Christopherson <seanjc@google.com>
Wed, 11 Jun 2025 22:45:15 +0000 (15:45 -0700)
committerSean Christopherson <seanjc@google.com>
Fri, 20 Jun 2025 20:52:59 +0000 (13:52 -0700)
Inhibit AVIC with a new "ID too big" flag if userspace creates a vCPU with
an ID that is too big, but otherwise allow vCPU creation to succeed.
Rejecting KVM_CREATE_VCPU with EINVAL violates KVM's ABI as KVM advertises
that the max vCPU ID is 4095, but disallows creating vCPUs with IDs bigger
than 254 (AVIC) or 511 (x2AVIC).

Alternatively, KVM could advertise an accurate value depending on which
AVIC mode is in use, but that wouldn't really solve the underlying problem,
e.g. would be a breaking change if KVM were to ever try and enable AVIC or
x2AVIC by default.

Cc: Maxim Levitsky <mlevitsk@redhat.com>
Tested-by: Sairaj Kodilkar <sarunkod@amd.com>
Link: https://lore.kernel.org/r/20250611224604.313496-14-seanjc@google.com
Signed-off-by: Sean Christopherson <seanjc@google.com>
arch/x86/include/asm/kvm_host.h
arch/x86/kvm/svm/avic.c
arch/x86/kvm/svm/svm.h

index 2a14564dd08a57eccdf812323783a041f89b928a..949577b52f642b69d70990954a0b5dee0db09384 100644 (file)
@@ -1315,6 +1315,12 @@ enum kvm_apicv_inhibit {
         */
        APICV_INHIBIT_REASON_LOGICAL_ID_ALIASED,
 
+       /*
+        * AVIC is disabled because the vCPU's APIC ID is beyond the max
+        * supported by AVIC/x2AVIC, i.e. the vCPU is unaddressable.
+        */
+       APICV_INHIBIT_REASON_PHYSICAL_ID_TOO_BIG,
+
        NR_APICV_INHIBIT_REASONS,
 };
 
@@ -1333,7 +1339,8 @@ enum kvm_apicv_inhibit {
        __APICV_INHIBIT_REASON(IRQWIN),                 \
        __APICV_INHIBIT_REASON(PIT_REINJ),              \
        __APICV_INHIBIT_REASON(SEV),                    \
-       __APICV_INHIBIT_REASON(LOGICAL_ID_ALIASED)
+       __APICV_INHIBIT_REASON(LOGICAL_ID_ALIASED),     \
+       __APICV_INHIBIT_REASON(PHYSICAL_ID_TOO_BIG)
 
 struct kvm_arch {
        unsigned long n_used_mmu_pages;
index f0401c1e355c822bc08d96dc171e971e8cc5f660..38b2f5c441e05a0008d4583f2c285007aa3131db 100644 (file)
@@ -278,9 +278,19 @@ static int avic_init_backing_page(struct kvm_vcpu *vcpu)
        int id = vcpu->vcpu_id;
        struct vcpu_svm *svm = to_svm(vcpu);
 
+       /*
+        * Inhibit AVIC if the vCPU ID is bigger than what is supported by AVIC
+        * hardware.  Immediately clear apicv_active, i.e. don't wait until the
+        * KVM_REQ_APICV_UPDATE request is processed on the first KVM_RUN, as
+        * avic_vcpu_load() expects to be called if and only if the vCPU has
+        * fully initialized AVIC.
+        */
        if ((!x2avic_enabled && id > AVIC_MAX_PHYSICAL_ID) ||
-           (id > X2AVIC_MAX_PHYSICAL_ID))
-               return -EINVAL;
+           (id > X2AVIC_MAX_PHYSICAL_ID)) {
+               kvm_set_apicv_inhibit(vcpu->kvm, APICV_INHIBIT_REASON_PHYSICAL_ID_TOO_BIG);
+               vcpu->arch.apic->apicv_active = false;
+               return 0;
+       }
 
        if (WARN_ON_ONCE(!vcpu->arch.apic->regs))
                return -EINVAL;
index 1585288200f4db0600be4cbb3f1d01e7400a7acb..71e3c003580e2d6de1124a738a43949aaaef6ac7 100644 (file)
@@ -722,7 +722,8 @@ extern struct kvm_x86_nested_ops svm_nested_ops;
        BIT(APICV_INHIBIT_REASON_PHYSICAL_ID_ALIASED) | \
        BIT(APICV_INHIBIT_REASON_APIC_ID_MODIFIED) |    \
        BIT(APICV_INHIBIT_REASON_APIC_BASE_MODIFIED) |  \
-       BIT(APICV_INHIBIT_REASON_LOGICAL_ID_ALIASED)    \
+       BIT(APICV_INHIBIT_REASON_LOGICAL_ID_ALIASED) |  \
+       BIT(APICV_INHIBIT_REASON_PHYSICAL_ID_TOO_BIG)   \
 )
 
 bool avic_hardware_setup(void);