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