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