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!(offset.is_multiple_of(PAGE_SIZE));
130        assert!(0 < offset && offset < self.size());
131
132        let old = ManuallyDrop::new(self);
133        let at = old.range.start + offset;
134
135        (
136            Self {
137                range: old.range.start..at,
138                _marker: core::marker::PhantomData,
139            },
140            Self {
141                range: at..old.range.end,
142                _marker: core::marker::PhantomData,
143            },
144        )
145    }
146}
147
148impl<M: AnyFrameMeta + ?Sized> Segment<M> {
149    /// Gets an extra handle to the frames in the byte offset range.
150    ///
151    /// The sliced byte offset range in indexed by the offset from the start of
152    /// the contiguous frames. The resulting frames holds extra reference counts.
153    ///
154    /// # Panics
155    ///
156    /// The function panics if the byte offset range is out of bounds, or if
157    /// any of the ends of the byte offset range is not base-page aligned.
158    pub fn slice(&self, range: &Range<usize>) -> Self {
159        assert!(range.start.is_multiple_of(PAGE_SIZE) && range.end.is_multiple_of(PAGE_SIZE));
160        let start = self.range.start + range.start;
161        let end = self.range.start + range.end;
162        assert!(start <= end && end <= self.range.end);
163
164        for paddr in (start..end).step_by(PAGE_SIZE) {
165            // SAFETY: We already have reference counts for the frames since
166            // for each frame there would be a forgotten handle when creating
167            // the `Segment` object.
168            unsafe { inc_frame_ref_count(paddr) };
169        }
170
171        Self {
172            range: start..end,
173            _marker: core::marker::PhantomData,
174        }
175    }
176
177    /// Forgets the [`Segment`] and gets a raw range of physical addresses.
178    pub(crate) fn into_raw(self) -> Range<Paddr> {
179        let range = self.range.clone();
180        let _ = ManuallyDrop::new(self);
181        range
182    }
183}
184
185impl Segment<dyn AnyFrameMeta> {
186    /// Converts a [`Segment`] with a specific metadata type into a
187    /// [`Segment<dyn AnyFrameMeta>`].
188    ///
189    /// This exists because:
190    ///
191    /// ```ignore
192    /// impl<M: AnyFrameMeta + ?Sized> From<Segment<M>> for Segment<dyn AnyFrameMeta>
193    /// ```
194    ///
195    /// will conflict with `impl<T> core::convert::From<T> for T` in crate `core`.
196    ///
197    /// See also [`Frame::from_unsized`].
198    pub fn from_unsized<M: AnyFrameMeta + ?Sized>(
199        segment: Segment<M>,
200    ) -> Segment<dyn AnyFrameMeta> {
201        let seg = ManuallyDrop::new(segment);
202        Self {
203            range: seg.range.clone(),
204            _marker: core::marker::PhantomData,
205        }
206    }
207}
208
209impl<M: AnyFrameMeta + ?Sized> HasPaddr for Segment<M> {
210    fn paddr(&self) -> Paddr {
211        self.range.start
212    }
213}
214
215impl<M: AnyFrameMeta + ?Sized> HasSize for Segment<M> {
216    fn size(&self) -> usize {
217        self.range.end - self.range.start
218    }
219}
220
221impl<M: AnyFrameMeta + ?Sized> From<Frame<M>> for Segment<M> {
222    fn from(frame: Frame<M>) -> Self {
223        let pa = frame.paddr();
224        let _ = ManuallyDrop::new(frame);
225        Self {
226            range: pa..pa + PAGE_SIZE,
227            _marker: core::marker::PhantomData,
228        }
229    }
230}
231
232impl<M: AnyFrameMeta + ?Sized> Iterator for Segment<M> {
233    type Item = Frame<M>;
234
235    fn next(&mut self) -> Option<Self::Item> {
236        if self.range.start < self.range.end {
237            // SAFETY: each frame in the range would be a handle forgotten
238            // when creating the `Segment` object.
239            let frame = unsafe { Frame::<M>::from_raw(self.range.start) };
240            self.range.start += PAGE_SIZE;
241            // The end cannot be non-page-aligned.
242            debug_assert!(self.range.start <= self.range.end);
243            Some(frame)
244        } else {
245            None
246        }
247    }
248}
249
250impl<M: AnyFrameMeta> From<Segment<M>> for Segment<dyn AnyFrameMeta> {
251    fn from(seg: Segment<M>) -> Self {
252        Self::from_unsized(seg)
253    }
254}
255
256impl<M: AnyFrameMeta> TryFrom<Segment<dyn AnyFrameMeta>> for Segment<M> {
257    type Error = Segment<dyn AnyFrameMeta>;
258
259    fn try_from(seg: Segment<dyn AnyFrameMeta>) -> core::result::Result<Self, Self::Error> {
260        // SAFETY: for each page there would be a forgotten handle
261        // when creating the `Segment` object.
262        let first_frame = unsafe { Frame::<dyn AnyFrameMeta>::from_raw(seg.range.start) };
263        let first_frame = ManuallyDrop::new(first_frame);
264        if !(first_frame.dyn_meta() as &dyn core::any::Any).is::<M>() {
265            return Err(seg);
266        }
267        // Since segments are homogeneous, we can safely assume that the rest
268        // of the frames are of the same type. We just debug-check here.
269        #[cfg(debug_assertions)]
270        {
271            for paddr in seg.range.clone().step_by(PAGE_SIZE) {
272                let frame = unsafe { Frame::<dyn AnyFrameMeta>::from_raw(paddr) };
273                let frame = ManuallyDrop::new(frame);
274                debug_assert!((frame.dyn_meta() as &dyn core::any::Any).is::<M>());
275            }
276        }
277        // SAFETY: The metadata is coerceable and the struct is transmutable.
278        Ok(unsafe { core::mem::transmute::<Segment<dyn AnyFrameMeta>, Segment<M>>(seg) })
279    }
280}
281
282impl<M: AnyUFrameMeta> From<Segment<M>> for USegment {
283    fn from(seg: Segment<M>) -> Self {
284        // SAFETY: The metadata is coerceable and the struct is transmutable.
285        unsafe { core::mem::transmute(seg) }
286    }
287}
288
289impl TryFrom<Segment<dyn AnyFrameMeta>> for USegment {
290    type Error = Segment<dyn AnyFrameMeta>;
291
292    /// Try converting a [`Segment<dyn AnyFrameMeta>`] into [`USegment`].
293    ///
294    /// If the usage of the page is not the same as the expected usage, it will
295    /// return the dynamic page itself as is.
296    fn try_from(seg: Segment<dyn AnyFrameMeta>) -> core::result::Result<Self, Self::Error> {
297        // SAFETY: for each page there would be a forgotten handle
298        // when creating the `Segment` object.
299        let first_frame = unsafe { Frame::<dyn AnyFrameMeta>::from_raw(seg.range.start) };
300        let first_frame = ManuallyDrop::new(first_frame);
301        if !first_frame.dyn_meta().is_untyped() {
302            return Err(seg);
303        }
304        // Since segments are homogeneous, we can safely assume that the rest
305        // of the frames are of the same type. We just debug-check here.
306        #[cfg(debug_assertions)]
307        {
308            for paddr in seg.range.clone().step_by(PAGE_SIZE) {
309                let frame = unsafe { Frame::<dyn AnyFrameMeta>::from_raw(paddr) };
310                let frame = ManuallyDrop::new(frame);
311                debug_assert!(frame.dyn_meta().is_untyped());
312            }
313        }
314        // SAFETY: The metadata is coerceable and the struct is transmutable.
315        Ok(unsafe { core::mem::transmute::<Segment<dyn AnyFrameMeta>, USegment>(seg) })
316    }
317}