atomic.h revision 315371
1/*-
2 * Copyright (c) 2013 Andrew Turner <andrew@freebsd.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 * $FreeBSD: stable/11/sys/arm64/include/atomic.h 315371 2017-03-16 06:00:27Z mjg $
27 */
28
29#ifndef	_MACHINE_ATOMIC_H_
30#define	_MACHINE_ATOMIC_H_
31
32#define	isb()		__asm __volatile("isb" : : : "memory")
33
34/*
35 * Options for DMB and DSB:
36 *	oshld	Outer Shareable, load
37 *	oshst	Outer Shareable, store
38 *	osh	Outer Shareable, all
39 *	nshld	Non-shareable, load
40 *	nshst	Non-shareable, store
41 *	nsh	Non-shareable, all
42 *	ishld	Inner Shareable, load
43 *	ishst	Inner Shareable, store
44 *	ish	Inner Shareable, all
45 *	ld	Full system, load
46 *	st	Full system, store
47 *	sy	Full system, all
48 */
49#define	dsb(opt)	__asm __volatile("dsb " __STRING(opt) : : : "memory")
50#define	dmb(opt)	__asm __volatile("dmb " __STRING(opt) : : : "memory")
51
52#define	mb()	dmb(sy)	/* Full system memory barrier all */
53#define	wmb()	dmb(st)	/* Full system memory barrier store */
54#define	rmb()	dmb(ld)	/* Full system memory barrier load */
55
56#define	ATOMIC_OP(op, asm_op, bar, a, l)				\
57static __inline void							\
58atomic_##op##_##bar##32(volatile uint32_t *p, uint32_t val)		\
59{									\
60	uint32_t tmp;							\
61	int res;							\
62									\
63	__asm __volatile(						\
64	    "1: ld"#a"xr   %w0, [%2]      \n"				\
65	    "   "#asm_op"  %w0, %w0, %w3  \n"				\
66	    "   st"#l"xr   %w1, %w0, [%2] \n"				\
67            "   cbnz       %w1, 1b        \n"				\
68	    : "=&r"(tmp), "=&r"(res)					\
69	    : "r" (p), "r" (val)					\
70	    : "memory"							\
71	);								\
72}									\
73									\
74static __inline void							\
75atomic_##op##_##bar##64(volatile uint64_t *p, uint64_t val)		\
76{									\
77	uint64_t tmp;							\
78	int res;							\
79									\
80	__asm __volatile(						\
81	    "1: ld"#a"xr   %0, [%2]      \n"				\
82	    "   "#asm_op"  %0, %0, %3    \n"				\
83	    "   st"#l"xr   %w1, %0, [%2] \n"				\
84            "   cbnz       %w1, 1b       \n"				\
85	    : "=&r"(tmp), "=&r"(res)					\
86	    : "r" (p), "r" (val)					\
87	    : "memory"							\
88	);								\
89}
90
91#define	ATOMIC(op, asm_op)						\
92    ATOMIC_OP(op, asm_op,     ,  ,  )					\
93    ATOMIC_OP(op, asm_op, acq_, a,  )					\
94    ATOMIC_OP(op, asm_op, rel_,  , l)					\
95
96ATOMIC(add,      add)
97ATOMIC(clear,    bic)
98ATOMIC(set,      orr)
99ATOMIC(subtract, sub)
100
101#define	ATOMIC_FCMPSET(bar, a, l)					\
102static __inline int							\
103atomic_fcmpset_##bar##32(volatile uint32_t *p, uint32_t *cmpval,	\
104    uint32_t newval)		 					\
105{									\
106	uint32_t tmp;							\
107	uint32_t _cmpval = *cmpval;					\
108	int res;							\
109									\
110	__asm __volatile(						\
111	    "1: mov      %w1, #1        \n"				\
112	    "   ld"#a"xr %w0, [%2]      \n"				\
113	    "   cmp      %w0, %w3       \n"				\
114	    "   b.ne     2f             \n"				\
115	    "   st"#l"xr %w1, %w4, [%2] \n"				\
116	    "2:"							\
117	    : "=&r"(tmp), "=&r"(res)					\
118	    : "r" (p), "r" (_cmpval), "r" (newval)			\
119	    : "cc", "memory"						\
120	);								\
121	*cmpval = tmp;							\
122									\
123	return (!res);							\
124}									\
125									\
126static __inline int							\
127atomic_fcmpset_##bar##64(volatile uint64_t *p, uint64_t *cmpval,	\
128    uint64_t newval)							\
129{									\
130	uint64_t tmp;							\
131	uint64_t _cmpval = *cmpval;					\
132	int res;							\
133									\
134	__asm __volatile(						\
135	    "1: mov      %w1, #1       \n"				\
136	    "   ld"#a"xr %0, [%2]      \n"				\
137	    "   cmp      %0, %3        \n"				\
138	    "   b.ne     2f            \n"				\
139	    "   st"#l"xr %w1, %4, [%2] \n"				\
140	    "2:"							\
141	    : "=&r"(tmp), "=&r"(res)					\
142	    : "r" (p), "r" (_cmpval), "r" (newval)			\
143	    : "cc", "memory"						\
144	);								\
145	*cmpval = tmp;							\
146									\
147	return (!res);							\
148}
149
150ATOMIC_FCMPSET(    ,  , )
151ATOMIC_FCMPSET(acq_, a, )
152ATOMIC_FCMPSET(rel_,  ,l)
153
154#undef ATOMIC_FCMPSET
155
156#define	ATOMIC_CMPSET(bar, a, l)					\
157static __inline int							\
158atomic_cmpset_##bar##32(volatile uint32_t *p, uint32_t cmpval,		\
159    uint32_t newval)							\
160{									\
161	uint32_t tmp;							\
162	int res;							\
163									\
164	__asm __volatile(						\
165	    "1: mov      %w1, #1        \n"				\
166	    "   ld"#a"xr %w0, [%2]      \n"				\
167	    "   cmp      %w0, %w3       \n"				\
168	    "   b.ne     2f             \n"				\
169	    "   st"#l"xr %w1, %w4, [%2] \n"				\
170            "   cbnz     %w1, 1b        \n"				\
171	    "2:"							\
172	    : "=&r"(tmp), "=&r"(res)					\
173	    : "r" (p), "r" (cmpval), "r" (newval)			\
174	    : "cc", "memory"							\
175	);								\
176									\
177	return (!res);							\
178}									\
179									\
180static __inline int							\
181atomic_cmpset_##bar##64(volatile uint64_t *p, uint64_t cmpval,		\
182    uint64_t newval)							\
183{									\
184	uint64_t tmp;							\
185	int res;							\
186									\
187	__asm __volatile(						\
188	    "1: mov      %w1, #1       \n"				\
189	    "   ld"#a"xr %0, [%2]      \n"				\
190	    "   cmp      %0, %3        \n"				\
191	    "   b.ne     2f            \n"				\
192	    "   st"#l"xr %w1, %4, [%2] \n"				\
193            "   cbnz     %w1, 1b       \n"				\
194	    "2:"							\
195	    : "=&r"(tmp), "=&r"(res)					\
196	    : "r" (p), "r" (cmpval), "r" (newval)			\
197	    : "cc", "memory"							\
198	);								\
199									\
200	return (!res);							\
201}
202
203ATOMIC_CMPSET(    ,  , )
204ATOMIC_CMPSET(acq_, a, )
205ATOMIC_CMPSET(rel_,  ,l)
206
207static __inline uint32_t
208atomic_fetchadd_32(volatile uint32_t *p, uint32_t val)
209{
210	uint32_t tmp, ret;
211	int res;
212
213	__asm __volatile(
214	    "1: ldxr	%w2, [%3]      \n"
215	    "   add	%w0, %w2, %w4  \n"
216	    "   stxr	%w1, %w0, [%3] \n"
217            "   cbnz	%w1, 1b        \n"
218	    : "=&r"(tmp), "=&r"(res), "=&r"(ret)
219	    : "r" (p), "r" (val)
220	    : "memory"
221	);
222
223	return (ret);
224}
225
226static __inline uint64_t
227atomic_fetchadd_64(volatile uint64_t *p, uint64_t val)
228{
229	uint64_t tmp, ret;
230	int res;
231
232	__asm __volatile(
233	    "1: ldxr	%2, [%3]      \n"
234	    "   add	%0, %2, %4    \n"
235	    "   stxr	%w1, %0, [%3] \n"
236            "   cbnz	%w1, 1b       \n"
237	    : "=&r"(tmp), "=&r"(res), "=&r"(ret)
238	    : "r" (p), "r" (val)
239	    : "memory"
240	);
241
242	return (ret);
243}
244
245static __inline uint32_t
246atomic_readandclear_32(volatile uint32_t *p)
247{
248	uint32_t ret;
249	int res;
250
251	__asm __volatile(
252	    "1: ldxr	%w1, [%2]      \n"
253	    "   stxr	%w0, wzr, [%2] \n"
254            "   cbnz	%w0, 1b        \n"
255	    : "=&r"(res), "=&r"(ret)
256	    : "r" (p)
257	    : "memory"
258	);
259
260	return (ret);
261}
262
263static __inline uint64_t
264atomic_readandclear_64(volatile uint64_t *p)
265{
266	uint64_t ret;
267	int res;
268
269	__asm __volatile(
270	    "1: ldxr	%1, [%2]      \n"
271	    "   stxr	%w0, xzr, [%2] \n"
272            "   cbnz	%w0, 1b        \n"
273	    : "=&r"(res), "=&r"(ret)
274	    : "r" (p)
275	    : "memory"
276	);
277
278	return (ret);
279}
280
281static __inline uint32_t
282atomic_swap_32(volatile uint32_t *p, uint32_t val)
283{
284	uint32_t ret;
285	int res;
286
287	__asm __volatile(
288	    "1: ldxr	%w0, [%2]      \n"
289	    "   stxr	%w1, %w3, [%2] \n"
290	    "   cbnz	%w1, 1b        \n"
291	    : "=&r"(ret), "=&r"(res)
292	    : "r" (p), "r" (val)
293	    : "memory"
294	);
295
296	return (ret);
297}
298
299static __inline uint64_t
300atomic_swap_64(volatile uint64_t *p, uint64_t val)
301{
302	uint64_t ret;
303	int res;
304
305	__asm __volatile(
306	    "1: ldxr	%0, [%2]      \n"
307	    "   stxr	%w1, %3, [%2] \n"
308	    "   cbnz	%w1, 1b       \n"
309	    : "=&r"(ret), "=&r"(res)
310	    : "r" (p), "r" (val)
311	    : "memory"
312	);
313
314	return (ret);
315}
316
317static __inline uint32_t
318atomic_load_acq_32(volatile uint32_t *p)
319{
320	uint32_t ret;
321
322	__asm __volatile(
323	    "ldar	%w0, [%1] \n"
324	    : "=&r" (ret)
325	    : "r" (p)
326	    : "memory");
327
328	return (ret);
329}
330
331static __inline uint64_t
332atomic_load_acq_64(volatile uint64_t *p)
333{
334	uint64_t ret;
335
336	__asm __volatile(
337	    "ldar	%0, [%1] \n"
338	    : "=&r" (ret)
339	    : "r" (p)
340	    : "memory");
341
342	return (ret);
343}
344
345static __inline void
346atomic_store_rel_32(volatile uint32_t *p, uint32_t val)
347{
348
349	__asm __volatile(
350	    "stlr	%w0, [%1] \n"
351	    :
352	    : "r" (val), "r" (p)
353	    : "memory");
354}
355
356static __inline void
357atomic_store_rel_64(volatile uint64_t *p, uint64_t val)
358{
359
360	__asm __volatile(
361	    "stlr	%0, [%1] \n"
362	    :
363	    : "r" (val), "r" (p)
364	    : "memory");
365}
366
367
368#define	atomic_add_int			atomic_add_32
369#define	atomic_fcmpset_int		atomic_fcmpset_32
370#define	atomic_clear_int		atomic_clear_32
371#define	atomic_cmpset_int		atomic_cmpset_32
372#define	atomic_fetchadd_int		atomic_fetchadd_32
373#define	atomic_readandclear_int		atomic_readandclear_32
374#define	atomic_set_int			atomic_set_32
375#define	atomic_swap_int			atomic_swap_32
376#define	atomic_subtract_int		atomic_subtract_32
377
378#define	atomic_add_acq_int		atomic_add_acq_32
379#define	atomic_fcmpset_acq_int		atomic_fcmpset_acq_32
380#define	atomic_clear_acq_int		atomic_clear_acq_32
381#define	atomic_cmpset_acq_int		atomic_cmpset_acq_32
382#define	atomic_load_acq_int		atomic_load_acq_32
383#define	atomic_set_acq_int		atomic_set_acq_32
384#define	atomic_subtract_acq_int		atomic_subtract_acq_32
385
386#define	atomic_add_rel_int		atomic_add_rel_32
387#define	atomic_fcmpset_rel_int		atomic_fcmpset_rel_32
388#define	atomic_clear_rel_int		atomic_add_rel_32
389#define	atomic_cmpset_rel_int		atomic_cmpset_rel_32
390#define	atomic_set_rel_int		atomic_set_rel_32
391#define	atomic_subtract_rel_int		atomic_subtract_rel_32
392#define	atomic_store_rel_int		atomic_store_rel_32
393
394#define	atomic_add_long			atomic_add_64
395#define	atomic_fcmpset_long		atomic_fcmpset_64
396#define	atomic_clear_long		atomic_clear_64
397#define	atomic_cmpset_long		atomic_cmpset_64
398#define	atomic_fetchadd_long		atomic_fetchadd_64
399#define	atomic_readandclear_long	atomic_readandclear_64
400#define	atomic_set_long			atomic_set_64
401#define	atomic_swap_long		atomic_swap_64
402#define	atomic_subtract_long		atomic_subtract_64
403
404#define	atomic_add_ptr			atomic_add_64
405#define	atomic_fcmpset_ptr		atomic_fcmpset_64
406#define	atomic_clear_ptr		atomic_clear_64
407#define	atomic_cmpset_ptr		atomic_cmpset_64
408#define	atomic_fetchadd_ptr		atomic_fetchadd_64
409#define	atomic_readandclear_ptr		atomic_readandclear_64
410#define	atomic_set_ptr			atomic_set_64
411#define	atomic_swap_ptr			atomic_swap_64
412#define	atomic_subtract_ptr		atomic_subtract_64
413
414#define	atomic_add_acq_long		atomic_add_acq_64
415#define	atomic_fcmpset_acq_long		atomic_fcmpset_acq_64
416#define	atomic_clear_acq_long		atomic_add_acq_64
417#define	atomic_cmpset_acq_long		atomic_cmpset_acq_64
418#define	atomic_load_acq_long		atomic_load_acq_64
419#define	atomic_set_acq_long		atomic_set_acq_64
420#define	atomic_subtract_acq_long	atomic_subtract_acq_64
421
422#define	atomic_add_acq_ptr		atomic_add_acq_64
423#define	atomic_fcmpset_acq_ptr		atomic_fcmpset_acq_64
424#define	atomic_clear_acq_ptr		atomic_add_acq_64
425#define	atomic_cmpset_acq_ptr		atomic_cmpset_acq_64
426#define	atomic_load_acq_ptr		atomic_load_acq_64
427#define	atomic_set_acq_ptr		atomic_set_acq_64
428#define	atomic_subtract_acq_ptr		atomic_subtract_acq_64
429
430#define	atomic_add_rel_long		atomic_add_rel_64
431#define	atomic_fcmpset_rel_long		atomic_fcmpset_rel_64
432#define	atomic_clear_rel_long		atomic_clear_rel_64
433#define	atomic_cmpset_rel_long		atomic_cmpset_rel_64
434#define	atomic_set_rel_long		atomic_set_rel_64
435#define	atomic_subtract_rel_long	atomic_subtract_rel_64
436#define	atomic_store_rel_long		atomic_store_rel_64
437
438#define	atomic_add_rel_ptr		atomic_add_rel_64
439#define	atomic_fcmpset_rel_ptr		atomic_fcmpset_rel_64
440#define	atomic_clear_rel_ptr		atomic_clear_rel_64
441#define	atomic_cmpset_rel_ptr		atomic_cmpset_rel_64
442#define	atomic_set_rel_ptr		atomic_set_rel_64
443#define	atomic_subtract_rel_ptr		atomic_subtract_rel_64
444#define	atomic_store_rel_ptr		atomic_store_rel_64
445
446static __inline void
447atomic_thread_fence_acq(void)
448{
449
450	dmb(ld);
451}
452
453static __inline void
454atomic_thread_fence_rel(void)
455{
456
457	dmb(sy);
458}
459
460static __inline void
461atomic_thread_fence_acq_rel(void)
462{
463
464	dmb(sy);
465}
466
467static __inline void
468atomic_thread_fence_seq_cst(void)
469{
470
471	dmb(sy);
472}
473
474#endif /* _MACHINE_ATOMIC_H_ */
475
476