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