Skip to main content

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

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