Skip to main content

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

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