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 eq_at(self, va1: usize, va2: usize) -> bool
pub open spec fn eq_at(self, va1: usize, va2: usize) -> bool
{
let (pa1, off1) = self.addr_transl(va1)->0;
let (pa2, off2) = self.addr_transl(va2)->0;
self.memory[pa1].contents[off1 as int] == self.memory[pa2].contents[off2 as int]
}Whether two virtual addresses denote equal byte contents in this view.
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 = Set::new(|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 = Set::new(|pa: usize| {
exists |va: usize| vaddr <= va < split_end && self.is_mapped(va, pa)
});
let right_pas = Set::new(|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).unwrap().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.