1/* FreeBSD specific atomic operations for ARM EABI.
2   Copyright (C) 2015 Free Software Foundation, Inc.
3
4This file is part of GCC.
5
6GCC is free software; you can redistribute it and/or modify it under
7the terms of the GNU General Public License as published by the Free
8Software Foundation; either version 3, or (at your option) any later
9version.
10
11GCC is distributed in the hope that it will be useful, but WITHOUT ANY
12WARRANTY; without even the implied warranty of MERCHANTABILITY or
13FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14for more details.
15
16Under Section 7 of GPL version 3, you are granted additional
17permissions described in the GCC Runtime Library Exception, version
183.1, as published by the Free Software Foundation.
19
20You should have received a copy of the GNU General Public License and
21a copy of the GCC Runtime Library Exception along with this program;
22see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
23<http://www.gnu.org/licenses/>.  */
24
25#include <sys/types.h>
26
27#define HIDDEN __attribute__ ((visibility ("hidden")))
28
29#define ARM_VECTORS_HIGH 0xffff0000U
30#define ARM_TP_ADDRESS   (ARM_VECTORS_HIGH + 0x1000)
31#define ARM_RAS_START    (ARM_TP_ADDRESS + 4)
32
33void HIDDEN
34__sync_synchronize (void)
35{
36#if defined (__ARM_ARCH_6__) || defined (__ARM_ARCH_6J__)       \
37    || defined (__ARM_ARCH_6K__) || defined (__ARM_ARCH_6T2__)  \
38    || defined (__ARM_ARCH_6Z__) || defined (__ARM_ARCH_6ZK__)  \
39    || defined (__ARM_ARCH_7__) || defined (__ARM_ARCH_7A__)
40#if defined (__ARM_ARCH_7__) || defined (__ARM_ARCH_7A__)
41    __asm __volatile ("dmb" : : : "memory");
42#else
43    __asm __volatile ("mcr p15, 0, r0, c7, c10, 5" : : : "memory");
44#endif
45#else
46    __asm __volatile ("nop" : : : "memory");
47#endif
48}
49
50#if defined (__ARM_ARCH_6__) || defined (__ARM_ARCH_6J__)        \
51    || defined (__ARM_ARCH_6K__) || defined (__ARM_ARCH_6T2__)   \
52    || defined (__ARM_ARCH_6Z__) || defined (__ARM_ARCH_6ZK__)   \
53    || defined (__ARM_ARCH_7__) || defined (__ARM_ARCH_7A__)
54
55/* These systems should be supported by the compiler.  */
56
57#else /* __ARM_ARCH_5__  */
58
59#define	SYNC_LOCK_TEST_AND_SET_N(N, TYPE, LDR, STR)			\
60TYPE HIDDEN    								\
61__sync_lock_test_and_set_##N (TYPE *mem, TYPE val)			\
62{									\
63        unsigned int old, temp, ras_start;                              \
64                                                                        \
65        ras_start = ARM_RAS_START;					\
66        __asm volatile (						\
67                /* Set up Restartable Atomic Sequence.  */		\
68                "1:"							\
69                "\tadr   %2, 1b\n"					\
70                "\tstr   %2, [%5]\n"					\
71                "\tadr   %2, 2f\n"					\
72                "\tstr   %2, [%5, #4]\n"				\
73                                                                        \
74                "\t"LDR" %0, %4\n"	/* Load old value.  */		\
75                "\t"STR" %3, %1\n"	/* Store new value.  */		\
76                                                                        \
77                /* Tear down Restartable Atomic Sequence.  */		\
78                "2:"							\
79                "\tmov   %2, #0x00000000\n"				\
80                "\tstr   %2, [%5]\n"					\
81                "\tmov   %2, #0xffffffff\n"				\
82                "\tstr   %2, [%5, #4]\n"				\
83                : "=&r" (old), "=m" (*mem), "=&r" (temp)		\
84                : "r" (val), "m" (*mem), "r" (ras_start));		\
85        return (old);							\
86}
87
88#define SYNC_LOCK_RELEASE_N(N, TYPE)					\
89void HIDDEN								\
90__sync_lock_release_##N (TYPE *ptr)     				\
91{					       				\
92    /* All writes before this point must be seen before we release	\
93       the lock itself.  */						\
94    __sync_synchronize ();     						\
95    *ptr = 0;								\
96}
97
98#define	SYNC_VAL_CAS_N(N, TYPE, LDR, STREQ)             		\
99TYPE HIDDEN    								\
100__sync_val_compare_and_swap_##N (TYPE *mem, TYPE expected,		\
101    TYPE desired)							\
102{									\
103        unsigned int old, temp, ras_start;                              \
104                                                                        \
105        ras_start = ARM_RAS_START;					\
106        __asm volatile (						\
107                /* Set up Restartable Atomic Sequence.  */		\
108                "1:"							\
109                "\tadr   %2, 1b\n"					\
110                "\tstr   %2, [%6]\n"					\
111                "\tadr   %2, 2f\n"					\
112                "\tstr   %2, [%6, #4]\n"				\
113                                                                        \
114                "\t"LDR" %0, %5\n"     /* Load old value.  */		\
115                "\tcmp   %0, %3\n"     /* Compare to expected value.  */\
116                "\t"STREQ" %4, %1\n"   /* Store new value.  */		\
117                                                                        \
118                /* Tear down Restartable Atomic Sequence.  */		\
119                "2:"							\
120                "\tmov   %2, #0x00000000\n"				\
121                "\tstr   %2, [%6]\n"					\
122                "\tmov   %2, #0xffffffff\n"				\
123                "\tstr   %2, [%6, #4]\n"				\
124                : "=&r" (old), "=m" (*mem), "=&r" (temp)		\
125                : "r" (expected), "r" (desired), "m" (*mem),		\
126                  "r" (ras_start));					\
127        return (old);							\
128}
129
130typedef unsigned char bool;
131
132#define SYNC_BOOL_CAS_N(N, TYPE)                                        \
133bool HIDDEN								\
134__sync_bool_compare_and_swap_##N (TYPE *ptr, TYPE oldval,		\
135                                  TYPE newval)                          \
136{									\
137    TYPE actual_oldval							\
138      = __sync_val_compare_and_swap_##N (ptr, oldval, newval);          \
139    return (oldval == actual_oldval);					\
140}
141
142#define	SYNC_FETCH_AND_OP_N(N, TYPE, LDR, STR, NAME, OP)		\
143TYPE HIDDEN								\
144__sync_fetch_and_##NAME##_##N (TYPE *mem, TYPE val)	       		\
145{									\
146        unsigned int old, temp, ras_start;                              \
147                                                                        \
148        ras_start = ARM_RAS_START;					\
149        __asm volatile (						\
150                /* Set up Restartable Atomic Sequence.  */		\
151                "1:"							\
152                "\tadr   %2, 1b\n"					\
153                "\tstr   %2, [%5]\n"					\
154                "\tadr   %2, 2f\n"					\
155                "\tstr   %2, [%5, #4]\n"				\
156                                                                        \
157                "\t"LDR" %0, %4\n"	/* Load old value.  */		\
158                "\t"OP"  %2, %0, %3\n"	/* Calculate new value.  */	\
159                "\t"STR" %2, %1\n"	/* Store new value.  */		\
160                                                                        \
161                /* Tear down Restartable Atomic Sequence.  */		\
162                "2:"							\
163                "\tmov   %2, #0x00000000\n"				\
164                "\tstr   %2, [%5]\n"					\
165                "\tmov   %2, #0xffffffff\n"				\
166                "\tstr   %2, [%5, #4]\n"				\
167                : "=&r" (old), "=m" (*mem), "=&r" (temp)		\
168                : "r" (val), "m" (*mem), "r" (ras_start));		\
169        return (old);							\
170}
171
172#define	SYNC_OP_AND_FETCH_N(N, TYPE, LDR, STR, NAME, OP)		\
173TYPE HIDDEN   								\
174__sync_##NAME##_and_fetch_##N (TYPE *mem, TYPE val)	       		\
175{									\
176        unsigned int old, temp, ras_start;                              \
177                                                                        \
178        ras_start = ARM_RAS_START;					\
179        __asm volatile (						\
180                /* Set up Restartable Atomic Sequence.  */		\
181                "1:"							\
182                "\tadr   %2, 1b\n"					\
183                "\tstr   %2, [%5]\n"					\
184                "\tadr   %2, 2f\n"					\
185                "\tstr   %2, [%5, #4]\n"				\
186                                                                        \
187                "\t"LDR" %0, %4\n"	/* Load old value.  */		\
188                "\t"OP"  %2, %0, %3\n"	/* Calculate new value.  */	\
189                "\t"STR" %2, %1\n"	/* Store new value.  */		\
190                                                                        \
191                /* Tear down Restartable Atomic Sequence.  */		\
192                "2:"							\
193                "\tmov   %2, #0x00000000\n"				\
194                "\tstr   %2, [%5]\n"					\
195                "\tmov   %2, #0xffffffff\n"				\
196                "\tstr   %2, [%5, #4]\n"				\
197                : "=&r" (old), "=m" (*mem), "=&r" (temp)		\
198                : "r" (val), "m" (*mem), "r" (ras_start));		\
199        return (old);							\
200}
201
202#define	EMIT_ALL_OPS_N(N, TYPE, LDR, STR, STREQ)			\
203SYNC_LOCK_TEST_AND_SET_N (N, TYPE, LDR, STR)				\
204SYNC_LOCK_RELEASE_N (N, TYPE)                                           \
205SYNC_VAL_CAS_N (N, TYPE, LDR, STREQ)	                		\
206SYNC_BOOL_CAS_N (N, TYPE)                                               \
207SYNC_FETCH_AND_OP_N (N, TYPE, LDR, STR, add, "add")		        \
208SYNC_FETCH_AND_OP_N (N, TYPE, LDR, STR, and, "and")		        \
209SYNC_FETCH_AND_OP_N (N, TYPE, LDR, STR, or, "orr")		        \
210SYNC_FETCH_AND_OP_N (N, TYPE, LDR, STR, sub, "sub")		        \
211SYNC_FETCH_AND_OP_N (N, TYPE, LDR, STR, xor, "eor")                     \
212SYNC_OP_AND_FETCH_N (N, TYPE, LDR, STR, add, "add")		        \
213SYNC_OP_AND_FETCH_N (N, TYPE, LDR, STR, and, "and")		        \
214SYNC_OP_AND_FETCH_N (N, TYPE, LDR, STR, or, "orr")		        \
215SYNC_OP_AND_FETCH_N (N, TYPE, LDR, STR, sub, "sub")		        \
216SYNC_OP_AND_FETCH_N (N, TYPE, LDR, STR, xor, "eor")
217
218
219
220EMIT_ALL_OPS_N (1, unsigned char, "ldrb", "strb", "streqb")
221EMIT_ALL_OPS_N (2, unsigned short, "ldrh", "strh", "streqh")
222EMIT_ALL_OPS_N (4, unsigned int, "ldr", "str", "streq")
223
224#endif
225