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(-) Index: b/hw/apic.c =================================================================== --- a/hw/apic.c +++ b/hw/apic.c @@ -216,8 +216,14 @@ static void apic_bus_deliver(const uint3 break; 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 */ Index: b/cpu-all.h =================================================================== --- a/cpu-all.h +++ b/cpu-all.h @@ -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); Index: b/cpu-exec.c =================================================================== --- a/cpu-exec.c +++ b/cpu-exec.c @@ -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)) { Index: b/target-i386/cpu.h =================================================================== --- a/target-i386/cpu.h +++ b/target-i386/cpu.h @@ -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) Index: b/target-i386/helper.c =================================================================== --- a/target-i386/helper.c +++ b/target-i386/helper.c @@ -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;