diff --git a/kernel/.cargo/config.toml b/kernel/.cargo/config.toml index 1bdbcb2..d96c283 100644 --- a/kernel/.cargo/config.toml +++ b/kernel/.cargo/config.toml @@ -10,4 +10,4 @@ target = "x86_64-unknown-kernel.json" runner = "./run.sh" rustflags = [ "-C", "force-frame-pointers=yes", -] \ No newline at end of file +] diff --git a/kernel/src/x86_64/gdt.rs b/kernel/src/x86_64/gdt.rs index 8ed2a14..bbb8cf2 100644 --- a/kernel/src/x86_64/gdt.rs +++ b/kernel/src/x86_64/gdt.rs @@ -1,7 +1,7 @@ use core::{ arch::asm, fmt::Debug, - mem::offset_of, + mem::{MaybeUninit, offset_of}, ops::{Deref, DerefMut}, }; @@ -468,12 +468,16 @@ impl DerefMut for TssEntry { } } +const PRIVILEGE_STACK_TABLE_SIZE: usize = 3; +const INTERRUPT_STACK_TABLE_SIZE: usize = 7; + #[repr(C, packed(4))] +#[derive(Debug)] pub struct TaskStateSegment { _reserved1: [u8; 4], - pub privilege_stack_table: [u64; 3], + pub privilege_stack_table: [u64; PRIVILEGE_STACK_TABLE_SIZE], _reserved2: [u8; 8], - pub interrupt_stack_table: [u64; 7], + pub interrupt_stack_table: [u64; INTERRUPT_STACK_TABLE_SIZE], _reserved3: [u8; 10], pub iomap_base: u16, } @@ -489,6 +493,13 @@ impl TaskStateSegment { _reserved3: [0; 10], } } + pub fn set_stack(&mut self, index: u8, stack: &'static Stack) { + assert!( + (index as usize) < INTERRUPT_STACK_TABLE_SIZE, + "Interrupt stack table index out of bounds" + ); + self.interrupt_stack_table[index as usize] = stack.top().to_address(); + } } const impl Default for TaskStateSegment { @@ -500,17 +511,35 @@ const impl Default for TaskStateSegment { pub const DF_STACK: u8 = 0; pub const NMI_STACK: u8 = 1; pub const MC_STACK: u8 = 2; +pub const PF_STACK: u8 = 3; + +const STACK_SIZE: usize = super::PAGE_SIZE * 5; +#[repr(align(16))] +pub struct Stack(MaybeUninit<[u8; STACK_SIZE]>); + +impl Stack { + const fn new() -> Self { + Stack(MaybeUninit::uninit()) + } + fn top(&self) -> *const u8 { + unsafe { + (&raw const *self) + .cast::() + .byte_add(super::PAGE_SIZE * 5) + } + } +} pub static TSS: LazyLock = LazyLock::new(|| { let mut tss = TaskStateSegment::new(); - const STACK_SIZE: usize = super::PAGE_SIZE * 5; - static mut STACKS: [[u8; STACK_SIZE]; 3] = [[0; STACK_SIZE]; 3]; + static mut STACKS: [Stack; 4] = [const { Stack::new() }; 4]; - let stack_top = |n: usize| unsafe { STACKS[n].as_ptr().add(STACK_SIZE).to_address() }; + tss.set_stack(DF_STACK, unsafe { &STACKS[DF_STACK as usize] }); + tss.set_stack(NMI_STACK, unsafe { &STACKS[NMI_STACK as usize] }); + tss.set_stack(MC_STACK, unsafe { &STACKS[MC_STACK as usize] }); + tss.set_stack(PF_STACK, unsafe { &STACKS[PF_STACK as usize] }); - tss.interrupt_stack_table[DF_STACK as usize] = stack_top(DF_STACK as usize); - tss.interrupt_stack_table[NMI_STACK as usize] = stack_top(NMI_STACK as usize); - tss.interrupt_stack_table[MC_STACK as usize] = stack_top(MC_STACK as usize); + serial_println!("Initialized TSS {:#?}", tss); tss }); diff --git a/kernel/src/x86_64/idt.rs b/kernel/src/x86_64/idt.rs index cf861eb..51c3fdd 100644 --- a/kernel/src/x86_64/idt.rs +++ b/kernel/src/x86_64/idt.rs @@ -89,6 +89,9 @@ impl Entry { ExceptionVector::NON_MASKABLE_INTERRUPT => { options.set_interrupt_stack_table_index(super::gdt::NMI_STACK); } + ExceptionVector::PAGE_FAULT => { + options.set_interrupt_stack_table_index(super::gdt::PF_STACK); + } ExceptionVector::MACHINE_CHECK => { options.set_interrupt_stack_table_index(super::gdt::MC_STACK); } diff --git a/kernel/tests/stack_overflow.rs b/kernel/tests/stack_overflow.rs index a38b14a..cedb005 100644 --- a/kernel/tests/stack_overflow.rs +++ b/kernel/tests/stack_overflow.rs @@ -25,12 +25,13 @@ pub extern "C" fn main() -> ! { _stack_frame: &mut idt::InterruptStackFrame, _error_code: u64, ) -> ! { - kernel::serial_println!("[ok] Double fault handler called"); + kernel::serial_println!("[ok] Fault handler called"); kernel::testing::exit_qemu(kernel::testing::QemuExitCode::Success) } static IDT: LazyLock = LazyLock::new(|| { let mut idt = InterruptDescriptorTable::new_default(); + idt.double_fault = unsafe { Entry::new( double_fault_handler as *const (), @@ -38,7 +39,19 @@ pub extern "C" fn main() -> ! { idt::EntryOptions::empty_interrupt_gate() .with_present(true) .with_privilege_level(RING0) - .with_interrupt_stack_table_index(DF_STACK), + .with_interrupt_stack_table_index(kernel::x86_64::gdt::DF_STACK), + ) + }; + + // this should fire #PF since we have it installed anways in the default idt + idt.page_fault = unsafe { + Entry::new( + double_fault_handler as *const (), + offset_of!(GlobalDescriptorTable, kernel_code) as u16, + idt::EntryOptions::empty_interrupt_gate() + .with_present(true) + .with_privilege_level(RING0) + .with_interrupt_stack_table_index(kernel::x86_64::gdt::PF_STACK), ) };