diff options
Diffstat (limited to 'arch/arm/kernel/traps.c')
-rw-r--r-- | arch/arm/kernel/traps.c | 280 |
1 files changed, 167 insertions, 113 deletions
diff --git a/arch/arm/kernel/traps.c b/arch/arm/kernel/traps.c index 5d04f325b..9267fec09 100644 --- a/arch/arm/kernel/traps.c +++ b/arch/arm/kernel/traps.c @@ -24,7 +24,6 @@ #include <asm/atomic.h> #include <asm/pgtable.h> -extern void die_if_kernel(char *str, struct pt_regs *regs, int err, int ret); extern void c_backtrace (unsigned long fp, int pmode); extern int ptrace_cancel_bpt (struct task_struct *); @@ -45,16 +44,17 @@ static inline void console_verbose(void) int kstack_depth_to_print = 200; -static int verify_stack_pointer (unsigned long stackptr, int size) +/* + * Stack pointers should always be within the kernels view of + * physical memory. If it is not there, then we can't dump + * out any information relating to the stack. + */ +static int verify_stack(unsigned long sp) { -#ifdef CONFIG_CPU_26 - if (stackptr < 0x02048000 || stackptr + size > 0x03000000) - return -EFAULT; -#else - if (stackptr < PAGE_OFFSET || stackptr + size > (unsigned long)high_memory) + if (sp < PAGE_OFFSET || sp > (unsigned long)high_memory) return -EFAULT; -#endif - return 0; + + return 0; } /* @@ -90,22 +90,26 @@ void dump_mem(unsigned long bottom, unsigned long top) static void dump_instr(unsigned long pc, int user) { - unsigned long module_start, module_end; int pmin = -2, pmax = 3, ok = 0; extern char start_kernel, _etext; if (!user) { + unsigned long module_start, module_end; + unsigned long kernel_start, kernel_end; + module_start = VMALLOC_START; module_end = module_start + MODULE_RANGE; - if ((pc >= (unsigned long) &start_kernel) && - (pc <= (unsigned long) &_etext)) { - if (pc + pmin < (unsigned long) &start_kernel) - pmin = ((unsigned long) &start_kernel) - pc; - if (pc + pmax > (unsigned long) &_etext) - pmax = ((unsigned long) &_etext) - pc; + kernel_start = (unsigned long)&start_kernel; + kernel_end = (unsigned long)&_etext; + + if (pc >= kernel_start && pc < kernel_end) { + if (pc + pmin < kernel_start) + pmin = kernel_start - pc; + if (pc + pmax > kernel_end) + pmax = kernel_end - pc; ok = 1; - } else if (pc >= module_start && pc <= module_end) { + } else if (pc >= module_start && pc < module_end) { if (pc + pmin < module_start) pmin = module_start - pc; if (pc + pmax > module_end) @@ -125,119 +129,138 @@ static void dump_instr(unsigned long pc, int user) printk ("pc not in code space\n"); } -static void dump_state(char *str, struct pt_regs *regs, int err) +spinlock_t die_lock; + +/* + * This function is protected against re-entrancy. + */ +void die(const char *str, struct pt_regs *regs, int err) { + struct task_struct *tsk = current; + + spin_lock_irq(&die_lock); + console_verbose(); printk("Internal error: %s: %x\n", str, err); printk("CPU: %d\n", smp_processor_id()); show_regs(regs); printk("Process %s (pid: %d, stackpage=%08lx)\n", - current->comm, current->pid, 4096+(unsigned long)current); + current->comm, current->pid, 4096+(unsigned long)tsk); + + if (!user_mode(regs)) { + unsigned long sp = (unsigned long)(regs + 1); + unsigned long fp; + int dump_info = 1; + + printk("Stack: "); + if (verify_stack(sp)) { + printk("invalid kernel stack pointer %08lx", sp); + dump_info = 0; + } else if (sp < 4096+(unsigned long)tsk) + printk("kernel stack pointer underflow"); + printk("\n"); + + if (dump_info) + dump_mem(sp - 16, 8192+(unsigned long)tsk); + + dump_info = 1; + + printk("Backtrace: "); + fp = regs->ARM_fp; + if (!fp) { + printk("no frame pointer"); + dump_info = 0; + } else if (verify_stack(fp)) { + printk("invalid frame pointer %08lx", fp); + dump_info = 0; + } else if (fp < 4096+(unsigned long)tsk) + printk("frame pointer underflow"); + printk("\n"); + + if (dump_info) + c_backtrace(fp, processor_mode(regs)); + + dump_instr(instruction_pointer(regs), 0); + } + + spin_unlock_irq(&die_lock); } -/* - * This function is protected against kernel-mode re-entrancy. If it - * is re-entered it will hang the system since we can't guarantee in - * this case that any of the functions that it calls are safe any more. - * Even the panic function could be a problem, but we'll give it a go. - */ -void die_if_kernel(char *str, struct pt_regs *regs, int err, int ret) +static void die_if_kernel(const char *str, struct pt_regs *regs, int err) { - static int died = 0; - unsigned long cstack, sstack, frameptr; - if (user_mode(regs)) return; - switch (died) { - case 2: - while (1); - case 1: - died ++; - panic ("die_if_kernel re-entered. Major kernel corruption. Please reboot me!"); - break; - case 0: - died ++; - break; - } - - dump_state(str, regs, err); - - cstack = (unsigned long)(regs + 1); - sstack = 4096+(unsigned long)current; - - printk("Stack: "); - if (verify_stack_pointer(cstack, 4)) - printk("invalid kernel stack pointer %08lx", cstack); - else if(cstack > sstack + 4096) - printk("(sp overflow)"); - else if(cstack < sstack) - printk("(sp underflow)"); - printk("\n"); - - dump_mem(cstack - 16, sstack + 4096); - - frameptr = regs->ARM_fp; - if (frameptr) { - if (verify_stack_pointer (frameptr, 4)) - printk ("Backtrace: invalid frame pointer\n"); - else { - printk("Backtrace: \n"); - c_backtrace (frameptr, processor_mode(regs)); - } - } - - dump_instr(instruction_pointer(regs), 0); - died = 0; - if (ret != -1) - do_exit (ret); - else { - cli (); - while (1); - } + die(str, regs, err); } -void bad_user_access_alignment (const void *ptr) +void bad_user_access_alignment(const void *ptr) { - void *pc; - __asm__("mov %0, lr\n": "=r" (pc)); - printk (KERN_ERR "bad_user_access_alignment called: ptr = %p, pc = %p\n", ptr, pc); + printk(KERN_ERR "bad user access alignment: ptr = %p, pc = %p\n", ptr, + __builtin_return_address(0)); current->tss.error_code = 0; current->tss.trap_no = 11; - force_sig (SIGBUS, current); -/* die_if_kernel("Oops - bad user access alignment", regs, mode, SIGBUS);*/ + force_sig(SIGBUS, current); +/* die_if_kernel("Oops - bad user access alignment", regs, mode);*/ } -asmlinkage void do_undefinstr (int address, struct pt_regs *regs, int mode) +asmlinkage void do_undefinstr(int address, struct pt_regs *regs, int mode) { +#ifdef CONFIG_DEBUG_USER + printk(KERN_INFO "%s (%d): undefined instruction: pc=%08lx\n", + current->comm, current->pid, instruction_pointer(regs)); +#endif current->tss.error_code = 0; current->tss.trap_no = 6; - force_sig (SIGILL, current); - die_if_kernel("Oops - undefined instruction", regs, mode, SIGILL); + force_sig(SIGILL, current); + die_if_kernel("Oops - undefined instruction", regs, mode); } -asmlinkage void do_excpt (int address, struct pt_regs *regs, int mode) +asmlinkage void do_excpt(int address, struct pt_regs *regs, int mode) { +#ifdef CONFIG_DEBUG_USER + printk(KERN_INFO "%s (%d): address exception: pc=%08lx\n", + current->comm, current->pid, instruction_pointer(regs)); +#endif current->tss.error_code = 0; current->tss.trap_no = 11; - force_sig (SIGBUS, current); - die_if_kernel("Oops - address exception", regs, mode, SIGBUS); + force_sig(SIGBUS, current); + die_if_kernel("Oops - address exception", regs, mode); } asmlinkage void do_unexp_fiq (struct pt_regs *regs) { #ifndef CONFIG_IGNORE_FIQ - printk ("Hmm. Unexpected FIQ received, but trying to continue\n"); - printk ("You may have a hardware problem...\n"); + printk("Hmm. Unexpected FIQ received, but trying to continue\n"); + printk("You may have a hardware problem...\n"); #endif } +/* + * bad_mode handles the impossible case in the vectors. + * If you see one of these, then it's extremely serious, + * and could mean you have buggy hardware. It never + * returns, and never tries to sync. We hope that we + * can dump out some state information... + */ asmlinkage void bad_mode(struct pt_regs *regs, int reason, int proc_mode) { - printk (KERN_CRIT "Bad mode in %s handler detected: mode %s\n", - handler[reason], - processor_modes[proc_mode]); - die_if_kernel ("Oops", regs, 0, -1); + console_verbose(); + + printk(KERN_CRIT "Bad mode in %s handler detected: mode %s\n", + handler[reason], processor_modes[proc_mode]); + + /* + * Dump out the vectors and stub routines + */ + printk(KERN_CRIT "Vectors:\n"); + dump_mem(0, 0x40); + printk(KERN_CRIT "Stubs:\n"); + dump_mem(0x200, 0x4b8); + + die("Oops", regs, 0); + cli(); + while(1); } /* @@ -249,54 +272,85 @@ asmlinkage void bad_mode(struct pt_regs *regs, int reason, int proc_mode) */ asmlinkage void math_state_restore (void) { - current->used_math = 1; + current->used_math = 1; } -asmlinkage void arm_syscall (int no, struct pt_regs *regs) +asmlinkage int arm_syscall (int no, struct pt_regs *regs) { switch (no) { case 0: /* branch through 0 */ force_sig(SIGSEGV, current); -// if (user_mode(regs)) { -// dump_state("branch through zero", regs, 0); -// if (regs->ARM_fp) -// c_backtrace (regs->ARM_fp, processor_mode(regs)); -// } - die_if_kernel ("branch through zero", regs, 0, SIGSEGV); + die_if_kernel("branch through zero", regs, 0); break; case 1: /* SWI_BREAK_POINT */ regs->ARM_pc -= 4; /* Decrement PC by one instruction */ - ptrace_cancel_bpt (current); - force_sig (SIGTRAP, current); + ptrace_cancel_bpt(current); + force_sig(SIGTRAP, current); + return regs->ARM_r0; + + case 2: /* sys_cacheflush */ +#ifdef CONFIG_CPU_32 + /* r0 = start, r1 = length, r2 = flags */ + processor.u.armv3v4._flush_cache_area(regs->ARM_r0, + regs->ARM_r1, + 1); +#endif break; default: - printk ("[%d] %s: arm syscall %d\n", current->pid, current->comm, no); - force_sig (SIGILL, current); + /* Calls 9f00xx..9f07ff are defined to return -ENOSYS + if not implemented, rather than raising SIGILL. This + way the calling program can gracefully determine whether + a feature is supported. */ + if (no <= 0x7ff) + return -ENOSYS; +#ifdef CONFIG_DEBUG_USER + /* experiance shows that these seem to indicate that + * something catastrophic has happened + */ + printk("[%d] %s: arm syscall %d\n", current->pid, current->comm, no); if (user_mode(regs)) { - show_regs (regs); - c_backtrace (regs->ARM_fp, processor_mode(regs)); + show_regs(regs); + c_backtrace(regs->ARM_fp, processor_mode(regs)); } - die_if_kernel ("Oops", regs, no, SIGILL); +#endif + force_sig(SIGILL, current); + die_if_kernel("Oops", regs, no); break; } + return 0; } asmlinkage void deferred(int n, struct pt_regs *regs) { - dump_state("old system call", regs, n); - force_sig (SIGILL, current); + /* You might think just testing `handler' would be enough, but PER_LINUX + * points it to no_lcall7 to catch undercover SVr4 binaries. Gutted. + */ + if (current->personality != PER_LINUX && current->exec_domain->handler) { + /* Hand it off to iBCS. The extra parameter and consequent type + * forcing is necessary because of the weird ARM calling convention. + */ + void (*handler)(int nr, struct pt_regs *regs) = (void *)current->exec_domain->handler; + (*handler)(n, regs); + return; + } + +#ifdef CONFIG_DEBUG_USER + printk(KERN_ERR "[%d] %s: old system call.\n", current->pid, + current->comm); +#endif + force_sig(SIGILL, current); } asmlinkage void arm_malalignedptr(const char *str, void *pc, volatile void *ptr) { - printk ("Mal-aligned pointer in %s: %p (PC=%p)\n", str, ptr, pc); + printk("Mal-aligned pointer in %s: %p (PC=%p)\n", str, ptr, pc); } -asmlinkage void arm_invalidptr (const char *function, int size) +asmlinkage void arm_invalidptr(const char *function, int size) { - printk ("Invalid pointer size in %s (PC=%p) size %d\n", + printk("Invalid pointer size in %s (pc=%p) size %d\n", function, __builtin_return_address(0), size); } |