Skip to content

Commit 9f2febf

Browse files
committed
KVM: SVM: move MSR_IA32_SPEC_CTRL save/restore to assembly
Restoration of the host IA32_SPEC_CTRL value is probably too late with respect to the return thunk training sequence. With respect to the user/kernel boundary, AMD says, "If software chooses to toggle STIBP (e.g., set STIBP on kernel entry, and clear it on kernel exit), software should set STIBP to 1 before executing the return thunk training sequence." I assume the same requirements apply to the guest/host boundary. The return thunk training sequence is in vmenter.S, quite close to the VM-exit. On hosts without V_SPEC_CTRL, however, the host's IA32_SPEC_CTRL value is not restored until much later. To avoid this, move the restoration of host SPEC_CTRL to assembly and, for consistency, move the restoration of the guest SPEC_CTRL as well. This is not particularly difficult, apart from some care to cover both 32- and 64-bit, and to share code between SEV-ES and normal vmentry. Cc: stable@vger.kernel.org Fixes: a149180 ("x86: Add magic AMD return-thunk") Suggested-by: Jim Mattson <jmattson@google.com> Reviewed-by: Sean Christopherson <seanjc@google.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
1 parent e287bd0 commit 9f2febf

5 files changed

Lines changed: 136 additions & 38 deletions

File tree

arch/x86/kernel/cpu/bugs.c

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -196,22 +196,15 @@ void __init check_bugs(void)
196196
}
197197

198198
/*
199-
* NOTE: This function is *only* called for SVM. VMX spec_ctrl handling is
200-
* done in vmenter.S.
199+
* NOTE: This function is *only* called for SVM, since Intel uses
200+
* MSR_IA32_SPEC_CTRL for SSBD.
201201
*/
202202
void
203203
x86_virt_spec_ctrl(u64 guest_spec_ctrl, u64 guest_virt_spec_ctrl, bool setguest)
204204
{
205-
u64 msrval, guestval = guest_spec_ctrl, hostval = spec_ctrl_current();
205+
u64 guestval, hostval;
206206
struct thread_info *ti = current_thread_info();
207207

208-
if (static_cpu_has(X86_FEATURE_MSR_SPEC_CTRL)) {
209-
if (hostval != guestval) {
210-
msrval = setguest ? guestval : hostval;
211-
wrmsrl(MSR_IA32_SPEC_CTRL, msrval);
212-
}
213-
}
214-
215208
/*
216209
* If SSBD is not handled in MSR_SPEC_CTRL on AMD, update
217210
* MSR_AMD64_L2_CFG or MSR_VIRT_SPEC_CTRL if supported.

arch/x86/kvm/kvm-asm-offsets.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ static void __used common(void)
1616
BLANK();
1717
OFFSET(SVM_vcpu_arch_regs, vcpu_svm, vcpu.arch.regs);
1818
OFFSET(SVM_current_vmcb, vcpu_svm, current_vmcb);
19+
OFFSET(SVM_spec_ctrl, vcpu_svm, spec_ctrl);
1920
OFFSET(SVM_vmcb01, vcpu_svm, vmcb01);
2021
OFFSET(KVM_VMCB_pa, kvm_vmcb_info, pa);
2122
OFFSET(SD_save_area_pa, svm_cpu_data, save_area_pa);

arch/x86/kvm/svm/svm.c

Lines changed: 14 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -720,6 +720,15 @@ static bool msr_write_intercepted(struct kvm_vcpu *vcpu, u32 msr)
720720
u32 offset;
721721
u32 *msrpm;
722722

723+
/*
724+
* For non-nested case:
725+
* If the L01 MSR bitmap does not intercept the MSR, then we need to
726+
* save it.
727+
*
728+
* For nested case:
729+
* If the L02 MSR bitmap does not intercept the MSR, then we need to
730+
* save it.
731+
*/
723732
msrpm = is_guest_mode(vcpu) ? to_svm(vcpu)->nested.msrpm:
724733
to_svm(vcpu)->msrpm;
725734

@@ -3901,23 +3910,24 @@ static fastpath_t svm_exit_handlers_fastpath(struct kvm_vcpu *vcpu)
39013910
return EXIT_FASTPATH_NONE;
39023911
}
39033912

