ostd/mm/frame/mod.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328
// 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 [`AnyUFrameMeta`]
//! 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};
use safety::safety;
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<M: AnyFrameMeta + ?Sized> {
ptr: *const MetaSlot,
_marker: PhantomData<M>,
}
unsafe impl<M: AnyFrameMeta + ?Sized> Send for Frame<M> {}
unsafe impl<M: AnyFrameMeta + ?Sized> Sync for Frame<M> {}
impl<M: AnyFrameMeta + ?Sized> core::fmt::Debug for Frame<M> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "Frame({:#x})", self.start_paddr())
}
}
impl<M: AnyFrameMeta + ?Sized> PartialEq for Frame<M> {
fn eq(&self, other: &Self) -> bool {
self.start_paddr() == other.start_paddr()
}
}
impl<M: AnyFrameMeta + ?Sized> Eq for Frame<M> {}
impl<M: AnyFrameMeta> Frame<M> {
/// 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<Self, GetFrameError> {
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::<M>() }
}
}
impl Frame<dyn AnyFrameMeta> {
/// 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<Self, GetFrameError> {
Ok(Self {
ptr: MetaSlot::get_from_in_use(paddr)?,
_marker: PhantomData,
})
}
}
impl<M: AnyFrameMeta + ?Sized> Frame<M> {
/// 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<dyn AnyFrameMeta>`]), 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 {
RefForgotten("The frame") : "For the frame pointed by paddr"
}]
pub(in crate::mm) unsafe fn from_raw(paddr: Paddr) -> Self {
debug_assert!(paddr < max_paddr());
let vaddr = mapping::frame_to_meta::<PagingConsts>(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<M: AnyFrameMeta + ?Sized> Clone for Frame<M> {
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<M: AnyFrameMeta + ?Sized> Drop for Frame<M> {
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`: <https://doc.rust-lang.org/std/sync/struct.Arc.html#method.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<M: AnyFrameMeta> TryFrom<Frame<dyn AnyFrameMeta>> for Frame<M> {
type Error = Frame<dyn AnyFrameMeta>;
/// Tries converting a [`Frame<dyn AnyFrameMeta>`] 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<dyn AnyFrameMeta>) -> Result<Self, Self::Error> {
if (dyn_frame.dyn_meta() as &dyn core::any::Any).is::<M>() {
// SAFETY: The metadata is coerceable and the struct is transmutable.
Ok(unsafe { core::mem::transmute::<Frame<dyn AnyFrameMeta>, Frame<M>>(dyn_frame) })
} else {
Err(dyn_frame)
}
}
}
impl<M: AnyFrameMeta> From<Frame<M>> for Frame<dyn AnyFrameMeta> {
fn from(frame: Frame<M>) -> Self {
// SAFETY: The metadata is coerceable and the struct is transmutable.
unsafe { core::mem::transmute(frame) }
}
}
impl<M: AnyUFrameMeta> From<Frame<M>> for UFrame {
fn from(frame: Frame<M>) -> Self {
// SAFETY: The metadata is coerceable and the struct is transmutable.
unsafe { core::mem::transmute(frame) }
}
}
impl From<UFrame> for Frame<dyn AnyFrameMeta> {
fn from(frame: UFrame) -> Self {
// SAFETY: The metadata is coerceable and the struct is transmutable.
unsafe { core::mem::transmute(frame) }
}
}
impl TryFrom<Frame<dyn AnyFrameMeta>> for UFrame {
type Error = Frame<dyn AnyFrameMeta>;
/// Tries converting a [`Frame<dyn AnyFrameMeta>`] 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<dyn AnyFrameMeta>) -> Result<Self, Self::Error> {
if dyn_frame.dyn_meta().is_untyped() {
// SAFETY: The metadata is coerceable and the struct is transmutable.
Ok(unsafe { core::mem::transmute::<Frame<dyn AnyFrameMeta>, UFrame>(dyn_frame) })
} else {
Err(dyn_frame)
}
}
}
/// Increases the reference count of the frame by one.
#[safety {
Valid("The frame"),
RefHeld("The frame") : "For a frame derived from paddr"
}]
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::<PagingConsts>(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() };
}