use core::{arch::naked_asm, fmt};
use 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 arg1(&self) -> usize {
self.rsi as _
}
pub const fn arg2(&self) -> usize {
self.rdx as _
}
pub const fn arg3(&self) -> usize {
self.r10 as _
}
pub const fn arg4(&self) -> usize {
self.r8 as _
}
pub const fn arg5(&self) -> usize {
self.r9 as _
}
pub const fn is_user(&self) -> bool {
self.cs & 0b11 == 3
}
}
#[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],
}
static_assertions::const_assert_eq!(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 = "uspace")]
pub gs_base: usize,
#[cfg(feature = "fp-simd")]
pub ext_state: ExtendedState,
#[cfg(feature = "uspace")]
pub cr3: 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(),
#[cfg(feature = "uspace")]
gs_base: 0,
}
}
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: 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(any(feature = "tls", feature = "uspace"))]
unsafe {
self.fs_base = crate::asm::read_thread_pointer();
crate::asm::write_thread_pointer(next_ctx.fs_base);
}
#[cfg(feature = "uspace")]
unsafe {
self.gs_base = x86::msr::rdmsr(x86::msr::IA32_KERNEL_GSBASE) as usize;
x86::msr::wrmsr(x86::msr::IA32_KERNEL_GSBASE, next_ctx.gs_base as u64);
super::gdt::write_tss_rsp0(next_ctx.kstack_top);
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",
)
}