VmSpace

Struct VmSpace 

Source
pub struct VmSpace<'a> {
    pub pt: PageTable<UserPtConfig>,
    pub shared_reader: bool,
    pub readers: Vec<&'a VmReader<'a>>,
    pub writers: Vec<&'a VmWriter<'a>>,
    pub marker: PhantomData<&'a ()>,
}
Expand description

A virtual address space for user-mode tasks, enabling safe manipulation of user-space memory.

The VmSpace type provides memory isolation guarantees between user-space and kernel-space. For example, given an arbitrary user-space pointer, one can read and write the memory location referred to by the user-space pointer without the risk of breaking the memory safety of the kernel space.

§Task Association Semantics

As far as OSTD is concerned, a VmSpace is not necessarily associated with a task. Once a VmSpace is activated (see [VmSpace::activate]), it remains activated until another VmSpace is activated possibly by another task running on the same CPU.

This means that it’s up to the kernel to ensure that a task’s VmSpace is always activated while the task is running. This can be done by using the injected post schedule handler (see inject_post_schedule_handler) to always activate the correct VmSpace after each context switch.

If the kernel otherwise decides not to ensure that the running task’s VmSpace is always activated, the kernel must deal with race conditions when calling methods that require the VmSpace to be activated, e.g., UserMode::execute, [VmSpace::reader], [VmSpace::writer]. Otherwise, the behavior is unspecified, though it’s guaranteed not to compromise the kernel’s memory safety.

§Memory Backing

A newly-created VmSpace is not backed by any physical memory pages. To provide memory pages for a VmSpace, one can allocate and map physical memory (UFrames) to the VmSpace using the cursor.

A VmSpace can also attach a page fault handler, which will be invoked to handle page faults generated from user space.

Fields§

§pt: PageTable<UserPtConfig>§shared_reader: bool

Whether we allow shared reading.

§readers: Vec<&'a VmReader<'a>>

All readers belonging to this VM space.

§writers: Vec<&'a VmWriter<'a>>

All writers belonging to this VM space.

§marker: PhantomData<&'a ()>

Implementations§

Source§

impl<'a> VmSpace<'a>

Source

pub uninterp fn new_spec() -> Self

Source

