xref: /OK3568_Linux_fs/kernel/arch/nios2/kernel/traps.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun  * Hardware exception handling
3*4882a593Smuzhiyun  *
4*4882a593Smuzhiyun  * Copyright (C) 2010 Tobias Klauser <tklauser@distanz.ch>
5*4882a593Smuzhiyun  * Copyright (C) 2004 Microtronix Datacom Ltd.
6*4882a593Smuzhiyun  * Copyright (C) 2001 Vic Phillips
7*4882a593Smuzhiyun  *
8*4882a593Smuzhiyun  * This file is subject to the terms and conditions of the GNU General
9*4882a593Smuzhiyun  * Public License.  See the file COPYING in the main directory of this
10*4882a593Smuzhiyun  * archive for more details.
11*4882a593Smuzhiyun  */
12*4882a593Smuzhiyun 
13*4882a593Smuzhiyun #include <linux/sched.h>
14*4882a593Smuzhiyun #include <linux/sched/debug.h>
15*4882a593Smuzhiyun #include <linux/kernel.h>
16*4882a593Smuzhiyun #include <linux/signal.h>
17*4882a593Smuzhiyun #include <linux/export.h>
18*4882a593Smuzhiyun #include <linux/mm.h>
19*4882a593Smuzhiyun #include <linux/ptrace.h>
20*4882a593Smuzhiyun 
21*4882a593Smuzhiyun #include <asm/traps.h>
22*4882a593Smuzhiyun #include <asm/sections.h>
23*4882a593Smuzhiyun #include <linux/uaccess.h>
24*4882a593Smuzhiyun 
25*4882a593Smuzhiyun static DEFINE_SPINLOCK(die_lock);
26*4882a593Smuzhiyun 
_send_sig(int signo,int code,unsigned long addr)27*4882a593Smuzhiyun static void _send_sig(int signo, int code, unsigned long addr)
28*4882a593Smuzhiyun {
29*4882a593Smuzhiyun 	force_sig_fault(signo, code, (void __user *) addr);
30*4882a593Smuzhiyun }
31*4882a593Smuzhiyun 
die(const char * str,struct pt_regs * regs,long err)32*4882a593Smuzhiyun void die(const char *str, struct pt_regs *regs, long err)
33*4882a593Smuzhiyun {
34*4882a593Smuzhiyun 	console_verbose();
35*4882a593Smuzhiyun 	spin_lock_irq(&die_lock);
36*4882a593Smuzhiyun 	pr_warn("Oops: %s, sig: %ld\n", str, err);
37*4882a593Smuzhiyun 	show_regs(regs);
38*4882a593Smuzhiyun 	spin_unlock_irq(&die_lock);
39*4882a593Smuzhiyun 	/*
40*4882a593Smuzhiyun 	 * do_exit() should take care of panic'ing from an interrupt
41*4882a593Smuzhiyun 	 * context so we don't handle it here
42*4882a593Smuzhiyun 	 */
43*4882a593Smuzhiyun 	do_exit(err);
44*4882a593Smuzhiyun }
45*4882a593Smuzhiyun 
_exception(int signo,struct pt_regs * regs,int code,unsigned long addr)46*4882a593Smuzhiyun void _exception(int signo, struct pt_regs *regs, int code, unsigned long addr)
47*4882a593Smuzhiyun {
48*4882a593Smuzhiyun 	if (!user_mode(regs))
49*4882a593Smuzhiyun 		die("Exception in kernel mode", regs, signo);
50*4882a593Smuzhiyun 
51*4882a593Smuzhiyun 	_send_sig(signo, code, addr);
52*4882a593Smuzhiyun }
53*4882a593Smuzhiyun 
54*4882a593Smuzhiyun /*
55*4882a593Smuzhiyun  * The show_stack() is external API which we do not use ourselves.
56*4882a593Smuzhiyun  */
57*4882a593Smuzhiyun 
58*4882a593Smuzhiyun int kstack_depth_to_print = 48;
59*4882a593Smuzhiyun 
show_stack(struct task_struct * task,unsigned long * stack,const char * loglvl)60*4882a593Smuzhiyun void show_stack(struct task_struct *task, unsigned long *stack,
61*4882a593Smuzhiyun 		const char *loglvl)
62*4882a593Smuzhiyun {
63*4882a593Smuzhiyun 	unsigned long *endstack, addr;
64*4882a593Smuzhiyun 	int i;
65*4882a593Smuzhiyun 
66*4882a593Smuzhiyun 	if (!stack) {
67*4882a593Smuzhiyun 		if (task)
68*4882a593Smuzhiyun 			stack = (unsigned long *)task->thread.ksp;
69*4882a593Smuzhiyun 		else
70*4882a593Smuzhiyun 			stack = (unsigned long *)&stack;
71*4882a593Smuzhiyun 	}
72*4882a593Smuzhiyun 
73*4882a593Smuzhiyun 	addr = (unsigned long) stack;
74*4882a593Smuzhiyun 	endstack = (unsigned long *) PAGE_ALIGN(addr);
75*4882a593Smuzhiyun 
76*4882a593Smuzhiyun 	printk("%sStack from %08lx:", loglvl, (unsigned long)stack);
77*4882a593Smuzhiyun 	for (i = 0; i < kstack_depth_to_print; i++) {
78*4882a593Smuzhiyun 		if (stack + 1 > endstack)
79*4882a593Smuzhiyun 			break;
80*4882a593Smuzhiyun 		if (i % 8 == 0)
81*4882a593Smuzhiyun 			printk("%s\n       ", loglvl);
82*4882a593Smuzhiyun 		printk("%s %08lx", loglvl, *stack++);
83*4882a593Smuzhiyun 	}
84*4882a593Smuzhiyun 
85*4882a593Smuzhiyun 	printk("%s\nCall Trace:", loglvl);
86*4882a593Smuzhiyun 	i = 0;
87*4882a593Smuzhiyun 	while (stack + 1 <= endstack) {
88*4882a593Smuzhiyun 		addr = *stack++;
89*4882a593Smuzhiyun 		/*
90*4882a593Smuzhiyun 		 * If the address is either in the text segment of the
91*4882a593Smuzhiyun 		 * kernel, or in the region which contains vmalloc'ed
92*4882a593Smuzhiyun 		 * memory, it *may* be the address of a calling
93*4882a593Smuzhiyun 		 * routine; if so, print it so that someone tracing
94*4882a593Smuzhiyun 		 * down the cause of the crash will be able to figure
95*4882a593Smuzhiyun 		 * out the call path that was taken.
96*4882a593Smuzhiyun 		 */
97*4882a593Smuzhiyun 		if (((addr >= (unsigned long) _stext) &&
98*4882a593Smuzhiyun 		     (addr <= (unsigned long) _etext))) {
99*4882a593Smuzhiyun 			if (i % 4 == 0)
100*4882a593Smuzhiyun 				pr_emerg("\n       ");
101*4882a593Smuzhiyun 			printk("%s [<%08lx>]", loglvl, addr);
102*4882a593Smuzhiyun 			i++;
103*4882a593Smuzhiyun 		}
104*4882a593Smuzhiyun 	}
105*4882a593Smuzhiyun 	printk("%s\n", loglvl);
106*4882a593Smuzhiyun }
107*4882a593Smuzhiyun 
trap_init(void)108*4882a593Smuzhiyun void __init trap_init(void)
109*4882a593Smuzhiyun {
110*4882a593Smuzhiyun 	/* Nothing to do here */
111*4882a593Smuzhiyun }
112*4882a593Smuzhiyun 
113*4882a593Smuzhiyun /* Breakpoint handler */
breakpoint_c(struct pt_regs * fp)114*4882a593Smuzhiyun asmlinkage void breakpoint_c(struct pt_regs *fp)
115*4882a593Smuzhiyun {
116*4882a593Smuzhiyun 	/*
117*4882a593Smuzhiyun 	 * The breakpoint entry code has moved the PC on by 4 bytes, so we must
118*4882a593Smuzhiyun 	 * move it back. This could be done on the host but we do it here
119*4882a593Smuzhiyun 	 * because monitor.S of JTAG gdbserver does it too.
120*4882a593Smuzhiyun 	 */
121*4882a593Smuzhiyun 	fp->ea -= 4;
122*4882a593Smuzhiyun 	_exception(SIGTRAP, fp, TRAP_BRKPT, fp->ea);
123*4882a593Smuzhiyun }
124*4882a593Smuzhiyun 
125*4882a593Smuzhiyun #ifndef CONFIG_NIOS2_ALIGNMENT_TRAP
126*4882a593Smuzhiyun /* Alignment exception handler */
handle_unaligned_c(struct pt_regs * fp,int cause)127*4882a593Smuzhiyun asmlinkage void handle_unaligned_c(struct pt_regs *fp, int cause)
128*4882a593Smuzhiyun {
129*4882a593Smuzhiyun 	unsigned long addr = RDCTL(CTL_BADADDR);
130*4882a593Smuzhiyun 
131*4882a593Smuzhiyun 	cause >>= 2;
132*4882a593Smuzhiyun 	fp->ea -= 4;
133*4882a593Smuzhiyun 
134*4882a593Smuzhiyun 	if (fixup_exception(fp))
135*4882a593Smuzhiyun 		return;
136*4882a593Smuzhiyun 
137*4882a593Smuzhiyun 	if (!user_mode(fp)) {
138*4882a593Smuzhiyun 		pr_alert("Unaligned access from kernel mode, this might be a hardware\n");
139*4882a593Smuzhiyun 		pr_alert("problem, dump registers and restart the instruction\n");
140*4882a593Smuzhiyun 		pr_alert("  BADADDR 0x%08lx\n", addr);
141*4882a593Smuzhiyun 		pr_alert("  cause   %d\n", cause);
142*4882a593Smuzhiyun 		pr_alert("  op-code 0x%08lx\n", *(unsigned long *)(fp->ea));
143*4882a593Smuzhiyun 		show_regs(fp);
144*4882a593Smuzhiyun 		return;
145*4882a593Smuzhiyun 	}
146*4882a593Smuzhiyun 
147*4882a593Smuzhiyun 	_exception(SIGBUS, fp, BUS_ADRALN, addr);
148*4882a593Smuzhiyun }
149*4882a593Smuzhiyun #endif /* CONFIG_NIOS2_ALIGNMENT_TRAP */
150*4882a593Smuzhiyun 
151*4882a593Smuzhiyun /* Illegal instruction handler */
handle_illegal_c(struct pt_regs * fp)152*4882a593Smuzhiyun asmlinkage void handle_illegal_c(struct pt_regs *fp)
153*4882a593Smuzhiyun {
154*4882a593Smuzhiyun 	fp->ea -= 4;
155*4882a593Smuzhiyun 	_exception(SIGILL, fp, ILL_ILLOPC, fp->ea);
156*4882a593Smuzhiyun }
157*4882a593Smuzhiyun 
158*4882a593Smuzhiyun /* Supervisor instruction handler */
handle_supervisor_instr(struct pt_regs * fp)159*4882a593Smuzhiyun asmlinkage void handle_supervisor_instr(struct pt_regs *fp)
160*4882a593Smuzhiyun {
161*4882a593Smuzhiyun 	fp->ea -= 4;
162*4882a593Smuzhiyun 	_exception(SIGILL, fp, ILL_PRVOPC, fp->ea);
163*4882a593Smuzhiyun }
164*4882a593Smuzhiyun 
165*4882a593Smuzhiyun /* Division error handler */
handle_diverror_c(struct pt_regs * fp)166*4882a593Smuzhiyun asmlinkage void handle_diverror_c(struct pt_regs *fp)
167*4882a593Smuzhiyun {
168*4882a593Smuzhiyun 	fp->ea -= 4;
169*4882a593Smuzhiyun 	_exception(SIGFPE, fp, FPE_INTDIV, fp->ea);
170*4882a593Smuzhiyun }
171*4882a593Smuzhiyun 
172*4882a593Smuzhiyun /* Unhandled exception handler */
unhandled_exception(struct pt_regs * regs,int cause)173*4882a593Smuzhiyun asmlinkage void unhandled_exception(struct pt_regs *regs, int cause)
174*4882a593Smuzhiyun {
175*4882a593Smuzhiyun 	unsigned long addr = RDCTL(CTL_BADADDR);
176*4882a593Smuzhiyun 
177*4882a593Smuzhiyun 	cause /= 4;
178*4882a593Smuzhiyun 
179*4882a593Smuzhiyun 	pr_emerg("Unhandled exception #%d in %s mode (badaddr=0x%08lx)\n",
180*4882a593Smuzhiyun 			cause, user_mode(regs) ? "user" : "kernel", addr);
181*4882a593Smuzhiyun 
182*4882a593Smuzhiyun 	regs->ea -= 4;
183*4882a593Smuzhiyun 	show_regs(regs);
184*4882a593Smuzhiyun 
185*4882a593Smuzhiyun 	pr_emerg("opcode: 0x%08lx\n", *(unsigned long *)(regs->ea));
186*4882a593Smuzhiyun }
187*4882a593Smuzhiyun 
handle_trap_1_c(struct pt_regs * fp)188*4882a593Smuzhiyun asmlinkage void handle_trap_1_c(struct pt_regs *fp)
189*4882a593Smuzhiyun {
190*4882a593Smuzhiyun 	_send_sig(SIGUSR1, 0, fp->ea);
191*4882a593Smuzhiyun }
192*4882a593Smuzhiyun 
handle_trap_2_c(struct pt_regs * fp)193*4882a593Smuzhiyun asmlinkage void handle_trap_2_c(struct pt_regs *fp)
194*4882a593Smuzhiyun {
195*4882a593Smuzhiyun 	_send_sig(SIGUSR2, 0, fp->ea);
196*4882a593Smuzhiyun }
197*4882a593Smuzhiyun 
handle_trap_3_c(struct pt_regs * fp)198*4882a593Smuzhiyun asmlinkage void handle_trap_3_c(struct pt_regs *fp)
199*4882a593Smuzhiyun {
200*4882a593Smuzhiyun 	_send_sig(SIGILL, ILL_ILLTRP, fp->ea);
201*4882a593Smuzhiyun }
202