Skip to main content

ostd/specs/mm/page_table/cursor/
mapping_set_lemmas.rs

1use vstd::prelude::*;
2use vstd_extra::{
3    arithmetic::{
4        lemma_nat_align_down_monotone, lemma_nat_align_down_sound,
5        lemma_nat_align_down_within_block, nat_align_down,
6    },
7    ghost_tree::*,
8    ownership::*,
9};
10
11use crate::specs::{
12    arch::{NR_ENTRIES, NR_LEVELS},
13    mm::page_table::{
14        cursor::{
15            owners::*,
16            page_size_lemmas::{lemma_page_size_divides, lemma_page_size_ge_base},
17        },
18        owners::*,
19        *,
20    },
21};
22
23use crate::arch::mm::PagingConsts;
24use crate::mm::{PagingConstsTrait, PagingLevel, Vaddr, page_size, page_table::*};
25
26verus! {
27
28broadcast use group_ghost_tree_lemmas;
29// ─── CursorContinuation mapping lemmas ───────────────────────────────────────
30
31impl<'rcu, C: PageTableConfig> CursorContinuation<'rcu, C> {
32    pub proof fn as_page_table_owner_preserves_view_mappings(self)
33        requires
34            self.inv(),
35            self.all_some(),
36        ensures
37            self.as_page_table_owner().view_rec(self.path()) == self.view_mappings(),
38            self.as_subtree().inv(),
39            PageTableOwner(self.as_subtree()).pt_inv(),
40    {
41        broadcast use {CursorContinuation::group_lemmas, PageTableOwner::group_lemmas};
42
43        self.as_page_table_owner_pt_inv();
44    }
45
46    pub proof fn view_mappings_take_child(self)
47        requires
48            self.inv(),
49            self.all_some(),
50        ensures
51            self.take_child().1.view_mappings() == self.view_mappings()
52                - self.view_mappings_take_child_spec(),
53    {
54        broadcast use CursorContinuation::group_lemmas;
55
56        let def = self.take_child().1.view_mappings();
57        let diff = self.view_mappings() - self.view_mappings_take_child_spec();
58        assert forall|m: Mapping| diff.contains(m) implies def.contains(m) by {
59            let i = choose|i: int|
60                0 <= i < self.children.len() && #[trigger] self.children[i] is Some
61                    && PageTableOwner(self.children[i].unwrap()).view_rec(
62                    self.path().push_tail(i),
63                ).contains(m);
64            assert(self.take_child().1.children[i] is Some);
65        };
66        assert forall|m: Mapping| #![trigger def.contains(m)] def.contains(m) implies diff.contains(
67            m,
68        ) by {
69            let left = self.take_child().1;
70            if self.view_mappings_take_child_spec().contains(m) {
71                let i = choose|i: int|
72                    0 <= i < left.children.len() && #[trigger] left.children[i] is Some
73                        && PageTableOwner(left.children[i].unwrap()).view_rec(
74                        left.path().push_tail(i),
75                    ).contains(m);
76
77                PageTableOwner(self.children[self.idx as int].unwrap()).lemma_view_rec_vaddr_range(
78                    self.path().push_tail(self.idx as int),
79                    m,
80                );
81                PageTableOwner(left.children[i as int].unwrap()).lemma_view_rec_vaddr_range(
82                    left.path().push_tail(i),
83                    m,
84                );
85
86                let size = page_size::<PagingConsts>(
87                    (INC_LEVELS - self.path().len() - 1) as PagingLevel,
88                );
89                // Positional disjointness; shift both sides by LEADING_BITS * 2^48.
90                sibling_paths_disjoint::<C>(self.path(), self.idx as int, i, size);
91                lemma_vaddr_of_eq_int::<C>(self.path().push_tail(self.idx as int));
92                lemma_vaddr_of_eq_int::<C>(self.path().push_tail(i));
93            }
94        };
95    }
96
97    pub proof fn view_mappings_put_child(self, child: OwnerSubtree<C>)
98        requires
99            self.inv(),
100            child.inv(),
101            self.all_but_index_some(),
102        ensures
103            self.put_child(child).view_mappings() == self.view_mappings() + PageTableOwner(
104                child,
105            ).view_rec(self.path().push_tail(self.idx as int)),
106    {
107        broadcast use CursorContinuation::group_lemmas;
108
109        let def = self.put_child(child).view_mappings();
110        let sum = self.view_mappings() + PageTableOwner(child).view_rec(
111            self.path().push_tail(self.idx as int),
112        );
113        assert forall|m: Mapping| sum.contains(m) implies def.contains(m) by {
114            if self.view_mappings().contains(m) {
115                let i = choose|i: int|
116                    0 <= i < self.children.len() && #[trigger] self.children[i] is Some
117                        && PageTableOwner(self.children[i].unwrap()).view_rec(
118                        self.path().push_tail(i),
119                    ).contains(m);
120                assert(self.put_child(child).children[i] == self.children[i]);
121            } else {
122                self.put_child(child).lemma_view_mappings_intro(m, self.idx as int);
123            }
124        };
125    }
126}
127
128impl<'rcu, C: PageTableConfig> CursorContinuation<'rcu, C> {
129    /// When a continuation has all_some and inv, its as_subtree() also has `TreeNode::inv()`.
130    proof fn as_subtree_inv(self)
131        requires
132            self.inv(),
133            self.all_some(),
134        ensures
135            self.as_subtree().inv(),
136    {
137    }
138
139    proof fn as_page_table_owner_pt_inv(self)
140        requires
141            self.inv(),
142            self.all_some(),
143        ensures
144            PageTableOwner(self.as_subtree()).pt_inv(),
145    {
146        reveal(PageTableOwner::pt_inv_at_depth);
147    }
148}
149
150// ─── CursorOwner mapping lemmas ──────────────────────────────────────────────
151impl<'rcu, C: PageTableConfig> CursorOwner<'rcu, C> {
152    /// The current subtree's mappings equal the filter over [subtree_va, subtree_va + page_size(level))
153    /// where subtree_va = vaddr(cur_subtree path).
154    pub proof fn lemma_cur_subtree_eq_filtered_mappings_path(self)
155        requires
156            self.inv(),
157            self.in_barrier_va(),
158        ensures
159            ({
160                let subtree_va = vaddr_of::<C>(self.cur_subtree().value().path) as int;
161                let size = page_size::<PagingConsts>(self.level) as int;
162                PageTableOwner(self.cur_subtree())@.mappings == self@.mappings.filter(
163                    |m: Mapping| subtree_va <= m.va_range.start < subtree_va + size,
164                )
165            }),
166    {
167        broadcast use {CursorContinuation::group_lemmas, CursorOwner::group_lemmas};
168
169        let cur_subtree = self.cur_subtree();
170        let cur_path = cur_subtree.value().path;
171        let subtree_va = vaddr_of::<C>(cur_path) as int;
172        let size = page_size::<PagingConsts>(self.level) as int;
173
174        let subtree_mappings = PageTableOwner(cur_subtree)@.mappings;
175        let filtered = self@.mappings.filter(
176            |m: Mapping| subtree_va <= m.va_range.start < subtree_va + size,
177        );
178
179        let cont = self.continuations[self.level - 1];
180        // cur_subtree.value.path == cont.path().push_tail(self.index())
181
182        // Forward: subtree mappings are in the filtered set
183        assert forall|m: Mapping| subtree_mappings.contains(m) implies filtered.contains(m) by {
184            // m is in the current subtree's view_rec => in cont[level-1].view_mappings() => in self.view_mappings()
185            // lemma_view_rec_vaddr_range gives m.va_range.start in
186            // [vaddr_of(cur_path), vaddr_of(cur_path) + page_size(level)),
187            // which is exactly our filter range.
188            PageTableOwner(cur_subtree).lemma_view_rec_vaddr_range(cur_path, m);
189        };
190
191        // Reverse: filtered mappings are in the subtree
192        assert forall|m: Mapping| filtered.contains(m) implies subtree_mappings.contains(m) by {
193            // m is in self.view_mappings() and subtree_va <= m.va_range.start < subtree_va + size
194            let i = choose|i: int|
195                self.level - 1 <= i < NR_LEVELS
196                    && #[trigger] self.continuations[i].view_mappings().contains(m);
197
198            let cont_i = self.continuations[i];
199            let j = choose|j: int|
200                #![auto]
201                0 <= j < NR_ENTRIES && cont_i.children[j] is Some && PageTableOwner(
202                    cont_i.children[j].unwrap(),
203                ).view_rec(cont_i.path().push_tail(j)).contains(m);
204
205            PageTableOwner(cont_i.children[j].unwrap()).lemma_view_rec_vaddr_range(
206                cont_i.path().push_tail(j),
207                m,
208            );
209
210            if i == self.level - 1 {
211                // j == index: cont_i.children[j] is exactly cur_subtree; m is already
212                // in subtree_mappings via view_rec at the same path.
213                if j as usize != self.index() {
214                    // Disjointness: sibling j's VA range doesn't overlap [subtree_va, subtree_va + page_size(level))
215                    let sib_size = page_size::<PagingConsts>(
216                        (INC_LEVELS - cont.path().len() - 1) as PagingLevel,
217                    );
218                    sibling_paths_disjoint::<C>(cont.path(), self.index() as int, j, sib_size);
219                    // Lift positional disjointness to canonical by adding
220                    // the same leading_bits * 2^48 to both sides.
221                    lemma_vaddr_of_eq_int::<C>(cont.path().push_tail(self.index() as int));
222                    lemma_vaddr_of_eq_int::<C>(cont.path().push_tail(j));
223                    assert(false);  // contradiction from disjointness + filter
224                }
225            } else {
226                if j as usize != cont_i.idx as int {
227                    // Subtree VA range is contained in ancestor child range
228                    self.subtree_va_in_ancestor_range(i);
229
230                    // Sibling j is disjoint from cont_i.idx child
231                    let sib_size = page_size::<PagingConsts>(
232                        (INC_LEVELS - cont_i.path().len() - 1) as PagingLevel,
233                    );
234                    sibling_paths_disjoint::<C>(cont_i.path(), cont_i.idx as int, j, sib_size);
235                    lemma_vaddr_of_eq_int::<C>(cont_i.path().push_tail(j));
236                    lemma_vaddr_of_eq_int::<C>(cur_path);
237                    assert(false);  // contradiction
238                } else {
239                    assert(false);  // cont_i.children[j] is Some contradicts
240                }
241            }
242        };
243
244    }
245
246    /// Version using nat_align_down(cur_va, page_size(level)) in the filter.
247    /// Uses the shared canonical subtree boundary rather than reconstructing the VA path.
248    pub proof fn lemma_cur_subtree_eq_filtered_mappings(self)
249        requires
250            self.inv(),
251            self.in_barrier_va(),
252        ensures
253            ({
254                let start = nat_align_down(
255                    self@.cur_va as nat,
256                    page_size::<PagingConsts>(self.level) as nat,
257                ) as Vaddr;
258                let size = page_size::<PagingConsts>(self.level);
259                PageTableOwner(self.cur_subtree())@.mappings == self@.mappings.filter(
260                    |m: Mapping| start <= m.va_range.start < start + size,
261                )
262            }),
263    {
264        self.lemma_cur_subtree_eq_filtered_mappings_path();
265        self.cur_va_in_subtree_range();
266    }
267
268    /// The cursor's VA falls within the canonical VA range of any ancestor
269    /// continuation's child that the cursor descended through. Canonical
270    /// form: positional `vaddr(path)` plus the architecture-parameterized upper-address base.
271    pub(super) proof fn cur_va_in_cont_child_range(self, lvl: int)
272        requires
273            self.inv(),
274            self.in_barrier_va(),
275            self.level - 1 <= lvl < NR_LEVELS,
276        ensures
277            vaddr(self.continuations[lvl].path().push_tail(self.continuations[lvl].idx as int))
278                + vaddr_leading_part_spec::<C>(self.cur_va()) <= self.cur_va(),
279            self.cur_va() < vaddr(
280                self.continuations[lvl].path().push_tail(self.continuations[lvl].idx as int),
281            ) + vaddr_leading_part_spec::<C>(self.cur_va()) + page_size::<PagingConsts>(
282                (lvl + 1) as PagingLevel,
283            ),
284            vaddr(self.continuations[lvl].path().push_tail(self.continuations[lvl].idx as int))
285                + vaddr_leading_part_spec::<C>(self.cur_va()) == nat_align_down(
286                self.cur_va() as nat,
287                page_size::<PagingConsts>((lvl + 1) as PagingLevel) as nat,
288            ),
289            vaddr_of::<C>(
290                self.continuations[lvl].path().push_tail(self.continuations[lvl].idx as int),
291            ) == nat_align_down(
292                self.cur_va() as nat,
293                page_size::<PagingConsts>((lvl + 1) as PagingLevel) as nat,
294            ),
295    {
296        C::lemma_paging_consts_properties();
297        C::axiom_current_paging_consts_hardcoded();
298        lemma_vaddr_leading_part_is_align_down::<C>(self.cur_va());
299        let cont = self.continuations[lvl];
300        let child_path = cont.path().push_tail(cont.idx as int);
301        assert(child_path.len() == C::NR_LEVELS() - lvl);
302        assert forall|k: int| 0 <= k < child_path.len() implies #[trigger] child_path[k]
303            == pte_index_spec::<C>(self.cur_va(), (C::NR_LEVELS() - k) as PagingLevel) by {
304            lemma_pte_index_bound::<C>(self.cur_va(), (C::NR_LEVELS() - k) as PagingLevel);
305        };
306        lemma_vaddr_path_aligned::<C>(child_path, self.cur_va());
307        lemma_vaddr_of_eq_int::<C>(child_path);
308        lemma_page_size_ge_base::<PagingConsts>((lvl + 1) as PagingLevel);
309        lemma_nat_align_down_sound(
310            self.cur_va() as nat,
311            page_size::<PagingConsts>((lvl + 1) as PagingLevel) as nat,
312        );
313    }
314
315    /// The current subtree's VA range [subtree_va, subtree_va + page_size(level)) is contained
316    /// within the VA range of any ancestor continuation's child that the cursor descended through.
317    proof fn subtree_va_in_ancestor_range(self, lvl: int)
318        requires
319            self.inv(),
320            self.in_barrier_va(),
321            self.level - 1 < lvl < NR_LEVELS,
322        ensures
323            ({
324                let subtree_va = vaddr(self.cur_subtree().value().path);
325                let idx_path_va = vaddr(
326                    self.continuations[lvl].path().push_tail(self.continuations[lvl].idx as int),
327                );
328                &&& idx_path_va <= subtree_va
329                &&& subtree_va + page_size::<PagingConsts>(self.level) <= idx_path_va + page_size::<
330                    PagingConsts,
331                >((lvl + 1) as PagingLevel)
332            }),
333    {
334        self.cur_va_in_cont_child_range(self.level - 1);
335        self.cur_va_in_cont_child_range(lvl);
336
337        let x = self.cur_va() as nat;
338        let fine = page_size::<PagingConsts>(self.level as PagingLevel) as nat;
339        let coarse = page_size::<PagingConsts>((lvl + 1) as PagingLevel) as nat;
340
341        lemma_page_size_divides::<PagingConsts>(
342            self.level as PagingLevel,
343            (lvl + 1) as PagingLevel,
344        );
345        lemma_nat_align_down_monotone(x, fine, coarse);
346        lemma_nat_align_down_within_block(x, fine, coarse);
347    }
348
349    /// Subtrees at different indices have disjoint VA ranges.
350    pub proof fn subtree_va_ranges_disjoint(self, j: int)
351        requires
352            self.inv(),
353            self.in_barrier_va(),
354            0 <= j < NR_ENTRIES,
355            j != self.index(),
356            self.continuations[self.level - 1].children[j] is Some,
357        ensures
358            vaddr(self.continuations[self.level - 1].path().push_tail(j))
359                + vaddr_leading_part_spec::<C>(self.cur_va()) + page_size::<PagingConsts>(
360                self.level as PagingLevel,
361            ) <= self.cur_va() || self.cur_va() < vaddr(
362                self.continuations[self.level - 1].path().push_tail(j),
363            ) + vaddr_leading_part_spec::<C>(self.cur_va()),
364    {
365        let cont = self.continuations[self.level - 1];
366        let idx = self.index();
367
368        // Establish cont.level() == self.level via case split
369        // cur_va is within the child at cont[level-1].idx
370        self.cur_va_in_cont_child_range(self.level - 1);
371
372        // Sibling paths are separated by `page_size(self.level)` (child page size).
373        let size = page_size::<PagingConsts>((INC_LEVELS - cont.path().len() - 1) as PagingLevel);
374        sibling_paths_disjoint::<C>(cont.path(), idx as int, j, size);
375    }
376
377    /// Children of higher-level continuations have VA ranges that don't include cur_va,
378    /// because cur_va's indices at those levels match the path to the current position.
379    pub proof fn higher_level_children_disjoint(self, i: int, j: int)
380        requires
381            self.inv(),
382            self.in_barrier_va(),
383            self.level - 1 < i < NR_LEVELS,
384            0 <= j < NR_ENTRIES,
385            j != self.continuations[i].idx,
386            self.continuations[i].children[j] is Some,
387        ensures
388            vaddr(self.continuations[i].path().push_tail(j)) + vaddr_leading_part_spec::<C>(
389                self.cur_va(),
390            ) + page_size::<PagingConsts>((i + 1) as PagingLevel) <= self.cur_va() || self.cur_va()
391                < vaddr(self.continuations[i].path().push_tail(j)) + vaddr_leading_part_spec::<C>(
392                self.cur_va(),
393            ),
394    {
395        let cont = self.continuations[i];
396
397        // Establish cont.level() == i + 1 via case split
398        // cur_va is within the child at cont[i].idx
399        self.cur_va_in_cont_child_range(i);
400
401        // Siblings at this depth are separated by `page_size(i+1)` (child page size).
402        let size = page_size::<PagingConsts>((INC_LEVELS - cont.path().len() - 1) as PagingLevel);
403        sibling_paths_disjoint::<C>(cont.path(), cont.idx as int, j, size);
404    }
405
406    /// Any mapping that covers cur_va must come from the current subtree.
407    /// This follows from the disjointness of VA ranges and the fact that
408    /// cur_va falls within the current subtree's VA range.
409    pub proof fn mapping_covering_cur_va_from_cur_subtree(self, m: Mapping)
410        requires
411            self.inv(),
412            self.in_barrier_va(),
413            self.view_mappings().contains(m),
414            m.va_range.start <= self.cur_va() < m.va_range.end,
415        ensures
416            PageTableOwner(self.cur_subtree()).view_rec(self.cur_subtree().value().path).contains(
417                m,
418            ),
419    {
420        broadcast use {CursorContinuation::group_lemmas, CursorOwner::group_lemmas};
421
422        lemma_vaddr_leading_part_is_align_down::<C>(self.cur_va());
423        // m comes from some continuation level i
424
425        let i = choose|i: int|
426            self.level - 1 <= i < NR_LEVELS
427                && #[trigger] self.continuations[i].view_mappings().contains(m);
428
429        let cont_i = self.continuations[i];
430        let j = choose|j: int|
431            #![auto]
432            0 <= j < NR_ENTRIES && cont_i.children[j] is Some && PageTableOwner(
433                cont_i.children[j].unwrap(),
434            ).view_rec(cont_i.path().push_tail(j)).contains(m);
435
436        let child_j = cont_i.children[j].unwrap();
437        let path_j = cont_i.path().push_tail(j);
438        PageTableOwner(child_j).lemma_view_rec_vaddr_range(path_j, m);
439        // Bridge lemma_view_rec_vaddr_range's canonical bounds to the
440        // disjointness lemmas (also canonical after the refactor).
441        lemma_vaddr_of_eq_int::<C>(path_j);
442
443        if i == self.level - 1 {
444            if j as usize != self.index() {
445                self.subtree_va_ranges_disjoint(j);
446            }
447        } else {
448            if j as usize != cont_i.idx as int {
449                self.higher_level_children_disjoint(i, j);
450            } else {
451                assert(false);
452            }
453        }
454    }
455
456    /// Combined replace: swap the lowest continuation, relating old and new mapping sets.
457    /// Avoids the Map::remove phantom key issue by requiring both old and new to have inv().
458    #[verifier::rlimit(60)]
459    pub proof fn view_mappings_replace_lowest(
460        old_self: Self,
461        new_self: Self,
462        old_cont: CursorContinuation<'rcu, C>,
463        new_cont: CursorContinuation<'rcu, C>,
464    )
465        requires
466            old_self.inv(),
467            old_self.in_barrier_va(),
468            new_self.inv(),
469            old_self.level == new_self.level,
470            old_self.continuations[old_self.level - 1] == old_cont,
471            new_self.continuations[new_self.level - 1] == new_cont,
472            forall|i: int|
473                old_self.level <= i < NR_LEVELS ==> old_self.continuations[i]
474                    == new_self.continuations[i],
475        ensures
476            new_self.view_mappings() == (old_self.view_mappings() - old_cont.view_mappings()).union(
477                new_cont.view_mappings(),
478            ),
479    {
480        hide(CursorContinuation::inv_children_rel);
481        hide(CursorContinuation::inv_children);
482        hide(CursorContinuation::pt_inv_children);
483        broadcast use {CursorContinuation::group_lemmas, CursorOwner::group_lemmas};
484
485        let level = old_self.level;
486
487        assert forall|m: Mapping| new_self.view_mappings().contains(m) implies ((
488        old_self.view_mappings().contains(m) && !old_cont.view_mappings().contains(m))
489            || new_cont.view_mappings().contains(m)) by {
490            let i = choose|i: int|
491                level - 1 <= i < NR_LEVELS
492                    && #[trigger] new_self.continuations[i].view_mappings().contains(m);
493            // i == level - 1: new_self.continuations[level-1] == new_cont, so the
494            // existential is witnessed by new_cont.view_mappings().contains(m).
495            if i != level - 1 {
496                assert(old_self.continuations[i] == new_self.continuations[i]);
497
498                if old_cont.view_mappings().contains(m) {
499                    old_self.lemma_inv_continuation(i);
500                    old_self.lemma_inv_continuation(level - 1);
501                    let cont_i = old_self.continuations[i];
502                    let j = choose|j: int|
503                        #![auto]
504                        0 <= j < NR_ENTRIES && cont_i.children[j] is Some && PageTableOwner(
505                            cont_i.children[j].unwrap(),
506                        ).view_rec(cont_i.path().push_tail(j)).contains(m);
507                    cont_i.lemma_inv_children_unroll(j);
508                    cont_i.lemma_pt_inv_children_unroll(j);
509                    cont_i.lemma_inv_children_rel_unroll(j);
510                    PageTableOwner(cont_i.children[j].unwrap()).lemma_view_rec_vaddr_range(
511                        cont_i.path().push_tail(j),
512                        m,
513                    );
514
515                    let k = choose|k: int|
516                        #![auto]
517                        0 <= k < NR_ENTRIES && old_cont.children[k] is Some && PageTableOwner(
518                            old_cont.children[k].unwrap(),
519                        ).view_rec(old_cont.path().push_tail(k)).contains(m);
520                    old_cont.lemma_inv_children_unroll(k);
521                    old_cont.lemma_pt_inv_children_unroll(k);
522                    old_cont.lemma_inv_children_rel_unroll(k);
523                    PageTableOwner(old_cont.children[k].unwrap()).lemma_view_rec_vaddr_range(
524                        old_cont.path().push_tail(k),
525                        m,
526                    );
527
528                    if j as usize != cont_i.idx as int {
529                        old_self.cur_va_in_cont_child_range(level as int);
530                        old_self.cur_va_in_cont_child_range(i);
531
532                        let x = old_self.cur_va() as nat;
533                        let ps_node = page_size::<PagingConsts>((level + 1) as PagingLevel) as nat;
534                        let ps_anc = page_size::<PagingConsts>((i + 1) as PagingLevel) as nat;
535
536                        lemma_page_size_ge_base::<PagingConsts>((i + 1) as PagingLevel);
537                        lemma_page_size_divides::<PagingConsts>(
538                            (level + 1) as PagingLevel,
539                            (i + 1) as PagingLevel,
540                        );
541
542                        lemma_nat_align_down_monotone(x, ps_node, ps_anc);
543                        lemma_nat_align_down_within_block(x, ps_node, ps_anc);
544
545                        let sib_size = page_size::<PagingConsts>(
546                            (INC_LEVELS - cont_i.path().len() - 1) as PagingLevel,
547                        );
548                        sibling_paths_disjoint::<C>(cont_i.path(), cont_i.idx as int, j, sib_size);
549                        // Lift positional disjointness to canonical.
550                        lemma_vaddr_of_eq_int::<C>(cont_i.path().push_tail(cont_i.idx as int));
551                        lemma_vaddr_of_eq_int::<C>(cont_i.path().push_tail(j));
552
553                        old_cont.as_subtree_inv();
554                        old_cont.as_page_table_owner_preserves_view_mappings();
555                        PageTableOwner(old_cont.as_subtree()).lemma_view_rec_vaddr_range(
556                            old_cont.path(),
557                            m,
558                        );
559                    } else {
560                        assert(cont_i.children[cont_i.idx as int] is None);
561                    }
562                }
563            }
564        };
565
566    }
567
568    #[verifier::rlimit(120)]
569    pub proof fn as_page_table_owner_preserves_view_mappings(self)
570        requires
571            self.inv(),
572        ensures
573            self.as_page_table_owner().view_rec(self.continuations[3].path())
574                == self.view_mappings(),
575            self.as_page_table_owner().0.inv(),
576            self.as_page_table_owner().0.level() == self.continuations[3].tree_level,
577            self.as_page_table_owner().pt_inv(),
578    {
579        broadcast use CursorOwner::group_lemmas;
580
581        if self.level == 4 {
582            self.continuations[3].as_page_table_owner_preserves_view_mappings();
583            self.lemma_inv_continuation(3);
584            assert(self.view_mappings() == self.continuations[3].view_mappings());
585            assert(self.as_page_table_owner().view_rec(self.continuations[3].path())
586                == self.view_mappings());
587        } else if self.level == 3 {
588            let c2 = self.continuations[2];
589            let c3 = self.continuations[3];
590
591            c2.as_page_table_owner_preserves_view_mappings();
592            c2.as_subtree_inv();
593            c3.view_mappings_put_child(c2.as_subtree());
594            c3.as_subtree_restore(c2);
595
596            let l4 = c3.restore(c2).0;
597            c2.as_page_table_owner_pt_inv();
598
599            c2.lemma_inv_children_unroll_all();
600            c3.lemma_inv_children_unroll_all();
601            l4.as_page_table_owner_preserves_view_mappings();
602
603            assert(self.view_mappings() == self.continuations[2].view_mappings().union(
604                self.continuations[3].view_mappings(),
605            )) by {
606                assert forall|m: Mapping| #[trigger]
607                    self.view_mappings().contains(
608                        m,
609                    ) implies self.continuations[2].view_mappings().contains(m)
610                    || self.continuations[3].view_mappings().contains(m) by {
611                    let i = choose|i: int|
612                        2 <= i < NR_LEVELS
613                            && #[trigger] self.continuations[i].view_mappings().contains(m);
614                };
615            };
616            assert(self.as_page_table_owner().view_rec(self.continuations[3].path())
617                == self.view_mappings());
618        } else if self.level == 2 {
619            let c1 = self.continuations[1];
620            let c2 = self.continuations[2];
621            let c3 = self.continuations[3];
622
623            c1.as_page_table_owner_preserves_view_mappings();
624            c1.as_subtree_inv();
625            c2.view_mappings_put_child(c1.as_subtree());
626            c2.as_subtree_restore(c1);
627
628            let l3 = c2.restore(c1).0;
629            c1.as_page_table_owner_pt_inv();
630
631            c1.lemma_inv_children_unroll_all();
632            c2.lemma_inv_children_unroll_all();
633            l3.as_page_table_owner_preserves_view_mappings();
634            l3.as_subtree_inv();
635            c3.as_subtree_restore(l3);
636            c3.view_mappings_put_child(l3.as_subtree());
637
638            let l4 = c3.restore(l3).0;
639            l3.as_page_table_owner_pt_inv();
640
641            c3.lemma_inv_children_unroll_all();
642            l4.as_page_table_owner_preserves_view_mappings();
643
644            assert(self.view_mappings() == c1.view_mappings().union(c2.view_mappings()).union(
645                c3.view_mappings(),
646            )) by {
647                assert forall|m: Mapping| self.view_mappings().contains(m) implies (
648                c1.view_mappings().contains(m) || c2.view_mappings().contains(m)
649                    || c3.view_mappings().contains(m)) by {
650                    let i = choose|i: int|
651                        1 <= i < NR_LEVELS
652                            && #[trigger] self.continuations[i].view_mappings().contains(m);
653                };
654            };
655            assert(self.as_page_table_owner().view_rec(self.continuations[3].path())
656                == self.view_mappings());
657        } else {
658            // level == 1
659            let c0 = self.continuations[0];
660            let c1 = self.continuations[1];
661            let c2 = self.continuations[2];
662            let c3 = self.continuations[3];
663
664            c0.as_page_table_owner_preserves_view_mappings();
665            c0.as_subtree_inv();
666            c1.view_mappings_put_child(c0.as_subtree());
667            c1.as_subtree_restore(c0);
668            let l2 = c1.restore(c0).0;
669            c0.as_page_table_owner_pt_inv();
670
671            c0.lemma_inv_children_unroll_all();
672            c1.lemma_inv_children_unroll_all();
673            l2.as_page_table_owner_preserves_view_mappings();
674            l2.as_subtree_inv();
675            c2.view_mappings_put_child(l2.as_subtree());
676            c2.as_subtree_restore(l2);
677            let l3 = c2.restore(l2).0;
678            l2.as_page_table_owner_pt_inv();
679
680            c2.lemma_inv_children_unroll_all();
681            l3.as_page_table_owner_preserves_view_mappings();
682            l3.as_subtree_inv();
683            c3.view_mappings_put_child(l3.as_subtree());
684            c3.as_subtree_restore(l3);
685            let l4 = c3.restore(l3).0;
686            l3.as_page_table_owner_pt_inv();
687
688            c3.lemma_inv_children_unroll_all();
689            l4.as_page_table_owner_preserves_view_mappings();
690
691            assert(self.view_mappings() == c0.view_mappings().union(c1.view_mappings()).union(
692                c2.view_mappings(),
693            ).union(c3.view_mappings())) by {
694                assert forall|m: Mapping| self.view_mappings().contains(m) implies (
695                c0.view_mappings().contains(m) || c1.view_mappings().contains(m)
696                    || c2.view_mappings().contains(m) || c3.view_mappings().contains(m)) by {
697                    let i = choose|i: int|
698                        0 <= i < NR_LEVELS
699                            && #[trigger] self.continuations[i].view_mappings().contains(m);
700                };
701            };
702            assert(self.as_page_table_owner().view_rec(self.continuations[3].path())
703                == self.view_mappings());
704        }
705    }
706
707    /// Every mapping in the cursor view satisfies `Mapping::inv()`.
708    pub proof fn lemma_view_mapping_inv(self)
709        requires
710            self.inv(),
711        ensures
712            forall|m: Mapping| self.view_mappings().contains(m) ==> #[trigger] m.inv(),
713    {
714        self.as_page_table_owner_preserves_view_mappings();
715        let pto = self.as_page_table_owner();
716        let root_path = self.continuations[3].path();
717        pto.lemma_view_rec_mapping_inv(root_path);
718    }
719
720    /// Non-overlapping mappings in the cursor view.
721    ///
722    /// Collapses the union-over-continuations `view_mappings` into a single
723    /// `view_rec` rooted at the reconstructed root page table, then applies
724    /// `lemma_view_rec_disjoint_vaddrs` on that single subtree.
725    /// Follows from tree structure alone.
726    pub proof fn as_page_table_owner_view_non_overlapping(self)
727        requires
728            self.inv(),
729        ensures
730            self@.non_overlapping(),
731    {
732        self.as_page_table_owner_preserves_view_mappings();
733        let pto = self.as_page_table_owner();
734        let root_path = self.continuations[3].path();
735
736        assert forall|m: Mapping, n: Mapping| #[trigger]
737            self@.mappings.contains(m) && #[trigger] self@.mappings.contains(n) && m
738                != n implies m.va_range.end <= n.va_range.start || n.va_range.end
739            <= m.va_range.start by {
740            pto.lemma_view_rec_disjoint_vaddrs(root_path, m, n);
741        };
742    }
743}
744
745} // verus!