ARM: show short message on segfault
[pandora-kernel.git] / arch / arm / kernel / traps.c
index bc9f9da..071772c 100644 (file)
 #include <linux/kdebug.h>
 #include <linux/module.h>
 #include <linux/kexec.h>
+#include <linux/bug.h>
 #include <linux/delay.h>
 #include <linux/init.h>
 #include <linux/sched.h>
 
 #include <linux/atomic.h>
 #include <asm/cacheflush.h>
+#include <asm/exception.h>
 #include <asm/system.h>
 #include <asm/unistd.h>
 #include <asm/traps.h>
 
 #include "signal.h"
 
-static const char *handler[]= { "prefetch abort", "data abort", "address exception", "interrupt" };
+static const char *handler[]= {
+       "prefetch abort",
+       "data abort",
+       "address exception",
+       "interrupt",
+       "undefined instruction",
+};
 
 void *vectors_page;
 
 #ifdef CONFIG_DEBUG_USER
-unsigned int user_debug;
+unsigned int user_debug = UDBG_SEGV_SHORT;
 
 static int __init user_debug_setup(char *str)
 {
@@ -255,7 +263,7 @@ static int __die(const char *str, int err, struct thread_info *thread, struct pt
        return ret;
 }
 
-static DEFINE_SPINLOCK(die_lock);
+static DEFINE_RAW_SPINLOCK(die_lock);
 
 /*
  * This function is protected against re-entrancy.
@@ -267,9 +275,11 @@ void die(const char *str, struct pt_regs *regs, int err)
 
        oops_enter();
 
-       spin_lock_irq(&die_lock);
+       raw_spin_lock_irq(&die_lock);
        console_verbose();
        bust_spinlocks(1);
+       if (!user_mode(regs))
+               report_bug(regs->ARM_pc, regs);
        ret = __die(str, err, thread, regs);
 
        if (regs && kexec_should_crash(thread->task))
@@ -277,7 +287,7 @@ void die(const char *str, struct pt_regs *regs, int err)
 
        bust_spinlocks(0);
        add_taint(TAINT_DIE);
-       spin_unlock_irq(&die_lock);
+       raw_spin_unlock_irq(&die_lock);
        oops_exit();
 
        if (in_interrupt())
@@ -301,25 +311,43 @@ void arm_notify_die(const char *str, struct pt_regs *regs,
        }
 }
 
+#ifdef CONFIG_GENERIC_BUG
+
+int is_valid_bugaddr(unsigned long pc)
+{
+#ifdef CONFIG_THUMB2_KERNEL
+       unsigned short bkpt;
+#else
+       unsigned long bkpt;
+#endif
+
+       if (probe_kernel_address((unsigned *)pc, bkpt))
+               return 0;
+
+       return bkpt == BUG_INSTR_VALUE;
+}
+
+#endif
+
 static LIST_HEAD(undef_hook);
-static DEFINE_SPINLOCK(undef_lock);
+static DEFINE_RAW_SPINLOCK(undef_lock);
 
 void register_undef_hook(struct undef_hook *hook)
 {
        unsigned long flags;
 
-       spin_lock_irqsave(&undef_lock, flags);
+       raw_spin_lock_irqsave(&undef_lock, flags);
        list_add(&hook->node, &undef_hook);
-       spin_unlock_irqrestore(&undef_lock, flags);
+       raw_spin_unlock_irqrestore(&undef_lock, flags);
 }
 
 void unregister_undef_hook(struct undef_hook *hook)
 {
        unsigned long flags;
 
-       spin_lock_irqsave(&undef_lock, flags);
+       raw_spin_lock_irqsave(&undef_lock, flags);
        list_del(&hook->node);
-       spin_unlock_irqrestore(&undef_lock, flags);
+       raw_spin_unlock_irqrestore(&undef_lock, flags);
 }
 
 static int call_undef_hook(struct pt_regs *regs, unsigned int instr)
@@ -328,30 +356,22 @@ static int call_undef_hook(struct pt_regs *regs, unsigned int instr)
        unsigned long flags;
        int (*fn)(struct pt_regs *regs, unsigned int instr) = NULL;
 
-       spin_lock_irqsave(&undef_lock, flags);
+       raw_spin_lock_irqsave(&undef_lock, flags);
        list_for_each_entry(hook, &undef_hook, node)
                if ((instr & hook->instr_mask) == hook->instr_val &&
                    (regs->ARM_cpsr & hook->cpsr_mask) == hook->cpsr_val)
                        fn = hook->fn;
-       spin_unlock_irqrestore(&undef_lock, flags);
+       raw_spin_unlock_irqrestore(&undef_lock, flags);
 
        return fn ? fn(regs, instr) : 1;
 }
 
 asmlinkage void __exception do_undefinstr(struct pt_regs *regs)
 {
-       unsigned int correction = thumb_mode(regs) ? 2 : 4;
        unsigned int instr;
        siginfo_t info;
        void __user *pc;
 
-       /*
-        * According to the ARM ARM, PC is 2 or 4 bytes ahead,
-        * depending whether we're in Thumb mode or not.
-        * Correct this offset.
-        */
-       regs->ARM_pc -= correction;
-
        pc = (void __user *)instruction_pointer(regs);
 
        if (processor_mode(regs) == SVC_MODE) {
@@ -366,20 +386,23 @@ asmlinkage void __exception do_undefinstr(struct pt_regs *regs)
 #endif
                        instr = *(u32 *) pc;
        } else if (thumb_mode(regs)) {
-               get_user(instr, (u16 __user *)pc);
+               if (get_user(instr, (u16 __user *)pc))
+                       goto die_sig;
                if (is_wide_instruction(instr)) {
                        unsigned int instr2;
-                       get_user(instr2, (u16 __user *)pc+1);
+                       if (get_user(instr2, (u16 __user *)pc+1))
+                               goto die_sig;
                        instr <<= 16;
                        instr |= instr2;
                }
-       } else {
-               get_user(instr, (u32 __user *)pc);
+       } else if (get_user(instr, (u32 __user *)pc)) {
+               goto die_sig;
        }
 
        if (call_undef_hook(regs, instr) == 0)
                return;
 
+die_sig:
 #ifdef CONFIG_DEBUG_USER
        if (user_debug & UDBG_UNDEFINED) {
                printk(KERN_INFO "%s (%d): undefined instruction: pc=%p\n",
@@ -449,14 +472,14 @@ static int bad_syscall(int n, struct pt_regs *regs)
        return regs->ARM_r0;
 }
 
-static inline void
+static inline int
 do_cache_op(unsigned long start, unsigned long end, int flags)
 {
        struct mm_struct *mm = current->active_mm;
        struct vm_area_struct *vma;
 
        if (end < start || flags)
-               return;
+               return -EINVAL;
 
        down_read(&mm->mmap_sem);
        vma = find_vma(mm, start);
@@ -466,9 +489,11 @@ do_cache_op(unsigned long start, unsigned long end, int flags)
                if (end > vma->vm_end)
                        end = vma->vm_end;
 
-               flush_cache_user_range(vma, start, end);
+               up_read(&mm->mmap_sem);
+               return flush_cache_user_range(start, end);
        }
        up_read(&mm->mmap_sem);
+       return -EINVAL;
 }
 
 /*
@@ -514,8 +539,7 @@ asmlinkage int arm_syscall(int no, struct pt_regs *regs)
         * the specified region).
         */
        case NR(cacheflush):
-               do_cache_op(regs->ARM_r0, regs->ARM_r1, regs->ARM_r2);
-               return 0;
+               return do_cache_op(regs->ARM_r0, regs->ARM_r1, regs->ARM_r2);
 
        case NR(usr26):
                if (!(elf_hwcap & HWCAP_26BIT))
@@ -530,7 +554,7 @@ asmlinkage int arm_syscall(int no, struct pt_regs *regs)
                return regs->ARM_r0;
 
        case NR(set_tls):
-               thread->tp_value = regs->ARM_r0;
+               thread->tp_value[0] = regs->ARM_r0;
                if (tls_emu)
                        return 0;
                if (has_tls_reg) {
@@ -648,7 +672,7 @@ static int get_tp_trap(struct pt_regs *regs, unsigned int instr)
        int reg = (instr >> 12) & 15;
        if (reg == 15)
                return 1;
-       regs->uregs[reg] = current_thread_info()->tp_value;
+       regs->uregs[reg] = current_thread_info()->tp_value[0];
        regs->ARM_pc += 4;
        return 0;
 }
@@ -706,16 +730,6 @@ baddataabort(int code, unsigned long instr, struct pt_regs *regs)
        arm_notify_die("unknown data abort code", regs, &info, instr, 0);
 }
 
-void __attribute__((noreturn)) __bug(const char *file, int line)
-{
-       printk(KERN_CRIT"kernel BUG at %s:%d!\n", file, line);
-       *(int *)0 = 0;
-
-       /* Avoid "noreturn function does return" */
-       for (;;);
-}
-EXPORT_SYMBOL(__bug);
-
 void __readwrite_bug(const char *fn)
 {
        printk("%s called, but not implemented\n", fn);