|  | /* | 
|  | * Copyright (C) 2011 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. | 
|  | */ | 
|  | #ifndef BIONIC_ATOMIC_ARM_H | 
|  | #define BIONIC_ATOMIC_ARM_H | 
|  |  | 
|  | #include <machine/cpu-features.h> | 
|  |  | 
|  | /* Some of the harware instructions used below are not available in Thumb-1 | 
|  | * mode (they are if you build in ARM or Thumb-2 mode though). To solve this | 
|  | * problem, we're going to use the same technique than libatomics_ops, | 
|  | * which is to temporarily switch to ARM, do the operation, then switch | 
|  | * back to Thumb-1. | 
|  | * | 
|  | * This results in two 'bx' jumps, just like a normal function call, but | 
|  | * everything is kept inlined, avoids loading or computing the function's | 
|  | * address, and prevents a little I-cache trashing too. | 
|  | * | 
|  | * However, it is highly recommended to avoid compiling any C library source | 
|  | * file that use these functions in Thumb-1 mode. | 
|  | * | 
|  | * Define three helper macros to implement this: | 
|  | */ | 
|  | #if defined(__thumb__) && !defined(__thumb2__) | 
|  | #  define  __ATOMIC_SWITCH_TO_ARM \ | 
|  | "adr r3, 5f\n" \ | 
|  | "bx  r3\n" \ | 
|  | ".align\n" \ | 
|  | ".arm\n" \ | 
|  | "5:\n" | 
|  | /* note: the leading \n below is intentional */ | 
|  | #  define __ATOMIC_SWITCH_TO_THUMB \ | 
|  | "\n" \ | 
|  | "adr r3, 6f\n" \ | 
|  | "bx  r3\n" \ | 
|  | ".thumb" \ | 
|  | "6:\n" | 
|  |  | 
|  | #  define __ATOMIC_CLOBBERS   "r3"  /* list of clobbered registers */ | 
|  |  | 
|  | /* Warn the user that ARM mode should really be preferred! */ | 
|  | #  warning Rebuilding this source file in ARM mode is highly recommended for performance!! | 
|  |  | 
|  | #else | 
|  | #  define  __ATOMIC_SWITCH_TO_ARM   /* nothing */ | 
|  | #  define  __ATOMIC_SWITCH_TO_THUMB /* nothing */ | 
|  | #  define  __ATOMIC_CLOBBERS        /* nothing */ | 
|  | #endif | 
|  |  | 
|  |  | 
|  | /* Define a full memory barrier, this is only needed if we build the | 
|  | * platform for a multi-core device. For the record, using a 'dmb' | 
|  | * instruction on a Nexus One device can take up to 180 ns even if | 
|  | * it is completely un-necessary on this device. | 
|  | * | 
|  | * NOTE: This is where the platform and NDK headers atomic headers are | 
|  | *        going to diverge. With the NDK, we don't know if the generated | 
|  | *        code is going to run on a single or multi-core device, so we | 
|  | *        need to be cautious. | 
|  | * | 
|  | *        Fortunately, we can use the kernel helper function that is | 
|  | *        mapped at address 0xffff0fa0 in all user process, and that | 
|  | *        provides a device-specific barrier operation. | 
|  | * | 
|  | *        I.e. on single-core devices, the helper immediately returns, | 
|  | *        on multi-core devices, it uses "dmb" or any other means to | 
|  | *        perform a full-memory barrier. | 
|  | * | 
|  | * There are three cases to consider for the platform: | 
|  | * | 
|  | *    - multi-core ARMv7-A       => use the 'dmb' hardware instruction | 
|  | *    - multi-core ARMv6         => use the coprocessor | 
|  | *    - single core ARMv5TE/6/7  => do not use any hardware barrier | 
|  | */ | 
|  | #if defined(ANDROID_SMP) && ANDROID_SMP == 1 | 
|  |  | 
|  | /* Sanity check, multi-core is only supported starting from ARMv6 */ | 
|  | #  if __ARM_ARCH__ < 6 | 
|  | #    error ANDROID_SMP should not be set to 1 for an ARM architecture less than 6 | 
|  | #  endif | 
|  |  | 
|  | #  ifdef __ARM_HAVE_DMB | 
|  | /* For ARMv7-A, we can use the 'dmb' instruction directly */ | 
|  | __ATOMIC_INLINE__ void | 
|  | __bionic_memory_barrier(void) | 
|  | { | 
|  | /* Note: we always build in ARM or Thumb-2 on ARMv7-A, so don't | 
|  | * bother with __ATOMIC_SWITCH_TO_ARM */ | 
|  | __asm__ __volatile__ ( "dmb" : : : "memory" ); | 
|  | } | 
|  | #  else /* !__ARM_HAVE_DMB */ | 
|  | /* Otherwise, i.e. for multi-core ARMv6, we need to use the coprocessor, | 
|  | * which requires the use of a general-purpose register, which is slightly | 
|  | * less efficient. | 
|  | */ | 
|  | __ATOMIC_INLINE__ void | 
|  | __bionic_memory_barrier(void) | 
|  | { | 
|  | __asm__ __volatile__ ( | 
|  | __SWITCH_TO_ARM | 
|  | "mcr p15, 0, %0, c7, c10, 5" | 
|  | __SWITCH_TO_THUMB | 
|  | : : "r" (0) : __ATOMIC_CLOBBERS "memory"); | 
|  | } | 
|  | #  endif /* !__ARM_HAVE_DMB */ | 
|  | #else /* !ANDROID_SMP */ | 
|  | __ATOMIC_INLINE__ void | 
|  | __bionic_memory_barrier(void) | 
|  | { | 
|  | /* A simple compiler barrier */ | 
|  | __asm__ __volatile__ ( "" : : : "memory" ); | 
|  | } | 
|  | #endif /* !ANDROID_SMP */ | 
|  |  | 
|  | /* Compare-and-swap, without any explicit barriers. Note that this functions | 
|  | * returns 0 on success, and 1 on failure. The opposite convention is typically | 
|  | * used on other platforms. | 
|  | * | 
|  | * There are two cases to consider: | 
|  | * | 
|  | *     - ARMv6+  => use LDREX/STREX instructions | 
|  | *     - < ARMv6 => use kernel helper function mapped at 0xffff0fc0 | 
|  | * | 
|  | * LDREX/STREX are only available starting from ARMv6 | 
|  | */ | 
|  | #ifdef __ARM_HAVE_LDREX_STREX | 
|  | __ATOMIC_INLINE__ int | 
|  | __bionic_cmpxchg(int32_t old_value, int32_t new_value, volatile int32_t* ptr) | 
|  | { | 
|  | int32_t prev, status; | 
|  | do { | 
|  | __asm__ __volatile__ ( | 
|  | __ATOMIC_SWITCH_TO_ARM | 
|  | "ldrex %0, [%3]\n" | 
|  | "mov %1, #0\n" | 
|  | "teq %0, %4\n" | 
|  | #ifdef __thumb2__ | 
|  | "it eq\n" | 
|  | #endif | 
|  | "strexeq %1, %5, [%3]" | 
|  | __ATOMIC_SWITCH_TO_THUMB | 
|  | : "=&r" (prev), "=&r" (status), "+m"(*ptr) | 
|  | : "r" (ptr), "Ir" (old_value), "r" (new_value) | 
|  | : __ATOMIC_CLOBBERS "cc"); | 
|  | } while (__builtin_expect(status != 0, 0)); | 
|  | return prev != old_value; | 
|  | } | 
|  | #  else /* !__ARM_HAVE_LDREX_STREX */ | 
|  |  | 
|  | /* Use the handy kernel helper function mapped at 0xffff0fc0 */ | 
|  | typedef int (kernel_cmpxchg)(int32_t, int32_t, volatile int32_t *); | 
|  |  | 
|  | __ATOMIC_INLINE__ int | 
|  | __kernel_cmpxchg(int32_t old_value, int32_t new_value, volatile int32_t* ptr) | 
|  | { | 
|  | /* Note: the kernel function returns 0 on success too */ | 
|  | return (*(kernel_cmpxchg *)0xffff0fc0)(old_value, new_value, ptr); | 
|  | } | 
|  |  | 
|  | __ATOMIC_INLINE__ int | 
|  | __bionic_cmpxchg(int32_t old_value, int32_t new_value, volatile int32_t* ptr) | 
|  | { | 
|  | return __kernel_cmpxchg(old_value, new_value, ptr); | 
|  | } | 
|  | #endif /* !__ARM_HAVE_LDREX_STREX */ | 
|  |  | 
|  | /* Swap operation, without any explicit barriers. | 
|  | * There are again two similar cases to consider: | 
|  | * | 
|  | *   ARMv6+ => use LDREX/STREX | 
|  | *   < ARMv6 => use SWP instead. | 
|  | */ | 
|  | #ifdef __ARM_HAVE_LDREX_STREX | 
|  | __ATOMIC_INLINE__ int32_t | 
|  | __bionic_swap(int32_t new_value, volatile int32_t* ptr) | 
|  | { | 
|  | int32_t prev, status; | 
|  | do { | 
|  | __asm__ __volatile__ ( | 
|  | __ATOMIC_SWITCH_TO_ARM | 
|  | "ldrex %0, [%3]\n" | 
|  | "strex %1, %4, [%3]" | 
|  | __ATOMIC_SWITCH_TO_THUMB | 
|  | : "=&r" (prev), "=&r" (status), "+m" (*ptr) | 
|  | : "r" (ptr), "r" (new_value) | 
|  | : __ATOMIC_CLOBBERS "cc"); | 
|  | } while (__builtin_expect(status != 0, 0)); | 
|  | return prev; | 
|  | } | 
|  | #else /* !__ARM_HAVE_LDREX_STREX */ | 
|  | __ATOMIC_INLINE__ int32_t | 
|  | __bionic_swap(int32_t new_value, volatile int32_t* ptr) | 
|  | { | 
|  | int32_t prev; | 
|  | /* NOTE: SWP is available in Thumb-1 too */ | 
|  | __asm__ __volatile__ ("swp %0, %2, [%3]" | 
|  | : "=&r" (prev), "+m" (*ptr) | 
|  | : "r" (new_value), "r" (ptr) | 
|  | : "cc"); | 
|  | return prev; | 
|  | } | 
|  | #endif /* !__ARM_HAVE_LDREX_STREX */ | 
|  |  | 
|  | /* Atomic increment - without any barriers | 
|  | * This returns the old value | 
|  | */ | 
|  | #ifdef __ARM_HAVE_LDREX_STREX | 
|  | __ATOMIC_INLINE__ int32_t | 
|  | __bionic_atomic_inc(volatile int32_t* ptr) | 
|  | { | 
|  | int32_t prev, tmp, status; | 
|  | do { | 
|  | __asm__ __volatile__ ( | 
|  | __ATOMIC_SWITCH_TO_ARM | 
|  | "ldrex %0, [%4]\n" | 
|  | "add %1, %0, #1\n" | 
|  | "strex %2, %1, [%4]" | 
|  | __ATOMIC_SWITCH_TO_THUMB | 
|  | : "=&r" (prev), "=&r" (tmp), "=&r" (status), "+m"(*ptr) | 
|  | : "r" (ptr) | 
|  | : __ATOMIC_CLOBBERS "cc"); | 
|  | } while (__builtin_expect(status != 0, 0)); | 
|  | return prev; | 
|  | } | 
|  | #else | 
|  | __ATOMIC_INLINE__ int32_t | 
|  | __bionic_atomic_inc(volatile int32_t* ptr) | 
|  | { | 
|  | int32_t  prev, status; | 
|  | do { | 
|  | prev = *ptr; | 
|  | status = __kernel_cmpxchg(prev, prev+1, ptr); | 
|  | } while (__builtin_expect(status != 0, 0)); | 
|  | return prev; | 
|  | } | 
|  | #endif | 
|  |  | 
|  | /* Atomic decrement - without any barriers | 
|  | * This returns the old value. | 
|  | */ | 
|  | #ifdef __ARM_HAVE_LDREX_STREX | 
|  | __ATOMIC_INLINE__ int32_t | 
|  | __bionic_atomic_dec(volatile int32_t* ptr) | 
|  | { | 
|  | int32_t prev, tmp, status; | 
|  | do { | 
|  | __asm__ __volatile__ ( | 
|  | __ATOMIC_SWITCH_TO_ARM | 
|  | "ldrex %0, [%4]\n" | 
|  | "sub %1, %0, #1\n" | 
|  | "strex %2, %1, [%4]" | 
|  | __ATOMIC_SWITCH_TO_THUMB | 
|  | : "=&r" (prev), "=&r" (tmp), "=&r" (status), "+m"(*ptr) | 
|  | : "r" (ptr) | 
|  | : __ATOMIC_CLOBBERS "cc"); | 
|  | } while (__builtin_expect(status != 0, 0)); | 
|  | return prev; | 
|  | } | 
|  | #else | 
|  | __ATOMIC_INLINE__ int32_t | 
|  | __bionic_atomic_dec(volatile int32_t* ptr) | 
|  | { | 
|  | int32_t  prev, status; | 
|  | do { | 
|  | prev = *ptr; | 
|  | status = __kernel_cmpxchg(prev, prev-1, ptr); | 
|  | } while (__builtin_expect(status != 0, 0)); | 
|  | return prev; | 
|  | } | 
|  | #endif | 
|  |  | 
|  | #endif /* SYS_ATOMICS_ARM_H */ |