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 <sainath.grandhi@intel.com>
Signed-off-by: Zide Chen <zide.chen@intel.com>
This commit is contained in:
Zide Chen 2021-07-05 19:02:24 -07:00 committed by wenlingz
parent 8894081379
commit 6d7eb6d7b6
6 changed files with 52 additions and 16 deletions

View File

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

View File

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

View File

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

View File

@ -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 */

View File

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

View File

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