use core::{arch::naked_asm, fmt};
use ax_memory_addr::VirtAddr;
#[repr(C)]
#[derive(Default, Clone, Copy)]
pub struct TrapFrame {
pub x: [u64; 31],
pub elr: u64,
pub spsr: u64,
pub sp: u64,
}
impl fmt::Debug for TrapFrame {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
writeln!(f, "TrapFrame: {{")?;
for (i, ®) in self.x.iter().enumerate() {
writeln!(f, " x{i}: {reg:#x},")?;
}
writeln!(f, " elr: {:#x},", self.elr)?;
writeln!(f, " spsr: {:#x},", self.spsr)?;
writeln!(f, " sp: {:#x},", self.sp)?;
write!(f, "}}")?;
Ok(())
}
}
impl TrapFrame {
pub const fn arg0(&self) -> usize {
self.x[0] as _
}
pub const fn set_arg0(&mut self, a0: usize) {
self.x[0] = a0 as _;
}
pub const fn arg1(&self) -> usize {
self.x[1] as _
}
pub const fn set_arg1(&mut self, a1: usize) {
self.x[1] = a1 as _;
}
pub const fn arg2(&self) -> usize {
self.x[2] as _
}
pub const fn set_arg2(&mut self, a2: usize) {
self.x[2] = a2 as _;
}
pub const fn arg3(&self) -> usize {
self.x[3] as _
}
pub const fn set_arg3(&mut self, a3: usize) {
self.x[3] = a3 as _;
}
pub const fn arg4(&self) -> usize {
self.x[4] as _
}
pub const fn set_arg4(&mut self, a4: usize) {
self.x[4] = a4 as _;
}
pub const fn arg5(&self) -> usize {
self.x[5] as _
}
pub const fn set_arg5(&mut self, a5: usize) {
self.x[5] = a5 as _;
}
pub const fn ip(&self) -> usize {
self.elr as _
}
pub const fn set_ip(&mut self, pc: usize) {
self.elr = pc as _;
}
pub const fn sysno(&self) -> usize {
self.x[8] as usize
}
pub const fn set_sysno(&mut self, sysno: usize) {
self.x[8] = sysno as _;
}
pub const fn retval(&self) -> usize {
self.x[0] as _
}
pub const fn set_retval(&mut self, r0: usize) {
self.x[0] = r0 as _;
}
pub const fn set_ra(&mut self, lr: usize) {
self.x[30] = lr as _;
}
pub fn backtrace(&self) -> axbacktrace::Backtrace {
axbacktrace::Backtrace::capture_trap(self.x[29] as _, self.elr as _, self.x[30] as _)
}
}
#[repr(C, align(16))]
#[derive(Debug, Default)]
pub struct FpState {
pub regs: [u128; 32],
pub fpcr: u32,
pub fpsr: u32,
}
#[cfg(feature = "fp-simd")]
impl FpState {
pub fn save(&mut self) {
unsafe { fpstate_save(self) }
}
pub fn restore(&self) {
unsafe { fpstate_restore(self) }
}
}
#[allow(missing_docs)]
#[repr(C)]
#[derive(Debug, Default)]
pub struct TaskContext {
pub sp: u64,
pub r19: u64,
pub r20: u64,
pub r21: u64,
pub r22: u64,
pub r23: u64,
pub r24: u64,
pub r25: u64,
pub r26: u64,
pub r27: u64,
pub r28: u64,
pub r29: u64,
pub lr: u64, pub tpidr_el0: u64,
#[cfg(feature = "uspace")]
pub ttbr0_el1: ax_memory_addr::PhysAddr,
#[cfg(feature = "fp-simd")]
pub fp_state: FpState,
}
impl TaskContext {
pub fn new() -> Self {
Self::default()
}
pub fn init(&mut self, entry: usize, kstack_top: VirtAddr, tls_area: VirtAddr) {
self.sp = kstack_top.as_usize() as u64;
self.lr = entry as u64;
self.tpidr_el0 = tls_area.as_usize() as u64;
}
#[cfg(feature = "uspace")]
pub fn set_page_table_root(&mut self, ttbr0_el1: ax_memory_addr::PhysAddr) {
self.ttbr0_el1 = ttbr0_el1;
}
pub fn switch_to(&mut self, next_ctx: &Self) {
#[cfg(feature = "tls")]
{
self.tpidr_el0 = crate::asm::read_thread_pointer() as _;
unsafe { crate::asm::write_thread_pointer(next_ctx.tpidr_el0 as _) };
}
#[cfg(feature = "fp-simd")]
{
self.fp_state.save();
next_ctx.fp_state.restore();
}
#[cfg(feature = "uspace")]
if self.ttbr0_el1 != next_ctx.ttbr0_el1 {
unsafe { crate::asm::write_user_page_table(next_ctx.ttbr0_el1) };
crate::asm::flush_tlb(None); }
unsafe { context_switch(self, next_ctx) }
}
}
#[unsafe(naked)]
unsafe extern "C" fn context_switch(_current_task: &mut TaskContext, _next_task: &TaskContext) {
naked_asm!(
"
// save old context (callee-saved registers)
stp x29, x30, [x0, 11 * 8]
stp x27, x28, [x0, 9 * 8]
stp x25, x26, [x0, 7 * 8]
stp x23, x24, [x0, 5 * 8]
stp x21, x22, [x0, 3 * 8]
stp x19, x20, [x0, 1 * 8]
mov x19, sp
str x19, [x0]
// restore new context
ldr x19, [x1]
mov sp, x19
ldp x19, x20, [x1, 1 * 8]
ldp x21, x22, [x1, 3 * 8]
ldp x23, x24, [x1, 5 * 8]
ldp x25, x26, [x1, 7 * 8]
ldp x27, x28, [x1, 9 * 8]
ldp x29, x30, [x1, 11 * 8]
ret",
)
}
#[unsafe(naked)]
#[cfg(feature = "fp-simd")]
unsafe extern "C" fn fpstate_save(state: &mut FpState) {
naked_asm!(
".arch armv8
// save fp/neon context
mrs x9, fpcr
mrs x10, fpsr
stp q0, q1, [x0, 0 * 16]
stp q2, q3, [x0, 2 * 16]
stp q4, q5, [x0, 4 * 16]
stp q6, q7, [x0, 6 * 16]
stp q8, q9, [x0, 8 * 16]
stp q10, q11, [x0, 10 * 16]
stp q12, q13, [x0, 12 * 16]
stp q14, q15, [x0, 14 * 16]
stp q16, q17, [x0, 16 * 16]
stp q18, q19, [x0, 18 * 16]
stp q20, q21, [x0, 20 * 16]
stp q22, q23, [x0, 22 * 16]
stp q24, q25, [x0, 24 * 16]
stp q26, q27, [x0, 26 * 16]
stp q28, q29, [x0, 28 * 16]
stp q30, q31, [x0, 30 * 16]
str x9, [x0, 64 * 8]
str x10, [x0, 65 * 8]
isb
ret"
)
}
#[unsafe(naked)]
#[cfg(feature = "fp-simd")]
unsafe extern "C" fn fpstate_restore(state: &FpState) {
naked_asm!(
".arch armv8
// restore fp/neon context
ldp q0, q1, [x0, 0 * 16]
ldp q2, q3, [x0, 2 * 16]
ldp q4, q5, [x0, 4 * 16]
ldp q6, q7, [x0, 6 * 16]
ldp q8, q9, [x0, 8 * 16]
ldp q10, q11, [x0, 10 * 16]
ldp q12, q13, [x0, 12 * 16]
ldp q14, q15, [x0, 14 * 16]
ldp q16, q17, [x0, 16 * 16]
ldp q18, q19, [x0, 18 * 16]
ldp q20, q21, [x0, 20 * 16]
ldp q22, q23, [x0, 22 * 16]
ldp q24, q25, [x0, 24 * 16]
ldp q26, q27, [x0, 26 * 16]
ldp q28, q29, [x0, 28 * 16]
ldp q30, q31, [x0, 30 * 16]
ldr x9, [x0, 64 * 8]
ldr x10, [x0, 65 * 8]
msr fpcr, x9
msr fpsr, x10
isb
ret"
)
}