KVM: nVMX: Support PERF_GLOBAL_CTRL with enlightened VMCS
Enlightened VMCS v1 got updated and now includes the required fields for loading PERF_GLOBAL_CTRL upon VMENTER/VMEXIT features. For KVM on Hyper-V enablement, KVM can just observe VMX control MSRs and use the features (with or without eVMCS) when possible. Hyper-V on KVM is messier as Windows 11 guests fail to boot if the controls are advertised and a new PV feature flag, CPUID.0x4000000A.EBX BIT(0), is not set. Honor the Hyper-V CPUID feature flag to play nice with Windows guests. Signed-off-by: Vitaly Kuznetsov <vkuznets@redhat.com> Link: https://lore.kernel.org/r/20220830133737.1539624-16-vkuznets@redhat.com Signed-off-by: Sean Christopherson <seanjc@google.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
This commit is contained in:
parent
3ff8a13d41
commit
4da77090b0
@ -2546,7 +2546,7 @@ int kvm_get_hv_cpuid(struct kvm_vcpu *vcpu, struct kvm_cpuid2 *cpuid,
|
|||||||
case HYPERV_CPUID_NESTED_FEATURES:
|
case HYPERV_CPUID_NESTED_FEATURES:
|
||||||
ent->eax = evmcs_ver;
|
ent->eax = evmcs_ver;
|
||||||
ent->eax |= HV_X64_NESTED_MSR_BITMAP;
|
ent->eax |= HV_X64_NESTED_MSR_BITMAP;
|
||||||
|
ent->ebx |= HV_X64_NESTED_EVMCS1_PERF_GLOBAL_CTRL;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case HYPERV_CPUID_SYNDBG_VENDOR_AND_MAX_FUNCTIONS:
|
case HYPERV_CPUID_SYNDBG_VENDOR_AND_MAX_FUNCTIONS:
|
||||||
|
@ -412,10 +412,28 @@ static u32 evmcs_get_unsupported_ctls(enum evmcs_ctrl_type ctrl_type)
|
|||||||
return evmcs_unsupported_ctrls[ctrl_type][evmcs_rev];
|
return evmcs_unsupported_ctrls[ctrl_type][evmcs_rev];
|
||||||
}
|
}
|
||||||
|
|
||||||
void nested_evmcs_filter_control_msr(u32 msr_index, u64 *pdata)
|
static bool evmcs_has_perf_global_ctrl(struct kvm_vcpu *vcpu)
|
||||||
|
{
|
||||||
|
struct kvm_vcpu_hv *hv_vcpu = to_hv_vcpu(vcpu);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* PERF_GLOBAL_CTRL has a quirk where some Windows guests may fail to
|
||||||
|
* boot if a PV CPUID feature flag is not also set. Treat the fields
|
||||||
|
* as unsupported if the flag is not set in guest CPUID. This should
|
||||||
|
* be called only for guest accesses, and all guest accesses should be
|
||||||
|
* gated on Hyper-V being enabled and initialized.
|
||||||
|
*/
|
||||||
|
if (WARN_ON_ONCE(!hv_vcpu))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return hv_vcpu->cpuid_cache.nested_ebx & HV_X64_NESTED_EVMCS1_PERF_GLOBAL_CTRL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void nested_evmcs_filter_control_msr(struct kvm_vcpu *vcpu, u32 msr_index, u64 *pdata)
|
||||||
{
|
{
|
||||||
u32 ctl_low = (u32)*pdata;
|
u32 ctl_low = (u32)*pdata;
|
||||||
u32 ctl_high = (u32)(*pdata >> 32);
|
u32 ctl_high = (u32)(*pdata >> 32);
|
||||||
|
u32 unsupported_ctrls;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Hyper-V 2016 and 2019 try using these features even when eVMCS
|
* Hyper-V 2016 and 2019 try using these features even when eVMCS
|
||||||
@ -424,11 +442,17 @@ void nested_evmcs_filter_control_msr(u32 msr_index, u64 *pdata)
|
|||||||
switch (msr_index) {
|
switch (msr_index) {
|
||||||
case MSR_IA32_VMX_EXIT_CTLS:
|
case MSR_IA32_VMX_EXIT_CTLS:
|
||||||
case MSR_IA32_VMX_TRUE_EXIT_CTLS:
|
case MSR_IA32_VMX_TRUE_EXIT_CTLS:
|
||||||
ctl_high &= ~evmcs_get_unsupported_ctls(EVMCS_EXIT_CTRLS);
|
unsupported_ctrls = evmcs_get_unsupported_ctls(EVMCS_EXIT_CTRLS);
|
||||||
|
if (!evmcs_has_perf_global_ctrl(vcpu))
|
||||||
|
unsupported_ctrls |= VM_EXIT_LOAD_IA32_PERF_GLOBAL_CTRL;
|
||||||
|
ctl_high &= ~unsupported_ctrls;
|
||||||
break;
|
break;
|
||||||
case MSR_IA32_VMX_ENTRY_CTLS:
|
case MSR_IA32_VMX_ENTRY_CTLS:
|
||||||
case MSR_IA32_VMX_TRUE_ENTRY_CTLS:
|
case MSR_IA32_VMX_TRUE_ENTRY_CTLS:
|
||||||
ctl_high &= ~evmcs_get_unsupported_ctls(EVMCS_ENTRY_CTRLS);
|
unsupported_ctrls = evmcs_get_unsupported_ctls(EVMCS_ENTRY_CTRLS);
|
||||||
|
if (!evmcs_has_perf_global_ctrl(vcpu))
|
||||||
|
unsupported_ctrls |= VM_ENTRY_LOAD_IA32_PERF_GLOBAL_CTRL;
|
||||||
|
ctl_high &= ~unsupported_ctrls;
|
||||||
break;
|
break;
|
||||||
case MSR_IA32_VMX_PROCBASED_CTLS2:
|
case MSR_IA32_VMX_PROCBASED_CTLS2:
|
||||||
ctl_high &= ~evmcs_get_unsupported_ctls(EVMCS_2NDEXEC);
|
ctl_high &= ~evmcs_get_unsupported_ctls(EVMCS_2NDEXEC);
|
||||||
|
@ -42,8 +42,6 @@ DECLARE_STATIC_KEY_FALSE(enable_evmcs);
|
|||||||
* PLE_GAP = 0x00004020,
|
* PLE_GAP = 0x00004020,
|
||||||
* PLE_WINDOW = 0x00004022,
|
* PLE_WINDOW = 0x00004022,
|
||||||
* VMX_PREEMPTION_TIMER_VALUE = 0x0000482E,
|
* VMX_PREEMPTION_TIMER_VALUE = 0x0000482E,
|
||||||
* GUEST_IA32_PERF_GLOBAL_CTRL = 0x00002808,
|
|
||||||
* HOST_IA32_PERF_GLOBAL_CTRL = 0x00002c04,
|
|
||||||
*
|
*
|
||||||
* Currently unsupported in KVM:
|
* Currently unsupported in KVM:
|
||||||
* GUEST_IA32_RTIT_CTL = 0x00002814,
|
* GUEST_IA32_RTIT_CTL = 0x00002814,
|
||||||
@ -61,9 +59,8 @@ DECLARE_STATIC_KEY_FALSE(enable_evmcs);
|
|||||||
SECONDARY_EXEC_TSC_SCALING | \
|
SECONDARY_EXEC_TSC_SCALING | \
|
||||||
SECONDARY_EXEC_PAUSE_LOOP_EXITING)
|
SECONDARY_EXEC_PAUSE_LOOP_EXITING)
|
||||||
#define EVMCS1_UNSUPPORTED_VMEXIT_CTRL \
|
#define EVMCS1_UNSUPPORTED_VMEXIT_CTRL \
|
||||||
(VM_EXIT_LOAD_IA32_PERF_GLOBAL_CTRL | \
|
(VM_EXIT_SAVE_VMX_PREEMPTION_TIMER)
|
||||||
VM_EXIT_SAVE_VMX_PREEMPTION_TIMER)
|
#define EVMCS1_UNSUPPORTED_VMENTRY_CTRL (0)
|
||||||
#define EVMCS1_UNSUPPORTED_VMENTRY_CTRL (VM_ENTRY_LOAD_IA32_PERF_GLOBAL_CTRL)
|
|
||||||
#define EVMCS1_UNSUPPORTED_VMFUNC (VMX_VMFUNC_EPTP_SWITCHING)
|
#define EVMCS1_UNSUPPORTED_VMFUNC (VMX_VMFUNC_EPTP_SWITCHING)
|
||||||
|
|
||||||
struct evmcs_field {
|
struct evmcs_field {
|
||||||
@ -243,7 +240,7 @@ bool nested_enlightened_vmentry(struct kvm_vcpu *vcpu, u64 *evmcs_gpa);
|
|||||||
uint16_t nested_get_evmcs_version(struct kvm_vcpu *vcpu);
|
uint16_t nested_get_evmcs_version(struct kvm_vcpu *vcpu);
|
||||||
int nested_enable_evmcs(struct kvm_vcpu *vcpu,
|
int nested_enable_evmcs(struct kvm_vcpu *vcpu,
|
||||||
uint16_t *vmcs_version);
|
uint16_t *vmcs_version);
|
||||||
void nested_evmcs_filter_control_msr(u32 msr_index, u64 *pdata);
|
void nested_evmcs_filter_control_msr(struct kvm_vcpu *vcpu, u32 msr_index, u64 *pdata);
|
||||||
int nested_evmcs_check_controls(struct vmcs12 *vmcs12);
|
int nested_evmcs_check_controls(struct vmcs12 *vmcs12);
|
||||||
|
|
||||||
#endif /* __KVM_X86_VMX_EVMCS_H */
|
#endif /* __KVM_X86_VMX_EVMCS_H */
|
||||||
|
@ -1931,7 +1931,7 @@ static int vmx_get_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
|
|||||||
* features out.
|
* features out.
|
||||||
*/
|
*/
|
||||||
if (!msr_info->host_initiated && guest_cpuid_has_evmcs(vcpu))
|
if (!msr_info->host_initiated && guest_cpuid_has_evmcs(vcpu))
|
||||||
nested_evmcs_filter_control_msr(msr_info->index,
|
nested_evmcs_filter_control_msr(vcpu, msr_info->index,
|
||||||
&msr_info->data);
|
&msr_info->data);
|
||||||
break;
|
break;
|
||||||
case MSR_IA32_RTIT_CTL:
|
case MSR_IA32_RTIT_CTL:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user