1- use  core:: intrinsics ; 
1+ use  core:: arch ; 
22use  core:: mem; 
3+ use  core:: sync:: atomic:: { AtomicU32 ,  Ordering } ; 
34
45// Kernel-provided user-mode helper functions: 
56// https://www.kernel.org/doc/Documentation/arm/kernel_user_helpers.txt 
67unsafe  fn  __kuser_cmpxchg ( oldval :  u32 ,  newval :  u32 ,  ptr :  * mut  u32 )  -> bool  { 
78    let  f:  extern  "C"  fn ( u32 ,  u32 ,  * mut  u32 )  -> u32  = mem:: transmute ( 0xffff0fc0usize  as  * const  ( ) ) ; 
89    f ( oldval,  newval,  ptr)  == 0 
910} 
11+ 
1012unsafe  fn  __kuser_memory_barrier ( )  { 
1113    let  f:  extern  "C"  fn ( )  = mem:: transmute ( 0xffff0fa0usize  as  * const  ( ) ) ; 
1214    f ( ) ; 
@@ -54,13 +56,52 @@ fn insert_aligned(aligned: u32, val: u32, shift: u32, mask: u32) -> u32 {
5456    ( aligned &  !( mask << shift) )  | ( ( val &  mask)  << shift) 
5557} 
5658
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+ 
5798// Generic atomic read-modify-write operation 
5899unsafe  fn  atomic_rmw < T ,  F :  Fn ( u32 )  -> u32 ,  G :  Fn ( u32 ,  u32 )  -> u32 > ( ptr :  * mut  T ,  f :  F ,  g :  G )  -> u32  { 
59100    let  aligned_ptr = align_ptr ( ptr) ; 
60101    let  ( shift,  mask)  = get_shift_mask ( ptr) ; 
61102
62103    loop  { 
63-         let  curval_aligned = intrinsics :: atomic_load_unordered ( aligned_ptr) ; 
104+         let  curval_aligned = atomic_load_aligned :: < T > ( aligned_ptr) ; 
64105        let  curval = extract_aligned ( curval_aligned,  shift,  mask) ; 
65106        let  newval = f ( curval) ; 
66107        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 {
76117    let  ( shift,  mask)  = get_shift_mask ( ptr) ; 
77118
78119    loop  { 
79-         let  curval_aligned = intrinsics :: atomic_load_unordered ( aligned_ptr) ; 
120+         let  curval_aligned = atomic_load_aligned :: < T > ( aligned_ptr) ; 
80121        let  curval = extract_aligned ( curval_aligned,  shift,  mask) ; 
81122        if  curval != oldval { 
82123            return  curval; 
0 commit comments