[PATCH v3 12/27] KVM: VMX: Add support for FRED context save/restore

From: Xin Li (Intel)
Date: Tue Oct 01 2024 - 01:05:13 EST


From: Xin Li <xin3.li@xxxxxxxxx>

Handle FRED MSR access requests, allowing FRED context to be set/get
from both host and guest.

During VM save/restore and live migration, FRED context needs to be
saved/restored, which requires FRED MSRs to be accessed from userspace,
e.g., Qemu.

Note, handling of MSR_IA32_FRED_SSP0, i.e., MSR_IA32_PL0_SSP, is not
added yet, which is done in the KVM CET patch set.

Signed-off-by: Xin Li <xin3.li@xxxxxxxxx>
Signed-off-by: Xin Li (Intel) <xin@xxxxxxxxx>
Tested-by: Shan Kang <shan.kang@xxxxxxxxx>
---

Changes since v2:
* Add a helper to convert FRED MSR index to VMCS field encoding to
make the code more compact (Chao Gao).
* Get rid of the "host_initiated" check because userspace has to set
CPUID before MSRs (Chao Gao & Sean Christopherson).
* Address a few cleanup comments (Sean Christopherson).

Changes since v1:
* Use kvm_cpu_cap_has() instead of cpu_feature_enabled() (Chao Gao).
* Fail host requested FRED MSRs access if KVM cannot virtualize FRED
(Chao Gao).
* Handle the case FRED MSRs are valid but KVM cannot virtualize FRED
(Chao Gao).
* Add sanity checks when writing to FRED MSRs.
---
arch/x86/kvm/vmx/vmx.c | 48 ++++++++++++++++++++++++++++++++++++++++++
arch/x86/kvm/x86.c | 25 ++++++++++++++++++++++
2 files changed, 73 insertions(+)

diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c
index c638492ebd59..65ab26b13d24 100644
--- a/arch/x86/kvm/vmx/vmx.c
+++ b/arch/x86/kvm/vmx/vmx.c
@@ -1424,6 +1424,24 @@ static void vmx_write_guest_kernel_gs_base(struct vcpu_vmx *vmx, u64 data)
preempt_enable();
vmx->msr_guest_kernel_gs_base = data;
}
+
+static u64 vmx_read_guest_fred_rsp0(struct vcpu_vmx *vmx)
+{
+ preempt_disable();
+ if (vmx->guest_state_loaded)
+ vmx->msr_guest_fred_rsp0 = read_msr(MSR_IA32_FRED_RSP0);
+ preempt_enable();
+ return vmx->msr_guest_fred_rsp0;
+}
+
+static void vmx_write_guest_fred_rsp0(struct vcpu_vmx *vmx, u64 data)
+{
+ preempt_disable();
+ if (vmx->guest_state_loaded)
+ wrmsrns(MSR_IA32_FRED_RSP0, data);
+ preempt_enable();
+ vmx->msr_guest_fred_rsp0 = data;
+}
#endif

static void grow_ple_window(struct kvm_vcpu *vcpu)
@@ -2036,6 +2054,24 @@ int vmx_get_feature_msr(u32 msr, u64 *data)
}
}

