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 {