[PATCH v2 08/28] KVM: VMX: Support TSC scaling with enlightened VMCS
From: Vitaly Kuznetsov
Date: Wed Jun 29 2022 - 11:07:18 EST
Enlightened VMCS v1 now includes the required field for TSC scaling
feature so SECONDARY_EXEC_TSC_SCALING doesn't need to be filtered out,
both for KVM on Hyper-V and Hyper-V on KVM cases.
Note: Hyper-V on KVM case requires bumping KVM_EVMCS_REVISION revision
so VMMs can specify if SECONDARY_EXEC_TSC_SCALING needs to be filtered
out or not, this is required to support live migration.
While on it, update the comment why VM_EXIT_LOAD_IA32_PERF_GLOBAL_CTRL/
VM_ENTRY_LOAD_IA32_PERF_GLOBAL_CTRL are kept filtered out and add
missing spaces in trace_kvm_nested_vmenter_failed() strings making the
output ugly.
Signed-off-by: Vitaly Kuznetsov <vkuznets@xxxxxxxxxx>
---
arch/x86/kvm/vmx/evmcs.c | 70 +++++++++++++++++++++++++++++----------
arch/x86/kvm/vmx/evmcs.h | 17 ++++------
arch/x86/kvm/vmx/nested.c | 2 +-
arch/x86/kvm/vmx/vmx.c | 2 +-
4 files changed, 62 insertions(+), 29 deletions(-)
diff --git a/arch/x86/kvm/vmx/evmcs.c b/arch/x86/kvm/vmx/evmcs.c
index 21d9f0ed4bd2..4fe65b6a9a92 100644
--- a/arch/x86/kvm/vmx/evmcs.c
+++ b/arch/x86/kvm/vmx/evmcs.c
@@ -368,7 +368,42 @@ uint16_t nested_get_evmcs_version(struct kvm_vcpu *vcpu)
return 0;
}
-void nested_evmcs_filter_control_msr(u32 msr_index, u64 *pdata)
+enum evmcs_unsupported_ctrl_type {
+ EVMCS_EXIT_CTLS,
+ EVMCS_ENTRY_CTLS,
+ EVMCS_2NDEXEC,
+ EVMCS_PINCTRL,
+ EVMCS_VMFUNC,
+};
+
+static u32 evmcs_get_unsupported_ctls(struct kvm_vcpu *vcpu,
+ enum evmcs_unsupported_ctrl_type ctrl_type)
+{
+ u32 evmcs_rev = to_vmx(vcpu)->nested.active_evmcs_revision;
+
+ if (!evmcs_rev)
+ return 0;
+
+ switch (ctrl_type) {
+ case EVMCS_EXIT_CTLS:
+ return EVMCS1_UNSUPPORTED_VMEXIT_CTRL;
+ case EVMCS_ENTRY_CTLS:
+ return EVMCS1_UNSUPPORTED_VMENTRY_CTRL;
+ case EVMCS_2NDEXEC:
+ if (evmcs_rev == 1)
+ return EVMCS1_UNSUPPORTED_2NDEXEC | SECONDARY_EXEC_TSC_SCALING;
+ else
+ return EVMCS1_UNSUPPORTED_2NDEXEC;
+ case EVMCS_PINCTRL:
+ return EVMCS1_UNSUPPORTED_PINCTRL;
+ case EVMCS_VMFUNC:
+ return EVMCS1_UNSUPPORTED_VMFUNC;
+ }
+
+ return 0;
+}
+
+void nested_evmcs_filter_control_msr(struct kvm_vcpu *vcpu, u32 msr_index, u64 *pdata)
{
u32 ctl_low = (u32)*pdata;
u32 ctl_high = (u32)(*pdata >> 32);
@@ -380,72 +415,73 @@ void nested_evmcs_filter_control_msr(u32 msr_index, u64 *pdata)
switch (msr_index) {
case MSR_IA32_VMX_EXIT_CTLS:
case MSR_IA32_VMX_TRUE_EXIT_CTLS:
- ctl_high &= ~EVMCS1_UNSUPPORTED_VMEXIT_CTRL;
+ ctl_high &= ~evmcs_get_unsupported_ctls(vcpu, EVMCS_EXIT_CTLS);
break;
case MSR_IA32_VMX_ENTRY_CTLS:
case MSR_IA32_VMX_TRUE_ENTRY_CTLS:
- ctl_high &= ~EVMCS1_UNSUPPORTED_VMENTRY_CTRL;
+ ctl_high &= ~evmcs_get_unsupported_ctls(vcpu, EVMCS_ENTRY_CTLS);
break;
case MSR_IA32_VMX_PROCBASED_CTLS2:
- ctl_high &= ~EVMCS1_UNSUPPORTED_2NDEXEC;
+ ctl_high &= ~evmcs_get_unsupported_ctls(vcpu, EVMCS_2NDEXEC);
break;
case MSR_IA32_VMX_TRUE_PINBASED_CTLS:
case MSR_IA32_VMX_PINBASED_CTLS:
- ctl_high &= ~EVMCS1_UNSUPPORTED_PINCTRL;
+ ctl_high &= ~evmcs_get_unsupported_ctls(vcpu, EVMCS_PINCTRL);
break;
case MSR_IA32_VMX_VMFUNC:
- ctl_low &= ~EVMCS1_UNSUPPORTED_VMFUNC;
+ ctl_low &= ~evmcs_get_unsupported_ctls(vcpu, EVMCS_VMFUNC);
break;
}
*pdata = ctl_low | ((u64)ctl_high << 32);
}
-int nested_evmcs_check_controls(struct vmcs12 *vmcs12)
+int nested_evmcs_check_controls(struct kvm_vcpu *vcpu, struct vmcs12 *vmcs12)
{
int ret = 0;
u32 unsupp_ctl;
unsupp_ctl = vmcs12->pin_based_vm_exec_control &
- EVMCS1_UNSUPPORTED_PINCTRL;
+ evmcs_get_unsupported_ctls(vcpu, EVMCS_PINCTRL);
if (unsupp_ctl) {
trace_kvm_nested_vmenter_failed(
- "eVMCS: unsupported pin-based VM-execution controls",
+ "eVMCS: unsupported pin-based VM-execution controls: ",
unsupp_ctl);
ret = -EINVAL;
}
unsupp_ctl = vmcs12->secondary_vm_exec_control &
- EVMCS1_UNSUPPORTED_2NDEXEC;
+ evmcs_get_unsupported_ctls(vcpu, EVMCS_2NDEXEC);
if (unsupp_ctl) {
trace_kvm_nested_vmenter_failed(
- "eVMCS: unsupported secondary VM-execution controls",
+ "eVMCS: unsupported secondary VM-execution controls: ",
unsupp_ctl);
ret = -EINVAL;
}
unsupp_ctl = vmcs12->vm_exit_controls &
- EVMCS1_UNSUPPORTED_VMEXIT_CTRL;
+ evmcs_get_unsupported_ctls(vcpu, EVMCS_EXIT_CTLS);
if (unsupp_ctl) {
trace_kvm_nested_vmenter_failed(
- "eVMCS: unsupported VM-exit controls",
+ "eVMCS: unsupported VM-exit controls: ",
unsupp_ctl);
ret = -EINVAL;
}
unsupp_ctl = vmcs12->vm_entry_controls &
- EVMCS1_UNSUPPORTED_VMENTRY_CTRL;
+ evmcs_get_unsupported_ctls(vcpu, EVMCS_ENTRY_CTLS);
if (unsupp_ctl) {
trace_kvm_nested_vmenter_failed(
- "eVMCS: unsupported VM-entry controls",
+ "eVMCS: unsupported VM-entry controls: ",
unsupp_ctl);
ret = -EINVAL;
}
- unsupp_ctl = vmcs12->vm_function_control & EVMCS1_UNSUPPORTED_VMFUNC;
+ unsupp_ctl = vmcs12->vm_function_control &
+ evmcs_get_unsupported_ctls(vcpu, EVMCS_VMFUNC);
if (unsupp_ctl) {
trace_kvm_nested_vmenter_failed(
- "eVMCS: unsupported VM-function controls",
+ "eVMCS: unsupported VM-function controls: ",
unsupp_ctl);
ret = -EINVAL;
}
diff --git a/arch/x86/kvm/vmx/evmcs.h b/arch/x86/kvm/vmx/evmcs.h
index 022a51ae81e4..2992e29b81b7 100644
--- a/arch/x86/kvm/vmx/evmcs.h
+++ b/arch/x86/kvm/vmx/evmcs.h
@@ -28,7 +28,7 @@ DECLARE_STATIC_KEY_FALSE(enable_evmcs);
* Internal KVM eVMCS revision number, gets bumped when eVMCS needs to be
* updated but its version remain intact.
*/
-#define KVM_EVMCS_REVISION 1
+#define KVM_EVMCS_REVISION 2
/*
* Enlightened VMCSv1 doesn't support these:
@@ -47,16 +47,14 @@ DECLARE_STATIC_KEY_FALSE(enable_evmcs);
* EPTP_LIST_ADDRESS = 0x00002024,
* VMREAD_BITMAP = 0x00002026,
* VMWRITE_BITMAP = 0x00002028,
- *
- * TSC_MULTIPLIER = 0x00002032,
* PLE_GAP = 0x00004020,
* PLE_WINDOW = 0x00004022,
* VMX_PREEMPTION_TIMER_VALUE = 0x0000482E,
- * GUEST_IA32_PERF_GLOBAL_CTRL = 0x00002808,
- * HOST_IA32_PERF_GLOBAL_CTRL = 0x00002c04,
*
- * Currently unsupported in KVM:
- * GUEST_IA32_RTIT_CTL = 0x00002814,
+ * While GUEST_IA32_PERF_GLOBAL_CTRL and HOST_IA32_PERF_GLOBAL_CTRL
+ * are present in eVMCSv1, Windows 11 still has issues booting when
+ * VM_EXIT_LOAD_IA32_PERF_GLOBAL_CTRL/VM_ENTRY_LOAD_IA32_PERF_GLOBAL_CTRL
+ * are exposed to it, keep them filtered out.
*/
#define EVMCS1_UNSUPPORTED_PINCTRL (PIN_BASED_POSTED_INTR | \
PIN_BASED_VMX_PREEMPTION_TIMER)
@@ -68,7 +66,6 @@ DECLARE_STATIC_KEY_FALSE(enable_evmcs);
SECONDARY_EXEC_ENABLE_PML | \
SECONDARY_EXEC_ENABLE_VMFUNC | \
SECONDARY_EXEC_SHADOW_VMCS | \
- SECONDARY_EXEC_TSC_SCALING | \
SECONDARY_EXEC_PAUSE_LOOP_EXITING)
#define EVMCS1_UNSUPPORTED_VMEXIT_CTRL \
(VM_EXIT_LOAD_IA32_PERF_GLOBAL_CTRL | \
@@ -252,7 +249,7 @@ enum nested_evmptrld_status {
bool nested_enlightened_vmentry(struct kvm_vcpu *vcpu, u64 *evmcs_gpa);
uint16_t nested_get_evmcs_version(struct kvm_vcpu *vcpu);
int nested_enable_evmcs(struct kvm_vcpu *vcpu, u32 evmcs_revision, uint16_t *vmcs_version);
-void nested_evmcs_filter_control_msr(u32 msr_index, u64 *pdata);
-int nested_evmcs_check_controls(struct vmcs12 *vmcs12);
+void nested_evmcs_filter_control_msr(struct kvm_vcpu *vcpu, u32 msr_index, u64 *pdata);
+int nested_evmcs_check_controls(struct kvm_vcpu *vcpu, struct vmcs12 *vmcs12);
#endif /* __KVM_X86_VMX_EVMCS_H */
diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c
index 2ff361a0285f..88625965f7b7 100644
--- a/arch/x86/kvm/vmx/nested.c
+++ b/arch/x86/kvm/vmx/nested.c
@@ -2891,7 +2891,7 @@ static int nested_vmx_check_controls(struct kvm_vcpu *vcpu,
return -EINVAL;
if (to_vmx(vcpu)->nested.active_evmcs_revision)
- return nested_evmcs_check_controls(vmcs12);
+ return nested_evmcs_check_controls(vcpu, vmcs12);
return 0;
}
diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c
index 76f83af7d901..99cd017cd3fe 100644
--- a/arch/x86/kvm/vmx/vmx.c
+++ b/arch/x86/kvm/vmx/vmx.c
@@ -1858,7 +1858,7 @@ static int vmx_get_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
*/
if (!msr_info->host_initiated &&
vmx->nested.active_evmcs_revision)
- nested_evmcs_filter_control_msr(msr_info->index,
+ nested_evmcs_filter_control_msr(vcpu, msr_info->index,
&msr_info->data);
break;
case MSR_IA32_RTIT_CTL:
--
2.35.3