Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs-2.6
[linux-2.6] / arch / frv / kernel / futex.c
1 /* futex.c: futex operations
2  *
3  * Copyright (C) 2005 Red Hat, Inc. All Rights Reserved.
4  * Written by David Howells (dhowells@redhat.com)
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version
9  * 2 of the License, or (at your option) any later version.
10  */
11
12 #include <linux/futex.h>
13 #include <linux/uaccess.h>
14 #include <asm/futex.h>
15 #include <asm/errno.h>
16
17 /*
18  * the various futex operations; MMU fault checking is ignored under no-MMU
19  * conditions
20  */
21 static inline int atomic_futex_op_xchg_set(int oparg, int __user *uaddr, int *_oldval)
22 {
23         int oldval, ret;
24
25         asm("0:                                         \n"
26             "   orcc            gr0,gr0,gr0,icc3        \n"     /* set ICC3.Z */
27             "   ckeq            icc3,cc7                \n"
28             "1: ld.p            %M0,%1                  \n"     /* LD.P/ORCR must be atomic */
29             "   orcr            cc7,cc7,cc3             \n"     /* set CC3 to true */
30             "2: cst.p           %3,%M0          ,cc3,#1 \n"
31             "   corcc           gr29,gr29,gr0   ,cc3,#1 \n"     /* clear ICC3.Z if store happens */
32             "   beq             icc3,#0,0b              \n"
33             "   setlos          0,%2                    \n"
34             "3:                                         \n"
35             ".subsection 2                              \n"
36             "4: setlos          %5,%2                   \n"
37             "   bra             3b                      \n"
38             ".previous                                  \n"
39             ".section __ex_table,\"a\"                  \n"
40             "   .balign         8                       \n"
41             "   .long           1b,4b                   \n"
42             "   .long           2b,4b                   \n"
43             ".previous"
44             : "+U"(*uaddr), "=&r"(oldval), "=&r"(ret), "=r"(oparg)
45             : "3"(oparg), "i"(-EFAULT)
46             : "memory", "cc7", "cc3", "icc3"
47             );
48
49         *_oldval = oldval;
50         return ret;
51 }
52
53 static inline int atomic_futex_op_xchg_add(int oparg, int __user *uaddr, int *_oldval)
54 {
55         int oldval, ret;
56
57         asm("0:                                         \n"
58             "   orcc            gr0,gr0,gr0,icc3        \n"     /* set ICC3.Z */
59             "   ckeq            icc3,cc7                \n"
60             "1: ld.p            %M0,%1                  \n"     /* LD.P/ORCR must be atomic */
61             "   orcr            cc7,cc7,cc3             \n"     /* set CC3 to true */
62             "   add             %1,%3,%3                \n"
63             "2: cst.p           %3,%M0          ,cc3,#1 \n"
64             "   corcc           gr29,gr29,gr0   ,cc3,#1 \n"     /* clear ICC3.Z if store happens */
65             "   beq             icc3,#0,0b              \n"
66             "   setlos          0,%2                    \n"
67             "3:                                         \n"
68             ".subsection 2                              \n"
69             "4: setlos          %5,%2                   \n"
70             "   bra             3b                      \n"
71             ".previous                                  \n"
72             ".section __ex_table,\"a\"                  \n"
73             "   .balign         8                       \n"
74             "   .long           1b,4b                   \n"
75             "   .long           2b,4b                   \n"
76             ".previous"
77             : "+U"(*uaddr), "=&r"(oldval), "=&r"(ret), "=r"(oparg)
78             : "3"(oparg), "i"(-EFAULT)
79             : "memory", "cc7", "cc3", "icc3"
80             );
81
82         *_oldval = oldval;
83         return ret;
84 }
85
86 static inline int atomic_futex_op_xchg_or(int oparg, int __user *uaddr, int *_oldval)
87 {
88         int oldval, ret;
89
90         asm("0:                                         \n"
91             "   orcc            gr0,gr0,gr0,icc3        \n"     /* set ICC3.Z */
92             "   ckeq            icc3,cc7                \n"
93             "1: ld.p            %M0,%1                  \n"     /* LD.P/ORCR must be atomic */
94             "   orcr            cc7,cc7,cc3             \n"     /* set CC3 to true */
95             "   or              %1,%3,%3                \n"
96             "2: cst.p           %3,%M0          ,cc3,#1 \n"
97             "   corcc           gr29,gr29,gr0   ,cc3,#1 \n"     /* clear ICC3.Z if store happens */
98             "   beq             icc3,#0,0b              \n"
99             "   setlos          0,%2                    \n"
100             "3:                                         \n"
101             ".subsection 2                              \n"
102             "4: setlos          %5,%2                   \n"
103             "   bra             3b                      \n"
104             ".previous                                  \n"
105             ".section __ex_table,\"a\"                  \n"
106             "   .balign         8                       \n"
107             "   .long           1b,4b                   \n"
108             "   .long           2b,4b                   \n"
109             ".previous"
110             : "+U"(*uaddr), "=&r"(oldval), "=&r"(ret), "=r"(oparg)
111             : "3"(oparg), "i"(-EFAULT)
112             : "memory", "cc7", "cc3", "icc3"
113             );
114
115         *_oldval = oldval;
116         return ret;
117 }
118
119 static inline int atomic_futex_op_xchg_and(int oparg, int __user *uaddr, int *_oldval)
120 {
121         int oldval, ret;
122
123         asm("0:                                         \n"
124             "   orcc            gr0,gr0,gr0,icc3        \n"     /* set ICC3.Z */
125             "   ckeq            icc3,cc7                \n"
126             "1: ld.p            %M0,%1                  \n"     /* LD.P/ORCR must be atomic */
127             "   orcr            cc7,cc7,cc3             \n"     /* set CC3 to true */
128             "   and             %1,%3,%3                \n"
129             "2: cst.p           %3,%M0          ,cc3,#1 \n"
130             "   corcc           gr29,gr29,gr0   ,cc3,#1 \n"     /* clear ICC3.Z if store happens */
131             "   beq             icc3,#0,0b              \n"
132             "   setlos          0,%2                    \n"
133             "3:                                         \n"
134             ".subsection 2                              \n"
135             "4: setlos          %5,%2                   \n"
136             "   bra             3b                      \n"
137             ".previous                                  \n"
138             ".section __ex_table,\"a\"                  \n"
139             "   .balign         8                       \n"
140             "   .long           1b,4b                   \n"
141             "   .long           2b,4b                   \n"
142             ".previous"
143             : "+U"(*uaddr), "=&r"(oldval), "=&r"(ret), "=r"(oparg)
144             : "3"(oparg), "i"(-EFAULT)
145             : "memory", "cc7", "cc3", "icc3"
146             );
147
148         *_oldval = oldval;
149         return ret;
150 }
151
152 static inline int atomic_futex_op_xchg_xor(int oparg, int __user *uaddr, int *_oldval)
153 {
154         int oldval, ret;
155
156         asm("0:                                         \n"
157             "   orcc            gr0,gr0,gr0,icc3        \n"     /* set ICC3.Z */
158             "   ckeq            icc3,cc7                \n"
159             "1: ld.p            %M0,%1                  \n"     /* LD.P/ORCR must be atomic */
160             "   orcr            cc7,cc7,cc3             \n"     /* set CC3 to true */
161             "   xor             %1,%3,%3                \n"
162             "2: cst.p           %3,%M0          ,cc3,#1 \n"
163             "   corcc           gr29,gr29,gr0   ,cc3,#1 \n"     /* clear ICC3.Z if store happens */
164             "   beq             icc3,#0,0b              \n"
165             "   setlos          0,%2                    \n"
166             "3:                                         \n"
167             ".subsection 2                              \n"
168             "4: setlos          %5,%2                   \n"
169             "   bra             3b                      \n"
170             ".previous                                  \n"
171             ".section __ex_table,\"a\"                  \n"
172             "   .balign         8                       \n"
173             "   .long           1b,4b                   \n"
174             "   .long           2b,4b                   \n"
175             ".previous"
176             : "+U"(*uaddr), "=&r"(oldval), "=&r"(ret), "=r"(oparg)
177             : "3"(oparg), "i"(-EFAULT)
178             : "memory", "cc7", "cc3", "icc3"
179             );
180
181         *_oldval = oldval;
182         return ret;
183 }
184
185 /*****************************************************************************/
186 /*
187  * do the futex operations
188  */
189 int futex_atomic_op_inuser(int encoded_op, int __user *uaddr)
190 {
191         int op = (encoded_op >> 28) & 7;
192         int cmp = (encoded_op >> 24) & 15;
193         int oparg = (encoded_op << 8) >> 20;
194         int cmparg = (encoded_op << 20) >> 20;
195         int oldval = 0, ret;
196
197         if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28))
198                 oparg = 1 << oparg;
199
200         if (!access_ok(VERIFY_WRITE, uaddr, sizeof(int)))
201                 return -EFAULT;
202
203         pagefault_disable();
204
205         switch (op) {
206         case FUTEX_OP_SET:
207                 ret = atomic_futex_op_xchg_set(oparg, uaddr, &oldval);
208                 break;
209         case FUTEX_OP_ADD:
210                 ret = atomic_futex_op_xchg_add(oparg, uaddr, &oldval);
211                 break;
212         case FUTEX_OP_OR:
213                 ret = atomic_futex_op_xchg_or(oparg, uaddr, &oldval);
214                 break;
215         case FUTEX_OP_ANDN:
216                 ret = atomic_futex_op_xchg_and(~oparg, uaddr, &oldval);
217                 break;
218         case FUTEX_OP_XOR:
219                 ret = atomic_futex_op_xchg_xor(oparg, uaddr, &oldval);
220                 break;
221         default:
222                 ret = -ENOSYS;
223                 break;
224         }
225
226         pagefault_enable();
227
228         if (!ret) {
229                 switch (cmp) {
230                 case FUTEX_OP_CMP_EQ: ret = (oldval == cmparg); break;
231                 case FUTEX_OP_CMP_NE: ret = (oldval != cmparg); break;
232                 case FUTEX_OP_CMP_LT: ret = (oldval < cmparg); break;
233                 case FUTEX_OP_CMP_GE: ret = (oldval >= cmparg); break;
234                 case FUTEX_OP_CMP_LE: ret = (oldval <= cmparg); break;
235                 case FUTEX_OP_CMP_GT: ret = (oldval > cmparg); break;
236                 default: ret = -ENOSYS; break;
237                 }
238         }
239
240         return ret;
241
242 } /* end futex_atomic_op_inuser() */