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