]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
KVM: nSVM: Always use NextRIP as vmcb02's NextRIP after first L2 VMRUN
authorYosry Ahmed <yosry@kernel.org>
Wed, 25 Feb 2026 00:59:47 +0000 (00:59 +0000)
committerSean Christopherson <seanjc@google.com>
Wed, 4 Mar 2026 18:38:38 +0000 (10:38 -0800)
For guests with NRIPS disabled, L1 does not provide NextRIP when running
an L2 with an injected soft interrupt, instead it advances the current RIP
before running it. KVM uses the current RIP as the NextRIP in vmcb02 to
emulate a CPU without NRIPS.

However, after L2 runs the first time, NextRIP will be updated by the CPU
and/or KVM, and the current RIP is no longer the correct value to use in
vmcb02.  Hence, after save/restore, use the current RIP if and only if a
nested run is pending, otherwise use NextRIP.  Give soft_int_next_rip the
same treatment, as it's the same logic, just for a narrower use case.

Fixes: cc440cdad5b7 ("KVM: nSVM: implement KVM_GET_NESTED_STATE and KVM_SET_NESTED_STATE")
CC: stable@vger.kernel.org
Signed-off-by: Yosry Ahmed <yosry@kernel.org>
Link: https://patch.msgid.link/20260225005950.3739782-6-yosry@kernel.org
[sean: give soft_int_next_rip the same treatment]
Signed-off-by: Sean Christopherson <seanjc@google.com>
arch/x86/kvm/svm/nested.c

index 2308e40691c46cbbe8251c4b6ac34dde689fab4b..1cc083f95e6a251d52766e83e258395d0eef57c0 100644 (file)
@@ -845,24 +845,32 @@ static void nested_vmcb02_prepare_control(struct vcpu_svm *svm,
        vmcb02->control.event_inj_err       = svm->nested.ctl.event_inj_err;
 
        /*
-        * next_rip is consumed on VMRUN as the return address pushed on the
+        * NextRIP is consumed on VMRUN as the return address pushed on the
         * stack for injected soft exceptions/interrupts.  If nrips is exposed
-        * to L1, take it verbatim from vmcb12.  If nrips is supported in
-        * hardware but not exposed to L1, stuff the actual L2 RIP to emulate
-        * what a nrips=0 CPU would do (L1 is responsible for advancing RIP
-        * prior to injecting the event).
+        * to L1, take it verbatim from vmcb12.
+        *
+        * If nrips is supported in hardware but not exposed to L1, stuff the
+        * actual L2 RIP to emulate what a nrips=0 CPU would do (L1 is
+        * responsible for advancing RIP prior to injecting the event). This is
+        * only the case for the first L2 run after VMRUN. After that (e.g.
+        * during save/restore), NextRIP is updated by the CPU and/or KVM, and
+        * the value of the L2 RIP from vmcb12 should not be used.
         */
-       if (guest_cpu_cap_has(vcpu, X86_FEATURE_NRIPS))
-               vmcb02->control.next_rip    = svm->nested.ctl.next_rip;
-       else if (boot_cpu_has(X86_FEATURE_NRIPS))
-               vmcb02->control.next_rip    = vmcb12_rip;
+       if (boot_cpu_has(X86_FEATURE_NRIPS)) {
+               if (guest_cpu_cap_has(vcpu, X86_FEATURE_NRIPS) ||
+                   !svm->nested.nested_run_pending)
+                       vmcb02->control.next_rip    = svm->nested.ctl.next_rip;
+               else
+                       vmcb02->control.next_rip    = vmcb12_rip;
+       }
 
        svm->nmi_l1_to_l2 = is_evtinj_nmi(vmcb02->control.event_inj);
        if (is_evtinj_soft(vmcb02->control.event_inj)) {
                svm->soft_int_injected = true;
                svm->soft_int_csbase = vmcb12_csbase;
                svm->soft_int_old_rip = vmcb12_rip;
-               if (guest_cpu_cap_has(vcpu, X86_FEATURE_NRIPS))
+               if (guest_cpu_cap_has(vcpu, X86_FEATURE_NRIPS) ||
+                   !svm->nested.nested_run_pending)
                        svm->soft_int_next_rip = svm->nested.ctl.next_rip;
                else
                        svm->soft_int_next_rip = vmcb12_rip;