pub open spec fn reader_requires( &self, vm_owner: VmSpaceOwner<'a>, vaddr: Vaddr, len: usize, ) -> bool

{
    &&& self.inv()
    &&& vm_owner.inv_with(*self)
    &&& vm_owner.inv()
    &&& vm_owner.active
    &&& vm_owner.can_create_reader(vaddr, len)
    &&& vaddr != 0 && len > 0 && vaddr + len <= MAX_USERSPACE_VADDR_SPEC()
    &&& current_page_table_paddr_spec() == self.pt.root_paddr_spec()

}
Source

pub open spec fn writer_requires( &self, vm_owner: VmSpaceOwner<'a>, vaddr: Vaddr, len: usize, ) -> bool

{
    &&& self.inv()
    &&& vm_owner.inv_with(*self)
    &&& vm_owner.inv()
    &&& vm_owner.active
    &&& vm_owner.can_create_writer(vaddr, len)
    &&& vaddr != 0 && len > 0 && vaddr + len <= MAX_USERSPACE_VADDR_SPEC()
    &&& current_page_table_paddr_spec() == self.pt.root_paddr_spec()

}
Source

pub open spec fn reader_ensures( &self, vm_owner_old: VmSpaceOwner<'_>, vm_owner_new: VmSpaceOwner<'_>, vaddr: Vaddr, len: usize, r: Result<VmReader<'_>, Error>, r_owner: Option<VmIoOwner<'_>>, ) -> bool

{
    &&& vm_owner_new.inv()
    &&& vm_owner_new.readers == vm_owner_old.readers
    &&& vm_owner_new.writers == vm_owner_old.writers
    &&& r matches Ok(
        reader,
    ) && r_owner matches Some(
        owner,
    ) ==> {
        &&& reader.inv()
        &&& owner.inv_with_reader(reader)
        &&& owner.mem_view is None

    }

}
Source

pub open spec fn writer_ensures( &self, vm_owner_old: VmSpaceOwner<'a>, vm_owner_new: VmSpaceOwner<'a>, vaddr: Vaddr, len: usize, r: Result<VmWriter<'a>, Error>, r_owner: Option<VmIoOwner<'a>>, ) -> bool

{
    &&& vm_owner_new.inv()
    &&& vm_owner_new.readers == vm_owner_old.readers
    &&& vm_owner_new.writers == vm_owner_old.writers
    &&& r matches Ok(
        writer,
    ) && r_owner matches Some(
        owner,
    ) ==> {
        &&& writer.inv()
        &&& owner.inv_with_writer(writer)
        &&& owner.mem_view is None

    }

}
Source

pub exec fn new() -> r : Self

ensures
r == Self::new_spec(),
r.inv(),

Creates a new VM address space.

Source

pub exec fn cursor<G: InAtomicMode>( &'a self, guard: &'a G, va: &Range<Vaddr>, ) -> Result<Cursor<'a, G>, Error>

Gets an immutable cursor in the virtual address range.

The cursor behaves like a lock guard, exclusively owning a sub-tree of the page table, preventing others from creating a cursor in it. So be sure to drop the cursor as soon as possible.

The creation of the cursor may block if another cursor having an overlapping range is alive.

Source

pub exec fn cursor_mut<G: InAtomicMode>( &'a self, guard: &'a G, va: &Range<Vaddr>, ) -> Result<CursorMut<'a, G>, Error>

Gets an mutable cursor in the virtual address range.

The same as Self::cursor, the cursor behaves like a lock guard, exclusively owning a sub-tree of the page table, preventing others from creating a cursor in it. So be sure to drop the cursor as soon as possible.

The creation of the cursor may block if another cursor having an overlapping range is alive. The modification to the mapping by the cursor may also block or be overridden the mapping of another cursor.

Source

pub exec fn activate_reader(&mut self, reader: &'a VmReader<'a>)

with
Tracked(vm_space_owner): Tracked <& 'a mut VmSpaceOwner <'a>>,
Tracked(owner_r): Tracked <& 'a mut VmIoOwner <'a>>,
requires
old(self).inv(),
old(vm_space_owner)
    .mem_view matches Some(
    mv,
) && forall |va: usize| (
    old(owner_r).range@.start <= va < old(owner_r).range@.end
        ==> mv.addr_transl(va) is Some
),
old(vm_space_owner).inv_with(*old(self)),
old(vm_space_owner).inv(),
old(vm_space_owner).active,
old(owner_r).inv_with_reader(*reader),
old(owner_r).mem_view is None,
reader.inv(),
ensures
self.inv(),
self.shared_reader == old(self).shared_reader,
self.readers@ == old(self).readers@.push(reader),
owner_r.inv_with_reader(*reader),
owner_r.mem_view
    == Some(
        VmIoMemView::ReadView(
            &old(vm_space_owner).mem_view@
                .unwrap()
                .borrow_at_spec(
                    old(owner_r).range@.start,
                    (old(owner_r).range@.end - old(owner_r).range@.start) as usize,
                ),
        ),
    ),

Activates the given reader to read data from the user space of the current task.

Source

pub exec fn activate_writer(&mut self, writer: &'a VmWriter<'a>)

with
Tracked(vm_space_owner): Tracked <& 'a mut VmSpaceOwner <'a>>,
Tracked(owner_w): Tracked <& 'a mut VmIoOwner <'a>>,
requires
old(self).inv(),
old(vm_space_owner)
    .mem_view matches Some(
    mv,
) && forall |va: usize| (
    old(owner_w).range@.start <= va < old(owner_w).range@.end
        ==> mv.addr_transl(va) is Some
),
old(vm_space_owner).inv_with(*old(self)),
old(vm_space_owner).inv(),
old(vm_space_owner).active,
old(owner_w).inv_with_writer(*writer),
old(owner_w).mem_view is None,
writer.inv(),
ensures
self.inv(),
self.shared_reader == old(self).shared_reader,
self.writers@ == old(self).writers@.push(writer),
owner_w.inv_with_writer(*writer),
owner_w.mem_view
    == Some(
        VmIoMemView::WriteView(
            old(vm_space_owner).mem_view@
                .unwrap()
                .split_spec(
                    old(owner_w).range@.start,
                    (old(owner_w).range@.end - old(owner_w).range@.start) as usize,
                )
                .0,
        ),
    ),

Activates the given writer to write data to the user space of the current task.

Source

pub exec fn create_reader( &self, vaddr: Vaddr, len: usize, ) -> r : Result<VmReader<'a>, Error>

with
Tracked(owner): Tracked <& 'a mut VmSpaceOwner <'a>>,
->
vm_reader_owner: Tracked <Option <VmIoOwner <'a>>>,
requires
self.reader_requires(*old(owner), vaddr, len),
ensures
self.reader_ensures(*old(owner), *owner, vaddr, len, r, vm_reader_owner@),

Creates a reader to read data from the user space of the current task.

Returns Err if this VmSpace is not belonged to the user space of the current task or the vaddr and len do not represent a user space memory range.

Users must ensure that no other page table is activated in the current task during the lifetime of the created VmReader. This guarantees that the VmReader can operate correctly.

Source

pub exec fn create_writer( self, vaddr: Vaddr, len: usize, ) -> r : Result<VmWriter<'a>, Error>

with
Tracked(owner): Tracked <& mut VmSpaceOwner <'a>>,
->
new_owner: Tracked <Option <VmIoOwner <'a>>>,
requires
self.writer_requires(*old(owner), vaddr, len),
ensures
self.writer_ensures(*old(owner), *owner, vaddr, len, r, new_owner@),

Returns Err if this VmSpace is not belonged to the user space of the current task or the vaddr and len do not represent a user space memory range.

Users must ensure that no other page table is activated in the current task during the lifetime of the created VmWriter. This guarantees that the VmWriter can operate correctly.

Trait Implementations§

Source§

impl Inv for VmSpace<'_>

Source§

open spec fn inv(self) -> bool

{
    &&& forall |i: int| 0 <= i < self.readers@.len() as int ==> self.readers@[i].inv()
    &&& forall |i: int| 0 <= i < self.writers@.len() as int ==> self.writers@[i].inv()

}

Auto Trait Implementations§

§

impl<'a> Freeze for VmSpace<'a>

§

impl<'a> !RefUnwindSafe for VmSpace<'a>

§

impl<'a> Send for VmSpace<'a>

§

impl<'a> Sync for VmSpace<'a>

§

impl<'a> Unpin for VmSpace<'a>

§

impl<'a> UnwindSafe for VmSpace<'a>

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>