From 55d9161138b54b4307882cadd115c4d6aaa99578 Mon Sep 17 00:00:00 2001 From: Zide Chen Date: Thu, 29 Apr 2021 12:23:42 -0700 Subject: [PATCH] hv: nested: support for VMXON emulation This patch emulates VMXON instruction. Basically checks some prerequisites to enable VMX operation on L1 guest (next patch), and prepares some virtual hardware environment in L0. Tracked-On: #5923 Signed-off-by: Sainath Grandhi Signed-off-by: Zide Chen Acked-by: Eddie Dong --- hypervisor/arch/x86/guest/nested.c | 41 +++++++++++++++++++ hypervisor/arch/x86/guest/vmexit.c | 12 ++++-- hypervisor/include/arch/x86/asm/cpu.h | 4 ++ .../include/arch/x86/asm/guest/nested.h | 14 +++++++ hypervisor/include/arch/x86/asm/guest/vcpu.h | 3 ++ 5 files changed, 70 insertions(+), 4 deletions(-) diff --git a/hypervisor/arch/x86/guest/nested.c b/hypervisor/arch/x86/guest/nested.c index 8a1d23e3a..d710b5405 100644 --- a/hypervisor/arch/x86/guest/nested.c +++ b/hypervisor/arch/x86/guest/nested.c @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -220,3 +221,43 @@ int32_t read_vmx_msr(struct acrn_vcpu *vcpu, uint32_t msr, uint64_t *val) *val = v; return err; } + +void nested_vmx_result(enum VMXResult result, int error_number) +{ + uint64_t rflags = exec_vmread(VMX_GUEST_RFLAGS); + + /* ISDM: section 30.2 CONVENTIONS */ + rflags &= ~(RFLAGS_C | RFLAGS_P | RFLAGS_A | RFLAGS_Z | RFLAGS_S | RFLAGS_O); + + if (result == VMfailValid) { + rflags |= RFLAGS_Z; + exec_vmwrite(VMX_INSTR_ERROR, error_number); + } else if (result == VMfailInvalid) { + rflags |= RFLAGS_C; + } else { + /* VMsucceed, do nothing */ + } + + if (result != VMsucceed) { + pr_err("VMX failed: %d/%d", result, error_number); + } + + exec_vmwrite(VMX_GUEST_RFLAGS, rflags); +} + +/* + * @pre vcpu != NULL + */ +int32_t vmxon_vmexit_handler(struct acrn_vcpu *vcpu) +{ + /* Will do permission check in next patch */ + if (is_nvmx_configured(vcpu->vm)) { + vcpu->arch.nested.vmxon = true; + + nested_vmx_result(VMsucceed, 0); + } else { + vcpu_inject_ud(vcpu); + } + + return 0; +} diff --git a/hypervisor/arch/x86/guest/vmexit.c b/hypervisor/arch/x86/guest/vmexit.c index c62e136e2..0c8ed839b 100644 --- a/hypervisor/arch/x86/guest/vmexit.c +++ b/hypervisor/arch/x86/guest/vmexit.c @@ -94,8 +94,14 @@ static const struct vm_exit_dispatch dispatch_table[NR_VMX_EXIT_REASONS] = { .handler = undefined_vmexit_handler}, [VMX_EXIT_REASON_VMXOFF] = { .handler = undefined_vmexit_handler}, +#ifndef CONFIG_NVMX_ENABLED [VMX_EXIT_REASON_VMXON] = { .handler = undefined_vmexit_handler}, +#else + [VMX_EXIT_REASON_VMXON] = { + .handler = vmxon_vmexit_handler, + .need_exit_qualification = 1}, +#endif [VMX_EXIT_REASON_CR_ACCESS] = { .handler = cr_access_vmexit_handler, .need_exit_qualification = 1}, @@ -460,10 +466,8 @@ static int32_t loadiwkey_vmexit_handler(struct acrn_vcpu *vcpu) return 0; } -/* vmexit handler for just injecting a #UD exception - * - * ACRN doesn't support nested virtualization, the following VMExit will inject #UD - * VMCLEAR/VMLAUNCH/VMPTRST/VMREAD/VMRESUME/VMWRITE/VMXOFF/VMXON. +/* + * vmexit handler for just injecting a #UD exception * ACRN doesn't enable VMFUNC, VMFUNC treated as undefined. */ static int32_t undefined_vmexit_handler(struct acrn_vcpu *vcpu) diff --git a/hypervisor/include/arch/x86/asm/cpu.h b/hypervisor/include/arch/x86/asm/cpu.h index a1b4d806c..79bd41af0 100644 --- a/hypervisor/include/arch/x86/asm/cpu.h +++ b/hypervisor/include/arch/x86/asm/cpu.h @@ -133,7 +133,11 @@ #define EFER_LMA 0x00000400U /* Long mode active (R) */ #define RFLAGS_C (1U<<0U) +#define RFLAGS_P (1U<<2U) +#define RFLAGS_A (1U<<4U) #define RFLAGS_Z (1U<<6U) +#define RFLAGS_S (1U<<7U) +#define RFLAGS_O (1U<<11U) #define RFLAGS_AC (1U<<18U) /* CPU clock frequencies (FSB) */ diff --git a/hypervisor/include/arch/x86/asm/guest/nested.h b/hypervisor/include/arch/x86/asm/guest/nested.h index 930ba99cc..d85ffc072 100644 --- a/hypervisor/include/arch/x86/asm/guest/nested.h +++ b/hypervisor/include/arch/x86/asm/guest/nested.h @@ -51,11 +51,25 @@ union value_64 { */ #define VMCS12_REVISION_ID 0x15407E12U +enum VMXResult { + VMsucceed, + VMfailValid, + VMfailInvalid, +}; +void nested_vmx_result(enum VMXResult, int error_number); +int32_t vmxon_vmexit_handler(struct acrn_vcpu *vcpu); + #ifdef CONFIG_NVMX_ENABLED +struct acrn_nested { + bool vmxon; /* To indicate if vCPU entered VMX operation */ +} __aligned(PAGE_SIZE); + bool is_vmx_msr(uint32_t msr); void init_vmx_msrs(struct acrn_vcpu *vcpu); int32_t read_vmx_msr(__unused struct acrn_vcpu *vcpu, uint32_t msr, uint64_t *val); #else +struct acrn_nested {}; + static inline bool is_vmx_msr(__unused uint32_t msr) { /* diff --git a/hypervisor/include/arch/x86/asm/guest/vcpu.h b/hypervisor/include/arch/x86/asm/guest/vcpu.h index 42216eeb6..0f29dd9e6 100644 --- a/hypervisor/include/arch/x86/asm/guest/vcpu.h +++ b/hypervisor/include/arch/x86/asm/guest/vcpu.h @@ -219,6 +219,9 @@ struct acrn_vcpu_arch { /* vmcs region for this vcpu, MUST be 4KB-aligned */ uint8_t vmcs[PAGE_SIZE]; + /* context for nested virtualization, 4KB-aligned */ + struct acrn_nested nested; + /* MSR bitmap region for this vcpu, MUST be 4-Kbyte aligned */ uint8_t msr_bitmap[PAGE_SIZE];