From d0df39cbb544ee50d367c632851066928fa7ac28 Mon Sep 17 00:00:00 2001 From: Zide Chen Date: Tue, 19 Jun 2018 16:36:41 -0700 Subject: [PATCH] hv: emulate CR0.CD and CR0.NW This patch makes use of IA32_PAT MSR to emulate cache disabled behaviour When the guest is requesting to set CR0.CD: - Keep guest's CR0.CD and CR0.NW bits unchanged - Write IA32_PAT MSR with all-UC entries to change the effective memory type for all GPA to UC for the guest VCPU - It depends on trapping wrmsr to IA32_PAT to prevent any entry in IA32_PAT being changed to non UC type by the guest When the guest is requesting to clear CR0.CD: - restore the content of guest's IA32_PAT MSR Signed-off-by: Zide Chen Acked-by: Eddie Dong --- hypervisor/arch/x86/vmx.c | 52 ++++++++++++++++++++++++------- hypervisor/include/arch/x86/msr.h | 18 +++++++++++ hypervisor/include/arch/x86/vmx.h | 2 +- 3 files changed, 59 insertions(+), 13 deletions(-) diff --git a/hypervisor/arch/x86/vmx.c b/hypervisor/arch/x86/vmx.c index edfa72e8a..0cbf7b936 100644 --- a/hypervisor/arch/x86/vmx.c +++ b/hypervisor/arch/x86/vmx.c @@ -10,15 +10,6 @@ extern struct efi_ctx* efi_ctx; #endif -#define PAT_POWER_ON_VALUE (PAT_MEM_TYPE_WB + \ - ((uint64_t)PAT_MEM_TYPE_WT << 8) + \ - ((uint64_t)PAT_MEM_TYPE_UCM << 16) + \ - ((uint64_t)PAT_MEM_TYPE_UC << 24) + \ - ((uint64_t)PAT_MEM_TYPE_WB << 32) + \ - ((uint64_t)PAT_MEM_TYPE_WT << 40) + \ - ((uint64_t)PAT_MEM_TYPE_UCM << 48) + \ - ((uint64_t)PAT_MEM_TYPE_UC << 56)) - #define REAL_MODE_BSP_INIT_CODE_SEL (0xf000) #define REAL_MODE_DATA_SEG_AR (0x0093) #define REAL_MODE_CODE_SEG_AR (0x009f) @@ -331,7 +322,14 @@ int vmx_wrmsr_pat(struct vcpu *vcpu, uint64_t value) } context->ia32_pat = value; - exec_vmwrite(VMX_GUEST_IA32_PAT_FULL, value); + + /* + * If context->cr0.CD is set, we defer any further requests to write + * guest's IA32_PAT, until the time when guest's CR0.CD is being cleared + */ + if ((context->cr0 & CR0_CD) == 0U) { + exec_vmwrite(VMX_GUEST_IA32_PAT_FULL, value); + } return 0; } @@ -351,8 +349,8 @@ int vmx_wrmsr_pat(struct vcpu *vcpu, uint64_t value) * - WP (16) Trapped to get if it inhibits supervisor level procedures to * write into ro-pages. * - AM (18) Flexible to guest - * - NW (29) Flexible to guest - * - CD (30) Flexible to guest + * - NW (29) Trapped to emulate cache disable situation + * - CD (30) Trapped to emulate cache disable situation * - PG (31) Trapped to track cpu/paging mode. * Set the value according to the value from guest. */ @@ -400,10 +398,40 @@ int vmx_write_cr0(struct vcpu *vcpu, uint64_t cr0) exec_vmwrite64(VMX_GUEST_IA32_EFER_FULL, context->ia32_efer); } + /* If CR0.CD or CR0.NW get changed */ + if (((context->cr0 ^ cr0) & (CR0_CD | CR0_NW)) != 0U) { + if ((cr0 & CR0_CD) == 0U && ((cr0 & CR0_NW) != 0U)) { + pr_err("not allow to set CR0.NW while clearing CR0.CD"); + vcpu_inject_gp(vcpu, 0); + return -EINVAL; + } + + /* No action if only CR0.NW is changed */ + if (((context->cr0 ^ cr0) & CR0_CD) != 0U) { + if ((cr0 & CR0_CD) != 0U) { + /* + * When the guest requests to set CR0.CD, we don't allow + * guest's CR0.CD to be actually set, instead, we write guest + * IA32_PAT with all-UC entries to emulate the cache + * disabled behavior + */ + exec_vmwrite(VMX_GUEST_IA32_PAT_FULL, PAT_ALL_UC_VALUE); + CACHE_FLUSH_INVALIDATE_ALL(); + } else { + /* Restore IA32_PAT to enable cache again */ + exec_vmwrite(VMX_GUEST_IA32_PAT_FULL, context->ia32_pat); + } + vcpu_make_request(vcpu, ACRN_REQUEST_EPT_FLUSH); + } + } + /* CR0 has no always off bits, except the always on bits, and reserved * bits, allow to set according to guest. */ cr0_vmx = cr0_always_on_mask | cr0; + + /* Don't set CD or NW bit to guest */ + cr0_vmx &= ~(CR0_CD | CR0_NW); exec_vmwrite(VMX_GUEST_CR0, cr0_vmx & 0xFFFFFFFFUL); exec_vmwrite(VMX_CR0_READ_SHADOW, cr0 & 0xFFFFFFFFUL); context->cr0 = cr0; diff --git a/hypervisor/include/arch/x86/msr.h b/hypervisor/include/arch/x86/msr.h index 9b8845444..48280f47c 100644 --- a/hypervisor/include/arch/x86/msr.h +++ b/hypervisor/include/arch/x86/msr.h @@ -519,6 +519,24 @@ /* 5 high-order bits in every field are reserved */ #define PAT_FIELD_RSV_BITS (0xF8U) +#define PAT_POWER_ON_VALUE (PAT_MEM_TYPE_WB + \ + ((uint64_t)PAT_MEM_TYPE_WT << 8) + \ + ((uint64_t)PAT_MEM_TYPE_UCM << 16) + \ + ((uint64_t)PAT_MEM_TYPE_UC << 24) + \ + ((uint64_t)PAT_MEM_TYPE_WB << 32) + \ + ((uint64_t)PAT_MEM_TYPE_WT << 40) + \ + ((uint64_t)PAT_MEM_TYPE_UCM << 48) + \ + ((uint64_t)PAT_MEM_TYPE_UC << 56)) + +#define PAT_ALL_UC_VALUE (PAT_MEM_TYPE_UC + \ + ((uint64_t)PAT_MEM_TYPE_UC << 8) + \ + ((uint64_t)PAT_MEM_TYPE_UC << 16) + \ + ((uint64_t)PAT_MEM_TYPE_UC << 24) + \ + ((uint64_t)PAT_MEM_TYPE_UC << 32) + \ + ((uint64_t)PAT_MEM_TYPE_UC << 40) + \ + ((uint64_t)PAT_MEM_TYPE_UC << 48) + \ + ((uint64_t)PAT_MEM_TYPE_UC << 56)) + /* MTRR memory type definitions */ #define MTRR_MEM_TYPE_UC 0x00U /* uncached */ #define MTRR_MEM_TYPE_WC 0x01U /* write combining */ diff --git a/hypervisor/include/arch/x86/vmx.h b/hypervisor/include/arch/x86/vmx.h index 16494ddad..ce45c136f 100644 --- a/hypervisor/include/arch/x86/vmx.h +++ b/hypervisor/include/arch/x86/vmx.h @@ -391,7 +391,7 @@ #define RFLAGS_Z (1U<<6) /* CR0 bits hv want to trap to track status change */ -#define CR0_TRAP_MASK (CR0_PE | CR0_PG | CR0_WP) +#define CR0_TRAP_MASK (CR0_PE | CR0_PG | CR0_WP | CR0_CD | CR0_NW ) #define CR0_RESERVED_MASK ~(CR0_PG | CR0_CD | CR0_NW | CR0_AM | CR0_WP | \ CR0_NE | CR0_ET | CR0_TS | CR0_EM | CR0_MP | CR0_PE)