diff --git a/ostd/src/boot/smp.rs b/ostd/src/boot/smp.rs index 8c071806d..01254eed8 100644 --- a/ostd/src/boot/smp.rs +++ b/ostd/src/boot/smp.rs @@ -11,8 +11,9 @@ use crate::{ arch::boot::smp::{bringup_all_aps, get_num_processors}, cpu, mm::{ + kspace::KernelMeta, paddr_to_vaddr, - page::{self, meta::KernelMeta, ContPages}, + page::{self, ContPages}, PAGE_SIZE, }, task::Task, diff --git a/ostd/src/cpu/local/mod.rs b/ostd/src/cpu/local/mod.rs index c91f8cd78..0ad414819 100644 --- a/ostd/src/cpu/local/mod.rs +++ b/ostd/src/cpu/local/mod.rs @@ -44,8 +44,9 @@ use spin::Once; use crate::{ arch, mm::{ + kspace::KernelMeta, paddr_to_vaddr, - page::{self, meta::KernelMeta, ContPages}, + page::{self, ContPages}, PAGE_SIZE, }, }; diff --git a/ostd/src/lib.rs b/ostd/src/lib.rs index 1d8df5a8f..dab36c37b 100644 --- a/ostd/src/lib.rs +++ b/ostd/src/lib.rs @@ -15,8 +15,10 @@ #![feature(linkage)] #![feature(min_specialization)] #![feature(negative_impls)] +#![feature(ptr_metadata)] #![feature(ptr_sub_ptr)] #![feature(sync_unsafe_cell)] +#![feature(trait_upcasting)] // The `generic_const_exprs` feature is incomplete however required for the page table // const generic implementation. We are using this feature in a conservative manner. #![allow(incomplete_features)] diff --git a/ostd/src/mm/frame/mod.rs b/ostd/src/mm/frame/mod.rs index e1dbdfb68..7d69dbe6f 100644 --- a/ostd/src/mm/frame/mod.rs +++ b/ostd/src/mm/frame/mod.rs @@ -15,14 +15,17 @@ use core::mem::ManuallyDrop; pub use segment::Segment; -use super::page::{ - meta::{FrameMeta, MetaSlot, PageMeta, PageUsage}, - DynPage, Page, +use super::{ + page::{ + meta::{impl_page_meta, MetaSlot}, + DynPage, Page, + }, + Infallible, }; use crate::{ mm::{ io::{FallibleVmRead, FallibleVmWrite, VmIo, VmReader, VmWriter}, - paddr_to_vaddr, HasPaddr, Infallible, Paddr, PAGE_SIZE, + paddr_to_vaddr, HasPaddr, Paddr, PAGE_SIZE, }, Error, Result, }; @@ -178,14 +181,11 @@ impl VmIo for Frame { } } -impl PageMeta for FrameMeta { - const USAGE: PageUsage = PageUsage::Frame; +/// Metadata for a frame. +#[derive(Debug, Default)] +pub struct FrameMeta {} - fn on_drop(_page: &mut Page) { - // Nothing should be done so far since dropping the page would - // have all taken care of. - } -} +impl_page_meta!(FrameMeta); // Here are implementations for `xarray`. diff --git a/ostd/src/mm/frame/options.rs b/ostd/src/mm/frame/options.rs index c87423a22..0fd4eeaff 100644 --- a/ostd/src/mm/frame/options.rs +++ b/ostd/src/mm/frame/options.rs @@ -4,10 +4,7 @@ use super::{Frame, Segment}; use crate::{ - mm::{ - page::{self, meta::FrameMeta}, - PAGE_SIZE, - }, + mm::{frame::FrameMeta, page, PAGE_SIZE}, prelude::*, Error, }; diff --git a/ostd/src/mm/frame/segment.rs b/ostd/src/mm/frame/segment.rs index 949b4ed9d..6f28b7292 100644 --- a/ostd/src/mm/frame/segment.rs +++ b/ostd/src/mm/frame/segment.rs @@ -6,8 +6,9 @@ use core::ops::Range; use crate::{ mm::{ + frame::FrameMeta, io::{FallibleVmRead, FallibleVmWrite}, - page::{meta::FrameMeta, ContPages}, + page::ContPages, Frame, HasPaddr, Infallible, Paddr, VmIo, VmReader, VmWriter, }, Error, Result, diff --git a/ostd/src/mm/kspace/mod.rs b/ostd/src/mm/kspace/mod.rs index eb4165bc1..75bf56c7c 100644 --- a/ostd/src/mm/kspace/mod.rs +++ b/ostd/src/mm/kspace/mod.rs @@ -50,7 +50,7 @@ use spin::Once; use super::{ nr_subpage_per_huge, page::{ - meta::{mapping, KernelMeta, MetaPageMeta}, + meta::{impl_page_meta, mapping, MetaPageMeta}, Page, }, page_prop::{CachePolicy, PageFlags, PageProperty, PrivilegedPageFlags}, @@ -247,3 +247,9 @@ pub unsafe fn activate_kernel_page_table() { crate::mm::page_table::boot_pt::dismiss(); } } + +/// The metadata of pages that contains the kernel itself. +#[derive(Debug, Default)] +pub struct KernelMeta {} + +impl_page_meta!(KernelMeta); diff --git a/ostd/src/mm/mod.rs b/ostd/src/mm/mod.rs index ea493a195..bd1f4ec9e 100644 --- a/ostd/src/mm/mod.rs +++ b/ostd/src/mm/mod.rs @@ -44,7 +44,7 @@ pub type PagingLevel = u8; /// A minimal set of constants that determines the paging system. /// This provides an abstraction over most paging modes in common architectures. -pub(crate) trait PagingConstsTrait: Clone + Debug + Default + Sync + 'static { +pub(crate) trait PagingConstsTrait: Clone + Debug + Default + Send + Sync + 'static { /// The smallest page size. /// This is also the page size at level 1 page tables. const BASE_PAGE_SIZE: usize; diff --git a/ostd/src/mm/page/meta.rs b/ostd/src/mm/page/meta.rs index 7d92dd7cf..cbf7a147e 100644 --- a/ostd/src/mm/page/meta.rs +++ b/ostd/src/mm/page/meta.rs @@ -37,53 +37,31 @@ pub mod mapping { use alloc::vec::Vec; use core::{ + any::Any, cell::UnsafeCell, - marker::PhantomData, - mem::{size_of, ManuallyDrop}, - panic, - sync::atomic::{AtomicU32, AtomicU8, Ordering}, + mem::size_of, + sync::atomic::{AtomicU32, Ordering}, }; use log::info; -use num_derive::FromPrimitive; use static_assertions::const_assert_eq; use super::{allocator, Page}; use crate::{ - arch::mm::{PageTableEntry, PagingConsts}, + arch::mm::PagingConsts, mm::{ - paddr_to_vaddr, page_size, - page_table::{boot_pt, PageTableEntryTrait}, - CachePolicy, Paddr, PageFlags, PageProperty, PagingConstsTrait, PagingLevel, - PrivilegedPageFlags, Vaddr, PAGE_SIZE, + paddr_to_vaddr, page_size, page_table::boot_pt, CachePolicy, Paddr, PageFlags, + PageProperty, PrivilegedPageFlags, Vaddr, PAGE_SIZE, }, }; -/// Represents the usage of a page. -#[repr(u8)] -#[derive(Debug, FromPrimitive, PartialEq)] -pub enum PageUsage { - // The zero variant is reserved for the unused type. Only an unused page - // can be designated for one of the other purposes. - #[allow(dead_code)] - Unused = 0, - /// The page is reserved or unusable. The kernel should not touch it. - #[allow(dead_code)] - Reserved = 1, +/// The maximum number of bytes of the metadata of a page. +pub const PAGE_METADATA_MAX_SIZE: usize = + META_SLOT_SIZE - size_of::() - size_of::(); +/// The maximum alignment in bytes of the metadata of a page. +pub const PAGE_METADATA_MAX_ALIGN: usize = align_of::(); - /// The page is used as a frame, i.e., a page of untyped memory. - Frame = 32, - - /// The page is used by a page table. - PageTable = 64, - /// The page stores metadata of other pages. - Meta = 65, - /// The page stores the kernel such as kernel code, data, etc. - Kernel = 66, - - /// The page stores data for kernel stack. - KernelStack = 67, -} +const META_SLOT_SIZE: usize = 64; #[repr(C)] pub(in crate::mm) struct MetaSlot { @@ -92,41 +70,58 @@ pub(in crate::mm) struct MetaSlot { /// It is placed at the beginning of a slot because: /// - the implementation can simply cast a `*const MetaSlot` /// to a `*const PageMeta` for manipulation; - /// - the subsequent fields can utilize the end padding of the - /// the inner union to save space. - _inner: MetaSlotInner, - /// To store [`PageUsage`]. - pub(super) usage: AtomicU8, + /// - if the metadata need special alignment, we can provide + /// at most `PAGE_METADATA_ALIGN` bytes of alignment; + /// - the subsequent fields can utilize the padding of the + /// reference count to save space. + storage: UnsafeCell<[u8; PAGE_METADATA_MAX_SIZE]>, + /// The reference count of the page. pub(super) ref_count: AtomicU32, + /// The virtual table that indicates the type of the metadata. + pub(super) vtable_ptr: UnsafeCell, } -pub(super) union MetaSlotInner { - _frame: ManuallyDrop, - _pt: ManuallyDrop, -} +type PageMetaVtablePtr = core::ptr::DynMetadata; -// Currently the sizes of the `MetaSlotInner` union variants are no larger -// than 8 bytes and aligned to 8 bytes. So the size of `MetaSlot` is 16 bytes. -// -// Note that the size of `MetaSlot` should be a multiple of 8 bytes to prevent -// cross-page accesses. -const_assert_eq!(size_of::(), 16); +const_assert_eq!(PAGE_SIZE % META_SLOT_SIZE, 0); +const_assert_eq!(size_of::(), META_SLOT_SIZE); -/// All page metadata types must implemented this sealed trait, -/// which ensures that each fields of `PageUsage` has one and only -/// one metadata type corresponding to the usage purpose. Any user -/// outside this module won't be able to add more metadata types -/// and break assumptions made by this module. +/// All page metadata types must implement this trait. /// /// If a page type needs specific drop behavior, it should specify /// when implementing this trait. When we drop the last handle to -/// this page, the `on_drop` method will be called. -pub trait PageMeta: Sync + private::Sealed + Sized { - const USAGE: PageUsage; - - fn on_drop(page: &mut Page); +/// this page, the `on_drop` method will be called. The `on_drop` +/// method is called with the physical address of the page. +/// +/// # Safety +/// +/// The implemented structure must have a size less than or equal to +/// [`PAGE_METADATA_MAX_SIZE`] and an alignment less than or equal to +/// [`PAGE_METADATA_MAX_ALIGN`]. +pub unsafe trait PageMeta: Any + Send + Sync + 'static { + fn on_drop(&mut self, _paddr: Paddr) {} } +/// Makes a structure usable as a page metadata. +/// +/// Directly implementing [`PageMeta`] is not safe since the size and alignment +/// must be checked. This macro provides a safe way to implement the trait with +/// compile-time checks. +#[macro_export] +macro_rules! impl_page_meta { + ($($t:ty),*) => { + $( + use static_assertions::const_assert; + const_assert!(size_of::<$t>() <= $crate::mm::page::meta::PAGE_METADATA_MAX_SIZE); + const_assert!(align_of::<$t>() <= $crate::mm::page::meta::PAGE_METADATA_MAX_ALIGN); + // SAFETY: The size and alignment of the structure are checked. + unsafe impl $crate::mm::page::meta::PageMeta for $t {} + )* + }; +} + +pub use impl_page_meta; + /// An internal routine in dropping implementations. /// /// # Safety @@ -134,154 +129,35 @@ pub trait PageMeta: Sync + private::Sealed + Sized { /// The caller should ensure that the pointer points to a page's metadata slot. The /// page should have a last handle to the page, and the page is about to be dropped, /// as the metadata slot after this operation becomes uninitialized. -pub(super) unsafe fn drop_as_last(ptr: *const MetaSlot) { +pub(super) unsafe fn drop_last_in_place(ptr: *mut MetaSlot) { // This would be guaranteed as a safety requirement. debug_assert_eq!((*ptr).ref_count.load(Ordering::Relaxed), 0); + + let paddr = mapping::meta_to_page::(ptr as Vaddr); + + let meta_ptr: *mut dyn PageMeta = core::ptr::from_raw_parts_mut(ptr, *(*ptr).vtable_ptr.get()); + // Let the custom dropper handle the drop. - let mut page = Page:: { - ptr, - _marker: PhantomData, - }; - M::on_drop(&mut page); - let _ = ManuallyDrop::new(page); + (*meta_ptr).on_drop(paddr); + // Drop the metadata. - core::ptr::drop_in_place(ptr as *mut M); - // No handles means no usage. This also releases the page as unused for further - // calls to `Page::from_unused`. - (*ptr).usage.store(0, Ordering::Release); + core::ptr::drop_in_place(meta_ptr); + // Deallocate the page. // It would return the page to the allocator for further use. This would be done // after the release of the metadata to avoid re-allocation before the metadata // is reset. - allocator::PAGE_ALLOCATOR.get().unwrap().lock().dealloc( - mapping::meta_to_page::(ptr as Vaddr) / PAGE_SIZE, - 1, - ); + allocator::PAGE_ALLOCATOR + .get() + .unwrap() + .lock() + .dealloc(paddr / PAGE_SIZE, 1); } - -mod private { - pub trait Sealed {} -} - -// ======= Start of all the specific metadata structures definitions ========== - -use private::Sealed; - +/// The metadata of pages that holds metadata of pages. #[derive(Debug, Default)] -#[repr(C)] -pub struct FrameMeta { - // If not doing so, the page table metadata would fit - // in the front padding of meta slot and make it 12 bytes. - // We make it 16 bytes. Further usage of frame metadata - // is welcome to exploit this space. - _unused_for_layout_padding: [u8; 8], -} - -impl Sealed for FrameMeta {} - -/// The metadata of any kinds of page table pages. -/// Make sure the the generic parameters don't effect the memory layout. -#[derive(Debug)] -#[repr(C)] -pub(in crate::mm) struct PageTablePageMeta< - E: PageTableEntryTrait = PageTableEntry, - C: PagingConstsTrait = PagingConsts, -> where - [(); C::NR_LEVELS as usize]:, -{ - /// The number of valid PTEs. It is mutable if the lock is held. - pub nr_children: UnsafeCell, - /// The level of the page table page. A page table page cannot be - /// referenced by page tables of different levels. - pub level: PagingLevel, - /// Whether the pages mapped by the node is tracked. - pub is_tracked: MapTrackingStatus, - /// The lock for the page table page. - pub lock: AtomicU8, - _phantom: core::marker::PhantomData<(E, C)>, -} - -/// Describe if the physical address recorded in this page table refers to a -/// page tracked by metadata. -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -#[repr(u8)] -pub(in crate::mm) enum MapTrackingStatus { - /// The page table node cannot contain references to any pages. It can only - /// contain references to child page table nodes. - NotApplicable, - /// The mapped pages are not tracked by metadata. If any child page table - /// nodes exist, they should also be tracked. - Untracked, - /// The mapped pages are tracked by metadata. If any child page table nodes - /// exist, they should also be tracked. - Tracked, -} - -impl PageTablePageMeta -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 Sealed for PageTablePageMeta where - [(); C::NR_LEVELS as usize]: -{ -} - -unsafe impl Send for PageTablePageMeta where - [(); C::NR_LEVELS as usize]: -{ -} - -unsafe impl Sync for PageTablePageMeta 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) { - 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) { - panic!("Kernel pages are not allowed to be dropped"); - } -} - -#[derive(Debug, Default)] -#[repr(C)] -pub struct KernelStackMeta {} - -impl Sealed for KernelStackMeta {} -impl PageMeta for KernelStackMeta { - const USAGE: PageUsage = PageUsage::KernelStack; - fn on_drop(_page: &mut Page) {} -} - -// ======== End of all the specific metadata structures definitions =========== +impl_page_meta!(MetaPageMeta); /// Initializes the metadata of all physical pages. /// diff --git a/ostd/src/mm/page/mod.rs b/ostd/src/mm/page/mod.rs index 9d9e14b70..67007b15c 100644 --- a/ostd/src/mm/page/mod.rs +++ b/ostd/src/mm/page/mod.rs @@ -19,16 +19,16 @@ mod cont_pages; pub mod meta; use core::{ + any::Any, marker::PhantomData, mem::ManuallyDrop, - panic, sync::atomic::{AtomicU32, AtomicUsize, Ordering}, }; pub use cont_pages::ContPages; -use meta::{mapping, FrameMeta, MetaSlot, PageMeta, PageUsage}; +use meta::{mapping, MetaSlot, PageMeta, PAGE_METADATA_MAX_ALIGN, PAGE_METADATA_MAX_SIZE}; -use super::{Frame, PagingLevel, PAGE_SIZE}; +use super::{frame::FrameMeta, Frame, PagingLevel, PAGE_SIZE}; use crate::mm::{Paddr, PagingConsts, Vaddr}; static MAX_PADDR: AtomicUsize = AtomicUsize::new(0); @@ -41,6 +41,7 @@ pub struct Page { } unsafe impl Send for Page {} + unsafe impl Sync for Page {} impl Page { @@ -56,20 +57,26 @@ impl Page { pub fn from_unused(paddr: Paddr, metadata: M) -> Self { assert!(paddr % PAGE_SIZE == 0); assert!(paddr < MAX_PADDR.load(Ordering::Relaxed) as Paddr); + + // Checking unsafe preconditions of the `PageMeta` trait. + debug_assert!(size_of::() <= PAGE_METADATA_MAX_SIZE); + debug_assert!(align_of::() <= PAGE_METADATA_MAX_ALIGN); + let vaddr = mapping::page_to_meta::(paddr); let ptr = vaddr as *const MetaSlot; - // SAFETY: The aligned pointer points to an initialized `MetaSlot`. - let usage = unsafe { &(*ptr).usage }; - // SAFETY: The aligned pointer points to an initialized `MetaSlot`. + // SAFETY: The aligned pointer points to a initialized `MetaSlot`. 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"); + ref_count + .compare_exchange(0, 1, Ordering::Acquire, 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); + // SAFETY: The aligned pointer points to a initialized `MetaSlot`. + let vtable_ptr = unsafe { (*ptr).vtable_ptr.get() }; + + // SAFETY: The pointer is valid and we have the exclusive access. + unsafe { vtable_ptr.write(core::ptr::metadata(&metadata as &dyn PageMeta)) }; // Initialize the metadata // SAFETY: The pointer points to the first byte of the `MetaSlot` @@ -183,9 +190,10 @@ impl Drop for Page { // A fence is needed here with the same reasons stated in the implementation of // `Arc::drop`: . core::sync::atomic::fence(Ordering::Acquire); + // SAFETY: this is the last reference and is about to be dropped. unsafe { - meta::drop_as_last::(self.ptr); + meta::drop_last_in_place(self.ptr as *mut MetaSlot); } } } @@ -229,6 +237,18 @@ impl DynPage { Self { ptr } } + /// Get the metadata of this page. + pub fn meta(&self) -> &dyn Any { + // SAFETY: The pointer is valid and no other writes will be done to it. + let vtable_ptr = unsafe { *(*self.ptr).vtable_ptr.get() }; + + let meta_ptr: *const dyn PageMeta = core::ptr::from_raw_parts(self.ptr, vtable_ptr); + + // SAFETY: The pointer is valid and the type is correct for the stored + // metadata. + (unsafe { &*meta_ptr }) as &dyn Any + } + /// Get the physical address of the start of the page pub fn paddr(&self) -> Paddr { mapping::meta_to_page::(self.ptr as Vaddr) @@ -244,14 +264,6 @@ impl DynPage { PAGE_SIZE } - /// Get the usage of the page. - pub fn usage(&self) -> PageUsage { - // SAFETY: structure is safely created with a pointer that points - // to initialized [`MetaSlot`] memory. - 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 } } @@ -265,7 +277,7 @@ impl TryFrom for Page { /// If the usage of the page is not the same as the expected usage, it will /// return the dynamic page itself as is. fn try_from(dyn_page: DynPage) -> Result { - if dyn_page.usage() == M::USAGE { + if dyn_page.meta().is::() { let result = Page { ptr: dyn_page.ptr, _marker: PhantomData, @@ -307,28 +319,10 @@ impl Drop for DynPage { // A fence is needed here with the same reasons stated in the implementation of // `Arc::drop`: . core::sync::atomic::fence(Ordering::Acquire); - // Drop the page and its metadata according to its usage. - // SAFETY: all `drop_as_last` calls in match arms operates on a last, about to be - // dropped page reference. + + // SAFETY: this is the last reference and is about to be dropped. unsafe { - match self.usage() { - PageUsage::Frame => { - meta::drop_as_last::(self.ptr); - } - PageUsage::PageTable => { - meta::drop_as_last::(self.ptr); - } - PageUsage::KernelStack => { - meta::drop_as_last::(self.ptr); - } - // The following pages don't have metadata and can't be dropped. - PageUsage::Unused - | PageUsage::Reserved - | PageUsage::Kernel - | PageUsage::Meta => { - panic!("dropping a dynamic page with usage {:?}", self.usage()); - } - } + meta::drop_last_in_place(self.ptr as *mut MetaSlot); } } } diff --git a/ostd/src/mm/page_table/cursor.rs b/ostd/src/mm/page_table/cursor.rs index b6dcedccd..24109b9f3 100644 --- a/ostd/src/mm/page_table/cursor.rs +++ b/ostd/src/mm/page_table/cursor.rs @@ -70,15 +70,13 @@ use core::{any::TypeId, marker::PhantomData, mem::ManuallyDrop, ops::Range}; use align_ext::AlignExt; use super::{ - page_size, pte_index, Child, Entry, KernelMode, PageTable, PageTableEntryTrait, PageTableError, - PageTableMode, PageTableNode, PagingConstsTrait, PagingLevel, RawPageTableNode, UserMode, + page_size, pte_index, Child, Entry, KernelMode, MapTrackingStatus, PageTable, + PageTableEntryTrait, PageTableError, PageTableMode, PageTableNode, PagingConstsTrait, + PagingLevel, RawPageTableNode, UserMode, }; use crate::{ mm::{ - kspace::should_map_as_tracked, - paddr_to_vaddr, - page::{meta::MapTrackingStatus, DynPage}, - Paddr, PageProperty, Vaddr, + kspace::should_map_as_tracked, paddr_to_vaddr, page::DynPage, Paddr, PageProperty, Vaddr, }, task::{disable_preempt, DisabledPreemptGuard}, }; diff --git a/ostd/src/mm/page_table/mod.rs b/ostd/src/mm/page_table/mod.rs index 217076573..793f6c836 100644 --- a/ostd/src/mm/page_table/mod.rs +++ b/ostd/src/mm/page_table/mod.rs @@ -3,8 +3,8 @@ use core::{fmt::Debug, marker::PhantomData, ops::Range}; use super::{ - nr_subpage_per_huge, page::meta::MapTrackingStatus, page_prop::PageProperty, page_size, Paddr, - PagingConstsTrait, PagingLevel, Vaddr, + nr_subpage_per_huge, page_prop::PageProperty, page_size, Paddr, PagingConstsTrait, PagingLevel, + Vaddr, }; use crate::{ arch::mm::{PageTableEntry, PagingConsts}, @@ -338,7 +338,9 @@ pub(super) unsafe fn page_walk( /// The interface for defining architecture-specific page table entries. /// /// Note that a default PTE should be a PTE that points to nothing. -pub trait PageTableEntryTrait: Clone + Copy + Debug + Default + Pod + Sized + Sync { +pub trait PageTableEntryTrait: + Clone + Copy + Debug + Default + Pod + Sized + Send + Sync + 'static +{ /// Create a set of new invalid page table flags that indicates an absent page. /// /// Note that currently the implementation requires an all zero PTE to be an absent PTE. diff --git a/ostd/src/mm/page_table/node/child.rs b/ostd/src/mm/page_table/node/child.rs index 4a6e9bd4d..af8238e29 100644 --- a/ostd/src/mm/page_table/node/child.rs +++ b/ostd/src/mm/page_table/node/child.rs @@ -4,11 +4,11 @@ use core::{mem::ManuallyDrop, panic}; -use super::{PageTableEntryTrait, RawPageTableNode}; +use super::{MapTrackingStatus, PageTableEntryTrait, RawPageTableNode}; use crate::{ arch::mm::{PageTableEntry, PagingConsts}, mm::{ - page::{inc_page_ref_count, meta::MapTrackingStatus, DynPage}, + page::{inc_page_ref_count, DynPage}, page_prop::PageProperty, Paddr, PagingConstsTrait, PagingLevel, }, diff --git a/ostd/src/mm/page_table/node/entry.rs b/ostd/src/mm/page_table/node/entry.rs index b96a6c1c7..e497aeedd 100644 --- a/ostd/src/mm/page_table/node/entry.rs +++ b/ostd/src/mm/page_table/node/entry.rs @@ -2,11 +2,8 @@ //! This module provides accessors to the page table entries in a node. -use super::{Child, PageTableEntryTrait, PageTableNode}; -use crate::mm::{ - nr_subpage_per_huge, page::meta::MapTrackingStatus, page_prop::PageProperty, page_size, - PagingConstsTrait, -}; +use super::{Child, MapTrackingStatus, PageTableEntryTrait, PageTableNode}; +use crate::mm::{nr_subpage_per_huge, page_prop::PageProperty, page_size, PagingConstsTrait}; /// A view of an entry in a page table node. /// diff --git a/ostd/src/mm/page_table/node/mod.rs b/ostd/src/mm/page_table/node/mod.rs index f5ebf46ba..27be8a5e6 100644 --- a/ostd/src/mm/page_table/node/mod.rs +++ b/ostd/src/mm/page_table/node/mod.rs @@ -28,7 +28,12 @@ mod child; mod entry; -use core::{marker::PhantomData, mem::ManuallyDrop, sync::atomic::Ordering}; +use core::{ + cell::SyncUnsafeCell, + marker::PhantomData, + mem::ManuallyDrop, + sync::atomic::{AtomicU8, Ordering}, +}; pub(in crate::mm) use self::{child::Child, entry::Entry}; use super::{nr_subpage_per_huge, PageTableEntryTrait}; @@ -36,11 +41,7 @@ use crate::{ arch::mm::{PageTableEntry, PagingConsts}, mm::{ paddr_to_vaddr, - page::{ - self, inc_page_ref_count, - meta::{MapTrackingStatus, PageMeta, PageTablePageMeta, PageUsage}, - DynPage, Page, - }, + page::{self, inc_page_ref_count, meta::PageMeta, DynPage, Page}, Paddr, PagingConstsTrait, PagingLevel, PAGE_SIZE, }, }; @@ -352,23 +353,73 @@ where } } -impl PageMeta for PageTablePageMeta +/// The metadata of any kinds of page table pages. +/// Make sure the the generic parameters don't effect the memory layout. +#[derive(Debug)] +pub(in crate::mm) struct PageTablePageMeta< + E: PageTableEntryTrait = PageTableEntry, + C: PagingConstsTrait = PagingConsts, +> where + [(); C::NR_LEVELS as usize]:, +{ + /// The number of valid PTEs. It is mutable if the lock is held. + pub nr_children: SyncUnsafeCell, + /// The level of the page table page. A page table page cannot be + /// referenced by page tables of different levels. + pub level: PagingLevel, + /// The lock for the page table page. + pub lock: AtomicU8, + /// Whether the pages mapped by the node is tracked. + pub is_tracked: MapTrackingStatus, + _phantom: core::marker::PhantomData<(E, C)>, +} + +/// Describe if the physical address recorded in this page table refers to a +/// page tracked by metadata. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[repr(u8)] +pub(in crate::mm) enum MapTrackingStatus { + /// The page table node cannot contain references to any pages. It can only + /// contain references to child page table nodes. + NotApplicable, + /// The mapped pages are not tracked by metadata. If any child page table + /// nodes exist, they should also be tracked. + Untracked, + /// The mapped pages are tracked by metadata. If any child page table nodes + /// exist, they should also be tracked. + Tracked, +} + +impl PageTablePageMeta where [(); C::NR_LEVELS as usize]:, { - const USAGE: PageUsage = PageUsage::PageTable; + pub fn new_locked(level: PagingLevel, is_tracked: MapTrackingStatus) -> Self { + Self { + nr_children: SyncUnsafeCell::new(0), + level, + lock: AtomicU8::new(1), + is_tracked, + _phantom: PhantomData, + } + } +} - fn on_drop(page: &mut Page) { - // SAFETY: This is the last reference so we have an exclusive access. - let nr_children = unsafe { *page.meta().nr_children.get() }; +// SAFETY: The layout of the `PageTablePageMeta` is ensured to be the same for +// all possible generic parameters. And the layout fits the requirements. +unsafe impl PageMeta for PageTablePageMeta +where + [(); C::NR_LEVELS as usize]:, +{ + fn on_drop(&mut self, paddr: Paddr) { + let nr_children = self.nr_children.get_mut(); - if nr_children == 0 { + if *nr_children == 0 { return; } - let paddr = page.paddr(); - let level = page.meta().level; - let is_tracked = page.meta().is_tracked; + let level = self.level; + let is_tracked = self.is_tracked; // Drop the children. for i in 0..nr_subpage_per_huge::() { diff --git a/ostd/src/mm/page_table/test.rs b/ostd/src/mm/page_table/test.rs index 3cfade4ef..6e27aa1e7 100644 --- a/ostd/src/mm/page_table/test.rs +++ b/ostd/src/mm/page_table/test.rs @@ -5,8 +5,9 @@ use core::mem::ManuallyDrop; use super::*; use crate::{ mm::{ + frame::FrameMeta, kspace::LINEAR_MAPPING_BASE_VADDR, - page::{allocator, meta::FrameMeta}, + page::allocator, page_prop::{CachePolicy, PageFlags}, MAX_USERSPACE_VADDR, }, diff --git a/ostd/src/task/kernel_stack.rs b/ostd/src/task/kernel_stack.rs index e75a3fcb1..9d916c198 100644 --- a/ostd/src/task/kernel_stack.rs +++ b/ostd/src/task/kernel_stack.rs @@ -1,9 +1,10 @@ // SPDX-License-Identifier: MPL-2.0 use crate::{ + impl_page_meta, mm::{ kspace::kvirt_area::{KVirtArea, Tracked}, - page::{allocator, meta::KernelStackMeta}, + page::allocator, page_prop::{CachePolicy, PageFlags, PageProperty, PrivilegedPageFlags}, PAGE_SIZE, }, @@ -34,6 +35,11 @@ pub struct KernelStack { has_guard_page: bool, } +#[derive(Debug, Default)] +struct KernelStackMeta {} + +impl_page_meta!(KernelStackMeta); + impl KernelStack { /// Generates a kernel stack with guard pages. /// 4 additional pages are allocated and regarded as guard pages, which should not be accessed.