ARM: 8165/1: alignment: don't break misaligned NEON load/store
[pandora-kernel.git] / arch / arm / mm / alignment.c
index cfbcf8b..6e39bf1 100644 (file)
@@ -38,6 +38,7 @@
  * This code is not portable to processors with late data abort handling.
  */
 #define CODING_BITS(i) (i & 0x0e000000)
+#define COND_BITS(i)   (i & 0xf0000000)
 
 #define LDST_I_BIT(i)  (i & (1 << 26))         /* Immediate constant   */
 #define LDST_P_BIT(i)  (i & (1 << 24))         /* Preindex             */
@@ -86,16 +87,6 @@ core_param(alignment, ai_usermode, int, 0600);
 #define UM_FIXUP       (1 << 1)
 #define UM_SIGNAL      (1 << 2)
 
-#ifdef CONFIG_PROC_FS
-static const char *usermode_action[] = {
-       "ignored",
-       "warn",
-       "fixup",
-       "fixup+warn",
-       "signal",
-       "signal+warn"
-};
-
 /* Return true if and only if the ARMv6 unaligned access model is in use. */
 static bool cpu_is_v6_unaligned(void)
 {
@@ -123,6 +114,16 @@ static int safe_usermode(int new_usermode, bool warn)
        return new_usermode;
 }
 
+#ifdef CONFIG_PROC_FS
+static const char *usermode_action[] = {
+       "ignored",
+       "warn",
+       "fixup",
+       "fixup+warn",
+       "signal",
+       "signal+warn"
+};
+
 static int alignment_proc_show(struct seq_file *m, void *v)
 {
        seq_printf(m, "User:\t\t%lu\n", ai_user);
@@ -749,7 +750,6 @@ do_alignment(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
        unsigned long instr = 0, instrptr;
        int (*handler)(unsigned long addr, unsigned long instr, struct pt_regs *regs);
        unsigned int type;
-       mm_segment_t fs;
        unsigned int fault;
        u16 tinstr = 0;
        int isize = 4;
@@ -760,16 +760,15 @@ do_alignment(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
 
        instrptr = instruction_pointer(regs);
 
-       fs = get_fs();
-       set_fs(KERNEL_DS);
        if (thumb_mode(regs)) {
-               fault = __get_user(tinstr, (u16 *)(instrptr & ~1));
+               u16 *ptr = (u16 *)(instrptr & ~1);
+               fault = probe_kernel_address(ptr, tinstr);
                if (!fault) {
                        if (cpu_architecture() >= CPU_ARCH_ARMv7 &&
                            IS_T32(tinstr)) {
                                /* Thumb-2 32-bit */
                                u16 tinst2 = 0;
-                               fault = __get_user(tinst2, (u16 *)(instrptr+2));
+                               fault = probe_kernel_address(ptr + 1, tinst2);
                                instr = (tinstr << 16) | tinst2;
                                thumb2_32b = 1;
                        } else {
@@ -778,8 +777,7 @@ do_alignment(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
                        }
                }
        } else
-               fault = __get_user(instr, (u32 *)instrptr);
-       set_fs(fs);
+               fault = probe_kernel_address(instrptr, instr);
 
        if (fault) {
                type = TYPE_FAULT;
@@ -815,6 +813,8 @@ do_alignment(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
                break;
 
        case 0x04000000:        /* ldr or str immediate */
+               if (COND_BITS(instr) == 0xf0000000) /* NEON VLDn, VSTn */
+                       goto bad;
                offset.un = OFFSET_BITS(instr);
                handler = do_alignment_ldrstr;
                break;