Home | History | Annotate | Line # | Download | only in arm
      1 /* 64bit Linux-specific atomic operations for ARM EABI.
      2    Copyright (C) 2008-2024 Free Software Foundation, Inc.
      3    Based on linux-atomic.c
      4 
      5    64 bit additions david.gilbert (at) linaro.org
      6 
      7 This file is part of GCC.
      8 
      9 GCC is free software; you can redistribute it and/or modify it under
     10 the terms of the GNU General Public License as published by the Free
     11 Software Foundation; either version 3, or (at your option) any later
     12 version.
     13 
     14 GCC is distributed in the hope that it will be useful, but WITHOUT ANY
     15 WARRANTY; without even the implied warranty of MERCHANTABILITY or
     16 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
     17 for more details.
     18 
     19 Under Section 7 of GPL version 3, you are granted additional
     20 permissions described in the GCC Runtime Library Exception, version
     21 3.1, as published by the Free Software Foundation.
     22 
     23 You should have received a copy of the GNU General Public License and
     24 a copy of the GCC Runtime Library Exception along with this program;
     25 see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
     26 <http://www.gnu.org/licenses/>.  */
     27 
     28 /* 64bit helper functions for atomic operations; the compiler will
     29    call these when the code is compiled for a CPU without ldrexd/strexd.
     30    (If the CPU had those then the compiler inlines the operation).
     31 
     32    These helpers require a kernel helper that's only present on newer
     33    kernels; we check for that in an init section and bail out rather
     34    unceremoneously.  */
     35 
     36 extern int write (int fd, const void *buf, unsigned int count);
     37 extern void abort (void);
     38 
     39 /* Kernel helper for compare-and-exchange.  */
     40 typedef int (__kernel_cmpxchg64_t) (const long long* oldval,
     41 					const long long* newval,
     42 					long long *ptr);
     43 #define __kernel_cmpxchg64 (*(__kernel_cmpxchg64_t *) 0xffff0f60)
     44 
     45 /* Kernel helper page version number.  */
     46 #define __kernel_helper_version (*(unsigned int *)0xffff0ffc)
     47 
     48 /* Check that the kernel has a new enough version at load.  */
     49 static void __check_for_sync8_kernelhelper (void)
     50 {
     51   if (__kernel_helper_version < 5)
     52     {
     53       const char err[] = "A newer kernel is required to run this binary. "
     54 				"(__kernel_cmpxchg64 helper)\n";
     55       /* At this point we need a way to crash with some information
     56 	 for the user - I'm not sure I can rely on much else being
     57 	 available at this point, so do the same as generic-morestack.c
     58 	 write () and abort ().  */
     59       write (2 /* stderr.  */, err, sizeof (err));
     60       abort ();
     61     }
     62 };
     63 
     64 static void (*__sync8_kernelhelper_inithook[]) (void)
     65 		__attribute__ ((used, section (".init_array"))) = {
     66   &__check_for_sync8_kernelhelper
     67 };
     68 
     69 #define HIDDEN __attribute__ ((visibility ("hidden")))
     70 
     71 #define FETCH_AND_OP_WORD64(OP, PFX_OP, INF_OP)			\
     72   long long HIDDEN						\
     73   __sync_fetch_and_##OP##_8 (long long *ptr, long long val)	\
     74   {								\
     75     int failure;						\
     76     long long tmp,tmp2;						\
     77 								\
     78     do {							\
     79       tmp = *ptr;						\
     80       tmp2 = PFX_OP (tmp INF_OP val);				\
     81       failure = __kernel_cmpxchg64 (&tmp, &tmp2, ptr);		\
     82     } while (failure != 0);					\
     83 								\
     84     return tmp;							\
     85   }
     86 
     87 FETCH_AND_OP_WORD64 (add,   , +)
     88 FETCH_AND_OP_WORD64 (sub,   , -)
     89 FETCH_AND_OP_WORD64 (or,    , |)
     90 FETCH_AND_OP_WORD64 (and,   , &)
     91 FETCH_AND_OP_WORD64 (xor,   , ^)
     92 FETCH_AND_OP_WORD64 (nand, ~, &)
     93 
     94 #define NAME_oldval(OP, WIDTH) __sync_fetch_and_##OP##_##WIDTH
     95 #define NAME_newval(OP, WIDTH) __sync_##OP##_and_fetch_##WIDTH
     96 
     97 /* Implement both __sync_<op>_and_fetch and __sync_fetch_and_<op> for
     98    subword-sized quantities.  */
     99 
    100 #define OP_AND_FETCH_WORD64(OP, PFX_OP, INF_OP)			\
    101   long long HIDDEN						\
    102   __sync_##OP##_and_fetch_8 (long long *ptr, long long val)	\
    103   {								\
    104     int failure;						\
    105     long long tmp,tmp2;						\
    106 								\
    107     do {							\
    108       tmp = *ptr;						\
    109       tmp2 = PFX_OP (tmp INF_OP val);				\
    110       failure = __kernel_cmpxchg64 (&tmp, &tmp2, ptr);		\
    111     } while (failure != 0);					\
    112 								\
    113     return tmp2;						\
    114   }
    115 
    116 OP_AND_FETCH_WORD64 (add,   , +)
    117 OP_AND_FETCH_WORD64 (sub,   , -)
    118 OP_AND_FETCH_WORD64 (or,    , |)
    119 OP_AND_FETCH_WORD64 (and,   , &)
    120 OP_AND_FETCH_WORD64 (xor,   , ^)
    121 OP_AND_FETCH_WORD64 (nand, ~, &)
    122 
    123 long long HIDDEN
    124 __sync_val_compare_and_swap_8 (long long *ptr, long long oldval,
    125 				long long newval)
    126 {
    127   int failure;
    128   long long actual_oldval;
    129 
    130   while (1)
    131     {
    132       actual_oldval = *ptr;
    133 
    134       if (__builtin_expect (oldval != actual_oldval, 0))
    135 	return actual_oldval;
    136 
    137       failure = __kernel_cmpxchg64 (&actual_oldval, &newval, ptr);
    138 
    139       if (__builtin_expect (!failure, 1))
    140 	return oldval;
    141     }
    142 }
    143 
    144 typedef unsigned char bool;
    145 
    146 bool HIDDEN
    147 __sync_bool_compare_and_swap_8 (long long *ptr, long long oldval,
    148 				 long long newval)
    149 {
    150   int failure = __kernel_cmpxchg64 (&oldval, &newval, ptr);
    151   return (failure == 0);
    152 }
    153 
    154 long long HIDDEN
    155 __sync_lock_test_and_set_8 (long long *ptr, long long val)
    156 {
    157   int failure;
    158   long long oldval;
    159 
    160   do {
    161     oldval = *ptr;
    162     failure = __kernel_cmpxchg64 (&oldval, &val, ptr);
    163   } while (failure != 0);
    164 
    165   return oldval;
    166 }
    167