]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - arch/sh/kernel/traps_32.c
sh: arch/sh/kernel/traps_32.c needs asm/fpu.h
[linux-2.6-omap-h63xx.git] / arch / sh / kernel / traps_32.c
index 2e58f7a6b746ba84a3534e33c4bab24e8e725e06..e08b3bfeb6566fc430e5a50c0ed93cde07eedf3c 100644 (file)
@@ -25,6 +25,7 @@
 #include <linux/limits.h>
 #include <asm/system.h>
 #include <asm/uaccess.h>
+#include <asm/fpu.h>
 
 #ifdef CONFIG_SH_KGDB
 #include <asm/kgdb.h>
@@ -147,6 +148,36 @@ static int die_if_no_fixup(const char * str, struct pt_regs * regs, long err)
        return -EFAULT;
 }
 
+static inline void sign_extend(unsigned int count, unsigned char *dst)
+{
+#ifdef __LITTLE_ENDIAN__
+       if ((count == 1) && dst[0] & 0x80) {
+               dst[1] = 0xff;
+               dst[2] = 0xff;
+               dst[3] = 0xff;
+       }
+       if ((count == 2) && dst[1] & 0x80) {
+               dst[2] = 0xff;
+               dst[3] = 0xff;
+       }
+#else
+       if ((count == 1) && dst[3] & 0x80) {
+               dst[2] = 0xff;
+               dst[1] = 0xff;
+               dst[0] = 0xff;
+       }
+       if ((count == 2) && dst[2] & 0x80) {
+               dst[1] = 0xff;
+               dst[0] = 0xff;
+       }
+#endif
+}
+
+static struct mem_access user_mem_access = {
+       copy_from_user,
+       copy_to_user,
+};
+
 /*
  * handle an instruction that does an unaligned memory access by emulating the
  * desired behaviour
@@ -154,7 +185,8 @@ static int die_if_no_fixup(const char * str, struct pt_regs * regs, long err)
  *   (if that instruction is in a branch delay slot)
  * - return 0 if emulation okay, -EFAULT on existential error
  */
-static int handle_unaligned_ins(u16 instruction, struct pt_regs *regs)
+static int handle_unaligned_ins(opcode_t instruction, struct pt_regs *regs,
+                               struct mem_access *ma)
 {
        int ret, index, count;
        unsigned long *rm, *rn;
@@ -178,25 +210,13 @@ static int handle_unaligned_ins(u16 instruction, struct pt_regs *regs)
                        dst = (unsigned char*) rn;
                        *(unsigned long*)dst = 0;
 
-#ifdef __LITTLE_ENDIAN__
-                       if (copy_from_user(dst, src, count))
-                               goto fetch_fault;
-
-                       if ((count == 2) && dst[1] & 0x80) {
-                               dst[2] = 0xff;
-                               dst[3] = 0xff;
-                       }
-#else
+#if !defined(__LITTLE_ENDIAN__)
                        dst += 4-count;
-
-                       if (__copy_user(dst, src, count))
+#endif
+                       if (ma->from(dst, src, count))
                                goto fetch_fault;
 
-                       if ((count == 2) && dst[2] & 0x80) {
-                               dst[0] = 0xff;
-                               dst[1] = 0xff;
-                       }
-#endif
+                       sign_extend(count, dst);
                } else {
                        /* to memory */
                        src = (unsigned char*) rm;
@@ -206,7 +226,7 @@ static int handle_unaligned_ins(u16 instruction, struct pt_regs *regs)
                        dst = (unsigned char*) *rn;
                        dst += regs->regs[0];
 
-                       if (copy_to_user(dst, src, count))
+                       if (ma->to(dst, src, count))
                                goto fetch_fault;
                }
                ret = 0;
@@ -217,7 +237,7 @@ static int handle_unaligned_ins(u16 instruction, struct pt_regs *regs)
                dst = (unsigned char*) *rn;
                dst += (instruction&0x000F)<<2;
 
