]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - arch/frv/kernel/traps.c
FRV: Add support for emulation of userspace atomic ops [try #2]
[linux-2.6-omap-h63xx.git] / arch / frv / kernel / traps.c
index 9eb84b2e6abc498362f57762a1e5e8e3c0c24b58..2f7e66877f3bd17c6b078d00f058f9c1959ff081 100644 (file)
@@ -9,7 +9,6 @@
  * 2 of the License, or (at your option) any later version.
  */
 
-#include <linux/config.h>
 #include <linux/sched.h>
 #include <linux/signal.h>
 #include <linux/kernel.h>
@@ -21,6 +20,7 @@
 #include <linux/init.h>
 #include <linux/module.h>
 
+#include <asm/asm-offsets.h>
 #include <asm/setup.h>
 #include <asm/fpu.h>
 #include <asm/system.h>
@@ -100,6 +100,233 @@ asmlinkage void illegal_instruction(unsigned long esfr1, unsigned long epcr0, un
        force_sig_info(info.si_signo, &info, current);
 } /* end illegal_instruction() */
 
+/*****************************************************************************/
+/*
+ * handle atomic operations with errors
+ * - arguments in gr8, gr9, gr10
+ * - original memory value placed in gr5
+ * - replacement memory value placed in gr9
+ */
+asmlinkage void atomic_operation(unsigned long esfr1, unsigned long epcr0,
+                                unsigned long esr0)
+{
+       static DEFINE_SPINLOCK(atomic_op_lock);
+       unsigned long x, y, z, *p;
+       mm_segment_t oldfs;
+       siginfo_t info;
+       int ret;
+
+       y = 0;
+       z = 0;
+
+       oldfs = get_fs();
+       if (!user_mode(__frame))
+               set_fs(KERNEL_DS);
+
+       switch (__frame->tbr & TBR_TT) {
+               /* TIRA gr0,#120
+                * u32 __atomic_user_cmpxchg32(u32 *ptr, u32 test, u32 new)
+                */
+       case TBR_TT_ATOMIC_CMPXCHG32:
+               p = (unsigned long *) __frame->gr8;
+               x = __frame->gr9;
+               y = __frame->gr10;
+
+               for (;;) {
+                       ret = get_user(z, p);
+                       if (ret < 0)
+                               goto error;
+
+                       if (z != x)
+                               goto done;
+
+                       spin_lock_irq(&atomic_op_lock);
+
+                       if (__get_user(z, p) == 0) {
+                               if (z != x)
+                                       goto done2;
+
+                               if (__put_user(y, p) == 0)
+                                       goto done2;
+                               goto error2;
+                       }
+
+                       spin_unlock_irq(&atomic_op_lock);
+               }
+
+               /* TIRA gr0,#121
+                * u32 __atomic_kernel_xchg32(void *v, u32 new)
+                */
+       case TBR_TT_ATOMIC_XCHG32:
+               p = (unsigned long *) __frame->gr8;
+               y = __frame->gr9;
+
+               for (;;) {
+                       ret = get_user(z, p);
+                       if (ret < 0)
+                               goto error;
+
+                       spin_lock_irq(&atomic_op_lock);
+
+                       if (__get_user(z, p) == 0) {
+                               if (__put_user(y, p) == 0)
+                                       goto done2;
+                               goto error2;
+                       }
+
+                       spin_unlock_irq(&atomic_op_lock);
+               }
+
+               /* TIRA gr0,#122
+                * ulong __atomic_kernel_XOR_return(ulong i, ulong *v)
+                */
+       case TBR_TT_ATOMIC_XOR:
+               p = (unsigned long *) __frame->gr8;
+               x = __frame->gr9;
+
+               for (;;) {
+                       ret = get_user(z, p);
+                       if (ret < 0)
+                               goto error;
+
+                       spin_lock_irq(&atomic_op_lock);
+
+                       if (__get_user(z, p) == 0) {
+                               y = x ^ z;
+                               if (__put_user(y, p) == 0)
+                                       goto done2;
+                               goto error2;
+                       }
+
+                       spin_unlock_irq(&atomic_op_lock);
+               }
+
+               /* TIRA gr0,#123
+                * ulong __atomic_kernel_OR_return(ulong i, ulong *v)
+                */
+       case TBR_TT_ATOMIC_OR:
+               p = (unsigned long *) __frame->gr8;
+               x = __frame->gr9;
+
+               for (;;) {
+                       ret = get_user(z, p);
+                       if (ret < 0)
+                               goto error;
+
+                       spin_lock_irq(&atomic_op_lock);
+
+                       if (__get_user(z, p) == 0) {
+                               y = x ^ z;
+                               if (__put_user(y, p) == 0)
+                                       goto done2;
+                               goto error2;
+                       }
+
+                       spin_unlock_irq(&atomic_op_lock);
+               }
+
+               /* TIRA gr0,#124
+                * ulong __atomic_kernel_AND_return(ulong i, ulong *v)
+                */
+       case TBR_TT_ATOMIC_AND:
+               p = (unsigned long *) __frame->gr8;
+               x = __frame->gr9;
+
+               for (;;) {
+                       ret = get_user(z, p);
+                       if (ret < 0)
+                               goto error;
+
+                       spin_lock_irq(&atomic_op_lock);
+
+                       if (__get_user(z, p) == 0) {
+                               y = x & z;
+                               if (__put_user(y, p) == 0)
+                                       goto done2;
+                               goto error2;
+                       }
+
+                       spin_unlock_irq(&atomic_op_lock);
+               }
+
+               /* TIRA gr0,#125
+                * int __atomic_user_sub_return(atomic_t *v, int i)
+                */
+       case TBR_TT_ATOMIC_SUB:
+               p = (unsigned long *) __frame->gr8;
+               x = __frame->gr9;
+
+               for (;;) {
+                       ret = get_user(z, p);
+                       if (ret < 0)
+                               goto error;
+
+                       spin_lock_irq(&atomic_op_lock);
+
+                       if (__get_user(z, p) == 0) {
+                               y = z - x;
+                               if (__put_user(y, p) == 0)
+                                       goto done2;
+                               goto error2;
+                       }
+
+                       spin_unlock_irq(&atomic_op_lock);
+               }
+
+               /* TIRA gr0,#126
+                * int __atomic_user_add_return(atomic_t *v, int i)
+                */
+       case TBR_TT_ATOMIC_ADD:
+               p = (unsigned long *) __frame->gr8;
+               x = __frame->gr9;
+
+               for (;;) {
+                       ret = get_user(z, p);
+                       if (ret < 0)
+                               goto error;
+
+                       spin_lock_irq(&atomic_op_lock);
+
+                       if (__get_user(z, p) == 0) {
+                               y = z + x;
+                               if (__put_user(y, p) == 0)
+                                       goto done2;
+                               goto error2;
+                       }
+
+                       spin_unlock_irq(&atomic_op_lock);
+               }
+
+       default:
+               BUG();
+       }
+
+done2:
+       spin_unlock_irq(&atomic_op_lock);
+done:
+       if (!user_mode(__frame))
+               set_fs(oldfs);
+       __frame->gr5 = z;
+       __frame->gr9 = y;
+       return;
+
+error2:
+       spin_unlock_irq(&atomic_op_lock);
+error:
+       if (!user_mode(__frame))
+               set_fs(oldfs);
+       __frame->pc -= 4;
+
+       die_if_kernel("-- Atomic Op Error --\n");
+
+       info.si_signo   = SIGSEGV;
+       info.si_code    = SEGV_ACCERR;
+       info.si_errno   = 0;
+       info.si_addr    = (void *) __frame->pc;
+
+       force_sig_info(info.si_signo, &info, current);
+}
+
 /*****************************************************************************/
 /*
  *
@@ -280,20 +507,20 @@ static const char *regnames[] = {
 
 void show_regs(struct pt_regs *regs)
 {
-       uint32_t *reg;
+       unsigned long *reg;
        int loop;
 
        printk("\n");
 
-       printk("Frame: @%08x [%s]\n",
-              (uint32_t) regs,
+       printk("Frame: @%08lx [%s]\n",
+              (unsigned long) regs,
               regs->psr & PSR_S ? "kernel" : "user");
 
-       reg = (uint32_t *) regs;
-       for (loop = 0; loop < REG__END; loop++) {
-               printk("%s %08x", regnames[loop + 0], reg[loop + 0]);
+       reg = (unsigned long *) regs;
+       for (loop = 0; loop < NR_PT_REGS; loop++) {
+               printk("%s %08lx", regnames[loop + 0], reg[loop + 0]);
 
-               if (loop == REG__END - 1 || loop % 5 == 4)
+               if (loop == NR_PT_REGS - 1 || loop % 5 == 4)
                        printk("\n");
                else
                        printk(" | ");
@@ -329,7 +556,7 @@ void die_if_kernel(const char *str, ...)
  */
 static void show_backtrace_regs(struct pt_regs *frame)
 {
-       uint32_t *reg;
+       unsigned long *reg;
        int loop;
 
        /* print the registers for this frame */
@@ -337,11 +564,11 @@ static void show_backtrace_regs(struct pt_regs *frame)
               frame->psr & PSR_S ? "Kernel Mode" : "User Mode",
               frame);
 
-       reg = (uint32_t *) frame;
-       for (loop = 0; loop < REG__END; loop++) {
-               printk("%s %08x", regnames[loop + 0], reg[loop + 0]);
+       reg = (unsigned long *) frame;
+       for (loop = 0; loop < NR_PT_REGS; loop++) {
+               printk("%s %08lx", regnames[loop + 0], reg[loop + 0]);
 
-               if (loop == REG__END - 1 || loop % 5 == 4)
+               if (loop == NR_PT_REGS - 1 || loop % 5 == 4)
                        printk("\n");
                else
                        printk(" | ");