Skip to content

Commit c0711f8

Browse files
committed
KVM: TDX: Explicitly set user-return MSRs that *may* be clobbered by the TDX-Module
Set all user-return MSRs to their post-TD-exit value when preparing to run a TDX vCPU to ensure the value that KVM expects to be loaded after running the vCPU is indeed the value that's loaded in hardware. If the TDX-Module doesn't actually enter the guest, i.e. doesn't do VM-Enter, then it won't "restore" VMM state, i.e. won't clobber user-return MSRs to their expected post-run values, in which case simply updating KVM's "cached" value will effectively corrupt the cache due to hardware still holding the original value. In theory, KVM could conditionally update the current user-return value if and only if tdh_vp_enter() succeeds, but in practice "success" doesn't guarantee the TDX-Module actually entered the guest, e.g. if the TDX-Module synthesizes an EPT Violation because it suspects a zero-step attack. Force-load the expected values instead of trying to decipher whether or not the TDX-Module restored/clobbered MSRs, as the risk doesn't justify the benefits. Effectively avoiding four WRMSRs once per run loop (even if the vCPU is scheduled out, user-return MSRs only need to be reloaded if the CPU exits to userspace or runs a non-TDX vCPU) is likely in the noise when amortized over all entries, given the cost of running a TDX vCPU. E.g. the cost of the WRMSRs is somewhere between ~300 and ~500 cycles, whereas the cost of a _single_ roundtrip to/from a TDX guest is thousands of cycles. Fixes: e0b4f31 ("KVM: TDX: restore user ret MSRs") Cc: stable@vger.kernel.org Cc: Yan Zhao <yan.y.zhao@intel.com> Cc: Xiaoyao Li <xiaoyao.li@intel.com> Cc: Rick Edgecombe <rick.p.edgecombe@intel.com> Reviewed-by: Xiaoyao Li <xiaoyao.li@intel.com> Link: https://patch.msgid.link/20251030191528.3380553-2-seanjc@google.com Signed-off-by: Sean Christopherson <seanjc@google.com>
1 parent ab4e41e commit c0711f8

File tree

4 files changed

+23
-44
lines changed

4 files changed

+23
-44
lines changed

arch/x86/include/asm/kvm_host.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2379,7 +2379,6 @@ int kvm_pv_send_ipi(struct kvm *kvm, unsigned long ipi_bitmap_low,
23792379
int kvm_add_user_return_msr(u32 msr);
23802380
int kvm_find_user_return_msr(u32 msr);
23812381
int kvm_set_user_return_msr(unsigned index, u64 val, u64 mask);
2382-
void kvm_user_return_msr_update_cache(unsigned int index, u64 val);
23832382
u64 kvm_get_user_return_msr(unsigned int slot);
23842383

23852384
static inline bool kvm_is_supported_user_return_msr(u32 msr)

arch/x86/kvm/vmx/tdx.c

Lines changed: 23 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -763,25 +763,6 @@ static bool tdx_protected_apic_has_interrupt(struct kvm_vcpu *vcpu)
763763
return tdx_vcpu_state_details_intr_pending(vcpu_state_details);
764764
}
765765

