From c68d3fab140cb1edd774e2c729b4e9395815f508 Mon Sep 17 00:00:00 2001 From: ivajon Date: Fri, 8 May 2026 11:40:51 +0200 Subject: [PATCH] Initial work on paging, also complete ish interrupt registration --- kernel/Cargo.lock | 39 +++++++++ kernel/Cargo.toml | 2 + kernel/src/handler.rs | 25 ++++++ kernel/src/lib.rs | 4 + kernel/src/main.rs | 64 +++++++++++++-- kernel/src/paging.rs | 107 ++++++++++++++++++++++++ uefi/src/memory.rs | 9 +++ x86_drivers/src/gdt.rs | 2 +- x86_drivers/src/idt.rs | 144 ++++++++++++++++++++++++++++++--- x86_drivers/src/idt/vectors.rs | 73 +++++++++++++++++ x86_drivers/src/lib.rs | 5 +- 11 files changed, 456 insertions(+), 18 deletions(-) create mode 100644 kernel/src/handler.rs create mode 100644 kernel/src/paging.rs create mode 100644 x86_drivers/src/idt/vectors.rs diff --git a/kernel/Cargo.lock b/kernel/Cargo.lock index 88df8e8..db2a4d2 100644 --- a/kernel/Cargo.lock +++ b/kernel/Cargo.lock @@ -2,14 +2,53 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "buddyalloc" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5ded974e2422fae8075cb72650eae5b12f076e480eb44930571f6bddf325b31" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "hash32" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" +dependencies = [ + "byteorder", +] + +[[package]] +name = "heapless" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25ba4bd83f9415b58b4ed8dc5714c76e626a105be4646c02630ad730ad3b5aa4" +dependencies = [ + "hash32", + "stable_deref_trait", +] + [[package]] name = "kernel" version = "0.1.0" dependencies = [ + "buddyalloc", + "heapless", "uefi", "x86_drivers", ] +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + [[package]] name = "uefi" version = "0.1.0" diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml index 72ca503..d183d36 100644 --- a/kernel/Cargo.toml +++ b/kernel/Cargo.toml @@ -4,5 +4,7 @@ version = "0.1.0" edition = "2024" [dependencies] +buddyalloc = "0.1.5" +heapless = "0.9.3" uefi = {path = "../uefi"} x86_drivers = {path = "../x86_drivers"} diff --git a/kernel/src/handler.rs b/kernel/src/handler.rs new file mode 100644 index 0000000..c9f05c2 --- /dev/null +++ b/kernel/src/handler.rs @@ -0,0 +1,25 @@ +use x86_drivers::idt::InterruptStackFrame; + +pub extern "x86-interrupt" fn page_fault(f: InterruptStackFrame, err: u64) { + let cr2: u64; + unsafe { + core::arch::asm!("mov {}, cr2", out(reg) cr2, options(nomem, nostack, preserves_flags)); + } + panic!("PAGE FAULT addr={:#x} err={:#x} ip={:#x}", cr2, err, f.ip); +} + +pub extern "x86-interrupt" fn double_fault(f: InterruptStackFrame, _err: u64) -> ! { + panic!("DOUBLE FAULT ip={:#x}", f.ip); +} + +pub extern "x86-interrupt" fn gp_fault(f: InterruptStackFrame, err: u64) { + panic!("GP FAULT err={:#x} ip={:#x}", err, f.ip); +} + +pub extern "x86-interrupt" fn invalid_opcode(f: InterruptStackFrame) { + panic!("INVALID OPCODE ip={:#x}", f.ip); +} + +pub extern "x86-interrupt" fn divide(f: InterruptStackFrame) { + panic!("DIVIDE ip={:#x}", f.ip); +} diff --git a/kernel/src/lib.rs b/kernel/src/lib.rs index 44df477..f373c5d 100644 --- a/kernel/src/lib.rs +++ b/kernel/src/lib.rs @@ -1,5 +1,9 @@ #![no_std] #![deny(clippy::perf, clippy::all, clippy::pedantic)] +#![feature(abi_x86_interrupt)] + +pub mod handler; +pub mod paging; use core::{ mem::MaybeUninit, diff --git a/kernel/src/main.rs b/kernel/src/main.rs index f7aae19..a159c48 100644 --- a/kernel/src/main.rs +++ b/kernel/src/main.rs @@ -4,7 +4,7 @@ use uefi::{EfiMemoryType, EfiSystemTable, ExitBootServicesKey, Handle, UefiStatus}; -use kernel::print; +use kernel::{handler, paging::create_region_map, print}; use x86_drivers::gdt::{DPL, GdtEntry, GdtPointer, GdtSelector}; pub use x86_drivers::prelude::*; @@ -32,10 +32,9 @@ extern "efiapi" fn efi_main(handle: Handle, table: *mut EfiSystemTable) -> UefiS .boot_services() .memory_map(arena) .expect("memory map failed"); - for el in map - .filter_map(|segment| segment.memory_type.interpret().map(|f| (f, segment))) - .filter(|(t, _segment)| *t == EfiMemoryType::ConventionalMemory) - { + + let map = create_region_map::<1024>(map); + for el in map { print!("Map element {:?}\r\n", el); } @@ -57,6 +56,61 @@ extern "efiapi" fn efi_main(handle: Handle, table: *mut EfiSystemTable) -> UefiS ); gdt_init(); print!("Initiated GDT"); + + let mut idt = Idt::new().set_table(); + print!("Set idt :)"); + + named_isr::PageFault::register_handler( + &mut idt, + handler::page_fault, + GDT_KERNEL_CODE_SELECTOR, + Ist::Idx0, + Gate::Interrupt, + DPL::Kernel, + ) + .unwrap(); + + named_isr::DoubleFault::register_handler( + &mut idt, + handler::double_fault, + GDT_KERNEL_CODE_SELECTOR, + Ist::Idx0, + Gate::Interrupt, + DPL::Kernel, + ) + .unwrap(); + + named_isr::GeneralProtectionFault::register_handler( + &mut idt, + handler::gp_fault, + GDT_KERNEL_CODE_SELECTOR, + Ist::Idx0, + Gate::Interrupt, + DPL::Kernel, + ) + .unwrap(); + + named_isr::InvalidOpcode::register_handler( + &mut idt, + handler::invalid_opcode, + GDT_KERNEL_CODE_SELECTOR, + Ist::Idx0, + Gate::Interrupt, + DPL::Kernel, + ) + .unwrap(); + + named_isr::DivisionError::register_handler( + &mut idt, + handler::divide, + GDT_KERNEL_CODE_SELECTOR, + Ist::Idx0, + Gate::Interrupt, + DPL::Kernel, + ) + .unwrap(); + print!("Set all ISRs :)"); + #[allow(clippy::empty_loop)] loop {} } diff --git a/kernel/src/paging.rs b/kernel/src/paging.rs new file mode 100644 index 0000000..8adc63d --- /dev/null +++ b/kernel/src/paging.rs @@ -0,0 +1,107 @@ +use uefi::{EfiMemoryType, PageIter}; + +use crate::print; + +#[repr(C, align(4096))] +pub struct Page { + data: [u8; 4096], +} + +#[derive(Clone, Copy, Debug)] +pub struct Region { + start: *const Page, + count: usize, +} + +impl Region { + pub const fn null() -> Self { + Self { + start: core::ptr::null(), + count: 0, + } + } + + pub const fn new(start: *const Page, count: usize) -> Self { + Self { start, count } + } + + pub const fn size(&self) -> usize { + self.count * core::mem::size_of::() + } + + pub fn end_addr(&self) -> usize { + self.start as usize + self.count * core::mem::size_of::() + } + + pub fn can_coalesce(&self, other: &Self) -> bool { + let end = self.end_addr(); + other.start as usize == end + } + + pub fn coalesce(self, other: Self) -> Self { + // TODO: make type system track this + assert!(self.can_coalesce(&other), "Must be able to coaless regions"); + Self::new(self.start, self.count + other.count) + } +} + +pub fn create_region_map( + iter: PageIter<'_>, +) -> heapless::Vec { + let mut vec = heapless::Vec::<_, BUFFER_SIZE>::from_iter( + iter.filter(|t| { + matches!( + t.memory_type.interpret(), + Some( + EfiMemoryType::ConventionalMemory + | EfiMemoryType::BootServicesCode + | EfiMemoryType::BootServicesData + ) + ) + }) + .map(|t| Region::new(t.physical_start.raw_ptr(), t.number_of_pages as usize)), + ); + + vec.sort_unstable_by_key(|f| f.start); + + for start in (0..(vec.len().saturating_sub(1))).rev() { + let potential_join = start + 1; + let possible = vec[start].can_coalesce(&vec[potential_join]); + + if !possible { + continue; + } + + let joinable = vec.remove(potential_join); + vec[start] = vec[start].coalesce(joinable); + } + + vec +} + +pub struct RegionAllocator { + allocators: heapless::Vec, +} + +pub struct Zone { + region: Region, + allocator: buddyalloc::Heap<24>, +} + +impl RegionAllocator { + pub fn new(regions: heapless::Vec) -> Self { + let allocators = heapless::Vec::from_iter(regions.into_iter().map(|region| Zone { + allocator: unsafe { + buddyalloc::Heap::new( + core::ptr::NonNull::new(region.start.cast_mut().cast()).unwrap(), + region.size(), + ) + .unwrap() + }, + region, + })); + Self { allocators } + } + + pub fn request_pages +} diff --git a/uefi/src/memory.rs b/uefi/src/memory.rs index 0c3b87f..7483883 100644 --- a/uefi/src/memory.rs +++ b/uefi/src/memory.rs @@ -97,3 +97,12 @@ impl TryFrom for EfiMemoryType { Self::from_u32(value).ok_or(value) } } +impl EfiPhysicalAddress { + pub const fn raw_ptr(&self) -> *const T { + self.0 as *const _ + } + + pub const fn raw(&self) -> u64 { + self.0 + } +} diff --git a/x86_drivers/src/gdt.rs b/x86_drivers/src/gdt.rs index 19f4718..3ffc233 100644 --- a/x86_drivers/src/gdt.rs +++ b/x86_drivers/src/gdt.rs @@ -159,7 +159,7 @@ impl Flags { } #[repr(transparent)] -#[derive(Debug)] +#[derive(Debug, Clone, Copy)] pub struct GdtSelector { inner: u16, } diff --git a/x86_drivers/src/idt.rs b/x86_drivers/src/idt.rs index 29b77ee..13df9ed 100644 --- a/x86_drivers/src/idt.rs +++ b/x86_drivers/src/idt.rs @@ -1,38 +1,68 @@ +use core::sync::atomic::{AtomicBool, Ordering}; + use crate::gdt::{DPL, GdtSelector}; +pub mod vectors; + #[repr(C, packed)] +#[must_use] pub struct InterruptTableDescriptor { size: u16, offset: u64, } #[repr(transparent)] +#[derive(Clone, Copy)] +#[must_use] pub struct IdtTableEntry { inner: u128, } pub type ISR = extern "x86-interrupt" fn(InterruptStackFrame) -> (); +pub type ISRWithIdx = extern "x86-interrupt" fn(InterruptStackFrame, u64) -> (); +pub type ISRWithIdxNeverReturns = extern "x86-interrupt" fn(InterruptStackFrame, u64) -> !; #[repr(C)] +#[derive(Clone, Copy)] +#[must_use] pub struct InterruptStackFrame { - ip: usize, - cs: usize, - flags: usize, - sp: usize, - ss: usize, + pub ip: usize, + pub cs: usize, + pub flags: usize, + pub sp: usize, + pub ss: usize, } #[repr(u8)] +#[must_use] pub enum Gate { Interrupt = 0b1110, Trap = 0b1111, } -impl IdtTableEntry { - pub fn new(isr: ISR, selector: GdtSelector, ist: u8, gate: Gate, priority: DPL) -> Self { - let mut inner = 0b0; - let offset = (isr as *const ()) as u64; +#[repr(u8)] +#[must_use] +pub enum Ist { + Idx0 = 0, + Idx1 = 1, + Idx2 = 2, + Idx3 = 3, + Idx4 = 4, + Idx5 = 5, + Idx6 = 6, + Idx7 = 7, +} +impl IdtTableEntry { + #[allow(clippy::cast_possible_truncation)] + const fn build( + offset: u64, + selector: GdtSelector, + ist: Ist, + gate: Gate, + priority: DPL, + ) -> Self { + let mut inner = 0; let bytes = (offset >> 32) as u32; inner |= (bytes as u128) << 64; @@ -44,8 +74,6 @@ impl IdtTableEntry { inner |= (selector.raw() as u128) << 16; - assert!(ist <= 0b111); - inner |= (ist as u128) << 32; inner |= (gate as u128) << 40; inner |= (priority as u128) << 45; @@ -53,4 +81,98 @@ impl IdtTableEntry { Self { inner } } + + pub const fn null() -> Self { + Self { inner: 0 } + } + + pub fn new(isr: ISR, selector: GdtSelector, ist: Ist, gate: Gate, priority: DPL) -> Self { + let offset = (isr as *const ()) as u64; + Self::build(offset, selector, ist, gate, priority) + } + + // TODO: Make the type system check ist valid here. + pub fn with_idx( + isr: ISRWithIdx, + selector: GdtSelector, + ist: Ist, + gate: Gate, + priority: DPL, + ) -> Self { + let offset = (isr as *const ()) as u64; + Self::build(offset, selector, ist, gate, priority) + } +} + +static mut IDT: [IdtTableEntry; 256] = [IdtTableEntry::null(); _]; +static ASSIGNED: [AtomicBool; 256] = [const { AtomicBool::new(false) }; 256]; +#[allow(static_mut_refs)] +const IDT_SIZE: u16 = unsafe { (core::mem::size_of_val(&IDT) - 1) as u16 }; +static IDT_INITIATED: AtomicBool = AtomicBool::new(false); + +#[must_use] +pub struct Idt; + +#[derive(Debug, Clone, Copy)] +#[must_use] +pub enum Error { + InvalidVectorIndex, + VectorAlreadyAssigned, +} + +impl Idt { + /// Registers a new handler + /// + /// # Safety + /// + /// This function is safe assuming that this is the only [`Idt`] that exists. This assumption + /// holds given that the user does not forcibly create one. + pub fn register_handler( + &mut self, + vector: usize, + entry: IdtTableEntry, + ) -> Result<&mut Self, Error> { + if vector >= ASSIGNED.len() { + return Err(Error::InvalidVectorIndex); + } + + if ASSIGNED[vector].swap(true, Ordering::AcqRel) { + return Err(Error::VectorAlreadyAssigned); + } + + unsafe { IDT[vector] = entry }; + + Ok(self) + } + + #[allow(static_mut_refs)] + fn table(&self) -> InterruptTableDescriptor { + let addr = unsafe { IDT.as_ptr() as *const _ as u64 }; + InterruptTableDescriptor { + size: IDT_SIZE, + offset: addr, + } + } +} + +impl Idt { + pub fn set_table(self) -> Idt { + let ptr = self.table(); + unsafe { + core::arch::asm!( + "lidt [{}]", + in(reg) &ptr, + options(readonly,nostack,preserves_flags) + ) + } + Idt + } + + pub fn new() -> Self { + assert!( + !IDT_INITIATED.swap(true, Ordering::AcqRel), + "IDT already initiated" + ); + Self + } } diff --git a/x86_drivers/src/idt/vectors.rs b/x86_drivers/src/idt/vectors.rs new file mode 100644 index 0000000..35d54d2 --- /dev/null +++ b/x86_drivers/src/idt/vectors.rs @@ -0,0 +1,73 @@ +use crate::idt::{ + DPL, Error, Gate, GdtSelector, ISR, ISRWithIdx, ISRWithIdxNeverReturns, Idt, IdtTableEntry, Ist, +}; + +macro_rules! vector { + ($( + $(~$idx_:literal)? + $($idx:literal: $name:ident => $type:ty)? + );*) => { + pub trait Vector { + type FunctionSignature; + const IDX:usize; + fn register_handler(table:&mut Idt, isr: Self::FunctionSignature, selector: GdtSelector, ist: Ist, gate: Gate, priority: DPL) -> Result<&mut Idt,Error>; + } + + pub mod named_isr { + $( + $(pub struct $name;)? + )* + } + + $( + $( + impl Vector for named_isr::$name { + type FunctionSignature = $type; + const IDX:usize = $idx; + + fn register_handler(table:&mut Idt, isr: Self::FunctionSignature, selector: GdtSelector, ist: Ist, gate: Gate, priority: DPL) -> Result<&mut Idt,Error> { + let offset = (isr as *const ()) as u64; + let desc = IdtTableEntry::build(offset, selector, ist, gate, priority); + table.register_handler(Self::IDX, desc) + } + } + )? + + )* + } +} + +vector!( + 0: DivisionError => ISR; + 1: Debug => ISR; + 2: NonMaskableInterrupt => ISR; + 3: Breakpoint => ISR; + 4: Overflow => ISR; + 5: BoundRangeExceeded => ISR; + 6: InvalidOpcode => ISR; + 7: DeviceNotAvailable => ISR; + 8: DoubleFault => ISRWithIdxNeverReturns; + 9: CoprocessorSegmentOverrun => ISR; + 10: InvalidTSS => ISRWithIdx; + 11: SegmentNotPresent => ISRWithIdx; + 12: StackSegmentFault => ISRWithIdx; + 13: GeneralProtectionFault => ISRWithIdx; + 14: PageFault => ISRWithIdx; + ~15; + 16: X87FloatingPointException => ISR; + 17: AlignmentCheck => ISRWithIdx; + 18: MachineCheck => ISR; + 19: SIMDFloatingPointException => ISR; + 20: VirtualizationException => ISR; + 21: ControlProtectionException => ISRWithIdx; + ~22; + ~23; + ~24; + ~25; + ~26; + ~27; + 28: HypervisorInjectionException => ISR; + 29: VMMCommunicationException => ISRWithIdx; + 30: SecurityException => ISRWithIdx; + ~31; +); diff --git a/x86_drivers/src/lib.rs b/x86_drivers/src/lib.rs index 259cc58..214653b 100644 --- a/x86_drivers/src/lib.rs +++ b/x86_drivers/src/lib.rs @@ -23,7 +23,8 @@ clippy::unused_self, clippy::must_use_candidate, clippy::new_without_default, - clippy::cast_possible_truncation + clippy::cast_possible_truncation, + clippy::missing_errors_doc )] pub mod uart; @@ -38,5 +39,7 @@ mod private { pub mod prelude { pub use crate::core::Core; + pub use crate::idt::vectors::*; + pub use crate::idt::*; pub use crate::uart::{InitUart, Uart}; }