// SPDX-License-Identifier: MPL-2.0 //! Frame (physical memory page) management. //! //! A frame is an aligned, contiguous range of bytes in physical memory. The //! sizes of base frames and huge frames (that are mapped as "huge pages") are //! architecture-dependent. A frame can be mapped to virtual address spaces //! using the page table. //! //! Frames can be accessed through frame handles, namely, [`Frame`]. A frame //! handle is a reference-counted pointer to a frame. When all handles to a //! frame are dropped, the frame is released and can be reused. Contiguous //! frames are managed with [`Segment`]. //! //! There are various kinds of frames. The top-level grouping of frame kinds //! are "typed" frames and "untyped" frames. Typed frames host Rust objects //! that must follow the visibility, lifetime and borrow rules of Rust, thus //! not being able to be directly manipulated. Untyped frames are raw memory //! that can be manipulated directly. So only untyped frames can be //! - safely shared to external entities such as device drivers or user-space //! applications. //! - or directly manipulated with readers and writers that neglect Rust's //! "alias XOR mutability" rule. //! //! The kind of a frame is determined by the type of its metadata. Untyped //! frames have its metadata type that implements the [`UntypedFrameMeta`] //! trait, while typed frames don't. //! //! Frames can have dedicated metadata, which is implemented in the [`meta`] //! module. The reference count and usage of a frame are stored in the metadata //! as well, leaving the handle only a pointer to the metadata slot. Users //! can create custom metadata types by implementing the [`AnyFrameMeta`] trait. pub mod allocator; pub mod linked_list; pub mod meta; pub mod segment; pub mod unique; pub mod untyped; mod frame_ref; pub use frame_ref::FrameRef; #[cfg(ktest)] mod test; use core::{ marker::PhantomData, mem::ManuallyDrop, sync::atomic::{AtomicUsize, Ordering}, }; pub use allocator::GlobalFrameAllocator; use meta::{mapping, AnyFrameMeta, GetFrameError, MetaSlot, REF_COUNT_UNUSED}; pub use segment::Segment; use untyped::{AnyUFrameMeta, UFrame}; use super::{PagingLevel, PAGE_SIZE}; use crate::mm::{Paddr, PagingConsts, Vaddr}; static MAX_PADDR: AtomicUsize = AtomicUsize::new(0); /// Returns the maximum physical address that is tracked by frame metadata. pub(in crate::mm) fn max_paddr() -> Paddr { let max_paddr = MAX_PADDR.load(Ordering::Relaxed) as Paddr; debug_assert_ne!(max_paddr, 0); max_paddr } /// A smart pointer to a frame. /// /// A frame is a contiguous range of bytes in physical memory. The [`Frame`] /// type is a smart pointer to a frame that is reference-counted. /// /// Frames are associated with metadata. The type of the metadata `M` is /// determines the kind of the frame. If `M` implements [`AnyUFrameMeta`], the /// frame is a untyped frame. Otherwise, it is a typed frame. #[repr(transparent)] pub struct Frame { ptr: *const MetaSlot, _marker: PhantomData, } unsafe impl Send for Frame {} unsafe impl Sync for Frame {} impl core::fmt::Debug for Frame { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { write!(f, "Frame({:#x})", self.start_paddr()) } } impl Frame { /// Gets a [`Frame`] with a specific usage from a raw, unused page. /// /// The caller should provide the initial metadata of the page. /// /// If the provided frame is not truly unused at the moment, it will return /// an error. If wanting to acquire a frame that is already in use, use /// [`Frame::from_in_use`] instead. pub fn from_unused(paddr: Paddr, metadata: M) -> Result { Ok(Self { ptr: MetaSlot::get_from_unused(paddr, metadata, false)?, _marker: PhantomData, }) } /// Gets the metadata of this page. pub fn meta(&self) -> &M { // SAFETY: The type is tracked by the type system. unsafe { &*self.slot().as_meta_ptr::() } } } impl Frame { /// Gets a dynamically typed [`Frame`] from a raw, in-use page. /// /// If the provided frame is not in use at the moment, it will return an error. /// /// The returned frame will have an extra reference count to the frame. pub fn from_in_use(paddr: Paddr) -> Result { Ok(Self { ptr: MetaSlot::get_from_in_use(paddr)?, _marker: PhantomData, }) } } impl Frame { /// Gets the physical address of the start of the frame. pub fn start_paddr(&self) -> Paddr { self.slot().frame_paddr() } /// Gets the map level of this page. /// /// This is the level of the page table entry that maps the frame, /// which determines the size of the frame. /// /// Currently, the level is always 1, which means the frame is a regular /// page frame. pub const fn map_level(&self) -> PagingLevel { 1 } /// Gets the size of this page in bytes. pub const fn size(&self) -> usize { PAGE_SIZE } /// Gets the dyncamically-typed metadata of this frame. /// /// If the type is known at compile time, use [`Frame::meta`] instead. pub fn dyn_meta(&self) -> &dyn AnyFrameMeta { // SAFETY: The metadata is initialized and valid. unsafe { &*self.slot().dyn_meta_ptr() } } /// Gets the reference count of the frame. /// /// It returns the number of all references to the frame, including all the /// existing frame handles ([`Frame`], [`Frame`]), and all /// the mappings in the page table that points to the frame. /// /// # Safety /// /// The function is safe to call, but using it requires extra care. The /// reference count can be changed by other threads at any time including /// potentially between calling this method and acting on the result. pub fn reference_count(&self) -> u64 { let refcnt = self.slot().ref_count.load(Ordering::Relaxed); debug_assert!(refcnt < meta::REF_COUNT_MAX); refcnt } /// Borrows a reference from the given frame. pub fn borrow(&self) -> FrameRef<'_, M> { // SAFETY: Both the lifetime and the type matches `self`. unsafe { FrameRef::borrow_paddr(self.start_paddr()) } } /// Forgets the handle to the frame. /// /// This will result in the frame being leaked without calling the custom dropper. /// /// A physical address to the frame is returned in case the frame needs to be /// restored using [`Frame::from_raw`] later. This is useful when some architectural /// data structures need to hold the frame handle such as the page table. pub(in crate::mm) fn into_raw(self) -> Paddr { let this = ManuallyDrop::new(self); this.start_paddr() } /// Restores a forgotten [`Frame`] from a physical address. /// /// # Safety /// /// The caller should only restore a `Frame` that was previously forgotten using /// [`Frame::into_raw`]. /// /// And the restoring operation should only be done once for a forgotten /// [`Frame`]. Otherwise double-free will happen. /// /// Also, the caller ensures that the usage of the frame is correct. There's /// no checking of the usage in this function. pub(in crate::mm) unsafe fn from_raw(paddr: Paddr) -> Self { let vaddr = mapping::frame_to_meta::(paddr); let ptr = vaddr as *const MetaSlot; Self { ptr, _marker: PhantomData, } } fn slot(&self) -> &MetaSlot { // SAFETY: `ptr` points to a valid `MetaSlot` that will never be // mutably borrowed, so taking an immutable reference to it is safe. unsafe { &*self.ptr } } } impl Clone for Frame { fn clone(&self) -> Self { // SAFETY: We have already held a reference to the frame. unsafe { self.slot().inc_ref_count() }; Self { ptr: self.ptr, _marker: PhantomData, } } } impl Drop for Frame { fn drop(&mut self) { let last_ref_cnt = self.slot().ref_count.fetch_sub(1, Ordering::Release); debug_assert!(last_ref_cnt != 0 && last_ref_cnt != REF_COUNT_UNUSED); if last_ref_cnt == 1 { // 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 { self.slot().drop_last_in_place() }; allocator::get_global_frame_allocator().dealloc(self.start_paddr(), PAGE_SIZE); } } } impl TryFrom> for Frame { type Error = Frame; /// Tries converting a [`Frame`] into the statically-typed [`Frame`]. /// /// If the usage of the frame is not the same as the expected usage, it will /// return the dynamic frame itself as is. fn try_from(dyn_frame: Frame) -> Result { if (dyn_frame.dyn_meta() as &dyn core::any::Any).is::() { // SAFETY: The metadata is coerceable and the struct is transmutable. Ok(unsafe { core::mem::transmute::, Frame>(dyn_frame) }) } else { Err(dyn_frame) } } } impl From> for Frame { fn from(frame: Frame) -> Self { // SAFETY: The metadata is coerceable and the struct is transmutable. unsafe { core::mem::transmute(frame) } } } impl From> for UFrame { fn from(frame: Frame) -> Self { // SAFETY: The metadata is coerceable and the struct is transmutable. unsafe { core::mem::transmute(frame) } } } impl From for Frame { fn from(frame: UFrame) -> Self { // SAFETY: The metadata is coerceable and the struct is transmutable. unsafe { core::mem::transmute(frame) } } } impl TryFrom> for UFrame { type Error = Frame; /// Tries converting a [`Frame`] into [`UFrame`]. /// /// If the usage of the frame is not the same as the expected usage, it will /// return the dynamic frame itself as is. fn try_from(dyn_frame: Frame) -> Result { if dyn_frame.dyn_meta().is_untyped() { // SAFETY: The metadata is coerceable and the struct is transmutable. Ok(unsafe { core::mem::transmute::, UFrame>(dyn_frame) }) } else { Err(dyn_frame) } } } /// Increases the reference count of the frame by one. /// /// # Safety /// /// The caller should ensure the following conditions: /// 1. The physical address must represent a valid frame; /// 2. The caller must have already held a reference to the frame. pub(in crate::mm) unsafe fn inc_frame_ref_count(paddr: Paddr) { debug_assert!(paddr % PAGE_SIZE == 0); debug_assert!(paddr < max_paddr()); let vaddr: Vaddr = mapping::frame_to_meta::(paddr); // SAFETY: `vaddr` points to a valid `MetaSlot` that will never be mutably borrowed, so taking // an immutable reference to it is always safe. let slot = unsafe { &*(vaddr as *const MetaSlot) }; // SAFETY: We have already held a reference to the frame. unsafe { slot.inc_ref_count() }; }