pub mod allocator;
pub mod cont_pages;
pub mod meta;
use core::{
marker::PhantomData,
mem::ManuallyDrop,
panic,
sync::atomic::{AtomicU32, AtomicUsize, Ordering},
};
pub use cont_pages::ContPages;
use meta::{mapping, FrameMeta, MetaSlot, PageMeta, PageUsage};
use super::{Frame, PagingLevel, PAGE_SIZE};
use crate::mm::{Paddr, PagingConsts, Vaddr};
static MAX_PADDR: AtomicUsize = AtomicUsize::new(0);
#[derive(Debug)]
pub struct Page<M: PageMeta> {
pub(super) ptr: *const MetaSlot,
pub(super) _marker: PhantomData<M>,
}
unsafe impl<M: PageMeta> Send for Page<M> {}
unsafe impl<M: PageMeta> Sync for Page<M> {}
impl<M: PageMeta> Page<M> {
pub fn from_unused(paddr: Paddr, metadata: M) -> Self {
assert!(paddr % PAGE_SIZE == 0);
assert!(paddr < MAX_PADDR.load(Ordering::Relaxed) as Paddr);
let vaddr = mapping::page_to_meta::<PagingConsts>(paddr);
let ptr = vaddr as *const MetaSlot;
let usage = unsafe { &(*ptr).usage };
let ref_count = unsafe { &(*ptr).ref_count };
usage
.compare_exchange(0, M::USAGE as u8, Ordering::SeqCst, Ordering::Relaxed)
.expect("page already in use when trying to get a new handle");
let old_ref_count = ref_count.fetch_add(1, Ordering::Relaxed);
debug_assert_eq!(old_ref_count, 0);
unsafe { (ptr as *mut M).write(metadata) };
Self {
ptr,
_marker: PhantomData,
}
}
#[allow(unused)]
pub(in crate::mm) fn into_raw(self) -> Paddr {
let paddr = self.paddr();
core::mem::forget(self);
paddr
}
pub(in crate::mm) unsafe fn from_raw(paddr: Paddr) -> Self {
let vaddr = mapping::page_to_meta::<PagingConsts>(paddr);
let ptr = vaddr as *const MetaSlot;
Self {
ptr,
_marker: PhantomData,
}
}
pub fn paddr(&self) -> Paddr {
mapping::meta_to_page::<PagingConsts>(self.ptr as Vaddr)
}
pub const fn level(&self) -> PagingLevel {
1
}
pub const fn size(&self) -> usize {
PAGE_SIZE
}
pub fn meta(&self) -> &M {
unsafe { &*(self.ptr as *const M) }
}
pub fn reference_count(&self) -> u32 {
self.ref_count().load(Ordering::Relaxed)
}
fn ref_count(&self) -> &AtomicU32 {
unsafe { &(*self.ptr).ref_count }
}
}
impl<M: PageMeta> Clone for Page<M> {
fn clone(&self) -> Self {
self.ref_count().fetch_add(1, Ordering::Relaxed);
Self {
ptr: self.ptr,
_marker: PhantomData,
}
}
}
impl<M: PageMeta> Drop for Page<M> {
fn drop(&mut self) {
let last_ref_cnt = self.ref_count().fetch_sub(1, Ordering::Release);
debug_assert!(last_ref_cnt > 0);
if last_ref_cnt == 1 {
core::sync::atomic::fence(Ordering::Acquire);
unsafe {
meta::drop_as_last::<M>(self.ptr);
}
}
}
}
#[derive(Debug)]
pub struct DynPage {
ptr: *const MetaSlot,
}
unsafe impl Send for DynPage {}
unsafe impl Sync for DynPage {}
impl DynPage {
pub(in crate::mm) fn into_raw(self) -> Paddr {
let paddr = self.paddr();
core::mem::forget(self);
paddr
}
pub(in crate::mm) unsafe fn from_raw(paddr: Paddr) -> Self {
let vaddr = mapping::page_to_meta::<PagingConsts>(paddr);
let ptr = vaddr as *const MetaSlot;
Self { ptr }
}
pub fn paddr(&self) -> Paddr {
mapping::meta_to_page::<PagingConsts>(self.ptr as Vaddr)
}
pub fn level(&self) -> PagingLevel {
1
}
pub fn size(&self) -> usize {
PAGE_SIZE
}
pub fn usage(&self) -> PageUsage {
let usage_raw = unsafe { (*self.ptr).usage.load(Ordering::Relaxed) };
num::FromPrimitive::from_u8(usage_raw).unwrap()
}
fn ref_count(&self) -> &AtomicU32 {
unsafe { &(*self.ptr).ref_count }
}
}
impl<M: PageMeta> TryFrom<DynPage> for Page<M> {
type Error = DynPage;
fn try_from(dyn_page: DynPage) -> Result<Self, Self::Error> {
if dyn_page.usage() == M::USAGE {
let result = Page {
ptr: dyn_page.ptr,
_marker: PhantomData,
};
let _ = ManuallyDrop::new(dyn_page);
Ok(result)
} else {
Err(dyn_page)
}
}
}
impl<M: PageMeta> From<Page<M>> for DynPage {
fn from(page: Page<M>) -> Self {
let result = Self { ptr: page.ptr };
let _ = ManuallyDrop::new(page);
result
}
}
impl From<Frame> for DynPage {
fn from(frame: Frame) -> Self {
Page::<FrameMeta>::from(frame).into()
}
}
impl Clone for DynPage {
fn clone(&self) -> Self {
self.ref_count().fetch_add(1, Ordering::Relaxed);
Self { ptr: self.ptr }
}
}
impl Drop for DynPage {
fn drop(&mut self) {
let last_ref_cnt = self.ref_count().fetch_sub(1, Ordering::Release);
debug_assert!(last_ref_cnt > 0);
if last_ref_cnt == 1 {
core::sync::atomic::fence(Ordering::Acquire);
unsafe {
match self.usage() {
PageUsage::Frame => {
meta::drop_as_last::<meta::FrameMeta>(self.ptr);
}
PageUsage::PageTable => {
meta::drop_as_last::<meta::PageTablePageMeta>(self.ptr);
}
PageUsage::Unused
| PageUsage::Reserved
| PageUsage::Kernel
| PageUsage::Meta => {
panic!("dropping a dynamic page with usage {:?}", self.usage());
}
}
}
}
}
}
pub(in crate::mm) unsafe fn inc_page_ref_count(paddr: Paddr) {
debug_assert!(paddr % PAGE_SIZE == 0);
debug_assert!(paddr < MAX_PADDR.load(Ordering::Relaxed) as Paddr);
let vaddr: Vaddr = mapping::page_to_meta::<PagingConsts>(paddr);
let slot = unsafe { &*(vaddr as *const MetaSlot) };
let old = slot.ref_count.fetch_add(1, Ordering::Relaxed);
debug_assert!(old > 0);
}