1#[cfg(feature = "context")]
2use core::arch::naked_asm;
3#[cfg(feature = "context")]
4use core::mem::{align_of, offset_of, size_of};
5
6#[cfg(feature = "context")]
7use ax_memory_addr::VirtAddr;
8
9#[cfg(feature = "fp-simd")]
10use super::fp::FpuState;
11use super::registers::GeneralRegisters;
12#[cfg(feature = "context")]
13use crate::{KernelTlsBase, TaskLocalState, context::TaskAnchor};
14
15#[repr(C)]
17#[derive(Debug, Default, Clone, Copy)]
18pub struct TrapFrame {
19 pub regs: GeneralRegisters,
21 pub prmd: usize,
23 pub era: usize,
25}
26
27impl TrapFrame {
28 pub fn interrupted_context(&self) -> crate::trap::InterruptedContext {
30 use crate::trap::{InterruptedContext, InterruptedPrivilege};
31 InterruptedContext {
32 pc: self.ip(),
33 sp: self.regs.sp,
34 fp: self.regs.fp,
35 privilege: match self.origin() {
36 crate::trap::TrapOrigin::Kernel => InterruptedPrivilege::Kernel,
37 crate::trap::TrapOrigin::User => InterruptedPrivilege::User,
38 },
39 }
40 }
41
42 pub const fn origin(&self) -> crate::TrapOrigin {
48 if self.prmd & 0b11 == 0 {
49 crate::TrapOrigin::Kernel
50 } else {
51 crate::TrapOrigin::User
52 }
53 }
54
55 pub const fn arg0(&self) -> usize {
57 self.regs.a0
58 }
59
60 pub const fn set_arg0(&mut self, a0: usize) {
62 self.regs.a0 = a0;
63 }
64
65 pub const fn arg1(&self) -> usize {
67 self.regs.a1
68 }
69
70 pub const fn set_arg1(&mut self, a1: usize) {
72 self.regs.a1 = a1;
73 }
74
75 pub const fn arg2(&self) -> usize {
77 self.regs.a2
78 }
79
80 pub const fn set_arg2(&mut self, a2: usize) {
82 self.regs.a2 = a2;
83 }
84
85 pub const fn arg3(&self) -> usize {
87 self.regs.a3
88 }
89
90 pub const fn set_arg3(&mut self, a3: usize) {
92 self.regs.a3 = a3;
93 }
94
95 pub const fn arg4(&self) -> usize {
97 self.regs.a4
98 }
99
100 pub const fn set_arg4(&mut self, a4: usize) {
102 self.regs.a4 = a4;
103 }
104
105 pub const fn arg5(&self) -> usize {
107 self.regs.a5
108 }
109
110 pub const fn set_arg5(&mut self, a5: usize) {
112 self.regs.a5 = a5;
113 }
114
115 pub const fn sysno(&self) -> usize {
117 self.regs.a7
118 }
119
120 pub const fn set_sysno(&mut self, a7: usize) {
122 self.regs.a7 = a7;
123 }
124
125 pub const fn ip(&self) -> usize {
127 self.era
128 }
129
130 pub const fn set_ip(&mut self, pc: usize) {
132 self.era = pc;
133 }
134
135 pub const fn sp(&self) -> usize {
137 self.regs.sp
138 }
139
140 pub const fn set_sp(&mut self, sp: usize) {
142 self.regs.sp = sp;
143 }
144
145 pub const fn retval(&self) -> usize {
147 self.regs.a0
148 }
149
150 pub const fn set_retval(&mut self, a0: usize) {
152 self.regs.a0 = a0;
153 }
154
155 pub const fn set_ra(&mut self, ra: usize) {
157 self.regs.ra = ra;
158 }
159
160 pub const fn tls(&self) -> usize {
162 self.regs.tp
163 }
164
165 pub const fn set_tls(&mut self, tls_area: usize) {
167 self.regs.tp = tls_area;
168 }
169
170 pub fn backtrace_registers(&self) -> crate::trap::BacktraceRegisters {
172 crate::trap::BacktraceRegisters::new(self.regs.fp as _, self.era as _, self.regs.ra as _)
173 }
174}
175
176#[allow(missing_docs)]
188#[repr(C)]
189#[derive(Debug, Default)]
190#[cfg(feature = "context")]
191pub struct TaskContext {
192 pub ra: usize,
194 pub sp: usize,
196 pub s: [usize; 10],
198 task_local: TaskLocalState,
200 #[cfg(feature = "fp-simd")]
201 pub fpu: FpuState,
203}
204
205#[cfg(feature = "context")]
208const _: () = {
209 assert!(size_of::<KernelTlsBase>() == size_of::<usize>());
210 assert!(align_of::<KernelTlsBase>() == align_of::<usize>());
211 assert!(offset_of!(TaskContext, ra) == 0);
212 assert!(offset_of!(TaskContext, sp) == offset_of!(TaskContext, ra) + size_of::<usize>());
213 assert!(size_of::<[usize; 10]>() == 10 * size_of::<usize>());
214 assert!(
215 offset_of!(TaskContext, task_local)
216 == offset_of!(TaskContext, s) + size_of::<[usize; 10]>()
217 );
218};
219
220#[cfg(feature = "context")]
221impl TaskContext {
222 pub fn new() -> Self {
224 Self::default()
225 }
226
227 pub fn init(&mut self, entry: usize, kstack_top: VirtAddr, kernel_tls: KernelTlsBase) {
230 self.sp = kstack_top.as_usize();
231 self.ra = entry;
232 self.task_local.set_kernel_tls(kernel_tls);
233 }
234
235 pub fn set_task_anchor(&mut self, header: TaskAnchor) {
237 self.task_local.set_task_anchor(header);
238 }
239
240 pub const fn task_anchor(&self) -> Option<TaskAnchor> {
242 self.task_local.task_anchor()
243 }
244
245 #[cfg(all(feature = "fp-simd", feature = "uspace"))]
251 pub fn clone_user_fp_state_into(&self, child: &mut Self) {
252 assert!(
253 self.task_anchor().is_some(),
254 "FP clone parent must be bound"
255 );
256 assert!(
257 child.task_anchor().is_none(),
258 "FP clone child must be unpublished"
259 );
260 child.fpu.save();
261 }
262
263 pub fn prepare_switch_to(&mut self, _next_ctx: &Self) {
265 #[cfg(feature = "fp-simd")]
266 {
267 self.fpu.save();
268 _next_ctx.fpu.restore();
269 }
270 }
271
272 #[inline(always)]
281 pub unsafe fn switch_to(&mut self, next_ctx: &Self) {
282 unsafe { context_switch_raw(self, next_ctx) }
283 }
284}
285
286#[cfg(kernel_tls)]
287#[unsafe(naked)]
288#[cfg(feature = "context")]
289unsafe extern "C" fn context_switch_raw(_current_task: &mut TaskContext, _next_task: &TaskContext) {
290 naked_asm!(
291 include_asm_macros!(),
292 "
293 // save old context (callee-saved registers)
294 st.d $ra, $a0, {ra_offset}
295 st.d $sp, $a0, {sp_offset}
296 st.d $s0, $a0, {s0_offset}
297 st.d $s1, $a0, {s1_offset}
298 st.d $s2, $a0, {s2_offset}
299 st.d $s3, $a0, {s3_offset}
300 st.d $s4, $a0, {s4_offset}
301 st.d $s5, $a0, {s5_offset}
302 st.d $s6, $a0, {s6_offset}
303 st.d $s7, $a0, {s7_offset}
304 st.d $s8, $a0, {s8_offset}
305 st.d $fp, $a0, {frame_pointer_offset}
306 // Keep task TLS inside the final, IRQ-disabled context-switch
307 // boundary. In particular, never add the CPU-owned $r21 here.
308 st.d $tp, $a0, {kernel_tls_offset}
309
310 // restore new context
311 ld.d $fp, $a1, {frame_pointer_offset}
312 ld.d $s8, $a1, {s8_offset}
313 ld.d $s7, $a1, {s7_offset}
314 ld.d $s6, $a1, {s6_offset}
315 ld.d $s5, $a1, {s5_offset}
316 ld.d $s4, $a1, {s4_offset}
317 ld.d $s3, $a1, {s3_offset}
318 ld.d $s2, $a1, {s2_offset}
319 ld.d $s1, $a1, {s1_offset}
320 ld.d $s0, $a1, {s0_offset}
321 ld.d $sp, $a1, {sp_offset}
322 ld.d $ra, $a1, {ra_offset}
323 ld.d $tp, $a1, {kernel_tls_offset}
324
325 ret",
326 ra_offset = const offset_of!(TaskContext, ra),
327 sp_offset = const offset_of!(TaskContext, sp),
328 s0_offset = const offset_of!(TaskContext, s),
329 s1_offset = const offset_of!(TaskContext, s) + size_of::<[usize; 1]>(),
330 s2_offset = const offset_of!(TaskContext, s) + size_of::<[usize; 2]>(),
331 s3_offset = const offset_of!(TaskContext, s) + size_of::<[usize; 3]>(),
332 s4_offset = const offset_of!(TaskContext, s) + size_of::<[usize; 4]>(),
333 s5_offset = const offset_of!(TaskContext, s) + size_of::<[usize; 5]>(),
334 s6_offset = const offset_of!(TaskContext, s) + size_of::<[usize; 6]>(),
335 s7_offset = const offset_of!(TaskContext, s) + size_of::<[usize; 7]>(),
336 s8_offset = const offset_of!(TaskContext, s) + size_of::<[usize; 8]>(),
337 frame_pointer_offset = const offset_of!(TaskContext, s) + size_of::<[usize; 9]>(),
338 kernel_tls_offset = const offset_of!(TaskContext, task_local)
339 + offset_of!(TaskLocalState, kernel_tls),
340 )
341}
342
343#[cfg(not(kernel_tls))]
344#[unsafe(naked)]
345#[cfg(feature = "context")]
346unsafe extern "C" fn context_switch_raw(_current_task: &mut TaskContext, _next_task: &TaskContext) {
347 naked_asm!(
348 include_asm_macros!(),
349 "
350 // Save old callee state. The CPU-owned r21/KS3 anchor and the
351 // LinuxCurrent task-owned tp value are not generic saved registers.
352 st.d $ra, $a0, {ra_offset}
353 st.d $sp, $a0, {sp_offset}
354 st.d $s0, $a0, {s0_offset}
355 st.d $s1, $a0, {s1_offset}
356 st.d $s2, $a0, {s2_offset}
357 st.d $s3, $a0, {s3_offset}
358 st.d $s4, $a0, {s4_offset}
359 st.d $s5, $a0, {s5_offset}
360 st.d $s6, $a0, {s6_offset}
361 st.d $s7, $a0, {s7_offset}
362 st.d $s8, $a0, {s8_offset}
363 st.d $fp, $a0, {frame_pointer_offset}
364
365 // Restore next state and make tp current immediately before the direct
366 // return. r21 and KS3 continue to identify the physical CPU.
367 ld.d $fp, $a1, {frame_pointer_offset}
368 ld.d $s8, $a1, {s8_offset}
369 ld.d $s7, $a1, {s7_offset}
370 ld.d $s6, $a1, {s6_offset}
371 ld.d $s5, $a1, {s5_offset}
372 ld.d $s4, $a1, {s4_offset}
373 ld.d $s3, $a1, {s3_offset}
374 ld.d $s2, $a1, {s2_offset}
375 ld.d $s1, $a1, {s1_offset}
376 ld.d $s0, $a1, {s0_offset}
377 ld.d $sp, $a1, {sp_offset}
378 ld.d $ra, $a1, {ra_offset}
379 ld.d $tp, $a1, {context_header_offset}
380 ret",
381 ra_offset = const offset_of!(TaskContext, ra),
382 sp_offset = const offset_of!(TaskContext, sp),
383 s0_offset = const offset_of!(TaskContext, s),
384 s1_offset = const offset_of!(TaskContext, s) + size_of::<[usize; 1]>(),
385 s2_offset = const offset_of!(TaskContext, s) + size_of::<[usize; 2]>(),
386 s3_offset = const offset_of!(TaskContext, s) + size_of::<[usize; 3]>(),
387 s4_offset = const offset_of!(TaskContext, s) + size_of::<[usize; 4]>(),
388 s5_offset = const offset_of!(TaskContext, s) + size_of::<[usize; 5]>(),
389 s6_offset = const offset_of!(TaskContext, s) + size_of::<[usize; 6]>(),
390 s7_offset = const offset_of!(TaskContext, s) + size_of::<[usize; 7]>(),
391 s8_offset = const offset_of!(TaskContext, s) + size_of::<[usize; 8]>(),
392 frame_pointer_offset = const offset_of!(TaskContext, s) + size_of::<[usize; 9]>(),
393 context_header_offset = const offset_of!(TaskContext, task_local)
394 + offset_of!(TaskLocalState, context_header),
395 )
396}