]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/commitdiff
[AVR32] NMI debugging
authorHaavard Skinnemoen <hskinnemoen@atmel.com>
Wed, 10 Oct 2007 12:58:29 +0000 (14:58 +0200)
committerHaavard Skinnemoen <hskinnemoen@atmel.com>
Fri, 25 Jan 2008 07:31:43 +0000 (08:31 +0100)
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 <hskinnemoen@atmel.com>
Documentation/kernel-parameters.txt
arch/avr32/Kconfig
arch/avr32/kernel/Makefile
arch/avr32/kernel/irq.c
arch/avr32/kernel/nmi_debug.c [new file with mode: 0644]
arch/avr32/kernel/traps.c
arch/avr32/mach-at32ap/at32ap700x.c
arch/avr32/mach-at32ap/extint.c
include/asm-avr32/irq.h
include/asm-avr32/kdebug.h

index c4178778e7fd01d65b99b2279aecad0727701770..17fc60e32443082da684a250e9e0b6fbecb34bbf 100644 (file)
@@ -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.
        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.
        BLACKFIN Blackfin architecture is enabled.
        DRM     Direct Rendering Management support is enabled.
        AX25    Appropriate AX.25 support is enabled.
        BLACKFIN Blackfin architecture is enabled.
        DRM     Direct Rendering Management support is enabled.
@@ -1123,6 +1124,10 @@ and is between 256 and 4096 characters. It is defined in the file
                        of returning the full 64-bit number.
                        The default is to return 64-bit inode numbers.
 
                        of returning the full 64-bit number.
                        The default is to return 64-bit inode numbers.
 
+       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
        nmi_watchdog=   [KNL,BUGS=X86-32] Debugging features for SMP kernels
 
        no387           [BUGS=X86-32] Tells the kernel to use the 387 maths
index 516015b3293b5e8054ff22be49f3323993799b77..e34e2c9c94cb5b678eeae91929682ab081c7e661 100644 (file)
@@ -170,6 +170,16 @@ config OWNERSHIP_TRACE
          enabling Nexus-compliant debuggers to keep track of the PID of the
          currently executing task.
 
          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.
+
 # FPU emulation goes here
 
 source "kernel/Kconfig.hz"
 # FPU emulation goes here
 
 source "kernel/Kconfig.hz"
index bc224a4e39fe72f6d691d7464aee46b1e1c23c20..e4b6d122b03341702f7b2d1477fd6d45b3fe9481 100644 (file)
@@ -12,3 +12,4 @@ obj-y                         += init_task.o switch_to.o cpu.o
 obj-$(CONFIG_MODULES)          += module.o avr32_ksyms.o
 obj-$(CONFIG_KPROBES)          += kprobes.o
 obj-$(CONFIG_STACKTRACE)       += stacktrace.o
 obj-$(CONFIG_MODULES)          += module.o avr32_ksyms.o
 obj-$(CONFIG_KPROBES)          += kprobes.o
 obj-$(CONFIG_STACKTRACE)       += stacktrace.o
+obj-$(CONFIG_NMI_DEBUGGING)    += nmi_debug.o
index 61f2de266f62a3d0060900bdf4ea41bf160726af..a8e767d836aac621c0c449406e32d06b0d13d2db 100644 (file)
@@ -25,6 +25,17 @@ void ack_bad_irq(unsigned int irq)
        printk("unexpected IRQ %u\n", 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)
 {
 #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 (file)
index 0000000..3414b85
--- /dev/null
@@ -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);
index 870c075e631479685f0af15a488ca68cecf3b877..cf6f686d9b0b4c97647316cd93a890930f100990 100644 (file)
@@ -9,6 +9,7 @@
 #include <linux/bug.h>
 #include <linux/init.h>
 #include <linux/kallsyms.h>
 #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>
 #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)
 {
 
 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)
 }
 
 asmlinkage void do_critical_exception(unsigned long ecr, struct pt_regs *regs)
index 9386e1f82fb8c16cf783bab5ace42650bee497d7..14e61f05e1f6a4366a9992f70535c301108a97ed 100644 (file)
@@ -13,6 +13,7 @@
 #include <linux/spi/spi.h>
 
 #include <asm/io.h>
 #include <linux/spi/spi.h>
 
 #include <asm/io.h>
+#include <asm/irq.h>
 
 #include <asm/arch/at32ap700x.h>
 #include <asm/arch/board.h>
 
 #include <asm/arch/at32ap700x.h>
 #include <asm/arch/board.h>
