linux 2.6.16.38 w/ vs2.0.3-rc1
[linux-2.6.git] / arch / powerpc / kernel / align.c
index 4734b5d..faaec9c 100644 (file)
@@ -35,19 +35,17 @@ struct aligninfo {
 
 #define INVALID        { 0, 0 }
 
-/* Bits in the flags field */
-#define LD     0       /* load */
-#define ST     1       /* store */
-#define        SE      2       /* sign-extend value */
-#define F      4       /* to/from fp regs */
-#define U      8       /* update index register */
-#define M      0x10    /* multiple load/store */
-#define SW     0x20    /* byte swap */
-#define S      0x40    /* single-precision fp or... */
-#define SX     0x40    /* ... byte count in XER */
+#define LD     1       /* load */
+#define ST     2       /* store */
+#define        SE      4       /* sign-extend value */
+#define F      8       /* to/from fp regs */
+#define U      0x10    /* update index register */
+#define M      0x20    /* multiple load/store */
+#define SW     0x40    /* byte swap int or ... */
+#define S      0x40    /* ... single-precision fp */
+#define SX     0x40    /* byte count in XER */
 #define HARD   0x80    /* string, stwcx. */
 
-/* DSISR bits reported for a DCBZ instruction: */
 #define DCBZ   0x5f    /* 8xx/82xx dcbz faults when cache not enabled */
 
 #define SWAP(a, b)     (t = (a), (a) = (b), (b) = t)
@@ -258,16 +256,12 @@ static int emulate_dcbz(struct pt_regs *regs, unsigned char __user *addr)
 #define REG_BYTE(rp, i)                *((u8 *)(rp) + (i))
 #endif
 
-#define SWIZ_PTR(p)            ((unsigned char __user *)((p) ^ swiz))
-
 static int emulate_multiple(struct pt_regs *regs, unsigned char __user *addr,
                            unsigned int reg, unsigned int nb,
-                           unsigned int flags, unsigned int instr,
-                           unsigned long swiz)
+                           unsigned int flags, unsigned int instr)
 {
        unsigned long *rptr;
-       unsigned int nb0, i, bswiz;
-       unsigned long p;
+       unsigned int nb0, i;
 
        /*
         * We do not try to emulate 8 bytes multiple as they aren't really
@@ -286,12 +280,9 @@ static int emulate_multiple(struct pt_regs *regs, unsigned char __user *addr,
                        if (nb == 0)
                                return 1;
                } else {
-                       unsigned long pc = regs->nip ^ (swiz & 4);
-
-                       if (__get_user(instr, (unsigned int __user *)pc))
+                       if (__get_user(instr,
+                                      (unsigned int __user *)regs->nip))
                                return -EFAULT;
-                       if (swiz == 0 && (flags & SW))
-                               instr = cpu_to_le32(instr);
                        nb = (instr >> 11) & 0x1f;
                        if (nb == 0)
                                nb = 32;
@@ -309,10 +300,7 @@ static int emulate_multiple(struct pt_regs *regs, unsigned char __user *addr,
                return -EFAULT; /* bad address */
 
        rptr = &regs->gpr[reg];
-       p = (unsigned long) addr;
-       bswiz = (flags & SW)? 3: 0;
-
-       if (!(flags & ST)) {
+       if (flags & LD) {
                /*
                 * This zeroes the top 4 bytes of the affected registers
                 * in 64-bit mode, and also zeroes out any remaining
@@ -323,28 +311,26 @@ static int emulate_multiple(struct pt_regs *regs, unsigned char __user *addr,
                        memset(&regs->gpr[0], 0,
                               ((nb0 + 3) / 4) * sizeof(unsigned long));
 
-               for (i = 0; i < nb; ++i, ++p)
-                       if (__get_user(REG_BYTE(rptr, i ^ bswiz), SWIZ_PTR(p)))
+               for (i = 0; i < nb; ++i)
+                       if (__get_user(REG_BYTE(rptr, i), addr + i))
                                return -EFAULT;
                if (nb0 > 0) {
                        rptr = &regs->gpr[0];
                        addr += nb;
-                       for (i = 0; i < nb0; ++i, ++p)
-                               if (__get_user(REG_BYTE(rptr, i ^ bswiz),
-                                              SWIZ_PTR(p)))
+                       for (i = 0; i < nb0; ++i)
+                               if (__get_user(REG_BYTE(rptr, i), addr + i))
                                        return -EFAULT;
                }
 
        } else {
-               for (i = 0; i < nb; ++i, ++p)
-                       if (__put_user(REG_BYTE(rptr, i ^ bswiz), SWIZ_PTR(p)))
+               for (i = 0; i < nb; ++i)
+                       if (__put_user(REG_BYTE(rptr, i), addr + i))
                                return -EFAULT;
                if (nb0 > 0) {
                        rptr = &regs->gpr[0];
                        addr += nb;
-                       for (i = 0; i < nb0; ++i, ++p)
-                               if (__put_user(REG_BYTE(rptr, i ^ bswiz),
-                                              SWIZ_PTR(p)))
+                       for (i = 0; i < nb0; ++i)
+                               if (__put_user(REG_BYTE(rptr, i), addr + i))
                                        return -EFAULT;
                }
        }
@@ -366,7 +352,7 @@ int fix_alignment(struct pt_regs *regs)
        unsigned int reg, areg;
        unsigned int dsisr;
        unsigned char __user *addr;
-       unsigned long p, swiz;
+       unsigned char __user *p;
        int ret, t;
        union {
                u64 ll;
@@ -394,15 +380,11 @@ int fix_alignment(struct pt_regs *regs)
         * let's make one up from the instruction
         */
        if (cpu_has_feature(CPU_FTR_NODSISRALIGN)) {
-               unsigned long pc = regs->nip;
-
-               if (cpu_has_feature(CPU_FTR_PPC_LE) && (regs->msr & MSR_LE))
-                       pc ^= 4;
-               if (unlikely(__get_user(instr, (unsigned int __user *)pc)))
+               unsigned int real_instr;
+               if (unlikely(__get_user(real_instr,
+                                       (unsigned int __user *)regs->nip)))
                        return -EFAULT;
-               if (cpu_has_feature(CPU_FTR_REAL_LE) && (regs->msr & MSR_LE))
-                       instr = cpu_to_le32(instr);
-               dsisr = make_dsisr(instr);
+               dsisr = make_dsisr(real_instr);
        }
 
        /* extract the operation and registers from the dsisr */
@@ -415,24 +397,6 @@ int fix_alignment(struct pt_regs *regs)
        nb = aligninfo[instr].len;
        flags = aligninfo[instr].flags;
 
-       /* Byteswap little endian loads and stores */
-       swiz = 0;
-       if (regs->msr & MSR_LE) {
-               flags ^= SW;
-               /*
-                * So-called "PowerPC little endian" mode works by
-                * swizzling addresses rather than by actually doing
-                * any byte-swapping.  To emulate this, we XOR each
-                * byte address with 7.  We also byte-swap, because
-                * the processor's address swizzling depends on the
-                * operand size (it xors the address with 7 for bytes,
-                * 6 for halfwords, 4 for words, 0 for doublewords) but
-                * we will xor with 7 and load/store each byte separately.
-                */
-               if (cpu_has_feature(CPU_FTR_PPC_LE))
-                       swiz = 7;
-       }
-
        /* DAR has the operand effective address */
        addr = (unsigned char __user *)regs->dar;
 
@@ -448,8 +412,7 @@ int fix_alignment(struct pt_regs *regs)
         * function
         */
        if (flags & M)
-               return emulate_multiple(regs, addr, reg, nb,
-                                       flags, instr, swiz);
+               return emulate_multiple(regs, addr, reg, nb, flags, instr);
 
        /* Verify the address of the operand */
        if (unlikely(user_mode(regs) &&
@@ -468,71 +431,51 @@ int fix_alignment(struct pt_regs *regs)
        /* If we are loading, get the data from user space, else
         * get it from register values
         */
-       if (!(flags & ST)) {
+       if (flags & LD) {
                data.ll = 0;
                ret = 0;
-               p = (unsigned long) addr;
+               p = addr;
                switch (nb) {
                case 8:
-                       ret |= __get_user(data.v[0], SWIZ_PTR(p++));
-                       ret |= __get_user(data.v[1], SWIZ_PTR(p++));
-                       ret |= __get_user(data.v[2], SWIZ_PTR(p++));
-                       ret |= __get_user(data.v[3], SWIZ_PTR(p++));
+                       ret |= __get_user(data.v[0], p++);
+                       ret |= __get_user(data.v[1], p++);
+                       ret |= __get_user(data.v[2], p++);
+                       ret |= __get_user(data.v[3], p++);
                case 4:
-                       ret |= __get_user(data.v[4], SWIZ_PTR(p++));
-                       ret |= __get_user(data.v[5], SWIZ_PTR(p++));
+                       ret |= __get_user(data.v[4], p++);
+                       ret |= __get_user(data.v[5], p++);
                case 2:
-                       ret |= __get_user(data.v[6], SWIZ_PTR(p++));
-                       ret |= __get_user(data.v[7], SWIZ_PTR(p++));
+                       ret |= __get_user(data.v[6], p++);
+                       ret |= __get_user(data.v[7], p++);
                        if (unlikely(ret))
                                return -EFAULT;
                }
-       } else if (flags & F) {
+       } else if (flags & F)
                data.dd = current->thread.fpr[reg];
-               if (flags & S) {
-                       /* Single-precision FP store requires conversion... */
-#ifdef CONFIG_PPC_FPU
-                       preempt_disable();
-                       enable_kernel_fp();
-                       cvt_df(&data.dd, (float *)&data.v[4], &current->thread);
-                       preempt_enable();
-#else
-                       return 0;
-#endif
-               }
-       } else
+       else
                data.ll = regs->gpr[reg];
 
-       if (flags & SW) {
-               switch (nb) {
-               case 8:
-                       SWAP(data.v[0], data.v[7]);
-                       SWAP(data.v[1], data.v[6]);
-                       SWAP(data.v[2], data.v[5]);
-                       SWAP(data.v[3], data.v[4]);
-                       break;
-               case 4:
-                       SWAP(data.v[4], data.v[7]);
-                       SWAP(data.v[5], data.v[6]);
-                       break;
-               case 2:
-                       SWAP(data.v[6], data.v[7]);
-                       break;
-               }
-       }
-
-       /* Perform other misc operations like sign extension
+       /* Perform other misc operations like sign extension, byteswap,
         * or floating point single precision conversion
         */
-       switch (flags & ~(U|SW)) {
+       switch (flags & ~U) {
        case LD+SE:     /* sign extend */
                if ( nb == 2 )
                        data.ll = data.x16.low16;
                else    /* nb must be 4 */
                        data.ll = data.x32.low32;
                break;
+       case LD+S:      /* byte-swap */
+       case ST+S:
+               if (nb == 2) {
+                       SWAP(data.v[6], data.v[7]);
+               } else {
+                       SWAP(data.v[4], data.v[7]);
+                       SWAP(data.v[5], data.v[6]);
+               }
+               break;
 
-       /* Single-precision FP load requires conversion... */
+       /* Single-precision FP load and store require conversions... */
        case LD+F+S:
 #ifdef CONFIG_PPC_FPU
                preempt_disable();
@@ -541,6 +484,16 @@ int fix_alignment(struct pt_regs *regs)
                preempt_enable();
 #else
                return 0;
+#endif
+               break;
+       case ST+F+S:
+#ifdef CONFIG_PPC_FPU
+               preempt_disable();
+               enable_kernel_fp();
+               cvt_df(&data.dd, (float *)&data.v[4], &current->thread);
+               preempt_enable();
+#else
+               return 0;
 #endif
                break;
        }
@@ -548,19 +501,19 @@ int fix_alignment(struct pt_regs *regs)
        /* Store result to memory or update registers */
        if (flags & ST) {
                ret = 0;
-               p = (unsigned long) addr;
+               p = addr;
                switch (nb) {
                case 8:
-                       ret |= __put_user(data.v[0], SWIZ_PTR(p++));
-                       ret |= __put_user(data.v[1], SWIZ_PTR(p++));
-                       ret |= __put_user(data.v[2], SWIZ_PTR(p++));
-                       ret |= __put_user(data.v[3], SWIZ_PTR(p++));
+                       ret |= __put_user(data.v[0], p++);
+                       ret |= __put_user(data.v[1], p++);
+                       ret |= __put_user(data.v[2], p++);
+                       ret |= __put_user(data.v[3], p++);
                case 4:
-                       ret |= __put_user(data.v[4], SWIZ_PTR(p++));
-                       ret |= __put_user(data.v[5], SWIZ_PTR(p++));
+                       ret |= __put_user(data.v[4], p++);
+                       ret |= __put_user(data.v[5], p++);
                case 2:
-                       ret |= __put_user(data.v[6], SWIZ_PTR(p++));
-                       ret |= __put_user(data.v[7], SWIZ_PTR(p++));
+                       ret |= __put_user(data.v[6], p++);
+                       ret |= __put_user(data.v[7], p++);
                }
                if (unlikely(ret))
                        return -EFAULT;