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