use super::{Child, PageTableEntryTrait, PageTableNode};
use crate::mm::{
nr_subpage_per_huge, page::meta::MapTrackingStatus, page_prop::PageProperty, page_size,
PagingConstsTrait,
};
pub(in crate::mm) struct Entry<'a, E: PageTableEntryTrait, C: PagingConstsTrait>
where
[(); C::NR_LEVELS as usize]:,
{
pte: E,
idx: usize,
node: &'a mut PageTableNode<E, C>,
}
impl<'a, E: PageTableEntryTrait, C: PagingConstsTrait> Entry<'a, E, C>
where
[(); C::NR_LEVELS as usize]:,
{
pub(in crate::mm) fn is_none(&self) -> bool {
!self.pte.is_present()
}
pub(in crate::mm) fn is_node(&self) -> bool {
self.pte.is_present() && !self.pte.is_last(self.node.level())
}
pub(in crate::mm) fn to_owned(&self) -> Child<E, C> {
unsafe { Child::clone_from_pte(&self.pte, self.node.level(), self.node.is_tracked()) }
}
pub(in crate::mm) fn protect(&mut self, op: &mut impl FnMut(&mut PageProperty)) {
if !self.pte.is_present() {
return;
}
let prop = self.pte.prop();
let mut new_prop = prop;
op(&mut new_prop);
if prop == new_prop {
return;
}
self.pte.set_prop(new_prop);
unsafe { self.node.write_pte(self.idx, self.pte) };
}
pub(in crate::mm) fn replace(self, new_child: Child<E, C>) -> Child<E, C> {
assert!(new_child.is_compatible(self.node.level(), self.node.is_tracked()));
let old_child =
unsafe { Child::from_pte(self.pte, self.node.level(), self.node.is_tracked()) };
if old_child.is_none() && !new_child.is_none() {
*self.node.nr_children_mut() += 1;
} else if !old_child.is_none() && new_child.is_none() {
*self.node.nr_children_mut() -= 1;
}
unsafe { self.node.write_pte(self.idx, new_child.into_pte()) };
old_child
}
pub(in crate::mm) fn split_if_untracked_huge(self) -> Option<PageTableNode<E, C>> {
let level = self.node.level();
if !(self.pte.is_last(level)
&& level > 1
&& self.node.is_tracked() == MapTrackingStatus::Untracked)
{
return None;
}
let pa = self.pte.paddr();
let prop = self.pte.prop();
let mut new_page = PageTableNode::<E, C>::alloc(level - 1, MapTrackingStatus::Untracked);
for i in 0..nr_subpage_per_huge::<C>() {
let small_pa = pa + i * page_size::<C>(level - 1);
let _ = new_page
.entry(i)
.replace(Child::Untracked(small_pa, level - 1, prop));
}
let _ = self.replace(Child::PageTable(new_page.clone_raw()));
Some(new_page)
}
pub(super) unsafe fn new_at(node: &'a mut PageTableNode<E, C>, idx: usize) -> Self {
let pte = unsafe { node.read_pte(idx) };
Self { pte, idx, node }
}
}