use core::{arch::naked_asm, fmt};
use ax_memory_addr::VirtAddr;
#[allow(missing_docs)]
#[repr(C)]
#[derive(Debug, Default, Clone, Copy)]
pub struct TrapFrame {
pub rax: u64,
pub rcx: u64,
pub rdx: u64,
pub rbx: u64,
pub rbp: u64,
pub rsi: u64,
pub rdi: u64,
pub r8: u64,
pub r9: u64,
pub r10: u64,
pub r11: u64,
pub r12: u64,
pub r13: u64,
pub r14: u64,
pub r15: u64,
pub vector: u64,
pub error_code: u64,
pub rip: u64,
pub cs: u64,
pub rflags: u64,
pub rsp: u64,
pub ss: u64,
}
impl TrapFrame {
pub const fn arg0(&self) -> usize {
self.rdi as _
}
pub const fn set_arg0(&mut self, rdi: usize) {
self.rdi = rdi as _;
}
pub const fn arg1(&self) -> usize {
self.rsi as _
}
pub const fn set_arg1(&mut self, rsi: usize) {
self.rsi = rsi as _;
}
pub const fn arg2(&self) -> usize {
self.rdx as _
}
pub const fn set_arg2(&mut self, rdx: usize) {
self.rdx = rdx as _;
}
pub const fn arg3(&self) -> usize {
self.r10 as _
}
pub const fn set_arg3(&mut self, r10: usize) {
self.r10 = r10 as _;
}
pub const fn arg4(&self) -> usize {
self.r8 as _
}
pub const fn set_arg4(&mut self, r8: usize) {
self.r8 = r8 as _;
}
pub const fn arg5(&self) -> usize {
self.r9 as _
}
pub const fn set_arg5(&mut self, r9: usize) {
self.r9 = r9 as _;
}
pub const fn ip(&self) -> usize {
self.rip as _
}
pub const fn set_ip(&mut self, rip: usize) {
self.rip = rip as _;
}
pub const fn sp(&self) -> usize {
self.rsp as _
}
pub const fn set_sp(&mut self, rsp: usize) {
self.rsp = rsp as _;
}
pub const fn sysno(&self) -> usize {
self.rax as usize
}
pub const fn set_sysno(&mut self, rax: usize) {
self.rax = rax as _;
}
pub const fn retval(&self) -> usize {
self.rax as _
}
pub const fn set_retval(&mut self, rax: usize) {
self.rax = rax as _;
}
pub fn backtrace(&self) -> axbacktrace::Backtrace {
axbacktrace::Backtrace::capture_trap(self.rbp as _, self.rip as _, 0)
}
}
#[repr(C)]
#[derive(Debug, Default)]
struct ContextSwitchFrame {
r15: u64,
r14: u64,
r13: u64,
r12: u64,
rbx: u64,
rbp: u64,
rip: u64,
}
#[allow(missing_docs)]
#[repr(C, align(16))]
#[derive(Debug)]
pub struct FxsaveArea {
pub fcw: u16,
pub fsw: u16,
pub ftw: u16,
pub fop: u16,
pub fip: u64,
pub fdp: u64,
pub mxcsr: u32,
pub mxcsr_mask: u32,
pub st: [u64; 16],
pub xmm: [u64; 32],
_padding: [u64; 12],
}
const _: () = assert!(core::mem::size_of::<FxsaveArea>() == 512);
pub struct ExtendedState {
pub fxsave_area: FxsaveArea,
}
#[cfg(feature = "fp-simd")]
impl ExtendedState {
#[inline]
pub fn save(&mut self) {
unsafe { core::arch::x86_64::_fxsave64(&mut self.fxsave_area as *mut _ as *mut u8) }
}
#[inline]
pub fn restore(&self) {
unsafe { core::arch::x86_64::_fxrstor64(&self.fxsave_area as *const _ as *const u8) }
}
pub const fn default() -> Self {
let mut area: FxsaveArea = unsafe { core::mem::MaybeUninit::zeroed().assume_init() };
area.fcw = 0x37f;
area.ftw = 0xffff;
area.mxcsr = 0x1f80;
Self { fxsave_area: area }
}
}
impl fmt::Debug for ExtendedState {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("ExtendedState")
.field("fxsave_area", &self.fxsave_area)
.finish()
}
}
#[derive(Debug)]
pub struct TaskContext {
pub kstack_top: VirtAddr,
pub rsp: u64,
pub fs_base: usize,
#[cfg(feature = "fp-simd")]
pub ext_state: ExtendedState,
#[cfg(feature = "uspace")]
pub cr3: ax_memory_addr::PhysAddr,
}
impl TaskContext {
pub fn new() -> Self {
Self {
kstack_top: va!(0),
rsp: 0,
fs_base: 0,
#[cfg(feature = "uspace")]
cr3: crate::asm::read_kernel_page_table(),
#[cfg(feature = "fp-simd")]
ext_state: ExtendedState::default(),
}
}
pub fn init(&mut self, entry: usize, kstack_top: VirtAddr, tls_area: VirtAddr) {
unsafe {
let frame_ptr = (kstack_top.as_mut_ptr() as *mut u64).sub(1);
let frame_ptr = (frame_ptr as *mut ContextSwitchFrame).sub(1);
core::ptr::write(
frame_ptr,
ContextSwitchFrame {
rip: entry as _,
..Default::default()
},
);
self.rsp = frame_ptr as u64;
}
self.kstack_top = kstack_top;
self.fs_base = tls_area.as_usize();
}
#[cfg(feature = "uspace")]
pub fn set_page_table_root(&mut self, cr3: ax_memory_addr::PhysAddr) {
self.cr3 = cr3;
}
pub fn switch_to(&mut self, next_ctx: &Self) {
#[cfg(feature = "fp-simd")]
{
self.ext_state.save();
next_ctx.ext_state.restore();
}
#[cfg(feature = "tls")]
unsafe {
self.fs_base = crate::asm::read_thread_pointer();
crate::asm::write_thread_pointer(next_ctx.fs_base);
}
#[cfg(feature = "uspace")]
unsafe {
if next_ctx.cr3 != self.cr3 {
crate::asm::write_user_page_table(next_ctx.cr3);
}
}
unsafe { context_switch(&mut self.rsp, &next_ctx.rsp) }
}
}
#[unsafe(naked)]
unsafe extern "C" fn context_switch(_current_stack: &mut u64, _next_stack: &u64) {
naked_asm!(
"
.code64
push rbp
push rbx
push r12
push r13
push r14
push r15
mov [rdi], rsp
mov rsp, [rsi]
pop r15
pop r14
pop r13
pop r12
pop rbx
pop rbp
ret",
)
}