136ac495dSmrg /* Linux-specific atomic operations for Nios II Linux.
2*8feb0f0bSmrg Copyright (C) 2008-2020 Free Software Foundation, Inc.
336ac495dSmrg
436ac495dSmrg This file is free software; you can redistribute it and/or modify it
536ac495dSmrg under the terms of the GNU General Public License as published by the
636ac495dSmrg Free Software Foundation; either version 3, or (at your option) any
736ac495dSmrg later version.
836ac495dSmrg
936ac495dSmrg This file is distributed in the hope that it will be useful, but
1036ac495dSmrg WITHOUT ANY WARRANTY; without even the implied warranty of
1136ac495dSmrg MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1236ac495dSmrg General Public License for more details.
1336ac495dSmrg
1436ac495dSmrg Under Section 7 of GPL version 3, you are granted additional
1536ac495dSmrg permissions described in the GCC Runtime Library Exception, version
1636ac495dSmrg 3.1, as published by the Free Software Foundation.
1736ac495dSmrg
1836ac495dSmrg You should have received a copy of the GNU General Public License and
1936ac495dSmrg a copy of the GCC Runtime Library Exception along with this program;
2036ac495dSmrg see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
2136ac495dSmrg <http://www.gnu.org/licenses/>. */
2236ac495dSmrg
2336ac495dSmrg /* We implement byte, short and int versions of each atomic operation
2436ac495dSmrg using the kernel helper defined below. There is no support for
2536ac495dSmrg 64-bit operations yet. */
2636ac495dSmrg
2736ac495dSmrg /* Crash a userspace program with SIGSEV. */
2836ac495dSmrg #define ABORT_INSTRUCTION asm ("stw zero, 0(zero)")
2936ac495dSmrg
3036ac495dSmrg /* Kernel helper for compare-and-exchange a 32-bit value. */
3136ac495dSmrg static inline long
__kernel_cmpxchg(int oldval,int newval,int * mem)3236ac495dSmrg __kernel_cmpxchg (int oldval, int newval, int *mem)
3336ac495dSmrg {
3436ac495dSmrg register int r2 asm ("r2");
3536ac495dSmrg register int *r4 asm ("r4") = mem;
3636ac495dSmrg register int r5 asm ("r5") = oldval;
3736ac495dSmrg register int r6 asm ("r6") = newval;
3836ac495dSmrg
3936ac495dSmrg /* Call the kernel provided fixed address cmpxchg helper routine. */
4036ac495dSmrg asm volatile ("movi %0, %4\n\t"
4136ac495dSmrg "callr %0\n"
4236ac495dSmrg : "=r" (r2)
4336ac495dSmrg : "r" (r4), "r" (r5), "r" (r6), "I" (0x00001004)
4436ac495dSmrg : "ra", "memory");
4536ac495dSmrg return r2;
4636ac495dSmrg }
4736ac495dSmrg
4836ac495dSmrg #define HIDDEN __attribute__ ((visibility ("hidden")))
4936ac495dSmrg
5036ac495dSmrg #ifdef __nios2_little_endian__
5136ac495dSmrg #define INVERT_MASK_1 0
5236ac495dSmrg #define INVERT_MASK_2 0
5336ac495dSmrg #else
5436ac495dSmrg #define INVERT_MASK_1 24
5536ac495dSmrg #define INVERT_MASK_2 16
5636ac495dSmrg #endif
5736ac495dSmrg
5836ac495dSmrg #define MASK_1 0xffu
5936ac495dSmrg #define MASK_2 0xffffu
6036ac495dSmrg
6136ac495dSmrg #define FETCH_AND_OP_WORD(OP, PFX_OP, INF_OP) \
6236ac495dSmrg int HIDDEN \
6336ac495dSmrg __sync_fetch_and_##OP##_4 (int *ptr, int val) \
6436ac495dSmrg { \
6536ac495dSmrg int failure, tmp; \
6636ac495dSmrg \
6736ac495dSmrg do { \
6836ac495dSmrg tmp = *ptr; \
6936ac495dSmrg failure = __kernel_cmpxchg (tmp, PFX_OP (tmp INF_OP val), ptr); \
7036ac495dSmrg } while (failure != 0); \
7136ac495dSmrg \
7236ac495dSmrg return tmp; \
7336ac495dSmrg }
7436ac495dSmrg
7536ac495dSmrg FETCH_AND_OP_WORD (add, , +)
7636ac495dSmrg FETCH_AND_OP_WORD (sub, , -)
7736ac495dSmrg FETCH_AND_OP_WORD (or, , |)
7836ac495dSmrg FETCH_AND_OP_WORD (and, , &)
7936ac495dSmrg FETCH_AND_OP_WORD (xor, , ^)
8036ac495dSmrg FETCH_AND_OP_WORD (nand, ~, &)
8136ac495dSmrg
8236ac495dSmrg #define NAME_oldval(OP, WIDTH) __sync_fetch_and_##OP##_##WIDTH
8336ac495dSmrg #define NAME_newval(OP, WIDTH) __sync_##OP##_and_fetch_##WIDTH
8436ac495dSmrg
8536ac495dSmrg /* Implement both __sync_<op>_and_fetch and __sync_fetch_and_<op> for
8636ac495dSmrg subword-sized quantities. */
8736ac495dSmrg
8836ac495dSmrg #define SUBWORD_SYNC_OP(OP, PFX_OP, INF_OP, TYPE, WIDTH, RETURN) \
8936ac495dSmrg TYPE HIDDEN \
9036ac495dSmrg NAME##_##RETURN (OP, WIDTH) (TYPE *ptr, TYPE val) \
9136ac495dSmrg { \
9236ac495dSmrg int *wordptr = (int *) ((unsigned long) ptr & ~3); \
9336ac495dSmrg unsigned int mask, shift, oldval, newval; \
9436ac495dSmrg int failure; \
9536ac495dSmrg \
9636ac495dSmrg shift = (((unsigned long) ptr & 3) << 3) ^ INVERT_MASK_##WIDTH; \
9736ac495dSmrg mask = MASK_##WIDTH << shift; \
9836ac495dSmrg \
9936ac495dSmrg do { \
10036ac495dSmrg oldval = *wordptr; \
10136ac495dSmrg newval = ((PFX_OP (((oldval & mask) >> shift) \
10236ac495dSmrg INF_OP (unsigned int) val)) << shift) & mask; \
10336ac495dSmrg newval |= oldval & ~mask; \
10436ac495dSmrg failure = __kernel_cmpxchg (oldval, newval, wordptr); \
10536ac495dSmrg } while (failure != 0); \
10636ac495dSmrg \
10736ac495dSmrg return (RETURN & mask) >> shift; \
10836ac495dSmrg }
10936ac495dSmrg
11036ac495dSmrg SUBWORD_SYNC_OP (add, , +, unsigned short, 2, oldval)
11136ac495dSmrg SUBWORD_SYNC_OP (sub, , -, unsigned short, 2, oldval)
11236ac495dSmrg SUBWORD_SYNC_OP (or, , |, unsigned short, 2, oldval)
11336ac495dSmrg SUBWORD_SYNC_OP (and, , &, unsigned short, 2, oldval)
11436ac495dSmrg SUBWORD_SYNC_OP (xor, , ^, unsigned short, 2, oldval)
11536ac495dSmrg SUBWORD_SYNC_OP (nand, ~, &, unsigned short, 2, oldval)
11636ac495dSmrg
11736ac495dSmrg SUBWORD_SYNC_OP (add, , +, unsigned char, 1, oldval)
11836ac495dSmrg SUBWORD_SYNC_OP (sub, , -, unsigned char, 1, oldval)
11936ac495dSmrg SUBWORD_SYNC_OP (or, , |, unsigned char, 1, oldval)
12036ac495dSmrg SUBWORD_SYNC_OP (and, , &, unsigned char, 1, oldval)
12136ac495dSmrg SUBWORD_SYNC_OP (xor, , ^, unsigned char, 1, oldval)
12236ac495dSmrg SUBWORD_SYNC_OP (nand, ~, &, unsigned char, 1, oldval)
12336ac495dSmrg
12436ac495dSmrg #define OP_AND_FETCH_WORD(OP, PFX_OP, INF_OP) \
12536ac495dSmrg int HIDDEN \
12636ac495dSmrg __sync_##OP##_and_fetch_4 (int *ptr, int val) \
12736ac495dSmrg { \
12836ac495dSmrg int tmp, failure; \
12936ac495dSmrg \
13036ac495dSmrg do { \
13136ac495dSmrg tmp = *ptr; \
13236ac495dSmrg failure = __kernel_cmpxchg (tmp, PFX_OP (tmp INF_OP val), ptr); \
13336ac495dSmrg } while (failure != 0); \
13436ac495dSmrg \
13536ac495dSmrg return PFX_OP (tmp INF_OP val); \
13636ac495dSmrg }
13736ac495dSmrg
13836ac495dSmrg OP_AND_FETCH_WORD (add, , +)
13936ac495dSmrg OP_AND_FETCH_WORD (sub, , -)
14036ac495dSmrg OP_AND_FETCH_WORD (or, , |)
14136ac495dSmrg OP_AND_FETCH_WORD (and, , &)
14236ac495dSmrg OP_AND_FETCH_WORD (xor, , ^)
14336ac495dSmrg OP_AND_FETCH_WORD (nand, ~, &)
14436ac495dSmrg
14536ac495dSmrg SUBWORD_SYNC_OP (add, , +, unsigned short, 2, newval)
14636ac495dSmrg SUBWORD_SYNC_OP (sub, , -, unsigned short, 2, newval)
14736ac495dSmrg SUBWORD_SYNC_OP (or, , |, unsigned short, 2, newval)
14836ac495dSmrg SUBWORD_SYNC_OP (and, , &, unsigned short, 2, newval)
14936ac495dSmrg SUBWORD_SYNC_OP (xor, , ^, unsigned short, 2, newval)
15036ac495dSmrg SUBWORD_SYNC_OP (nand, ~, &, unsigned short, 2, newval)
15136ac495dSmrg
15236ac495dSmrg SUBWORD_SYNC_OP (add, , +, unsigned char, 1, newval)
15336ac495dSmrg SUBWORD_SYNC_OP (sub, , -, unsigned char, 1, newval)
15436ac495dSmrg SUBWORD_SYNC_OP (or, , |, unsigned char, 1, newval)
15536ac495dSmrg SUBWORD_SYNC_OP (and, , &, unsigned char, 1, newval)
15636ac495dSmrg SUBWORD_SYNC_OP (xor, , ^, unsigned char, 1, newval)
15736ac495dSmrg SUBWORD_SYNC_OP (nand, ~, &, unsigned char, 1, newval)
15836ac495dSmrg
15936ac495dSmrg int HIDDEN
__sync_val_compare_and_swap_4(int * ptr,int oldval,int newval)16036ac495dSmrg __sync_val_compare_and_swap_4 (int *ptr, int oldval, int newval)
16136ac495dSmrg {
16236ac495dSmrg int actual_oldval, fail;
16336ac495dSmrg
16436ac495dSmrg while (1)
16536ac495dSmrg {
16636ac495dSmrg actual_oldval = *ptr;
16736ac495dSmrg
16836ac495dSmrg if (oldval != actual_oldval)
16936ac495dSmrg return actual_oldval;
17036ac495dSmrg
17136ac495dSmrg fail = __kernel_cmpxchg (actual_oldval, newval, ptr);
17236ac495dSmrg
17336ac495dSmrg if (!fail)
17436ac495dSmrg return oldval;
17536ac495dSmrg }
17636ac495dSmrg }
17736ac495dSmrg
17836ac495dSmrg #define SUBWORD_VAL_CAS(TYPE, WIDTH) \
17936ac495dSmrg TYPE HIDDEN \
18036ac495dSmrg __sync_val_compare_and_swap_##WIDTH (TYPE *ptr, TYPE oldval, \
18136ac495dSmrg TYPE newval) \
18236ac495dSmrg { \
18336ac495dSmrg int *wordptr = (int *)((unsigned long) ptr & ~3), fail; \
18436ac495dSmrg unsigned int mask, shift, actual_oldval, actual_newval; \
18536ac495dSmrg \
18636ac495dSmrg shift = (((unsigned long) ptr & 3) << 3) ^ INVERT_MASK_##WIDTH; \
18736ac495dSmrg mask = MASK_##WIDTH << shift; \
18836ac495dSmrg \
18936ac495dSmrg while (1) \
19036ac495dSmrg { \
19136ac495dSmrg actual_oldval = *wordptr; \
19236ac495dSmrg \
19336ac495dSmrg if (((actual_oldval & mask) >> shift) != (unsigned int) oldval) \
19436ac495dSmrg return (actual_oldval & mask) >> shift; \
19536ac495dSmrg \
19636ac495dSmrg actual_newval = (actual_oldval & ~mask) \
19736ac495dSmrg | (((unsigned int) newval << shift) & mask); \
19836ac495dSmrg \
19936ac495dSmrg fail = __kernel_cmpxchg (actual_oldval, actual_newval, \
20036ac495dSmrg wordptr); \
20136ac495dSmrg \
20236ac495dSmrg if (!fail) \
20336ac495dSmrg return oldval; \
20436ac495dSmrg } \
20536ac495dSmrg }
20636ac495dSmrg
20736ac495dSmrg SUBWORD_VAL_CAS (unsigned short, 2)
20836ac495dSmrg SUBWORD_VAL_CAS (unsigned char, 1)
20936ac495dSmrg
21036ac495dSmrg typedef unsigned char bool;
21136ac495dSmrg
21236ac495dSmrg bool HIDDEN
__sync_bool_compare_and_swap_4(int * ptr,int oldval,int newval)21336ac495dSmrg __sync_bool_compare_and_swap_4 (int *ptr, int oldval, int newval)
21436ac495dSmrg {
21536ac495dSmrg int failure = __kernel_cmpxchg (oldval, newval, ptr);
21636ac495dSmrg return (failure == 0);
21736ac495dSmrg }
21836ac495dSmrg
21936ac495dSmrg #define SUBWORD_BOOL_CAS(TYPE, WIDTH) \
22036ac495dSmrg bool HIDDEN \
22136ac495dSmrg __sync_bool_compare_and_swap_##WIDTH (TYPE *ptr, TYPE oldval, \
22236ac495dSmrg TYPE newval) \
22336ac495dSmrg { \
22436ac495dSmrg TYPE actual_oldval \
22536ac495dSmrg = __sync_val_compare_and_swap_##WIDTH (ptr, oldval, newval); \
22636ac495dSmrg return (oldval == actual_oldval); \
22736ac495dSmrg }
22836ac495dSmrg
22936ac495dSmrg SUBWORD_BOOL_CAS (unsigned short, 2)
23036ac495dSmrg SUBWORD_BOOL_CAS (unsigned char, 1)
23136ac495dSmrg
23236ac495dSmrg int HIDDEN
__sync_lock_test_and_set_4(int * ptr,int val)23336ac495dSmrg __sync_lock_test_and_set_4 (int *ptr, int val)
23436ac495dSmrg {
23536ac495dSmrg int failure, oldval;
23636ac495dSmrg
23736ac495dSmrg do {
23836ac495dSmrg oldval = *ptr;
23936ac495dSmrg failure = __kernel_cmpxchg (oldval, val, ptr);
24036ac495dSmrg } while (failure != 0);
24136ac495dSmrg
24236ac495dSmrg return oldval;
24336ac495dSmrg }
24436ac495dSmrg
24536ac495dSmrg #define SUBWORD_TEST_AND_SET(TYPE, WIDTH) \
24636ac495dSmrg TYPE HIDDEN \
24736ac495dSmrg __sync_lock_test_and_set_##WIDTH (TYPE *ptr, TYPE val) \
24836ac495dSmrg { \
24936ac495dSmrg int failure; \
25036ac495dSmrg unsigned int oldval, newval, shift, mask; \
25136ac495dSmrg int *wordptr = (int *) ((unsigned long) ptr & ~3); \
25236ac495dSmrg \
25336ac495dSmrg shift = (((unsigned long) ptr & 3) << 3) ^ INVERT_MASK_##WIDTH; \
25436ac495dSmrg mask = MASK_##WIDTH << shift; \
25536ac495dSmrg \
25636ac495dSmrg do { \
25736ac495dSmrg oldval = *wordptr; \
25836ac495dSmrg newval = (oldval & ~mask) \
25936ac495dSmrg | (((unsigned int) val << shift) & mask); \
26036ac495dSmrg failure = __kernel_cmpxchg (oldval, newval, wordptr); \
26136ac495dSmrg } while (failure != 0); \
26236ac495dSmrg \
26336ac495dSmrg return (oldval & mask) >> shift; \
26436ac495dSmrg }
26536ac495dSmrg
26636ac495dSmrg SUBWORD_TEST_AND_SET (unsigned short, 2)
26736ac495dSmrg SUBWORD_TEST_AND_SET (unsigned char, 1)
26836ac495dSmrg
26936ac495dSmrg #define SYNC_LOCK_RELEASE(TYPE, WIDTH) \
27036ac495dSmrg void HIDDEN \
27136ac495dSmrg __sync_lock_release_##WIDTH (TYPE *ptr) \
27236ac495dSmrg { \
27336ac495dSmrg /* All writes before this point must be seen before we release \
27436ac495dSmrg the lock itself. */ \
27536ac495dSmrg __builtin_sync (); \
27636ac495dSmrg *ptr = 0; \
27736ac495dSmrg }
27836ac495dSmrg
27936ac495dSmrg SYNC_LOCK_RELEASE (int, 4)
28036ac495dSmrg SYNC_LOCK_RELEASE (short, 2)
28136ac495dSmrg SYNC_LOCK_RELEASE (char, 1)
282