diff --git a/kernel/src/main.rs b/kernel/src/main.rs index 485b672..12b956f 100644 --- a/kernel/src/main.rs +++ b/kernel/src/main.rs @@ -46,7 +46,7 @@ extern "C" fn _start() -> ! { kernel::x86_64::paging::get_physical_addr(VirtAddr(_start as *const () as u64)) ); - let pmm = kernel::memory::PhysicalMemoryAllocator::from_memory_map( + let pmm = kernel::memory::PhysicalMemoryManager::from_memory_map( kernel::boot::BOOT_INFO .get() .expect("Boot info not initialized") diff --git a/kernel/src/memory.rs b/kernel/src/memory.rs index 1b55fd3..73f17a6 100644 --- a/kernel/src/memory.rs +++ b/kernel/src/memory.rs @@ -1,4 +1,10 @@ -use core::{alloc::Allocator, cell::Cell, fmt::Debug, num::NonZeroUsize, ptr::NonNull}; +use core::{ + alloc::Allocator, + cell::Cell, + fmt::Debug, + num::{NonZero, NonZeroUsize}, + ptr::NonNull, +}; use bit_field::BitField; use rbtree::RBTree; @@ -9,6 +15,8 @@ pub static HHDM_BASE: OnceLock = OnceLock::new(); pub trait VirtAddrTranslationExt: Sized { fn into_phy_addr(self) -> Option; + + #[expect(clippy::wrong_self_convention)] fn is_mapped(self) -> bool { self.into_phy_addr().is_some() } @@ -129,7 +137,118 @@ pub struct PhysicalMemoryManager { /// /// When a range of pages is freed, we try to locate its buddy and merge /// them into a larger chunk recursively. - buddies: [u64; 40], + buddies: [RBTree; 40], +} + +impl Debug for PhysicalMemoryManager { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("PhysicalMemoryManager") + .field_with("buddies", |f| { + f.debug_list() + .entries(self.buddies.iter().enumerate().map(|(i, tree)| { + struct BuddyDbg<'a> { + index: usize, + tree: &'a RBTree, + } + impl Debug for BuddyDbg<'_> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{:#x}: {:?}", 1u64 << self.index, self.tree) + } + } + BuddyDbg { index: i, tree } + })) + .finish() + }) + .finish() + } +} + +impl PhysicalMemoryManager { + pub fn from_memory_map(memory_map: &[crate::boot::MemoryRegion]) -> Self { + let usable_regions = memory_map + .iter() + .filter(|region| region.region_type.is_usable()); + + let buddies = [(); 40].map(|_| RBTree::default()); + + let mut pmm = PhysicalMemoryManager { buddies }; + + usable_regions.for_each(|region| { + let idx = region.start / crate::x86_64::PAGE_SIZE as u64; + let count = region.length / crate::x86_64::PAGE_SIZE as u64; + + pmm.free_region(idx as usize, count as usize); + }); + + pmm + } + + fn free_region(&mut self, page_index: usize, mut count: usize) { + serial_println!( + "Freeing region: page_index = {:#x}, count = {:#x}", + page_index, + count + ); + while let Some(bit) = NonZero::new(count.isolate_lowest_one()).map(NonZero::get) { + count -= bit; + + let mut bin = bit.trailing_zeros() as usize; + + let mut index = page_index + count; + let mut buddy: NonNull; + loop { + // each bin corresponds to a power-of-two number of pages. + // each chunk of pages has a buddy that is the same size (in the same bin) + + // for example, the chunk [0, 4) has a buddy [4, 8). + let buddy_index = if index & bit == 0 { + // if the chunk is aligned to the size of the chunk, its buddy + // is located at page_index + bit. + index + bit + } else { + // if the chunk is not aligned to the size of the chunk, its + // buddy is located at page_index - bit. + index - bit + }; + + serial_println!( + "looking for buddy: page_index = {:#x}, buddy_index = {:#x}, bin = {}", + index, + buddy_index, + bin + ); + + match self.buddies[bin].remove(PhysicalPageNodeKey::from_page_index(buddy_index)) { + Some(chunk) => { + serial_println!( + "found buddy: page_index = {:#x}, buddy_index = {:#x}, bin = {}", + index, + buddy_index, + bin + ); + buddy = chunk; + index = PhysicalPageNode::page_index(buddy).min(index); + bin += 1; + } + None => break, + } + } + + let node_ptr = PhysicalPageNode::new_from_page_index(index); + serial_println!( + "inserting node: page_index = {:#x}, size = {:#x}, bin = {}", + index, + bit, + bin + ); + let None = self.buddies[bin].insert_node(node_ptr) else { + panic!( + "Attempted to free a region that is already free: page_index = {}, count = {}", + page_index, bit + ) + }; + } + } } pub struct PhysicalMemoryAllocator { @@ -699,6 +818,27 @@ impl Debug for PhysicalPageNode { } impl PhysicalPageNode { + fn phy(&self) -> Option { + PhyAddr::from_hhdm_virt(self) + } + + fn page_index(this: NonNull) -> usize { + let phy = PhyAddr::from_hhdm_virt(VirtAddr::from(this)); + phy.expect("PhysicalPageNode is not in HHDM").page_index() as usize + } + + fn new_from_page_index(page_index: usize) -> NonNull { + let phy = PhyAddr::from_page_index(page_index); + let virt = phy.as_hhdm_virt(); + let ptr = virt.as_mut::(); + + unsafe { + ptr.write(Self::new_red()); + } + + NonNull::new(ptr).unwrap() + } + fn new_red() -> Self { PhysicalPageNode(Cell::new(1 << 120)) } @@ -757,6 +897,26 @@ impl PhysicalPageNode { /// the key does not require any additional bits. struct PhysicalPageNodeKey; +impl Debug for PhysicalPageNodeKey { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{:?}", self.phy()) + } +} + +impl PhysicalPageNodeKey { + fn from_page_index(page_index: usize) -> &'static Self { + let phy = PhyAddr::from_page_index(page_index); + let virt = phy.as_hhdm_virt(); + let ptr = virt.as_ptr::(); + + unsafe { &*ptr } + } + + fn phy(&self) -> Option { + PhyAddr::from_hhdm_virt(self) + } +} + impl Eq for PhysicalPageNodeKey {} impl PartialEq for PhysicalPageNodeKey { diff --git a/kernel/src/x86_64/backtrace.rs b/kernel/src/x86_64/backtrace.rs index 01ea8fe..3640685 100644 --- a/kernel/src/x86_64/backtrace.rs +++ b/kernel/src/x86_64/backtrace.rs @@ -1,3 +1,4 @@ +#![allow(dead_code)] use core::ffi::c_void; use crate::{ @@ -336,12 +337,10 @@ impl Elf64Sym { if let Some(bytes) = crate::limine::EXECUTABLE_FILE_REQUEST .file() .map(LimineFile::bytes) + && let Ok(ehdr) = plain::from_bytes::(bytes) + && let Some(symtab) = ehdr.symtab(bytes) { - if let Ok(ehdr) = plain::from_bytes::(bytes) { - if let Some(symtab) = ehdr.symtab(bytes) { - return symtab; - } - } + return symtab; } &[] }); @@ -375,6 +374,7 @@ impl Elf64Sym { _ => panic!("Invalid bind value"), } } + fn kind(&self) -> Elf64SymType { match self.info & 0xf { 0 => Elf64SymType::Notype, diff --git a/kernel/src/x86_64/gdt.rs b/kernel/src/x86_64/gdt.rs index ee15688..89d5219 100644 --- a/kernel/src/x86_64/gdt.rs +++ b/kernel/src/x86_64/gdt.rs @@ -539,11 +539,15 @@ pub const PF_STACK: u8 = 3; const STACK_SIZE: usize = super::PAGE_SIZE * 5; #[repr(align(4096))] -pub struct Stack(MaybeUninit<[u8; STACK_SIZE]>); +pub struct Stack { + _bytes: MaybeUninit<[u8; STACK_SIZE]>, +} impl Stack { const fn new() -> Self { - Stack(MaybeUninit::uninit()) + Stack { + _bytes: MaybeUninit::uninit(), + } } fn top(&self) -> *const u8 { unsafe {