~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

Linux Cross Reference
Linux/arch/arm/kernel/traps.c

Version: ~ [ 2.2.5 ] ~ [ 2.4.1 ] ~ [ 2.4.9 ] ~ [ 2.6.17.10 ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 /*
  2  *  linux/arch/arm/kernel/traps.c
  3  *
  4  *  Copyright (C) 1995, 1996 Russell King
  5  *  Fragments that appear the same as linux/arch/i386/kernel/traps.c (C) Linus Torvalds
  6  *
  7  * This program is free software; you can redistribute it and/or modify
  8  * it under the terms of the GNU General Public License version 2 as
  9  * published by the Free Software Foundation.
 10  *
 11  *  'traps.c' handles hardware exceptions after we have saved some state in
 12  *  'linux/arch/arm/lib/traps.S'.  Mostly a debugging aid, but will probably
 13  *  kill the offending process.
 14  */
 15 #include <linux/config.h>
 16 #include <linux/types.h>
 17 #include <linux/kernel.h>
 18 #include <linux/signal.h>
 19 #include <linux/sched.h>
 20 #include <linux/mm.h>
 21 #include <linux/spinlock.h>
 22 #include <linux/ptrace.h>
 23 #include <linux/init.h>
 24 
 25 #include <asm/atomic.h>
 26 #include <asm/io.h>
 27 #include <asm/pgtable.h>
 28 #include <asm/system.h>
 29 #include <asm/uaccess.h>
 30 
 31 #include "ptrace.h"
 32 
 33 extern void c_backtrace (unsigned long fp, int pmode);
 34 extern void show_pte(struct mm_struct *mm, unsigned long addr);
 35 
 36 const char *processor_modes[]=
 37 { "USER_26", "FIQ_26" , "IRQ_26" , "SVC_26" , "UK4_26" , "UK5_26" , "UK6_26" , "UK7_26" ,
 38   "UK8_26" , "UK9_26" , "UK10_26", "UK11_26", "UK12_26", "UK13_26", "UK14_26", "UK15_26",
 39   "USER_32", "FIQ_32" , "IRQ_32" , "SVC_32" , "UK4_32" , "UK5_32" , "UK6_32" , "ABT_32" ,
 40   "UK8_32" , "UK9_32" , "UK10_32", "UND_32" , "UK12_32", "UK13_32", "UK14_32", "SYS_32"
 41 };
 42 
 43 static const char *handler[]= { "prefetch abort", "data abort", "address exception", "interrupt" };
 44 
 45 /*
 46  * Stack pointers should always be within the kernels view of
 47  * physical memory.  If it is not there, then we can't dump
 48  * out any information relating to the stack.
 49  */
 50 static int verify_stack(unsigned long sp)
 51 {
 52         if (sp < PAGE_OFFSET || sp > (unsigned long)high_memory)
 53                 return -EFAULT;
 54 
 55         return 0;
 56 }
 57 
 58 /*
 59  * Dump out the contents of some memory nicely...
 60  */
 61 void dump_mem(unsigned long bottom, unsigned long top)
 62 {
 63         unsigned long p = bottom & ~31;
 64         int i;
 65 
 66         for (p = bottom & ~31; p < top;) {
 67                 printk("%08lx: ", p);
 68 
 69                 for (i = 0; i < 8; i++, p += 4) {
 70                         unsigned int val;
 71 
 72                         if (p < bottom || p >= top)
 73                                 printk("         ");
 74                         else {
 75                                 __get_user(val, (unsigned long *)p);
 76                                 printk("%08x ", val);
 77                         }
 78                         if (i == 3)
 79                                 printk(" ");
 80                 }
 81                 printk ("\n");
 82         }
 83 }
 84 
 85 /*
 86  * These constants are for searching for possible module text
 87  * segments.  VMALLOC_OFFSET comes from mm/vmalloc.c; MODULE_RANGE is
 88  * a guess of how much space is likely to be vmalloced.
 89  */
 90 #define VMALLOC_OFFSET (8*1024*1024)
 91 #define MODULE_RANGE (8*1024*1024)
 92 
 93 static void dump_instr(struct pt_regs *regs)
 94 {
 95         unsigned long addr = instruction_pointer(regs);
 96         const int thumb = thumb_mode(regs);
 97         const int width = thumb ? 4 : 8;
 98         int i;
 99 
100         printk("Code: ");
101         for (i = -2; i < 3; i++) {
102                 unsigned int val, bad;
103 
104                 if (thumb)
105                         bad = __get_user(val, &((u16 *)addr)[i]);
106                 else
107                         bad = __get_user(val, &((u32 *)addr)[i]);
108 
109                 if (!bad)
110                         printk(i == 0 ? "(%0*x) " : "%0*x", width, val);
111                 else {
112                         printk("bad PC value.");
113                         break;
114                 }
115         }
116         printk("\n");
117 }
118 
119 static void dump_stack(struct task_struct *tsk, unsigned long sp)
120 {
121         printk("Stack:\n");
122         dump_mem(sp - 16, 8192+(unsigned long)tsk);
123 }
124 
125 static void dump_backtrace(struct pt_regs *regs, struct task_struct *tsk)
126 {
127         unsigned int fp;
128         int ok = 1;
129 
130         printk("Backtrace: ");
131         fp = regs->ARM_fp;
132         if (!fp) {
133                 printk("no frame pointer");
134                 ok = 0;
135         } else if (verify_stack(fp)) {
136                 printk("invalid frame pointer %08lx", fp);
137                 ok = 0;
138         } else if (fp < 4096+(unsigned long)tsk)
139                 printk("frame pointer underflow");
140         printk("\n");
141 
142         if (ok)
143                 c_backtrace(fp, processor_mode(regs));
144 }
145 
146 spinlock_t die_lock = SPIN_LOCK_UNLOCKED;
147 
148 /*
149  * This function is protected against re-entrancy.
150  */
151 void die(const char *str, struct pt_regs *regs, int err)
152 {
153         struct task_struct *tsk = current;
154 
155         console_verbose();
156         spin_lock_irq(&die_lock);
157 
158         printk("Internal error: %s: %x\n", str, err);
159         printk("CPU: %d\n", smp_processor_id());
160         show_regs(regs);
161         printk("Process %s (pid: %d, stackpage=%08lx)\n",
162                 current->comm, current->pid, 4096+(unsigned long)tsk);
163 
164         if (!user_mode(regs)) {
165                 mm_segment_t fs;
166 
167                 /*
168                  * We need to switch to kernel mode so that we can
169                  * use __get_user to safely read from kernel space.
170                  * Note that we now dump the code first, just in case
171                  * the backtrace kills us.
172                  */
173                 fs = get_fs();
174                 set_fs(KERNEL_DS);
175 
176                 dump_instr(regs);
177                 dump_stack(tsk, (unsigned long)(regs + 1));
178                 dump_backtrace(regs, tsk);
179 
180                 set_fs(fs);
181         }
182 
183         spin_unlock_irq(&die_lock);
184         do_exit(SIGSEGV);
185 }
186 
187 void die_if_kernel(const char *str, struct pt_regs *regs, int err)
188 {
189         if (user_mode(regs))
190                 return;
191 
192         die(str, regs, err);
193 }
194 
195 asmlinkage void do_undefinstr(int address, struct pt_regs *regs, int mode)
196 {
197         unsigned long addr = instruction_pointer(regs);
198         siginfo_t info;
199 
200 #ifdef CONFIG_DEBUG_USER
201         printk(KERN_INFO "%s (%d): undefined instruction: pc=%08lx\n",
202                 current->comm, current->pid, addr);
203         dump_instr(regs);
204 #endif
205 
206         current->thread.error_code = 0;
207         current->thread.trap_no = 6;
208 
209         info.si_signo = SIGILL;
210         info.si_errno = 0;
211         info.si_code  = ILL_ILLOPC;
212         info.si_addr  = (void *)addr;
213 
214         force_sig_info(SIGILL, &info, current);
215 
216         die_if_kernel("Oops - undefined instruction", regs, mode);
217 }
218 
219 asmlinkage void do_excpt(int address, struct pt_regs *regs, int mode)
220 {
221         siginfo_t info;
222 
223 #ifdef CONFIG_DEBUG_USER
224         printk(KERN_INFO "%s (%d): address exception: pc=%08lx\n",
225                 current->comm, current->pid, instruction_pointer(regs));
226         dump_instr(regs);
227 #endif
228 
229         current->thread.error_code = 0;
230         current->thread.trap_no = 11;
231 
232         info.si_signo = SIGBUS;
233         info.si_errno = 0;
234         info.si_code  = BUS_ADRERR;
235         info.si_addr  = (void *)address;
236 
237         force_sig_info(SIGBUS, &info, current);
238 
239         die_if_kernel("Oops - address exception", regs, mode);
240 }
241 
242 asmlinkage void do_unexp_fiq (struct pt_regs *regs)
243 {
244 #ifndef CONFIG_IGNORE_FIQ
245         printk("Hmm.  Unexpected FIQ received, but trying to continue\n");
246         printk("You may have a hardware problem...\n");
247 #endif
248 }
249 
250 /*
251  * bad_mode handles the impossible case in the vectors.  If you see one of
252  * these, then it's extremely serious, and could mean you have buggy hardware.
253  * It never returns, and never tries to sync.  We hope that we can at least
254  * dump out some state information...
255  */
256 asmlinkage void bad_mode(struct pt_regs *regs, int reason, int proc_mode)
257 {
258         console_verbose();
259 
260         printk(KERN_CRIT "Bad mode in %s handler detected: mode %s\n",
261                 handler[reason], processor_modes[proc_mode]);
262 
263         /*
264          * Dump out the vectors and stub routines.  Maybe a better solution
265          * would be to dump them out only if we detect that they are corrupted.
266          */
267         printk(KERN_CRIT "Vectors:\n");
268         dump_mem(0, 0x40);
269         printk(KERN_CRIT "Stubs:\n");
270         dump_mem(0x200, 0x4b8);
271 
272         die("Oops", regs, 0);
273         cli();
274         panic("bad mode");
275 }
276 
277 /*
278  * Handle some more esoteric system calls
279  */
280 asmlinkage int arm_syscall(int no, struct pt_regs *regs)
281 {
282         siginfo_t info;
283 
284         switch (no) {
285         case 0: /* branch through 0 */
286                 info.si_signo = SIGSEGV;
287                 info.si_errno = 0;
288                 info.si_code  = SEGV_MAPERR;
289                 info.si_addr  = NULL;
290 
291                 force_sig_info(SIGSEGV, &info, current);
292 
293                 die_if_kernel("branch through zero", regs, 0);
294                 break;
295 
296         case 1: /* SWI BREAK_POINT */
297                 /*
298                  * The PC is always left pointing at the next
299                  * instruction.  Fix this.
300                  */
301                 regs->ARM_pc -= 4;
302                 __ptrace_cancel_bpt(current);
303 
304                 info.si_signo = SIGTRAP;
305                 info.si_errno = 0;
306                 info.si_code  = TRAP_BRKPT;
307                 info.si_addr  = (void *)instruction_pointer(regs);
308 
309                 force_sig_info(SIGTRAP, &info, current);
310                 return regs->ARM_r0;
311 
312         case 2: /* sys_cacheflush */
313 #ifdef CONFIG_CPU_32
314                 /* r0 = start, r1 = end, r2 = flags */
315                 cpu_cache_clean_invalidate_range(regs->ARM_r0, regs->ARM_r1, 1);
316 #endif
317                 break;
318 
319         default:
320                 /* Calls 9f00xx..9f07ff are defined to return -ENOSYS
321                    if not implemented, rather than raising SIGILL.  This
322                    way the calling program can gracefully determine whether
323                    a feature is supported.  */
324                 if (no <= 0x7ff)
325                         return -ENOSYS;
326 #ifdef CONFIG_DEBUG_USER
327                 /* experience shows that these seem to indicate that
328                  * something catastrophic has happened
329                  */
330                 printk("[%d] %s: arm syscall %d\n", current->pid, current->comm, no);
331                 dump_instr(regs);
332                 if (user_mode(regs)) {
333                         show_regs(regs);
334                         c_backtrace(regs->ARM_fp, processor_mode(regs));
335                 }
336 #endif
337                 force_sig(SIGILL, current);
338                 die_if_kernel("Oops", regs, no);
339                 break;
340         }
341         return 0;
342 }
343 
344 asmlinkage void deferred(int n, struct pt_regs *regs)
345 {
346         /* You might think just testing `handler' would be enough, but PER_LINUX
347          * points it to no_lcall7 to catch undercover SVr4 binaries.  Gutted.
348          */
349         if (current->personality != PER_LINUX && current->exec_domain->handler) {
350                 /* Hand it off to iBCS.  The extra parameter and consequent type 
351                  * forcing is necessary because of the weird ARM calling convention.
352                  */
353                 void (*handler)(int nr, struct pt_regs *regs) = (void *)current->exec_domain->handler;
354                 (*handler)(n, regs);
355                 return;
356         }
357 
358 #ifdef CONFIG_DEBUG_USER
359         printk(KERN_ERR "[%d] %s: obsolete system call %08x.\n",
360                 current->pid, current->comm, n);
361         dump_instr(regs);
362 #endif
363         force_sig(SIGILL, current);
364         die_if_kernel("Oops", regs, n);
365 }
366 
367 void __bad_xchg(volatile void *ptr, int size)
368 {
369         printk("xchg: bad data size: pc 0x%p, ptr 0x%p, size %d\n",
370                 __builtin_return_address(0), ptr, size);
371         BUG();
372 }
373 
374 /*
375  * A data abort trap was taken, but we did not handle the instruction.
376  * Try to abort the user program, or panic if it was the kernel.
377  */
378 asmlinkage void
379 baddataabort(int code, unsigned long instr, struct pt_regs *regs)
380 {
381         unsigned long addr = instruction_pointer(regs);
382         siginfo_t info;
383 
384 #ifdef CONFIG_DEBUG_USER
385         printk(KERN_ERR "[%d] %s: bad data abort: code %d instr 0x%08lx\n",
386                 current->pid, current->comm, code, instr);
387         dump_instr(regs);
388         show_pte(current->mm, addr);
389 #endif
390 
391         info.si_signo = SIGILL;
392         info.si_errno = 0;
393         info.si_code  = ILL_ILLOPC;
394         info.si_addr  = (void *)addr;
395 
396         force_sig_info(SIGILL, &info, current);
397         die_if_kernel("unknown data abort code", regs, instr);
398 }
399 
400 void __bug(const char *file, int line, void *data)
401 {
402         printk(KERN_CRIT"kernel BUG at %s:%d!", file, line);
403         if (data)
404                 printk(KERN_CRIT" - extra data = %p", data);
405         printk("\n");
406         *(int *)0 = 0;
407 }
408 
409 void __readwrite_bug(const char *fn)
410 {
411         printk("%s called, but not implemented", fn);
412         BUG();
413 }
414 
415 void __pte_error(const char *file, int line, unsigned long val)
416 {
417         printk("%s:%d: bad pte %08lx.\n", file, line, val);
418 }
419 
420 void __pmd_error(const char *file, int line, unsigned long val)
421 {
422         printk("%s:%d: bad pmd %08lx.\n", file, line, val);
423 }
424 
425 void __pgd_error(const char *file, int line, unsigned long val)
426 {
427         printk("%s:%d: bad pgd %08lx.\n", file, line, val);
428 }
429 
430 asmlinkage void __div0(void)
431 {
432         printk("Division by zero in kernel.\n");
433         __backtrace();
434 }
435 
436 void abort(void)
437 {
438         void *lr = __builtin_return_address(0);
439 
440         printk(KERN_CRIT "abort() called from %p!  (Please "
441                "report to rmk@arm.linux.org.uk)\n", lr);
442 
443         BUG();
444 
445         /* if that doesn't kill us, halt */
446         panic("Oops failed to kill thread");
447 }
448 
449 void __init trap_init(void)
450 {
451         extern void __trap_init(void);
452 
453         __trap_init();
454 #ifdef CONFIG_CPU_32
455         modify_domain(DOMAIN_USER, DOMAIN_CLIENT);
456 #endif
457 }
458 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

This page was automatically generated by the LXR engine.
Visit the LXR main site for more information.