- 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.
This commit is contained in:
2025-03-04 01:28:39 +00:00
parent f502104a6e
commit 2186b829aa
9 changed files with 175 additions and 75 deletions
+1 -1
View File
@@ -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,
@@ -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<FoundryAllocator> = 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<Size4KiB>> {
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<T> Locked<T> {
}
}
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);
@@ -1,2 +1,3 @@
pub mod heap_alloc;
pub mod stack_alloc;
pub(crate) mod page_alloc;
@@ -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<Item = PhysFrame> + 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<Size4KiB>> {
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<Size4KiB> 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<PhysFrame> {
let frame = self.usable_frames().nth(self.next);
self.next += 1;
frame
}
}
@@ -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::<Size4KiB>::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<Size4KiB>,
frame_allocator: &mut impl FrameAllocator<Size4KiB>,
) -> Result<StackBounds, mapper::MapToError<Size4KiB>> { 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;
+12 -59
View File
@@ -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<Mutex<FoundryOSFrameAllocator>> = Once::new();
pub static OFFSET_PAGE_TABLE: Once<Mutex<OffsetPageTable>> = 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<Item = PhysFrame> + 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<Size4KiB> 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<PhysFrame> {
let frame = self.usable_frames().nth(self.next);
self.next += 1;
frame
}
}
+49
View File
@@ -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),
}
}
}
+5 -5
View File
@@ -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... ");