Add support for unmapping parents.

Currently, this uses a relatively naive method of simply scanning the
512 entries for the PRESENT flag. But, unless the optimizer cannot, it
can be reduced to calculating the bitwise OR of every entry and then
checking that.

If this turns out to be too slow, which it might be when unmapping lots
of pages, then we can (1) either fall back to using a counter like the
old paging code did, or even better (2) use the now-1:1 grants tree to
check if it became empty. Putting the grants code in RMM might be
suboptimal, so instead we can add "unmap_range" and have the kernel
paging code take the offset of the next grant, if any, and then possibly
unmap entire P1s/P2s/P3s -- whatever is in the page tables within that
range.

Note that I am fairly certain that method (1) was the cause of the
visually notorious orbital memory corruption bug.
This commit is contained in:
4lDO2
2022-07-23 00:18:56 +02:00
parent 229ae5da40
commit 9462df03e7
+30 -12
View File
@@ -119,27 +119,45 @@ impl<A: Arch, F: FrameAllocator> PageMapper<A, F> {
Some((entry.address().ok()?, entry.flags()))
}
pub unsafe fn unmap(&mut self, virt: VirtualAddress) -> Option<PageFlush<A>> {
let (old, _, flush) = self.unmap_phys(virt)?;
pub unsafe fn unmap(&mut self, virt: VirtualAddress, unmap_parents: bool) -> Option<PageFlush<A>> {
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<A>, PageFlush<A>)> {
pub unsafe fn unmap_phys(&mut self, virt: VirtualAddress, unmap_parents: bool) -> Option<(PhysicalAddress, PageFlags<A>, PageFlush<A>)> {
//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<A: Arch>(virt: VirtualAddress, table: &mut PageTable<A>, initial_level: usize, unmap_parents: bool, allocator: &mut impl FrameAllocator) -> Option<(PhysicalAddress, PageFlags<A>)> {
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<A, F: core::fmt::Debug> core::fmt::Debug for PageMapper<A, F> {