Skip to main content

ostd/mm/frame/
mod.rs

1// SPDX-License-Identifier: MPL-2.0
2
3//! Frame (physical memory page) management.
4//!
5//! A frame is an aligned, contiguous range of bytes in physical memory. The
6//! sizes of base frames and huge frames (that are mapped as "huge pages") are
7//! architecture-dependent. A frame can be mapped to virtual address spaces
8//! using the page table.
9//!
10//! Frames can be accessed through frame handles, namely, [`Frame`]. A frame
11//! handle is a reference-counted pointer to a frame. When all handles to a
12//! frame are dropped, the frame is released and can be reused.  Contiguous
13//! frames are managed with [`Segment`].
14//!
15//! There are various kinds of frames. The top-level grouping of frame kinds
16//! are "typed" frames and "untyped" frames. Typed frames host Rust objects
17//! that must follow the visibility, lifetime and borrow rules of Rust, thus
18//! not being able to be directly manipulated. Untyped frames are raw memory
19//! that can be manipulated directly. So only untyped frames can be
20//!  - safely shared to external entities such as device drivers or user-space
21//!    applications.
22//!  - or directly manipulated with readers and writers that neglect Rust's
23//!    "alias XOR mutability" rule.
24//!
25//! The kind of a frame is determined by the type of its metadata. Untyped
26//! frames have its metadata type that implements the [`AnyUFrameMeta`]
27//! trait, while typed frames don't.
28//!
29//! Frames can have dedicated metadata, which is implemented in the [`meta`]
30//! module. The reference count and usage of a frame are stored in the metadata
31//! as well, leaving the handle only a pointer to the metadata slot. Users
32//! can create custom metadata types by implementing the [`AnyFrameMeta`] trait.
33
34pub mod allocator;
35pub mod linked_list;
36pub mod meta;
37pub mod segment;
38pub mod unique;
39pub mod untyped;
40
41#[cfg(all(target_arch = "x86_64", feature = "cvm_guest"))]
42pub(crate) mod unaccepted;
43
44mod frame_ref;
45pub use frame_ref::FrameRef;
46
47#[cfg(ktest)]
48mod test;
49
50use core::{
51    marker::PhantomData,
52    mem::ManuallyDrop,
53    sync::atomic::{AtomicUsize, Ordering},
54};
55
56pub use allocator::GlobalFrameAllocator;
57use meta::{AnyFrameMeta, GetFrameError, MetaSlot, REF_COUNT_UNUSED, mapping};
58pub use segment::Segment;
59use untyped::{AnyUFrameMeta, UFrame};
60
61use crate::{
62    mm::{HasPaddr, HasSize, PAGE_SIZE, Paddr, PagingConsts, PagingLevel, Vaddr},
63    sync::RcuDrop,
64};
65
66// These bounds are set once during frame metadata initialization, before APs start.
67static MIN_PADDR: AtomicUsize = AtomicUsize::new(0);
68static MAX_PADDR: AtomicUsize = AtomicUsize::new(0);
69
70/// Returns the minimum physical address that is tracked by frame metadata.
71pub(in crate::mm) fn min_paddr() -> Paddr {
72    MIN_PADDR.load(Ordering::Relaxed)
73}
74
75/// Returns the maximum physical address that is tracked by frame metadata.
76pub(in crate::mm) fn max_paddr() -> Paddr {
77    let max_paddr = MAX_PADDR.load(Ordering::Relaxed) as Paddr;
78    debug_assert_ne!(max_paddr, 0);
79    max_paddr
80}
81
82/// A smart pointer to a frame.
83///
84/// A frame is a contiguous range of bytes in physical memory. The [`Frame`]
85/// type is a smart pointer to a frame that is reference-counted.
86///
87/// Frames are associated with metadata. The type of the metadata `M` is
88/// determines the kind of the frame. If `M` implements [`AnyUFrameMeta`], the
89/// frame is a untyped frame. Otherwise, it is a typed frame.
90#[repr(transparent)]
91pub struct Frame<M: AnyFrameMeta + ?Sized> {
92    ptr: *const MetaSlot,
93    _marker: PhantomData<M>,
94}
95
96unsafe impl<M: AnyFrameMeta + ?Sized> Send for Frame<M> {}
97
98unsafe impl<M: AnyFrameMeta + ?Sized> Sync for Frame<M> {}
99
100impl<M: AnyFrameMeta + ?Sized> core::fmt::Debug for Frame<M> {
101    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
102        write!(f, "Frame({:#x})", self.paddr())
103    }
104}
105
106impl<M: AnyFrameMeta + ?Sized> PartialEq for Frame<M> {
107    fn eq(&self, other: &Self) -> bool {
108        self.paddr() == other.paddr()
109    }
110}
111impl<M: AnyFrameMeta + ?Sized> Eq for Frame<M> {}
112
113impl<M: AnyFrameMeta> Frame<M> {
114    /// Gets a [`Frame`] with a specific usage from a raw, unused page.
115    ///
116    /// The caller should provide the initial metadata of the page.
117    ///
118    /// If the provided frame is not truly unused at the moment, it will return
119    /// an error. If wanting to acquire a frame that is already in use, use
120    /// [`Frame::from_in_use`] instead.
121    pub fn from_unused(paddr: Paddr, metadata: M) -> Result<Self, GetFrameError> {
122        Ok(Self {
123            ptr: MetaSlot::get_from_unused(paddr, metadata, false)?,
124            _marker: PhantomData,
125        })
126    }
127
128    /// Gets the metadata of this page.
129    pub fn meta(&self) -> &M {
130        // SAFETY: The type is tracked by the type system.
131        unsafe { &*self.slot().as_meta_ptr::<M>() }
132    }
133}
134
135impl Frame<dyn AnyFrameMeta> {
136    /// Gets a dynamically typed [`Frame`] from a raw, in-use page.
137    ///
138    /// If the provided frame is not in use at the moment, it will return an error.
139    ///
140    /// The returned frame will have an extra reference count to the frame.
141    pub fn from_in_use(paddr: Paddr) -> Result<Self, GetFrameError> {
142        Ok(Self {
143            ptr: MetaSlot::get_from_in_use(paddr)?,
144            _marker: PhantomData,
145        })
146    }
147}
148
149impl<M: AnyFrameMeta + ?Sized> Frame<M> {
150    /// Gets the map level of this page.
151    ///
152    /// This is the level of the page table entry that maps the frame,
153    /// which determines the size of the frame.
154    ///
155    /// Currently, the level is always 1, which means the frame is a regular
156    /// page frame.
157    pub const fn map_level(&self) -> PagingLevel {
158        1
159    }
160
161    /// Gets the dynamically-typed metadata of this frame.
162    ///
163    /// If the type is known at compile time, use [`Frame::meta`] instead.
164    pub fn dyn_meta(&self) -> &dyn AnyFrameMeta {
165        // SAFETY: The metadata is initialized and valid.
166        unsafe { &*self.slot().dyn_meta_ptr() }
167    }
168
169    /// Gets the reference count of the frame.
170    ///
171    /// It returns the number of all references to the frame, including all the
172    /// existing frame handles ([`Frame`], [`Frame<dyn AnyFrameMeta>`]), and all
173    /// the mappings in the page table that points to the frame.
174    ///
175    /// # Safety
176    ///
177    /// The function is safe to call, but using it requires extra care. The
178    /// reference count can be changed by other threads at any time including
179    /// potentially between calling this method and acting on the result.
180    pub fn reference_count(&self) -> u64 {
181        let refcnt = self.slot().ref_count.load(Ordering::Relaxed);
182        debug_assert!(refcnt < meta::REF_COUNT_MAX);
183        refcnt
184    }
185
186    /// Borrows a reference from the given frame.
187    pub fn borrow(&self) -> FrameRef<'_, M> {
188        // SAFETY: Both the lifetime and the type matches `self`.
189        unsafe { FrameRef::borrow_paddr(self.paddr()) }
190    }
191
192    /// Forgets the handle to the frame.
193    ///
194    /// This will result in the frame being leaked without calling the custom dropper.
195    ///
196    /// A physical address to the frame is returned in case the frame needs to be
197    /// restored using [`Frame::from_raw`] later. This is useful when some architectural
198    /// data structures need to hold the frame handle such as the page table.
199    pub(in crate::mm) fn into_raw(self) -> Paddr {
200        let this = ManuallyDrop::new(self);
201        this.paddr()
202    }
203
204    /// Restores a forgotten [`Frame`] from a physical address.
205    ///
206    /// # Safety
207    ///
208    /// The caller should only restore a `Frame` that was previously forgotten using
209    /// [`Frame::into_raw`].
210    ///
211    /// And the restoring operation should only be done once for a forgotten
212    /// [`Frame`]. Otherwise double-free will happen.
213    ///
214    /// Also, the caller ensures that the usage of the frame is correct. There's
215    /// no checking of the usage in this function.
216    pub(crate) unsafe fn from_raw(paddr: Paddr) -> Self {
217        debug_assert!(paddr < max_paddr());
218
219        let vaddr = mapping::frame_to_meta::<PagingConsts>(paddr);
220        let ptr = vaddr as *const MetaSlot;
221
222        Self {
223            ptr,
224            _marker: PhantomData,
225        }
226    }
227
228    fn slot(&self) -> &MetaSlot {
229        // SAFETY: `ptr` points to a valid `MetaSlot` that will never be
230        // mutably borrowed, so taking an immutable reference to it is safe.
231        unsafe { &*self.ptr }
232    }
233}
234
235impl<M: AnyFrameMeta + ?Sized> HasPaddr for Frame<M> {
236    fn paddr(&self) -> Paddr {
237        self.slot().frame_paddr()
238    }
239}
240
241impl<M: AnyFrameMeta + ?Sized> HasSize for Frame<M> {
242    fn size(&self) -> usize {
243        PAGE_SIZE
244    }
245}
246
247impl<M: AnyFrameMeta + ?Sized> Clone for Frame<M> {
248    fn clone(&self) -> Self {
249        // SAFETY: We have already held a reference to the frame.
250        unsafe { self.slot().inc_ref_count() };
251
252        Self {
253            ptr: self.ptr,
254            _marker: PhantomData,
255        }
256    }
257}
258
259impl<M: AnyFrameMeta + ?Sized> Drop for Frame<M> {
260    fn drop(&mut self) {
261        let last_ref_cnt = self.slot().ref_count.fetch_sub(1, Ordering::Release);
262        debug_assert!(last_ref_cnt != 0 && last_ref_cnt != REF_COUNT_UNUSED);
263
264        if last_ref_cnt == 1 {
265            // A fence is needed here with the same reasons stated in the implementation of
266            // `Arc::drop`: <https://doc.rust-lang.org/std/sync/struct.Arc.html#method.drop>.
267            core::sync::atomic::fence(Ordering::Acquire);
268
269            // SAFETY: This is the last reference and is about to be dropped.
270            unsafe { self.slot().drop_last_in_place() };
271
272            allocator::get_global_frame_allocator().dealloc(self.paddr(), PAGE_SIZE);
273        }
274    }
275}
276
277impl<M: AnyFrameMeta> TryFrom<Frame<dyn AnyFrameMeta>> for Frame<M> {
278    type Error = Frame<dyn AnyFrameMeta>;
279
280    /// Tries converting a [`Frame<dyn AnyFrameMeta>`] into the statically-typed [`Frame`].
281    ///
282    /// If the usage of the frame is not the same as the expected usage, it will
283    /// return the dynamic frame itself as is.
284    fn try_from(dyn_frame: Frame<dyn AnyFrameMeta>) -> Result<Self, Self::Error> {
285        if (dyn_frame.dyn_meta() as &dyn core::any::Any).is::<M>() {
286            // SAFETY: The metadata is coerceable and the struct is transmutable.
287            Ok(unsafe { core::mem::transmute::<Frame<dyn AnyFrameMeta>, Frame<M>>(dyn_frame) })
288        } else {
289            Err(dyn_frame)
290        }
291    }
292}
293
294impl Frame<dyn AnyFrameMeta> {
295    /// Converts a [`Frame`] with a specific metadata type into a
296    /// [`Frame<dyn AnyFrameMeta>`].
297    ///
298    /// This exists because:
299    ///
300    /// ```ignore
301    /// impl<M: AnyFrameMeta + ?Sized> From<Frame<M>> for Frame<dyn AnyFrameMeta>
302    /// ```
303    ///
304    /// will conflict with `impl<T> core::convert::From<T> for T` in crate `core`.
305    pub fn from_unsized<M: AnyFrameMeta + ?Sized>(frame: Frame<M>) -> Frame<dyn AnyFrameMeta> {
306        // SAFETY: The metadata is coerceable and the struct is transmutable.
307        unsafe { core::mem::transmute(frame) }
308    }
309
310    /// Converts an RCU-dropped [`Frame`] with a specific metadata type into a
311    /// RCU-dropped [`Frame<dyn AnyFrameMeta>`].
312    ///
313    /// See also [`Frame::from_unsized`] for the reason of why not implementing
314    /// [`From`] directly.
315    pub fn rcu_from_unsized<M: AnyFrameMeta + ?Sized>(frame: RcuDrop<Frame<M>>) -> RcuDrop<Self> {
316        // SAFETY: The resulting frame will be dropped after the RCU grace period.
317        let (frame, panic_guard) = unsafe { RcuDrop::into_inner(frame) };
318        let dyn_frame = Self::from_unsized(frame);
319        panic_guard.forget();
320        RcuDrop::new(dyn_frame)
321    }
322}
323
324impl<M: AnyFrameMeta> From<Frame<M>> for Frame<dyn AnyFrameMeta> {
325    fn from(frame: Frame<M>) -> Self {
326        Self::from_unsized(frame)
327    }
328}
329
330impl<M: AnyUFrameMeta> From<Frame<M>> for UFrame {
331    fn from(frame: Frame<M>) -> Self {
332        // SAFETY: The metadata is coerceable and the struct is transmutable.
333        unsafe { core::mem::transmute(frame) }
334    }
335}
336
337impl From<UFrame> for Frame<dyn AnyFrameMeta> {
338    fn from(frame: UFrame) -> Self {
339        // SAFETY: The metadata is coerceable and the struct is transmutable.
340        unsafe { core::mem::transmute(frame) }
341    }
342}
343
344impl TryFrom<Frame<dyn AnyFrameMeta>> for UFrame {
345    type Error = Frame<dyn AnyFrameMeta>;
346
347    /// Tries converting a [`Frame<dyn AnyFrameMeta>`] into [`UFrame`].
348    ///
349    /// If the usage of the frame is not the same as the expected usage, it will
350    /// return the dynamic frame itself as is.
351    fn try_from(dyn_frame: Frame<dyn AnyFrameMeta>) -> Result<Self, Self::Error> {
352        if dyn_frame.dyn_meta().is_untyped() {
353            // SAFETY: The metadata is coerceable and the struct is transmutable.
354            Ok(unsafe { core::mem::transmute::<Frame<dyn AnyFrameMeta>, UFrame>(dyn_frame) })
355        } else {
356            Err(dyn_frame)
357        }
358    }
359}
360
361/// Increases the reference count of the frame by one.
362///
363/// # Safety
364///
365/// The caller should ensure the following conditions:
366///  1. The physical address must represent a valid frame;
367///  2. The caller must have already held a reference to the frame.
368pub(in crate::mm) unsafe fn inc_frame_ref_count(paddr: Paddr) {
369    debug_assert!(paddr.is_multiple_of(PAGE_SIZE));
370    debug_assert!(paddr < max_paddr());
371
372    let vaddr: Vaddr = mapping::frame_to_meta::<PagingConsts>(paddr);
373    // SAFETY: `vaddr` points to a valid `MetaSlot` that will never be mutably borrowed, so taking
374    // an immutable reference to it is always safe.
375    let slot = unsafe { &*(vaddr as *const MetaSlot) };
376
377    // SAFETY: We have already held a reference to the frame.
378    unsafe { slot.inc_ref_count() };
379}