From c2283743f0952c14e0920dff003927771a80fbe2 Mon Sep 17 00:00:00 2001 From: Zide Chen Date: Tue, 29 May 2018 17:41:52 -0700 Subject: [PATCH] hv: basic MTRR virtualization Linux commit edfe63ec97ed ("x86/mtrr: Fix Xorg crashes in Qemu sessions") disables PAT feature if MTRR is not enabled. This patch does partial emulation of MTRR to prevent this from happening: enable fixed-range MTRRs and disable virable range MTRRs By default IA32_PAT MSR (SDM Vol3 11.12.4, Table 11-12) doesn't include 'WC' type. If MTRR is disabled from the guests, Linux doesn't allow writing IA32_PAT MSR so WC type can't be enabled. This creates some performance issues for certian applications that rely on WC memory type. Implementation summary: - Enable MTRR feature: MTRRdefType.E=1 - Enable fixed range MTRRs: MTRRCAP.fix=1, MTRRdefType.FE=1 - For simplicity, disable variable range MTRRs: MTRRCAP.vcnt=0. It's expected that this bit is honored by the guests and they won't change the guest memory type through variable MTRRs. Signed-off-by: bliu11 Signed-off-by: Zide Chen Reviewed-by: Jason Chen CJ Acked-by: Eddie Dong --- hypervisor/Makefile | 1 + hypervisor/arch/x86/mtrr.c | 163 +++++++++++++++++++++++ hypervisor/include/arch/x86/guest/vcpu.h | 3 + hypervisor/include/arch/x86/hv_arch.h | 1 + hypervisor/include/arch/x86/mtrr.h | 51 +++++++ 5 files changed, 219 insertions(+) create mode 100755 hypervisor/arch/x86/mtrr.c create mode 100755 hypervisor/include/arch/x86/mtrr.h diff --git a/hypervisor/Makefile b/hypervisor/Makefile index c44402c89..cb9d4acf1 100644 --- a/hypervisor/Makefile +++ b/hypervisor/Makefile @@ -104,6 +104,7 @@ C_SRCS += arch/x86/vmx.c C_SRCS += arch/x86/assign.c C_SRCS += arch/x86/trusty.c C_SRCS += arch/x86/cpu_state_tbl.c +C_SRCS += arch/x86/mtrr.c C_SRCS += arch/x86/guest/vcpu.c C_SRCS += arch/x86/guest/vm.c C_SRCS += arch/x86/guest/instr_emul_wrapper.c diff --git a/hypervisor/arch/x86/mtrr.c b/hypervisor/arch/x86/mtrr.c new file mode 100755 index 000000000..5e2f1b617 --- /dev/null +++ b/hypervisor/arch/x86/mtrr.c @@ -0,0 +1,163 @@ +/* + * Copyright (C) <2018> Intel Corporation + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include + +#ifdef CONFIG_MTRR_ENABLED + +#define MTRR_FIXED_RANGE_ALL_WB ((uint64_t)MTRR_MEM_TYPE_WB \ + | (((uint64_t)MTRR_MEM_TYPE_WB) << 8) \ + | (((uint64_t)MTRR_MEM_TYPE_WB) << 16) \ + | (((uint64_t)MTRR_MEM_TYPE_WB) << 24) \ + | (((uint64_t)MTRR_MEM_TYPE_WB) << 32) \ + | (((uint64_t)MTRR_MEM_TYPE_WB) << 40) \ + | (((uint64_t)MTRR_MEM_TYPE_WB) << 48) \ + | (((uint64_t)MTRR_MEM_TYPE_WB) << 56)) + +struct fixed_range_mtrr_maps { + uint32_t msr; + uint32_t start; + uint32_t sub_range_size; +}; + +#define MAX_FIXED_RANGE_ADDR 0x100000 +static struct fixed_range_mtrr_maps fixed_mtrr_map[FIXED_RANGE_MTRR_NUM] = { + { MSR_IA32_MTRR_FIX64K_00000, 0x0, 0x10000 }, + { MSR_IA32_MTRR_FIX16K_80000, 0x80000, 0x4000 }, + { MSR_IA32_MTRR_FIX16K_A0000, 0xA0000, 0x4000 }, + { MSR_IA32_MTRR_FIX4K_C0000, 0xC0000, 0x1000 }, + { MSR_IA32_MTRR_FIX4K_C8000, 0xC8000, 0x1000 }, + { MSR_IA32_MTRR_FIX4K_D0000, 0xD0000, 0x1000 }, + { MSR_IA32_MTRR_FIX4K_D8000, 0xD8000, 0x1000 }, + { MSR_IA32_MTRR_FIX4K_E0000, 0xE0000, 0x1000 }, + { MSR_IA32_MTRR_FIX4K_E8000, 0xE8000, 0x1000 }, + { MSR_IA32_MTRR_FIX4K_F0000, 0xF0000, 0x1000 }, + { MSR_IA32_MTRR_FIX4K_F8000, 0xF8000, 0x1000 }, +}; + +int is_fixed_range_mtrr(uint32_t msr) +{ + return (msr >= fixed_mtrr_map[0].msr) + && (msr <= fixed_mtrr_map[FIXED_RANGE_MTRR_NUM - 1].msr); +} + +static int get_index_of_fixed_mtrr(uint32_t msr) +{ + int i; + + for (i = 0; i < FIXED_RANGE_MTRR_NUM; i++) { + if (fixed_mtrr_map[i].msr == msr) + break; + } + return i; +} + +int get_subrange_size_of_fixed_mtrr(int subrange_id) +{ + return fixed_mtrr_map[subrange_id].sub_range_size; +} + +int get_subrange_start_of_fixed_mtrr(int index, int subrange_id) +{ + return (fixed_mtrr_map[index].start + subrange_id * + get_subrange_size_of_fixed_mtrr(index)); +} + +int get_subrange_end_of_fixed_mtrr(int index, int subrange_id) +{ + return (get_subrange_start_of_fixed_mtrr(index, subrange_id) + + get_subrange_size_of_fixed_mtrr(index) - 1); +} + +static inline bool is_mtrr_enabled(struct vcpu *vcpu) +{ + return vcpu->mtrr.def_type.bits.enable; +} + +static inline bool is_fixed_range_mtrr_enabled(struct vcpu *vcpu) +{ + return (vcpu->mtrr.cap.bits.fix && + vcpu->mtrr.def_type.bits.fixed_enable); +} + +static inline uint8_t get_default_memory_type(struct vcpu *vcpu) +{ + return (uint8_t)(vcpu->mtrr.def_type.bits.type); +} + +void init_mtrr(struct vcpu *vcpu) +{ + union mtrr_cap_reg cap = {0}; + int i; + + /* + * We emulate fixed range MTRRs only + * And expecting the guests won't write variable MTRRs + * since MTRRCap.vcnt is 0 + */ + vcpu->mtrr.cap.bits.vcnt = 0; + vcpu->mtrr.cap.bits.fix = 1; + vcpu->mtrr.def_type.bits.enable = 1; + vcpu->mtrr.def_type.bits.fixed_enable = 1; + vcpu->mtrr.def_type.bits.type = MTRR_MEM_TYPE_UC; + + if (is_vm0(vcpu->vm) && cpu_has_cap(X86_FEATURE_MTRR)) { + cap.value = msr_read(MSR_IA32_MTRR_CAP); + } + + for (i = 0; i < FIXED_RANGE_MTRR_NUM; i++) { + if (cap.bits.fix) { + /* + * The system firmware runs in VMX non-root mode on VM0. + * In some cases, the firmware needs particular mem type at + * certain mmeory locations (e.g. UC for some hardware + * registers), so we need to configure EPT according to the + * content of physical MTRRs. + */ + vcpu->mtrr.fixed_range[i].value = msr_read(fixed_mtrr_map[i].msr); + } else { + /* + * For non-vm0 EPT, all memory is setup with WB type in EPT, + * so we setup fixed range MTRRs accordingly + */ + vcpu->mtrr.fixed_range[i].value = MTRR_FIXED_RANGE_ALL_WB; + } + + pr_dbg("vm%d vcpu%d fixed-range MTRR[%d]: %16llx", + vcpu->vm->attr.id, vcpu->vcpu_id, i, + vcpu->mtrr.fixed_range[i].value); + } +} + +void mtrr_wrmsr(struct vcpu *vcpu, uint32_t msr, uint64_t value) +{ + if (msr == MSR_IA32_MTRR_DEF_TYPE) { + if (vcpu->mtrr.def_type.value != value) { + vcpu->mtrr.def_type.value = value; + } + } else if (is_fixed_range_mtrr(msr)) + vcpu->mtrr.fixed_range[get_index_of_fixed_mtrr(msr)].value = value; + else + pr_err("Write to unexpected MSR: 0x%x", msr); +} + +uint64_t mtrr_rdmsr(struct vcpu *vcpu, uint32_t msr) +{ + struct mtrr_state *mtrr = &vcpu->mtrr; + uint64_t ret = 0; + + if (msr == MSR_IA32_MTRR_CAP) + ret = mtrr->cap.value; + else if (msr == MSR_IA32_MTRR_DEF_TYPE) + ret = mtrr->def_type.value; + else if (is_fixed_range_mtrr(msr)) + ret = mtrr->fixed_range[get_index_of_fixed_mtrr(msr)].value; + else + pr_err("read unexpected MSR: 0x%x", msr); + + return ret; +} + +#endif /* CONFIG_MTRR_ENABLED */ diff --git a/hypervisor/include/arch/x86/guest/vcpu.h b/hypervisor/include/arch/x86/guest/vcpu.h index fe6807036..3a02f4698 100644 --- a/hypervisor/include/arch/x86/guest/vcpu.h +++ b/hypervisor/include/arch/x86/guest/vcpu.h @@ -246,6 +246,9 @@ struct vcpu { */ uint64_t msr_tsc_aux_guest; uint64_t *guest_msrs; +#ifdef CONFIG_MTRR_ENABLED + struct mtrr_state mtrr; +#endif }; #define is_vcpu_bsp(vcpu) ((vcpu)->vcpu_id == 0) diff --git a/hypervisor/include/arch/x86/hv_arch.h b/hypervisor/include/arch/x86/hv_arch.h index c832e00f9..263b0b4f9 100644 --- a/hypervisor/include/arch/x86/hv_arch.h +++ b/hypervisor/include/arch/x86/hv_arch.h @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include diff --git a/hypervisor/include/arch/x86/mtrr.h b/hypervisor/include/arch/x86/mtrr.h new file mode 100755 index 000000000..96dcaa421 --- /dev/null +++ b/hypervisor/include/arch/x86/mtrr.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) <2018> Intel Corporation + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef MTRR_H +#define MTRR_H + +#define FIXED_RANGE_MTRR_NUM 11 +#define MTRR_SUB_RANGE_NUM 8 + +union mtrr_cap_reg { + uint64_t value; + struct { + uint64_t vcnt:8; + uint64_t fix:1; + uint64_t res0:1; + uint64_t wc:1; + uint64_t res1:21; + uint64_t res2:32; + } bits; +}; + +union mtrr_def_type_reg { + uint64_t value; + struct { + uint64_t type:8; + uint64_t res0:2; + uint64_t fixed_enable:1; + uint64_t enable:1; + uint64_t res1:20; + uint64_t res2:32; + } bits; +}; + +union mtrr_fixed_range_reg { + uint64_t value; + uint8_t type[MTRR_SUB_RANGE_NUM]; +}; + +struct mtrr_state { + union mtrr_cap_reg cap; + union mtrr_def_type_reg def_type; + union mtrr_fixed_range_reg fixed_range[FIXED_RANGE_MTRR_NUM]; +}; + +void mtrr_wrmsr(struct vcpu *vcpu, uint32_t msr, uint64_t value); +uint64_t mtrr_rdmsr(struct vcpu *vcpu, uint32_t msr); +void init_mtrr(struct vcpu *vcpu); + +#endif /* MTRR_H */