From 2186b829aa55e30174ac7c1ffbaf8b0825b7f002 Mon Sep 17 00:00:00 2001 From: zxq5 Date: Tue, 4 Mar 2025 01:28:39 +0000 Subject: [PATCH] - made improvements to memory code (refactored) - started on improvements to the page frame allocator. it should be able to provide a usable page for any given virtual memory address requested. --- kernel/src/arch/x86_64/cpu/apic.rs | 2 +- .../x86_64/memory/allocation/heap_alloc.rs | 12 ++- .../src/arch/x86_64/memory/allocation/mod.rs | 1 + .../x86_64/memory/allocation/page_alloc.rs | 77 +++++++++++++++++++ .../x86_64/memory/allocation/stack_alloc.rs | 28 +++++-- .../memory/{memory_map.rs => mapping.rs} | 0 kernel/src/arch/x86_64/memory/mod.rs | 71 +++-------------- kernel/src/arch/x86_64/memory/units.rs | 49 ++++++++++++ kernel/src/lib.rs | 10 +-- 9 files changed, 175 insertions(+), 75 deletions(-) create mode 100644 kernel/src/arch/x86_64/memory/allocation/page_alloc.rs rename kernel/src/arch/x86_64/memory/{memory_map.rs => mapping.rs} (100%) create mode 100644 kernel/src/arch/x86_64/memory/units.rs diff --git a/kernel/src/arch/x86_64/cpu/apic.rs b/kernel/src/arch/x86_64/cpu/apic.rs index d7bcd97..25ad2e0 100644 --- a/kernel/src/arch/x86_64/cpu/apic.rs +++ b/kernel/src/arch/x86_64/cpu/apic.rs @@ -1,6 +1,6 @@ use core::arch::x86_64::__cpuid; -use crate::arch::x86_64::memory::memory_map::PHYSICAL_MEMORY_OFFSET; +use crate::arch::x86_64::memory::mapping::PHYSICAL_MEMORY_OFFSET; use crate::serial_print; use x86_64::{ PhysAddr, VirtAddr, diff --git a/kernel/src/arch/x86_64/memory/allocation/heap_alloc.rs b/kernel/src/arch/x86_64/memory/allocation/heap_alloc.rs index 0f50818..96c5274 100644 --- a/kernel/src/arch/x86_64/memory/allocation/heap_alloc.rs +++ b/kernel/src/arch/x86_64/memory/allocation/heap_alloc.rs @@ -2,20 +2,19 @@ use core::alloc::{GlobalAlloc, Layout}; use core::ptr; use spin::{Mutex, MutexGuard}; use x86_64::structures::paging::{Size4KiB, mapper::MapToError}; +use crate::arch::x86_64::memory::{HEAP_SIZE, HEAP_VIRTUAL_SPACE}; /// We are currently using a linked list heap allocator which uses our underlying page allocator. #[global_allocator] /// This is now Rust's global allocator, so we can use stuff requiring heap allocations. static ALLOCATOR: Locked = Locked::new(FoundryAllocator::new()); -pub const HEAP_START: usize = 0x4444_4444_0000; -pub const HEAP_SIZE: usize = 1024 * 1024 * 1024; /// Sets up the heap using the backing page frame allocator. pub unsafe fn init_heap() -> Result<(), MapToError> { unsafe { // code to allocate frames is now done in the page fault interrupt handler! - ALLOCATOR.lock().init(HEAP_START, HEAP_SIZE); + ALLOCATOR.lock().init(HEAP_VIRTUAL_SPACE, HEAP_SIZE); Ok(()) } } @@ -36,7 +35,7 @@ impl Locked { } } -const BLOCK_SIZES: &[usize] = &[8, 16, 32, 64, 128, 256, 512, 1024, 2048]; +const BLOCK_SIZES: &[usize] = &[8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096]; struct ListNode { next: Option<&'static mut ListNode>, @@ -61,6 +60,11 @@ impl FoundryAllocator { fallback: Locked::new(FoundryFallbackAllocator::new()), } } + /// Initializes the fallback allocator with the given heap start address and size. + /// + /// # Safety + /// + /// This function is unsafe because it does not check whether the given heap start address and size are valid. pub unsafe fn init(&mut self, heap_start: usize, heap_size: usize) { unsafe { self.fallback.lock().init(heap_start, heap_size); diff --git a/kernel/src/arch/x86_64/memory/allocation/mod.rs b/kernel/src/arch/x86_64/memory/allocation/mod.rs index 74b817c..6877073 100644 --- a/kernel/src/arch/x86_64/memory/allocation/mod.rs +++ b/kernel/src/arch/x86_64/memory/allocation/mod.rs @@ -1,2 +1,3 @@ pub mod heap_alloc; pub mod stack_alloc; +pub(crate) mod page_alloc; diff --git a/kernel/src/arch/x86_64/memory/allocation/page_alloc.rs b/kernel/src/arch/x86_64/memory/allocation/page_alloc.rs new file mode 100644 index 0000000..3fa9aae --- /dev/null +++ b/kernel/src/arch/x86_64/memory/allocation/page_alloc.rs @@ -0,0 +1,77 @@ +use x86_64::structures::paging::{FrameAllocator, Mapper, Page, PageTableFlags, PhysFrame, Size4KiB}; +use limine::response::MemoryMapResponse; +use spin::Mutex; +use limine::memory_map::EntryType; +use x86_64::{PhysAddr, VirtAddr}; +use x86_64::structures::paging::mapper::MapToError; +use crate::arch::x86_64::memory::{FRAME_ALLOCATOR, OFFSET_PAGE_TABLE}; + +pub 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 fn init(memory_map: &'static MemoryMapResponse) { + FRAME_ALLOCATOR.call_once(|| Mutex::new(Self { + memory_map, + next: 0, + })); + } + + pub fn count_usable_frames(&self) -> u32 { + self.usable_frames().count() as u32 + } + + pub fn available_memory(&self) -> u64 { + ( self.memory_map.entries().len() * 4096 ) as u64 // multiply the number of physical frames by 4096 to get the memory size + } + + /// 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 + use<> { + 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()) + } + + + + fn allocate_page(&mut self, start_addr: VirtAddr, flags: PageTableFlags) -> Result<(), MapToError> { + let page = Page::from_start_address(start_addr).map_err(|_| MapToError::FrameAllocationFailed)?; + let frame = self.allocate_frame().ok_or(MapToError::FrameAllocationFailed)?; + let _ = unsafe { + let mut mapper = OFFSET_PAGE_TABLE.get().unwrap().lock(); + mapper.map_to(page, frame, flags, &mut *FRAME_ALLOCATOR.get().unwrap().lock())? + }; + Ok(()) + } +} + +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 + } +} \ No newline at end of file diff --git a/kernel/src/arch/x86_64/memory/allocation/stack_alloc.rs b/kernel/src/arch/x86_64/memory/allocation/stack_alloc.rs index d6a7c3b..513b9fe 100644 --- a/kernel/src/arch/x86_64/memory/allocation/stack_alloc.rs +++ b/kernel/src/arch/x86_64/memory/allocation/stack_alloc.rs @@ -1,9 +1,10 @@ use x86_64::structures::paging::{mapper, FrameAllocator, Mapper, Page, Size4KiB}; use x86_64::VirtAddr; +use crate::arch::x86_64::memory::STACK_VIRTUAL_SPACE; fn reserve_stack_memory(size_in_pages: u64) -> Page { use core::sync::atomic::{AtomicU64, Ordering}; - static STACK_ALLOC_NEXT: AtomicU64 = AtomicU64::new(0x_5555_5555_0000); + static STACK_ALLOC_NEXT: AtomicU64 = AtomicU64::new(STACK_VIRTUAL_SPACE as u64); let start_addr = VirtAddr::new(STACK_ALLOC_NEXT.fetch_add( size_in_pages * Page::::SIZE, Ordering::Relaxed, @@ -12,17 +13,32 @@ fn reserve_stack_memory(size_in_pages: u64) -> Page { .expect("`STACK_ALLOC_NEXT` not page aligned") } +/// Allocates a stack in the virtual address space, mapped to physical pages. +/// +/// This function allocates a stack in the virtual address space, mapped to physical pages. +/// The stack is allocated as a sequence of pages, with the first page allocated as a guard page. +/// The stack is then mapped to the allocated physical frame. +/// +/// The function takes the size of the stack in pages, a mutable reference to a mapper, and a +/// mutable reference to a frame allocator. It returns a `Result` containing a `StackBounds` +/// struct, which contains the start and end virtual addresses of the allocated stack. +/// +/// # Safety +/// +/// This function is unsafe because it maps physical frames to virtual addresses without any +/// protection. This can lead to bugs if the physical frames are not correctly allocated, or if the +/// virtual addresses are not correctly aligned. +/// +/// # Panics +/// +/// This function will panic if the allocation of the physical frame fails. pub unsafe fn alloc_stack( size_in_pages: u64, mapper: &mut impl Mapper, frame_allocator: &mut impl FrameAllocator, ) -> Result> { unsafe { use x86_64::structures::paging::PageTableFlags as Flags; - - - - - + let guard_page = reserve_stack_memory(size_in_pages + 1); let stack_start = guard_page + 1; let stack_end = stack_start + size_in_pages; diff --git a/kernel/src/arch/x86_64/memory/memory_map.rs b/kernel/src/arch/x86_64/memory/mapping.rs similarity index 100% rename from kernel/src/arch/x86_64/memory/memory_map.rs rename to kernel/src/arch/x86_64/memory/mapping.rs diff --git a/kernel/src/arch/x86_64/memory/mod.rs b/kernel/src/arch/x86_64/memory/mod.rs index c7d2d7e..fe6d18b 100644 --- a/kernel/src/arch/x86_64/memory/mod.rs +++ b/kernel/src/arch/x86_64/memory/mod.rs @@ -1,14 +1,20 @@ pub mod allocation; -pub mod memory_map; +pub mod mapping; +pub mod units; -// use lib_alloc::allocator::FoundryAllocator; -use limine::{memory_map::EntryType, response::MemoryMapResponse}; use spin::{Mutex, Once}; use x86_64::{ - PhysAddr, VirtAddr, - registers::control::Cr3, - structures::paging::{FrameAllocator, OffsetPageTable, PageTable, PhysFrame, Size4KiB}, + registers::control::Cr3, structures::paging::{FrameAllocator, OffsetPageTable, PageTable} + , + VirtAddr, }; +use allocation::page_alloc::FoundryOSFrameAllocator; +use units::MemoryUnits::*; + +pub const STACK_VIRTUAL_SPACE: usize = 0x5555_5555_0000; // start address of the memory space where we store allocated stacks +pub const HEAP_VIRTUAL_SPACE: usize = 0x4444_4444_0000; // start address of heap allocated memory +pub const HEAP_SIZE: usize = MiB(1).to_bytes(); + pub static FRAME_ALLOCATOR: Once> = Once::new(); pub static OFFSET_PAGE_TABLE: Once> = Once::new(); @@ -53,56 +59,3 @@ pub fn init_page_table(physical_memory_offset: VirtAddr) { } } -pub 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) { unsafe { - FRAME_ALLOCATOR.call_once(|| Mutex::new(Self { - memory_map, - next: 0, - })); - }} - - pub fn count_usable_frames(&self) -> u32 { - self.usable_frames().count() as u32 - } - - /// 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 + use<> { - 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 - } -} \ No newline at end of file diff --git a/kernel/src/arch/x86_64/memory/units.rs b/kernel/src/arch/x86_64/memory/units.rs new file mode 100644 index 0000000..4d24d50 --- /dev/null +++ b/kernel/src/arch/x86_64/memory/units.rs @@ -0,0 +1,49 @@ +pub enum MemoryUnits { + B(usize), + KiB(usize), + MiB(usize), + GiB(usize), +} + +impl MemoryUnits { + pub const fn to_bytes(&self) -> usize { + match self { + Self::B(b) => *b, + Self::KiB(kib) => *kib * 1024, + Self::MiB(mib) => *mib * 1024 * 1024, + Self::GiB(gib) => *gib * 1024 * 1024 * 1024, + } + } + + pub const fn from_bytes(bytes: usize) -> Self { + if bytes < 1024 { + Self::B(bytes) + } else if bytes < 1024 * 1024 { + Self::KiB(bytes / 1024) + } else if bytes < 1024 * 1024 * 1024 { + Self::MiB(bytes / (1024 * 1024)) + } else { + Self::GiB(bytes / (1024 * 1024 * 1024)) + } + } + + pub const fn convert(&mut self) { + match self { + Self::B(b) if *b > 1024 => *self = Self::KiB(*b / 1024), + Self::KiB(kib) if *kib > 1024 => *self = Self::MiB(*kib / 1024), + Self::MiB(mib) if *mib > 1024 => *self = Self::GiB(*mib / 1024), + _ => (), + } + } +} + +impl core::fmt::Display for MemoryUnits { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + Self::B(b) => write!(f, "{} B", b), + Self::KiB(kib) => write!(f, "{} KiB", kib), + Self::MiB(mib) => write!(f, "{} MiB", mib), + Self::GiB(gib) => write!(f, "{} GiB", gib), + } + } +} \ No newline at end of file diff --git a/kernel/src/lib.rs b/kernel/src/lib.rs index f9f4b7d..20340e1 100644 --- a/kernel/src/lib.rs +++ b/kernel/src/lib.rs @@ -17,11 +17,11 @@ use crate::{ prelude::*, }; use arch::x86_64::memory::allocation::heap_alloc::init_heap; -use arch::x86_64::memory::memory_map; +use arch::x86_64::memory::mapping; use core::arch::asm; use limine::BaseRevision; use x86_64::VirtAddr; -use crate::arch::x86_64::memory::FoundryOSFrameAllocator; +use arch::x86_64::memory::allocation::page_alloc::FoundryOSFrameAllocator; pub mod arch; pub mod resources; @@ -67,7 +67,7 @@ pub fn boot() -> Result<(), &'static str> { use arch::x86_64::{gdt, interrupts}; - let memory_map = memory_map::get_memory_map(); + let memory_map = mapping::get_memory_map(); print_log!(" Initialising Serial... "); if arch::x86_64::drivers::serial::init().is_err() { @@ -85,12 +85,12 @@ pub fn boot() -> Result<(), &'static str> { println_log!("[Success]"); print_log!(" Initialising Memory Subsystem... "); - let physical_memory_offset = VirtAddr::new(*memory_map::PHYSICAL_MEMORY_OFFSET); + let physical_memory_offset = VirtAddr::new(*mapping::PHYSICAL_MEMORY_OFFSET); init_page_table(physical_memory_offset); println_log!("[Success]"); print_log!(" Setting Up Page Table... "); - unsafe { FoundryOSFrameAllocator::init(memory_map) }; + FoundryOSFrameAllocator::init(memory_map); println_log!("[Success]"); print_log!(" Initialising Heap... ");