ostd/mm/io.rs
1// SPDX-License-Identifier: MPL-2.0
2//! Abstractions for reading and writing virtual memory (VM) objects.
3//!
4//! # Safety
5//!
6//! The core virtual memory (VM) access APIs provided by this module are [`VmReader`] and
7//! [`VmWriter`], which allow for writing to or reading from a region of memory _safely_.
8//! `VmReader` and `VmWriter` objects can be constructed from memory regions of either typed memory
9//! (e.g., `&[u8]`) or untyped memory (e.g, [`UFrame`]). Behind the scene, `VmReader` and `VmWriter`
10//! must be constructed via their [`from_user_space`] and [`from_kernel_space`] methods, whose
11//! safety depends on whether the given memory regions are _valid_ or not.
12//!
13//! [`UFrame`]: crate::mm::UFrame
14//! [`from_user_space`]: `VmReader::from_user_space`
15//! [`from_kernel_space`]: `VmReader::from_kernel_space`
16//!
17//! Here is a list of conditions for memory regions to be considered valid:
18//!
19//! - The memory region as a whole must be either typed or untyped memory, not both typed and
20//! untyped.
21//!
22//! - If the memory region is typed, we require that:
23//! - the [validity requirements] from the official Rust documentation must be met, and
24//! - the type of the memory region (which must exist since the memory is typed) must be
25//! plain-old-data, so that the writer can fill it with arbitrary data safely.
26//!
27//! [validity requirements]: core::ptr#safety
28//!
29//! - If the memory region is untyped, we require that:
30//! - the underlying pages must remain alive while the validity requirements are in effect, and
31//! - the kernel must access the memory region using only the APIs provided in this module, but
32//! external accesses from hardware devices or user programs do not count.
33//!
34//! We have the last requirement for untyped memory to be valid because the safety interaction with
35//! other ways to access the memory region (e.g., atomic/volatile memory loads/stores) is not
36//! currently specified. Tis may be relaxed in the future, if appropriate and necessary.
37//!
38//! Note that data races on untyped memory are explicitly allowed (since pages can be mapped to
39//! user space, making it impossible to avoid data races). However, they may produce erroneous
40//! results, such as unexpected bytes being copied, but do not cause soundness problems.
41use crate::arch::mm::{__memcpy_fallible, __memset_fallible};
42
43use core::marker::PhantomData;
44use core::ops::Range;
45use vstd::arithmetic::power2::is_pow2;
46use vstd::prelude::*;
47use vstd::simple_pptr::*;
48use vstd_extra::assert;
49use vstd_extra::ownership::Inv;
50use vstd_extra::panic::may_panic;
51
52use crate::error::*;
53pub use crate::specs::mm::io::{
54 VmIoMemView, VmIoOwner, axiom_kernel_mem_view, axiom_slice_in_kernel,
55};
56use crate::specs::mm::virt_mem::{MemView, VirtPtr};
57use crate::{
58 Pod,
59 mm::{
60 MAX_USERSPACE_VADDR,
61 kspace::{KERNEL_BASE_VADDR, KERNEL_END_VADDR},
62 },
63};
64
65verus! {
66
67proof fn lemma_add_aligned_stride(start: usize, i: usize, len: usize, align: usize)
68 requires
69 align > 0,
70 start % align == 0,
71 len % align == 0,
72 ensures
73 (start + i * len) % align as int == 0,
74{
75 let a = align as int;
76 let q_start = start as int / a;
77 let q_len = len as int / a;
78 vstd::arithmetic::div_mod::lemma_fundamental_div_mod(start as int, a);
79 vstd::arithmetic::div_mod::lemma_fundamental_div_mod(len as int, a);
80 assert(start as int % a == 0);
81 assert(len as int % a == 0);
82 assert(start == q_start * a);
83 assert(len == q_len * a);
84 assert((q_start + i * q_len) * a == q_start * a + (i * q_len) * a) by (nonlinear_arith);
85 assert((i * q_len) * a == i * (q_len * a)) by (nonlinear_arith);
86 assert(i * (q_len * a) == i * len) by (nonlinear_arith)
87 requires
88 len == q_len * a,
89 ;
90 assert(q_start * a + i * len == start + i * len) by (nonlinear_arith)
91 requires
92 start == q_start * a,
93 ;
94 assert(start + i * len == (q_start + i * q_len) * a);
95 vstd::arithmetic::div_mod::lemma_mod_multiples_basic(q_start + i as int * q_len, a);
96}
97
98/// Verus spec stub for [`<*mut T>::is_aligned`]: returns whether the pointer's address is a
99/// multiple of `align_of::<T>()`.
100pub assume_specification<T>[ <*mut T>::is_aligned ](_0: *mut T) -> (res: bool)
101 ensures
102 res <==> (_0 as usize) % core::mem::align_of::<T>() == 0,
103;
104
105/// Copies `len` bytes from `src` to `dst`, stopping early on page fault.
106/// Returns the number of bytes successfully copied (which is at most `len`).
107///
108/// The return value bound is the only thing the verifier promises; the actual
109/// memory state after the call is trusted (the underlying arch primitive is
110/// `extern "C"`).
111///
112/// # Safety
113/// - `src` must be valid for reads of `len` bytes.
114/// - `dst` must either be valid for writes of `len` bytes or be in user space.
115#[verifier::external_body]
116#[verus_spec(r =>
117 ensures
118 r <= len,
119)]
120unsafe fn memcpy_fallible(dst: VirtPtr, src: VirtPtr, len: usize) -> usize {
121 // SAFETY: The safety is upheld by the caller.
122 let failed_bytes = unsafe { __memcpy_fallible(dst.vaddr as *mut u8, src.vaddr as *const u8, len)
123 };
124 len - failed_bytes
125}
126
127/// Fills `len` bytes of memory at `dst` with the specified `value`, stopping
128/// early on page fault. Returns the number of bytes successfully set (at most `len`).
129///
130/// The return value bound is the only thing the verifier promises; the actual
131/// memory state after the call is trusted (the underlying arch primitive is
132/// `extern "C"`).
133///
134/// # Safety
135/// - `dst` must either be valid for writes of `len` bytes or be in user space.
136#[verifier::external_body]
137#[verus_spec(r =>
138 ensures
139 r <= len,
140)]
141unsafe fn memset_fallible(dst: VirtPtr, value: u8, len: usize) -> usize {
142 // SAFETY: The safety is upheld by the caller.
143 let failed_bytes = unsafe { __memset_fallible(dst.vaddr as *mut u8, value, len) };
144 len - failed_bytes
145}
146
147/// Marker type indicating that VM I/O operations may fail (e.g., user-space access).
148pub struct Fallible {}
149
150/// Marker type indicating that VM I/O operations cannot fail (e.g., kernel-space access).
151pub struct Infallible {}
152
153/// Copies `len` bytes from `src` to `dst`.
154///
155/// This is the escape hatch into the abstract [`VirtPtr`] memory model: it is
156/// the only place in the executable code that performs a multi-byte copy, and
157/// it discharges the obligation by delegating to [`VirtPtr::copy_nonoverlapping`].
158///
159/// # Safety
160///
161/// - `src` must be [valid] for reads of `len` bytes.
162/// - `dst` must be [valid] for writes of `len` bytes.
163///
164/// [valid]: crate::mm::io#safety
165#[inline]
166#[verus_spec(
167 with
168 Tracked(mem_src): Tracked<&MemView>,
169 Tracked(mem_dst): Tracked<&mut MemView>,
170 requires
171 src.inv(),
172 dst.inv(),
173 src.vaddr + len <= src.range@.end,
174 forall|i: usize|
175 #![trigger mem_src.addr_transl(i)]
176 src.vaddr <= i < src.vaddr + len ==> {
177 &&& mem_src.addr_transl(i) is Some
178 &&& mem_src.memory.contains_key((mem_src.addr_transl(i)->0).0)
179 &&& mem_src.memory[(mem_src.addr_transl(i)->0).0].contents[(mem_src.addr_transl(i)->0).1 as int] is Init
180 },
181 dst.vaddr + len <= dst.range@.end,
182 forall|i: usize|
183 dst.vaddr <= i < dst.vaddr + len ==> {
184 &&& old(mem_dst).addr_transl(i) is Some
185 },
186 ensures
187 *final(mem_dst) == VirtPtr::memcpy_spec(src, dst, *mem_src, *old(mem_dst), len),
188 final(mem_dst).mappings == old(mem_dst).mappings,
189 old(mem_dst).memory.dom().subset_of(final(mem_dst).memory.dom()),
190 forall|i: usize|
191 #![trigger final(mem_dst).addr_transl(i)]
192 dst.vaddr <= i < dst.vaddr + len ==> {
193 &&& final(mem_dst).addr_transl(i) is Some
194 },
195)]
196unsafe fn memcpy(dst: VirtPtr, src: VirtPtr, len: usize) {
197 /*
198 // Original memcpy using volatile_copy_memory (replaced during Verus migration):
199 unsafe { core::intrinsics::volatile_copy_memory(dst, src, len) };
200 */
201 VirtPtr::copy_nonoverlapping(&src, &dst, Tracked(mem_src), Tracked(mem_dst), len);
202}
203
204/// [`VmReader`] is a reader for reading data from a contiguous range of memory.
205///
206/// The memory range read by [`VmReader`] can be in either kernel space or user space.
207/// When the operating range is in kernel space, the memory within that range
208/// is guaranteed to be valid, and the corresponding memory reads are infallible.
209/// When the operating range is in user space, it is ensured that the page table of
210/// the process creating the [`VmReader`] is active for the duration of `'a`,
211/// and the corresponding memory reads are considered fallible.
212///
213/// When perform reading with a [`VmWriter`], if one of them represents typed memory,
214/// it can ensure that the reading range in this reader and writing range in the
215/// writer are not overlapped.
216///
217/// NOTE: The overlap mentioned above is at both the virtual address level
218/// and physical address level. There is not guarantee for the operation results
219/// of [`VmReader`] and [`VmWriter`] in overlapping untyped addresses, and it is
220/// the user's responsibility to handle this situation.
221pub struct VmReader<'a, Fallibility = Fallible> {
222 pub ghost_id: Ghost<nat>,
223 pub cursor: VirtPtr,
224 pub end: VirtPtr,
225 pub phantom: PhantomData<(&'a [u8], Fallibility)>,
226}
227
228/// [`VmWriter`] is a writer for writing data to a contiguous range of memory.
229///
230/// The memory range write by [`VmWriter`] can be in either kernel space or user space.
231/// When the operating range is in kernel space, the memory within that range
232/// is guaranteed to be valid, and the corresponding memory writes are infallible.
233/// When the operating range is in user space, it is ensured that the page table of
234/// the process creating the [`VmWriter`] is active for the duration of `'a`,
235/// and the corresponding memory writes are considered fallible.
236///
237/// When perform writing with a [`VmReader`], if one of them represents typed memory,
238/// it can ensure that the writing range in this writer and reading range in the
239/// reader are not overlapped.
240///
241/// NOTE: The overlap mentioned above is at both the virtual address level
242/// and physical address level. There is not guarantee for the operation results
243/// of [`VmReader`] and [`VmWriter`] in overlapping untyped addresses, and it is
244/// the user's responsibility to handle this situation.
245pub struct VmWriter<'a, Fallibility = Fallible> {
246 pub ghost_id: Ghost<nat>,
247 pub cursor: VirtPtr,
248 pub end: VirtPtr,
249 pub phantom: PhantomData<(&'a [u8], Fallibility)>,
250}
251
252#[verus_verify]
253impl<'a> VmWriter<'a, Infallible> {
254 /// Constructs a [`VmWriter`] from a pointer and a length, which represents
255 /// a memory range in kernel space.
256 ///
257 /// # Verified Properties
258 /// ## Preconditions
259 /// - The memory region represented by `ptr` and `len` must be valid for writes of `len` bytes
260 /// during the entire lifetime `a`. This means that the underlying pages must remain alive,
261 /// and the kernel must access the memory region using only the APIs provided in this module.
262 /// - The range `ptr.vaddr..ptr.vaddr + len` must represent a kernel space memory range.
263 /// ## Postconditions
264 /// - An infallible [`VmWriter`] will be created with the range `ptr.vaddr..ptr.vaddr + len`.
265 /// - The created [`VmWriter`] will have a unique identifier `id`, and its cursor will be
266 /// initialized to `ptr`.
267 /// - The created [`VmWriter`] will be associated with a [`VmIoOwner`] that has the same `id`, the
268 /// same memory range, and is marked as kernel space and infallible.
269 /// - The memory view of the associated [`VmIoOwner`] will be `None`, indicating that it does not
270 /// have any specific permissions yet.
271 /// ## Safety
272 ///
273 /// `ptr` must be [valid] for writes of `len` bytes during the entire lifetime `a`.
274 ///
275 /// [valid]: crate::mm::io#safety
276 #[verus_spec(r =>
277 with
278 Ghost(id): Ghost<nat>,
279 Tracked(fallible): Tracked<bool>,
280 -> owner: Tracked<VmIoOwner>,
281 ensures
282 r.inv_wf(),
283 owner@.id == id,
284 owner@.is_fallible == fallible,
285 owner@.is_kernel,
286 r.cursor == ptr,
287 r.end == ptr.wrapping_add_spec(len),
288 r.cursor.range@ == ptr.range@,
289 r.end.range@ == ptr.range@,
290 fallible ==> owner@.mem_view is None,
291 !fallible && ptr.inv() && ptr.range@.start == ptr.vaddr
292 && len == ptr.range@.end - ptr.range@.start
293 && (len == 0 || KERNEL_BASE_VADDR <= ptr.vaddr)
294 && (len == 0 || ptr.vaddr + len <= KERNEL_END_VADDR) ==> {
295 &&& r.inv()
296 &&& owner@.inv()
297 &&& r.wf(owner@)
298 &&& owner@.has_write_view()
299 },
300 )]
301 pub unsafe fn from_kernel_space(ptr: VirtPtr, len: usize) -> Self {
302 let ghost range: Range<usize> = ptr.vaddr..(ptr.vaddr + len) as usize;
303 let tracked mem_view: Option<VmIoMemView> = if fallible {
304 None
305 } else {
306 let tracked mv = axiom_kernel_mem_view(range);
307 Some(VmIoMemView::WriteView(mv))
308 };
309 let tracked owner = VmIoOwner {
310 id,
311 range: ptr.vaddr..(ptr.vaddr + len) as usize,
312 is_fallible: fallible,
313 is_kernel: true,
314 mem_view,
315 };
316
317 proof_with!(|= Tracked(owner));
318 Self { ghost_id: Ghost(id), cursor: ptr, end: ptr.wrapping_add(len), phantom: PhantomData }
319 }
320
321 /// Converts an infallible writer into a fallible one.
322 #[verus_spec(r =>
323 ensures
324 r.cursor == self.cursor,
325 r.end == self.end,
326 r.ghost_id == self.ghost_id,
327 )]
328 pub fn to_fallible(self) -> VmWriter<'a, Fallible> {
329 VmWriter {
330 ghost_id: self.ghost_id,
331 cursor: self.cursor,
332 end: self.end,
333 phantom: PhantomData,
334 }
335 }
336
337 /// Writes a value of `Pod` type to the kernel-space buffer.
338 ///
339 /// If the length of the `Pod` type exceeds `self.avail()`, this method
340 /// will return `Err(InvalidArgs)`. Kernel-space writes don't fault, so
341 /// no rollback is needed — see [`VmWriter<Fallible>::write_val`] for the
342 /// user-space variant with cursor rewind.
343 ///
344 /// # Verified Properties
345 /// ## Preconditions
346 /// - The writer and its owner must satisfy their invariants.
347 /// - The owner must match this writer and carry a write memory view.
348 /// ## Postconditions
349 /// - The writer and owner still satisfy their invariants.
350 /// - On success, the cursor advances by `size_of::<T>()`.
351 /// - On error, the writer state is unchanged.
352 #[verus_spec(r =>
353 with
354 Tracked(owner): Tracked<&mut VmIoOwner>,
355 requires
356 old(self).inv(),
357 old(self).wf(*old(owner)),
358 old(owner).has_write_view(),
359 ensures
360 final(self).inv(),
361 final(owner).inv(),
362 final(self).wf(*final(owner)),
363 match r {
364 Ok(_) => {
365 &&& final(self).avail_spec() == old(self).avail_spec() - core::mem::size_of::<T>()
366 &&& final(self).cursor.vaddr == old(self).cursor.vaddr + core::mem::size_of::<T>()
367 },
368 Err(_) => {
369 *old(self) == *final(self)
370 },
371 }
372 )]
373 pub fn write_val<T: Pod>(&mut self, new_val: &T) -> Result<()> {
374 let len = core::mem::size_of::<T>();
375 if self.avail() < len {
376 return Err(Error::InvalidArgs);
377 }
378 proof_decl! {
379 let tracked mut reader_owner_inner: VmIoOwner;
380 }
381 #[verus_spec(with => Tracked(reader_owner_inner))]
382 let mut reader = VmReader::from(new_val.as_bytes());
383 #[verus_spec(with Tracked(owner), Tracked(&mut reader_owner_inner))]
384 let _ = self.write(&mut reader);
385 Ok(())
386 }
387
388 /// Panic condition for [`Self::fill`]: either the cursor isn't aligned
389 /// for `T`, or the available space isn't a multiple of `size_of::<T>()`.
390 pub open spec fn fill_panic_condition<T>(self) -> bool {
391 ||| self.cursor.vaddr as int % core::mem::align_of::<T>() as int != 0
392 ||| (self.end.vaddr - self.cursor.vaddr) % core::mem::size_of::<T>() as int != 0
393 }
394
395 /// Fills the available space by repeatedly writing the same `Pod` value.
396 ///
397 /// Returns the number of elements written.
398 ///
399 /// # Panics
400 /// If cursor isn't aligned for `T`, or `avail()` isn't a multiple of
401 /// `size_of::<T>()` ([`Self::fill_panic_condition`]).
402 #[verus_spec(r =>
403 with
404 Tracked(writer_owner): Tracked<&mut VmIoOwner>,
405 -> reader_owner: Tracked<VmIoOwner>,
406 requires
407 old(self).inv(),
408 old(self).wf(*old(writer_owner)),
409 old(writer_owner).has_write_view(),
410 core::mem::size_of::<T>() > 0,
411 core::mem::align_of::<T>() > 0,
412 core::mem::size_of::<T>() % core::mem::align_of::<T>() == 0,
413 old(self).fill_panic_condition::<T>() ==> may_panic(),
414 ensures
415 final(self).inv(),
416 final(self).wf(*final(writer_owner)),
417 !old(self).fill_panic_condition::<T>(),
418 final(self).cursor == old(self).end,
419 final(self).end == old(self).end,
420 // writer_owner: fully advanced past the filled region.
421 final(writer_owner).range.start == old(writer_owner).range.end,
422 final(writer_owner).range.end == old(writer_owner).range.end,
423 // reader_owner: brand-new ReadView over the filled region.
424 reader_owner@.inv(),
425 reader_owner@.range.start == old(writer_owner).range.start,
426 reader_owner@.range.end == old(writer_owner).range.end,
427 reader_owner@.has_read_view(),
428 reader_owner@.is_kernel == old(writer_owner).is_kernel,
429 // Return value: exactly `avail / size_of::<T>()` elements written.
430 r * core::mem::size_of::<T>() == old(self).avail_spec(),
431 )]
432 pub fn fill<T: Pod>(&mut self, value: T) -> usize {
433 let cursor = self.cursor.cast::<T>();
434 assert!(cursor.is_aligned());
435
436 let avail = self.avail();
437 assert!(avail % core::mem::size_of::<T>() == 0);
438 let len = core::mem::size_of::<T>();
439 let written_num = avail / len;
440 proof {
441 // (avail / len) * len == avail when avail % len == 0 and len > 0.
442 assert(written_num * len == avail) by (nonlinear_arith)
443 requires
444 len > 0,
445 avail % len == 0,
446 written_num == avail / len,
447 ;
448 }
449
450 proof_decl! {
451 let tracked mut reader_owner_inner: VmIoOwner;
452 }
453
454 let tracked mut mv = match writer_owner.mem_view.tracked_take() {
455 VmIoMemView::WriteView(v) => v,
456 _ => { proof_from_false() },
457 };
458 let ghost mv_pre = mv;
459 let ghost start = self.cursor.vaddr;
460 let ghost end = self.end.vaddr;
461 let ghost cursor_range = self.cursor.range@;
462
463 let mut cursor_i: VirtPtr = self.cursor;
464 let mut i: usize = 0;
465 while i < written_num
466 invariant
467 self.inv(),
468 self.cursor.vaddr == start,
469 self.end.vaddr == end,
470 self.cursor.range@ == cursor_range,
471 end - start == avail,
472 avail == written_num * len,
473 len == core::mem::size_of::<T>(),
474 len > 0,
475 core::mem::align_of::<T>() > 0,
476 len % core::mem::align_of::<T>() == 0,
477 start % core::mem::align_of::<T>() == 0,
478 cursor_i.range@ == cursor_range,
479 cursor_i.vaddr == start + i * len,
480 cursor_i.vaddr <= end,
481 cursor_i.vaddr % core::mem::align_of::<T>() == 0,
482 i <= written_num,
483 mv.mappings == mv_pre.mappings,
484 forall|va: usize|
485 #![trigger mv.addr_transl(va)]
486 start <= va < end ==> mv.addr_transl(va) is Some,
487 decreases written_num - i,
488 {
489 proof {
490 // (i + 1) * len <= written_num * len, hence cursor_i.vaddr + len <= end
491 assert((i + 1) * len <= written_num * len) by (nonlinear_arith)
492 requires
493 i < written_num,
494 len > 0,
495 ;
496 assert(i * len + len == (i + 1) * len) by (nonlinear_arith);
497 // forall va in [cursor_i.vaddr, cursor_i.vaddr + len), mv.addr_transl is Some
498 assert forall|va: usize|
499 cursor_i.vaddr <= va < cursor_i.vaddr + len implies #[trigger] mv.addr_transl(
500 va,
501 ) is Some by {};
502 }
503 // SAFETY: written_num is bounded by avail / size_of::<T>() so each
504 // write targets memory owned by this writer, and cursor is aligned.
505 #[allow(unused_unsafe)]
506 unsafe { cursor_i.write_volatile::<T>(Tracked(&mut mv), value) };
507 let ghost cursor_i_pre = cursor_i;
508 cursor_i = cursor_i.wrapping_add(len);
509 i = i + 1;
510 proof {
511 // cursor_i.vaddr == cursor_i_pre.vaddr + len == start + i*len
512 assert(cursor_i_pre.vaddr + len == start + i * len) by (nonlinear_arith)
513 requires
514 cursor_i_pre.vaddr == start + (i - 1) * len,
515 len > 0,
516 ;
517 // upper bound: i <= written_num ==> i*len <= avail
518 assert(i * len <= written_num * len) by (nonlinear_arith)
519 requires
520 i <= written_num,
521 len > 0,
522 ;
523 // alignment: cursor_i.vaddr == start + i*len, both summands divisible by align.
524 assert(cursor_i.vaddr == start + i * len);
525 lemma_add_aligned_stride(start, i, len, core::mem::align_of::<T>());
526 assert(cursor_i.vaddr % core::mem::align_of::<T>() == 0);
527 }
528 }
529
530 proof {
531 writer_owner.mem_view = Some(VmIoMemView::WriteView(mv));
532 // Split off the front of writer_owner (the filled region) as a new
533 // VmIoOwner and convert its WriteView to a ReadView so the caller
534 // can read back what was just written.
535 reader_owner_inner = writer_owner.split(avail);
536 reader_owner_inner.write_to_read();
537 }
538
539 // All available space has been filled; cursor moves to end.
540 self.cursor = self.end;
541 proof_with!(|= Tracked(reader_owner_inner));
542 written_num
543 }
544
545 /// Writes data into `self` by reading from the provided `reader`.
546 ///
547 /// This function treats `self` as the destination buffer. It pulls data *from*
548 /// the source `reader` and writes it into the current instance.
549 ///
550 /// # Arguments
551 ///
552 /// * `reader` - The source `VmReader` to read data from.
553 ///
554 /// # Returns
555 ///
556 /// Returns the number of bytes written to `self` (which is equal to the number of bytes read from `reader`).
557 ///
558 /// # Verified Properties
559 /// ## Preconditions
560 /// - The writer, reader, and both associated owners must satisfy their invariants.
561 /// - The owners must match the given writer and reader.
562 /// - The writer owner must carry a write memory view.
563 /// - The source and destination ranges must not overlap.
564 /// - The reader owner must provide initialized readable memory for the readable range.
565 /// ## Postconditions
566 /// - The writer, reader, and both owners still satisfy their invariants.
567 /// - The owners still match the updated writer and reader.
568 /// - The returned byte count equals the minimum of writable bytes and readable bytes.
569 /// - Both cursors advance by exactly the returned byte count.
570 #[verus_spec(r =>
571 with
572 Tracked(owner_w): Tracked<&mut VmIoOwner>,
573 Tracked(owner_r): Tracked<&mut VmIoOwner>,
574 requires
575 old(self).inv(),
576 old(reader).inv(),
577 old(self).wf(*old(owner_w)),
578 old(reader).wf(*old(owner_r)),
579 old(owner_w).has_write_view(),
580 old(owner_r).read_view_initialized(),
581 ensures
582 final(self).inv(),
583 final(reader).inv(),
584 final(owner_w).inv(),
585 final(owner_r).inv(),
586 final(self).wf(*final(owner_w)),
587 final(reader).wf(*final(owner_r)),
588 r == vstd::math::min(old(self).avail_spec() as int, old(reader).remain_spec() as int),
589 final(self).avail_spec() == old(self).avail_spec() - r as usize,
590 final(self).cursor.vaddr == old(self).cursor.vaddr + r as usize,
591 final(reader).remain_spec() == old(reader).remain_spec() - r as usize,
592 final(reader).cursor.vaddr == old(reader).cursor.vaddr + r as usize,
593 )]
594 pub fn write(&mut self, reader: &mut VmReader<'_, Infallible>) -> usize {
595 proof_decl! {
596 let tracked mut _discarded_consumed_w: VmIoOwner;
597 }
598 #[verus_spec(with Tracked(owner_r), Tracked(owner_w) => Tracked(_discarded_consumed_w))]
599 reader.read(self)
600 }
601
602 /// Writes a value of the `PodOnce` type using one non-tearing memory store.
603 ///
604 /// If the length of the `PodOnce` type exceeds `self.avail()`, this method will return `Err`.
605 ///
606 /// # Panics
607 ///
608 /// This method will panic if the current position of the writer does not meet the alignment
609 /// requirements of type `T`.
610 ///
611 /// # Verified Properties
612 /// ## Preconditions
613 /// - The writer and its owner must satisfy their invariants.
614 /// - The owner must match this writer and carry a write memory view.
615 /// - Every byte in the writable range must translate in the write view.
616 /// ## Postconditions
617 /// - The writer and owner still satisfy their invariants.
618 /// - On success, the cursor advances by `size_of::<T>()` and the cursor was
619 /// aligned to `align_of::<T>()` (the runtime `assert!` would otherwise diverge).
620 /// - On error, the writer state is unchanged.
621 #[verus_spec(r =>
622 with
623 Tracked(owner): Tracked<&mut VmIoOwner>,
624 requires
625 old(self).inv(),
626 old(self).wf(*old(owner)),
627 old(owner).has_write_view(),
628 // The runtime `assert!(cursor.is_aligned())` diverges unless the
629 // cursor is aligned for `T`.
630 old(self).cursor.vaddr % core::mem::align_of::<T>() != 0 ==> may_panic(),
631 ensures
632 final(self).inv(),
633 final(owner).inv(),
634 final(self).wf(*final(owner)),
635 match r {
636 Ok(_) => {
637 &&& old(self).cursor.vaddr % core::mem::align_of::<T>() == 0
638 &&& final(self).avail_spec() == old(self).avail_spec() - core::mem::size_of::<T>()
639 &&& final(self).cursor.vaddr == old(self).cursor.vaddr + core::mem::size_of::<T>()
640 },
641 Err(_) => {
642 *old(self) == *final(self)
643 },
644 }
645 )]
646 pub fn write_once<T: PodOnce>(&mut self, new_val: &T) -> Result<()> {
647 if self.avail() < core::mem::size_of::<T>() {
648 return Err(Error::InvalidArgs);
649 }
650 let cursor = self.cursor.cast::<T>();
651 assert!(cursor.is_aligned());
652
653 // NOTE: vostd has `const { assert!(pod_once_impls::is_non_tearing::<T>()) };` here, but
654 // verus doesn't yet support const block expressions. The non-tearing guarantee for our
655 // `PodOnce` impls is restricted to types of size 1/2/4/8 by convention.
656
657 // SAFETY: We have checked that the number of bytes available is at least the size of `T`
658 // and that the cursor is properly aligned with respect to the type `T`. All other safety
659 // requirements are the same as for `Self::write`.
660
661 let len = core::mem::size_of::<T>();
662 let tracked mut mem_dst = match owner.mem_view.tracked_take() {
663 VmIoMemView::WriteView(mv) => mv,
664 _ => { proof_from_false() },
665 };
666 let ghost mem_dst_pre = mem_dst;
667
668 proof {
669 assert forall|i: usize|
670 #![trigger mem_dst.addr_transl(i)]
671 self.cursor.vaddr <= i < self.cursor.vaddr + core::mem::size_of::<T>() implies {
672 mem_dst.addr_transl(i) is Some
673 } by {
674 assert(owner.range.start == self.cursor.vaddr);
675 assert(owner.range.end == self.end.vaddr);
676 }
677 }
678 #[allow(unused_unsafe)]
679 unsafe { self.cursor.write_volatile::<T>(Tracked(&mut mem_dst), *new_val) };
680
681 self.cursor = self.cursor.wrapping_add(len);
682
683 proof {
684 owner.mem_view = Some(VmIoMemView::WriteView(mem_dst));
685
686 assert forall|va| owner.range.start <= va < owner.range.end implies mem_dst.addr_transl(
687 va,
688 ) is Some by {
689 assert(mem_dst.mappings == mem_dst_pre.mappings);
690 assert(mem_dst.addr_transl(va) == mem_dst_pre.addr_transl(va));
691 }
692
693 owner.advance(len);
694 }
695
696 Ok(())
697 }
698}
699
700impl<Fallibility> Clone for VmReader<'_, Fallibility> {
701 /// [`Clone`] can be implemented for [`VmReader`]
702 /// because it either points to untyped memory or represents immutable references.
703 ///
704 /// Note that we cannot implement [`Clone`] for [`VmWriter`]
705 /// because it can represent mutable references, which must remain exclusive.
706 fn clone(&self) -> Self {
707 Self { ghost_id: self.ghost_id, cursor: self.cursor, end: self.end, phantom: PhantomData }
708 }
709}
710
711#[verus_verify]
712impl<'a> VmReader<'a, Infallible> {
713 /// Constructs a [`VmReader`] from a pointer and a length, which represents
714 /// a memory range in kernel space.
715 ///
716 /// # Verified Properties
717 /// ## Preconditions
718 /// - The memory region represented by `ptr` and `len` must be valid for reads of `len` bytes
719 /// during the entire lifetime `a`. This means that the underlying pages must remain alive,
720 /// and the kernel must access the memory region using only the APIs provided in this module.
721 /// - The range `ptr.vaddr..ptr.vaddr + len` must represent a kernel space memory range.
722 /// ## Postconditions
723 /// - An infallible [`VmReader`] will be created with the range `ptr.vaddr..ptr.vaddr + len`.
724 /// - The created [`VmReader`] will have a unique identifier `id`, and its cursor will be
725 /// initialized to `ptr`.
726 /// - The created [`VmReader`] will be associated with a [`VmIoOwner`] that has the same `id`,
727 /// the same memory range, and is marked as kernel space and infallible.
728 /// ## Safety
729 ///
730 /// `ptr` must be [valid] for reads of `len` bytes during the entire lifetime `a`.
731 ///
732 /// [valid]: crate::mm::io#safety
733 #[verus_spec(r =>
734 with
735 Ghost(id): Ghost<nat>,
736 -> owner: Tracked<VmIoOwner>,
737 ensures
738 r.inv_wf(),
739 owner@.id == id,
740 owner@.is_kernel,
741 r.cursor == ptr,
742 r.end == ptr.wrapping_add_spec(len),
743 r.cursor.range@ == ptr.range@,
744 r.end.range@ == ptr.range@,
745 ptr.inv() && ptr.range@.start == ptr.vaddr
746 && len == ptr.range@.end - ptr.range@.start
747 && (len == 0 || KERNEL_BASE_VADDR <= ptr.vaddr)
748 && (len == 0 || ptr.vaddr + len <= KERNEL_END_VADDR) ==> {
749 &&& r.inv()
750 &&& owner@.inv()
751 &&& r.wf(owner@)
752 &&& owner@.read_view_initialized()
753 },
754 )]
755 pub unsafe fn from_kernel_space(ptr: VirtPtr, len: usize) -> Self {
756 let ghost range: Range<usize> = ptr.vaddr..(ptr.vaddr + len) as usize;
757 let tracked mv = axiom_kernel_mem_view(range);
758 let tracked owner = VmIoOwner {
759 id,
760 range,
761 is_fallible: false,
762 is_kernel: true,
763 mem_view: Some(VmIoMemView::ReadView(mv)),
764 };
765
766 proof_with!(|= Tracked(owner));
767 Self { ghost_id: Ghost(id), cursor: ptr, end: ptr.wrapping_add(len), phantom: PhantomData }
768 }
769
770 /// Converts an infallible reader into a fallible one.
771 pub fn to_fallible(self) -> (r: VmReader<'a, Fallible>)
772 ensures
773 r.remain_spec() == self.remain_spec(),
774 r.cursor == self.cursor,
775 r.end == self.end,
776 r.ghost_id == self.ghost_id,
777 {
778 VmReader {
779 ghost_id: self.ghost_id,
780 cursor: self.cursor,
781 end: self.end,
782 phantom: PhantomData,
783 }
784 }
785
786 /// Reads data from `self` and writes it into the provided `writer`.
787 ///
788 /// This function acts as the source side of a transfer. It copies data from
789 /// the current instance (`self`) into the destination `writer`, up to the limit
790 /// of available data in `self` or available space in `writer` (whichever is smaller).
791 ///
792 /// # Logic
793 ///
794 /// 1. Calculates the copy length: `min(self.remaining_data, writer.available_space)`.
795 /// 2. Copies bytes from `self`'s internal buffer to `writer`'s buffer.
796 /// 3. Advances the cursors of both `self` and `writer`.
797 ///
798 /// # Arguments
799 ///
800 /// * `writer` - The destination `VmWriter` where the data will be copied to.
801 ///
802 /// # Returns
803 ///
804 /// The number of bytes actually transferred.
805 ///
806 /// # Verified Properties
807 /// ## Preconditions
808 /// - The reader, writer, and both associated owners must satisfy their invariants.
809 /// - The owners must match the given reader and writer.
810 /// - The writer owner must carry a write memory view.
811 /// - The source and destination ranges must not overlap.
812 /// - The reader owner must provide initialized readable memory for the readable range.
813 /// ## Postconditions
814 /// - The reader, writer, and both owners still satisfy their invariants.
815 /// - The owners still match the updated reader and writer.
816 /// - The returned byte count equals the minimum of readable bytes and writable bytes.
817 /// - Both cursors advance by exactly the returned byte count.
818 /// - `consumed_w` is the just-written portion of the writer's owner, covering
819 /// `[old(owner_w).range@.start, old(owner_w).range@.start + r)`.
820 #[verus_spec(r =>
821 with
822 Tracked(owner_r): Tracked<&mut VmIoOwner>,
823 Tracked(owner_w): Tracked<&mut VmIoOwner>,
824 -> consumed_w: Tracked<VmIoOwner>,
825 requires
826 old(self).inv(),
827 old(writer).inv(),
828 old(self).wf(*old(owner_r)),
829 old(writer).wf(*old(owner_w)),
830 old(owner_w).has_write_view(),
831 old(owner_r).read_view_initialized(),
832 ensures
833 final(self).inv(),
834 final(writer).inv(),
835 final(self).wf(*final(owner_r)),
836 final(writer).wf(*final(owner_w)),
837 r == vstd::math::min(old(self).remain_spec() as int, old(writer).avail_spec() as int),
838 final(self).remain_spec() == old(self).remain_spec() - r as usize,
839 final(self).cursor.vaddr == old(self).cursor.vaddr + r as usize,
840 final(writer).avail_spec() == old(writer).avail_spec() - r as usize,
841 final(writer).cursor.vaddr == old(writer).cursor.vaddr + r as usize,
842 consumed_w@.inv(),
843 consumed_w@.range.start == old(owner_w).range.start,
844 consumed_w@.range.end == old(owner_w).range.start + r as usize,
845 consumed_w@.has_write_view(),
846 )]
847 pub fn read(&mut self, writer: &mut VmWriter<'_, Infallible>) -> usize {
848 let copy_len = self.remain().min(writer.avail());
849 proof_decl! {
850 let tracked mut consumed_w_owner_inner: VmIoOwner;
851 }
852 if copy_len == 0 {
853 proof {
854 consumed_w_owner_inner = owner_w.split(0);
855 }
856 proof_with!(|= Tracked(consumed_w_owner_inner));
857 0
858 } else {
859 let tracked mv_r = match owner_r.mem_view.tracked_take() {
860 VmIoMemView::ReadView(mv) => mv,
861 _ => { proof_from_false() },
862 };
863 let tracked mut mv_w = match owner_w.mem_view.tracked_take() {
864 VmIoMemView::WriteView(mv) => mv,
865 _ => { proof_from_false() },
866 };
867 let ghost mv_w_pre = mv_w;
868
869 proof {
870 assert forall|i: usize|
871 #![trigger mv_r.addr_transl(i)]
872 self.cursor.vaddr <= i < self.cursor.vaddr + copy_len implies {
873 &&& mv_r.addr_transl(i) is Some
874 &&& mv_r.memory.contains_key(mv_r.addr_transl(i).unwrap().0)
875 &&& mv_r.memory[mv_r.addr_transl(i).unwrap().0].contents[mv_r.addr_transl(
876 i,
877 ).unwrap().1 as int] is Init
878 } by {
879 assert(owner_r.range.start == self.cursor.vaddr);
880 assert(owner_r.range.end == self.end.vaddr);
881 }
882 }
883 // SAFETY: The source and destination are subsets of memory ranges specified by the
884 // reader and writer, so they are valid for reading and writing.
885 unsafe {
886 #[verus_spec(with Tracked(&mv_r), Tracked(&mut mv_w))]
887 memcpy(writer.cursor, self.cursor, copy_len)
888 };
889
890 self.cursor = self.cursor.wrapping_add(copy_len);
891 writer.cursor = writer.cursor.wrapping_add(copy_len);
892
893 proof {
894 owner_w.mem_view = Some(VmIoMemView::WriteView(mv_w));
895 owner_r.mem_view = Some(VmIoMemView::ReadView(mv_r));
896
897 assert forall|va|
898 owner_w.range.start <= va < owner_w.range.end implies mv_w.addr_transl(
899 va,
900 ) is Some by {
901 assert(mv_w.mappings == mv_w_pre.mappings);
902 assert(mv_w.addr_transl(va) == mv_w_pre.addr_transl(va));
903 }
904
905 consumed_w_owner_inner = owner_w.split(copy_len);
906 owner_r.advance(copy_len);
907 }
908 proof_with!(|= Tracked(consumed_w_owner_inner));
909 copy_len
910 }
911 }
912
913 /// Reads a value of `Pod` type.
914 ///
915 /// If the length of the `Pod` type exceeds `self.remain()`,
916 /// this method will return `Err`.
917 ///
918 /// # Verified Properties
919 /// ## Preconditions
920 /// - The reader and its owner must satisfy their invariants.
921 /// - The owner must match this reader and carry an initialized read memory view.
922 /// - The caller supplies a tracked writer owner whose front `size_of::<T>()` bytes
923 /// will become the owner of the returned value. The borrowed `writer_owner` shrinks
924 /// to cover the remaining range.
925 /// ## Postconditions
926 /// - The reader and its owner still satisfy their invariants.
927 /// - On success, the cursor advances by `size_of::<T>()` and the returned `val_owner`
928 /// owns the bytes backing `val`.
929 /// - On error, the reader state is unchanged.
930 #[verus_spec(r =>
931 with
932 Tracked(owner): Tracked<&mut VmIoOwner>,
933 requires
934 old(self).inv(),
935 old(self).wf(*old(owner)),
936 old(owner).read_view_initialized(),
937 ensures
938 final(self).inv(),
939 final(owner).inv(),
940 final(self).wf(*final(owner)),
941 match r {
942 Ok(_) => {
943 &&& final(self).remain_spec() == old(self).remain_spec() - core::mem::size_of::<T>()
944 &&& final(self).cursor.vaddr == old(self).cursor.vaddr + core::mem::size_of::<T>()
945 },
946 Err(_) => {
947 *old(self) == *final(self)
948 },
949 }
950 )]
951 pub fn read_val<T: Pod>(&mut self) -> Result<T> {
952 let len = core::mem::size_of::<T>();
953 if self.remain() < len {
954 Err(Error::InvalidArgs)
955 } else {
956 let mut val = T::new_uninit();
957 proof_decl! {
958 let tracked mut writer_owner_inner: VmIoOwner;
959 }
960 #[verus_spec(with => Tracked(writer_owner_inner))]
961 let mut writer = VmWriter::from(val.as_bytes_mut());
962 #[verus_spec(with Tracked(owner), Tracked(&mut writer_owner_inner))]
963 let _ = self.read(&mut writer);
964 Ok(val)
965 }
966 }
967
968 /// Reads a value of the `PodOnce` type using one non-tearing memory load.
969 ///
970 /// If the length of the `PodOnce` type exceeds `self.remain()`, this method will return `Err`.
971 ///
972 /// This method will not compile if the `Pod` type is too large for the current architecture
973 /// and the operation must be tear into multiple memory loads.
974 ///
975 /// # Panics
976 ///
977 /// This method will panic if the current position of the reader does not meet the alignment
978 /// requirements of type `T`.
979 ///
980 /// # Verified Properties
981 /// ## Preconditions
982 /// - The reader and its owner must satisfy their invariants.
983 /// - The owner must match this reader and carry a read memory view.
984 /// - The readable range must translate to initialized bytes in the read view.
985 /// ## Postconditions
986 /// - The reader and owner still satisfy their invariants.
987 /// - On success, the cursor advances by `size_of::<T>()` and the cursor was
988 /// aligned to `align_of::<T>()` (the runtime `assert!` would otherwise diverge).
989 /// - On error, the reader state is unchanged.
990 #[verus_spec(r =>
991 with
992 Tracked(owner): Tracked<&mut VmIoOwner>,
993 requires
994 old(self).inv(),
995 old(self).wf(*old(owner)),
996 old(owner).read_view_initialized(),
997 old(self).cursor.vaddr % core::mem::align_of::<T>() != 0 ==> may_panic(),
998 ensures
999 final(self).inv(),
1000 final(owner).inv(),
1001 final(self).wf(*final(owner)),
1002 final(owner).read_view_initialized(),
1003 old(self).remain_spec() >= core::mem::size_of::<T>() ==> r is Ok,
1004 final(self).end == old(self).end,
1005 final(self).ghost_id == old(self).ghost_id,
1006 match r {
1007 Ok(v) => {
1008 &&& old(self).cursor.vaddr % core::mem::align_of::<T>() == 0
1009 &&& final(self).remain_spec() == old(self).remain_spec() - core::mem::size_of::<T>()
1010 &&& final(self).cursor.vaddr == old(self).cursor.vaddr + core::mem::size_of::<T>()
1011 &&& ostd_pod::pod_bytes::<T>(v)
1012 == crate::specs::mm::io::VmIoOwner::read_view_of(*old(owner))
1013 .read_bytes(old(self).cursor.vaddr, core::mem::size_of::<T>())
1014 &&& forall|va: usize|
1015 #![trigger crate::specs::mm::io::VmIoOwner::read_view_of(*final(owner)).read(va)]
1016 final(self).cursor.vaddr <= va < old(self).end.vaddr
1017 && crate::specs::mm::io::VmIoOwner::read_view_of(*old(owner)).addr_transl(va) is Some
1018 && crate::specs::mm::io::VmIoOwner::read_view_of(*old(owner)).memory.contains_key(
1019 (crate::specs::mm::io::VmIoOwner::read_view_of(*old(owner)).addr_transl(va)->0).0
1020 ) ==> {
1021 &&& crate::specs::mm::io::VmIoOwner::read_view_of(*old(owner)).addr_transl(va)
1022 == crate::specs::mm::io::VmIoOwner::read_view_of(*final(owner)).addr_transl(va)
1023 &&& crate::specs::mm::io::VmIoOwner::read_view_of(*old(owner)).read(va)
1024 == crate::specs::mm::io::VmIoOwner::read_view_of(*final(owner)).read(va)
1025 }
1026 },
1027 Err(_) => {
1028 *old(self) == *final(self)
1029 },
1030 }
1031 )]
1032 pub fn read_once<T: PodOnce>(&mut self) -> Result<T> {
1033 if self.remain() < core::mem::size_of::<T>() {
1034 return Err(Error::InvalidArgs);
1035 }
1036 let cursor = self.cursor.cast::<T>();
1037 assert!(cursor.is_aligned());
1038
1039 // NOTE: vostd has `const { assert!(pod_once_impls::is_non_tearing::<T>()) };` here, but
1040 // verus doesn't yet support const block expressions. The non-tearing guarantee for our
1041 // `PodOnce` impls is restricted to types of size 1/2/4/8 by convention.
1042
1043 // SAFETY: We have checked that the number of bytes remaining is at least the size of `T`
1044 // and that the cursor is properly aligned with respect to the type `T`. All other safety
1045 // requirements are the same as for `Self::read`.
1046
1047 let tracked mem_src = owner.tracked_read_view_unwrap();
1048 proof {
1049 assert forall|i: usize|
1050 #![trigger mem_src.addr_transl(i)]
1051 self.cursor.vaddr <= i < self.cursor.vaddr + core::mem::size_of::<T>() implies {
1052 &&& mem_src.addr_transl(i) is Some
1053 &&& mem_src.memory.contains_key(mem_src.addr_transl(i).unwrap().0)
1054 &&& mem_src.memory[mem_src.addr_transl(i).unwrap().0].contents[mem_src.addr_transl(
1055 i,
1056 ).unwrap().1 as int] is Init
1057 } by {}
1058 }
1059 #[allow(unused_unsafe)]
1060 let val = unsafe { self.cursor.read_volatile::<T>(Tracked(mem_src)) };
1061 self.cursor = self.cursor.wrapping_add(core::mem::size_of::<T>());
1062
1063 proof {
1064 owner.advance(core::mem::size_of::<T>());
1065 }
1066
1067 Ok(val)
1068 }
1069}
1070
1071#[verus_verify]
1072impl<'a> VmReader<'a, Fallible> {
1073 /// Constructs a [`VmReader`] from a pointer and a length, which represents
1074 /// a memory range in USER space.
1075 ///
1076 /// # Verified Properties
1077 /// ## Preconditions
1078 /// - `ptr` must satisfy [`VirtPtr::inv`].
1079 /// ## Postconditions
1080 /// - The returned [`VmReader`] satisfies its invariant.
1081 /// - The returned reader is associated with a [`VmIoOwner`] that satisfies both [`VmIoOwner::inv`]
1082 /// and [`VmReader::wf`].
1083 /// - The owner has the same range as `ptr`, has no memory view yet, and is marked as user-space.
1084 #[verus_spec(r =>
1085 with
1086 Ghost(id): Ghost<nat>,
1087 -> owner: Tracked<VmIoOwner>,
1088 ensures
1089 r.inv_wf(),
1090 owner@.id == id,
1091 owner@.range == ptr.range@,
1092 owner@.mem_view is None,
1093 !owner@.is_kernel,
1094 r.cursor == ptr,
1095 r.end == ptr.wrapping_add_spec(len),
1096 r.end.range@ == ptr.range@,
1097 ptr.inv() && ptr.range@.start == ptr.vaddr
1098 && len == ptr.range@.end - ptr.range@.start ==> {
1099 &&& r.inv()
1100 &&& owner@.inv()
1101 &&& r.wf(owner@)
1102 },
1103 )]
1104 pub unsafe fn from_user_space(ptr: VirtPtr, len: usize) -> Self {
1105 let tracked owner = VmIoOwner {
1106 id,
1107 range: ptr.range@,
1108 is_fallible: true,
1109 is_kernel: false,
1110 mem_view: None,
1111 };
1112 proof_with!(|= Tracked(owner));
1113 Self { ghost_id: Ghost(id), cursor: ptr, end: ptr.wrapping_add(len), phantom: PhantomData }
1114 }
1115
1116 /// Reads a value of `Pod` type from a (potentially) user-space buffer.
1117 ///
1118 /// If the length of the `Pod` type exceeds `self.remain()`, or the value
1119 /// can not be read completely (e.g. due to a page fault), this method
1120 /// returns `Err` and the reader's cursor is rolled back to its original
1121 /// position.
1122 #[verus_spec(r =>
1123 requires
1124 old(self).inv(),
1125 ensures
1126 final(self).inv(),
1127 final(self).end == old(self).end,
1128 final(self).ghost_id == old(self).ghost_id,
1129 final(self).cursor.range == old(self).cursor.range,
1130 r is Err ==> *final(self) == *old(self),
1131 )]
1132 pub fn read_val<T: Pod>(&mut self) -> Result<T> {
1133 let len = core::mem::size_of::<T>();
1134 if self.remain() < len {
1135 Err(Error::InvalidArgs)
1136 } else {
1137 let mut val = T::new_uninit();
1138 proof_decl! {
1139 let tracked mut writer_owner_inner: VmIoOwner;
1140 }
1141 #[verus_spec(with => Tracked(writer_owner_inner))]
1142 let mut writer = VmWriter::from(val.as_bytes_mut());
1143
1144 match self.read_fallible(&mut writer) {
1145 Ok(_) => Ok(val),
1146 Err((err, copied_len)) => {
1147 self.cursor = self.cursor.sub(copied_len);
1148 Err(err)
1149 },
1150 }
1151 }
1152 }
1153
1154 /// Collects all the remaining bytes into a `Vec<u8>`.
1155 ///
1156 /// If the memory read failed, this method will return `Err`
1157 /// and the current reader's cursor remains pointing to
1158 /// the original starting position.
1159 ///
1160 /// The destination buffer is allocated fresh inside this function. The
1161 /// kernel-space precondition for the resulting `VmWriter` is discharged
1162 /// via [`axiom_slice_in_kernel`] — the natural trust boundary where a
1163 /// native Rust slice meets the tracked-memory model.
1164 #[verus_spec(r =>
1165 requires
1166 old(self).inv(),
1167 ensures
1168 final(self).inv(),
1169 final(self).end == old(self).end,
1170 final(self).ghost_id == old(self).ghost_id,
1171 final(self).cursor.range == old(self).cursor.range,
1172 r is Err ==> *final(self) == *old(self),
1173 )]
1174 pub fn collect(&mut self) -> Result<alloc::vec::Vec<u8>> {
1175 let len = self.remain();
1176 let mut buf = alloc::vec![0u8; len];
1177
1178 let ptr = {
1179 let slice: &[u8] = buf.as_slice();
1180 let ptr = slice.as_virt_ptr();
1181 proof {
1182 axiom_slice_in_kernel(slice);
1183 }
1184 ptr
1185 };
1186 proof_decl! {
1187 let tracked mut owner: VmIoOwner;
1188 }
1189 let mut writer = unsafe {
1190 #[verus_spec(with Ghost(0nat), Tracked(false) => Tracked(owner))]
1191 VmWriter::from_kernel_space(ptr, len)
1192 };
1193 match self.read_fallible(&mut writer) {
1194 Ok(_) => Ok(buf),
1195 Err((err, copied_len)) => {
1196 self.cursor = self.cursor.sub(copied_len);
1197 Err(err)
1198 },
1199 }
1200 }
1201}
1202
1203type Result<T> = core::result::Result<T, Error>;
1204
1205/// A trait that enables reading/writing data from/to a VM object,
1206/// e.g., [`USegment`], [`Vec<UFrame>`] and [`UFrame`].
1207///
1208/// # Concurrency
1209///
1210/// The methods may be executed by multiple concurrent reader and writer
1211/// threads. In this case, if the results of concurrent reads or writes
1212/// desire predictability or atomicity, the users should add extra mechanism
1213/// for such properties.
1214///
1215/// [`USegment`]: crate::mm::USegment
1216/// [`UFrame`]: crate::mm::UFrame
1217///
1218/// Note: In this trait we follow the standard of `vstd` trait that allows precondition and
1219/// postcondition overriding by introducing `obeys_`, `_requires`, and `_ensures` spec functions.
1220///
1221/// `P` is the type of the permission/ownership token used to track the state of the VM object.
1222pub trait VmIo<P: Sized>: Send + Sync + Sized {
1223 spec fn obeys_vmio_spec() -> bool;
1224
1225 open spec fn obeys_vmio_read_requires() -> bool
1226 recommends
1227 Self::obeys_vmio_spec(),
1228 {
1229 false
1230 }
1231
1232 open spec fn obeys_vmio_write_requires() -> bool
1233 recommends
1234 Self::obeys_vmio_spec(),
1235 {
1236 false
1237 }
1238
1239 spec fn obeys_vmio_read_spec() -> bool
1240 recommends
1241 Self::obeys_vmio_spec(),
1242 ;
1243
1244 spec fn obeys_vmio_write_spec() -> bool
1245 recommends
1246 Self::obeys_vmio_spec(),
1247 ;
1248
1249 open spec fn read_requires(
1250 self,
1251 offset: usize,
1252 writer: VmWriter<'_>,
1253 writer_own: VmIoOwner,
1254 owner: P,
1255 ) -> bool {
1256 true
1257 }
1258
1259 open spec fn write_requires(
1260 self,
1261 offset: usize,
1262 reader: VmReader<'_>,
1263 reader_own: VmIoOwner,
1264 owner: P,
1265 ) -> bool {
1266 true
1267 }
1268
1269 spec fn read_spec(
1270 self,
1271 offset: usize,
1272 old_writer: VmWriter<'_>,
1273 new_writer: VmWriter<'_>,
1274 old_writer_own: VmIoOwner,
1275 new_writer_own: VmIoOwner,
1276 old_owner: P,
1277 new_owner: P,
1278 r: Result<()>,
1279 ) -> bool;
1280
1281 spec fn write_spec(
1282 self,
1283 offset: usize,
1284 old_reader: VmReader<'_>,
1285 new_reader: VmReader<'_>,
1286 old_writer_own: VmIoOwner,
1287 new_writer_own: VmIoOwner,
1288 old_owner: P,
1289 new_owner: P,
1290 r: Result<()>,
1291 ) -> bool;
1292
1293 /// Reads requested data at a specified offset into a given [`VmWriter`].
1294 ///
1295 /// # No short reads
1296 ///
1297 /// On success, the `writer` must be written with the requested data
1298 /// completely. If, for any reason, the requested data is only partially
1299 /// available, then the method shall return an error.
1300 fn read(
1301 &self,
1302 offset: usize,
1303 writer: &mut VmWriter<'_>,
1304 Tracked(writer_own): Tracked<&mut VmIoOwner>,
1305 Tracked(owner): Tracked<&mut P>,
1306 ) -> (r: Result<()>)
1307 requires
1308 Self::obeys_vmio_read_requires() ==> Self::read_requires(
1309 *self,
1310 offset,
1311 *old(writer),
1312 *old(writer_own),
1313 *old(owner),
1314 ),
1315 ensures
1316 Self::obeys_vmio_read_spec() ==> Self::read_spec(
1317 *self,
1318 offset,
1319 *old(writer),
1320 *final(writer),
1321 *old(writer_own),
1322 *final(writer_own),
1323 *old(owner),
1324 *final(owner),
1325 r,
1326 ),
1327 ;
1328
1329 /// Writes all data from a given `VmReader` at a specified offset.
1330 ///
1331 /// # No short writes
1332 ///
1333 /// On success, the data from the `reader` must be read to the VM object entirely.
1334 /// If, for any reason, the input data can only be written partially,
1335 /// then the method shall return an error.
1336 fn write(
1337 &self,
1338 offset: usize,
1339 reader: &mut VmReader,
1340 Tracked(writer_own): Tracked<&mut VmIoOwner>,
1341 Tracked(owner): Tracked<&mut P>,
1342 ) -> (r: Result<()>)
1343 requires
1344 Self::obeys_vmio_write_requires() ==> Self::write_requires(
1345 *self,
1346 offset,
1347 *old(reader),
1348 *old(writer_own),
1349 *old(owner),
1350 ),
1351 ensures
1352 Self::obeys_vmio_write_spec() ==> Self::write_spec(
1353 *self,
1354 offset,
1355 *old(reader),
1356 *final(reader),
1357 *old(writer_own),
1358 *final(writer_own),
1359 *old(owner),
1360 *final(owner),
1361 r,
1362 ),
1363 ;
1364
1365 /// Reads a specified number of bytes at a specified offset into a given buffer.
1366 ///
1367 /// Wraps `buf` in a [`VmWriter`] (whose tracked owner is minted internally
1368 /// from the slice via [`VmWriter::from`]) and delegates to [`Self::read`].
1369 /// The shallow contract here only forwards the result; impls with non-trivial
1370 /// `read_requires` must override.
1371 fn read_bytes(&self, offset: usize, buf: &mut [u8], Tracked(owner): Tracked<&mut P>) -> (r:
1372 Result<()>)
1373 requires
1374 !Self::obeys_vmio_read_requires(),
1375 {
1376 proof_decl! {
1377 let tracked mut writer_own_inner: VmIoOwner;
1378 }
1379 #[verus_spec(with => Tracked(writer_own_inner))]
1380 let infallible_writer = VmWriter::from(buf);
1381 let mut writer = infallible_writer.to_fallible();
1382 self.read(offset, &mut writer, Tracked(&mut writer_own_inner), Tracked(owner))
1383 }
1384
1385 /// Reads a value of a specified type at a specified offset.
1386 fn read_val<T: Pod>(&self, offset: usize, Tracked(owner): Tracked<&mut P>) -> (r: Result<T>)
1387 requires
1388 !Self::obeys_vmio_read_requires(),
1389 {
1390 let mut val = T::new_uninit();
1391 match self.read_bytes(offset, val.as_bytes_mut(), Tracked(owner)) {
1392 Ok(_) => Ok(val),
1393 Err(e) => Err(e),
1394 }
1395 }
1396
1397 /// Reads a slice of a specified type at a specified offset.
1398 fn read_slice<T: Pod>(
1399 &self,
1400 offset: usize,
1401 slice: &mut [T],
1402 Tracked(owner): Tracked<&mut P>,
1403 ) -> (r: Result<()>)
1404 requires
1405 !Self::obeys_vmio_read_requires(),
1406 {
1407 let len_in_bytes = core::mem::size_of_val(slice);
1408 let ptr = slice.as_mut_ptr() as *mut u8;
1409 // SAFETY: the slice can be transmuted to a writable byte slice since
1410 // the elements are all Plain-Old-Data (Pod) types.
1411 let buf = unsafe { core::slice::from_raw_parts_mut(ptr, len_in_bytes) };
1412 self.read_bytes(offset, buf, Tracked(owner))
1413 }
1414
1415 /// Writes a specified number of bytes from a given buffer at a specified offset.
1416 ///
1417 /// Wraps `buf` in a [`VmReader`] (whose tracked owner is minted internally
1418 /// from the slice via [`VmReader::from`]) and delegates to [`Self::write`].
1419 /// The shallow contract here only forwards the result; impls with non-trivial
1420 /// `write_requires` must override.
1421 fn write_bytes(&self, offset: usize, buf: &[u8], Tracked(owner): Tracked<&mut P>) -> (r: Result<
1422 (),
1423 >)
1424 requires
1425 !Self::obeys_vmio_write_requires(),
1426 {
1427 proof_decl! {
1428 let tracked mut reader_own_inner: VmIoOwner;
1429 }
1430 #[verus_spec(with => Tracked(reader_own_inner))]
1431 let infallible_reader = VmReader::from(buf);
1432 let mut reader = infallible_reader.to_fallible();
1433 self.write(offset, &mut reader, Tracked(&mut reader_own_inner), Tracked(owner))
1434 }
1435
1436 /// Writes a value of a specified type at a specified offset.
1437 fn write_val<T: Pod>(&self, offset: usize, new_val: &T, Tracked(owner): Tracked<&mut P>) -> (r:
1438 Result<()>)
1439 requires
1440 !Self::obeys_vmio_write_requires(),
1441 {
1442 self.write_bytes(offset, new_val.as_bytes(), Tracked(owner))
1443 }
1444
1445 /// Writes a slice of a specified type at a specified offset.
1446 fn write_slice<T: Pod>(
1447 &self,
1448 offset: usize,
1449 slice: &[T],
1450 Tracked(owner): Tracked<&mut P>,
1451 ) -> (r: Result<()>)
1452 requires
1453 !Self::obeys_vmio_write_requires(),
1454 {
1455 let len_in_bytes = core::mem::size_of_val(slice);
1456 let ptr = slice.as_ptr() as *const u8;
1457 // SAFETY: the slice can be transmuted to a readable byte slice since
1458 // the elements are all Plain-Old-Data (Pod) types.
1459 let buf = unsafe { core::slice::from_raw_parts(ptr, len_in_bytes) };
1460 self.write_bytes(offset, buf, Tracked(owner))
1461 }
1462
1463 /// Writes a sequence of values given by an iterator (`iter`) from the
1464 /// specified offset (`offset`).
1465 ///
1466 /// Stops on iterator exhaustion or write error. Returns `Ok(nr_written)`
1467 /// if at least one value was written; only the first-call error path
1468 /// surfaces as `Err`.
1469 ///
1470 /// `align` rounds the offset and item size up: `0`/`1` means no padding,
1471 /// otherwise must be a power of two. Bad `align` panics at runtime — this
1472 /// is captured as a *post-condition*, not a pre-condition, since the panic
1473 /// is what enforces it.
1474 ///
1475 /// Preconditions left for the caller:
1476 /// - `offset + (align - 1)` and `size_of::<T>() + (align - 1)` don't overflow
1477 /// (so the post-panic call to `align_up` is safe).
1478 /// - The supplied iterator obeys Verus' prophetic iterator laws and provides
1479 /// a `decrease` measure (true for slice iterators, `repeat_n`, and the
1480 /// stdlib iterator combinators ostd uses).
1481 /// - `self`'s impl has no custom `write_requires` (use the override route
1482 /// for impls that do).
1483 fn write_vals<
1484 'a,
1485 T: Pod + 'a,
1486 I: ::vstd::std_specs::iter::IteratorSpec<Item = &'a T> + Iterator<Item = &'a T>,
1487 >(&self, offset: usize, iter: I, align: usize, Tracked(owner): Tracked<&mut P>) -> (r: Result<
1488 usize,
1489 >)
1490 requires
1491 !Self::obeys_vmio_write_requires(),
1492 offset + align <= usize::MAX,
1493 core::mem::size_of::<T>() + align <= usize::MAX,
1494 iter.obeys_prophetic_iter_laws(),
1495 iter.decrease() is Some,
1496 // `align_up` (called for `align > 1`) diverges unless `align`
1497 // is a power of two.
1498 !(align <= 1 || is_pow2(align as int)) ==> may_panic(),
1499 ensures
1500 align <= 1 || is_pow2(align as int),
1501 {
1502 use ::align_ext::AlignExt;
1503 let mut nr_written: usize = 0;
1504 // `align_up` itself panics on invalid `align`, so reaching the post-`else`
1505 // statements gives us the post-condition for free.
1506 let (mut offset, item_size) = if align <= 1 {
1507 (offset, core::mem::size_of::<T>())
1508 } else {
1509 (offset.align_up(align), core::mem::size_of::<T>().align_up(align))
1510 };
1511 let mut iter = iter;
1512 loop
1513 invariant
1514 !Self::obeys_vmio_write_requires(),
1515 iter.obeys_prophetic_iter_laws(),
1516 iter.decrease() is Some,
1517 align == 0 || align == 1 || is_pow2(align as int),
1518 decreases iter.decrease().unwrap(),
1519 {
1520 match iter.next() {
1521 Some(item) => {
1522 // Stop *before* writing if we couldn't safely advance afterwards.
1523 if nr_written == usize::MAX || (item_size > 0 && offset > usize::MAX
1524 - item_size) {
1525 return Ok(nr_written);
1526 }
1527 match self.write_val(offset, item, Tracked(owner)) {
1528 Ok(_) => {
1529 offset = offset + item_size;
1530 nr_written = nr_written + 1;
1531 },
1532 Err(e) => {
1533 if nr_written > 0 {
1534 return Ok(nr_written);
1535 }
1536 return Err(e);
1537 },
1538 }
1539 },
1540 None => return Ok(nr_written),
1541 }
1542 }
1543 }
1544}
1545
1546/// A trait that enables reading/writing data from/to a VM object using one non-tearing memory
1547/// load/store.
1548///
1549/// See also [`VmIo`], which enables reading/writing data from/to a VM object without the guarantee
1550/// of using one non-tearing memory load/store.
1551pub trait VmIoOnce: Sized {
1552 spec fn obeys_vmio_once_read_requires() -> bool;
1553
1554 spec fn obeys_vmio_once_write_requires() -> bool;
1555
1556 spec fn obeys_vmio_once_read_ensures() -> bool;
1557
1558 spec fn obeys_vmio_once_write_ensures() -> bool;
1559
1560 /// Reads a value of the `PodOnce` type at the specified offset using one non-tearing memory
1561 /// load.
1562 ///
1563 /// Except that the offset is specified explicitly, the semantics of this method is the same as
1564 /// [`VmReader::read_once`].
1565 fn read_once<T: PodOnce>(&self, offset: usize) -> Result<T>;
1566
1567 /// Writes a value of the `PodOnce` type at the specified offset using one non-tearing memory
1568 /// store.
1569 ///
1570 /// Except that the offset is specified explicitly, the semantics of this method is the same as
1571 /// [`VmWriter::write_once`].
1572 fn write_once<T: PodOnce>(&self, offset: usize, new_val: &T) -> Result<()>;
1573}
1574
1575/*
1576// Original impl_vm_io_pointer macro and invocations (removed during Verus migration):
1577macro_rules! impl_vm_io_pointer {
1578 ($typ:ty,$from:tt) => {
1579 #[inherit_methods(from = $from)]
1580 impl<T: VmIo> VmIo for $typ {
1581 fn read(&self, offset: usize, writer: &mut VmWriter) -> Result<()>;
1582 fn read_bytes(&self, offset: usize, buf: &mut [u8]) -> Result<()>;
1583 fn read_val<F: Pod>(&self, offset: usize) -> Result<F>;
1584 fn read_slice<F: Pod>(&self, offset: usize, slice: &mut [F]) -> Result<()>;
1585 fn write(&self, offset: usize, reader: &mut VmReader) -> Result<()>;
1586 fn write_bytes(&self, offset: usize, buf: &[u8]) -> Result<()>;
1587 fn write_val<F: Pod>(&self, offset: usize, new_val: &F) -> Result<()>;
1588 fn write_slice<F: Pod>(&self, offset: usize, slice: &[F]) -> Result<()>;
1589 }
1590 };
1591}
1592
1593impl_vm_io_pointer!(&T, "(**self)");
1594impl_vm_io_pointer!(&mut T, "(**self)");
1595impl_vm_io_pointer!(Box<T>, "(**self)");
1596impl_vm_io_pointer!(Arc<T>, "(**self)");
1597*/
1598
1599/*
1600// Original impl_vm_io_once_pointer macro and invocations (removed during Verus migration):
1601macro_rules! impl_vm_io_once_pointer {
1602 ($typ:ty,$from:tt) => {
1603 #[inherit_methods(from = $from)]
1604 impl<T: VmIoOnce> VmIoOnce for $typ {
1605 fn read_once<F: PodOnce>(&self, offset: usize) -> Result<F>;
1606 fn write_once<F: PodOnce>(&self, offset: usize, new_val: &F) -> Result<()>;
1607 }
1608 };
1609}
1610
1611impl_vm_io_once_pointer!(&T, "(**self)");
1612impl_vm_io_once_pointer!(&mut T, "(**self)");
1613impl_vm_io_once_pointer!(Box<T>, "(**self)");
1614impl_vm_io_once_pointer!(Arc<T>, "(**self)");
1615*/
1616
1617#[verus_verify]
1618impl<Fallibility> VmReader<'_, Fallibility> {
1619 pub open spec fn remain_spec(&self) -> usize {
1620 (self.end.vaddr - self.cursor.vaddr) as usize
1621 }
1622
1623 /// Returns the number of remaining bytes that can be read.
1624 ///
1625 /// # Verified Properties
1626 /// ## Preconditions
1627 /// - `self` must satisfy its invariant.
1628 /// ## Postconditions
1629 /// - The returned value equals [`Self::remain_spec`].
1630 #[verus_spec(r =>
1631 requires
1632 self.inv(),
1633 ensures
1634 r == self.remain_spec(),
1635 )]
1636 #[verifier::when_used_as_spec(remain_spec)]
1637 pub fn remain(&self) -> usize {
1638 self.end.addr() - self.cursor.addr()
1639 }
1640
1641 /// Returns the cursor pointer, which refers to the address of the next byte to read.
1642 pub fn cursor(&self) -> VirtPtr {
1643 self.cursor
1644 }
1645
1646 /// Returns whether there is remaining data to read.
1647 #[verus_spec(
1648 requires
1649 self.inv(),
1650 )]
1651 pub fn has_remain(&self) -> bool {
1652 self.remain() > 0
1653 }
1654
1655 /// Limits the length of remaining data.
1656 ///
1657 /// This method ensures the post-condition `self.remain() <= max_remain`.
1658 #[verus_spec(r =>
1659 requires
1660 old(self).inv(),
1661 ensures
1662 r.inv(),
1663 r.remain_spec() <= max_remain,
1664 r.remain_spec() <= old(self).remain_spec(),
1665 r.cursor == old(self).cursor,
1666 r.ghost_id == old(self).ghost_id,
1667 )]
1668 pub fn limit(&mut self, max_remain: usize) -> &mut Self {
1669 if max_remain < self.remain() {
1670 self.end = self.cursor.wrapping_add(max_remain);
1671 }
1672 self
1673 }
1674
1675 /// Skips the first `nbytes` bytes of data.
1676 ///
1677 /// The length of remaining data is decreased accordingly.
1678 ///
1679 /// # Panics
1680 ///
1681 /// If `nbytes` is greater than `self.remain()`, then the method panics.
1682 #[verus_spec(r =>
1683 requires
1684 old(self).inv(),
1685 nbytes <= old(self).remain_spec(),
1686 ensures
1687 r.inv(),
1688 r.cursor.vaddr == old(self).cursor.vaddr + nbytes,
1689 r.remain_spec() == old(self).remain_spec() - nbytes,
1690 r.end == old(self).end,
1691 r.ghost_id == old(self).ghost_id,
1692 )]
1693 pub fn skip(&mut self, nbytes: usize) -> &mut Self {
1694 assert!(nbytes <= self.remain());
1695 self.cursor = self.cursor.wrapping_add(nbytes);
1696 self
1697 }
1698
1699 /// Same as [`Self::skip`] but returns `()` instead of `&mut Self`.
1700 ///
1701 /// Sidesteps a Verus modeling quirk: `&mut self`-returning-`&mut Self`
1702 /// reborrows don't auto-propagate the return-value's ensures (`r.*`)
1703 /// to the post-state of `*self` (`final(self).*`). Callers that don't
1704 /// need to chain can use this in-place variant to avoid `r`-vs-`self`
1705 /// reborrow tracking.
1706 #[verus_spec(
1707 with
1708 Tracked(owner): Tracked<&mut crate::specs::mm::io::VmIoOwner>,
1709 requires
1710 old(self).inv(),
1711 old(self).wf(*old(owner)),
1712 old(owner).mem_view is Some,
1713 nbytes <= old(self).remain_spec(),
1714 ensures
1715 final(self).inv(),
1716 final(owner).inv(),
1717 final(self).wf(*final(owner)),
1718 old(owner).read_view_initialized() ==> final(owner).read_view_initialized(),
1719 final(self).cursor.vaddr == old(self).cursor.vaddr + nbytes,
1720 final(self).remain_spec() == old(self).remain_spec() - nbytes,
1721 final(self).end == old(self).end,
1722 final(self).ghost_id == old(self).ghost_id,
1723
1724 old(owner).mem_view matches Some(crate::specs::mm::io::VmIoMemView::ReadView(_)) ==>
1725 forall|va: usize|
1726 #![trigger crate::specs::mm::io::VmIoOwner::read_view_of(*final(owner)).read(va)]
1727 final(self).cursor.vaddr <= va < old(self).end.vaddr
1728 && crate::specs::mm::io::VmIoOwner::read_view_of(*old(owner)).addr_transl(va) is Some
1729 && crate::specs::mm::io::VmIoOwner::read_view_of(*old(owner)).memory.contains_key(
1730 (crate::specs::mm::io::VmIoOwner::read_view_of(*old(owner)).addr_transl(va)->0).0
1731 ) ==> {
1732 &&& crate::specs::mm::io::VmIoOwner::read_view_of(*old(owner)).addr_transl(va)
1733 == crate::specs::mm::io::VmIoOwner::read_view_of(*final(owner)).addr_transl(va)
1734 &&& crate::specs::mm::io::VmIoOwner::read_view_of(*old(owner)).read(va)
1735 == crate::specs::mm::io::VmIoOwner::read_view_of(*final(owner)).read(va)
1736 },
1737 )]
1738 pub fn skip_in_place(&mut self, nbytes: usize) {
1739 assert!(nbytes <= self.remain());
1740 self.cursor = self.cursor.wrapping_add(nbytes);
1741 proof {
1742 owner.advance(nbytes);
1743 }
1744 }
1745}
1746
1747#[verus_verify]
1748impl<'a, Fallibility> VmWriter<'a, Fallibility> {
1749 pub open spec fn avail_spec(&self) -> usize {
1750 (self.end.vaddr - self.cursor.vaddr) as usize
1751 }
1752
1753 /// Returns the number of available bytes that can be written.
1754 ///
1755 /// This has the same implementation as [`VmReader::remain`] but semantically
1756 /// they are different.
1757 ///
1758 /// # Verified Properties
1759 /// ## Preconditions
1760 /// - `self` must satisfy its invariant.
1761 /// ## Postconditions
1762 /// - The returned value equals [`Self::avail_spec`].
1763 #[inline]
1764 #[verus_spec(r =>
1765 requires
1766 self.inv(),
1767 ensures
1768 r == self.avail_spec(),
1769 )]
1770 #[verifier::when_used_as_spec(avail_spec)]
1771 pub fn avail(&self) -> usize {
1772 self.end.addr() - self.cursor.addr()
1773 }
1774
1775 /// Returns the cursor pointer, which refers to the address of the next byte to write.
1776 pub fn cursor(&self) -> VirtPtr {
1777 self.cursor
1778 }
1779
1780 /// Returns if it has available space to write.
1781 #[verus_spec(
1782 requires
1783 self.inv(),
1784 )]
1785 pub fn has_avail(&self) -> bool {
1786 self.avail() > 0
1787 }
1788
1789 /// Limits the length of available space.
1790 ///
1791 /// This method ensures the post-condition `self.avail() <= max_avail`.
1792 #[verus_spec(r =>
1793 requires
1794 old(self).inv(),
1795 ensures
1796 r.inv(),
1797 r.avail_spec() <= max_avail,
1798 r.avail_spec() <= old(self).avail_spec(),
1799 r.cursor == old(self).cursor,
1800 r.ghost_id == old(self).ghost_id,
1801 )]
1802 pub fn limit(&mut self, max_avail: usize) -> &mut Self {
1803 if max_avail < self.avail() {
1804 self.end = self.cursor.wrapping_add(max_avail);
1805 }
1806 self
1807 }
1808
1809 /// Skips the first `nbytes` of available space.
1810 ///
1811 /// The length of available space is decreased accordingly.
1812 ///
1813 /// # Panics
1814 ///
1815 /// If `nbytes` is greater than `self.avail()`, then the method panics.
1816 #[verus_spec(r =>
1817 requires
1818 old(self).inv(),
1819 nbytes <= old(self).avail_spec(),
1820 ensures
1821 r.inv(),
1822 r.cursor.vaddr == old(self).cursor.vaddr + nbytes,
1823 r.avail_spec() == old(self).avail_spec() - nbytes,
1824 r.end == old(self).end,
1825 r.ghost_id == old(self).ghost_id,
1826 )]
1827 pub fn skip(&mut self, nbytes: usize) -> &mut Self {
1828 assert!(nbytes <= self.avail());
1829 self.cursor = self.cursor.wrapping_add(nbytes);
1830 self
1831 }
1832}
1833
1834#[verus_verify]
1835impl<'a> VmWriter<'a, Fallible> {
1836 /// Constructs a [`VmWriter`] from a pointer and a length, which represents
1837 /// a memory range in USER space.
1838 ///
1839 /// # Verified Properties
1840 /// ## Preconditions
1841 /// - `ptr` must satisfy [`VirtPtr::inv`].
1842 /// ## Postconditions
1843 /// - The returned [`VmWriter`] satisfies its invariant.
1844 /// - The returned writer is associated with a [`VmIoOwner`] that satisfies both [`VmIoOwner::inv`]
1845 /// and [`VmWriter::wf`].
1846 /// - The owner has the same range as `ptr`, has no memory view yet, and is marked as user-space.
1847 #[verus_spec(r =>
1848 with
1849 Ghost(id): Ghost<nat>,
1850 -> owner: Tracked<VmIoOwner>,
1851 ensures
1852 r.inv_wf(),
1853 owner@.id == id,
1854 owner@.range == ptr.range@,
1855 owner@.mem_view is None,
1856 !owner@.is_kernel,
1857 r.cursor == ptr,
1858 r.end == ptr.wrapping_add_spec(len),
1859 r.end.range@ == ptr.range@,
1860 ptr.inv() && ptr.range@.start == ptr.vaddr
1861 && len == ptr.range@.end - ptr.range@.start ==> {
1862 &&& r.inv()
1863 &&& owner@.inv()
1864 &&& r.wf(owner@)
1865 },
1866 )]
1867 pub unsafe fn from_user_space(ptr: VirtPtr, len: usize) -> Self {
1868 let tracked owner = VmIoOwner {
1869 id,
1870 range: ptr.range@,
1871 is_fallible: true,
1872 is_kernel: false,
1873 mem_view: None,
1874 };
1875 proof_with!(|= Tracked(owner));
1876 Self { ghost_id: Ghost(id), cursor: ptr, end: ptr.wrapping_add(len), phantom: PhantomData }
1877 }
1878
1879 /// Writes a value of `Pod` type to user space.
1880 ///
1881 /// If the underlying memory access faults during the write, the cursor
1882 /// is rolled back to its starting position before returning `Err`.
1883 ///
1884 /// # Verified Properties
1885 /// ## Postconditions
1886 /// - On success, the cursor advances by `size_of::<T>()`.
1887 /// - On error, the cursor is at its original position (writer state preserved).
1888 #[verus_spec(r =>
1889 requires
1890 old(self).inv(),
1891 ensures
1892 final(self).inv(),
1893 final(self).end == old(self).end,
1894 final(self).ghost_id == old(self).ghost_id,
1895 final(self).cursor.range == old(self).cursor.range,
1896 r is Err ==> *final(self) == *old(self),
1897 )]
1898 pub fn write_val<T: Pod>(&mut self, new_val: &T) -> Result<()> {
1899 let len = core::mem::size_of::<T>();
1900 if self.avail() < len {
1901 return Err(Error::InvalidArgs);
1902 }
1903 proof_decl! {
1904 let tracked mut reader_owner_inner: VmIoOwner;
1905 }
1906 #[verus_spec(with => Tracked(reader_owner_inner))]
1907 let mut reader = VmReader::from(new_val.as_bytes());
1908 match self.write_fallible(&mut reader) {
1909 Ok(_) => Ok(()),
1910 Err((err, copied_len)) => {
1911 self.cursor = self.cursor.sub(copied_len);
1912 Err(err)
1913 },
1914 }
1915 }
1916
1917 /// Writes `len` zeros to the target memory.
1918 ///
1919 /// This method attempts to fill up to `len` bytes with zeros. If the
1920 /// available memory from the current cursor position is less than `len`,
1921 /// it will only fill the available space.
1922 ///
1923 /// If the memory write failed due to an unresolvable page fault, this
1924 /// method will return `Err` with the length set so far.
1925 #[verus_spec(r =>
1926 requires
1927 old(self).inv(),
1928 ensures
1929 final(self).inv(),
1930 final(self).end == old(self).end,
1931 final(self).ghost_id == old(self).ghost_id,
1932 final(self).cursor.range == old(self).cursor.range,
1933 final(self).cursor.vaddr >= old(self).cursor.vaddr,
1934 final(self).cursor.vaddr <= old(self).end.vaddr,
1935 match r {
1936 Ok(n) => {
1937 &&& n <= len
1938 &&& n <= old(self).avail_spec()
1939 &&& final(self).cursor.vaddr == old(self).cursor.vaddr + n
1940 },
1941 Err((_, n)) => {
1942 &&& n <= len
1943 &&& n <= old(self).avail_spec()
1944 &&& final(self).cursor.vaddr == old(self).cursor.vaddr + n
1945 },
1946 }
1947 )]
1948 pub fn fill_zeros(&mut self, len: usize) -> core::result::Result<usize, (Error, usize)> {
1949 let len_to_set = self.avail().min(len);
1950 if len_to_set == 0 {
1951 return Ok(0);
1952 }
1953 // SAFETY: The destination is a subset of the memory range specified by
1954 // the current writer, so it is either valid for writing or in user space.
1955
1956 let set_len = unsafe { memset_fallible(self.cursor, 0u8, len_to_set) };
1957 self.cursor = self.cursor.wrapping_add(set_len);
1958
1959 if set_len < len_to_set {
1960 Err((Error::PageFault, set_len))
1961 } else {
1962 Ok(len_to_set)
1963 }
1964 }
1965}
1966
1967/// Extension trait for byte slices that produces a [`VirtPtr`] covering the
1968/// slice's bytes. Mirrors the role of [`<[u8]>::as_ptr`] but in our verified
1969/// pointer model.
1970pub trait AsVirtPtr {
1971 fn as_virt_ptr(&self) -> VirtPtr;
1972}
1973
1974#[verus_verify]
1975impl AsVirtPtr for [u8] {
1976 #[verus_spec(r =>
1977 ensures
1978 r.inv(),
1979 r.range@.start == r.vaddr,
1980 r.range@.end - r.range@.start == self.len(),
1981 r.vaddr == ::vstd_extra::external::slice::as_ptr_spec(self) as usize,
1982 )]
1983 fn as_virt_ptr(&self) -> VirtPtr {
1984 let addr = self.as_ptr() as usize;
1985 let len = self.len();
1986 proof {
1987 ::vstd_extra::external::slice::axiom_slice_addr_no_overflow(self);
1988 }
1989 let ghost range: Range<usize> = addr..(addr + len) as usize;
1990 VirtPtr { vaddr: addr, range: Ghost(range) }
1991 }
1992}
1993
1994/*
1995// Original From<&mut [u8]> for VmWriter (replaced by pub fn from during Verus migration):
1996impl<'a> From<&'a mut [u8]> for VmWriter<'a, Infallible> {
1997 fn from(slice: &'a mut [u8]) -> Self {
1998 unsafe { Self::from_kernel_space(slice.as_mut_ptr(), slice.len()) }
1999 }
2000}
2001*/
2002
2003#[verus_verify]
2004impl<'a> VmWriter<'a, Infallible> {
2005 /// Constructs a [`VmWriter<'a, Infallible>`] from a mutable byte slice.
2006 ///
2007 /// The slice's address establishes the writer's cursor range; the kernel-space
2008 /// trust ([`axiom_slice_in_kernel`] + [`axiom_kernel_mem_view`] via
2009 /// [`Self::from_kernel_space`]) supplies the tracked [`VmIoOwner`] with its
2010 /// `WriteView`. Callers receive the owner via `proof_with!`.
2011 ///
2012 /// # Safety
2013 ///
2014 /// The slice's memory must be valid for writes during `'a` — guaranteed by
2015 /// Rust's borrow checker for `&'a mut [u8]`.
2016 #[verus_spec(r =>
2017 with
2018 -> owner: Tracked<VmIoOwner>,
2019 ensures
2020 r.inv(),
2021 owner@.inv(),
2022 r.wf(*owner),
2023 owner@.has_write_view(),
2024 r.cursor.range == owner@.range,
2025 owner@.range.end - owner@.range.start == old(slice).len(),
2026 )]
2027 pub fn from(slice: &'a mut [u8]) -> Self {
2028 // SAFETY:
2029 // - The memory range points to typed memory.
2030 // - The validity requirements for write accesses are met because the pointer is converted
2031 // from a mutable reference that outlives the lifetime `'a`.
2032 // - The type, i.e., the `u8` slice, is plain-old-data.
2033 let shared: &[u8] = &*slice;
2034 proof {
2035 axiom_slice_in_kernel(shared);
2036 }
2037 let ptr = shared.as_virt_ptr();
2038 let len = shared.len();
2039 proof_decl! {
2040 let tracked mut owner_inner: VmIoOwner;
2041 }
2042 let writer = unsafe {
2043 #[verus_spec(with Ghost(0nat), Tracked(false) => Tracked(owner_inner))]
2044 Self::from_kernel_space(ptr, len)
2045 };
2046 proof_with!(|= Tracked(owner_inner));
2047 writer
2048 }
2049}
2050
2051/*
2052// Original From<&[u8]> for VmReader (replaced by pub fn from during Verus migration):
2053impl<'a> From<&'a [u8]> for VmReader<'a, Infallible> {
2054 fn from(slice: &'a [u8]) -> Self {
2055 unsafe { Self::from_kernel_space(slice.as_ptr(), slice.len()) }
2056 }
2057}
2058*/
2059
2060#[verus_verify]
2061impl<'a> VmReader<'a, Infallible> {
2062 /// Constructs a [`VmReader<'a, Infallible>`] from a shared byte slice.
2063 ///
2064 /// The slice's address establishes the reader's cursor range; the kernel-space
2065 /// trust ([`axiom_slice_in_kernel`] + [`axiom_kernel_mem_view`] via
2066 /// [`Self::from_kernel_space`]) supplies the tracked [`VmIoOwner`] with its
2067 /// initialized `ReadView`. Callers receive the owner via `proof_with!`.
2068 ///
2069 /// # Safety
2070 ///
2071 /// The slice's memory must be valid for reads during `'a` — guaranteed by
2072 /// Rust's borrow checker for `&'a [u8]`.
2073 #[verus_spec(r =>
2074 with
2075 -> owner: Tracked<VmIoOwner>,
2076 ensures
2077 r.inv(),
2078 owner@.inv(),
2079 r.wf(*owner),
2080 owner@.read_view_initialized(),
2081 r.cursor.range == owner@.range,
2082 owner@.range.end - owner@.range.start == slice.len(),
2083 )]
2084 pub fn from(slice: &'a [u8]) -> Self {
2085 // SAFETY:
2086 // - The memory range points to typed memory.
2087 // - The validity requirements for read accesses are met because the pointer is converted
2088 // from a shared reference that outlives the lifetime `'a`.
2089 // - The type, i.e., the `u8` slice, is plain-old-data.
2090 proof {
2091 axiom_slice_in_kernel(slice);
2092 }
2093 let ptr = slice.as_virt_ptr();
2094 let len = slice.len();
2095 proof_decl! {
2096 let tracked mut owner_inner: VmIoOwner;
2097 }
2098 let reader = unsafe {
2099 #[verus_spec(with Ghost(0nat) => Tracked(owner_inner))]
2100 Self::from_kernel_space(ptr, len)
2101 };
2102 proof_with!(|= Tracked(owner_inner));
2103 reader
2104 }
2105}
2106
2107} // verus!
2108/// Fallible memory read from a `VmWriter`.
2109pub trait FallibleVmRead<F> {
2110 fn read_fallible(
2111 &mut self,
2112 writer: &mut VmWriter<'_, F>,
2113 ) -> core::result::Result<usize, (Error, usize)>;
2114}
2115
2116/// Fallible memory write from a `VmReader`.
2117pub trait FallibleVmWrite<F> {
2118 fn write_fallible(
2119 &mut self,
2120 reader: &mut VmReader<'_, F>,
2121 ) -> core::result::Result<usize, (Error, usize)>;
2122}
2123
2124macro_rules! impl_read_fallible {
2125 ($reader_fallibility:ty, $writer_fallibility:ty) => {
2126 ::vstd::prelude::verus! {
2127 impl<'a> FallibleVmRead<$writer_fallibility> for VmReader<'a, $reader_fallibility> {
2128 #[verus_spec(r =>
2129 requires
2130 old(self).inv(),
2131 old(writer).inv(),
2132 ensures
2133 final(self).end == old(self).end,
2134 final(self).ghost_id == old(self).ghost_id,
2135 final(self).cursor.range == old(self).cursor.range,
2136 final(writer).end == old(writer).end,
2137 final(writer).ghost_id == old(writer).ghost_id,
2138 final(writer).cursor.range == old(writer).cursor.range,
2139 final(self).inv(),
2140 final(writer).inv(),
2141 match r {
2142 Ok(n) => {
2143 &&& final(self).cursor.vaddr == old(self).cursor.vaddr + n
2144 &&& final(writer).cursor.vaddr == old(writer).cursor.vaddr + n
2145 &&& n <= old(self).remain_spec()
2146 &&& n <= old(writer).avail_spec()
2147 },
2148 Err((_, copied_len)) => {
2149 &&& final(self).cursor.vaddr == old(self).cursor.vaddr + copied_len
2150 &&& final(writer).cursor.vaddr == old(writer).cursor.vaddr + copied_len
2151 &&& copied_len <= old(self).remain_spec()
2152 &&& copied_len <= old(writer).avail_spec()
2153 },
2154 }
2155 )]
2156 fn read_fallible(
2157 &mut self,
2158 writer: &mut VmWriter<'_, $writer_fallibility>,
2159 ) -> core::result::Result<usize, (Error, usize)> {
2160 let copy_len = self.remain().min(writer.avail());
2161 if copy_len == 0 {
2162 return Ok(0);
2163 }
2164
2165 // SAFETY: The source and destination are subsets of memory ranges specified by
2166 // the reader and writer, so they are either valid for reading and writing or in
2167 // user space.
2168 let copied_len = unsafe {
2169 memcpy_fallible(writer.cursor, self.cursor, copy_len)
2170 };
2171 self.cursor = self.cursor.wrapping_add(copied_len);
2172 writer.cursor = writer.cursor.wrapping_add(copied_len);
2173
2174 if copied_len < copy_len {
2175 Err((Error::PageFault, copied_len))
2176 } else {
2177 Ok(copied_len)
2178 }
2179 }
2180 }
2181 } // verus!
2182};
2183}
2184
2185macro_rules! impl_write_fallible {
2186 ($writer_fallibility:ty, $reader_fallibility:ty) => {
2187 ::vstd::prelude::verus! {
2188 impl<'a> FallibleVmWrite<$reader_fallibility> for VmWriter<'a, $writer_fallibility> {
2189 #[verus_spec(r =>
2190 requires
2191 old(self).inv(),
2192 old(reader).inv(),
2193 ensures
2194 final(self).end == old(self).end,
2195 final(self).ghost_id == old(self).ghost_id,
2196 final(self).cursor.range == old(self).cursor.range,
2197 final(reader).end == old(reader).end,
2198 final(reader).ghost_id == old(reader).ghost_id,
2199 final(reader).cursor.range == old(reader).cursor.range,
2200 final(self).inv(),
2201 final(reader).inv(),
2202 match r {
2203 Ok(n) => {
2204 &&& final(self).cursor.vaddr == old(self).cursor.vaddr + n
2205 &&& final(reader).cursor.vaddr == old(reader).cursor.vaddr + n
2206 &&& n <= old(self).avail_spec()
2207 &&& n <= old(reader).remain_spec()
2208 },
2209 Err((_, copied_len)) => {
2210 &&& final(self).cursor.vaddr == old(self).cursor.vaddr + copied_len
2211 &&& final(reader).cursor.vaddr == old(reader).cursor.vaddr + copied_len
2212 &&& copied_len <= old(self).avail_spec()
2213 &&& copied_len <= old(reader).remain_spec()
2214 },
2215 }
2216 )]
2217 fn write_fallible(
2218 &mut self,
2219 reader: &mut VmReader<'_, $reader_fallibility>,
2220 ) -> core::result::Result<usize, (Error, usize)> {
2221 reader.read_fallible(self)
2222 }
2223 }
2224 } // verus!
2225};
2226}
2227
2228impl_read_fallible!(Fallible, Infallible);
2229impl_read_fallible!(Fallible, Fallible);
2230impl_read_fallible!(Infallible, Fallible);
2231impl_write_fallible!(Fallible, Infallible);
2232impl_write_fallible!(Fallible, Fallible);
2233impl_write_fallible!(Infallible, Fallible);
2234
2235verus! {
2236
2237/// A marker trait for POD types that can be read or written with one instruction.
2238///
2239/// This trait is mostly a hint, since it's safe and can be implemented for _any_ POD type. If it
2240/// is implemented for a type that cannot be read or written with a single instruction, calling
2241/// `read_once`/`write_once` will lead to a failed compile-time assertion.
2242pub trait PodOnce: Pod {
2243
2244}
2245
2246#[cfg(any(
2247 target_arch = "x86_64",
2248 target_arch = "riscv64",
2249 target_arch = "loongarch64"
2250))]
2251mod pod_once_impls {
2252 use super::PodOnce;
2253
2254 impl PodOnce for u8 {
2255
2256 }
2257
2258 impl PodOnce for u16 {
2259
2260 }
2261
2262 impl PodOnce for u32 {
2263
2264 }
2265
2266 impl PodOnce for u64 {
2267
2268 }
2269
2270 impl PodOnce for usize {
2271
2272 }
2273
2274 impl PodOnce for i8 {
2275
2276 }
2277
2278 impl PodOnce for i16 {
2279
2280 }
2281
2282 impl PodOnce for i32 {
2283
2284 }
2285
2286 impl PodOnce for i64 {
2287
2288 }
2289
2290 impl PodOnce for isize {
2291
2292 }
2293
2294 /// Checks whether the memory operation created by `ptr::read_volatile` and
2295 /// `ptr::write_volatile` doesn't tear.
2296 ///
2297 /// Note that the Rust documentation makes no such guarantee, and even the wording in the LLVM
2298 /// LangRef is ambiguous. But this is unlikely to break in practice because the Linux kernel
2299 /// also uses "volatile" semantics to implement `READ_ONCE`/`WRITE_ONCE`.
2300 pub(super) const fn is_non_tearing<T>() -> bool {
2301 let size = core::mem::size_of::<T>();
2302
2303 size == 1 || size == 2 || size == 4 || size == 8
2304 }
2305
2306}
2307
2308} // verus!