Change the NMI handler to use the die notifier chain to signal anyone
who cares. Add a simple "nmi debugger" which hooks into this chain and
that may dump registers, task state, etc. when it happens.
Signed-off-by: Haavard Skinnemoen <[EMAIL PROTECTED]>
---
Documentation/kernel-parameters.txt | 5 ++
arch/avr32/Kconfig | 10 ++++
arch/avr32/kernel/Makefile | 1 +
arch/avr32/kernel/irq.c | 11 +++++
arch/avr32/kernel/nmi_debug.c | 82 +++++++++++++++++++++++++++++++++++
arch/avr32/kernel/traps.c | 21 ++++++++-
arch/avr32/mach-at32ap/at32ap7000.c | 25 +++++++++++
include/asm-avr32/irq.h | 5 ++
include/asm-avr32/kdebug.h | 1 +
9 files changed, 158 insertions(+), 3 deletions(-)
create mode 100644 arch/avr32/kernel/nmi_debug.c
diff --git a/Documentation/kernel-parameters.txt
b/Documentation/kernel-parameters.txt
index b41cde3..524634e 100644
--- a/Documentation/kernel-parameters.txt
+++ b/Documentation/kernel-parameters.txt
@@ -34,6 +34,7 @@ parameter is applicable:
ALSA ALSA sound support is enabled.
APIC APIC support is enabled.
APM Advanced Power Management support is enabled.
+ AVR32 AVR32 architecture is enabled.
AX25 Appropriate AX.25 support is enabled.
DRM Direct Rendering Management support is enabled.
EDD BIOS Enhanced Disk Drive Services (EDD) is enabled
@@ -1076,6 +1077,10 @@ and is between 256 and 4096 characters. It is defined in
the file
[NFS] set the maximum lifetime for idmapper cache
entries.
+ nmi_debug= [KNL,AVR32] Specify one or more actions to take
+ when a NMI is triggered.
+ Format: [state][,regs][,debounce][,die]
+
nmi_watchdog= [KNL,BUGS=X86-32] Debugging features for SMP kernels
no387 [BUGS=X86-32] Tells the kernel to use the 387 maths
diff --git a/arch/avr32/Kconfig b/arch/avr32/Kconfig
index ec6c7c5..2f5fe0d 100644
--- a/arch/avr32/Kconfig
+++ b/arch/avr32/Kconfig
@@ -178,6 +178,16 @@ config OWNERSHIP_TRACE
enabling Nexus-compliant debuggers to keep track of the PID of the
currently executing task.
+config NMI_DEBUGGING
+ bool "NMI Debugging"
+ default n
+ help
+ Say Y here and pass the nmi_debug command-line parameter to
+ the kernel to turn on NMI debugging. Depending on the value
+ of the nmi_debug option, various pieces of information will
+ be dumped to the console when a Non-Maskable Interrupt
+ happens.
+
config DW_DMAC
tristate "Synopsys DesignWare DMA Controller support"
default y if CPU_AT32AP7000
diff --git a/arch/avr32/kernel/Makefile b/arch/avr32/kernel/Makefile
index 1aedaeb..0f45975 100644
--- a/arch/avr32/kernel/Makefile
+++ b/arch/avr32/kernel/Makefile
@@ -12,3 +12,4 @@ obj-y += init_task.o switch_to.o cpu.o
obj-y += dma-controller.o
obj-$(CONFIG_MODULES) += module.o avr32_ksyms.o
obj-$(CONFIG_KPROBES) += kprobes.o
+obj-$(CONFIG_NMI_DEBUGGING) += nmi_debug.o
diff --git a/arch/avr32/kernel/irq.c b/arch/avr32/kernel/irq.c
index 61f2de2..a8e767d 100644
--- a/arch/avr32/kernel/irq.c
+++ b/arch/avr32/kernel/irq.c
@@ -25,6 +25,17 @@ void ack_bad_irq(unsigned int irq)
printk("unexpected IRQ %u\n", irq);
}
+/* May be overridden by platform code */
+int __weak nmi_enable(void)
+{
+ return -ENOSYS;
+}
+
+void __weak nmi_disable(void)
+{
+
+}
+
#ifdef CONFIG_PROC_FS
int show_interrupts(struct seq_file *p, void *v)
{
diff --git a/arch/avr32/kernel/nmi_debug.c b/arch/avr32/kernel/nmi_debug.c
new file mode 100644
index 0000000..3414b85
--- /dev/null
+++ b/arch/avr32/kernel/nmi_debug.c
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2007 Atmel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/delay.h>
+#include <linux/kdebug.h>
+#include <linux/notifier.h>
+#include <linux/sched.h>
+
+#include <asm/irq.h>
+
+enum nmi_action {
+ NMI_SHOW_STATE = 1 << 0,
+ NMI_SHOW_REGS = 1 << 1,
+ NMI_DIE = 1 << 2,
+ NMI_DEBOUNCE = 1 << 3,
+};
+
+static unsigned long nmi_actions;
+
+static int nmi_debug_notify(struct notifier_block *self,
+ unsigned long val, void *data)
+{
+ struct die_args *args = data;
+
+ if (likely(val != DIE_NMI))
+ return NOTIFY_DONE;
+
+ if (nmi_actions & NMI_SHOW_STATE)
+ show_state();
+ if (nmi_actions & NMI_SHOW_REGS)
+ show_regs(args->regs);
+ if (nmi_actions & NMI_DEBOUNCE)
+ mdelay(10);
+ if (nmi_actions & NMI_DIE)
+ return NOTIFY_BAD;
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block nmi_debug_nb = {
+ .notifier_call = nmi_debug_notify,
+};
+
+static int __init nmi_debug_setup(char *str)
+{
+ char *p, *sep;
+
+ register_die_notifier(&nmi_debug_nb);
+ if (nmi_enable()) {
+ printk(KERN_WARNING "Unable to enable NMI.\n");
+ return 0;
+ }
+
+ if (*str != '=')
+ return 0;
+
+ for (p = str + 1; *p; p = sep + 1) {
+ sep = strchr(p, ',');
+ if (sep)
+ *sep = 0;
+ if (strcmp(p, "state") == 0)
+ nmi_actions |= NMI_SHOW_STATE;
+ else if (strcmp(p, "regs") == 0)
+ nmi_actions |= NMI_SHOW_REGS;
+ else if (strcmp(p, "debounce") == 0)
+ nmi_actions |= NMI_DEBOUNCE;
+ else if (strcmp(p, "die") == 0)
+ nmi_actions |= NMI_DIE;
+ else
+ printk(KERN_WARNING "NMI: Unrecognized action `%s'\n",
+ p);
+ if (!sep)
+ break;
+ }
+
+ return 0;
+}
+__setup("nmi_debug", nmi_debug_setup);
diff --git a/arch/avr32/kernel/traps.c b/arch/avr32/kernel/traps.c
index 9a73ce7..d18b46b 100644
--- a/arch/avr32/kernel/traps.c
+++ b/arch/avr32/kernel/traps.c
@@ -9,6 +9,7 @@
#include <linux/bug.h>
#include <linux/init.h>
#include <linux/kallsyms.h>
+#include <linux/kdebug.h>
#include <linux/module.h>
#include <linux/notifier.h>
#include <linux/sched.h>
@@ -107,9 +108,23 @@ void _exception(long signr, struct pt_regs *regs, int code,
asmlinkage void do_nmi(unsigned long ecr, struct pt_regs *regs)
{
- printk(KERN_ALERT "Got Non-Maskable Interrupt, dumping regs\n");
- show_regs_log_lvl(regs, KERN_ALERT);
- show_stack_log_lvl(current, regs->sp, regs, KERN_ALERT);
+ int ret;
+
+ nmi_enter();
+
+ ret = notify_die(DIE_NMI, "NMI", regs, 0, ecr, SIGINT);
+ switch (ret) {
+ case NOTIFY_OK:
+ case NOTIFY_STOP:
+ return;
+ case NOTIFY_BAD:
+ die("Fatal Non-Maskable Interrupt", regs, SIGINT);
+ default:
+ break;
+ }
+
+ printk(KERN_ALERT "Got NMI, but nobody cared. Disabling...\n");
+ nmi_disable();
}
asmlinkage void do_critical_exception(unsigned long ecr, struct pt_regs *regs)
diff --git a/arch/avr32/mach-at32ap/at32ap7000.c
b/arch/avr32/mach-at32ap/at32ap7000.c
index a12ca61..a69165b 100644
--- a/arch/avr32/mach-at32ap/at32ap7000.c
+++ b/arch/avr32/mach-at32ap/at32ap7000.c
@@ -13,6 +13,7 @@
#include <linux/spi/spi.h>
#include <asm/io.h>
+#include <asm/irq.h>
#include <asm/arch/at32ap7000.h>
#include <asm/arch/board.h>
@@ -21,6 +22,7 @@
#include <video/atmel_lcdc.h>
#include "clock.h"
+#include "eic.h"
#include "hmatrix.h"
#include "pio.h"
#include "pm.h"
@@ -567,6 +569,29 @@ static struct resource dmaca0_resource[] = {
DEFINE_DEV(dmaca, 0);
DEV_CLK(hclk, dmaca0, hsb, 10);
+int nmi_enable(void)
+{
+ /* We know that phys==virt for internal devices */
+ void __iomem *regs = (void __iomem *)0xfff00100;
+
+ clk_enable(&at32_pm_pclk);
+ eic_writel(regs, NMIC, EIC_NMIC_ENABLE);
+ eic_readl(regs, NMIC);
+ clk_disable(&at32_pm_pclk);
+
+ return 0;
+}
+
+void nmi_disable(void)
+{
+ void __iomem *regs = (void __iomem *)0xfff00100;
+
+ clk_enable(&at32_pm_pclk);
+ eic_writel(regs, NMIC, 0);
+ eic_readl(regs, NMIC);
+ clk_disable(&at32_pm_pclk);
+}
+
/* --------------------------------------------------------------------
* HMATRIX
* -------------------------------------------------------------------- */
diff --git a/include/asm-avr32/irq.h b/include/asm-avr32/irq.h
index 83e6549..9315724 100644
--- a/include/asm-avr32/irq.h
+++ b/include/asm-avr32/irq.h
@@ -11,4 +11,9 @@
#define irq_canonicalize(i) (i)
+#ifndef __ASSEMBLER__
+int nmi_enable(void);
+void nmi_disable(void);
+#endif
+
#endif /* __ASM_AVR32_IOCTLS_H */
diff --git a/include/asm-avr32/kdebug.h b/include/asm-avr32/kdebug.h
index 7f54e2b..eebea1b 100644
--- a/include/asm-avr32/kdebug.h
+++ b/include/asm-avr32/kdebug.h
@@ -7,6 +7,7 @@
enum die_val {
DIE_BREAKPOINT,
DIE_SSTEP,
+ DIE_NMI,
};
/*
--
1.5.3.2
-
To unsubscribe from this list: send the line "unsubscribe linux-arch" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at http://vger.kernel.org/majordomo-info.html