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