Merge branch 'stable-3.2' into pandora-3.2
[pandora-kernel.git] / arch / arm / kernel / traps.c
index 99a5727..667587b 100644 (file)
 
 #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)
 {
@@ -124,30 +130,26 @@ static void dump_mem(const char *lvl, const char *str, unsigned long bottom,
        set_fs(fs);
 }
 
-static void dump_instr(const char *lvl, struct pt_regs *regs)
+static void __dump_instr(const char *lvl, struct pt_regs *regs)
 {
        unsigned long addr = instruction_pointer(regs);
        const int thumb = thumb_mode(regs);
        const int width = thumb ? 4 : 8;
-       mm_segment_t fs;
        char str[sizeof("00000000 ") * 5 + 2 + 1], *p = str;
        int i;
 
        /*
-        * We need to switch to kernel mode so that we can use __get_user
-        * to safely read from kernel space.  Note that we now dump the
-        * code first, just in case the backtrace kills us.
+        * Note that we now dump the code first, just in case the backtrace
+        * kills us.
         */
-       fs = get_fs();
-       set_fs(KERNEL_DS);
 
        for (i = -4; i < 1 + !!thumb; i++) {
                unsigned int val, bad;
 
                if (thumb)
-                       bad = __get_user(val, &((u16 *)addr)[i]);
+                       bad = get_user(val, &((u16 *)addr)[i]);
                else
-                       bad = __get_user(val, &((u32 *)addr)[i]);
+                       bad = get_user(val, &((u32 *)addr)[i]);
 
                if (!bad)
                        p += sprintf(p, i == 0 ? "(%0*x) " : "%0*x ",
@@ -158,8 +160,20 @@ static void dump_instr(const char *lvl, struct pt_regs *regs)
                }
        }
        printk("%sCode: %s\n", lvl, str);
+}
 
-       set_fs(fs);
+static void dump_instr(const char *lvl, struct pt_regs *regs)
+{
+       mm_segment_t fs;
+
+       if (!user_mode(regs)) {
+               fs = get_fs();
+               set_fs(KERNEL_DS);
+               __dump_instr(lvl, regs);
+               set_fs(fs);
+       } else {
+               __dump_instr(lvl, regs);
+       }
 }
 
 #ifdef CONFIG_ARM_UNWIND
@@ -362,18 +376,10 @@ static int call_undef_hook(struct pt_regs *regs, unsigned int instr)
 
 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) {
@@ -388,20 +394,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",
@@ -471,14 +480,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);
@@ -488,9 +497,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;
 }
 
 /*
@@ -536,8 +547,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))
@@ -552,7 +562,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) {
@@ -670,7 +680,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;
 }