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>
Esse commit está contido em:
Paul Mundt
2009-09-01 17:38:32 +09:00
commit 1e1030dccb
9 arquivos alterados com 133 adições e 9 exclusões

Ver arquivo

@@ -4,6 +4,7 @@
/* Grossly misnamed. */
enum die_val {
DIE_TRAP,
DIE_NMI,
DIE_OOPS,
};

Ver arquivo

@@ -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 *);

Ver arquivo

@@ -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/

Ver arquivo

@@ -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)

Ver arquivo

@@ -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 */
/*

Ver arquivo

@@ -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();

77
arch/sh/kernel/nmi_debug.c Arquivo normal
Ver arquivo

@@ -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);

Ver arquivo

@@ -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();
}