Commit e7ba176b authored by Haavard Skinnemoen's avatar Haavard Skinnemoen

[AVR32] NMI debugging

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: default avatarHaavard Skinnemoen <hskinnemoen@atmel.com>
parent f6135d12
...@@ -34,6 +34,7 @@ parameter is applicable: ...@@ -34,6 +34,7 @@ parameter is applicable:
ALSA ALSA sound support is enabled. ALSA ALSA sound support is enabled.
APIC APIC support is enabled. APIC APIC support is enabled.
APM Advanced Power Management support is enabled. APM Advanced Power Management support is enabled.
AVR32 AVR32 architecture is enabled.
AX25 Appropriate AX.25 support is enabled. AX25 Appropriate AX.25 support is enabled.
BLACKFIN Blackfin architecture is enabled. BLACKFIN Blackfin architecture is enabled.
DRM Direct Rendering Management support is enabled. DRM Direct Rendering Management support is enabled.
...@@ -1123,6 +1124,10 @@ and is between 256 and 4096 characters. It is defined in the file ...@@ -1123,6 +1124,10 @@ and is between 256 and 4096 characters. It is defined in the file
of returning the full 64-bit number. of returning the full 64-bit number.
The default is to return 64-bit inode numbers. 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 nmi_watchdog= [KNL,BUGS=X86-32] Debugging features for SMP kernels
no387 [BUGS=X86-32] Tells the kernel to use the 387 maths no387 [BUGS=X86-32] Tells the kernel to use the 387 maths
......
...@@ -170,6 +170,16 @@ config OWNERSHIP_TRACE ...@@ -170,6 +170,16 @@ config OWNERSHIP_TRACE
enabling Nexus-compliant debuggers to keep track of the PID of the enabling Nexus-compliant debuggers to keep track of the PID of the
currently executing task. 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 # FPU emulation goes here
source "kernel/Kconfig.hz" source "kernel/Kconfig.hz"
......
...@@ -12,3 +12,4 @@ obj-y += init_task.o switch_to.o cpu.o ...@@ -12,3 +12,4 @@ obj-y += init_task.o switch_to.o cpu.o
obj-$(CONFIG_MODULES) += module.o avr32_ksyms.o obj-$(CONFIG_MODULES) += module.o avr32_ksyms.o
obj-$(CONFIG_KPROBES) += kprobes.o obj-$(CONFIG_KPROBES) += kprobes.o
obj-$(CONFIG_STACKTRACE) += stacktrace.o obj-$(CONFIG_STACKTRACE) += stacktrace.o
obj-$(CONFIG_NMI_DEBUGGING) += nmi_debug.o
...@@ -25,6 +25,17 @@ void ack_bad_irq(unsigned int irq) ...@@ -25,6 +25,17 @@ void ack_bad_irq(unsigned int irq)
printk("unexpected IRQ %u\n", 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 #ifdef CONFIG_PROC_FS
int show_interrupts(struct seq_file *p, void *v) int show_interrupts(struct seq_file *p, void *v)
{ {
......
/*
* 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);
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include <linux/bug.h> #include <linux/bug.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/kallsyms.h> #include <linux/kallsyms.h>
#include <linux/kdebug.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/notifier.h> #include <linux/notifier.h>
#include <linux/sched.h> #include <linux/sched.h>
...@@ -107,9 +108,23 @@ void _exception(long signr, struct pt_regs *regs, int code, ...@@ -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) asmlinkage void do_nmi(unsigned long ecr, struct pt_regs *regs)
{ {
printk(KERN_ALERT "Got Non-Maskable Interrupt, dumping regs\n"); int ret;
show_regs_log_lvl(regs, KERN_ALERT);
show_stack_log_lvl(current, regs->sp, regs, KERN_ALERT); 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) asmlinkage void do_critical_exception(unsigned long ecr, struct pt_regs *regs)
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include <linux/spi/spi.h> #include <linux/spi/spi.h>
#include <asm/io.h> #include <asm/io.h>
#include <asm/irq.h>
#include <asm/arch/at32ap700x.h> #include <asm/arch/at32ap700x.h>
#include <asm/arch/board.h> #include <asm/arch/board.h>
......
...@@ -26,16 +26,10 @@ ...@@ -26,16 +26,10 @@
#define EIC_MODE 0x0014 #define EIC_MODE 0x0014
#define EIC_EDGE 0x0018 #define EIC_EDGE 0x0018
#define EIC_LEVEL 0x001c #define EIC_LEVEL 0x001c
#define EIC_TEST 0x0020
#define EIC_NMIC 0x0024 #define EIC_NMIC 0x0024
/* Bitfields in TEST */
#define EIC_TESTEN_OFFSET 31
#define EIC_TESTEN_SIZE 1
/* Bitfields in NMIC */ /* Bitfields in NMIC */
#define EIC_EN_OFFSET 0 #define EIC_NMIC_ENABLE (1 << 0)
#define EIC_EN_SIZE 1
/* Bit manipulation macros */ /* Bit manipulation macros */
#define EIC_BIT(name) \ #define EIC_BIT(name) \
...@@ -63,6 +57,9 @@ struct eic { ...@@ -63,6 +57,9 @@ struct eic {
unsigned int first_irq; unsigned int first_irq;
}; };
static struct eic *nmi_eic;
static bool nmi_enabled;
static void eic_ack_irq(unsigned int irq) static void eic_ack_irq(unsigned int irq)
{ {
struct eic *eic = get_irq_chip_data(irq); struct eic *eic = get_irq_chip_data(irq);
...@@ -174,6 +171,24 @@ static void demux_eic_irq(unsigned int irq, struct irq_desc *desc) ...@@ -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) static int __init eic_probe(struct platform_device *pdev)
{ {
struct eic *eic; struct eic *eic;
...@@ -230,6 +245,16 @@ static int __init eic_probe(struct platform_device *pdev) ...@@ -230,6 +245,16 @@ static int __init eic_probe(struct platform_device *pdev)
set_irq_chained_handler(int_irq, demux_eic_irq); set_irq_chained_handler(int_irq, demux_eic_irq);
set_irq_data(int_irq, eic); 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, dev_info(&pdev->dev,
"External Interrupt Controller at 0x%p, IRQ %u\n", "External Interrupt Controller at 0x%p, IRQ %u\n",
eic->regs, int_irq); eic->regs, int_irq);
......
...@@ -11,4 +11,9 @@ ...@@ -11,4 +11,9 @@
#define irq_canonicalize(i) (i) #define irq_canonicalize(i) (i)
#ifndef __ASSEMBLER__
int nmi_enable(void);
void nmi_disable(void);
#endif
#endif /* __ASM_AVR32_IOCTLS_H */ #endif /* __ASM_AVR32_IOCTLS_H */
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
enum die_val { enum die_val {
DIE_BREAKPOINT, DIE_BREAKPOINT,
DIE_SSTEP, DIE_SSTEP,
DIE_NMI,
}; };
#endif /* __ASM_AVR32_KDEBUG_H */ #endif /* __ASM_AVR32_KDEBUG_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment