blob: d8ee15cc608248cdaf19806c17ae2e6acda7bbd3 [file] [log] [blame]
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -08001/*
2 * Copyright (C) 2005 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <machine/cpu-features.h>
18
Andy McFadden8dfa47d2010-05-27 10:10:18 -070019 .text
20 .align
21
22 .global android_atomic_acquire_load
23 .type android_atomic_acquire_load, %function
24 .global android_atomic_release_load
25 .type android_atomic_release_load, %function
26
27 .global android_atomic_acquire_store
28 .type android_atomic_acquire_store, %function
29 .global android_atomic_release_store
30 .type android_atomic_release_store, %function
31
32 .global android_atomic_inc
33 .type android_atomic_inc, %function
34 .global android_atomic_dec
35 .type android_atomic_dec, %function
36
37 .global android_atomic_add
38 .type android_atomic_add, %function
39 .global android_atomic_and
40 .type android_atomic_and, %function
41 .global android_atomic_or
42 .type android_atomic_or, %function
43
44 .global android_atomic_release_swap
45 .type android_atomic_release_swap, %function
46 .global android_atomic_acquire_swap
47 .type android_atomic_acquire_swap, %function
48
49 .global android_atomic_release_cas
50 .type android_atomic_release_cas, %function
51 .global android_atomic_acquire_cas
52 .type android_atomic_acquire_cas, %function
53
54/* must be on or off; cannot be left undefined */
55#if !defined(ANDROID_SMP)
56# error "ANDROID_SMP not defined"
57#endif
58
59
60#if defined(__ARM_HAVE_LDREX_STREX)
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080061/*
Andy McFadden8dfa47d2010-05-27 10:10:18 -070062 * ===========================================================================
63 * ARMv6+ implementation
64 * ===========================================================================
65 *
66 * These functions use the LDREX/STREX instructions to perform atomic
67 * operations ("LL/SC" approach). On an SMP build they will include
68 * an appropriate memory barrier.
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080069 */
70
Andy McFadden8dfa47d2010-05-27 10:10:18 -070071/* generate the memory barrier instruction when the build requires it */
72#if ANDROID_SMP == 1
73# if defined(__ARM_HAVE_DMB)
74# define SMP_DMB dmb
75# else
76 /* Data Memory Barrier operation, initated by writing a value into a
77 specific register with the Move to Coprocessor instruction. We
78 arbitrarily use r0 here. */
79# define SMP_DMB mcr p15, 0, r0, c7, c10, 5
80# endif
81#else
82# define SMP_DMB
83#endif
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080084
85/*
Andy McFadden8dfa47d2010-05-27 10:10:18 -070086 * Sidebar: do we need to use the -EX instructions for atomic load/store?
87 *
88 * Consider the following situation (time advancing downward):
89 *
90 * P1 P2
91 * val = LDREX(mem)
92 * val = val + 1
93 * STR(mem, otherval)
94 * STREX(mem, val)
95 *
96 * If these instructions issue on separate cores, the STREX will correctly
97 * fail because of the intervening store from the other core. If this same
98 * sequence of instructions executes in two threads on the same core, the
99 * STREX will incorrectly succeed.
100 *
101 * There are two ways to fix this:
102 * (1) Use LDREX/STREX for the atomic store operations. This doesn't
103 * prevent the program from doing a non-exclusive store, but at least
104 * this way if they always use atomic ops to access the memory location
105 * there won't be any problems.
106 * (2) Have the kernel clear the LDREX reservation on thread context switch.
107 * This will sometimes clear the reservation unnecessarily, but guarantees
108 * correct behavior.
109 *
110 * The Android kernel performs a CLREX (v7) or dummy STREX (pre-v7), so we
111 * can get away with a non-exclusive store here.
112 *
113 * -----
114 *
115 * It's worth noting that using non-exclusive LDR and STR means the "load"
116 * and "store" operations aren't quite the same as read-modify-write or
117 * swap operations. By definition those must read and write memory in a
118 * in a way that is coherent across all cores, whereas our non-exclusive
119 * load and store have no such requirement.
120 *
121 * In practice this doesn't matter, because the only guarantees we make
122 * about who sees what when are tied to the acquire/release semantics.
123 * Other cores may not see our atomic releasing store as soon as they would
124 * if the code used LDREX/STREX, but a store-release operation doesn't make
125 * any guarantees as to how soon the store will be visible. It's allowable
126 * for operations that happen later in program order to become visible
127 * before the store. For an acquring store we issue a full barrier after
128 * the STREX, ensuring that other processors see events in the proper order.
129 */
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800130
131/*
Andy McFadden8dfa47d2010-05-27 10:10:18 -0700132 * android_atomic_acquire_load / android_atomic_release_load
133 * input: r0 = address
134 * output: r0 = value
135 */
136android_atomic_acquire_load:
137 .fnstart
138 ldr r0, [r0]
139 SMP_DMB
140 bx lr
141 .fnend
142
143android_atomic_release_load:
144 .fnstart
145 SMP_DMB
146 ldr r0, [r0]
147 bx lr
148 .fnend
149
150
151/*
152 * android_atomic_acquire_store / android_atomic_release_store
153 * input: r0 = value, r1 = address
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800154 * output: void
155 */
Andy McFadden8dfa47d2010-05-27 10:10:18 -0700156android_atomic_acquire_store:
157 .fnstart
Mathias Agopianca5e0bc2009-09-03 14:49:58 -0700158 str r0, [r1]
Andy McFadden8dfa47d2010-05-27 10:10:18 -0700159 SMP_DMB
160 bx lr
161 .fnend
162
163android_atomic_release_store:
164 .fnstart
165 SMP_DMB
166 str r0, [r1]
167 bx lr
168 .fnend
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800169
170/*
Andy McFadden8dfa47d2010-05-27 10:10:18 -0700171 * Common sequence for read-modify-write operations.
172 *
173 * input: r1 = address
174 * output: r0 = original value, returns to caller
175 */
176 .macro RMWEX op, arg
1771: ldrex r0, [r1] @ load current value into r0
178 \op r2, r0, \arg @ generate new value into r2
179 strex r3, r2, [r1] @ try to store new value; result in r3
180 cmp r3, #0 @ success?
181 bxeq lr @ yes, return
182 b 1b @ no, retry
183 .endm
184
185
186/*
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800187 * android_atomic_inc
188 * input: r0 = address
189 * output: r0 = old value
190 */
Andy McFadden8dfa47d2010-05-27 10:10:18 -0700191android_atomic_inc:
192 .fnstart
193 SMP_DMB
194 mov r1, r0
195 RMWEX add, #1
196 .fnend
197
198
199/*
200 * android_atomic_dec
201 * input: r0 = address
202 * output: r0 = old value
203 */
204android_atomic_dec:
205 .fnstart
206 SMP_DMB
207 mov r1, r0
208 RMWEX sub, #1
209 .fnend
210
211
212/*
213 * android_atomic_add
214 * input: r0 = value, r1 = address
215 * output: r0 = old value
216 */
217android_atomic_add:
218 .fnstart
219 SMP_DMB
220 mov ip, r0
221 RMWEX add, ip
222 .fnend
223
224
225/*
226 * android_atomic_and
227 * input: r0 = value, r1 = address
228 * output: r0 = old value
229 */
230android_atomic_and:
231 .fnstart
232 SMP_DMB
233 mov ip, r0
234 RMWEX and, ip
235 .fnend
236
237
238/*
239 * android_atomic_or
240 * input: r0 = value, r1 = address
241 * output: r0 = old value
242 */
243android_atomic_or:
244 .fnstart
245 SMP_DMB
246 mov ip, r0
247 RMWEX orr, ip
248 .fnend
249
250
251/*
252 * android_atomic_acquire_swap / android_atomic_release_swap
253 * input: r0 = value, r1 = address
254 * output: r0 = old value
255 */
256android_atomic_acquire_swap:
257 .fnstart
2581: ldrex r2, [r1] @ load current value into r2
259 strex r3, r0, [r1] @ store new value
260 teq r3, #0 @ strex success?
261 bne 1b @ no, loop
262 mov r0, r2 @ return old value
263 SMP_DMB
264 bx lr
265 .fnend
266
267android_atomic_release_swap:
268 .fnstart
269 SMP_DMB
2701: ldrex r2, [r1]
271 strex r3, r0, [r1]
272 teq r3, #0
273 bne 1b
274 mov r0, r2
275 bx lr
276 .fnend
277
278
279/*
280 * android_atomic_acquire_cas / android_atomic_release_cas
281 * input: r0 = oldvalue, r1 = newvalue, r2 = address
282 * output: r0 = 0 (xchg done) or non-zero (xchg not done)
283 */
284android_atomic_acquire_cas:
285 .fnstart
2861: mov ip, #2 @ ip=2 means "new != old"
287 ldrex r3, [r2] @ load current value into r3
288 teq r0, r3 @ new == old?
289 strexeq ip, r1, [r2] @ yes, try store, set ip to 0 or 1
290 teq ip, #1 @ strex failure?
291 beq 1b @ yes, retry
292 mov r0, ip @ return 0 on success, 2 on failure
293 SMP_DMB
294 bx lr
295 .fnend
296
297android_atomic_release_cas:
298 .fnstart
299 SMP_DMB
3001: mov ip, #2
301 ldrex r3, [r2]
302 teq r0, r3
303 strexeq ip, r1, [r2]
304 teq ip, #1
305 beq 1b
306 mov r0, ip
307 bx lr
308 .fnend
309
310
311#else /*not defined __ARM_HAVE_LDREX_STREX*/
312/*
313 * ===========================================================================
314 * Pre-ARMv6 implementation
315 * ===========================================================================
316 *
317 * These functions call through the kernel cmpxchg facility, or use the
318 * (now deprecated) SWP instruction. They are not SMP-safe.
319 */
320#if ANDROID_SMP == 1
321# error "SMP defined, but LDREX/STREX not available"
322#endif
323
324/*
325 * int __kernel_cmpxchg(int oldval, int newval, int *ptr)
326 * clobbered: r3, ip, flags
327 * return 0 if a swap was made, non-zero otherwise.
328 */
329 .equ kernel_cmpxchg, 0xFFFF0FC0
330 .equ kernel_atomic_base, 0xFFFF0FFF
331
332
333/*
334 * android_atomic_acquire_load / android_atomic_release_load
335 * input: r0 = address
336 * output: r0 = value
337 */
338android_atomic_acquire_load:
339android_atomic_release_load:
340 .fnstart
341 ldr r0, [r0]
342 bx lr
343 .fnend
344
345
346/*
347 * android_atomic_acquire_store / android_atomic_release_store
348 * input: r0 = value, r1 = address
349 * output: void
350 */
351android_atomic_acquire_store:
352android_atomic_release_store:
353 .fnstart
354 str r0, [r1]
355 bx lr
356 .fnend
357
358
359/*
360 * android_atomic_inc
361 * input: r0 = address
362 * output: r0 = old value
363 */
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800364android_atomic_inc:
Ben Cheng8a0a5272009-10-12 16:51:23 -0700365 .fnstart
366 .save {r4, lr}
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800367 stmdb sp!, {r4, lr}
368 mov r2, r0
3691: @ android_atomic_inc
370 ldr r0, [r2]
371 mov r3, #kernel_atomic_base
372#ifdef __ARM_HAVE_PC_INTERWORK
373 add lr, pc, #4
374 add r1, r0, #1
375 add pc, r3, #(kernel_cmpxchg - kernel_atomic_base)
376#else
377 add r1, r0, #1
378 add r3, r3, #(kernel_cmpxchg - kernel_atomic_base)
379 mov lr, pc
380 bx r3
381#endif
382 bcc 1b
383 sub r0, r1, #1
384 ldmia sp!, {r4, lr}
385 bx lr
Ben Cheng8a0a5272009-10-12 16:51:23 -0700386 .fnend
Andy McFadden8dfa47d2010-05-27 10:10:18 -0700387
388
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800389/*
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800390 * android_atomic_dec
Andy McFadden8dfa47d2010-05-27 10:10:18 -0700391 * input: r0 = address
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800392 * output: r0 = old value
393 */
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800394android_atomic_dec:
Ben Cheng8a0a5272009-10-12 16:51:23 -0700395 .fnstart
396 .save {r4, lr}
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800397 stmdb sp!, {r4, lr}
398 mov r2, r0
3991: @ android_atomic_dec
400 ldr r0, [r2]
401 mov r3, #kernel_atomic_base
402#ifdef __ARM_HAVE_PC_INTERWORK
403 add lr, pc, #4
404 sub r1, r0, #1
405 add pc, r3, #(kernel_cmpxchg - kernel_atomic_base)
406#else
407 sub r1, r0, #1
408 add r3, r3, #(kernel_cmpxchg - kernel_atomic_base)
409 mov lr, pc
410 bx r3
411#endif
412 bcc 1b
413 add r0, r1, #1
414 ldmia sp!, {r4, lr}
415 bx lr
Ben Cheng8a0a5272009-10-12 16:51:23 -0700416 .fnend
Andy McFadden8dfa47d2010-05-27 10:10:18 -0700417
418
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800419/*
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800420 * android_atomic_add
Andy McFadden8dfa47d2010-05-27 10:10:18 -0700421 * input: r0 = value, r1 = address
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800422 * output: r0 = old value
423 */
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800424android_atomic_add:
Ben Cheng8a0a5272009-10-12 16:51:23 -0700425 .fnstart
426 .save {r4, lr}
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800427 stmdb sp!, {r4, lr}
428 mov r2, r1
429 mov r4, r0
4301: @ android_atomic_add
431 ldr r0, [r2]
432 mov r3, #kernel_atomic_base
433#ifdef __ARM_HAVE_PC_INTERWORK
434 add lr, pc, #4
435 add r1, r0, r4
436 add pc, r3, #(kernel_cmpxchg - kernel_atomic_base)
437#else
438 add r1, r0, r4
439 add r3, r3, #(kernel_cmpxchg - kernel_atomic_base)
440 mov lr, pc
441 bx r3
442#endif
443 bcc 1b
444 sub r0, r1, r4
445 ldmia sp!, {r4, lr}
446 bx lr
Ben Cheng8a0a5272009-10-12 16:51:23 -0700447 .fnend
Andy McFadden8dfa47d2010-05-27 10:10:18 -0700448
449
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800450/*
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800451 * android_atomic_and
Andy McFadden8dfa47d2010-05-27 10:10:18 -0700452 * input: r0 = value, r1 = address
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800453 * output: r0 = old value
454 */
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800455android_atomic_and:
Ben Cheng8a0a5272009-10-12 16:51:23 -0700456 .fnstart
Andy McFadden8dfa47d2010-05-27 10:10:18 -0700457 .save {r4, r5, ip, lr} /* include ip for 64-bit stack alignment */
458 stmdb sp!, {r4, r5, ip, lr}
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800459 mov r2, r1 /* r2 = address */
460 mov r4, r0 /* r4 = the value */
4611: @ android_atomic_and
462 ldr r0, [r2] /* r0 = address[0] */
463 mov r3, #kernel_atomic_base
464#ifdef __ARM_HAVE_PC_INTERWORK
465 add lr, pc, #8
466 mov r5, r0 /* r5 = save address[0] */
467 and r1, r0, r4 /* r1 = new value */
468 add pc, r3, #(kernel_cmpxchg - kernel_atomic_base) /* call cmpxchg() */
469#else
470 mov r5, r0 /* r5 = save address[0] */
471 and r1, r0, r4 /* r1 = new value */
472 add r3, r3, #(kernel_cmpxchg - kernel_atomic_base) /* call cmpxchg() */
473 mov lr, pc
474 bx r3
475#endif
476 bcc 1b
477 mov r0, r5
Andy McFadden8dfa47d2010-05-27 10:10:18 -0700478 ldmia sp!, {r4, r5, ip, lr}
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800479 bx lr
Ben Cheng8a0a5272009-10-12 16:51:23 -0700480 .fnend
Andy McFadden8dfa47d2010-05-27 10:10:18 -0700481
482
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800483/*
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800484 * android_atomic_or
Andy McFadden8dfa47d2010-05-27 10:10:18 -0700485 * input: r0 = value, r1 = address
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800486 * output: r0 = old value
487 */
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800488android_atomic_or:
Ben Cheng8a0a5272009-10-12 16:51:23 -0700489 .fnstart
Andy McFadden8dfa47d2010-05-27 10:10:18 -0700490 .save {r4, r5, ip, lr} /* include ip for 64-bit stack alignment */
491 stmdb sp!, {r4, r5, ip, lr}
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800492 mov r2, r1 /* r2 = address */
493 mov r4, r0 /* r4 = the value */
4941: @ android_atomic_or
495 ldr r0, [r2] /* r0 = address[0] */
496 mov r3, #kernel_atomic_base
497#ifdef __ARM_HAVE_PC_INTERWORK
498 add lr, pc, #8
499 mov r5, r0 /* r5 = save address[0] */
500 orr r1, r0, r4 /* r1 = new value */
501 add pc, r3, #(kernel_cmpxchg - kernel_atomic_base) /* call cmpxchg() */
502#else
503 mov r5, r0 /* r5 = save address[0] */
504 orr r1, r0, r4 /* r1 = new value */
505 add r3, r3, #(kernel_cmpxchg - kernel_atomic_base) /* call cmpxchg() */
506 mov lr, pc
507 bx r3
508#endif
509 bcc 1b
510 mov r0, r5
Andy McFadden8dfa47d2010-05-27 10:10:18 -0700511 ldmia sp!, {r4, r5, ip, lr}
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800512 bx lr
Ben Cheng8a0a5272009-10-12 16:51:23 -0700513 .fnend
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800514
Andy McFadden8dfa47d2010-05-27 10:10:18 -0700515
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800516/*
Andy McFadden8dfa47d2010-05-27 10:10:18 -0700517 * android_atomic_acquire_swap / android_atomic_release_swap
518 * input: r0 = value, r1 = address
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800519 * output: r0 = old value
520 */
Andy McFadden8dfa47d2010-05-27 10:10:18 -0700521android_atomic_acquire_swap:
522android_atomic_release_swap:
523 .fnstart
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800524 swp r0, r0, [r1]
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800525 bx lr
Andy McFadden8dfa47d2010-05-27 10:10:18 -0700526 .fnend
527
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800528
529/*
Andy McFadden8dfa47d2010-05-27 10:10:18 -0700530 * android_atomic_acquire_cas / android_atomic_release_cas
531 * input: r0 = oldvalue, r1 = newvalue, r2 = address
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800532 * output: r0 = 0 (xchg done) or non-zero (xchg not done)
533 */
Andy McFadden8dfa47d2010-05-27 10:10:18 -0700534android_atomic_acquire_cas:
535android_atomic_release_cas:
Ben Cheng8a0a5272009-10-12 16:51:23 -0700536 .fnstart
537 .save {r4, lr}
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800538 stmdb sp!, {r4, lr}
539 mov r4, r0 /* r4 = save oldvalue */
5401: @ android_atomic_cmpxchg
541 mov r3, #kernel_atomic_base
542#ifdef __ARM_HAVE_PC_INTERWORK
543 add lr, pc, #4
544 mov r0, r4 /* r0 = oldvalue */
545 add pc, r3, #(kernel_cmpxchg - kernel_atomic_base)
546#else
547 mov r0, r4 /* r0 = oldvalue */
548 add r3, r3, #(kernel_cmpxchg - kernel_atomic_base)
549 mov lr, pc
550 bx r3
551#endif
552 bcs 2f /* swap was made. we're good, return. */
553 ldr r3, [r2] /* swap not made, see if it's because *ptr!=oldvalue */
554 cmp r3, r4
555 beq 1b
5562: @ android_atomic_cmpxchg
557 ldmia sp!, {r4, lr}
558 bx lr
Ben Cheng8a0a5272009-10-12 16:51:23 -0700559 .fnend
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800560
Andy McFadden8dfa47d2010-05-27 10:10:18 -0700561#endif /*not defined __ARM_HAVE_LDREX_STREX*/