pub mod mapping {
use core::mem::size_of;
use super::MetaSlot;
use crate::mm::{kspace::FRAME_METADATA_RANGE, Paddr, PagingConstsTrait, Vaddr, PAGE_SIZE};
pub const fn page_to_meta<C: PagingConstsTrait>(paddr: Paddr) -> Vaddr {
let base = FRAME_METADATA_RANGE.start;
let offset = paddr / PAGE_SIZE;
base + offset * size_of::<MetaSlot>()
}
pub const fn meta_to_page<C: PagingConstsTrait>(vaddr: Vaddr) -> Paddr {
let base = FRAME_METADATA_RANGE.start;
let offset = (vaddr - base) / size_of::<MetaSlot>();
offset * PAGE_SIZE
}
}
use alloc::vec::Vec;
use core::{
cell::UnsafeCell,
marker::PhantomData,
mem::{size_of, ManuallyDrop},
panic,
sync::atomic::{AtomicU32, AtomicU8, Ordering},
};
use log::info;
use num_derive::FromPrimitive;
use static_assertions::const_assert_eq;
use super::{allocator, Page};
use crate::{
arch::mm::{PageTableEntry, PagingConsts},
mm::{
paddr_to_vaddr, page_size,
page_table::{boot_pt, PageTableEntryTrait},
CachePolicy, Paddr, PageFlags, PageProperty, PagingConstsTrait, PagingLevel,
PrivilegedPageFlags, Vaddr, PAGE_SIZE,
},
};
#[repr(u8)]
#[derive(Debug, FromPrimitive, PartialEq)]
pub enum PageUsage {
#[allow(dead_code)]
Unused = 0,
#[allow(dead_code)]
Reserved = 1,
Frame = 32,
PageTable = 64,
Meta = 65,
Kernel = 66,
}
#[repr(C)]
pub(in crate::mm) struct MetaSlot {
_inner: MetaSlotInner,
pub(super) usage: AtomicU8,
pub(super) ref_count: AtomicU32,
}
pub(super) union MetaSlotInner {
_frame: ManuallyDrop<FrameMeta>,
_pt: ManuallyDrop<PageTablePageMeta>,
}
const_assert_eq!(size_of::<MetaSlot>(), 16);
pub trait PageMeta: Sync + private::Sealed + Sized {
const USAGE: PageUsage;
fn on_drop(page: &mut Page<Self>);
}
pub(super) unsafe fn drop_as_last<M: PageMeta>(ptr: *const MetaSlot) {
debug_assert_eq!((*ptr).ref_count.load(Ordering::Relaxed), 0);
let mut page = Page::<M> {
ptr,
_marker: PhantomData,
};
M::on_drop(&mut page);
let _ = ManuallyDrop::new(page);
core::ptr::drop_in_place(ptr as *mut M);
(*ptr).usage.store(0, Ordering::Release);
allocator::PAGE_ALLOCATOR.get().unwrap().lock().dealloc(
mapping::meta_to_page::<PagingConsts>(ptr as Vaddr) / PAGE_SIZE,
1,
);
}
mod private {
pub trait Sealed {}
}
use private::Sealed;
#[derive(Debug, Default)]
#[repr(C)]
pub struct FrameMeta {
_unused_for_layout_padding: [u8; 8],
}
impl Sealed for FrameMeta {}
#[derive(Debug)]
#[repr(C)]
pub(in crate::mm) struct PageTablePageMeta<
E: PageTableEntryTrait = PageTableEntry,
C: PagingConstsTrait = PagingConsts,
> where
[(); C::NR_LEVELS as usize]:,
{
pub nr_children: UnsafeCell<u16>,
pub level: PagingLevel,
pub is_tracked: MapTrackingStatus,
pub lock: AtomicU8,
_phantom: core::marker::PhantomData<(E, C)>,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[repr(u8)]
pub(in crate::mm) enum MapTrackingStatus {
NotApplicable,
Untracked,
Tracked,
}
impl<E: PageTableEntryTrait, C: PagingConstsTrait> PageTablePageMeta<E, C>
where
[(); C::NR_LEVELS as usize]:,
{
pub fn new_locked(level: PagingLevel, is_tracked: MapTrackingStatus) -> Self {
Self {
nr_children: UnsafeCell::new(0),
level,
is_tracked,
lock: AtomicU8::new(1),
_phantom: PhantomData,
}
}
}
impl<E: PageTableEntryTrait, C: PagingConstsTrait> Sealed for PageTablePageMeta<E, C> where
[(); C::NR_LEVELS as usize]:
{
}
unsafe impl<E: PageTableEntryTrait, C: PagingConstsTrait> Send for PageTablePageMeta<E, C> where
[(); C::NR_LEVELS as usize]:
{
}
unsafe impl<E: PageTableEntryTrait, C: PagingConstsTrait> Sync for PageTablePageMeta<E, C> where
[(); C::NR_LEVELS as usize]:
{
}
#[derive(Debug, Default)]
#[repr(C)]
pub struct MetaPageMeta {}
impl Sealed for MetaPageMeta {}
impl PageMeta for MetaPageMeta {
const USAGE: PageUsage = PageUsage::Meta;
fn on_drop(_page: &mut Page<Self>) {
panic!("Meta pages are currently not allowed to be dropped");
}
}
#[derive(Debug, Default)]
#[repr(C)]
pub struct KernelMeta {}
impl Sealed for KernelMeta {}
impl PageMeta for KernelMeta {
const USAGE: PageUsage = PageUsage::Kernel;
fn on_drop(_page: &mut Page<Self>) {
panic!("Kernel pages are not allowed to be dropped");
}
}
pub(crate) fn init() -> Vec<Page<MetaPageMeta>> {
let max_paddr = {
let regions = crate::boot::memory_regions();
regions.iter().map(|r| r.base() + r.len()).max().unwrap()
};
info!(
"Initializing page metadata for physical memory up to {:x}",
max_paddr
);
super::MAX_PADDR.store(max_paddr, Ordering::Relaxed);
let num_pages = max_paddr / page_size::<PagingConsts>(1);
let num_meta_pages = (num_pages * size_of::<MetaSlot>()).div_ceil(PAGE_SIZE);
let meta_pages = alloc_meta_pages(num_meta_pages);
boot_pt::with_borrow(|boot_pt| {
for (i, frame_paddr) in meta_pages.iter().enumerate() {
let vaddr = mapping::page_to_meta::<PagingConsts>(0) + i * PAGE_SIZE;
let prop = PageProperty {
flags: PageFlags::RW,
cache: CachePolicy::Writeback,
priv_flags: PrivilegedPageFlags::GLOBAL,
};
unsafe { boot_pt.map_base_page(vaddr, frame_paddr / PAGE_SIZE, prop) };
}
})
.unwrap();
meta_pages
.into_iter()
.map(|paddr| Page::<MetaPageMeta>::from_unused(paddr, MetaPageMeta::default()))
.collect()
}
fn alloc_meta_pages(nframes: usize) -> Vec<Paddr> {
let mut meta_pages = Vec::new();
let start_frame = allocator::PAGE_ALLOCATOR
.get()
.unwrap()
.lock()
.alloc(nframes)
.unwrap()
* PAGE_SIZE;
let vaddr = paddr_to_vaddr(start_frame) as *mut u8;
unsafe { core::ptr::write_bytes(vaddr, 0, PAGE_SIZE * nframes) };
for i in 0..nframes {
let paddr = start_frame + i * PAGE_SIZE;
meta_pages.push(paddr);
}
meta_pages
}