1#[cfg(feature = "context")]
2use core::arch::naked_asm;
3use core::fmt;
4#[cfg(feature = "context")]
5use core::mem::{align_of, offset_of, size_of};
6
7#[cfg(feature = "context")]
8use ax_memory_addr::VirtAddr;
9
10#[cfg(feature = "fp-simd")]
11use super::fp::FpState;
12#[cfg(feature = "context")]
13use crate::{KernelTlsBase, TaskLocalState, context::TaskAnchor};
14
15#[repr(C)]
17#[derive(Default, Clone, Copy)]
18pub struct TrapFrame {
19 pub x: super::registers::GeneralRegisters,
21 pub elr: u64,
23 pub spsr: u64,
25
26 pub sp: u64,
34}
35
36impl fmt::Debug for TrapFrame {
37 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
38 writeln!(f, "TrapFrame: {{")?;
39 for (i, ®) in self.x.iter().enumerate() {
40 writeln!(f, " x{i}: {reg:#x},")?;
41 }
42 writeln!(f, " elr: {:#x},", self.elr)?;
43 writeln!(f, " spsr: {:#x},", self.spsr)?;
44 writeln!(f, " sp: {:#x},", self.sp)?;
45 write!(f, "}}")?;
46 Ok(())
47 }
48}
49
50impl TrapFrame {
51 pub fn interrupted_context(&self) -> crate::trap::InterruptedContext {
53 use crate::trap::{InterruptedContext, InterruptedPrivilege};
54 InterruptedContext {
55 pc: self.ip(),
56 sp: self.sp as usize,
57 fp: self.x[29] as usize,
58 lr: self.x[30] as usize,
59 privilege: match self.origin() {
60 crate::trap::TrapOrigin::Kernel => InterruptedPrivilege::Kernel,
61 crate::trap::TrapOrigin::User => InterruptedPrivilege::User,
62 },
63 }
64 }
65
66 pub const fn origin(&self) -> crate::TrapOrigin {
68 if self.spsr & 0b1_1111 == 0 {
69 crate::TrapOrigin::User
70 } else {
71 crate::TrapOrigin::Kernel
72 }
73 }
74
75 pub const fn arg0(&self) -> usize {
77 self.x[0] as _
78 }
79
80 pub const fn set_arg0(&mut self, a0: usize) {
82 self.x[0] = a0 as _;
83 }
84
85 pub const fn arg1(&self) -> usize {
87 self.x[1] as _
88 }
89
90 pub const fn set_arg1(&mut self, a1: usize) {
92 self.x[1] = a1 as _;
93 }
94
95 pub const fn arg2(&self) -> usize {
97 self.x[2] as _
98 }
99
100 pub const fn set_arg2(&mut self, a2: usize) {
102 self.x[2] = a2 as _;
103 }
104
105 pub const fn arg3(&self) -> usize {
107 self.x[3] as _
108 }
109
110 pub const fn set_arg3(&mut self, a3: usize) {
112 self.x[3] = a3 as _;
113 }
114
115 pub const fn arg4(&self) -> usize {
117 self.x[4] as _
118 }
119
120 pub const fn set_arg4(&mut self, a4: usize) {
122 self.x[4] = a4 as _;
123 }
124
125 pub const fn arg5(&self) -> usize {
127 self.x[5] as _
128 }
129
130 pub const fn set_arg5(&mut self, a5: usize) {
132 self.x[5] = a5 as _;
133 }
134
135 pub const fn ip(&self) -> usize {
137 self.elr as _
138 }
139
140 pub const fn set_ip(&mut self, pc: usize) {
142 self.elr = pc as _;
143 }
144
145 pub const fn sysno(&self) -> usize {
147 self.x[8] as usize
148 }
149
150 pub const fn set_sysno(&mut self, sysno: usize) {
152 self.x[8] = sysno as _;
153 }
154
155 pub const fn retval(&self) -> usize {
157 self.x[0] as _
158 }
159
160 pub const fn set_retval(&mut self, r0: usize) {
162 self.x[0] = r0 as _;
163 }
164
165 pub const fn set_ra(&mut self, lr: usize) {
167 self.x[30] = lr as _;
168 }
169
170 pub fn backtrace_registers(&self) -> crate::trap::BacktraceRegisters {
172 crate::trap::BacktraceRegisters::new(self.x[29] as _, self.elr as _, self.x[30] as _)
173 }
174}
175
176#[allow(missing_docs)]
188#[repr(C)]
189#[derive(Debug, Default)]
190#[cfg(feature = "context")]
191pub struct TaskContext {
192 sp: u64,
193 r19: u64,
194 r20: u64,
195 r21: u64,
196 r22: u64,
197 r23: u64,
198 r24: u64,
199 r25: u64,
200 r26: u64,
201 r27: u64,
202 r28: u64,
203 r29: u64,
204 lr: u64, task_local: TaskLocalState,
207 #[cfg(feature = "fp-simd")]
208 fp_state: FpState,
209}
210
211#[cfg(feature = "context")]
214const _: () = {
215 assert!(size_of::<KernelTlsBase>() == size_of::<usize>());
216 assert!(align_of::<KernelTlsBase>() == align_of::<usize>());
217 assert!(offset_of!(TaskContext, sp) == 0);
218 assert!(offset_of!(TaskContext, r20) == offset_of!(TaskContext, r19) + size_of::<u64>());
219 assert!(offset_of!(TaskContext, r22) == offset_of!(TaskContext, r21) + size_of::<u64>());
220 assert!(offset_of!(TaskContext, r24) == offset_of!(TaskContext, r23) + size_of::<u64>());
221 assert!(offset_of!(TaskContext, r26) == offset_of!(TaskContext, r25) + size_of::<u64>());
222 assert!(offset_of!(TaskContext, r28) == offset_of!(TaskContext, r27) + size_of::<u64>());
223 assert!(offset_of!(TaskContext, lr) == offset_of!(TaskContext, r29) + size_of::<u64>());
224 assert!(offset_of!(TaskContext, task_local) == offset_of!(TaskContext, lr) + size_of::<u64>());
225};
226
227#[cfg(feature = "context")]
228impl TaskContext {
229 pub fn new() -> Self {
237 Self::default()
238 }
239
240 pub fn init(&mut self, entry: usize, kstack_top: VirtAddr, kernel_tls: KernelTlsBase) {
243 self.sp = kstack_top.as_usize() as u64;
244 self.lr = entry as u64;
245 self.task_local.set_kernel_tls(kernel_tls);
246 }
247
248 pub fn set_task_anchor(&mut self, header: TaskAnchor) {
250 self.task_local.set_task_anchor(header);
251 }
252
253 pub const fn task_anchor(&self) -> Option<TaskAnchor> {
255 self.task_local.task_anchor()
256 }
257
258 #[cfg(all(feature = "fp-simd", feature = "uspace"))]
264 pub fn clone_user_fp_state_into(&self, child: &mut Self) {
265 assert!(
266 self.task_anchor().is_some(),
267 "FP clone parent must be bound"
268 );
269 assert!(
270 child.task_anchor().is_none(),
271 "FP clone child must be unpublished"
272 );
273 child.fp_state.save();
274 }
275
276 pub fn prepare_switch_to(&mut self, _next_ctx: &Self) {
278 #[cfg(feature = "fp-simd")]
279 {
280 self.fp_state.save();
281 _next_ctx.fp_state.restore();
282 }
283 }
284
285 #[inline(always)]
294 pub unsafe fn switch_to(&mut self, next_ctx: &Self) {
295 unsafe { context_switch_raw(self, next_ctx) }
296 }
297}
298
299#[cfg(kernel_tls)]
300#[unsafe(naked)]
301#[cfg(feature = "context")]
302unsafe extern "C" fn context_switch_raw(_current_task: &mut TaskContext, _next_task: &TaskContext) {
303 naked_asm!(
304 "
305 // save old context (callee-saved registers)
306 stp x29, x30, [x0, {r29_offset}]
307 stp x27, x28, [x0, {r27_offset}]
308 stp x25, x26, [x0, {r25_offset}]
309 stp x23, x24, [x0, {r23_offset}]
310 stp x21, x22, [x0, {r21_offset}]
311 stp x19, x20, [x0, {r19_offset}]
312 mov x19, sp
313 str x19, [x0, {sp_offset}]
314 mrs x9, tpidr_el0
315 str x9, [x0, {kernel_tls_offset}]
316
317 // restore new context
318 ldr x9, [x1, {kernel_tls_offset}]
319 msr tpidr_el0, x9
320 ldr x19, [x1, {sp_offset}]
321 mov sp, x19
322 ldp x19, x20, [x1, {r19_offset}]
323 ldp x21, x22, [x1, {r21_offset}]
324 ldp x23, x24, [x1, {r23_offset}]
325 ldp x25, x26, [x1, {r25_offset}]
326 ldp x27, x28, [x1, {r27_offset}]
327 ldp x29, x30, [x1, {r29_offset}]
328 ldr x9, [x1, {context_header_offset}]
329 msr sp_el0, x9
330
331 ret",
332 sp_offset = const offset_of!(TaskContext, sp),
333 r19_offset = const offset_of!(TaskContext, r19),
334 r21_offset = const offset_of!(TaskContext, r21),
335 r23_offset = const offset_of!(TaskContext, r23),
336 r25_offset = const offset_of!(TaskContext, r25),
337 r27_offset = const offset_of!(TaskContext, r27),
338 r29_offset = const offset_of!(TaskContext, r29),
339 context_header_offset = const offset_of!(TaskContext, task_local)
340 + offset_of!(TaskLocalState, context_header),
341 kernel_tls_offset = const offset_of!(TaskContext, task_local)
342 + offset_of!(TaskLocalState, kernel_tls),
343 )
344}
345
346#[cfg(not(kernel_tls))]
347#[unsafe(naked)]
348#[cfg(feature = "context")]
349unsafe extern "C" fn context_switch_raw(_current_task: &mut TaskContext, _next_task: &TaskContext) {
350 naked_asm!(
351 "
352 // save old context (callee-saved registers)
353 stp x29, x30, [x0, {r29_offset}]
354 stp x27, x28, [x0, {r27_offset}]
355 stp x25, x26, [x0, {r25_offset}]
356 stp x23, x24, [x0, {r23_offset}]
357 stp x21, x22, [x0, {r21_offset}]
358 stp x19, x20, [x0, {r19_offset}]
359 mov x19, sp
360 str x19, [x0, {sp_offset}]
361
362 // LinuxCurrent keeps task identity in SP_EL0. TPIDR_EL0 remains
363 // userspace-owned and is never part of a kernel task switch.
364 ldr x19, [x1, {sp_offset}]
365 mov sp, x19
366 ldp x19, x20, [x1, {r19_offset}]
367 ldp x21, x22, [x1, {r21_offset}]
368 ldp x23, x24, [x1, {r23_offset}]
369 ldp x25, x26, [x1, {r25_offset}]
370 ldp x27, x28, [x1, {r27_offset}]
371 ldp x29, x30, [x1, {r29_offset}]
372 ldr x9, [x1, {context_header_offset}]
373 msr sp_el0, x9
374 ret",
375 sp_offset = const offset_of!(TaskContext, sp),
376 r19_offset = const offset_of!(TaskContext, r19),
377 r21_offset = const offset_of!(TaskContext, r21),
378 r23_offset = const offset_of!(TaskContext, r23),
379 r25_offset = const offset_of!(TaskContext, r25),
380 r27_offset = const offset_of!(TaskContext, r27),
381 r29_offset = const offset_of!(TaskContext, r29),
382 context_header_offset = const offset_of!(TaskContext, task_local)
383 + offset_of!(TaskLocalState, context_header),
384 )
385}