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    /// Specification for splitting this view at `split_end = vaddr + len`.
232    ///
233    /// Returns `(left, right)` where:
234    /// - `left` covers `[vaddr, split_end)`, i.e. all mappings overlapping that range.
235    /// - `right` covers addresses `>= split_end`.
236    pub open spec fn split(self, vaddr: usize, len: usize) -> (MemView, MemView) {
237        let split_end = vaddr + len;
238
239        // The left part.
240        let left_mappings = self.mappings.filter(
241            |m: Mapping| m.va_range.start < split_end && m.va_range.end > vaddr,
242        );
243        let right_mappings = self.mappings.filter(|m: Mapping| m.va_range.end > split_end);
244
245        let left_pas = self.memory.dom().filter(
246            |pa: usize| exists|va: usize| vaddr <= va < split_end && self.is_mapped(va, pa),
247        );
248        let right_pas = self.memory.dom().filter(
249            |pa: usize| exists|va: usize| va >= split_end && self.is_mapped(va, pa),
250        );
251
252        (
253            MemView { mappings: left_mappings, memory: self.memory.restrict(left_pas) },
254            MemView { mappings: right_mappings, memory: self.memory.restrict(right_pas) },
255        )
256    }
257
258    /// Tracked wrapper of [`Self::borrow_at`].
259    ///
260    /// # Verified Properties
261    ///
262    /// ## Postconditions
263    /// - `r == self.borrow_at(vaddr, len)`.
264    #[verifier::external_body]
265    pub proof fn tracked_borrow_at(tracked &self, vaddr: usize, len: usize) -> (tracked r: MemView)
266        ensures
267            r == self.borrow_at(vaddr, len),
268    {
269        unimplemented!()
270    }
271
272    /// Tracked wrapper of [`Self::split`].
273    ///
274    /// # Verified Properties
275    ///
276    /// ## Postconditions
277    /// - `r == self.split(vaddr, len)`.
278    #[verifier::external_body]
279    pub proof fn tracked_split(tracked self, vaddr: usize, len: usize) -> (tracked r: (Self, Self))
280        ensures
281            r == self.split(vaddr, len),
282    {
283        unimplemented!()
284    }
285
286    /// Lemma: [`Self::split`] preserves translation semantics on each side.
287    ///
288    /// # Verified Properties
289    ///
290    /// ## Preconditions
291    /// - `original.split(vaddr, len) == (left, right)`.
292    ///
293    /// ## Postconditions
294    /// - `right.memory.dom().subset_of(original.memory.dom())`.
295    /// - For any `va` in `[vaddr, vaddr + len)`,
296    ///   `original.addr_transl(va) == left.addr_transl(va)`.
297    /// - For any `va >= vaddr + len`,
298    ///   `original.addr_transl(va) == right.addr_transl(va)`.
299    pub proof fn lemma_split_preserves_transl(
300        original: MemView,
301        vaddr: usize,
302        len: usize,
303        left: MemView,
304        right: MemView,
305    )
306        requires
307            original.split(vaddr, len) == (left, right),
308        ensures
309            right.memory.dom().subset_of(original.memory.dom()),
310            forall|va: usize|
311                vaddr <= va < vaddr + len ==> {
312                    #[trigger] original.addr_transl(va) == left.addr_transl(va)
313                },
314            forall|va: usize|
315                va >= vaddr + len ==> {
316                    #[trigger] original.addr_transl(va) == right.addr_transl(va)
317                },
318    {
319        assert forall|va: usize| vaddr <= va < vaddr + len implies original.addr_transl(va)
320            == left.addr_transl(va) by {
321            assert(left.mappings == original.mappings.filter(
322                |m: Mapping| m.va_range.start < vaddr + len && m.va_range.end > vaddr,
323            ));
324            let o_mappings = original.mappings.filter(
325                |m: Mapping| m.va_range.start <= va < m.va_range.end,
326            );
327            let l_mappings = left.mappings.filter(
328                |m: Mapping| m.va_range.start <= va < m.va_range.end,
329            );
330
331            assert forall|m: Mapping| #[trigger] o_mappings.contains(m) implies l_mappings.contains(
332                m,
333            ) by {
334                assert(left.mappings.contains(m));
335            };
336            assert(o_mappings == l_mappings);
337        }
338
339        assert forall|va: usize| va >= vaddr + len implies original.addr_transl(va)
340            == right.addr_transl(va) by {
341            let split_end = vaddr + len;
342
343            let o_mappings = original.mappings.filter(
344                |m: Mapping| m.va_range.start <= va < m.va_range.end,
345            );
346            let r_mappings = right.mappings.filter(
347                |m: Mapping| m.va_range.start <= va < m.va_range.end,
348            );
349
350            assert forall|m: Mapping| o_mappings.contains(m) implies r_mappings.contains(m) by {
351                assert(right.mappings.contains(m));
352                assert(r_mappings.contains(m));
353            }
354
355            assert(o_mappings == r_mappings);
356        }
357    }
358
359    /// Specification for merging two views.
360    ///
361    /// Mappings are unioned, and memory conflicts are resolved by
362    /// [`vstd::map::Map::union_prefer_right`] with `other` taking precedence.
363    pub open spec fn join(self, other: MemView) -> MemView {
364        MemView {
365            mappings: self.mappings.union(other.mappings),
366            memory: self.memory.union_prefer_right(other.memory),
367        }
368    }
369
370    /// Tracked wrapper of [`Self::join`].
371    ///
372    /// # Verified Properties
373    ///
374    /// ## Preconditions
375    /// - `old(self).mappings.disjoint(other.mappings)`.
376    ///
377    /// ## Postconditions
378    /// - `*self == old(self).join(other)`.
379    pub proof fn tracked_join(tracked &mut self, tracked other: Self)
380        requires
381            old(self).mappings.disjoint(other.mappings),
382        ensures
383            *final(self) == old(self).join(other),
384    {
385        self.mappings = self.mappings.union(other.mappings);
386        self.memory.tracked_union_prefer_right(other.memory);
387    }
388
389    /// Lemma: splitting and then joining reconstructs the original view.
390    ///
391    /// # Verified Properties
392    ///
393    /// ## Preconditions
394    /// - `this.split(vaddr, len) == (lhs, rhs)`.
395    /// - Every mapping in `this` starts at or after `vaddr`.
396    /// - Every physical frame tracked by `this.memory` is reachable from some
397    ///   virtual address at or after `vaddr`.
398    ///
399    /// ## Postconditions
400    /// - `lhs.join(rhs)` has the same mappings and memory contents as `this`.
401    pub proof fn lemma_split_join_identity(
402        this: MemView,
403        lhs: MemView,
404        rhs: MemView,
405        vaddr: usize,
406        len: usize,
407    )
408        requires
409            this.split(vaddr, len) == (lhs, rhs),
410            forall|m: Mapping| #[trigger]
411                this.mappings.contains(m) ==> vaddr <= m.va_range.start < m.va_range.end,
412            forall|pa: Paddr| #[trigger]
413                this.memory.contains_key(pa) ==> exists|va: usize|
414                    vaddr <= va && #[trigger] this.is_mapped(va, pa),
415        ensures
416            this.mappings == lhs.join(rhs).mappings,
417            this.memory == lhs.join(rhs).memory,
418    {
419    }
420
421    /// Lemma: byte-level reads agree on the right half of a split.
422    ///
423    /// For any `va >= vaddr + len` whose translated frame is present in
424    /// `original.memory`, the stored frame contents at `va` match the
425    /// original view. This is the byte-level analogue of
426    /// [`Self::lemma_split_preserves_transl`] and supports propagating
427    /// the read view across [`VmIoOwner::advance`] calls (used by loops
428    /// over `read_once`).
429    pub proof fn lemma_split_preserves_read(
430        original: MemView,
431        vaddr: usize,
432        len: usize,
433        left: MemView,
434        right: MemView,
435    )
436        requires
437            original.split(vaddr, len) == (left, right),
438        ensures
439            forall|va: usize|
440                #![trigger original.read(va)]
441                va >= vaddr + len && original.addr_transl(va) is Some
442                    && original.memory.contains_key((original.addr_transl(va)->0).0)
443                    ==> original.read(va) == right.read(va),
444    {
445        Self::lemma_split_preserves_transl(original, vaddr, len, left, right);
446        let split_end = vaddr + len;
447        assert forall|va: usize|
448            va >= split_end && original.addr_transl(va) is Some && original.memory.contains_key(
449                original.addr_transl(va).unwrap().0,
450            ) implies #[trigger] original.read(va) == right.read(va) by {
451            let pa = original.addr_transl(va).unwrap().0;
452            assert(original.is_mapped(va, pa));
453
454        }
455    }
456
457    /// Lemma: pointwise [`Self::read`] equality implies [`Self::read_bytes`] equality.
458    pub proof fn lemma_read_bytes_eq_pointwise(a: MemView, b: MemView, va: usize, n: usize)
459        requires
460            forall|i: usize| va <= i < va + n ==> a.read(i) == b.read(i),
461            va + n <= usize::MAX,
462        ensures
463            a.read_bytes(va, n) == b.read_bytes(va, n),
464        decreases n,
465    {
466        if n == 0 {
467            return;
468        }
469        Self::lemma_read_bytes_eq_pointwise(a, b, (va + 1) as usize, (n - 1) as usize);
470    }
471}
472
473impl Inv for VirtPtr {
474    open spec fn inv(self) -> bool {
475        self.range@.start <= self.vaddr <= self.range@.end
476    }
477}
478
479impl Clone for VirtPtr {
480    fn clone(&self) -> (res: Self)
481        ensures
482            res == self,
483    {
484        Self { vaddr: self.vaddr, range: Ghost(self.range@) }
485    }
486}
487
488impl Copy for VirtPtr {
489
490}
491
492impl VirtPtr {
493    /// Creates a pointer at `vaddr` with logical range `[vaddr, vaddr + len)`.
494    #[vstd::contrib::auto_spec]
495    pub fn new(vaddr: Vaddr, len: usize) -> Self {
496        Self { vaddr, range: Ghost(Range { start: vaddr, end: (vaddr + len) as usize }) }
497    }
498
499    /// Whether the pointer has consistent bounds.
500    pub open spec fn is_defined(self) -> bool {
501        self.range@.start <= self.vaddr <= self.range@.end
502    }
503
504    /// Whether dereferencing the current pointer position is allowed.
505    ///
506    /// This excludes the one-past-end position (`vaddr == range.end`).
507    pub open spec fn is_valid(self) -> bool {
508        &&& self.is_defined()
509        &&& self.vaddr < self.range@.end
510    }
511
512    /// Reads one byte from `self.vaddr` in the provided memory view.
513    ///
514    /// # Verified Properties
515    ///
516    /// ## Preconditions
517    /// - `mem.addr_transl(self.vaddr) is Some`.
518    /// - Target byte is initialized:
519    ///   `mem.memory[mem.addr_transl(self.vaddr).unwrap().0].contents[mem.addr_transl(self.vaddr).unwrap().1 as int] is Init`.
520    /// - `self.is_valid()`.
521    ///
522    /// ## Postconditions
523    /// - Returns `mem.read(self.vaddr).value()`.
524    #[verifier::external_body]
525    pub fn read(self, Tracked(mem): Tracked<&MemView>) -> u8
526        requires
527            mem.addr_transl(self.vaddr) is Some,
528            mem.memory[(mem.addr_transl(self.vaddr)->0).0].contents[(mem.addr_transl(
529                self.vaddr,
530            )->0).1 as int] is Init,
531            self.is_valid(),
532        returns
533            mem.read(self.vaddr).value(),
534    {
535        unimplemented!()
536    }
537
538    /// Writes one byte to `self.vaddr` in the provided memory view.
539    ///
540    /// # Verified Properties
541    ///
542    /// ## Preconditions
543    /// - `old(mem).addr_transl(self.vaddr) is Some`.
544    /// - `self.is_valid()`.
545    ///
546    /// ## Postconditions
547    /// - `*mem == old(mem).write(self.vaddr, x)`.
548    #[verifier::external_body]
549    pub fn write(self, Tracked(mem): Tracked<&mut MemView>, x: u8)
550        requires
551            old(mem).addr_transl(self.vaddr) is Some,
552            self.is_valid(),
553        ensures
554            *final(mem) == old(mem).write(self.vaddr, x),
555    {
556        unimplemented!()
557    }
558
559    /// Reads a [`PodOnce`] value using one volatile memory load from a verified memory view.
560    ///
561    /// # Verified Properties
562    ///
563    /// ## Preconditions
564    /// - `self.inv()`.
565    /// - The readable range `[self.vaddr, self.vaddr + size_of::<T>())` must fit in `self.range@`.
566    /// - `self.vaddr` must satisfy the alignment requirement of `T`.
567    /// - Every byte in that range must translate in `mem` and be initialized.
568    ///
569    /// ## Safety
570    /// - This function is trusted because the underlying volatile typed read relies on raw-pointer
571    ///   operations that Verus does not yet model directly.
572    #[verifier::external_body]
573    pub fn read_volatile<T: PodOnce>(self, Tracked(mem): Tracked<&MemView>) -> (val: T)
574        requires
575            self.inv(),
576            core::mem::size_of::<T>() <= self.range@.end - self.vaddr,
577            self.vaddr % core::mem::align_of::<T>() == 0,
578            forall|i: usize|
579                #![trigger mem.addr_transl(i)]
580                self.vaddr <= i < self.vaddr + core::mem::size_of::<T>() ==> {
581                    &&& mem.addr_transl(i) is Some
582                    &&& mem.memory.contains_key((mem.addr_transl(i)->0).0)
583                    &&& mem.memory[(mem.addr_transl(i)->0).0].contents[(mem.addr_transl(
584                        i,
585                    )->0).1 as int] is Init
586                },
587        ensures
588            pod_bytes(val) == mem.read_bytes(self.vaddr, core::mem::size_of::<T>()),
589    {
590        let pnt = self.vaddr as *const T;
591        unsafe { pnt.read_volatile() }
592    }
593
594    /// Writes a [`Pod`] value of size `size_of::<T>()` bytes using a volatile memory store
595    /// to a verified memory view.
596    ///
597    /// For `T: PodOnce` the underlying store is non-tearing (single instruction). For
598    /// arbitrary `T: Pod` the store may tear across multiple instructions but completes
599    /// before this function returns.
600    ///
601    /// # Verified Properties
602    ///
603    /// ## Preconditions
604    /// - `self.inv()`.
605    /// - The writable range `[self.vaddr, self.vaddr + size_of::<T>())` must fit in `self.range@`.
606    /// - `self.vaddr` must satisfy the alignment requirement of `T`.
607    /// - Every byte in that range must translate in `mem`.
608    ///
609    /// ## Postconditions
610    /// - The final memory equals `old(mem).write_bytes(self.vaddr, pod_bytes(val))`.
611    /// - `mem.mappings` is unchanged (derivable from `write_bytes`, but stated
612    ///   directly for caller convenience).
613    /// - `mem.memory.dom()` can only grow (same).
614    /// - `addr_transl` is preserved for every virtual address (same).
615    ///
616    /// ## Safety
617    /// - This function is trusted because the underlying volatile typed write relies on raw-pointer
618    ///   operations that Verus does not yet model directly.
619    #[verifier::external_body]
620    pub fn write_volatile<T: Pod>(self, Tracked(mem): Tracked<&mut MemView>, val: T)
621        requires
622            self.inv(),
623            core::mem::size_of::<T>() <= self.range@.end - self.vaddr,
624            self.vaddr % core::mem::align_of::<T>() == 0,
625            forall|i: usize|
626                #![trigger old(mem).addr_transl(i)]
627                self.vaddr <= i < self.vaddr + core::mem::size_of::<T>() ==> {
628                    &&& old(mem).addr_transl(i) is Some
629                },
630        ensures
631            *final(mem) == old(mem).write_bytes(self.vaddr, pod_bytes(val)),
632            final(mem).mappings == old(mem).mappings,
633            old(mem).memory.dom().subset_of(final(mem).memory.dom()),
634            forall|va: usize|
635                #![trigger final(mem).addr_transl(va)]
636                old(mem).addr_transl(va) == final(mem).addr_transl(va),
637    {
638        let pnt = self.vaddr as *mut T;
639        unsafe { pnt.write_volatile(val) }
640    }
641
642    pub open spec fn add_spec(self, n: usize) -> Self {
643        VirtPtr { vaddr: (self.vaddr + n) as usize, range: self.range }
644    }
645
646    /// Advances the pointer by `n` bytes.
647    ///
648    /// This operation only updates the cursor; it does not perform memory access.
649    ///
650    /// # Verified Properties
651    ///
652    /// ## Preconditions
653    /// - `0 <= old(self).vaddr + n < usize::MAX`.
654    ///
655    /// ## Postconditions
656    /// - `*self == old(self).add_spec(n)`.
657    pub fn add(&mut self, n: usize)
658        requires
659    // Option 1: strict C standard compliance
660    // old(self).range@.start <= self.vaddr + n <= old(self).range@.end,
661    // Option 2: just make sure it doesn't overflow
662
663            0 <= old(self).vaddr + n < usize::MAX,
664        ensures
665            *final(self) == old(self).add_spec(
666                n,
667            ),
668    // If we take option 1, we can also ensure:
669    // self.is_defined()
670
671    {
672        self.vaddr = self.vaddr + n
673    }
674
675    pub open spec fn sub_spec(self, n: usize) -> Self {
676        VirtPtr { vaddr: (self.vaddr - n) as usize, range: self.range }
677    }
678
679    /// Returns a new pointer whose address is `self.vaddr` rewound by `n` bytes.
680    ///
681    /// Mirrors the shape of [`<*const T>::sub`] / [`<*mut T>::sub`] in the raw-pointer
682    /// API: takes `self` by value and returns the new pointer.
683    ///
684    /// # Verified Properties
685    ///
686    /// ## Preconditions
687    /// - `n <= self.vaddr` (no underflow).
688    ///
689    /// ## Postconditions
690    /// - `res == self.sub_spec(n)`.
691    pub fn sub(self, n: usize) -> (res: Self)
692        requires
693            n <= self.vaddr,
694        returns
695            self.sub_spec(n),
696    {
697        VirtPtr { vaddr: self.vaddr - n, range: self.range }
698    }
699
700    pub open spec fn wrapping_add_spec(self, n: usize) -> Self {
701        VirtPtr { vaddr: self.vaddr.wrapping_add(n), range: self.range }
702    }
703
704    /// Returns a new pointer whose address is `self.vaddr` advanced by `n`
705    /// bytes using wrapping arithmetic.
706    pub fn wrapping_add(self, n: usize) -> (res: Self)
707        returns
708            self.wrapping_add_spec(n),
709    {
710        VirtPtr { vaddr: self.vaddr.wrapping_add(n), range: self.range }
711    }
712
713    /// Reinterprets `self.vaddr` as a typed raw pointer to `T`.
714    ///
715    /// This is purely a type cast; no memory access is performed and no
716    /// alignment is checked. The returned pointer carries no provenance — it
717    /// is suitable for runtime checks (e.g., `is_aligned`) and for unsafe
718    /// volatile reads/writes whose preconditions are independently verified.
719    pub fn cast<T>(self) -> (res: *mut T)
720        ensures
721            res as usize == self.vaddr,
722    {
723        /* Verus rejects the `usize` to `*mut T` cast; `with_addr` builds the same address
724         * from a null pointer, which also matches the documented null provenance.
725         * Origin Rust: self.vaddr as *mut T
726         */
727        core::ptr::null_mut::<T>().with_addr(self.vaddr)
728    }
729
730    pub open spec fn read_offset_spec(self, mem: MemView, n: usize) -> u8 {
731        mem.read((self.vaddr + n) as usize).value()
732    }
733
734    /// Reads from `self.vaddr + n` without mutating `self`.
735    ///
736    /// Implemented by creating a temporary pointer and calling [`Self::read`].
737    /// When `self.vaddr == self.range.start`, this behaves like array indexing.
738    ///
739    /// # Verified Properties
740    ///
741    /// ## Preconditions
742    /// - `self.vaddr + n < usize::MAX`.
743    /// - `self.range@.start <= self.vaddr + n < self.range@.end`.
744    /// - `mem.addr_transl((self.vaddr + n) as usize) is Some`.
745    /// - Target byte at offset is initialized.
746    ///
747    /// ## Postconditions
748    /// - Returns `self.read_offset_spec(*mem, n)`.
749    pub fn read_offset(&self, Tracked(mem): Tracked<&MemView>, n: usize) -> u8
750        requires
751            self.vaddr + n < usize::MAX,
752            self.range@.start <= self.vaddr + n < self.range@.end,
753            mem.addr_transl((self.vaddr + n) as usize) is Some,
754            mem.memory[(mem.addr_transl((self.vaddr + n) as usize)->0).0].contents[(mem.addr_transl(
755                (self.vaddr + n) as usize,
756            )->0).1 as int] is Init,
757        returns
758            self.read_offset_spec(*mem, n),
759    {
760        let mut tmp = self.clone();
761        tmp.add(n);
762        tmp.read(Tracked(mem))
763    }
764
765    pub open spec fn write_offset_spec(self, mem: MemView, n: usize, x: u8) -> MemView {
766        mem.write((self.vaddr + n) as usize, x)
767    }
768
769    /// Writes to `self.vaddr + n` without mutating `self`.
770    ///
771    /// # Verified Properties
772    ///
773    /// ## Preconditions
774    /// - `self.inv()`.
775    /// - `self.range@.start <= self.vaddr + n < self.range@.end`.
776    /// - `old(mem).addr_transl((self.vaddr + n) as usize) is Some`.
777    pub fn write_offset(&self, Tracked(mem): Tracked<&mut MemView>, n: usize, x: u8)
778        requires
779            self.inv(),
780            self.vaddr + n < usize::MAX,
781            self.range@.start <= self.vaddr + n < self.range@.end,
782            old(mem).addr_transl((self.vaddr + n) as usize) is Some,
783        ensures
784            *final(mem) == self.write_offset_spec(*old(mem), n, x),
785    {
786        let mut tmp = self.clone();
787        tmp.add(n);
788        tmp.write(Tracked(mem), x)
789    }
790
791    pub open spec fn copy_offset_spec(
792        src: Self,
793        dst: Self,
794        mem_src: MemView,
795        mem_dst: MemView,
796        n: usize,
797    ) -> MemView {
798        let x = src.read_offset_spec(mem_src, n);
799        dst.write_offset_spec(mem_dst, n, x)
800    }
801
802    /// Copies one byte from `src + n` to `dst + n`.
803    ///
804    /// Source and destination are reasoned about via distinct memory views.
805    ///
806    /// # Verified Properties
807    ///
808    /// ## Preconditions
809    /// - `src.inv()` and `dst.inv()`.
810    /// - Source offset and destination offset are both in-range.
811    /// - Source offset is mapped and initialized in `mem_src`.
812    /// - Destination offset is mapped in `old(mem_dst)`.
813    ///
814    /// ## Postconditions
815    /// - `*mem_dst == Self::copy_offset_spec(*src, *dst, *mem_src, *old(mem_dst), n)`.
816    /// - `mem_dst.mappings == old(mem_dst).mappings`.
817    /// - `mem_dst.memory.dom() == old(mem_dst).memory.dom()`.
818    pub fn copy_offset(
819        src: &Self,
820        dst: &Self,
821        Tracked(mem_src): Tracked<&MemView>,
822        Tracked(mem_dst): Tracked<&mut MemView>,
823        n: usize,
824    )
825        requires
826            src.inv(),
827            dst.inv(),
828            src.vaddr + n < usize::MAX,
829            dst.vaddr + n < usize::MAX,
830            src.range@.start <= src.vaddr + n < src.range@.end,
831            mem_src.addr_transl((src.vaddr + n) as usize) is Some,
832            mem_src.memory.contains_key((mem_src.addr_transl((src.vaddr + n) as usize)->0).0),
833            mem_src.memory[(mem_src.addr_transl((src.vaddr + n) as usize)->0).0].contents[(
834            mem_src.addr_transl((src.vaddr + n) as usize)->0).1 as int] is Init,
835            dst.range@.start <= dst.vaddr + n < dst.range@.end,
836            old(mem_dst).addr_transl((dst.vaddr + n) as usize) is Some,
837        ensures
838            *final(mem_dst) == Self::copy_offset_spec(*src, *dst, *mem_src, *old(mem_dst), n),
839            final(mem_dst).mappings == old(mem_dst).mappings,
840            old(mem_dst).memory.dom().subset_of(final(mem_dst).memory.dom()),
841    {
842        let x = src.read_offset(Tracked(mem_src), n);
843        dst.write_offset(Tracked(mem_dst), n, x)
844    }
845
846    pub open spec fn memcpy_spec(
847        src: Self,
848        dst: Self,
849        mem_src: MemView,
850        mem_dst: MemView,
851        n: usize,
852    ) -> MemView
853        decreases n,
854    {
855        if n == 0 {
856            mem_dst
857        } else {
858            let mem_dst_1 = Self::copy_offset_spec(src, dst, mem_src, mem_dst, (n - 1) as usize);
859
860            Self::memcpy_spec(src, dst, mem_src, mem_dst_1, (n - 1) as usize)
861        }
862    }
863
864    /// Copies `n` bytes from `src` to `dst` in the given memory view.
865    ///
866    /// `mem_src` and `mem_dst` are independent tracked memory views — the verifier
867    /// enforces this by virtue of the `&MemView` / `&mut MemView` ownership pattern,
868    /// so no vaddr-level non-overlap precondition is needed. (`mem_src` is never
869    /// mutated during the copy; each byte is read from its original state.)
870    ///
871    /// # Verified Properties
872    ///
873    /// ## Preconditions
874    /// - `src.inv()` and `dst.inv()`.
875    /// - Source slice `[src.vaddr, src.vaddr + n)` is in-range and initialized in `mem_src`.
876    /// - Destination slice `[dst.vaddr, dst.vaddr + n)` is in-range and mapped in `old(mem_dst)`.
877    ///
878    /// ## Postconditions
879    /// - `*mem_dst == Self::memcpy_spec(*src, *dst, *mem_src, *old(mem_dst), n)`.
880    /// - `mem_dst.mappings == old(mem_dst).mappings`.
881    /// - `mem_dst.memory.dom() == old(mem_dst).memory.dom()`.
882    /// - Destination slice remains mapped in `mem_dst`.
883    pub fn copy_nonoverlapping(
884        src: &Self,
885        dst: &Self,
886        Tracked(mem_src): Tracked<&MemView>,
887        Tracked(mem_dst): Tracked<&mut MemView>,
888        n: usize,
889    )
890        requires
891            src.inv(),
892            dst.inv(),
893            src.vaddr + n <= src.range@.end,
894            forall|i: usize|
895                #![trigger mem_src.addr_transl(i)]
896                src.vaddr <= i < src.vaddr + n ==> {
897                    &&& mem_src.addr_transl(i) is Some
898                    &&& mem_src.memory.contains_key((mem_src.addr_transl(i)->0).0)
899                    &&& mem_src.memory[(mem_src.addr_transl(i)->0).0].contents[(mem_src.addr_transl(
900                        i,
901                    )->0).1 as int] is Init
902                },
903            dst.vaddr + n <= dst.range@.end,
904            forall|i: usize|
905                dst.vaddr <= i < dst.vaddr + n ==> {
906                    &&& old(mem_dst).addr_transl(i) is Some
907                },
908        ensures
909            *final(mem_dst) == Self::memcpy_spec(*src, *dst, *mem_src, *old(mem_dst), n),
910            final(mem_dst).mappings == old(mem_dst).mappings,
911            // dom() can only grow (writes may insert new pa keys).
912            old(mem_dst).memory.dom().subset_of(final(mem_dst).memory.dom()),
913            forall|i: usize|
914                #![trigger final(mem_dst).addr_transl(i)]
915                dst.vaddr <= i < dst.vaddr + n ==> {
916                    &&& final(mem_dst).addr_transl(i) is Some
917                },
918        decreases n,
919    {
920        if n == 0 {
921            return;
922        } else {
923            let ghost mem0 = *mem_dst;
924
925            Self::copy_offset(src, dst, Tracked(mem_src), Tracked(mem_dst), n - 1);
926
927            proof {
928                assert(forall|i: usize|
929                    dst.vaddr <= i < dst.vaddr + n - 1 ==> mem_dst.addr_transl(i)
930                        == #[trigger] mem0.addr_transl(i));
931                assert forall|i: usize|
932                    dst.vaddr <= i < dst.vaddr + n - 1 implies mem_dst.addr_transl(i) is Some by {
933                    assert(mem_dst.addr_transl(i) == mem0.addr_transl(i));
934                }
935            }
936
937            Self::copy_nonoverlapping(src, dst, Tracked(mem_src), Tracked(mem_dst), n - 1);
938        }
939    }
940
941    /// Builds a valid [`VirtPtr`] from a concrete virtual-address range.
942    ///
943    /// # Verified Properties
944    ///
945    /// ## Preconditions
946    /// - `len <= usize::MAX - vaddr`.
947    ///
948    /// ## Postconditions
949    /// - `r.range@.start == vaddr`.
950    /// - `r.range@.end == (vaddr + len) as usize`.
951    /// - When `len > 0`, the result is valid for dereferencing.
952    pub fn from_vaddr(vaddr: usize, len: usize) -> (r: Self)
953        requires
954            len <= usize::MAX - vaddr,
955        ensures
956            r.inv(),
957            r.vaddr == vaddr,
958            r.range@.start == vaddr,
959            r.range@.end == (vaddr + len) as usize,
960            len > 0 ==> r.is_valid(),
961    {
962        Self { vaddr, range: Ghost(Range { start: vaddr, end: (vaddr + len) as usize }) }
963    }
964
965    /// Executable helper to split the [`VirtPtr`] into two contiguous ranges.
966    ///
967    /// This updates ghost ranges to match a split operation over the same region.
968    ///
969    /// # Verified Properties
970    ///
971    /// ## Preconditions
972    /// - `self.is_valid()`.
973    /// - `0 <= n <= self.range@.end - self.range@.start`.
974    /// - `self.vaddr == self.range@.start`.
975    ///
976    /// ## Postconditions
977    /// - Left pointer covers `[self.range@.start, self.range@.start + n)`.
978    /// - Right pointer covers `[self.range@.start + n, self.range@.end)`.
979    #[verus_spec(r =>
980        requires
981            self.is_valid(),
982            0 <= n <= self.range@.end - self.range@.start,
983            self.vaddr == self.range@.start,
984        ensures
985            r.0.range@.start == self.range@.start,
986            r.0.range@.end == self.range@.start + n,
987            r.0.vaddr == self.range@.start,
988            r.1.range@.start == self.range@.start + n,
989            r.1.range@.end == self.range@.end,
990            r.1.vaddr == self.range@.start + n,
991    )]
992    pub fn split(self, n: usize) -> (Self, Self) {
993        let left = VirtPtr {
994            vaddr: self.vaddr,
995            range: Ghost(Range { start: self.vaddr, end: (self.vaddr + n) as usize }),
996        };
997
998        let right = VirtPtr {
999            vaddr: self.vaddr + n,
1000            range: Ghost(Range { start: (self.vaddr + n) as usize, end: self.range@.end }),
1001        };
1002
1003        (left, right)
1004    }
1005
1006    pub fn addr(&self) -> Vaddr
1007        returns
1008            self.vaddr,
1009    {
1010        self.vaddr
1011    }
1012}
1013
1014/// A [`GlobalMemView`] is a more abstract view of memory that elides most of the details. The API specifications
1015/// of higher-level objects like [`VmSpaceOwner`](crate::specs::mm::vm_space::VmSpaceOwner) use this view.
1016pub tracked struct GlobalMemView {
1017    pub ghost pt_mappings: Set<Mapping>,
1018    pub ghost tlb_mappings: Set<Mapping>,
1019    pub ghost unmapped_pas: Set<Paddr>,
1020    pub memory: Map<Paddr, FrameContents>,
1021}
1022
1023impl Inv for GlobalMemView {
1024    open spec fn inv(self) -> bool {
1025        &&& forall|m: Mapping|
1026            #![trigger self.pt_mappings.contains(m)]
1027            self.pt_mappings.contains(m) ==> {
1028                &&& m.inv()
1029                &&& forall|pa: Paddr|
1030                    m.pa_range.start <= pa < m.pa_range.end ==> {
1031                        &&& self.memory.dom().contains(pa)
1032                    }
1033                &&& self.memory.contains_key(m.pa_range.start)
1034                &&& self.memory[m.pa_range.start].size == m.page_size
1035                &&& self.memory[m.pa_range.start].inv()
1036            }
1037        &&& forall|m: Mapping|
1038            #![auto]
1039            self.tlb_mappings.contains(m) ==> self.pt_mappings.contains(m)
1040        &&& forall|m: Mapping|
1041            forall|n: Mapping|
1042                #![auto]
1043                self.pt_mappings.contains(m) ==> self.pt_mappings.contains(n) ==> m != n
1044                    ==> #[trigger] m.va_range.end <= n.va_range.start || n.va_range.end
1045                    <= m.va_range.start
1046        &&& forall|m1: Mapping, m2: Mapping|
1047            #![trigger self.pt_mappings.contains(m1), self.pt_mappings.contains(m2)]
1048            self.pt_mappings.contains(m1) && self.pt_mappings.contains(m2) && m1 != m2
1049                ==> m1.pa_range.end <= m2.pa_range.start || m2.pa_range.end <= m1.pa_range.start
1050        &&& self.unmapped_correct()
1051    }
1052}
1053
1054impl GlobalMemView {
1055    pub open spec fn addr_transl(self, va: usize) -> Option<(usize, usize)> {
1056        let mappings = self.tlb_mappings.filter(
1057            |m: Mapping| m.va_range.start <= va < m.va_range.end,
1058        );
1059        if 0 < mappings.len() {
1060            let m = mappings.choose();  // In a well-formed TLB there will only be one, but if malformed this is non-deterministic!
1061            let off = va - m.va_range.start;
1062            Some((m.pa_range.start, off as usize))
1063        } else {
1064            None
1065        }
1066    }
1067
1068    pub open spec fn is_mapped(self, pa: usize) -> bool {
1069        exists|m: Mapping| self.pt_mappings.contains(m) && m.pa_range.start <= pa < m.pa_range.end
1070    }
1071
1072    pub open spec fn unmapped_correct(self) -> bool {
1073        forall|pa: Paddr| #![auto] self.is_mapped(pa) <==> !self.unmapped_pas.contains(pa)
1074    }
1075
1076    pub open spec fn take_view(self, vaddr: usize, len: usize) -> (Self, MemView) {
1077        let range_end = vaddr + len;
1078
1079        let leave_mappings: Set<Mapping> = self.tlb_mappings.filter(
1080            |m: Mapping| m.va_range.end <= vaddr || m.va_range.start > range_end,
1081        );
1082
1083        let take_mappings = self.tlb_mappings.filter(
1084            |m: Mapping| m.va_range.start < range_end && m.va_range.end > vaddr,
1085        );
1086
1087        let leave_pas = self.memory.dom().filter(
1088            |pa: usize|
1089                exists|m: Mapping|
1090                    leave_mappings.contains(m) && m.pa_range.start <= pa < m.pa_range.end,
1091        );
1092        let take_pas = self.memory.dom().filter(
1093            |pa: usize|
1094                exists|m: Mapping|
1095                    take_mappings.contains(m) && m.pa_range.start <= pa < m.pa_range.end,
1096        );
1097
1098        (
1099            GlobalMemView {
1100                tlb_mappings: leave_mappings,
1101                memory: self.memory.restrict(leave_pas),
1102                ..self
1103            },
1104            MemView { mappings: take_mappings, memory: self.memory.restrict(take_pas) },
1105        )
1106    }
1107
1108    pub proof fn tracked_take_view(tracked &mut self, vaddr: usize, len: usize) -> (tracked view:
1109        MemView)
1110        requires
1111            old(self).inv(),
1112        ensures
1113            *final(self) == old(self).take_view(vaddr, len).0,
1114            view == old(self).take_view(vaddr, len).1,
1115    {
1116        let ghost taken = old(self).take_view(vaddr, len);
1117        let ghost non_leave_pas = old(self).memory.dom().difference(taken.0.memory.dom());
1118        let tracked mut non_leave_memory = self.memory.tracked_remove_keys(non_leave_pas);
1119        assert(self.memory == taken.0.memory);
1120        let tracked view_memory = non_leave_memory.tracked_remove_keys(taken.1.memory.dom());
1121        assert(view_memory == taken.1.memory);
1122        self.tlb_mappings = taken.0.tlb_mappings;
1123        MemView { mappings: taken.1.mappings, memory: view_memory }
1124    }
1125
1126    pub open spec fn return_view(self, view: MemView) -> Self {
1127        GlobalMemView {
1128            tlb_mappings: self.tlb_mappings.union(view.mappings),
1129            memory: self.memory.union_prefer_right(view.memory),
1130            ..self
1131        }
1132    }
1133
1134    pub proof fn tracked_return_view(tracked &mut self, tracked view: MemView)
1135        ensures
1136            *final(self) == old(self).return_view(view),
1137    {
1138        self.tlb_mappings = self.tlb_mappings.union(view.mappings);
1139        self.memory.tracked_union_prefer_right(view.memory);
1140    }
1141
1142    pub open spec fn tlb_flush_vaddr(self, vaddr: Vaddr) -> Self {
1143        let tlb_mappings = self.tlb_mappings.filter(
1144            |m: Mapping| m.va_range.end <= vaddr || vaddr < m.va_range.start,
1145        );
1146        GlobalMemView { tlb_mappings, ..self }
1147    }
1148
1149    pub proof fn tracked_tlb_flush_vaddr(tracked &mut self, vaddr: Vaddr)
1150        requires
1151            old(self).inv(),
1152        ensures
1153            *final(self) == old(self).tlb_flush_vaddr(vaddr),
1154            final(self).inv(),
1155    {
1156        self.tlb_mappings = old(self).tlb_flush_vaddr(vaddr).tlb_mappings;
1157
1158        assert(self.pt_mappings == old(self).pt_mappings);
1159        assert(forall|pa: Paddr| #[trigger] self.is_mapped(pa) == old(self).is_mapped(pa));
1160    }
1161
1162    pub open spec fn tlb_soft_fault(self, vaddr: Vaddr) -> Self {
1163        let mapping = self.pt_mappings.filter(
1164            |m: Mapping| m.va_range.start <= vaddr < m.va_range.end,
1165        ).choose();
1166        GlobalMemView { tlb_mappings: self.tlb_mappings.insert(mapping), ..self }
1167    }
1168
1169    pub proof fn tracked_tlb_soft_fault(tracked &mut self, vaddr: Vaddr)
1170        requires
1171            old(self).inv(),
1172            old(self).addr_transl(vaddr) is None,
1173            exists|m: Mapping|
1174                old(self).pt_mappings.contains(m) && m.va_range.start <= vaddr < m.va_range.end,
1175        ensures
1176            *final(self) == old(self).tlb_soft_fault(vaddr),
1177            final(self).inv(),
1178    {
1179        let ghost pt_candidates = old(self).pt_mappings.filter(
1180            |m: Mapping| m.va_range.start <= vaddr < m.va_range.end,
1181        );
1182        assert(pt_candidates.len() > 0) by {
1183            let m = choose|m: Mapping|
1184                old(self).pt_mappings.contains(m) && m.va_range.start <= vaddr < m.va_range.end;
1185            assert(pt_candidates.contains(m));
1186        };
1187        assert(pt_candidates.contains(pt_candidates.choose()));
1188        self.tlb_mappings = old(self).tlb_soft_fault(vaddr).tlb_mappings;
1189
1190        assert(self.pt_mappings == old(self).pt_mappings);
1191        assert(forall|pa: Paddr| #[trigger] self.is_mapped(pa) == old(self).is_mapped(pa));
1192    }
1193
1194    pub open spec fn pt_map(self, m: Mapping) -> Self {
1195        let pt_mappings = self.pt_mappings.insert(m);
1196        let unmapped_pas = self.unmapped_pas.difference(
1197            Set::<usize>::range(m.pa_range.start, m.pa_range.end),
1198        );
1199        GlobalMemView { pt_mappings, unmapped_pas, ..self }
1200    }
1201
1202    pub proof fn tracked_pt_map(tracked &mut self, m: Mapping)
1203        requires
1204            forall|pa: Paddr|
1205                m.pa_range.start <= pa < m.pa_range.end ==> old(self).unmapped_pas.contains(pa),
1206            old(self).inv(),
1207        ensures
1208            *final(self) == old(self).pt_map(m),
1209    {
1210        self.pt_mappings = self.pt_mappings.insert(m);
1211        self.unmapped_pas = self.unmapped_pas.difference(
1212            Set::<usize>::range(m.pa_range.start, m.pa_range.end),
1213        );
1214    }
1215
1216    pub open spec fn pt_unmap(self, m: Mapping) -> Self {
1217        let pt_mappings = self.pt_mappings.remove(m);
1218        let unmapped_pas = self.unmapped_pas.union(
1219            Set::<usize>::range(m.pa_range.start, m.pa_range.end),
1220        );
1221        GlobalMemView { pt_mappings, unmapped_pas, ..self }
1222    }
1223
1224    pub proof fn tracked_pt_unmap(tracked &mut self, m: Mapping)
1225        requires
1226            old(self).pt_mappings.contains(m),
1227            !old(self).tlb_mappings.contains(m),
1228            old(self).inv(),
1229        ensures
1230            *final(self) == old(self).pt_unmap(m),
1231            final(self).inv(),
1232    {
1233        self.pt_mappings = self.pt_mappings.remove(m);
1234        self.unmapped_pas = self.unmapped_pas.union(
1235            Set::<usize>::range(m.pa_range.start, m.pa_range.end),
1236        );
1237
1238        assert forall|pa: Paddr| #[trigger]
1239            self.is_mapped(pa) <==> !self.unmapped_pas.contains(pa) by {
1240            if m.pa_range.start <= pa < m.pa_range.end {
1241                if self.is_mapped(pa) {
1242                    let n = choose|n: Mapping|
1243                        self.pt_mappings.contains(n) && n.pa_range.start <= pa < n.pa_range.end;
1244                }
1245            } else if old(self).is_mapped(pa) {
1246                let n = choose|n: Mapping|
1247                    old(self).pt_mappings.contains(n) && n.pa_range.start <= pa < n.pa_range.end;
1248            }
1249        };
1250    }
1251
1252    pub proof fn lemma_va_mapping_unique(self, va: usize)
1253        requires
1254            self.inv(),
1255            // Some mapping must cover `va` for the filter to be non-empty.
1256            self.addr_transl(va) is Some,
1257        ensures
1258            self.tlb_mappings.filter(
1259                |m: Mapping| m.va_range.start <= va < m.va_range.end,
1260            ).is_singleton(),
1261    {
1262    }
1263}
1264
1265} // verus!