Merge tag 'imx-fixes-3.13-2' of git://git.linaro.org/people/shawnguo/linux-2.6 into...
[firefly-linux-kernel-4.4.55.git] / arch / arm / kernel / traps.c
index ab517fcce21b8308dc957bc5b6809039c2e5f1fe..dbf0923e8d76bda9392b902e0c8e500025d70402 100644 (file)
@@ -34,6 +34,7 @@
 #include <asm/unwind.h>
 #include <asm/tls.h>
 #include <asm/system_misc.h>
+#include <asm/opcodes.h>
 
 static const char *handler[]= { "prefetch abort", "data abort", "address exception", "interrupt" };
 
@@ -341,15 +342,17 @@ void arm_notify_die(const char *str, struct pt_regs *regs,
 int is_valid_bugaddr(unsigned long pc)
 {
 #ifdef CONFIG_THUMB2_KERNEL
-       unsigned short bkpt;
+       u16 bkpt;
+       u16 insn = __opcode_to_mem_thumb16(BUG_INSTR_VALUE);
 #else
-       unsigned long bkpt;
+       u32 bkpt;
+       u32 insn = __opcode_to_mem_arm(BUG_INSTR_VALUE);
 #endif
 
        if (probe_kernel_address((unsigned *)pc, bkpt))
                return 0;
 
-       return bkpt == BUG_INSTR_VALUE;
+       return bkpt == insn;
 }
 
 #endif
@@ -402,25 +405,28 @@ asmlinkage void __exception do_undefinstr(struct pt_regs *regs)
        if (processor_mode(regs) == SVC_MODE) {
 #ifdef CONFIG_THUMB2_KERNEL
                if (thumb_mode(regs)) {
-                       instr = ((u16 *)pc)[0];
+                       instr = __mem_to_opcode_thumb16(((u16 *)pc)[0]);
                        if (is_wide_instruction(instr)) {
-                               instr <<= 16;
-                               instr |= ((u16 *)pc)[1];
+                               u16 inst2;
+                               inst2 = __mem_to_opcode_thumb16(((u16 *)pc)[1]);
+                               instr = __opcode_thumb32_compose(instr, inst2);
                        }
                } else
 #endif
-                       instr = *(u32 *) pc;
+                       instr = __mem_to_opcode_arm(*(u32 *) pc);
        } else if (thumb_mode(regs)) {
                if (get_user(instr, (u16 __user *)pc))
                        goto die_sig;
+               instr = __mem_to_opcode_thumb16(instr);
                if (is_wide_instruction(instr)) {
                        unsigned int instr2;
                        if (get_user(instr2, (u16 __user *)pc+1))
                                goto die_sig;
-                       instr <<= 16;
-                       instr |= instr2;
+                       instr2 = __mem_to_opcode_thumb16(instr2);
+                       instr = __opcode_thumb32_compose(instr, instr2);
                }
        } else if (get_user(instr, (u32 __user *)pc)) {
+               instr = __mem_to_opcode_arm(instr);
                goto die_sig;
        }
 
@@ -497,28 +503,64 @@ static int bad_syscall(int n, struct pt_regs *regs)
        return regs->ARM_r0;
 }
 
+static long do_cache_op_restart(struct restart_block *);
+
 static inline int
-do_cache_op(unsigned long start, unsigned long end, int flags)
+__do_cache_op(unsigned long start, unsigned long end)
+{
+       int ret;
+       unsigned long chunk = PAGE_SIZE;
+
+       do {
+               if (signal_pending(current)) {
+                       struct thread_info *ti = current_thread_info();
+
+                       ti->restart_block = (struct restart_block) {
+                               .fn     = do_cache_op_restart,
+                       };
+
+                       ti->arm_restart_block = (struct arm_restart_block) {
+                               {
+                                       .cache = {
+                                               .start  = start,
+                                               .end    = end,
+                                       },
+                               },
+                       };
+
+                       return -ERESTART_RESTARTBLOCK;
+               }
+
+               ret = flush_cache_user_range(start, start + chunk);
+               if (ret)
+                       return ret;
+
+               cond_resched();
+               start += chunk;
+       } while (start < end);
+
+       return 0;
+}
+
+static long do_cache_op_restart(struct restart_block *unused)
 {
-       struct mm_struct *mm = current->active_mm;
-       struct vm_area_struct *vma;
+       struct arm_restart_block *restart_block;
+
+       restart_block = &current_thread_info()->arm_restart_block;
+       return __do_cache_op(restart_block->cache.start,
+                            restart_block->cache.end);
+}
 
+static inline int
+do_cache_op(unsigned long start, unsigned long end, int flags)
+{
        if (end < start || flags)
                return -EINVAL;
 
-       down_read(&mm->mmap_sem);
-       vma = find_vma(mm, start);
-       if (vma && vma->vm_start < end) {
-               if (start < vma->vm_start)
-                       start = vma->vm_start;
-               if (end > vma->vm_end)
-                       end = vma->vm_end;
+       if (!access_ok(VERIFY_READ, start, end - start))
+               return -EFAULT;
 
-               up_read(&mm->mmap_sem);
-               return flush_cache_user_range(start, end);
-       }
-       up_read(&mm->mmap_sem);
-       return -EINVAL;
+       return __do_cache_op(start, end);
 }
 
 /*
@@ -814,7 +856,7 @@ static void __init kuser_init(void *vectors)
                memcpy(vectors + 0xfe0, vectors + 0xfe8, 4);
 }
 #else
-static void __init kuser_init(void *vectors)
+static inline void __init kuser_init(void *vectors)
 {
 }
 #endif