linux-atomic.c revision 1.1.1.1
1/* Linux-specific atomic operations for PA Linux.
2   Copyright (C) 2008-2013 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
31/* All PA-RISC implementations supported by linux have strongly
32   ordered loads and stores.  Only cache flushes and purges can be
33   delayed.  The data cache implementations are all globally
34   coherent.  Thus, there is no need to synchonize memory accesses.
35
36   GCC automatically issues a asm memory barrier when it encounters
37   a __sync_synchronize builtin.  Thus, we do not need to define this
38   builtin.
39
40   We implement byte, short and int versions of each atomic operation
41   using the kernel helper defined below.  There is no support for
42   64-bit operations yet.  */
43
44/* A privileged instruction to crash a userspace program with SIGILL.  */
45#define ABORT_INSTRUCTION asm ("iitlbp %r0,(%sr0, %r0)")
46
47/* Determine kernel LWS function call (0=32-bit, 1=64-bit userspace).  */
48#define LWS_CAS (sizeof(unsigned long) == 4 ? 0 : 1)
49
50/* Kernel helper for compare-and-exchange a 32-bit value.  */
51static inline long
52__kernel_cmpxchg (int oldval, int newval, int *mem)
53{
54  register unsigned long lws_mem asm("r26") = (unsigned long) (mem);
55  register long lws_ret   asm("r28");
56  register long lws_errno asm("r21");
57  register int lws_old asm("r25") = oldval;
58  register int lws_new asm("r24") = newval;
59  asm volatile (	"ble	0xb0(%%sr2, %%r0)	\n\t"
60			"ldi	%5, %%r20		\n\t"
61	: "=r" (lws_ret), "=r" (lws_errno), "=r" (lws_mem),
62	  "=r" (lws_old), "=r" (lws_new)
63	: "i" (LWS_CAS), "2" (lws_mem), "3" (lws_old), "4" (lws_new)
64	: "r1", "r20", "r22", "r23", "r29", "r31", "memory"
65  );
66  if (__builtin_expect (lws_errno == -EFAULT || lws_errno == -ENOSYS, 0))
67    ABORT_INSTRUCTION;
68
69  /* If the kernel LWS call succeeded (lws_errno == 0), lws_ret contains
70     the old value from memory.  If this value is equal to OLDVAL, the
71     new value was written to memory.  If not, return -EBUSY.  */
72  if (!lws_errno && lws_ret != oldval)
73    lws_errno = -EBUSY;
74
75  return lws_errno;
76}
77
78#define HIDDEN __attribute__ ((visibility ("hidden")))
79
80/* Big endian masks  */
81#define INVERT_MASK_1 24
82#define INVERT_MASK_2 16
83
84#define MASK_1 0xffu
85#define MASK_2 0xffffu
86
87#define FETCH_AND_OP_WORD(OP, PFX_OP, INF_OP)				\
88  int HIDDEN								\
89  __sync_fetch_and_##OP##_4 (int *ptr, int val)				\
90  {									\
91    int failure, tmp;							\
92									\
93    do {								\
94      tmp = *ptr;							\
95      failure = __kernel_cmpxchg (tmp, PFX_OP (tmp INF_OP val), ptr);	\
96    } while (failure != 0);						\
97									\
98    return tmp;								\
99  }
100
101FETCH_AND_OP_WORD (add,   , +)
102FETCH_AND_OP_WORD (sub,   , -)
103FETCH_AND_OP_WORD (or,    , |)
104FETCH_AND_OP_WORD (and,   , &)
105FETCH_AND_OP_WORD (xor,   , ^)
106FETCH_AND_OP_WORD (nand, ~, &)
107
108#define NAME_oldval(OP, WIDTH) __sync_fetch_and_##OP##_##WIDTH
109#define NAME_newval(OP, WIDTH) __sync_##OP##_and_fetch_##WIDTH
110
111/* Implement both __sync_<op>_and_fetch and __sync_fetch_and_<op> for
112   subword-sized quantities.  */
113
114#define SUBWORD_SYNC_OP(OP, PFX_OP, INF_OP, TYPE, WIDTH, RETURN)	\
115  TYPE HIDDEN								\
116  NAME##_##RETURN (OP, WIDTH) (TYPE *ptr, TYPE val)			\
117  {									\
118    int *wordptr = (int *) ((unsigned long) ptr & ~3);			\
119    unsigned int mask, shift, oldval, newval;				\
120    int failure;							\
121									\
122    shift = (((unsigned long) ptr & 3) << 3) ^ INVERT_MASK_##WIDTH;	\
123    mask = MASK_##WIDTH << shift;					\
124									\
125    do {								\
126      oldval = *wordptr;						\
127      newval = ((PFX_OP (((oldval & mask) >> shift)			\
128                         INF_OP (unsigned int) val)) << shift) & mask;	\
129      newval |= oldval & ~mask;						\
130      failure = __kernel_cmpxchg (oldval, newval, wordptr);		\
131    } while (failure != 0);						\
132									\
133    return (RETURN & mask) >> shift;					\
134  }
135
136SUBWORD_SYNC_OP (add,   , +, unsigned short, 2, oldval)
137SUBWORD_SYNC_OP (sub,   , -, unsigned short, 2, oldval)
138SUBWORD_SYNC_OP (or,    , |, unsigned short, 2, oldval)
139SUBWORD_SYNC_OP (and,   , &, unsigned short, 2, oldval)
140SUBWORD_SYNC_OP (xor,   , ^, unsigned short, 2, oldval)
141SUBWORD_SYNC_OP (nand, ~, &, unsigned short, 2, oldval)
142
143SUBWORD_SYNC_OP (add,   , +, unsigned char, 1, oldval)
144SUBWORD_SYNC_OP (sub,   , -, unsigned char, 1, oldval)
145SUBWORD_SYNC_OP (or,    , |, unsigned char, 1, oldval)
146SUBWORD_SYNC_OP (and,   , &, unsigned char, 1, oldval)
147SUBWORD_SYNC_OP (xor,   , ^, unsigned char, 1, oldval)
148SUBWORD_SYNC_OP (nand, ~, &, unsigned char, 1, oldval)
149
150#define OP_AND_FETCH_WORD(OP, PFX_OP, INF_OP)				\
151  int HIDDEN								\
152  __sync_##OP##_and_fetch_4 (int *ptr, int val)				\
153  {									\
154    int tmp, failure;							\
155									\
156    do {								\
157      tmp = *ptr;							\
158      failure = __kernel_cmpxchg (tmp, PFX_OP (tmp INF_OP val), ptr);	\
159    } while (failure != 0);						\
160									\
161    return PFX_OP (tmp INF_OP val);					\
162  }
163
164OP_AND_FETCH_WORD (add,   , +)
165OP_AND_FETCH_WORD (sub,   , -)
166OP_AND_FETCH_WORD (or,    , |)
167OP_AND_FETCH_WORD (and,   , &)
168OP_AND_FETCH_WORD (xor,   , ^)
169OP_AND_FETCH_WORD (nand, ~, &)
170
171SUBWORD_SYNC_OP (add,   , +, unsigned short, 2, newval)
172SUBWORD_SYNC_OP (sub,   , -, unsigned short, 2, newval)
173SUBWORD_SYNC_OP (or,    , |, unsigned short, 2, newval)
174SUBWORD_SYNC_OP (and,   , &, unsigned short, 2, newval)
175SUBWORD_SYNC_OP (xor,   , ^, unsigned short, 2, newval)
176SUBWORD_SYNC_OP (nand, ~, &, unsigned short, 2, newval)
177
178SUBWORD_SYNC_OP (add,   , +, unsigned char, 1, newval)
179SUBWORD_SYNC_OP (sub,   , -, unsigned char, 1, newval)
180SUBWORD_SYNC_OP (or,    , |, unsigned char, 1, newval)
181SUBWORD_SYNC_OP (and,   , &, unsigned char, 1, newval)
182SUBWORD_SYNC_OP (xor,   , ^, unsigned char, 1, newval)
183SUBWORD_SYNC_OP (nand, ~, &, unsigned char, 1, newval)
184
185int HIDDEN
186__sync_val_compare_and_swap_4 (int *ptr, int oldval, int newval)
187{
188  int actual_oldval, fail;
189
190  while (1)
191    {
192      actual_oldval = *ptr;
193
194      if (__builtin_expect (oldval != actual_oldval, 0))
195	return actual_oldval;
196
197      fail = __kernel_cmpxchg (actual_oldval, newval, ptr);
198
199      if (__builtin_expect (!fail, 1))
200	return actual_oldval;
201    }
202}
203
204#define SUBWORD_VAL_CAS(TYPE, WIDTH)					\
205  TYPE HIDDEN								\
206  __sync_val_compare_and_swap_##WIDTH (TYPE *ptr, TYPE oldval,		\
207				       TYPE newval)			\
208  {									\
209    int *wordptr = (int *)((unsigned long) ptr & ~3), fail;		\
210    unsigned int mask, shift, actual_oldval, actual_newval;		\
211									\
212    shift = (((unsigned long) ptr & 3) << 3) ^ INVERT_MASK_##WIDTH;	\
213    mask = MASK_##WIDTH << shift;					\
214									\
215    while (1)								\
216      {									\
217	actual_oldval = *wordptr;					\
218									\
219	if (__builtin_expect (((actual_oldval & mask) >> shift)		\
220			      != (unsigned int) oldval, 0))		\
221	  return (actual_oldval & mask) >> shift;			\
222									\
223	actual_newval = (actual_oldval & ~mask)				\
224			| (((unsigned int) newval << shift) & mask);	\
225									\
226	fail = __kernel_cmpxchg (actual_oldval, actual_newval,		\
227				 wordptr);				\
228									\
229	if (__builtin_expect (!fail, 1))				\
230	  return (actual_oldval & mask) >> shift;			\
231      }									\
232  }
233
234SUBWORD_VAL_CAS (unsigned short, 2)
235SUBWORD_VAL_CAS (unsigned char,  1)
236
237typedef unsigned char bool;
238
239bool HIDDEN
240__sync_bool_compare_and_swap_4 (int *ptr, int oldval, int newval)
241{
242  int failure = __kernel_cmpxchg (oldval, newval, ptr);
243  return (failure == 0);
244}
245
246#define SUBWORD_BOOL_CAS(TYPE, WIDTH)					\
247  bool HIDDEN								\
248  __sync_bool_compare_and_swap_##WIDTH (TYPE *ptr, TYPE oldval,		\
249					TYPE newval)			\
250  {									\
251    TYPE actual_oldval							\
252      = __sync_val_compare_and_swap_##WIDTH (ptr, oldval, newval);	\
253    return (oldval == actual_oldval);					\
254  }
255
256SUBWORD_BOOL_CAS (unsigned short, 2)
257SUBWORD_BOOL_CAS (unsigned char,  1)
258
259int HIDDEN
260__sync_lock_test_and_set_4 (int *ptr, int val)
261{
262  int failure, oldval;
263
264  do {
265    oldval = *ptr;
266    failure = __kernel_cmpxchg (oldval, val, ptr);
267  } while (failure != 0);
268
269  return oldval;
270}
271
272#define SUBWORD_TEST_AND_SET(TYPE, WIDTH)				\
273  TYPE HIDDEN								\
274  __sync_lock_test_and_set_##WIDTH (TYPE *ptr, TYPE val)		\
275  {									\
276    int failure;							\
277    unsigned int oldval, newval, shift, mask;				\
278    int *wordptr = (int *) ((unsigned long) ptr & ~3);			\
279									\
280    shift = (((unsigned long) ptr & 3) << 3) ^ INVERT_MASK_##WIDTH;	\
281    mask = MASK_##WIDTH << shift;					\
282									\
283    do {								\
284      oldval = *wordptr;						\
285      newval = (oldval & ~mask)						\
286	       | (((unsigned int) val << shift) & mask);		\
287      failure = __kernel_cmpxchg (oldval, newval, wordptr);		\
288    } while (failure != 0);						\
289									\
290    return (oldval & mask) >> shift;					\
291  }
292
293SUBWORD_TEST_AND_SET (unsigned short, 2)
294SUBWORD_TEST_AND_SET (unsigned char,  1)
295
296#define SYNC_LOCK_RELEASE(TYPE, WIDTH)					\
297  void HIDDEN								\
298  __sync_lock_release_##WIDTH (TYPE *ptr)				\
299  {									\
300    *ptr = 0;								\
301  }
302
303SYNC_LOCK_RELEASE (int,   4)
304SYNC_LOCK_RELEASE (short, 2)
305SYNC_LOCK_RELEASE (char,  1)
306