diff --git a/src/page/entry.rs b/src/page/entry.rs index 0f593a26a4..800482cc2f 100644 --- a/src/page/entry.rs +++ b/src/page/entry.rs @@ -24,8 +24,14 @@ impl PageEntry { } #[inline(always)] - pub fn address(&self) -> PhysicalAddress { - PhysicalAddress(self.data & A::ENTRY_ADDRESS_MASK) + pub fn address(&self) -> Result { + let addr = PhysicalAddress(self.data & A::ENTRY_ADDRESS_MASK); + + if self.present() { + Ok(addr) + } else { + Err(addr) + } } #[inline(always)] diff --git a/src/page/flags.rs b/src/page/flags.rs index 6058637e65..22b8780caf 100644 --- a/src/page/flags.rs +++ b/src/page/flags.rs @@ -111,7 +111,11 @@ impl PageFlags { impl fmt::Debug for PageFlags { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("PageFlags") - .field("data", &self.data) + .field("present", &self.has_present()) + .field("write", &self.has_write()) + .field("executable", &self.has_execute()) + .field("user", &self.has_user()) + .field("bits", &format_args!("{:#0x}", self.data)) .finish() } -} \ No newline at end of file +} diff --git a/src/page/mapper.rs b/src/page/mapper.rs index 6617547ca7..d4f478bf80 100644 --- a/src/page/mapper.rs +++ b/src/page/mapper.rs @@ -8,44 +8,61 @@ use crate::{ PageFlush, PageTable, PhysicalAddress, + TableKind, VirtualAddress, }; -pub struct PageMapper<'f, A, F> { +pub struct PageMapper { table_addr: PhysicalAddress, - allocator: &'f mut F, - phantom: PhantomData, + allocator: F, + _phantom: PhantomData A>, } -impl<'f, A: Arch, F: FrameAllocator> PageMapper<'f, A, F> { - pub unsafe fn new(table_addr: PhysicalAddress, allocator: &'f mut F) -> Self { +impl PageMapper { + pub unsafe fn new(table_addr: PhysicalAddress, allocator: F) -> Self { Self { table_addr, allocator, - phantom: PhantomData, + _phantom: PhantomData, } } - pub unsafe fn create(allocator: &'f mut F) -> Option { + pub unsafe fn create(mut allocator: F) -> Option { let table_addr = allocator.allocate_one()?; Some(Self::new(table_addr, allocator)) } - pub unsafe fn current(allocator: &'f mut F) -> Self { + pub unsafe fn current(allocator: F) -> Self { let table_addr = A::table(); Self::new(table_addr, allocator) } + pub fn is_current(&self) -> bool { + unsafe { self.table().phys() == A::table() } + } - pub unsafe fn make_current(&mut self) { + pub unsafe fn make_current(&self) { A::set_table(self.table_addr); } - pub unsafe fn table(&self) -> PageTable { - PageTable::new( - VirtualAddress::new(0), - self.table_addr, - A::PAGE_LEVELS - 1 - ) + pub fn table(&self) -> PageTable { + // SAFETY: The only way to initialize a PageMapper is via new(), and we assume it upholds + // all necessary invariants for this to be safe. + unsafe { + PageTable::new( + VirtualAddress::new(0), + self.table_addr, + A::PAGE_LEVELS - 1 + ) + } + } + + pub unsafe fn remap(&mut self, virt: VirtualAddress, flags: PageFlags) -> Option> { + self.visit(virt, |p1, i| { + let mut entry = p1.entry(i)?; + entry.set_flags(flags); + p1.set_entry(i, entry); + Some(PageFlush::new(virt)) + }).flatten() } pub unsafe fn map(&mut self, virt: VirtualAddress, flags: PageFlags) -> Option> { @@ -71,7 +88,8 @@ impl<'f, A: Arch, F: FrameAllocator> PageMapper<'f, A, F> { None => { let next_phys = self.allocator.allocate_one()?; //TODO: correct flags? - table.set_entry(i, PageEntry::new(next_phys.data() | A::ENTRY_FLAG_READWRITE | A::ENTRY_FLAG_DEFAULT_TABLE)); + let flags = A::ENTRY_FLAG_READWRITE | A::ENTRY_FLAG_DEFAULT_TABLE | if virt.kind() == TableKind::User { A::ENTRY_FLAG_USER } else { 0 }; + table.set_entry(i, PageEntry::new(next_phys.data() | flags)); table.next(i)? } }; @@ -79,14 +97,35 @@ impl<'f, A: Arch, F: FrameAllocator> PageMapper<'f, A, F> { } } } + pub unsafe fn map_linearly(&mut self, phys: PhysicalAddress, flags: PageFlags) -> Option<(VirtualAddress, PageFlush)> { + let virt = A::phys_to_virt(phys); + self.map_phys(virt, phys, flags).map(|flush| (virt, flush)) + } + fn visit(&self, virt: VirtualAddress, f: impl FnOnce(&mut PageTable, usize) -> T) -> Option { + let mut table = self.table(); + unsafe { + loop { + let i = table.index_of(virt)?; + if table.level() == 0 { + return Some(f(&mut table, i)); + } else { + table = table.next(i)?; + } + } + } + } + pub fn translate(&self, virt: VirtualAddress) -> Option<(PhysicalAddress, PageFlags)> { + let entry = self.visit(virt, |p1, i| unsafe { p1.entry(i) })??; + Some((entry.address().ok()?, entry.flags())) + } pub unsafe fn unmap(&mut self, virt: VirtualAddress) -> Option> { - let (old, flush) = self.unmap_phys(virt)?; - self.allocator.free_one(old.address()); + let (old, _, flush) = self.unmap_phys(virt)?; + self.allocator.free_one(old); Some(flush) } - pub unsafe fn unmap_phys(&mut self, virt: VirtualAddress) -> Option<(PageEntry, PageFlush)> { + pub unsafe fn unmap_phys(&mut self, virt: VirtualAddress) -> Option<(PhysicalAddress, PageFlags, PageFlush)> { //TODO: verify virt is aligned let mut table = self.table(); //TODO: unmap parents @@ -96,10 +135,18 @@ impl<'f, A: Arch, F: FrameAllocator> PageMapper<'f, A, F> { let entry_opt = table.entry(i); table.set_entry(i, PageEntry::new(0)); let entry = entry_opt?; - return Some((entry, PageFlush::new(virt))); + return Some((entry.address().ok()?, entry.flags(), PageFlush::new(virt))); } else { table = table.next(i)?; } } } } +impl core::fmt::Debug for PageMapper { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("PageMapper") + .field("frame", &self.table_addr) + .field("allocator", &self.allocator) + .finish() + } +} diff --git a/src/page/table.rs b/src/page/table.rs index 23a8fa1293..0abc78486d 100644 --- a/src/page/table.rs +++ b/src/page/table.rs @@ -93,16 +93,14 @@ impl PageTable { } pub unsafe fn next(&self, i: usize) -> Option { - if self.level > 0 { - let entry = self.entry(i)?; - if entry.present() { - return Some(PageTable::new( - self.entry_base(i)?, - entry.address(), - self.level - 1 - )); - } + if self.level == 0 { + return None; } - None + + Some(PageTable::new( + self.entry_base(i)?, + self.entry(i)?.address().ok()?, + self.level - 1, + )) } }