From f0d06165d3d60fb1cb6b50459c93c6d05d8b1ebd Mon Sep 17 00:00:00 2001 From: Binbin Wu Date: Tue, 30 Apr 2019 10:44:54 +0800 Subject: [PATCH] hv: vmsr: handle guest msr ia32_misc_enable read/write Guest MSR_IA32_MISC_ENABLE read simply returns the value set by guest. Guest MSR_IA32_MISC_ENABLE write: - Clear EFER.NXE if MSR_IA32_MISC_ENABLE_XD_DISABLE set. - MSR_IA32_MISC_ENABLE_MONITOR_ENA: Allow guest to control this feature when HV doesn't use this feature and hw has no bug. vcpuid update according to the change of the msr will be covered in following patch. Tracked-On: #2834 Signed-off-by: Binbin Wu Acked-by: Eddie Dong --- hypervisor/arch/x86/cpu_caps.c | 13 +++++- hypervisor/arch/x86/guest/vmsr.c | 58 ++++++++++++++++++++++++++ hypervisor/include/arch/x86/cpu_caps.h | 1 + 3 files changed, 71 insertions(+), 1 deletion(-) diff --git a/hypervisor/arch/x86/cpu_caps.c b/hypervisor/arch/x86/cpu_caps.c index 821ff257f..444692522 100644 --- a/hypervisor/arch/x86/cpu_caps.c +++ b/hypervisor/arch/x86/cpu_caps.c @@ -57,6 +57,17 @@ bool pcpu_has_cap(uint32_t bit) return ret; } +bool monitor_cap_buggy(void) +{ + bool buggy = false; + + if ((boot_cpu_data.family == 0x6U) && (boot_cpu_data.model == 0x5cU)) { + buggy = true; + } + + return buggy; +} + bool has_monitor_cap(void) { bool ret = false; @@ -66,7 +77,7 @@ bool has_monitor_cap(void) * in hypervisor, but still expose it to the guests and * let them handle it correctly */ - if ((boot_cpu_data.family != 0x6U) || (boot_cpu_data.model != 0x5cU)) { + if (!monitor_cap_buggy()) { ret = true; } } diff --git a/hypervisor/arch/x86/guest/vmsr.c b/hypervisor/arch/x86/guest/vmsr.c index e376f4234..bdd34118c 100644 --- a/hypervisor/arch/x86/guest/vmsr.c +++ b/hypervisor/arch/x86/guest/vmsr.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -400,6 +401,11 @@ int32_t rdmsr_vmexit_handler(struct acrn_vcpu *vcpu) v = 0U; break; } + case MSR_IA32_MISC_ENABLE: + { + v = vcpu_get_guest_msr(vcpu, MSR_IA32_MISC_ENABLE); + break; + } default: { if (is_x2apic_msr(msr)) { @@ -466,6 +472,53 @@ static void set_guest_tsc_adjust(struct acrn_vcpu *vcpu, uint64_t tsc_adjust) vcpu_set_guest_msr(vcpu, MSR_IA32_TSC_ADJUST, tsc_adjust); } +static void set_guest_ia32_misc_enalbe(struct acrn_vcpu *vcpu, uint64_t v) +{ + uint32_t eax, ebx = 0U, ecx = 0U, edx = 0U; + bool update_vmsr = true; + uint64_t msr_value; + /* According to SDM Vol4 2.1 & Vol 3A 4.1.4, + * EFER.NXE should be cleared if guest disable XD in IA32_MISC_ENABLE + */ + if ((v & MSR_IA32_MISC_ENABLE_XD_DISABLE) != 0UL) { + vcpu_set_efer(vcpu, vcpu_get_efer(vcpu) & ~MSR_IA32_EFER_NXE_BIT); + } + + /* Handle MISC_ENABLE_MONITOR_ENA + * If has_monitor_cap() retrn true, this means the feature is enabed on host. + * HV will use monitor/mwait. + * - if guest try to set this bit, do nothing since it is already enabled + * - if guest try to clear this bit, not allow to disable in physcial MSR, + * just clear the corresponding bit in vcpuid. + * If has_monitor_cap() retrn false, this means the feature is not enabled on host. + * HV will not use monitor/mwait. Allow guest to change the bit to physcial MSR + */ + if (((v ^ vcpu_get_guest_msr(vcpu, MSR_IA32_MISC_ENABLE)) & MSR_IA32_MISC_ENABLE_MONITOR_ENA) != 0UL) { + eax = 1U; + guest_cpuid(vcpu, &eax, &ebx, &ecx, &edx); + /* According to SDM Vol4 2.1 Table 2-2, + * Writing this bit when the SSE3 feature flag is set to 0 may generate a #GP exception. + */ + if ((ecx & CPUID_ECX_SSE3) == 0U) { + vcpu_inject_gp(vcpu, 0U); + update_vmsr = false; + } else if ((!has_monitor_cap()) && (!monitor_cap_buggy())) { + msr_value = msr_read(MSR_IA32_MISC_ENABLE) & ~MSR_IA32_MISC_ENABLE_MONITOR_ENA; + msr_value |= v & MSR_IA32_MISC_ENABLE_MONITOR_ENA; + /* This will not change the return value of has_monitor_cap() since the feature values + * are cached when platform init. + */ + msr_write(MSR_IA32_MISC_ENABLE, msr_value); + } else { + /* Not allow to change MISC_ENABLE_MONITOR_ENA in MSR */ + } + } + + if (update_vmsr) { + vcpu_set_guest_msr(vcpu, MSR_IA32_MISC_ENABLE, v); + } +} + int32_t wrmsr_vmexit_handler(struct acrn_vcpu *vcpu) { int32_t err = 0; @@ -553,6 +606,11 @@ int32_t wrmsr_vmexit_handler(struct acrn_vcpu *vcpu) err = -EACCES; break; } + case MSR_IA32_MISC_ENABLE: + { + set_guest_ia32_misc_enalbe(vcpu, v); + break; + } default: { if (is_x2apic_msr(msr)) { diff --git a/hypervisor/include/arch/x86/cpu_caps.h b/hypervisor/include/arch/x86/cpu_caps.h index 9c25d1da2..47e0aa800 100644 --- a/hypervisor/include/arch/x86/cpu_caps.h +++ b/hypervisor/include/arch/x86/cpu_caps.h @@ -37,6 +37,7 @@ struct cpuinfo_x86 { }; bool has_monitor_cap(void); +bool monitor_cap_buggy(void); bool is_apicv_advanced_feature_supported(void); bool pcpu_has_cap(uint32_t bit); bool pcpu_has_vmx_ept_cap(uint32_t bit_mask);