Skip to main content

ostd/mm/frame/
segment.rs

1// SPDX-License-Identifier: MPL-2.0
2
3//! A contiguous range of frames.
4
5use core::{fmt::Debug, mem::ManuallyDrop, ops::Range};
6
7use super::{
8    Frame, inc_frame_ref_count,
9    meta::{AnyFrameMeta, GetFrameError},
10};
11use crate::mm::{AnyUFrameMeta, HasPaddr, HasSize, PAGE_SIZE, Paddr, Split};
12
13/// A contiguous range of homogeneous physical memory frames.
14///
15/// This is a handle to multiple contiguous frames. It will be more lightweight
16/// than owning an array of frame handles.
17///
18/// The ownership is achieved by the reference counting mechanism of frames.
19/// When constructing a [`Segment`], the frame handles are created then
20/// forgotten, leaving the reference count. When dropping a it, the frame
21/// handles are restored and dropped, decrementing the reference count.
22///
23/// All the metadata of the frames are homogeneous, i.e., they are of the same
24/// type.
25#[repr(transparent)]
26pub struct Segment<M: AnyFrameMeta + ?Sized> {
27    range: Range<Paddr>,
28    _marker: core::marker::PhantomData<M>,
29}
30
31impl<M: AnyFrameMeta + ?Sized> Debug for Segment<M> {
32    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
33        write!(f, "Segment({:#x}..{:#x})", self.range.start, self.range.end)
34    }
35}
36
37/// A contiguous range of homogeneous untyped physical memory frames that have any metadata.
38///
39/// In other words, the metadata of the frames are of the same type, and they
40/// are untyped, but the type of metadata is not known at compile time. An
41/// [`USegment`] as a parameter accepts any untyped segments.
42///
43/// The usage of this frame will not be changed while this object is alive.
44pub type USegment = Segment<dyn AnyUFrameMeta>;
45
46impl<M: AnyFrameMeta + ?Sized> Drop for Segment<M> {
47    fn drop(&mut self) {
48        for paddr in self.range.clone().step_by(PAGE_SIZE) {
49            // SAFETY: for each frame there would be a forgotten handle
50            // when creating the `Segment` object.
51            drop(unsafe { Frame::<M>::from_raw(paddr) });
52        }
53    }
54}
55
56impl<M: AnyFrameMeta + ?Sized> Clone for Segment<M> {
57    fn clone(&self) -> Self {
58        for paddr in self.range.clone().step_by(PAGE_SIZE) {
59            // SAFETY: for each frame there would be a forgotten handle
60            // when creating the `Segment` object, so we already have
61            // reference counts for the frames.
62            unsafe { inc_frame_ref_count(paddr) };
63        }
64        Self {
65            range: self.range.clone(),
66            _marker: core::marker::PhantomData,
67        }
68    }
69}
70
71impl<M: AnyFrameMeta> Segment<M> {
72    /// Creates a new [`Segment`] from unused frames.
73    ///
74    /// The caller must provide a closure to initialize metadata for all the frames.
75    /// The closure receives the physical address of the frame and returns the
76    /// metadata, which is similar to [`core::array::from_fn`].
77    ///
78    /// It returns an error if:
79    ///  - the physical address is invalid or not aligned;
80    ///  - any of the frames cannot be created with a specific reason.
81    ///
82    /// # Panics
83    ///
84    /// It panics if the range is empty.
85    pub fn from_unused<F>(range: Range<Paddr>, mut metadata_fn: F) -> Result<Self, GetFrameError>
86    where
87        F: FnMut(Paddr) -> M,
88    {
89        if !range.start.is_multiple_of(PAGE_SIZE) || !range.end.is_multiple_of(PAGE_SIZE) {
90            return Err(GetFrameError::NotAligned);
91        }
92        if range.end > super::max_paddr() {
93            return Err(GetFrameError::OutOfBound);
94        }
95        assert!(range.start < range.end);
96        // Construct a segment early to recycle previously forgotten frames if
97        // the subsequent operations fails in the middle.
98        let mut segment = Self {
99            range: range.start..range.start,
100            _marker: core::marker::PhantomData,
101        };
102        for paddr in range.step_by(PAGE_SIZE) {
103            let frame = Frame::<M>::from_unused(paddr, metadata_fn(paddr))?;
104            let _ = ManuallyDrop::new(frame);
105            segment.range.end = paddr + PAGE_SIZE;
106        }
107        Ok(segment)
108    }
109
110    /// Restores the [`Segment`] from the raw physical address range.
111    ///
112    /// # Safety
113    ///
114    /// The range must be a forgotten [`Segment`] that matches the type `M`.
115    /// It could be manually forgotten by [`core::mem::forget`],
116    /// [`ManuallyDrop`], or [`Self::into_raw`].
117    pub(crate) unsafe fn from_raw(range: Range<Paddr>) -> Self {
118        debug_assert_eq!(range.start % PAGE_SIZE, 0);
119        debug_assert_eq!(range.end % PAGE_SIZE, 0);
120        Self {
121            range,
122            _marker: core::marker::PhantomData,
123        }
124    }
125}
126
127impl<M: AnyFrameMeta + ?Sized> Split for Segment<M> {
128    fn split(self, offset: usize) -> (Self, Self) {
129        assert!(
130            offset.is_multiple_of(PAGE_SIZE),
131            "segment virtual address not aligned for splitting"
132        );
133        assert!(
134            0 < offset && offset < self.size(),
135            "segment virtual address out-of-bound for splitting"
136        );
137
138        let old = ManuallyDrop::new(self);
139        let at = old.range.start + offset;
140
141        (
142            Self {
143                range: old.range.start..at,
144                _marker: core::marker::PhantomData,
145            },
146            Self {
147                range: at..old.range.end,
148                _marker: core::marker::PhantomData,
149            },
150        )
151    }
152}
153
154impl<M: AnyFrameMeta + ?Sized> Segment<M> {
155    /// Gets an extra handle to the frames in the byte offset range.
156    ///
157    /// The sliced byte offset range in indexed by the offset from the start of
158    /// the contiguous frames. The resulting frames holds extra reference counts.
159    ///
160    /// # Panics
161    ///
162    /// The function panics if the byte offset range is out of bounds, or if
163    /// any of the ends of the byte offset range is not base-page aligned.
164    pub fn slice(&self, range: &Range<usize>) -> Self {
165        assert!(
166            range.start.is_multiple_of(PAGE_SIZE) && range.end.is_multiple_of(PAGE_SIZE),
167            "segment virtual address not aligned for slicing"
168        );
169        assert!(
170            range.start <= range.end && range.end <= self.size(),
171            "segment virtual address out-of-bound for slicing"
172        );
173
174        let start = self.range.start + range.start;
175        let end = self.range.start + range.end;
176
177        for paddr in (start..end).step_by(PAGE_SIZE) {
178            // SAFETY: We already have reference counts for the frames since
179            // for each frame there would be a forgotten handle when creating
180            // the `Segment` object.
181            unsafe { inc_frame_ref_count(paddr) };
182        }
183
184        Self {
185            range: start..end,
186            _marker: core::marker::PhantomData,
187        }
188    }
189
190    /// Forgets the [`Segment`] and gets a raw range of physical addresses.
191    pub(crate) fn into_raw(self) -> Range<Paddr> {
192        let range = self.range.clone();
193        let _ = ManuallyDrop::new(self);
194        range
195    }
196}
197
198impl Segment<dyn AnyFrameMeta> {
199    /// Converts a [`Segment`] with a specific metadata type into a
200    /// [`Segment<dyn AnyFrameMeta>`].
201    ///
202    /// This exists because:
203    ///
204    /// ```ignore
205    /// impl<M: AnyFrameMeta + ?Sized> From<Segment<M>> for Segment<dyn AnyFrameMeta>
206    /// ```
207    ///
208    /// will conflict with `impl<T> core::convert::From<T> for T` in crate `core`.
209    ///
210    /// See also [`Frame::from_unsized`].
211    pub fn from_unsized<M: AnyFrameMeta + ?Sized>(
212        segment: Segment<M>,
213    ) -> Segment<dyn AnyFrameMeta> {
214        let seg = ManuallyDrop::new(segment);
215        Self {
216            range: seg.range.clone(),
217            _marker: core::marker::PhantomData,
218        }
219    }
220}
221
222impl<M: AnyFrameMeta + ?Sized> HasPaddr for Segment<M> {
223    fn paddr(&self) -> Paddr {
224        self.range.start
225    }
226}
227
228impl<M: AnyFrameMeta + ?Sized> HasSize for Segment<M> {
229    fn size(&self) -> usize {
230        self.range.end - self.range.start
231    }
232}
233
234impl<M: AnyFrameMeta + ?Sized> From<Frame<M>> for Segment<M> {
235    fn from(frame: Frame<M>) -> Self {
236        let pa = frame.paddr();
237        let _ = ManuallyDrop::new(frame);
238        Self {
239            range: pa..pa + PAGE_SIZE,
240            _marker: core::marker::PhantomData,
241        }
242    }
243}
244
245impl<M: AnyFrameMeta + ?Sized> Iterator for Segment<M> {
246    type Item = Frame<M>;
247
248    fn next(&mut self) -> Option<Self::Item> {
249        if self.range.start < self.range.end {
250            // SAFETY: each frame in the range would be a handle forgotten
251            // when creating the `Segment` object.
252            let frame = unsafe { Frame::<M>::from_raw(self.range.start) };
253            self.range.start += PAGE_SIZE;
254            // The end cannot be non-page-aligned.
255            debug_assert!(self.range.start <= self.range.end);
256            Some(frame)
257        } else {
258            None
259        }
260    }
261}
262
263impl<M: AnyFrameMeta> From<Segment<M>> for Segment<dyn AnyFrameMeta> {
264    fn from(seg: Segment<M>) -> Self {
265        Self::from_unsized(seg)
266    }
267}
268
269impl<M: AnyFrameMeta> TryFrom<Segment<dyn AnyFrameMeta>> for Segment<M> {
270    type Error = Segment<dyn AnyFrameMeta>;
271
272    fn try_from(seg: Segment<dyn AnyFrameMeta>) -> Result<Self, Self::Error> {
273        // SAFETY: for each page there would be a forgotten handle
274        // when creating the `Segment` object.
275        let first_frame = unsafe { Frame::<dyn AnyFrameMeta>::from_raw(seg.range.start) };
276        let first_frame = ManuallyDrop::new(first_frame);
277        if !(first_frame.dyn_meta() as &dyn core::any::Any).is::<M>() {
278            return Err(seg);
279        }
280        // Since segments are homogeneous, we can safely assume that the rest
281        // of the frames are of the same type. We just debug-check here.
282        #[cfg(debug_assertions)]
283        {
284            for paddr in seg.range.clone().step_by(PAGE_SIZE) {
285                let frame = unsafe { Frame::<dyn AnyFrameMeta>::from_raw(paddr) };
286                let frame = ManuallyDrop::new(frame);
287                debug_assert!((frame.dyn_meta() as &dyn core::any::Any).is::<M>());
288            }
289        }
290        // SAFETY: The metadata is coerceable and the struct is transmutable.
291        Ok(unsafe { core::mem::transmute::<Segment<dyn AnyFrameMeta>, Segment<M>>(seg) })
292    }
293}
294
295impl<M: AnyUFrameMeta> From<Segment<M>> for USegment {
296    fn from(seg: Segment<M>) -> Self {
297        // SAFETY: The metadata is coerceable and the struct is transmutable.
298        unsafe { core::mem::transmute(seg) }
299    }
300}
301
302impl TryFrom<Segment<dyn AnyFrameMeta>> for USegment {
303    type Error = Segment<dyn AnyFrameMeta>;
304
305    /// Try converting a [`Segment<dyn AnyFrameMeta>`] into [`USegment`].
306    ///
307    /// If the usage of the page is not the same as the expected usage, it will
308    /// return the dynamic page itself as is.
309    fn try_from(seg: Segment<dyn AnyFrameMeta>) -> Result<Self, Self::Error> {
310        // SAFETY: for each page there would be a forgotten handle
311        // when creating the `Segment` object.
312        let first_frame = unsafe { Frame::<dyn AnyFrameMeta>::from_raw(seg.range.start) };
313        let first_frame = ManuallyDrop::new(first_frame);
314        if !first_frame.dyn_meta().is_untyped() {
315            return Err(seg);
316        }
317        // Since segments are homogeneous, we can safely assume that the rest
318        // of the frames are of the same type. We just debug-check here.
319        #[cfg(debug_assertions)]
320        {
321            for paddr in seg.range.clone().step_by(PAGE_SIZE) {
322                let frame = unsafe { Frame::<dyn AnyFrameMeta>::from_raw(paddr) };
323                let frame = ManuallyDrop::new(frame);
324                debug_assert!(frame.dyn_meta().is_untyped());
325            }
326        }
327        // SAFETY: The metadata is coerceable and the struct is transmutable.
328        Ok(unsafe { core::mem::transmute::<Segment<dyn AnyFrameMeta>, USegment>(seg) })
329    }
330}