#![cfg(target_pointer_width = "64")] use rmm::{ Arch, BuddyAllocator, BumpAllocator, EmulateArch, Flusher, FrameAllocator, FrameCount, MemoryArea, PageFlags, PageFlushAll, PageMapper, PageTable, PhysicalAddress, TableKind, VirtualAddress, GIGABYTE, KILOBYTE, MEGABYTE, TERABYTE, }; use std::marker::PhantomData; pub fn format_size(size: usize) -> String { if size >= 2 * TERABYTE { format!("{} TB", size / TERABYTE) } else if size >= 2 * GIGABYTE { format!("{} GB", size / GIGABYTE) } else if size >= 2 * MEGABYTE { format!("{} MB", size / MEGABYTE) } else if size >= 2 * KILOBYTE { format!("{} KB", size / KILOBYTE) } else { format!("{} B", size) } } #[allow(dead_code)] unsafe fn dump_tables(table: PageTable) { unsafe { let level = table.level(); for i in 0..A::PAGE_ENTRIES { if level == 0 { if let Some(entry) = table.entry(i) { if entry.present() { let base = table.entry_base(i).unwrap(); println!( "0x{:X}: 0x{:X}", base.data(), entry.address().unwrap().data() ); } } } else { if let Some(next) = table.next(i) { dump_tables(next); } } } } } pub struct SlabNode { next: PhysicalAddress, count: usize, phantom: PhantomData, } impl SlabNode { pub fn new(next: PhysicalAddress, count: usize) -> Self { Self { next, count, phantom: PhantomData, } } pub fn empty() -> Self { Self::new(PhysicalAddress::new(0), 0) } pub unsafe fn insert(&mut self, phys: PhysicalAddress) { unsafe { let virt = A::phys_to_virt(phys); A::write(virt, self.next); self.next = phys; self.count += 1; } } pub unsafe fn remove(&mut self) -> Option { unsafe { if self.count > 0 { let phys = self.next; let virt = A::phys_to_virt(phys); self.next = A::read(virt); self.count -= 1; Some(phys) } else { None } } } } pub struct SlabAllocator { //TODO: Allow allocations up to maximum pageable size nodes: [SlabNode; 4], phantom: PhantomData, } impl SlabAllocator { pub unsafe fn new(areas: &'static [MemoryArea], offset: usize) -> Self { unsafe { let mut allocator = Self { nodes: [ SlabNode::empty(), SlabNode::empty(), SlabNode::empty(), SlabNode::empty(), ], phantom: PhantomData, }; // Add unused areas to free lists let mut area_offset = offset; for area in areas.iter() { if area_offset < area.size { let area_base = area.base.add(area_offset); let area_size = area.size - area_offset; allocator.free(area_base, area_size); area_offset = 0; } else { area_offset -= area.size; } } allocator } } pub unsafe fn allocate(&mut self, size: usize) -> Option { unsafe { for level in 0..A::PAGE_LEVELS - 1 { let level_shift = level * A::PAGE_ENTRY_SHIFT + A::PAGE_SHIFT; let level_size = 1 << level_shift; if size <= level_size { if let Some(base) = self.nodes[level].remove() { self.free(base.add(size), level_size - size); return Some(base); } } } None } } //TODO: This causes fragmentation, since neighbors are not identified //TODO: remainders less than PAGE_SIZE will be lost pub unsafe fn free(&mut self, mut base: PhysicalAddress, mut size: usize) { unsafe { for level in (0..A::PAGE_LEVELS - 1).rev() { let level_shift = level * A::PAGE_ENTRY_SHIFT + A::PAGE_SHIFT; let level_size = 1 << level_shift; while size >= level_size { println!("Add {:X} {}", base.data(), format_size(level_size)); self.nodes[level].insert(base); base = base.add(level_size); size -= level_size; } } } } pub unsafe fn remaining(&mut self) -> usize { let mut remaining = 0; for level in (0..A::PAGE_LEVELS - 1).rev() { let level_shift = level * A::PAGE_ENTRY_SHIFT + A::PAGE_SHIFT; let level_size = 1 << level_shift; remaining += self.nodes[level].count * level_size; } remaining } } unsafe fn new_tables(areas: &'static [MemoryArea]) { unsafe { // First, calculate how much memory we have let mut size = 0; for area in areas.iter() { size += area.size; } println!("Memory: {}", format_size(size)); // Create a basic allocator for the first pages let mut bump_allocator = BumpAllocator::::new(areas, 0); { // Map all physical areas at PHYS_OFFSET let mut mapper = PageMapper::::create(TableKind::Kernel, &mut bump_allocator) .expect("failed to create Mapper"); for area in areas.iter() { for i in 0..area.size / A::PAGE_SIZE { let phys = area.base.add(i * A::PAGE_SIZE); let virt = A::phys_to_virt(phys); let flush = mapper .map_phys(virt, phys, PageFlags::::new().write(true)) .expect("failed to map page to frame"); flush.ignore(); // Not the active table } } // Use the new table mapper.make_current(); } // Create the physical memory map let offset = bump_allocator.offset(); println!("Permanently used: {}", format_size(offset)); let mut allocator = BuddyAllocator::::new(bump_allocator).unwrap(); for i in 0..16 { { let phys_opt = allocator.allocate_one(); println!("page {}: {:X?}", i, phys_opt); if i % 3 == 0 { if let Some(phys) = phys_opt { println!("free {}: {:X?}", i, phys_opt); allocator.free_one(phys); } } } { let phys_opt = allocator.allocate(FrameCount::new(16)); println!("page*16 {}: {:X?}", i, phys_opt); if i % 2 == 0 { if let Some(phys) = phys_opt { println!("free*16 {}: {:X?}", i, phys_opt); allocator.free(phys, FrameCount::new(16)); } } } } let mut mapper = PageMapper::::current(TableKind::Kernel, &mut allocator); let mut flush_all = PageFlushAll::new(); for i in 0..16 { let virt = VirtualAddress::new(MEGABYTE + i * A::PAGE_SIZE); let flush = mapper .map(virt, PageFlags::::new().user(true).write(true)) .expect("failed to map page"); flush_all.consume(flush); } flush_all.flush(); let mut flush_all = PageFlushAll::new(); for i in 0..16 { let virt = VirtualAddress::new(MEGABYTE + i * A::PAGE_SIZE); let flush = mapper.unmap(virt, false).expect("failed to unmap page"); flush_all.consume(flush); } flush_all.flush(); let usage = allocator.usage(); println!("Allocator usage:"); println!( " Used: {}", format_size(usage.used().data() * A::PAGE_SIZE) ); println!( " Free: {}", format_size(usage.free().data() * A::PAGE_SIZE) ); println!( " Total: {}", format_size(usage.total().data() * A::PAGE_SIZE) ); } } unsafe fn inner() { unsafe { let areas = A::init(); // Debug table //dump_tables(PageTable::::top()); new_tables::(areas); //dump_tables(PageTable::::top()); for i in &[1, 2, 4, 8, 16, 32] { let phys = PhysicalAddress::new(i * MEGABYTE); let virt = A::phys_to_virt(phys); // Test read println!( "0x{:X} (0x{:X}) = 0x{:X}", virt.data(), phys.data(), A::read::(virt) ); // Test write A::write::(virt, 0x5A); // Test read println!( "0x{:X} (0x{:X}) = 0x{:X}", virt.data(), phys.data(), A::read::(virt) ); } } } fn main() { unsafe { inner::(); } }