diff options
-rw-r--r-- | Documentation/kernel-parameters.txt | 2 | ||||
-rw-r--r-- | arch/sh/include/asm/kdebug.h | 1 | ||||
-rw-r--r-- | arch/sh/include/asm/system.h | 2 | ||||
-rw-r--r-- | arch/sh/kernel/Makefile | 7 | ||||
-rw-r--r-- | arch/sh/kernel/cpu/sh3/entry.S | 26 | ||||
-rw-r--r-- | arch/sh/kernel/cpu/sh3/ex.S | 4 | ||||
-rw-r--r-- | arch/sh/kernel/irq.c | 2 | ||||
-rw-r--r-- | arch/sh/kernel/nmi_debug.c | 77 | ||||
-rw-r--r-- | arch/sh/kernel/traps.c | 21 |
9 files changed, 133 insertions, 9 deletions
diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 7936b801fe6a..76c355214dc3 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt | |||
@@ -1514,7 +1514,7 @@ and is between 256 and 4096 characters. It is defined in the file | |||
1514 | of returning the full 64-bit number. | 1514 | of returning the full 64-bit number. |
1515 | The default is to return 64-bit inode numbers. | 1515 | The default is to return 64-bit inode numbers. |
1516 | 1516 | ||
1517 | nmi_debug= [KNL,AVR32] Specify one or more actions to take | 1517 | nmi_debug= [KNL,AVR32,SH] Specify one or more actions to take |
1518 | when a NMI is triggered. | 1518 | when a NMI is triggered. |
1519 | Format: [state][,regs][,debounce][,die] | 1519 | Format: [state][,regs][,debounce][,die] |
1520 | 1520 | ||
diff --git a/arch/sh/include/asm/kdebug.h b/arch/sh/include/asm/kdebug.h index 0b9f896f203c..985219f9759e 100644 --- a/arch/sh/include/asm/kdebug.h +++ b/arch/sh/include/asm/kdebug.h | |||
@@ -4,6 +4,7 @@ | |||
4 | /* Grossly misnamed. */ | 4 | /* Grossly misnamed. */ |
5 | enum die_val { | 5 | enum die_val { |
6 | DIE_TRAP, | 6 | DIE_TRAP, |
7 | DIE_NMI, | ||
7 | DIE_OOPS, | 8 | DIE_OOPS, |
8 | }; | 9 | }; |
9 | 10 | ||
diff --git a/arch/sh/include/asm/system.h b/arch/sh/include/asm/system.h index 6b272238a46e..b5c5acdc8c0e 100644 --- a/arch/sh/include/asm/system.h +++ b/arch/sh/include/asm/system.h | |||
@@ -169,7 +169,7 @@ BUILD_TRAP_HANDLER(breakpoint); | |||
169 | BUILD_TRAP_HANDLER(singlestep); | 169 | BUILD_TRAP_HANDLER(singlestep); |
170 | BUILD_TRAP_HANDLER(fpu_error); | 170 | BUILD_TRAP_HANDLER(fpu_error); |
171 | BUILD_TRAP_HANDLER(fpu_state_restore); | 171 | BUILD_TRAP_HANDLER(fpu_state_restore); |
172 | BUILD_TRAP_HANDLER(unwinder); | 172 | BUILD_TRAP_HANDLER(nmi); |
173 | 173 | ||
174 | #ifdef CONFIG_BUG | 174 | #ifdef CONFIG_BUG |
175 | extern void handle_BUG(struct pt_regs *); | 175 | extern void handle_BUG(struct pt_regs *); |
diff --git a/arch/sh/kernel/Makefile b/arch/sh/kernel/Makefile index f37cf02ad9be..a2d0a40f3848 100644 --- a/arch/sh/kernel/Makefile +++ b/arch/sh/kernel/Makefile | |||
@@ -10,9 +10,10 @@ CFLAGS_REMOVE_ftrace.o = -pg | |||
10 | endif | 10 | endif |
11 | 11 | ||
12 | obj-y := debugtraps.o dumpstack.o idle.o io.o io_generic.o irq.o \ | 12 | obj-y := debugtraps.o dumpstack.o idle.o io.o io_generic.o irq.o \ |
13 | machvec.o process_$(BITS).o ptrace_$(BITS).o setup.o \ | 13 | machvec.o nmi_debug.o process_$(BITS).o ptrace_$(BITS).o \ |
14 | signal_$(BITS).o sys_sh.o sys_sh$(BITS).o syscalls_$(BITS).o \ | 14 | setup.o signal_$(BITS).o sys_sh.o sys_sh$(BITS).o \ |
15 | time.o topology.o traps.o traps_$(BITS).o unwinder.o | 15 | syscalls_$(BITS).o time.o topology.o traps.o \ |
16 | traps_$(BITS).o unwinder.o | ||
16 | 17 | ||
17 | obj-y += cpu/ | 18 | obj-y += cpu/ |
18 | obj-$(CONFIG_VSYSCALL) += vsyscall/ | 19 | obj-$(CONFIG_VSYSCALL) += vsyscall/ |
diff --git a/arch/sh/kernel/cpu/sh3/entry.S b/arch/sh/kernel/cpu/sh3/entry.S index aebd33d18ff7..d1142d365925 100644 --- a/arch/sh/kernel/cpu/sh3/entry.S +++ b/arch/sh/kernel/cpu/sh3/entry.S | |||
@@ -532,7 +532,33 @@ ENTRY(handle_interrupt) | |||
532 | mov.l 2f, r4 | 532 | mov.l 2f, r4 |
533 | mov.l 3f, r9 | 533 | mov.l 3f, r9 |
534 | mov.l @r4, r4 ! pass INTEVT vector as arg0 | 534 | mov.l @r4, r4 ! pass INTEVT vector as arg0 |
535 | |||
536 | shlr2 r4 | ||
537 | shlr r4 | ||
538 | mov r4, r0 ! save vector->jmp table offset for later | ||
539 | |||
540 | shlr2 r4 ! vector to IRQ# conversion | ||
541 | add #-0x10, r4 | ||
542 | |||
543 | cmp/pz r4 ! is it a valid IRQ? | ||
544 | bt 10f | ||
545 | |||
546 | /* | ||
547 | * We got here as a result of taking the INTEVT path for something | ||
548 | * that isn't a valid hard IRQ, therefore we bypass the do_IRQ() | ||
549 | * path and special case the event dispatch instead. This is the | ||
550 | * expected path for the NMI (and any other brilliantly implemented | ||
551 | * exception), which effectively wants regular exception dispatch | ||
552 | * but is unfortunately reported through INTEVT rather than | ||
553 | * EXPEVT. Grr. | ||
554 | */ | ||
555 | mov.l 6f, r9 | ||
556 | mov.l @(r0, r9), r9 | ||
535 | jmp @r9 | 557 | jmp @r9 |
558 | mov r15, r8 ! trap handlers take saved regs in r8 | ||
559 | |||
560 | 10: | ||
561 | jmp @r9 ! Off to do_IRQ() we go. | ||
536 | mov r15, r5 ! pass saved registers as arg1 | 562 | mov r15, r5 ! pass saved registers as arg1 |
537 | 563 | ||
538 | ENTRY(exception_none) | 564 | ENTRY(exception_none) |
diff --git a/arch/sh/kernel/cpu/sh3/ex.S b/arch/sh/kernel/cpu/sh3/ex.S index e5a0de39a2db..46610c35c232 100644 --- a/arch/sh/kernel/cpu/sh3/ex.S +++ b/arch/sh/kernel/cpu/sh3/ex.S | |||
@@ -48,9 +48,7 @@ ENTRY(exception_handling_table) | |||
48 | .long system_call ! Unconditional Trap /* 160 */ | 48 | .long system_call ! Unconditional Trap /* 160 */ |
49 | .long exception_error ! reserved_instruction (filled by trap_init) /* 180 */ | 49 | .long exception_error ! reserved_instruction (filled by trap_init) /* 180 */ |
50 | .long exception_error ! illegal_slot_instruction (filled by trap_init) /*1A0*/ | 50 | .long exception_error ! illegal_slot_instruction (filled by trap_init) /*1A0*/ |
51 | ENTRY(nmi_slot) | 51 | .long nmi_trap_handler /* 1C0 */ ! Allow trap to debugger |
52 | .long kgdb_handle_exception /* 1C0 */ ! Allow trap to debugger | ||
53 | ENTRY(user_break_point_trap) | ||
54 | .long break_point_trap /* 1E0 */ | 52 | .long break_point_trap /* 1E0 */ |
55 | 53 | ||
56 | /* | 54 | /* |
diff --git a/arch/sh/kernel/irq.c b/arch/sh/kernel/irq.c index d1053392e287..60f8af4497c7 100644 --- a/arch/sh/kernel/irq.c +++ b/arch/sh/kernel/irq.c | |||
@@ -114,7 +114,7 @@ asmlinkage int do_IRQ(unsigned int irq, struct pt_regs *regs) | |||
114 | #endif | 114 | #endif |
115 | 115 | ||
116 | irq_enter(); | 116 | irq_enter(); |
117 | irq = irq_demux(evt2irq(irq)); | 117 | irq = irq_demux(irq); |
118 | 118 | ||
119 | #ifdef CONFIG_IRQSTACKS | 119 | #ifdef CONFIG_IRQSTACKS |
120 | curctx = (union irq_ctx *)current_thread_info(); | 120 | curctx = (union irq_ctx *)current_thread_info(); |
diff --git a/arch/sh/kernel/nmi_debug.c b/arch/sh/kernel/nmi_debug.c new file mode 100644 index 000000000000..ff0abbd1e652 --- /dev/null +++ b/arch/sh/kernel/nmi_debug.c | |||
@@ -0,0 +1,77 @@ | |||
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 | #include <linux/hardirq.h> | ||
13 | |||
14 | enum nmi_action { | ||
15 | NMI_SHOW_STATE = 1 << 0, | ||
16 | NMI_SHOW_REGS = 1 << 1, | ||
17 | NMI_DIE = 1 << 2, | ||
18 | NMI_DEBOUNCE = 1 << 3, | ||
19 | }; | ||
20 | |||
21 | static unsigned long nmi_actions; | ||
22 | |||
23 | static int nmi_debug_notify(struct notifier_block *self, | ||
24 | unsigned long val, void *data) | ||
25 | { | ||
26 | struct die_args *args = data; | ||
27 | |||
28 | if (likely(val != DIE_NMI)) | ||
29 | return NOTIFY_DONE; | ||
30 | |||
31 | if (nmi_actions & NMI_SHOW_STATE) | ||
32 | show_state(); | ||
33 | if (nmi_actions & NMI_SHOW_REGS) | ||
34 | show_regs(args->regs); | ||
35 | if (nmi_actions & NMI_DEBOUNCE) | ||
36 | mdelay(10); | ||
37 | if (nmi_actions & NMI_DIE) | ||
38 | return NOTIFY_BAD; | ||
39 | |||
40 | return NOTIFY_OK; | ||
41 | } | ||
42 | |||
43 | static struct notifier_block nmi_debug_nb = { | ||
44 | .notifier_call = nmi_debug_notify, | ||
45 | }; | ||
46 | |||
47 | static int __init nmi_debug_setup(char *str) | ||
48 | { | ||
49 | char *p, *sep; | ||
50 | |||
51 | register_die_notifier(&nmi_debug_nb); | ||
52 | |||
53 | if (*str != '=') | ||
54 | return 0; | ||
55 | |||
56 | for (p = str + 1; *p; p = sep + 1) { | ||
57 | sep = strchr(p, ','); | ||
58 | if (sep) | ||
59 | *sep = 0; | ||
60 | if (strcmp(p, "state") == 0) | ||
61 | nmi_actions |= NMI_SHOW_STATE; | ||
62 | else if (strcmp(p, "regs") == 0) | ||
63 | nmi_actions |= NMI_SHOW_REGS; | ||
64 | else if (strcmp(p, "debounce") == 0) | ||
65 | nmi_actions |= NMI_DEBOUNCE; | ||
66 | else if (strcmp(p, "die") == 0) | ||
67 | nmi_actions |= NMI_DIE; | ||
68 | else | ||
69 | printk(KERN_WARNING "NMI: Unrecognized action `%s'\n", | ||
70 | p); | ||
71 | if (!sep) | ||
72 | break; | ||
73 | } | ||
74 | |||
75 | return 0; | ||
76 | } | ||
77 | __setup("nmi_debug", nmi_debug_setup); | ||
diff --git a/arch/sh/kernel/traps.c b/arch/sh/kernel/traps.c index f69bd968fcca..a8396f36bd14 100644 --- a/arch/sh/kernel/traps.c +++ b/arch/sh/kernel/traps.c | |||
@@ -5,6 +5,7 @@ | |||
5 | #include <linux/signal.h> | 5 | #include <linux/signal.h> |
6 | #include <linux/sched.h> | 6 | #include <linux/sched.h> |
7 | #include <linux/uaccess.h> | 7 | #include <linux/uaccess.h> |
8 | #include <linux/hardirq.h> | ||
8 | #include <asm/unwinder.h> | 9 | #include <asm/unwinder.h> |
9 | #include <asm/system.h> | 10 | #include <asm/system.h> |
10 | 11 | ||
@@ -91,3 +92,23 @@ BUILD_TRAP_HANDLER(bug) | |||
91 | 92 | ||
92 | force_sig(SIGTRAP, current); | 93 | force_sig(SIGTRAP, current); |
93 | } | 94 | } |
95 | |||
96 | BUILD_TRAP_HANDLER(nmi) | ||
97 | { | ||
98 | TRAP_HANDLER_DECL; | ||
99 | |||
100 | nmi_enter(); | ||
101 | |||
102 | switch (notify_die(DIE_NMI, "NMI", regs, 0, vec & 0xff, SIGINT)) { | ||
103 | case NOTIFY_OK: | ||
104 | case NOTIFY_STOP: | ||
105 | break; | ||
106 | case NOTIFY_BAD: | ||
107 | die("Fatal Non-Maskable Interrupt", regs, SIGINT); | ||
108 | default: | ||
109 | printk(KERN_ALERT "Got NMI, but nobody cared. Ignoring...\n"); | ||
110 | break; | ||
111 | } | ||
112 | |||
113 | nmi_exit(); | ||
114 | } | ||