summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/kernel-parameters.txt2
-rw-r--r--arch/sh/include/asm/kdebug.h1
-rw-r--r--arch/sh/include/asm/system.h2
-rw-r--r--arch/sh/kernel/Makefile7
-rw-r--r--arch/sh/kernel/cpu/sh3/entry.S26
-rw-r--r--arch/sh/kernel/cpu/sh3/ex.S4
-rw-r--r--arch/sh/kernel/irq.c2
-rw-r--r--arch/sh/kernel/nmi_debug.c77
-rw-r--r--arch/sh/kernel/traps.c21
9 files changed, 133 insertions, 9 deletions
diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt
index 7936b801fe6..76c355214dc 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
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
+ nmi_debug= [KNL,AVR32,SH] Specify one or more actions to take
when a NMI is triggered.
Format: [state][,regs][,debounce][,die]
diff --git a/arch/sh/include/asm/kdebug.h b/arch/sh/include/asm/kdebug.h
index 0b9f896f203..985219f9759 100644
--- a/arch/sh/include/asm/kdebug.h
+++ b/arch/sh/include/asm/kdebug.h
@@ -4,6 +4,7 @@
/* Grossly misnamed. */
enum die_val {
DIE_TRAP,
+ DIE_NMI,
DIE_OOPS,
};
diff --git a/arch/sh/include/asm/system.h b/arch/sh/include/asm/system.h
index 6b272238a46..b5c5acdc8c0 100644
--- a/arch/sh/include/asm/system.h
+++ b/arch/sh/include/asm/system.h
@@ -169,7 +169,7 @@ BUILD_TRAP_HANDLER(breakpoint);
BUILD_TRAP_HANDLER(singlestep);
BUILD_TRAP_HANDLER(fpu_error);
BUILD_TRAP_HANDLER(fpu_state_restore);
-BUILD_TRAP_HANDLER(unwinder);
+BUILD_TRAP_HANDLER(nmi);
#ifdef CONFIG_BUG
extern void handle_BUG(struct pt_regs *);
diff --git a/arch/sh/kernel/Makefile b/arch/sh/kernel/Makefile
index f37cf02ad9b..a2d0a40f384 100644
--- a/arch/sh/kernel/Makefile
+++ b/arch/sh/kernel/Makefile
@@ -10,9 +10,10 @@ CFLAGS_REMOVE_ftrace.o = -pg
endif
obj-y := debugtraps.o dumpstack.o idle.o io.o io_generic.o irq.o \
- machvec.o process_$(BITS).o ptrace_$(BITS).o setup.o \
- signal_$(BITS).o sys_sh.o sys_sh$(BITS).o syscalls_$(BITS).o \
- time.o topology.o traps.o traps_$(BITS).o unwinder.o
+ machvec.o nmi_debug.o process_$(BITS).o ptrace_$(BITS).o \
+ setup.o signal_$(BITS).o sys_sh.o sys_sh$(BITS).o \
+ syscalls_$(BITS).o time.o topology.o traps.o \
+ traps_$(BITS).o unwinder.o
obj-y += cpu/
obj-$(CONFIG_VSYSCALL) += vsyscall/
diff --git a/arch/sh/kernel/cpu/sh3/entry.S b/arch/sh/kernel/cpu/sh3/entry.S
index aebd33d18ff..d1142d36592 100644
--- a/arch/sh/kernel/cpu/sh3/entry.S
+++ b/arch/sh/kernel/cpu/sh3/entry.S
@@ -532,7 +532,33 @@ ENTRY(handle_interrupt)
mov.l 2f, r4
mov.l 3f, r9
mov.l @r4, r4 ! pass INTEVT vector as arg0
+
+ shlr2 r4
+ shlr r4
+ mov r4, r0 ! save vector->jmp table offset for later
+
+ shlr2 r4 ! vector to IRQ# conversion
+ add #-0x10, r4
+
+ cmp/pz r4 ! is it a valid IRQ?
+ bt 10f
+
+ /*
+ * We got here as a result of taking the INTEVT path for something
+ * that isn't a valid hard IRQ, therefore we bypass the do_IRQ()
+ * path and special case the event dispatch instead. This is the
+ * expected path for the NMI (and any other brilliantly implemented
+ * exception), which effectively wants regular exception dispatch
+ * but is unfortunately reported through INTEVT rather than
+ * EXPEVT. Grr.
+ */
+ mov.l 6f, r9
+ mov.l @(r0, r9), r9
jmp @r9
+ mov r15, r8 ! trap handlers take saved regs in r8
+
+10:
+ jmp @r9 ! Off to do_IRQ() we go.
mov r15, r5 ! pass saved registers as arg1
ENTRY(exception_none)
diff --git a/arch/sh/kernel/cpu/sh3/ex.S b/arch/sh/kernel/cpu/sh3/ex.S
index e5a0de39a2d..46610c35c23 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)
.long system_call ! Unconditional Trap /* 160 */
.long exception_error ! reserved_instruction (filled by trap_init) /* 180 */
.long exception_error ! illegal_slot_instruction (filled by trap_init) /*1A0*/
-ENTRY(nmi_slot)
- .long kgdb_handle_exception /* 1C0 */ ! Allow trap to debugger
-ENTRY(user_break_point_trap)
+ .long nmi_trap_handler /* 1C0 */ ! Allow trap to debugger
.long break_point_trap /* 1E0 */
/*
diff --git a/arch/sh/kernel/irq.c b/arch/sh/kernel/irq.c
index d1053392e28..60f8af4497c 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)
#endif
irq_enter();
- irq = irq_demux(evt2irq(irq));
+ irq = irq_demux(irq);
#ifdef CONFIG_IRQSTACKS
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 00000000000..ff0abbd1e65
--- /dev/null
+++ b/arch/sh/kernel/nmi_debug.c
@@ -0,0 +1,77 @@
+/*
+ * 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 <linux/hardirq.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 (*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/sh/kernel/traps.c b/arch/sh/kernel/traps.c
index f69bd968fcc..a8396f36bd1 100644
--- a/arch/sh/kernel/traps.c
+++ b/arch/sh/kernel/traps.c
@@ -5,6 +5,7 @@
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/uaccess.h>
+#include <linux/hardirq.h>
#include <asm/unwinder.h>
#include <asm/system.h>
@@ -91,3 +92,23 @@ BUILD_TRAP_HANDLER(bug)
force_sig(SIGTRAP, current);
}
+
+BUILD_TRAP_HANDLER(nmi)
+{
+ TRAP_HANDLER_DECL;
+
+ nmi_enter();
+
+ switch (notify_die(DIE_NMI, "NMI", regs, 0, vec & 0xff, SIGINT)) {
+ case NOTIFY_OK:
+ case NOTIFY_STOP:
+ break;
+ case NOTIFY_BAD:
+ die("Fatal Non-Maskable Interrupt", regs, SIGINT);
+ default:
+ printk(KERN_ALERT "Got NMI, but nobody cared. Ignoring...\n");
+ break;
+ }
+
+ nmi_exit();
+}