diff --git a/Cargo.lock b/Cargo.lock index dd6facc8..75ac89d4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -597,6 +597,7 @@ dependencies = [ "mycelium-trace", "mycelium-util", "mycotest", + "pin-project", "proptest", "rand_core", "raw-cpuid", diff --git a/hal-x86_64/Cargo.toml b/hal-x86_64/Cargo.toml index ea66edc2..8c7a4bca 100644 --- a/hal-x86_64/Cargo.toml +++ b/hal-x86_64/Cargo.toml @@ -16,6 +16,7 @@ hal-core = { path = "../hal-core" } mycelium-util = { path = "../util" } mycelium-trace = { path = "../trace" } mycotest = { path = "../mycotest"} +pin-project = "1" rand_core = { version = "0.6.4", default_features = false, optional = true } raw-cpuid = "10.6.0" tracing = { git = "https://github.com/tokio-rs/tracing", default_features = false, features = ["attributes"] } diff --git a/hal-x86_64/src/cpu/local.rs b/hal-x86_64/src/cpu/local.rs index 405a6114..1e1c97a3 100644 --- a/hal-x86_64/src/cpu/local.rs +++ b/hal-x86_64/src/cpu/local.rs @@ -11,6 +11,7 @@ use mycelium_util::{fmt, sync::Lazy}; #[repr(C)] #[derive(Debug)] +#[pin_project] pub struct GsLocalData { /// This *must* be the first field of the local data struct, because we read /// from `gs:0x0` to get the local data's address. @@ -19,6 +20,7 @@ pub struct GsLocalData { processor: Processor, /// Because this struct is self-referential, it may not be `Unpin`. _must_pin: PhantomPinned, + /// Arbitrary user data. /// // TODO(eliza): consider storing this in some kind of heap allocated tree diff --git a/src/arch/x86_64.rs b/src/arch/x86_64.rs index 41ac221c..c8c3bc82 100644 --- a/src/arch/x86_64.rs +++ b/src/arch/x86_64.rs @@ -14,6 +14,7 @@ mod framebuf; pub mod interrupt; mod oops; pub mod pci; +mod segment; pub use self::{ boot::ArchInfo, oops::{oops, Oops}, diff --git a/src/arch/x86_64/interrupt.rs b/src/arch/x86_64/interrupt.rs index 0bb4084e..dd65972b 100644 --- a/src/arch/x86_64/interrupt.rs +++ b/src/arch/x86_64/interrupt.rs @@ -2,17 +2,13 @@ use super::{oops, Oops}; use core::sync::atomic::{AtomicUsize, Ordering}; use hal_core::{interrupt, VAddr}; pub use hal_x86_64::interrupt::*; -use hal_x86_64::{ - cpu::Ring, - segment::{self, Gdt}, - task, -}; +use hal_x86_64::{cpu::Ring, task}; use maitake::time; use mycelium_util::{fmt, sync}; #[tracing::instrument] pub fn enable_exceptions() { - init_gdt(); + segmentation::init_gdt(); tracing::info!("GDT initialized!"); Controller::init::(); @@ -30,32 +26,6 @@ pub fn enable_hardware_interrupts(acpi: Option<&acpi::InterruptModel>) { tracing::info!(granularity = ?TIMER_INTERVAL, "global timer initialized") } -// TODO(eliza): put this somewhere good. -type StackFrame = [u8; 4096]; - -// chosen by fair dice roll, guaranteed to be random -const DOUBLE_FAULT_STACK_SIZE: usize = 8; - -/// Stack used by ISRs during a double fault. -/// -/// /!\ EXTREMELY SERIOUS WARNING: this has to be `static mut` or else it -/// will go in `.bss` and we'll all die or something. -static mut DOUBLE_FAULT_STACK: [StackFrame; DOUBLE_FAULT_STACK_SIZE] = - [[0; 4096]; DOUBLE_FAULT_STACK_SIZE]; - -static TSS: sync::Lazy = sync::Lazy::new(|| { - tracing::trace!("initializing TSS.."); - let mut tss = task::StateSegment::empty(); - tss.interrupt_stacks[Idt::DOUBLE_FAULT_IST_OFFSET] = unsafe { - // safety: asdf - VAddr::of(&DOUBLE_FAULT_STACK).offset(DOUBLE_FAULT_STACK_SIZE as i32) - }; - tracing::debug!(?tss, "TSS initialized"); - tss -}); - -static GDT: sync::InitOnce = sync::InitOnce::uninitialized(); - const TIMER_INTERVAL: time::Duration = time::Duration::from_millis(10); pub(super) static TIMER: time::Timer = time::Timer::new(TIMER_INTERVAL); @@ -112,62 +82,6 @@ impl hal_core::interrupt::Handlers for InterruptHandlers { } } -#[inline] -#[tracing::instrument(level = tracing::Level::DEBUG)] -pub(super) fn init_gdt() { - tracing::trace!("initializing GDT..."); - let mut gdt = Gdt::new(); - - // add one kernel code segment - let code_segment = segment::Descriptor::code().with_ring(Ring::Ring0); - let code_selector = gdt.add_segment(code_segment); - tracing::debug!( - descriptor = fmt::alt(code_segment), - selector = fmt::alt(code_selector), - "added code segment" - ); - - // add the TSS. - - let tss = segment::SystemDescriptor::tss(&TSS); - let tss_selector = gdt.add_sys_segment(tss); - tracing::debug!( - tss.descriptor = fmt::alt(tss), - tss.selector = fmt::alt(tss_selector), - "added TSS" - ); - - // all done! long mode barely uses this thing lol. - GDT.init(gdt); - - // load the GDT - let gdt = GDT.get(); - tracing::debug!(GDT = ?gdt, "GDT initialized"); - gdt.load(); - - tracing::trace!("GDT loaded"); - - // set new segment selectors - let code_selector = segment::Selector::current_cs(); - tracing::trace!(code_selector = fmt::alt(code_selector)); - unsafe { - // set the code segment selector - code_selector.set_cs(); - - // in protected mode and long mode, the code segment, stack segment, - // data segment, and extra segment must all have base address 0 and - // limit `2^64`, since actual segmentation is not used in those modes. - // therefore, we must zero the SS, DS, and ES registers. - segment::Selector::null().set_ss(); - segment::Selector::null().set_ds(); - segment::Selector::null().set_es(); - - task::StateSegment::load_tss(tss_selector); - } - - tracing::debug!("segment selectors set"); -} - mycotest::decl_test! { fn interrupts_work() -> mycotest::TestResult { let test_interrupt_fires = TEST_INTERRUPT_WAS_FIRED.load(Ordering::Acquire); diff --git a/src/arch/x86_64/segmentation.rs b/src/arch/x86_64/segmentation.rs new file mode 100644 index 00000000..35d838a4 --- /dev/null +++ b/src/arch/x86_64/segmentation.rs @@ -0,0 +1,59 @@ +use hal_x86_64::segment::{self, Gdt}; +use mycelium_util::spin::Mutex; + +pub(super) static GDT: Mutex> = Mutex::new(Gdt::new()); + +#[tracing::instrument(level = tracing::Level::DEBUG)] +pub(super) fn init_gdt() { + tracing::trace!("initializing GDT..."); + let mut gdt = GDT.lock(); + + // add one kernel code segment + let code_segment = segment::Descriptor::code().with_ring(Ring::Ring0); + let code_selector = gdt.add_segment(code_segment); + tracing::debug!( + descriptor = fmt::alt(code_segment), + selector = fmt::alt(code_selector), + "added code segment" + ); + + // add the TSS. + + let tss = segment::SystemDescriptor::tss(&TSS); + let tss_selector = gdt.add_sys_segment(tss); + tracing::debug!( + tss.descriptor = fmt::alt(tss), + tss.selector = fmt::alt(tss_selector), + "added TSS" + ); + + // all done! long mode barely uses this thing lol. + GDT.init(gdt); + + // load the GDT + let gdt = GDT.get(); + tracing::debug!(GDT = ?gdt, "GDT initialized"); + gdt.load(); + + tracing::trace!("GDT loaded"); + + // set new segment selectors + let code_selector = segment::Selector::current_cs(); + tracing::trace!(code_selector = fmt::alt(code_selector)); + unsafe { + // set the code segment selector + code_selector.set_cs(); + + // in protected mode and long mode, the code segment, stack segment, + // data segment, and extra segment must all have base address 0 and + // limit `2^64`, since actual segmentation is not used in those modes. + // therefore, we must zero the SS, DS, and ES registers. + segment::Selector::null().set_ss(); + segment::Selector::null().set_ds(); + segment::Selector::null().set_es(); + + task::StateSegment::load_tss(tss_selector); + } + + tracing::debug!("segment selectors set"); +}