diff options
Diffstat (limited to 'libcutils/atomic-android-arm.S')
-rw-r--r-- | libcutils/atomic-android-arm.S | 274 |
1 files changed, 274 insertions, 0 deletions
diff --git a/libcutils/atomic-android-arm.S b/libcutils/atomic-android-arm.S new file mode 100644 index 0000000..c56ec5d --- /dev/null +++ b/libcutils/atomic-android-arm.S @@ -0,0 +1,274 @@ +/* + * Copyright (C) 2005 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <machine/cpu-features.h> + +/* + * NOTE: these atomic operations are SMP safe on all architectures, + * except swap(), see below. + */ + + .text + .align + + .global android_atomic_write + + .global android_atomic_inc + .global android_atomic_dec + + .global android_atomic_add + .global android_atomic_and + .global android_atomic_or + + .global android_atomic_swap + + .global android_atomic_cmpxchg + +/* + * ---------------------------------------------------------------------------- + * int __kernel_cmpxchg(int oldval, int newval, int *ptr) + * clobbered: r3, ip, flags + * return 0 if a swap was made, non-zero otherwise. + */ + + .equ kernel_cmpxchg, 0xFFFF0FC0 + .equ kernel_atomic_base, 0xFFFF0FFF + +/* + * ---------------------------------------------------------------------------- + * android_atomic_write + * input: r0=value, r1=address + * output: void + */ + +android_atomic_write: + stmdb sp!, {r4, lr} + mov r2, r1 + mov r1, r0 +1: @ android_atomic_write + ldr r0, [r2] + mov r3, #kernel_atomic_base +#ifdef __ARM_HAVE_PC_INTERWORK + add lr, pc, #4 + add pc, r3, #(kernel_cmpxchg - kernel_atomic_base) +#else + add r3, r3, #(kernel_cmpxchg - kernel_atomic_base) + mov lr, pc + bx r3 +#endif + bcc 1b + ldmia sp!, {r4, lr} + bx lr + +/* + * ---------------------------------------------------------------------------- + * android_atomic_inc + * input: r0 = address + * output: r0 = old value + */ + +android_atomic_inc: + stmdb sp!, {r4, lr} + mov r2, r0 +1: @ android_atomic_inc + ldr r0, [r2] + mov r3, #kernel_atomic_base +#ifdef __ARM_HAVE_PC_INTERWORK + add lr, pc, #4 + add r1, r0, #1 + add pc, r3, #(kernel_cmpxchg - kernel_atomic_base) +#else + add r1, r0, #1 + add r3, r3, #(kernel_cmpxchg - kernel_atomic_base) + mov lr, pc + bx r3 +#endif + bcc 1b + sub r0, r1, #1 + ldmia sp!, {r4, lr} + bx lr + +/* + * ---------------------------------------------------------------------------- + * android_atomic_dec + * input: r0=address + * output: r0 = old value + */ + +android_atomic_dec: + stmdb sp!, {r4, lr} + mov r2, r0 +1: @ android_atomic_dec + ldr r0, [r2] + mov r3, #kernel_atomic_base +#ifdef __ARM_HAVE_PC_INTERWORK + add lr, pc, #4 + sub r1, r0, #1 + add pc, r3, #(kernel_cmpxchg - kernel_atomic_base) +#else + sub r1, r0, #1 + add r3, r3, #(kernel_cmpxchg - kernel_atomic_base) + mov lr, pc + bx r3 +#endif + bcc 1b + add r0, r1, #1 + ldmia sp!, {r4, lr} + bx lr + +/* + * ---------------------------------------------------------------------------- + * android_atomic_add + * input: r0=value, r1=address + * output: r0 = old value + */ + +android_atomic_add: + stmdb sp!, {r4, lr} + mov r2, r1 + mov r4, r0 +1: @ android_atomic_add + ldr r0, [r2] + mov r3, #kernel_atomic_base +#ifdef __ARM_HAVE_PC_INTERWORK + add lr, pc, #4 + add r1, r0, r4 + add pc, r3, #(kernel_cmpxchg - kernel_atomic_base) +#else + add r1, r0, r4 + add r3, r3, #(kernel_cmpxchg - kernel_atomic_base) + mov lr, pc + bx r3 +#endif + bcc 1b + sub r0, r1, r4 + ldmia sp!, {r4, lr} + bx lr + + +/* + * ---------------------------------------------------------------------------- + * android_atomic_and + * input: r0=value, r1=address + * output: r0 = old value + */ + +android_atomic_and: + stmdb sp!, {r4, r5, lr} + mov r2, r1 /* r2 = address */ + mov r4, r0 /* r4 = the value */ +1: @ android_atomic_and + ldr r0, [r2] /* r0 = address[0] */ + mov r3, #kernel_atomic_base +#ifdef __ARM_HAVE_PC_INTERWORK + add lr, pc, #8 + mov r5, r0 /* r5 = save address[0] */ + and r1, r0, r4 /* r1 = new value */ + add pc, r3, #(kernel_cmpxchg - kernel_atomic_base) /* call cmpxchg() */ +#else + mov r5, r0 /* r5 = save address[0] */ + and r1, r0, r4 /* r1 = new value */ + add r3, r3, #(kernel_cmpxchg - kernel_atomic_base) /* call cmpxchg() */ + mov lr, pc + bx r3 +#endif + bcc 1b + mov r0, r5 + ldmia sp!, {r4, r5, lr} + bx lr + +/* + * ---------------------------------------------------------------------------- + * android_atomic_or + * input: r0=value, r1=address + * output: r0 = old value + */ + +android_atomic_or: + stmdb sp!, {r4, r5, lr} + mov r2, r1 /* r2 = address */ + mov r4, r0 /* r4 = the value */ +1: @ android_atomic_or + ldr r0, [r2] /* r0 = address[0] */ + mov r3, #kernel_atomic_base +#ifdef __ARM_HAVE_PC_INTERWORK + add lr, pc, #8 + mov r5, r0 /* r5 = save address[0] */ + orr r1, r0, r4 /* r1 = new value */ + add pc, r3, #(kernel_cmpxchg - kernel_atomic_base) /* call cmpxchg() */ +#else + mov r5, r0 /* r5 = save address[0] */ + orr r1, r0, r4 /* r1 = new value */ + add r3, r3, #(kernel_cmpxchg - kernel_atomic_base) /* call cmpxchg() */ + mov lr, pc + bx r3 +#endif + bcc 1b + mov r0, r5 + ldmia sp!, {r4, r5, lr} + bx lr + +/* + * ---------------------------------------------------------------------------- + * android_atomic_swap + * input: r0=value, r1=address + * output: r0 = old value + */ + +/* FIXME: this is not safe on SMP systems + * a general way to do it is to use kernel_cmpxchg */ + +android_atomic_swap: + swp r0, r0, [r1] + bx lr + +/* + * ---------------------------------------------------------------------------- + * android_atomic_cmpxchg + * input: r0=oldvalue, r1=newvalue, r2=address + * output: r0 = 0 (xchg done) or non-zero (xchg not done) + */ + +android_atomic_cmpxchg: + stmdb sp!, {r4, lr} + mov r4, r0 /* r4 = save oldvalue */ +1: @ android_atomic_cmpxchg + mov r3, #kernel_atomic_base +#ifdef __ARM_HAVE_PC_INTERWORK + add lr, pc, #4 + mov r0, r4 /* r0 = oldvalue */ + add pc, r3, #(kernel_cmpxchg - kernel_atomic_base) +#else + mov r0, r4 /* r0 = oldvalue */ + add r3, r3, #(kernel_cmpxchg - kernel_atomic_base) + mov lr, pc + bx r3 +#endif + bcs 2f /* swap was made. we're good, return. */ + ldr r3, [r2] /* swap not made, see if it's because *ptr!=oldvalue */ + cmp r3, r4 + beq 1b +2: @ android_atomic_cmpxchg + ldmia sp!, {r4, lr} + bx lr + +/* + * ---------------------------------------------------------------------------- + * android_atomic_cmpxchg_64 + * input: r0-r1=oldvalue, r2-r3=newvalue, arg4 (on stack)=address + * output: r0 = 0 (xchg done) or non-zero (xchg not done) + */ +/* TODO: NEED IMPLEMENTATION FOR THIS ARCHITECTURE */ |