1 /*
2  * Copyright 2009-2016 Samy Al Bahra.
3  * Copyright 2013-2016 Olivier Houchard.
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  */
27 
28 #ifndef CK_PR_AARCH64_LLSC_H
29 #define CK_PR_AARCH64_LLSC_H
30 
31 #ifndef CK_PR_H
32 #error Do not include this file directly, use ck_pr.h
33 #endif
34 
35 CK_CC_INLINE static bool
ck_pr_cas_64_2_value(uint64_t target[2],uint64_t compare[2],uint64_t set[2],uint64_t value[2])36 ck_pr_cas_64_2_value(uint64_t target[2], uint64_t compare[2], uint64_t set[2], uint64_t value[2])
37 {
38         uint64_t tmp1, tmp2;
39 
40         __asm__ __volatile__("1:"
41                              "ldxp %0, %1, [%4]\n"
42                              "mov %2, %0\n"
43                              "mov %3, %1\n"
44                              "eor %0, %0, %5\n"
45                              "eor %1, %1, %6\n"
46                              "orr %1, %0, %1\n"
47                              "mov %w0, #0\n"
48                              "cbnz %1, 2f\n"
49                              "stxp %w0, %7, %8, [%4]\n"
50                              "cbnz %w0, 1b\n"
51                              "mov %w0, #1\n"
52                              "2:"
53                              : "=&r" (tmp1), "=&r" (tmp2), "=&r" (value[0]), "=&r" (value[1])
54                              : "r" (target), "r" (compare[0]), "r" (compare[1]), "r" (set[0]), "r" (set[1])
55                              : "cc", "memory");
56 
57         return (tmp1);
58 }
59 
60 CK_CC_INLINE static bool
ck_pr_cas_ptr_2_value(void * target,void * compare,void * set,void * value)61 ck_pr_cas_ptr_2_value(void *target, void *compare, void *set, void *value)
62 {
63         return (ck_pr_cas_64_2_value(CK_CPP_CAST(uint64_t *, target),
64                                    CK_CPP_CAST(uint64_t *, compare),
65                                    CK_CPP_CAST(uint64_t *, set),
66                                    CK_CPP_CAST(uint64_t *, value)));
67 }
68 
69 CK_CC_INLINE static bool
ck_pr_cas_64_2(uint64_t target[2],uint64_t compare[2],uint64_t set[2])70 ck_pr_cas_64_2(uint64_t target[2], uint64_t compare[2], uint64_t set[2])
71 {
72         uint64_t tmp1, tmp2;
73 
74         __asm__ __volatile__("1:"
75                              "ldxp %0, %1, [%2]\n"
76                              "eor %0, %0, %3\n"
77                              "eor %1, %1, %4\n"
78                              "orr %1, %0, %1\n"
79                              "mov %w0, #0\n"
80                              "cbnz %1, 2f\n"
81                              "stxp %w0, %5, %6, [%2]\n"
82                              "cbnz %w0, 1b\n"
83                              "mov %w0, #1\n"
84                              "2:"
85                              : "=&r" (tmp1), "=&r" (tmp2)
86                              : "r" (target), "r" (compare[0]), "r" (compare[1]), "r" (set[0]), "r" (set[1])
87                              : "cc", "memory");
88 
89         return (tmp1);
90 }
91 CK_CC_INLINE static bool
ck_pr_cas_ptr_2(void * target,void * compare,void * set)92 ck_pr_cas_ptr_2(void *target, void *compare, void *set)
93 {
94         return (ck_pr_cas_64_2(CK_CPP_CAST(uint64_t *, target),
95                              CK_CPP_CAST(uint64_t *, compare),
96                              CK_CPP_CAST(uint64_t *, set)));
97 }
98 
99 
100 #define CK_PR_CAS(N, M, T, W, R)					\
101         CK_CC_INLINE static bool					\
102         ck_pr_cas_##N##_value(M *target, T compare, T set, M *value)	\
103         {								\
104                 T previous;						\
105                 T tmp;							\
106                 __asm__ __volatile__("1:\n"				\
107                                      "ldxr" W " %" R "0, [%2]\n"	\
108                                      "cmp  %" R "0, %" R "4\n"		\
109                                      "b.ne 2f\n"			\
110                                      "stxr" W " %w1, %" R "3, [%2]\n"	\
111                                      "cbnz %w1, 1b\n"			\
112                                      "2:"				\
113                     : "=&r" (previous),					\
114                     "=&r" (tmp)						\
115                     : "r"   (target),					\
116                     "r"   (set),					\
117                     "r"   (compare)					\
118                     : "memory", "cc");					\
119                 *(T *)value = previous;					\
120                 return (previous == compare);				\
121         }								\
122         CK_CC_INLINE static bool					\
123         ck_pr_cas_##N(M *target, T compare, T set)			\
124         {								\
125                 T previous;						\
126                 T tmp;							\
127                 __asm__ __volatile__(					\
128                                      "1:"				\
129                                      "ldxr" W " %" R "0, [%2]\n"	\
130                                      "cmp  %" R "0, %" R "4\n"		\
131                                      "b.ne 2f\n"			\
132                                      "stxr" W " %w1, %" R "3, [%2]\n"	\
133                                      "cbnz %w1, 1b\n"			\
134                                      "2:"				\
135                     : "=&r" (previous),					\
136                     "=&r" (tmp)						\
137                     : "r"   (target),					\
138                     "r"   (set),					\
139                     "r"   (compare)					\
140                     : "memory", "cc");					\
141                 return (previous == compare);				\
142         }
143 
144 CK_PR_CAS(ptr, void, void *, "", "")
145 
146 #define CK_PR_CAS_S(N, M, W, R)	CK_PR_CAS(N, M, M, W, R)
147 CK_PR_CAS_S(64, uint64_t, "", "")
148 #ifndef CK_PR_DISABLE_DOUBLE
149 CK_PR_CAS_S(double, double, "", "")
150 #endif
151 CK_PR_CAS_S(32, uint32_t, "", "w")
152 CK_PR_CAS_S(uint, unsigned int, "", "w")
153 CK_PR_CAS_S(int, int, "", "w")
154 CK_PR_CAS_S(16, uint16_t, "h", "w")
155 CK_PR_CAS_S(8, uint8_t, "b", "w")
156 CK_PR_CAS_S(short, short, "h", "w")
157 CK_PR_CAS_S(char, char, "b", "w")
158 
159 
160 #undef CK_PR_CAS_S
161 #undef CK_PR_CAS
162 
163 #define CK_PR_FAS(N, M, T, W, R)				\
164         CK_CC_INLINE static T					\
165         ck_pr_fas_##N(M *target, T v)				\
166         {							\
167                 T previous;					\
168                 T tmp;						\
169                 __asm__ __volatile__("1:"			\
170                                      "ldxr" W " %" R "0, [%2]\n"\
171                                      "stxr" W " %w1, %" R "3, [%2]\n"\
172                                      "cbnz %w1, 1b\n"		\
173                                         : "=&r" (previous),	\
174                                           "=&r" (tmp) 		\
175                                         : "r"   (target),	\
176                                           "r"   (v)		\
177                                         : "memory", "cc");	\
178                 return (previous);				\
179         }
180 
181 CK_PR_FAS(64, uint64_t, uint64_t, "", "")
182 CK_PR_FAS(32, uint32_t, uint32_t, "", "w")
183 CK_PR_FAS(ptr, void, void *, "", "")
184 CK_PR_FAS(int, int, int, "", "w")
185 CK_PR_FAS(uint, unsigned int, unsigned int, "", "w")
186 CK_PR_FAS(16, uint16_t, uint16_t, "h", "w")
187 CK_PR_FAS(8, uint8_t, uint8_t, "b", "w")
188 CK_PR_FAS(short, short, short, "h", "w")
189 CK_PR_FAS(char, char, char, "b", "w")
190 
191 
192 #undef CK_PR_FAS
193 
194 #define CK_PR_UNARY(O, N, M, T, I, W, R)			\
195         CK_CC_INLINE static void				\
196         ck_pr_##O##_##N(M *target)				\
197         {							\
198                 T previous = 0;					\
199                 T tmp = 0;					\
200                 __asm__ __volatile__("1:"			\
201                                      "ldxr" W " %" R "0, [%2]\n"\
202                                       I "\n"			\
203                                      "stxr" W " %w1, %" R "0, [%2]\n"	\
204                                      "cbnz %w1, 1b\n"		\
205                                         : "=&r" (previous),	\
206                                           "=&r" (tmp)		\
207                                         : "r"   (target)	\
208                                         : "memory", "cc");	\
209                 return;						\
210         }
211 
212 CK_PR_UNARY(inc, ptr, void, void *, "add %0, %0, #1", "", "")
213 CK_PR_UNARY(dec, ptr, void, void *, "sub %0, %0, #1", "", "")
214 CK_PR_UNARY(not, ptr, void, void *, "mvn %0, %0", "", "")
215 CK_PR_UNARY(inc, 64, uint64_t, uint64_t, "add %0, %0, #1", "", "")
216 CK_PR_UNARY(dec, 64, uint64_t, uint64_t, "sub %0, %0, #1", "", "")
217 CK_PR_UNARY(not, 64, uint64_t, uint64_t, "mvn %0, %0", "", "")
218 
219 #define CK_PR_UNARY_S(S, T, W)					\
220         CK_PR_UNARY(inc, S, T, T, "add %w0, %w0, #1", W, "w")	\
221         CK_PR_UNARY(dec, S, T, T, "sub %w0, %w0, #1", W, "w")	\
222         CK_PR_UNARY(not, S, T, T, "mvn %w0, %w0", W, "w")	\
223 
224 CK_PR_UNARY_S(32, uint32_t, "")
225 CK_PR_UNARY_S(uint, unsigned int, "")
226 CK_PR_UNARY_S(int, int, "")
227 CK_PR_UNARY_S(16, uint16_t, "h")
228 CK_PR_UNARY_S(8, uint8_t, "b")
229 CK_PR_UNARY_S(short, short, "h")
230 CK_PR_UNARY_S(char, char, "b")
231 
232 #undef CK_PR_UNARY_S
233 #undef CK_PR_UNARY
234 
235 #define CK_PR_BINARY(O, N, M, T, I, W, R)			\
236         CK_CC_INLINE static void				\
237         ck_pr_##O##_##N(M *target, T delta)			\
238         {							\
239                 T previous;					\
240                 T tmp;						\
241                 __asm__ __volatile__("1:"			\
242                                      "ldxr" W " %" R "0, [%2]\n"\
243                                       I " %" R "0, %" R "0, %" R "3\n"	\
244                                      "stxr" W " %w1, %" R "0, [%2]\n"	\
245                                      "cbnz %w1, 1b\n"		\
246                                         : "=&r" (previous),	\
247                                           "=&r" (tmp)		\
248                                         : "r"   (target),	\
249                                           "r"   (delta)		\
250                                         : "memory", "cc");	\
251                 return;						\
252         }
253 
254 CK_PR_BINARY(and, ptr, void, uintptr_t, "and", "", "")
255 CK_PR_BINARY(add, ptr, void, uintptr_t, "add", "", "")
256 CK_PR_BINARY(or, ptr, void, uintptr_t, "orr", "", "")
257 CK_PR_BINARY(sub, ptr, void, uintptr_t, "sub", "", "")
258 CK_PR_BINARY(xor, ptr, void, uintptr_t, "eor", "", "")
259 CK_PR_BINARY(and, 64, uint64_t, uint64_t, "and", "", "")
260 CK_PR_BINARY(add, 64, uint64_t, uint64_t, "add", "", "")
261 CK_PR_BINARY(or, 64, uint64_t, uint64_t, "orr", "", "")
262 CK_PR_BINARY(sub, 64, uint64_t, uint64_t, "sub", "", "")
263 CK_PR_BINARY(xor, 64, uint64_t, uint64_t, "eor", "", "")
264 
265 #define CK_PR_BINARY_S(S, T, W)				\
266         CK_PR_BINARY(and, S, T, T, "and", W, "w")	\
267         CK_PR_BINARY(add, S, T, T, "add", W, "w")	\
268         CK_PR_BINARY(or, S, T, T, "orr", W, "w")	\
269         CK_PR_BINARY(sub, S, T, T, "sub", W, "w")	\
270         CK_PR_BINARY(xor, S, T, T, "eor", W, "w")
271 
272 CK_PR_BINARY_S(32, uint32_t, "")
273 CK_PR_BINARY_S(uint, unsigned int, "")
274 CK_PR_BINARY_S(int, int, "")
275 CK_PR_BINARY_S(16, uint16_t, "h")
276 CK_PR_BINARY_S(8, uint8_t, "b")
277 CK_PR_BINARY_S(short, short, "h")
278 CK_PR_BINARY_S(char, char, "b")
279 
280 #undef CK_PR_BINARY_S
281 #undef CK_PR_BINARY
282 
283 CK_CC_INLINE static void *
ck_pr_faa_ptr(void * target,uintptr_t delta)284 ck_pr_faa_ptr(void *target, uintptr_t delta)
285 {
286         uintptr_t previous, r, tmp;
287 
288         __asm__ __volatile__("1:"
289                              "ldxr %0, [%3]\n"
290                              "add %1, %4, %0\n"
291                              "stxr %w2, %1, [%3]\n"
292                              "cbnz %w2, 1b\n"
293                                 : "=&r" (previous),
294                                   "=&r" (r),
295                                   "=&r" (tmp)
296                                 : "r"   (target),
297                                   "r"   (delta)
298                                 : "memory", "cc");
299 
300         return (void *)(previous);
301 }
302 
303 CK_CC_INLINE static uint64_t
ck_pr_faa_64(uint64_t * target,uint64_t delta)304 ck_pr_faa_64(uint64_t *target, uint64_t delta)
305 {
306         uint64_t previous, r, tmp;
307 
308         __asm__ __volatile__("1:"
309                              "ldxr %0, [%3]\n"
310                              "add %1, %4, %0\n"
311                              "stxr %w2, %1, [%3]\n"
312                              "cbnz %w2, 1b;"
313                                 : "=&r" (previous),
314                                   "=&r" (r),
315                                   "=&r" (tmp)
316                                 : "r"   (target),
317                                   "r"   (delta)
318                                 : "memory", "cc");
319 
320         return (previous);
321 }
322 
323 #define CK_PR_FAA(S, T, W)						\
324         CK_CC_INLINE static T						\
325         ck_pr_faa_##S(T *target, T delta)				\
326         {								\
327                 T previous, r, tmp;					\
328                 __asm__ __volatile__("1:"				\
329                                      "ldxr" W " %w0, [%3]\n"		\
330                                      "add %w1, %w4, %w0\n"		\
331                                      "stxr" W " %w2, %w1, [%3]\n"	\
332                                      "cbnz %w2, 1b\n"			\
333                                         : "=&r" (previous),		\
334                                           "=&r" (r),			\
335                                           "=&r" (tmp)			\
336                                         : "r"   (target),		\
337                                           "r"   (delta)			\
338                                         : "memory", "cc");		\
339                 return (previous);					\
340         }
341 
342 CK_PR_FAA(32, uint32_t, "")
343 CK_PR_FAA(uint, unsigned int, "")
344 CK_PR_FAA(int, int, "")
345 CK_PR_FAA(16, uint16_t, "h")
346 CK_PR_FAA(8, uint8_t, "b")
347 CK_PR_FAA(short, short, "h")
348 CK_PR_FAA(char, char, "b")
349 
350 #undef CK_PR_FAA
351 
352 #endif /* CK_PR_AARCH64_LLSC_H */
353