Add RISC-V CAS via load-reserved/store conditional
[akaros.git] / kern / arch / riscv / atomic.h
1 #ifndef ROS_KERN_ARCH_ATOMIC_H
2 #define ROS_KERN_ARCH_ATOMIC_H
3
4 #include <ros/common.h>
5 #include <arch/arch.h>
6
7 #ifdef __riscv64
8 # define LR_P "lr.d"
9 # define SC_P "sc.d"
10 #else
11 # define LR_P "lr.w"
12 # define SC_P "sc.w"
13 #endif
14
15 static bool atomic_cas(atomic_t *addr, long exp_val, long new_val)
16 {
17   return __sync_bool_compare_and_swap(addr, exp_val, new_val);
18 }
19
20 static bool atomic_cas_ptr(void** addr, void* exp_val, void* new_val)
21 {
22   return __sync_bool_compare_and_swap(addr, exp_val, new_val);
23 }
24
25 static bool atomic_cas_u32(uint32_t *addr, uint32_t exp_val, uint32_t new_val)
26 {
27   return __sync_bool_compare_and_swap(addr, exp_val, new_val);
28 }
29
30 static inline void atomic_init(atomic_t *number, long val)
31 {
32   *(volatile long*)number = val;
33 }
34
35 static inline long atomic_read(atomic_t *number)
36 {
37   return *(volatile long*)number;
38 }
39
40 static inline void atomic_set(atomic_t *number, long val)
41 {
42   *(volatile long*)number = val;
43 }
44
45 /* Adds val to number, returning number's original value */
46 static inline long atomic_fetch_and_add(atomic_t *number, long val)
47 {
48         return __sync_fetch_and_add((long*)number, val);
49 }
50
51 static inline void atomic_add(atomic_t *number, long val)
52 {
53         atomic_fetch_and_add(number, val);
54 }
55
56 static inline void atomic_inc(atomic_t *number)
57 {
58         atomic_add(number, 1);
59 }
60
61 static inline void atomic_dec(atomic_t *number)
62 {
63         atomic_add(number, -1);
64 }
65
66 /* Adds val to number, so long as number was not zero.  Returns TRUE if the
67  * operation succeeded (added, not zero), returns FALSE if number is zero. */
68 static inline bool atomic_add_not_zero(atomic_t *number, long val)
69 {
70         long old_num, new_num;
71         do {
72                 old_num = atomic_read(number);
73                 if (!old_num)
74                         return FALSE;
75                 new_num = old_num + val;
76         } while (!atomic_cas(number, old_num, new_num));
77         return TRUE;
78 }
79
80 /* Subtraces val from number, returning True if the new value is 0. */
81 static inline bool atomic_sub_and_test(atomic_t *number, long val)
82 {
83         return __sync_fetch_and_sub((long*)number, val) == val;
84 }
85
86 static inline void atomic_and(atomic_t *number, long mask)
87 {
88         __sync_fetch_and_and(number, mask);
89 }
90
91 static inline void atomic_or(atomic_t *number, long mask)
92 {
93         __sync_fetch_and_or(number, mask);
94 }
95
96 static inline long atomic_swap(atomic_t *addr, long val)
97 {
98         return (long)__sync_lock_test_and_set(addr, val); // yes, really
99 }
100
101 static inline uint32_t atomic_swap_u32(uint32_t *addr, uint32_t val)
102 {
103         return __sync_lock_test_and_set(addr, val); // yes, really
104 }
105
106 // RISC-V has atomic word ops, not byte ops, so we must manipulate addresses
107 static inline void atomic_andb(volatile uint8_t* number, uint8_t mask)
108 {
109         uintptr_t offset = (uintptr_t)number & 3;
110         uint32_t wmask = (1<<(8*offset+8)) - (1<<(8*offset));
111         wmask = ~wmask | ((uint32_t)mask << (8*offset));
112
113         __sync_fetch_and_and((uint32_t*)((uintptr_t)number & ~3), wmask);
114 }
115
116 static inline void atomic_orb(volatile uint8_t* number, uint8_t mask)
117 {
118         uintptr_t offset = (uintptr_t)number & 3;
119         uint32_t wmask = (uint32_t)mask << (8*offset);
120
121         __sync_fetch_and_or((uint32_t*)((uintptr_t)number & ~3), wmask);
122 }
123
124 static inline bool spin_locked(spinlock_t* lock)
125 {
126         return lock->rlock;
127 }
128
129 static inline uint32_t spin_trylock(spinlock_t* lock)
130 {
131         return __sync_fetch_and_or(&lock->rlock, 1);
132 }
133
134 static inline void __spin_lock(spinlock_t *lock)
135 {
136         do
137         {
138                 while (lock->rlock)
139                         ;
140         } while (spin_trylock(lock));
141         mb();
142 }
143
144 static inline void __spin_unlock(spinlock_t *lock)
145 {
146         mb();
147         lock->rlock = 0;
148 }
149
150 static inline void __spinlock_init(spinlock_t *lock)
151 {
152         lock->rlock = 0;
153 }
154
155 static inline void spinlock_debug(spinlock_t* lock)
156 {
157 }
158
159 #endif /* ROS_KERN_ARCH_ATOMIC_H */