MemView

Struct MemView 

Source
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:

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

Source

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.

Source

pub open spec fn read(self, va: usize) -> MemContents<u8>

{
    let (pa, off) = self.addr_transl(va).unwrap();
    self.memory[pa].contents[off as int]
}

Specification read through virtual translation.

Equivalent to resolving via Self::addr_transl and reading from Self::memory.

Source

pub open spec fn write(self, va: usize, x: u8) -> Self

{
    let (pa, off) = self.addr_transl(va).unwrap();
    MemView {
        memory: self
            .memory
            .insert(
                pa,
                FrameContents {
                    contents: self
                        .memory[pa]
                        .contents
                        .update(off as int, raw_ptr::MemContents::Init(x)),
                    size: self.memory[pa].size,
                    range: self.memory[pa].range,
                },
            ),
        ..self
    }
}

Specification write through virtual translation.

Returns a new MemView with one byte updated, preserving all mappings.

Source

pub open spec fn eq_at(self, va1: usize, va2: usize) -> bool

{
    let (pa1, off1) = self.addr_transl(va1).unwrap();
    let (pa2, off2) = self.addr_transl(va2).unwrap();
    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.

Source

pub open spec fn is_mapped(self, va: usize, pa: usize) -> bool

{ self.addr_transl(va) is Some && self.addr_transl(va).unwrap().0 == pa }

Whether va is translated and mapped to frame base pa.

Source

pub open spec fn borrow_at_spec(&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.

Source

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.

Source

pub open spec fn split_spec(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:

  • left covers [vaddr, split_end),
  • right covers addresses >= split_end.
Source

pub proof fn borrow_at(tracked &self, vaddr: usize, len: usize) -> tracked r : &MemView

ensures
r == self.borrow_at_spec(vaddr, len),

Executable proof wrapper of Self::borrow_at_spec.

§Verified Properties
§Postconditions
  • r == self.borrow_at_spec(vaddr, len).
Source

pub proof fn split(tracked self, vaddr: usize, len: usize) -> tracked r : (Self, Self)

ensures
r == self.split_spec(vaddr, len),

Executable proof wrapper of Self::split_spec.

§Verified Properties
§Postconditions
  • r == self.split_spec(vaddr, len).
Source

pub proof fn lemma_split_preserves_transl( original: MemView, vaddr: usize, len: usize, left: MemView, right: MemView, )

requires
original.split_spec(vaddr, len) == (left, right),
ensures
right.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_spec preserves translation semantics on each side.

§Verified Properties
§Preconditions
  • original.split_spec(vaddr, len) == (left, right).
§Postconditions
  • right.memory.dom().subset_of(original.memory.dom()).
  • For any va in [vaddr, vaddr + len), original.addr_transl(va) == left.addr_transl(va).
  • For any va >= vaddr + len, original.addr_transl(va) == right.addr_transl(va).
Source

pub open spec fn join_spec(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.

Source

pub proof fn join(tracked &mut self, tracked other: Self)

requires
old(self).mappings.disjoint(other.mappings),
ensures
*self == old(self).join_spec(other),

Executable proof wrapper of Self::join_spec.

§Verified Properties
§Preconditions
  • old(self).mappings.disjoint(other.mappings).
§Postconditions
  • *self == old(self).join_spec(other).
Source

pub proof fn lemma_split_join_identity( this: MemView, lhs: MemView, rhs: MemView, vaddr: usize, len: usize, )

requires
this.split_spec(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)
},
ensures
this.mappings =~= lhs.join_spec(rhs).mappings,
this.memory =~= lhs.join_spec(rhs).memory,

Lemma: splitting and then joining reconstructs the original view.

§Verified Properties
§Preconditions
  • this.split_spec(vaddr, len) == (lhs, rhs).
  • Every mapping in this starts at or after vaddr.
  • Every physical frame tracked by this.memory is reachable from some virtual address at or after vaddr.
§Postconditions
  • lhs.join_spec(rhs) has the same mappings and memory contents as this.

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

§

impl<T, VERUS_SPEC__A> FromSpec<T> for VERUS_SPEC__A
where VERUS_SPEC__A: From<T>,

§

fn obeys_from_spec() -> bool

§

fn from_spec(v: T) -> VERUS_SPEC__A

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

§

impl<T, VERUS_SPEC__A> IntoSpec<T> for VERUS_SPEC__A
where VERUS_SPEC__A: Into<T>,

§

fn obeys_into_spec() -> bool

§

fn into_spec(self) -> T

§

impl<T, U> IntoSpecImpl<U> for T
where U: From<T>,

§

fn obeys_into_spec() -> bool

§

fn into_spec(self) -> U

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
§

impl<T, VERUS_SPEC__A> TryFromSpec<T> for VERUS_SPEC__A
where VERUS_SPEC__A: TryFrom<T>,

§

fn obeys_try_from_spec() -> bool

§

fn try_from_spec( v: T, ) -> Result<VERUS_SPEC__A, <VERUS_SPEC__A as TryFrom<T>>::Error>

Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<T, VERUS_SPEC__A> TryIntoSpec<T> for VERUS_SPEC__A
where VERUS_SPEC__A: TryInto<T>,

§

fn obeys_try_into_spec() -> bool

§

fn try_into_spec(self) -> Result<T, <VERUS_SPEC__A as TryInto<T>>::Error>

§

impl<T, U> TryIntoSpecImpl<U> for T
where U: TryFrom<T>,

§

fn obeys_try_into_spec() -> bool

§

fn try_into_spec(self) -> Result<U, <U as TryFrom<T>>::Error>