According to section “VM Entries” in Intel SDM vol 3C, VM-entry checks are performed in a certain order. Checks on MSRs that are loaded on VM-entry from VM-entry MSR-load area, should be done after verifying VMCS controls, host-state area and guest-state area. As KVM relies on CPU hardware to perform some of these checks, we need to defer VM-exit due to invalid VM-entry MSR-load area to until after CPU hardware completes the earlier checks and is ready to do VMLAUNCH/VMRESUME. In order to defer errors arising from invalid VM-entry MSR-load area in vmcs12, we set up a single invalid entry, which is illegal according to section "Loading MSRs in Intel SDM vol 3C, in VM-entry MSR-load area of vmcs02. This will cause the CPU hardware to VM-exit with "VM-entry failure due to MSR loading" after it completes checks on VMCS controls, host-state area and guest-state area. We reflect a synthesized Exit Qualification to our guest. Suggested-by: Jim Mattson <jmattson@xxxxxxxxxx> Signed-off-by: Krish Sadhukhan <krish.sadhukhan@xxxxxxxxxx> Reviewed-by: Mihai Carabas <mihai.carabas@xxxxxxxxxx> Reviewed-by: Liran Alon <liran.alon@xxxxxxxxxx> --- arch/x86/kvm/vmx/nested.c | 34 +++++++++++++++++++++++++++++++--- arch/x86/kvm/vmx/nested.h | 15 +++++++++++++-- arch/x86/kvm/vmx/vmx.c | 18 ++++++++++++++++++ arch/x86/kvm/vmx/vmx.h | 6 ++++++ 4 files changed, 68 insertions(+), 5 deletions(-) diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c index e76eb4f07f6c..cebdcb105ea8 100644 --- a/arch/x86/kvm/vmx/nested.c +++ b/arch/x86/kvm/vmx/nested.c @@ -3029,6 +3029,8 @@ static u8 vmx_has_apicv_interrupt(struct kvm_vcpu *vcpu) static void load_vmcs12_host_state(struct kvm_vcpu *vcpu, struct vmcs12 *vmcs12); +extern struct vmx_msr_entry *vmcs12_invalid_msr_load_area; + /* * If from_vmentry is false, this is being called from state restore (either RSM * or KVM_SET_NESTED_STATE). Otherwise it's called from vmlaunch/vmresume. @@ -3100,12 +3102,38 @@ int nested_vmx_enter_non_root_mode(struct kvm_vcpu *vcpu, bool from_vmentry) goto vmentry_fail_vmexit_guest_mode; if (from_vmentry) { - exit_reason = EXIT_REASON_MSR_LOAD_FAIL; exit_qual = nested_vmx_load_msr(vcpu, vmcs12->vm_entry_msr_load_addr, vmcs12->vm_entry_msr_load_count); - if (exit_qual) - goto vmentry_fail_vmexit_guest_mode; + if (exit_qual) { + /* + * According to section “VM Entries” in Intel SDM + * vol 3C, VM-entry checks are performed in a certain + * order. Checks on MSRs that are loaded on VM-entry + * from VM-entry MSR-load area, should be done after + * verifying VMCS controls, host-state area and + * guest-state area. As KVM relies on CPU hardware to + * perform some of these checks, we need to defer + * VM-exit due to invalid VM-entry MSR-load area to + * until after CPU hardware completes the earlier + * checks and is ready to do VMLAUNCH/VMRESUME. + * + * In order to defer errors arising from invalid + * VM-entry MSR-load area in vmcs12, we set up a + * single invalid entry, which is illegal according + * to section "Loading MSRs in Intel SDM vol 3C, in + * VM-entry MSR-load area of vmcs02. This will cause + * the CPU hardware to VM-exit with "VM-entry + * failure due to MSR loading" after it completes + * checks on VMCS controls, host-state area and + * guest-state area. + */ + vmx->nested.invalid_msr_load_exit_qual = exit_qual; + vmcs_write32(VM_ENTRY_MSR_LOAD_COUNT, 1); + vmcs_write64(VM_ENTRY_MSR_LOAD_ADDR, + __pa(vmcs12_invalid_msr_load_area)); + vmx->nested.dirty_vmcs12 = true; + } } else { /* * The MMU is not initialized to point at the right entities yet and diff --git a/arch/x86/kvm/vmx/nested.h b/arch/x86/kvm/vmx/nested.h index 187d39bf0bf1..bb51ec8cf7da 100644 --- a/arch/x86/kvm/vmx/nested.h +++ b/arch/x86/kvm/vmx/nested.h @@ -64,7 +64,9 @@ static inline bool nested_ept_ad_enabled(struct kvm_vcpu *vcpu) static inline int nested_vmx_reflect_vmexit(struct kvm_vcpu *vcpu, u32 exit_reason) { + u32 exit_qual; u32 exit_intr_info = vmcs_read32(VM_EXIT_INTR_INFO); + struct vmx_msr_entry *addr; /* * At this point, the exit interruption info in exit_intr_info @@ -81,8 +83,17 @@ static inline int nested_vmx_reflect_vmexit(struct kvm_vcpu *vcpu, vmcs_read32(VM_EXIT_INTR_ERROR_CODE); } - nested_vmx_vmexit(vcpu, exit_reason, exit_intr_info, - vmcs_readl(EXIT_QUALIFICATION)); + exit_qual = vmcs_readl(EXIT_QUALIFICATION); + + addr = __va(vmcs_read64(VM_ENTRY_MSR_LOAD_ADDR)); + if (addr && addr->index == MSR_FS_BASE && + (exit_reason == (VMX_EXIT_REASONS_FAILED_VMENTRY | + EXIT_REASON_MSR_LOAD_FAIL))) { + exit_qual = (to_vmx(vcpu))->nested.invalid_msr_load_exit_qual; + } + + nested_vmx_vmexit(vcpu, exit_reason, exit_intr_info, exit_qual); + return 1; } diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c index e7970a2e8eae..7ece11322430 100644 --- a/arch/x86/kvm/vmx/vmx.c +++ b/arch/x86/kvm/vmx/vmx.c @@ -7914,6 +7914,13 @@ static void vmx_cleanup_l1d_flush(void) l1tf_vmx_mitigation = VMENTER_L1D_FLUSH_AUTO; } +/* + * This is used to set up an invalid VM-entry MSR-load area for vmcs02 + * if an error is detected while processing the entries in VM-entry + * MSR-load area of vmcs12. + */ +struct vmx_msr_entry *vmcs12_invalid_msr_load_area = NULL; + static void vmx_exit(void) { #ifdef CONFIG_KEXEC_CORE @@ -7947,6 +7954,9 @@ static void vmx_exit(void) } #endif vmx_cleanup_l1d_flush(); + + if (vmcs12_invalid_msr_load_area) + kfree(vmcs12_invalid_msr_load_area); } module_exit(vmx_exit); @@ -8012,6 +8022,14 @@ static int __init vmx_init(void) #endif vmx_check_vmcs12_offsets(); + vmcs12_invalid_msr_load_area = + kzalloc(sizeof(struct vmx_msr_entry), GFP_KERNEL_ACCOUNT); + if (!vmcs12_invalid_msr_load_area) { + vmx_exit(); + return 15; + } + vmcs12_invalid_msr_load_area->index = MSR_FS_BASE; + return 0; } module_init(vmx_init); diff --git a/arch/x86/kvm/vmx/vmx.h b/arch/x86/kvm/vmx/vmx.h index bee16687dc0b..ee7f40abd199 100644 --- a/arch/x86/kvm/vmx/vmx.h +++ b/arch/x86/kvm/vmx/vmx.h @@ -183,6 +183,12 @@ struct nested_vmx { gpa_t hv_evmcs_vmptr; struct kvm_host_map hv_evmcs_map; struct hv_enlightened_vmcs *hv_evmcs; + + /* + * This field is used for Exit Qualification when VM-entry fails + * due to invalid VM-entry MSR-load area in vmcs12. + */ + u32 invalid_msr_load_exit_qual; }; struct vcpu_vmx { -- 2.20.1