KVM VMX changes for 6.3:
- Handle NMI VM-Exits before leaving the noinstr region - A few trivial cleanups in the VM-Enter flows - Stop enabling VMFUNC for L1 purely to document that KVM doesn't support EPTP switching (or any other VM function) for L1 - Fix a crash when using eVMCS's enlighted MSR bitmaps -----BEGIN PGP SIGNATURE----- iQJGBAABCgAwFiEEMHr+pfEFOIzK+KY1YJEiAU0MEvkFAmPsL4USHHNlYW5qY0Bn b29nbGUuY29tAAoJEGCRIgFNDBL5fvQP/jOhCos/8VrNPJ5vtVkELSgbefbLFPbR ThCefVekbnbqkJ4PdxLFOw/nzK+NREnoet02lvLXIeBt243qMeDWbNuGncZ+rqqX ohJ6ALIS7ag2egG2ZllM4KTV1jWnm7bG6C8QsJwrFuG7wHe69lPYNnv34OD4l8QO PR7reXBl7iyqQehF0rCqkYvtpv1QI+VlVd3ODCg/0vaZX4AOPZI6X1hX2+myUfTy LI1oQ88SQ0ptoH5o7xBtNQw7Zknm/SP82Djzpg/1Wpr3cZv4g/1vblNcxpU/D+3I Cp8xM42c82GIX1Zk6uYfsoCyQZ7Bd38llbgcLM5Fi0F2IuKJKoN1iuH77RJ1Juqf v+SkRmFAr5Uf3UYVp4ck4FxTRH58ooDPeIv2jdaguyE9NgRfbVsghn6QDC+gkZMx z4xdwCBXgsAAkhJwOLqDy98ZrQZoy1kxlmG2jLIe5RvOzI4WQDUJkJAd+u0dBD/W U7PRqbqfJFtvvKN784BTa4CO8m+KwaNz4Xrwcg4YtBnKBgDU/cxFUZnnSEHdi9Pr tVf462dfReK3slK3rkPw5HlacFdQsgfX6+VX1eeFKY+gE88TXoZks/X7ma24MXEw hOu6VZMat1NjkfcgUcMffBVZlejK7C6oXTeVmqVKsx6hUs+zzqxpQ05AT4TMbAUI gPTHJyy9ByHO =WXGG -----END PGP SIGNATURE----- Merge tag 'kvm-x86-vmx-6.3' of https://github.com/kvm-x86/linux into HEAD KVM VMX changes for 6.3: - Handle NMI VM-Exits before leaving the noinstr region - A few trivial cleanups in the VM-Enter flows - Stop enabling VMFUNC for L1 purely to document that KVM doesn't support EPTP switching (or any other VM function) for L1 - Fix a crash when using eVMCS's enlighted MSR bitmaps
This commit is contained in:
commit
27b025ebb0
|
@ -582,18 +582,14 @@ DECLARE_IDTENTRY_RAW(X86_TRAP_MC, xenpv_exc_machine_check);
|
|||
|
||||
/* NMI */
|
||||
|
||||
#if defined(CONFIG_X86_64) && IS_ENABLED(CONFIG_KVM_INTEL)
|
||||
#if IS_ENABLED(CONFIG_KVM_INTEL)
|
||||
/*
|
||||
* Special NOIST entry point for VMX which invokes this on the kernel
|
||||
* stack. asm_exc_nmi() requires an IST to work correctly vs. the NMI
|
||||
* 'executing' marker.
|
||||
*
|
||||
* On 32bit this just uses the regular NMI entry point because 32-bit does
|
||||
* not have ISTs.
|
||||
* Special entry point for VMX which invokes this on the kernel stack, even for
|
||||
* 64-bit, i.e. without using an IST. asm_exc_nmi() requires an IST to work
|
||||
* correctly vs. the NMI 'executing' marker. Used for 32-bit kernels as well
|
||||
* to avoid more ifdeffery.
|
||||
*/
|
||||
DECLARE_IDTENTRY(X86_TRAP_NMI, exc_nmi_noist);
|
||||
#else
|
||||
#define asm_exc_nmi_noist asm_exc_nmi
|
||||
DECLARE_IDTENTRY(X86_TRAP_NMI, exc_nmi_kvm_vmx);
|
||||
#endif
|
||||
|
||||
DECLARE_IDTENTRY_NMI(X86_TRAP_NMI, exc_nmi);
|
||||
|
|
|
@ -527,14 +527,14 @@ nmi_restart:
|
|||
mds_user_clear_cpu_buffers();
|
||||
}
|
||||
|
||||
#if defined(CONFIG_X86_64) && IS_ENABLED(CONFIG_KVM_INTEL)
|
||||
DEFINE_IDTENTRY_RAW(exc_nmi_noist)
|
||||
#if IS_ENABLED(CONFIG_KVM_INTEL)
|
||||
DEFINE_IDTENTRY_RAW(exc_nmi_kvm_vmx)
|
||||
{
|
||||
exc_nmi(regs);
|
||||
}
|
||||
#endif
|
||||
#if IS_MODULE(CONFIG_KVM_INTEL)
|
||||
EXPORT_SYMBOL_GPL(asm_exc_nmi_noist);
|
||||
EXPORT_SYMBOL_GPL(asm_exc_nmi_kvm_vmx);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
void stop_nmi(void)
|
||||
|
|
|
@ -75,6 +75,18 @@ static inline void kvm_register_mark_dirty(struct kvm_vcpu *vcpu,
|
|||
__set_bit(reg, (unsigned long *)&vcpu->arch.regs_dirty);
|
||||
}
|
||||
|
||||
/*
|
||||
* kvm_register_test_and_mark_available() is a special snowflake that uses an
|
||||
* arch bitop directly to avoid the explicit instrumentation that comes with
|
||||
* the generic bitops. This allows code that cannot be instrumented (noinstr
|
||||
* functions), e.g. the low level VM-Enter/VM-Exit paths, to cache registers.
|
||||
*/
|
||||
static __always_inline bool kvm_register_test_and_mark_available(struct kvm_vcpu *vcpu,
|
||||
enum kvm_reg reg)
|
||||
{
|
||||
return arch___test_and_set_bit(reg, (unsigned long *)&vcpu->arch.regs_avail);
|
||||
}
|
||||
|
||||
/*
|
||||
* The "raw" register helpers are only for cases where the full 64 bits of a
|
||||
* register are read/written irrespective of current vCPU mode. In other words,
|
||||
|
|
|
@ -196,7 +196,7 @@ static __always_inline void evmcs_write64(unsigned long field, u64 value)
|
|||
current_evmcs->hv_clean_fields &= ~clean_field;
|
||||
}
|
||||
|
||||
static inline void evmcs_write32(unsigned long field, u32 value)
|
||||
static __always_inline void evmcs_write32(unsigned long field, u32 value)
|
||||
{
|
||||
u16 clean_field;
|
||||
int offset = get_evmcs_offset(field, &clean_field);
|
||||
|
@ -208,7 +208,7 @@ static inline void evmcs_write32(unsigned long field, u32 value)
|
|||
current_evmcs->hv_clean_fields &= ~clean_field;
|
||||
}
|
||||
|
||||
static inline void evmcs_write16(unsigned long field, u16 value)
|
||||
static __always_inline void evmcs_write16(unsigned long field, u16 value)
|
||||
{
|
||||
u16 clean_field;
|
||||
int offset = get_evmcs_offset(field, &clean_field);
|
||||
|
@ -220,7 +220,7 @@ static inline void evmcs_write16(unsigned long field, u16 value)
|
|||
current_evmcs->hv_clean_fields &= ~clean_field;
|
||||
}
|
||||
|
||||
static inline u64 evmcs_read64(unsigned long field)
|
||||
static __always_inline u64 evmcs_read64(unsigned long field)
|
||||
{
|
||||
int offset = get_evmcs_offset(field, NULL);
|
||||
|
||||
|
@ -230,7 +230,7 @@ static inline u64 evmcs_read64(unsigned long field)
|
|||
return *(u64 *)((char *)current_evmcs + offset);
|
||||
}
|
||||
|
||||
static inline u32 evmcs_read32(unsigned long field)
|
||||
static __always_inline u32 evmcs_read32(unsigned long field)
|
||||
{
|
||||
int offset = get_evmcs_offset(field, NULL);
|
||||
|
||||
|
@ -240,7 +240,7 @@ static inline u32 evmcs_read32(unsigned long field)
|
|||
return *(u32 *)((char *)current_evmcs + offset);
|
||||
}
|
||||
|
||||
static inline u16 evmcs_read16(unsigned long field)
|
||||
static __always_inline u16 evmcs_read16(unsigned long field)
|
||||
{
|
||||
int offset = get_evmcs_offset(field, NULL);
|
||||
|
||||
|
@ -250,16 +250,6 @@ static inline u16 evmcs_read16(unsigned long field)
|
|||
return *(u16 *)((char *)current_evmcs + offset);
|
||||
}
|
||||
|
||||
static inline void evmcs_touch_msr_bitmap(void)
|
||||
{
|
||||
if (unlikely(!current_evmcs))
|
||||
return;
|
||||
|
||||
if (current_evmcs->hv_enlightenments_control.msr_bitmap)
|
||||
current_evmcs->hv_clean_fields &=
|
||||
~HV_VMX_ENLIGHTENED_CLEAN_FIELD_MSR_BITMAP;
|
||||
}
|
||||
|
||||
static inline void evmcs_load(u64 phys_addr)
|
||||
{
|
||||
struct hv_vp_assist_page *vp_ap =
|
||||
|
@ -274,13 +264,12 @@ static inline void evmcs_load(u64 phys_addr)
|
|||
void evmcs_sanitize_exec_ctrls(struct vmcs_config *vmcs_conf);
|
||||
#else /* !IS_ENABLED(CONFIG_HYPERV) */
|
||||
static __always_inline void evmcs_write64(unsigned long field, u64 value) {}
|
||||
static inline void evmcs_write32(unsigned long field, u32 value) {}
|
||||
static inline void evmcs_write16(unsigned long field, u16 value) {}
|
||||
static inline u64 evmcs_read64(unsigned long field) { return 0; }
|
||||
static inline u32 evmcs_read32(unsigned long field) { return 0; }
|
||||
static inline u16 evmcs_read16(unsigned long field) { return 0; }
|
||||
static __always_inline void evmcs_write32(unsigned long field, u32 value) {}
|
||||
static __always_inline void evmcs_write16(unsigned long field, u16 value) {}
|
||||
static __always_inline u64 evmcs_read64(unsigned long field) { return 0; }
|
||||
static __always_inline u32 evmcs_read32(unsigned long field) { return 0; }
|
||||
static __always_inline u16 evmcs_read16(unsigned long field) { return 0; }
|
||||
static inline void evmcs_load(u64 phys_addr) {}
|
||||
static inline void evmcs_touch_msr_bitmap(void) {}
|
||||
#endif /* IS_ENABLED(CONFIG_HYPERV) */
|
||||
|
||||
#define EVMPTR_INVALID (-1ULL)
|
||||
|
|
|
@ -5864,11 +5864,10 @@ static int handle_vmfunc(struct kvm_vcpu *vcpu)
|
|||
u32 function = kvm_rax_read(vcpu);
|
||||
|
||||
/*
|
||||
* VMFUNC is only supported for nested guests, but we always enable the
|
||||
* secondary control for simplicity; for non-nested mode, fake that we
|
||||
* didn't by injecting #UD.
|
||||
* VMFUNC should never execute cleanly while L1 is active; KVM supports
|
||||
* VMFUNC for nested VMs, but not for L1.
|
||||
*/
|
||||
if (!is_guest_mode(vcpu)) {
|
||||
if (WARN_ON_ONCE(!is_guest_mode(vcpu))) {
|
||||
kvm_queue_exception(vcpu, UD_VECTOR);
|
||||
return 1;
|
||||
}
|
||||
|
@ -6881,6 +6880,7 @@ void nested_vmx_setup_ctls_msrs(struct vmcs_config *vmcs_conf, u32 ept_caps)
|
|||
SECONDARY_EXEC_VIRTUAL_INTR_DELIVERY |
|
||||
SECONDARY_EXEC_RDRAND_EXITING |
|
||||
SECONDARY_EXEC_ENABLE_INVPCID |
|
||||
SECONDARY_EXEC_ENABLE_VMFUNC |
|
||||
SECONDARY_EXEC_RDSEED_EXITING |
|
||||
SECONDARY_EXEC_XSAVES |
|
||||
SECONDARY_EXEC_TSC_SCALING |
|
||||
|
@ -6913,18 +6913,13 @@ void nested_vmx_setup_ctls_msrs(struct vmcs_config *vmcs_conf, u32 ept_caps)
|
|||
SECONDARY_EXEC_ENABLE_PML;
|
||||
msrs->ept_caps |= VMX_EPT_AD_BIT;
|
||||
}
|
||||
}
|
||||
|
||||
if (cpu_has_vmx_vmfunc()) {
|
||||
msrs->secondary_ctls_high |=
|
||||
SECONDARY_EXEC_ENABLE_VMFUNC;
|
||||
/*
|
||||
* Advertise EPTP switching unconditionally
|
||||
* since we emulate it
|
||||
* Advertise EPTP switching irrespective of hardware support,
|
||||
* KVM emulates it in software so long as VMFUNC is supported.
|
||||
*/
|
||||
if (enable_ept)
|
||||
msrs->vmfunc_controls =
|
||||
VMX_VMFUNC_EPTP_SWITCHING;
|
||||
if (cpu_has_vmx_vmfunc())
|
||||
msrs->vmfunc_controls = VMX_VMFUNC_EPTP_SWITCHING;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -75,7 +75,7 @@ struct loaded_vmcs {
|
|||
struct vmcs_controls_shadow controls_shadow;
|
||||
};
|
||||
|
||||
static inline bool is_intr_type(u32 intr_info, u32 type)
|
||||
static __always_inline bool is_intr_type(u32 intr_info, u32 type)
|
||||
{
|
||||
const u32 mask = INTR_INFO_VALID_MASK | INTR_INFO_INTR_TYPE_MASK;
|
||||
|
||||
|
@ -146,7 +146,7 @@ static inline bool is_icebp(u32 intr_info)
|
|||
return is_intr_type(intr_info, INTR_TYPE_PRIV_SW_EXCEPTION);
|
||||
}
|
||||
|
||||
static inline bool is_nmi(u32 intr_info)
|
||||
static __always_inline bool is_nmi(u32 intr_info)
|
||||
{
|
||||
return is_intr_type(intr_info, INTR_TYPE_NMI_INTR);
|
||||
}
|
||||
|
|
|
@ -31,6 +31,39 @@
|
|||
#define VCPU_R15 __VCPU_REGS_R15 * WORD_SIZE
|
||||
#endif
|
||||
|
||||
.macro VMX_DO_EVENT_IRQOFF call_insn call_target
|
||||
/*
|
||||
* Unconditionally create a stack frame, getting the correct RSP on the
|
||||
* stack (for x86-64) would take two instructions anyways, and RBP can
|
||||
* be used to restore RSP to make objtool happy (see below).
|
||||
*/
|
||||
push %_ASM_BP
|
||||
mov %_ASM_SP, %_ASM_BP
|
||||
|
||||
#ifdef CONFIG_X86_64
|
||||
/*
|
||||
* Align RSP to a 16-byte boundary (to emulate CPU behavior) before
|
||||
* creating the synthetic interrupt stack frame for the IRQ/NMI.
|
||||
*/
|
||||
and $-16, %rsp
|
||||
push $__KERNEL_DS
|
||||
push %rbp
|
||||
#endif
|
||||
pushf
|
||||
push $__KERNEL_CS
|
||||
\call_insn \call_target
|
||||
|
||||
/*
|
||||
* "Restore" RSP from RBP, even though IRET has already unwound RSP to
|
||||
* the correct value. objtool doesn't know the callee will IRET and,
|
||||
* without the explicit restore, thinks the stack is getting walloped.
|
||||
* Using an unwind hint is problematic due to x86-64's dynamic alignment.
|
||||
*/
|
||||
mov %_ASM_BP, %_ASM_SP
|
||||
pop %_ASM_BP
|
||||
RET
|
||||
.endm
|
||||
|
||||
.section .noinstr.text, "ax"
|
||||
|
||||
/**
|
||||
|
@ -69,8 +102,8 @@ SYM_FUNC_START(__vmx_vcpu_run)
|
|||
*/
|
||||
push %_ASM_ARG2
|
||||
|
||||
/* Copy @flags to BL, _ASM_ARG3 is volatile. */
|
||||
mov %_ASM_ARG3B, %bl
|
||||
/* Copy @flags to EBX, _ASM_ARG3 is volatile. */
|
||||
mov %_ASM_ARG3L, %ebx
|
||||
|
||||
lea (%_ASM_SP), %_ASM_ARG2
|
||||
call vmx_update_host_rsp
|
||||
|
@ -106,7 +139,7 @@ SYM_FUNC_START(__vmx_vcpu_run)
|
|||
mov (%_ASM_SP), %_ASM_AX
|
||||
|
||||
/* Check if vmlaunch or vmresume is needed */
|
||||
testb $VMX_RUN_VMRESUME, %bl
|
||||
test $VMX_RUN_VMRESUME, %ebx
|
||||
|
||||
/* Load guest registers. Don't clobber flags. */
|
||||
mov VCPU_RCX(%_ASM_AX), %_ASM_CX
|
||||
|
@ -128,7 +161,7 @@ SYM_FUNC_START(__vmx_vcpu_run)
|
|||
/* Load guest RAX. This kills the @regs pointer! */
|
||||
mov VCPU_RAX(%_ASM_AX), %_ASM_AX
|
||||
|
||||
/* Check EFLAGS.ZF from 'testb' above */
|
||||
/* Check EFLAGS.ZF from 'test VMX_RUN_VMRESUME' above */
|
||||
jz .Lvmlaunch
|
||||
|
||||
/*
|
||||
|
@ -266,6 +299,10 @@ SYM_INNER_LABEL(vmx_vmexit, SYM_L_GLOBAL)
|
|||
|
||||
SYM_FUNC_END(__vmx_vcpu_run)
|
||||
|
||||
SYM_FUNC_START(vmx_do_nmi_irqoff)
|
||||
VMX_DO_EVENT_IRQOFF call asm_exc_nmi_kvm_vmx
|
||||
SYM_FUNC_END(vmx_do_nmi_irqoff)
|
||||
|
||||
|
||||
.section .text, "ax"
|
||||
|
||||
|
@ -320,35 +357,6 @@ SYM_FUNC_START(vmread_error_trampoline)
|
|||
SYM_FUNC_END(vmread_error_trampoline)
|
||||
#endif
|
||||
|
||||
SYM_FUNC_START(vmx_do_interrupt_nmi_irqoff)
|
||||
/*
|
||||
* Unconditionally create a stack frame, getting the correct RSP on the
|
||||
* stack (for x86-64) would take two instructions anyways, and RBP can
|
||||
* be used to restore RSP to make objtool happy (see below).
|
||||
*/
|
||||
push %_ASM_BP
|
||||
mov %_ASM_SP, %_ASM_BP
|
||||
|
||||
#ifdef CONFIG_X86_64
|
||||
/*
|
||||
* Align RSP to a 16-byte boundary (to emulate CPU behavior) before
|
||||
* creating the synthetic interrupt stack frame for the IRQ/NMI.
|
||||
*/
|
||||
and $-16, %rsp
|
||||
push $__KERNEL_DS
|
||||
push %rbp
|
||||
#endif
|
||||
pushf
|
||||
push $__KERNEL_CS
|
||||
CALL_NOSPEC _ASM_ARG1
|
||||
|
||||
/*
|
||||
* "Restore" RSP from RBP, even though IRET has already unwound RSP to
|
||||
* the correct value. objtool doesn't know the callee will IRET and,
|
||||
* without the explicit restore, thinks the stack is getting walloped.
|
||||
* Using an unwind hint is problematic due to x86-64's dynamic alignment.
|
||||
*/
|
||||
mov %_ASM_BP, %_ASM_SP
|
||||
pop %_ASM_BP
|
||||
RET
|
||||
SYM_FUNC_END(vmx_do_interrupt_nmi_irqoff)
|
||||
SYM_FUNC_START(vmx_do_interrupt_irqoff)
|
||||
VMX_DO_EVENT_IRQOFF CALL_NOSPEC _ASM_ARG1
|
||||
SYM_FUNC_END(vmx_do_interrupt_irqoff)
|
||||
|
|
|
@ -3934,8 +3934,13 @@ static void vmx_msr_bitmap_l01_changed(struct vcpu_vmx *vmx)
|
|||
* 'Enlightened MSR Bitmap' feature L0 needs to know that MSR
|
||||
* bitmap has changed.
|
||||
*/
|
||||
if (static_branch_unlikely(&enable_evmcs))
|
||||
evmcs_touch_msr_bitmap();
|
||||
if (IS_ENABLED(CONFIG_HYPERV) && static_branch_unlikely(&enable_evmcs)) {
|
||||
struct hv_enlightened_vmcs *evmcs = (void *)vmx->vmcs01.vmcs;
|
||||
|
||||
if (evmcs->hv_enlightenments_control.msr_bitmap)
|
||||
evmcs->hv_clean_fields &=
|
||||
~HV_VMX_ENLIGHTENED_CLEAN_FIELD_MSR_BITMAP;
|
||||
}
|
||||
|
||||
vmx->nested.force_msr_bitmap_recalc = true;
|
||||
}
|
||||
|
@ -4588,6 +4593,12 @@ static u32 vmx_secondary_exec_control(struct vcpu_vmx *vmx)
|
|||
SECONDARY_EXEC_VIRTUAL_INTR_DELIVERY);
|
||||
exec_control &= ~SECONDARY_EXEC_VIRTUALIZE_X2APIC_MODE;
|
||||
|
||||
/*
|
||||
* KVM doesn't support VMFUNC for L1, but the control is set in KVM's
|
||||
* base configuration as KVM emulates VMFUNC[EPTP_SWITCHING] for L2.
|
||||
*/
|
||||
exec_control &= ~SECONDARY_EXEC_ENABLE_VMFUNC;
|
||||
|
||||
/* SECONDARY_EXEC_DESC is enabled/disabled on writes to CR4.UMIP,
|
||||
* in vmx_set_cr4. */
|
||||
exec_control &= ~SECONDARY_EXEC_DESC;
|
||||
|
@ -5168,8 +5179,13 @@ static int handle_exception_nmi(struct kvm_vcpu *vcpu)
|
|||
vect_info = vmx->idt_vectoring_info;
|
||||
intr_info = vmx_get_intr_info(vcpu);
|
||||
|
||||
/*
|
||||
* Machine checks are handled by handle_exception_irqoff(), or by
|
||||
* vmx_vcpu_run() if a #MC occurs on VM-Entry. NMIs are handled by
|
||||
* vmx_vcpu_enter_exit().
|
||||
*/
|
||||
if (is_machine_check(intr_info) || is_nmi(intr_info))
|
||||
return 1; /* handled by handle_exception_nmi_irqoff() */
|
||||
return 1;
|
||||
|
||||
/*
|
||||
* Queue the exception here instead of in handle_nm_fault_irqoff().
|
||||
|
@ -6859,17 +6875,8 @@ static void vmx_apicv_post_state_restore(struct kvm_vcpu *vcpu)
|
|||
memset(vmx->pi_desc.pir, 0, sizeof(vmx->pi_desc.pir));
|
||||
}
|
||||
|
||||
void vmx_do_interrupt_nmi_irqoff(unsigned long entry);
|
||||
|
||||
static void handle_interrupt_nmi_irqoff(struct kvm_vcpu *vcpu,
|
||||
unsigned long entry)
|
||||
{
|
||||
bool is_nmi = entry == (unsigned long)asm_exc_nmi_noist;
|
||||
|
||||
kvm_before_interrupt(vcpu, is_nmi ? KVM_HANDLING_NMI : KVM_HANDLING_IRQ);
|
||||
vmx_do_interrupt_nmi_irqoff(entry);
|
||||
kvm_after_interrupt(vcpu);
|
||||
}
|
||||
void vmx_do_interrupt_irqoff(unsigned long entry);
|
||||
void vmx_do_nmi_irqoff(void);
|
||||
|
||||
static void handle_nm_fault_irqoff(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
|
@ -6891,9 +6898,8 @@ static void handle_nm_fault_irqoff(struct kvm_vcpu *vcpu)
|
|||
rdmsrl(MSR_IA32_XFD_ERR, vcpu->arch.guest_fpu.xfd_err);
|
||||
}
|
||||
|
||||
static void handle_exception_nmi_irqoff(struct vcpu_vmx *vmx)
|
||||
static void handle_exception_irqoff(struct vcpu_vmx *vmx)
|
||||
{
|
||||
const unsigned long nmi_entry = (unsigned long)asm_exc_nmi_noist;
|
||||
u32 intr_info = vmx_get_intr_info(&vmx->vcpu);
|
||||
|
||||
/* if exit due to PF check for async PF */
|
||||
|
@ -6905,9 +6911,6 @@ static void handle_exception_nmi_irqoff(struct vcpu_vmx *vmx)
|
|||
/* Handle machine checks before interrupts are enabled */
|
||||
else if (is_machine_check(intr_info))
|
||||
kvm_machine_check();
|
||||
/* We need to handle NMIs before interrupts are enabled */
|
||||
else if (is_nmi(intr_info))
|
||||
handle_interrupt_nmi_irqoff(&vmx->vcpu, nmi_entry);
|
||||
}
|
||||
|
||||
static void handle_external_interrupt_irqoff(struct kvm_vcpu *vcpu)
|
||||
|
@ -6920,7 +6923,10 @@ static void handle_external_interrupt_irqoff(struct kvm_vcpu *vcpu)
|
|||
"unexpected VM-Exit interrupt info: 0x%x", intr_info))
|
||||
return;
|
||||
|
||||
handle_interrupt_nmi_irqoff(vcpu, gate_offset(desc));
|
||||
kvm_before_interrupt(vcpu, KVM_HANDLING_IRQ);
|
||||
vmx_do_interrupt_irqoff(gate_offset(desc));
|
||||
kvm_after_interrupt(vcpu);
|
||||
|
||||
vcpu->arch.at_instruction_boundary = true;
|
||||
}
|
||||
|
||||
|
@ -6934,7 +6940,7 @@ static void vmx_handle_exit_irqoff(struct kvm_vcpu *vcpu)
|
|||
if (vmx->exit_reason.basic == EXIT_REASON_EXTERNAL_INTERRUPT)
|
||||
handle_external_interrupt_irqoff(vcpu);
|
||||
else if (vmx->exit_reason.basic == EXIT_REASON_EXCEPTION_NMI)
|
||||
handle_exception_nmi_irqoff(vmx);
|
||||
handle_exception_irqoff(vmx);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -7169,9 +7175,10 @@ static fastpath_t vmx_exit_handlers_fastpath(struct kvm_vcpu *vcpu)
|
|||
}
|
||||
|
||||
static noinstr void vmx_vcpu_enter_exit(struct kvm_vcpu *vcpu,
|
||||
struct vcpu_vmx *vmx,
|
||||
unsigned long flags)
|
||||
unsigned int flags)
|
||||
{
|
||||
struct vcpu_vmx *vmx = to_vmx(vcpu);
|
||||
|
||||
guest_state_enter_irqoff();
|
||||
|
||||
/* L1D Flush includes CPU buffer clear to mitigate MDS */
|
||||
|
@ -7195,6 +7202,18 @@ static noinstr void vmx_vcpu_enter_exit(struct kvm_vcpu *vcpu,
|
|||
|
||||
vmx_enable_fb_clear(vmx);
|
||||
|
||||
if (unlikely(vmx->fail))
|
||||
vmx->exit_reason.full = 0xdead;
|
||||
else
|
||||
vmx->exit_reason.full = vmcs_read32(VM_EXIT_REASON);
|
||||
|
||||
if ((u16)vmx->exit_reason.basic == EXIT_REASON_EXCEPTION_NMI &&
|
||||
is_nmi(vmx_get_intr_info(vcpu))) {
|
||||
kvm_before_interrupt(vcpu, KVM_HANDLING_NMI);
|
||||
vmx_do_nmi_irqoff();
|
||||
kvm_after_interrupt(vcpu);
|
||||
}
|
||||
|
||||
guest_state_exit_irqoff();
|
||||
}
|
||||
|
||||
|
@ -7289,7 +7308,7 @@ static fastpath_t vmx_vcpu_run(struct kvm_vcpu *vcpu)
|
|||
kvm_wait_lapic_expire(vcpu);
|
||||
|
||||
/* The actual VMENTER/EXIT is in the .noinstr.text section. */
|
||||
vmx_vcpu_enter_exit(vcpu, vmx, __vmx_vcpu_run_flags(vmx));
|
||||
vmx_vcpu_enter_exit(vcpu, __vmx_vcpu_run_flags(vmx));
|
||||
|
||||
/* All fields are clean at this point */
|
||||
if (static_branch_unlikely(&enable_evmcs)) {
|
||||
|
@ -7336,12 +7355,9 @@ static fastpath_t vmx_vcpu_run(struct kvm_vcpu *vcpu)
|
|||
|
||||
vmx->idt_vectoring_info = 0;
|
||||
|
||||
if (unlikely(vmx->fail)) {
|
||||
vmx->exit_reason.full = 0xdead;
|
||||
if (unlikely(vmx->fail))
|
||||
return EXIT_FASTPATH_NONE;
|
||||
}
|
||||
|
||||
vmx->exit_reason.full = vmcs_read32(VM_EXIT_REASON);
|
||||
if (unlikely((u16)vmx->exit_reason.basic == EXIT_REASON_MCE_DURING_VMENTRY))
|
||||
kvm_machine_check();
|
||||
|
||||
|
|
|
@ -640,12 +640,12 @@ BUILD_CONTROLS_SHADOW(tertiary_exec, TERTIARY_VM_EXEC_CONTROL, 64)
|
|||
(1 << VCPU_EXREG_EXIT_INFO_1) | \
|
||||
(1 << VCPU_EXREG_EXIT_INFO_2))
|
||||
|
||||
static inline struct kvm_vmx *to_kvm_vmx(struct kvm *kvm)
|
||||
static __always_inline struct kvm_vmx *to_kvm_vmx(struct kvm *kvm)
|
||||
{
|
||||
return container_of(kvm, struct kvm_vmx, kvm);
|
||||
}
|
||||
|
||||
static inline struct vcpu_vmx *to_vmx(struct kvm_vcpu *vcpu)
|
||||
static __always_inline struct vcpu_vmx *to_vmx(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
return container_of(vcpu, struct vcpu_vmx, vcpu);
|
||||
}
|
||||
|
@ -669,25 +669,23 @@ void intel_pmu_cross_mapped_check(struct kvm_pmu *pmu);
|
|||
int intel_pmu_create_guest_lbr_event(struct kvm_vcpu *vcpu);
|
||||
void vmx_passthrough_lbr_msrs(struct kvm_vcpu *vcpu);
|
||||
|
||||
static inline unsigned long vmx_get_exit_qual(struct kvm_vcpu *vcpu)
|
||||
static __always_inline unsigned long vmx_get_exit_qual(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
struct vcpu_vmx *vmx = to_vmx(vcpu);
|
||||
|
||||
if (!kvm_register_is_available(vcpu, VCPU_EXREG_EXIT_INFO_1)) {
|
||||
kvm_register_mark_available(vcpu, VCPU_EXREG_EXIT_INFO_1);
|
||||
if (!kvm_register_test_and_mark_available(vcpu, VCPU_EXREG_EXIT_INFO_1))
|
||||
vmx->exit_qualification = vmcs_readl(EXIT_QUALIFICATION);
|
||||
}
|
||||
|
||||
return vmx->exit_qualification;
|
||||
}
|
||||
|
||||
static inline u32 vmx_get_intr_info(struct kvm_vcpu *vcpu)
|
||||
static __always_inline u32 vmx_get_intr_info(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
struct vcpu_vmx *vmx = to_vmx(vcpu);
|
||||
|
||||
if (!kvm_register_is_available(vcpu, VCPU_EXREG_EXIT_INFO_2)) {
|
||||
kvm_register_mark_available(vcpu, VCPU_EXREG_EXIT_INFO_2);
|
||||
if (!kvm_register_test_and_mark_available(vcpu, VCPU_EXREG_EXIT_INFO_2))
|
||||
vmx->exit_intr_info = vmcs_read32(VM_EXIT_INTR_INFO);
|
||||
}
|
||||
|
||||
return vmx->exit_intr_info;
|
||||
}
|
||||
|
||||
|
|
|
@ -100,8 +100,10 @@ static __always_inline unsigned long __vmcs_readl(unsigned long field)
|
|||
return value;
|
||||
|
||||
do_fail:
|
||||
instrumentation_begin();
|
||||
WARN_ONCE(1, KBUILD_MODNAME ": vmread failed: field=%lx\n", field);
|
||||
pr_warn_ratelimited(KBUILD_MODNAME ": vmread failed: field=%lx\n", field);
|
||||
instrumentation_end();
|
||||
return 0;
|
||||
|
||||
do_exception:
|
||||
|
|
|
@ -394,13 +394,13 @@ enum kvm_intr_type {
|
|||
KVM_HANDLING_NMI,
|
||||
};
|
||||
|
||||
static inline void kvm_before_interrupt(struct kvm_vcpu *vcpu,
|
||||
enum kvm_intr_type intr)
|
||||
static __always_inline void kvm_before_interrupt(struct kvm_vcpu *vcpu,
|
||||
enum kvm_intr_type intr)
|
||||
{
|
||||
WRITE_ONCE(vcpu->arch.handling_intr_from_guest, (u8)intr);
|
||||
}
|
||||
|
||||
static inline void kvm_after_interrupt(struct kvm_vcpu *vcpu)
|
||||
static __always_inline void kvm_after_interrupt(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
WRITE_ONCE(vcpu->arch.handling_intr_from_guest, 0);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue