4 * Userspace RCU library - architecture compatibility checks
6 * Copyright (c) 2009 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23 #include <urcu/arch.h>
25 #if defined(URCU_ARCH_I386)
30 #include <urcu/assert.h>
31 #include <urcu/uatomic.h>
34 * Using attribute "weak" for __rcu_cas_avail and
35 * __urcu_x86_compat_mutex. Those are globally visible by the entire
36 * program, even though many shared objects may have their own version.
37 * The first version that gets loaded will be used by the entire
38 * program (executable and all shared objects).
42 * It does not really matter if the constructor is called before using
43 * the library, as long as the caller checks if __rcu_cas_avail < 0 and calls
44 * compat_arch_init() explicitly if needed.
46 int __attribute__((constructor
)) __rcu_cas_init(void);
54 int __rcu_cas_avail
= -1;
57 pthread_mutex_t __urcu_x86_compat_mutex
= PTHREAD_MUTEX_INITIALIZER
;
60 * get_eflags/set_eflags/compare_and_swap_is_available imported from glibc
61 * 2.3.5. linuxthreads/sysdeps/i386/pt-machine.h.
64 static int get_eflags (void)
67 __asm__
__volatile__ ("pushfl; popl %0" : "=r" (res
) : );
71 static void set_eflags (int newflags
)
73 __asm__
__volatile__ ("pushl %0; popfl" : : "r" (newflags
) : "cc");
76 static int compare_and_swap_is_available (void)
78 int oldflags
= get_eflags ();
80 /* Flip AC bit in EFLAGS. */
81 set_eflags (oldflags
^ 0x40000);
82 /* See if bit changed. */
83 changed
= (get_eflags () ^ oldflags
) & 0x40000;
85 set_eflags (oldflags
);
86 /* If the AC flag did not change, it's a 386 and it lacks cmpxchg.
87 Otherwise, it's a 486 or above and it has cmpxchg. */
91 static void mutex_lock_signal_save(pthread_mutex_t
*mutex
, sigset_t
*oldmask
)
97 ret
= sigfillset(&newmask
);
98 urcu_posix_assert(!ret
);
99 ret
= pthread_sigmask(SIG_BLOCK
, &newmask
, oldmask
);
100 urcu_posix_assert(!ret
);
101 ret
= pthread_mutex_lock(&__urcu_x86_compat_mutex
);
102 urcu_posix_assert(!ret
);
105 static void mutex_lock_signal_restore(pthread_mutex_t
*mutex
, sigset_t
*oldmask
)
109 ret
= pthread_mutex_unlock(&__urcu_x86_compat_mutex
);
110 urcu_posix_assert(!ret
);
111 ret
= pthread_sigmask(SIG_SETMASK
, oldmask
, NULL
);
112 urcu_posix_assert(!ret
);
115 unsigned long _compat_uatomic_set(void *addr
, unsigned long _new
, int len
)
118 unsigned long result
;
120 mutex_lock_signal_save(&__urcu_x86_compat_mutex
, &mask
);
123 *(unsigned char *)addr
= (unsigned char)_new
;
124 result
= *(unsigned char *)addr
;
127 *(unsigned short *)addr
= (unsigned short)_new
;
128 result
= *(unsigned short *)addr
;
131 *(unsigned int *)addr
= (unsigned int)_new
;
132 result
= *(unsigned int *)addr
;
136 * generate an illegal instruction. Cannot catch this with
137 * linker tricks when optimizations are disabled.
140 __asm__
__volatile__("ud2");
142 mutex_lock_signal_restore(&__urcu_x86_compat_mutex
, &mask
);
146 unsigned long _compat_uatomic_xchg(void *addr
, unsigned long _new
, int len
)
149 unsigned long retval
;
151 mutex_lock_signal_save(&__urcu_x86_compat_mutex
, &mask
);
154 retval
= *(unsigned char *)addr
;
155 *(unsigned char *)addr
= (unsigned char)_new
;
158 retval
= *(unsigned short *)addr
;
159 *(unsigned short *)addr
= (unsigned short)_new
;
162 retval
= *(unsigned int *)addr
;
163 *(unsigned int *)addr
= (unsigned int)_new
;
167 * generate an illegal instruction. Cannot catch this with
168 * linker tricks when optimizations are disabled.
170 retval
= 0; /* silence gcc warnings */
171 __asm__
__volatile__("ud2");
173 mutex_lock_signal_restore(&__urcu_x86_compat_mutex
, &mask
);
177 unsigned long _compat_uatomic_cmpxchg(void *addr
, unsigned long old
,
178 unsigned long _new
, int len
)
180 unsigned long retval
;
183 mutex_lock_signal_save(&__urcu_x86_compat_mutex
, &mask
);
187 unsigned char result
= *(unsigned char *)addr
;
188 if (result
== (unsigned char)old
)
189 *(unsigned char *)addr
= (unsigned char)_new
;
195 unsigned short result
= *(unsigned short *)addr
;
196 if (result
== (unsigned short)old
)
197 *(unsigned short *)addr
= (unsigned short)_new
;
203 unsigned int result
= *(unsigned int *)addr
;
204 if (result
== (unsigned int)old
)
205 *(unsigned int *)addr
= (unsigned int)_new
;
211 * generate an illegal instruction. Cannot catch this with
212 * linker tricks when optimizations are disabled.
214 retval
= 0; /* silence gcc warnings */
215 __asm__
__volatile__("ud2");
217 mutex_lock_signal_restore(&__urcu_x86_compat_mutex
, &mask
);
221 void _compat_uatomic_or(void *addr
, unsigned long v
, int len
)
225 mutex_lock_signal_save(&__urcu_x86_compat_mutex
, &mask
);
228 *(unsigned char *)addr
|= (unsigned char)v
;
231 *(unsigned short *)addr
|= (unsigned short)v
;
234 *(unsigned int *)addr
|= (unsigned int)v
;
238 * generate an illegal instruction. Cannot catch this with
239 * linker tricks when optimizations are disabled.
241 __asm__
__volatile__("ud2");
243 mutex_lock_signal_restore(&__urcu_x86_compat_mutex
, &mask
);
246 void _compat_uatomic_and(void *addr
, unsigned long v
, int len
)
250 mutex_lock_signal_save(&__urcu_x86_compat_mutex
, &mask
);
253 *(unsigned char *)addr
&= (unsigned char)v
;
256 *(unsigned short *)addr
&= (unsigned short)v
;
259 *(unsigned int *)addr
&= (unsigned int)v
;
263 * generate an illegal instruction. Cannot catch this with
264 * linker tricks when optimizations are disabled.
266 __asm__
__volatile__("ud2");
268 mutex_lock_signal_restore(&__urcu_x86_compat_mutex
, &mask
);
271 unsigned long _compat_uatomic_add_return(void *addr
, unsigned long v
, int len
)
274 unsigned long result
;
276 mutex_lock_signal_save(&__urcu_x86_compat_mutex
, &mask
);
279 *(unsigned char *)addr
+= (unsigned char)v
;
280 result
= *(unsigned char *)addr
;
283 *(unsigned short *)addr
+= (unsigned short)v
;
284 result
= *(unsigned short *)addr
;
287 *(unsigned int *)addr
+= (unsigned int)v
;
288 result
= *(unsigned int *)addr
;
292 * generate an illegal instruction. Cannot catch this with
293 * linker tricks when optimizations are disabled.
295 result
= 0; /* silence gcc warnings */
296 __asm__
__volatile__("ud2");
298 mutex_lock_signal_restore(&__urcu_x86_compat_mutex
, &mask
);
302 int __rcu_cas_init(void)
304 if (__rcu_cas_avail
< 0)
305 __rcu_cas_avail
= compare_and_swap_is_available();
306 return __rcu_cas_avail
;