ARM: fix build error in arch/arm/kernel/process.c
[safe/jmp/linux-2.6] / arch / arm / mm / alignment.c
index 81f4a8a..a2ab51f 100644 (file)
@@ -3,7 +3,7 @@
  *
  *  Copyright (C) 1995  Linus Torvalds
  *  Modifications for ARM processor (c) 1995-2001 Russell King
- *  Thumb aligment fault fixups (c) 2004 MontaVista Software, Inc.
+ *  Thumb alignment fault fixups (c) 2004 MontaVista Software, Inc.
  *  - Adapted from gdb/sim/arm/thumbemu.c -- Thumb instruction emulation.
  *    Copyright (C) 1996, Cygnus Software Technologies Ltd.
  *
  * it under the terms of the GNU General Public License version 2 as
  * published by the Free Software Foundation.
  */
-#include <linux/config.h>
+#include <linux/moduleparam.h>
 #include <linux/compiler.h>
 #include <linux/kernel.h>
 #include <linux/errno.h>
 #include <linux/string.h>
-#include <linux/ptrace.h>
 #include <linux/proc_fs.h>
 #include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/uaccess.h>
 
-#include <asm/uaccess.h>
 #include <asm/unaligned.h>
 
 #include "fault.h"
@@ -45,7 +45,7 @@
 
 #define LDST_P_EQ_U(i) ((((i) ^ ((i) >> 1)) & (1 << 23)) == 0)
 
-#define LDSTH_I_BIT(i) (i & (1 << 22))         /* half-word immed      */
+#define LDSTHD_I_BIT(i)        (i & (1 << 22))         /* double/half-word immed */
 #define LDM_S_BIT(i)   (i & (1 << 22))         /* write CPSR from SPSR */
 
 #define RN_BITS(i)     ((i >> 16) & 15)        /* Rn                   */
 #define SHIFT_ASR      0x40
 #define SHIFT_RORRRX   0x60
 
+#define BAD_INSTR      0xdeadc0de
+
+/* Thumb-2 32 bit format per ARMv7 DDI0406A A6.3, either f800h,e800h,f800h */
+#define IS_T32(hi16) \
+       (((hi16) & 0xe000) == 0xe000 && ((hi16) & 0x1800))
+
 static unsigned long ai_user;
 static unsigned long ai_sys;
 static unsigned long ai_skipped;
 static unsigned long ai_half;
 static unsigned long ai_word;
+static unsigned long ai_dword;
 static unsigned long ai_multi;
 static int ai_usermode;
 
