mirror of
https://github.com/adulau/aha.git
synced 2024-12-28 03:36:19 +00:00
[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: Haavard Skinnemoen <hskinnemoen@atmel.com>
This commit is contained in:
parent
f6135d12db
commit
e7ba176b47
10 changed files with 166 additions and 10 deletions
|
@ -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.
|
||||
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
82
arch/avr32/kernel/nmi_debug.c
Normal file
82
arch/avr32/kernel/nmi_debug.c
Normal file
|
@ -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);
|
|
@ -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>
|
||||
|
@ -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)
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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) \
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
enum die_val {
|
||||
DIE_BREAKPOINT,
|
||||
DIE_SSTEP,
|
||||
DIE_NMI,
|
||||
};
|
||||
|
||||
#endif /* __ASM_AVR32_KDEBUG_H */
|
||||
|
|
Loading…
Reference in a new issue