1/* Linux-specific atomic operations for PA Linux.
2   Copyright (C) 2008-2020 Free Software Foundation, Inc.
3   Based on code contributed by CodeSourcery for ARM EABI Linux.
4   Modifications for PA Linux by Helge Deller <deller@gmx.de>
5
6This file is part of GCC.
7
8GCC is free software; you can redistribute it and/or modify it under
9the terms of the GNU General Public License as published by the Free
10Software Foundation; either version 3, or (at your option) any later
11version.
12
13GCC is distributed in the hope that it will be useful, but WITHOUT ANY
14WARRANTY; without even the implied warranty of MERCHANTABILITY or
15FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
16for more details.
17
18Under Section 7 of GPL version 3, you are granted additional
19permissions described in the GCC Runtime Library Exception, version
203.1, as published by the Free Software Foundation.
21
22You should have received a copy of the GNU General Public License and
23a copy of the GCC Runtime Library Exception along with this program;
24see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
25<http://www.gnu.org/licenses/>.  */
26
27#define EFAULT  14
28#define EBUSY   16
29#define ENOSYS 251
30
31typedef unsigned char u8;
32typedef short unsigned int u16;
33#ifdef __LP64__
34typedef long unsigned int u64;
35#else
36typedef long long unsigned int u64;
37#endif
38
39/* PA-RISC 2.0 supports out-of-order execution for loads and stores.
40   Thus, we need to synchonize memory accesses.  For more info, see:
41   "Advanced Performance Features of the 64-bit PA-8000" by Doug Hunt.
42
43   We implement byte, short and int versions of each atomic operation
44   using the kernel helper defined below.  There is no support for
45   64-bit operations yet.  */
46
47/* Determine kernel LWS function call (0=32-bit, 1=64-bit userspace).  */
48#define LWS_CAS (sizeof(long) == 4 ? 0 : 1)
49
50/* Kernel helper for compare-and-exchange a 32-bit value.  */
51static inline long
52__kernel_cmpxchg (volatile void *mem, int oldval, int newval)
53{
54  register unsigned long lws_mem asm("r26") = (unsigned long) (mem);
55  register int lws_old asm("r25") = oldval;
56  register int lws_new asm("r24") = newval;
57  register long lws_ret   asm("r28");
58  register long lws_errno asm("r21");
59  asm volatile (	"ble	0xb0(%%sr2, %%r0)	\n\t"
60			"ldi	%2, %%r20		\n\t"
61	: "=r" (lws_ret), "=r" (lws_errno)
62	: "i" (LWS_CAS), "r" (lws_mem), "r" (lws_old), "r" (lws_new)
63	: "r1", "r20", "r22", "r23", "r29", "r31", "memory"
64  );
65
66  /* If the kernel LWS call succeeded (lws_errno == 0), lws_ret contains
67     the old value from memory.  If this value is equal to OLDVAL, the
68     new value was written to memory.  If not, return -EBUSY.  */
69  if (!lws_errno && lws_ret != oldval)
70    return -EBUSY;
71
72  return lws_errno;
73}
74
75static inline long
76__kernel_cmpxchg2 (volatile void *mem, const void *oldval, const void *newval,
77		   int val_size)
78{
79  register unsigned long lws_mem asm("r26") = (unsigned long) (mem);
80  register unsigned long lws_old asm("r25") = (unsigned long) oldval;
81  register unsigned long lws_new asm("r24") = (unsigned long) newval;
82  register int lws_size asm("r23") = val_size;
83  register long lws_ret   asm("r28");
84  register long lws_errno asm("r21");
85  asm volatile (	"ble	0xb0(%%sr2, %%r0)	\n\t"
86			"ldi	%6, %%r20		\n\t"
87	: "=r" (lws_ret), "=r" (lws_errno), "+r" (lws_mem),
88	  "+r" (lws_old), "+r" (lws_new), "+r" (lws_size)
89	: "i" (2)
90	: "r1", "r20", "r22", "r29", "r31", "fr4", "memory"
91  );
92
93  /* If the kernel LWS call is successful, lws_ret contains 0.  */
94  if (__builtin_expect (lws_ret == 0, 1))
95    return 0;
96
97  /* If the kernel LWS call fails with no error, return -EBUSY */
98  if (__builtin_expect (!lws_errno, 0))
99    return -EBUSY;
100
101  return lws_errno;
102}
103#define HIDDEN __attribute__ ((visibility ("hidden")))
104
105/* Big endian masks  */
106#define INVERT_MASK_1 24
107#define INVERT_MASK_2 16
108
109#define MASK_1 0xffu
110#define MASK_2 0xffffu
111
112#define FETCH_AND_OP_2(OP, PFX_OP, INF_OP, TYPE, WIDTH, INDEX)		\
113  TYPE HIDDEN								\
114  __sync_fetch_and_##OP##_##WIDTH (volatile void *ptr, TYPE val)	\
115  {									\
116    TYPE tmp, newval;							\
117    long failure;							\
118									\
119    do {								\
120      tmp = __atomic_load_n ((volatile TYPE *)ptr, __ATOMIC_RELAXED);	\
121      newval = PFX_OP (tmp INF_OP val);					\
122      failure = __kernel_cmpxchg2 (ptr, &tmp, &newval, INDEX);		\
123    } while (failure != 0);						\
124									\
125    return tmp;								\
126  }
127
128FETCH_AND_OP_2 (add,   , +, u64, 8, 3)
129FETCH_AND_OP_2 (sub,   , -, u64, 8, 3)
130FETCH_AND_OP_2 (or,    , |, u64, 8, 3)
131FETCH_AND_OP_2 (and,   , &, u64, 8, 3)
132FETCH_AND_OP_2 (xor,   , ^, u64, 8, 3)
133FETCH_AND_OP_2 (nand, ~, &, u64, 8, 3)
134
135FETCH_AND_OP_2 (add,   , +, u16, 2, 1)
136FETCH_AND_OP_2 (sub,   , -, u16, 2, 1)
137FETCH_AND_OP_2 (or,    , |, u16, 2, 1)
138FETCH_AND_OP_2 (and,   , &, u16, 2, 1)
139FETCH_AND_OP_2 (xor,   , ^, u16, 2, 1)
140FETCH_AND_OP_2 (nand, ~, &, u16, 2, 1)
141
142FETCH_AND_OP_2 (add,   , +, u8, 1, 0)
143FETCH_AND_OP_2 (sub,   , -, u8, 1, 0)
144FETCH_AND_OP_2 (or,    , |, u8, 1, 0)
145FETCH_AND_OP_2 (and,   , &, u8, 1, 0)
146FETCH_AND_OP_2 (xor,   , ^, u8, 1, 0)
147FETCH_AND_OP_2 (nand, ~, &, u8, 1, 0)
148
149#define OP_AND_FETCH_2(OP, PFX_OP, INF_OP, TYPE, WIDTH, INDEX)		\
150  TYPE HIDDEN								\
151  __sync_##OP##_and_fetch_##WIDTH (volatile void *ptr, TYPE val)	\
152  {									\
153    TYPE tmp, newval;							\
154    long failure;							\
155									\
156    do {								\
157      tmp = __atomic_load_n ((volatile TYPE *)ptr, __ATOMIC_RELAXED);	\
158      newval = PFX_OP (tmp INF_OP val);					\
159      failure = __kernel_cmpxchg2 (ptr, &tmp, &newval, INDEX);		\
160    } while (failure != 0);						\
161									\
162    return PFX_OP (tmp INF_OP val);					\
163  }
164
165OP_AND_FETCH_2 (add,   , +, u64, 8, 3)
166OP_AND_FETCH_2 (sub,   , -, u64, 8, 3)
167OP_AND_FETCH_2 (or,    , |, u64, 8, 3)
168OP_AND_FETCH_2 (and,   , &, u64, 8, 3)
169OP_AND_FETCH_2 (xor,   , ^, u64, 8, 3)
170OP_AND_FETCH_2 (nand, ~, &, u64, 8, 3)
171
172OP_AND_FETCH_2 (add,   , +, u16, 2, 1)
173OP_AND_FETCH_2 (sub,   , -, u16, 2, 1)
174OP_AND_FETCH_2 (or,    , |, u16, 2, 1)
175OP_AND_FETCH_2 (and,   , &, u16, 2, 1)
176OP_AND_FETCH_2 (xor,   , ^, u16, 2, 1)
177OP_AND_FETCH_2 (nand, ~, &, u16, 2, 1)
178
179OP_AND_FETCH_2 (add,   , +, u8, 1, 0)
180OP_AND_FETCH_2 (sub,   , -, u8, 1, 0)
181OP_AND_FETCH_2 (or,    , |, u8, 1, 0)
182OP_AND_FETCH_2 (and,   , &, u8, 1, 0)
183OP_AND_FETCH_2 (xor,   , ^, u8, 1, 0)
184OP_AND_FETCH_2 (nand, ~, &, u8, 1, 0)
185
186#define FETCH_AND_OP_WORD(OP, PFX_OP, INF_OP)				\
187  unsigned int HIDDEN							\
188  __sync_fetch_and_##OP##_4 (volatile void *ptr, unsigned int val)	\
189  {									\
190    unsigned int tmp;							\
191    long failure;							\
192									\
193    do {								\
194      tmp = __atomic_load_n ((volatile unsigned int *)ptr,		\
195			     __ATOMIC_RELAXED);				\
196      failure = __kernel_cmpxchg (ptr, tmp, PFX_OP (tmp INF_OP val));	\
197    } while (failure != 0);						\
198									\
199    return tmp;								\
200  }
201
202FETCH_AND_OP_WORD (add,   , +)
203FETCH_AND_OP_WORD (sub,   , -)
204FETCH_AND_OP_WORD (or,    , |)
205FETCH_AND_OP_WORD (and,   , &)
206FETCH_AND_OP_WORD (xor,   , ^)
207FETCH_AND_OP_WORD (nand, ~, &)
208
209#define OP_AND_FETCH_WORD(OP, PFX_OP, INF_OP)				\
210  unsigned int HIDDEN							\
211  __sync_##OP##_and_fetch_4 (volatile void *ptr, unsigned int val)	\
212  {									\
213    unsigned int tmp;							\
214    long failure;							\
215									\
216    do {								\
217      tmp = __atomic_load_n ((volatile unsigned int *)ptr,		\
218			     __ATOMIC_RELAXED);				\
219      failure = __kernel_cmpxchg (ptr, tmp, PFX_OP (tmp INF_OP val));	\
220    } while (failure != 0);						\
221									\
222    return PFX_OP (tmp INF_OP val);					\
223  }
224
225OP_AND_FETCH_WORD (add,   , +)
226OP_AND_FETCH_WORD (sub,   , -)
227OP_AND_FETCH_WORD (or,    , |)
228OP_AND_FETCH_WORD (and,   , &)
229OP_AND_FETCH_WORD (xor,   , ^)
230OP_AND_FETCH_WORD (nand, ~, &)
231
232typedef unsigned char bool;
233
234#define COMPARE_AND_SWAP_2(TYPE, WIDTH, INDEX)				\
235  TYPE HIDDEN								\
236  __sync_val_compare_and_swap_##WIDTH (volatile void *ptr, TYPE oldval,	\
237				       TYPE newval)			\
238  {									\
239    TYPE actual_oldval;							\
240    long fail;								\
241									\
242    while (1)								\
243      {									\
244	actual_oldval = __atomic_load_n ((volatile TYPE *)ptr,		\
245					 __ATOMIC_RELAXED);		\
246									\
247	if (__builtin_expect (oldval != actual_oldval, 0))		\
248	  return actual_oldval;						\
249									\
250	fail = __kernel_cmpxchg2 (ptr, &actual_oldval, &newval, INDEX);	\
251									\
252	if (__builtin_expect (!fail, 1))				\
253	  return actual_oldval;						\
254      }									\
255  }									\
256									\
257  _Bool HIDDEN								\
258  __sync_bool_compare_and_swap_##WIDTH (volatile void *ptr,		\
259					TYPE oldval, TYPE newval)	\
260  {									\
261    long failure = __kernel_cmpxchg2 (ptr, &oldval, &newval, INDEX);	\
262    return (failure == 0);						\
263  }
264
265COMPARE_AND_SWAP_2 (u64, 8, 3)
266COMPARE_AND_SWAP_2 (u16, 2, 1)
267COMPARE_AND_SWAP_2 (u8, 1, 0)
268
269unsigned int HIDDEN
270__sync_val_compare_and_swap_4 (volatile void *ptr, unsigned int oldval,
271			       unsigned int newval)
272{
273  long fail;
274  unsigned int actual_oldval;
275
276  while (1)
277    {
278      actual_oldval = __atomic_load_n ((volatile unsigned int *)ptr,
279				       __ATOMIC_RELAXED);
280
281      if (__builtin_expect (oldval != actual_oldval, 0))
282	return actual_oldval;
283
284      fail = __kernel_cmpxchg (ptr, actual_oldval, newval);
285
286      if (__builtin_expect (!fail, 1))
287	return actual_oldval;
288    }
289}
290
291_Bool HIDDEN
292__sync_bool_compare_and_swap_4 (volatile void *ptr, unsigned int oldval,
293				unsigned int newval)
294{
295  long failure = __kernel_cmpxchg (ptr, oldval, newval);
296  return (failure == 0);
297}
298
299#define SYNC_LOCK_TEST_AND_SET_2(TYPE, WIDTH, INDEX)			\
300TYPE HIDDEN								\
301  __sync_lock_test_and_set_##WIDTH (volatile void *ptr, TYPE val)	\
302  {									\
303    TYPE oldval;							\
304    long failure;							\
305									\
306    do {								\
307      oldval = __atomic_load_n ((volatile TYPE *)ptr,			\
308				__ATOMIC_RELAXED);			\
309      failure = __kernel_cmpxchg2 (ptr, &oldval, &val, INDEX);		\
310    } while (failure != 0);						\
311									\
312    return oldval;							\
313  }
314
315SYNC_LOCK_TEST_AND_SET_2 (u64, 8, 3)
316SYNC_LOCK_TEST_AND_SET_2 (u16, 2, 1)
317SYNC_LOCK_TEST_AND_SET_2 (u8, 1, 0)
318
319unsigned int HIDDEN
320__sync_lock_test_and_set_4 (volatile void *ptr, unsigned int val)
321{
322  long failure;
323  unsigned int oldval;
324
325  do {
326    oldval = __atomic_load_n ((volatile unsigned int *)ptr, __ATOMIC_RELAXED);
327    failure = __kernel_cmpxchg (ptr, oldval, val);
328  } while (failure != 0);
329
330  return oldval;
331}
332
333#define SYNC_LOCK_RELEASE_1(TYPE, WIDTH, INDEX)			\
334  void HIDDEN							\
335  __sync_lock_release_##WIDTH (volatile void *ptr)		\
336  {								\
337    TYPE oldval, val = 0;					\
338    long failure;						\
339								\
340    do {							\
341      oldval = __atomic_load_n ((volatile TYPE *)ptr,		\
342				__ATOMIC_RELAXED);		\
343      failure = __kernel_cmpxchg2 (ptr, &oldval, &val, INDEX);	\
344    } while (failure != 0);					\
345  }
346
347SYNC_LOCK_RELEASE_1 (u64, 8, 3)
348SYNC_LOCK_RELEASE_1 (u16, 2, 1)
349SYNC_LOCK_RELEASE_1 (u8, 1, 0)
350
351void HIDDEN
352__sync_lock_release_4 (volatile void *ptr)
353{
354  long failure;
355  unsigned int oldval;
356
357  do {
358    oldval = __atomic_load_n ((volatile unsigned int *)ptr, __ATOMIC_RELAXED);
359    failure = __kernel_cmpxchg (ptr, oldval, 0);
360  } while (failure != 0);
361}
362