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(Clone, Copy, 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);
const XSAVE_AREA_SIZE: usize = 1024;
#[repr(C, align(64))]
struct XsaveArea {
legacy: FxsaveArea,
rest: [u8; XSAVE_AREA_SIZE - 512],
}
const _: () = assert!(core::mem::size_of::<XsaveArea>() == XSAVE_AREA_SIZE);
pub struct ExtendedState {
area: XsaveArea,
}
#[cfg(feature = "fp-simd")]
impl ExtendedState {
#[inline]
pub fn fxsave_area(&self) -> &FxsaveArea {
&self.area.legacy
}
#[inline]
fn xsave_enabled() -> bool {
let cr4 = unsafe { x86::controlregs::cr4() };
cr4.contains(x86::controlregs::Cr4::CR4_ENABLE_OS_XSAVE)
}
#[inline]
fn xsave_mask() -> u64 {
unsafe { x86::controlregs::xcr0().bits() }
}
#[inline]
pub fn save(&mut self) {
let ptr = &mut self.area as *mut _ as *mut u8;
if Self::xsave_enabled() {
unsafe { core::arch::x86_64::_xsave64(ptr, Self::xsave_mask()) }
} else {
unsafe { core::arch::x86_64::_fxsave64(ptr) }
}
}
#[inline]
pub fn restore(&self) {
let ptr = &self.area as *const _ as *const u8;
if Self::xsave_enabled() {
unsafe { core::arch::x86_64::_xrstor64(ptr, Self::xsave_mask()) }
} else {
unsafe { core::arch::x86_64::_fxrstor64(ptr) }
}
}
pub const fn default() -> Self {
let mut area: XsaveArea = unsafe { core::mem::MaybeUninit::zeroed().assume_init() };
area.legacy.fcw = 0x37f;
area.legacy.ftw = 0xffff;
area.legacy.mxcsr = 0x1f80;
Self { area }
}
}
impl fmt::Debug for ExtendedState {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("ExtendedState")
.field("fxsave_area", &self.area.legacy)
.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",
)
}