pub struct VmReader<'a> {
pub id: Ghost<nat>,
pub cursor: VirtPtr,
pub end: VirtPtr,
pub phantom: PhantomData<&'a [u8]>,
}Expand description
VmReader is a reader for reading data from a contiguous range of memory.
The memory range read by VmReader 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 reads are infallible.
When the operating range is in user space, it is ensured that the page table of
the process creating the VmReader is active for the duration of 'a,
and the corresponding memory reads are considered fallible.
When perform reading with a VmWriter, if one of them represents typed memory,
it can ensure that the reading range in this reader and writing range in the
writer 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> VmReader<'a>
impl<'a> VmReader<'a>
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_reader(r),owner@.range == ptr.range@,owner@.mem_view is None,!owner@.is_kernel,!owner@.is_fallible,Constructs a VmReader from a pointer and a length, which represents
a memory range in USER space.
⚠️ WARNING: Currently not implemented yet.
§Verified Properties
§Preconditions
ptrmust satisfyVirtPtr::inv.
§Postconditions
- The returned
VmReadersatisfies its invariant. - The returned reader is associated with a
VmIoOwnerthat satisfies bothVmIoOwner::invandVmIoOwner::inv_with_reader. - The owner has the same range as
ptr, has no memory view yet, and is marked as user-space and infallible.
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>,
->
owner: Tracked<VmIoOwner<'a>>,requiresptr.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_reader(r),r.cursor.vaddr == ptr.vaddr,r.end.vaddr == ptr.vaddr + len,r.cursor == ptr,r.end.range@ == ptr.range@,owner@.is_kernel,owner@.id == id,Constructs a VmReader 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 reads 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
VmReaderwill be created with the rangeptr.vaddr..ptr.vaddr + len. - The created
VmReaderwill have a unique identifierid, and its cursor will be initialized toptr. - The created
VmReaderwill be associated with aVmIoOwnerthat has the sameid, the same memory range, and is marked as kernel space and infallible.
§Safety
ptr must be valid for reads of len bytes during the entire lifetime a.
Sourcepub exec fn from_pod<T: Pod>(val: &mut T) -> r : Result<VmReader<'a>, Error>
pub exec fn from_pod<T: Pod>(val: &mut T) -> r : Result<VmReader<'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.remain_spec() == core::mem::size_of::<T>()
&&& owner.inv()
&&& owner.inv_with_reader(r)
}
},Converts a PoD value into a VmReader that reads from the memory occupied by the PoD value.
§Verified Properties
§Preconditions
None as the Rust’s type system guarantees that if &mut T is valid, then we can always
create a valid VmReader for it.
§Postconditions
Source§impl VmReader<'_>
impl VmReader<'_>
Sourcepub open spec fn remain_spec(&self) -> usize
pub open spec fn remain_spec(&self) -> usize
{ (self.end.vaddr - self.cursor.vaddr) as usize }Sourcepub exec fn remain(&self) -> r : usize
pub exec fn remain(&self) -> r : usize
self.inv(),ensuresr == self.remain_spec(),Returns the number of remaining bytes that can be read.
§Verified Properties
§Preconditions
selfmust satisfy its invariant.
§Postconditions
- The returned value equals
Self::remain_spec.
Sourcepub exec fn advance(&mut self, len: usize)
pub exec fn advance(&mut self, len: usize)
old(self).inv(),len <= old(self).remain_spec(),ensuresself.inv(),self.cursor.vaddr == old(self).cursor.vaddr + len,self.remain_spec() == old(self).remain_spec() - len,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 readable bytes.
§Postconditions
selfstill satisfies its invariant.- The cursor advances by
len. - The remaining readable bytes decrease by
len. - The reader identity and end cursor are preserved.
Sourcepub exec fn read(&mut self, writer: &mut VmWriter<'_>) -> r : usize
pub exec fn read(&mut self, writer: &mut VmWriter<'_>) -> r : usize
Tracked(owner_r): Tracked<&mut VmIoOwner<'_>>,
Tracked(owner_w): Tracked<&mut VmIoOwner<'_>>,requiresold(self).inv(),old(writer).inv(),old(owner_r).inv(),old(owner_w).inv(),old(owner_r).inv_with_reader(*old(self)),old(owner_w).inv_with_writer(*old(writer)),old(owner_w).mem_view matches Some(VmIoMemView::WriteView(_)),old(writer).cursor.range@.start >= old(self).cursor.range@.end
|| old(self).cursor.range@.start >= old(writer).cursor.range@.end,old(owner_r)
.mem_view matches Some(
VmIoMemView::ReadView(mem_src),
) && forall |i: usize| {
old(self).cursor.vaddr <= i < old(self).cursor.vaddr + old(self).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(),writer.inv(),owner_r.inv(),owner_w.inv(),owner_r.inv_with_reader(*self),owner_w.inv_with_writer(*writer),r == vstd::math::min(old(self).remain_spec() as int, old(writer).avail_spec() as int),self.remain_spec() == old(self).remain_spec() - r as usize,self.cursor.vaddr == old(self).cursor.vaddr + r as usize,writer.avail_spec() == old(writer).avail_spec() - r as usize,writer.cursor.vaddr == old(writer).cursor.vaddr + r as usize,Reads data from self and writes it into the provided writer.
This function acts as the source side of a transfer. It copies data from
the current instance (self) into the destination writer, up to the limit
of available data in self or available space in writer (whichever is smaller).
§Logic
- Calculates the copy length:
min(self.remaining_data, writer.available_space). - Copies bytes from
self’s internal buffer towriter’s buffer. - Advances the cursors of both
selfandwriter.
§Arguments
writer- The destinationVmWriterwhere the data will be copied to.
§Returns
The number of bytes actually transferred.
§Verified Properties
§Preconditions
- The reader, writer, and both associated owners must satisfy their invariants.
- The owners must match the given reader and writer.
- 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 reader, writer, and both owners still satisfy their invariants.
- The owners still match the updated reader and writer.
- The returned byte count equals the minimum of readable bytes and writable bytes.
- Both cursors advance by exactly the returned byte count.
Sourcepub exec fn read_val<T: Pod>(&mut self) -> r : Result<T, Error>
pub exec fn read_val<T: Pod>(&mut self) -> r : Result<T, Error>
Tracked(owner): Tracked<&mut VmIoOwner<'_>>,requiresold(self).inv(),old(owner).inv(),old(owner).inv_with_reader(*old(self)),old(owner).mem_view matches Some(VmIoMemView::ReadView(_)),old(owner)
.mem_view matches Some(
VmIoMemView::ReadView(mem_src),
) ==> {
forall |i: usize| {
old(self).cursor.vaddr <= i < old(self).cursor.vaddr + old(self).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(),owner.inv(),owner.inv_with_reader(*self),match r {
Ok(_) => (
&&& self.remain_spec() == old(self).remain_spec() - core::mem::size_of::<T>()
&&& self.cursor.vaddr == old(self).cursor.vaddr + core::mem::size_of::<T>()
),
Err(_) => *old(self) == *self,
},Reads a value of Pod type.
If the length of the Pod type exceeds self.remain(),
this method will return Err.
§Verified Properties
§Preconditions
- The reader and its owner must satisfy their invariants.
- The owner must match this reader and carry a read memory view.
- The readable range must translate to initialized bytes in the read view.
§Postconditions
- The reader and owner still satisfy their invariants.
- On success, the cursor advances by
size_of::<T>(). - On error, the reader state is unchanged.
Sourcepub exec fn read_val_once<T: PodOnce>(&mut self) -> r : Result<T, Error>
pub exec fn read_val_once<T: PodOnce>(&mut self) -> r : Result<T, Error>
Tracked(owner): Tracked<&mut VmIoOwner<'_>>,requiresold(self).inv(),old(owner).inv(),old(owner).inv_with_reader(*old(self)),old(owner).mem_view matches Some(VmIoMemView::ReadView(_)),old(self).cursor.vaddr % core::mem::align_of::<T>() == 0,old(owner)
.mem_view matches Some(
VmIoMemView::ReadView(mem_src),
) ==> {
forall |i: usize| {
old(self).cursor.vaddr <= i < old(self).cursor.vaddr + core::mem::size_of::<T>()
==> {
&&& 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(),owner.inv(),owner.inv_with_reader(*self),match r {
Ok(_) => (
&&& self.remain_spec() == old(self).remain_spec() - core::mem::size_of::<T>()
&&& self.cursor.vaddr == old(self).cursor.vaddr + core::mem::size_of::<T>()
),
Err(_) => *old(self) == *self,
},Reads a value of the PodOnce type using one non-tearing memory load.
If the length of the PodOnce type exceeds self.remain(), this method will return Err.
This method will not compile if the Pod type is too large for the current architecture
and the operation must be tear into multiple memory loads.
§Panics
This method will panic if the current position of the reader does not meet the alignment
requirements of type T.
§Verified Properties
§Preconditions
- The reader and its owner must satisfy their invariants.
- The owner must match this reader and carry a read memory view.
- The readable range must translate to initialized bytes in the read view.
- The current cursor must satisfy the alignment requirements of
T.
§Postconditions
- The reader and owner still satisfy their invariants.
- On success, the cursor advances by
size_of::<T>(). - On error, the reader state is unchanged.
Trait Implementations§
Source§impl Clone for VmReader<'_>
impl Clone for VmReader<'_>
Source§impl<'a> TryFromSpecImpl<&'a [u8]> for VmReader<'a>
impl<'a> TryFromSpecImpl<&'a [u8]> for VmReader<'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,
})
}
}