use bitflags::bitflags;
use core::marker::PhantomData;
use core::ops::Range;
use crate::address_space::PageTableMapper;
use crate::{PageFormat, PteType};
use num_traits::{FromPrimitive, PrimInt, Unsigned};
bitflags! {
pub struct PteRemovalFlags: u32 {
const FREE_PAGES = 1 << 0;
const FREE_PAGE_TABLES = 1 << 1;
}
}
pub struct PteRemover<'a, PTE, Mapper, Error>
where
PTE: FromPrimitive + PrimInt + Unsigned,
Mapper: PageTableMapper<PTE, Error>,
{
pub mapper: &'a mut Mapper,
pub flags: PteRemovalFlags,
pub format: &'a PageFormat<'a, PTE>,
pub error: PhantomData<Error>,
}
impl<'a, PTE, Mapper, Error> crate::PageWalkerMut<PTE, Error> for PteRemover<'a, PTE, Mapper, Error>
where
PTE: FromPrimitive + PrimInt + Unsigned,
Mapper: PageTableMapper<PTE, Error>,
{
fn read_pte(&self, phys_addr: PTE) -> Result<PTE, Error> {
self.mapper.read_pte(phys_addr)
}
fn write_pte(&mut self, phys_addr: PTE, value: PTE) -> Result<(), Error> {
self.mapper.write_pte(phys_addr, value)
}
fn handle_pte(&mut self, pte_type: PteType, _range: Range<usize>, pte: &mut PTE) -> Result<(), Error> {
let physical_mask = self.format.physical_mask;
if let PteType::Page(level) = pte_type {
let level = &self.format.levels[level];
if level.is_present(*pte) {
if self.flags.contains(PteRemovalFlags::FREE_PAGES) {
self.mapper.free_page(physical_mask & *pte);
}
*pte = PTE::zero();
}
}
Ok(())
}
fn handle_post_pte(&mut self, index: usize, _range: Range<usize>, pte: &mut PTE) -> Result<(), Error> {
let level = &self.format.levels[index];
let physical_mask = self.format.physical_mask;
let phys_addr = physical_mask & *pte;
for i in 0..level.entries() {
let offset: PTE = PTE::from_usize(i * core::mem::size_of::<PTE>()).unwrap();
if self.read_pte(phys_addr + offset)? != PTE::zero() {
return Ok(());
}
}
if self.flags.contains(PteRemovalFlags::FREE_PAGE_TABLES) {
self.mapper.free_page(physical_mask & *pte);
*pte = PTE::zero();
}
Ok(())
}
}