-               if (copy_to_user(dst,src,4))
+               if (ma->to(dst, src, 4))
                        goto fetch_fault;
                ret = 0;
                break;
@@ -230,7 +250,7 @@ static int handle_unaligned_ins(u16 instruction, struct pt_regs *regs)
 #if !defined(__LITTLE_ENDIAN__)
                src += 4-count;
 #endif
-               if (copy_to_user(dst, src, count))
+               if (ma->to(dst, src, count))
                        goto fetch_fault;
                ret = 0;
                break;
@@ -241,7 +261,7 @@ static int handle_unaligned_ins(u16 instruction, struct pt_regs *regs)
                dst = (unsigned char*) rn;
                *(unsigned long*)dst = 0;
 
-               if (copy_from_user(dst,src,4))
+               if (ma->from(dst, src, 4))
                        goto fetch_fault;
                ret = 0;
                break;
@@ -253,25 +273,12 @@ static int handle_unaligned_ins(u16 instruction, struct pt_regs *regs)
                dst = (unsigned char*) rn;
                *(unsigned long*)dst = 0;
 
-#ifdef __LITTLE_ENDIAN__
-               if (copy_from_user(dst, src, count))
-                       goto fetch_fault;
-
-               if ((count == 2) && dst[1] & 0x80) {
-                       dst[2] = 0xff;
-                       dst[3] = 0xff;
-               }
-#else
+#if !defined(__LITTLE_ENDIAN__)
                dst += 4-count;
-
-               if (copy_from_user(dst, src, count))
-                       goto fetch_fault;
-
-               if ((count == 2) && dst[2] & 0x80) {
-                       dst[0] = 0xff;
-                       dst[1] = 0xff;
-               }
 #endif
+               if (ma->from(dst, src, count))
+                       goto fetch_fault;
+               sign_extend(count, dst);
                ret = 0;
                break;
 
@@ -285,7 +292,7 @@ static int handle_unaligned_ins(u16 instruction, struct pt_regs *regs)
                        dst = (unsigned char*) *rm; /* called Rn in the spec */
                        dst += (instruction&0x000F)<<1;
 
-                       if (copy_to_user(dst, src, 2))
+                       if (ma->to(dst, src, 2))
                                goto fetch_fault;
                        ret = 0;
                        break;
@@ -299,21 +306,9 @@ static int handle_unaligned_ins(u16 instruction, struct pt_regs *regs)
 #if !defined(__LITTLE_ENDIAN__)
                        dst += 2;
 #endif
-
-                       if (copy_from_user(dst, src, 2))
+                       if (ma->from(dst, src, 2))
                                goto fetch_fault;
-
-#ifdef __LITTLE_ENDIAN__
-                       if (dst[1] & 0x80) {
-                               dst[2] = 0xff;
-                               dst[3] = 0xff;
-                       }
-#else
-                       if (dst[2] & 0x80) {
-                               dst[0] = 0xff;
-                               dst[1] = 0xff;
-                       }
-#endif
+                       sign_extend(2, dst);
                        ret = 0;
                        break;
                }
@@ -332,11 +327,14 @@ static int handle_unaligned_ins(u16 instruction, struct pt_regs *regs)
  * emulate the instruction in the delay slot
  * - fetches the instruction from PC+2
  */
-static inline int handle_unaligned_delayslot(struct pt_regs *regs)
+static inline int handle_delayslot(struct pt_regs *regs,
+                                  opcode_t old_instruction,
+                                  struct mem_access *ma)
 {
-       u16 instruction;
+       opcode_t instruction;
+       void *addr = (void *)(regs->pc + instruction_size(old_instruction));
 
-       if (copy_from_user(&instruction, (u16 *)(regs->pc+2), 2)) {
+       if (copy_from_user(&instruction, addr, sizeof(instruction))) {
                /* the instruction-fetch faulted */
                if (user_mode(regs))
                        return -EFAULT;
@@ -346,7 +344,7 @@ static inline int handle_unaligned_delayslot(struct pt_regs *regs)
                    regs, 0);
        }
 
-       return handle_unaligned_ins(instruction,regs);
+       return handle_unaligned_ins(instruction, regs, ma);
 }
 
 /*
@@ -369,10 +367,11 @@ static inline int handle_unaligned_delayslot(struct pt_regs *regs)
  * XXX: SH-2A needs this too, but it needs an overhaul thanks to mixed 32-bit
  * opcodes..
  */
