source: mainline/uspace/lib/c/arch/arm32/src/atomic.c@ 67cdc670

Last change on this file since 67cdc670 was 67cdc670, checked in by Miroslav Cimerman <mc@…>, 6 months ago

lib/c/arch/arm32: add atomic_{load,store}_8

  • Property mode set to 100644
File size: 8.9 KB
Line 
1/*
2 * Copyright (c) 2007 Michal Kebrt
3 * Copyright (c) 2018 CZ.NIC, z.s.p.o.
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 *
10 * - Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * - Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * - The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30/*
31 * Older ARMs don't have atomic instructions, so we need to define a bunch
32 * of symbols for GCC to use.
33 */
34
35#include <stdbool.h>
36#include "ras_page.h"
37
38volatile unsigned *ras_page;
39
40unsigned long long __atomic_load_8(const volatile void *mem0, int model)
41{
42 const volatile unsigned long long *mem = mem0;
43
44 (void) model;
45
46 unsigned long long ret;
47
48 /*
49 * The following instructions between labels 1 and 2 constitute a
50 * Restartable Atomic Seqeunce. Should the sequence be non-atomic,
51 * the kernel will restart it.
52 */
53 asm volatile (
54 "1:\n"
55 " adr %[ret], 1b\n"
56 " str %[ret], %[rp0]\n"
57 " adr %[ret], 2f\n"
58 " str %[ret], %[rp1]\n"
59
60 " ldrd %[ret], %[addr]\n"
61 "2:\n"
62 : [ret] "=&r" (ret),
63 [rp0] "=m" (ras_page[0]),
64 [rp1] "=m" (ras_page[1])
65 : [addr] "m" (*mem)
66 );
67
68 ras_page[0] = 0;
69 ras_page[1] = 0xffffffff;
70
71 return ret;
72}
73
74void __atomic_store_8(volatile void *mem0, unsigned long long val, int model)
75{
76 volatile unsigned long long *mem = mem0;
77
78 (void) model;
79
80 /* scratch register */
81 unsigned tmp;
82
83 /*
84 * The following instructions between labels 1 and 2 constitute a
85 * Restartable Atomic Seqeunce. Should the sequence be non-atomic,
86 * the kernel will restart it.
87 */
88 asm volatile (
89 "1:\n"
90 " adr %[tmp], 1b\n"
91 " str %[tmp], %[rp0]\n"
92 " adr %[tmp], 2f\n"
93 " str %[tmp], %[rp1]\n"
94
95 " strd %[imm], %[addr]\n"
96 "2:\n"
97 : [tmp] "=&r" (tmp),
98 [rp0] "=m" (ras_page[0]),
99 [rp1] "=m" (ras_page[1]),
100 [addr] "=m" (*mem)
101 : [imm] "r" (val)
102 );
103
104 ras_page[0] = 0;
105 ras_page[1] = 0xffffffff;
106}
107
108bool __atomic_compare_exchange_4(volatile void *mem0, void *expected0,
109 unsigned desired, bool weak, int success, int failure)
110{
111 volatile unsigned *mem = mem0;
112 unsigned *expected = expected0;
113
114 (void) success;
115 (void) failure;
116 (void) weak;
117
118 unsigned ov = *expected;
119 unsigned ret;
120
121 /*
122 * The following instructions between labels 1 and 2 constitute a
123 * Restartable Atomic Sequence. Should the sequence be non-atomic,
124 * the kernel will restart it.
125 */
126 asm volatile (
127 "1:\n"
128 " adr %[ret], 1b\n"
129 " str %[ret], %[rp0]\n"
130 " adr %[ret], 2f\n"
131 " str %[ret], %[rp1]\n"
132
133 " ldr %[ret], %[addr]\n"
134 " cmp %[ret], %[ov]\n"
135 " streq %[nv], %[addr]\n"
136 "2:\n"
137 : [ret] "=&r" (ret),
138 [rp0] "=m" (ras_page[0]),
139 [rp1] "=m" (ras_page[1]),
140 [addr] "+m" (*mem)
141 : [ov] "r" (ov),
142 [nv] "r" (desired)
143 : "memory"
144 );
145
146 ras_page[0] = 0;
147 ras_page[1] = 0xffffffff;
148
149 if (ret == ov)
150 return true;
151
152 *expected = ret;
153 return false;
154}
155
156unsigned char __atomic_exchange_1(volatile void *mem0, unsigned char val,
157 int model)
158{
159 volatile unsigned char *mem = mem0;
160
161 (void) model;
162
163 unsigned ret;
164
165 /*
166 * The following instructions between labels 1 and 2 constitute a
167 * Restartable Atomic Seqeunce. Should the sequence be non-atomic,
168 * the kernel will restart it.
169 */
170 asm volatile (
171 "1:\n"
172 " adr %[ret], 1b\n"
173 " str %[ret], %[rp0]\n"
174 " adr %[ret], 2f\n"
175 " str %[ret], %[rp1]\n"
176 " ldrb %[ret], %[addr]\n"
177 " strb %[imm], %[addr]\n"
178 "2:\n"
179 : [ret] "=&r" (ret),
180 [rp0] "=m" (ras_page[0]),
181 [rp1] "=m" (ras_page[1]),
182 [addr] "+m" (*mem)
183 : [imm] "r" (val)
184 );
185
186 ras_page[0] = 0;
187 ras_page[1] = 0xffffffff;
188
189 return ret;
190}
191
192unsigned short __atomic_exchange_2(volatile void *mem0, unsigned short val,
193 int model)
194{
195 volatile unsigned short *mem = mem0;
196
197 (void) model;
198
199 unsigned ret;
200
201 /*
202 * The following instructions between labels 1 and 2 constitute a
203 * Restartable Atomic Seqeunce. Should the sequence be non-atomic,
204 * the kernel will restart it.
205 */
206 asm volatile (
207 "1:\n"
208 " adr %[ret], 1b\n"
209 " str %[ret], %[rp0]\n"
210 " adr %[ret], 2f\n"
211 " str %[ret], %[rp1]\n"
212 " ldrh %[ret], %[addr]\n"
213 " strh %[imm], %[addr]\n"
214 "2:\n"
215 : [ret] "=&r" (ret),
216 [rp0] "=m" (ras_page[0]),
217 [rp1] "=m" (ras_page[1]),
218 [addr] "+m" (*mem)
219 : [imm] "r" (val)
220 );
221
222 ras_page[0] = 0;
223 ras_page[1] = 0xffffffff;
224
225 return ret;
226}
227
228unsigned __atomic_exchange_4(volatile void *mem0, unsigned val, int model)
229{
230 volatile unsigned *mem = mem0;
231
232 (void) model;
233
234 unsigned ret;
235
236 /*
237 * The following instructions between labels 1 and 2 constitute a
238 * Restartable Atomic Seqeunce. Should the sequence be non-atomic,
239 * the kernel will restart it.
240 */
241 asm volatile (
242 "1:\n"
243 " adr %[ret], 1b\n"
244 " str %[ret], %[rp0]\n"
245 " adr %[ret], 2f\n"
246 " str %[ret], %[rp1]\n"
247 " ldr %[ret], %[addr]\n"
248 " str %[imm], %[addr]\n"
249 "2:\n"
250 : [ret] "=&r" (ret),
251 [rp0] "=m" (ras_page[0]),
252 [rp1] "=m" (ras_page[1]),
253 [addr] "+m" (*mem)
254 : [imm] "r" (val)
255 );
256
257 ras_page[0] = 0;
258 ras_page[1] = 0xffffffff;
259
260 return ret;
261}
262
263unsigned short __atomic_fetch_add_2(volatile void *mem0, unsigned short val,
264 int model)
265{
266 volatile unsigned short *mem = mem0;
267
268 (void) model;
269
270 unsigned short ret;
271
272 /*
273 * The following instructions between labels 1 and 2 constitute a
274 * Restartable Atomic Seqeunce. Should the sequence be non-atomic,
275 * the kernel will restart it.
276 */
277 asm volatile (
278 "1:\n"
279 " adr %[ret], 1b\n"
280 " str %[ret], %[rp0]\n"
281 " adr %[ret], 2f\n"
282 " str %[ret], %[rp1]\n"
283 " ldrh %[ret], %[addr]\n"
284 " add %[ret], %[ret], %[imm]\n"
285 " strh %[ret], %[addr]\n"
286 "2:\n"
287 : [ret] "=&r" (ret),
288 [rp0] "=m" (ras_page[0]),
289 [rp1] "=m" (ras_page[1]),
290 [addr] "+m" (*mem)
291 : [imm] "r" (val)
292 );
293
294 ras_page[0] = 0;
295 ras_page[1] = 0xffffffff;
296
297 return ret - val;
298}
299
300unsigned __atomic_fetch_add_4(volatile void *mem0, unsigned val, int model)
301{
302 volatile unsigned *mem = mem0;
303
304 (void) model;
305
306 unsigned ret;
307
308 /*
309 * The following instructions between labels 1 and 2 constitute a
310 * Restartable Atomic Seqeunce. Should the sequence be non-atomic,
311 * the kernel will restart it.
312 */
313 asm volatile (
314 "1:\n"
315 " adr %[ret], 1b\n"
316 " str %[ret], %[rp0]\n"
317 " adr %[ret], 2f\n"
318 " str %[ret], %[rp1]\n"
319 " ldr %[ret], %[addr]\n"
320 " add %[ret], %[ret], %[imm]\n"
321 " str %[ret], %[addr]\n"
322 "2:\n"
323 : [ret] "=&r" (ret),
324 [rp0] "=m" (ras_page[0]),
325 [rp1] "=m" (ras_page[1]),
326 [addr] "+m" (*mem)
327 : [imm] "r" (val)
328 );
329
330 ras_page[0] = 0;
331 ras_page[1] = 0xffffffff;
332
333 return ret - val;
334}
335
336unsigned __atomic_fetch_sub_4(volatile void *mem, unsigned val, int model)
337{
338 return __atomic_fetch_add_4(mem, -val, model);
339}
340
341bool __atomic_test_and_set(volatile void *ptr, int memorder)
342{
343 volatile unsigned char *b = ptr;
344
345 unsigned char orig = __atomic_exchange_n(b, (unsigned char) true, memorder);
346 return orig != 0;
347}
348
349void __sync_synchronize(void)
350{
351 // FIXME: Full memory barrier. We might need a syscall for this.
352}
353
354unsigned __sync_add_and_fetch_4(volatile void *vptr, unsigned val)
355{
356 return __atomic_fetch_add_4(vptr, val, __ATOMIC_SEQ_CST) + val;
357}
358
359unsigned __sync_sub_and_fetch_4(volatile void *vptr, unsigned val)
360{
361 return __atomic_fetch_sub_4(vptr, val, __ATOMIC_SEQ_CST) - val;
362}
363
364bool __sync_bool_compare_and_swap_4(volatile void *ptr, unsigned old_val, unsigned new_val)
365{
366 return __atomic_compare_exchange_4(ptr, &old_val, new_val, false,
367 __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
368}
369
370unsigned __sync_val_compare_and_swap_4(volatile void *ptr, unsigned old_val, unsigned new_val)
371{
372 __atomic_compare_exchange_4(ptr, &old_val, new_val, false,
373 __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
374 return old_val;
375}
Note: See TracBrowser for help on using the repository browser.