#[cfg(feature = "context")]
use core::arch::naked_asm;
use core::fmt;
#[cfg(feature = "context")]
use core::mem::{align_of, offset_of, size_of};
#[cfg(feature = "context")]
use ax_memory_addr::VirtAddr;
#[cfg(feature = "fp-simd")]
use super::fp::FpState;
#[cfg(feature = "context")]
use crate::{KernelTlsBase, TaskLocalState, context::TaskAnchor};
#[repr(C)]
#[derive(Default, Clone, Copy)]
pub struct TrapFrame {
pub x: super::registers::GeneralRegisters,
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 fn interrupted_context(&self) -> crate::trap::InterruptedContext {
use crate::trap::{InterruptedContext, InterruptedPrivilege};
InterruptedContext {
pc: self.ip(),
sp: self.sp as usize,
fp: self.x[29] as usize,
privilege: match self.origin() {
crate::trap::TrapOrigin::Kernel => InterruptedPrivilege::Kernel,
crate::trap::TrapOrigin::User => InterruptedPrivilege::User,
},
}
}
pub const fn origin(&self) -> crate::TrapOrigin {
if self.spsr & 0b1_1111 == 0 {
crate::TrapOrigin::User
} else {
crate::TrapOrigin::Kernel
}
}
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_registers(&self) -> crate::trap::BacktraceRegisters {
crate::trap::BacktraceRegisters::new(self.x[29] as _, self.elr as _, self.x[30] as _)
}
}
#[allow(missing_docs)]
#[repr(C)]
#[derive(Debug, Default)]
#[cfg(feature = "context")]
pub struct TaskContext {
sp: u64,
r19: u64,
r20: u64,
r21: u64,
r22: u64,
r23: u64,
r24: u64,
r25: u64,
r26: u64,
r27: u64,
r28: u64,
r29: u64,
lr: u64, task_local: TaskLocalState,
#[cfg(feature = "fp-simd")]
fp_state: FpState,
}
#[cfg(feature = "context")]
const _: () = {
assert!(size_of::<KernelTlsBase>() == size_of::<usize>());
assert!(align_of::<KernelTlsBase>() == align_of::<usize>());
assert!(offset_of!(TaskContext, sp) == 0);
assert!(offset_of!(TaskContext, r20) == offset_of!(TaskContext, r19) + size_of::<u64>());
assert!(offset_of!(TaskContext, r22) == offset_of!(TaskContext, r21) + size_of::<u64>());
assert!(offset_of!(TaskContext, r24) == offset_of!(TaskContext, r23) + size_of::<u64>());
assert!(offset_of!(TaskContext, r26) == offset_of!(TaskContext, r25) + size_of::<u64>());
assert!(offset_of!(TaskContext, r28) == offset_of!(TaskContext, r27) + size_of::<u64>());
assert!(offset_of!(TaskContext, lr) == offset_of!(TaskContext, r29) + size_of::<u64>());
assert!(offset_of!(TaskContext, task_local) == offset_of!(TaskContext, lr) + size_of::<u64>());
};
#[cfg(feature = "context")]
impl TaskContext {
pub fn new() -> Self {
Self::default()
}
pub fn init(&mut self, entry: usize, kstack_top: VirtAddr, kernel_tls: KernelTlsBase) {
self.sp = kstack_top.as_usize() as u64;
self.lr = entry as u64;
self.task_local.set_kernel_tls(kernel_tls);
}
pub fn set_task_anchor(&mut self, header: TaskAnchor) {
self.task_local.set_task_anchor(header);
}
pub const fn task_anchor(&self) -> Option<TaskAnchor> {
self.task_local.task_anchor()
}
#[cfg(all(feature = "fp-simd", feature = "uspace"))]
pub fn clone_user_fp_state_into(&self, child: &mut Self) {
assert!(
self.task_anchor().is_some(),
"FP clone parent must be bound"
);
assert!(
child.task_anchor().is_none(),
"FP clone child must be unpublished"
);
child.fp_state.save();
}
pub fn prepare_switch_to(&mut self, _next_ctx: &Self) {
#[cfg(feature = "fp-simd")]
{
self.fp_state.save();
_next_ctx.fp_state.restore();
}
}
#[inline(always)]
pub unsafe fn switch_to(&mut self, next_ctx: &Self) {
unsafe { context_switch_raw(self, next_ctx) }
}
}
#[cfg(kernel_tls)]
#[unsafe(naked)]
#[cfg(feature = "context")]
unsafe extern "C" fn context_switch_raw(_current_task: &mut TaskContext, _next_task: &TaskContext) {
naked_asm!(
"
// save old context (callee-saved registers)
stp x29, x30, [x0, {r29_offset}]
stp x27, x28, [x0, {r27_offset}]
stp x25, x26, [x0, {r25_offset}]
stp x23, x24, [x0, {r23_offset}]
stp x21, x22, [x0, {r21_offset}]
stp x19, x20, [x0, {r19_offset}]
mov x19, sp
str x19, [x0, {sp_offset}]
mrs x9, tpidr_el0
str x9, [x0, {kernel_tls_offset}]
// restore new context
ldr x9, [x1, {kernel_tls_offset}]
msr tpidr_el0, x9
ldr x19, [x1, {sp_offset}]
mov sp, x19
ldp x19, x20, [x1, {r19_offset}]
ldp x21, x22, [x1, {r21_offset}]
ldp x23, x24, [x1, {r23_offset}]
ldp x25, x26, [x1, {r25_offset}]
ldp x27, x28, [x1, {r27_offset}]
ldp x29, x30, [x1, {r29_offset}]
ldr x9, [x1, {context_header_offset}]
msr sp_el0, x9
ret",
sp_offset = const offset_of!(TaskContext, sp),
r19_offset = const offset_of!(TaskContext, r19),
r21_offset = const offset_of!(TaskContext, r21),
r23_offset = const offset_of!(TaskContext, r23),
r25_offset = const offset_of!(TaskContext, r25),
r27_offset = const offset_of!(TaskContext, r27),
r29_offset = const offset_of!(TaskContext, r29),
context_header_offset = const offset_of!(TaskContext, task_local)
+ offset_of!(TaskLocalState, context_header),
kernel_tls_offset = const offset_of!(TaskContext, task_local)
+ offset_of!(TaskLocalState, kernel_tls),
)
}
#[cfg(not(kernel_tls))]
#[unsafe(naked)]
#[cfg(feature = "context")]
unsafe extern "C" fn context_switch_raw(_current_task: &mut TaskContext, _next_task: &TaskContext) {
naked_asm!(
"
// save old context (callee-saved registers)
stp x29, x30, [x0, {r29_offset}]
stp x27, x28, [x0, {r27_offset}]
stp x25, x26, [x0, {r25_offset}]
stp x23, x24, [x0, {r23_offset}]
stp x21, x22, [x0, {r21_offset}]
stp x19, x20, [x0, {r19_offset}]
mov x19, sp
str x19, [x0, {sp_offset}]
// LinuxCurrent keeps task identity in SP_EL0. TPIDR_EL0 remains
// userspace-owned and is never part of a kernel task switch.
ldr x19, [x1, {sp_offset}]
mov sp, x19
ldp x19, x20, [x1, {r19_offset}]
ldp x21, x22, [x1, {r21_offset}]
ldp x23, x24, [x1, {r23_offset}]
ldp x25, x26, [x1, {r25_offset}]
ldp x27, x28, [x1, {r27_offset}]
ldp x29, x30, [x1, {r29_offset}]
ldr x9, [x1, {context_header_offset}]
msr sp_el0, x9
ret",
sp_offset = const offset_of!(TaskContext, sp),
r19_offset = const offset_of!(TaskContext, r19),
r21_offset = const offset_of!(TaskContext, r21),
r23_offset = const offset_of!(TaskContext, r23),
r25_offset = const offset_of!(TaskContext, r25),
r27_offset = const offset_of!(TaskContext, r27),
r29_offset = const offset_of!(TaskContext, r29),
context_header_offset = const offset_of!(TaskContext, task_local)
+ offset_of!(TaskLocalState, context_header),
)
}