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:
+30
-12
@@ -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> {
|
||||
|
||||
Reference in New Issue
Block a user