ax_cpu/user_access.rs
1//! Nofault access to user-space words.
2
3/// Direction of an architecture-level user-memory access check.
4#[derive(Clone, Copy, Debug, Eq, PartialEq)]
5pub enum UserAccessType {
6 /// The kernel intends to read bytes supplied by user space.
7 Read,
8 /// The kernel intends to write bytes into user space.
9 Write,
10}
11
12/// Failure returned by a nofault user read.
13#[derive(Clone, Copy, Debug, Eq, PartialEq)]
14pub enum UserAccessError {
15 /// The user word was not accessible without resolving a page fault.
16 Fault,
17}
18
19/// Atomic operation supported by [`user_atomic_u32`].
20#[repr(u32)]
21#[derive(Clone, Copy, Debug, Eq, PartialEq)]
22pub enum UserAtomicU32Op {
23 /// Replaces the word with the supplied argument.
24 Set = 0,
25 /// Adds the supplied argument with wrapping semantics.
26 Add = 1,
27 /// ORs the word with the supplied argument.
28 Or = 2,
29 /// ANDs the word with the inverse of the supplied argument.
30 AndNot = 3,
31 /// XORs the word with the supplied argument.
32 Xor = 4,
33}
34
35/// Failure returned by a nofault user atomic operation.
36#[derive(Clone, Copy, Debug, Eq, PartialEq)]
37pub enum UserAtomicError {
38 /// The user word was not accessible without resolving a page fault.
39 Fault,
40 /// A bounded load-linked/store-conditional sequence made no progress.
41 Retry,
42}
43
44unsafe extern "C" {
45 fn __axcpu_user_read_u32(address: *const u32, value: *mut u32) -> u32;
46
47 fn __axcpu_user_cmpxchg_u32(
48 address: *mut u32,
49 expected: u32,
50 replacement: u32,
51 observed: *mut u32,
52 ) -> u32;
53
54 fn __axcpu_user_atomic_u32(
55 address: *mut u32,
56 operation: u32,
57 argument: u32,
58 old_value: *mut u32,
59 ) -> u32;
60}
61
62/// Reads one aligned user-space word without resolving faults.
63///
64/// # Safety
65///
66/// `address` must be aligned for `u32` and point into the calling task's user
67/// address range. The caller must resolve [`UserAccessError::Fault`] only after
68/// releasing every lock whose critical section must remain nofault.
69pub unsafe fn user_read_u32(address: *const u32) -> Result<u32, UserAccessError> {
70 let mut value = 0;
71 let status = unsafe { __axcpu_user_read_u32(address, &mut value) };
72 match status {
73 0 => Ok(value),
74 1 => Err(UserAccessError::Fault),
75 _ => unreachable!("architecture returned an invalid user read status"),
76 }
77}
78
79/// Atomically updates one aligned user-space word without resolving faults.
80///
81/// The previous value is returned on success. A fault is redirected through
82/// the dedicated nofault exception table before the OS page-fault handler is
83/// invoked, so this function never sleeps or allocates.
84///
85/// # Safety
86///
87/// `address` must be aligned for `u32` and point into the calling task's user
88/// address range. The caller must serialize this operation with the protocol
89/// that consumes its result and must resolve [`UserAtomicError::Fault`] only
90/// after releasing every lock whose critical section must remain nofault.
91pub unsafe fn user_atomic_u32(
92 address: *mut u32,
93 operation: UserAtomicU32Op,
94 argument: u32,
95) -> Result<u32, UserAtomicError> {
96 let mut old_value = 0;
97 let status =
98 unsafe { __axcpu_user_atomic_u32(address, operation as u32, argument, &mut old_value) };
99 match status {
100 0 => Ok(old_value),
101 1 => Err(UserAtomicError::Fault),
102 2 => Err(UserAtomicError::Retry),
103 _ => unreachable!("architecture returned an invalid user atomic status"),
104 }
105}
106
107/// Compares and conditionally replaces one aligned user word without faulting in pages.
108///
109/// Returns the observed word: equality with `expected` means the replacement
110/// was stored. A successful replacement orders surrounding memory accesses;
111/// a mismatch only promises an atomic observation. Faults and bounded LL/SC
112/// exhaustion use the same exception-table protocol as [`user_atomic_u32`].
113///
114/// # Safety
115///
116/// `address` must be aligned for `u32` and lie in the current task's user
117/// address range. Its active address space and user-access permissions must
118/// remain installed through the call. Resolve faults only after releasing
119/// every lock whose critical section must remain nofault.
120pub unsafe fn user_cmpxchg_u32(
121 address: *mut u32,
122 expected: u32,
123 replacement: u32,
124) -> Result<u32, UserAtomicError> {
125 let mut observed = 0;
126 // SAFETY: the caller supplies the active user address; the output points
127 // to this function's initialized kernel-local word for the whole call.
128 let status = unsafe { __axcpu_user_cmpxchg_u32(address, expected, replacement, &mut observed) };
129 match status {
130 0 => Ok(observed),
131 1 => Err(UserAtomicError::Fault),
132 2 => Err(UserAtomicError::Retry),
133 _ => unreachable!("architecture returned an invalid user cmpxchg status"),
134 }
135}