diff --git a/src/page/mapper.rs b/src/page/mapper.rs index d4f478bf80..17d4b11f80 100644 --- a/src/page/mapper.rs +++ b/src/page/mapper.rs @@ -119,27 +119,45 @@ impl PageMapper { Some((entry.address().ok()?, entry.flags())) } - pub unsafe fn unmap(&mut self, virt: VirtualAddress) -> Option> { - let (old, _, flush) = self.unmap_phys(virt)?; + pub unsafe fn unmap(&mut self, virt: VirtualAddress, unmap_parents: bool) -> Option> { + let (old, _, flush) = self.unmap_phys(virt, unmap_parents)?; self.allocator.free_one(old); Some(flush) } - pub unsafe fn unmap_phys(&mut self, virt: VirtualAddress) -> Option<(PhysicalAddress, PageFlags, PageFlush)> { + pub unsafe fn unmap_phys(&mut self, virt: VirtualAddress, unmap_parents: bool) -> Option<(PhysicalAddress, PageFlags, PageFlush)> { //TODO: verify virt is aligned let mut table = self.table(); - //TODO: unmap parents - loop { - let i = table.index_of(virt)?; - if table.level() == 0 { - let entry_opt = table.entry(i); + let level = table.level(); + unmap_phys_inner(virt, &mut table, level, false, &mut self.allocator).map(|(pa, pf)| (pa, pf, PageFlush::new(virt))) + } +} +unsafe fn unmap_phys_inner(virt: VirtualAddress, table: &mut PageTable, initial_level: usize, unmap_parents: bool, allocator: &mut impl FrameAllocator) -> Option<(PhysicalAddress, PageFlags)> { + let i = table.index_of(virt)?; + + if table.level() == 0 { + let entry_opt = table.entry(i); + table.set_entry(i, PageEntry::new(0)); + let entry = entry_opt?; + + Some((entry.address().ok()?, entry.flags())) + } else { + let mut subtable = table.next(i)?; + + let res = unmap_phys_inner(virt, &mut subtable, initial_level, unmap_parents, allocator)?; + + if unmap_parents { + // TODO: Use a counter? This would reduce the remaining number of available bits, but could be + // faster (benchmark is needed). + let is_still_populated = (0..A::PAGE_ENTRIES).map(|j| subtable.entry(j).expect("must be within bounds")).any(|e| e.present()); + + if !is_still_populated { + allocator.free_one(table.phys()); table.set_entry(i, PageEntry::new(0)); - let entry = entry_opt?; - return Some((entry.address().ok()?, entry.flags(), PageFlush::new(virt))); - } else { - table = table.next(i)?; } } + + Some(res) } } impl core::fmt::Debug for PageMapper {