pub struct VmWriter<'a> {
pub id: Ghost<nat>,
pub cursor: VirtPtr,
pub end: VirtPtr,
pub phantom: PhantomData<&'a [u8]>,
}Expand description
VmWriter is a writer for writing data to a contiguous range of memory.
The memory range write by VmWriter can be in either kernel space or user space.
When the operating range is in kernel space, the memory within that range
is guaranteed to be valid, and the corresponding memory writes are infallible.
When the operating range is in user space, it is ensured that the page table of
the process creating the VmWriter is active for the duration of 'a,
and the corresponding memory writes are considered fallible.
When perform writing with a VmReader, if one of them represents typed memory,
it can ensure that the writing range in this writer and reading range in the
reader are not overlapped.
NOTE: The overlap mentioned above is at both the virtual address level
and physical address level. There is not guarantee for the operation results
of VmReader and VmWriter in overlapping untyped addresses, and it is
the user’s responsibility to handle this situation.
Fields§
§id: Ghost<nat>§cursor: VirtPtr§end: VirtPtr§phantom: PhantomData<&'a [u8]>Implementations§
Source§impl<'a> VmWriter<'a>
impl<'a> VmWriter<'a>
Sourcepub unsafe exec fn from_kernel_space(ptr: VirtPtr, len: usize) -> r : Self
pub unsafe exec fn from_kernel_space(ptr: VirtPtr, len: usize) -> r : Self
Ghost(id): Ghost<nat>,
Tracked(fallible): Tracked<bool>,
->
owner: Tracked<VmIoOwner<'a>>,requires!fallible,ptr.inv(),ptr.range@.start == ptr.vaddr,len == ptr.range@.end - ptr.range@.start,len == 0 || KERNEL_BASE_VADDR <= ptr.vaddr,len == 0 || ptr.vaddr + len <= KERNEL_END_VADDR,ensuresowner@.inv(),owner@.inv_with_writer(r),owner@.is_fallible == fallible,owner@.id == id,owner@.is_kernel,owner@.mem_view is None,r.cursor == ptr,r.end.vaddr == ptr.vaddr + len,r.cursor.range@ == ptr.range@,r.end.range@ == ptr.range@,Constructs a VmWriter from a pointer and a length, which represents
a memory range in kernel space.
§Verified Properties
§Preconditions
- The memory region represented by
ptrandlenmust be valid for writes oflenbytes during the entire lifetimea. This means that the underlying pages must remain alive, and the kernel must access the memory region using only the APIs provided in this module. - The range
ptr.vaddr..ptr.vaddr + lenmust represent a kernel space memory range.
§Postconditions
- An infallible
VmWriterwill be created with the rangeptr.vaddr..ptr.vaddr + len. - The created
VmWriterwill have a unique identifierid, and its cursor will be initialized toptr. - The created
VmWriterwill be associated with aVmIoOwnerthat has the sameid, the same memory range, and is marked as kernel space and infallible. - The memory view of the associated
VmIoOwnerwill beNone, indicating that it does not have any specific permissions yet.
§Safety
ptr must be valid for writes of len bytes during the entire lifetime a.
Sourcepub exec fn from_pod<T: Pod>(val: T) -> r : Result<VmWriter<'a>, Error>
pub exec fn from_pod<T: Pod>(val: T) -> r : Result<VmWriter<'a>, Error>
Ghost(id): Ghost<nat>,
->
owner: Tracked<Result<VmIoOwner<'a>>>,ensuresr is Ok <==> owner@ is Ok,r matches Ok(
r,
) ==> {
&&& owner@ matches Ok(
owner,
) ==> {
&&& r.inv()
&&& r.avail_spec() == core::mem::size_of::<T>()
&&& owner.inv()
&&& owner.inv_with_writer(r)
}
},Converts a PoD value into a VmWriter that writes to the memory occupied by the PoD value.
§Verified Properties
§Preconditions
None as the Rust’s type system guarantees that if T is valid, then we can always create a
valid VmWriter for it.
§Postconditions
Source§impl<'a> VmWriter<'a>
impl<'a> VmWriter<'a>
Sourcepub open spec fn avail_spec(&self) -> usize
pub open spec fn avail_spec(&self) -> usize
{ (self.end.vaddr - self.cursor.vaddr) as usize }Sourcepub unsafe exec fn from_user_space(ptr: VirtPtr) -> r : Self
pub unsafe exec fn from_user_space(ptr: VirtPtr) -> r : Self
Ghost(id): Ghost<nat>,
->
owner: Tracked<VmIoOwner<'a>>,requiresptr.inv(),ensuresr.inv(),owner@.id == id,owner@.inv(),owner@.inv_with_writer(r),owner@.range == ptr.range@,owner@.mem_view is None,!owner@.is_kernel,!owner@.is_fallible,Constructs a VmWriter from a pointer, which represents a memory range in USER space.
⚠️ WARNING: Currently not implemented yet.
§Verified Properties
§Preconditions
ptrmust satisfyVirtPtr::inv.
§Postconditions
- The returned
VmWritersatisfies its invariant. - The returned writer is associated with a
VmIoOwnerthat satisfies bothVmIoOwner::invandVmIoOwner::inv_with_writer. - The owner has the same range as
ptr, has no memory view yet, and is marked as user-space and infallible.
Sourcepub exec fn avail(&self) -> r : usize
pub exec fn avail(&self) -> r : usize
self.inv(),ensuresr == self.avail_spec(),Returns the number of available bytes that can be written.
This has the same implementation as VmReader::remain but semantically
they are different.
§Verified Properties
§Preconditions
selfmust satisfy its invariant.
§Postconditions
- The returned value equals
Self::avail_spec.
Sourcepub exec fn advance(&mut self, len: usize)
pub exec fn advance(&mut self, len: usize)
old(self).inv(),len <= old(self).avail_spec(),ensuresself.inv(),self.avail_spec() == old(self).avail_spec() - len,self.cursor.vaddr == old(self).cursor.vaddr + len,self.cursor.range@ == old(self).cursor.range@,self.id == old(self).id,self.end == old(self).end,Advances the cursor by len bytes.
§Verified Properties
§Preconditions
selfmust satisfy its invariant.lenmust not exceed the remaining writable bytes.
§Postconditions
selfstill satisfies its invariant.- The cursor advances by
len. - The remaining writable bytes decrease by
len. - The writer identity and end cursor are preserved.
Sourcepub exec fn write(&mut self, reader: &mut VmReader<'_>) -> r : usize
pub exec fn write(&mut self, reader: &mut VmReader<'_>) -> r : usize
Tracked(owner_w): Tracked<&mut VmIoOwner<'_>>,
Tracked(owner_r): Tracked<&mut VmIoOwner<'_>>,requiresold(self).inv(),old(reader).inv(),old(owner_w).inv(),old(owner_r).inv(),old(owner_w).inv_with_writer(*old(self)),old(owner_r).inv_with_reader(*old(reader)),old(owner_w).mem_view matches Some(VmIoMemView::WriteView(_)),old(self).cursor.range@.start >= old(reader).cursor.range@.end
|| old(reader).cursor.range@.start >= old(self).cursor.range@.end,old(owner_r)
.mem_view matches Some(
VmIoMemView::ReadView(mem_src),
) && forall |i: usize| {
old(reader).cursor.vaddr <= i < old(reader).cursor.vaddr + old(reader).remain_spec()
==> {
&&& mem_src.addr_transl(i) is Some
&&& mem_src.memory.contains_key(mem_src.addr_transl(i).unwrap().0)
&&& mem_src
.memory[mem_src.addr_transl(i).unwrap().0]
.contents[mem_src.addr_transl(i).unwrap().1 as int] is Init
}
},ensuresself.inv(),reader.inv(),owner_w.inv(),owner_r.inv(),owner_w.inv_with_writer(*self),owner_r.inv_with_reader(*reader),r == vstd::math::min(old(self).avail_spec() as int, old(reader).remain_spec() as int),self.avail_spec() == old(self).avail_spec() - r as usize,self.cursor.vaddr == old(self).cursor.vaddr + r as usize,reader.remain_spec() == old(reader).remain_spec() - r as usize,reader.cursor.vaddr == old(reader).cursor.vaddr + r as usize,Writes data into self by reading from the provided reader.
This function treats self as the destination buffer. It pulls data from
the source reader and writes it into the current instance.
§Arguments
reader- The sourceVmReaderto read data from.
§Returns
Returns the number of bytes written to self (which is equal to the number of bytes read from reader).
§Verified Properties
§Preconditions
- The writer, reader, and both associated owners must satisfy their invariants.
- The owners must match the given writer and reader.
- The writer owner must carry a write memory view.
- The source and destination ranges must not overlap.
- The reader owner must provide initialized readable memory for the readable range.
§Postconditions
- The writer, reader, and both owners still satisfy their invariants.
- The owners still match the updated writer and reader.
- The returned byte count equals the minimum of writable bytes and readable bytes.
- Both cursors advance by exactly the returned byte count.
Sourcepub exec fn write_val<T: Pod>(&mut self, val: &mut T) -> r : Result<(), Error>
pub exec fn write_val<T: Pod>(&mut self, val: &mut T) -> r : Result<(), Error>
Ghost(id): Ghost<nat>,
Tracked(owner_w): Tracked<&mut VmIoOwner<'_>>,
Tracked(memview_r): Tracked<&MemView>,requiresold(self).inv(),old(owner_w).inv(),old(owner_w).inv_with_writer(*old(self)),old(owner_w).mem_view is Some,ensuresself.inv(),owner_w.inv(),owner_w.inv_with_writer(*self),match r {
Ok(_) => (
&&& self.avail_spec() == old(self).avail_spec() - core::mem::size_of::<T>()
&&& self.cursor.vaddr == old(self).cursor.vaddr + core::mem::size_of::<T>()
),
Err(_) => *old(self) == *self,
},Writes a value of Pod type.
If the length of the Pod type exceeds self.avail(),
this method will return Err.
§Verified Properties
§Preconditions
- The writer and its owner must satisfy their invariants.
- The owner must match this writer and carry a memory view.
§Postconditions
- The writer and owner still satisfy their invariants.
- The owner parameters tracked by
VmIoOwner::params_eqare preserved. - On success, the cursor advances by
size_of::<T>(). - On error, the writer state is unchanged.
Sourcepub exec fn write_once<T: PodOnce>(&mut self, new_val: &mut T) -> r : Result<(), Error>
pub exec fn write_once<T: PodOnce>(&mut self, new_val: &mut T) -> r : Result<(), Error>
Tracked(owner_w): Tracked<&mut VmIoOwner<'_>>,requiresold(self).inv(),old(owner_w).mem_view is Some,old(owner_w).inv(),old(owner_w).inv_with_writer(*old(self)),ensuresself.inv(),owner_w.inv(),owner_w.inv_with_writer(*self),match r {
Ok(_) => (
&&& self.avail_spec() == old(self).avail_spec() - core::mem::size_of::<T>()
&&& self.cursor.vaddr == old(self).cursor.vaddr + core::mem::size_of::<T>()
),
Err(_) => *old(self) == *self,
},Writes a value of the PodOnce type using one non-tearing memory store.
If the length of the PodOnce type exceeds self.avail(), this method will return Err.
§Verified Properties
§Preconditions
- The writer and its owner must satisfy their invariants.
- The owner must match this writer and carry a memory view.
§Postconditions
- The writer and owner still satisfy their invariants.
- On success, the cursor advances by
size_of::<T>(). - On error, the writer state is unchanged.
Trait Implementations§
Source§impl<'a> TryFromSpecImpl<&'a [u8]> for VmWriter<'a>
impl<'a> TryFromSpecImpl<&'a [u8]> for VmWriter<'a>
Source§open spec fn obeys_try_from_spec() -> bool
open spec fn obeys_try_from_spec() -> bool
{ true }Source§open spec fn try_from_spec(slice: &'a [u8]) -> Result<Self, Error>
open spec fn try_from_spec(slice: &'a [u8]) -> Result<Self, Error>
{
let addr = slice.as_ptr() as usize;
let len = slice.len();
if len != 0
&& (addr < KERNEL_BASE_VADDR || len >= KERNEL_END_VADDR
|| addr > KERNEL_END_VADDR - slice.len()) || addr == 0
{
Err(Error::IoError)
} else {
Ok(Self {
id: Ghost(arbitrary()),
cursor: VirtPtr {
vaddr: addr,
range: Ghost(addr..(addr + len) as usize),
},
end: VirtPtr {
vaddr: (addr + len) as usize,
range: Ghost(addr..(addr + len) as usize),
},
phantom: PhantomData,
})
}
}