+core_param(alignment, ai_usermode, int, 0600);
+
+#define UM_WARN                (1 << 0)
+#define UM_FIXUP       (1 << 1)
+#define UM_SIGNAL      (1 << 2)
+
 #ifdef CONFIG_PROC_FS
 static const char *usermode_action[] = {
        "ignored",
@@ -93,6 +106,8 @@ proc_alignment_read(char *page, char **start, off_t off, int count, int *eof,
        p += sprintf(p, "Skipped:\t%lu\n", ai_skipped);
        p += sprintf(p, "Half:\t\t%lu\n", ai_half);
        p += sprintf(p, "Word:\t\t%lu\n", ai_word);
+       if (cpu_architecture() >= CPU_ARCH_ARMv5TE)
+               p += sprintf(p, "DWord:\t\t%lu\n", ai_dword);
        p += sprintf(p, "Multi:\t\t%lu\n", ai_multi);
        p += sprintf(p, "User faults:\t%i (%s)\n", ai_usermode,
                        usermode_action[ai_usermode]);
@@ -108,7 +123,7 @@ proc_alignment_read(char *page, char **start, off_t off, int count, int *eof,
 }
 
 static int proc_alignment_write(struct file *file, const char __user *buffer,
-                              unsigned long count, void *data)
+                               unsigned long count, void *data)
 {
        char mode;
 
@@ -116,7 +131,7 @@ static int proc_alignment_write(struct file *file, const char __user *buffer,
                if (get_user(mode, buffer))
                        return -EFAULT;
                if (mode >= '0' && mode <= '5')
-                          ai_usermode = mode - '0';
+                       ai_usermode = mode - '0';
        }
        return count;
 }
@@ -147,17 +162,19 @@ union offset_union {
 
 #define __get8_unaligned_check(ins,val,addr,err)       \
        __asm__(                                        \
-       "1:     "ins"   %1, [%2], #1\n"                 \
+ ARM(  "1:     "ins"   %1, [%2], #1\n" )               \
+ THUMB(        "1:     "ins"   %1, [%2]\n"     )               \
+ THUMB(        "       add     %2, %2, #1\n"   )               \
        "2:\n"                                          \
-       "       .section .fixup,\"ax\"\n"               \
+       "       .pushsection .fixup,\"ax\"\n"           \
        "       .align  2\n"                            \
        "3:     mov     %0, #1\n"                       \
        "       b       2b\n"                           \
-       "       .previous\n"                            \
-       "       .section __ex_table,\"a\"\n"            \
+       "       .popsection\n"                          \
+       "       .pushsection __ex_table,\"a\"\n"        \
        "       .align  3\n"                            \
        "       .long   1b, 3b\n"                       \
-       "       .previous\n"                            \
+       "       .popsection\n"                          \
        : "=r" (err), "=&r" (val), "=r" (addr)          \
        : "0" (err), "2" (addr))
 
@@ -203,20 +220,22 @@ union offset_union {
        do {                                                    \
                unsigned int err = 0, v = val, a = addr;        \
                __asm__( FIRST_BYTE_16                          \
-               "1:     "ins"   %1, [%2], #1\n"                 \
+        ARM(   "1:     "ins"   %1, [%2], #1\n" )               \
+        THUMB( "1:     "ins"   %1, [%2]\n"     )               \
+        THUMB( "       add     %2, %2, #1\n"   )               \
                "       mov     %1, %1, "NEXT_BYTE"\n"          \
                "2:     "ins"   %1, [%2]\n"                     \
                "3:\n"                                          \
-               "       .section .fixup,\"ax\"\n"               \
+               "       .pushsection .fixup,\"ax\"\n"           \
                "       .align  2\n"                            \
                "4:     mov     %0, #1\n"                       \
                "       b       3b\n"                           \
-               "       .previous\n"                            \
-               "       .section __ex_table,\"a\"\n"            \
+               "       .popsection\n"                          \
+               "       .pushsection __ex_table,\"a\"\n"        \
                "       .align  3\n"                            \
                "       .long   1b, 4b\n"                       \
                "       .long   2b, 4b\n"                       \
-               "       .previous\n"                            \
+               "       .popsection\n"                          \
                : "=r" (err), "=&r" (v), "=&r" (a)              \
                : "0" (err), "1" (v), "2" (a));                 \
                if (err)                                        \
@@ -233,33 +252,39 @@ union offset_union {
        do {                                                    \
                unsigned int err = 0, v = val, a = addr;        \
                __asm__( FIRST_BYTE_32                          \
-               "1:     "ins"   %1, [%2], #1\n"                 \
+        ARM(   "1:     "ins"   %1, [%2], #1\n" )               \
+        THUMB( "1:     "ins"   %1, [%2]\n"     )               \
+        THUMB( "       add     %2, %2, #1\n"   )               \
                "       mov     %1, %1, "NEXT_BYTE"\n"          \
-               "2:     "ins"   %1, [%2], #1\n"                 \
+        ARM(   "2:     "ins"   %1, [%2], #1\n" )               \
+        THUMB( "2:     "ins"   %1, [%2]\n"     )               \
+        THUMB( "       add     %2, %2, #1\n"   )               \
                "       mov     %1, %1, "NEXT_BYTE"\n"          \
-               "3:     "ins"   %1, [%2], #1\n"                 \
+        ARM(   "3:     "ins"   %1, [%2], #1\n" )               \
+        THUMB( "3:     "ins"   %1, [%2]\n"     )               \
+        THUMB( "       add     %2, %2, #1\n"   )               \
                "       mov     %1, %1, "NEXT_BYTE"\n"          \
                "4:     "ins"   %1, [%2]\n"                     \
                "5:\n"                                          \
-               "       .section .fixup,\"ax\"\n"               \
+               "       .pushsection .fixup,\"ax\"\n"           \
                "       .align  2\n"                            \
                "6:     mov     %0, #1\n"                       \
                "       b       5b\n"                           \
-               "       .previous\n"                            \
-               "       .section __ex_table,\"a\"\n"            \
+               "       .popsection\n"                          \
+               "       .pushsection __ex_table,\"a\"\n"        \
                "       .align  3\n"                            \
                "       .long   1b, 6b\n"                       \
                "       .long   2b, 6b\n"                       \
                "       .long   3b, 6b\n"                       \
                "       .long   4b, 6b\n"                       \
-               "       .previous\n"                            \
+               "       .popsection\n"                          \
                : "=r" (err), "=&r" (v), "=&r" (a)              \
                : "0" (err), "1" (v), "2" (a));                 \
                if (err)                                        \
                        goto fault;                             \
        } while (0)
 
-#define put32_unaligned_check(val,addr)         \
+#define put32_unaligned_check(val,addr) \
        __put32_unaligned_check("strb", val, addr)
 
 #define put32t_unaligned_check(val,addr) \
@@ -283,12 +308,6 @@ do_alignment_ldrhstrh(unsigned long addr, unsigned long instr, struct pt_regs *r
 {
        unsigned int rd = RD_BITS(instr);
 
-       if ((instr & 0x01f00ff0) == 0x01000090)
-               goto swp;
-
-       if ((instr & 0x90) != 0x90 || (instr & 0x60) == 0)
-               goto bad;
-
        ai_half += 1;
 
        if (user_mode(regs))
@@ -309,25 +328,76 @@ do_alignment_ldrhstrh(unsigned long addr, unsigned long instr, struct pt_regs *r
        return TYPE_LDST;
 
  user:
-       if (LDST_L_BIT(instr)) {
-               unsigned long val;
-               get16t_unaligned_check(val, addr);
+       if (LDST_L_BIT(instr)) {
+               unsigned long val;
+               get16t_unaligned_check(val, addr);
 
-               /* signed half-word? */
-               if (instr & 0x40)
-                       val = (signed long)((signed short) val);
+               /* signed half-word? */
+               if (instr & 0x40)
+                       val = (signed long)((signed short) val);
 
-               regs->uregs[rd] = val;
-       } else
-               put16t_unaligned_check(regs->uregs[rd], addr);
+               regs->uregs[rd] = val;
+       } else
+               put16t_unaligned_check(regs->uregs[rd], addr);
 
-       return TYPE_LDST;
+       return TYPE_LDST;
 
- swp:
-       printk(KERN_ERR "Alignment trap: not handling swp instruction\n");
+ fault:
+       return TYPE_FAULT;
+}
+
+static int
+do_alignment_ldrdstrd(unsigned long addr, unsigned long instr,
+                     struct pt_regs *regs)
+{
+       unsigned int rd = RD_BITS(instr);
+       unsigned int rd2;
+       int load;
+
+       if ((instr & 0xfe000000) == 0xe8000000) {
+               /* ARMv7 Thumb-2 32-bit LDRD/STRD */
+               rd2 = (instr >> 8) & 0xf;
+               load = !!(LDST_L_BIT(instr));
+       } else if (((rd & 1) == 1) || (rd == 14))
+               goto bad;
+       else {
+               load = ((instr & 0xf0) == 0xd0);
+               rd2 = rd + 1;
+       }
+
+       ai_dword += 1;
+
+       if (user_mode(regs))
+               goto user;
+
+       if (load) {
+               unsigned long val;
+               get32_unaligned_check(val, addr);
+               regs->uregs[rd] = val;
+               get32_unaligned_check(val, addr + 4);
+               regs->uregs[rd2] = val;
+       } else {
+               put32_unaligned_check(regs->uregs[rd], addr);
+               put32_unaligned_check(regs->uregs[rd2], addr + 4);
+       }
+
+       return TYPE_LDST;
+
+ user:
+       if (load) {
+               unsigned long val;
+               get32t_unaligned_check(val, addr);
+               regs->uregs[rd] = val;
+               get32t_unaligned_check(val, addr + 4);
+               regs->uregs[rd2] = val;
+       } else {
+               put32t_unaligned_check(regs->uregs[rd], addr);
+               put32t_unaligned_check(regs->uregs[rd2], addr + 4);
+       }
+
+       return TYPE_LDST;
  bad:
        return TYPE_ERROR;
-
  fault:
        return TYPE_FAULT;
 }
@@ -405,7 +475,7 @@ do_alignment_ldmstm(unsigned long addr, unsigned long instr, struct pt_regs *reg
        if (LDST_P_EQ_U(instr)) /* U = P */
                eaddr += 4;
 
-       /* 
+       /*
         * For alignment faults on the ARM922T/ARM920T the MMU  makes
         * the FSR (and hence addr) equal to the updated base address
         * of the multiple access rather than the restored value.
@@ -532,7 +602,7 @@ thumb2arm(u16 tinstr)
        /* 6.5.1 Format 3: */
        case 0x4800 >> 11:                              /* 7.1.28 LDR(3) */
                /* NOTE: This case is not technically possible. We're
-                *       loading 32-bit memory data via PC relative
+                *       loading 32-bit memory data via PC relative
                 *       addressing mode. So we can and should eliminate
                 *       this case. But I'll leave it here for now.
                 */
@@ -575,10 +645,74 @@ thumb2arm(u16 tinstr)
                /* Else fall through for illegal instruction case */
 
        default:
-               return 0xdeadc0de;
+               return BAD_INSTR;
        }
 }
 
+/*
+ * Convert Thumb-2 32 bit LDM, STM, LDRD, STRD to equivalent instruction
+ * handlable by ARM alignment handler, also find the corresponding handler,
+ * so that we can reuse ARM userland alignment fault fixups for Thumb.
+ *
+ * @pinstr: original Thumb-2 instruction; returns new handlable instruction
+ * @regs: register context.
+ * @poffset: return offset from faulted addr for later writeback
+ *
+ * NOTES:
+ * 1. Comments below refer to ARMv7 DDI0406A Thumb Instruction sections.
+ * 2. Register name Rt from ARMv7 is same as Rd from ARMv6 (Rd is Rt)
+ */
+static void *
+do_alignment_t32_to_handler(unsigned long *pinstr, struct pt_regs *regs,
+                           union offset_union *poffset)
+{
+       unsigned long instr = *pinstr;
+       u16 tinst1 = (instr >> 16) & 0xffff;
+       u16 tinst2 = instr & 0xffff;
+       poffset->un = 0;
+
+       switch (tinst1 & 0xffe0) {
+       /* A6.3.5 Load/Store multiple */
+       case 0xe880:            /* STM/STMIA/STMEA,LDM/LDMIA, PUSH/POP T2 */
+       case 0xe8a0:            /* ...above writeback version */
+       case 0xe900:            /* STMDB/STMFD, LDMDB/LDMEA */
+       case 0xe920:            /* ...above writeback version */
+               /* no need offset decision since handler calculates it */
+               return do_alignment_ldmstm;
+
+       case 0xf840:            /* POP/PUSH T3 (single register) */
+               if (RN_BITS(instr) == 13 && (tinst2 & 0x09ff) == 0x0904) {
+                       u32 L = !!(LDST_L_BIT(instr));
+                       const u32 subset[2] = {
+                               0xe92d0000,     /* STMDB sp!,{registers} */
+                               0xe8bd0000,     /* LDMIA sp!,{registers} */
+                       };
+                       *pinstr = subset[L] | (1<<RD_BITS(instr));
+                       return do_alignment_ldmstm;
+               }
+               /* Else fall through for illegal instruction case */
+               break;
+
+       /* A6.3.6 Load/store double, STRD/LDRD(immed, lit, reg) */
+       case 0xe860:
+       case 0xe960:
+       case 0xe8e0:
+       case 0xe9e0:
+               poffset->un = (tinst2 & 0xff) << 2;
+       case 0xe940:
+       case 0xe9c0:
+               return do_alignment_ldrdstrd;
+
+       /*
+        * No need to handle load/store instructions up to word size
+        * since ARMv6 and later CPUs can perform unaligned accesses.
+        */
+       default:
+               break;
+       }
+       return NULL;
+}
+
 static int
 do_alignment(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
 {
@@ -589,22 +723,35 @@ do_alignment(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
        mm_segment_t fs;
        unsigned int fault;
        u16 tinstr = 0;
+       int isize = 4;
+       int thumb2_32b = 0;
 
        instrptr = instruction_pointer(regs);
 
        fs = get_fs();
        set_fs(KERNEL_DS);
-       if thumb_mode(regs) {
+       if (thumb_mode(regs)) {
                fault = __get_user(tinstr, (u16 *)(instrptr & ~1));
-               if (!(fault))
-                       instr = thumb2arm(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));
+                               instr = (tinstr << 16) | tinst2;
+                               thumb2_32b = 1;
+                       } else {
+                               isize = 2;
+                               instr = thumb2arm(tinstr);
+                       }
+               }
        } else
                fault = __get_user(instr, (u32 *)instrptr);
        set_fs(fs);
 
        if (fault) {
                type = TYPE_FAULT;
-               goto bad_or_fault;
+               goto bad_or_fault;
        }
 
        if (user_mode(regs))
@@ -614,15 +761,25 @@ do_alignment(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
 
  fixup:
 
-       regs->ARM_pc += thumb_mode(regs) ? 2 : 4;
+       regs->ARM_pc += isize;
 
        switch (CODING_BITS(instr)) {
-       case 0x00000000:        /* ldrh or strh */
-               if (LDSTH_I_BIT(instr))
+       case 0x00000000:        /* 3.13.4 load/store instruction extensions */
+               if (LDSTHD_I_BIT(instr))
                        offset.un = (instr & 0xf00) >> 4 | (instr & 15);
                else
                        offset.un = regs->uregs[RM_BITS(instr)];
-               handler = do_alignment_ldrhstrh;
+
+               if ((instr & 0x000000f0) == 0x000000b0 || /* LDRH, STRH */
+                   (instr & 0x001000f0) == 0x001000f0)   /* LDRSH */
+                       handler = do_alignment_ldrhstrh;
+               else if ((instr & 0x001000f0) == 0x000000d0 || /* LDRD */
+                        (instr & 0x001000f0) == 0x000000f0)   /* STRD */
+                       handler = do_alignment_ldrdstrd;
+               else if ((instr & 0x01f00ff0) == 0x01000090) /* SWP */
+                       goto swp;
+               else
+                       goto bad;
                break;
 
        case 0x04000000:        /* ldr or str immediate */
@@ -663,18 +820,25 @@ do_alignment(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
                handler = do_alignment_ldrstr;
                break;
 
-       case 0x08000000:        /* ldm or stm */
-               handler = do_alignment_ldmstm;
+       case 0x08000000:        /* ldm or stm, or thumb-2 32bit instruction */
+               if (thumb2_32b)
+                       handler = do_alignment_t32_to_handler(&instr, regs, &offset);
+               else
+                       handler = do_alignment_ldmstm;
                break;
 
        default:
                goto bad;
        }
 
+       if (!handler)
+               goto bad;
        type = handler(addr, instr, regs);
 
-       if (type == TYPE_ERROR || type == TYPE_FAULT)
+       if (type == TYPE_ERROR || type == TYPE_FAULT) {
+               regs->ARM_pc -= isize;
                goto bad_or_fault;
+       }
 
        if (type == TYPE_LDST)
                do_alignment_finish_ldst(addr, instr, regs, offset);
@@ -684,39 +848,41 @@ do_alignment(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
  bad_or_fault:
        if (type == TYPE_ERROR)
                goto bad;
-       regs->ARM_pc -= thumb_mode(regs) ? 2 : 4;
        /*
         * We got a fault - fix it up, or die.
         */
-       do_bad_area(current, current->mm, addr, fsr, regs);
+       do_bad_area(addr, fsr, regs);
        return 0;
 
+ swp:
+       printk(KERN_ERR "Alignment trap: not handling swp instruction\n");
+
  bad:
        /*
         * Oops, we didn't handle the instruction.
         */
        printk(KERN_ERR "Alignment trap: not handling instruction "
                "%0*lx at [<%08lx>]\n",
-               thumb_mode(regs) ? 4 : 8,
-               thumb_mode(regs) ? tinstr : instr, instrptr);
+               isize << 1,
+               isize == 2 ? tinstr : instr, instrptr);
        ai_skipped += 1;
        return 1;
 
  user:
        ai_user += 1;
 
-       if (ai_usermode & 1)
+       if (ai_usermode & UM_WARN)
                printk("Alignment trap: %s (%d) PC=0x%08lx Instr=0x%0*lx "
                       "Address=0x%08lx FSR 0x%03x\n", current->comm,
-                       current->pid, instrptr,
-                       thumb_mode(regs) ? 4 : 8,
-                       thumb_mode(regs) ? tinstr : instr,
+                       task_pid_nr(current), instrptr,
+                       isize << 1,
+                       isize == 2 ? tinstr : instr,
                        addr, fsr);
 
-       if (ai_usermode & 2)
+       if (ai_usermode & UM_FIXUP)
                goto fixup;
 
-       if (ai_usermode & 4)
+       if (ai_usermode & UM_SIGNAL)
                force_sig(SIGBUS, current);
        else
                set_cr(cr_no_alignment);
@@ -735,11 +901,7 @@ static int __init alignment_init(void)
 #ifdef CONFIG_PROC_FS
        struct proc_dir_entry *res;
 
-       res = proc_mkdir("cpu", NULL);
-       if (!res)
-               return -ENOMEM;
-
-       res = create_proc_entry("alignment", S_IWUSR | S_IRUGO, res);
+       res = create_proc_entry("cpu/alignment", S_IWUSR | S_IRUGO, NULL);
        if (!res)
                return -ENOMEM;
 
@@ -747,6 +909,22 @@ static int __init alignment_init(void)
        res->write_proc = proc_alignment_write;
 #endif
 
+       /*
+        * ARMv6 and later CPUs can perform unaligned accesses for
+        * most single load and store instructions up to word size.
+        * LDM, STM, LDRD and STRD still need to be handled.
+        *
+        * Ignoring the alignment fault is not an option on these
+        * CPUs since we spin re-faulting the instruction without
+        * making any progress.
+        */
+       if (cpu_architecture() >= CPU_ARCH_ARMv6 && (cr_alignment & CR_U)) {
+               cr_alignment &= ~CR_A;
+               cr_no_alignment &= ~CR_A;
+               set_cr(cr_alignment);
+               ai_usermode = UM_FIXUP;
+       }
+
        hook_fault_code(1, do_alignment, SIGILL, "alignment exception");
        hook_fault_code(3, do_alignment, SIGILL, "alignment exception");