GNU Linux-libre 4.9.309-gnu1
[releases.git] / arch / powerpc / include / asm / futex.h
1 #ifndef _ASM_POWERPC_FUTEX_H
2 #define _ASM_POWERPC_FUTEX_H
3
4 #ifdef __KERNEL__
5
6 #include <linux/futex.h>
7 #include <linux/uaccess.h>
8 #include <asm/errno.h>
9 #include <asm/synch.h>
10 #include <asm/asm-compat.h>
11
12 #define __futex_atomic_op(insn, ret, oldval, uaddr, oparg) \
13   __asm__ __volatile ( \
14         PPC_ATOMIC_ENTRY_BARRIER \
15 "1:     lwarx   %0,0,%2\n" \
16         insn \
17         PPC405_ERR77(0, %2) \
18 "2:     stwcx.  %1,0,%2\n" \
19         "bne-   1b\n" \
20         PPC_ATOMIC_EXIT_BARRIER \
21         "li     %1,0\n" \
22 "3:     .section .fixup,\"ax\"\n" \
23 "4:     li      %1,%3\n" \
24         "b      3b\n" \
25         ".previous\n" \
26         ".section __ex_table,\"a\"\n" \
27         ".align 3\n" \
28         PPC_LONG "1b,4b,2b,4b\n" \
29         ".previous" \
30         : "=&r" (oldval), "=&r" (ret) \
31         : "b" (uaddr), "i" (-EFAULT), "r" (oparg) \
32         : "cr0", "memory")
33
34 static inline int arch_futex_atomic_op_inuser(int op, int oparg, int *oval,
35                 u32 __user *uaddr)
36 {
37         int oldval = 0, ret;
38
39         allow_write_to_user(uaddr, sizeof(*uaddr));
40         pagefault_disable();
41
42         switch (op) {
43         case FUTEX_OP_SET:
44                 __futex_atomic_op("mr %1,%4\n", ret, oldval, uaddr, oparg);
45                 break;
46         case FUTEX_OP_ADD:
47                 __futex_atomic_op("add %1,%0,%4\n", ret, oldval, uaddr, oparg);
48                 break;
49         case FUTEX_OP_OR:
50                 __futex_atomic_op("or %1,%0,%4\n", ret, oldval, uaddr, oparg);
51                 break;
52         case FUTEX_OP_ANDN:
53                 __futex_atomic_op("andc %1,%0,%4\n", ret, oldval, uaddr, oparg);
54                 break;
55         case FUTEX_OP_XOR:
56                 __futex_atomic_op("xor %1,%0,%4\n", ret, oldval, uaddr, oparg);
57                 break;
58         default:
59                 ret = -ENOSYS;
60         }
61
62         pagefault_enable();
63
64         *oval = oldval;
65
66         prevent_write_to_user(uaddr, sizeof(*uaddr));
67         return ret;
68 }
69
70 static inline int
71 futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr,
72                               u32 oldval, u32 newval)
73 {
74         int ret = 0;
75         u32 prev;
76
77         if (!access_ok(VERIFY_WRITE, uaddr, sizeof(u32)))
78                 return -EFAULT;
79
80         allow_write_to_user(uaddr, sizeof(*uaddr));
81         __asm__ __volatile__ (
82         PPC_ATOMIC_ENTRY_BARRIER
83 "1:     lwarx   %1,0,%3         # futex_atomic_cmpxchg_inatomic\n\
84         cmpw    0,%1,%4\n\
85         bne-    3f\n"
86         PPC405_ERR77(0,%3)
87 "2:     stwcx.  %5,0,%3\n\
88         bne-    1b\n"
89         PPC_ATOMIC_EXIT_BARRIER
90 "3:     .section .fixup,\"ax\"\n\
91 4:      li      %0,%6\n\
92         b       3b\n\
93         .previous\n\
94         .section __ex_table,\"a\"\n\
95         .align 3\n\
96         " PPC_LONG "1b,4b,2b,4b\n\
97         .previous" \
98         : "+r" (ret), "=&r" (prev), "+m" (*uaddr)
99         : "r" (uaddr), "r" (oldval), "r" (newval), "i" (-EFAULT)
100         : "cc", "memory");
101
102         *uval = prev;
103         prevent_write_to_user(uaddr, sizeof(*uaddr));
104         return ret;
105 }
106
107 #endif /* __KERNEL__ */
108 #endif /* _ASM_POWERPC_FUTEX_H */