1 1.1 mrg /* Linux-specific atomic operations for PA Linux. 2 1.11 mrg Copyright (C) 2008-2022 Free Software Foundation, Inc. 3 1.1 mrg Based on code contributed by CodeSourcery for ARM EABI Linux. 4 1.1 mrg Modifications for PA Linux by Helge Deller <deller (at) gmx.de> 5 1.1 mrg 6 1.1 mrg This file is part of GCC. 7 1.1 mrg 8 1.1 mrg GCC is free software; you can redistribute it and/or modify it under 9 1.1 mrg the terms of the GNU General Public License as published by the Free 10 1.1 mrg Software Foundation; either version 3, or (at your option) any later 11 1.1 mrg version. 12 1.1 mrg 13 1.1 mrg GCC is distributed in the hope that it will be useful, but WITHOUT ANY 14 1.1 mrg WARRANTY; without even the implied warranty of MERCHANTABILITY or 15 1.1 mrg FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 16 1.1 mrg for more details. 17 1.1 mrg 18 1.1 mrg Under Section 7 of GPL version 3, you are granted additional 19 1.1 mrg permissions described in the GCC Runtime Library Exception, version 20 1.1 mrg 3.1, as published by the Free Software Foundation. 21 1.1 mrg 22 1.1 mrg You should have received a copy of the GNU General Public License and 23 1.1 mrg a copy of the GCC Runtime Library Exception along with this program; 24 1.1 mrg see the files COPYING3 and COPYING.RUNTIME respectively. If not, see 25 1.1 mrg <http://www.gnu.org/licenses/>. */ 26 1.1 mrg 27 1.1 mrg #define EFAULT 14 28 1.1 mrg #define EBUSY 16 29 1.1 mrg #define ENOSYS 251 30 1.1 mrg 31 1.11 mrg #define _ASM_EFAULT "-14" 32 1.11 mrg 33 1.10 mrg typedef unsigned char u8; 34 1.10 mrg typedef short unsigned int u16; 35 1.10 mrg #ifdef __LP64__ 36 1.10 mrg typedef long unsigned int u64; 37 1.10 mrg #else 38 1.10 mrg typedef long long unsigned int u64; 39 1.10 mrg #endif 40 1.10 mrg 41 1.5 mrg /* PA-RISC 2.0 supports out-of-order execution for loads and stores. 42 1.5 mrg Thus, we need to synchonize memory accesses. For more info, see: 43 1.5 mrg "Advanced Performance Features of the 64-bit PA-8000" by Doug Hunt. 44 1.1 mrg 45 1.1 mrg We implement byte, short and int versions of each atomic operation 46 1.1 mrg using the kernel helper defined below. There is no support for 47 1.1 mrg 64-bit operations yet. */ 48 1.1 mrg 49 1.1 mrg /* Determine kernel LWS function call (0=32-bit, 1=64-bit userspace). */ 50 1.3 mrg #define LWS_CAS (sizeof(long) == 4 ? 0 : 1) 51 1.1 mrg 52 1.1 mrg /* Kernel helper for compare-and-exchange a 32-bit value. */ 53 1.1 mrg static inline long 54 1.8 mrg __kernel_cmpxchg (volatile void *mem, int oldval, int newval) 55 1.1 mrg { 56 1.1 mrg register unsigned long lws_mem asm("r26") = (unsigned long) (mem); 57 1.3 mrg register int lws_old asm("r25") = oldval; 58 1.3 mrg register int lws_new asm("r24") = newval; 59 1.1 mrg register long lws_ret asm("r28"); 60 1.1 mrg register long lws_errno asm("r21"); 61 1.1 mrg asm volatile ( "ble 0xb0(%%sr2, %%r0) \n\t" 62 1.3 mrg "ldi %2, %%r20 \n\t" 63 1.11 mrg "cmpiclr,<> " _ASM_EFAULT ", %%r21, %%r0\n\t" 64 1.11 mrg "iitlbp %%r0,(%%sr0, %%r0) \n\t" 65 1.3 mrg : "=r" (lws_ret), "=r" (lws_errno) 66 1.3 mrg : "i" (LWS_CAS), "r" (lws_mem), "r" (lws_old), "r" (lws_new) 67 1.1 mrg : "r1", "r20", "r22", "r23", "r29", "r31", "memory" 68 1.1 mrg ); 69 1.1 mrg 70 1.1 mrg /* If the kernel LWS call succeeded (lws_errno == 0), lws_ret contains 71 1.1 mrg the old value from memory. If this value is equal to OLDVAL, the 72 1.1 mrg new value was written to memory. If not, return -EBUSY. */ 73 1.1 mrg if (!lws_errno && lws_ret != oldval) 74 1.8 mrg return -EBUSY; 75 1.1 mrg 76 1.1 mrg return lws_errno; 77 1.1 mrg } 78 1.1 mrg 79 1.3 mrg static inline long 80 1.8 mrg __kernel_cmpxchg2 (volatile void *mem, const void *oldval, const void *newval, 81 1.3 mrg int val_size) 82 1.3 mrg { 83 1.3 mrg register unsigned long lws_mem asm("r26") = (unsigned long) (mem); 84 1.3 mrg register unsigned long lws_old asm("r25") = (unsigned long) oldval; 85 1.3 mrg register unsigned long lws_new asm("r24") = (unsigned long) newval; 86 1.3 mrg register int lws_size asm("r23") = val_size; 87 1.3 mrg register long lws_ret asm("r28"); 88 1.3 mrg register long lws_errno asm("r21"); 89 1.3 mrg asm volatile ( "ble 0xb0(%%sr2, %%r0) \n\t" 90 1.3 mrg "ldi %6, %%r20 \n\t" 91 1.11 mrg "cmpiclr,<> " _ASM_EFAULT ", %%r21, %%r0\n\t" 92 1.11 mrg "iitlbp %%r0,(%%sr0, %%r0) \n\t" 93 1.3 mrg : "=r" (lws_ret), "=r" (lws_errno), "+r" (lws_mem), 94 1.3 mrg "+r" (lws_old), "+r" (lws_new), "+r" (lws_size) 95 1.3 mrg : "i" (2) 96 1.3 mrg : "r1", "r20", "r22", "r29", "r31", "fr4", "memory" 97 1.3 mrg ); 98 1.3 mrg 99 1.3 mrg /* If the kernel LWS call is successful, lws_ret contains 0. */ 100 1.3 mrg if (__builtin_expect (lws_ret == 0, 1)) 101 1.3 mrg return 0; 102 1.3 mrg 103 1.3 mrg /* If the kernel LWS call fails with no error, return -EBUSY */ 104 1.3 mrg if (__builtin_expect (!lws_errno, 0)) 105 1.3 mrg return -EBUSY; 106 1.3 mrg 107 1.3 mrg return lws_errno; 108 1.3 mrg } 109 1.1 mrg #define HIDDEN __attribute__ ((visibility ("hidden"))) 110 1.1 mrg 111 1.1 mrg /* Big endian masks */ 112 1.1 mrg #define INVERT_MASK_1 24 113 1.1 mrg #define INVERT_MASK_2 16 114 1.1 mrg 115 1.1 mrg #define MASK_1 0xffu 116 1.1 mrg #define MASK_2 0xffffu 117 1.1 mrg 118 1.3 mrg #define FETCH_AND_OP_2(OP, PFX_OP, INF_OP, TYPE, WIDTH, INDEX) \ 119 1.3 mrg TYPE HIDDEN \ 120 1.8 mrg __sync_fetch_and_##OP##_##WIDTH (volatile void *ptr, TYPE val) \ 121 1.1 mrg { \ 122 1.3 mrg TYPE tmp, newval; \ 123 1.3 mrg long failure; \ 124 1.1 mrg \ 125 1.1 mrg do { \ 126 1.8 mrg tmp = __atomic_load_n ((volatile TYPE *)ptr, __ATOMIC_RELAXED); \ 127 1.3 mrg newval = PFX_OP (tmp INF_OP val); \ 128 1.3 mrg failure = __kernel_cmpxchg2 (ptr, &tmp, &newval, INDEX); \ 129 1.1 mrg } while (failure != 0); \ 130 1.1 mrg \ 131 1.1 mrg return tmp; \ 132 1.1 mrg } 133 1.1 mrg 134 1.10 mrg FETCH_AND_OP_2 (add, , +, u64, 8, 3) 135 1.10 mrg FETCH_AND_OP_2 (sub, , -, u64, 8, 3) 136 1.10 mrg FETCH_AND_OP_2 (or, , |, u64, 8, 3) 137 1.10 mrg FETCH_AND_OP_2 (and, , &, u64, 8, 3) 138 1.10 mrg FETCH_AND_OP_2 (xor, , ^, u64, 8, 3) 139 1.10 mrg FETCH_AND_OP_2 (nand, ~, &, u64, 8, 3) 140 1.10 mrg 141 1.10 mrg FETCH_AND_OP_2 (add, , +, u16, 2, 1) 142 1.10 mrg FETCH_AND_OP_2 (sub, , -, u16, 2, 1) 143 1.10 mrg FETCH_AND_OP_2 (or, , |, u16, 2, 1) 144 1.10 mrg FETCH_AND_OP_2 (and, , &, u16, 2, 1) 145 1.10 mrg FETCH_AND_OP_2 (xor, , ^, u16, 2, 1) 146 1.10 mrg FETCH_AND_OP_2 (nand, ~, &, u16, 2, 1) 147 1.10 mrg 148 1.10 mrg FETCH_AND_OP_2 (add, , +, u8, 1, 0) 149 1.10 mrg FETCH_AND_OP_2 (sub, , -, u8, 1, 0) 150 1.10 mrg FETCH_AND_OP_2 (or, , |, u8, 1, 0) 151 1.10 mrg FETCH_AND_OP_2 (and, , &, u8, 1, 0) 152 1.10 mrg FETCH_AND_OP_2 (xor, , ^, u8, 1, 0) 153 1.10 mrg FETCH_AND_OP_2 (nand, ~, &, u8, 1, 0) 154 1.1 mrg 155 1.3 mrg #define OP_AND_FETCH_2(OP, PFX_OP, INF_OP, TYPE, WIDTH, INDEX) \ 156 1.1 mrg TYPE HIDDEN \ 157 1.8 mrg __sync_##OP##_and_fetch_##WIDTH (volatile void *ptr, TYPE val) \ 158 1.1 mrg { \ 159 1.3 mrg TYPE tmp, newval; \ 160 1.3 mrg long failure; \ 161 1.1 mrg \ 162 1.3 mrg do { \ 163 1.8 mrg tmp = __atomic_load_n ((volatile TYPE *)ptr, __ATOMIC_RELAXED); \ 164 1.3 mrg newval = PFX_OP (tmp INF_OP val); \ 165 1.3 mrg failure = __kernel_cmpxchg2 (ptr, &tmp, &newval, INDEX); \ 166 1.3 mrg } while (failure != 0); \ 167 1.3 mrg \ 168 1.3 mrg return PFX_OP (tmp INF_OP val); \ 169 1.3 mrg } 170 1.3 mrg 171 1.10 mrg OP_AND_FETCH_2 (add, , +, u64, 8, 3) 172 1.10 mrg OP_AND_FETCH_2 (sub, , -, u64, 8, 3) 173 1.10 mrg OP_AND_FETCH_2 (or, , |, u64, 8, 3) 174 1.10 mrg OP_AND_FETCH_2 (and, , &, u64, 8, 3) 175 1.10 mrg OP_AND_FETCH_2 (xor, , ^, u64, 8, 3) 176 1.10 mrg OP_AND_FETCH_2 (nand, ~, &, u64, 8, 3) 177 1.10 mrg 178 1.10 mrg OP_AND_FETCH_2 (add, , +, u16, 2, 1) 179 1.10 mrg OP_AND_FETCH_2 (sub, , -, u16, 2, 1) 180 1.10 mrg OP_AND_FETCH_2 (or, , |, u16, 2, 1) 181 1.10 mrg OP_AND_FETCH_2 (and, , &, u16, 2, 1) 182 1.10 mrg OP_AND_FETCH_2 (xor, , ^, u16, 2, 1) 183 1.10 mrg OP_AND_FETCH_2 (nand, ~, &, u16, 2, 1) 184 1.10 mrg 185 1.10 mrg OP_AND_FETCH_2 (add, , +, u8, 1, 0) 186 1.10 mrg OP_AND_FETCH_2 (sub, , -, u8, 1, 0) 187 1.10 mrg OP_AND_FETCH_2 (or, , |, u8, 1, 0) 188 1.10 mrg OP_AND_FETCH_2 (and, , &, u8, 1, 0) 189 1.10 mrg OP_AND_FETCH_2 (xor, , ^, u8, 1, 0) 190 1.10 mrg OP_AND_FETCH_2 (nand, ~, &, u8, 1, 0) 191 1.3 mrg 192 1.3 mrg #define FETCH_AND_OP_WORD(OP, PFX_OP, INF_OP) \ 193 1.8 mrg unsigned int HIDDEN \ 194 1.8 mrg __sync_fetch_and_##OP##_4 (volatile void *ptr, unsigned int val) \ 195 1.3 mrg { \ 196 1.8 mrg unsigned int tmp; \ 197 1.3 mrg long failure; \ 198 1.1 mrg \ 199 1.1 mrg do { \ 200 1.8 mrg tmp = __atomic_load_n ((volatile unsigned int *)ptr, \ 201 1.8 mrg __ATOMIC_RELAXED); \ 202 1.3 mrg failure = __kernel_cmpxchg (ptr, tmp, PFX_OP (tmp INF_OP val)); \ 203 1.1 mrg } while (failure != 0); \ 204 1.1 mrg \ 205 1.3 mrg return tmp; \ 206 1.1 mrg } 207 1.1 mrg 208 1.3 mrg FETCH_AND_OP_WORD (add, , +) 209 1.3 mrg FETCH_AND_OP_WORD (sub, , -) 210 1.3 mrg FETCH_AND_OP_WORD (or, , |) 211 1.3 mrg FETCH_AND_OP_WORD (and, , &) 212 1.3 mrg FETCH_AND_OP_WORD (xor, , ^) 213 1.3 mrg FETCH_AND_OP_WORD (nand, ~, &) 214 1.1 mrg 215 1.1 mrg #define OP_AND_FETCH_WORD(OP, PFX_OP, INF_OP) \ 216 1.8 mrg unsigned int HIDDEN \ 217 1.8 mrg __sync_##OP##_and_fetch_4 (volatile void *ptr, unsigned int val) \ 218 1.1 mrg { \ 219 1.8 mrg unsigned int tmp; \ 220 1.3 mrg long failure; \ 221 1.1 mrg \ 222 1.1 mrg do { \ 223 1.8 mrg tmp = __atomic_load_n ((volatile unsigned int *)ptr, \ 224 1.8 mrg __ATOMIC_RELAXED); \ 225 1.3 mrg failure = __kernel_cmpxchg (ptr, tmp, PFX_OP (tmp INF_OP val)); \ 226 1.1 mrg } while (failure != 0); \ 227 1.1 mrg \ 228 1.1 mrg return PFX_OP (tmp INF_OP val); \ 229 1.1 mrg } 230 1.1 mrg 231 1.1 mrg OP_AND_FETCH_WORD (add, , +) 232 1.1 mrg OP_AND_FETCH_WORD (sub, , -) 233 1.1 mrg OP_AND_FETCH_WORD (or, , |) 234 1.1 mrg OP_AND_FETCH_WORD (and, , &) 235 1.1 mrg OP_AND_FETCH_WORD (xor, , ^) 236 1.1 mrg OP_AND_FETCH_WORD (nand, ~, &) 237 1.1 mrg 238 1.3 mrg typedef unsigned char bool; 239 1.3 mrg 240 1.3 mrg #define COMPARE_AND_SWAP_2(TYPE, WIDTH, INDEX) \ 241 1.3 mrg TYPE HIDDEN \ 242 1.8 mrg __sync_val_compare_and_swap_##WIDTH (volatile void *ptr, TYPE oldval, \ 243 1.3 mrg TYPE newval) \ 244 1.3 mrg { \ 245 1.3 mrg TYPE actual_oldval; \ 246 1.3 mrg long fail; \ 247 1.3 mrg \ 248 1.3 mrg while (1) \ 249 1.3 mrg { \ 250 1.8 mrg actual_oldval = __atomic_load_n ((volatile TYPE *)ptr, \ 251 1.8 mrg __ATOMIC_RELAXED); \ 252 1.3 mrg \ 253 1.3 mrg if (__builtin_expect (oldval != actual_oldval, 0)) \ 254 1.3 mrg return actual_oldval; \ 255 1.3 mrg \ 256 1.3 mrg fail = __kernel_cmpxchg2 (ptr, &actual_oldval, &newval, INDEX); \ 257 1.3 mrg \ 258 1.3 mrg if (__builtin_expect (!fail, 1)) \ 259 1.3 mrg return actual_oldval; \ 260 1.3 mrg } \ 261 1.3 mrg } \ 262 1.3 mrg \ 263 1.8 mrg _Bool HIDDEN \ 264 1.8 mrg __sync_bool_compare_and_swap_##WIDTH (volatile void *ptr, \ 265 1.8 mrg TYPE oldval, TYPE newval) \ 266 1.3 mrg { \ 267 1.3 mrg long failure = __kernel_cmpxchg2 (ptr, &oldval, &newval, INDEX); \ 268 1.3 mrg return (failure == 0); \ 269 1.3 mrg } 270 1.3 mrg 271 1.10 mrg COMPARE_AND_SWAP_2 (u64, 8, 3) 272 1.10 mrg COMPARE_AND_SWAP_2 (u16, 2, 1) 273 1.10 mrg COMPARE_AND_SWAP_2 (u8, 1, 0) 274 1.8 mrg 275 1.8 mrg unsigned int HIDDEN 276 1.8 mrg __sync_val_compare_and_swap_4 (volatile void *ptr, unsigned int oldval, 277 1.8 mrg unsigned int newval) 278 1.1 mrg { 279 1.3 mrg long fail; 280 1.8 mrg unsigned int actual_oldval; 281 1.1 mrg 282 1.1 mrg while (1) 283 1.1 mrg { 284 1.8 mrg actual_oldval = __atomic_load_n ((volatile unsigned int *)ptr, 285 1.8 mrg __ATOMIC_RELAXED); 286 1.1 mrg 287 1.1 mrg if (__builtin_expect (oldval != actual_oldval, 0)) 288 1.1 mrg return actual_oldval; 289 1.1 mrg 290 1.3 mrg fail = __kernel_cmpxchg (ptr, actual_oldval, newval); 291 1.1 mrg 292 1.1 mrg if (__builtin_expect (!fail, 1)) 293 1.1 mrg return actual_oldval; 294 1.1 mrg } 295 1.1 mrg } 296 1.1 mrg 297 1.8 mrg _Bool HIDDEN 298 1.8 mrg __sync_bool_compare_and_swap_4 (volatile void *ptr, unsigned int oldval, 299 1.8 mrg unsigned int newval) 300 1.1 mrg { 301 1.3 mrg long failure = __kernel_cmpxchg (ptr, oldval, newval); 302 1.1 mrg return (failure == 0); 303 1.1 mrg } 304 1.1 mrg 305 1.3 mrg #define SYNC_LOCK_TEST_AND_SET_2(TYPE, WIDTH, INDEX) \ 306 1.3 mrg TYPE HIDDEN \ 307 1.8 mrg __sync_lock_test_and_set_##WIDTH (volatile void *ptr, TYPE val) \ 308 1.1 mrg { \ 309 1.3 mrg TYPE oldval; \ 310 1.3 mrg long failure; \ 311 1.3 mrg \ 312 1.3 mrg do { \ 313 1.8 mrg oldval = __atomic_load_n ((volatile TYPE *)ptr, \ 314 1.8 mrg __ATOMIC_RELAXED); \ 315 1.3 mrg failure = __kernel_cmpxchg2 (ptr, &oldval, &val, INDEX); \ 316 1.3 mrg } while (failure != 0); \ 317 1.3 mrg \ 318 1.3 mrg return oldval; \ 319 1.1 mrg } 320 1.1 mrg 321 1.10 mrg SYNC_LOCK_TEST_AND_SET_2 (u64, 8, 3) 322 1.10 mrg SYNC_LOCK_TEST_AND_SET_2 (u16, 2, 1) 323 1.10 mrg SYNC_LOCK_TEST_AND_SET_2 (u8, 1, 0) 324 1.1 mrg 325 1.8 mrg unsigned int HIDDEN 326 1.8 mrg __sync_lock_test_and_set_4 (volatile void *ptr, unsigned int val) 327 1.1 mrg { 328 1.3 mrg long failure; 329 1.8 mrg unsigned int oldval; 330 1.1 mrg 331 1.1 mrg do { 332 1.8 mrg oldval = __atomic_load_n ((volatile unsigned int *)ptr, __ATOMIC_RELAXED); 333 1.3 mrg failure = __kernel_cmpxchg (ptr, oldval, val); 334 1.1 mrg } while (failure != 0); 335 1.1 mrg 336 1.1 mrg return oldval; 337 1.1 mrg } 338 1.1 mrg 339 1.8 mrg #define SYNC_LOCK_RELEASE_1(TYPE, WIDTH, INDEX) \ 340 1.3 mrg void HIDDEN \ 341 1.8 mrg __sync_lock_release_##WIDTH (volatile void *ptr) \ 342 1.3 mrg { \ 343 1.8 mrg TYPE oldval, val = 0; \ 344 1.8 mrg long failure; \ 345 1.8 mrg \ 346 1.8 mrg do { \ 347 1.8 mrg oldval = __atomic_load_n ((volatile TYPE *)ptr, \ 348 1.8 mrg __ATOMIC_RELAXED); \ 349 1.8 mrg failure = __kernel_cmpxchg2 (ptr, &oldval, &val, INDEX); \ 350 1.8 mrg } while (failure != 0); \ 351 1.1 mrg } 352 1.1 mrg 353 1.10 mrg SYNC_LOCK_RELEASE_1 (u64, 8, 3) 354 1.10 mrg SYNC_LOCK_RELEASE_1 (u16, 2, 1) 355 1.10 mrg SYNC_LOCK_RELEASE_1 (u8, 1, 0) 356 1.8 mrg 357 1.8 mrg void HIDDEN 358 1.8 mrg __sync_lock_release_4 (volatile void *ptr) 359 1.8 mrg { 360 1.8 mrg long failure; 361 1.8 mrg unsigned int oldval; 362 1.8 mrg 363 1.8 mrg do { 364 1.8 mrg oldval = __atomic_load_n ((volatile unsigned int *)ptr, __ATOMIC_RELAXED); 365 1.8 mrg failure = __kernel_cmpxchg (ptr, oldval, 0); 366 1.8 mrg } while (failure != 0); 367 1.8 mrg } 368