Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

sh: nmi_debug support.

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>

+133 -9
+1 -1
Documentation/kernel-parameters.txt
··· 1514 1514 of returning the full 64-bit number. 1515 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 1518 when a NMI is triggered. 1519 1519 Format: [state][,regs][,debounce][,die] 1520 1520
+1
arch/sh/include/asm/kdebug.h
··· 4 4 /* Grossly misnamed. */ 5 5 enum die_val { 6 6 DIE_TRAP, 7 + DIE_NMI, 7 8 DIE_OOPS, 8 9 }; 9 10
+1 -1
arch/sh/include/asm/system.h
··· 169 169 BUILD_TRAP_HANDLER(singlestep); 170 170 BUILD_TRAP_HANDLER(fpu_error); 171 171 BUILD_TRAP_HANDLER(fpu_state_restore); 172 - BUILD_TRAP_HANDLER(unwinder); 172 + BUILD_TRAP_HANDLER(nmi); 173 173 174 174 #ifdef CONFIG_BUG 175 175 extern void handle_BUG(struct pt_regs *);
+4 -3
arch/sh/kernel/Makefile
··· 10 10 endif 11 11 12 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 \ 14 - signal_$(BITS).o sys_sh.o sys_sh$(BITS).o syscalls_$(BITS).o \ 15 - time.o topology.o traps.o traps_$(BITS).o unwinder.o 13 + machvec.o nmi_debug.o process_$(BITS).o ptrace_$(BITS).o \ 14 + setup.o signal_$(BITS).o sys_sh.o sys_sh$(BITS).o \ 15 + syscalls_$(BITS).o time.o topology.o traps.o \ 16 + traps_$(BITS).o unwinder.o 16 17 17 18 obj-y += cpu/ 18 19 obj-$(CONFIG_VSYSCALL) += vsyscall/
+26
arch/sh/kernel/cpu/sh3/entry.S
··· 532 532 mov.l 2f, r4 533 533 mov.l 3f, r9 534 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 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 562 mov r15, r5 ! pass saved registers as arg1 537 563 538 564 ENTRY(exception_none)
+1 -3
arch/sh/kernel/cpu/sh3/ex.S
··· 48 48 .long system_call ! Unconditional Trap /* 160 */ 49 49 .long exception_error ! reserved_instruction (filled by trap_init) /* 180 */ 50 50 .long exception_error ! illegal_slot_instruction (filled by trap_init) /*1A0*/ 51 - ENTRY(nmi_slot) 52 - .long kgdb_handle_exception /* 1C0 */ ! Allow trap to debugger 53 - ENTRY(user_break_point_trap) 51 + .long nmi_trap_handler /* 1C0 */ ! Allow trap to debugger 54 52 .long break_point_trap /* 1E0 */ 55 53 56 54 /*
+1 -1
arch/sh/kernel/irq.c
··· 114 114 #endif 115 115 116 116 irq_enter(); 117 - irq = irq_demux(evt2irq(irq)); 117 + irq = irq_demux(irq); 118 118 119 119 #ifdef CONFIG_IRQSTACKS 120 120 curctx = (union irq_ctx *)current_thread_info();
+77
arch/sh/kernel/nmi_debug.c
··· 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);
+21
arch/sh/kernel/traps.c
··· 5 5 #include <linux/signal.h> 6 6 #include <linux/sched.h> 7 7 #include <linux/uaccess.h> 8 + #include <linux/hardirq.h> 8 9 #include <asm/unwinder.h> 9 10 #include <asm/system.h> 10 11 ··· 91 90 #endif 92 91 93 92 force_sig(SIGTRAP, current); 93 + } 94 + 95 + BUILD_TRAP_HANDLER(nmi) 96 + { 97 + TRAP_HANDLER_DECL; 98 + 99 + nmi_enter(); 100 + 101 + switch (notify_die(DIE_NMI, "NMI", regs, 0, vec & 0xff, SIGINT)) { 102 + case NOTIFY_OK: 103 + case NOTIFY_STOP: 104 + break; 105 + case NOTIFY_BAD: 106 + die("Fatal Non-Maskable Interrupt", regs, SIGINT); 107 + default: 108 + printk(KERN_ALERT "Got NMI, but nobody cared. Ignoring...\n"); 109 + break; 110 + } 111 + 112 + nmi_exit(); 94 113 }