1use core::arch::naked_asm;
2
3use ax_memory_addr::VirtAddr;
4use riscv::register::sstatus::{self, FS};
5
6#[allow(missing_docs)]
8#[repr(C)]
9#[derive(Debug, Default, Clone, Copy)]
10pub struct GeneralRegisters {
11 pub zero: usize,
12 pub ra: usize,
13 pub sp: usize,
14 pub gp: usize,
15 pub tp: usize,
16 pub t0: usize,
17 pub t1: usize,
18 pub t2: usize,
19 pub s0: usize,
20 pub s1: usize,
21 pub a0: usize,
22 pub a1: usize,
23 pub a2: usize,
24 pub a3: usize,
25 pub a4: usize,
26 pub a5: usize,
27 pub a6: usize,
28 pub a7: usize,
29 pub s2: usize,
30 pub s3: usize,
31 pub s4: usize,
32 pub s5: usize,
33 pub s6: usize,
34 pub s7: usize,
35 pub s8: usize,
36 pub s9: usize,
37 pub s10: usize,
38 pub s11: usize,
39 pub t3: usize,
40 pub t4: usize,
41 pub t5: usize,
42 pub t6: usize,
43}
44
45#[repr(C)]
47#[derive(Debug, Clone, Copy)]
48pub struct FpState {
49 pub fp: [u64; 32],
51 pub fcsr: usize,
53 pub fs: FS,
55}
56
57impl Default for FpState {
58 fn default() -> Self {
59 Self {
60 fs: FS::Initial,
61 fp: [0; 32],
62 fcsr: 0,
63 }
64 }
65}
66
67#[cfg(feature = "fp-simd")]
68impl FpState {
69 #[inline]
71 pub fn restore(&self) {
72 unsafe { restore_fp_registers(self) }
73 }
74
75 #[inline]
77 pub fn save(&mut self) {
78 unsafe { save_fp_registers(self) }
79 }
80
81 #[inline]
83 pub fn clear() {
84 unsafe { clear_fp_registers() }
85 }
86
87 pub fn switch_to(&mut self, next_fp_state: &FpState) {
91 let current_fs = sstatus::read().fs();
93 if current_fs == FS::Dirty {
95 self.save();
97 self.fs = FS::Clean;
99 }
100 match next_fp_state.fs {
102 FS::Clean => next_fp_state.restore(), FS::Initial => FpState::clear(), FS::Off => {} FS::Dirty => unreachable!("FP state of the next task should not be dirty"),
106 }
107 unsafe { sstatus::set_fs(next_fp_state.fs) }; }
109}
110
111#[repr(C)]
113#[derive(Debug, Clone, Copy)]
114pub struct TrapFrame {
115 pub regs: GeneralRegisters,
117 pub sepc: usize,
119 pub sstatus: sstatus::Sstatus,
121}
122
123impl Default for TrapFrame {
124 fn default() -> Self {
125 Self {
126 regs: GeneralRegisters::default(),
127 sepc: 0,
128 sstatus: sstatus::Sstatus::from_bits(0),
129 }
130 }
131}
132
133impl TrapFrame {
134 pub const fn arg0(&self) -> usize {
136 self.regs.a0
137 }
138
139 pub const fn set_arg0(&mut self, a0: usize) {
141 self.regs.a0 = a0;
142 }
143
144 pub const fn arg1(&self) -> usize {
146 self.regs.a1
147 }
148
149 pub const fn set_arg1(&mut self, a1: usize) {
151 self.regs.a1 = a1;
152 }
153
154 pub const fn arg2(&self) -> usize {
156 self.regs.a2
157 }
158
159 pub const fn set_arg2(&mut self, a2: usize) {
161 self.regs.a2 = a2;
162 }
163
164 pub const fn arg3(&self) -> usize {
166 self.regs.a3
167 }
168
169 pub const fn set_arg3(&mut self, a3: usize) {
171 self.regs.a3 = a3;
172 }
173
174 pub const fn arg4(&self) -> usize {
176 self.regs.a4
177 }
178
179 pub const fn set_arg4(&mut self, a4: usize) {
181 self.regs.a4 = a4;
182 }
183
184 pub const fn arg5(&self) -> usize {
186 self.regs.a5
187 }
188
189 pub const fn set_arg5(&mut self, a5: usize) {
191 self.regs.a5 = a5;
192 }
193
194 pub const fn sysno(&self) -> usize {
196 self.regs.a7
197 }
198
199 pub const fn set_sysno(&mut self, a7: usize) {
201 self.regs.a7 = a7;
202 }
203
204 pub const fn ip(&self) -> usize {
206 self.sepc
207 }
208
209 pub const fn set_ip(&mut self, pc: usize) {
211 self.sepc = pc;
212 }
213
214 pub const fn sp(&self) -> usize {
216 self.regs.sp
217 }
218
219 pub const fn set_sp(&mut self, sp: usize) {
221 self.regs.sp = sp;
222 }
223
224 pub const fn retval(&self) -> usize {
226 self.regs.a0
227 }
228
229 pub const fn set_retval(&mut self, a0: usize) {
231 self.regs.a0 = a0;
232 }
233
234 pub const fn set_ra(&mut self, ra: usize) {
236 self.regs.ra = ra;
237 }
238
239 pub const fn tls(&self) -> usize {
241 self.regs.tp
242 }
243
244 pub const fn set_tls(&mut self, tls_area: usize) {
246 self.regs.tp = tls_area;
247 }
248
249 pub fn backtrace(&self) -> axbacktrace::Backtrace {
251 axbacktrace::Backtrace::capture_trap(self.regs.s0 as _, self.sepc as _, self.regs.ra as _)
252 }
253}
254
255#[allow(missing_docs)]
267#[repr(C)]
268#[derive(Debug, Default)]
269pub struct TaskContext {
270 pub ra: usize, pub sp: usize, pub s0: usize, pub s1: usize,
275
276 pub s2: usize, pub s3: usize,
278 pub s4: usize,
279 pub s5: usize,
280 pub s6: usize,
281 pub s7: usize,
282 pub s8: usize,
283 pub s9: usize,
284 pub s10: usize,
285 pub s11: usize,
286 pub tp: usize,
288 #[cfg(feature = "uspace")]
290 pub satp: ax_memory_addr::PhysAddr,
291 #[cfg(feature = "fp-simd")]
292 pub fp_state: FpState,
293}
294
295impl TaskContext {
296 pub fn new() -> Self {
304 Self {
305 #[cfg(feature = "uspace")]
306 satp: crate::asm::read_kernel_page_table(),
307 ..Default::default()
308 }
309 }
310
311 pub fn init(&mut self, entry: usize, kstack_top: VirtAddr, tls_area: VirtAddr) {
314 self.sp = kstack_top.as_usize();
315 self.ra = entry;
316 self.tp = tls_area.as_usize();
317 }
318
319 #[cfg(feature = "uspace")]
324 pub fn set_page_table_root(&mut self, satp: ax_memory_addr::PhysAddr) {
325 self.satp = satp;
326 }
327
328 pub fn switch_to(&mut self, next_ctx: &Self) {
333 #[cfg(feature = "tls")]
334 {
335 self.tp = crate::asm::read_thread_pointer();
336 unsafe { crate::asm::write_thread_pointer(next_ctx.tp) };
337 }
338 #[cfg(feature = "uspace")]
339 if self.satp != next_ctx.satp {
340 unsafe { crate::asm::write_user_page_table(next_ctx.satp) };
341 crate::asm::flush_tlb(None); }
343 #[cfg(feature = "fp-simd")]
344 {
345 self.fp_state.switch_to(&next_ctx.fp_state);
346 }
347
348 unsafe { context_switch(self, next_ctx) }
349 }
350}
351
352#[cfg(feature = "fp-simd")]
353#[unsafe(naked)]
354unsafe extern "C" fn save_fp_registers(fp_state: &mut FpState) {
355 naked_asm!(
356 include_fp_asm_macros!(),
357 "
358 PUSH_FLOAT_REGS a0
359 frcsr t0
360 STR t0, a0, 32
361 ret"
362 )
363}
364
365#[cfg(feature = "fp-simd")]
366#[unsafe(naked)]
367unsafe extern "C" fn restore_fp_registers(fp_state: &FpState) {
368 naked_asm!(
369 include_fp_asm_macros!(),
370 "
371 POP_FLOAT_REGS a0
372 LDR t0, a0, 32
373 fscsr x0, t0
374 ret"
375 )
376}
377
378#[cfg(feature = "fp-simd")]
379#[unsafe(naked)]
380unsafe extern "C" fn clear_fp_registers() {
381 naked_asm!(
382 include_fp_asm_macros!(),
383 "
384 CLEAR_FLOAT_REGS
385 ret"
386 )
387}
388
389#[unsafe(naked)]
390unsafe extern "C" fn context_switch(_current_task: &mut TaskContext, _next_task: &TaskContext) {
391 naked_asm!(
392 include_asm_macros!(),
393 "
394 // save old context (callee-saved registers)
395 STR ra, a0, 0
396 STR sp, a0, 1
397 STR s0, a0, 2
398 STR s1, a0, 3
399 STR s2, a0, 4
400 STR s3, a0, 5
401 STR s4, a0, 6
402 STR s5, a0, 7
403 STR s6, a0, 8
404 STR s7, a0, 9
405 STR s8, a0, 10
406 STR s9, a0, 11
407 STR s10, a0, 12
408 STR s11, a0, 13
409
410 // restore new context
411 LDR s11, a1, 13
412 LDR s10, a1, 12
413 LDR s9, a1, 11
414 LDR s8, a1, 10
415 LDR s7, a1, 9
416 LDR s6, a1, 8
417 LDR s5, a1, 7
418 LDR s4, a1, 6
419 LDR s3, a1, 5
420 LDR s2, a1, 4
421 LDR s1, a1, 3
422 LDR s0, a1, 2
423 LDR sp, a1, 1
424 LDR ra, a1, 0
425
426 ret",
427 )
428}