1 /* SPDX-License-Identifier: GPL-2.0 */ 2 3 #include <linux/entry-common.h> 4 #include <linux/kvm_types.h> 5 #include <linux/hrtimer_rearm.h> 6 #include <asm/fred.h> 7 #include <asm/desc.h> 8 9 #if IS_ENABLED(CONFIG_KVM_INTEL) 10 /* 11 * On VMX, NMIs and IRQs (as configured by KVM) are acknowledged by hardware as 12 * part of the VM-Exit, i.e. the event itself is consumed as part the VM-Exit. 13 * x86_entry_from_kvm() is invoked by KVM to effectively forward NMIs and IRQs 14 * to the kernel for servicing. On SVM, a.k.a. AMD, the NMI/IRQ VM-Exit is 15 * purely a signal that an NMI/IRQ is pending, i.e. the event that triggered 16 * the VM-Exit is held pending until it's unblocked in the host. 17 */ 18 noinstr void x86_entry_from_kvm(unsigned int event_type, unsigned int vector) 19 { 20 if (event_type == EVENT_TYPE_EXTINT) { 21 #ifdef CONFIG_X86_64 22 /* 23 * Use FRED dispatch, even when running IDT. The dispatch 24 * tables are kept in sync between FRED and IDT, and the FRED 25 * dispatch works well with CFI. 26 */ 27 fred_entry_from_kvm(event_type, vector); 28 #else 29 idt_entry_from_kvm(vector); 30 #endif 31 /* 32 * Strictly speaking, only the NMI path requires noinstr. 33 */ 34 instrumentation_begin(); 35 /* 36 * KVM/VMX will dispatch from IRQ-disabled but for a context 37 * that will have IRQs-enabled. This confuses the entry code 38 * and it will not have reprogrammed the timer. Do so now. 39 */ 40 hrtimer_rearm_deferred(); 41 instrumentation_end(); 42 43 return; 44 } 45 46 WARN_ON_ONCE(event_type != EVENT_TYPE_NMI); 47 48 #ifdef CONFIG_X86_64 49 if (cpu_feature_enabled(X86_FEATURE_FRED)) 50 return fred_entry_from_kvm(event_type, vector); 51 #endif 52 53 /* 54 * Notably, we must use IDT dispatch for NMI when running in IDT mode. 55 * The FRED NMI context is significantly different and will not work 56 * right (specifically FRED fixed the NMI recursion issue). 57 */ 58 idt_do_nmi_irqoff(); 59 } 60 EXPORT_SYMBOL_FOR_KVM(x86_entry_from_kvm); 61 #endif 62