diff --git a/.cargo/config.toml b/.cargo/config.toml index 3b01eb5..cd125e0 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -3,7 +3,7 @@ target = "x86_64-kernel" target-dir = "build/target" [unstable] -build-std = ["core", "compiler_builtins"] +build-std = ["core", "compiler_builtins", "alloc"] build-std-features = ["compiler-builtins-mem"] [env] diff --git a/.gitmodules b/.gitmodules index de08738..d74f31e 100644 --- a/.gitmodules +++ b/.gitmodules @@ -10,3 +10,6 @@ [submodule "lib/lib_serial"] path = lib/lib_serial url = https://git.zxq5.dev/OsDev/FoundryOS-lib_serial.git +[submodule "lib/lib_alloc"] + path = lib/lib_alloc + url = https://git.zxq5.dev/OsDev/FoundryOS-lib_alloc.git diff --git a/Cargo.lock b/Cargo.lock index c575ba9..97d2c0b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -34,6 +34,7 @@ name = "foundry_os" version = "0.1.0" dependencies = [ "cc", + "lib_alloc", "lib_ascii", "lib_framebuffer", "lib_serial", @@ -53,6 +54,10 @@ dependencies = [ "spin", ] +[[package]] +name = "lib_alloc" +version = "0.1.0" + [[package]] name = "lib_application" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index 972ad1a..d9f1b96 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,8 @@ members = [ "lib/lib_serial", "lib/lib_ascii", "kernel", - "lib/lib_application", + "lib/lib_application", + "lib/lib_alloc", ] resolver = "2" diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml index 91a92ca..9958b56 100644 --- a/kernel/Cargo.toml +++ b/kernel/Cargo.toml @@ -8,6 +8,7 @@ limine = "0.3.1" lib_framebuffer = { path = "../lib/lib_framebuffer" } lib_serial = { path = "../lib/lib_serial" } lib_ascii = { path = "../lib/lib_ascii" } +lib_alloc = { path = "../lib/lib_alloc" } x86_64 = "0.15.2" spin = "0.9.8" pic8259 = "0.11.0" diff --git a/kernel/src/arch/x86_64/memory.rs b/kernel/src/arch/x86_64/memory.rs new file mode 100644 index 0000000..5eaf729 --- /dev/null +++ b/kernel/src/arch/x86_64/memory.rs @@ -0,0 +1,137 @@ +use lib_alloc::allocator::FoundryAllocator; +use limine::{memory_map::EntryType, response::MemoryMapResponse}; +use x86_64::{ + addr, + registers::control::Cr3, + structures::paging::{ + page_table::FrameError, FrameAllocator, OffsetPageTable, PageTable, PhysFrame, Size4KiB, + }, + PhysAddr, VirtAddr, +}; + +#[global_allocator] +static ALLOCATOR: FoundryAllocator = FoundryAllocator; + +/// Returns a mutable reference to the current level 4 page table. +/// +/// # Safety +/// +/// The caller must ensure that the level 4 page table is not modified +/// simultaneously. The caller must also ensure that the physical memory offset +/// is correct, to ensure that the correct virtual address is constructed. +unsafe fn active_l4_table(physical_memory_offset: VirtAddr) -> &'static mut PageTable { + let (level_4_frame, _) = Cr3::read(); + + let phys_addr = level_4_frame.start_address(); + let virt = phys_addr.as_u64() + physical_memory_offset.as_u64(); + &mut *(virt as *mut PageTable) +} + +/// Initializes the `OffsetPageTable` for the current CPU architecture. +/// +/// # Safety +/// +/// This function must be called only once and should be called before any +/// memory operations are performed that rely on virtual memory management. +/// The provided `physical_memory_offset` must be accurate to ensure correct +/// translation of physical addresses. +/// +/// # Parameters +/// +/// - `physical_memory_offset`: The offset to convert physical addresses to +/// virtual addresses in the higher-half direct map. +/// +/// # Returns +/// +/// Returns an `OffsetPageTable` that allows for manipulation of the page +/// tables for the current CPU architecture. +pub unsafe fn init(physical_memory_offset: VirtAddr) -> OffsetPageTable<'static> { + let l4_table = active_l4_table(physical_memory_offset); + OffsetPageTable::new(l4_table, physical_memory_offset) +} + +pub(crate) struct FoundryOSFrameAllocator { + memory_map: &'static MemoryMapResponse, + next: usize, +} + +impl FoundryOSFrameAllocator { + /// Creates a new `FoundryOSFrameAllocator` from a memory map. + /// + /// This function takes a reference to a `MemoryMapResponse` and initializes a + /// `FoundryOSFrameAllocator` with it. The `next` field is set to 0, indicating that + /// the first frame to be allocated is the first frame in the memory map. + pub unsafe fn init(memory_map: &'static MemoryMapResponse) -> FoundryOSFrameAllocator { + FoundryOSFrameAllocator { + memory_map, + next: 0, + } + } + + /// An iterator over all usable frames in the memory map. + /// + /// Yields one `PhysFrame` for each available 4KiB frame in the memory map. + /// + /// This function is used to allocate frames for the pagemap. + fn usable_frames(&self) -> impl Iterator { + let regions = self.memory_map.entries().iter(); + let usable_regions = regions.filter(|region| region.entry_type == EntryType::USABLE); + + let addr_ranges = usable_regions.map(|region| region.base..region.base + region.length); + + let frame_addresses = addr_ranges.flat_map(|r| r.step_by(4096)); + + frame_addresses.map(|addr| PhysFrame::from_start_address(PhysAddr::new(addr)).unwrap()) + } +} + +unsafe impl FrameAllocator for FoundryOSFrameAllocator { + /// Allocates a frame from the list of usable frames. + /// + /// This function returns the next available `PhysFrame` from the memory map, + /// if one exists. Once a frame is allocated, the internal counter is incremented + /// to point to the next frame for future allocations. + /// + /// # Returns + /// + /// - `Some(PhysFrame)`: If a usable frame is available. + /// - `None`: If there are no more usable frames to allocate. + + fn allocate_frame(&mut self) -> Option { + let frame = self.usable_frames().nth(self.next); + self.next += 1; + frame + } +} + +// pub unsafe fn translate_addr(addr: VirtAddr, physical_memory_offset: VirtAddr) -> Option { +// translate_addr_inner(addr, physical_memory_offset) +// } + +// fn translate_addr_inner(addr: VirtAddr, physical_memory_offset: VirtAddr) -> Option { +// let (l4_table_frame, _) = Cr3::read(); + +// let table_indexes = [ +// addr.p4_index(), +// addr.p3_index(), +// addr.p2_index(), +// addr.p1_index(), +// ]; +// let mut frame = l4_table_frame; + +// for &i in &table_indexes { +// let virt = physical_memory_offset + frame.start_address().as_u64(); +// let table_ptr: *const PageTable = virt.as_ptr(); +// let table = unsafe { &*table_ptr }; + +// let entry = &table[i]; + +// frame = match entry.frame() { +// Ok(frame) => frame, +// Err(FrameError::FrameNotPresent) => return None, +// Err(FrameError::HugeFrame) => panic!("huge frames are not supported!"), +// }; +// } + +// Some(frame.start_address() + u64::from(addr.page_offset())) +// } diff --git a/kernel/src/arch/x86_64/mod.rs b/kernel/src/arch/x86_64/mod.rs index cd849ac..c8adf09 100644 --- a/kernel/src/arch/x86_64/mod.rs +++ b/kernel/src/arch/x86_64/mod.rs @@ -1,3 +1,7 @@ pub mod gdt; pub mod interrupts; + +pub mod memory; + +pub(crate) mod memmap; diff --git a/kernel/src/lib.rs b/kernel/src/lib.rs index fa3c6dd..dc8d5d9 100644 --- a/kernel/src/lib.rs +++ b/kernel/src/lib.rs @@ -1,12 +1,16 @@ #![no_std] #![feature(abi_x86_interrupt)] +extern crate alloc; + use core::arch::asm; use limine::request::{RequestsEndMarker, RequestsStartMarker}; use limine::BaseRevision; pub use lib_ascii::{print, print_log, println, println_log, WRITER}; pub use lib_serial::{serial_print, serial_println, serial_read}; +use x86_64::structures::paging::Translate; +use x86_64::{PhysAddr, VirtAddr}; mod arch; @@ -28,6 +32,8 @@ static _END_MARKER: RequestsEndMarker = RequestsEndMarker::new(); #[panic_handler] fn rust_panic(_info: &core::panic::PanicInfo) -> ! { + println!("Kernel panic: {}", _info); + serial_println!("Kernel panic: {}", _info); hcf(); } @@ -49,11 +55,19 @@ pub fn boot() -> Result<(), &'static str> { return Err("base revision not supported"); } + use arch::x86_64::{gdt, interrupts, memmap, memory}; + + let memory_map = memmap::get_memory_map(); lib_serial::init()?; - arch::x86_64::gdt::init(); - arch::x86_64::interrupts::init_idt(); + gdt::init(); + interrupts::init_idt(); + + let mut frame_allocator = unsafe { memory::FoundryOSFrameAllocator::init(memory_map) }; x86_64::instructions::interrupts::enable(); + let physical_memory_offset = VirtAddr::new(*memmap::PHYSICAL_MEMORY_OFFSET); + let l4_table = unsafe { memory::init(physical_memory_offset) }; + Ok(()) } diff --git a/lib/lib_alloc b/lib/lib_alloc new file mode 160000 index 0000000..3aeb5d6 --- /dev/null +++ b/lib/lib_alloc @@ -0,0 +1 @@ +Subproject commit 3aeb5d66c8e7f3adb4e7ff01d89c01b45a89c159