Skip to main content

ostd/specs/mm/
virt_mem.rs

1//! Virtual-memory specification model used by [`VmSpace`] proofs.
2//!
3//! This module defines:
4//! - [`VirtPtr`], a specification-level virtual pointer over a bounded virtual range;
5//! - [`FrameContents`], a physical-frame content model with alignment and range invariants;
6//! - [`MemView`], a projection of virtual-to-physical mappings and frame contents that supports
7//!   translation, read/write reasoning, borrowing, splitting, and joining.
8//!
9//! The model is intentionally verification-oriented (Verus specs/proofs) and is used by
10//! [`crate::mm::vm_space`] to state and prove reader/writer permission isolation as well
11//! as correctness of low-level virtual memory operations that are used by them.
12//!
13//! [`VmSpace`]: crate::mm::vm_space::VmSpace
14use vstd::{pervasive::arbitrary, prelude::*, raw_ptr, set};
15use vstd_extra::prelude::*;
16
17use crate::specs::{arch::MAX_PADDR, mm::page_table::Mapping};
18
19use crate::Pod;
20use crate::mm::{Paddr, PodOnce, Vaddr};
21use core::{marker::PhantomData, ops::Range};
22use ostd_pod::{decode_pod, pod_bytes};
23
24verus! {
25
26/// Specification-level virtual pointer with an associated half-open range.
27///
28/// [`VirtPtr`] models a C-like pointer used by verification code:
29/// - `vaddr` is the current cursor position;
30/// - `range` is the allocation-like bounds `[start, end)`.
31///
32/// Most operations require `is_valid()` (i.e., the current pointer is within
33/// range and not one-past-end), while offset operations additionally require
34/// the computed address to stay inside the same range.
35pub struct VirtPtr {
36    /// Current virtual address represented by this pointer value.
37    pub vaddr: Vaddr,
38    /// Logical bounds of the pointer's valid object/range.
39    pub range: Ghost<Range<Vaddr>>,
40}
41
42/// Byte contents of one physical frame range tracked in a [`MemView`].
43///
44/// This is the physical-memory side of the model: each frame base `Paddr` maps
45/// to a [`FrameContents`] value whose metadata (`size`, `range`) constrains how
46/// `contents` can be interpreted.
47pub ghost struct FrameContents {
48    /// Per-byte initialization/value state for this frame.
49    pub contents: Seq<raw_ptr::MemContents<u8>>,
50    /// Frame size in bytes.
51    pub size: usize,
52    /// Physical range covered by this frame, modeled as `[start, end)`.
53    pub range: Range<Paddr>,
54}
55
56impl Inv for FrameContents {
57    /// Well-formedness invariant for [`FrameContents`].
58    ///
59    /// # Invariant
60    /// - `contents.len() == size`.
61    /// - `size == range.end - range.start`.
62    /// - `range.start` and `range.end` are aligned to `size`.
63    /// - `range` is ordered and remains below [`MAX_PADDR`].
64    open spec fn inv(self) -> bool {
65        &&& self.contents.len() == self.size@
66        &&& self.size@ == self.range.end - self.range.start
67        &&& self.range.start % self.size == 0
68        &&& self.range.end % self.size == 0
69        &&& self.range.start <= self.range.end < MAX_PADDR
70    }
71}
72
73/// A local virtual-memory view used in proofs.
74///
75/// A [`MemView`] pairs:
76/// - [`Self::mappings`]: a set of [`Mapping`] entries used by [`Self::addr_transl`];
77/// - [`Self::memory`]: frame bytes keyed by physical frame base (`Paddr`) via [`FrameContents`].
78///
79/// In practice this view is obtained from [`GlobalMemView`] using
80/// [`GlobalMemView::take_view`], then consumed by APIs such as [`VirtPtr::read`],
81/// [`VirtPtr::write`], and higher-level ownership proofs in
82/// [`VmSpaceOwner`].
83///
84/// [`VmSpaceOwner`]: crate::mm::vm_space::vm_space_specs::VmSpaceOwner
85pub tracked struct MemView {
86    /// Virtual-to-physical mapping set used for address translation.
87    pub ghost mappings: Set<Mapping>,
88    /// Physical frame contents for mapped pages referenced by [`Self::mappings`].
89    pub memory: Map<Paddr, FrameContents>,
90}
91
92impl MemView {
93    /// Translates a virtual address to `(frame_base_pa, frame_offset)`.
94    ///
95    /// Returns [`Option::None`] if no mapping in [`Self::mappings`] covers `va`.
96    pub open spec fn addr_transl(self, va: usize) -> Option<(usize, usize)> {
97        let mappings = self.mappings.filter(|m: Mapping| m.va_range.start <= va < m.va_range.end);
98        if 0 < mappings.len() {
99            let m = mappings.choose();  // In a well-formed PT there will only be one, but if malformed this is non-deterministic!
100            let off = va - m.va_range.start;
101            Some((m.pa_range.start, off as usize))
102        } else {
103            None
104        }
105    }
106
107    /// Specification read through virtual translation.
108    ///
109    /// Equivalent to resolving via [`Self::addr_transl`] and reading from [`Self::memory`].
110    pub open spec fn read(self, va: usize) -> raw_ptr::MemContents<u8> {
111        let (pa, off) = self.addr_transl(va)->0;
112        self.memory[pa].contents[off as int]
113    }
114
115    /// Specification write through virtual translation.
116    ///
117    /// Returns a new [`MemView`] with one byte updated, preserving all mappings.
118    pub open spec fn write(self, va: usize, x: u8) -> Self {
119        let (pa, off) = self.addr_transl(va)->0;
120        MemView {
121            memory: self.memory.insert(
122                pa,
123                FrameContents {
124                    contents: self.memory[pa].contents.update(
125                        off as int,
126                        raw_ptr::MemContents::Init(x),
127                    ),
128                    ..self.memory[pa]
129                },
130            ),
131            ..self
132        }
133    }
134
135    /// Spec for writing `bytes.len()` bytes starting at `va`.
136    ///
137    /// `bytes[0]` is written to `va`, `bytes[1]` to `va + 1`, etc.
138    pub open spec fn write_bytes(self, va: usize, bytes: Seq<u8>) -> Self
139        decreases bytes.len(),
140    {
141        if bytes.len() == 0 {
142            self
143        } else {
144            self.write(va, bytes[0]).write_bytes((va + 1) as usize, bytes.drop_first())
145        }
146    }
147
148    /// Spec for reading `len` bytes starting at `va`.
149    ///
150    /// The byte at `va + i` is `self.read(va + i).value()`. Callers should only
151    /// rely on the result when every byte in the range is initialized; otherwise
152    /// the returned sequence contains arbitrary values where memory is `Uninit`.
153    pub open spec fn read_bytes(self, va: usize, len: usize) -> Seq<u8>
154        decreases len,
155    {
156        if len == 0 {
157            Seq::empty()
158        } else {
159            seq![self.read(va).value()].add(self.read_bytes((va + 1) as usize, (len - 1) as usize))
160        }
161    }
162
163    /// Lemma: [`Self::write_bytes`] preserves [`Self::mappings`].
164    pub proof fn lemma_write_bytes_mappings(self, va: usize, bytes: Seq<u8>)
165        ensures
166            self.write_bytes(va, bytes).mappings == self.mappings,
167        decreases bytes.len(),
168    {
169        if bytes.len() > 0 {
170            self.write(va, bytes[0]).lemma_write_bytes_mappings(
171                (va + 1) as usize,
172                bytes.drop_first(),
173            );
174        }
175    }
176
177    /// Lemma: [`Self::write_bytes`] preserves [`Self::addr_transl`] at every address.
178    pub proof fn lemma_write_bytes_addr_transl(self, va: usize, bytes: Seq<u8>, query: usize)
179        ensures
180            self.write_bytes(va, bytes).addr_transl(query) == self.addr_transl(query),
181        decreases bytes.len(),
182    {
183        if bytes.len() > 0 {
184            self.write(va, bytes[0]).lemma_write_bytes_addr_transl(
185                (va + 1) as usize,
186                bytes.drop_first(),
187                query,
188            );
189        }
190    }
191
192    /// Lemma: [`Self::write_bytes`] only grows [`Self::memory`]'s domain.
193    pub proof fn lemma_write_bytes_memory_dom_grows(self, va: usize, bytes: Seq<u8>)
194        ensures
195            self.memory.dom().subset_of(self.write_bytes(va, bytes).memory.dom()),
196        decreases bytes.len(),
197    {
198        if bytes.len() > 0 {
199            self.write(va, bytes[0]).lemma_write_bytes_memory_dom_grows(
200                (va + 1) as usize,
201                bytes.drop_first(),
202            );
203        }
204    }
205
206    /// Whether `va` is translated and mapped to frame base `pa`.
207    pub open spec fn is_mapped(self, va: usize, pa: usize) -> bool {
208        self.addr_transl(va) is Some && self.addr_transl(va)->Some_0.0 == pa
209    }
210
211    /// Specification for borrowing a sub-view covering `[vaddr, vaddr + len)`.
212    ///
213    /// The result keeps overlapping mappings and restricts memory to physical
214    /// frames reachable from that virtual range.
215    pub open spec fn borrow_at(&self, vaddr: usize, len: usize) -> MemView {
216        let range_end = vaddr + len;
217
218        let valid_pas = self.memory.dom().filter(
219            |pa: usize|
220                exists|va: usize| vaddr <= va < range_end && #[trigger] self.is_mapped(va, pa),
221        );
222
223        MemView {
224            mappings: self.mappings.filter(
225                |m: Mapping| m.va_range.start < range_end && m.va_range.end > vaddr,
226            ),
227            memory: self.memory.restrict(valid_pas),
228        }
229    }
230
231    /// Whether mappings in this view are pairwise VA-disjoint.
232    pub open spec fn mappings_are_disjoint(self) -> bool {
233        forall|m1: Mapping, m2: Mapping|
234            #![trigger self.mappings.contains(m1), self.mappings.contains(m2)]
235            self.mappings.contains(m1) && self.mappings.contains(m2) && m1 != m2 ==> {
236                m1.va_range.end <= m2.va_range.start || m2.va_range.end <= m1.va_range.start
237            }
238    }
239
240    /// Specification for splitting this view at `split_end = vaddr + len`.
241    ///
242    /// Returns `(left, right)` where:
243    /// - `left` covers `[vaddr, split_end)`, i.e. all mappings overlapping that range.
244    /// - `right` covers addresses `>= split_end`.
245    pub open spec fn split(self, vaddr: usize, len: usize) -> (MemView, MemView) {
246        let split_end = vaddr + len;
247
248        // The left part.
249        let left_mappings = self.mappings.filter(
250            |m: Mapping| m.va_range.start < split_end && m.va_range.end > vaddr,
251        );
252        let right_mappings = self.mappings.filter(|m: Mapping| m.va_range.end > split_end);
253
254        let left_pas = self.memory.dom().filter(
255            |pa: usize| exists|va: usize| vaddr <= va < split_end && self.is_mapped(va, pa),
256        );
257        let right_pas = self.memory.dom().filter(
258            |pa: usize| exists|va: usize| va >= split_end && self.is_mapped(va, pa),
259        );
260
261        (
262            MemView { mappings: left_mappings, memory: self.memory.restrict(left_pas) },
263            MemView { mappings: right_mappings, memory: self.memory.restrict(right_pas) },
264        )
265    }
266
267    /// Tracked wrapper of [`Self::borrow_at`].
268    ///
269    /// # Verified Properties
270    ///
271    /// ## Postconditions
272    /// - `r == self.borrow_at(vaddr, len)`.
273    #[verifier::external_body]
274    pub proof fn tracked_borrow_at(tracked &self, vaddr: usize, len: usize) -> (tracked r: MemView)
275        ensures
276            r == self.borrow_at(vaddr, len),
277    {
278        unimplemented!()
279    }
280
281    /// Tracked wrapper of [`Self::split`].
282    ///
283    /// # Verified Properties
284    ///
285    /// ## Postconditions
286    /// - `r == self.split(vaddr, len)`.
287    #[verifier::external_body]
288    pub proof fn tracked_split(tracked self, vaddr: usize, len: usize) -> (tracked r: (Self, Self))
289        ensures
290            r == self.split(vaddr, len),
291    {
292        unimplemented!()
293    }
294
295    /// Lemma: [`Self::split`] preserves translation semantics on each side.
296    ///
297    /// # Verified Properties
298    ///
299    /// ## Preconditions
300    /// - `original.split(vaddr, len) == (left, right)`.
301    ///
302    /// ## Postconditions
303    /// - `right.memory.dom().subset_of(original.memory.dom())`.
304    /// - For any `va` in `[vaddr, vaddr + len)`,
305    ///   `original.addr_transl(va) == left.addr_transl(va)`.
306    /// - For any `va >= vaddr + len`,
307    ///   `original.addr_transl(va) == right.addr_transl(va)`.
308    pub proof fn lemma_split_preserves_transl(
309        original: MemView,
310        vaddr: usize,
311        len: usize,
312        left: MemView,
313        right: MemView,
314    )
315        requires
316            original.split(vaddr, len) == (left, right),
317        ensures
318            right.memory.dom().subset_of(original.memory.dom()),
319            forall|va: usize|
320                vaddr <= va < vaddr + len ==> {
321                    #[trigger] original.addr_transl(va) == left.addr_transl(va)
322                },
323            forall|va: usize|
324                va >= vaddr + len ==> {
325                    #[trigger] original.addr_transl(va) == right.addr_transl(va)
326                },
327    {
328        assert forall|va: usize| vaddr <= va < vaddr + len implies original.addr_transl(va)
329            == left.addr_transl(va) by {
330            assert(left.mappings == original.mappings.filter(
331                |m: Mapping| m.va_range.start < vaddr + len && m.va_range.end > vaddr,
332            ));
333            let o_mappings = original.mappings.filter(
334                |m: Mapping| m.va_range.start <= va < m.va_range.end,
335            );
336            let l_mappings = left.mappings.filter(
337                |m: Mapping| m.va_range.start <= va < m.va_range.end,
338            );
339
340            assert forall|m: Mapping| #[trigger] o_mappings.contains(m) implies l_mappings.contains(
341                m,
342            ) by {
343                assert(left.mappings.contains(m));
344            };
345            assert(o_mappings == l_mappings);
346        }
347
348        assert forall|va: usize| va >= vaddr + len implies original.addr_transl(va)
349            == right.addr_transl(va) by {
350            let split_end = vaddr + len;
351
352            let o_mappings = original.mappings.filter(
353                |m: Mapping| m.va_range.start <= va < m.va_range.end,
354            );
355            let r_mappings = right.mappings.filter(
356                |m: Mapping| m.va_range.start <= va < m.va_range.end,
357            );
358
359            assert forall|m: Mapping| o_mappings.contains(m) implies r_mappings.contains(m) by {
360                assert(right.mappings.contains(m));
361                assert(r_mappings.contains(m));
362            }
363
364            assert(o_mappings == r_mappings);
365        }
366    }
367
368    /// Specification for merging two views.
369    ///
370    /// Mappings are unioned, and memory conflicts are resolved by
371    /// [`vstd::map::Map::union_prefer_right`] with `other` taking precedence.
372    pub open spec fn join(self, other: MemView) -> MemView {
373        MemView {
374            mappings: self.mappings.union(other.mappings),
375            memory: self.memory.union_prefer_right(other.memory),
376        }
377    }
378
379    /// Tracked wrapper of [`Self::join`].
380    ///
381    /// # Verified Properties
382    ///
383    /// ## Preconditions
384    /// - `old(self).mappings.disjoint(other.mappings)`.
385    ///
386    /// ## Postconditions
387    /// - `*self == old(self).join(other)`.
388    #[verifier::external_body]
389    pub proof fn tracked_join(tracked &mut self, tracked other: Self)
390        requires
391            old(self).mappings.disjoint(other.mappings),
392        ensures
393            *final(self) == old(self).join(other),
394    {
395        unimplemented!()
396    }
397
398    /// Lemma: splitting and then joining reconstructs the original view.
399    ///
400    /// # Verified Properties
401    ///
402    /// ## Preconditions
403    /// - `this.split(vaddr, len) == (lhs, rhs)`.
404    /// - Every mapping in `this` starts at or after `vaddr`.
405    /// - Every physical frame tracked by `this.memory` is reachable from some
406    ///   virtual address at or after `vaddr`.
407    ///
408    /// ## Postconditions
409    /// - `lhs.join(rhs)` has the same mappings and memory contents as `this`.
410    pub proof fn lemma_split_join_identity(
411        this: MemView,
412        lhs: MemView,
413        rhs: MemView,
414        vaddr: usize,
415        len: usize,
416    )
417        requires
418            this.split(vaddr, len) == (lhs, rhs),
419            forall|m: Mapping| #[trigger]
420                this.mappings.contains(m) ==> vaddr <= m.va_range.start < m.va_range.end,
421            forall|pa: Paddr| #[trigger]
422                this.memory.contains_key(pa) ==> exists|va: usize|
423                    vaddr <= va && #[trigger] this.is_mapped(va, pa),
424        ensures
425            this.mappings == lhs.join(rhs).mappings,
426            this.memory == lhs.join(rhs).memory,
427    {
428    }
429
430    /// Lemma: byte-level reads agree on the right half of a split.
431    ///
432    /// For any `va >= vaddr + len` whose translated frame is present in
433    /// `original.memory`, the stored frame contents at `va` match the
434    /// original view. This is the byte-level analogue of
435    /// [`Self::lemma_split_preserves_transl`] and supports propagating
436    /// the read view across [`VmIoOwner::advance`] calls (used by loops
437    /// over `read_once`).
438    pub proof fn lemma_split_preserves_read(
439        original: MemView,
440        vaddr: usize,
441        len: usize,
442        left: MemView,
443        right: MemView,
444    )
445        requires
446            original.split(vaddr, len) == (left, right),
447        ensures
448            forall|va: usize|
449                #![trigger original.read(va)]
450                va >= vaddr + len && original.addr_transl(va) is Some
451                    && original.memory.contains_key((original.addr_transl(va)->0).0)
452                    ==> original.read(va) == right.read(va),
453    {
454        Self::lemma_split_preserves_transl(original, vaddr, len, left, right);
455        let split_end = vaddr + len;
456        assert forall|va: usize|
457            va >= split_end && original.addr_transl(va) is Some && original.memory.contains_key(
458                original.addr_transl(va).unwrap().0,
459            ) implies #[trigger] original.read(va) == right.read(va) by {
460            let pa = original.addr_transl(va).unwrap().0;
461            assert(original.is_mapped(va, pa));
462
463        }
464    }
465
466    /// Lemma: pointwise [`Self::read`] equality implies [`Self::read_bytes`] equality.
467    pub proof fn lemma_read_bytes_eq_pointwise(a: MemView, b: MemView, va: usize, n: usize)
468        requires
469            forall|i: usize| va <= i < va + n ==> a.read(i) == b.read(i),
470            va + n <= usize::MAX,
471        ensures
472            a.read_bytes(va, n) == b.read_bytes(va, n),
473        decreases n,
474    {
475        if n == 0 {
476            return;
477        }
478        Self::lemma_read_bytes_eq_pointwise(a, b, (va + 1) as usize, (n - 1) as usize);
479    }
480}
481
482impl Inv for VirtPtr {
483    open spec fn inv(self) -> bool {
484        &&& self.range@.start <= self.vaddr <= self.range@.end
485        &&& self.range@.end >= self.range@.start
486    }
487}
488
489impl Clone for VirtPtr {
490    fn clone(&self) -> (res: Self)
491        ensures
492            res == self,
493    {
494        Self { vaddr: self.vaddr, range: Ghost(self.range@) }
495    }
496}
497
498impl Copy for VirtPtr {
499
500}
501
502impl VirtPtr {
503    /// Creates a pointer at `vaddr` with logical range `[vaddr, vaddr + len)`.
504    #[vstd::contrib::auto_spec]
505    pub fn new(vaddr: Vaddr, len: usize) -> Self {
506        Self { vaddr, range: Ghost(Range { start: vaddr, end: (vaddr + len) as usize }) }
507    }
508
509    /// Whether the pointer has consistent bounds.
510    pub open spec fn is_defined(self) -> bool {
511        self.range@.start <= self.vaddr <= self.range@.end
512    }
513
514    /// Whether dereferencing the current pointer position is allowed.
515    ///
516    /// This excludes the one-past-end position (`vaddr == range.end`).
517    pub open spec fn is_valid(self) -> bool {
518        &&& self.is_defined()
519        &&& self.vaddr < self.range@.end
520    }
521
522    /// Reads one byte from `self.vaddr` in the provided memory view.
523    ///
524    /// # Verified Properties
525    ///
526    /// ## Preconditions
527    /// - `mem.addr_transl(self.vaddr) is Some`.
528    /// - Target byte is initialized:
529    ///   `mem.memory[mem.addr_transl(self.vaddr).unwrap().0].contents[mem.addr_transl(self.vaddr).unwrap().1 as int] is Init`.
530    /// - `self.is_valid()`.
531    ///
532    /// ## Postconditions
533    /// - Returns `mem.read(self.vaddr).value()`.
534    #[verifier::external_body]
535    pub fn read(self, Tracked(mem): Tracked<&MemView>) -> u8
536        requires
537            mem.addr_transl(self.vaddr) is Some,
538            mem.memory[(mem.addr_transl(self.vaddr)->0).0].contents[(mem.addr_transl(
539                self.vaddr,
540            )->0).1 as int] is Init,
541            self.is_valid(),
542        returns
543            mem.read(self.vaddr).value(),
544    {
545        unimplemented!()
546    }
547
548    /// Writes one byte to `self.vaddr` in the provided memory view.
549    ///
550    /// # Verified Properties
551    ///
552    /// ## Preconditions
553    /// - `old(mem).addr_transl(self.vaddr) is Some`.
554    /// - `self.is_valid()`.
555    ///
556    /// ## Postconditions
557    /// - `*mem == old(mem).write(self.vaddr, x)`.
558    #[verifier::external_body]
559    pub fn write(self, Tracked(mem): Tracked<&mut MemView>, x: u8)
560        requires
561            old(mem).addr_transl(self.vaddr) is Some,
562            self.is_valid(),
563        ensures
564            *final(mem) == old(mem).write(self.vaddr, x),
565    {
566        unimplemented!()
567    }
568
569    /// Reads a [`PodOnce`] value using one volatile memory load from a verified memory view.
570    ///
571    /// # Verified Properties
572    ///
573    /// ## Preconditions
574    /// - `self.inv()`.
575    /// - The readable range `[self.vaddr, self.vaddr + size_of::<T>())` must fit in `self.range@`.
576    /// - `self.vaddr` must satisfy the alignment requirement of `T`.
577    /// - Every byte in that range must translate in `mem` and be initialized.
578    ///
579    /// ## Safety
580    /// - This function is trusted because the underlying volatile typed read relies on raw-pointer
581    ///   operations that Verus does not yet model directly.
582    #[verifier::external_body]
583    pub fn read_volatile<T: PodOnce>(self, Tracked(mem): Tracked<&MemView>) -> (val: T)
584        requires
585            self.inv(),
586            core::mem::size_of::<T>() <= self.range@.end - self.vaddr,
587            self.vaddr % core::mem::align_of::<T>() == 0,
588            forall|i: usize|
589                #![trigger mem.addr_transl(i)]
590                self.vaddr <= i < self.vaddr + core::mem::size_of::<T>() ==> {
591                    &&& mem.addr_transl(i) is Some
592                    &&& mem.memory.contains_key((mem.addr_transl(i)->0).0)
593                    &&& mem.memory[(mem.addr_transl(i)->0).0].contents[(mem.addr_transl(
594                        i,
595                    )->0).1 as int] is Init
596                },
597        ensures
598            pod_bytes(val) == mem.read_bytes(self.vaddr, core::mem::size_of::<T>()),
599    {
600        let pnt = self.vaddr as *const T;
601        unsafe { pnt.read_volatile() }
602    }
603
604    /// Writes a [`Pod`] value of size `size_of::<T>()` bytes using a volatile memory store
605    /// to a verified memory view.
606    ///
607    /// For `T: PodOnce` the underlying store is non-tearing (single instruction). For
608    /// arbitrary `T: Pod` the store may tear across multiple instructions but completes
609    /// before this function returns.
610    ///
611    /// # Verified Properties
612    ///
613    /// ## Preconditions
614    /// - `self.inv()`.
615    /// - The writable range `[self.vaddr, self.vaddr + size_of::<T>())` must fit in `self.range@`.
616    /// - `self.vaddr` must satisfy the alignment requirement of `T`.
617    /// - Every byte in that range must translate in `mem`.
618    ///
619    /// ## Postconditions
620    /// - The final memory equals `old(mem).write_bytes(self.vaddr, pod_bytes(val))`.
621    /// - `mem.mappings` is unchanged (derivable from `write_bytes`, but stated
622    ///   directly for caller convenience).
623    /// - `mem.memory.dom()` can only grow (same).
624    /// - `addr_transl` is preserved for every virtual address (same).
625    ///
626    /// ## Safety
627    /// - This function is trusted because the underlying volatile typed write relies on raw-pointer
628    ///   operations that Verus does not yet model directly.
629    #[verifier::external_body]
630    pub fn write_volatile<T: Pod>(self, Tracked(mem): Tracked<&mut MemView>, val: T)
631        requires
632            self.inv(),
633            core::mem::size_of::<T>() <= self.range@.end - self.vaddr,
634            self.vaddr % core::mem::align_of::<T>() == 0,
635            forall|i: usize|
636                #![trigger old(mem).addr_transl(i)]
637                self.vaddr <= i < self.vaddr + core::mem::size_of::<T>() ==> {
638                    &&& old(mem).addr_transl(i) is Some
639                },
640        ensures
641            *final(mem) == old(mem).write_bytes(self.vaddr, pod_bytes(val)),
642            final(mem).mappings == old(mem).mappings,
643            old(mem).memory.dom().subset_of(final(mem).memory.dom()),
644            forall|va: usize|
645                #![trigger final(mem).addr_transl(va)]
646                old(mem).addr_transl(va) == final(mem).addr_transl(va),
647    {
648        let pnt = self.vaddr as *mut T;
649        unsafe { pnt.write_volatile(val) }
650    }
651
652    pub open spec fn add_spec(self, n: usize) -> Self {
653        VirtPtr { vaddr: (self.vaddr + n) as usize, range: self.range }
654    }
655
656    /// Advances the pointer by `n` bytes.
657    ///
658    /// This operation only updates the cursor; it does not perform memory access.
659    ///
660    /// # Verified Properties
661    ///
662    /// ## Preconditions
663    /// - `0 <= old(self).vaddr + n < usize::MAX`.
664    ///
665    /// ## Postconditions
666    /// - `*self == old(self).add_spec(n)`.
667    pub fn add(&mut self, n: usize)
668        requires
669    // Option 1: strict C standard compliance
670    // old(self).range@.start <= self.vaddr + n <= old(self).range@.end,
671    // Option 2: just make sure it doesn't overflow
672
673            0 <= old(self).vaddr + n < usize::MAX,
674        ensures
675            *final(self) == old(self).add_spec(
676                n,
677            ),
678    // If we take option 1, we can also ensure:
679    // self.is_defined()
680
681    {
682        self.vaddr = self.vaddr + n
683    }
684
685    pub open spec fn sub_spec(self, n: usize) -> Self {
686        VirtPtr { vaddr: (self.vaddr - n) as usize, range: self.range }
687    }
688
689    /// Returns a new pointer whose address is `self.vaddr` rewound by `n` bytes.
690    ///
691    /// Mirrors the shape of [`<*const T>::sub`] / [`<*mut T>::sub`] in the raw-pointer
692    /// API: takes `self` by value and returns the new pointer.
693    ///
694    /// # Verified Properties
695    ///
696    /// ## Preconditions
697    /// - `n <= self.vaddr` (no underflow).
698    ///
699    /// ## Postconditions
700    /// - `res == self.sub_spec(n)`.
701    pub fn sub(self, n: usize) -> (res: Self)
702        requires
703            n <= self.vaddr,
704        returns
705            self.sub_spec(n),
706    {
707        VirtPtr { vaddr: self.vaddr - n, range: self.range }
708    }
709
710    pub open spec fn wrapping_add_spec(self, n: usize) -> Self {
711        VirtPtr { vaddr: self.vaddr.wrapping_add(n), range: self.range }
712    }
713
714    /// Returns a new pointer whose address is `self.vaddr` advanced by `n`
715    /// bytes using wrapping arithmetic.
716    pub fn wrapping_add(self, n: usize) -> (res: Self)
717        returns
718            self.wrapping_add_spec(n),
719    {
720        VirtPtr { vaddr: self.vaddr.wrapping_add(n), range: self.range }
721    }
722
723    /// Reinterprets `self.vaddr` as a typed raw pointer to `T`.
724    ///
725    /// This is purely a type cast; no memory access is performed and no
726    /// alignment is checked. The returned pointer carries no provenance — it
727    /// is suitable for runtime checks (e.g., `is_aligned`) and for unsafe
728    /// volatile reads/writes whose preconditions are independently verified.
729    #[verifier::external_body]
730    pub fn cast<T>(self) -> (res: *mut T)
731        ensures
732            res as usize == self.vaddr,
733    {
734        self.vaddr as *mut T
735    }
736
737    pub open spec fn read_offset_spec(self, mem: MemView, n: usize) -> u8 {
738        mem.read((self.vaddr + n) as usize).value()
739    }
740
741    /// Reads from `self.vaddr + n` without mutating `self`.
742    ///
743    /// Implemented by creating a temporary pointer and calling [`Self::read`].
744    /// When `self.vaddr == self.range.start`, this behaves like array indexing.
745    ///
746    /// # Verified Properties
747    ///
748    /// ## Preconditions
749    /// - `self.vaddr + n < usize::MAX`.
750    /// - `self.range@.start <= self.vaddr + n < self.range@.end`.
751    /// - `mem.addr_transl((self.vaddr + n) as usize) is Some`.
752    /// - Target byte at offset is initialized.
753    ///
754    /// ## Postconditions
755    /// - Returns `self.read_offset_spec(*mem, n)`.
756    pub fn read_offset(&self, Tracked(mem): Tracked<&MemView>, n: usize) -> u8
757        requires
758            self.vaddr + n < usize::MAX,
759            self.range@.start <= self.vaddr + n < self.range@.end,
760            mem.addr_transl((self.vaddr + n) as usize) is Some,
761            mem.memory[(mem.addr_transl((self.vaddr + n) as usize)->0).0].contents[(mem.addr_transl(
762                (self.vaddr + n) as usize,
763            )->0).1 as int] is Init,
764        returns
765            self.read_offset_spec(*mem, n),
766    {
767        let mut tmp = self.clone();
768        tmp.add(n);
769        tmp.read(Tracked(mem))
770    }
771
772    pub open spec fn write_offset_spec(self, mem: MemView, n: usize, x: u8) -> MemView {
773        mem.write((self.vaddr + n) as usize, x)
774    }
775
776    /// Writes to `self.vaddr + n` without mutating `self`.
777    ///
778    /// # Verified Properties
779    ///
780    /// ## Preconditions
781    /// - `self.inv()`.
782    /// - `self.range@.start <= self.vaddr + n < self.range@.end`.
783    /// - `old(mem).addr_transl((self.vaddr + n) as usize) is Some`.
784    pub fn write_offset(&self, Tracked(mem): Tracked<&mut MemView>, n: usize, x: u8)
785        requires
786            self.inv(),
787            self.vaddr + n < usize::MAX,
788            self.range@.start <= self.vaddr + n < self.range@.end,
789            old(mem).addr_transl((self.vaddr + n) as usize) is Some,
790        ensures
791            *final(mem) == self.write_offset_spec(*old(mem), n, x),
792    {
793        let mut tmp = self.clone();
794        tmp.add(n);
795        tmp.write(Tracked(mem), x)
796    }
797
798    pub open spec fn copy_offset_spec(
799        src: Self,
800        dst: Self,
801        mem_src: MemView,
802        mem_dst: MemView,
803        n: usize,
804    ) -> MemView {
805        let x = src.read_offset_spec(mem_src, n);
806        dst.write_offset_spec(mem_dst, n, x)
807    }
808
809    /// Copies one byte from `src + n` to `dst + n`.
810    ///
811    /// Source and destination are reasoned about via distinct memory views.
812    ///
813    /// # Verified Properties
814    ///
815    /// ## Preconditions
816    /// - `src.inv()` and `dst.inv()`.
817    /// - Source offset and destination offset are both in-range.
818    /// - Source offset is mapped and initialized in `mem_src`.
819    /// - Destination offset is mapped in `old(mem_dst)`.
820    ///
821    /// ## Postconditions
822    /// - `*mem_dst == Self::copy_offset_spec(*src, *dst, *mem_src, *old(mem_dst), n)`.
823    /// - `mem_dst.mappings == old(mem_dst).mappings`.
824    /// - `mem_dst.memory.dom() == old(mem_dst).memory.dom()`.
825    pub fn copy_offset(
826        src: &Self,
827        dst: &Self,
828        Tracked(mem_src): Tracked<&MemView>,
829        Tracked(mem_dst): Tracked<&mut MemView>,
830        n: usize,
831    )
832        requires
833            src.inv(),
834            dst.inv(),
835            src.vaddr + n < usize::MAX,
836            dst.vaddr + n < usize::MAX,
837            src.range@.start <= src.vaddr + n < src.range@.end,
838            mem_src.addr_transl((src.vaddr + n) as usize) is Some,
839            mem_src.memory.contains_key((mem_src.addr_transl((src.vaddr + n) as usize)->0).0),
840            mem_src.memory[(mem_src.addr_transl((src.vaddr + n) as usize)->0).0].contents[(
841            mem_src.addr_transl((src.vaddr + n) as usize)->0).1 as int] is Init,
842            dst.range@.start <= dst.vaddr + n < dst.range@.end,
843            old(mem_dst).addr_transl((dst.vaddr + n) as usize) is Some,
844        ensures
845            *final(mem_dst) == Self::copy_offset_spec(*src, *dst, *mem_src, *old(mem_dst), n),
846            final(mem_dst).mappings == old(mem_dst).mappings,
847            old(mem_dst).memory.dom().subset_of(final(mem_dst).memory.dom()),
848    {
849        let x = src.read_offset(Tracked(mem_src), n);
850        dst.write_offset(Tracked(mem_dst), n, x)
851    }
852
853    pub open spec fn memcpy_spec(
854        src: Self,
855        dst: Self,
856        mem_src: MemView,
857        mem_dst: MemView,
858        n: usize,
859    ) -> MemView
860        decreases n,
861    {
862        if n == 0 {
863            mem_dst
864        } else {
865            let mem_dst_1 = Self::copy_offset_spec(src, dst, mem_src, mem_dst, (n - 1) as usize);
866
867            Self::memcpy_spec(src, dst, mem_src, mem_dst_1, (n - 1) as usize)
868        }
869    }
870
871    /// Copies `n` bytes from `src` to `dst` in the given memory view.
872    ///
873    /// `mem_src` and `mem_dst` are independent tracked memory views — the verifier
874    /// enforces this by virtue of the `&MemView` / `&mut MemView` ownership pattern,
875    /// so no vaddr-level non-overlap precondition is needed. (`mem_src` is never
876    /// mutated during the copy; each byte is read from its original state.)
877    ///
878    /// # Verified Properties
879    ///
880    /// ## Preconditions
881    /// - `src.inv()` and `dst.inv()`.
882    /// - Source slice `[src.vaddr, src.vaddr + n)` is in-range and initialized in `mem_src`.
883    /// - Destination slice `[dst.vaddr, dst.vaddr + n)` is in-range and mapped in `old(mem_dst)`.
884    ///
885    /// ## Postconditions
886    /// - `*mem_dst == Self::memcpy_spec(*src, *dst, *mem_src, *old(mem_dst), n)`.
887    /// - `mem_dst.mappings == old(mem_dst).mappings`.
888    /// - `mem_dst.memory.dom() == old(mem_dst).memory.dom()`.
889    /// - Destination slice remains mapped in `mem_dst`.
890    pub fn copy_nonoverlapping(
891        src: &Self,
892        dst: &Self,
893        Tracked(mem_src): Tracked<&MemView>,
894        Tracked(mem_dst): Tracked<&mut MemView>,
895        n: usize,
896    )
897        requires
898            src.inv(),
899            dst.inv(),
900            src.range@.start <= src.vaddr,
901            src.vaddr + n <= src.range@.end,
902            forall|i: usize|
903                #![trigger mem_src.addr_transl(i)]
904                src.vaddr <= i < src.vaddr + n ==> {
905                    &&& mem_src.addr_transl(i) is Some
906                    &&& mem_src.memory.contains_key((mem_src.addr_transl(i)->0).0)
907                    &&& mem_src.memory[(mem_src.addr_transl(i)->0).0].contents[(mem_src.addr_transl(
908                        i,
909                    )->0).1 as int] is Init
910                },
911            dst.range@.start <= dst.vaddr,
912            dst.vaddr + n <= dst.range@.end,
913            forall|i: usize|
914                dst.vaddr <= i < dst.vaddr + n ==> {
915                    &&& old(mem_dst).addr_transl(i) is Some
916                },
917        ensures
918            *final(mem_dst) == Self::memcpy_spec(*src, *dst, *mem_src, *old(mem_dst), n),
919            final(mem_dst).mappings == old(mem_dst).mappings,
920            // dom() can only grow (writes may insert new pa keys).
921            old(mem_dst).memory.dom().subset_of(final(mem_dst).memory.dom()),
922            forall|i: usize|
923                #![trigger final(mem_dst).addr_transl(i)]
924                dst.vaddr <= i < dst.vaddr + n ==> {
925                    &&& final(mem_dst).addr_transl(i) is Some
926                },
927        decreases n,
928    {
929        if n == 0 {
930            return;
931        } else {
932            let ghost mem0 = *mem_dst;
933
934            Self::copy_offset(src, dst, Tracked(mem_src), Tracked(mem_dst), n - 1);
935
936            proof {
937                assert(forall|i: usize|
938                    dst.vaddr <= i < dst.vaddr + n - 1 ==> mem_dst.addr_transl(i)
939                        == #[trigger] mem0.addr_transl(i));
940                assert forall|i: usize|
941                    dst.vaddr <= i < dst.vaddr + n - 1 implies mem_dst.addr_transl(i) is Some by {
942                    assert(mem_dst.addr_transl(i) == mem0.addr_transl(i));
943                }
944            }
945
946            Self::copy_nonoverlapping(src, dst, Tracked(mem_src), Tracked(mem_dst), n - 1);
947        }
948    }
949
950    /// Builds a valid [`VirtPtr`] from a concrete virtual-address range.
951    ///
952    /// # Verified Properties
953    ///
954    /// ## Preconditions
955    /// - `len <= usize::MAX - vaddr`.
956    ///
957    /// ## Postconditions
958    /// - `r.range@.start == vaddr`.
959    /// - `r.range@.end == (vaddr + len) as usize`.
960    /// - When `len > 0`, the result is valid for dereferencing.
961    pub fn from_vaddr(vaddr: usize, len: usize) -> (r: Self)
962        requires
963            len <= usize::MAX - vaddr,
964        ensures
965            r.inv(),
966            r.vaddr == vaddr,
967            r.range@.start == vaddr,
968            r.range@.end == (vaddr + len) as usize,
969            len > 0 ==> r.is_valid(),
970    {
971        Self { vaddr, range: Ghost(Range { start: vaddr, end: (vaddr + len) as usize }) }
972    }
973
974    /// Executable helper to split the [`VirtPtr`] into two contiguous ranges.
975    ///
976    /// This updates ghost ranges to match a split operation over the same region.
977    ///
978    /// # Verified Properties
979    ///
980    /// ## Preconditions
981    /// - `self.is_valid()`.
982    /// - `0 <= n <= self.range@.end - self.range@.start`.
983    /// - `self.vaddr == self.range@.start`.
984    ///
985    /// ## Postconditions
986    /// - Left pointer covers `[self.range@.start, self.range@.start + n)`.
987    /// - Right pointer covers `[self.range@.start + n, self.range@.end)`.
988    #[verus_spec(r =>
989        requires
990            self.is_valid(),
991            0 <= n <= self.range@.end - self.range@.start,
992            self.vaddr == self.range@.start,
993        ensures
994            r.0.range@.start == self.range@.start,
995            r.0.range@.end == self.range@.start + n,
996            r.0.vaddr == self.range@.start,
997            r.1.range@.start == self.range@.start + n,
998            r.1.range@.end == self.range@.end,
999            r.1.vaddr == self.range@.start + n,
1000    )]
1001    pub fn split(self, n: usize) -> (Self, Self) {
1002        let left = VirtPtr {
1003            vaddr: self.vaddr,
1004            range: Ghost(Range { start: self.vaddr, end: (self.vaddr + n) as usize }),
1005        };
1006
1007        let right = VirtPtr {
1008            vaddr: self.vaddr + n,
1009            range: Ghost(Range { start: (self.vaddr + n) as usize, end: self.range@.end }),
1010        };
1011
1012        (left, right)
1013    }
1014
1015    pub fn addr(&self) -> Vaddr
1016        returns
1017            self.vaddr,
1018    {
1019        self.vaddr
1020    }
1021}
1022
1023/// A [`GlobalMemView`] is a more abstract view of memory that elides most of the details. The API specifications
1024/// of higher-level objects like [`VmSpaceOwner`](crate::specs::mm::vm_space::VmSpaceOwner) use this view.
1025pub tracked struct GlobalMemView {
1026    pub ghost pt_mappings: Set<Mapping>,
1027    pub ghost tlb_mappings: Set<Mapping>,
1028    pub ghost unmapped_pas: Set<Paddr>,
1029    pub memory: Map<Paddr, FrameContents>,
1030}
1031
1032impl Inv for GlobalMemView {
1033    open spec fn inv(self) -> bool {
1034        &&& forall|m: Mapping|
1035            #![trigger self.pt_mappings.contains(m)]
1036            self.pt_mappings.contains(m) ==> {
1037                &&& m.inv()
1038                &&& forall|pa: Paddr|
1039                    m.pa_range.start <= pa < m.pa_range.end ==> {
1040                        &&& self.memory.dom().contains(pa)
1041                    }
1042                &&& self.memory.contains_key(m.pa_range.start)
1043                &&& self.memory[m.pa_range.start].size == m.page_size
1044                &&& self.memory[m.pa_range.start].inv()
1045            }
1046        &&& forall|m: Mapping|
1047            #![auto]
1048            self.tlb_mappings.contains(m) ==> self.pt_mappings.contains(m)
1049        &&& forall|m: Mapping|
1050            forall|n: Mapping|
1051                #![auto]
1052                self.pt_mappings.contains(m) ==> self.pt_mappings.contains(n) ==> m != n
1053                    ==> #[trigger] m.va_range.end <= n.va_range.start || n.va_range.end
1054                    <= m.va_range.start
1055        &&& forall|m1: Mapping, m2: Mapping|
1056            #![trigger self.pt_mappings.contains(m1), self.pt_mappings.contains(m2)]
1057            self.pt_mappings.contains(m1) && self.pt_mappings.contains(m2) && m1 != m2
1058                ==> m1.pa_range.end <= m2.pa_range.start || m2.pa_range.end <= m1.pa_range.start
1059        &&& forall|m: Mapping|
1060            forall|n: Mapping|
1061                #![auto]
1062                self.tlb_mappings.contains(m) ==> self.tlb_mappings.contains(n) ==> m != n
1063                    ==> #[trigger] m.va_range.end <= n.va_range.start || n.va_range.end
1064                    <= m.va_range.start
1065        &&& self.all_pas_accounted_for()
1066        &&& self.pas_uniquely_mapped()
1067        &&& self.unmapped_correct()
1068    }
1069}
1070
1071impl GlobalMemView {
1072    pub open spec fn addr_transl(self, va: usize) -> Option<(usize, usize)> {
1073        let mappings = self.tlb_mappings.filter(
1074            |m: Mapping| m.va_range.start <= va < m.va_range.end,
1075        );
1076        if 0 < mappings.len() {
1077            let m = mappings.choose();  // In a well-formed TLB there will only be one, but if malformed this is non-deterministic!
1078            let off = va - m.va_range.start;
1079            Some((m.pa_range.start, off as usize))
1080        } else {
1081            None
1082        }
1083    }
1084
1085    pub open spec fn is_mapped(self, pa: usize) -> bool {
1086        exists|m: Mapping| self.pt_mappings.contains(m) && m.pa_range.start <= pa < m.pa_range.end
1087    }
1088
1089    pub open spec fn all_pas_accounted_for(self) -> bool {
1090        forall|pa: Paddr|
1091            0 <= pa < MAX_PADDR ==> #[trigger] self.is_mapped(pa)
1092                || #[trigger] self.unmapped_pas.contains(pa)
1093    }
1094
1095    pub open spec fn pas_uniquely_mapped(self) -> bool {
1096        forall|m1: Mapping, m2: Mapping|
1097            #![trigger self.tlb_mappings.contains(m1), self.tlb_mappings.contains(m2)]
1098            self.tlb_mappings.contains(m1) && self.tlb_mappings.contains(m2) && m1 != m2
1099                ==> m1.pa_range.end <= m2.pa_range.start || m2.pa_range.end <= m1.pa_range.start
1100    }
1101
1102    pub open spec fn unmapped_correct(self) -> bool {
1103        forall|pa: Paddr| #![auto] self.is_mapped(pa) <==> !self.unmapped_pas.contains(pa)
1104    }
1105
1106    pub open spec fn take_view(self, vaddr: usize, len: usize) -> (Self, MemView) {
1107        let range_end = vaddr + len;
1108
1109        let leave_mappings: Set<Mapping> = self.tlb_mappings.filter(
1110            |m: Mapping| m.va_range.end <= vaddr || m.va_range.start > range_end,
1111        );
1112
1113        let take_mappings = self.tlb_mappings.filter(
1114            |m: Mapping| m.va_range.start < range_end && m.va_range.end > vaddr,
1115        );
1116
1117        let leave_pas = self.memory.dom().filter(
1118            |pa: usize|
1119                exists|m: Mapping|
1120                    leave_mappings.contains(m) && m.pa_range.start <= pa < m.pa_range.end,
1121        );
1122        let take_pas = self.memory.dom().filter(
1123            |pa: usize|
1124                exists|m: Mapping|
1125                    take_mappings.contains(m) && m.pa_range.start <= pa < m.pa_range.end,
1126        );
1127
1128        (
1129            GlobalMemView {
1130                tlb_mappings: leave_mappings,
1131                memory: self.memory.restrict(leave_pas),
1132                ..self
1133            },
1134            MemView { mappings: take_mappings, memory: self.memory.restrict(take_pas) },
1135        )
1136    }
1137
1138    pub proof fn tracked_take_view(tracked &mut self, vaddr: usize, len: usize) -> (tracked view:
1139        MemView)
1140        requires
1141            old(self).inv(),
1142        ensures
1143            *final(self) == old(self).take_view(vaddr, len).0,
1144            view == old(self).take_view(vaddr, len).1,
1145    {
1146        let ghost taken = old(self).take_view(vaddr, len);
1147        let ghost non_leave_pas = old(self).memory.dom().difference(taken.0.memory.dom());
1148        let tracked mut non_leave_memory = self.memory.tracked_remove_keys(non_leave_pas);
1149        assert(self.memory == taken.0.memory);
1150        let tracked view_memory = non_leave_memory.tracked_remove_keys(taken.1.memory.dom());
1151        assert(view_memory == taken.1.memory);
1152        self.tlb_mappings = taken.0.tlb_mappings;
1153        MemView { mappings: taken.1.mappings, memory: view_memory }
1154    }
1155
1156    pub open spec fn return_view(self, view: MemView) -> Self {
1157        GlobalMemView {
1158            tlb_mappings: self.tlb_mappings.union(view.mappings),
1159            memory: self.memory.union_prefer_right(view.memory),
1160            ..self
1161        }
1162    }
1163
1164    pub proof fn tracked_return_view(tracked &mut self, tracked view: MemView)
1165        ensures
1166            *final(self) == old(self).return_view(view),
1167    {
1168        self.tlb_mappings = self.tlb_mappings.union(view.mappings);
1169        self.memory.tracked_union_prefer_right(view.memory);
1170    }
1171
1172    pub open spec fn tlb_flush_vaddr(self, vaddr: Vaddr) -> Self {
1173        let tlb_mappings = self.tlb_mappings.filter(
1174            |m: Mapping| m.va_range.end <= vaddr || vaddr < m.va_range.start,
1175        );
1176        GlobalMemView { tlb_mappings, ..self }
1177    }
1178
1179    pub proof fn tracked_tlb_flush_vaddr(tracked &mut self, vaddr: Vaddr)
1180        requires
1181            old(self).inv(),
1182        ensures
1183            *final(self) == old(self).tlb_flush_vaddr(vaddr),
1184            final(self).inv(),
1185    {
1186        self.tlb_mappings = old(self).tlb_flush_vaddr(vaddr).tlb_mappings;
1187
1188        assert(self.pt_mappings == old(self).pt_mappings);
1189        assert(forall|pa: Paddr| #[trigger] self.is_mapped(pa) == old(self).is_mapped(pa));
1190    }
1191
1192    pub open spec fn tlb_soft_fault(self, vaddr: Vaddr) -> Self {
1193        let mapping = self.pt_mappings.filter(
1194            |m: Mapping| m.va_range.start <= vaddr < m.va_range.end,
1195        ).choose();
1196        GlobalMemView { tlb_mappings: self.tlb_mappings.insert(mapping), ..self }
1197    }
1198
1199    pub proof fn tracked_tlb_soft_fault(tracked &mut self, vaddr: Vaddr)
1200        requires
1201            old(self).inv(),
1202            old(self).addr_transl(vaddr) is None,
1203            exists|m: Mapping|
1204                old(self).pt_mappings.contains(m) && m.va_range.start <= vaddr < m.va_range.end,
1205        ensures
1206            *final(self) == old(self).tlb_soft_fault(vaddr),
1207            final(self).inv(),
1208    {
1209        let ghost pt_candidates = old(self).pt_mappings.filter(
1210            |m: Mapping| m.va_range.start <= vaddr < m.va_range.end,
1211        );
1212        assert(pt_candidates.len() > 0) by {
1213            let m = choose|m: Mapping|
1214                old(self).pt_mappings.contains(m) && m.va_range.start <= vaddr < m.va_range.end;
1215            assert(pt_candidates.contains(m));
1216        };
1217        assert(pt_candidates.contains(pt_candidates.choose()));
1218        self.tlb_mappings = old(self).tlb_soft_fault(vaddr).tlb_mappings;
1219
1220        assert(self.pt_mappings == old(self).pt_mappings);
1221        assert(forall|pa: Paddr| #[trigger] self.is_mapped(pa) == old(self).is_mapped(pa));
1222    }
1223
1224    pub open spec fn pt_map(self, m: Mapping) -> Self {
1225        let pt_mappings = self.pt_mappings.insert(m);
1226        let unmapped_pas = self.unmapped_pas.difference(
1227            Set::<usize>::range(m.pa_range.start, m.pa_range.end),
1228        );
1229        GlobalMemView { pt_mappings, unmapped_pas, ..self }
1230    }
1231
1232    pub proof fn tracked_pt_map(tracked &mut self, m: Mapping)
1233        requires
1234            forall|pa: Paddr|
1235                m.pa_range.start <= pa < m.pa_range.end ==> old(self).unmapped_pas.contains(pa),
1236            old(self).inv(),
1237        ensures
1238            *final(self) == old(self).pt_map(m),
1239    {
1240        self.pt_mappings = self.pt_mappings.insert(m);
1241        self.unmapped_pas = self.unmapped_pas.difference(
1242            Set::<usize>::range(m.pa_range.start, m.pa_range.end),
1243        );
1244    }
1245
1246    pub open spec fn pt_unmap(self, m: Mapping) -> Self {
1247        let pt_mappings = self.pt_mappings.remove(m);
1248        let unmapped_pas = self.unmapped_pas.union(
1249            Set::<usize>::range(m.pa_range.start, m.pa_range.end),
1250        );
1251        GlobalMemView { pt_mappings, unmapped_pas, ..self }
1252    }
1253
1254    pub proof fn tracked_pt_unmap(tracked &mut self, m: Mapping)
1255        requires
1256            old(self).pt_mappings.contains(m),
1257            !old(self).tlb_mappings.contains(m),
1258            old(self).inv(),
1259        ensures
1260            *final(self) == old(self).pt_unmap(m),
1261            final(self).inv(),
1262    {
1263        self.pt_mappings = self.pt_mappings.remove(m);
1264        self.unmapped_pas = self.unmapped_pas.union(
1265            Set::<usize>::range(m.pa_range.start, m.pa_range.end),
1266        );
1267
1268        assert forall|pa: Paddr| #[trigger]
1269            self.is_mapped(pa) <==> !self.unmapped_pas.contains(pa) by {
1270            if m.pa_range.start <= pa < m.pa_range.end {
1271                if self.is_mapped(pa) {
1272                    let n = choose|n: Mapping|
1273                        self.pt_mappings.contains(n) && n.pa_range.start <= pa < n.pa_range.end;
1274                }
1275            } else if old(self).is_mapped(pa) {
1276                let n = choose|n: Mapping|
1277                    old(self).pt_mappings.contains(n) && n.pa_range.start <= pa < n.pa_range.end;
1278            }
1279        };
1280    }
1281
1282    pub proof fn lemma_va_mapping_unique(self, va: usize)
1283        requires
1284            self.inv(),
1285            // Some mapping must cover `va` for the filter to be non-empty.
1286            self.addr_transl(va) is Some,
1287        ensures
1288            self.tlb_mappings.filter(
1289                |m: Mapping| m.va_range.start <= va < m.va_range.end,
1290            ).is_singleton(),
1291    {
1292    }
1293}
1294
1295} // verus!