Skip to main content

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_atomic_u32(
48        address: *mut u32,
49        operation: u32,
50        argument: u32,
51        old_value: *mut u32,
52    ) -> u32;
53}
54
55/// Reads one aligned user-space word without resolving faults.
56///
57/// # Safety
58///
59/// `address` must be aligned for `u32` and point into the calling task's user
60/// address range. The caller must resolve [`UserAccessError::Fault`] only after
61/// releasing every lock whose critical section must remain nofault.
62pub unsafe fn user_read_u32(address: *const u32) -> Result<u32, UserAccessError> {
63    let mut value = 0;
64    let status = unsafe { __axcpu_user_read_u32(address, &mut value) };
65    match status {
66        0 => Ok(value),
67        1 => Err(UserAccessError::Fault),
68        _ => unreachable!("architecture returned an invalid user read status"),
69    }
70}
71
72/// Atomically updates one aligned user-space word without resolving faults.
73///
74/// The previous value is returned on success. A fault is redirected through
75/// the dedicated nofault exception table before the OS page-fault handler is
76/// invoked, so this function never sleeps or allocates.
77///
78/// # Safety
79///
80/// `address` must be aligned for `u32` and point into the calling task's user
81/// address range. The caller must serialize this operation with the protocol
82/// that consumes its result and must resolve [`UserAtomicError::Fault`] only
83/// after releasing every lock whose critical section must remain nofault.
84pub unsafe fn user_atomic_u32(
85    address: *mut u32,
86    operation: UserAtomicU32Op,
87    argument: u32,
88) -> Result<u32, UserAtomicError> {
89    let mut old_value = 0;
90    let status =
91        unsafe { __axcpu_user_atomic_u32(address, operation as u32, argument, &mut old_value) };
92    match status {
93        0 => Ok(old_value),
94        1 => Err(UserAtomicError::Fault),
95        2 => Err(UserAtomicError::Retry),
96        _ => unreachable!("architecture returned an invalid user atomic status"),
97    }
98}