3904-
static noinstr void svm_vcpu_enter_exit(struct kvm_vcpu *vcpu)
3913+
static noinstr void svm_vcpu_enter_exit(struct kvm_vcpu *vcpu, bool spec_ctrl_intercepted)
39053914
{
39063915
struct vcpu_svm *svm = to_svm(vcpu);
39073916

39083917
guest_state_enter_irqoff();
39093918

39103919
if (sev_es_guest(vcpu->kvm))
3911-
__svm_sev_es_vcpu_run(svm);
3920+
__svm_sev_es_vcpu_run(svm, spec_ctrl_intercepted);
39123921
else
3913-
__svm_vcpu_run(svm);
3922+
__svm_vcpu_run(svm, spec_ctrl_intercepted);
39143923

39153924
guest_state_exit_irqoff();
39163925
}
39173926

39183927
static __no_kcsan fastpath_t svm_vcpu_run(struct kvm_vcpu *vcpu)
39193928
{
39203929
struct vcpu_svm *svm = to_svm(vcpu);
3930+
bool spec_ctrl_intercepted = msr_write_intercepted(vcpu, MSR_IA32_SPEC_CTRL);
39213931

39223932
trace_kvm_entry(vcpu);
39233933

@@ -3976,26 +3986,7 @@ static __no_kcsan fastpath_t svm_vcpu_run(struct kvm_vcpu *vcpu)
39763986
if (!static_cpu_has(X86_FEATURE_V_SPEC_CTRL))
39773987
x86_spec_ctrl_set_guest(svm->spec_ctrl, svm->virt_spec_ctrl);
39783988

3979-
svm_vcpu_enter_exit(vcpu);
3980-
3981-
/*
3982-
* We do not use IBRS in the kernel. If this vCPU has used the
3983-
* SPEC_CTRL MSR it may have left it on; save the value and
3984-
* turn it off. This is much more efficient than blindly adding
3985-
* it to the atomic save/restore list. Especially as the former
3986-
* (Saving guest MSRs on vmexit) doesn't even exist in KVM.
3987-
*
3988-
* For non-nested case:
3989-
* If the L01 MSR bitmap does not intercept the MSR, then we need to
3990-
* save it.
3991-
*
3992-
* For nested case:
3993-
* If the L02 MSR bitmap does not intercept the MSR, then we need to
3994-
* save it.
3995-
*/
3996-
if (!static_cpu_has(X86_FEATURE_V_SPEC_CTRL) &&
3997-
unlikely(!msr_write_intercepted(vcpu, MSR_IA32_SPEC_CTRL)))
3998-
svm->spec_ctrl = native_read_msr(MSR_IA32_SPEC_CTRL);
3989+
svm_vcpu_enter_exit(vcpu, spec_ctrl_intercepted);
39993990

40003991
if (!sev_es_guest(vcpu->kvm))
40013992
reload_tss(vcpu);

arch/x86/kvm/svm/svm.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -682,7 +682,7 @@ void sev_es_unmap_ghcb(struct vcpu_svm *svm);
682682

683683
/* vmenter.S */
684684

685-
void __svm_sev_es_vcpu_run(struct vcpu_svm *svm);
686-
void __svm_vcpu_run(struct vcpu_svm *svm);
685+
void __svm_sev_es_vcpu_run(struct vcpu_svm *svm, bool spec_ctrl_intercepted);
686+
void __svm_vcpu_run(struct vcpu_svm *svm, bool spec_ctrl_intercepted);
687687

688688
#endif

arch/x86/kvm/svm/vmenter.S

Lines changed: 116 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,69 @@
3232

3333
.section .noinstr.text, "ax"
3434

35+
.macro RESTORE_GUEST_SPEC_CTRL
36+
/* No need to do anything if SPEC_CTRL is unset or V_SPEC_CTRL is set */
37+
ALTERNATIVE_2 "", \
38+
"jmp 800f", X86_FEATURE_MSR_SPEC_CTRL, \
39+
"", X86_FEATURE_V_SPEC_CTRL
40+
801:
41+
.endm
42+
.macro RESTORE_GUEST_SPEC_CTRL_BODY
43+
800:
44+
/*
45+
* SPEC_CTRL handling: if the guest's SPEC_CTRL value differs from the
46+
* host's, write the MSR. This is kept out-of-line so that the common
47+
* case does not have to jump.
48+
*
49+
* IMPORTANT: To avoid RSB underflow attacks and any other nastiness,
50+
* there must not be any returns or indirect branches between this code
51+
* and vmentry.
52+
*/
53+
movl SVM_spec_ctrl(%_ASM_DI), %eax
54+
cmp PER_CPU_VAR(x86_spec_ctrl_current), %eax
55+
je 801b
56+
mov $MSR_IA32_SPEC_CTRL, %ecx
57+
xor %edx, %edx
58+
wrmsr
59+
jmp 801b
60+
.endm
61+
62+
.macro RESTORE_HOST_SPEC_CTRL
63+
/* No need to do anything if SPEC_CTRL is unset or V_SPEC_CTRL is set */
64+
ALTERNATIVE_2 "", \
65+
"jmp 900f", X86_FEATURE_MSR_SPEC_CTRL, \
66+
"", X86_FEATURE_V_SPEC_CTRL
67+
901:
68+
.endm
69+
.macro RESTORE_HOST_SPEC_CTRL_BODY
70+
900:
71+
/* Same for after vmexit. */
72+
mov $MSR_IA32_SPEC_CTRL, %ecx
73+
74+
/*
75+
* Load the value that the guest had written into MSR_IA32_SPEC_CTRL,
76+
* if it was not intercepted during guest execution.
77+
*/
78+
cmpb $0, (%_ASM_SP)
79+
jnz 998f
80+
rdmsr
81+
movl %eax, SVM_spec_ctrl(%_ASM_DI)
82+
998:
83+
84+
/* Now restore the host value of the MSR if different from the guest's. */
85+
movl PER_CPU_VAR(x86_spec_ctrl_current), %eax
86+
cmp SVM_spec_ctrl(%_ASM_DI), %eax
87+
je 901b
88+
xor %edx, %edx
89+
wrmsr
90+
jmp 901b
91+
.endm
92+
93+
3594
/**
3695
* __svm_vcpu_run - Run a vCPU via a transition to SVM guest mode
3796
* @svm: struct vcpu_svm *
97+
* @spec_ctrl_intercepted: bool
3898
*/
3999
SYM_FUNC_START(__svm_vcpu_run)
40100
push %_ASM_BP
@@ -54,17 +114,26 @@ SYM_FUNC_START(__svm_vcpu_run)
54114
* order compared to when they are needed.
55115
*/
56116

117+
/* Accessed directly from the stack in RESTORE_HOST_SPEC_CTRL. */
118+
push %_ASM_ARG2
119+
57120
/* Needed to restore access to percpu variables. */
58121
__ASM_SIZE(push) PER_CPU_VAR(svm_data + SD_save_area_pa)
59122

60-
/* Save @svm. */
123+
/* Finally save @svm. */
61124
push %_ASM_ARG1
62125

63126
.ifnc _ASM_ARG1, _ASM_DI
64-
/* Move @svm to RDI. */
127+
/*
128+
* Stash @svm in RDI early. On 32-bit, arguments are in RAX, RCX
129+
* and RDX which are clobbered by RESTORE_GUEST_SPEC_CTRL.
130+
*/
65131
mov %_ASM_ARG1, %_ASM_DI
66132
.endif
67133

134+
/* Clobbers RAX, RCX, RDX. */
135+
RESTORE_GUEST_SPEC_CTRL
136+
68137
/*
69138
* Use a single vmcb (vmcb01 because it's always valid) for
70139
* context switching guest state via VMLOAD/VMSAVE, that way
@@ -142,6 +211,9 @@ SYM_FUNC_START(__svm_vcpu_run)
142211
FILL_RETURN_BUFFER %_ASM_AX, RSB_CLEAR_LOOPS, X86_FEATURE_RETPOLINE
143212
#endif
144213

214+
/* Clobbers RAX, RCX, RDX. */
215+
RESTORE_HOST_SPEC_CTRL
216+
145217
/*
146218
* Mitigate RETBleed for AMD/Hygon Zen uarch. RET should be
147219
* untrained as soon as we exit the VM and are back to the
@@ -177,6 +249,9 @@ SYM_FUNC_START(__svm_vcpu_run)
177249
xor %r15d, %r15d
178250
#endif
179251

252+
/* "Pop" @spec_ctrl_intercepted. */
253+
pop %_ASM_BX
254+
180255
pop %_ASM_BX
181256

182257
#ifdef CONFIG_X86_64
@@ -191,6 +266,9 @@ SYM_FUNC_START(__svm_vcpu_run)
191266
pop %_ASM_BP
192267
RET
193268

269+
RESTORE_GUEST_SPEC_CTRL_BODY
270+
RESTORE_HOST_SPEC_CTRL_BODY
271+
194272
10: cmpb $0, kvm_rebooting
195273
jne 2b
196274
ud2
@@ -214,6 +292,7 @@ SYM_FUNC_END(__svm_vcpu_run)
214292
/**
215293
* __svm_sev_es_vcpu_run - Run a SEV-ES vCPU via a transition to SVM guest mode
216294
* @svm: struct vcpu_svm *
295+
* @spec_ctrl_intercepted: bool
217296
*/
218297
SYM_FUNC_START(__svm_sev_es_vcpu_run)
219298
push %_ASM_BP
@@ -228,8 +307,30 @@ SYM_FUNC_START(__svm_sev_es_vcpu_run)
228307
#endif
229308
push %_ASM_BX
230309

310+
/*
311+
* Save variables needed after vmexit on the stack, in inverse
312+
* order compared to when they are needed.
313+
*/
314+
315+
/* Accessed directly from the stack in RESTORE_HOST_SPEC_CTRL. */
316+
push %_ASM_ARG2
317+
318+
/* Save @svm. */
319+
push %_ASM_ARG1
320+
321+
.ifnc _ASM_ARG1, _ASM_DI
322+
/*
323+
* Stash @svm in RDI early. On 32-bit, arguments are in RAX, RCX
324+
* and RDX which are clobbered by RESTORE_GUEST_SPEC_CTRL.
325+
*/
326+
mov %_ASM_ARG1, %_ASM_DI
327+
.endif
328+
329+
/* Clobbers RAX, RCX, RDX. */
330+
RESTORE_GUEST_SPEC_CTRL
331+
231332
/* Get svm->current_vmcb->pa into RAX. */
232-
mov SVM_current_vmcb(%_ASM_ARG1), %_ASM_AX
333+
mov SVM_current_vmcb(%_ASM_DI), %_ASM_AX
233334
mov KVM_VMCB_pa(%_ASM_AX), %_ASM_AX
234335

235336
/* Enter guest mode */
@@ -239,11 +340,17 @@ SYM_FUNC_START(__svm_sev_es_vcpu_run)
239340

240341
2: cli
241342

343+
/* Pop @svm to RDI, guest registers have been saved already. */
344+
pop %_ASM_DI
345+
242346
#ifdef CONFIG_RETPOLINE
243347
/* IMPORTANT: Stuff the RSB immediately after VM-Exit, before RET! */
244348
FILL_RETURN_BUFFER %_ASM_AX, RSB_CLEAR_LOOPS, X86_FEATURE_RETPOLINE
245349
#endif
246350

351+
/* Clobbers RAX, RCX, RDX. */
352+
RESTORE_HOST_SPEC_CTRL
353+
247354
/*
248355
* Mitigate RETBleed for AMD/Hygon Zen uarch. RET should be
249356
* untrained as soon as we exit the VM and are back to the
@@ -253,6 +360,9 @@ SYM_FUNC_START(__svm_sev_es_vcpu_run)
253360
*/
254361
UNTRAIN_RET
255362

363+
/* "Pop" @spec_ctrl_intercepted. */
364+
pop %_ASM_BX
365+
256366
pop %_ASM_BX
257367

258368
#ifdef CONFIG_X86_64
@@ -267,6 +377,9 @@ SYM_FUNC_START(__svm_sev_es_vcpu_run)
267377
pop %_ASM_BP
268378
RET
269379

380+
RESTORE_GUEST_SPEC_CTRL_BODY
381+
RESTORE_HOST_SPEC_CTRL_BODY
382+
270383
3: cmpb $0, kvm_rebooting
271384
jne 2b
272385
ud2

0 commit comments

Comments
 (0)