Skip to main content

ax_cpu/arch/aarch64/
uspace.rs

1//! Structures and functions for user space.
2
3use core::{
4    mem::{offset_of, size_of},
5    ops::{Deref, DerefMut},
6};
7
8use aarch64_cpu::registers::FAR_EL1;
9pub use aarch64_cpu::registers::{ESR_EL1, Readable};
10use ax_memory_addr::VirtAddr;
11pub use tock_registers::{
12    LocalRegisterCopy, RegisterLongName, UIntLike,
13    debug::{RegisterDebugInfo, RegisterDebugValue},
14    fields::{Field, FieldValue, TryFromValue},
15};
16
17use super::trap::{TrapKind, is_valid_page_fault};
18pub use crate::uspace_common::{ExceptionKind, ExceptionSyndrome, ReturnReason};
19use crate::{arch::current::context::TrapFrame, trap::PageFaultFlags};
20
21/// Context to enter user space.
22#[repr(C, align(16))]
23#[derive(Debug, Clone, Copy)]
24pub struct UserContext {
25    tf: TrapFrame,
26    /// Stack Pointer (SP_EL0).
27    pub sp: u64,
28    /// Software Thread ID Register (TPIDR_EL0).
29    pub tpidr: u64,
30}
31
32// SAFETY: `TrapFrame`, `sp`, and `tpidr` are contiguous integer storage and
33// their combined size is already a multiple of the declared 16-byte alignment.
34unsafe impl bytemuck::NoUninit for UserContext {}
35
36const _: () = {
37    assert!(size_of::<TrapFrame>() == 34 * size_of::<u64>());
38    assert!(offset_of!(UserContext, tf) == 0);
39    assert!(offset_of!(UserContext, sp) == size_of::<TrapFrame>());
40    assert!(offset_of!(UserContext, tpidr) == size_of::<TrapFrame>() + size_of::<u64>());
41    assert!(size_of::<UserContext>() == size_of::<TrapFrame>() + 2 * size_of::<u64>());
42};
43
44impl UserContext {
45    /// Creates a new user context with the given entry point, stack top, and argument.
46    pub fn new(entry: usize, ustack_top: VirtAddr, arg0: usize) -> Self {
47        use aarch64_cpu::registers::SPSR_EL1;
48        let mut regs = [0; 31];
49        regs[0] = arg0 as _;
50        Self {
51            tf: TrapFrame {
52                x: regs,
53                elr: entry as _,
54                spsr: (SPSR_EL1::M::EL0t
55                    + SPSR_EL1::D::Masked
56                    + SPSR_EL1::A::Masked
57                    + SPSR_EL1::I::Unmasked
58                    + SPSR_EL1::F::Masked)
59                    .value,
60                sp: 0,
61            },
62            sp: ustack_top.as_usize() as _,
63            tpidr: 0,
64        }
65    }
66
67    /// Normalizes a cloned user context so it can safely return to EL0.
68    pub fn prepare_clone_child_return_state(&mut self) {
69        use aarch64_cpu::registers::SPSR_EL1;
70
71        self.tf.spsr = (self.tf.spsr
72            & !(SPSR_EL1::M.mask
73                | SPSR_EL1::D.mask
74                | SPSR_EL1::A.mask
75                | SPSR_EL1::I.mask
76                | SPSR_EL1::F.mask))
77            | (SPSR_EL1::M::EL0t
78                + SPSR_EL1::D::Masked
79                + SPSR_EL1::A::Masked
80                + SPSR_EL1::I::Unmasked
81                + SPSR_EL1::F::Masked)
82                .value;
83    }
84
85    /// Clears any architecture single-step state after a debug exception.
86    ///
87    /// AArch64 user single-step is currently emulated by the Starry ptrace layer,
88    /// so there is no saved CPU flag to clear here.
89    pub const fn clear_single_step_after_debug(&mut self) -> bool {
90        false
91    }
92
93    /// Returns the syscall instruction length in bytes.
94    pub const fn syscall_insn_len(&self) -> usize {
95        4
96    }
97
98    /// Gets the stack pointer.
99    pub const fn sp(&self) -> usize {
100        self.sp as _
101    }
102
103    /// Sets the stack pointer.
104    pub const fn set_sp(&mut self, sp: usize) {
105        self.sp = sp as _;
106    }
107
108    /// Gets the TLS area.
109    pub const fn tls(&self) -> usize {
110        self.tpidr as _
111    }
112
113    /// Sets the TLS area.
114    pub const fn set_tls(&mut self, tls: usize) {
115        self.tpidr = tls as _;
116    }
117
118    /// Returns whether this register image can be restored as an interruptible
119    /// EL0 context.
120    pub fn has_interruptible_user_return_mode(&self) -> bool {
121        use aarch64_cpu::registers::SPSR_EL1;
122
123        let runtime_daif =
124            SPSR_EL1::D::Masked + SPSR_EL1::A::Masked + SPSR_EL1::I::Unmasked + SPSR_EL1::F::Masked;
125        self.tf.spsr & SPSR_EL1::M::EL0t.mask() == SPSR_EL1::M::EL0t.value
126            && self.tf.spsr & runtime_daif.mask() == runtime_daif.value
127    }
128
129    /// Enters user space without validating the runtime transition.
130    ///
131    /// It restores the user registers and jumps to the user entry point
132    /// (saved in `elr`).
133    ///
134    /// This function returns when an exception or syscall occurs.
135    ///
136    /// # Safety
137    ///
138    /// The caller must be the runtime's prepared user-entry boundary for the
139    /// current scheduler task. Its context-switch tail must be complete, no
140    /// IRQ/preemption guard or hard interrupt may be active, and local IRQs
141    /// must remain disabled after the final scheduler-work check. The active
142    /// logical address space, hardware root and CPU footprint must match this
143    /// task and keep every user address referenced by `self` valid. SPSR must
144    /// describe an interruptible EL0 return. No code may run between those
145    /// validations and this call.
146    pub unsafe fn run_unchecked(&mut self) -> ReturnReason {
147        unsafe extern "C" {
148            fn enter_user(uctx: &mut UserContext) -> TrapKind;
149        }
150
151        assert!(
152            !crate::asm::irqs_enabled(),
153            "raw user entry requires the prepared IRQ-off boundary"
154        );
155        assert!(
156            self.has_interruptible_user_return_mode(),
157            "raw user entry requires an interruptible EL0 register image"
158        );
159        let kind = unsafe { enter_user(self) };
160
161        let ret = match kind {
162            TrapKind::Irq => {
163                crate::trap::dispatch_irq(
164                    0,
165                    crate::trap::TrapOrigin::User,
166                    Some(crate::trap::InterruptedContext {
167                        pc: self.tf.ip(),
168                        sp: self.sp as usize,
169                        fp: self.tf.x[29] as usize,
170                        privilege: crate::trap::InterruptedPrivilege::User,
171                    }),
172                );
173                ReturnReason::Interrupt
174            }
175            TrapKind::Fiq | TrapKind::SError => ReturnReason::Unknown,
176            TrapKind::Synchronous => {
177                let esr = ESR_EL1.extract();
178                let far = FAR_EL1.get() as usize;
179
180                let iss = esr.read(ESR_EL1::ISS);
181
182                match esr.read_as_enum(ESR_EL1::EC) {
183                    Some(ESR_EL1::EC::Value::SVC64) => ReturnReason::Syscall,
184                    Some(ESR_EL1::EC::Value::InstrAbortLowerEL) if is_valid_page_fault(iss) => {
185                        ReturnReason::PageFault(
186                            va!(far),
187                            PageFaultFlags::EXECUTE | PageFaultFlags::USER,
188                        )
189                    }
190                    Some(ESR_EL1::EC::Value::DataAbortLowerEL) if is_valid_page_fault(iss) => {
191                        let wnr = (iss & (1 << 6)) != 0; // WnR: Write not Read
192                        let cm = (iss & (1 << 8)) != 0; // CM: Cache maintenance
193                        ReturnReason::PageFault(
194                            va!(far),
195                            if wnr & !cm {
196                                PageFaultFlags::WRITE
197                            } else {
198                                PageFaultFlags::READ
199                            } | PageFaultFlags::USER,
200                        )
201                    }
202                    _ => ReturnReason::Exception(ExceptionInfo { esr, far }),
203                }
204            }
205        };
206
207        crate::asm::enable_irqs();
208        ret
209    }
210}
211
212const _: unsafe fn(&mut UserContext) -> ReturnReason = UserContext::run_unchecked;
213
214impl Deref for UserContext {
215    type Target = TrapFrame;
216
217    fn deref(&self) -> &Self::Target {
218        &self.tf
219    }
220}
221
222impl DerefMut for UserContext {
223    fn deref_mut(&mut self) -> &mut Self::Target {
224        &mut self.tf
225    }
226}
227
228/// Information about an exception that occurred in user space.
229#[derive(Debug, Clone, Copy)]
230pub struct ExceptionInfo {
231    /// Exception Syndrome Register
232    pub esr: LocalRegisterCopy<u64, ESR_EL1::Register>,
233    /// Fault Address Register
234    pub far: usize,
235}
236
237impl ExceptionInfo {
238    /// Returns the faulting virtual address when the CPU records one.
239    pub const fn fault_addr(&self) -> Option<usize> {
240        Some(self.far)
241    }
242
243    /// Returns architecture-neutral syndrome information for this exception.
244    pub fn syndrome(&self) -> ExceptionSyndrome {
245        ExceptionSyndrome {
246            raw: self.esr_value(),
247            class: self.ec_value(),
248            iss: self.iss_value(),
249        }
250    }
251
252    /// Returns the raw Exception Syndrome Register value.
253    pub fn esr_value(&self) -> u64 {
254        self.esr.get()
255    }
256
257    /// Returns the raw exception class bits.
258    pub fn ec_value(&self) -> u64 {
259        self.esr.read(ESR_EL1::EC)
260    }
261
262    /// Returns the instruction specific syndrome bits.
263    pub fn iss_value(&self) -> u64 {
264        self.esr.read(ESR_EL1::ISS)
265    }
266
267    /// Returns a generalized kind of this exception.
268    pub fn kind(&self) -> ExceptionKind {
269        match self.esr.read_as_enum(ESR_EL1::EC) {
270            Some(ESR_EL1::EC::Value::Brk64) | Some(ESR_EL1::EC::Value::Bkpt32) => {
271                ExceptionKind::Breakpoint
272            }
273            Some(ESR_EL1::EC::Value::IllegalExecutionState) | Some(ESR_EL1::EC::Value::Unknown) => {
274                ExceptionKind::IllegalInstruction
275            }
276            Some(ESR_EL1::EC::Value::PCAlignmentFault)
277            | Some(ESR_EL1::EC::Value::SPAlignmentFault) => ExceptionKind::Misaligned,
278            _ => ExceptionKind::Other,
279        }
280    }
281}