mod child;
mod entry;
use core::{marker::PhantomData, mem::ManuallyDrop, sync::atomic::Ordering};
pub(in crate::mm) use self::{child::Child, entry::Entry};
use super::{nr_subpage_per_huge, PageTableEntryTrait};
use crate::{
arch::mm::{PageTableEntry, PagingConsts},
mm::{
paddr_to_vaddr,
page::{
self, inc_page_ref_count,
meta::{MapTrackingStatus, PageMeta, PageTablePageMeta, PageUsage},
DynPage, Page,
},
Paddr, PagingConstsTrait, PagingLevel, PAGE_SIZE,
},
};
#[derive(Debug)]
pub(super) struct RawPageTableNode<E: PageTableEntryTrait, C: PagingConstsTrait>
where
[(); C::NR_LEVELS as usize]:,
{
raw: Paddr,
level: PagingLevel,
_phantom: PhantomData<(E, C)>,
}
impl<E: PageTableEntryTrait, C: PagingConstsTrait> RawPageTableNode<E, C>
where
[(); C::NR_LEVELS as usize]:,
{
pub(super) fn paddr(&self) -> Paddr {
self.raw
}
pub(super) fn level(&self) -> PagingLevel {
self.level
}
pub(super) fn lock(self) -> PageTableNode<E, C> {
let level = self.level;
let page: Page<PageTablePageMeta<E, C>> = self.into();
while page
.meta()
.lock
.compare_exchange(0, 1, Ordering::Acquire, Ordering::Relaxed)
.is_err()
{
core::hint::spin_loop();
}
debug_assert_eq!(page.meta().level, level);
PageTableNode::<E, C> { page }
}
pub(super) fn clone_shallow(&self) -> Self {
self.inc_ref_count();
Self {
raw: self.raw,
level: self.level,
_phantom: PhantomData,
}
}
pub(crate) unsafe fn activate(&self) {
use crate::{
arch::mm::{activate_page_table, current_page_table_paddr},
mm::CachePolicy,
};
assert_eq!(self.level, C::NR_LEVELS);
let last_activated_paddr = current_page_table_paddr();
if last_activated_paddr == self.raw {
return;
}
activate_page_table(self.raw, CachePolicy::Writeback);
self.inc_ref_count();
drop(Self {
raw: last_activated_paddr,
level: C::NR_LEVELS,
_phantom: PhantomData,
});
}
pub(super) unsafe fn first_activate(&self) {
use crate::{arch::mm::activate_page_table, mm::CachePolicy};
self.inc_ref_count();
activate_page_table(self.raw, CachePolicy::Writeback);
}
fn inc_ref_count(&self) {
unsafe {
inc_page_ref_count(self.paddr());
}
}
unsafe fn from_raw_parts(paddr: Paddr, level: PagingLevel) -> Self {
Self {
raw: paddr,
level,
_phantom: PhantomData,
}
}
}
impl<E: PageTableEntryTrait, C: PagingConstsTrait> From<RawPageTableNode<E, C>>
for Page<PageTablePageMeta<E, C>>
where
[(); C::NR_LEVELS as usize]:,
{
fn from(raw: RawPageTableNode<E, C>) -> Self {
let raw = ManuallyDrop::new(raw);
unsafe { Page::<PageTablePageMeta<E, C>>::from_raw(raw.paddr()) }
}
}
impl<E: PageTableEntryTrait, C: PagingConstsTrait> Drop for RawPageTableNode<E, C>
where
[(); C::NR_LEVELS as usize]:,
{
fn drop(&mut self) {
drop(unsafe { Page::<PageTablePageMeta<E, C>>::from_raw(self.paddr()) });
}
}
#[derive(Debug)]
pub(super) struct PageTableNode<
E: PageTableEntryTrait = PageTableEntry,
C: PagingConstsTrait = PagingConsts,
> where
[(); C::NR_LEVELS as usize]:,
{
page: Page<PageTablePageMeta<E, C>>,
}
impl<E: PageTableEntryTrait, C: PagingConstsTrait> PageTableNode<E, C>
where
[(); C::NR_LEVELS as usize]:,
{
pub(super) fn entry(&mut self, idx: usize) -> Entry<'_, E, C> {
assert!(idx < nr_subpage_per_huge::<C>());
unsafe { Entry::new_at(self, idx) }
}
pub(super) fn level(&self) -> PagingLevel {
self.page.meta().level
}
pub(super) fn is_tracked(&self) -> MapTrackingStatus {
self.page.meta().is_tracked
}
pub(super) fn alloc(level: PagingLevel, is_tracked: MapTrackingStatus) -> Self {
let meta = PageTablePageMeta::new_locked(level, is_tracked);
let page = page::allocator::alloc_single::<PageTablePageMeta<E, C>>(meta).unwrap();
let ptr = paddr_to_vaddr(page.paddr()) as *mut u8;
unsafe { core::ptr::write_bytes(ptr, 0, PAGE_SIZE) };
debug_assert!(E::new_absent().as_bytes().iter().all(|&b| b == 0));
Self { page }
}
pub(super) fn into_raw(self) -> RawPageTableNode<E, C> {
let this = ManuallyDrop::new(self);
this.page.meta().lock.store(0, Ordering::Release);
unsafe { RawPageTableNode::from_raw_parts(this.page.paddr(), this.page.meta().level) }
}
pub(super) fn clone_raw(&self) -> RawPageTableNode<E, C> {
let page = ManuallyDrop::new(self.page.clone());
unsafe { RawPageTableNode::from_raw_parts(page.paddr(), page.meta().level) }
}
pub(super) fn nr_children(&self) -> u16 {
unsafe { *self.page.meta().nr_children.get() }
}
unsafe fn read_pte(&self, idx: usize) -> E {
debug_assert!(idx < nr_subpage_per_huge::<C>());
let ptr = paddr_to_vaddr(self.page.paddr()) as *const E;
unsafe { ptr.add(idx).read() }
}
unsafe fn write_pte(&mut self, idx: usize, pte: E) {
debug_assert!(idx < nr_subpage_per_huge::<C>());
let ptr = paddr_to_vaddr(self.page.paddr()) as *mut E;
unsafe { ptr.add(idx).write(pte) }
}
fn nr_children_mut(&mut self) -> &mut u16 {
unsafe { &mut *self.page.meta().nr_children.get() }
}
}
impl<E: PageTableEntryTrait, C: PagingConstsTrait> Drop for PageTableNode<E, C>
where
[(); C::NR_LEVELS as usize]:,
{
fn drop(&mut self) {
self.page.meta().lock.store(0, Ordering::Release);
}
}
impl<E: PageTableEntryTrait, C: PagingConstsTrait> PageMeta for PageTablePageMeta<E, C>
where
[(); C::NR_LEVELS as usize]:,
{
const USAGE: PageUsage = PageUsage::PageTable;
fn on_drop(page: &mut Page<Self>) {
let nr_children = unsafe { *page.meta().nr_children.get() };
if nr_children == 0 {
return;
}
let paddr = page.paddr();
let level = page.meta().level;
let is_tracked = page.meta().is_tracked;
for i in 0..nr_subpage_per_huge::<C>() {
let pte_ptr = unsafe { (paddr_to_vaddr(paddr) as *const E).add(i) };
let pte = unsafe { pte_ptr.read() };
if pte.is_present() {
let paddr = pte.paddr();
if !pte.is_last(level) {
drop(unsafe { Page::<Self>::from_raw(paddr) });
} else if is_tracked == MapTrackingStatus::Tracked {
drop(unsafe { DynPage::from_raw(paddr) });
}
}
}
}
}