sh: nmi_debug support.
authorPaul Mundt <lethal@linux-sh.org>
Tue, 1 Sep 2009 08:38:32 +0000 (17:38 +0900)
committerPaul Mundt <lethal@linux-sh.org>
Tue, 1 Sep 2009 08:38:32 +0000 (17:38 +0900)
This implements support for NMI debugging that was shamelessly copied
from the avr32 port. A bit of special magic is needed in the interrupt
exception path given that the NMI exception handler is stubbed in to the
regular exception handling table despite being reported in INTEVT. So we
mangle the lookup and kick off an EXPEVT-style exception dispatch from
the INTEVT path for exceptions that do_IRQ() has no chance of handling.
As a result, we also drop the evt2irq() conversion from the do_IRQ() path
and just do it in assembly.

Signed-off-by: Paul Mundt <lethal@linux-sh.org>
Documentation/kernel-parameters.txt
arch/sh/include/asm/kdebug.h
arch/sh/include/asm/system.h
arch/sh/kernel/Makefile
arch/sh/kernel/cpu/sh3/entry.S
arch/sh/kernel/cpu/sh3/ex.S
arch/sh/kernel/irq.c
arch/sh/kernel/nmi_debug.c [new file with mode: 0644]
arch/sh/kernel/traps.c

index 7936b801fe6acc8e4dfe0f6f288a2e8ce9e81e08..76c355214dc356a02c5a24f637a137b510858898 100644 (file)
@@ -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]
 
index 0b9f896f203c20ff35972de69c20dd9d7463c928..985219f9759ef0aaa908e7308930627730498d6c 100644 (file)
@@ -4,6 +4,7 @@
 /* Grossly misnamed. */
 enum die_val {
        DIE_TRAP,
+       DIE_NMI,
        DIE_OOPS,
 };
 
index 6b272238a46e717ac4e9329db5c31893fa354509..b5c5acdc8c0e546c285d236b651f9207e8fc3c31 100644 (file)
@@ -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 *);
index f37cf02ad9be2ff09c9b4312d792e61f322f74fb..a2d0a40f3848270a41bb7bce46d9b30dccc65cf1 100644 (file)
@@ -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/
index aebd33d18ff7111539ab3cc1225ae6dea124b10f..d1142d36592529ba0e9088fc1c5b5979721b7eea 100644 (file)
@@ -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)
index e5a0de39a2dbab62054d9714b4fd420e7efb29a2..46610c35c232e8f5d051a485dfff69ff214bb1bc 100644 (file)
@@ -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 */
 
        /*
index d1053392e287b8792b04a50c095bfa29bcbd977e..60f8af4497c78fc5fc051947f766966b1a19f3e2 100644 (file)
@@ -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 (file)
index 0000000..ff0abbd
--- /dev/null
@@ -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);
index f69bd968fcca389e27d86f556c88e1d45e0ebe82..a8396f36bd1485f0653ccdc31abdc98d323df01e 100644 (file)
@@ -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();
+}