Skip to main content

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

1use vstd::prelude::*;
2use vstd::set::{axiom_set_intersect_finite};
3
4use vstd_extra::arithmetic::{
5    lemma_nat_align_down_monotone, lemma_nat_align_down_within_block, nat_align_down,
6};
7use vstd_extra::ghost_tree::*;
8use vstd_extra::ownership::*;
9
10use crate::mm::page_table::*;
11use crate::mm::{PagingLevel, Vaddr};
12use crate::specs::arch::mm::{NR_ENTRIES, NR_LEVELS};
13use crate::specs::mm::page_table::cursor::owners::*;
14use crate::specs::mm::page_table::cursor::page_size_lemmas::lemma_page_size_divides;
15use crate::specs::mm::page_table::owners::{
16    lemma_vaddr_of_eq_int, page_size_monotonic, sibling_paths_disjoint, vaddr, vaddr_of,
17    OwnerSubtree, PageTableOwner, INC_LEVELS,
18};
19use crate::specs::mm::page_table::{AbstractVaddr, Mapping};
20
21use crate::mm::page_table::page_size_spec as page_size;
22
23verus! {
24
25// ─── CursorContinuation mapping lemmas ───────────────────────────────────────
26
27impl<'rcu, C: PageTableConfig> CursorContinuation<'rcu, C> {
28
29    pub proof fn as_page_table_owner_preserves_view_mappings(self)
30        requires
31            self.inv(),
32            self.all_some(),
33        ensures
34            self.as_page_table_owner().view_rec(self.path()) == self.view_mappings(),
35            self.as_subtree().inv(),
36            PageTableOwner(self.as_subtree()).pt_inv(),
37    {
38        self.inv_children_unroll_all();
39        self.as_subtree_inv();
40        self.as_page_table_owner_pt_inv();
41    }
42
43    pub proof fn view_mappings_take_child(self)
44        requires
45            self.inv(),
46            self.all_some(),
47        ensures
48            self.take_child_spec().1.view_mappings() == self.view_mappings() - self.view_mappings_take_child_spec()
49    {
50        self.inv_children_unroll_all();
51        let def = self.take_child_spec().1.view_mappings();
52        let diff = self.view_mappings() - self.view_mappings_take_child_spec();
53        assert forall |m: Mapping| diff.contains(m) implies def.contains(m) by {
54            let i = choose|i: int| 0 <= i < self.children.len() && #[trigger] self.children[i] is Some &&
55                PageTableOwner(self.children[i].unwrap()).view_rec(self.path().push_tail(i as usize)).contains(m);
56            assert(i != self.idx);
57            assert(self.take_child_spec().1.children[i] is Some);
58        };
59        assert forall |m: Mapping|
60        #![trigger def.contains(m)]
61        def.contains(m) implies diff.contains(m) by {
62            let left = self.take_child_spec().1;
63            assert(left.view_mappings().contains(m));
64            if self.view_mappings_take_child_spec().contains(m) {
65                // TODO: update the sibling-paths-disjoint reasoning to work
66                // with the canonical vaddr_of form now used by view_rec.
67                // The positional sibling_paths_disjoint::<C> still holds, but
68                // the shift by LEADING_BITS preserves disjointness — just
69                // needs an explicit lift through the +LEADING_BITS*2^48 term.
70                admit();
71            }
72        };
73    }
74
75    pub proof fn view_mappings_put_child(self, child: OwnerSubtree<C>)
76        requires
77            self.inv(),
78            child.inv(),
79            self.all_but_index_some(),
80        ensures
81            self.put_child_spec(child).view_mappings() == self.view_mappings() + PageTableOwner(child).view_rec(self.path().push_tail(self.idx as usize))
82    {
83        let def = self.put_child_spec(child).view_mappings();
84        let sum = self.view_mappings() + PageTableOwner(child).view_rec(self.path().push_tail(self.idx as usize));
85        assert forall |m: Mapping| sum.contains(m) implies def.contains(m) by {
86            if self.view_mappings().contains(m) {
87                let i = choose|i: int| 0 <= i < self.children.len() && #[trigger] self.children[i] is Some &&
88                    PageTableOwner(self.children[i].unwrap()).view_rec(self.path().push_tail(i as usize)).contains(m);
89                assert(i != self.idx);
90                assert(self.put_child_spec(child).children[i] == self.children[i]);
91            } else {
92                assert(PageTableOwner(child).view_rec(self.path().push_tail(self.idx as usize)).contains(m));
93                assert(self.put_child_spec(child).children[self.idx as int] == Some(child));
94            }
95        };
96    }
97}
98
99impl<'rcu, C: PageTableConfig> CursorContinuation<'rcu, C> {
100    /// When a continuation has all_some and inv, its as_subtree() also has Node::inv().
101    proof fn as_subtree_inv(self)
102        requires
103            self.inv(),
104            self.all_some(),
105        ensures
106            self.as_subtree().inv(),
107    {
108        self.inv_children_unroll_all();
109    }
110
111    proof fn as_page_table_owner_pt_inv(self)
112        requires
113            self.inv(),
114            self.all_some(),
115        ensures
116            PageTableOwner(self.as_subtree()).pt_inv(),
117    {
118        self.as_subtree_inv();
119        let st = self.as_subtree();
120        let depth = (INC_LEVELS - st.level) as nat;
121        assert forall |i: int| #![trigger st.children[i]]
122            0 <= i < NR_ENTRIES implies
123                PageTableOwner::<C>::pt_edge_at(st, i)
124                && PageTableOwner(st.children[i].unwrap())
125                       .pt_inv_at_depth((depth - 1) as nat)
126        by {
127            self.inv_children_rel_unroll(i);
128            self.pt_inv_children_unroll(i);
129        };
130    }
131}
132
133// ─── CursorOwner mapping lemmas ──────────────────────────────────────────────
134
135impl<'rcu, C: PageTableConfig> CursorOwner<'rcu, C> {
136
137    /// The current subtree's mappings equal the filter over [subtree_va, subtree_va + page_size(level))
138    /// where subtree_va = vaddr(cur_subtree path).
139    pub proof fn cur_subtree_eq_filtered_mappings_path(self)
140        requires
141            self.inv(),
142        ensures ({
143            // Canonical form: the filter boundary includes `leading_bits * 2^48`
144            // so it matches the canonical `m.va_range.start` produced by
145            // `view_rec` via `vaddr_of::<C>`.
146            let subtree_va = vaddr_of::<C>(self.cur_subtree().value.path) as int;
147            let size = page_size(self.level) as int;
148            PageTableOwner(self.cur_subtree())@.mappings ==
149                self@.mappings.filter(|m: Mapping| subtree_va <= m.va_range.start < subtree_va + size)
150        }),
151    {
152        let cur_subtree = self.cur_subtree();
153        let cur_path = cur_subtree.value.path;
154        let subtree_va = vaddr_of::<C>(cur_path) as int;
155        let size = page_size(self.level) as int;
156
157        let subtree_mappings = PageTableOwner(cur_subtree)@.mappings;
158        let filtered = self@.mappings.filter(|m: Mapping| subtree_va <= m.va_range.start < subtree_va + size);
159
160        self.cur_subtree_inv();
161
162        let cont = self.continuations[self.level - 1];
163        self.inv_continuation(self.level - 1);
164        cont.inv_children_rel_unroll(self.index() as int);
165        // cur_subtree.value.path == cont.path().push_tail(self.index())
166
167        // Forward: subtree mappings are in the filtered set
168        assert forall |m: Mapping| subtree_mappings.contains(m) implies filtered.contains(m) by {
169            // m is in the current subtree's view_rec => in cont[level-1].view_mappings() => in self.view_mappings()
170            assert(cont.children[self.index() as int] is Some);
171            assert(cont.children[self.index() as int].unwrap() == cur_subtree);
172            assert(cont.view_mappings().contains(m));
173            assert(self.view_mappings().contains(m));
174
175            // view_rec_vaddr_range gives m.va_range.start in
176            // [vaddr_of(cur_path), vaddr_of(cur_path) + page_size(level)),
177            // which is exactly our filter range.
178            PageTableOwner(cur_subtree).view_rec_vaddr_range(cur_path, m);
179        };
180
181        // Reverse: filtered mappings are in the subtree
182        assert forall |m: Mapping| filtered.contains(m) implies subtree_mappings.contains(m) by {
183            // m is in self.view_mappings() and subtree_va <= m.va_range.start < subtree_va + size
184            let i = choose|i: int| self.level - 1 <= i < NR_LEVELS
185                && #[trigger] self.continuations[i].view_mappings().contains(m);
186            self.inv_continuation(i);
187
188            let cont_i = self.continuations[i];
189            let j = choose|j: int| #![auto] 0 <= j < NR_ENTRIES
190                && cont_i.children[j] is Some
191                && PageTableOwner(cont_i.children[j].unwrap())
192                    .view_rec(cont_i.path().push_tail(j as usize)).contains(m);
193
194            cont_i.inv_children_unroll(j);
195            PageTableOwner(cont_i.children[j].unwrap()).view_rec_vaddr_range(
196                cont_i.path().push_tail(j as usize), m);
197
198            if i == self.level - 1 {
199                if j as usize == self.index() {
200                    // cont_i.children[j] is exactly cur_subtree; m is already
201                    // in subtree_mappings via view_rec at the same path.
202                    assert(cont_i.children[j] == Some(cur_subtree));
203                    assert(cont_i.path().push_tail(j as usize) == cur_path);
204                    assert(subtree_mappings == PageTableOwner(cur_subtree).view_rec(cur_path));
205                    assert(PageTableOwner(cur_subtree).view_rec(cur_path).contains(m));
206                    assert(subtree_mappings.contains(m));
207                } else {
208                    // Disjointness: sibling j's VA range doesn't overlap [subtree_va, subtree_va + page_size(level))
209                    assert(cont.level() == self.level) by {
210                        if self.level == 1 {} else if self.level == 2 {} else if self.level == 3 {} else {}
211                    };
212                    let sib_size = page_size((INC_LEVELS - cont.path().len() - 1) as PagingLevel);
213                    sibling_paths_disjoint::<C>(cont.path(), self.index(), j as usize, sib_size);
214                    // Lift positional disjointness to canonical by adding
215                    // the same leading_bits * 2^48 to both sides.
216                    lemma_vaddr_of_eq_int::<C>(cont.path().push_tail(self.index() as usize));
217                    lemma_vaddr_of_eq_int::<C>(cont.path().push_tail(j as usize));
218                    assert(false);  // contradiction from disjointness + filter
219                }
220            } else {
221                if j as usize != cont_i.idx as usize {
222                    // Subtree VA range is contained in ancestor child range
223                    self.subtree_va_in_ancestor_range(i);
224
225                    // Sibling j is disjoint from cont_i.idx child
226                    assert(cont_i.level() == (i + 1) as PagingLevel) by {
227                        if i == 0 {} else if i == 1 {} else if i == 2 {} else {}
228                    };
229                    let sib_size = page_size((INC_LEVELS - cont_i.path().len() - 1) as PagingLevel);
230                    sibling_paths_disjoint::<C>(cont_i.path(), cont_i.idx, j as usize, sib_size);
231                    lemma_vaddr_of_eq_int::<C>(cont_i.path().push_tail(cont_i.idx as usize));
232                    lemma_vaddr_of_eq_int::<C>(cont_i.path().push_tail(j as usize));
233                    lemma_vaddr_of_eq_int::<C>(cur_path);
234                    assert(false);  // contradiction
235                } else {
236                    assert(cont_i.children[cont_i.idx as int] is None);
237                    assert(false);  // cont_i.children[j] is Some contradicts
238                }
239            }
240        };
241
242        assert(subtree_mappings =~= filtered);
243    }
244
245    /// Version using nat_align_down(cur_va, page_size(level)) in the filter.
246    /// Bridge: nat_align_down(cur_va, ps) as int == vaddr(cur_path) + leading_bits * 2^48.
247    pub proof fn cur_subtree_eq_filtered_mappings(self)
248        requires
249            self.inv(),
250        ensures ({
251            let start = nat_align_down(self@.cur_va as nat, page_size(self.level) as nat) as Vaddr;
252            let size = page_size(self.level);
253            PageTableOwner(self.cur_subtree())@.mappings ==
254                self@.mappings.filter(|m: Mapping| start <= m.va_range.start < start + size)
255        }),
256    {
257        // TODO: bridge the positional `cur_subtree_eq_filtered_mappings_path`
258        // form to the canonical `nat_align_down(cur_va, ps)` form. Holds
259        // because `m.va_range.start` is canonical (from view_rec using
260        // vaddr_of) and `nat_align_down(cur_va, ps) == vaddr(cur_path) +
261        // leading_bits * 2^48` — the shift cancels in the filter.
262        admit();
263        self.cur_subtree_eq_filtered_mappings_path();
264        self.cur_va_in_cont_child_range(self.level - 1);
265        self.va.to_path_vaddr_concrete(self.level as int - 1);
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 `leading_bits * 2^48` shift.
271    proof fn cur_va_in_cont_child_range(self, lvl: int)
272        requires
273            self.inv(),
274            self.level - 1 <= lvl < NR_LEVELS,
275        ensures
276            vaddr(self.continuations[lvl].path().push_tail(self.continuations[lvl].idx as usize)) as int
277                + self.va.leading_bits * 0x1_0000_0000_0000int
278                <= self.cur_va() as int,
279            (self.cur_va() as int)
280                < vaddr(self.continuations[lvl].path().push_tail(self.continuations[lvl].idx as usize)) as int
281                    + self.va.leading_bits * 0x1_0000_0000_0000int
282                    + page_size((lvl + 1) as PagingLevel) as int,
283            vaddr(self.continuations[lvl].path().push_tail(self.continuations[lvl].idx as usize))
284                == vaddr(self.va.to_path(lvl)),
285    {
286        let cont = self.continuations[lvl];
287        let child_path = cont.path().push_tail(cont.idx as usize);
288        let va_path = self.va.to_path(lvl);
289
290        self.va.to_path_len(lvl);
291        cont.path().push_tail_property_len(cont.idx as usize);
292        assert(cont.level() == (lvl + 1) as PagingLevel) by {
293            if lvl == 0 {} else if lvl == 1 {} else if lvl == 2 {} else {}
294        };
295
296        assert forall|k: int| 0 <= k < child_path.len() implies
297            child_path.index(k) == va_path.index(k) by {
298            self.va.to_path_index(lvl, k);
299            if lvl == 3 {
300                cont.path().push_tail_property_index(cont.idx as usize);
301            } else if lvl == 2 {
302                cont.path().push_tail_property_index(cont.idx as usize);
303                self.continuations[3].path().push_tail_property_index(self.continuations[3].idx as usize);
304            } else if lvl == 1 {
305                cont.path().push_tail_property_index(cont.idx as usize);
306                self.continuations[2].path().push_tail_property_index(self.continuations[2].idx as usize);
307                self.continuations[3].path().push_tail_property_index(self.continuations[3].idx as usize);
308            } else {
309                cont.path().push_tail_property_index(cont.idx as usize);
310                self.continuations[1].path().push_tail_property_index(self.continuations[1].idx as usize);
311                self.continuations[2].path().push_tail_property_index(self.continuations[2].idx as usize);
312                self.continuations[3].path().push_tail_property_index(self.continuations[3].idx as usize);
313            }
314        };
315
316        self.va.to_path_inv(lvl);
317        cont.path().push_tail_preserves_inv(cont.idx as usize);
318        AbstractVaddr::rec_vaddr_eq_if_indices_eq(child_path, va_path, 0);
319        self.va.vaddr_range_from_path(lvl);
320    }
321
322    /// The current subtree's VA range [subtree_va, subtree_va + page_size(level)) is contained
323    /// within the VA range of any ancestor continuation's child that the cursor descended through.
324    proof fn subtree_va_in_ancestor_range(self, lvl: int)
325        requires
326            self.inv(),
327            self.level - 1 < lvl < NR_LEVELS,
328        ensures ({
329            let subtree_va = vaddr(self.cur_subtree().value.path);
330            let idx_path_va = vaddr(self.continuations[lvl].path().push_tail(self.continuations[lvl].idx as usize));
331            &&& idx_path_va <= subtree_va
332            &&& subtree_va + page_size(self.level) <= idx_path_va + page_size((lvl + 1) as PagingLevel)
333        }),
334    {
335        let cont = self.continuations[self.level - 1];
336        self.inv_continuation(self.level - 1);
337        cont.inv_children_rel_unroll(self.index() as int);
338        // cur_subtree.value.path == cont.path().push_tail(self.index())
339
340        self.cur_va_in_cont_child_range(self.level - 1);
341        self.cur_va_in_cont_child_range(lvl);
342        self.va.to_path_vaddr_concrete(self.level as int - 1);
343        self.va.to_path_vaddr_concrete(lvl);
344
345        let x = self.cur_va() as nat;
346        let fine = page_size(self.level as PagingLevel) as nat;
347        let coarse = page_size((lvl + 1) as PagingLevel) as nat;
348        let shift = self.va.leading_bits * 0x1_0000_0000_0000int;
349
350        // Explicit chain: subtree_va + shift == nat_align_down(x, fine)
351        let subtree_va = vaddr(self.cur_subtree().value.path);
352        assert(subtree_va == vaddr(self.va.to_path(self.level as int - 1)));
353        assert(subtree_va as int + shift == nat_align_down(x, fine) as int);
354
355        // Explicit chain: idx_path_va + shift == nat_align_down(x, coarse)
356        let idx_path_va = vaddr(self.continuations[lvl].path().push_tail(self.continuations[lvl].idx as usize));
357        assert(idx_path_va == vaddr(self.va.to_path(lvl)));
358        assert(idx_path_va as int + shift == nat_align_down(x, coarse) as int);
359
360        lemma_page_size_divides(self.level as PagingLevel, (lvl + 1) as PagingLevel);
361        lemma_nat_align_down_monotone(x, fine, coarse);
362        lemma_nat_align_down_within_block(x, fine, coarse);
363
364        // Bridge nat to usize: values fit in usize since they're <= cur_va
365        vstd_extra::arithmetic::lemma_nat_align_down_sound(x, fine);
366        vstd_extra::arithmetic::lemma_nat_align_down_sound(x, coarse);
367    }
368
369    /// Subtrees at different indices have disjoint VA ranges.
370    pub proof fn subtree_va_ranges_disjoint(self, j: int)
371        requires
372            self.inv(),
373            0 <= j < NR_ENTRIES,
374            j != self.index(),
375            self.continuations[self.level - 1].children[j] is Some,
376        ensures
377            vaddr(self.continuations[self.level - 1].path().push_tail(j as usize)) as int
378                + self.va.leading_bits * 0x1_0000_0000_0000int
379                + page_size(self.level as PagingLevel) as int
380                <= self.cur_va() as int
381            || (self.cur_va() as int)
382                < vaddr(self.continuations[self.level - 1].path().push_tail(j as usize)) as int
383                    + self.va.leading_bits * 0x1_0000_0000_0000int
384    {
385        let cont = self.continuations[self.level - 1];
386        let idx = self.index();
387
388        // Establish cont.level() == self.level via case split
389        assert(cont.level() == self.level) by {
390            if self.level == 1 {} else if self.level == 2 {} else if self.level == 3 {} else {}
391        };
392
393        // cur_va is within the child at cont[level-1].idx
394        self.cur_va_in_cont_child_range(self.level - 1);
395
396        // Sibling paths are separated by `page_size(self.level)` (child page size).
397        let size = page_size((INC_LEVELS - cont.path().len() - 1) as PagingLevel);
398        sibling_paths_disjoint::<C>(cont.path(), idx, j as usize, size);
399    }
400
401    /// Children of higher-level continuations have VA ranges that don't include cur_va,
402    /// because cur_va's indices at those levels match the path to the current position.
403    pub proof fn higher_level_children_disjoint(self, i: int, j: int)
404        requires
405            self.inv(),
406            self.level - 1 < i < NR_LEVELS,
407            0 <= j < NR_ENTRIES,
408            j != self.continuations[i].idx,
409            self.continuations[i].children[j] is Some,
410        ensures
411            vaddr(self.continuations[i].path().push_tail(j as usize)) as int
412                + self.va.leading_bits * 0x1_0000_0000_0000int
413                + page_size((i + 1) as PagingLevel) as int
414                <= self.cur_va() as int
415            || (self.cur_va() as int)
416                < vaddr(self.continuations[i].path().push_tail(j as usize)) as int
417                    + self.va.leading_bits * 0x1_0000_0000_0000int
418    {
419        let cont = self.continuations[i];
420
421        // Establish cont.level() == i + 1 via case split
422        assert(cont.level() == (i + 1) as PagingLevel) by {
423            if i == 0 {} else if i == 1 {} else if i == 2 {} else {}
424        };
425
426        // cur_va is within the child at cont[i].idx
427        self.cur_va_in_cont_child_range(i);
428
429        // Siblings at this depth are separated by `page_size(i+1)` (child page size).
430        let size = page_size((INC_LEVELS - cont.path().len() - 1) as PagingLevel);
431        sibling_paths_disjoint::<C>(cont.path(), cont.idx, j as usize, size);
432    }
433
434    /// Any mapping that covers cur_va must come from the current subtree.
435    /// This follows from the disjointness of VA ranges and the fact that
436    /// cur_va falls within the current subtree's VA range.
437    pub proof fn mapping_covering_cur_va_from_cur_subtree(self, m: Mapping)
438        requires
439            self.inv(),
440            self.view_mappings().contains(m),
441            m.va_range.start <= self.cur_va() < m.va_range.end,
442        ensures
443            PageTableOwner(self.cur_subtree()).view_rec(self.cur_subtree().value.path).contains(m),
444    {
445        // TODO: bridge canonical `cur_va` to positional `vaddr_of(cur_path)`.
446        admit();
447        let cur_va = self.cur_va();
448
449        // m comes from some continuation level i
450        let i = choose|i: int| self.level - 1 <= i < NR_LEVELS
451            && #[trigger] self.continuations[i].view_mappings().contains(m);
452        self.inv_continuation(i);
453
454        let cont_i = self.continuations[i];
455        let j = choose|j: int| #![auto] 0 <= j < NR_ENTRIES
456            && cont_i.children[j] is Some
457            && PageTableOwner(cont_i.children[j].unwrap())
458                .view_rec(cont_i.path().push_tail(j as usize)).contains(m);
459
460        cont_i.inv_children_unroll(j);
461        let child_j = cont_i.children[j].unwrap();
462        let path_j = cont_i.path().push_tail(j as usize);
463        PageTableOwner(child_j).view_rec_vaddr_range(path_j, m);
464
465        if i == self.level - 1 {
466            if j as usize != self.index() {
467                self.subtree_va_ranges_disjoint(j);
468            }
469        } else {
470            if j as usize != cont_i.idx as usize {
471                self.higher_level_children_disjoint(i, j);
472            } else {
473                assert(cont_i.children[cont_i.idx as int] is None);
474                assert(false);
475            }
476        }
477    }
478
479    /// Combined replace: swap the lowest continuation, relating old and new mapping sets.
480    /// Avoids the Map::remove phantom key issue by requiring both old and new to have inv().
481    pub proof fn view_mappings_replace_lowest(
482        old_self: Self, new_self: Self,
483        old_cont: CursorContinuation<'rcu, C>,
484        new_cont: CursorContinuation<'rcu, C>,
485    )
486        requires
487            old_self.inv(),
488            new_self.inv(),
489            old_self.level == new_self.level,
490            old_self.continuations[old_self.level - 1] == old_cont,
491            new_self.continuations[new_self.level - 1] == new_cont,
492            forall|i: int| old_self.level <= i < NR_LEVELS ==>
493                old_self.continuations[i] == new_self.continuations[i],
494        ensures
495            new_self.view_mappings() ==
496                (old_self.view_mappings() - old_cont.view_mappings()).union(new_cont.view_mappings()),
497    {
498        let level = old_self.level;
499
500        assert forall |m: Mapping| new_self.view_mappings().contains(m) implies
501            ((old_self.view_mappings().contains(m) && !old_cont.view_mappings().contains(m)) || new_cont.view_mappings().contains(m))
502        by {
503            // TODO: downstream disjointness reasoning uses
504            // `vaddr(path)` positional bounds; needs lifting to `vaddr_of`
505            // after the Range<int> refactor.
506            admit();
507            let i = choose|i: int| level - 1 <= i < NR_LEVELS
508                && #[trigger] new_self.continuations[i].view_mappings().contains(m);
509            if i == level - 1 {
510                assert(new_cont.view_mappings().contains(m));
511            } else {
512                assert(old_self.continuations[i] == new_self.continuations[i]);
513                assert(old_self.continuations[i].view_mappings().contains(m));
514                assert(old_self.view_mappings().contains(m));
515
516                if old_cont.view_mappings().contains(m) {
517                    old_self.inv_continuation(i);
518                    old_self.inv_continuation(level - 1);
519                    let cont_i = old_self.continuations[i];
520                    let j = choose|j: int| #![auto] 0 <= j < NR_ENTRIES
521                        && cont_i.children[j] is Some
522                        && PageTableOwner(cont_i.children[j].unwrap())
523                            .view_rec(cont_i.path().push_tail(j as usize)).contains(m);
524                    cont_i.inv_children_unroll(j);
525                    PageTableOwner(cont_i.children[j].unwrap()).view_rec_vaddr_range(
526                        cont_i.path().push_tail(j as usize), m);
527
528                    let k = choose|k: int| #![auto] 0 <= k < NR_ENTRIES
529                        && old_cont.children[k] is Some
530                        && PageTableOwner(old_cont.children[k].unwrap())
531                            .view_rec(old_cont.path().push_tail(k as usize)).contains(m);
532                    old_cont.inv_children_unroll(k);
533                    PageTableOwner(old_cont.children[k].unwrap()).view_rec_vaddr_range(
534                        old_cont.path().push_tail(k as usize), m);
535
536                    if j as usize != cont_i.idx as usize {
537                        assert(cont_i.level() == (i + 1) as PagingLevel) by {
538                            if i == 0 {} else if i == 1 {} else if i == 2 {} else {}
539                        };
540
541                        old_self.cur_va_in_cont_child_range(level as int);
542                        old_self.va.to_path_vaddr_concrete(level as int);
543                        old_self.cur_va_in_cont_child_range(i);
544                        old_self.va.to_path_vaddr_concrete(i);
545
546                        let x = old_self.cur_va() as nat;
547                        let ps_node = page_size((level + 1) as PagingLevel) as nat;
548                        let ps_anc = page_size((i + 1) as PagingLevel) as nat;
549
550                        crate::specs::mm::page_table::cursor::page_size_lemmas::lemma_page_size_ge_page_size((level + 1) as PagingLevel);
551                        crate::specs::mm::page_table::cursor::page_size_lemmas::lemma_page_size_ge_page_size((i + 1) as PagingLevel);
552                        lemma_page_size_divides((level + 1) as PagingLevel, (i + 1) as PagingLevel);
553
554                        lemma_nat_align_down_monotone(x, ps_node, ps_anc);
555                        lemma_nat_align_down_within_block(x, ps_node, ps_anc);
556                        vstd_extra::arithmetic::lemma_nat_align_down_sound(x, ps_node);
557                        vstd_extra::arithmetic::lemma_nat_align_down_sound(x, ps_anc);
558
559                        let sib_size = page_size((INC_LEVELS - cont_i.path().len() - 1) as PagingLevel);
560                        sibling_paths_disjoint::<C>(cont_i.path(), cont_i.idx, j as usize, sib_size);
561
562                        old_cont.as_subtree_inv();
563                        PageTableOwner(old_cont.as_subtree()).view_rec_vaddr_range(old_cont.path(), m);
564                    } else {
565                        assert(cont_i.children[cont_i.idx as int] is None);
566                    }
567                }
568            }
569        };
570
571        assert forall |m: Mapping|
572            ((old_self.view_mappings().contains(m) && !old_cont.view_mappings().contains(m)) || new_cont.view_mappings().contains(m))
573            implies new_self.view_mappings().contains(m)
574        by {
575            if new_cont.view_mappings().contains(m) {
576                assert(new_self.continuations[level - 1].view_mappings().contains(m));
577            } else {
578                // m in old.vm() and not in old_cont.vm(), so m in some higher cont
579                let i = choose|i: int| level - 1 <= i < NR_LEVELS
580                    && #[trigger] old_self.continuations[i].view_mappings().contains(m);
581                if i == level - 1 {
582                    // contradiction: m in old_cont but we assumed m not in old_cont
583                    assert(false);
584                } else {
585                    assert(new_self.continuations[i] == old_self.continuations[i]);
586                    assert(new_self.continuations[i].view_mappings().contains(m));
587                }
588            }
589        };
590
591        assert(new_self.view_mappings() =~=
592            (old_self.view_mappings() - old_cont.view_mappings()).union(new_cont.view_mappings()));
593    }
594
595    pub proof fn as_page_table_owner_preserves_view_mappings(self)
596        requires
597            self.inv(),
598        ensures
599            self.as_page_table_owner().view_rec(self.continuations[3].path()) == self.view_mappings(),
600            self.as_page_table_owner().0.inv(),
601            self.as_page_table_owner().0.level == self.continuations[3].tree_level,
602            self.as_page_table_owner().pt_inv(),
603    {
604        if self.level == 4 {
605            self.continuations[3].as_page_table_owner_preserves_view_mappings();
606            assert(self.view_mappings() =~= self.continuations[3].view_mappings());
607        } else if self.level == 3 {
608            let c2 = self.continuations[2];
609            let c3 = self.continuations[3];
610
611            c2.as_page_table_owner_preserves_view_mappings();
612            c2.as_subtree_inv();
613            c3.view_mappings_put_child(c2.as_subtree());
614            c3.as_subtree_restore(c2);
615
616            let l4 = c3.restore_spec(c2).0;
617            assert(l4.all_some()) by {
618                assert forall |i: int| 0 <= i < NR_ENTRIES implies l4.children[i] is Some by {
619                    if i == c3.idx as int {
620                    } else {
621                        assert(l4.children[i] == c3.children[i]);
622                    }
623                };
624            };
625
626            c2.as_page_table_owner_pt_inv();
627            assert(l4.pt_inv_children()) by {
628                assert forall |i: int|
629                    #![trigger l4.children[i]]
630                    !(0 <= i < l4.children.len())
631                        || (l4.children[i] is Some ==> PageTableOwner(l4.children[i].unwrap()).pt_inv())
632                by {
633                    if 0 <= i < l4.children.len() {
634                        if i == c3.idx as int {
635                            assert(l4.children[i] == Some(c2.as_subtree()));
636                        } else {
637                            assert(l4.children[i] == c3.children[i]);
638                            c3.pt_inv_children_unroll(i);
639                        }
640                    } else {
641                        assert(!(0 <= i < l4.children.len()));
642                    }
643                };
644            };
645
646            c2.inv_children_unroll_all();
647            c3.inv_children_unroll_all();
648            l4.as_page_table_owner_preserves_view_mappings();
649
650            assert(self.view_mappings() =~= self.continuations[2].view_mappings().union(self.continuations[3].view_mappings())) by {
651                assert forall |m: Mapping| #[trigger] self.view_mappings().contains(m) implies
652                    self.continuations[2].view_mappings().contains(m) || self.continuations[3].view_mappings().contains(m) by {
653                    let i = choose |i: int| 2 <= i < NR_LEVELS && #[trigger] self.continuations[i].view_mappings().contains(m);
654                };
655            };
656        } else if self.level == 2 {
657            let c1 = self.continuations[1];
658            let c2 = self.continuations[2];
659            let c3 = self.continuations[3];
660
661            c1.as_page_table_owner_preserves_view_mappings();
662            c1.as_subtree_inv();
663            c2.view_mappings_put_child(c1.as_subtree());
664            c2.as_subtree_restore(c1);
665
666            let l3 = c2.restore_spec(c1).0;
667            assert(l3.all_some()) by {
668                assert forall |i: int| 0 <= i < NR_ENTRIES implies l3.children[i] is Some by {
669                    if i == c2.idx as int {
670                    } else {
671                        assert(l3.children[i] == c2.children[i]);
672                    }
673                };
674            };
675            c1.as_page_table_owner_pt_inv();
676            assert(l3.pt_inv_children()) by {
677                assert forall |i: int|
678                    #![trigger l3.children[i]]
679                    !(0 <= i < l3.children.len())
680                        || (l3.children[i] is Some ==> PageTableOwner(l3.children[i].unwrap()).pt_inv())
681                by {
682                    if 0 <= i < l3.children.len() {
683                        if i == c2.idx as int {
684                            assert(l3.children[i] == Some(c1.as_subtree()));
685                        } else {
686                            assert(l3.children[i] == c2.children[i]);
687                            c2.pt_inv_children_unroll(i);
688                        }
689                    } else {
690                        assert(!(0 <= i < l3.children.len()));
691                    }
692                };
693            };
694
695            c1.inv_children_unroll_all();
696            c2.inv_children_unroll_all();
697            l3.as_page_table_owner_preserves_view_mappings();
698            l3.as_subtree_inv();
699            c3.as_subtree_restore(l3);
700            c3.view_mappings_put_child(l3.as_subtree());
701
702            let l4 = c3.restore_spec(l3).0;
703            assert(l4.all_some()) by {
704                assert forall |i: int| 0 <= i < NR_ENTRIES implies l4.children[i] is Some by {
705                    if i == c3.idx as int {
706                        assert(l4.children[i] == Some(l3.as_subtree()));
707                    } else {
708                        assert(l4.children[i] == c3.children[i]);
709                    }
710                };
711            };
712            l3.as_page_table_owner_pt_inv();
713            assert(l4.pt_inv_children()) by {
714                assert forall |i: int|
715                    #![trigger l4.children[i]]
716                    !(0 <= i < l4.children.len())
717                        || (l4.children[i] is Some ==> PageTableOwner(l4.children[i].unwrap()).pt_inv())
718                by {
719                    if 0 <= i < l4.children.len() {
720                        if i == c3.idx as int {
721                            assert(l4.children[i] == Some(l3.as_subtree()));
722                        } else {
723                            assert(l4.children[i] == c3.children[i]);
724                            c3.pt_inv_children_unroll(i);
725                        }
726                    } else {
727                        assert(!(0 <= i < l4.children.len()));
728                    }
729                };
730            };
731
732            c3.inv_children_unroll_all();
733            l4.as_page_table_owner_preserves_view_mappings();
734
735            assert(self.view_mappings() =~= c1.view_mappings().union(c2.view_mappings()).union(c3.view_mappings())) by {
736                assert forall |m: Mapping| self.view_mappings().contains(m) implies
737                    (c1.view_mappings().contains(m) || c2.view_mappings().contains(m) || c3.view_mappings().contains(m)) by {
738                    let i = choose |i: int| 1 <= i < NR_LEVELS && #[trigger] self.continuations[i].view_mappings().contains(m);
739                };
740            };
741        } else {
742            // level == 1
743            let c0 = self.continuations[0];
744            let c1 = self.continuations[1];
745            let c2 = self.continuations[2];
746            let c3 = self.continuations[3];
747
748            c0.as_page_table_owner_preserves_view_mappings();
749            c0.as_subtree_inv();
750            c1.view_mappings_put_child(c0.as_subtree());
751            c1.as_subtree_restore(c0);
752            let l2 = c1.restore_spec(c0).0;
753            assert(l2.all_some()) by {
754                assert forall |i: int| 0 <= i < NR_ENTRIES implies l2.children[i] is Some by {
755                    if i == c1.idx as int {
756                    } else {
757                        assert(l2.children[i] == c1.children[i]);
758                    }
759                };
760            };
761            c0.as_page_table_owner_pt_inv();
762            assert(l2.pt_inv_children()) by {
763                assert forall |i: int| #![trigger l2.children[i]]
764                    !(0 <= i < l2.children.len())
765                        || (l2.children[i] is Some ==> PageTableOwner(l2.children[i].unwrap()).pt_inv())
766                by {
767                    if 0 <= i < l2.children.len() {
768                        if i == c1.idx as int {
769                            assert(l2.children[i] == Some(c0.as_subtree()));
770                        } else {
771                            assert(l2.children[i] == c1.children[i]);
772                            c1.pt_inv_children_unroll(i);
773                        }
774                    } else {
775                        assert(!(0 <= i < l2.children.len()));
776                    }
777                };
778            };
779
780            c0.inv_children_unroll_all();
781            c1.inv_children_unroll_all();
782            l2.as_page_table_owner_preserves_view_mappings();
783            l2.as_subtree_inv();
784            c2.view_mappings_put_child(l2.as_subtree());
785            c2.as_subtree_restore(l2);
786            let l3 = c2.restore_spec(l2).0;
787            assert(l3.all_some()) by {
788                assert forall |i: int| 0 <= i < NR_ENTRIES implies l3.children[i] is Some by {
789                    if i == c2.idx as int {
790                    } else {
791                        assert(l3.children[i] == c2.children[i]);
792                    }
793                };
794            };
795            l2.as_page_table_owner_pt_inv();
796            assert(l3.pt_inv_children()) by {
797                assert forall |i: int| #![trigger l3.children[i]]
798                    !(0 <= i < l3.children.len())
799                        || (l3.children[i] is Some ==> PageTableOwner(l3.children[i].unwrap()).pt_inv())
800                by {
801                    if 0 <= i < l3.children.len() {
802                        if i == c2.idx as int {
803                            assert(l3.children[i] == Some(l2.as_subtree()));
804                        } else {
805                            assert(l3.children[i] == c2.children[i]);
806                            c2.pt_inv_children_unroll(i);
807                        }
808                    } else {
809                        assert(!(0 <= i < l3.children.len()));
810                    }
811                };
812            };
813
814            c2.inv_children_unroll_all();
815            l3.as_page_table_owner_preserves_view_mappings();
816            l3.as_subtree_inv();
817            c3.view_mappings_put_child(l3.as_subtree());
818            c3.as_subtree_restore(l3);
819            let l4 = c3.restore_spec(l3).0;
820            assert(l4.all_some()) by {
821                assert forall |i: int| 0 <= i < NR_ENTRIES implies l4.children[i] is Some by {
822                    if i == c3.idx as int {
823                    } else {
824                        assert(l4.children[i] == c3.children[i]);
825                    }
826                };
827            };
828            l3.as_page_table_owner_pt_inv();
829            assert(l4.pt_inv_children()) by {
830                assert forall |i: int| #![trigger l4.children[i]]
831                    !(0 <= i < l4.children.len())
832                        || (l4.children[i] is Some ==> PageTableOwner(l4.children[i].unwrap()).pt_inv())
833                by {
834                    if 0 <= i < l4.children.len() {
835                        if i == c3.idx as int {
836                            assert(l4.children[i] == Some(l3.as_subtree()));
837                        } else {
838                            assert(l4.children[i] == c3.children[i]);
839                            c3.pt_inv_children_unroll(i);
840                        }
841                    } else {
842                        assert(!(0 <= i < l4.children.len()));
843                    }
844                };
845            };
846
847            c3.inv_children_unroll_all();
848            l4.as_page_table_owner_preserves_view_mappings();
849
850            assert(self.view_mappings() =~= c0.view_mappings().union(c1.view_mappings()).union(c2.view_mappings()).union(c3.view_mappings())) by {
851                assert forall |m: Mapping| self.view_mappings().contains(m) implies
852                    (c0.view_mappings().contains(m) || c1.view_mappings().contains(m) || c2.view_mappings().contains(m) || c3.view_mappings().contains(m)) by {
853                    let i = choose |i: int| 0 <= i < NR_LEVELS && #[trigger] self.continuations[i].view_mappings().contains(m);
854                };
855            };
856        }
857    }
858
859    /// Every mapping in the cursor view satisfies `Mapping::inv()`.
860    ///
861    /// Collapses the cursor view into a single-root `view_rec` and applies
862    /// `view_rec_mapping_inv`. Inherits the latter's two narrow `assume`s
863    /// on `vaddr(path)` arithmetic.
864    pub proof fn view_mapping_inv(self)
865        requires
866            self.inv(),
867        ensures
868            forall |m: Mapping| self.view_mappings().contains(m) ==> #[trigger] m.inv(),
869    {
870        self.as_page_table_owner_preserves_view_mappings();
871        let pto = self.as_page_table_owner();
872        let root_path = self.continuations[3].path();
873        self.inv_continuation(NR_LEVELS as int - 1);
874        pto.view_rec_mapping_inv(root_path);
875    }
876
877    /// Every mapping in the cursor view has `page_size ∈ {4K, 2M, 1G}`.
878    ///
879    /// Uses the standard collapse trick: `view_mappings` equals
880    /// `as_page_table_owner().view_rec(continuations[3].path())`, then applies
881    /// `view_rec_mapping_page_size`. The root's `parent_level == 5 == INC_LEVELS`
882    /// is given by the cursor invariant (continuations[3].entry_own.parent_level == 5).
883    pub proof fn view_mapping_page_size_valid(self)
884        requires
885            self.inv(),
886        ensures
887            forall |m: Mapping| #[trigger] self.view_mappings().contains(m) ==>
888                set![4096usize, 2097152usize, 1073741824usize].contains(m.page_size),
889    {
890        self.as_page_table_owner_preserves_view_mappings();
891        let pto = self.as_page_table_owner();
892        let root_path = self.continuations[3].path();
893        self.inv_continuation(NR_LEVELS as int - 1);
894        // pto.0.level == continuations[3].tree_level == 0
895        // pto.0.value.parent_level == continuations[3].entry_own.parent_level == 5
896        // == INC_LEVELS == INC_LEVELS - 0 == INC_LEVELS - pto.0.level
897        pto.view_rec_mapping_page_size(root_path);
898    }
899
900    /// Finiteness of the cursor view.
901    ///
902    /// Collapses the union-over-continuations `view_mappings` into a single
903    /// `view_rec` rooted at the reconstructed root page table, then uses
904    /// `view_rec_finite` (bounded depth / branching).
905    pub proof fn view_mappings_finite(self)
906        requires
907            self.inv(),
908        ensures
909            self.view_mappings().finite(),
910    {
911        self.as_page_table_owner_preserves_view_mappings();
912        let pto = self.as_page_table_owner();
913        let root_path = self.continuations[3].path();
914        self.inv_continuation(NR_LEVELS as int - 1);
915        pto.view_rec_finite(root_path);
916    }
917
918    /// Non-overlapping mappings in the cursor view.
919    ///
920    /// Collapses the union-over-continuations `view_mappings` into a single
921    /// `view_rec` rooted at the reconstructed root page table, then applies
922    /// `view_rec_disjoint_vaddrs` on that single subtree.
923    /// Follows from tree structure alone.
924    pub proof fn as_page_table_owner_view_non_overlapping(self)
925        requires
926            self.inv(),
927        ensures
928            self@.non_overlapping(),
929    {
930        self.as_page_table_owner_preserves_view_mappings();
931        let pto = self.as_page_table_owner();
932        let root_path = self.continuations[3].path();
933
934        assert(root_path.len() == self.continuations[3].tree_level);
935        assert(self.continuations[3].tree_level == 0) by {
936            self.inv_continuation(NR_LEVELS as int - 1);
937            // continuations[3].tree_level == INC_LEVELS - continuations[3].level() - 1
938            // and continuations[3].level() == 4 (root).
939        };
940        assert(root_path.len() == 0);
941        assert(pto.0.level == 0);
942
943        assert forall |m: Mapping, n: Mapping|
944            #[trigger] self@.mappings.contains(m) && #[trigger] self@.mappings.contains(n) && m != n implies
945            m.va_range.end <= n.va_range.start || n.va_range.end <= m.va_range.start
946        by {
947            assert(self@.mappings == self.view_mappings());
948            assert(pto.view_rec(root_path).contains(m));
949            assert(pto.view_rec(root_path).contains(n));
950            pto.view_rec_disjoint_vaddrs(root_path, m, n);
951        };
952    }
953}
954
955} // verus!