use super::gdt::{GDTEntry, GDT}; use super::interrupt::InterruptControl; use super::trap::TrapContext; use core::arch::asm; use core::marker::PhantomPinned; use core::mem::size_of; use core::pin::Pin; use eonix_preempt::PreemptGuard; use eonix_sync_base::LazyLock; #[eonix_percpu::define_percpu] static LOCAL_CPU: LazyLock = LazyLock::new(CPU::new); #[repr(C)] #[derive(Debug, Clone, Copy)] #[allow(non_camel_case_types)] struct TSS_SP { low: u32, high: u32, } #[repr(C)] pub(crate) struct TSS { _reserved1: u32, rsp: [TSS_SP; 3], _reserved2: u32, _reserved3: u32, ist: [TSS_SP; 7], _reserved4: u32, _reserved5: u32, _reserved6: u16, iomap_base: u16, _pinned: PhantomPinned, } #[derive(Debug, Clone)] pub enum UserTLS { /// TODO: This is not used yet. #[allow(dead_code)] TLS64(u64), TLS32 { base: u64, desc: GDTEntry, }, } /// Architecture-specific cpu status data. pub struct CPU { cpuid: usize, gdt: GDT, tss: TSS, interrupt: InterruptControl, } impl UserTLS { /// # Return /// Returns the TLS descriptor and the index of the TLS segment. pub fn new32(base: u32, limit: u32, is_limit_in_pages: bool) -> (Self, u32) { let flags = if is_limit_in_pages { 0xc } else { 0x4 }; ( Self::TLS32 { base: base as u64, desc: GDTEntry::new(base, limit, 0xf2, flags), }, 7, ) } } impl CPU { pub fn new() -> Self { let (interrupt_control, cpuid) = InterruptControl::new(); Self { cpuid, gdt: GDT::new(), tss: TSS::new(), interrupt: interrupt_control, } } /// Load GDT and TSS in place. /// /// # Safety /// Make sure preemption and interrupt are disabled before calling this function. pub(crate) unsafe fn init(mut self: Pin<&mut Self>) { let tss = &self.as_ref().get_ref().tss; let tss_addr = tss as *const _ as u64; let mut gdt = unsafe { // SAFETY: We don't move the field out. self.as_mut().map_unchecked_mut(|me| &mut me.gdt) }; unsafe { // SAFETY: We don't move `gdt` out. gdt.as_mut().get_unchecked_mut().set_tss(tss_addr as u64); } gdt.load(); let mut interrupt = unsafe { // SAFETY: We don't move the field out. self.as_mut().map_unchecked_mut(|me| &mut me.interrupt) }; // SAFETY: `self` is pinned, so are its fields. interrupt.as_mut().setup_idt(); interrupt.as_mut().setup_timer(); } /// Bootstrap all CPUs. /// This should only be called on the BSP. pub unsafe fn bootstrap_cpus(&self) { self.interrupt.send_sipi(); } pub unsafe fn load_interrupt_stack(self: Pin<&mut Self>, rsp: u64) { unsafe { self.map_unchecked_mut(|me| &mut me.tss) .set_rsp0(rsp + size_of::() as u64); } } pub fn set_tls32(self: Pin<&mut Self>, user_tls: &UserTLS) { let UserTLS::TLS32 { desc, base } = user_tls else { unimplemented!("TLS64 is not supported yet") }; unsafe { // SAFETY: We don't move the GDT object. self.get_unchecked_mut().gdt.set_tls32(*desc); } const IA32_KERNEL_GS_BASE: u32 = 0xc0000102; wrmsr(IA32_KERNEL_GS_BASE, *base); } pub fn cpuid(&self) -> usize { self.cpuid } pub fn end_of_interrupt(self: Pin<&mut Self>) { unsafe { // SAFETY: We don't move the `interrupt` field out. self.map_unchecked_mut(|me| &mut me.interrupt) .end_of_interrupt(); } } pub fn local() -> PreemptGuard> { unsafe { // SAFETY: We pass the reference into a `PreemptGuard`, which ensures // that preemption is disabled. PreemptGuard::new(Pin::new_unchecked(LOCAL_CPU.as_mut().get_mut())) } } } impl TSS { pub fn new() -> Self { Self { _reserved1: 0, rsp: [TSS_SP { low: 0, high: 0 }; 3], _reserved2: 0, _reserved3: 0, ist: [TSS_SP { low: 0, high: 0 }; 7], _reserved4: 0, _reserved5: 0, _reserved6: 0, iomap_base: 0, _pinned: PhantomPinned, } } pub fn set_rsp0(self: Pin<&mut Self>, rsp: u64) { unsafe { // SAFETY: We don't move the TSS object. let me = self.get_unchecked_mut(); me.rsp[0].low = rsp as u32; me.rsp[0].high = (rsp >> 32) as u32; } } } #[inline(always)] pub fn halt() { unsafe { asm!("hlt", options(att_syntax, nostack)); } } #[inline(always)] pub fn rdmsr(msr: u32) -> u64 { let edx: u32; let eax: u32; unsafe { asm!( "rdmsr", in("ecx") msr, out("eax") eax, out("edx") edx, options(att_syntax), ); } (edx as u64) << 32 | eax as u64 } #[inline(always)] pub fn wrmsr(msr: u32, value: u64) { let eax = value as u32; let edx = (value >> 32) as u32; unsafe { asm!( "wrmsr", in("ecx") msr, in("eax") eax, in("edx") edx, options(att_syntax), ); } }