]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/blobdiff - arch/x86/kvm/vmx/vmenter.S
KVM: VMX: Prevent RSB underflow before vmenter
[mirror_ubuntu-jammy-kernel.git] / arch / x86 / kvm / vmx / vmenter.S
index 3a6461694fc2502be9d223fed57249cd0d051fe1..8a37ca74d9cf2ec7e62d667748cc65e5e9ab639f 100644 (file)
@@ -1,10 +1,13 @@
 /* SPDX-License-Identifier: GPL-2.0 */
 #include <linux/linkage.h>
 #include <asm/asm.h>
+#include <asm/asm-offsets.h>
 #include <asm/bitsperlong.h>
 #include <asm/kvm_vcpu_regs.h>
 #include <asm/nospec-branch.h>
+#include <asm/percpu.h>
 #include <asm/segment.h>
+#include "run_flags.h"
 
 #define WORD_SIZE (BITS_PER_LONG / 8)
 
 
 .section .noinstr.text, "ax"
 
-/**
- * vmx_vmenter - VM-Enter the current loaded VMCS
- *
- * %RFLAGS.ZF: !VMCS.LAUNCHED, i.e. controls VMLAUNCH vs. VMRESUME
- *
- * Returns:
- *     %RFLAGS.CF is set on VM-Fail Invalid
- *     %RFLAGS.ZF is set on VM-Fail Valid
- *     %RFLAGS.{CF,ZF} are cleared on VM-Success, i.e. VM-Exit
- *
- * Note that VMRESUME/VMLAUNCH fall-through and return directly if
- * they VM-Fail, whereas a successful VM-Enter + VM-Exit will jump
- * to vmx_vmexit.
- */
-SYM_FUNC_START_LOCAL(vmx_vmenter)
-       /* EFLAGS.ZF is set if VMCS.LAUNCHED == 0 */
-       je 2f
-
-1:     vmresume
-       ret
-
-2:     vmlaunch
-       ret
-
-3:     cmpb $0, kvm_rebooting
-       je 4f
-       ret
-4:     ud2
-
-       _ASM_EXTABLE(1b, 3b)
-       _ASM_EXTABLE(2b, 3b)
-
-SYM_FUNC_END(vmx_vmenter)
-
-/**
- * vmx_vmexit - Handle a VMX VM-Exit
- *
- * Returns:
- *     %RFLAGS.{CF,ZF} are cleared on VM-Success, i.e. VM-Exit
- *
- * This is vmx_vmenter's partner in crime.  On a VM-Exit, control will jump
- * here after hardware loads the host's state, i.e. this is the destination
- * referred to by VMCS.HOST_RIP.
- */
-SYM_FUNC_START(vmx_vmexit)
-#ifdef CONFIG_RETPOLINE
-       ALTERNATIVE "jmp .Lvmexit_skip_rsb", "", X86_FEATURE_RETPOLINE
-       /* Preserve guest's RAX, it's used to stuff the RSB. */
-       push %_ASM_AX
-
-       /* IMPORTANT: Stuff the RSB immediately after VM-Exit, before RET! */
-       FILL_RETURN_BUFFER %_ASM_AX, RSB_CLEAR_LOOPS, X86_FEATURE_RETPOLINE
-
-       /* Clear RFLAGS.CF and RFLAGS.ZF to preserve VM-Exit, i.e. !VM-Fail. */
-       or $1, %_ASM_AX
-
-       pop %_ASM_AX
-.Lvmexit_skip_rsb:
-#endif
-       ret
-SYM_FUNC_END(vmx_vmexit)
-
 /**
  * __vmx_vcpu_run - Run a vCPU via a transition to VMX guest mode
- * @vmx:       struct vcpu_vmx * (forwarded to vmx_update_host_rsp)
+ * @vmx:       struct vcpu_vmx *
  * @regs:      unsigned long * (to guest registers)
- * @launched:  %true if the VMCS has been launched
+ * @flags:     VMX_RUN_VMRESUME:       use VMRESUME instead of VMLAUNCH
+ *             VMX_RUN_SAVE_SPEC_CTRL: save guest SPEC_CTRL into vmx->spec_ctrl
  *
  * Returns:
  *     0 on VM-Exit, 1 on VM-Fail
@@ -115,24 +57,56 @@ SYM_FUNC_START(__vmx_vcpu_run)
 #endif
        push %_ASM_BX
 
+       /* Save @vmx for SPEC_CTRL handling */
+       push %_ASM_ARG1
+
+       /* Save @flags for SPEC_CTRL handling */
+       push %_ASM_ARG3
+
        /*
         * Save @regs, _ASM_ARG2 may be modified by vmx_update_host_rsp() and
         * @regs is needed after VM-Exit to save the guest's register values.
         */
        push %_ASM_ARG2
 
