1 // SPDX-FileCopyrightText: 2009 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
3 // SPDX-License-Identifier: LGPL-2.1-or-later
6 * Userspace RCU library - architecture compatibility checks
11 #ifdef URCU_ARCH_X86_NO_CAS
16 #include <urcu/assert.h>
17 #include <urcu/uatomic.h>
20 * Using attribute "weak" for __rcu_cas_avail and
21 * __urcu_x86_compat_mutex. Those are globally visible by the entire
22 * program, even though many shared objects may have their own version.
23 * The first version that gets loaded will be used by the entire
24 * program (executable and all shared objects).
28 * It does not really matter if the constructor is called before using
29 * the library, as long as the caller checks if __rcu_cas_avail < 0 and calls
30 * compat_arch_init() explicitly if needed.
32 int __attribute__((constructor
)) __rcu_cas_init(void);
40 int __rcu_cas_avail
= -1;
43 pthread_mutex_t __urcu_x86_compat_mutex
= PTHREAD_MUTEX_INITIALIZER
;
46 * get_eflags/set_eflags/compare_and_swap_is_available imported from glibc
47 * 2.3.5. linuxthreads/sysdeps/i386/pt-machine.h.
50 static int get_eflags (void)
53 __asm__
__volatile__ ("pushfl; popl %0" : "=r" (res
) : );
57 static void set_eflags (int newflags
)
59 __asm__
__volatile__ ("pushl %0; popfl" : : "r" (newflags
) : "cc");
62 static int compare_and_swap_is_available (void)
64 int oldflags
= get_eflags ();
66 /* Flip AC bit in EFLAGS. */
67 set_eflags (oldflags
^ 0x40000);
68 /* See if bit changed. */
69 changed
= (get_eflags () ^ oldflags
) & 0x40000;
71 set_eflags (oldflags
);
72 /* If the AC flag did not change, it's a 386 and it lacks cmpxchg.
73 Otherwise, it's a 486 or above and it has cmpxchg. */
77 static void mutex_lock_signal_save(pthread_mutex_t
*mutex
, sigset_t
*oldmask
)
83 ret
= sigfillset(&newmask
);
84 urcu_posix_assert(!ret
);
85 ret
= pthread_sigmask(SIG_BLOCK
, &newmask
, oldmask
);
86 urcu_posix_assert(!ret
);
87 ret
= pthread_mutex_lock(&__urcu_x86_compat_mutex
);
88 urcu_posix_assert(!ret
);
91 static void mutex_lock_signal_restore(pthread_mutex_t
*mutex
, sigset_t
*oldmask
)
95 ret
= pthread_mutex_unlock(&__urcu_x86_compat_mutex
);
96 urcu_posix_assert(!ret
);
97 ret
= pthread_sigmask(SIG_SETMASK
, oldmask
, NULL
);
98 urcu_posix_assert(!ret
);
101 unsigned long _compat_uatomic_set(void *addr
, unsigned long _new
, int len
)
104 unsigned long result
;
106 mutex_lock_signal_save(&__urcu_x86_compat_mutex
, &mask
);
109 *(unsigned char *)addr
= (unsigned char)_new
;
110 result
= *(unsigned char *)addr
;
113 *(unsigned short *)addr
= (unsigned short)_new
;
114 result
= *(unsigned short *)addr
;
117 *(unsigned int *)addr
= (unsigned int)_new
;
118 result
= *(unsigned int *)addr
;
122 * generate an illegal instruction. Cannot catch this with
123 * linker tricks when optimizations are disabled.
126 __asm__
__volatile__("ud2");
128 mutex_lock_signal_restore(&__urcu_x86_compat_mutex
, &mask
);
132 unsigned long _compat_uatomic_xchg(void *addr
, unsigned long _new
, int len
)
135 unsigned long retval
;
137 mutex_lock_signal_save(&__urcu_x86_compat_mutex
, &mask
);
140 retval
= *(unsigned char *)addr
;
141 *(unsigned char *)addr
= (unsigned char)_new
;
144 retval
= *(unsigned short *)addr
;
145 *(unsigned short *)addr
= (unsigned short)_new
;
148 retval
= *(unsigned int *)addr
;
149 *(unsigned int *)addr
= (unsigned int)_new
;
153 * generate an illegal instruction. Cannot catch this with
154 * linker tricks when optimizations are disabled.
156 retval
= 0; /* silence gcc warnings */
157 __asm__
__volatile__("ud2");
159 mutex_lock_signal_restore(&__urcu_x86_compat_mutex
, &mask
);
163 unsigned long _compat_uatomic_cmpxchg(void *addr
, unsigned long old
,
164 unsigned long _new
, int len
)
166 unsigned long retval
;
169 mutex_lock_signal_save(&__urcu_x86_compat_mutex
, &mask
);
173 unsigned char result
= *(unsigned char *)addr
;
174 if (result
== (unsigned char)old
)
175 *(unsigned char *)addr
= (unsigned char)_new
;
181 unsigned short result
= *(unsigned short *)addr
;
182 if (result
== (unsigned short)old
)
183 *(unsigned short *)addr
= (unsigned short)_new
;
189 unsigned int result
= *(unsigned int *)addr
;
190 if (result
== (unsigned int)old
)
191 *(unsigned int *)addr
= (unsigned int)_new
;
197 * generate an illegal instruction. Cannot catch this with
198 * linker tricks when optimizations are disabled.
200 retval
= 0; /* silence gcc warnings */
201 __asm__
__volatile__("ud2");
203 mutex_lock_signal_restore(&__urcu_x86_compat_mutex
, &mask
);
207 void _compat_uatomic_or(void *addr
, unsigned long v
, int len
)
211 mutex_lock_signal_save(&__urcu_x86_compat_mutex
, &mask
);
214 *(unsigned char *)addr
|= (unsigned char)v
;
217 *(unsigned short *)addr
|= (unsigned short)v
;
220 *(unsigned int *)addr
|= (unsigned int)v
;
224 * generate an illegal instruction. Cannot catch this with
225 * linker tricks when optimizations are disabled.
227 __asm__
__volatile__("ud2");
229 mutex_lock_signal_restore(&__urcu_x86_compat_mutex
, &mask
);
232 void _compat_uatomic_and(void *addr
, unsigned long v
, int len
)
236 mutex_lock_signal_save(&__urcu_x86_compat_mutex
, &mask
);
239 *(unsigned char *)addr
&= (unsigned char)v
;
242 *(unsigned short *)addr
&= (unsigned short)v
;
245 *(unsigned int *)addr
&= (unsigned int)v
;
249 * generate an illegal instruction. Cannot catch this with
250 * linker tricks when optimizations are disabled.
252 __asm__
__volatile__("ud2");
254 mutex_lock_signal_restore(&__urcu_x86_compat_mutex
, &mask
);
257 unsigned long _compat_uatomic_add_return(void *addr
, unsigned long v
, int len
)
260 unsigned long result
;
262 mutex_lock_signal_save(&__urcu_x86_compat_mutex
, &mask
);
265 *(unsigned char *)addr
+= (unsigned char)v
;
266 result
= *(unsigned char *)addr
;
269 *(unsigned short *)addr
+= (unsigned short)v
;
270 result
= *(unsigned short *)addr
;
273 *(unsigned int *)addr
+= (unsigned int)v
;
274 result
= *(unsigned int *)addr
;
278 * generate an illegal instruction. Cannot catch this with
279 * linker tricks when optimizations are disabled.
281 result
= 0; /* silence gcc warnings */
282 __asm__
__volatile__("ud2");
284 mutex_lock_signal_restore(&__urcu_x86_compat_mutex
, &mask
);
288 int __rcu_cas_init(void)
290 if (__rcu_cas_avail
< 0)
291 __rcu_cas_avail
= compare_and_swap_is_available();
292 return __rcu_cas_avail
;