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