-       /* Copy @launched to BL, _ASM_ARG3 is volatile. */
+       /* Copy @flags to BL, _ASM_ARG3 is volatile. */
        mov %_ASM_ARG3B, %bl
 
-       /* Adjust RSP to account for the CALL to vmx_vmenter(). */
-       lea -WORD_SIZE(%_ASM_SP), %_ASM_ARG2
+       lea (%_ASM_SP), %_ASM_ARG2
        call vmx_update_host_rsp
 
+       ALTERNATIVE "jmp .Lspec_ctrl_done", "", X86_FEATURE_MSR_SPEC_CTRL
+
+       /*
+        * SPEC_CTRL handling: if the guest's SPEC_CTRL value differs from the
+        * host's, write the MSR.
+        *
+        * IMPORTANT: To avoid RSB underflow attacks and any other nastiness,
+        * there must not be any returns or indirect branches between this code
+        * and vmentry.
+        */
+       mov 2*WORD_SIZE(%_ASM_SP), %_ASM_DI
+       movl VMX_spec_ctrl(%_ASM_DI), %edi
+       movl PER_CPU_VAR(x86_spec_ctrl_current), %esi
+       cmp %edi, %esi
+       je .Lspec_ctrl_done
+       mov $MSR_IA32_SPEC_CTRL, %ecx
+       xor %edx, %edx
+       mov %edi, %eax
+       wrmsr
+
+.Lspec_ctrl_done:
+
+       /*
+        * Since vmentry is serializing on affected CPUs, there's no need for
+        * an LFENCE to stop speculation from skipping the wrmsr.
+        */
+
        /* Load @regs to RAX. */
        mov (%_ASM_SP), %_ASM_AX
 
        /* Check if vmlaunch or vmresume is needed */
-       testb %bl, %bl
+       testb $VMX_RUN_VMRESUME, %bl
 
        /* Load guest registers.  Don't clobber flags. */
        mov VCPU_RCX(%_ASM_AX), %_ASM_CX
@@ -154,11 +128,36 @@ SYM_FUNC_START(__vmx_vcpu_run)
        /* Load guest RAX.  This kills the @regs pointer! */
        mov VCPU_RAX(%_ASM_AX), %_ASM_AX
 
-       /* Enter guest mode */
-       call vmx_vmenter
+       /* Check EFLAGS.ZF from 'testb' above */
+       jz .Lvmlaunch
+
+       /*
+        * After a successful VMRESUME/VMLAUNCH, control flow "magically"
+        * resumes below at 'vmx_vmexit' due to the VMCS HOST_RIP setting.
+        * So this isn't a typical function and objtool needs to be told to
+        * save the unwind state here and restore it below.
+        */
+       UNWIND_HINT_SAVE
+
+/*
+ * If VMRESUME/VMLAUNCH and corresponding vmexit succeed, execution resumes at
+ * the 'vmx_vmexit' label below.
+ */
+.Lvmresume:
+       vmresume
+       jmp .Lvmfail
+
+.Lvmlaunch:
+       vmlaunch
+       jmp .Lvmfail
+
+       _ASM_EXTABLE(.Lvmresume, .Lfixup)
+       _ASM_EXTABLE(.Lvmlaunch, .Lfixup)
 
-       /* Jump on VM-Fail. */
-       jbe 2f
+SYM_INNER_LABEL(vmx_vmexit, SYM_L_GLOBAL)
+
+       /* Restore unwind state from before the VMRESUME/VMLAUNCH. */
+       UNWIND_HINT_RESTORE
 
        /* Temporarily save guest's RAX. */
        push %_ASM_AX
