pub struct MemView {
pub mappings: Set<Mapping>,
pub memory: Map<Paddr, FrameContents>,
}Expand description
A local virtual-memory view used in proofs.
A MemView pairs:
Self::mappings: a set ofMappingentries used bySelf::addr_transl;Self::memory: frame bytes keyed by physical frame base (Paddr) viaFrameContents.
In practice this view is obtained from GlobalMemView using
GlobalMemView::take_view, then consumed by APIs such as VirtPtr::read,
VirtPtr::write, and higher-level ownership proofs in
VmSpaceOwner.
Fields§
§mappings: Set<Mapping>Virtual-to-physical mapping set used for address translation.
memory: Map<Paddr, FrameContents>Physical frame contents for mapped pages referenced by Self::mappings.
Implementations§
Source§impl MemView
impl MemView
Sourcepub open spec fn addr_transl(self, va: usize) -> Option<(usize, usize)>
pub open spec fn addr_transl(self, va: usize) -> Option<(usize, usize)>
{
let mappings = self
.mappings
.filter(|m: Mapping| m.va_range.start <= va < m.va_range.end);
if 0 < mappings.len() {
let m = mappings.choose();
let off = va - m.va_range.start;
Some((m.pa_range.start, off as usize))
} else {
None
}
}Translates a virtual address to (frame_base_pa, frame_offset).
Returns Option::None if no mapping in Self::mappings covers va.
Sourcepub open spec fn read(self, va: usize) -> MemContents<u8>
pub open spec fn read(self, va: usize) -> MemContents<u8>
{
let (pa, off) = self.addr_transl(va)->0;
self.memory[pa].contents[off as int]
}Specification read through virtual translation.
Equivalent to resolving via Self::addr_transl and reading from Self::memory.
Sourcepub open spec fn write(self, va: usize, x: u8) -> Self
pub open spec fn write(self, va: usize, x: u8) -> Self
{
let (pa, off) = self.addr_transl(va)->0;
MemView {
memory: self
.memory
.insert(
pa,
FrameContents {
contents: self
.memory[pa]
.contents
.update(off as int, raw_ptr::MemContents::Init(x)),
..self.memory[pa]
},
),
..self
}
}Specification write through virtual translation.
Returns a new MemView with one byte updated, preserving all mappings.
Sourcepub open spec fn write_bytes(self, va: usize, bytes: Seq<u8>) -> Self
pub open spec fn write_bytes(self, va: usize, bytes: Seq<u8>) -> Self
{
if bytes.len() == 0 {
self
} else {
self.write(va, bytes[0]).write_bytes((va + 1) as usize, bytes.drop_first())
}
}Spec for writing bytes.len() bytes starting at va.
bytes[0] is written to va, bytes[1] to va + 1, etc.
Sourcepub open spec fn read_bytes(self, va: usize, len: usize) -> Seq<u8>
pub open spec fn read_bytes(self, va: usize, len: usize) -> Seq<u8>
{
if len == 0 {
Seq::empty()
} else {
seq![self.read(va).value()]
.add(self.read_bytes((va + 1) as usize, (len - 1) as usize))
}
}Spec for reading len bytes starting at va.
The byte at va + i is self.read(va + i).value(). Callers should only
rely on the result when every byte in the range is initialized; otherwise
the returned sequence contains arbitrary values where memory is Uninit.
Sourcepub proof fn lemma_write_bytes_mappings(self, va: usize, bytes: Seq<u8>)
pub proof fn lemma_write_bytes_mappings(self, va: usize, bytes: Seq<u8>)
self.write_bytes(va, bytes).mappings == self.mappings,Lemma: Self::write_bytes preserves Self::mappings.
Sourcepub proof fn lemma_write_bytes_addr_transl(
self,
va: usize,
bytes: Seq<u8>,
query: usize,
)
pub proof fn lemma_write_bytes_addr_transl( self, va: usize, bytes: Seq<u8>, query: usize, )
self.write_bytes(va, bytes).addr_transl(query) == self.addr_transl(query),Lemma: Self::write_bytes preserves Self::addr_transl at every address.
Sourcepub proof fn lemma_write_bytes_memory_dom_grows(self, va: usize, bytes: Seq<u8>)
pub proof fn lemma_write_bytes_memory_dom_grows(self, va: usize, bytes: Seq<u8>)
self.memory.dom().subset_of(self.write_bytes(va, bytes).memory.dom()),Lemma: Self::write_bytes only grows Self::memory’s domain.
Sourcepub open spec fn is_mapped(self, va: usize, pa: usize) -> bool
pub open spec fn is_mapped(self, va: usize, pa: usize) -> bool
{ self.addr_transl(va) is Some && self.addr_transl(va)->Some_0.0 == pa }Whether va is translated and mapped to frame base pa.
Sourcepub open spec fn borrow_at(&self, vaddr: usize, len: usize) -> MemView
pub open spec fn borrow_at(&self, vaddr: usize, len: usize) -> MemView
{
let range_end = vaddr + len;
let valid_pas = self
.memory
.dom()
.filter(|pa: usize| {
exists |va: usize| {
vaddr <= va < range_end && #[trigger] self.is_mapped(va, pa)
}
});
MemView {
mappings: self
.mappings
.filter(|m: Mapping| m.va_range.start < range_end && m.va_range.end > vaddr),
memory: self.memory.restrict(valid_pas),
}
}Specification for borrowing a sub-view covering [vaddr, vaddr + len).
The result keeps overlapping mappings and restricts memory to physical frames reachable from that virtual range.
Sourcepub open spec fn mappings_are_disjoint(self) -> bool
pub open spec fn mappings_are_disjoint(self) -> bool
{
forall |m1: Mapping, m2: Mapping| {
self.mappings.contains(m1) && self.mappings.contains(m2) && m1 != m2
==> {
m1.va_range.end <= m2.va_range.start
|| m2.va_range.end <= m1.va_range.start
}
}
}Whether mappings in this view are pairwise VA-disjoint.
Sourcepub open spec fn split(self, vaddr: usize, len: usize) -> (MemView, MemView)
pub open spec fn split(self, vaddr: usize, len: usize) -> (MemView, MemView)
{
let split_end = vaddr + len;
let left_mappings = self
.mappings
.filter(|m: Mapping| m.va_range.start < split_end && m.va_range.end > vaddr);
let right_mappings = self.mappings.filter(|m: Mapping| m.va_range.end > split_end);
let left_pas = self
.memory
.dom()
.filter(|pa: usize| {
exists |va: usize| vaddr <= va < split_end && self.is_mapped(va, pa)
});
let right_pas = self
.memory
.dom()
.filter(|pa: usize| {
exists |va: usize| va >= split_end && self.is_mapped(va, pa)
});
(
MemView {
mappings: left_mappings,
memory: self.memory.restrict(left_pas),
},
MemView {
mappings: right_mappings,
memory: self.memory.restrict(right_pas),
},
)
}Specification for splitting this view at split_end = vaddr + len.
Returns (left, right) where:
leftcovers[vaddr, split_end), i.e. all mappings overlapping that range.rightcovers addresses>= split_end.
Sourcepub proof fn tracked_borrow_at(tracked &self, vaddr: usize, len: usize) -> tracked r : MemView
pub proof fn tracked_borrow_at(tracked &self, vaddr: usize, len: usize) -> tracked r : MemView
r == self.borrow_at(vaddr, len),Tracked wrapper of Self::borrow_at.
§Verified Properties
§Postconditions
r == self.borrow_at(vaddr, len).
Sourcepub proof fn tracked_split(tracked self, vaddr: usize, len: usize) -> tracked r : (Self, Self)
pub proof fn tracked_split(tracked self, vaddr: usize, len: usize) -> tracked r : (Self, Self)
r == self.split(vaddr, len),Tracked wrapper of Self::split.
§Verified Properties
§Postconditions
r == self.split(vaddr, len).
Sourcepub proof fn lemma_split_preserves_transl(
original: MemView,
vaddr: usize,
len: usize,
left: MemView,
right: MemView,
)
pub proof fn lemma_split_preserves_transl( original: MemView, vaddr: usize, len: usize, left: MemView, right: MemView, )
original.split(vaddr, len) == (left, right),ensuresright.memory.dom().subset_of(original.memory.dom()),forall |va: usize| {
vaddr <= va < vaddr + len
==> { #[trigger] original.addr_transl(va) == left.addr_transl(va) }
},forall |va: usize| {
va >= vaddr + len
==> { #[trigger] original.addr_transl(va) == right.addr_transl(va) }
},Lemma: Self::split preserves translation semantics on each side.
§Verified Properties
§Preconditions
original.split(vaddr, len) == (left, right).
§Postconditions
right.memory.dom().subset_of(original.memory.dom()).- For any
vain[vaddr, vaddr + len),original.addr_transl(va) == left.addr_transl(va). - For any
va >= vaddr + len,original.addr_transl(va) == right.addr_transl(va).
Sourcepub open spec fn join(self, other: MemView) -> MemView
pub open spec fn join(self, other: MemView) -> MemView
{
MemView {
mappings: self.mappings.union(other.mappings),
memory: self.memory.union_prefer_right(other.memory),
}
}Specification for merging two views.
Mappings are unioned, and memory conflicts are resolved by
[vstd::map::Map::union_prefer_right] with other taking precedence.
Sourcepub proof fn tracked_join(tracked &mut self, tracked other: Self)
pub proof fn tracked_join(tracked &mut self, tracked other: Self)
old(self).mappings.disjoint(other.mappings),ensures*final(self) == old(self).join(other),Tracked wrapper of Self::join.
§Verified Properties
§Preconditions
old(self).mappings.disjoint(other.mappings).
§Postconditions
*self == old(self).join(other).
Sourcepub proof fn lemma_split_join_identity(
this: MemView,
lhs: MemView,
rhs: MemView,
vaddr: usize,
len: usize,
)
pub proof fn lemma_split_join_identity( this: MemView, lhs: MemView, rhs: MemView, vaddr: usize, len: usize, )
this.split(vaddr, len) == (lhs, rhs),forall |m: Mapping| {
#[trigger] this.mappings.contains(m) ==> vaddr <= m.va_range.start < m.va_range.end
},forall |pa: Paddr| {
#[trigger] this.memory.contains_key(pa)
==> exists |va: usize| vaddr <= va && #[trigger] this.is_mapped(va, pa)
},ensuresthis.mappings == lhs.join(rhs).mappings,this.memory == lhs.join(rhs).memory,Lemma: splitting and then joining reconstructs the original view.
§Verified Properties
§Preconditions
this.split(vaddr, len) == (lhs, rhs).- Every mapping in
thisstarts at or aftervaddr. - Every physical frame tracked by
this.memoryis reachable from some virtual address at or aftervaddr.
§Postconditions
lhs.join(rhs)has the same mappings and memory contents asthis.
Sourcepub proof fn lemma_split_preserves_read(
original: MemView,
vaddr: usize,
len: usize,
left: MemView,
right: MemView,
)
pub proof fn lemma_split_preserves_read( original: MemView, vaddr: usize, len: usize, left: MemView, right: MemView, )
original.split(vaddr, len) == (left, right),ensuresforall |va: usize| {
va >= vaddr + len && original.addr_transl(va) is Some
&& original.memory.contains_key((original.addr_transl(va)->0).0)
==> original.read(va) == right.read(va)
},Lemma: byte-level reads agree on the right half of a split.
For any va >= vaddr + len whose translated frame is present in
original.memory, the stored frame contents at va match the
original view. This is the byte-level analogue of
Self::lemma_split_preserves_transl and supports propagating
the read view across [VmIoOwner::advance] calls (used by loops
over read_once).
Sourcepub proof fn lemma_read_bytes_eq_pointwise(
a: MemView,
b: MemView,
va: usize,
n: usize,
)
pub proof fn lemma_read_bytes_eq_pointwise( a: MemView, b: MemView, va: usize, n: usize, )
forall |i: usize| va <= i < va + n ==> a.read(i) == b.read(i),va + n <= usize::MAX,ensuresa.read_bytes(va, n) == b.read_bytes(va, n),Lemma: pointwise Self::read equality implies Self::read_bytes equality.
Auto Trait Implementations§
impl Freeze for MemView
impl RefUnwindSafe for MemView
impl Send for MemView
impl Sync for MemView
impl Unpin for MemView
impl UnsafeUnpin for MemView
impl UnwindSafe for MemView
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
§impl<T> Conv for T
impl<T> Conv for T
§impl<T> FmtForward for T
impl<T> FmtForward for T
§fn fmt_binary(self) -> FmtBinary<Self>where
Self: Binary,
fn fmt_binary(self) -> FmtBinary<Self>where
Self: Binary,
self to use its Binary implementation when Debug-formatted.§fn fmt_display(self) -> FmtDisplay<Self>where
Self: Display,
fn fmt_display(self) -> FmtDisplay<Self>where
Self: Display,
self to use its Display implementation when
Debug-formatted.§fn fmt_lower_exp(self) -> FmtLowerExp<Self>where
Self: LowerExp,
fn fmt_lower_exp(self) -> FmtLowerExp<Self>where
Self: LowerExp,
self to use its LowerExp implementation when
Debug-formatted.§fn fmt_lower_hex(self) -> FmtLowerHex<Self>where
Self: LowerHex,
fn fmt_lower_hex(self) -> FmtLowerHex<Self>where
Self: LowerHex,
self to use its LowerHex implementation when
Debug-formatted.§fn fmt_octal(self) -> FmtOctal<Self>where
Self: Octal,
fn fmt_octal(self) -> FmtOctal<Self>where
Self: Octal,
self to use its Octal implementation when Debug-formatted.§fn fmt_pointer(self) -> FmtPointer<Self>where
Self: Pointer,
fn fmt_pointer(self) -> FmtPointer<Self>where
Self: Pointer,
self to use its Pointer implementation when
Debug-formatted.§fn fmt_upper_exp(self) -> FmtUpperExp<Self>where
Self: UpperExp,
fn fmt_upper_exp(self) -> FmtUpperExp<Self>where
Self: UpperExp,
self to use its UpperExp implementation when
Debug-formatted.§fn fmt_upper_hex(self) -> FmtUpperHex<Self>where
Self: UpperHex,
fn fmt_upper_hex(self) -> FmtUpperHex<Self>where
Self: UpperHex,
self to use its UpperHex implementation when
Debug-formatted.§fn fmt_list(self) -> FmtList<Self>where
&'a Self: for<'a> IntoIterator,
fn fmt_list(self) -> FmtList<Self>where
&'a Self: for<'a> IntoIterator,
§impl<T, VERUS_SPEC__A> FromSpec<T> for VERUS_SPEC__Awhere
VERUS_SPEC__A: From<T>,
impl<T, VERUS_SPEC__A> FromSpec<T> for VERUS_SPEC__Awhere
VERUS_SPEC__A: From<T>,
fn obeys_from_spec() -> bool
fn from_spec(v: T) -> VERUS_SPEC__A
§impl<T, VERUS_SPEC__A> IntoSpec<T> for VERUS_SPEC__Awhere
VERUS_SPEC__A: Into<T>,
impl<T, VERUS_SPEC__A> IntoSpec<T> for VERUS_SPEC__Awhere
VERUS_SPEC__A: Into<T>,
fn obeys_into_spec() -> bool
fn into_spec(self) -> T
§impl<T, U> IntoSpecImpl<U> for Twhere
U: From<T>,
impl<T, U> IntoSpecImpl<U> for Twhere
U: From<T>,
fn obeys_into_spec() -> bool
fn into_spec(self) -> U
§impl<T> Pipe for Twhere
T: ?Sized,
impl<T> Pipe for Twhere
T: ?Sized,
§fn pipe<R>(self, func: impl FnOnce(Self) -> R) -> Rwhere
Self: Sized,
fn pipe<R>(self, func: impl FnOnce(Self) -> R) -> Rwhere
Self: Sized,
§fn pipe_ref<'a, R>(&'a self, func: impl FnOnce(&'a Self) -> R) -> Rwhere
R: 'a,
fn pipe_ref<'a, R>(&'a self, func: impl FnOnce(&'a Self) -> R) -> Rwhere
R: 'a,
self and passes that borrow into the pipe function. Read more§fn pipe_ref_mut<'a, R>(&'a mut self, func: impl FnOnce(&'a mut Self) -> R) -> Rwhere
R: 'a,
fn pipe_ref_mut<'a, R>(&'a mut self, func: impl FnOnce(&'a mut Self) -> R) -> Rwhere
R: 'a,
self and passes that borrow into the pipe function. Read more§fn pipe_borrow<'a, B, R>(&'a self, func: impl FnOnce(&'a B) -> R) -> R
fn pipe_borrow<'a, B, R>(&'a self, func: impl FnOnce(&'a B) -> R) -> R
§fn pipe_borrow_mut<'a, B, R>(
&'a mut self,
func: impl FnOnce(&'a mut B) -> R,
) -> R
fn pipe_borrow_mut<'a, B, R>( &'a mut self, func: impl FnOnce(&'a mut B) -> R, ) -> R
§fn pipe_as_ref<'a, U, R>(&'a self, func: impl FnOnce(&'a U) -> R) -> R
fn pipe_as_ref<'a, U, R>(&'a self, func: impl FnOnce(&'a U) -> R) -> R
self, then passes self.as_ref() into the pipe function.§fn pipe_as_mut<'a, U, R>(&'a mut self, func: impl FnOnce(&'a mut U) -> R) -> R
fn pipe_as_mut<'a, U, R>(&'a mut self, func: impl FnOnce(&'a mut U) -> R) -> R
self, then passes self.as_mut() into the pipe
function.§fn pipe_deref<'a, T, R>(&'a self, func: impl FnOnce(&'a T) -> R) -> R
fn pipe_deref<'a, T, R>(&'a self, func: impl FnOnce(&'a T) -> R) -> R
self, then passes self.deref() into the pipe function.impl<A> SpecEq<&A> for Awhere
A: ?Sized,
impl<A> SpecEq<&mut A> for Awhere
A: ?Sized,
impl<A> SpecEq<A> for Awhere
A: ?Sized,
impl<A> SpecEq<Ghost<A>> for A
impl<A> SpecEq<Tracked<A>> for A
§impl<T> Tap for T
impl<T> Tap for T
§fn tap_borrow<B>(self, func: impl FnOnce(&B)) -> Self
fn tap_borrow<B>(self, func: impl FnOnce(&B)) -> Self
Borrow<B> of a value. Read more§fn tap_borrow_mut<B>(self, func: impl FnOnce(&mut B)) -> Self
fn tap_borrow_mut<B>(self, func: impl FnOnce(&mut B)) -> Self
BorrowMut<B> of a value. Read more§fn tap_ref<R>(self, func: impl FnOnce(&R)) -> Self
fn tap_ref<R>(self, func: impl FnOnce(&R)) -> Self
AsRef<R> view of a value. Read more§fn tap_ref_mut<R>(self, func: impl FnOnce(&mut R)) -> Self
fn tap_ref_mut<R>(self, func: impl FnOnce(&mut R)) -> Self
AsMut<R> view of a value. Read more§fn tap_deref<T>(self, func: impl FnOnce(&T)) -> Self
fn tap_deref<T>(self, func: impl FnOnce(&T)) -> Self
Deref::Target of a value. Read more§fn tap_deref_mut<T>(self, func: impl FnOnce(&mut T)) -> Self
fn tap_deref_mut<T>(self, func: impl FnOnce(&mut T)) -> Self
Deref::Target of a value. Read more§fn tap_dbg(self, func: impl FnOnce(&Self)) -> Self
fn tap_dbg(self, func: impl FnOnce(&Self)) -> Self
.tap() only in debug builds, and is erased in release builds.§fn tap_mut_dbg(self, func: impl FnOnce(&mut Self)) -> Self
fn tap_mut_dbg(self, func: impl FnOnce(&mut Self)) -> Self
.tap_mut() only in debug builds, and is erased in release
builds.§fn tap_borrow_dbg<B>(self, func: impl FnOnce(&B)) -> Self
fn tap_borrow_dbg<B>(self, func: impl FnOnce(&B)) -> Self
.tap_borrow() only in debug builds, and is erased in release
builds.§fn tap_borrow_mut_dbg<B>(self, func: impl FnOnce(&mut B)) -> Self
fn tap_borrow_mut_dbg<B>(self, func: impl FnOnce(&mut B)) -> Self
.tap_borrow_mut() only in debug builds, and is erased in release
builds.§fn tap_ref_dbg<R>(self, func: impl FnOnce(&R)) -> Self
fn tap_ref_dbg<R>(self, func: impl FnOnce(&R)) -> Self
.tap_ref() only in debug builds, and is erased in release
builds.§fn tap_ref_mut_dbg<R>(self, func: impl FnOnce(&mut R)) -> Self
fn tap_ref_mut_dbg<R>(self, func: impl FnOnce(&mut R)) -> Self
.tap_ref_mut() only in debug builds, and is erased in release
builds.§fn tap_deref_dbg<T>(self, func: impl FnOnce(&T)) -> Self
fn tap_deref_dbg<T>(self, func: impl FnOnce(&T)) -> Self
.tap_deref() only in debug builds, and is erased in release
builds.