766-
/*
767-
* Compared to vmx_prepare_switch_to_guest(), there is not much to do
768-
* as SEAMCALL/SEAMRET calls take care of most of save and restore.
769-
*/
770-
void tdx_prepare_switch_to_guest(struct kvm_vcpu *vcpu)
771-
{
772-
struct vcpu_vt *vt = to_vt(vcpu);
773-
774-
if (vt->guest_state_loaded)
775-
return;
776-
777-
if (likely(is_64bit_mm(current->mm)))
778-
vt->msr_host_kernel_gs_base = current->thread.gsbase;
779-
else
780-
vt->msr_host_kernel_gs_base = read_msr(MSR_KERNEL_GS_BASE);
781-
782-
vt->guest_state_loaded = true;
783-
}
784-
785766
struct tdx_uret_msr {
786767
u32 msr;
787768
unsigned int slot;
@@ -795,31 +776,45 @@ static struct tdx_uret_msr tdx_uret_msrs[] = {
795776
{.msr = MSR_TSC_AUX,},
796777
};
797778

798-
static void tdx_user_return_msr_update_cache(void)
779+
void tdx_prepare_switch_to_guest(struct kvm_vcpu *vcpu)
799780
{
781+
struct vcpu_vt *vt = to_vt(vcpu);
800782
int i;
801783

784+
if (vt->guest_state_loaded)
785+
return;
786+
787+
if (likely(is_64bit_mm(current->mm)))
788+
vt->msr_host_kernel_gs_base = current->thread.gsbase;
789+
else
790+
vt->msr_host_kernel_gs_base = read_msr(MSR_KERNEL_GS_BASE);
791+
792+
vt->guest_state_loaded = true;
793+
794+
/*
795+
* Explicitly set user-return MSRs that are clobbered by the TDX-Module
796+
* if VP.ENTER succeeds, i.e. on TD-Exit, with the values that would be
797+
* written by the TDX-Module. Don't rely on the TDX-Module to actually
798+
* clobber the MSRs, as the contract is poorly defined and not upheld.
799+
* E.g. the TDX-Module will synthesize an EPT Violation without doing
800+
* VM-Enter if it suspects a zero-step attack, and never "restore" VMM
801+
* state.
802+
*/
802803
for (i = 0; i < ARRAY_SIZE(tdx_uret_msrs); i++)
803-
kvm_user_return_msr_update_cache(tdx_uret_msrs[i].slot,
804-
tdx_uret_msrs[i].defval);
804+
kvm_set_user_return_msr(tdx_uret_msrs[i].slot,
805+
tdx_uret_msrs[i].defval, -1ull);
805806
}
806807

807808
static void tdx_prepare_switch_to_host(struct kvm_vcpu *vcpu)
808809
{
809810
struct vcpu_vt *vt = to_vt(vcpu);
810-
struct vcpu_tdx *tdx = to_tdx(vcpu);
811811

812812
if (!vt->guest_state_loaded)
813813
return;
814814

815815
++vcpu->stat.host_state_reload;
816816
wrmsrl(MSR_KERNEL_GS_BASE, vt->msr_host_kernel_gs_base);
817817

818-
if (tdx->guest_entered) {
819-
tdx_user_return_msr_update_cache();
820-
tdx->guest_entered = false;
821-
}
822-
823818
vt->guest_state_loaded = false;
824819
}
825820

@@ -1059,7 +1054,6 @@ fastpath_t tdx_vcpu_run(struct kvm_vcpu *vcpu, u64 run_flags)
10591054
update_debugctlmsr(vcpu->arch.host_debugctl);
10601055

10611056
tdx_load_host_xsave_state(vcpu);
1062-
tdx->guest_entered = true;
10631057

10641058
vcpu->arch.regs_avail &= TDX_REGS_AVAIL_SET;
10651059

@@ -3443,10 +3437,6 @@ static int __init __tdx_bringup(void)
34433437
/*
34443438
* Check if MSRs (tdx_uret_msrs) can be saved/restored
34453439
* before returning to user space.
3446-
*
3447-
* this_cpu_ptr(user_return_msrs)->registered isn't checked
3448-
* because the registration is done at vcpu runtime by
3449-
* tdx_user_return_msr_update_cache().
34503440
*/
34513441
tdx_uret_msrs[i].slot = kvm_find_user_return_msr(tdx_uret_msrs[i].msr);
34523442
if (tdx_uret_msrs[i].slot == -1) {

arch/x86/kvm/vmx/tdx.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,6 @@ struct vcpu_tdx {
6767
u64 vp_enter_ret;
6868

6969
enum vcpu_tdx_state state;
70-
bool guest_entered;
7170

7271
u64 map_gpa_next;
7372
u64 map_gpa_end;

arch/x86/kvm/x86.c

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -681,15 +681,6 @@ int kvm_set_user_return_msr(unsigned slot, u64 value, u64 mask)
681681
}
682682
EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_set_user_return_msr);
683683

684-
void kvm_user_return_msr_update_cache(unsigned int slot, u64 value)
685-
{
686-
struct kvm_user_return_msrs *msrs = this_cpu_ptr(user_return_msrs);
687-
688-
msrs->values[slot].curr = value;
689-
kvm_user_return_register_notifier(msrs);
690-
}
691-
EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_user_return_msr_update_cache);
692-
693684
u64 kvm_get_user_return_msr(unsigned int slot)
694685
{
695686
return this_cpu_ptr(user_return_msrs)->values[slot].curr;

0 commit comments

Comments
 (0)