mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	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>
This commit is contained in:
		
							parent
							
								
									ac6a0cf671
								
							
						
					
					
						commit
						1e1030dccb
					
				
					 9 changed files with 133 additions and 9 deletions
				
			
		| 
						 | 
				
			
			@ -1514,7 +1514,7 @@ 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
 | 
			
		||||
	nmi_debug=	[KNL,AVR32,SH] Specify one or more actions to take
 | 
			
		||||
			when a NMI is triggered.
 | 
			
		||||
			Format: [state][,regs][,debounce][,die]
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,6 +4,7 @@
 | 
			
		|||
/* Grossly misnamed. */
 | 
			
		||||
enum die_val {
 | 
			
		||||
	DIE_TRAP,
 | 
			
		||||
	DIE_NMI,
 | 
			
		||||
	DIE_OOPS,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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 *);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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/
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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 */
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								arch/sh/kernel/nmi_debug.c
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -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);
 | 
			
		||||
| 
						 | 
				
			
			@ -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();
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue