Skip to main content

ax_cpu/aarch64/
context.rs

1use core::{arch::naked_asm, fmt};
2
3use ax_memory_addr::VirtAddr;
4
5/// Saved registers when a trap (exception) occurs.
6#[repr(C)]
7#[derive(Default, Clone, Copy)]
8pub struct TrapFrame {
9    /// General-purpose registers (X0..X30).
10    pub x: [u64; 31],
11    /// Exception Link Register (ELR_EL1).
12    pub elr: u64,
13    /// Saved Process Status Register (SPSR_EL1).
14    pub spsr: u64,
15
16    /// make sure the size is 16 bytes aligned
17    pub __pad: u64,
18}
19
20impl fmt::Debug for TrapFrame {
21    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
22        writeln!(f, "TrapFrame: {{")?;
23        for (i, &reg) in self.x.iter().enumerate() {
24            writeln!(f, "    x{i}: {reg:#x},")?;
25        }
26        writeln!(f, "    elr: {:#x},", self.elr)?;
27        writeln!(f, "    spsr: {:#x},", self.spsr)?;
28        writeln!(f, "    pad: {:#x},", self.__pad)?;
29        write!(f, "}}")?;
30        Ok(())
31    }
32}
33
34impl TrapFrame {
35    /// Gets the 0th syscall argument.
36    pub const fn arg0(&self) -> usize {
37        self.x[0] as _
38    }
39
40    /// Sets the 0th syscall argument.
41    pub const fn set_arg0(&mut self, a0: usize) {
42        self.x[0] = a0 as _;
43    }
44
45    /// Gets the 1st syscall argument.
46    pub const fn arg1(&self) -> usize {
47        self.x[1] as _
48    }
49
50    /// Sets the 1st syscall argument.
51    pub const fn set_arg1(&mut self, a1: usize) {
52        self.x[1] = a1 as _;
53    }
54
55    /// Gets the 2nd syscall argument.
56    pub const fn arg2(&self) -> usize {
57        self.x[2] as _
58    }
59
60    /// Sets the 2nd syscall argument.
61    pub const fn set_arg2(&mut self, a2: usize) {
62        self.x[2] = a2 as _;
63    }
64
65    /// Gets the 3rd syscall argument.
66    pub const fn arg3(&self) -> usize {
67        self.x[3] as _
68    }
69
70    /// Sets the 3rd syscall argument.
71    pub const fn set_arg3(&mut self, a3: usize) {
72        self.x[3] = a3 as _;
73    }
74
75    /// Gets the 4th syscall argument.
76    pub const fn arg4(&self) -> usize {
77        self.x[4] as _
78    }
79
80    /// Sets the 4th syscall argument.
81    pub const fn set_arg4(&mut self, a4: usize) {
82        self.x[4] = a4 as _;
83    }
84
85    /// Gets the 5th syscall argument.
86    pub const fn arg5(&self) -> usize {
87        self.x[5] as _
88    }
89
90    /// Sets the 5th syscall argument.
91    pub const fn set_arg5(&mut self, a5: usize) {
92        self.x[5] = a5 as _;
93    }
94
95    /// Gets the instruction pointer.
96    pub const fn ip(&self) -> usize {
97        self.elr as _
98    }
99
100    /// Sets the instruction pointer.
101    pub const fn set_ip(&mut self, pc: usize) {
102        self.elr = pc as _;
103    }
104
105    /// Get the syscall number.
106    pub const fn sysno(&self) -> usize {
107        self.x[8] as usize
108    }
109
110    /// Sets the syscall number.
111    pub const fn set_sysno(&mut self, sysno: usize) {
112        self.x[8] = sysno as _;
113    }
114
115    /// Gets the return value register.
116    pub const fn retval(&self) -> usize {
117        self.x[0] as _
118    }
119
120    /// Sets the return value register.
121    pub const fn set_retval(&mut self, r0: usize) {
122        self.x[0] = r0 as _;
123    }
124
125    /// Sets the return address.
126    pub const fn set_ra(&mut self, lr: usize) {
127        self.x[30] = lr as _;
128    }
129
130    /// Unwind the stack and get the backtrace.
131    pub fn backtrace(&self) -> axbacktrace::Backtrace {
132        axbacktrace::Backtrace::capture_trap(self.x[29] as _, self.elr as _, self.x[30] as _)
133    }
134}
135
136/// FP & SIMD registers.
137#[repr(C, align(16))]
138#[derive(Debug, Default)]
139pub struct FpState {
140    /// 128-bit SIMD & FP registers (V0..V31)
141    pub regs: [u128; 32],
142    /// Floating-point Control Register (FPCR)
143    pub fpcr: u32,
144    /// Floating-point Status Register (FPSR)
145    pub fpsr: u32,
146}
147
148#[cfg(feature = "fp-simd")]
149impl FpState {
150    /// Saves the current FP/SIMD states from CPU to this structure.
151    pub fn save(&mut self) {
152        unsafe { fpstate_save(self) }
153    }
154
155    /// Restores the FP/SIMD states from this structure to CPU.
156    pub fn restore(&self) {
157        unsafe { fpstate_restore(self) }
158    }
159}
160
161/// Saved hardware states of a task.
162///
163/// The context usually includes:
164///
165/// - Callee-saved registers
166/// - Stack pointer register
167/// - Thread pointer register (for kernel-space thread-local storage)
168/// - FP/SIMD registers
169///
170/// On context switch, current task saves its context from CPU to memory,
171/// and the next task restores its context from memory to CPU.
172#[allow(missing_docs)]
173#[repr(C)]
174#[derive(Debug, Default)]
175pub struct TaskContext {
176    pub sp: u64,
177    pub r19: u64,
178    pub r20: u64,
179    pub r21: u64,
180    pub r22: u64,
181    pub r23: u64,
182    pub r24: u64,
183    pub r25: u64,
184    pub r26: u64,
185    pub r27: u64,
186    pub r28: u64,
187    pub r29: u64,
188    pub lr: u64, // r30
189    /// Thread Pointer
190    pub tpidr_el0: u64,
191    /// The `ttbr0_el1` register value, i.e., the page table root.
192    #[cfg(feature = "uspace")]
193    pub ttbr0_el1: ax_memory_addr::PhysAddr,
194    #[cfg(feature = "fp-simd")]
195    pub fp_state: FpState,
196}
197
198impl TaskContext {
199    /// Creates a dummy context for a new task.
200    ///
201    /// Note the context is not initialized, it will be filled by [`switch_to`]
202    /// (for initial tasks) and [`init`] (for regular tasks) methods.
203    ///
204    /// [`init`]: TaskContext::init
205    /// [`switch_to`]: TaskContext::switch_to
206    pub fn new() -> Self {
207        Self::default()
208    }
209
210    /// Initializes the context for a new task, with the given entry point and
211    /// kernel stack.
212    pub fn init(&mut self, entry: usize, kstack_top: VirtAddr, tls_area: VirtAddr) {
213        self.sp = kstack_top.as_usize() as u64;
214        self.lr = entry as u64;
215        self.tpidr_el0 = tls_area.as_usize() as u64;
216    }
217
218    /// Changes the page table root in this context.
219    ///
220    /// The hardware register for user page table root (`ttbr0_el1` for aarch64 in EL1)
221    /// will be updated to the next task's after [`Self::switch_to`].
222    #[cfg(feature = "uspace")]
223    pub fn set_page_table_root(&mut self, ttbr0_el1: ax_memory_addr::PhysAddr) {
224        self.ttbr0_el1 = ttbr0_el1;
225    }
226
227    /// Switches to another task.
228    ///
229    /// It first saves the current task's context from CPU to this place, and then
230    /// restores the next task's context from `next_ctx` to CPU.
231    pub fn switch_to(&mut self, next_ctx: &Self) {
232        #[cfg(feature = "tls")]
233        {
234            self.tpidr_el0 = crate::asm::read_thread_pointer() as _;
235            unsafe { crate::asm::write_thread_pointer(next_ctx.tpidr_el0 as _) };
236        }
237        #[cfg(feature = "fp-simd")]
238        {
239            self.fp_state.save();
240            next_ctx.fp_state.restore();
241        }
242        #[cfg(feature = "uspace")]
243        if self.ttbr0_el1 != next_ctx.ttbr0_el1 {
244            unsafe { crate::asm::write_user_page_table(next_ctx.ttbr0_el1) };
245            crate::asm::flush_tlb(None); // currently flush the entire TLB
246        }
247        unsafe { context_switch(self, next_ctx) }
248    }
249}
250
251#[unsafe(naked)]
252unsafe extern "C" fn context_switch(_current_task: &mut TaskContext, _next_task: &TaskContext) {
253    naked_asm!(
254        "
255        // save old context (callee-saved registers)
256        stp     x29, x30, [x0, 11 * 8]
257        stp     x27, x28, [x0, 9 * 8]
258        stp     x25, x26, [x0, 7 * 8]
259        stp     x23, x24, [x0, 5 * 8]
260        stp     x21, x22, [x0, 3 * 8]
261        stp     x19, x20, [x0, 1 * 8]
262        mov     x19, sp
263        str     x19, [x0]
264
265        // restore new context
266        ldr     x19, [x1]
267        mov     sp, x19
268        ldp     x19, x20, [x1, 1 * 8]
269        ldp     x21, x22, [x1, 3 * 8]
270        ldp     x23, x24, [x1, 5 * 8]
271        ldp     x25, x26, [x1, 7 * 8]
272        ldp     x27, x28, [x1, 9 * 8]
273        ldp     x29, x30, [x1, 11 * 8]
274
275        ret",
276    )
277}
278
279#[unsafe(naked)]
280#[cfg(feature = "fp-simd")]
281unsafe extern "C" fn fpstate_save(state: &mut FpState) {
282    naked_asm!(
283        ".arch armv8
284        // save fp/neon context
285        mrs     x9, fpcr
286        mrs     x10, fpsr
287        stp     q0, q1, [x0, 0 * 16]
288        stp     q2, q3, [x0, 2 * 16]
289        stp     q4, q5, [x0, 4 * 16]
290        stp     q6, q7, [x0, 6 * 16]
291        stp     q8, q9, [x0, 8 * 16]
292        stp     q10, q11, [x0, 10 * 16]
293        stp     q12, q13, [x0, 12 * 16]
294        stp     q14, q15, [x0, 14 * 16]
295        stp     q16, q17, [x0, 16 * 16]
296        stp     q18, q19, [x0, 18 * 16]
297        stp     q20, q21, [x0, 20 * 16]
298        stp     q22, q23, [x0, 22 * 16]
299        stp     q24, q25, [x0, 24 * 16]
300        stp     q26, q27, [x0, 26 * 16]
301        stp     q28, q29, [x0, 28 * 16]
302        stp     q30, q31, [x0, 30 * 16]
303        str     x9, [x0, 64 *  8]
304        str     x10, [x0, 65 * 8]
305
306        isb
307        ret"
308    )
309}
310
311#[unsafe(naked)]
312#[cfg(feature = "fp-simd")]
313unsafe extern "C" fn fpstate_restore(state: &FpState) {
314    naked_asm!(
315        ".arch armv8
316        // restore fp/neon context
317        ldp     q0, q1, [x0, 0 * 16]
318        ldp     q2, q3, [x0, 2 * 16]
319        ldp     q4, q5, [x0, 4 * 16]
320        ldp     q6, q7, [x0, 6 * 16]
321        ldp     q8, q9, [x0, 8 * 16]
322        ldp     q10, q11, [x0, 10 * 16]
323        ldp     q12, q13, [x0, 12 * 16]
324        ldp     q14, q15, [x0, 14 * 16]
325        ldp     q16, q17, [x0, 16 * 16]
326        ldp     q18, q19, [x0, 18 * 16]
327        ldp     q20, q21, [x0, 20 * 16]
328        ldp     q22, q23, [x0, 22 * 16]
329        ldp     q24, q25, [x0, 24 * 16]
330        ldp     q26, q27, [x0, 26 * 16]
331        ldp     q28, q29, [x0, 28 * 16]
332        ldp     q30, q31, [x0, 30 * 16]
333        ldr     x9, [x0, 64 * 8]
334        ldr     x10, [x0, 65 * 8]
335        msr     fpcr, x9
336        msr     fpsr, x10
337
338        isb
339        ret"
340    )
341}