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 <baohong.liu@intel.com> Signed-off-by: Zide Chen <zide.chen@intel.com> Reviewed-by: Jason Chen CJ <jason.cj.chen@intel.com> Acked-by: Eddie Dong <eddie.dong@intel.com>
This commit is contained in:
parent
5d2ab4d9ef
commit
c2283743f0
|
@ -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
|
||||
|
|
|
@ -0,0 +1,163 @@
|
|||
/*
|
||||
* Copyright (C) <2018> Intel Corporation
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include <hypervisor.h>
|
||||
|
||||
#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 */
|
|
@ -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)
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include <lapic.h>
|
||||
#include <msr.h>
|
||||
#include <io.h>
|
||||
#include <mtrr.h>
|
||||
#include <vcpu.h>
|
||||
#include <trusty.h>
|
||||
#include <pm.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 */
|
Loading…
Reference in New Issue