Skip to content

Commit e44eb58

Browse files
committed
KVM: x86: Load guest FPU state when access XSAVE-managed MSRs
Load the guest's FPU state if userspace is accessing MSRs whose values are managed by XSAVES. Introduce two helpers, kvm_{get,set}_xstate_msr(), to facilitate access to such kind of MSRs. If MSRs supported in kvm_caps.supported_xss are passed through to guest, the guest MSRs are swapped with host's before vCPU exits to userspace and after it reenters kernel before next VM-entry. Because the modified code is also used for the KVM_GET_MSRS device ioctl(), explicitly check @vcpu is non-null before attempting to load guest state. The XSAVE-managed MSRs cannot be retrieved via the device ioctl() without loading guest FPU state (which doesn't exist). Note that guest_cpuid_has() is not queried as host userspace is allowed to access MSRs that have not been exposed to the guest, e.g. it might do KVM_SET_MSRS prior to KVM_SET_CPUID2. The two helpers are put here in order to manifest accessing xsave-managed MSRs requires special check and handling to guarantee the correctness of read/write to the MSRs. Co-developed-by: Yang Weijiang <weijiang.yang@intel.com> Signed-off-by: Yang Weijiang <weijiang.yang@intel.com> Reviewed-by: Maxim Levitsky <mlevitsk@redhat.com> Tested-by: Mathias Krause <minipli@grsecurity.net> Tested-by: John Allen <john.allen@amd.com> Tested-by: Rick Edgecombe <rick.p.edgecombe@intel.com> Signed-off-by: Chao Gao <chao.gao@intel.com> [sean: drop S_CET, add big comment, move accessors to x86.c] Reviewed-by: Binbin Wu <binbin.wu@linux.intel.com> Reviewed-by: Xiaoyao Li <xiaoyao.li@intel.com> Reviewed-by: Xin Li (Intel) <xin@zytor.com> Link: https://lore.kernel.org/r/20250919223258.1604852-10-seanjc@google.com Signed-off-by: Sean Christopherson <seanjc@google.com>
1 parent 779ed05 commit e44eb58

1 file changed

Lines changed: 86 additions & 1 deletion

File tree

arch/x86/kvm/x86.c

Lines changed: 86 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,9 @@ static int __set_sregs2(struct kvm_vcpu *vcpu, struct kvm_sregs2 *sregs2);
136136
static void __get_sregs2(struct kvm_vcpu *vcpu, struct kvm_sregs2 *sregs2);
137137

138138
static DEFINE_MUTEX(vendor_module_lock);
139+
static void kvm_load_guest_fpu(struct kvm_vcpu *vcpu);
140+
static void kvm_put_guest_fpu(struct kvm_vcpu *vcpu);
141+
139142
struct kvm_x86_ops kvm_x86_ops __read_mostly;
140143

141144
#define KVM_X86_OP(func) \
@@ -3801,6 +3804,67 @@ static void record_steal_time(struct kvm_vcpu *vcpu)
38013804
mark_page_dirty_in_slot(vcpu->kvm, ghc->memslot, gpa_to_gfn(ghc->gpa));
38023805
}
38033806

