1 /* traps.c: high-level exception handler for FR-V
2  *
3  * Copyright (C) 2003 Red Hat, Inc. All Rights Reserved.
4  * Written by David Howells (dhowells@redhat.com)
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version
9  * 2 of the License, or (at your option) any later version.
10  */
11 
12 #include <linux/sched.h>
13 #include <linux/signal.h>
14 #include <linux/kernel.h>
15 #include <linux/mm.h>
16 #include <linux/types.h>
17 #include <linux/user.h>
18 #include <linux/string.h>
19 #include <linux/linkage.h>
20 #include <linux/init.h>
21 #include <linux/module.h>
22 
23 #include <asm/asm-offsets.h>
24 #include <asm/setup.h>
25 #include <asm/fpu.h>
26 #include <asm/uaccess.h>
27 #include <asm/pgtable.h>
28 #include <asm/siginfo.h>
29 #include <asm/unaligned.h>
30 
31 void show_backtrace(struct pt_regs *, unsigned long);
32 
33 extern asmlinkage void __break_hijack_kernel_event(void);
34 
35 /*****************************************************************************/
36 /*
37  * instruction access error
38  */
insn_access_error(unsigned long esfr1,unsigned long epcr0,unsigned long esr0)39 asmlinkage void insn_access_error(unsigned long esfr1, unsigned long epcr0, unsigned long esr0)
40 {
41 	siginfo_t info;
42 
43 	die_if_kernel("-- Insn Access Error --\n"
44 		      "EPCR0 : %08lx\n"
45 		      "ESR0  : %08lx\n",
46 		      epcr0, esr0);
47 
48 	info.si_signo	= SIGSEGV;
49 	info.si_code	= SEGV_ACCERR;
50 	info.si_errno	= 0;
51 	info.si_addr	= (void __user *) ((epcr0 & EPCR0_V) ? (epcr0 & EPCR0_PC) : __frame->pc);
52 
53 	force_sig_info(info.si_signo, &info, current);
54 } /* end insn_access_error() */
55 
56 /*****************************************************************************/
57 /*
58  * handler for:
59  * - illegal instruction
60  * - privileged instruction
61  * - unsupported trap
62  * - debug exceptions
63  */
illegal_instruction(unsigned long esfr1,unsigned long epcr0,unsigned long esr0)64 asmlinkage void illegal_instruction(unsigned long esfr1, unsigned long epcr0, unsigned long esr0)
65 {
66 	siginfo_t info;
67 
68 	die_if_kernel("-- Illegal Instruction --\n"
69 		      "EPCR0 : %08lx\n"
70 		      "ESR0  : %08lx\n"
71 		      "ESFR1 : %08lx\n",
72 		      epcr0, esr0, esfr1);
73 
74 	info.si_errno	= 0;
75 	info.si_addr	= (void __user *) ((epcr0 & EPCR0_V) ? (epcr0 & EPCR0_PC) : __frame->pc);
76 
77 	switch (__frame->tbr & TBR_TT) {
78 	case TBR_TT_ILLEGAL_INSTR:
79 		info.si_signo	= SIGILL;
80 		info.si_code	= ILL_ILLOPC;
81 		break;
82 	case TBR_TT_PRIV_INSTR:
83 		info.si_signo	= SIGILL;
84 		info.si_code	= ILL_PRVOPC;
85 		break;
86 	case TBR_TT_TRAP2 ... TBR_TT_TRAP126:
87 		info.si_signo	= SIGILL;
88 		info.si_code	= ILL_ILLTRP;
89 		break;
90 	/* GDB uses "tira gr0, #1" as a breakpoint instruction.  */
91 	case TBR_TT_TRAP1:
92 	case TBR_TT_BREAK:
93 		info.si_signo	= SIGTRAP;
94 		info.si_code	=
95 			(__frame->__status & REG__STATUS_STEPPED) ? TRAP_TRACE : TRAP_BRKPT;
96 		break;
97 	}
98 
99 	force_sig_info(info.si_signo, &info, current);
100 } /* end illegal_instruction() */
101 
102 /*****************************************************************************/
103 /*
104  * handle atomic operations with errors
105  * - arguments in gr8, gr9, gr10
106  * - original memory value placed in gr5
107  * - replacement memory value placed in gr9
108  */
atomic_operation(unsigned long esfr1,unsigned long epcr0,unsigned long esr0)109 asmlinkage void atomic_operation(unsigned long esfr1, unsigned long epcr0,
110 				 unsigned long esr0)
111 {
112 	static DEFINE_SPINLOCK(atomic_op_lock);
113 	unsigned long x, y, z;
114 	unsigned long __user *p;
115 	mm_segment_t oldfs;
116 	siginfo_t info;
117 	int ret;
118 
119 	y = 0;
120 	z = 0;
121 
122 	oldfs = get_fs();
123 	if (!user_mode(__frame))
124 		set_fs(KERNEL_DS);
125 
126 	switch (__frame->tbr & TBR_TT) {
127 		/* TIRA gr0,#120
128 		 * u32 __atomic_user_cmpxchg32(u32 *ptr, u32 test, u32 new)
129 		 */
130 	case TBR_TT_ATOMIC_CMPXCHG32:
131 		p = (unsigned long __user *) __frame->gr8;
132 		x = __frame->gr9;
133 		y = __frame->gr10;
134 
135 		for (;;) {
136 			ret = get_user(z, p);
137 			if (ret < 0)
138 				goto error;
139 
140 			if (z != x)
141 				goto done;
142 
143 			spin_lock_irq(&atomic_op_lock);
144 
145 			if (__get_user(z, p) == 0) {
146 				if (z != x)
147 					goto done2;
148 
149 				if (__put_user(y, p) == 0)
150 					goto done2;
151 				goto error2;
152 			}
153 
154 			spin_unlock_irq(&atomic_op_lock);
155 		}
156 
157 		/* TIRA gr0,#121
158 		 * u32 __atomic_kernel_xchg32(void *v, u32 new)
159 		 */
160 	case TBR_TT_ATOMIC_XCHG32:
161 		p = (unsigned long __user *) __frame->gr8;
162 		y = __frame->gr9;
163 
164 		for (;;) {
165 			ret = get_user(z, p);
166 			if (ret < 0)
167 				goto error;
168 
169 			spin_lock_irq(&atomic_op_lock);
170 
171 			if (__get_user(z, p) == 0) {
172 				if (__put_user(y, p) == 0)
173 					goto done2;
174 				goto error2;
175 			}
176 
177 			spin_unlock_irq(&atomic_op_lock);
178 		}
179 
180 		/* TIRA gr0,#122
181 		 * ulong __atomic_kernel_XOR_return(ulong i, ulong *v)
182 		 */
183 	case TBR_TT_ATOMIC_XOR:
184 		p = (unsigned long __user *) __frame->gr8;
185 		x = __frame->gr9;
186 
187 		for (;;) {
188 			ret = get_user(z, p);
189 			if (ret < 0)
190 				goto error;
191 
192 			spin_lock_irq(&atomic_op_lock);
193 
194 			if (__get_user(z, p) == 0) {
195 				y = x ^ z;
196 				if (__put_user(y, p) == 0)
197 					goto done2;
198 				goto error2;
199 			}
200 
201 			spin_unlock_irq(&atomic_op_lock);
202 		}
203 
204 		/* TIRA gr0,#123
205 		 * ulong __atomic_kernel_OR_return(ulong i, ulong *v)
206 		 */
207 	case TBR_TT_ATOMIC_OR:
208 		p = (unsigned long __user *) __frame->gr8;
209 		x = __frame->gr9;
210 
211 		for (;;) {
212 			ret = get_user(z, p);
213 			if (ret < 0)
214 				goto error;
215 
216 			spin_lock_irq(&atomic_op_lock);
217 
218 			if (__get_user(z, p) == 0) {
219 				y = x ^ z;
220 				if (__put_user(y, p) == 0)
221 					goto done2;
222 				goto error2;
223 			}
224 
225 			spin_unlock_irq(&atomic_op_lock);
226 		}
227 
228 		/* TIRA gr0,#124
229 		 * ulong __atomic_kernel_AND_return(ulong i, ulong *v)
230 		 */
231 	case TBR_TT_ATOMIC_AND:
232 		p = (unsigned long __user *) __frame->gr8;
233 		x = __frame->gr9;
234 
235 		for (;;) {
236 			ret = get_user(z, p);
237 			if (ret < 0)
238 				goto error;
239 
240 			spin_lock_irq(&atomic_op_lock);
241 
242 			if (__get_user(z, p) == 0) {
243 				y = x & z;
244 				if (__put_user(y, p) == 0)
245 					goto done2;
246 				goto error2;
247 			}
248 
249 			spin_unlock_irq(&atomic_op_lock);
250 		}
251 
252 		/* TIRA gr0,#125
253 		 * int __atomic_user_sub_return(atomic_t *v, int i)
254 		 */
255 	case TBR_TT_ATOMIC_SUB:
256 		p = (unsigned long __user *) __frame->gr8;
257 		x = __frame->gr9;
258 
259 		for (;;) {
260 			ret = get_user(z, p);
261 			if (ret < 0)
262 				goto error;
263 
264 			spin_lock_irq(&atomic_op_lock);
265 
266 			if (__get_user(z, p) == 0) {
267 				y = z - x;
268 				if (__put_user(y, p) == 0)
269 					goto done2;
270 				goto error2;
271 			}
272 
273 			spin_unlock_irq(&atomic_op_lock);
274 		}
275 
276 		/* TIRA gr0,#126
277 		 * int __atomic_user_add_return(atomic_t *v, int i)
278 		 */
279 	case TBR_TT_ATOMIC_ADD:
280 		p = (unsigned long __user *) __frame->gr8;
281 		x = __frame->gr9;
282 
283 		for (;;) {
284 			ret = get_user(z, p);
285 			if (ret < 0)
286 				goto error;
287 
288 			spin_lock_irq(&atomic_op_lock);
289 
290 			if (__get_user(z, p) == 0) {
291 				y = z + x;
292 				if (__put_user(y, p) == 0)
293 					goto done2;
294 				goto error2;
295 			}
296 
297 			spin_unlock_irq(&atomic_op_lock);
298 		}
299 
300 	default:
301 		BUG();
302 	}
303 
304 done2:
305 	spin_unlock_irq(&atomic_op_lock);
306 done:
307 	if (!user_mode(__frame))
308 		set_fs(oldfs);
309 	__frame->gr5 = z;
310 	__frame->gr9 = y;
311 	return;
312 
313 error2:
314 	spin_unlock_irq(&atomic_op_lock);
315 error:
316 	if (!user_mode(__frame))
317 		set_fs(oldfs);
318 	__frame->pc -= 4;
319 
320 	die_if_kernel("-- Atomic Op Error --\n");
321 
322 	info.si_signo	= SIGSEGV;
323 	info.si_code	= SEGV_ACCERR;
324 	info.si_errno	= 0;
325 	info.si_addr	= (void __user *) __frame->pc;
326 
327 	force_sig_info(info.si_signo, &info, current);
328 }
329 
330 /*****************************************************************************/
331 /*
332  *
333  */
media_exception(unsigned long msr0,unsigned long msr1)334 asmlinkage void media_exception(unsigned long msr0, unsigned long msr1)
335 {
336 	siginfo_t info;
337 
338 	die_if_kernel("-- Media Exception --\n"
339 		      "MSR0 : %08lx\n"
340 		      "MSR1 : %08lx\n",
341 		      msr0, msr1);
342 
343 	info.si_signo	= SIGFPE;
344 	info.si_code	= FPE_MDAOVF;
345 	info.si_errno	= 0;
346 	info.si_addr	= (void __user *) __frame->pc;
347 
348 	force_sig_info(info.si_signo, &info, current);
349 } /* end media_exception() */
350 
351 /*****************************************************************************/
352 /*
353  * instruction or data access exception
354  */
memory_access_exception(unsigned long esr0,unsigned long ear0,unsigned long epcr0)355 asmlinkage void memory_access_exception(unsigned long esr0,
356 					unsigned long ear0,
357 					unsigned long epcr0)
358 {
359 	siginfo_t info;
360 
361 #ifdef CONFIG_MMU
362 	unsigned long fixup;
363 
364 	fixup = search_exception_table(__frame->pc);
365 	if (fixup) {
366 		__frame->pc = fixup;
367 		return;
368 	}
369 #endif
370 
371 	die_if_kernel("-- Memory Access Exception --\n"
372 		      "ESR0  : %08lx\n"
373 		      "EAR0  : %08lx\n"
374 		      "EPCR0 : %08lx\n",
375 		      esr0, ear0, epcr0);
376 
377 	info.si_signo	= SIGSEGV;
378 	info.si_code	= SEGV_ACCERR;
379 	info.si_errno	= 0;
380 	info.si_addr	= NULL;
381 
382 	if ((esr0 & (ESRx_VALID | ESR0_EAV)) == (ESRx_VALID | ESR0_EAV))
383 		info.si_addr = (void __user *) ear0;
384 
385 	force_sig_info(info.si_signo, &info, current);
386 
387 } /* end memory_access_exception() */
388 
389 /*****************************************************************************/
390 /*
391  * data access error
392  * - double-word data load from CPU control area (0xFExxxxxx)
393  * - read performed on inactive or self-refreshing SDRAM
394  * - error notification from slave device
395  * - misaligned address
396  * - access to out of bounds memory region
397  * - user mode accessing privileged memory region
398  * - write to R/O memory region
399  */
data_access_error(unsigned long esfr1,unsigned long esr15,unsigned long ear15)400 asmlinkage void data_access_error(unsigned long esfr1, unsigned long esr15, unsigned long ear15)
401 {
402 	siginfo_t info;
403 
404 	die_if_kernel("-- Data Access Error --\n"
405 		      "ESR15 : %08lx\n"
406 		      "EAR15 : %08lx\n",
407 		      esr15, ear15);
408 
409 	info.si_signo	= SIGSEGV;
410 	info.si_code	= SEGV_ACCERR;
411 	info.si_errno	= 0;
412 	info.si_addr	= (void __user *)
413 		(((esr15 & (ESRx_VALID|ESR15_EAV)) == (ESRx_VALID|ESR15_EAV)) ? ear15 : 0);
414 
415 	force_sig_info(info.si_signo, &info, current);
416 } /* end data_access_error() */
417 
418 /*****************************************************************************/
419 /*
420  * data store error - should only happen if accessing inactive or self-refreshing SDRAM
421  */
data_store_error(unsigned long esfr1,unsigned long esr15)422 asmlinkage void data_store_error(unsigned long esfr1, unsigned long esr15)
423 {
424 	die_if_kernel("-- Data Store Error --\n"
425 		      "ESR15 : %08lx\n",
426 		      esr15);
427 	BUG();
428 } /* end data_store_error() */
429 
430 /*****************************************************************************/
431 /*
432  *
433  */
division_exception(unsigned long esfr1,unsigned long esr0,unsigned long isr)434 asmlinkage void division_exception(unsigned long esfr1, unsigned long esr0, unsigned long isr)
435 {
436 	siginfo_t info;
437 
438 	die_if_kernel("-- Division Exception --\n"
439 		      "ESR0 : %08lx\n"
440 		      "ISR  : %08lx\n",
441 		      esr0, isr);
442 
443 	info.si_signo	= SIGFPE;
444 	info.si_code	= FPE_INTDIV;
445 	info.si_errno	= 0;
446 	info.si_addr	= (void __user *) __frame->pc;
447 
448 	force_sig_info(info.si_signo, &info, current);
449 } /* end division_exception() */
450 
451 /*****************************************************************************/
452 /*
453  *
454  */
compound_exception(unsigned long esfr1,unsigned long esr0,unsigned long esr14,unsigned long esr15,unsigned long msr0,unsigned long msr1)455 asmlinkage void compound_exception(unsigned long esfr1,
456 				   unsigned long esr0, unsigned long esr14, unsigned long esr15,
457 				   unsigned long msr0, unsigned long msr1)
458 {
459 	die_if_kernel("-- Compound Exception --\n"
460 		      "ESR0  : %08lx\n"
461 		      "ESR15 : %08lx\n"
462 		      "ESR15 : %08lx\n"
463 		      "MSR0  : %08lx\n"
464 		      "MSR1  : %08lx\n",
465 		      esr0, esr14, esr15, msr0, msr1);
466 	BUG();
467 } /* end compound_exception() */
468 
469 /*****************************************************************************/
470 /*
471  * The architecture-independent backtrace generator
472  */
dump_stack(void)473 void dump_stack(void)
474 {
475 	show_stack(NULL, NULL);
476 }
477 
478 EXPORT_SYMBOL(dump_stack);
479 
show_stack(struct task_struct * task,unsigned long * sp)480 void show_stack(struct task_struct *task, unsigned long *sp)
481 {
482 }
483 
show_trace_task(struct task_struct * tsk)484 void show_trace_task(struct task_struct *tsk)
485 {
486 	printk("CONTEXT: stack=0x%lx frame=0x%p LR=0x%lx RET=0x%lx\n",
487 	       tsk->thread.sp, tsk->thread.frame, tsk->thread.lr, tsk->thread.sched_lr);
488 }
489 
490 static const char *regnames[] = {
491 	"PSR ", "ISR ", "CCR ", "CCCR",
492 	"LR  ", "LCR ", "PC  ", "_stt",
493 	"sys ", "GR8*", "GNE0", "GNE1",
494 	"IACH", "IACL",
495 	"TBR ", "SP  ", "FP  ", "GR3 ",
496 	"GR4 ", "GR5 ", "GR6 ", "GR7 ",
497 	"GR8 ", "GR9 ", "GR10", "GR11",
498 	"GR12", "GR13", "GR14", "GR15",
499 	"GR16", "GR17", "GR18", "GR19",
500 	"GR20", "GR21", "GR22", "GR23",
501 	"GR24", "GR25", "GR26", "GR27",
502 	"EFRM", "CURR", "GR30", "BFRM"
503 };
504 
show_regs(struct pt_regs * regs)505 void show_regs(struct pt_regs *regs)
506 {
507 	unsigned long *reg;
508 	int loop;
509 
510 	printk("\n");
511 
512 	printk("Frame: @%08lx [%s]\n",
513 	       (unsigned long) regs,
514 	       regs->psr & PSR_S ? "kernel" : "user");
515 
516 	reg = (unsigned long *) regs;
517 	for (loop = 0; loop < NR_PT_REGS; loop++) {
518 		printk("%s %08lx", regnames[loop + 0], reg[loop + 0]);
519 
520 		if (loop == NR_PT_REGS - 1 || loop % 5 == 4)
521 			printk("\n");
522 		else
523 			printk(" | ");
524 	}
525 
526 	printk("Process %s (pid: %d)\n", current->comm, current->pid);
527 }
528 
die_if_kernel(const char * str,...)529 void die_if_kernel(const char *str, ...)
530 {
531 	char buffer[256];
532 	va_list va;
533 
534 	if (user_mode(__frame))
535 		return;
536 
537 	va_start(va, str);
538 	vsprintf(buffer, str, va);
539 	va_end(va);
540 
541 	console_verbose();
542 	printk("\n===================================\n");
543 	printk("%s\n", buffer);
544 	show_backtrace(__frame, 0);
545 
546 	__break_hijack_kernel_event();
547 	do_exit(SIGSEGV);
548 }
549 
550 /*****************************************************************************/
551 /*
552  * dump the contents of an exception frame
553  */
show_backtrace_regs(struct pt_regs * frame)554 static void show_backtrace_regs(struct pt_regs *frame)
555 {
556 	unsigned long *reg;
557 	int loop;
558 
559 	/* print the registers for this frame */
560 	printk("<-- %s Frame: @%p -->\n",
561 	       frame->psr & PSR_S ? "Kernel Mode" : "User Mode",
562 	       frame);
563 
564 	reg = (unsigned long *) frame;
565 	for (loop = 0; loop < NR_PT_REGS; loop++) {
566 		printk("%s %08lx", regnames[loop + 0], reg[loop + 0]);
567 
568 		if (loop == NR_PT_REGS - 1 || loop % 5 == 4)
569 			printk("\n");
570 		else
571 			printk(" | ");
572 	}
573 
574 	printk("--------\n");
575 } /* end show_backtrace_regs() */
576 
577 /*****************************************************************************/
578 /*
579  * generate a backtrace of the kernel stack
580  */
show_backtrace(struct pt_regs * frame,unsigned long sp)581 void show_backtrace(struct pt_regs *frame, unsigned long sp)
582 {
583 	struct pt_regs *frame0;
584 	unsigned long tos = 0, stop = 0, base;
585 	int format;
586 
587 	base = ((((unsigned long) frame) + 8191) & ~8191) - sizeof(struct user_context);
588 	frame0 = (struct pt_regs *) base;
589 
590 	if (sp) {
591 		tos = sp;
592 		stop = (unsigned long) frame;
593 	}
594 
595 	printk("\nProcess %s (pid: %d)\n\n", current->comm, current->pid);
596 
597 	for (;;) {
598 		/* dump stack segment between frames */
599 		//printk("%08lx -> %08lx\n", tos, stop);
600 		format = 0;
601 		while (tos < stop) {
602 			if (format == 0)
603 				printk(" %04lx :", tos & 0xffff);
604 
605 			printk(" %08lx", *(unsigned long *) tos);
606 
607 			tos += 4;
608 			format++;
609 			if (format == 8) {
610 				printk("\n");
611 				format = 0;
612 			}
613 		}
614 
615 		if (format > 0)
616 			printk("\n");
617 
618 		/* dump frame 0 outside of the loop */
619 		if (frame == frame0)
620 			break;
621 
622 		tos = frame->sp;
623 		if (((unsigned long) frame) + sizeof(*frame) != tos) {
624 			printk("-- TOS %08lx does not follow frame %p --\n",
625 			       tos, frame);
626 			break;
627 		}
628 
629 		show_backtrace_regs(frame);
630 
631 		/* dump the stack between this frame and the next */
632 		stop = (unsigned long) frame->next_frame;
633 		if (stop != base &&
634 		    (stop < tos ||
635 		     stop > base ||
636 		     (stop < base && stop + sizeof(*frame) > base) ||
637 		     stop & 3)) {
638 			printk("-- next_frame %08lx is invalid (range %08lx-%08lx) --\n",
639 			       stop, tos, base);
640 			break;
641 		}
642 
643 		/* move to next frame */
644 		frame = frame->next_frame;
645 	}
646 
647 	/* we can always dump frame 0, even if the rest of the stack is corrupt */
648 	show_backtrace_regs(frame0);
649 
650 } /* end show_backtrace() */
651 
652 /*****************************************************************************/
653 /*
654  * initialise traps
655  */
trap_init(void)656 void __init trap_init (void)
657 {
658 } /* end trap_init() */
659