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
41mod frame_ref;
42pub use frame_ref::FrameRef;
43
44#[cfg(ktest)]
45mod test;
46
47use core::{
48 marker::PhantomData,
49 mem::ManuallyDrop,
50 sync::atomic::{AtomicUsize, Ordering},
51};
52
53pub use allocator::GlobalFrameAllocator;
54use meta::{AnyFrameMeta, GetFrameError, MetaSlot, REF_COUNT_UNUSED, mapping};
55pub use segment::Segment;
56use untyped::{AnyUFrameMeta, UFrame};
57
58use crate::mm::{HasPaddr, HasSize, PAGE_SIZE, Paddr, PagingConsts, PagingLevel, Vaddr};
59
60static MAX_PADDR: AtomicUsize = AtomicUsize::new(0);
61
62/// Returns the maximum physical address that is tracked by frame metadata.
63pub(in crate::mm) fn max_paddr() -> Paddr {
64 let max_paddr = MAX_PADDR.load(Ordering::Relaxed) as Paddr;
65 debug_assert_ne!(max_paddr, 0);
66 max_paddr
67}
68
69/// A smart pointer to a frame.
70///
71/// A frame is a contiguous range of bytes in physical memory. The [`Frame`]
72/// type is a smart pointer to a frame that is reference-counted.
73///
74/// Frames are associated with metadata. The type of the metadata `M` is
75/// determines the kind of the frame. If `M` implements [`AnyUFrameMeta`], the
76/// frame is a untyped frame. Otherwise, it is a typed frame.
77#[repr(transparent)]
78pub struct Frame<M: AnyFrameMeta + ?Sized> {
79 ptr: *const MetaSlot,
80 _marker: PhantomData<M>,
81}
82
83unsafe impl<M: AnyFrameMeta + ?Sized> Send for Frame<M> {}
84
85unsafe impl<M: AnyFrameMeta + ?Sized> Sync for Frame<M> {}
86
87impl<M: AnyFrameMeta + ?Sized> core::fmt::Debug for Frame<M> {
88 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
89 write!(f, "Frame({:#x})", self.paddr())
90 }
91}
92
93impl<M: AnyFrameMeta + ?Sized> PartialEq for Frame<M> {
94 fn eq(&self, other: &Self) -> bool {
95 self.paddr() == other.paddr()
96 }
97}
98impl<M: AnyFrameMeta + ?Sized> Eq for Frame<M> {}
99
100impl<M: AnyFrameMeta> Frame<M> {
101 /// Gets a [`Frame`] with a specific usage from a raw, unused page.
102 ///
103 /// The caller should provide the initial metadata of the page.
104 ///
105 /// If the provided frame is not truly unused at the moment, it will return
106 /// an error. If wanting to acquire a frame that is already in use, use
107 /// [`Frame::from_in_use`] instead.
108 pub fn from_unused(paddr: Paddr, metadata: M) -> Result<Self, GetFrameError> {
109 Ok(Self {
110 ptr: MetaSlot::get_from_unused(paddr, metadata, false)?,
111 _marker: PhantomData,
112 })
113 }
114
115 /// Gets the metadata of this page.
116 pub fn meta(&self) -> &M {
117 // SAFETY: The type is tracked by the type system.
118 unsafe { &*self.slot().as_meta_ptr::<M>() }
119 }
120}
121
122impl Frame<dyn AnyFrameMeta> {
123 /// Gets a dynamically typed [`Frame`] from a raw, in-use page.
124 ///
125 /// If the provided frame is not in use at the moment, it will return an error.
126 ///
127 /// The returned frame will have an extra reference count to the frame.
128 pub fn from_in_use(paddr: Paddr) -> Result<Self, GetFrameError> {
129 Ok(Self {
130 ptr: MetaSlot::get_from_in_use(paddr)?,
131 _marker: PhantomData,
132 })
133 }
134}
135
136impl<M: AnyFrameMeta + ?Sized> Frame<M> {
137 /// Gets the map level of this page.
138 ///
139 /// This is the level of the page table entry that maps the frame,
140 /// which determines the size of the frame.
141 ///
142 /// Currently, the level is always 1, which means the frame is a regular
143 /// page frame.
144 pub const fn map_level(&self) -> PagingLevel {
145 1
146 }
147
148 /// Gets the dyncamically-typed metadata of this frame.
149 ///
150 /// If the type is known at compile time, use [`Frame::meta`] instead.
151 pub fn dyn_meta(&self) -> &dyn AnyFrameMeta {
152 // SAFETY: The metadata is initialized and valid.
153 unsafe { &*self.slot().dyn_meta_ptr() }
154 }
155
156 /// Gets the reference count of the frame.
157 ///
158 /// It returns the number of all references to the frame, including all the
159 /// existing frame handles ([`Frame`], [`Frame<dyn AnyFrameMeta>`]), and all
160 /// the mappings in the page table that points to the frame.
161 ///
162 /// # Safety
163 ///
164 /// The function is safe to call, but using it requires extra care. The
165 /// reference count can be changed by other threads at any time including
166 /// potentially between calling this method and acting on the result.
167 pub fn reference_count(&self) -> u64 {
168 let refcnt = self.slot().ref_count.load(Ordering::Relaxed);
169 debug_assert!(refcnt < meta::REF_COUNT_MAX);
170 refcnt
171 }
172
173 /// Borrows a reference from the given frame.
174 pub fn borrow(&self) -> FrameRef<'_, M> {
175 // SAFETY: Both the lifetime and the type matches `self`.
176 unsafe { FrameRef::borrow_paddr(self.paddr()) }
177 }
178
179 /// Forgets the handle to the frame.
180 ///
181 /// This will result in the frame being leaked without calling the custom dropper.
182 ///
183 /// A physical address to the frame is returned in case the frame needs to be
184 /// restored using [`Frame::from_raw`] later. This is useful when some architectural
185 /// data structures need to hold the frame handle such as the page table.
186 pub(in crate::mm) fn into_raw(self) -> Paddr {
187 let this = ManuallyDrop::new(self);
188 this.paddr()
189 }
190
191 /// Restores a forgotten [`Frame`] from a physical address.
192 ///
193 /// # Safety
194 ///
195 /// The caller should only restore a `Frame` that was previously forgotten using
196 /// [`Frame::into_raw`].
197 ///
198 /// And the restoring operation should only be done once for a forgotten
199 /// [`Frame`]. Otherwise double-free will happen.
200 ///
201 /// Also, the caller ensures that the usage of the frame is correct. There's
202 /// no checking of the usage in this function.
203 pub(in crate::mm) unsafe fn from_raw(paddr: Paddr) -> Self {
204 debug_assert!(paddr < max_paddr());
205
206 let vaddr = mapping::frame_to_meta::<PagingConsts>(paddr);
207 let ptr = vaddr as *const MetaSlot;
208
209 Self {
210 ptr,
211 _marker: PhantomData,
212 }
213 }
214
215 fn slot(&self) -> &MetaSlot {
216 // SAFETY: `ptr` points to a valid `MetaSlot` that will never be
217 // mutably borrowed, so taking an immutable reference to it is safe.
218 unsafe { &*self.ptr }
219 }
220}
221
222impl<M: AnyFrameMeta + ?Sized> HasPaddr for Frame<M> {
223 fn paddr(&self) -> Paddr {
224 self.slot().frame_paddr()
225 }
226}
227
228impl<M: AnyFrameMeta + ?Sized> HasSize for Frame<M> {
229 fn size(&self) -> usize {
230 PAGE_SIZE
231 }
232}
233
234impl<M: AnyFrameMeta + ?Sized> Clone for Frame<M> {
235 fn clone(&self) -> Self {
236 // SAFETY: We have already held a reference to the frame.
237 unsafe { self.slot().inc_ref_count() };
238
239 Self {
240 ptr: self.ptr,
241 _marker: PhantomData,
242 }
243 }
244}
245
246impl<M: AnyFrameMeta + ?Sized> Drop for Frame<M> {
247 fn drop(&mut self) {
248 let last_ref_cnt = self.slot().ref_count.fetch_sub(1, Ordering::Release);
249 debug_assert!(last_ref_cnt != 0 && last_ref_cnt != REF_COUNT_UNUSED);
250
251 if last_ref_cnt == 1 {
252 // A fence is needed here with the same reasons stated in the implementation of
253 // `Arc::drop`: <https://doc.rust-lang.org/std/sync/struct.Arc.html#method.drop>.
254 core::sync::atomic::fence(Ordering::Acquire);
255
256 // SAFETY: this is the last reference and is about to be dropped.
257 unsafe { self.slot().drop_last_in_place() };
258
259 allocator::get_global_frame_allocator().dealloc(self.paddr(), PAGE_SIZE);
260 }
261 }
262}
263
264impl<M: AnyFrameMeta> TryFrom<Frame<dyn AnyFrameMeta>> for Frame<M> {
265 type Error = Frame<dyn AnyFrameMeta>;
266
267 /// Tries converting a [`Frame<dyn AnyFrameMeta>`] into the statically-typed [`Frame`].
268 ///
269 /// If the usage of the frame is not the same as the expected usage, it will
270 /// return the dynamic frame itself as is.
271 fn try_from(dyn_frame: Frame<dyn AnyFrameMeta>) -> Result<Self, Self::Error> {
272 if (dyn_frame.dyn_meta() as &dyn core::any::Any).is::<M>() {
273 // SAFETY: The metadata is coerceable and the struct is transmutable.
274 Ok(unsafe { core::mem::transmute::<Frame<dyn AnyFrameMeta>, Frame<M>>(dyn_frame) })
275 } else {
276 Err(dyn_frame)
277 }
278 }
279}
280
281impl<M: AnyFrameMeta> From<Frame<M>> for Frame<dyn AnyFrameMeta> {
282 fn from(frame: Frame<M>) -> Self {
283 // SAFETY: The metadata is coerceable and the struct is transmutable.
284 unsafe { core::mem::transmute(frame) }
285 }
286}
287
288impl<M: AnyUFrameMeta> From<Frame<M>> for UFrame {
289 fn from(frame: Frame<M>) -> Self {
290 // SAFETY: The metadata is coerceable and the struct is transmutable.
291 unsafe { core::mem::transmute(frame) }
292 }
293}
294
295impl From<UFrame> for Frame<dyn AnyFrameMeta> {
296 fn from(frame: UFrame) -> Self {
297 // SAFETY: The metadata is coerceable and the struct is transmutable.
298 unsafe { core::mem::transmute(frame) }
299 }
300}
301
302impl TryFrom<Frame<dyn AnyFrameMeta>> for UFrame {
303 type Error = Frame<dyn AnyFrameMeta>;
304
305 /// Tries converting a [`Frame<dyn AnyFrameMeta>`] into [`UFrame`].
306 ///
307 /// If the usage of the frame is not the same as the expected usage, it will
308 /// return the dynamic frame itself as is.
309 fn try_from(dyn_frame: Frame<dyn AnyFrameMeta>) -> Result<Self, Self::Error> {
310 if dyn_frame.dyn_meta().is_untyped() {
311 // SAFETY: The metadata is coerceable and the struct is transmutable.
312 Ok(unsafe { core::mem::transmute::<Frame<dyn AnyFrameMeta>, UFrame>(dyn_frame) })
313 } else {
314 Err(dyn_frame)
315 }
316 }
317}
318
319/// Increases the reference count of the frame by one.
320///
321/// # Safety
322///
323/// The caller should ensure the following conditions:
324/// 1. The physical address must represent a valid frame;
325/// 2. The caller must have already held a reference to the frame.
326pub(in crate::mm) unsafe fn inc_frame_ref_count(paddr: Paddr) {
327 debug_assert!(paddr.is_multiple_of(PAGE_SIZE));
328 debug_assert!(paddr < max_paddr());
329
330 let vaddr: Vaddr = mapping::frame_to_meta::<PagingConsts>(paddr);
331 // SAFETY: `vaddr` points to a valid `MetaSlot` that will never be mutably borrowed, so taking
332 // an immutable reference to it is always safe.
333 let slot = unsafe { &*(vaddr as *const MetaSlot) };
334
335 // SAFETY: We have already held a reference to the frame.
336 unsafe { slot.inc_ref_count() };
337}