use core::{mem::ManuallyDrop, panic};
use super::{PageTableEntryTrait, RawPageTableNode};
use crate::{
arch::mm::{PageTableEntry, PagingConsts},
mm::{
page::{inc_page_ref_count, meta::MapTrackingStatus, DynPage},
page_prop::PageProperty,
Paddr, PagingConstsTrait, PagingLevel,
},
};
#[derive(Debug)]
pub(in crate::mm) enum Child<
E: PageTableEntryTrait = PageTableEntry,
C: PagingConstsTrait = PagingConsts,
> where
[(); C::NR_LEVELS as usize]:,
{
PageTable(RawPageTableNode<E, C>),
Page(DynPage, PageProperty),
Untracked(Paddr, PagingLevel, PageProperty),
None,
}
impl<E: PageTableEntryTrait, C: PagingConstsTrait> Child<E, C>
where
[(); C::NR_LEVELS as usize]:,
{
pub(in crate::mm) fn is_none(&self) -> bool {
matches!(self, Child::None)
}
pub(super) fn is_compatible(
&self,
node_level: PagingLevel,
is_tracked: MapTrackingStatus,
) -> bool {
match self {
Child::PageTable(pt) => node_level == pt.level() + 1,
Child::Page(p, _) => {
node_level == p.level() && is_tracked == MapTrackingStatus::Tracked
}
Child::Untracked(_, level, _) => {
node_level == *level && is_tracked == MapTrackingStatus::Untracked
}
Child::None => true,
}
}
pub(super) fn into_pte(self) -> E {
match self {
Child::PageTable(pt) => {
let pt = ManuallyDrop::new(pt);
E::new_pt(pt.paddr())
}
Child::Page(page, prop) => {
let level = page.level();
E::new_page(page.into_raw(), level, prop)
}
Child::Untracked(pa, level, prop) => E::new_page(pa, level, prop),
Child::None => E::new_absent(),
}
}
pub(super) unsafe fn from_pte(
pte: E,
level: PagingLevel,
is_tracked: MapTrackingStatus,
) -> Self {
if !pte.is_present() {
return Child::None;
}
let paddr = pte.paddr();
if !pte.is_last(level) {
return Child::PageTable(unsafe { RawPageTableNode::from_raw_parts(paddr, level - 1) });
}
match is_tracked {
MapTrackingStatus::Tracked => {
let page = unsafe { DynPage::from_raw(paddr) };
Child::Page(page, pte.prop())
}
MapTrackingStatus::Untracked => Child::Untracked(paddr, level, pte.prop()),
MapTrackingStatus::NotApplicable => panic!("Invalid tracking status"),
}
}
pub(super) unsafe fn clone_from_pte(
pte: &E,
level: PagingLevel,
is_tracked: MapTrackingStatus,
) -> Self {
if !pte.is_present() {
return Child::None;
}
let paddr = pte.paddr();
if !pte.is_last(level) {
unsafe { inc_page_ref_count(paddr) };
return Child::PageTable(unsafe { RawPageTableNode::from_raw_parts(paddr, level - 1) });
}
match is_tracked {
MapTrackingStatus::Tracked => {
unsafe { inc_page_ref_count(paddr) };
let page = unsafe { DynPage::from_raw(paddr) };
Child::Page(page, pte.prop())
}
MapTrackingStatus::Untracked => Child::Untracked(paddr, level, pte.prop()),
MapTrackingStatus::NotApplicable => panic!("Invalid tracking status"),
}
}
}