+#ifdef CONFIG_X86_64
+static u32 fred_msr_vmcs_fields[] = {
+ GUEST_IA32_FRED_RSP1,
+ GUEST_IA32_FRED_RSP2,
+ GUEST_IA32_FRED_RSP3,
+ GUEST_IA32_FRED_STKLVLS,
+ GUEST_IA32_FRED_SSP1,
+ GUEST_IA32_FRED_SSP2,
+ GUEST_IA32_FRED_SSP3,
+ GUEST_IA32_FRED_CONFIG,
+};
+
+static u32 fred_msr_to_vmcs(u32 msr)
+{
+ return fred_msr_vmcs_fields[msr - MSR_IA32_FRED_RSP1];
+}
+#endif
+
/*
* Reads an msr value (of 'msr_info->index') into 'msr_info->data'.
* Returns 0 on success, non-0 otherwise.
@@ -2058,6 +2094,12 @@ int vmx_get_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
case MSR_KERNEL_GS_BASE:
msr_info->data = vmx_read_guest_kernel_gs_base(vmx);
break;
+ case MSR_IA32_FRED_RSP0:
+ msr_info->data = vmx_read_guest_fred_rsp0(vmx);
+ break;
+ case MSR_IA32_FRED_RSP1 ... MSR_IA32_FRED_CONFIG:
+ msr_info->data = vmcs_read64(fred_msr_to_vmcs(msr_info->index));
+ break;
#endif
case MSR_EFER:
return kvm_get_msr_common(vcpu, msr_info);
@@ -2265,6 +2307,12 @@ int vmx_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
vmx_update_exception_bitmap(vcpu);
}
break;
+ case MSR_IA32_FRED_RSP0:
+ vmx_write_guest_fred_rsp0(vmx, data);
+ break;
+ case MSR_IA32_FRED_RSP1 ... MSR_IA32_FRED_CONFIG:
+ vmcs_write64(fred_msr_to_vmcs(msr_index), data);
+ break;
#endif
case MSR_IA32_SYSENTER_CS:
if (is_guest_mode(vcpu))
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index e8de9f4734a6..b31ebafbe0bc 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -320,6 +320,9 @@ static const u32 msrs_to_save_base[] = {
MSR_STAR,
#ifdef CONFIG_X86_64
MSR_CSTAR, MSR_KERNEL_GS_BASE, MSR_SYSCALL_MASK, MSR_LSTAR,
+ MSR_IA32_FRED_RSP0, MSR_IA32_FRED_RSP1, MSR_IA32_FRED_RSP2,
+ MSR_IA32_FRED_RSP3, MSR_IA32_FRED_STKLVLS, MSR_IA32_FRED_SSP1,
+ MSR_IA32_FRED_SSP2, MSR_IA32_FRED_SSP3, MSR_IA32_FRED_CONFIG,
#endif
MSR_IA32_TSC, MSR_IA32_CR_PAT, MSR_VM_HSAVE_PA,
MSR_IA32_FEAT_CTL, MSR_IA32_BNDCFGS, MSR_TSC_AUX,
@@ -1891,6 +1894,20 @@ static int __kvm_set_msr(struct kvm_vcpu *vcpu, u32 index, u64 data,

data = (u32)data;
break;
+ case MSR_IA32_FRED_RSP0 ... MSR_IA32_FRED_CONFIG:
+ if (!guest_can_use(vcpu, X86_FEATURE_FRED))
+ return 1;
+
+ if (index != MSR_IA32_FRED_STKLVLS && is_noncanonical_address(data, vcpu))
+ return 1;
+ if ((index >= MSR_IA32_FRED_RSP0 && index <= MSR_IA32_FRED_RSP3) &&
+ (data & GENMASK_ULL(5, 0)))
+ return 1;
+ if ((index >= MSR_IA32_FRED_SSP1 && index <= MSR_IA32_FRED_SSP3) &&
+ (data & GENMASK_ULL(2, 0)))
+ return 1;
+
+ break;
}

msr.data = data;
@@ -1935,6 +1952,10 @@ int __kvm_get_msr(struct kvm_vcpu *vcpu, u32 index, u64 *data,
!guest_cpuid_has(vcpu, X86_FEATURE_RDPID))
return 1;
break;
+ case MSR_IA32_FRED_RSP0 ... MSR_IA32_FRED_CONFIG:
+ if (!guest_can_use(vcpu, X86_FEATURE_FRED))
+ return 1;
+ break;
}

msr.index = index;
@@ -7460,6 +7481,10 @@ static void kvm_probe_msr_to_save(u32 msr_index)
if (!(kvm_get_arch_capabilities() & ARCH_CAP_TSX_CTRL_MSR))
return;
break;
+ case MSR_IA32_FRED_RSP0 ... MSR_IA32_FRED_CONFIG:
+ if (!kvm_cpu_cap_has(X86_FEATURE_FRED))
+ return;
+ break;
default:
break;
}
--
2.46.2