From: "Xin Li (Intel)" <[email protected]> Extend nested VMX field validation to include FRED-specific VMCS fields, mirroring hardware behavior.
This enables support for nested FRED by ensuring control and guest/host state fields are properly checked. Signed-off-by: Xin Li (Intel) <[email protected]> Signed-off-by: Sohil Mehta <[email protected]> --- v10: - Use has_nested_exception to describe the event being injected rather than the CPU capability, for consistency with has_error_code (Chao). - Restructure the interruption-information checks into a switch on the interruption type (Chao). - Add a CC() statement to the reserved interruption type check so a VM-entry failure can be correlated with a specific check (Chao). - Reject the nested-exception bit (bit 13) for interruption types other than hardware exception, and fold in the nested-exception capability check. - Bound the "other event" vector explicitly instead of relying on a default arm. - Reject a zero VM-entry instruction length when injecting SYSCALL or SYSENTER if the vCPU doesn't enumerate zero-length instruction injection. --- arch/x86/kvm/vmx/nested.c | 119 +++++++++++++++++++++++++++++++++----- arch/x86/kvm/vmx/nested.h | 5 ++ 2 files changed, 111 insertions(+), 13 deletions(-) diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c index 83e846fb3fc8..3e0a1ed6440d 100644 --- a/arch/x86/kvm/vmx/nested.c +++ b/arch/x86/kvm/vmx/nested.c @@ -3111,6 +3111,8 @@ static int nested_check_vm_entry_controls(struct kvm_vcpu *vcpu, struct vmcs12 *vmcs12) { struct vcpu_vmx *vmx = to_vmx(vcpu); + bool fred_enabled = (vmcs12->vm_entry_controls & VM_ENTRY_IA32E_MODE) && + (vmcs12->guest_cr4 & X86_CR4_FRED); if (CC(!vmx_control_verify(vmcs12->vm_entry_controls, vmx->nested.msrs.entry_ctls_low, @@ -3128,22 +3130,11 @@ static int nested_check_vm_entry_controls(struct kvm_vcpu *vcpu, u8 vector = intr_info & INTR_INFO_VECTOR_MASK; u32 intr_type = intr_info & INTR_INFO_INTR_TYPE_MASK; bool has_error_code = intr_info & INTR_INFO_DELIVER_CODE_MASK; + bool has_nested_exception = intr_info & INTR_INFO_NESTED_EXCEPTION_MASK; bool urg = nested_cpu_has2(vmcs12, SECONDARY_EXEC_UNRESTRICTED_GUEST); bool prot_mode = !urg || vmcs12->guest_cr0 & X86_CR0_PE; - /* VM-entry interruption-info field: interruption type */ - if (CC(intr_type == INTR_TYPE_RESERVED) || - CC(intr_type == INTR_TYPE_OTHER_EVENT && - !nested_cpu_supports_monitor_trap_flag(vcpu))) - return -EINVAL; - - /* VM-entry interruption-info field: vector */ - if (CC(intr_type == INTR_TYPE_NMI_INTR && vector != NMI_VECTOR) || - CC(intr_type == INTR_TYPE_HARD_EXCEPTION && vector > 31) || - CC(intr_type == INTR_TYPE_OTHER_EVENT && vector != 0)) - return -EINVAL; - /* * Cannot deliver error code in real mode or if the interrupt * type is not hardware exception. For other cases, do the @@ -3167,8 +3158,28 @@ static int nested_check_vm_entry_controls(struct kvm_vcpu *vcpu, if (CC(intr_info & INTR_INFO_RESVD_BITS_MASK)) return -EINVAL; - /* VM-entry instruction length */ + if (CC(intr_type == INTR_TYPE_RESERVED)) + return -EINVAL; + + /* + * Only for hardware exceptions and when the CPU enumerates + * VMX nested-exception support, bit 13 (indicating a nested + * exception) has value 1. Otherwise it is reserved. + */ + if (CC(has_nested_exception && + (intr_type != INTR_TYPE_HARD_EXCEPTION || + !nested_cpu_has_nested_exception(vcpu)))) + return -EINVAL; + switch (intr_type) { + case INTR_TYPE_NMI_INTR: + if (CC(vector != NMI_VECTOR)) + return -EINVAL; + break; + case INTR_TYPE_HARD_EXCEPTION: + if (CC(vector > 31)) + return -EINVAL; + break; case INTR_TYPE_SOFT_EXCEPTION: case INTR_TYPE_SOFT_INTR: case INTR_TYPE_PRIV_SW_EXCEPTION: @@ -3176,6 +3187,28 @@ static int nested_check_vm_entry_controls(struct kvm_vcpu *vcpu, CC(vmcs12->vm_entry_instruction_len == 0 && CC(!nested_cpu_has_zero_length_injection(vcpu)))) return -EINVAL; + break; + case INTR_TYPE_OTHER_EVENT: + if (CC(vector > 2)) + return -EINVAL; + + switch (vector) { + case 0: + if (CC(!nested_cpu_supports_monitor_trap_flag(vcpu))) + return -EINVAL; + break; + case 1: + case 2: + if (CC(!fred_enabled)) + return -EINVAL; + if (CC(vmcs12->vm_entry_instruction_len > X86_MAX_INSTRUCTION_LENGTH)) + return -EINVAL; + if (CC(vmcs12->vm_entry_instruction_len == 0 && + !nested_cpu_has_zero_length_injection(vcpu))) + return -EINVAL; + break; + } + break; } } @@ -3262,9 +3295,27 @@ static int nested_vmx_check_host_state(struct kvm_vcpu *vcpu, if (ia32e) { if (CC(!(vmcs12->host_cr4 & X86_CR4_PAE))) return -EINVAL; + if (nested_cpu_load_host_fred_state(vmcs12)) { + if (CC(vmcs12->host_ia32_fred_config & FRED_CONFIG_RESERVED) || + CC(vmcs12->host_ia32_fred_rsp1 & GENMASK_ULL(5, 0)) || + CC(vmcs12->host_ia32_fred_rsp2 & GENMASK_ULL(5, 0)) || + CC(vmcs12->host_ia32_fred_rsp3 & GENMASK_ULL(5, 0)) || + CC(vmcs12->host_ia32_fred_ssp1 & GENMASK_ULL(2, 0)) || + CC(vmcs12->host_ia32_fred_ssp2 & GENMASK_ULL(2, 0)) || + CC(vmcs12->host_ia32_fred_ssp3 & GENMASK_ULL(2, 0)) || + CC(is_noncanonical_msr_address(vmcs12->host_ia32_fred_config & PAGE_MASK, vcpu)) || + CC(is_noncanonical_msr_address(vmcs12->host_ia32_fred_rsp1, vcpu)) || + CC(is_noncanonical_msr_address(vmcs12->host_ia32_fred_rsp2, vcpu)) || + CC(is_noncanonical_msr_address(vmcs12->host_ia32_fred_rsp3, vcpu)) || + CC(is_noncanonical_msr_address(vmcs12->host_ia32_fred_ssp1, vcpu)) || + CC(is_noncanonical_msr_address(vmcs12->host_ia32_fred_ssp2, vcpu)) || + CC(is_noncanonical_msr_address(vmcs12->host_ia32_fred_ssp3, vcpu))) + return -EINVAL; + } } else { if (CC(vmcs12->vm_entry_controls & VM_ENTRY_IA32E_MODE) || CC(vmcs12->host_cr4 & X86_CR4_PCIDE) || + CC(vmcs12->host_cr4 & X86_CR4_FRED) || CC((vmcs12->host_rip) >> 32)) return -EINVAL; } @@ -3447,6 +3498,48 @@ static int nested_vmx_check_guest_state(struct kvm_vcpu *vcpu, CC((vmcs12->guest_bndcfgs & MSR_IA32_BNDCFGS_RSVD)))) return -EINVAL; + if (ia32e) { + if (nested_cpu_load_guest_fred_state(vmcs12)) { + if (CC(vmcs12->guest_ia32_fred_config & FRED_CONFIG_RESERVED) || + CC(vmcs12->guest_ia32_fred_rsp1 & GENMASK_ULL(5, 0)) || + CC(vmcs12->guest_ia32_fred_rsp2 & GENMASK_ULL(5, 0)) || + CC(vmcs12->guest_ia32_fred_rsp3 & GENMASK_ULL(5, 0)) || + CC(vmcs12->guest_ia32_fred_ssp1 & GENMASK_ULL(2, 0)) || + CC(vmcs12->guest_ia32_fred_ssp2 & GENMASK_ULL(2, 0)) || + CC(vmcs12->guest_ia32_fred_ssp3 & GENMASK_ULL(2, 0)) || + CC(is_noncanonical_msr_address(vmcs12->guest_ia32_fred_config & PAGE_MASK, vcpu)) || + CC(is_noncanonical_msr_address(vmcs12->guest_ia32_fred_rsp1, vcpu)) || + CC(is_noncanonical_msr_address(vmcs12->guest_ia32_fred_rsp2, vcpu)) || + CC(is_noncanonical_msr_address(vmcs12->guest_ia32_fred_rsp3, vcpu)) || + CC(is_noncanonical_msr_address(vmcs12->guest_ia32_fred_ssp1, vcpu)) || + CC(is_noncanonical_msr_address(vmcs12->guest_ia32_fred_ssp2, vcpu)) || + CC(is_noncanonical_msr_address(vmcs12->guest_ia32_fred_ssp3, vcpu))) + return -EINVAL; + } + if (vmcs12->guest_cr4 & X86_CR4_FRED) { + unsigned int ss_dpl = VMX_AR_DPL(vmcs12->guest_ss_ar_bytes); + + if (CC(ss_dpl == 1 || ss_dpl == 2)) + return -EINVAL; + + switch (ss_dpl) { + case 0: + if (CC(!(vmcs12->guest_cs_ar_bytes & VMX_AR_L_MASK))) + return -EINVAL; + break; + case 3: + if (CC(vmcs12->guest_rflags & X86_EFLAGS_IOPL)) + return -EINVAL; + if (CC(vmcs12->guest_interruptibility_info & GUEST_INTR_STATE_STI)) + return -EINVAL; + break; + } + } + } else { + if (CC(vmcs12->guest_cr4 & X86_CR4_FRED)) + return -EINVAL; + } + if (vmcs12->vm_entry_controls & VM_ENTRY_LOAD_CET_STATE) { if (nested_vmx_check_cet_state_common(vcpu, vmcs12->guest_s_cet, vmcs12->guest_ssp, diff --git a/arch/x86/kvm/vmx/nested.h b/arch/x86/kvm/vmx/nested.h index dc5a1e9cb4e9..551121824128 100644 --- a/arch/x86/kvm/vmx/nested.h +++ b/arch/x86/kvm/vmx/nested.h @@ -334,6 +334,11 @@ static inline bool nested_cpu_has_no_hw_errcode_cc(struct kvm_vcpu *vcpu) return to_vmx(vcpu)->nested.msrs.basic & VMX_BASIC_NO_HW_ERROR_CODE_CC; } +static inline bool nested_cpu_has_nested_exception(struct kvm_vcpu *vcpu) +{ + return to_vmx(vcpu)->nested.msrs.basic & VMX_BASIC_NESTED_EXCEPTION; +} + /* No difference in the restrictions on guest and host CR4 in VMX operation. */ #define nested_guest_cr4_valid nested_cr4_valid #define nested_host_cr4_valid nested_cr4_valid -- 2.43.0

