From a98324c3b480cecfc20be7f1a31b131616df670b Mon Sep 17 00:00:00 2001 From: pjht Date: Sun, 7 Jul 2024 08:01:52 -0500 Subject: [PATCH] Switch to a buddy allocator for physical memory --- Cargo.lock | 35 +++++++- Cargo.toml | 2 + src/physical_memory.rs | 189 ++++++++++++++++++++++++++++------------- src/qemu_exit.rs | 3 +- src/virtual_memory.rs | 2 +- 5 files changed, 170 insertions(+), 61 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 722837c..0ace970 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -50,6 +50,15 @@ version = "0.11.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a35ba5100c2431e20b924c8103c2cf8adb919ed9880f625e8770c3cb9d1b06aa" +[[package]] +name = "buddy_system_allocator" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d44d578cadd17312c75e7d0ef489361f160ace58f7139aa32001fee1a51b89b5" +dependencies = [ + "spin", +] + [[package]] name = "cfg-if" version = "1.0.0" @@ -71,6 +80,17 @@ version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" +[[package]] +name = "derive-try-from-primitive" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "302ccf094df1151173bb6f5a2282fcd2f45accd5eae1bdf82dcbfefbc501ad5c" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "elf" version = "0.7.4" @@ -107,7 +127,9 @@ name = "kernel" version = "0.1.0" dependencies = [ "bootloader_api", + "buddy_system_allocator", "crossbeam-queue", + "derive-try-from-primitive", "elf", "hashbrown", "intrusive-collections", @@ -266,6 +288,17 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "syn" version = "2.0.66" @@ -371,5 +404,5 @@ checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.66", ] diff --git a/Cargo.toml b/Cargo.toml index 1382cdb..76300bd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,3 +22,5 @@ slab = { version = "0.4.9", default-features = false } intrusive-collections = "0.9.6" elf = { version = "0.7.4", default-features = false } x86_64 = "0.15.1" +buddy_system_allocator = "0.9.1" +derive-try-from-primitive = { version = "1.0.0", default-features = false } diff --git a/src/physical_memory.rs b/src/physical_memory.rs index fe629a2..5112e94 100644 --- a/src/physical_memory.rs +++ b/src/physical_memory.rs @@ -1,75 +1,148 @@ -use crate::{bootinfo::BOOTINFO, dbg, println, virtual_memory::AsVirt}; +use crate::{bootinfo::BOOTINFO, println, virtual_memory::{AsVirt, PHYS_OFFSET}}; use bootloader_api::info::MemoryRegionKind; -use core::mem; +use core::{alloc::Layout, ptr::NonNull}; +use derive_try_from_primitive::TryFromPrimitive; +use linked_list_allocator::hole::HoleList; use spin::{Lazy, Mutex}; -use tap::Tap; use x86_64::{ structures::paging::{FrameAllocator, FrameDeallocator, PhysFrame, Size4KiB}, PhysAddr, }; -type FrameIterator = impl Iterator; - -#[derive(Default)] -struct FrameStack(Option<(&'static mut FrameStack, PhysFrame)>); -pub struct FrameManager { - stack: FrameStack, - iter: FrameIterator, +#[repr(u32)] +#[derive(Copy, Clone, Debug, TryFromPrimitive)] +enum EfiMemoryTypes { + Reserved, + LoaderCode, + LoaderData, + BootServicesCode, + BootServicesData, + RuntimeServicesCode, + RuntimeServicesData, + Conventional, + Unusable, + ACPIReclaim, + ACPIMemoryNVS, + MMIO, + MMIOPortSpace, + PalCode, + Persistent, } -unsafe impl FrameAllocator for FrameManager { - fn allocate_frame(&mut self) -> Option { - self.iter.next().or_else(|| self.stack.pop()) - } +pub struct PhysicalMemory { + alloc: HoleList, + frames_allocated: usize, + frames_freed: usize, } -impl FrameStack { - /// # Safety - /// The frame must be unique - unsafe fn push(&mut self, frame: PhysFrame) { - self.0 = Some(( - unsafe { &mut *frame.as_virt_ptr::().tap_mut(|x| x.write(mem::take(self))) }, - frame, - )); - } +unsafe impl Send for PhysicalMemory {} +unsafe impl Sync for PhysicalMemory {} - fn pop(&mut self) -> Option { - let (head, frame) = self.0.take()?; - *self = mem::take(head); - Some(frame) - } -} - -impl FrameDeallocator for FrameManager { - unsafe fn deallocate_frame(&mut self, frame: PhysFrame) { - unsafe { self.stack.push(frame) } - } -} - -pub static PHYSICAL_MEMORY: Lazy> = Lazy::new(|| { - let region_iter = - BOOTINFO.memory_regions.iter().filter(|region| region.kind == MemoryRegionKind::Usable); - let frame_iter = region_iter - .clone() - .flat_map(|region| { - ((region.start >> 12) + if region.start & 0xFFF > 0 { 1 } else { 0 }) - ..(region.end >> 12) - }) - .map(|num| PhysFrame::from_start_address(PhysAddr::new(num << 12)).unwrap()); - - #[allow(clippy::cast_precision_loss)] - let mut mem_size = region_iter - .map(|region| { - 4096 * ((region.end >> 12) - - ((region.start >> 12) + if region.start & 0xFFF > 0 { 1 } else { 0 })) - }) - .sum::() as f64; +fn format_byte_count(count: usize) -> (f64, &'static str) { + let mut count = count as f64; let mut prefix = 0; - while mem_size >= 1024.0 { - mem_size /= 1024.0; + while count >= 1024.0 { + count /= 1024.0; prefix += 1; } let prefix = ["", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei"][prefix]; - println!("[PMM] Initialized, found {:.2} {}B of memory", mem_size, prefix); - Mutex::new(FrameManager { stack: FrameStack(None), iter: frame_iter }) + (count, prefix) +} + +impl PhysicalMemory { + pub fn print_stats(&self) { + let (fmtd_alloced, alloced_pfx) = format_byte_count(self.frames_allocated * 4096); + let (fmtd_freed, freed_pfx) = format_byte_count(self.frames_freed * 4096); + let (fmtd_total, total_pfx) = + format_byte_count((self.frames_allocated - self.frames_freed) * 4096); + println!( + "[PMM] {:2} {}B allocated, {:2} {}B freed ({:2} {}B total)", + fmtd_alloced, alloced_pfx, fmtd_freed, freed_pfx, fmtd_total, total_pfx + ); + } +} + +const FRAME_LAYOUT: Layout = unsafe { Layout::from_size_align_unchecked(4096, 4096) }; + +unsafe impl FrameAllocator for PhysicalMemory { + fn allocate_frame(&mut self) -> Option { + self.frames_allocated += 1; + self.alloc.allocate_first_fit(FRAME_LAYOUT).ok().map(|(ptr, _)| { + PhysFrame::from_start_address(PhysAddr::new( + (ptr.as_ptr() as u64) - PHYS_OFFSET.as_u64(), + )) + .unwrap() + }) + } +} + +impl FrameDeallocator for PhysicalMemory { + unsafe fn deallocate_frame(&mut self, frame: PhysFrame) { + self.frames_freed += 1; + unsafe { self.alloc.deallocate(NonNull::new(frame.as_virt_ptr()).unwrap(), FRAME_LAYOUT) }; + } +} + +pub static PHYSICAL_MEMORY: Lazy> = Lazy::new(|| { + println!("[PMM] Bootloader reports the following regions:"); + let mut region_iter = BOOTINFO.memory_regions.iter().peekable(); + let mut total_mem = 0; + let mut usable_mem = 0; + let mut alloc = HoleList::empty(); + loop { + let mut region = if let Some(region) = region_iter.next() { + region.clone() + } else { + break; + }; + loop { + if let Some(next_region) = region_iter.peek() { + if (next_region.kind == region.kind) && (next_region.start == region.end) { + region.end = next_region.end; + region_iter.next(); + } else { + break; + } + } else { + break; + } + } + let (fmtd_size, pfx) = format_byte_count((region.end - region.start) as usize); + if let MemoryRegionKind::UnknownUefi(efi_type) = region.kind { + println!( + "[PMM] Efi{:?}: {:#x} - {:#x} ({:2} {}B)", + EfiMemoryTypes::try_from(efi_type).unwrap(), + region.start, + region.end, + fmtd_size, + pfx + ); + } else { + println!( + "[PMM] {:?}: {:#x} - {:#x} ({:2} {}B)", + region.kind, region.start, region.end, fmtd_size, pfx + ); + } + total_mem += region.end - region.start; + if region.kind == MemoryRegionKind::Usable { + region.end = region.end & !(0xFFF); + if region.start & 0xFFF != 0 { + region.start = (region.start & !(0xFFF)) + 0x1000; + } + usable_mem += region.end - region.start; + unsafe { + alloc.deallocate( + NonNull::new(PhysAddr::new(region.start).as_virt_ptr()).unwrap(), + Layout::from_size_align((region.end - region.start) as usize, 4096).unwrap(), + ); + } + } + } + let (fmtd_usable, usable_pfx) = format_byte_count(usable_mem as usize); + let (fmtd_total, total_pfx) = format_byte_count(total_mem as usize); + println!( + "[PMM] Initialized, found {:.2} {}B of usable memory, {:2} {}B total", + fmtd_usable, usable_pfx, fmtd_total, total_pfx + ); + Mutex::new(PhysicalMemory { alloc, frames_allocated: 0, frames_freed: 0 }) }); diff --git a/src/qemu_exit.rs b/src/qemu_exit.rs index e152882..5ff08be 100644 --- a/src/qemu_exit.rs +++ b/src/qemu_exit.rs @@ -1,8 +1,9 @@ use x86_64::instructions::port::Port; -use crate::kernel_heap::HEAP; +use crate::{kernel_heap::HEAP, physical_memory::PHYSICAL_MEMORY}; pub fn exit_qemu() -> ! { + PHYSICAL_MEMORY.lock().print_stats(); HEAP.print_stats(); unsafe { Port::new(0xf4).write(0u32); diff --git a/src/virtual_memory.rs b/src/virtual_memory.rs index 33c7a97..d477a45 100644 --- a/src/virtual_memory.rs +++ b/src/virtual_memory.rs @@ -105,7 +105,7 @@ impl AsVirt for PhysAddr { } } -static PHYS_OFFSET: Lazy = +pub static PHYS_OFFSET: Lazy = Lazy::new(|| VirtAddr::new(BOOTINFO.physical_memory_offset.into_option().unwrap())); pub struct ASpaceMutex(Mutex);