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