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;