Skip to content

Commit 5b96bef

Browse files
committed
Auto merge of #115 - Amanieu:linux-arm-atomic, r=alexcrichton
Add atomic support for pre-ARMv6 on Linux This uses the [kernel user helpers](https://www.kernel.org/doc/Documentation/arm/kernel_user_helpers.txt) which are available starting from kernel 2.6.15. Since Rust currently requires 2.6.18 at a minimum, this should be fine in practice. I did not include support for 64-bit atomics since that functionality is only available in kernel 3.1. This PR allows Rust to work on older ARM versions such as ARMv4 and ARMv5 with the full libstd.
2 parents fe8d893 + 73e38dc commit 5b96bef

File tree

3 files changed

+178
-0
lines changed

3 files changed

+178
-0
lines changed

build.rs

+5
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,11 @@ fn main() {
4040
if llvm_target[0] == "thumbv6m" {
4141
println!("cargo:rustc-cfg=thumbv6m")
4242
}
43+
44+
// Only emit the ARM Linux atomic emulation on pre-ARMv6 architectures.
45+
if llvm_target[0] == "armv5te" {
46+
println!("cargo:rustc-cfg=armv5te")
47+
}
4348
}
4449

4550
#[cfg(feature = "gen-tests")]

src/arm_linux.rs

+170
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
use core::intrinsics;
2+
use core::mem;
3+
4+
// Kernel-provided user-mode helper functions:
5+
// https://www.kernel.org/doc/Documentation/arm/kernel_user_helpers.txt
6+
unsafe fn __kuser_cmpxchg(oldval: u32, newval: u32, ptr: *mut u32) -> bool {
7+
let out: u32;
8+
// FIXME: we can't use BLX on ARMv4
9+
asm!("blx ${0}"
10+
: "={r0}" (out)
11+
: "r" (0xffff0fc0u32)
12+
"{r0}" (oldval),
13+
"{r1}" (newval),
14+
"{r2}" (ptr)
15+
: "r3", "r12", "lr", "cc", "memory");
16+
out == 0
17+
}
18+
unsafe fn __kuser_memory_barrier() {
19+
// FIXME: we can't use BLX on ARMv4
20+
asm!("blx ${0}"
21+
:
22+
: "r" (0xffff0fa0u32)
23+
: "lr", "memory");
24+
}
25+
26+
// Word-align a pointer
27+
fn align_ptr<T>(ptr: *mut T) -> *mut u32 {
28+
// This gives us a mask of 0 when T == u32 since the pointer is already
29+
// supposed to be aligned, which avoids any masking in that case.
30+
let ptr_mask = 3 & (4 - mem::size_of::<T>());
31+
(ptr as usize & !ptr_mask) as *mut u32
32+
}
33+
34+
// Calculate the shift and mask of a value inside an aligned word
35+
fn get_shift_mask<T>(ptr: *mut T) -> (u32, u32) {
36+
// Mask to get the low byte/halfword/word
37+
let mask = match mem::size_of::<T>() {
38+
1 => 0xff,
39+
2 => 0xffff,
40+
4 => 0xffffffff,
41+
_ => unreachable!(),
42+
};
43+
44+
// If we are on big-endian then we need to adjust the shift accordingly
45+
let endian_adjust = if cfg!(target_endian = "little") {
46+
0
47+
} else {
48+
4 - mem::size_of::<T>() as u32
49+
};
50+
51+
// Shift to get the desired element in the word
52+
let ptr_mask = 3 & (4 - mem::size_of::<T>());
53+
let shift = ((ptr as usize & ptr_mask) as u32 ^ endian_adjust) * 8;
54+
55+
(shift, mask)
56+
}
57+
58+
// Extract a value from an aligned word
59+
fn extract_aligned(aligned: u32, shift: u32, mask: u32) -> u32 {
60+
(aligned >> shift) & mask
61+
}
62+
63+
// Insert a value into an aligned word
64+
fn insert_aligned(aligned: u32, val: u32, shift: u32, mask: u32) -> u32 {
65+
(aligned & !(mask << shift)) | ((val & mask) << shift)
66+
}
67+
68+
// Generic atomic read-modify-write operation
69+
unsafe fn atomic_rmw<T, F: Fn(u32) -> u32>(ptr: *mut T, f: F) -> u32 {
70+
let aligned_ptr = align_ptr(ptr);
71+
let (shift, mask) = get_shift_mask(ptr);
72+
73+
loop {
74+
let curval_aligned = intrinsics::atomic_load_unordered(aligned_ptr);
75+
let curval = extract_aligned(curval_aligned, shift, mask);
76+
let newval = f(curval);
77+
let newval_aligned = insert_aligned(curval_aligned, newval, shift, mask);
78+
if __kuser_cmpxchg(curval_aligned, newval_aligned, aligned_ptr) {
79+
return curval;
80+
}
81+
}
82+
}
83+
84+
// Generic atomic compare-exchange operation
85+
unsafe fn atomic_cmpxchg<T>(oldval: u32, newval: u32, ptr: *mut T) -> u32 {
86+
let aligned_ptr = align_ptr(ptr);
87+
let (shift, mask) = get_shift_mask(ptr);
88+
89+
loop {
90+
let curval_aligned = intrinsics::atomic_load_unordered(aligned_ptr);
91+
let curval = extract_aligned(curval_aligned, shift, mask);
92+
if curval != oldval {
93+
return curval;
94+
}
95+
let newval_aligned = insert_aligned(curval_aligned, newval, shift, mask);
96+
if __kuser_cmpxchg(curval_aligned, newval_aligned, aligned_ptr) {
97+
return oldval;
98+
}
99+
}
100+
}
101+
102+
macro_rules! atomic_rmw {
103+
($name:ident, $ty:ty, $op:expr) => {
104+
#[cfg_attr(not(feature = "mangled-names"), no_mangle)]
105+
pub unsafe extern "C" fn $name(ptr: *mut $ty, val: $ty) -> $ty {
106+
atomic_rmw(ptr, |x| $op(x as $ty, val) as u32) as $ty
107+
}
108+
}
109+
}
110+
macro_rules! atomic_cmpxchg {
111+
($name:ident, $ty:ty) => {
112+
#[cfg_attr(not(feature = "mangled-names"), no_mangle)]
113+
pub unsafe extern "C" fn $name(oldval: $ty, newval: $ty, ptr: *mut $ty) -> $ty {
114+
atomic_cmpxchg(oldval as u32, newval as u32, ptr) as $ty
115+
}
116+
}
117+
}
118+
119+
atomic_rmw!(__sync_fetch_and_add_1, u8, |a: u8, b: u8| a.wrapping_add(b));
120+
atomic_rmw!(__sync_fetch_and_add_2, u16, |a: u16, b: u16| a.wrapping_add(b));
121+
atomic_rmw!(__sync_fetch_and_add_4, u32, |a: u32, b: u32| a.wrapping_add(b));
122+
123+
atomic_rmw!(__sync_fetch_and_sub_1, u8, |a: u8, b: u8| a.wrapping_sub(b));
124+
atomic_rmw!(__sync_fetch_and_sub_2, u16, |a: u16, b: u16| a.wrapping_sub(b));
125+
atomic_rmw!(__sync_fetch_and_sub_4, u32, |a: u32, b: u32| a.wrapping_sub(b));
126+
127+
atomic_rmw!(__sync_fetch_and_and_1, u8, |a: u8, b: u8| a & b);
128+
atomic_rmw!(__sync_fetch_and_and_2, u16, |a: u16, b: u16| a & b);
129+
atomic_rmw!(__sync_fetch_and_and_4, u32, |a: u32, b: u32| a & b);
130+
131+
atomic_rmw!(__sync_fetch_and_or_1, u8, |a: u8, b: u8| a | b);
132+
atomic_rmw!(__sync_fetch_and_or_2, u16, |a: u16, b: u16| a | b);
133+
atomic_rmw!(__sync_fetch_and_or_4, u32, |a: u32, b: u32| a | b);
134+
135+
atomic_rmw!(__sync_fetch_and_xor_1, u8, |a: u8, b: u8| a ^ b);
136+
atomic_rmw!(__sync_fetch_and_xor_2, u16, |a: u16, b: u16| a ^ b);
137+
atomic_rmw!(__sync_fetch_and_xor_4, u32, |a: u32, b: u32| a ^ b);
138+
139+
atomic_rmw!(__sync_fetch_and_nand_1, u8, |a: u8, b: u8| !a & b);
140+
atomic_rmw!(__sync_fetch_and_nand_2, u16, |a: u16, b: u16| !a & b);
141+
atomic_rmw!(__sync_fetch_and_nand_4, u32, |a: u32, b: u32| !a & b);
142+
143+
atomic_rmw!(__sync_fetch_and_max_1, i8, |a: i8, b: i8| if a > b { a } else { b });
144+
atomic_rmw!(__sync_fetch_and_max_2, i16, |a: i16, b: i16| if a > b { a } else { b });
145+
atomic_rmw!(__sync_fetch_and_max_4, i32, |a: i32, b: i32| if a > b { a } else { b });
146+
147+
atomic_rmw!(__sync_fetch_and_umax_1, u8, |a: u8, b: u8| if a > b { a } else { b });
148+
atomic_rmw!(__sync_fetch_and_umax_2, u16, |a: u16, b: u16| if a > b { a } else { b });
149+
atomic_rmw!(__sync_fetch_and_umax_4, u32, |a: u32, b: u32| if a > b { a } else { b });
150+
151+
atomic_rmw!(__sync_fetch_and_min_1, i8, |a: i8, b: i8| if a < b { a } else { b });
152+
atomic_rmw!(__sync_fetch_and_min_2, i16, |a: i16, b: i16| if a < b { a } else { b });
153+
atomic_rmw!(__sync_fetch_and_min_4, i32, |a: i32, b: i32| if a < b { a } else { b });
154+
155+
atomic_rmw!(__sync_fetch_and_umin_1, u8, |a: u8, b: u8| if a < b { a } else { b });
156+
atomic_rmw!(__sync_fetch_and_umin_2, u16, |a: u16, b: u16| if a < b { a } else { b });
157+
atomic_rmw!(__sync_fetch_and_umin_4, u32, |a: u32, b: u32| if a < b { a } else { b });
158+
159+
atomic_rmw!(__sync_lock_test_and_set_1, u8, |_: u8, b: u8| b);
160+
atomic_rmw!(__sync_lock_test_and_set_2, u16, |_: u16, b: u16| b);
161+
atomic_rmw!(__sync_lock_test_and_set_4, u32, |_: u32, b: u32| b);
162+
163+
atomic_cmpxchg!(__sync_val_compare_and_swap_1, u8);
164+
atomic_cmpxchg!(__sync_val_compare_and_swap_2, u16);
165+
atomic_cmpxchg!(__sync_val_compare_and_swap_4, u32);
166+
167+
#[cfg_attr(not(feature = "mangled-names"), no_mangle)]
168+
pub unsafe extern "C" fn __sync_synchronize() {
169+
__kuser_memory_barrier();
170+
}

src/lib.rs

+3
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,9 @@ pub mod mem;
5151
#[cfg(target_arch = "arm")]
5252
pub mod arm;
5353

54+
#[cfg(all(armv5te, target_os = "linux", target_arch = "arm"))]
55+
pub mod arm_linux;
56+
5457
#[cfg(target_arch = "x86")]
5558
pub mod x86;
5659

0 commit comments

Comments
 (0)