// SPDX-License-Identifier: MPL-2.0 //! A contiguous range of frames. use core::{mem::ManuallyDrop, ops::Range}; use super::{inc_frame_ref_count, meta::AnyFrameMeta, Frame}; use crate::mm::{AnyUFrameMeta, Paddr, PAGE_SIZE}; /// A contiguous range of homogeneous physical memory frames. /// /// This is a handle to multiple contiguous frames. It will be more lightweight /// than owning an array of frame handles. /// /// The ownership is achieved by the reference counting mechanism of frames. /// When constructing a [`Segment`], the frame handles are created then /// forgotten, leaving the reference count. When dropping a it, the frame /// handles are restored and dropped, decrementing the reference count. /// /// All the metadata of the frames are homogeneous, i.e., they are of the same /// type. #[derive(Debug)] #[repr(transparent)] pub struct Segment { range: Range, _marker: core::marker::PhantomData, } /// A contiguous range of homogeneous untyped physical memory frames that have any metadata. /// /// In other words, the metadata of the frames are of the same type, and they /// are untyped, but the type of metadata is not known at compile time. An /// [`USegment`] as a parameter accepts any untyped segments. /// /// The usage of this frame will not be changed while this object is alive. pub type USegment = Segment; impl Drop for Segment { fn drop(&mut self) { for paddr in self.range.clone().step_by(PAGE_SIZE) { // SAFETY: for each frame there would be a forgotten handle // when creating the `Segment` object. drop(unsafe { Frame::::from_raw(paddr) }); } } } impl Clone for Segment { fn clone(&self) -> Self { for paddr in self.range.clone().step_by(PAGE_SIZE) { // SAFETY: for each frame there would be a forgotten handle // when creating the `Segment` object, so we already have // reference counts for the frames. unsafe { inc_frame_ref_count(paddr) }; } Self { range: self.range.clone(), _marker: core::marker::PhantomData, } } } impl Segment { /// Creates a new [`Segment`] from unused frames. /// /// The caller must provide a closure to initialize metadata for all the frames. /// The closure receives the physical address of the frame and returns the /// metadata, which is similar to [`core::array::from_fn`]. /// /// # Panics /// /// The function panics if: /// - the physical address is invalid or not aligned; /// - any of the frames are already in use. pub fn from_unused(range: Range, mut metadata_fn: F) -> Self where F: FnMut(Paddr) -> M, { for paddr in range.clone().step_by(PAGE_SIZE) { let _ = ManuallyDrop::new(Frame::::from_unused(paddr, metadata_fn(paddr))); } Self { range, _marker: core::marker::PhantomData, } } } impl Segment { /// Gets the start physical address of the contiguous frames. pub fn start_paddr(&self) -> Paddr { self.range.start } /// Gets the end physical address of the contiguous frames. pub fn end_paddr(&self) -> Paddr { self.range.end } /// Gets the length in bytes of the contiguous frames. pub fn size(&self) -> usize { self.range.end - self.range.start } /// Splits the frames into two at the given byte offset from the start. /// /// The resulting frames cannot be empty. So the offset cannot be neither /// zero nor the length of the frames. /// /// # Panics /// /// The function panics if the offset is out of bounds, at either ends, or /// not base-page-aligned. pub fn split(self, offset: usize) -> (Self, Self) { assert!(offset % PAGE_SIZE == 0); assert!(0 < offset && offset < self.size()); let old = ManuallyDrop::new(self); let at = old.range.start + offset; ( Self { range: old.range.start..at, _marker: core::marker::PhantomData, }, Self { range: at..old.range.end, _marker: core::marker::PhantomData, }, ) } /// Gets an extra handle to the frames in the byte offset range. /// /// The sliced byte offset range in indexed by the offset from the start of /// the contiguous frames. The resulting frames holds extra reference counts. /// /// # Panics /// /// The function panics if the byte offset range is out of bounds, or if /// any of the ends of the byte offset range is not base-page aligned. pub fn slice(&self, range: &Range) -> Self { assert!(range.start % PAGE_SIZE == 0 && range.end % PAGE_SIZE == 0); let start = self.range.start + range.start; let end = self.range.start + range.end; assert!(start <= end && end <= self.range.end); for paddr in (start..end).step_by(PAGE_SIZE) { // SAFETY: We already have reference counts for the frames since // for each frame there would be a forgotten handle when creating // the `Segment` object. unsafe { inc_frame_ref_count(paddr) }; } Self { range: start..end, _marker: core::marker::PhantomData, } } } impl From> for Segment { fn from(frame: Frame) -> Self { let pa = frame.start_paddr(); let _ = ManuallyDrop::new(frame); Self { range: pa..pa + PAGE_SIZE, _marker: core::marker::PhantomData, } } } impl Iterator for Segment { type Item = Frame; fn next(&mut self) -> Option { if self.range.start < self.range.end { // SAFETY: each frame in the range would be a handle forgotten // when creating the `Segment` object. let frame = unsafe { Frame::::from_raw(self.range.start) }; self.range.start += PAGE_SIZE; // The end cannot be non-page-aligned. debug_assert!(self.range.start <= self.range.end); Some(frame) } else { None } } } impl From> for Segment { fn from(seg: Segment) -> Self { let seg = ManuallyDrop::new(seg); Self { range: seg.range.clone(), _marker: core::marker::PhantomData, } } } impl TryFrom> for Segment { type Error = Segment; fn try_from(seg: Segment) -> core::result::Result { // SAFETY: for each page there would be a forgotten handle // when creating the `Segment` object. let first_frame = unsafe { Frame::::from_raw(seg.range.start) }; let first_frame = ManuallyDrop::new(first_frame); if !(first_frame.dyn_meta() as &dyn core::any::Any).is::() { return Err(seg); } // Since segments are homogeneous, we can safely assume that the rest // of the frames are of the same type. We just debug-check here. #[cfg(debug_assertions)] { for paddr in seg.range.clone().step_by(PAGE_SIZE) { let frame = unsafe { Frame::::from_raw(paddr) }; let frame = ManuallyDrop::new(frame); debug_assert!((frame.dyn_meta() as &dyn core::any::Any).is::()); } } // SAFETY: The metadata is coerceable and the struct is transmutable. Ok(unsafe { core::mem::transmute::, Segment>(seg) }) } } impl From> for USegment { fn from(seg: Segment) -> Self { // SAFETY: The metadata is coerceable and the struct is transmutable. unsafe { core::mem::transmute(seg) } } } impl From<&Segment> for &USegment { fn from(seg: &Segment) -> Self { // SAFETY: The metadata is coerceable and the struct is transmutable. unsafe { core::mem::transmute(seg) } } } impl TryFrom> for USegment { type Error = Segment; /// Try converting a [`Segment`] into [`USegment`]. /// /// If the usage of the page is not the same as the expected usage, it will /// return the dynamic page itself as is. fn try_from(seg: Segment) -> core::result::Result { // SAFETY: for each page there would be a forgotten handle // when creating the `Segment` object. let first_frame = unsafe { Frame::::from_raw(seg.range.start) }; let first_frame = ManuallyDrop::new(first_frame); if !first_frame.dyn_meta().is_untyped() { return Err(seg); } // Since segments are homogeneous, we can safely assume that the rest // of the frames are of the same type. We just debug-check here. #[cfg(debug_assertions)] { for paddr in seg.range.clone().step_by(PAGE_SIZE) { let frame = unsafe { Frame::::from_raw(paddr) }; let frame = ManuallyDrop::new(frame); debug_assert!(frame.dyn_meta().is_untyped()); } } // SAFETY: The metadata is coerceable and the struct is transmutable. Ok(unsafe { core::mem::transmute::, USegment>(seg) }) } }