-#ifndef CONFIG_CPU_SH2A
+
 static int handle_unaligned_notify_count = 10;
 
-static int handle_unaligned_access(u16 instruction, struct pt_regs *regs)
+int handle_unaligned_access(opcode_t instruction, struct pt_regs *regs,
+                           struct mem_access *ma)
 {
        u_int rm;
        int ret, index;
@@ -387,7 +386,7 @@ static int handle_unaligned_access(u16 instruction, struct pt_regs *regs)
                printk(KERN_NOTICE "Fixing up unaligned userspace access "
                       "in \"%s\" pid=%d pc=0x%p ins=0x%04hx\n",
                       current->comm, task_pid_nr(current),
-                      (u16 *)regs->pc, instruction);
+                      (void *)regs->pc, instruction);
        }
 
        ret = -EFAULT;
@@ -395,19 +394,19 @@ static int handle_unaligned_access(u16 instruction, struct pt_regs *regs)
        case 0x0000:
                if (instruction==0x000B) {
                        /* rts */
-                       ret = handle_unaligned_delayslot(regs);
+                       ret = handle_delayslot(regs, instruction, ma);
                        if (ret==0)
                                regs->pc = regs->pr;
                }
                else if ((instruction&0x00FF)==0x0023) {
                        /* braf @Rm */
-                       ret = handle_unaligned_delayslot(regs);
+                       ret = handle_delayslot(regs, instruction, ma);
                        if (ret==0)
                                regs->pc += rm + 4;
                }
                else if ((instruction&0x00FF)==0x0003) {
                        /* bsrf @Rm */
-                       ret = handle_unaligned_delayslot(regs);
+                       ret = handle_delayslot(regs, instruction, ma);
                        if (ret==0) {
                                regs->pr = regs->pc + 4;
                                regs->pc += rm + 4;
@@ -428,13 +427,13 @@ static int handle_unaligned_access(u16 instruction, struct pt_regs *regs)
        case 0x4000:
                if ((instruction&0x00FF)==0x002B) {
                        /* jmp @Rm */
-                       ret = handle_unaligned_delayslot(regs);
+                       ret = handle_delayslot(regs, instruction, ma);
                        if (ret==0)
                                regs->pc = rm;
                }
                else if ((instruction&0x00FF)==0x000B) {
                        /* jsr @Rm */
-                       ret = handle_unaligned_delayslot(regs);
+                       ret = handle_delayslot(regs, instruction, ma);
                        if (ret==0) {
                                regs->pr = regs->pc + 4;
                                regs->pc = rm;
@@ -461,7 +460,7 @@ static int handle_unaligned_access(u16 instruction, struct pt_regs *regs)
                case 0x0B00: /* bf   lab - no delayslot*/
                        break;
                case 0x0F00: /* bf/s lab */
-                       ret = handle_unaligned_delayslot(regs);
+                       ret = handle_delayslot(regs, instruction, ma);
                        if (ret==0) {
 #if defined(CONFIG_CPU_SH4) || defined(CONFIG_SH7705_CACHE_32KB)
                                if ((regs->sr & 0x00000001) != 0)
@@ -474,7 +473,7 @@ static int handle_unaligned_access(u16 instruction, struct pt_regs *regs)
                case 0x0900: /* bt   lab - no delayslot */
                        break;
                case 0x0D00: /* bt/s lab */
-                       ret = handle_unaligned_delayslot(regs);
+                       ret = handle_delayslot(regs, instruction, ma);
                        if (ret==0) {
 #if defined(CONFIG_CPU_SH4) || defined(CONFIG_SH7705_CACHE_32KB)
                                if ((regs->sr & 0x00000001) == 0)
@@ -488,13 +487,13 @@ static int handle_unaligned_access(u16 instruction, struct pt_regs *regs)
                break;
 
        case 0xA000: /* bra label */
-               ret = handle_unaligned_delayslot(regs);
+               ret = handle_delayslot(regs, instruction, ma);
                if (ret==0)
                        regs->pc += SH_PC_12BIT_OFFSET(instruction);
                break;
 
        case 0xB000: /* bsr label */
-               ret = handle_unaligned_delayslot(regs);
+               ret = handle_delayslot(regs, instruction, ma);
                if (ret==0) {
                        regs->pr = regs->pc + 4;
                        regs->pc += SH_PC_12BIT_OFFSET(instruction);
@@ -505,12 +504,11 @@ static int handle_unaligned_access(u16 instruction, struct pt_regs *regs)
 
        /* handle non-delay-slot instruction */
  simple:
-       ret = handle_unaligned_ins(instruction,regs);
+       ret = handle_unaligned_ins(instruction, regs, ma);
        if (ret==0)
                regs->pc += instruction_size(instruction);
        return ret;
 }
-#endif /* CONFIG_CPU_SH2A */
 
 #ifdef CONFIG_CPU_HAS_SR_RB
 #define lookup_exception_vector(x)     \
@@ -538,10 +536,8 @@ asmlinkage void do_address_error(struct pt_regs *regs,
        unsigned long error_code = 0;
        mm_segment_t oldfs;
        siginfo_t info;
-#ifndef CONFIG_CPU_SH2A
-       u16 instruction;
+       opcode_t instruction;
        int tmp;
-#endif
 
        /* Intentional ifdef */
 #ifdef CONFIG_CPU_HAS_SR_RB
@@ -561,9 +557,9 @@ asmlinkage void do_address_error(struct pt_regs *regs,
                        goto uspace_segv;
                }
 
-#ifndef CONFIG_CPU_SH2A
                set_fs(USER_DS);
-               if (copy_from_user(&instruction, (u16 *)(regs->pc), 2)) {
+               if (copy_from_user(&instruction, (void *)(regs->pc),
+                                  sizeof(instruction))) {
                        /* Argh. Fault on the instruction itself.
                           This should never happen non-SMP
                        */
@@ -571,13 +567,12 @@ asmlinkage void do_address_error(struct pt_regs *regs,
                        goto uspace_segv;
                }
 
-               tmp = handle_unaligned_access(instruction, regs);
+               tmp = handle_unaligned_access(instruction, regs,
+                                             &user_mem_access);
                set_fs(oldfs);
 
                if (tmp==0)
                        return; /* sorted */
-#endif
-
 uspace_segv:
                printk(KERN_NOTICE "Sending SIGBUS to \"%s\" due to unaligned "
                       "access (PC %lx PR %lx)\n", current->comm, regs->pc,
@@ -592,9 +587,9 @@ uspace_segv:
                if (regs->pc & 1)
                        die("unaligned program counter", regs, error_code);
 
-#ifndef CONFIG_CPU_SH2A
                set_fs(KERNEL_DS);
-               if (copy_from_user(&instruction, (u16 *)(regs->pc), 2)) {
+               if (copy_from_user(&instruction, (void *)(regs->pc),
+                                  sizeof(instruction))) {
                        /* Argh. Fault on the instruction itself.
                           This should never happen non-SMP
                        */
@@ -602,14 +597,8 @@ uspace_segv:
                        die("insn faulting in do_address_error", regs, 0);
                }
 
-               handle_unaligned_access(instruction, regs);
+               handle_unaligned_access(instruction, regs, &user_mem_access);
                set_fs(oldfs);
-#else
-               printk(KERN_NOTICE "Killing process \"%s\" due to unaligned "
-                      "access\n", current->comm);
-
-               force_sig(SIGSEGV, current);
-#endif
        }
 }