From 3fffa68665781cd49aa08d45a996de6204935b3e Mon Sep 17 00:00:00 2001 From: Shuo A Liu Date: Tue, 11 May 2021 18:54:14 +0800 Subject: [PATCH] hv: Support WAITPKG instructions in guest VM TPAUSE, UMONITOR or UMWAIT instructions execution in guest VM cause a #UD if "enable user wait and pause" (bit 26) of VMX_PROCBASED_CTLS2 is not set. To fix this issue, set the bit 26 of VMX_PROCBASED_CTLS2. Besides, these WAITPKG instructions uses MSR_IA32_UMWAIT_CONTROL. So load corresponding vMSR value during context switch in of a vCPU. Please note, the TPAUSE or UMWAIT instruction causes a VM exit if the "RDTSC exiting" and "enable user wait and pause" are both 1. In ACRN hypervisor, "RDTSC exiting" is always 0. So TPAUSE or UMWAIT doesn't cause a VM exit. Performance impact: MSR_IA32_UMWAIT_CONTROL read costs ~19 cycles; MSR_IA32_UMWAIT_CONTROL write costs ~63 cycles. Tracked-On: #6006 Signed-off-by: Shuo A Liu --- hypervisor/arch/x86/guest/vcpu.c | 8 +++++++ hypervisor/arch/x86/guest/vmcs.c | 2 +- hypervisor/arch/x86/guest/vmsr.c | 23 +++++++++++++++++++ hypervisor/include/arch/x86/asm/cpufeatures.h | 1 + hypervisor/include/arch/x86/asm/guest/vcpu.h | 2 +- hypervisor/include/arch/x86/asm/msr.h | 1 + hypervisor/include/arch/x86/asm/vmx.h | 1 + 7 files changed, 36 insertions(+), 2 deletions(-) diff --git a/hypervisor/arch/x86/guest/vcpu.c b/hypervisor/arch/x86/guest/vcpu.c index 7028374d6..2229b0f3a 100644 --- a/hypervisor/arch/x86/guest/vcpu.c +++ b/hypervisor/arch/x86/guest/vcpu.c @@ -857,6 +857,7 @@ static void context_switch_in(struct thread_object *next) { struct acrn_vcpu *vcpu = container_of(next, struct acrn_vcpu, thread_obj); struct ext_context *ectx = &(vcpu->arch.contexts[vcpu->arch.cur_context].ext_ctx); + uint64_t vmsr_val; load_vmcs(vcpu); @@ -866,6 +867,13 @@ static void context_switch_in(struct thread_object *next) msr_write(MSR_IA32_FMASK, ectx->ia32_fmask); msr_write(MSR_IA32_KERNEL_GS_BASE, ectx->ia32_kernel_gs_base); + if (pcpu_has_cap(X86_FEATURE_WAITPKG)) { + vmsr_val = vcpu_get_guest_msr(vcpu, MSR_IA32_UMWAIT_CONTROL); + if (vmsr_val != msr_read(MSR_IA32_UMWAIT_CONTROL)) { + msr_write(MSR_IA32_UMWAIT_CONTROL, vmsr_val); + } + } + load_iwkey(vcpu); rstore_xsave_area(vcpu, ectx); diff --git a/hypervisor/arch/x86/guest/vmcs.c b/hypervisor/arch/x86/guest/vmcs.c index 8f89f41d2..d5311a4bc 100644 --- a/hypervisor/arch/x86/guest/vmcs.c +++ b/hypervisor/arch/x86/guest/vmcs.c @@ -319,7 +319,7 @@ static void init_exec_ctrl(struct acrn_vcpu *vcpu) value32 = check_vmx_ctrl(MSR_IA32_VMX_PROCBASED_CTLS2, VMX_PROCBASED_CTLS2_VAPIC | VMX_PROCBASED_CTLS2_EPT |VMX_PROCBASED_CTLS2_VPID | VMX_PROCBASED_CTLS2_RDTSCP | VMX_PROCBASED_CTLS2_UNRESTRICT | - VMX_PROCBASED_CTLS2_PAUSE_LOOP); + VMX_PROCBASED_CTLS2_PAUSE_LOOP | VMX_PROCBASED_CTLS2_UWAIT_PAUSE); /* SDM Vol3, 25.3, setting "enable INVPCID" VM-execution to 1 with "INVLPG exiting" disabled, * passes-through INVPCID instruction to guest if the instruction is supported. diff --git a/hypervisor/arch/x86/guest/vmsr.c b/hypervisor/arch/x86/guest/vmsr.c index c25f6bf86..f0fdce57a 100644 --- a/hypervisor/arch/x86/guest/vmsr.c +++ b/hypervisor/arch/x86/guest/vmsr.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -40,6 +41,7 @@ static const uint32_t emulated_guest_msrs[NUM_GUEST_MSRS] = { * MSRs don't need isolation between worlds * Number of entries: NUM_COMMON_MSRS */ + MSR_IA32_UMWAIT_CONTROL, MSR_IA32_TSC_DEADLINE, MSR_IA32_BIOS_UPDT_TRIG, MSR_IA32_BIOS_SIGN_ID, @@ -553,6 +555,16 @@ int32_t rdmsr_vmexit_handler(struct acrn_vcpu *vcpu) } break; } + case MSR_IA32_UMWAIT_CONTROL: + { + /* Feature X86_FEATURE_WAITPKG is always presented */ + if (pcpu_has_cap(X86_FEATURE_WAITPKG)) { + v = vcpu_get_guest_msr(vcpu, msr); + } else { + err = -EACCES; + } + break; + } case MSR_TEST_CTL: { /* If has MSR_TEST_CTL, give emulated value @@ -905,6 +917,17 @@ int32_t wrmsr_vmexit_handler(struct acrn_vcpu *vcpu) } break; } + case MSR_IA32_UMWAIT_CONTROL: + { + /* Feature X86_FEATURE_WAITPKG is always presented */ + if (pcpu_has_cap(X86_FEATURE_WAITPKG)) { + vcpu_set_guest_msr(vcpu, msr, v); + msr_write(msr, v); + } else { + err = -EACCES; + } + break; + } case MSR_TEST_CTL: { /* If VM has MSR_TEST_CTL, ignore write operation diff --git a/hypervisor/include/arch/x86/asm/cpufeatures.h b/hypervisor/include/arch/x86/asm/cpufeatures.h index d8447c096..80c9b26fd 100644 --- a/hypervisor/include/arch/x86/asm/cpufeatures.h +++ b/hypervisor/include/arch/x86/asm/cpufeatures.h @@ -80,6 +80,7 @@ #define X86_FEATURE_CLFLUSHOPT ((FEAT_7_0_EBX << 5U) + 23U) /* Intel-defined CPU features, CPUID level 0x00000007 (ECX)*/ +#define X86_FEATURE_WAITPKG ((FEAT_7_0_ECX << 5U) + 5U) #define X86_FEATURE_KEYLOCKER ((FEAT_7_0_ECX << 5U) + 23U) /* Intel-defined CPU features, CPUID level 0x00000007 (EDX)*/ diff --git a/hypervisor/include/arch/x86/asm/guest/vcpu.h b/hypervisor/include/arch/x86/asm/guest/vcpu.h index 11c339a67..d5b8f5b8d 100644 --- a/hypervisor/include/arch/x86/asm/guest/vcpu.h +++ b/hypervisor/include/arch/x86/asm/guest/vcpu.h @@ -171,7 +171,7 @@ enum reset_mode; #define SECURE_WORLD 1 #define NUM_WORLD_MSRS 2U -#define NUM_COMMON_MSRS 21U +#define NUM_COMMON_MSRS 22U #define NUM_GUEST_MSRS (NUM_WORLD_MSRS + NUM_COMMON_MSRS) #define EOI_EXIT_BITMAP_SIZE 256U diff --git a/hypervisor/include/arch/x86/asm/msr.h b/hypervisor/include/arch/x86/asm/msr.h index 4ed2185a0..00ed0a289 100644 --- a/hypervisor/include/arch/x86/asm/msr.h +++ b/hypervisor/include/arch/x86/asm/msr.h @@ -42,6 +42,7 @@ #define MSR_IA32_PMC6 0x000000C7U #define MSR_IA32_PMC7 0x000000C8U #define MSR_IA32_CORE_CAPABILITIES 0x000000CFU +#define MSR_IA32_UMWAIT_CONTROL 0x000000E1U /* Max. qualified performance clock counter */ #define MSR_IA32_MPERF 0x000000E7U /* Actual performance clock counter */ diff --git a/hypervisor/include/arch/x86/asm/vmx.h b/hypervisor/include/arch/x86/asm/vmx.h index a5f1880ff..ab19babd1 100644 --- a/hypervisor/include/arch/x86/asm/vmx.h +++ b/hypervisor/include/arch/x86/asm/vmx.h @@ -317,6 +317,7 @@ #define VMX_PROCBASED_CTLS2_RDSEED (1U<<16U) #define VMX_PROCBASED_CTLS2_EPT_VE (1U<<18U) #define VMX_PROCBASED_CTLS2_XSVE_XRSTR (1U<<20U) +#define VMX_PROCBASED_CTLS2_UWAIT_PAUSE (1U<<26U) #define VMX_PROCBASED_CTLS3_LOADIWKEY (1U<<0U) /* MSR_IA32_VMX_EPT_VPID_CAP: EPT and VPID capability bits */