|
| 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 | +} |
0 commit comments