File: /usr/src/linux/arch/sh/kernel/traps.c

1     /* $Id: traps.c,v 1.14 2001/07/24 08:07:10 gniibe Exp $
2      *
3      *  linux/arch/sh/traps.c
4      *
5      *  SuperH version: Copyright (C) 1999 Niibe Yutaka
6      *                  Copyright (C) 2000 Philipp Rumpf
7      *                  Copyright (C) 2000 David Howells
8      */
9     
10     /*
11      * 'Traps.c' handles hardware traps and faults after we have saved some
12      * state in 'entry.S'.
13      */
14     #include <linux/config.h>
15     #include <linux/sched.h>
16     #include <linux/kernel.h>
17     #include <linux/string.h>
18     #include <linux/errno.h>
19     #include <linux/ptrace.h>
20     #include <linux/timer.h>
21     #include <linux/mm.h>
22     #include <linux/smp.h>
23     #include <linux/smp_lock.h>
24     #include <linux/init.h>
25     #include <linux/delay.h>
26     #include <linux/spinlock.h>
27     
28     #include <asm/system.h>
29     #include <asm/uaccess.h>
30     #include <asm/io.h>
31     #include <asm/atomic.h>
32     #include <asm/processor.h>
33     
34     #define DO_ERROR(trapnr, signr, str, name, tsk) \
35     asmlinkage void do_##name(unsigned long r4, unsigned long r5, \
36     			  unsigned long r6, unsigned long r7, \
37     			  struct pt_regs regs) \
38     { \
39     	unsigned long error_code; \
40      \
41     	asm volatile("stc	r2_bank, %0": "=r" (error_code)); \
42     	sti(); \
43     	tsk->thread.error_code = error_code; \
44     	tsk->thread.trap_no = trapnr; \
45     	force_sig(signr, tsk); \
46     	die_if_no_fixup(str,&regs,error_code); \
47     }
48     
49     /*
50      * These constants are for searching for possible module text
51      * segments.  VMALLOC_OFFSET comes from mm/vmalloc.c; MODULE_RANGE is
52      * a guess of how much space is likely to be vmalloced.
53      */
54     #define VMALLOC_OFFSET (8*1024*1024)
55     #define MODULE_RANGE (8*1024*1024)
56     
57     spinlock_t die_lock;
58     
59     void die(const char * str, struct pt_regs * regs, long err)
60     {
61     	console_verbose();
62     	spin_lock_irq(&die_lock);
63     	printk("%s: %04lx\n", str, err & 0xffff);
64     	show_regs(regs);
65     	spin_unlock_irq(&die_lock);
66     	do_exit(SIGSEGV);
67     }
68     
69     static inline void die_if_kernel(const char * str, struct pt_regs * regs, long err)
70     {
71     	if (!user_mode(regs))
72     		die(str, regs, err);
73     }
74     
75     static int handle_unaligned_notify_count = 10;
76     
77     /*
78      * try and fix up kernelspace address errors
79      * - userspace errors just cause EFAULT to be returned, resulting in SEGV
80      * - kernel/userspace interfaces cause a jump to an appropriate handler
81      * - other kernel errors are bad
82      * - return 0 if fixed-up, -EFAULT if non-fatal (to the kernel) fault
83      */
84     static int die_if_no_fixup(const char * str, struct pt_regs * regs, long err)
85     {
86     	if (!user_mode(regs))
87     	{
88     		unsigned long fixup;
89     		fixup = search_exception_table(regs->pc);
90     		if (fixup) {
91     			regs->pc = fixup;
92     			return 0;
93     		}
94     		die(str, regs, err);
95     	}
96     	return -EFAULT;
97     }
98     
99     /*
100      * handle an instruction that does an unaligned memory access by emulating the
101      * desired behaviour
102      * - note that PC _may not_ point to the faulting instruction
103      *   (if that instruction is in a branch delay slot)
104      * - return 0 if emulation okay, -EFAULT on existential error
105      */
106     static int handle_unaligned_ins(u16 instruction, struct pt_regs *regs)
107     {
108     	int ret, index, count;
109     	unsigned long *rm, *rn;
110     	unsigned char *src, *dst;
111     
112     	index = (instruction>>8)&15;	/* 0x0F00 */
113     	rn = &regs->regs[index];
114     
115     	index = (instruction>>4)&15;	/* 0x00F0 */
116     	rm = &regs->regs[index];
117     
118     	count = 1<<(instruction&3);
119     
120     	ret = -EFAULT;
121     	switch (instruction>>12) {
122     	case 0: /* mov.[bwl] to/from memory via r0+rn */
123     		if (instruction & 8) {
124     			/* from memory */
125     			src = (unsigned char*) *rm;
126     			src += regs->regs[0];
127     			dst = (unsigned char*) rn;
128     			*(unsigned long*)dst = 0;
129     
130     #ifdef __LITTLE_ENDIAN__
131     			if (copy_from_user(dst, src, count))
132     				goto fetch_fault;
133     
134     			if ((count == 2) && dst[1] & 0x80) {
135     				dst[2] = 0xff;
136     				dst[3] = 0xff;
137     			}
138     #else
139     			dst += 4-count;
140     
141     			if (__copy_user(dst, src, count))
142     				goto fetch_fault;
143     
144     			if ((count == 2) && dst[2] & 0x80) {
145     				dst[0] = 0xff;
146     				dst[1] = 0xff;
147     			}
148     #endif
149     		} else {
150     			/* to memory */
151     			src = (unsigned char*) rm;
152     #if !defined(__LITTLE_ENDIAN__)
153     			src += 4-count;
154     #endif
155     			dst = (unsigned char*) *rn;
156     			dst += regs->regs[0];
157     
158     			if (copy_to_user(dst, src, count))
159     				goto fetch_fault;
160     		}
161     		ret = 0;
162     		break;
163     
164     	case 1: /* mov.l Rm,@(disp,Rn) */
165     		src = (unsigned char*) rm;
166     		dst = (unsigned char*) *rn;
167     		dst += (instruction&0x000F)<<2;
168     
169     		if (copy_to_user(dst,src,4))
170     			goto fetch_fault;
171     		ret = 0;
172      		break;
173     
174     	case 2: /* mov.[bwl] to memory, possibly with pre-decrement */
175     		if (instruction & 4)
176     			*rn -= count;
177     		src = (unsigned char*) rm;
178     		dst = (unsigned char*) *rn;
179     #if !defined(__LITTLE_ENDIAN__)
180     		src += 4-count;
181     #endif
182     		if (copy_to_user(dst, src, count))
183     			goto fetch_fault;
184     		ret = 0;
185     		break;
186     
187     	case 5: /* mov.l @(disp,Rm),Rn */
188     		src = (unsigned char*) *rm;
189     		src += (instruction&0x000F)<<2;
190     		dst = (unsigned char*) rn;
191     		*(unsigned long*)dst = 0;
192     
193     		if (copy_from_user(dst,src,4))
194     			goto fetch_fault;
195     		ret = 0;
196      		break;
197     
198     	case 6:	/* mov.[bwl] from memory, possibly with post-increment */
199     		src = (unsigned char*) *rm;
200     		if (instruction & 4)
201     			*rm += count;
202     		dst = (unsigned char*) rn;
203     		*(unsigned long*)dst = 0;
204     		
205     #ifdef __LITTLE_ENDIAN__
206     		if (copy_from_user(dst, src, count))
207     			goto fetch_fault;
208     
209     		if ((count == 2) && dst[1] & 0x80) {
210     			dst[2] = 0xff;
211     			dst[3] = 0xff;
212     		}
213     #else
214     		dst += 4-count;
215     		
216     		if (copy_from_user(dst, src, count))
217     			goto fetch_fault;
218     
219     		if ((count == 2) && dst[2] & 0x80) {
220     			dst[0] = 0xff;
221     			dst[1] = 0xff;
222     		}
223     #endif
224     		ret = 0;
225     		break;
226     
227     	case 8:
228     		switch ((instruction&0xFF00)>>8) {
229     		case 0x81: /* mov.w R0,@(disp,Rn) */
230     			src = (unsigned char*) &regs->regs[0];
231     #if !defined(__LITTLE_ENDIAN__)
232     			src += 2;
233     #endif
234     			dst = (unsigned char*) *rm; /* called Rn in the spec */
235     			dst += (instruction&0x000F)<<1;
236     
237     			if (copy_to_user(dst, src, 2))
238     				goto fetch_fault;
239     			ret = 0;
240     			break;
241     
242     		case 0x85: /* mov.w @(disp,Rm),R0 */
243     			src = (unsigned char*) *rm;
244     			src += (instruction&0x000F)<<1;
245     			dst = (unsigned char*) &regs->regs[0];
246     			*(unsigned long*)dst = 0;
247     
248     #if !defined(__LITTLE_ENDIAN__)
249     			dst += 2;
250     #endif
251     
252     			if (copy_from_user(dst, src, 2))
253     				goto fetch_fault;
254     
255     #ifdef __LITTLE_ENDIAN__
256     			if (dst[1] & 0x80) {
257     				dst[2] = 0xff;
258     				dst[3] = 0xff;
259     			}
260     #else
261     			if (dst[2] & 0x80) {
262     				dst[0] = 0xff;
263     				dst[1] = 0xff;
264     			}
265     #endif
266     			ret = 0;
267     			break;
268     		}
269     		break;
270     	}
271     	return ret;
272     
273      fetch_fault:
274     	/* Argh. Address not only misaligned but also non-existent.
275     	 * Raise an EFAULT and see if it's trapped
276     	 */
277     	return die_if_no_fixup("Fault in unaligned fixup", regs, 0);
278     }
279     
280     /*
281      * emulate the instruction in the delay slot
282      * - fetches the instruction from PC+2
283      */
284     static inline int handle_unaligned_delayslot(struct pt_regs *regs)
285     {
286     	u16 instruction;
287     
288     	if (copy_from_user(&instruction, (u16 *)(regs->pc+2), 2)) {
289     		/* the instruction-fetch faulted */
290     		if (user_mode(regs))
291     			return -EFAULT;
292     
293     		/* kernel */
294     		die("delay-slot-insn faulting in handle_unaligned_delayslot", regs, 0);
295     	}
296     
297     	return handle_unaligned_ins(instruction,regs);
298     }
299     
300     /*
301      * handle an instruction that does an unaligned memory access
302      * - have to be careful of branch delay-slot instructions that fault
303      *   - if the branch would be taken PC points to the branch
304      *   - if the branch would not be taken, PC points to delay-slot
305      * - return 0 if handled, -EFAULT if failed (may not return if in kernel)
306      */
307     static int handle_unaligned_access(u16 instruction, struct pt_regs *regs)
308     {
309     	u_int rm;
310     	int ret, index;
311     
312     	index = (instruction>>8)&15;	/* 0x0F00 */
313     	rm = regs->regs[index];
314     
315     	/* shout about the first ten userspace fixups */
316     	if (user_mode(regs) && handle_unaligned_notify_count>0) {
317     		handle_unaligned_notify_count--;
318     
319     		printk("Fixing up unaligned userspace access in \"%s\" pid=%d pc=0x%p ins=0x%04hx\n",
320     		       current->comm,current->pid,(u16*)regs->pc,instruction);
321     	}
322     
323     	ret = -EFAULT;
324     	switch (instruction&0xF000) {
325     	case 0x0000:
326     		if (instruction==0x000B) {
327     			/* rts */
328     			ret = handle_unaligned_delayslot(regs);
329     			if (ret==0)
330     				regs->pc = regs->pr;
331     		}
332     		else if ((instruction&0x00FF)==0x0023) {
333     			/* braf @Rm */
334     			ret = handle_unaligned_delayslot(regs);
335     			if (ret==0)
336     				regs->pc += rm + 4;
337     		}
338     		else if ((instruction&0x00FF)==0x0003) {
339     			/* bsrf @Rm */
340     			ret = handle_unaligned_delayslot(regs);
341     			if (ret==0) {
342     				regs->pr = regs->pc + 4;
343     				regs->pc += rm + 4;
344     			}
345     		}
346     		else {
347     			/* mov.[bwl] to/from memory via r0+rn */
348     			goto simple;
349     		}
350     		break;
351     
352     	case 0x1000: /* mov.l Rm,@(disp,Rn) */
353     		goto simple;
354     
355     	case 0x2000: /* mov.[bwl] to memory, possibly with pre-decrement */
356     		goto simple;
357     
358     	case 0x4000:
359     		if ((instruction&0x00FF)==0x002B) {
360     			/* jmp @Rm */
361     			ret = handle_unaligned_delayslot(regs);
362     			if (ret==0)
363     				regs->pc = rm;
364     		}
365     		else if ((instruction&0x00FF)==0x000B) {
366     			/* jsr @Rm */
367     			ret = handle_unaligned_delayslot(regs);
368     			if (ret==0) {
369     				regs->pr = regs->pc + 4;
370     				regs->pc = rm;
371     			}
372     		}
373     		else {
374     			/* mov.[bwl] to/from memory via r0+rn */
375     			goto simple;
376     		}
377     		break;
378     
379     	case 0x5000: /* mov.l @(disp,Rm),Rn */
380     		goto simple;
381     
382     	case 0x6000: /* mov.[bwl] from memory, possibly with post-increment */
383     		goto simple;
384     
385     	case 0x8000: /* bf lab, bf/s lab, bt lab, bt/s lab */
386     		switch (instruction&0x0F00) {
387     		case 0x0100: /* mov.w R0,@(disp,Rm) */
388     			goto simple;
389     		case 0x0500: /* mov.w @(disp,Rm),R0 */
390     			goto simple;
391     		case 0x0B00: /* bf   lab - no delayslot*/
392     			break;
393     		case 0x0F00: /* bf/s lab */
394     			ret = handle_unaligned_delayslot(regs);
395     			if (ret==0)
396     				regs->pc += (instruction&0x00FF)*2 + 4;
397     			break;
398     		case 0x0900: /* bt   lab - no delayslot */
399     			break;
400     		case 0x0D00: /* bt/s lab */
401     			ret = handle_unaligned_delayslot(regs);
402     			if (ret==0)
403     				regs->pc += (instruction&0x00FF)*2 + 4;
404     			break;
405     		}
406     		break;
407     
408     	case 0xA000: /* bra label */
409     		ret = handle_unaligned_delayslot(regs);
410     		if (ret==0)
411     			regs->pc += (instruction&0x0FFF)*2 + 4;
412     		break;
413     
414     	case 0xB000: /* bsr label */
415     		ret = handle_unaligned_delayslot(regs);
416     		if (ret==0) {
417     			regs->pr = regs->pc + 4;
418     			regs->pc += (instruction&0x0FFF)*2 + 4;
419     		}
420     		break;
421     	}
422     	return ret;
423     
424     	/* handle non-delay-slot instruction */
425      simple:
426     	ret = handle_unaligned_ins(instruction,regs);
427     	if (ret==0)
428     		regs->pc += 2;
429     	return ret;
430     }
431     
432     /*
433      * Handle various address error exceptions
434      */
435     asmlinkage void do_address_error(struct pt_regs *regs, 
436     				 unsigned long writeaccess,
437     				 unsigned long address)
438     {
439     	unsigned long error_code;
440     	mm_segment_t oldfs;
441     	u16 instruction;
442     	int tmp;
443     
444     	asm volatile("stc       r2_bank,%0": "=r" (error_code));
445     
446     	oldfs = get_fs();
447     
448     	if (user_mode(regs)) {
449     		sti();
450     		current->thread.error_code = error_code;
451     		current->thread.trap_no = (writeaccess) ? 8 : 7;
452     
453     		/* bad PC is not something we can fix */
454     		if (regs->pc & 1)
455     			goto uspace_segv;
456     
457     		set_fs(USER_DS);
458     		if (copy_from_user(&instruction, (u16 *)(regs->pc), 2)) {
459     			/* Argh. Fault on the instruction itself.
460     			   This should never happen non-SMP
461     			*/
462     			set_fs(oldfs);
463     			goto uspace_segv;
464     		}
465     
466     		tmp = handle_unaligned_access(instruction, regs);
467     		set_fs(oldfs);
468     
469     		if (tmp==0)
470     			return; /* sorted */
471     
472     	uspace_segv:
473     		printk(KERN_NOTICE "Killing process \"%s\" due to unaligned access\n", current->comm);
474     		force_sig(SIGSEGV, current);
475     	} else {
476     		if (regs->pc & 1)
477     			die("unaligned program counter", regs, error_code);
478     
479     		set_fs(KERNEL_DS);
480     		if (copy_from_user(&instruction, (u16 *)(regs->pc), 2)) {
481     			/* Argh. Fault on the instruction itself.
482     			   This should never happen non-SMP
483     			*/
484     			set_fs(oldfs);
485     			die("insn faulting in do_address_error", regs, 0);
486     		}
487     
488     		handle_unaligned_access(instruction, regs);
489     		set_fs(oldfs);
490     	}
491     }
492     
493     DO_ERROR(12, SIGILL,  "reserved instruction", reserved_inst, current)
494     DO_ERROR(13, SIGILL,  "illegal slot instruction", illegal_slot_inst, current)
495     
496     asmlinkage void do_exception_error(unsigned long r4, unsigned long r5,
497     				   unsigned long r6, unsigned long r7,
498     				   struct pt_regs regs)
499     {
500     	long ex;
501     	asm volatile("stc	r2_bank, %0" : "=r" (ex));
502     	die_if_kernel("exception", &regs, ex);
503     }
504     
505     #if defined(CONFIG_SH_STANDARD_BIOS)
506     void *gdb_vbr_vector;
507     #endif
508     
509     void __init trap_init(void)
510     {
511     	extern void *vbr_base;
512     	extern void *exception_handling_table[14];
513     
514     	exception_handling_table[12] = (void *)do_reserved_inst;
515     	exception_handling_table[13] = (void *)do_illegal_slot_inst;
516     
517     #if defined(CONFIG_SH_STANDARD_BIOS)
518         	/*
519     	 * Read the old value of the VBR register to initialise
520     	 * the vector through which debug and BIOS traps are
521     	 * delegated by the Linux trap handler.
522     	 */
523     	{
524     	    register unsigned long vbr;
525     	    asm volatile("stc vbr, %0" : "=r" (vbr));
526     	    gdb_vbr_vector = (void *)(vbr + 0x100);
527     	    printk("Setting GDB trap vector to 0x%08lx\n",
528     	    	(unsigned long)gdb_vbr_vector);
529     	}
530     #endif
531     
532     	/* NOTE: The VBR value should be at P1
533     	   (or P2, virtural "fixed" address space).
534     	   It's definitely should not in physical address.  */
535     
536     	asm volatile("ldc	%0, vbr"
537     		     : /* no output */
538     		     : "r" (&vbr_base)
539     		     : "memory");
540     }
541     
542     void dump_stack(void)
543     {
544     	unsigned long *start;
545     	unsigned long *end;
546     	unsigned long *p;
547     
548     	asm("mov	r15, %0" : "=r" (start));
549     	asm("stc	r7_bank, %0" : "=r" (end));
550     	end += 8192/4;
551     
552     	printk("%08lx:%08lx\n", (unsigned long)start, (unsigned long)end);
553     	for (p=start; p < end; p++) {
554     		extern long _text, _etext;
555     		unsigned long v=*p;
556     
557     		if ((v >= (unsigned long )&_text)
558     		    && (v <= (unsigned long )&_etext)) {
559     			printk("%08lx\n", v);
560     		}
561     	}
562     }
563