From 2c17b88c9578843c505dda71781572dc5d67cd94 Mon Sep 17 00:00:00 2001 From: Zide Chen Date: Thu, 29 Apr 2021 13:27:50 -0700 Subject: [PATCH] hv: nested: support for VMCLEAR emulation This patch is to emulate VMCLEAR instruction. L1 hypervisor issues VMCLEAR on a VMCS12 whose state could be any of these: active and current, active but not current, not yet VMPTRLDed. To emulate the VMCLEAR instruction, ACRN sets the VMCS12 launch state to "clear", and if L0 already cached this VMCS12, need to sync it back to guest memory: - sync shadow fields from shadow VMCS VMCS to cache VMCS12 - copy cache VMCS12 to L1 guest memory Tracked-On: #5923 Signed-off-by: Sainath Grandhi Signed-off-by: Zide Chen --- hypervisor/arch/x86/guest/nested.c | 71 +++++++++++++++++++ hypervisor/arch/x86/guest/vmexit.c | 7 +- .../include/arch/x86/asm/guest/nested.h | 5 ++ 3 files changed, 81 insertions(+), 2 deletions(-) diff --git a/hypervisor/arch/x86/guest/nested.c b/hypervisor/arch/x86/guest/nested.c index bfc587b5f..d14eed7fd 100644 --- a/hypervisor/arch/x86/guest/nested.c +++ b/hypervisor/arch/x86/guest/nested.c @@ -984,6 +984,77 @@ int32_t vmptrld_vmexit_handler(struct acrn_vcpu *vcpu) return 0; } +/* + * @pre vcpu != NULL + */ +int32_t vmclear_vmexit_handler(struct acrn_vcpu *vcpu) +{ + struct acrn_nested *nested = &vcpu->arch.nested; + uint64_t vmcs12_gpa; + + if (check_vmx_permission(vcpu)) { + vmcs12_gpa = get_vmptr_gpa(vcpu); + + if (!validate_vmptr_gpa(vmcs12_gpa)) { + nested_vmx_result(VMfailValid, VMXERR_VMPTRLD_INVALID_ADDRESS); + } else if (vmcs12_gpa == nested->vmxon_ptr) { + nested_vmx_result(VMfailValid, VMXERR_VMCLEAR_VMXON_POINTER); + } else { + if (vcpu->arch.nested.current_vmcs12_ptr == vmcs12_gpa) { + /* + * The target VMCS12 is active and current. + * VMCS02 is active and being used as a shadow VMCS. + */ + + nested->vmcs12.launch_state = VMCS12_LAUNCH_STATE_CLEAR; + + /* + * Disable VMCS shadowing to avoid VMCS02 will be loaded by VMPTRLD + * and referenced by VMCS01 as a shadow VMCS simultaneously. + * + * After this VMCLEAR, there is no active VMCS12 on this vCPU, so the + * "VMCS shadowing" VM-execution control must be 0. + */ + disable_vmcs_shadowing(); + + /* Flush shadow VMCS to memory */ + clear_va_vmcs(nested->vmcs02); + + /* + * upon VMCLEAR vmcs12, need to sync the cache VMCS12 back to guest memory. + * VMPTRLD the shadow VMCS so that we are able to sync it to VMCS12. + */ + load_va_vmcs(nested->vmcs02); + + /* Sync shadow VMCS to cache VMCS12, and copy cache VMCS12 to L1 guest */ + flush_current_vmcs12(vcpu); + + /* VMCLEAR VMCS02 */ + clear_va_vmcs(nested->vmcs02); + + /* Switch back to vmcs01 (no VMCS shadowing) */ + load_va_vmcs(vcpu->arch.vmcs); + + /* no current VMCS12 */ + nested->current_vmcs12_ptr = INVALID_GPA; + } else { + /* + * we need to update the VMCS12 launch state in L1 memory in these two cases: + * - L1 hypervisor VMCLEAR a VMCS12 that is already flushed by ACRN to L1 guest + * - L1 hypervisor VMCLEAR a never VMPTRLDed VMCS12. + */ + uint32_t launch_state = VMCS12_LAUNCH_STATE_CLEAR; + (void)copy_to_gpa(vcpu->vm, &launch_state, vmcs12_gpa + + offsetof(struct acrn_vmcs12, launch_state), sizeof(launch_state)); + } + + nested_vmx_result(VMsucceed, 0); + } + } + + return 0; +} + void init_nested_vmx(__unused struct acrn_vm *vm) { static bool initialized = false; diff --git a/hypervisor/arch/x86/guest/vmexit.c b/hypervisor/arch/x86/guest/vmexit.c index fc01838ad..7d2413118 100644 --- a/hypervisor/arch/x86/guest/vmexit.c +++ b/hypervisor/arch/x86/guest/vmexit.c @@ -78,8 +78,6 @@ static const struct vm_exit_dispatch dispatch_table[NR_VMX_EXIT_REASONS] = { .handler = unhandled_vmexit_handler}, [VMX_EXIT_REASON_VMCALL] = { .handler = vmcall_vmexit_handler}, - [VMX_EXIT_REASON_VMCLEAR] = { - .handler = undefined_vmexit_handler}, [VMX_EXIT_REASON_VMLAUNCH] = { .handler = undefined_vmexit_handler}, [VMX_EXIT_REASON_VMPTRST] = { @@ -91,6 +89,8 @@ static const struct vm_exit_dispatch dispatch_table[NR_VMX_EXIT_REASONS] = { [VMX_EXIT_REASON_VMWRITE] = { .handler = undefined_vmexit_handler}, #ifndef CONFIG_NVMX_ENABLED + [VMX_EXIT_REASON_VMCLEAR] = { + .handler = undefined_vmexit_handler}, [VMX_EXIT_REASON_VMPTRLD] = { .handler = undefined_vmexit_handler}, [VMX_EXIT_REASON_VMXOFF] = { @@ -98,6 +98,9 @@ static const struct vm_exit_dispatch dispatch_table[NR_VMX_EXIT_REASONS] = { [VMX_EXIT_REASON_VMXON] = { .handler = undefined_vmexit_handler}, #else + [VMX_EXIT_REASON_VMCLEAR] = { + .handler = vmclear_vmexit_handler, + .need_exit_qualification = 1}, [VMX_EXIT_REASON_VMPTRLD] = { .handler = vmptrld_vmexit_handler, .need_exit_qualification = 1}, diff --git a/hypervisor/include/arch/x86/asm/guest/nested.h b/hypervisor/include/arch/x86/asm/guest/nested.h index 973853574..d8a7e9b5a 100644 --- a/hypervisor/include/arch/x86/asm/guest/nested.h +++ b/hypervisor/include/arch/x86/asm/guest/nested.h @@ -83,6 +83,7 @@ union value_64 { #define VMCS_SHADOW_BIT_INDICATOR (1U << 31U) /* refer to ISDM: Table 30-1. VM-Instruction Error Numbers */ +#define VMXERR_VMCLEAR_VMXON_POINTER (3) #define VMXERR_VMPTRLD_INVALID_ADDRESS (9) #define VMXERR_VMPTRLD_INCORRECT_VMCS_REVISION_ID (10) #define VMXERR_VMPTRLD_VMXON_POINTER (11) @@ -94,6 +95,9 @@ union value_64 { */ #define VMCS12_REVISION_ID 0x15407E12U +#define VMCS12_LAUNCH_STATE_CLEAR (0U) +#define VMCS12_LAUNCH_STATE_LAUNCHED (1U) + /* * struct acrn_vmcs12 describes the emulated VMCS for the nested guest (L2). */ @@ -308,6 +312,7 @@ void nested_vmx_result(enum VMXResult, int error_number); int32_t vmxon_vmexit_handler(struct acrn_vcpu *vcpu); int32_t vmxoff_vmexit_handler(struct acrn_vcpu *vcpu); int32_t vmptrld_vmexit_handler(struct acrn_vcpu *vcpu); +int32_t vmclear_vmexit_handler(struct acrn_vcpu *vcpu); #ifdef CONFIG_NVMX_ENABLED struct acrn_nested {