Skip to content

Commit

Permalink
[AVR32] NMI debugging
Browse files Browse the repository at this point in the history
Change the NMI handler to use the die notifier chain to signal anyone
who cares. Add a simple "nmi debugger" which hooks into this chain and
that may dump registers, task state, etc. when it happens.

Signed-off-by: Haavard Skinnemoen <hskinnemoen@atmel.com>
  • Loading branch information
Haavard Skinnemoen committed Jan 25, 2008
1 parent f6135d1 commit e7ba176
Show file tree
Hide file tree
Showing 10 changed files with 166 additions and 10 deletions.
5 changes: 5 additions & 0 deletions Documentation/kernel-parameters.txt
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ parameter is applicable:
ALSA ALSA sound support is enabled.
APIC APIC support is enabled.
APM Advanced Power Management support is enabled.
AVR32 AVR32 architecture is enabled.
AX25 Appropriate AX.25 support is enabled.
BLACKFIN Blackfin architecture is enabled.
DRM Direct Rendering Management support is enabled.
Expand Down Expand Up @@ -1123,6 +1124,10 @@ 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
when a NMI is triggered.
Format: [state][,regs][,debounce][,die]

nmi_watchdog= [KNL,BUGS=X86-32] Debugging features for SMP kernels

no387 [BUGS=X86-32] Tells the kernel to use the 387 maths
Expand Down
10 changes: 10 additions & 0 deletions arch/avr32/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,16 @@ config OWNERSHIP_TRACE
enabling Nexus-compliant debuggers to keep track of the PID of the
currently executing task.

config NMI_DEBUGGING
bool "NMI Debugging"
default n
help
Say Y here and pass the nmi_debug command-line parameter to
the kernel to turn on NMI debugging. Depending on the value
of the nmi_debug option, various pieces of information will
be dumped to the console when a Non-Maskable Interrupt
happens.

# FPU emulation goes here

source "kernel/Kconfig.hz"
Expand Down
1 change: 1 addition & 0 deletions arch/avr32/kernel/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ obj-y += init_task.o switch_to.o cpu.o
obj-$(CONFIG_MODULES) += module.o avr32_ksyms.o
obj-$(CONFIG_KPROBES) += kprobes.o
obj-$(CONFIG_STACKTRACE) += stacktrace.o
obj-$(CONFIG_NMI_DEBUGGING) += nmi_debug.o
11 changes: 11 additions & 0 deletions arch/avr32/kernel/irq.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,17 @@ void ack_bad_irq(unsigned int irq)
printk("unexpected IRQ %u\n", irq);
}

/* May be overridden by platform code */
int __weak nmi_enable(void)
{
return -ENOSYS;
}

void __weak nmi_disable(void)
{

}

#ifdef CONFIG_PROC_FS
int show_interrupts(struct seq_file *p, void *v)
{
Expand Down
82 changes: 82 additions & 0 deletions arch/avr32/kernel/nmi_debug.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/*
* 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 <asm/irq.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 (nmi_enable()) {
printk(KERN_WARNING "Unable to enable NMI.\n");
return 0;
}

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);
21 changes: 18 additions & 3 deletions arch/avr32/kernel/traps.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include <linux/bug.h>
#include <linux/init.h>
#include <linux/kallsyms.h>
#include <linux/kdebug.h>
#include <linux/module.h>
#include <linux/notifier.h>
#include <linux/sched.h>
Expand Down Expand Up @@ -107,9 +108,23 @@ void _exception(long signr, struct pt_regs *regs, int code,

asmlinkage void do_nmi(unsigned long ecr, struct pt_regs *regs)
{
printk(KERN_ALERT "Got Non-Maskable Interrupt, dumping regs\n");
show_regs_log_lvl(regs, KERN_ALERT);
show_stack_log_lvl(current, regs->sp, regs, KERN_ALERT);
int ret;

nmi_enter();

ret = notify_die(DIE_NMI, "NMI", regs, 0, ecr, SIGINT);
switch (ret) {
case NOTIFY_OK:
case NOTIFY_STOP:
return;
case NOTIFY_BAD:
die("Fatal Non-Maskable Interrupt", regs, SIGINT);
default:
break;
}

printk(KERN_ALERT "Got NMI, but nobody cared. Disabling...\n");
nmi_disable();
}

asmlinkage void do_critical_exception(unsigned long ecr, struct pt_regs *regs)
Expand Down
1 change: 1 addition & 0 deletions arch/avr32/mach-at32ap/at32ap700x.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include <linux/spi/spi.h>

#include <asm/io.h>
#include <asm/irq.h>

#include <asm/arch/at32ap700x.h>
#include <asm/arch/board.h>
Expand Down
39 changes: 32 additions & 7 deletions arch/avr32/mach-at32ap/extint.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,10 @@
#define EIC_MODE 0x0014
#define EIC_EDGE 0x0018
#define EIC_LEVEL 0x001c
#define EIC_TEST 0x0020
#define EIC_NMIC 0x0024

/* Bitfields in TEST */
#define EIC_TESTEN_OFFSET 31
#define EIC_TESTEN_SIZE 1

/* Bitfields in NMIC */
#define EIC_EN_OFFSET 0
#define EIC_EN_SIZE 1
#define EIC_NMIC_ENABLE (1 << 0)

/* Bit manipulation macros */
#define EIC_BIT(name) \
Expand Down Expand Up @@ -63,6 +57,9 @@ struct eic {
unsigned int first_irq;
};

static struct eic *nmi_eic;
static bool nmi_enabled;

static void eic_ack_irq(unsigned int irq)
{
struct eic *eic = get_irq_chip_data(irq);
Expand Down Expand Up @@ -174,6 +171,24 @@ static void demux_eic_irq(unsigned int irq, struct irq_desc *desc)
}
}

int nmi_enable(void)
{
nmi_enabled = true;

if (nmi_eic)
eic_writel(nmi_eic, NMIC, EIC_NMIC_ENABLE);

return 0;
}

void nmi_disable(void)
{
if (nmi_eic)
eic_writel(nmi_eic, NMIC, 0);

nmi_enabled = false;
}

static int __init eic_probe(struct platform_device *pdev)
{
struct eic *eic;
Expand Down Expand Up @@ -230,6 +245,16 @@ static int __init eic_probe(struct platform_device *pdev)
set_irq_chained_handler(int_irq, demux_eic_irq);
set_irq_data(int_irq, eic);

if (pdev->id == 0) {
nmi_eic = eic;
if (nmi_enabled)
/*
* Someone tried to enable NMI before we were
* ready. Do it now.
*/
nmi_enable();
}

dev_info(&pdev->dev,
"External Interrupt Controller at 0x%p, IRQ %u\n",
eic->regs, int_irq);
Expand Down
5 changes: 5 additions & 0 deletions include/asm-avr32/irq.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,9 @@

#define irq_canonicalize(i) (i)

#ifndef __ASSEMBLER__
int nmi_enable(void);
void nmi_disable(void);
#endif

#endif /* __ASM_AVR32_IOCTLS_H */
1 change: 1 addition & 0 deletions include/asm-avr32/kdebug.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
enum die_val {
DIE_BREAKPOINT,
DIE_SSTEP,
DIE_NMI,
};

#endif /* __ASM_AVR32_KDEBUG_H */

0 comments on commit e7ba176

Please sign in to comment.