index f5bfd4c81fe70b883fdd620fdd5796fdb41328e3..e108e7bba8c00bc61656088132378964214242ce 100644 (file)
 #define EIC_MODE                               0x0014
 #define EIC_EDGE                               0x0018
 #define EIC_LEVEL                              0x001c
 #define EIC_MODE                               0x0014
 #define EIC_EDGE                               0x0018
 #define EIC_LEVEL                              0x001c
-#define EIC_TEST                               0x0020
 #define EIC_NMIC                               0x0024
 
 #define EIC_NMIC                               0x0024
 
-/* Bitfields in TEST */
-#define EIC_TESTEN_OFFSET                      31
-#define EIC_TESTEN_SIZE                                1
-
 /* Bitfields in NMIC */
 /* Bitfields in NMIC */
-#define EIC_EN_OFFSET                          0
-#define EIC_EN_SIZE                            1
+#define EIC_NMIC_ENABLE                                (1 << 0)
 
 /* Bit manipulation macros */
 #define EIC_BIT(name)                                  \
 
 /* Bit manipulation macros */
 #define EIC_BIT(name)                                  \
@@ -63,6 +57,9 @@ struct eic {
        unsigned int first_irq;
 };
 
        unsigned int first_irq;
 };
 
+static struct eic *nmi_eic;
+static bool nmi_enabled;
+
 static void eic_ack_irq(unsigned int irq)
 {
        struct eic *eic = get_irq_chip_data(irq);
 static void eic_ack_irq(unsigned int irq)
 {
        struct eic *eic = get_irq_chip_data(irq);
@@ -174,6 +171,24 @@ static void demux_eic_irq(unsigned int irq, struct irq_desc *desc)
        }
 }
 
        }
 }
 
+int nmi_enable(void)
+{
+       nmi_enabled = true;
+
+       if (nmi_eic)
+               eic_writel(nmi_eic, NMIC, EIC_NMIC_ENABLE);
+
+       return 0;
+}
+
+void nmi_disable(void)
+{
+       if (nmi_eic)
+               eic_writel(nmi_eic, NMIC, 0);
+
+       nmi_enabled = false;
+}
+
 static int __init eic_probe(struct platform_device *pdev)
 {
        struct eic *eic;
 static int __init eic_probe(struct platform_device *pdev)
 {
        struct eic *eic;
@@ -230,6 +245,16 @@ static int __init eic_probe(struct platform_device *pdev)
        set_irq_chained_handler(int_irq, demux_eic_irq);
        set_irq_data(int_irq, eic);
 
        set_irq_chained_handler(int_irq, demux_eic_irq);
        set_irq_data(int_irq, eic);
 
+       if (pdev->id == 0) {
+               nmi_eic = eic;
+               if (nmi_enabled)
+                       /*
+                        * Someone tried to enable NMI before we were
+                        * ready. Do it now.
+                        */
+                       nmi_enable();
+       }
+
        dev_info(&pdev->dev,
                 "External Interrupt Controller at 0x%p, IRQ %u\n",
                 eic->regs, int_irq);
        dev_info(&pdev->dev,
                 "External Interrupt Controller at 0x%p, IRQ %u\n",
                 eic->regs, int_irq);
index 83e6549d7783f0ab2bff22b1a4a94f822ccb16e7..9315724c059675d7bdcda46007c63c9260afda7c 100644 (file)
@@ -11,4 +11,9 @@
 
 #define irq_canonicalize(i)    (i)
 
 
 #define irq_canonicalize(i)    (i)
 
+#ifndef __ASSEMBLER__
+int nmi_enable(void);
+void nmi_disable(void);
+#endif
+
 #endif /* __ASM_AVR32_IOCTLS_H */
 #endif /* __ASM_AVR32_IOCTLS_H */
index fd7e99046b2f366be239078692c957381c223bc6..ca4f9542365afdd27a061ca22e12448afd0a6a46 100644 (file)
@@ -5,6 +5,7 @@
 enum die_val {
        DIE_BREAKPOINT,
        DIE_SSTEP,
 enum die_val {
        DIE_BREAKPOINT,
        DIE_SSTEP,
+       DIE_NMI,
 };
 
 #endif /* __ASM_AVR32_KDEBUG_H */
 };
 
 #endif /* __ASM_AVR32_KDEBUG_H */