blob: ef9987a61d88c62e79e38e3c406a3bbcc42e1a23 [file] [log] [blame]
Ralf Baechle0004a9d2006-10-31 03:45:07 +00001/*
2 * This file is subject to the terms and conditions of the GNU General Public
3 * License. See the file "COPYING" in the main directory of this archive
4 * for more details.
5 *
6 * Copyright (c) 2006 Ralf Baechle (ralf@linux-mips.org)
7 */
Jakub Jelinek4732efb2005-09-06 15:16:25 -07008#ifndef _ASM_FUTEX_H
9#define _ASM_FUTEX_H
10
11#ifdef __KERNEL__
12
13#include <linux/futex.h>
Jeff Dike730f4122008-04-30 00:54:49 -070014#include <linux/uaccess.h>
Markos Chandrasa6813fe2013-12-04 09:58:36 +000015#include <asm/asm-eva.h>
Ralf Baechle0004a9d2006-10-31 03:45:07 +000016#include <asm/barrier.h>
Maciej W. Rozyckib0984c42014-11-15 22:08:48 +000017#include <asm/compiler.h>
Jakub Jelinek4732efb2005-09-06 15:16:25 -070018#include <asm/errno.h>
Ralf Baechle6ee1da92006-05-03 20:42:39 +010019#include <asm/war.h>
Jakub Jelinek4732efb2005-09-06 15:16:25 -070020
Ralf Baechleebfaeba2005-09-15 08:52:34 +000021#define __futex_atomic_op(insn, ret, oldval, uaddr, oparg) \
22{ \
Ralf Baechle6ee1da92006-05-03 20:42:39 +010023 if (cpu_has_llsc && R10000_LLSC_WAR) { \
24 __asm__ __volatile__( \
25 " .set push \n" \
26 " .set noat \n" \
Ralf Baechlea809d462014-03-30 13:20:10 +020027 " .set arch=r4000 \n" \
Atsushi Nemoto0307e8d2006-06-11 23:25:43 +090028 "1: ll %1, %4 # __futex_atomic_op \n" \
Ralf Baechle6ee1da92006-05-03 20:42:39 +010029 " .set mips0 \n" \
30 " " insn " \n" \
Ralf Baechlea809d462014-03-30 13:20:10 +020031 " .set arch=r4000 \n" \
Atsushi Nemoto0307e8d2006-06-11 23:25:43 +090032 "2: sc $1, %2 \n" \
Ralf Baechle6ee1da92006-05-03 20:42:39 +010033 " beqzl $1, 1b \n" \
Ralf Baechle17099b12007-07-14 13:24:05 +010034 __WEAK_LLSC_MB \
Ralf Baechle6ee1da92006-05-03 20:42:39 +010035 "3: \n" \
Maciej W. Rozycki0e525e42014-11-15 22:09:31 +000036 " .insn \n" \
Ralf Baechle6ee1da92006-05-03 20:42:39 +010037 " .set pop \n" \
38 " .set mips0 \n" \
39 " .section .fixup,\"ax\" \n" \
Atsushi Nemoto0307e8d2006-06-11 23:25:43 +090040 "4: li %0, %6 \n" \
Ralf Baechle0f67e902007-11-20 10:44:18 +000041 " j 3b \n" \
Ralf Baechle6ee1da92006-05-03 20:42:39 +010042 " .previous \n" \
43 " .section __ex_table,\"a\" \n" \
44 " "__UA_ADDR "\t1b, 4b \n" \
45 " "__UA_ADDR "\t2b, 4b \n" \
46 " .previous \n" \
Maciej W. Rozyckib0984c42014-11-15 22:08:48 +000047 : "=r" (ret), "=&r" (oldval), \
48 "=" GCC_OFF12_ASM() (*uaddr) \
49 : "0" (0), GCC_OFF12_ASM() (*uaddr), "Jr" (oparg), \
50 "i" (-EFAULT) \
Atsushi Nemoto0307e8d2006-06-11 23:25:43 +090051 : "memory"); \
Ralf Baechle6ee1da92006-05-03 20:42:39 +010052 } else if (cpu_has_llsc) { \
53 __asm__ __volatile__( \
54 " .set push \n" \
55 " .set noat \n" \
Ralf Baechlea809d462014-03-30 13:20:10 +020056 " .set arch=r4000 \n" \
Markos Chandrasa6813fe2013-12-04 09:58:36 +000057 "1: "user_ll("%1", "%4")" # __futex_atomic_op\n" \
Ralf Baechle6ee1da92006-05-03 20:42:39 +010058 " .set mips0 \n" \
59 " " insn " \n" \
Ralf Baechlea809d462014-03-30 13:20:10 +020060 " .set arch=r4000 \n" \
Markos Chandrasa6813fe2013-12-04 09:58:36 +000061 "2: "user_sc("$1", "%2")" \n" \
Ralf Baechle6ee1da92006-05-03 20:42:39 +010062 " beqz $1, 1b \n" \
Ralf Baechle17099b12007-07-14 13:24:05 +010063 __WEAK_LLSC_MB \
Ralf Baechle6ee1da92006-05-03 20:42:39 +010064 "3: \n" \
Maciej W. Rozycki0e525e42014-11-15 22:09:31 +000065 " .insn \n" \
Ralf Baechle6ee1da92006-05-03 20:42:39 +010066 " .set pop \n" \
67 " .set mips0 \n" \
68 " .section .fixup,\"ax\" \n" \
Atsushi Nemoto0307e8d2006-06-11 23:25:43 +090069 "4: li %0, %6 \n" \
Ralf Baechle0f67e902007-11-20 10:44:18 +000070 " j 3b \n" \
Ralf Baechle6ee1da92006-05-03 20:42:39 +010071 " .previous \n" \
72 " .section __ex_table,\"a\" \n" \
73 " "__UA_ADDR "\t1b, 4b \n" \
74 " "__UA_ADDR "\t2b, 4b \n" \
75 " .previous \n" \
Maciej W. Rozyckib0984c42014-11-15 22:08:48 +000076 : "=r" (ret), "=&r" (oldval), \
77 "=" GCC_OFF12_ASM() (*uaddr) \
78 : "0" (0), GCC_OFF12_ASM() (*uaddr), "Jr" (oparg), \
79 "i" (-EFAULT) \
Atsushi Nemoto0307e8d2006-06-11 23:25:43 +090080 : "memory"); \
Ralf Baechle6ee1da92006-05-03 20:42:39 +010081 } else \
82 ret = -ENOSYS; \
Ralf Baechleebfaeba2005-09-15 08:52:34 +000083}
84
Jakub Jelinek4732efb2005-09-06 15:16:25 -070085static inline int
Michel Lespinasse8d7718a2011-03-10 18:50:58 -080086futex_atomic_op_inuser(int encoded_op, u32 __user *uaddr)
Jakub Jelinek4732efb2005-09-06 15:16:25 -070087{
88 int op = (encoded_op >> 28) & 7;
89 int cmp = (encoded_op >> 24) & 15;
90 int oparg = (encoded_op << 8) >> 20;
91 int cmparg = (encoded_op << 20) >> 20;
92 int oldval = 0, ret;
93 if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28))
94 oparg = 1 << oparg;
95
Michel Lespinasse8d7718a2011-03-10 18:50:58 -080096 if (! access_ok (VERIFY_WRITE, uaddr, sizeof(u32)))
Jakub Jelinek4732efb2005-09-06 15:16:25 -070097 return -EFAULT;
98
Peter Zijlstraa8663742006-12-06 20:32:20 -080099 pagefault_disable();
Jakub Jelinek4732efb2005-09-06 15:16:25 -0700100
101 switch (op) {
102 case FUTEX_OP_SET:
Ralf Baechle70342282013-01-22 12:59:30 +0100103 __futex_atomic_op("move $1, %z5", ret, oldval, uaddr, oparg);
Ralf Baechleebfaeba2005-09-15 08:52:34 +0000104 break;
105
Jakub Jelinek4732efb2005-09-06 15:16:25 -0700106 case FUTEX_OP_ADD:
Ralf Baechle70342282013-01-22 12:59:30 +0100107 __futex_atomic_op("addu $1, %1, %z5",
108 ret, oldval, uaddr, oparg);
Ralf Baechleebfaeba2005-09-15 08:52:34 +0000109 break;
Jakub Jelinek4732efb2005-09-06 15:16:25 -0700110 case FUTEX_OP_OR:
Atsushi Nemoto0307e8d2006-06-11 23:25:43 +0900111 __futex_atomic_op("or $1, %1, %z5",
Ralf Baechle70342282013-01-22 12:59:30 +0100112 ret, oldval, uaddr, oparg);
Ralf Baechleebfaeba2005-09-15 08:52:34 +0000113 break;
Jakub Jelinek4732efb2005-09-06 15:16:25 -0700114 case FUTEX_OP_ANDN:
Atsushi Nemoto0307e8d2006-06-11 23:25:43 +0900115 __futex_atomic_op("and $1, %1, %z5",
Ralf Baechle70342282013-01-22 12:59:30 +0100116 ret, oldval, uaddr, ~oparg);
Ralf Baechleebfaeba2005-09-15 08:52:34 +0000117 break;
Jakub Jelinek4732efb2005-09-06 15:16:25 -0700118 case FUTEX_OP_XOR:
Atsushi Nemoto0307e8d2006-06-11 23:25:43 +0900119 __futex_atomic_op("xor $1, %1, %z5",
Ralf Baechle70342282013-01-22 12:59:30 +0100120 ret, oldval, uaddr, oparg);
Ralf Baechleebfaeba2005-09-15 08:52:34 +0000121 break;
Jakub Jelinek4732efb2005-09-06 15:16:25 -0700122 default:
123 ret = -ENOSYS;
124 }
125
Peter Zijlstraa8663742006-12-06 20:32:20 -0800126 pagefault_enable();
Jakub Jelinek4732efb2005-09-06 15:16:25 -0700127
128 if (!ret) {
129 switch (cmp) {
130 case FUTEX_OP_CMP_EQ: ret = (oldval == cmparg); break;
131 case FUTEX_OP_CMP_NE: ret = (oldval != cmparg); break;
132 case FUTEX_OP_CMP_LT: ret = (oldval < cmparg); break;
133 case FUTEX_OP_CMP_GE: ret = (oldval >= cmparg); break;
134 case FUTEX_OP_CMP_LE: ret = (oldval <= cmparg); break;
135 case FUTEX_OP_CMP_GT: ret = (oldval > cmparg); break;
136 default: ret = -ENOSYS;
137 }
138 }
139 return ret;
140}
141
Ingo Molnare9056f12006-03-27 01:16:21 -0800142static inline int
Michel Lespinasse8d7718a2011-03-10 18:50:58 -0800143futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr,
144 u32 oldval, u32 newval)
Ingo Molnare9056f12006-03-27 01:16:21 -0800145{
Michel Lespinasse8d7718a2011-03-10 18:50:58 -0800146 int ret = 0;
147 u32 val;
Ralf Baechle6ee1da92006-05-03 20:42:39 +0100148
Michel Lespinasse8d7718a2011-03-10 18:50:58 -0800149 if (!access_ok(VERIFY_WRITE, uaddr, sizeof(u32)))
Ralf Baechle6ee1da92006-05-03 20:42:39 +0100150 return -EFAULT;
151
152 if (cpu_has_llsc && R10000_LLSC_WAR) {
153 __asm__ __volatile__(
154 "# futex_atomic_cmpxchg_inatomic \n"
155 " .set push \n"
156 " .set noat \n"
Ralf Baechlea809d462014-03-30 13:20:10 +0200157 " .set arch=r4000 \n"
Michel Lespinasse37a9d912011-03-10 18:48:51 -0800158 "1: ll %1, %3 \n"
159 " bne %1, %z4, 3f \n"
Ralf Baechle6ee1da92006-05-03 20:42:39 +0100160 " .set mips0 \n"
Michel Lespinasse37a9d912011-03-10 18:48:51 -0800161 " move $1, %z5 \n"
Ralf Baechlea809d462014-03-30 13:20:10 +0200162 " .set arch=r4000 \n"
Michel Lespinasse37a9d912011-03-10 18:48:51 -0800163 "2: sc $1, %2 \n"
Ralf Baechle6ee1da92006-05-03 20:42:39 +0100164 " beqzl $1, 1b \n"
Ralf Baechle17099b12007-07-14 13:24:05 +0100165 __WEAK_LLSC_MB
Ralf Baechle6ee1da92006-05-03 20:42:39 +0100166 "3: \n"
Maciej W. Rozycki0e525e42014-11-15 22:09:31 +0000167 " .insn \n"
Ralf Baechle6ee1da92006-05-03 20:42:39 +0100168 " .set pop \n"
169 " .section .fixup,\"ax\" \n"
Michel Lespinasse37a9d912011-03-10 18:48:51 -0800170 "4: li %0, %6 \n"
Ralf Baechle6ee1da92006-05-03 20:42:39 +0100171 " j 3b \n"
172 " .previous \n"
173 " .section __ex_table,\"a\" \n"
174 " "__UA_ADDR "\t1b, 4b \n"
175 " "__UA_ADDR "\t2b, 4b \n"
176 " .previous \n"
Maciej W. Rozyckib0984c42014-11-15 22:08:48 +0000177 : "+r" (ret), "=&r" (val), "=" GCC_OFF12_ASM() (*uaddr)
178 : GCC_OFF12_ASM() (*uaddr), "Jr" (oldval), "Jr" (newval),
179 "i" (-EFAULT)
Ralf Baechle6ee1da92006-05-03 20:42:39 +0100180 : "memory");
181 } else if (cpu_has_llsc) {
182 __asm__ __volatile__(
183 "# futex_atomic_cmpxchg_inatomic \n"
184 " .set push \n"
185 " .set noat \n"
Ralf Baechlea809d462014-03-30 13:20:10 +0200186 " .set arch=r4000 \n"
Markos Chandrasa6813fe2013-12-04 09:58:36 +0000187 "1: "user_ll("%1", "%3")" \n"
Michel Lespinasse37a9d912011-03-10 18:48:51 -0800188 " bne %1, %z4, 3f \n"
Ralf Baechle6ee1da92006-05-03 20:42:39 +0100189 " .set mips0 \n"
Michel Lespinasse37a9d912011-03-10 18:48:51 -0800190 " move $1, %z5 \n"
Ralf Baechlea809d462014-03-30 13:20:10 +0200191 " .set arch=r4000 \n"
Markos Chandrasa6813fe2013-12-04 09:58:36 +0000192 "2: "user_sc("$1", "%2")" \n"
Ralf Baechle6ee1da92006-05-03 20:42:39 +0100193 " beqz $1, 1b \n"
Ralf Baechle17099b12007-07-14 13:24:05 +0100194 __WEAK_LLSC_MB
Ralf Baechle6ee1da92006-05-03 20:42:39 +0100195 "3: \n"
Maciej W. Rozycki0e525e42014-11-15 22:09:31 +0000196 " .insn \n"
Ralf Baechle6ee1da92006-05-03 20:42:39 +0100197 " .set pop \n"
198 " .section .fixup,\"ax\" \n"
Michel Lespinasse37a9d912011-03-10 18:48:51 -0800199 "4: li %0, %6 \n"
Ralf Baechle6ee1da92006-05-03 20:42:39 +0100200 " j 3b \n"
201 " .previous \n"
202 " .section __ex_table,\"a\" \n"
203 " "__UA_ADDR "\t1b, 4b \n"
204 " "__UA_ADDR "\t2b, 4b \n"
205 " .previous \n"
Maciej W. Rozyckib0984c42014-11-15 22:08:48 +0000206 : "+r" (ret), "=&r" (val), "=" GCC_OFF12_ASM() (*uaddr)
207 : GCC_OFF12_ASM() (*uaddr), "Jr" (oldval), "Jr" (newval),
208 "i" (-EFAULT)
Ralf Baechle6ee1da92006-05-03 20:42:39 +0100209 : "memory");
210 } else
211 return -ENOSYS;
212
Michel Lespinasse37a9d912011-03-10 18:48:51 -0800213 *uval = val;
214 return ret;
Ingo Molnare9056f12006-03-27 01:16:21 -0800215}
216
Jakub Jelinek4732efb2005-09-06 15:16:25 -0700217#endif
Ralf Baechle0f67e902007-11-20 10:44:18 +0000218#endif /* _ASM_FUTEX_H */