diff --git a/src/obkrnl/Cargo.toml b/src/obkrnl/Cargo.toml index 3a79cae9..3b8e5ed3 100644 --- a/src/obkrnl/Cargo.toml +++ b/src/obkrnl/Cargo.toml @@ -5,6 +5,7 @@ edition = "2021" [dependencies] anstyle = { version = "1.0.8", default-features = false } +bitfield-struct = "0.8.0" hashbrown = "0.14.5" macros = { path = "../macros" } obconf = { path = "../obconf" } diff --git a/src/obkrnl/src/aarch64.rs b/src/obkrnl/src/aarch64.rs new file mode 100644 index 00000000..2ae06f2e --- /dev/null +++ b/src/obkrnl/src/aarch64.rs @@ -0,0 +1,3 @@ +pub unsafe fn setup_main_cpu() { + todo!() +} diff --git a/src/obkrnl/src/main.rs b/src/obkrnl/src/main.rs index 9224317f..c18c578a 100644 --- a/src/obkrnl/src/main.rs +++ b/src/obkrnl/src/main.rs @@ -10,6 +10,9 @@ use core::mem::zeroed; use core::panic::PanicInfo; use obconf::{BootEnv, Config}; +#[cfg_attr(target_arch = "aarch64", path = "aarch64.rs")] +#[cfg_attr(target_arch = "x86_64", path = "x86_64.rs")] +mod arch; mod config; mod console; mod context; @@ -37,14 +40,16 @@ extern crate alloc; /// See PS4 kernel entry point for a reference. #[allow(dead_code)] #[cfg_attr(target_os = "none", no_mangle)] -extern "C" fn _start(env: &'static BootEnv, conf: &'static Config) -> ! { +unsafe extern "C" fn _start(env: &'static BootEnv, conf: &'static Config) -> ! { // SAFETY: This function has a lot of restrictions. See Context documentation for more details. - unsafe { crate::config::setup(env, conf) }; + crate::config::setup(env, conf); info!("Starting Obliteration Kernel."); + self::arch::setup_main_cpu(); + // Setup thread0 to represent this thread. - let thread0 = unsafe { Thread::new_bare() }; + let thread0 = Thread::new_bare(); // Initialize foundations. let pmgr = ProcMgr::new(); @@ -52,9 +57,9 @@ extern "C" fn _start(env: &'static BootEnv, conf: &'static Config) -> ! { // Activate CPU context. We use a different mechanism here. The PS4 put all of pcpu at a global // level but we put it on each CPU stack instead. let thread0 = Arc::new(thread0); - let mut cx = unsafe { Context::new(0, thread0, pmgr.clone()) }; + let mut cx = Context::new(0, thread0, pmgr.clone()); - unsafe { cx.activate() }; + cx.activate(); main(pmgr); } diff --git a/src/obkrnl/src/x86_64.rs b/src/obkrnl/src/x86_64.rs new file mode 100644 index 00000000..11e6c631 --- /dev/null +++ b/src/obkrnl/src/x86_64.rs @@ -0,0 +1,96 @@ +use bitfield_struct::bitfield; +use core::arch::global_asm; + +pub unsafe fn setup_main_cpu() { + // Switch GDT from bootloader GDT to our own. + static GDT: [SegmentDescriptor; 3] = [ + // Null descriptor. + SegmentDescriptor::new(), + // Code segment. + SegmentDescriptor::new() + .with_ty(0b1000) // This required somehow although the docs said it is ignored. + .with_s(true) // Same here. + .with_p(true) + .with_l(true), // 64-bit mode. + // Data segment. + SegmentDescriptor::new() + .with_ty(0b0010) // This required somehow although the docs said it is ignored. + .with_s(true) // Same here. + .with_p(true), + ]; + + set_gdtr( + &Gdtr { + limit: (size_of_val(&GDT) - 1).try_into().unwrap(), + addr: GDT.as_ptr(), + }, + SegmentSelector::new().with_si(1), + SegmentSelector::new().with_si(2), + ); +} + +extern "C" { + fn set_gdtr(v: &Gdtr, code: SegmentSelector, data: SegmentSelector); +} + +// See lgdt on the PS4 for a reference. +global_asm!( + "set_gdtr:", + "lgdt qword ptr [rdi]", + "mov ds, dx", + "mov es, dx", + "mov fs, dx", + "mov gs, dx", + "mov ss, dx", + "pop rax", // Return address. + "push rsi", // Code segment selector. + "push rax", + "retfq" // Set CS then return. +); + +/// Raw value of a Global Descriptor-Table Register. +/// +/// See Global Descriptor-Table Register section on AMD64 Architecture Programmer's Manual Volume 2 +/// for details. +#[repr(C, packed)] +struct Gdtr { + limit: u16, + addr: *const SegmentDescriptor, +} + +/// Raw value of a Segment Descriptor. +/// +/// See Legacy Segment Descriptors section on AMD64 Architecture Programmer's Manual Volume 2 for +/// more details. +#[bitfield(u64)] +struct SegmentDescriptor { + limit1: u16, + #[bits(24)] + base1: u32, + #[bits(4)] + ty: u8, + s: bool, + #[bits(2)] + dpl: u8, + p: bool, + #[bits(4)] + limit2: u8, + avl: bool, + l: bool, + db: bool, + g: bool, + base2: u8, +} + +/// Raw value of a Segment Selector (e.g. `CS` and `DS` register). +/// +/// See Segment Selectors section on AMD64 Architecture Programmer's Manual Volume 2 for more +/// details. +#[bitfield(u16)] +struct SegmentSelector { + #[bits(2)] + rpl: u8, + ti: bool, + #[bits(13)] + si: u16, +}