From 6d7eb6d7b65f4801a0fd78d94d5147474302f7e8 Mon Sep 17 00:00:00 2001 From: Zide Chen Date: Mon, 5 Jul 2021 19:02:24 -0700 Subject: [PATCH] hv: emulate IA32_EFER and adjust Load EFER VMX controls This helps to improve performance: - Don't need to execute VMREAD in vcpu_get_efer(), which is frequently called. - VMX_EXIT_CTLS_SAVE_EFER can be removed from VM-Exit Controls. - If the value of IA32_EFER MSR is identical between the host and guest (highly likely), adjust the VMX controls not to load IA32_EFER on VMExit and VMEntry. It's convenient to continue use the exiting vcpu_s/get_efer() APIs, other than the common vcpu_s/get_guest_msr(). Tracked-On: #6289 Signed-off-by: Sainath Grandhi Signed-off-by: Zide Chen --- hypervisor/arch/x86/guest/vcpu.c | 14 ++++++++++---- hypervisor/arch/x86/guest/vmcs.c | 14 ++++---------- hypervisor/arch/x86/guest/vmsr.c | 11 +++++++++++ hypervisor/arch/x86/mmu.c | 8 +++++++- hypervisor/include/arch/x86/asm/guest/vcpu.h | 2 +- hypervisor/include/arch/x86/asm/guest/vmcs.h | 19 +++++++++++++++++++ 6 files changed, 52 insertions(+), 16 deletions(-) diff --git a/hypervisor/arch/x86/guest/vcpu.c b/hypervisor/arch/x86/guest/vcpu.c index d349e202d..4462e09b2 100644 --- a/hypervisor/arch/x86/guest/vcpu.c +++ b/hypervisor/arch/x86/guest/vcpu.c @@ -92,10 +92,6 @@ uint64_t vcpu_get_efer(struct acrn_vcpu *vcpu) struct run_context *ctx = &vcpu->arch.contexts[vcpu->arch.cur_context].run_ctx; - if (!bitmap_test(CPU_REG_EFER, &vcpu->reg_updated) && - !bitmap_test_and_set_lock(CPU_REG_EFER, &vcpu->reg_cached)) { - ctx->ia32_efer = exec_vmread64(VMX_GUEST_IA32_EFER_FULL); - } return ctx->ia32_efer; } @@ -103,6 +99,16 @@ void vcpu_set_efer(struct acrn_vcpu *vcpu, uint64_t val) { vcpu->arch.contexts[vcpu->arch.cur_context].run_ctx.ia32_efer = val; + + if (val == msr_read(MSR_IA32_EFER)) { + clear_vmcs_bit(VMX_ENTRY_CONTROLS, VMX_ENTRY_CTLS_LOAD_EFER); + clear_vmcs_bit(VMX_EXIT_CONTROLS, VMX_EXIT_CTLS_LOAD_EFER); + } else { + set_vmcs_bit(VMX_ENTRY_CONTROLS, VMX_ENTRY_CTLS_LOAD_EFER); + set_vmcs_bit(VMX_EXIT_CONTROLS, VMX_EXIT_CTLS_LOAD_EFER); + } + + /* Write the new value to VMCS in either case */ bitmap_set_lock(CPU_REG_EFER, &vcpu->reg_updated); } diff --git a/hypervisor/arch/x86/guest/vmcs.c b/hypervisor/arch/x86/guest/vmcs.c index 9f12d59b5..e7f66a24b 100644 --- a/hypervisor/arch/x86/guest/vmcs.c +++ b/hypervisor/arch/x86/guest/vmcs.c @@ -481,12 +481,8 @@ static void init_entry_ctrl(const struct acrn_vcpu *vcpu) /* Log messages to show initializing VMX entry controls */ pr_dbg("Initialize Entry control "); - /* Set up VMX entry controls - pg 2908 24.8.1 * Set IA32e guest mode - - * on VM entry processor is in IA32e 64 bitmode * Start guest with host - * IA32_PAT and IA32_EFER - */ - value32 = (VMX_ENTRY_CTLS_LOAD_EFER | - VMX_ENTRY_CTLS_LOAD_PAT); + /* Set up VMX entry controls - ISDM 24.8.1 */ + value32 = VMX_ENTRY_CTLS_LOAD_PAT; if (get_vcpu_mode(vcpu) == CPU_MODE_64BIT) { value32 |= (VMX_ENTRY_CTLS_IA32E_MODE); @@ -525,13 +521,11 @@ static void init_exit_ctrl(const struct acrn_vcpu *vcpu) * size is 64 bit Set up to acknowledge interrupt on exit, if 1 the HW * acks the interrupt in VMX non-root and saves the interrupt vector to * the relevant VM exit field for further processing by Hypervisor - * Enable saving and loading of IA32_PAT and IA32_EFER on VMEXIT Enable - * saving of pre-emption timer on VMEXIT + * Enable saving and loading IA32_PAT on VMEXIT */ value32 = check_vmx_ctrl(MSR_IA32_VMX_EXIT_CTLS, VMX_EXIT_CTLS_ACK_IRQ | VMX_EXIT_CTLS_SAVE_PAT | - VMX_EXIT_CTLS_LOAD_PAT | VMX_EXIT_CTLS_LOAD_EFER | - VMX_EXIT_CTLS_SAVE_EFER | VMX_EXIT_CTLS_HOST_ADDR64); + VMX_EXIT_CTLS_LOAD_PAT | VMX_EXIT_CTLS_HOST_ADDR64); exec_vmwrite32(VMX_EXIT_CONTROLS, value32); pr_dbg("VMX_EXIT_CONTROL: 0x%x ", value32); diff --git a/hypervisor/arch/x86/guest/vmsr.c b/hypervisor/arch/x86/guest/vmsr.c index eb2a81de2..28b37763e 100644 --- a/hypervisor/arch/x86/guest/vmsr.c +++ b/hypervisor/arch/x86/guest/vmsr.c @@ -37,6 +37,7 @@ static const uint32_t emulated_guest_msrs[NUM_GUEST_MSRS] = { * Number of entries: NUM_WORLD_MSRS */ MSR_IA32_PAT, + MSR_IA32_EFER, MSR_IA32_TSC_ADJUST, /* @@ -523,6 +524,11 @@ int32_t rdmsr_vmexit_handler(struct acrn_vcpu *vcpu) v = vcpu_get_guest_msr(vcpu, MSR_IA32_PAT); break; } + case MSR_IA32_EFER: + { + v = vcpu_get_efer(vcpu); + break; + } case MSR_IA32_APIC_BASE: { /* Read APIC base */ @@ -873,6 +879,11 @@ int32_t wrmsr_vmexit_handler(struct acrn_vcpu *vcpu) err = write_pat_msr(vcpu, v); break; } + case MSR_IA32_EFER: + { + vcpu_set_efer(vcpu, v); + break; + } case MSR_IA32_APIC_BASE: { err = vlapic_set_apicbase(vcpu_vlapic(vcpu), v); diff --git a/hypervisor/arch/x86/mmu.c b/hypervisor/arch/x86/mmu.c index 15b506e95..dcae12f58 100644 --- a/hypervisor/arch/x86/mmu.c +++ b/hypervisor/arch/x86/mmu.c @@ -160,7 +160,13 @@ void enable_paging(void) * instruction fetching from pages with XD bit set. */ tmp64 = msr_read(MSR_IA32_EFER); - tmp64 |= MSR_IA32_EFER_NXE_BIT; + + /* + * SCE bit is not used by the host. However we set this bit so that + * it's highly likely that the value of IA32_EFER the host and the guest + * is identical, and we don't need to switch this MSR on VMX transitions + */ + tmp64 |= MSR_IA32_EFER_NXE_BIT | MSR_IA32_EFER_SCE_BIT; msr_write(MSR_IA32_EFER, tmp64); /* Enable Write Protect, inhibiting writing to read-only pages */ diff --git a/hypervisor/include/arch/x86/asm/guest/vcpu.h b/hypervisor/include/arch/x86/asm/guest/vcpu.h index 4f1ed34fe..e626921de 100644 --- a/hypervisor/include/arch/x86/asm/guest/vcpu.h +++ b/hypervisor/include/arch/x86/asm/guest/vcpu.h @@ -172,7 +172,7 @@ enum reset_mode; #define SECURE_WORLD 1 #define NUM_WORLD_MSRS 2U -#define NUM_COMMON_MSRS 22U +#define NUM_COMMON_MSRS 23U #ifdef CONFIG_NVMX_ENABLED #define NUM_GUEST_MSRS (NUM_WORLD_MSRS + NUM_COMMON_MSRS + NUM_VMX_MSRS) #else diff --git a/hypervisor/include/arch/x86/asm/guest/vmcs.h b/hypervisor/include/arch/x86/asm/guest/vmcs.h index 5391686fa..d1738b045 100644 --- a/hypervisor/include/arch/x86/asm/guest/vmcs.h +++ b/hypervisor/include/arch/x86/asm/guest/vmcs.h @@ -40,6 +40,25 @@ static inline uint64_t apic_access_offset(uint64_t qual) { return (qual & APIC_ACCESS_OFFSET); } + +static inline void clear_vmcs_bit(uint32_t vmcs_field, uint32_t bit) +{ + uint64_t val64; + + val64 = exec_vmread(vmcs_field); + val64 &= ~bit; + exec_vmwrite(vmcs_field, val64); +} + +static inline void set_vmcs_bit(uint32_t vmcs_field, uint32_t bit) +{ + uint64_t val64; + + val64 = exec_vmread(vmcs_field); + val64 |= bit; + exec_vmwrite(vmcs_field, val64); +} + void init_vmcs(struct acrn_vcpu *vcpu); void load_vmcs(const struct acrn_vcpu *vcpu); void init_host_state(void);