3807+
/*
3808+
* Returns true if the MSR in question is managed via XSTATE, i.e. is context
3809+
* switched with the rest of guest FPU state. Note! S_CET is _not_ context
3810+
* switched via XSTATE even though it _is_ saved/restored via XSAVES/XRSTORS.
3811+
* Because S_CET is loaded on VM-Enter and VM-Exit via dedicated VMCS fields,
3812+
* the value saved/restored via XSTATE is always the host's value. That detail
3813+
* is _extremely_ important, as the guest's S_CET must _never_ be resident in
3814+
* hardware while executing in the host. Loading guest values for U_CET and
3815+
* PL[0-3]_SSP while executing in the kernel is safe, as U_CET is specific to
3816+
* userspace, and PL[0-3]_SSP are only consumed when transitioning to lower
3817+
* privilege levels, i.e. are effectively only consumed by userspace as well.
3818+
*/
3819+
static bool is_xstate_managed_msr(struct kvm_vcpu *vcpu, u32 msr)
3820+
{
3821+
if (!vcpu)
3822+
return false;
3823+
3824+
switch (msr) {
3825+
case MSR_IA32_U_CET:
3826+
return guest_cpu_cap_has(vcpu, X86_FEATURE_SHSTK) ||
3827+
guest_cpu_cap_has(vcpu, X86_FEATURE_IBT);
3828+
case MSR_IA32_PL0_SSP ... MSR_IA32_PL3_SSP:
3829+
return guest_cpu_cap_has(vcpu, X86_FEATURE_SHSTK);
3830+
default:
3831+
return false;
3832+
}
3833+
}
3834+
3835+
/*
3836+
* Lock (and if necessary, re-load) the guest FPU, i.e. XSTATE, and access an
3837+
* MSR that is managed via XSTATE. Note, the caller is responsible for doing
3838+
* the initial FPU load, this helper only ensures that guest state is resident
3839+
* in hardware (the kernel can load its FPU state in IRQ context).
3840+
*/
3841+
static __always_inline void kvm_access_xstate_msr(struct kvm_vcpu *vcpu,
3842+
struct msr_data *msr_info,
3843+
int access)
3844+
{
3845+
BUILD_BUG_ON(access != MSR_TYPE_R && access != MSR_TYPE_W);
3846+
3847+
KVM_BUG_ON(!is_xstate_managed_msr(vcpu, msr_info->index), vcpu->kvm);
3848+
KVM_BUG_ON(!vcpu->arch.guest_fpu.fpstate->in_use, vcpu->kvm);
3849+
3850+
kvm_fpu_get();
3851+
if (access == MSR_TYPE_R)
3852+
rdmsrq(msr_info->index, msr_info->data);
3853+
else
3854+
wrmsrq(msr_info->index, msr_info->data);
3855+
kvm_fpu_put();
3856+
}
3857+
3858+
static __maybe_unused void kvm_set_xstate_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
3859+
{
3860+
kvm_access_xstate_msr(vcpu, msr_info, MSR_TYPE_W);
3861+
}
3862+
3863+
static __maybe_unused void kvm_get_xstate_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
3864+
{
3865+
kvm_access_xstate_msr(vcpu, msr_info, MSR_TYPE_R);
3866+
}
3867+
38043868
int kvm_set_msr_common(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
38053869
{
38063870
u32 msr = msr_info->index;
@@ -4551,11 +4615,25 @@ static int __msr_io(struct kvm_vcpu *vcpu, struct kvm_msrs *msrs,
45514615
int (*do_msr)(struct kvm_vcpu *vcpu,
45524616
unsigned index, u64 *data))
45534617
{
4618+
bool fpu_loaded = false;
45544619
int i;
45554620

4556-
for (i = 0; i < msrs->nmsrs; ++i)
4621+
for (i = 0; i < msrs->nmsrs; ++i) {
4622+
/*
4623+
* If userspace is accessing one or more XSTATE-managed MSRs,
4624+
* temporarily load the guest's FPU state so that the guest's
4625+
* MSR value(s) is resident in hardware and thus can be accessed
4626+
* via RDMSR/WRMSR.
4627+
*/
4628+
if (!fpu_loaded && is_xstate_managed_msr(vcpu, entries[i].index)) {
4629+
kvm_load_guest_fpu(vcpu);
4630+
fpu_loaded = true;
4631+
}
45574632
if (do_msr(vcpu, entries[i].index, &entries[i].data))
45584633
break;
4634+
}
4635+
if (fpu_loaded)
4636+
kvm_put_guest_fpu(vcpu);
45594637

45604638
return i;
45614639
}
@@ -5965,6 +6043,7 @@ static int kvm_get_set_one_reg(struct kvm_vcpu *vcpu, unsigned int ioctl,
59656043
struct kvm_one_reg one_reg;
59666044
struct kvm_x86_reg_id *reg;
59676045
u64 __user *user_val;
6046+
bool load_fpu;
59686047
int r;
59696048

59706049
if (copy_from_user(&one_reg, argp, sizeof(one_reg)))
@@ -5991,12 +6070,18 @@ static int kvm_get_set_one_reg(struct kvm_vcpu *vcpu, unsigned int ioctl,
59916070

59926071
guard(srcu)(&vcpu->kvm->srcu);
59936072

6073+
load_fpu = is_xstate_managed_msr(vcpu, reg->index);
6074+
if (load_fpu)
6075+
kvm_load_guest_fpu(vcpu);
6076+
59946077
user_val = u64_to_user_ptr(one_reg.addr);
59956078
if (ioctl == KVM_GET_ONE_REG)
59966079
r = kvm_get_one_msr(vcpu, reg->index, user_val);
59976080
else
59986081
r = kvm_set_one_msr(vcpu, reg->index, user_val);
59996082

6083+
if (load_fpu)
6084+
kvm_put_guest_fpu(vcpu);
60006085
return r;
60016086
}
60026087

0 commit comments

Comments
 (0)