#[repr(transparent)]pub struct Segment<M: AnyFrameMeta + ?Sized> {
pub range: Range<Paddr>,
pub _marker: PhantomData<M>,
}Expand description
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.
Fields§
§range: Range<Paddr>The physical address range of the segment.
_marker: PhantomData<M>Marker for the metadata type.
Implementations§
Source§impl<M: AnyFrameMeta + ?Sized> Segment<M>
impl<M: AnyFrameMeta + ?Sized> Segment<M>
Sourcepub open spec fn inv_with(&self, owner: &SegmentOwner<M>) -> bool
pub open spec fn inv_with(&self, owner: &SegmentOwner<M>) -> bool
{
&&& self.inv()
&&& owner.perms.len() * PAGE_SIZE == self.range.end - self.range.start
&&& forall |i: int| {
0 <= i < owner.perms.len() as int
==> owner.perms[i].addr()
== meta_addr(frame_to_index((self.range.start + i * PAGE_SIZE) as usize))
}
}The invariant of a Segment with a specific owner, such that besides Self::inv:
- the number of permissions in the owner matches the number of frames in the segment;
- the physical address of each permission matches the corresponding frame in the segment.
Interested readers are encouraged to see frame_to_index and meta_addr for how
we convert between physical addresses and meta region indices.
Source§impl<M: AnyFrameMeta + Repr<MetaSlotStorage> + OwnerOf> Segment<M>
impl<M: AnyFrameMeta + Repr<MetaSlotStorage> + OwnerOf> Segment<M>
Sourcepub open spec fn start_paddr_spec(&self) -> Paddr
pub open spec fn start_paddr_spec(&self) -> Paddr
self.inv(),{ self.range.start }Returns the starting physical address of the contiguous frames.
Sourcepub open spec fn end_paddr_spec(&self) -> Paddr
pub open spec fn end_paddr_spec(&self) -> Paddr
self.inv(),{ self.range.end }Returns the ending physical address of the contiguous frames.
Sourcepub open spec fn size_spec(&self) -> usize
pub open spec fn size_spec(&self) -> usize
self.inv(),{ (self.range.end - self.range.start) as usize }Returns the length in bytes of the contiguous frames.
Sourcepub open spec fn nrpage_spec(&self) -> usize
pub open spec fn nrpage_spec(&self) -> usize
self.inv(),{ self.size_spec() / PAGE_SIZE }Returns the number of pages of the contiguous frames.
Sourcepub open spec fn split_spec(self, offset: usize) -> (Self, Self)
pub open spec fn split_spec(self, offset: usize) -> (Self, Self)
self.inv(),offset % PAGE_SIZE == 0,0 < offset < self.size_spec(),{
let at = (self.range.start + offset) as usize;
let idx = at / PAGE_SIZE;
(
Self {
range: self.range.start..at,
_marker: core::marker::PhantomData,
},
Self {
range: at..self.range.end,
_marker: core::marker::PhantomData,
},
)
}Splits the contiguous frames into two at the given byte offset from the start in spec mode.
Sourcepub exec fn start_paddr(&self) -> res : Paddr
pub exec fn start_paddr(&self) -> res : Paddr
self.inv(),Gets the start physical address of the contiguous frames.
Sourcepub exec fn end_paddr(&self) -> res : Paddr
pub exec fn end_paddr(&self) -> res : Paddr
self.inv(),Gets the end physical address of the contiguous frames.
Sourcepub exec fn size(&self) -> res : usize
pub exec fn size(&self) -> res : usize
self.inv(),Gets the length in bytes of the contiguous frames.
Sourcepub open spec fn from_unused_requires(
regions: MetaRegionOwners,
range: Range<Paddr>,
metadata_fn: impl Fn(Paddr) -> (Paddr, M),
) -> bool
pub open spec fn from_unused_requires( regions: MetaRegionOwners, range: Range<Paddr>, metadata_fn: impl Fn(Paddr) -> (Paddr, M), ) -> bool
{
&&& regions.inv()
&&& range.start % PAGE_SIZE == 0
&&& range.end % PAGE_SIZE == 0
&&& range.start <= range.end < MAX_PADDR
&&& forall |paddr_in: Paddr| {
(range.start <= paddr_in < range.end && paddr_in % PAGE_SIZE == 0)
==> {
&&& metadata_fn.requires((paddr_in,))
}
}
&&& forall |paddr_in: Paddr, paddr_out: Paddr, m: M| {
metadata_fn.ensures((paddr_in,), (paddr_out, m))
==> {
&&& paddr_out < MAX_PADDR
&&& paddr_out % PAGE_SIZE == 0
&&& paddr_in == paddr_out
&&& regions.slots.contains_key(frame_to_index(paddr_out))
&&& regions.slot_owners[frame_to_index(paddr_out)].usage is Unused
&&& regions
.slot_owners[frame_to_index(paddr_out)]
.inner_perms
.in_list
.points_to(0)
}
}
}The wrapper for the precondition for Self::from_unused:
- the metadata function must be well-formed and valid for all frames in the range;
- the metadata function must ensure that the frames can be created and owned by the segment.
- for any frame created via the closure
metadata_fn, the corresponding slot inregionsmust be unused and not dropped in the owner (MetaRegionOwners).
Sourcepub open spec fn from_unused_ensures(
old_regions: MetaRegionOwners,
new_regions: MetaRegionOwners,
owner: Option<SegmentOwner<M>>,
range: Range<Paddr>,
metadata_fn: impl Fn(Paddr) -> (Paddr, M),
r: Result<Self, GetFrameError>,
) -> bool
pub open spec fn from_unused_ensures( old_regions: MetaRegionOwners, new_regions: MetaRegionOwners, owner: Option<SegmentOwner<M>>, range: Range<Paddr>, metadata_fn: impl Fn(Paddr) -> (Paddr, M), r: Result<Self, GetFrameError>, ) -> bool
{
&&& r matches Ok(
r,
) ==> {
&&& new_regions.inv()
&&& r.range.start == range.start
&&& r.range.end == range.end
&&& owner matches Some(
owner,
) && {
&&& r.inv_with(&owner)
&&& forall |i: int| {
0 <= i < owner.perms.len() as int
==> {
&&& owner.perms[i].addr()
== meta_addr(
frame_to_index_spec((range.start + i * PAGE_SIZE) as usize),
)
}
}
&&& forall |paddr: Paddr| {
(range.start <= paddr < range.end && paddr % PAGE_SIZE == 0)
==> !new_regions.slots.contains_key(frame_to_index_spec(paddr))
}
}
}
}The wrapper for the postcondition for Self::from_unused:
- if the result is
Ok, then the returned segment must satisfy the invariant with the owner; - the returned segment must have the same physical address range as the input;
- the returned owner must be
Someif the result isOk, and the owner must satisfy the invariant;
Sourcepub open spec fn split_requires(self, owner: SegmentOwner<M>, offset: usize) -> bool
pub open spec fn split_requires(self, owner: SegmentOwner<M>, offset: usize) -> bool
{
&&& self.inv_with(&owner)
&&& offset % PAGE_SIZE == 0
&&& 0 < offset < self.size()
}The wrapper for the precondition for Self::split:
- the segment must satisfy the invariant with the owner (
Self::inv_with) - the offset must be aligned and within bounds.
Sourcepub open spec fn split_ensures(
self,
offset: usize,
lhs: Self,
rhs: Self,
ori_owner: SegmentOwner<M>,
lhs_owner: SegmentOwner<M>,
rhs_owner: SegmentOwner<M>,
) -> bool
pub open spec fn split_ensures( self, offset: usize, lhs: Self, rhs: Self, ori_owner: SegmentOwner<M>, lhs_owner: SegmentOwner<M>, rhs_owner: SegmentOwner<M>, ) -> bool
{
&&& lhs.inv_with(&lhs_owner)
&&& rhs.inv_with(&rhs_owner)
&&& (lhs, rhs) == self.split_spec(offset)
&&& ori_owner.perms =~= (lhs_owner.perms + rhs_owner.perms)
}The wrapper for the postcondition for Self::split:
- the resulting segments must satisfy the invariant with the corresponding owners;
- the resulting segments must be the same as the result of
Self::split_spec; - the permissions in the original owner must be split into the resulting owners.
Sourcepub open spec fn into_raw_requires(
self,
regions: MetaRegionOwners,
owner: SegmentOwner<M>,
) -> bool
pub open spec fn into_raw_requires( self, regions: MetaRegionOwners, owner: SegmentOwner<M>, ) -> bool
{
&&& self.inv_with(&owner)
&&& regions.inv()
&&& owner.inv()
}The wrapper for the precondition for Self::into_raw:
- the segment must satisfy the invariant with the owner;
- the meta region in
regionsmust satisfy the invariant.
Sourcepub open spec fn into_raw_ensures(
self,
old_regions: MetaRegionOwners,
regions: MetaRegionOwners,
r: Range<Paddr>,
) -> bool
pub open spec fn into_raw_ensures( self, old_regions: MetaRegionOwners, regions: MetaRegionOwners, r: Range<Paddr>, ) -> bool
{
&&& r == self.range
&&& regions.inv()
&&& regions =~= old_regions
}The wrapper for the postcondition for Self::into_raw:
- the returned physical address range must be the same as the segment’s range;
- the meta region is unchanged.
Sourcepub open spec fn from_raw_requires(
regions: MetaRegionOwners,
range: Range<Paddr>,
owner: SegmentOwner<M>,
) -> bool
pub open spec fn from_raw_requires( regions: MetaRegionOwners, range: Range<Paddr>, owner: SegmentOwner<M>, ) -> bool
{
&&& regions.inv()
&&& range.start % PAGE_SIZE == 0
&&& range.end % PAGE_SIZE == 0
&&& range.start < range.end < MAX_PADDR
}The wrapper for the precondition for Self::from_raw:
- the range must be a forgotten
Segmentthat matches the typeM; - the caller must provide the owner with valid permissions;
- the range must be aligned and within bounds.
Sourcepub open spec fn from_raw_ensures(
self,
old_regions: MetaRegionOwners,
new_regions: MetaRegionOwners,
owner: SegmentOwner<M>,
range: Range<Paddr>,
) -> bool
pub open spec fn from_raw_ensures( self, old_regions: MetaRegionOwners, new_regions: MetaRegionOwners, owner: SegmentOwner<M>, range: Range<Paddr>, ) -> bool
{
&&& self.range == range
&&& new_regions.inv()
&&& new_regions =~= old_regions
}The wrapper for the postcondition for Self::from_raw:
- the returned segment must have the same physical address range as the input;
- the meta region is unchanged.
Sourcepub open spec fn slice_requires(self, owner: SegmentOwner<M>, range: Range<Paddr>) -> bool
pub open spec fn slice_requires(self, owner: SegmentOwner<M>, range: Range<Paddr>) -> bool
{
&&& self.inv_with(&owner)
&&& range.start % PAGE_SIZE == 0
&&& range.end % PAGE_SIZE == 0
&&& self.range.start + range.start <= self.range.start + range.end <= self.range.end
}The wrapper for the precondition for slicing a segment with a given range:
- the segment must satisfy the invariant with the owner (
Self::inv_with) - the slicing range must be aligned and within bounds of the segment.
Sourcepub open spec fn slice_ensures(
self,
owner: SegmentOwner<M>,
range: Range<Paddr>,
res: Self,
) -> bool
pub open spec fn slice_ensures( self, owner: SegmentOwner<M>, range: Range<Paddr>, res: Self, ) -> bool
{
&&& res
.inv_with(
&SegmentOwner {
perms: owner
.perms
.subrange(
(range.start / PAGE_SIZE) as int,
(range.end / PAGE_SIZE) as int,
),
},
)
}The wrapper for the postcondition for slicing a segment with a given range:
- the resulting slice must satisfy the invariant with the owner;
- the resulting slice must have the same physical address range as the slicing range.
See also [vstd::seq::Seq::subrange].
Sourcepub open spec fn next_requires(
self,
regions: MetaRegionOwners,
owner: SegmentOwner<M>,
) -> bool
pub open spec fn next_requires( self, regions: MetaRegionOwners, owner: SegmentOwner<M>, ) -> bool
{
&&& self.inv()
&&& regions.inv()
&&& owner.perms.len() > 0
&&& !regions.slots.contains_key(frame_to_index(self.range.start))
&&& regions.slot_owners.contains_key(frame_to_index(self.range.start))
&&& regions.slot_owners[frame_to_index(self.range.start)].raw_count == 1
&&& regions.slot_owners[frame_to_index(self.range.start)].self_addr
== frame_to_meta(self.range.start)
&&& owner.perms[0].points_to.is_init()
&&& owner.perms[0].points_to.addr() == frame_to_meta(self.range.start)
&&& owner
.perms[0]
.points_to
.value()
.wf(regions.slot_owners[frame_to_index(self.range.start)])
}Checks if the current segment can be iterated to get the next frame:
- the segment and meta regions must satisfy their respective invariants;
- the frame’s slot must not be in
regions.slots(the owner holds the permission); - the frame’s raw_count must be 1 (it was forgotten once).
Sourcepub open spec fn next_ensures(
old_self: Self,
new_self: Self,
old_regions: MetaRegionOwners,
new_regions: MetaRegionOwners,
res: Option<Frame<M>>,
) -> bool
pub open spec fn next_ensures( old_self: Self, new_self: Self, old_regions: MetaRegionOwners, new_regions: MetaRegionOwners, res: Option<Frame<M>>, ) -> bool
{
&&& new_regions.inv()
&&& new_self.inv()
&&& match res {
None => (
&&& new_self.range.start == old_self.range.end
),
Some(f) => (
&&& new_self.range.start == old_self.range.start + PAGE_SIZE
&&& f.paddr() == old_self.range.start
&&& new_regions.slots.contains_key(frame_to_index(f.paddr()))
&&& new_regions.slot_owners[frame_to_index(f.paddr())].raw_count == 0
),
}
}The wrapper for the postcondition for iterating to the next frame:
Sourcepub exec fn from_unused(
range: Range<Paddr>,
metadata_fn: impl Fn(Paddr) -> (Paddr, M),
) -> res : Result<Self, GetFrameError>
pub exec fn from_unused( range: Range<Paddr>, metadata_fn: impl Fn(Paddr) -> (Paddr, M), ) -> res : Result<Self, GetFrameError>
Tracked(regions): Tracked<&mut MetaRegionOwners>,
->
owner: Tracked<Option<SegmentOwner<M>>>,requiresSelf::from_unused_requires(*old(regions), range, metadata_fn),ensuresSelf::from_unused_ensures(*old(regions), *regions, owner@, range, metadata_fn, r),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.
It returns an error if:
- any of the frames cannot be created with a specific reason.
§Verified Properties
§Preconditions
See Self::from_unused_requires.
§Postconditions
Sourcepub exec fn split(self, offset: usize) -> r : (Self, Self)
pub exec fn split(self, offset: usize) -> r : (Self, Self)
Tracked(owner): Tracked<SegmentOwner<M>>,
Tracked(regions): Tracked<&mut MetaRegionOwners>,
->
frame_perms: (Tracked<SegmentOwner<M>>, Tracked<SegmentOwner<M>>),requiresSelf::split_requires(self, owner, offset),ensuresSelf::split_ensures(self, offset, r.0, r.1, owner, frame_perms.0@, frame_perms.1@),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.
§Verified Properties
§Preconditions
See Self::split_requires.
§Postconditions
See Self::split_ensures.
Sourcepub exec fn slice(&self, range: &Range<Paddr>) -> r : Self
pub exec fn slice(&self, range: &Range<Paddr>) -> r : Self
Tracked(owner): Tracked<&SegmentOwner<M>>,requiresSelf::slice_requires(*self, *owner, *range),ensuresSelf::slice_ensures(*self, *owner, *range, r),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.
§Verified Properties
§Verified Properties
§Preconditions
See Self::slice_requires.
§Postconditions
See Self::slice_ensures.
Sourcepub exec fn next(&mut self) -> res : Option<Frame<M>>
pub exec fn next(&mut self) -> res : Option<Frame<M>>
Tracked(regions): Tracked<&mut MetaRegionOwners>,
Tracked(owner): Tracked<&mut SegmentOwner<M>>,requiresSelf::next_requires(*old(self), *old(regions), *old(owner)),ensuresSelf::next_ensures(*old(self), *self, *old(regions), *regions, res),Gets the next frame in the segment (raw), if any.
Since the segments here must be “non-active” where
there is no extra Verus-tracked permission SegmentOwner
associated with it; Segment becomes a kind of “zombie”
container through which we can only iterate the frames and
get the frame out of the regions instead.
§Note
We chose to make next the member function of Segment rather than a trait method
because the current Verus standard library has limited support for core::iter::Iterator
and associated types, and we want to avoid the complexity of defining a custom iterator trait
and implementing it for Segment.
§Verified Properties
§Preconditions
See Self::next_requires.
§Postconditions
See Self::next_ensures.
Trait Implementations§
Source§impl<M: AnyFrameMeta + ?Sized> Inv for Segment<M>
impl<M: AnyFrameMeta + ?Sized> Inv for Segment<M>
Source§open spec fn inv(self) -> bool
open spec fn inv(self) -> bool
{
&&& self.range.start % PAGE_SIZE == 0
&&& self.range.end % PAGE_SIZE == 0
&&& self.range.start <= self.range.end < MAX_PADDR
}The invariant of a Segment:
- the physical addresses of the frames are aligned and within bounds.
- the range is well-formed, i.e., the start is less than or equal to the end.
Source§impl<M: AnyFrameMeta + ?Sized> TrackDrop for Segment<M>
impl<M: AnyFrameMeta + ?Sized> TrackDrop for Segment<M>
Source§open spec fn constructor_requires(self, s: Self::State) -> bool
open spec fn constructor_requires(self, s: Self::State) -> bool
{ true }Source§open spec fn constructor_ensures(self, s0: Self::State, s1: Self::State) -> bool
open spec fn constructor_ensures(self, s0: Self::State, s1: Self::State) -> bool
{ s0 =~= s1 }Source§proof fn constructor_spec(self, tracked s: &mut Self::State)
proof fn constructor_spec(self, tracked s: &mut Self::State)
Source§open spec fn drop_requires(self, s: Self::State) -> bool
open spec fn drop_requires(self, s: Self::State) -> bool
{ true }Source§open spec fn drop_ensures(self, s0: Self::State, s1: Self::State) -> bool
open spec fn drop_ensures(self, s0: Self::State, s1: Self::State) -> bool
{ s0 =~= s1 }