diff --git a/Cargo.toml b/Cargo.toml index 5580d869da..adafa2a7dc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,9 +6,6 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] - [features] default = ["std"] std = [] diff --git a/src/allocator/frame/buddy.rs b/src/allocator/frame/buddy.rs new file mode 100644 index 0000000000..945b02be96 --- /dev/null +++ b/src/allocator/frame/buddy.rs @@ -0,0 +1,229 @@ +use core::{ + marker::PhantomData, + mem, +}; + +use crate::{ + Arch, + BumpAllocator, + FrameAllocator, + MemoryArea, + PhysicalAddress, + VirtualAddress, +}; + +#[derive(Clone, Copy, Debug)] +#[repr(packed)] +struct BuddyEntry { + base: PhysicalAddress, + size: usize, + map: PhysicalAddress, +} + +impl BuddyEntry { + pub fn empty() -> Self { + Self { + base: PhysicalAddress::new(0), + size: 0, + map: PhysicalAddress::new(0), + } + } +} + +#[derive(Clone, Copy, Debug)] +#[repr(packed)] +struct BuddyMapFooter { + next: PhysicalAddress, + //TODO: index of last known free bit +} + +pub struct BuddyAllocator { + table_phys: PhysicalAddress, + table_virt: VirtualAddress, + clear_frees: bool, + phantom: PhantomData, +} + +impl BuddyAllocator { + const BUDDY_ENTRIES: usize = A::PAGE_SIZE / mem::size_of::(); + const MAP_PAGE_BYTES: usize = (A::PAGE_SIZE - mem::size_of::()); + const MAP_PAGE_BITS: usize = Self::MAP_PAGE_BYTES * 8; + + pub unsafe fn new(mut bump_allocator: BumpAllocator, clear_frees: bool) -> Option { + // Allocate buddy table + let table_phys = bump_allocator.allocate(1)?; + let table_virt = A::phys_to_virt(table_phys); + for i in 0 .. (A::PAGE_SIZE / mem::size_of::()) { + let virt = table_virt.add(i * mem::size_of::()); + A::write(virt, BuddyEntry::empty()); + } + + let mut allocator = Self { + table_phys, + table_virt, + clear_frees, + phantom: PhantomData, + }; + + // Add areas to buddy table, combining areas when possible + for area in bump_allocator.areas().iter() { + for i in 0 .. (A::PAGE_SIZE / mem::size_of::()) { + let virt = table_virt.add(i * mem::size_of::()); + let mut entry = A::read::(virt); + let inserted = if area.base.add(area.size) == entry.base { + // Combine entry at start + entry.base = area.base; + entry.size += area.size; + true + } else if area.base == entry.base.add(entry.size) { + // Combine entry at end + entry.size += area.size; + true + } else if entry.size == 0 { + // Create new entry + entry.base = area.base; + entry.size = area.size; + true + } else { + false + }; + if inserted { + A::write(virt, entry); + break; + } + } + } + + //TODO: sort areas? + + // Allocate buddy maps + for i in 0 .. Self::BUDDY_ENTRIES { + let virt = table_virt.add(i * mem::size_of::()); + let mut entry = A::read::(virt); + if entry.size > 0 { + println!("{}: {:X?}", i, entry); + + let pages = entry.size / A::PAGE_SIZE; + println!(" pages: {}", pages); + let map_pages = (pages + (Self::MAP_PAGE_BITS - 1)) / Self::MAP_PAGE_BITS; + println!(" map pages: {}", map_pages); + + for _ in 0 .. map_pages { + let map_phys = bump_allocator.allocate(1)?; + let map_virt = A::phys_to_virt(map_phys); + for i in 0..Self::MAP_PAGE_BYTES { + A::write(map_virt.add(i), 0); + } + A::write(map_virt.add(Self::MAP_PAGE_BYTES), BuddyMapFooter { + next: entry.map, + }); + entry.map = map_phys; + } + + A::write(virt, entry); + } + } + + // Mark unused areas as free + let mut area_offset = bump_allocator.offset(); + for area in bump_allocator.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; + } + } + + Some(allocator) + } +} + +impl FrameAllocator for BuddyAllocator { + unsafe fn allocate(&mut self, size: usize) -> Option { + //TODO: support other sizes + if size != A::PAGE_SIZE { + return None; + } + + for i in 0 .. Self::BUDDY_ENTRIES { + let virt = self.table_virt.add(i * mem::size_of::()); + let entry = A::read::(virt); + + //TODO: improve performance + let mut map_phys = entry.map; + let mut offset = 0; + while map_phys.data() != 0 { + let map_virt = A::phys_to_virt(map_phys); + for i in 0 .. Self::MAP_PAGE_BYTES { + let map_byte_virt = map_virt.add(i); + let mut value: u8 = A::read(map_byte_virt); + if (value & u8::MAX) != 0 { + for bit in 0..8 { + if (value & (1 << bit)) != 0 { + value &= !(1 << bit); + A::write(map_byte_virt, value); + let page_phys = entry.base.add(offset + bit * A::PAGE_SIZE); + return Some(page_phys); + } + } + } + offset += A::PAGE_SIZE * 8; + } + + let footer = A::read::(map_virt); + map_phys = footer.next; + } + } + None + } + + unsafe fn free(&mut self, base: PhysicalAddress, size: usize) { + if self.clear_frees { + // Zero freed pages for security, also ensures all allocs are zerod + //TODO: improve performance + //TODO: assumes linear physical mapping + let mut zero_virt = A::phys_to_virt(base); + let zero_end = zero_virt.add(size); + while zero_virt < zero_end { + A::write(zero_virt, 0usize); + zero_virt = zero_virt.add(mem::size_of::()); + } + } + + for i in 0 .. Self::BUDDY_ENTRIES { + let virt = self.table_virt.add(i * mem::size_of::()); + let entry = A::read::(virt); + if base >= entry.base && base.add(size) <= entry.base.add(entry.size) { + //TODO: Correct logic + let pages = size / A::PAGE_SIZE; + for page in 0 .. pages { + let page_base = base.add(page * A::PAGE_SIZE); + let index = (page_base.data() - entry.base.data()) / A::PAGE_SIZE; + let mut map_page = index / Self::MAP_PAGE_BITS; + let map_bit = index % Self::MAP_PAGE_BITS; + + //TODO: improve performance + let mut map_phys = entry.map; + loop { + if map_phys.data() == 0 { unimplemented!() } + let map_virt = A::phys_to_virt(map_phys); + if map_page == 0 { + let map_byte_virt = map_virt.add(map_bit / 8); + let mut value: u8 = A::read(map_byte_virt); + value |= 1 << (map_bit % 8); + A::write(map_byte_virt, value); + break; + } else { + let footer = A::read::(map_virt); + map_phys = footer.next; + map_page -= 1; + } + } + } + } + } + } +} diff --git a/src/allocator/frame/bump.rs b/src/allocator/frame/bump.rs new file mode 100644 index 0000000000..330a21af85 --- /dev/null +++ b/src/allocator/frame/bump.rs @@ -0,0 +1,55 @@ +use core::marker::PhantomData; + +use crate::{ + Arch, + FrameAllocator, + MemoryArea, + PhysicalAddress, +}; + +pub struct BumpAllocator { + areas: &'static [MemoryArea], + offset: usize, + phantom: PhantomData, +} + +impl BumpAllocator { + pub fn new(areas: &'static [MemoryArea], offset: usize) -> Self { + Self { + areas, + offset, + phantom: PhantomData, + } + } + + pub fn areas(&self) -> &'static [MemoryArea] { + self.areas + } + + pub fn offset(&self) -> usize { + self.offset + } +} + +impl FrameAllocator for BumpAllocator { + unsafe fn allocate(&mut self, count: usize) -> Option { + //TODO: support allocation of multiple pages + if count != 1 { + return None; + } + + let mut offset = self.offset; + for area in self.areas.iter() { + if offset < area.size { + self.offset += A::PAGE_SIZE; + return Some(area.base.add(offset)); + } + offset -= area.size; + } + None + } + + unsafe fn free(&mut self, address: PhysicalAddress, count: usize) { + unimplemented!(); + } +} diff --git a/src/allocator/frame/mod.rs b/src/allocator/frame/mod.rs new file mode 100644 index 0000000000..7dc20163a3 --- /dev/null +++ b/src/allocator/frame/mod.rs @@ -0,0 +1,12 @@ +use crate::PhysicalAddress; + +pub use self::buddy::*; +pub use self::bump::*; + +mod buddy; +mod bump; + +pub trait FrameAllocator { + unsafe fn allocate(&mut self, count: usize) -> Option; + unsafe fn free(&mut self, address: PhysicalAddress, count: usize); +} diff --git a/src/allocator/mod.rs b/src/allocator/mod.rs new file mode 100644 index 0000000000..0027153038 --- /dev/null +++ b/src/allocator/mod.rs @@ -0,0 +1,3 @@ +pub use self::frame::*; + +mod frame; diff --git a/src/lib.rs b/src/lib.rs index d72240bac5..f100557a34 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,13 +2,12 @@ #![feature(asm)] pub use crate::{ + allocator::*, arch::*, - page::{ - PageEntry, - PageTable - }, + page::*, }; +mod allocator; mod arch; mod page; diff --git a/src/main.rs b/src/main.rs index 1c2f96b3c5..2564a17623 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,9 +4,13 @@ use rmm::{ GIGABYTE, TERABYTE, Arch, + BuddyAllocator, + BumpAllocator, EmulateArch, + FrameAllocator, MemoryArea, PageEntry, + PageMapper, PageTable, PhysicalAddress, VirtualAddress, @@ -49,34 +53,6 @@ unsafe fn dump_tables(table: PageTable) { } } -pub struct BumpAllocator { - areas: &'static [MemoryArea], - offset: usize, - phantom: PhantomData, -} - -impl BumpAllocator { - pub fn new(areas: &'static [MemoryArea], offset: usize) -> Self { - Self { - areas, - offset, - phantom: PhantomData, - } - } - - pub fn allocate(&mut self) -> Option { - let mut offset = self.offset; - for area in self.areas.iter() { - if offset < area.size { - self.offset += A::PAGE_SIZE; - return Some(area.base.add(offset)); - } - offset -= area.size; - } - None - } -} - pub struct SlabNode { next: PhysicalAddress, count: usize, @@ -190,267 +166,6 @@ impl SlabAllocator { } } -#[derive(Clone, Copy, Debug)] -#[repr(packed)] -pub struct BuddyEntry { - pub base: PhysicalAddress, - pub size: usize, - pub map: PhysicalAddress, -} - -impl BuddyEntry { - pub fn empty() -> Self { - Self { - base: PhysicalAddress::new(0), - size: 0, - map: PhysicalAddress::new(0), - } - } -} - -#[derive(Clone, Copy, Debug)] -#[repr(packed)] -pub struct BuddyMapFooter { - pub next: PhysicalAddress, - //TODO: index of last known free bit -} - -pub struct BuddyAllocator { - table_phys: PhysicalAddress, - table_virt: VirtualAddress, - clear_frees: bool, - phantom: PhantomData, -} - -impl BuddyAllocator { - const BUDDY_ENTRIES: usize = A::PAGE_SIZE / mem::size_of::(); - const MAP_PAGE_BYTES: usize = (A::PAGE_SIZE - mem::size_of::()); - const MAP_PAGE_BITS: usize = Self::MAP_PAGE_BYTES * 8; - - pub unsafe fn new(areas: &'static [MemoryArea], offset: usize, clear_frees: bool) -> Option { - // First, we need an allocator, so we can allocate the buddy tables - // Since the tables are static, we can use the bump allocator - let mut bump_allocator = BumpAllocator::::new(areas, offset); - - // Allocate buddy table - let table_phys = bump_allocator.allocate()?; - let table_virt = A::phys_to_virt(table_phys); - for i in 0 .. (A::PAGE_SIZE / mem::size_of::()) { - let virt = table_virt.add(i * mem::size_of::()); - A::write(virt, BuddyEntry::empty()); - } - - let mut allocator = Self { - table_phys, - table_virt, - clear_frees, - phantom: PhantomData, - }; - - // Add areas to buddy table, combining areas when possible - for area in areas.iter() { - for i in 0 .. (A::PAGE_SIZE / mem::size_of::()) { - let virt = table_virt.add(i * mem::size_of::()); - let mut entry = A::read::(virt); - let inserted = if area.base.add(area.size) == entry.base { - // Combine entry at start - entry.base = area.base; - entry.size += area.size; - true - } else if area.base == entry.base.add(entry.size) { - // Combine entry at end - entry.size += area.size; - true - } else if entry.size == 0 { - // Create new entry - entry.base = area.base; - entry.size = area.size; - true - } else { - false - }; - if inserted { - A::write(virt, entry); - break; - } - } - } - - //TODO: sort areas? - - // Allocate buddy maps - for i in 0 .. Self::BUDDY_ENTRIES { - let virt = table_virt.add(i * mem::size_of::()); - let mut entry = A::read::(virt); - if entry.size > 0 { - println!("{}: {:X?}", i, entry); - - let pages = entry.size / A::PAGE_SIZE; - println!(" pages: {}", pages); - let map_pages = (pages + (Self::MAP_PAGE_BITS - 1)) / Self::MAP_PAGE_BITS; - println!(" map pages: {}", map_pages); - - for _ in 0 .. map_pages { - let map_phys = bump_allocator.allocate()?; - let map_virt = A::phys_to_virt(map_phys); - for i in 0..Self::MAP_PAGE_BYTES { - A::write(map_virt.add(i), 0); - } - A::write(map_virt.add(Self::MAP_PAGE_BYTES), BuddyMapFooter { - next: entry.map, - }); - entry.map = map_phys; - } - - A::write(virt, entry); - } - } - - // Mark unused areas as free - let mut area_offset = bump_allocator.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; - } - } - - Some(allocator) - } - - pub unsafe fn allocate(&mut self, size: usize) -> Option { - //TODO: support other sizes - if size != A::PAGE_SIZE { - return None; - } - - for i in 0 .. Self::BUDDY_ENTRIES { - let virt = self.table_virt.add(i * mem::size_of::()); - let entry = A::read::(virt); - - //TODO: improve performance - let mut map_phys = entry.map; - let mut offset = 0; - while map_phys.data() != 0 { - let map_virt = A::phys_to_virt(map_phys); - for i in 0 .. Self::MAP_PAGE_BYTES { - let map_byte_virt = map_virt.add(i); - let mut value: u8 = A::read(map_byte_virt); - if (value & u8::MAX) != 0 { - for bit in 0..8 { - if (value & (1 << bit)) != 0 { - value &= !(1 << bit); - A::write(map_byte_virt, value); - let page_phys = entry.base.add(offset + bit * A::PAGE_SIZE); - return Some(page_phys); - } - } - } - offset += A::PAGE_SIZE * 8; - } - - let footer = A::read::(map_virt); - map_phys = footer.next; - } - } - None - } - - pub unsafe fn free(&mut self, base: PhysicalAddress, size: usize) { - if self.clear_frees { - // Zero freed pages for security, also ensures all allocs are zerod - //TODO: improve performance - //TODO: assumes linear physical mapping - let mut zero_virt = A::phys_to_virt(base); - let zero_end = zero_virt.add(size); - while zero_virt < zero_end { - A::write(zero_virt, 0usize); - zero_virt = zero_virt.add(mem::size_of::()); - } - } - - for i in 0 .. Self::BUDDY_ENTRIES { - let virt = self.table_virt.add(i * mem::size_of::()); - let entry = A::read::(virt); - if base >= entry.base && base.add(size) <= entry.base.add(entry.size) { - //TODO: Correct logic - let pages = size / A::PAGE_SIZE; - for page in 0 .. pages { - let page_base = base.add(page * A::PAGE_SIZE); - let index = (page_base.data() - entry.base.data()) / A::PAGE_SIZE; - let mut map_page = index / Self::MAP_PAGE_BITS; - let map_bit = index % Self::MAP_PAGE_BITS; - - //TODO: improve performance - let mut map_phys = entry.map; - loop { - if map_phys.data() == 0 { unimplemented!() } - let map_virt = A::phys_to_virt(map_phys); - if map_page == 0 { - let map_byte_virt = map_virt.add(map_bit / 8); - let mut value: u8 = A::read(map_byte_virt); - value |= 1 << (map_bit % 8); - A::write(map_byte_virt, value); - break; - } else { - let footer = A::read::(map_virt); - map_phys = footer.next; - map_page -= 1; - } - } - } - } - } - } -} - -pub struct Mapper { - table_addr: PhysicalAddress, - allocator: BumpAllocator, -} - -impl Mapper { - pub unsafe fn new(mut allocator: BumpAllocator) -> Option { - let table_addr = allocator.allocate()?; - Some(Self { - table_addr, - allocator, - }) - } - - pub unsafe fn map(&mut self, virt: VirtualAddress, entry: PageEntry) -> Option<()> { - let mut table = PageTable::new( - VirtualAddress::new(0), - self.table_addr, - A::PAGE_LEVELS - 1 - ); - loop { - let i = table.index_of(virt)?; - if table.level() == 0 { - //TODO: check for overwriting entry - table.set_entry(i, entry); - return Some(()); - } else { - let next_opt = table.next(i); - let next = match next_opt { - Some(some) => some, - None => { - let phys = self.allocator.allocate()?; - //TODO: correct flags? - table.set_entry(i, PageEntry::new(phys.data() | A::ENTRY_FLAG_WRITABLE | A::ENTRY_FLAG_PRESENT)); - table.next(i)? - } - }; - table = next; - } - } - } -} - unsafe fn new_tables(areas: &'static [MemoryArea]) { // First, calculate how much memory we have let mut size = 0; @@ -461,29 +176,33 @@ unsafe fn new_tables(areas: &'static [MemoryArea]) { println!("Memory: {}", format_size(size)); // Create a basic allocator for the first pages - let allocator = BumpAllocator::::new(areas, 0); + let mut bump_allocator = BumpAllocator::::new(areas, 0); - // Map all physical areas at PHYS_OFFSET - let mut mapper = Mapper::new(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); - mapper.map( - virt, - PageEntry::new(phys.data() | A::ENTRY_FLAG_WRITABLE | A::ENTRY_FLAG_PRESENT) - ).expect("failed to map frame"); + { + // Map all physical areas at PHYS_OFFSET + let mut mapper = PageMapper::::new( + &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); + mapper.map( + virt, + PageEntry::new(phys.data() | A::ENTRY_FLAG_WRITABLE | A::ENTRY_FLAG_PRESENT) + ).expect("failed to map frame"); + } } + + // Use the new table + mapper.activate(); } - // Use the new table - A::set_table(mapper.table_addr); - // Create the physical memory map - let offset = mapper.allocator.offset; + let offset = bump_allocator.offset(); println!("Permanently used: {}", format_size(offset)); - let mut allocator = BuddyAllocator::::new(areas, offset, true).unwrap(); + let mut allocator = BuddyAllocator::::new(bump_allocator, true).unwrap(); for i in 0..16 { let phys_opt = allocator.allocate(4 * KILOBYTE); println!("4 KB page {}: {:X?}", i, phys_opt); diff --git a/src/page/mapper.rs b/src/page/mapper.rs new file mode 100644 index 0000000000..cb1b57f44f --- /dev/null +++ b/src/page/mapper.rs @@ -0,0 +1,59 @@ +use core::marker::PhantomData; + +use crate::{ + Arch, + FrameAllocator, + PageEntry, + PageTable, + PhysicalAddress, + VirtualAddress, +}; + +pub struct PageMapper<'f, A, F> { + table_addr: PhysicalAddress, + allocator: &'f mut F, + phantom: PhantomData, +} + +impl<'f, A: Arch, F: FrameAllocator> PageMapper<'f, A, F> { + pub unsafe fn new(allocator: &'f mut F) -> Option { + let table_addr = allocator.allocate(1)?; + Some(Self { + table_addr, + allocator, + phantom: PhantomData, + }) + } + + pub unsafe fn activate(&mut self) { + A::set_table(self.table_addr); + } + + pub unsafe fn map(&mut self, virt: VirtualAddress, entry: PageEntry) -> Option<()> { + let mut table = PageTable::new( + VirtualAddress::new(0), + self.table_addr, + A::PAGE_LEVELS - 1 + ); + loop { + let i = table.index_of(virt)?; + if table.level() == 0 { + //TODO: check for overwriting entry + table.set_entry(i, entry); + return Some(()); + } else { + let next_opt = table.next(i); + let next = match next_opt { + Some(some) => some, + None => { + let phys = self.allocator.allocate(1)?; + //TODO: correct flags? + table.set_entry(i, PageEntry::new(phys.data() | A::ENTRY_FLAG_WRITABLE | A::ENTRY_FLAG_PRESENT)); + table.next(i)? + } + }; + table = next; + } + } + } +} diff --git a/src/page/mod.rs b/src/page/mod.rs index bef67a6fd6..268d3be9d3 100644 --- a/src/page/mod.rs +++ b/src/page/mod.rs @@ -1,7 +1,9 @@ pub use self::{ entry::PageEntry, + mapper::PageMapper, table::PageTable, }; mod entry; +mod mapper; mod table;