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