@@ -185,21 +184,23 @@ SYM_FUNC_START(__vmx_vcpu_run)
        mov %r15, VCPU_R15(%_ASM_AX)
 #endif
 
-       /* Clear RAX to indicate VM-Exit (as opposed to VM-Fail). */
-       xor %eax, %eax
+       /* Clear return value to indicate VM-Exit (as opposed to VM-Fail). */
+       xor %ebx, %ebx
 
+.Lclear_regs:
        /*
-        * Clear all general purpose registers except RSP and RAX to prevent
+        * Clear all general purpose registers except RSP and RBX to prevent
         * speculative use of the guest's values, even those that are reloaded
         * via the stack.  In theory, an L1 cache miss when restoring registers
         * could lead to speculative execution with the guest's values.
         * Zeroing XORs are dirt cheap, i.e. the extra paranoia is essentially
         * free.  RSP and RAX are exempt as RSP is restored by hardware during
-        * VM-Exit and RAX is explicitly loaded with 0 or 1 to return VM-Fail.
+        * VM-Exit and RBX is explicitly loaded with 0 or 1 to hold the return
+        * value.
         */
-1:     xor %ecx, %ecx
+       xor %eax, %eax
+       xor %ecx, %ecx
        xor %edx, %edx
-       xor %ebx, %ebx
        xor %ebp, %ebp
        xor %esi, %esi
        xor %edi, %edi
@@ -216,8 +217,30 @@ SYM_FUNC_START(__vmx_vcpu_run)
 
        /* "POP" @regs. */
        add $WORD_SIZE, %_ASM_SP
-       pop %_ASM_BX
 
+       /*
+        * IMPORTANT: RSB filling and SPEC_CTRL handling must be done before
+        * the first unbalanced RET after vmexit!
+        *
+        * For retpoline or IBRS, RSB filling is needed to prevent poisoned RSB
+        * entries and (in some cases) RSB underflow.
+        *
+        * eIBRS has its own protection against poisoned RSB, so it doesn't
+        * need the RSB filling sequence.  But it does need to be enabled
+        * before the first unbalanced RET.
+         */
+
+       FILL_RETURN_BUFFER %_ASM_CX, RSB_CLEAR_LOOPS, X86_FEATURE_RSB_VMEXIT
+
+       pop %_ASM_ARG2  /* @flags */
+       pop %_ASM_ARG1  /* @vmx */
+
+       call vmx_spec_ctrl_restore_host
+
+       /* Put return value in AX */
+       mov %_ASM_BX, %_ASM_AX
+
+       pop %_ASM_BX
 #ifdef CONFIG_X86_64
        pop %r12
        pop %r13
@@ -228,11 +251,17 @@ SYM_FUNC_START(__vmx_vcpu_run)
        pop %edi
 #endif
        pop %_ASM_BP
-       ret
+       RET
+
+.Lfixup:
+       cmpb $0, kvm_rebooting
+       jne .Lvmfail
+       ud2
+.Lvmfail:
+       /* VM-Fail: set return value to 1 */
+       mov $1, %_ASM_BX
+       jmp .Lclear_regs
 
-       /* VM-Fail.  Out-of-line to avoid a taken Jcc after VM-Exit. */
-2:     mov $1, %eax
-       jmp 1b
 SYM_FUNC_END(__vmx_vcpu_run)
 
 
@@ -293,7 +322,7 @@ SYM_FUNC_START(vmread_error_trampoline)
        pop %_ASM_AX
        pop %_ASM_BP
 
-       ret
+       RET
 SYM_FUNC_END(vmread_error_trampoline)
 
 SYM_FUNC_START(vmx_do_interrupt_nmi_irqoff)
@@ -326,5 +355,5 @@ SYM_FUNC_START(vmx_do_interrupt_nmi_irqoff)
         */
        mov %_ASM_BP, %_ASM_SP
        pop %_ASM_BP
-       ret
+       RET
 SYM_FUNC_END(vmx_do_interrupt_nmi_irqoff)