aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHaavard Skinnemoen <hskinnemoen@atmel.com>2007-10-10 08:58:29 -0400
committerHaavard Skinnemoen <hskinnemoen@atmel.com>2008-01-25 02:31:43 -0500
commite7ba176b47db2ed53f258a6b4fe9d9fc6fa437a9 (patch)
treebeb9ffab7da0c24f11c04b6eb4ca29b23b1dd07b
parentf6135d12db4bed3b992052020f1c50d749cd8dc6 (diff)
[AVR32] NMI debugging
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>
-rw-r--r--Documentation/kernel-parameters.txt5
-rw-r--r--arch/avr32/Kconfig10
-rw-r--r--arch/avr32/kernel/Makefile1
-rw-r--r--arch/avr32/kernel/irq.c11
-rw-r--r--arch/avr32/kernel/nmi_debug.c82
-rw-r--r--arch/avr32/kernel/traps.c21
-rw-r--r--arch/avr32/mach-at32ap/at32ap700x.c1
-rw-r--r--arch/avr32/mach-at32ap/extint.c39
-rw-r--r--include/asm-avr32/irq.h5
-rw-r--r--include/asm-avr32/kdebug.h1
10 files changed, 166 insertions, 10 deletions
diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt
index c4178778e7fd..17fc60e32443 100644
--- a/Documentation/kernel-parameters.txt
+++ b/Documentation/kernel-parameters.txt
@@ -34,6 +34,7 @@ parameter is applicable:
34 ALSA ALSA sound support is enabled. 34 ALSA ALSA sound support is enabled.
35 APIC APIC support is enabled. 35 APIC APIC support is enabled.
36 APM Advanced Power Management support is enabled. 36 APM Advanced Power Management support is enabled.
37 AVR32 AVR32 architecture is enabled.
37 AX25 Appropriate AX.25 support is enabled. 38 AX25 Appropriate AX.25 support is enabled.
38 BLACKFIN Blackfin architecture is enabled. 39 BLACKFIN Blackfin architecture is enabled.
39 DRM Direct Rendering Management support is enabled. 40 DRM Direct Rendering Management support is enabled.
@@ -1123,6 +1124,10 @@ and is between 256 and 4096 characters. It is defined in the file
1123 of returning the full 64-bit number. 1124 of returning the full 64-bit number.
1124 The default is to return 64-bit inode numbers. 1125 The default is to return 64-bit inode numbers.
1125 1126
1127 nmi_debug= [KNL,AVR32] Specify one or more actions to take
1128 when a NMI is triggered.
1129 Format: [state][,regs][,debounce][,die]
1130
1126 nmi_watchdog= [KNL,BUGS=X86-32] Debugging features for SMP kernels 1131 nmi_watchdog= [KNL,BUGS=X86-32] Debugging features for SMP kernels
1127 1132
1128 no387 [BUGS=X86-32] Tells the kernel to use the 387 maths 1133 no387 [BUGS=X86-32] Tells the kernel to use the 387 maths
diff --git a/arch/avr32/Kconfig b/arch/avr32/Kconfig
index 516015b3293b..e34e2c9c94cb 100644
--- a/arch/avr32/Kconfig
+++ b/arch/avr32/Kconfig
@@ -170,6 +170,16 @@ config OWNERSHIP_TRACE
170 enabling Nexus-compliant debuggers to keep track of the PID of the 170 enabling Nexus-compliant debuggers to keep track of the PID of the
171 currently executing task. 171 currently executing task.
172 172
173config NMI_DEBUGGING
174 bool "NMI Debugging"
175 default n
176 help
177 Say Y here and pass the nmi_debug command-line parameter to
178 the kernel to turn on NMI debugging. Depending on the value
179 of the nmi_debug option, various pieces of information will
180 be dumped to the console when a Non-Maskable Interrupt
181 happens.
182
173# FPU emulation goes here 183# FPU emulation goes here
174 184
175source "kernel/Kconfig.hz" 185source "kernel/Kconfig.hz"
diff --git a/arch/avr32/kernel/Makefile b/arch/avr32/kernel/Makefile
index bc224a4e39fe..e4b6d122b033 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
12obj-$(CONFIG_MODULES) += module.o avr32_ksyms.o 12obj-$(CONFIG_MODULES) += module.o avr32_ksyms.o
13obj-$(CONFIG_KPROBES) += kprobes.o 13obj-$(CONFIG_KPROBES) += kprobes.o
14obj-$(CONFIG_STACKTRACE) += stacktrace.o 14obj-$(CONFIG_STACKTRACE) += stacktrace.o
15obj-$(CONFIG_NMI_DEBUGGING) += nmi_debug.o
diff --git a/arch/avr32/kernel/irq.c b/arch/avr32/kernel/irq.c
index 61f2de266f62..a8e767d836aa 100644
--- a/arch/avr32/kernel/irq.c
+++ b/arch/avr32/kernel/irq.c
@@ -25,6 +25,17 @@ void ack_bad_irq(unsigned int irq)
25 printk("unexpected IRQ %u\n", irq); 25 printk("unexpected IRQ %u\n", irq);
26} 26}
27 27
28/* May be overridden by platform code */
29int __weak nmi_enable(void)
30{
31 return -ENOSYS;
32}
33
34void __weak nmi_disable(void)
35{
36
37}
38
28#ifdef CONFIG_PROC_FS 39#ifdef CONFIG_PROC_FS
29int show_interrupts(struct seq_file *p, void *v) 40int show_interrupts(struct seq_file *p, void *v)
30{ 41{
diff --git a/arch/avr32/kernel/nmi_debug.c b/arch/avr32/kernel/nmi_debug.c
new file mode 100644
index 000000000000..3414b8566c29
--- /dev/null
+++ b/arch/avr32/kernel/nmi_debug.c
@@ -0,0 +1,82 @@
1/*
2 * Copyright (C) 2007 Atmel Corporation
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 */
8#include <linux/delay.h>
9#include <linux/kdebug.h>
10#include <linux/notifier.h>
11#include <linux/sched.h>
12
13#include <asm/irq.h>
14
15enum nmi_action {
16 NMI_SHOW_STATE = 1 << 0,
17 NMI_SHOW_REGS = 1 << 1,
18 NMI_DIE = 1 << 2,
19 NMI_DEBOUNCE = 1 << 3,
20};
21
22static unsigned long nmi_actions;
23
24static int nmi_debug_notify(struct notifier_block *self,
25 unsigned long val, void *data)
26{
27 struct die_args *args = data;
28
29 if (likely(val != DIE_NMI))
30 return NOTIFY_DONE;
31
32 if (nmi_actions & NMI_SHOW_STATE)
33 show_state();
34 if (nmi_actions & NMI_SHOW_REGS)
35 show_regs(args->regs);
36 if (nmi_actions & NMI_DEBOUNCE)
37 mdelay(10);
38 if (nmi_actions & NMI_DIE)
39 return NOTIFY_BAD;
40
41 return NOTIFY_OK;
42}
43
44static struct notifier_block nmi_debug_nb = {
45 .notifier_call = nmi_debug_notify,
46};
47
48static int __init nmi_debug_setup(char *str)
49{
50 char *p, *sep;
51
52 register_die_notifier(&nmi_debug_nb);
53 if (nmi_enable()) {
54 printk(KERN_WARNING "Unable to enable NMI.\n");
55 return 0;
56 }
57
58 if (*str != '=')
59 return 0;
60
61 for (p = str + 1; *p; p = sep + 1) {
62 sep = strchr(p, ',');
63 if (sep)
64 *sep = 0;
65 if (strcmp(p, "state") == 0)
66 nmi_actions |= NMI_SHOW_STATE;
67 else if (strcmp(p, "regs") == 0)
68 nmi_actions |= NMI_SHOW_REGS;
69 else if (strcmp(p, "debounce") == 0)
70 nmi_actions |= NMI_DEBOUNCE;
71 else if (strcmp(p, "die") == 0)
72 nmi_actions |= NMI_DIE;
73 else
74 printk(KERN_WARNING "NMI: Unrecognized action `%s'\n",
75 p);
76 if (!sep)
77 break;
78 }
79
80 return 0;
81}
82__setup("nmi_debug", nmi_debug_setup);
diff --git a/arch/avr32/kernel/traps.c b/arch/avr32/kernel/traps.c
index 870c075e6314..cf6f686d9b0b 100644
--- a/arch/avr32/kernel/traps.c
+++ b/arch/avr32/kernel/traps.c
@@ -9,6 +9,7 @@
9#include <linux/bug.h> 9#include <linux/bug.h>
10#include <linux/init.h> 10#include <linux/init.h>
11#include <linux/kallsyms.h> 11#include <linux/kallsyms.h>
12#include <linux/kdebug.h>
12#include <linux/module.h> 13#include <linux/module.h>
13#include <linux/notifier.h> 14#include <linux/notifier.h>
14#include <linux/sched.h> 15#include <linux/sched.h>
@@ -107,9 +108,23 @@ void _exception(long signr, struct pt_regs *regs, int code,
107 108
108asmlinkage void do_nmi(unsigned long ecr, struct pt_regs *regs) 109asmlinkage void do_nmi(unsigned long ecr, struct pt_regs *regs)
109{ 110{
110 printk(KERN_ALERT "Got Non-Maskable Interrupt, dumping regs\n"); 111 int ret;
111 show_regs_log_lvl(regs, KERN_ALERT); 112
112 show_stack_log_lvl(current, regs->sp, regs, KERN_ALERT); 113 nmi_enter();
114
115 ret = notify_die(DIE_NMI, "NMI", regs, 0, ecr, SIGINT);
116 switch (ret) {
117 case NOTIFY_OK:
118 case NOTIFY_STOP:
119 return;
120 case NOTIFY_BAD:
121 die("Fatal Non-Maskable Interrupt", regs, SIGINT);
122 default:
123 break;
124 }
125
126 printk(KERN_ALERT "Got NMI, but nobody cared. Disabling...\n");
127 nmi_disable();
113} 128}
114 129
115asmlinkage void do_critical_exception(unsigned long ecr, struct pt_regs *regs) 130asmlinkage void do_critical_exception(unsigned long ecr, struct pt_regs *regs)
diff --git a/arch/avr32/mach-at32ap/at32ap700x.c b/arch/avr32/mach-at32ap/at32ap700x.c
index 9386e1f82fb8..14e61f05e1f6 100644
--- a/arch/avr32/mach-at32ap/at32ap700x.c
+++ b/arch/avr32/mach-at32ap/at32ap700x.c
@@ -13,6 +13,7 @@
13#include <linux/spi/spi.h> 13#include <linux/spi/spi.h>
14 14
15#include <asm/io.h> 15#include <asm/io.h>
16#include <asm/irq.h>
16 17
17#include <asm/arch/at32ap700x.h> 18#include <asm/arch/at32ap700x.h>
18#include <asm/arch/board.h> 19#include <asm/arch/board.h>
diff --git a/arch/avr32/mach-at32ap/extint.c b/arch/avr32/mach-at32ap/extint.c
index f5bfd4c81fe7..e108e7bba8c0 100644
--- a/arch/avr32/mach-at32ap/extint.c
+++ b/arch/avr32/mach-at32ap/extint.c
@@ -26,16 +26,10 @@
26#define EIC_MODE 0x0014 26#define EIC_MODE 0x0014
27#define EIC_EDGE 0x0018 27#define EIC_EDGE 0x0018
28#define EIC_LEVEL 0x001c 28#define EIC_LEVEL 0x001c
29#define EIC_TEST 0x0020
30#define EIC_NMIC 0x0024 29#define EIC_NMIC 0x0024
31 30
32/* Bitfields in TEST */
33#define EIC_TESTEN_OFFSET 31
34#define EIC_TESTEN_SIZE 1
35
36/* Bitfields in NMIC */ 31/* Bitfields in NMIC */
37#define EIC_EN_OFFSET 0 32#define EIC_NMIC_ENABLE (1 << 0)
38#define EIC_EN_SIZE 1
39 33
40/* Bit manipulation macros */ 34/* Bit manipulation macros */
41#define EIC_BIT(name) \ 35#define EIC_BIT(name) \
@@ -63,6 +57,9 @@ struct eic {
63 unsigned int first_irq; 57 unsigned int first_irq;
64}; 58};
65 59
60static struct eic *nmi_eic;
61static bool nmi_enabled;
62
66static void eic_ack_irq(unsigned int irq) 63static void eic_ack_irq(unsigned int irq)
67{ 64{
68 struct eic *eic = get_irq_chip_data(irq); 65 struct eic *eic = get_irq_chip_data(irq);
@@ -174,6 +171,24 @@ static void demux_eic_irq(unsigned int irq, struct irq_desc *desc)
174 } 171 }
175} 172}
176 173
174int nmi_enable(void)
175{
176 nmi_enabled = true;
177
178 if (nmi_eic)
179 eic_writel(nmi_eic, NMIC, EIC_NMIC_ENABLE);
180
181 return 0;
182}
183
184void nmi_disable(void)
185{
186 if (nmi_eic)
187 eic_writel(nmi_eic, NMIC, 0);
188
189 nmi_enabled = false;
190}
191
177static int __init eic_probe(struct platform_device *pdev) 192static int __init eic_probe(struct platform_device *pdev)
178{ 193{
179 struct eic *eic; 194 struct eic *eic;
@@ -230,6 +245,16 @@ static int __init eic_probe(struct platform_device *pdev)
230 set_irq_chained_handler(int_irq, demux_eic_irq); 245 set_irq_chained_handler(int_irq, demux_eic_irq);
231 set_irq_data(int_irq, eic); 246 set_irq_data(int_irq, eic);
232 247
248 if (pdev->id == 0) {
249 nmi_eic = eic;
250 if (nmi_enabled)
251 /*
252 * Someone tried to enable NMI before we were
253 * ready. Do it now.
254 */
255 nmi_enable();
256 }
257
233 dev_info(&pdev->dev, 258 dev_info(&pdev->dev,
234 "External Interrupt Controller at 0x%p, IRQ %u\n", 259 "External Interrupt Controller at 0x%p, IRQ %u\n",
235 eic->regs, int_irq); 260 eic->regs, int_irq);
diff --git a/include/asm-avr32/irq.h b/include/asm-avr32/irq.h
index 83e6549d7783..9315724c0596 100644
--- a/include/asm-avr32/irq.h
+++ b/include/asm-avr32/irq.h
@@ -11,4 +11,9 @@
11 11
12#define irq_canonicalize(i) (i) 12#define irq_canonicalize(i) (i)
13 13
14#ifndef __ASSEMBLER__
15int nmi_enable(void);
16void nmi_disable(void);
17#endif
18
14#endif /* __ASM_AVR32_IOCTLS_H */ 19#endif /* __ASM_AVR32_IOCTLS_H */
diff --git a/include/asm-avr32/kdebug.h b/include/asm-avr32/kdebug.h
index fd7e99046b2f..ca4f9542365a 100644
--- a/include/asm-avr32/kdebug.h
+++ b/include/asm-avr32/kdebug.h
@@ -5,6 +5,7 @@
5enum die_val { 5enum die_val {
6 DIE_BREAKPOINT, 6 DIE_BREAKPOINT,
7 DIE_SSTEP, 7 DIE_SSTEP,
8 DIE_NMI,
8}; 9};
9 10
10#endif /* __ASM_AVR32_KDEBUG_H */ 11#endif /* __ASM_AVR32_KDEBUG_H */