]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
KVM: SVM: Recalc instructions intercepts when EFER.SVME is toggled
authorKevin Cheng <chengkev@google.com>
Wed, 4 Mar 2026 00:30:10 +0000 (16:30 -0800)
committerSean Christopherson <seanjc@google.com>
Thu, 5 Mar 2026 00:08:57 +0000 (16:08 -0800)
The AMD APM states that VMRUN, VMLOAD, VMSAVE, CLGI, VMMCALL, and
INVLPGA instructions should generate a #UD when EFER.SVME is cleared.
Currently, when VMLOAD, VMSAVE, or CLGI are executed in L1 with
EFER.SVME cleared, no #UD is generated in certain cases. This is because
the intercepts for these instructions are cleared based on whether or
not vls or vgif is enabled. The #UD fails to be generated when the
intercepts are absent.

Fix the missing #UD generation by ensuring that all relevant
instructions have intercepts set when SVME.EFER is disabled.

VMMCALL is special because KVM's ABI is that VMCALL/VMMCALL are always
supported for L1 and never fault.

Signed-off-by: Kevin Cheng <chengkev@google.com>
[sean: isolate Intel CPU "compatibility" in EFER.SVME=1 path]
Reviewed-by: Yosry Ahmed <yosry@kernel.org>
Link: https://patch.msgid.link/20260304003010.1108257-3-seanjc@google.com
Signed-off-by: Sean Christopherson <seanjc@google.com>
arch/x86/kvm/svm/svm.c

index 30d3291e4738e9138ef5832593268fe251657e3b..5fbd87450f4fbfb2c69b7b4448b7322681e87ee7 100644 (file)
@@ -245,6 +245,8 @@ int svm_set_efer(struct kvm_vcpu *vcpu, u64 efer)
                        if (svm_gp_erratum_intercept && !sev_guest(vcpu->kvm))
                                set_exception_intercept(svm, GP_VECTOR);
                }
+
+               kvm_make_request(KVM_REQ_RECALC_INTERCEPTS, vcpu);
        }
 
        svm->vmcb->save.efer = efer | EFER_SVME;
@@ -1032,27 +1034,31 @@ static void svm_recalc_instruction_intercepts(struct kvm_vcpu *vcpu)
        }
 
        /*
-        * No need to toggle VIRTUAL_VMLOAD_VMSAVE_ENABLE_MASK here, it is
-        * always set if vls is enabled. If the intercepts are set, the bit is
-        * meaningless anyway.
+        * Intercept instructions that #UD if EFER.SVME=0, as SVME must be set
+        * even when running the guest, i.e. hardware will only ever see
+        * EFER.SVME=1.
+        *
+        * No need to toggle any of the vgif/vls/etc. enable bits here, as they
+        * are set when the VMCB is initialized and never cleared (if the
+        * relevant intercepts are set, the enablements are meaningless anyway).
         */
-       if (guest_cpuid_is_intel_compatible(vcpu)) {
+       if (!(vcpu->arch.efer & EFER_SVME)) {
                svm_set_intercept(svm, INTERCEPT_VMLOAD);
                svm_set_intercept(svm, INTERCEPT_VMSAVE);
+               svm_set_intercept(svm, INTERCEPT_CLGI);
+               svm_set_intercept(svm, INTERCEPT_STGI);
        } else {
                /*
                 * If hardware supports Virtual VMLOAD VMSAVE then enable it
                 * in VMCB and clear intercepts to avoid #VMEXIT.
                 */
-               if (vls) {
+               if (guest_cpuid_is_intel_compatible(vcpu)) {
+                       svm_set_intercept(svm, INTERCEPT_VMLOAD);
+                       svm_set_intercept(svm, INTERCEPT_VMSAVE);
+               } else if (vls) {
                        svm_clr_intercept(svm, INTERCEPT_VMLOAD);
                        svm_clr_intercept(svm, INTERCEPT_VMSAVE);
                }
-       }
-
-       if (vgif) {
-               svm_clr_intercept(svm, INTERCEPT_STGI);
-               svm_clr_intercept(svm, INTERCEPT_CLGI);
 
                /*
                 * Process pending events when clearing STGI/CLGI intercepts if
@@ -1060,8 +1066,13 @@ static void svm_recalc_instruction_intercepts(struct kvm_vcpu *vcpu)
                 * that KVM re-evaluates if the intercept needs to be set again
                 * to track when GIF is re-enabled (e.g. for NMI injection).
                 */
-               if (svm_has_pending_gif_event(svm))
-                       kvm_make_request(KVM_REQ_EVENT, &svm->vcpu);
+               if (vgif) {
+                       svm_clr_intercept(svm, INTERCEPT_CLGI);
+                       svm_clr_intercept(svm, INTERCEPT_STGI);
+
+                       if (svm_has_pending_gif_event(svm))
+                               kvm_make_request(KVM_REQ_EVENT, &svm->vcpu);
+               }
        }
 
        if (kvm_need_rdpmc_intercept(vcpu))