/* * NMI exception handler. */ #include #include #include #include #include #include #include #include #include #include #include #include static RAW_NOTIFIER_HEAD(nmi_chain); static DEFINE_PER_CPU(struct cpu_context, nmi_context); static DECLARE_BITMAP(sync_mask, NR_CPUS); static int __kprobes nmi_sync_handler(unsigned int cmd, struct pt_regs *regs) { struct cpumask *cpu_mask; struct cpu_context *ctx; int this_cpu; this_cpu = smp_processor_id(); cpu_mask = to_cpumask(sync_mask); if (cpumask_test_cpu(this_cpu, cpu_mask)) { ctx = this_cpu_ptr(&nmi_context); ctx->task = current; ctx->regs = regs; smp_mb__before_atomic(); cpumask_clear_cpu(this_cpu, cpu_mask); /* Patiently wait for the end of the world... */ for (;;) cpu_relax(); } return NMI_DONE; } static int __init register_nmi_sync(void) { register_nmi_handler(NMI_LOCAL, nmi_sync_handler, 0, "wd_sync"); return 0; } early_initcall(register_nmi_sync); /** */ int register_nmi_notifier(struct notifier_block *nb) { return raw_notifier_chain_register(&nmi_chain, nb); } /** * Called in raw_notifier_call_chain() */ struct cpu_context *nmi_get_context(int cpu) { return per_cpu_ptr(&nmi_context, cpu); } /** * nmi-context */ void nmi_exception(struct pt_regs *regs) { struct cpumask *cpumask = to_cpumask(sync_mask); struct cpu_context *ctx; int i, this_cpu; this_cpu = smp_processor_id(); /* Can't use nmi printk, since we never return from nmi context and * it therefore just buffers away all messages, and they never get * output. */ printk_nmi_exit(); /* Prepare cpumask for all CPUs we have to wait for... */ cpumask_xor(cpumask, cpumask_of(this_cpu), cpu_online_mask); /* ...and trigger an NMI on all of them. */ pr_devel("sending NMI to other CPUs:\n"); apic->send_IPI_allbutself(NMI_VECTOR); /* Give the other CPUs up to 10s for syncing. */ for (i = 0; i < 10 * MSEC_PER_SEC; i++) { if (cpumask_empty(cpumask)) break; mdelay(1); } ctx = this_cpu_ptr(&nmi_context); ctx->task = current; ctx->regs = regs; raw_notifier_call_chain(&nmi_chain, 0, NULL); /* If the notifiers didn't take down the machine, restart */ die("NMI", regs, DIE_NMI); machine_restart(NULL); }