Home | History | Annotate | Line # | Download | only in nios2
      1 /* Linux-specific atomic operations for Nios II Linux.
      2    Copyright (C) 2008-2024 Free Software Foundation, Inc.
      3 
      4 This file is free software; you can redistribute it and/or modify it
      5 under the terms of the GNU General Public License as published by the
      6 Free Software Foundation; either version 3, or (at your option) any
      7 later version.
      8 
      9 This file is distributed in the hope that it will be useful, but
     10 WITHOUT ANY WARRANTY; without even the implied warranty of
     11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     12 General Public License for more details.
     13 
     14 Under Section 7 of GPL version 3, you are granted additional
     15 permissions described in the GCC Runtime Library Exception, version
     16 3.1, as published by the Free Software Foundation.
     17 
     18 You should have received a copy of the GNU General Public License and
     19 a copy of the GCC Runtime Library Exception along with this program;
     20 see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
     21 <http://www.gnu.org/licenses/>.  */
     22 
     23 /* We implement byte, short and int versions of each atomic operation
     24    using the kernel helper defined below.  There is no support for
     25    64-bit operations yet.  */
     26 
     27 /* Crash a userspace program with SIGSEV.  */
     28 #define ABORT_INSTRUCTION asm ("stw zero, 0(zero)")
     29 
     30 /* Kernel helper for compare-and-exchange a 32-bit value.  */
     31 static inline long
     32 __kernel_cmpxchg (int oldval, int newval, int *mem)
     33 {
     34   register int r2 asm ("r2");
     35   register int *r4 asm ("r4") = mem;
     36   register int r5 asm ("r5") = oldval;
     37   register int r6 asm ("r6") = newval;
     38 
     39   /* Call the kernel provided fixed address cmpxchg helper routine.  */
     40   asm volatile ("movi %0, %4\n\t"
     41 		"callr %0\n"
     42 		: "=r" (r2)
     43 		: "r" (r4), "r" (r5), "r" (r6), "I" (0x00001004)
     44 		: "ra", "memory");
     45   return r2;
     46 }
     47 
     48 #define HIDDEN __attribute__ ((visibility ("hidden")))
     49 
     50 #ifdef __nios2_little_endian__
     51 #define INVERT_MASK_1 0
     52 #define INVERT_MASK_2 0
     53 #else
     54 #define INVERT_MASK_1 24
     55 #define INVERT_MASK_2 16
     56 #endif
     57 
     58 #define MASK_1 0xffu
     59 #define MASK_2 0xffffu
     60 
     61 #define FETCH_AND_OP_WORD(OP, PFX_OP, INF_OP)				\
     62   int HIDDEN								\
     63   __sync_fetch_and_##OP##_4 (int *ptr, int val)				\
     64   {									\
     65     int failure, tmp;							\
     66 									\
     67     do {								\
     68       tmp = *ptr;							\
     69       failure = __kernel_cmpxchg (tmp, PFX_OP (tmp INF_OP val), ptr);	\
     70     } while (failure != 0);						\
     71 									\
     72     return tmp;								\
     73   }
     74 
     75 FETCH_AND_OP_WORD (add,   , +)
     76 FETCH_AND_OP_WORD (sub,   , -)
     77 FETCH_AND_OP_WORD (or,    , |)
     78 FETCH_AND_OP_WORD (and,   , &)
     79 FETCH_AND_OP_WORD (xor,   , ^)
     80 FETCH_AND_OP_WORD (nand, ~, &)
     81 
     82 #define NAME_oldval(OP, WIDTH) __sync_fetch_and_##OP##_##WIDTH
     83 #define NAME_newval(OP, WIDTH) __sync_##OP##_and_fetch_##WIDTH
     84 
     85 /* Implement both __sync_<op>_and_fetch and __sync_fetch_and_<op> for
     86    subword-sized quantities.  */
     87 
     88 #define SUBWORD_SYNC_OP(OP, PFX_OP, INF_OP, TYPE, WIDTH, RETURN)	\
     89   TYPE HIDDEN								\
     90   NAME##_##RETURN (OP, WIDTH) (TYPE *ptr, TYPE val)			\
     91   {									\
     92     int *wordptr = (int *) ((unsigned long) ptr & ~3);			\
     93     unsigned int mask, shift, oldval, newval;				\
     94     int failure;							\
     95 									\
     96     shift = (((unsigned long) ptr & 3) << 3) ^ INVERT_MASK_##WIDTH;	\
     97     mask = MASK_##WIDTH << shift;					\
     98 									\
     99     do {								\
    100       oldval = *wordptr;						\
    101       newval = ((PFX_OP (((oldval & mask) >> shift)			\
    102 			 INF_OP (unsigned int) val)) << shift) & mask;	\
    103       newval |= oldval & ~mask;						\
    104       failure = __kernel_cmpxchg (oldval, newval, wordptr);		\
    105     } while (failure != 0);						\
    106 									\
    107     return (RETURN & mask) >> shift;					\
    108   }
    109 
    110 SUBWORD_SYNC_OP (add,   , +, unsigned short, 2, oldval)
    111 SUBWORD_SYNC_OP (sub,   , -, unsigned short, 2, oldval)
    112 SUBWORD_SYNC_OP (or,    , |, unsigned short, 2, oldval)
    113 SUBWORD_SYNC_OP (and,   , &, unsigned short, 2, oldval)
    114 SUBWORD_SYNC_OP (xor,   , ^, unsigned short, 2, oldval)
    115 SUBWORD_SYNC_OP (nand, ~, &, unsigned short, 2, oldval)
    116 
    117 SUBWORD_SYNC_OP (add,   , +, unsigned char, 1, oldval)
    118 SUBWORD_SYNC_OP (sub,   , -, unsigned char, 1, oldval)
    119 SUBWORD_SYNC_OP (or,    , |, unsigned char, 1, oldval)
    120 SUBWORD_SYNC_OP (and,   , &, unsigned char, 1, oldval)
    121 SUBWORD_SYNC_OP (xor,   , ^, unsigned char, 1, oldval)
    122 SUBWORD_SYNC_OP (nand, ~, &, unsigned char, 1, oldval)
    123 
    124 #define OP_AND_FETCH_WORD(OP, PFX_OP, INF_OP)				\
    125   int HIDDEN								\
    126   __sync_##OP##_and_fetch_4 (int *ptr, int val)				\
    127   {									\
    128     int tmp, failure;							\
    129 									\
    130     do {								\
    131       tmp = *ptr;							\
    132       failure = __kernel_cmpxchg (tmp, PFX_OP (tmp INF_OP val), ptr);	\
    133     } while (failure != 0);						\
    134 									\
    135     return PFX_OP (tmp INF_OP val);					\
    136   }
    137 
    138 OP_AND_FETCH_WORD (add,   , +)
    139 OP_AND_FETCH_WORD (sub,   , -)
    140 OP_AND_FETCH_WORD (or,    , |)
    141 OP_AND_FETCH_WORD (and,   , &)
    142 OP_AND_FETCH_WORD (xor,   , ^)
    143 OP_AND_FETCH_WORD (nand, ~, &)
    144 
    145 SUBWORD_SYNC_OP (add,   , +, unsigned short, 2, newval)
    146 SUBWORD_SYNC_OP (sub,   , -, unsigned short, 2, newval)
    147 SUBWORD_SYNC_OP (or,    , |, unsigned short, 2, newval)
    148 SUBWORD_SYNC_OP (and,   , &, unsigned short, 2, newval)
    149 SUBWORD_SYNC_OP (xor,   , ^, unsigned short, 2, newval)
    150 SUBWORD_SYNC_OP (nand, ~, &, unsigned short, 2, newval)
    151 
    152 SUBWORD_SYNC_OP (add,   , +, unsigned char, 1, newval)
    153 SUBWORD_SYNC_OP (sub,   , -, unsigned char, 1, newval)
    154 SUBWORD_SYNC_OP (or,    , |, unsigned char, 1, newval)
    155 SUBWORD_SYNC_OP (and,   , &, unsigned char, 1, newval)
    156 SUBWORD_SYNC_OP (xor,   , ^, unsigned char, 1, newval)
    157 SUBWORD_SYNC_OP (nand, ~, &, unsigned char, 1, newval)
    158 
    159 int HIDDEN
    160 __sync_val_compare_and_swap_4 (int *ptr, int oldval, int newval)
    161 {
    162   int actual_oldval, fail;
    163 
    164   while (1)
    165     {
    166       actual_oldval = *ptr;
    167 
    168       if (oldval != actual_oldval)
    169 	return actual_oldval;
    170 
    171       fail = __kernel_cmpxchg (actual_oldval, newval, ptr);
    172 
    173       if (!fail)
    174 	return oldval;
    175     }
    176 }
    177 
    178 #define SUBWORD_VAL_CAS(TYPE, WIDTH)					\
    179   TYPE HIDDEN								\
    180   __sync_val_compare_and_swap_##WIDTH (TYPE *ptr, TYPE oldval,		\
    181 				       TYPE newval)			\
    182   {									\
    183     int *wordptr = (int *)((unsigned long) ptr & ~3), fail;		\
    184     unsigned int mask, shift, actual_oldval, actual_newval;		\
    185 									\
    186     shift = (((unsigned long) ptr & 3) << 3) ^ INVERT_MASK_##WIDTH;	\
    187     mask = MASK_##WIDTH << shift;					\
    188 									\
    189     while (1)								\
    190       {									\
    191 	actual_oldval = *wordptr;					\
    192 									\
    193 	if (((actual_oldval & mask) >> shift) != (unsigned int) oldval)	\
    194           return (actual_oldval & mask) >> shift;			\
    195 									\
    196 	actual_newval = (actual_oldval & ~mask)				\
    197 			| (((unsigned int) newval << shift) & mask);	\
    198 									\
    199 	fail = __kernel_cmpxchg (actual_oldval, actual_newval,		\
    200 				 wordptr);				\
    201 									\
    202 	if (!fail)							\
    203 	  return oldval;						\
    204       }									\
    205   }
    206 
    207 SUBWORD_VAL_CAS (unsigned short, 2)
    208 SUBWORD_VAL_CAS (unsigned char,  1)
    209 
    210 typedef unsigned char bool;
    211 
    212 bool HIDDEN
    213 __sync_bool_compare_and_swap_4 (int *ptr, int oldval, int newval)
    214 {
    215   int failure = __kernel_cmpxchg (oldval, newval, ptr);
    216   return (failure == 0);
    217 }
    218 
    219 #define SUBWORD_BOOL_CAS(TYPE, WIDTH)					\
    220   bool HIDDEN								\
    221   __sync_bool_compare_and_swap_##WIDTH (TYPE *ptr, TYPE oldval,		\
    222 					TYPE newval)			\
    223   {									\
    224     TYPE actual_oldval							\
    225       = __sync_val_compare_and_swap_##WIDTH (ptr, oldval, newval);	\
    226     return (oldval == actual_oldval);					\
    227   }
    228 
    229 SUBWORD_BOOL_CAS (unsigned short, 2)
    230 SUBWORD_BOOL_CAS (unsigned char,  1)
    231 
    232 int HIDDEN
    233 __sync_lock_test_and_set_4 (int *ptr, int val)
    234 {
    235   int failure, oldval;
    236 
    237   do {
    238     oldval = *ptr;
    239     failure = __kernel_cmpxchg (oldval, val, ptr);
    240   } while (failure != 0);
    241 
    242   return oldval;
    243 }
    244 
    245 #define SUBWORD_TEST_AND_SET(TYPE, WIDTH)				\
    246   TYPE HIDDEN								\
    247   __sync_lock_test_and_set_##WIDTH (TYPE *ptr, TYPE val)		\
    248   {									\
    249     int failure;							\
    250     unsigned int oldval, newval, shift, mask;				\
    251     int *wordptr = (int *) ((unsigned long) ptr & ~3);			\
    252 									\
    253     shift = (((unsigned long) ptr & 3) << 3) ^ INVERT_MASK_##WIDTH;	\
    254     mask = MASK_##WIDTH << shift;					\
    255 									\
    256     do {								\
    257       oldval = *wordptr;						\
    258       newval = (oldval & ~mask)						\
    259 	       | (((unsigned int) val << shift) & mask);		\
    260       failure = __kernel_cmpxchg (oldval, newval, wordptr);		\
    261     } while (failure != 0);						\
    262 									\
    263     return (oldval & mask) >> shift;					\
    264   }
    265 
    266 SUBWORD_TEST_AND_SET (unsigned short, 2)
    267 SUBWORD_TEST_AND_SET (unsigned char,  1)
    268 
    269 #define SYNC_LOCK_RELEASE(TYPE, WIDTH)					\
    270   void HIDDEN								\
    271   __sync_lock_release_##WIDTH (TYPE *ptr)				\
    272   {									\
    273     /* All writes before this point must be seen before we release	\
    274        the lock itself.  */						\
    275     __builtin_sync ();							\
    276     *ptr = 0;								\
    277   }
    278 
    279 SYNC_LOCK_RELEASE (int,   4)
    280 SYNC_LOCK_RELEASE (short, 2)
    281 SYNC_LOCK_RELEASE (char,  1)
    282