mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-12-18 13:49:36 +07: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
@ -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
|
|||||||
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
|
|||||||
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
|
|||||||
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)
|
|||||||
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)
|
||||||
{
|
{
|
||||||
|
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/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,
|
|||||||
|
|
||||||
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 @@
|
|||||||
#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 @@
|
|||||||
#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 {
|
|||||||
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)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
|||||||
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 @@
|
|||||||
|
|
||||||
#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 @@
|
|||||||
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 */
|
||||||
|
Loading…
Reference in New Issue
Block a user