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,43 @@ fn insert_aligned(aligned: u32, val: u32, shift: u32, mask: u32) -> u32 {
5456 ( aligned & !( mask << shift) ) | ( ( val & mask) << shift)
5557}
5658
59+ /// Loads the value at `ptr`.
60+ ///
61+ /// # Safety
62+ ///
63+ /// `ptr` must be aligned and point to memory within a page that allows read access.
64+ /// If `T` has a size of 4, then `ptr` must be valid for an atomic read.
65+ unsafe fn load_aligned < T > ( ptr : * mut u32 ) -> u32 {
66+ if mem:: size_of :: < T > ( ) == 4 {
67+ // SAFETY: As `T` has a size of 4, the caller garantees this is sound.
68+ unsafe { AtomicU32 :: from_ptr ( ptr) . load ( Ordering :: Relaxed ) }
69+ } else {
70+ // SAFETY:
71+ // As all 4 bytes pointed to by `ptr` might not be dereferenceable due to the provenance of
72+ // `ptr` when doing atomic operations on a `u8`/`i8`/`u16`/`i16`, inline ASM is used to
73+ // avoid causing undefined behaviour. The `ldr` instruction does not touch the stack or
74+ // flags, or write to memory, so `nostack`, `preserves_flags` and `readonly` are sound. The
75+ // caller garantees that `ptr` is aligned, as required by `ldr`.
76+ unsafe {
77+ let res: u32 ;
78+ arch:: asm!(
79+ "ldr {res}, [{ptr}]" ,
80+ ptr = in( reg) ptr,
81+ res = lateout( reg) res,
82+ options( nostack, preserves_flags, readonly)
83+ ) ;
84+ res
85+ }
86+ }
87+ }
88+
5789// Generic atomic read-modify-write operation
5890unsafe fn atomic_rmw < T , F : Fn ( u32 ) -> u32 , G : Fn ( u32 , u32 ) -> u32 > ( ptr : * mut T , f : F , g : G ) -> u32 {
5991 let aligned_ptr = align_ptr ( ptr) ;
6092 let ( shift, mask) = get_shift_mask ( ptr) ;
6193
6294 loop {
63- let curval_aligned = intrinsics :: atomic_load_unordered ( aligned_ptr) ;
95+ let curval_aligned = load_aligned :: < T > ( aligned_ptr) ;
6496 let curval = extract_aligned ( curval_aligned, shift, mask) ;
6597 let newval = f ( curval) ;
6698 let newval_aligned = insert_aligned ( curval_aligned, newval, shift, mask) ;
@@ -76,7 +108,7 @@ unsafe fn atomic_cmpxchg<T>(ptr: *mut T, oldval: u32, newval: u32) -> u32 {
76108 let ( shift, mask) = get_shift_mask ( ptr) ;
77109
78110 loop {
79- let curval_aligned = intrinsics :: atomic_load_unordered ( aligned_ptr) ;
111+ let curval_aligned = load_aligned :: < T > ( aligned_ptr) ;
80112 let curval = extract_aligned ( curval_aligned, shift, mask) ;
81113 if curval != oldval {
82114 return curval;
0 commit comments