asterinas/ostd/src/mm/frame/segment.rs

270 lines
9.7 KiB
Rust
Raw Normal View History

// SPDX-License-Identifier: MPL-2.0
//! A contiguous range of frames.
2024-05-30 11:25:58 +00:00
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.
2024-12-24 07:07:07 +00:00
///
/// All the metadata of the frames are homogeneous, i.e., they are of the same
2024-12-24 07:07:07 +00:00
/// type.
2024-09-04 02:54:56 +00:00
#[derive(Debug)]
#[repr(transparent)]
pub struct Segment<M: AnyFrameMeta + ?Sized> {
range: Range<Paddr>,
_marker: core::marker::PhantomData<M>,
}
/// 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<dyn AnyUFrameMeta>;
impl<M: AnyFrameMeta + ?Sized> Drop for Segment<M> {
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::<M>::from_raw(paddr) });
}
}
}
impl<M: AnyFrameMeta + ?Sized> Clone for Segment<M> {
2024-09-04 02:54:56 +00:00
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,
}
}
2024-09-04 02:54:56 +00:00
}
impl<M: AnyFrameMeta> Segment<M> {
/// 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<F>(range: Range<Paddr>, mut metadata_fn: F) -> Self
where
F: FnMut(Paddr) -> M,
{
for paddr in range.clone().step_by(PAGE_SIZE) {
let _ = ManuallyDrop::new(Frame::<M>::from_unused(paddr, metadata_fn(paddr)));
}
Self {
range,
_marker: core::marker::PhantomData,
}
}
2024-12-24 07:07:07 +00:00
}
impl<M: AnyFrameMeta + ?Sized> Segment<M> {
/// 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.
2024-09-04 02:54:56 +00:00
///
/// The resulting frames cannot be empty. So the offset cannot be neither
/// zero nor the length of the frames.
2024-09-04 02:54:56 +00:00
///
/// # Panics
///
/// The function panics if the offset is out of bounds, at either ends, or
2024-09-04 02:54:56 +00:00
/// 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.
2024-09-04 02:54:56 +00:00
///
/// The sliced byte offset range in indexed by the offset from the start of
/// the contiguous frames. The resulting frames holds extra reference counts.
2024-09-04 02:54:56 +00:00
///
/// # 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.
2024-09-04 02:54:56 +00:00
pub fn slice(&self, range: &Range<usize>) -> 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) };
2024-09-04 02:54:56 +00:00
}
Self {
range: start..end,
_marker: core::marker::PhantomData,
2024-09-04 02:54:56 +00:00
}
}
}
impl<M: AnyFrameMeta + ?Sized> From<Frame<M>> for Segment<M> {
fn from(frame: Frame<M>) -> Self {
let pa = frame.start_paddr();
let _ = ManuallyDrop::new(frame);
Self {
range: pa..pa + PAGE_SIZE,
_marker: core::marker::PhantomData,
}
}
}
impl<M: AnyFrameMeta + ?Sized> Iterator for Segment<M> {
type Item = Frame<M>;
2024-09-04 02:54:56 +00:00
fn next(&mut self) -> Option<Self::Item> {
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::<M>::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<M: AnyFrameMeta> From<Segment<M>> for Segment<dyn AnyFrameMeta> {
fn from(seg: Segment<M>) -> Self {
let seg = ManuallyDrop::new(seg);
Self {
range: seg.range.clone(),
_marker: core::marker::PhantomData,
}
}
}
impl<M: AnyFrameMeta> TryFrom<Segment<dyn AnyFrameMeta>> for Segment<M> {
type Error = Segment<dyn AnyFrameMeta>;
fn try_from(seg: Segment<dyn AnyFrameMeta>) -> core::result::Result<Self, Self::Error> {
// SAFETY: for each page there would be a forgotten handle
// when creating the `Segment` object.
let first_frame = unsafe { Frame::<dyn AnyFrameMeta>::from_raw(seg.range.start) };
let first_frame = ManuallyDrop::new(first_frame);
if !(first_frame.dyn_meta() as &dyn core::any::Any).is::<M>() {
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::<dyn AnyFrameMeta>::from_raw(paddr) };
let frame = ManuallyDrop::new(frame);
debug_assert!((frame.dyn_meta() as &dyn core::any::Any).is::<M>());
}
}
// SAFETY: The metadata is coerceable and the struct is transmutable.
Ok(unsafe { core::mem::transmute::<Segment<dyn AnyFrameMeta>, Segment<M>>(seg) })
}
}
impl<M: AnyUFrameMeta> From<Segment<M>> for USegment {
fn from(seg: Segment<M>) -> Self {
// SAFETY: The metadata is coerceable and the struct is transmutable.
unsafe { core::mem::transmute(seg) }
}
}
impl<M: AnyUFrameMeta> From<&Segment<M>> for &USegment {
fn from(seg: &Segment<M>) -> Self {
// SAFETY: The metadata is coerceable and the struct is transmutable.
unsafe { core::mem::transmute(seg) }
}
}
impl TryFrom<Segment<dyn AnyFrameMeta>> for USegment {
type Error = Segment<dyn AnyFrameMeta>;
/// Try converting a [`Segment<dyn AnyFrameMeta>`] 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<dyn AnyFrameMeta>) -> core::result::Result<Self, Self::Error> {
// SAFETY: for each page there would be a forgotten handle
// when creating the `Segment` object.
let first_frame = unsafe { Frame::<dyn AnyFrameMeta>::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::<dyn AnyFrameMeta>::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::<Segment<dyn AnyFrameMeta>, USegment>(seg) })
}
}