Add page flushing, add support for mapping anonymous pages

This commit is contained in:
Jeremy Soller
2020-09-08 14:56:57 -06:00
parent 6375c175f7
commit e8ea483832
4 changed files with 106 additions and 26 deletions
+18 -15
View File
@@ -13,6 +13,7 @@ use rmm::{
PageMapper,
PageTable,
PhysicalAddress,
VirtualAddress,
};
use core::{
@@ -178,17 +179,19 @@ unsafe fn new_tables<A: Arch>(areas: &'static [MemoryArea]) {
{
// Map all physical areas at PHYS_OFFSET
let mut mapper = PageMapper::<A, _>::new(
let mut mapper = PageMapper::<A, _>::create(
&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(
let flush = mapper.map_phys(
virt,
PageEntry::new(phys.data() | A::ENTRY_FLAG_WRITABLE | A::ENTRY_FLAG_PRESENT)
).expect("failed to map frame");
phys,
A::ENTRY_FLAG_WRITABLE
).expect("failed to map page to frame");
flush.ignore(); // Not the active table
}
}
@@ -211,17 +214,17 @@ unsafe fn new_tables<A: Arch>(areas: &'static [MemoryArea]) {
}
}
}
// for i in 0..16 {
// let phys_opt = allocator.allocate(2 * MEGABYTE);
// println!("2 MB page {}: {:X?}", i, phys_opt);
// if i % 2 == 0 {
// if let Some(phys) = phys_opt {
// allocator.free(phys, 2 * MEGABYTE);
// }
// }
// }
//
// println!("Remaining: {}", format_size(allocator.remaining()));
let mut mapper = PageMapper::<A, _>::current(
&mut allocator
);
for i in 0..16 {
let virt = VirtualAddress::new(MEGABYTE + i * A::PAGE_SIZE);
let flush = mapper.map(
virt,
A::ENTRY_FLAG_USER | A::ENTRY_FLAG_WRITABLE
).expect("failed to map page");
}
}
unsafe fn inner<A: Arch>() {
+57
View File
@@ -0,0 +1,57 @@
use core::{
marker::PhantomData,
mem,
};
use crate::{
Arch,
VirtualAddress,
};
#[must_use = "The page table must be flushed, or the changes unsafely ignored"]
pub struct PageFlush<A> {
virt: VirtualAddress,
phantom: PhantomData<A>,
}
impl<A: Arch> PageFlush<A> {
pub fn new(virt: VirtualAddress) -> Self {
Self {
virt,
phantom: PhantomData,
}
}
pub fn flush(self) {
unsafe { A::invalidate(self.virt); }
}
pub unsafe fn ignore(self) {
mem::forget(self);
}
}
#[must_use = "The page table must be flushed, or the changes unsafely ignored"]
pub struct PageFlushAll<A> {
phantom: PhantomData<A>,
}
impl <A: Arch> PageFlushAll<A> {
pub fn new() -> Self {
Self {
phantom: PhantomData,
}
}
pub fn consume(&self, flush: PageFlush<A>) {
unsafe { flush.ignore(); }
}
pub fn flush(self) {
unsafe { A::invalidate_all(); }
}
pub unsafe fn ignore(self) {
mem::forget(self);
}
}
+26 -8
View File
@@ -4,6 +4,7 @@ use crate::{
Arch,
FrameAllocator,
PageEntry,
PageFlush,
PageTable,
PhysicalAddress,
VirtualAddress,
@@ -16,13 +17,22 @@ pub struct PageMapper<'f, A, F> {
}
impl<'f, A: Arch, F: FrameAllocator> PageMapper<'f, A, F> {
pub unsafe fn new(allocator: &'f mut F) -> Option<Self> {
let table_addr = allocator.allocate_one()?;
Some(Self {
pub unsafe fn new(table_addr: PhysicalAddress, allocator: &'f mut F) -> Self {
Self {
table_addr,
allocator,
phantom: PhantomData,
})
}
}
pub unsafe fn create(allocator: &'f mut F) -> Option<Self> {
let table_addr = allocator.allocate_one()?;
Some(Self::new(table_addr, allocator))
}
pub unsafe fn current(allocator: &'f mut F) -> Self {
let table_addr = A::table();
Self::new(table_addr, allocator)
}
pub unsafe fn activate(&mut self) {
@@ -37,22 +47,30 @@ impl<'f, A: Arch, F: FrameAllocator> PageMapper<'f, A, F> {
)
}
pub unsafe fn map(&mut self, virt: VirtualAddress, entry: PageEntry<A>) -> Option<()> {
pub unsafe fn map(&mut self, virt: VirtualAddress, flags: usize) -> Option<PageFlush<A>> {
let phys = self.allocator.allocate_one()?;
self.map_phys(virt, phys, flags)
}
pub unsafe fn map_phys(&mut self, virt: VirtualAddress, phys: PhysicalAddress, flags: usize) -> Option<PageFlush<A>> {
//TODO: verify virt and phys are aligned
//TODO: verify flags have correct bits
let entry = PageEntry::new(phys.data() | flags | A::ENTRY_FLAG_PRESENT);
let mut table = self.table();
loop {
let i = table.index_of(virt)?;
if table.level() == 0 {
//TODO: check for overwriting entry
table.set_entry(i, entry);
return Some(());
return Some(PageFlush::new(virt));
} else {
let next_opt = table.next(i);
let next = match next_opt {
Some(some) => some,
None => {
let phys = self.allocator.allocate_one()?;
let next_phys = self.allocator.allocate_one()?;
//TODO: correct flags?
table.set_entry(i, PageEntry::new(phys.data() | A::ENTRY_FLAG_WRITABLE | A::ENTRY_FLAG_PRESENT));
table.set_entry(i, PageEntry::new(next_phys.data() | A::ENTRY_FLAG_WRITABLE | A::ENTRY_FLAG_PRESENT));
table.next(i)?
}
};
+5 -3
View File
@@ -1,9 +1,11 @@
pub use self::{
entry::PageEntry,
mapper::PageMapper,
table::PageTable,
entry::*,
flush::*,
mapper::*,
table::*,
};
mod entry;
mod flush;
mod mapper;
mod table;