1
- use core:: intrinsics ;
1
+ use core:: arch ;
2
2
use core:: mem;
3
+ use core:: sync:: atomic:: { AtomicU32 , Ordering } ;
3
4
4
5
// Kernel-provided user-mode helper functions:
5
6
// https://www.kernel.org/doc/Documentation/arm/kernel_user_helpers.txt
6
7
unsafe fn __kuser_cmpxchg ( oldval : u32 , newval : u32 , ptr : * mut u32 ) -> bool {
7
8
let f: extern "C" fn ( u32 , u32 , * mut u32 ) -> u32 = mem:: transmute ( 0xffff0fc0usize as * const ( ) ) ;
8
9
f ( oldval, newval, ptr) == 0
9
10
}
11
+
10
12
unsafe fn __kuser_memory_barrier ( ) {
11
13
let f: extern "C" fn ( ) = mem:: transmute ( 0xffff0fa0usize as * const ( ) ) ;
12
14
f ( ) ;
@@ -54,13 +56,52 @@ fn insert_aligned(aligned: u32, val: u32, shift: u32, mask: u32) -> u32 {
54
56
( aligned & !( mask << shift) ) | ( ( val & mask) << shift)
55
57
}
56
58
59
+ /// Performs a relaxed atomic load of 4 bytes at `ptr`. Some of the bytes are allowed to be out of
60
+ /// bounds as long as `size_of::<T>()` bytes are in bounds.
61
+ ///
62
+ /// # Safety
63
+ ///
64
+ /// - `ptr` must be 4-aligned.
65
+ /// - `size_of::<T>()` must be at most 4.
66
+ /// - if `size_of::<T>() == 1`, `ptr` or `ptr` offset by 1, 2 or 3 bytes must be valid for a relaxed
67
+ /// atomic read of 1 byte.
68
+ /// - if `size_of::<T>() == 2`, `ptr` or `ptr` offset by 2 bytes must be valid for a relaxed atomic
69
+ /// read of 2 bytes.
70
+ /// - if `size_of::<T>() == 4`, `ptr` must be valid for a relaxed atomic read of 4 bytes.
71
+ unsafe fn atomic_load_aligned < T > ( ptr : * mut u32 ) -> u32 {
72
+ if mem:: size_of :: < T > ( ) == 4 {
73
+ // SAFETY: As `T` has a size of 4, the caller garantees this is sound.
74
+ unsafe { AtomicU32 :: from_ptr ( ptr) . load ( Ordering :: Relaxed ) }
75
+ } else {
76
+ // SAFETY:
77
+ // As all 4 bytes pointed to by `ptr` might not be dereferenceable due to being out of
78
+ // bounds when doing atomic operations on a `u8`/`i8`/`u16`/`i16`, inline ASM is used to
79
+ // avoid causing undefined behaviour. However, as `ptr` is 4-aligned and at least 1 byte of
80
+ // `ptr` is dereferencable, the load won't cause a segfault as the page size is always
81
+ // larger than 4 bytes.
82
+ // The `ldr` instruction does not touch the stack or flags, or write to memory, so
83
+ // `nostack`, `preserves_flags` and `readonly` are sound. The caller garantees that `ptr` is
84
+ // 4-aligned, as required by `ldr`.
85
+ unsafe {
86
+ let res: u32 ;
87
+ arch:: asm!(
88
+ "ldr {res}, [{ptr}]" ,
89
+ ptr = in( reg) ptr,
90
+ res = lateout( reg) res,
91
+ options( nostack, preserves_flags, readonly)
92
+ ) ;
93
+ res
94
+ }
95
+ }
96
+ }
97
+
57
98
// Generic atomic read-modify-write operation
58
99
unsafe fn atomic_rmw < T , F : Fn ( u32 ) -> u32 , G : Fn ( u32 , u32 ) -> u32 > ( ptr : * mut T , f : F , g : G ) -> u32 {
59
100
let aligned_ptr = align_ptr ( ptr) ;
60
101
let ( shift, mask) = get_shift_mask ( ptr) ;
61
102
62
103
loop {
63
- let curval_aligned = intrinsics :: atomic_load_unordered ( aligned_ptr) ;
104
+ let curval_aligned = atomic_load_aligned :: < T > ( aligned_ptr) ;
64
105
let curval = extract_aligned ( curval_aligned, shift, mask) ;
65
106
let newval = f ( curval) ;
66
107
let newval_aligned = insert_aligned ( curval_aligned, newval, shift, mask) ;
@@ -76,7 +117,7 @@ unsafe fn atomic_cmpxchg<T>(ptr: *mut T, oldval: u32, newval: u32) -> u32 {
76
117
let ( shift, mask) = get_shift_mask ( ptr) ;
77
118
78
119
loop {
79
- let curval_aligned = intrinsics :: atomic_load_unordered ( aligned_ptr) ;
120
+ let curval_aligned = atomic_load_aligned :: < T > ( aligned_ptr) ;
80
121
let curval = extract_aligned ( curval_aligned, shift, mask) ;
81
122
if curval != oldval {
82
123
return curval;
0 commit comments