APIC: add NMI and SMI IPI support
PATCH 1/1 APIC: add NMI and SMI IPI support
While testing KGDB (yeah, it actually seem to make it into mainline!) under QEMU, I failed to get it running in SMP mode. Reason: NMI IPIs are not correctly handled by QEMU's emulated APIC.
To overcome this, the patch below introduces a new interruption request, CPU_INTERRUPT_NMI, so that a VCPU can cleanly send this special interrupt to other VCPUs. It also introduces HF_NMI_MASK which shall ensure that NMIs are not recursively triggered, but I must confess that this particular property was not really tested yet.
CPU_INTERRUPT_NMI is then trivially exploited by apic_bus_deliver to send out both NMIs and (for the sake of completeness - it's untested as well SMIs).
With this patch applied, I'm finally able to run (and potentially debug) KGDB for Linux SMP guests.
Jan
--- cpu-all.h | 1 + cpu-exec.c | 6 ++++++ hw/apic.c | 8 +++++++- target-i386/cpu.h | 2 ++ target-i386/helper.c | 2 ++ 5 files changed, 18 insertions(+), 1 deletion(-)
hw/apic.c +7 -1
@@ -216,8 +216,14 @@ static void apic_bus_deliver(const uint3break ;case APIC_DM_SMI : +foreach_apic (apic_iter, deliver_bitmask, +cpu_interrupt (apic_iter->cpu_env,CPU_INTERRUPT_SMI ) ); +return ; +case APIC_DM_NMI : -break ; +foreach_apic (apic_iter, deliver_bitmask, +cpu_interrupt (apic_iter->cpu_env,CPU_INTERRUPT_NMI ) ); +return ;case APIC_DM_INIT :/* normal INIT IPI sent to processors */
cpu-all.h +1 -0
@@ -748,6 +748,7 @@ extern int code_copy_enabled;#define CPU_INTERRUPT_SMI 0x40/* (x86 only) SMI interrupt pending */ #define CPU_INTERRUPT_DEBUG 0x80/* Debug event occured. */ #define CPU_INTERRUPT_VIRQ 0x100/* virtual interrupt pending. */ +#define CPU_INTERRUPT_NMI 0x200/* NMI pending. */ void cpu_interrupt (CPUState *s,int mask);void cpu_reset_interrupt (CPUState *env,int mask);
cpu-exec.c +6 -0
@@ -505,6 +505,12 @@ int cpu_exec(CPUState *env1) env->interrupt_request &= ~CPU_INTERRUPT_SMI ;do_smm_enter ();BREAK_CHAIN ; + }else if ((interrupt_request &CPU_INTERRUPT_NMI ) && + !(env->hflags &HF_NMI_MASK )) { + env->interrupt_request &= ~CPU_INTERRUPT_NMI ; + env->hflags |=HF_NMI_MASK ; +do_interrupt (EXCP02_NMI ,0 ,0 ,0 ,1 ); +BREAK_CHAIN ; }else if ((interrupt_request &CPU_INTERRUPT_HARD ) && (env->eflags &IF_MASK || env->hflags &HF_HIF_MASK ) && !(env->hflags &HF_INHIBIT_IRQ_MASK )) {
target-i386/cpu.h +2 -0
@@ -148,6 +148,7 @@#define HF_SMM_SHIFT 19/* CPU in SMM mode */ #define HF_GIF_SHIFT 20/* if set CPU takes interrupts */ #define HF_HIF_SHIFT 21/* shadow copy of IF_MASK when in SVM */ +#define HF_NMI_SHIFT 22/* CPU serving NMI */ #define HF_CPL_MASK (3 << HF_CPL_SHIFT)#define HF_SOFTMMU_MASK (1 << HF_SOFTMMU_SHIFT)
@@ -167,6 +168,7 @@#define HF_SMM_MASK (1 << HF_SMM_SHIFT)#define HF_GIF_MASK (1 << HF_GIF_SHIFT)#define HF_HIF_MASK (1 << HF_HIF_SHIFT) +#define HF_NMI_MASK (1 << HF_NMI_SHIFT)#define CR0_PE_MASK (1 << 0)#define CR0_MP_MASK (1 << 1)
target-i386/helper.c +2 -0
@@ -2382,6 +2382,7 @@ void helper_iret_real(int shift)if (shift ==0 ) eflags_mask &=0xffff ;load_eflags (new_eflags, eflags_mask); + env->hflags &= ~HF_NMI_MASK ; }static inline void validate_seg (int seg_reg,int cpl)
@@ -2633,6 +2634,7 @@ void helper_iret_protected(int shift, in }else {helper_ret_protected (shift,1 ,0 ); } + env->hflags &= ~HF_NMI_MASK ;#ifdef USE_KQEMU if (kqemu_is_ok (env)) {CC_OP =CC_OP_EFLAGS ;