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 <binbin.wu@intel.com>
Acked-by: Eddie Dong <eddie.dong@intel.com>
This commit is contained in:
Binbin Wu 2019-04-30 10:44:54 +08:00 committed by Eddie Dong
parent a0a6eb43c4
commit f0d06165d3
3 changed files with 71 additions and 1 deletions

View File

@ -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;
}
}

View File

@ -8,6 +8,7 @@
#include <errno.h>
#include <pgtable.h>
#include <msr.h>
#include <cpuid.h>
#include <vcpu.h>
#include <vm.h>
#include <vmcs.h>
@ -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)) {

View File

@ -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);