Skip to main content

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

1use vstd::{
2    arithmetic::{
3        div_mod::{lemma_div_non_zero, lemma_fundamental_div_mod},
4        mul::{lemma_mul_inequality, lemma_mul_is_commutative},
5        power2::pow2,
6    },
7    bits::lemma_usize_shr_is_div,
8    prelude::*,
9    seq_lib::*,
10    set::lemma_set_contains_len,
11};
12use vstd_extra::{
13    arithmetic::lemma_nat_align_down_within_block,
14    drop_tracking::*,
15    ghost_tree::*,
16    ownership::*,
17    panic::may_panic,
18    prelude::*,
19    seq_extra::{forall_seq, lemma_forall_seq_index},
20};
21
22use crate::specs::{
23    arch::*,
24    mm::{
25        frame::{
26            mapping::{frame_to_index, index_to_meta},
27            meta_owners::MetaSlotStorage,
28            meta_region_owners::MetaRegionOwners,
29        },
30        page_table::{owners::*, vaddr_range_proofs::*, *},
31    },
32    task::InAtomicMode,
33};
34
35use crate::arch::mm::PagingConsts;
36use crate::mm::{
37    MAX_USERSPACE_VADDR, Paddr, PagingConstsTrait, PagingLevel, Vaddr,
38    frame::{
39        Frame,
40        meta::{REF_COUNT_MAX, REF_COUNT_UNIQUE, REF_COUNT_UNUSED},
41    },
42    nr_subpage_per_huge,
43    page_prop::PageProperty,
44    page_size,
45    page_table::*,
46};
47use core::{marker::PhantomData, ops::Range};
48
49verus! {
50
51broadcast use group_ghost_tree_lemmas;
52
53pub tracked struct CursorContinuation<'rcu, C: PageTableConfig> {
54    pub entry_own: EntryOwner<C>,
55    pub ghost idx: usize,
56    pub ghost tree_level: nat,
57    pub children: Seq<Option<OwnerSubtree<C>>>,
58    pub ghost path: TreePath<NR_ENTRIES>,
59    pub ghost guard: PageTableGuard<'rcu, C>,
60}
61
62impl<'rcu, C: PageTableConfig> CursorContinuation<'rcu, C> {
63    pub open spec fn path(self) -> TreePath<NR_ENTRIES> {
64        self.entry_own.path
65    }
66
67    pub open spec fn child(self) -> OwnerSubtree<C> {
68        self.children[self.idx as int]->0
69    }
70
71    pub open spec fn take_child(self) -> (OwnerSubtree<C>, Self) {
72        let child = self.children[self.idx as int]->0;
73        let cont = Self {
74            children: self.children.remove(self.idx as int).insert(self.idx as int, None),
75            ..self
76        };
77        (child, cont)
78    }
79
80    pub proof fn tracked_take_child(tracked &mut self) -> (tracked res: OwnerSubtree<C>)
81        requires
82            old(self).inv(),
83            old(self).idx < old(self).children.len(),
84            old(self).children[old(self).idx as int] is Some,
85        ensures
86            res == old(self).take_child().0,
87            *final(self) == old(self).take_child().1,
88            res.inv(),
89    {
90        let tracked child = self.children.tracked_remove(old(self).idx as int).tracked_unwrap();
91        self.children.tracked_insert(old(self).idx as int, None);
92        child
93    }
94
95    pub open spec fn put_child(self, child: OwnerSubtree<C>) -> Self {
96        Self {
97            children: self.children.remove(self.idx as int).insert(self.idx as int, Some(child)),
98            ..self
99        }
100    }
101
102    pub proof fn tracked_put_child(tracked &mut self, tracked child: OwnerSubtree<C>)
103        requires
104            old(self).idx < old(self).children.len(),
105            old(self).children[old(self).idx as int] is None,
106        ensures
107            *final(self) == old(self).put_child(child),
108    {
109        let _ = self.children.tracked_remove(old(self).idx as int);
110        self.children.tracked_insert(old(self).idx as int, Some(child));
111    }
112
113    pub proof fn take_put_child(self)
114        requires
115            self.idx < self.children.len(),
116            self.children[self.idx as int] is Some,
117        ensures
118            self.take_child().1.put_child(self.take_child().0) == self,
119    {
120        let child = self.take_child().0;
121        let cont = self.take_child().1;
122        assert(cont.put_child(child).children == self.children);
123    }
124
125    /// Taking a child preserves the continuation invariant.
126    pub proof fn take_child_preserves_inv(self)
127        requires
128            self.inv(),
129            self.idx < self.children.len(),
130            self.children[self.idx as int] is Some,
131        ensures
132            self.take_child().1.inv(),
133    {
134    }
135
136    pub open spec fn make_cont(self, idx: usize, guard: PageTableGuard<'rcu, C>) -> (Self, Self) {
137        let child = Self {
138            entry_own: self.children[self.idx as int]->0.value(),
139            tree_level: (self.tree_level + 1) as nat,
140            idx: idx,
141            children: self.children[self.idx as int]->0.children(),
142            path: self.path.push_tail(self.idx as int),
143            guard: guard,
144        };
145        let cont = Self { children: self.children.update(self.idx as int, None), ..self };
146        (child, cont)
147    }
148
149    pub proof fn tracked_make_cont(
150        tracked &mut self,
151        idx: usize,
152        guard: PageTableGuard<'rcu, C>,
153    ) -> (tracked res: Self)
154        requires
155            old(self).all_some(),
156            old(self).children.len() == NR_ENTRIES,
157            old(self).idx < NR_ENTRIES,
158            idx < NR_ENTRIES,
159        ensures
160            res == old(self).make_cont(idx, guard).0,
161            *final(self) == old(self).make_cont(idx, guard).1,
162    {
163        lemma_update_is_remove_insert(self.children, old(self).idx as int, None);
164        let tracked child = self.children.tracked_remove(old(self).idx as int).tracked_unwrap();
165        self.children.tracked_insert(old(self).idx as int, None);
166        let tracked (entry_own, children) = child.tracked_into_parts();
167        Self {
168            entry_own,
169            tree_level: (old(self).tree_level + 1) as nat,
170            idx,
171            children,
172            path: old(self).path.push_tail(old(self).idx as int),
173            guard,
174        }
175    }
176
177    pub open spec fn restore(self, child: Self) -> (Self, PageTableGuard<'rcu, C>) {
178        let child_node = OwnerSubtree::new(child.entry_own, child.tree_level, child.children);
179        (
180            Self { children: self.children.update(self.idx as int, Some(child_node)), ..self },
181            child.guard,
182        )
183    }
184
185    pub proof fn tracked_restore(tracked &mut self, tracked child: Self) -> (guard: PageTableGuard<
186        'rcu,
187        C,
188    >)
189        requires
190            old(self).idx < old(self).children.len(),
191        ensures
192            *final(self) == old(self).restore(child).0,
193            guard == old(self).restore(child).1,
194    {
195        let tracked child_node = OwnerSubtree::tracked_new(
196            child.entry_own,
197            child.tree_level,
198            child.children,
199        );
200        lemma_update_is_remove_insert(self.children, self.idx as int, Some(child_node));
201        let _ = self.children.tracked_remove(self.idx as int);
202        self.children.tracked_insert(self.idx as int, Some(child_node));
203        child.guard
204    }
205
206    pub open spec fn new(
207        owner_subtree: OwnerSubtree<C>,
208        idx: usize,
209        guard: PageTableGuard<'rcu, C>,
210    ) -> Self {
211        Self {
212            entry_own: owner_subtree.value(),
213            idx: idx,
214            tree_level: owner_subtree.level(),
215            children: owner_subtree.children(),
216            path: TreePath::new(Seq::empty()),
217            guard: guard,
218        }
219    }
220
221    pub proof fn tracked_new(
222        tracked owner_subtree: OwnerSubtree<C>,
223        idx: usize,
224        guard: PageTableGuard<'rcu, C>,
225    ) -> tracked Self
226        returns
227            Self::new(owner_subtree, idx, guard),
228    {
229        let ghost tree_level = owner_subtree.level();
230        let tracked (entry_own, children) = owner_subtree.tracked_into_parts();
231        Self { entry_own, idx, tree_level, children, path: TreePath::new(Seq::empty()), guard }
232    }
233
234    /// Every present child subtree satisfies `f` at its corresponding tree path.
235    #[verifier::opaque]
236    pub open spec fn map_children(
237        self,
238        f: spec_fn(EntryOwner<C>, TreePath<NR_ENTRIES>) -> bool,
239    ) -> bool {
240        forall|i: int|
241            #![trigger(self.children[i])]
242            0 <= i < self.children.len() ==> self.children[i] is Some
243                ==> self.children[i]->0.subtree_satisfies(self.path().push_tail(i), f)
244    }
245
246    /// Extracts one child's property without exposing the sibling quantifier.
247    pub proof fn lemma_map_children_unroll(
248        self,
249        f: spec_fn(EntryOwner<C>, TreePath<NR_ENTRIES>) -> bool,
250        i: int,
251    )
252        requires
253            self.map_children(f),
254            0 <= i < self.children.len(),
255            self.children[i] is Some,
256        ensures
257            self.children[i]->0.subtree_satisfies(self.path().push_tail(i), f),
258    {
259        reveal(CursorContinuation::map_children);
260    }
261
262    // map_children_lift, lemma_map_children_lift_skip_idx, as_subtree_restore
263    // have been moved to tree_lemmas.rs.
264    pub open spec fn level(self) -> PagingLevel {
265        self.entry_own.node().level()
266    }
267
268    pub open spec fn inv_children(self) -> bool {
269        self.children.all(|child: Option<OwnerSubtree<C>>| child is Some ==> child->0.inv())
270    }
271
272    pub proof fn lemma_inv_children_unroll(self, i: int)
273        requires
274            self.inv_children(),
275            0 <= i < self.children.len(),
276            self.children[i] is Some,
277        ensures
278            self.children[i]->0.inv(),
279    {
280        let pred = |child: Option<OwnerSubtree<C>>| child is Some ==> child.unwrap().inv();
281        assert(pred(self.children[i]));
282    }
283
284    pub proof fn lemma_inv_children_unroll_all(self)
285        requires
286            self.inv_children(),
287        ensures
288            forall|i: int|
289                #![auto]
290                0 <= i < self.children.len() ==> self.children[i] is Some
291                    ==> self.children[i]->0.inv(),
292    {
293        let pred = |child: Option<OwnerSubtree<C>>| child is Some ==> child.unwrap().inv();
294        assert forall|i: int|
295            0 <= i < self.children.len()
296                && #[trigger] self.children[i] is Some implies self.children[i].unwrap().inv() by {
297            self.lemma_inv_children_unroll(i)
298        }
299    }
300
301    pub open spec fn inv_children_rel_pred(self) -> spec_fn(int, Option<OwnerSubtree<C>>) -> bool {
302        |i: int, child: Option<OwnerSubtree<C>>|
303            {
304                child is Some ==> {
305                    &&& child->0.value().parent_level == self.level()
306                    &&& child->0.level() == self.tree_level + 1
307                    &&& child->0.value().path.len() == self.entry_own.node().tree_level + 1
308                    &&& child->0.value().match_pte(
309                        self.entry_own.node().children_perm.value()[i],
310                        self.entry_own.node().level(),
311                    )
312                    &&& child->0.value().path == self.path().push_tail(i)
313                }
314            }
315    }
316
317    pub open spec fn inv_children_rel(self) -> bool {
318        forall_seq(self.children, self.inv_children_rel_pred())
319    }
320
321    pub open spec fn pt_inv_children_pred() -> spec_fn(int, Option<OwnerSubtree<C>>) -> bool {
322        |i: int, child: Option<OwnerSubtree<C>>| child is Some ==> PageTableOwner(child->0).pt_inv()
323    }
324
325    pub open spec fn pt_inv_children(self) -> bool {
326        forall_seq(self.children, Self::pt_inv_children_pred())
327    }
328
329    pub proof fn lemma_pt_inv_children_unroll(self, i: int)
330        requires
331            self.pt_inv_children(),
332            0 <= i < self.children.len(),
333            self.children[i] is Some,
334        ensures
335            PageTableOwner(self.children[i]->0).pt_inv(),
336    {
337    }
338
339    pub proof fn lemma_inv_children_rel_unroll(self, i: int)
340        requires
341            self.inv_children_rel(),
342            0 <= i < self.children.len(),
343            self.children[i] is Some,
344        ensures
345            self.children[i]->0.value().parent_level == self.level(),
346            self.children[i]->0.level() == self.tree_level + 1,
347            self.children[i]->0.value().path.len() == self.entry_own.node().tree_level + 1,
348            self.children[i]->0.value().match_pte(
349                self.entry_own.node().children_perm.value()[i],
350                self.entry_own.node().level(),
351            ),
352            self.children[i]->0.value().path == self.path().push_tail(i),
353    {
354    }
355
356    pub open spec fn inv(self) -> bool {
357        &&& self.children.len() == NR_ENTRIES
358        &&& 0 <= self.idx < NR_ENTRIES
359        &&& self.inv_children()
360        &&& self.inv_children_rel()
361        &&& self.pt_inv_children()
362        &&& self.entry_own.is_node()
363        &&& self.entry_own.inv()
364        &&& self.entry_own.node().relate_guard(self.guard)
365        &&& self.tree_level == INC_LEVELS - self.level() - 1
366        &&& self.tree_level < INC_LEVELS - 1
367        &&& self.path().len() == self.tree_level
368    }
369
370    pub open spec fn all_some(self) -> bool {
371        forall|i: int| 0 <= i < NR_ENTRIES ==> self.children[i] is Some
372    }
373
374    pub open spec fn all_but_index_some(self) -> bool {
375        &&& forall|i: int| 0 <= i < self.idx ==> self.children[i] is Some
376        &&& forall|i: int| self.idx < i < NR_ENTRIES ==> self.children[i] is Some
377        &&& self.children[self.idx as int] is None
378    }
379
380    pub open spec fn inc_index(self) -> Self {
381        Self { idx: (self.idx + 1) as usize, ..self }
382    }
383
384    pub proof fn do_inc_index(tracked &mut self)
385        requires
386            old(self).idx + 1 < NR_ENTRIES,
387        ensures
388            *final(self) == old(self).inc_index(),
389    {
390        self.idx = (self.idx + 1) as usize;
391    }
392
393    pub open spec fn node_locked(self, guards: Guards) -> bool {
394        guards.lock_held(self.guard.inner.inner@.slot_vaddr())
395    }
396
397    pub open spec fn view_mappings(self) -> Set<Mapping> {
398        self.children.map(
399            |i, child: Option<OwnerSubtree<C>>|
400                if child is Some {
401                    PageTableOwner(child->0).view_rec(self.path().push_tail(i))
402                } else {
403                    Set::empty()
404                },
405        ).to_set().flatten()
406    }
407
408    pub broadcast proof fn lemma_view_mappings_contains(self)
409        ensures
410            #![trigger self.view_mappings()]
411            forall|m: Mapping| #[trigger]
412                self.view_mappings().contains(m) ==> exists|i: int|
413                    #![trigger self.children[i]]
414                    0 <= i < self.children.len() && self.children[i] is Some && PageTableOwner(
415                        self.children[i]->0,
416                    ).view_rec(self.path().push_tail(i)).contains(m),
417    {
418        broadcast use vstd::seq_lib::group_seq_properties;
419
420    }
421
422    pub broadcast proof fn lemma_view_mappings_intro(self, m: Mapping, i: int)
423        requires
424            0 <= i < self.children.len(),
425            self.children[i] is Some,
426            #[trigger] PageTableOwner(self.children[i]->0).view_rec(
427                self.path().push_tail(i),
428            ).contains(m),
429        ensures
430            self.view_mappings().contains(m),
431    {
432        broadcast use vstd::seq_lib::group_seq_properties;
433
434        let mapped = self.children.map(
435            |i, child: Option<OwnerSubtree<C>>|
436                if child is Some {
437                    PageTableOwner(child->0).view_rec(self.path().push_tail(i))
438                } else {
439                    Set::empty()
440                },
441        );
442        assert(mapped.to_set().contains(mapped[i]));
443    }
444
445    pub open spec fn as_subtree(self) -> OwnerSubtree<C> {
446        OwnerSubtree::new(self.entry_own, self.tree_level, self.children)
447    }
448
449    pub open spec fn as_page_table_owner(self) -> PageTableOwner<C> {
450        PageTableOwner(self.as_subtree())
451    }
452
453    pub open spec fn view_mappings_take_child_spec(self) -> Set<Mapping> {
454        PageTableOwner(self.children[self.idx as int]->0).view_rec(
455            self.path().push_tail(self.idx as int),
456        )
457    }
458
459    /// Proves `rel_children` for a child that was taken from the continuation, modified
460    /// (by protect, alloc_if_none, or split_if_mapped_huge), and placed back at the same index.
461    ///
462    /// The key inputs are:
463    /// - `node_matching` from the operation's postcondition (provides `match_pte`)
464    /// - The child's path and path length (preserved by the operation)
465    /// - The entry's path (unchanged through the reconstruction)
466    /// Proves `rel_children` from `node_matching`. After taking a child from a continuation,
467    /// modifying it (protect/alloc/split), and restoring `entry_own.node = Some(parent_owner)`,
468    /// `rel_children` holds for any `entry_own` that has `node == Some(parent_owner)` and the
469    /// correct `path`.
470    pub proof fn lemma_rel_children_from_node_matching(
471        entry: &Entry<'_, 'rcu, C>,
472        child_value: EntryOwner<C>,
473        parent_owner: NodeOwner<C>,
474        guard: PageTableGuard<'rcu, C>,
475        entry_own: EntryOwner<C>,
476        idx: usize,
477    )
478        requires
479            entry.node_matching(child_value, parent_owner, guard),
480            entry.idx == idx,
481            entry_own.is_node(),
482            entry_own.node() == parent_owner,
483            child_value.path == entry_own.path.push_tail(idx as int),
484            child_value.path.len() == parent_owner.tree_level + 1,
485        ensures
486            child_value.path.len() == parent_owner.tree_level + 1,
487            child_value.match_pte(
488                parent_owner.children_perm.value()[idx as int],
489                parent_owner.level(),
490            ),
491            child_value.path == entry_own.path.push_tail(idx as int),
492            child_value.parent_level == parent_owner.level(),
493    {
494    }
495
496    /// After restoring `entry_own.node = Some(parent_owner)` and putting the child back
497    /// at `idx`, the continuation invariant holds.
498    ///
499    /// Caller passes the pre-modification continuation `cont_old` and its
500    /// parent_owner `parent_old` so we can recover the per-`j != idx`
501    /// `inv_children_rel`/`pt_inv_children` facts from `cont_old.inv()`.
502    /// Operations that take/restore (alloc_if_none, split_if_mapped_huge,
503    /// protect, replace) all preserve the parent's other PTEs and the
504    /// children at `j != idx`.
505    pub proof fn lemma_continuation_inv_holds_after_child_restore(
506        self,
507        cont_old: Self,
508        parent_old: NodeOwner<C>,
509    )
510        requires
511    // Old continuation was inv with parent_old wired in
512
513            cont_old.inv(),
514            cont_old.entry_own.is_node(),
515            cont_old.entry_own.node() == parent_old,
516            // Frozen fields shared with cont_old
517            self.children.len() == cont_old.children.len(),
518            self.idx == cont_old.idx,
519            self.tree_level == cont_old.tree_level,
520            self.guard == cont_old.guard,
521            self.path == cont_old.path,
522            // entry_own changed only by the Node payload and otherwise keeps
523            // the surrounding owner metadata.
524            self.entry_own.is_absent() == cont_old.entry_own.is_absent(),
525            self.entry_own.path == cont_old.entry_own.path,
526            self.entry_own.parent_level == cont_old.entry_own.parent_level,
527            // entry_own's new parent is well-formed and structurally matches the old
528            self.entry_own.is_node(),
529            self.entry_own.inv(),
530            self.entry_own.node().relate_guard(self.guard),
531            self.entry_own.node().level() == parent_old.level(),
532            self.entry_own.node().tree_level == parent_old.tree_level,
533            // Other PTEs preserved (operation only touched the entry at idx)
534            forall|j: int|
535                0 <= j < NR_ENTRIES && j != self.idx
536                    ==> #[trigger] self.entry_own.node().children_perm.value()[j]
537                    == parent_old.children_perm.value()[j],
538            // Children at j != idx untouched
539            forall|j: int|
540                0 <= j < NR_ENTRIES && j != self.idx ==> #[trigger] self.children[j]
541                    == cont_old.children[j],
542            // Standard size/index facts (also implied by cont_old.inv()
543            // + frozen fields, but stated directly to avoid extra unrolls).
544            self.children.len() == NR_ENTRIES,
545            0 <= self.idx < NR_ENTRIES,
546            self.tree_level == INC_LEVELS - self.level() - 1,
547            self.tree_level < INC_LEVELS - 1,
548            self.path().len() == self.tree_level,
549            // The new child at idx is well-formed
550            self.children[self.idx as int] is Some,
551            self.children[self.idx as int]->0.inv(),
552            self.children[self.idx as int]->0.value().parent_level == self.level(),
553            self.children[self.idx as int]->0.value().path == self.path().push_tail(
554                self.idx as int,
555            ),
556            self.children[self.idx as int]->0.level() == self.tree_level + 1,
557            self.children[self.idx as int]->0.value().path.len() == self.entry_own.node().tree_level
558                + 1,
559            self.children[self.idx as int]->0.value().match_pte(
560                self.entry_own.node().children_perm.value()[self.idx as int],
561                self.entry_own.node().level(),
562            ),
563            // The new child satisfies the PT-specific tree invariant. This is
564            // operation-specific (alloc_if_none/protect/split_if_mapped_huge/
565            // replace each establish it differently) so it's lifted to a
566            // precondition rather than discharged here.
567            PageTableOwner(self.children[self.idx as int]->0).pt_inv(),
568        ensures
569            self.inv(),
570    {
571    }
572
573    pub proof fn tracked_new_child(
574        tracked &self,
575        paddr: Paddr,
576        prop: PageProperty,
577        tracked permission: Option<C::Perm>,
578        tracked regions: &mut MetaRegionOwners,
579    ) -> (tracked res: OwnerSubtree<C>)
580        requires
581            self.inv(),
582            self.level() < NR_LEVELS,
583            old(regions).slots.contains_key(frame_to_index(paddr)),
584            valid_frame_paddr(paddr),
585            paddr % page_size::<PagingConsts>(self.level()) == 0,
586            paddr + page_size::<PagingConsts>(self.level()) <= MAX_PADDR,
587            C::raw_item_well_formed((paddr, self.level(), prop, Tracked(permission))),
588            C::E::new_page_req(paddr, self.level(), prop),
589            self.path().push_tail(self.idx as int).inv(),
590        ensures
591            final(regions).slot_owners == old(regions).slot_owners,
592            final(regions).slots == old(regions).slots,
593            // Allocating a child doesn't touch the segment obligation ledger.
594            res.value() == EntryOwner::<C>::new_frame(
595                paddr,
596                self.path().push_tail(self.idx as int),
597                self.level(),
598                prop,
599                permission,
600            ),
601            res.inv(),
602            res.level() == self.tree_level + 1,
603            res == OwnerSubtree::new_val(res.value(), res.level() as nat),
604    {
605        let tracked mut owner = EntryOwner::<C>::tracked_new_frame(
606            paddr,
607            self.path().push_tail(self.idx as int),
608            self.level(),
609            prop,
610            permission,
611        );
612        OwnerSubtree::tracked_new_val(owner, self.tree_level + 1)
613    }
614
615    pub broadcast group group_lemmas {
616        CursorContinuation::lemma_view_mappings_contains,
617        CursorContinuation::lemma_view_mappings_intro,
618    }
619}
620
621pub tracked struct CursorOwner<'rcu, C: PageTableConfig> {
622    pub ghost level: PagingLevel,
623    pub continuations: Map<int, CursorContinuation<'rcu, C>>,
624    pub ghost va: Vaddr,
625    pub ghost barrier_va: Range<Vaddr>,
626    pub ghost guard_level: PagingLevel,
627    pub ghost prefix: Vaddr,
628    pub ghost popped_too_high: bool,
629}
630
631impl<'rcu, C: PageTableConfig> Inv for CursorOwner<'rcu, C> {
632    open spec fn inv(self) -> bool {
633        &&& self.va % C::BASE_PAGE_SIZE() == 0
634        &&& 1 <= self.level <= NR_LEVELS
635        &&& 1 <= self.guard_level
636            <= NR_LEVELS
637        // A terminal cursor may equal the managed range's exclusive end.
638        &&& page_table_managed_start_spec::<C>() <= self.va <= page_table_managed_end_spec::<C>()
639        &&& self.locked_range().start <= self.barrier_va.start < self.barrier_va.end
640            <= self.locked_range().end
641        &&& self.barrier_va.start % C::BASE_PAGE_SIZE() == 0
642        &&& self.barrier_va.end % C::BASE_PAGE_SIZE() == 0
643        &&& is_valid_range_spec::<C>(self.barrier_va)
644        &&& self.barrier_va.end + page_size::<C>(self.guard_level)
645            <= page_table_coverage_end_spec::<
646            C,
647        >()
648        // End states may retain a cursor level above the guard.
649        &&& self.popped_too_high ==> self.level >= self.guard_level
650        &&& !self.popped_too_high ==> self.level <= self.guard_level
651            || self.at_or_past_barrier_end()
652        &&& self.continuations[self.level - 1].all_some()
653        &&& forall|i: int|
654            self.level <= i < NR_LEVELS ==> {
655                (#[trigger] self.continuations[i]).all_but_index_some()
656            }
657        &&& self.level <= NR_LEVELS - 1 ==> {
658            &&& C::TOP_LEVEL_INDEX_RANGE().start <= self.continuations[NR_LEVELS - 1].idx
659            &&& self.continuations[NR_LEVELS - 1].idx < C::TOP_LEVEL_INDEX_RANGE().end
660        }
661        &&& forall|j: int|
662            #![trigger self.continuations[NR_LEVELS - 1].children[j]]
663            0 <= j < NR_ENTRIES && !(C::TOP_LEVEL_INDEX_RANGE().start <= j
664                < C::TOP_LEVEL_INDEX_RANGE().end) ==> self.continuations[NR_LEVELS
665                - 1].children[j] is Some ==> (self.continuations[NR_LEVELS
666                - 1].children[j].unwrap().value().is_borrowed() || self.continuations[NR_LEVELS
667                - 1].children[j].unwrap().value().is_absent())
668        &&& self.prefix % C::BASE_PAGE_SIZE() == 0
669        &&& self.prefix <= self.va
670        &&& forall|i: int|
671            1 <= i <= self.guard_level ==> #[trigger] pte_index_spec::<C>(
672                self.prefix,
673                i as PagingLevel,
674            ) == 0
675        &&& self.guard_level < NR_LEVELS ==> page_table_managed_start_spec::<C>() <= self.prefix
676            < page_table_managed_end_spec::<C>()
677        &&& vaddr_leading_part_spec::<C>(self.va) == vaddr_leading_part_spec::<C>(self.prefix)
678        &&& vaddr_leading_part_spec::<C>(self.prefix) == page_table_coverage_start_spec::<C>()
679        &&& self.level <= self.guard_level ==> forall|i: int|
680            #![trigger self.continuations[i].idx]
681            self.guard_level <= i < NR_LEVELS ==> self.continuations[i].idx == pte_index_spec::<C>(
682                self.prefix,
683                (i + 1) as PagingLevel,
684            )
685        &&& !self.popped_too_high && (self.in_locked_range() || self.level < self.guard_level)
686            ==> forall|i: int|
687            self.guard_level <= i < NR_LEVELS ==> #[trigger] pte_index_spec::<C>(
688                self.va,
689                (i + 1) as PagingLevel,
690            ) == pte_index_spec::<C>(self.prefix, (i + 1) as PagingLevel)
691        &&& self.level <= 4 ==> {
692            &&& self.continuations.contains_key(3)
693            &&& self.continuations[3].inv()
694            &&& self.continuations[3].level()
695                == 4
696            // Obviously there is no level 5 pt, but that would be the level of the parent of the root pt.
697            &&& self.continuations[3].entry_own.parent_level
698                == 5
699            // After leaving the guard node, its continuations retain their old indices.
700            &&& self.in_locked_range() ==> pte_index_spec::<C>(self.va, (3 + 1) as PagingLevel)
701                == self.continuations[3].idx
702        }
703        &&& self.level <= 3 ==> {
704            &&& self.continuations.contains_key(2)
705            &&& self.continuations[2].inv()
706            &&& self.continuations[2].level() == 3
707            &&& self.continuations[2].entry_own.parent_level == 4
708            &&& self.in_locked_range() ==> pte_index_spec::<C>(self.va, (2 + 1) as PagingLevel)
709                == self.continuations[2].idx
710            &&& self.continuations[2].guard.inner.inner@.slot_vaddr()
711                != self.continuations[3].guard.inner.inner@.slot_vaddr()
712            // Path consistency: child path = parent path pushed with parent's index
713            &&& self.continuations[2].path() == self.continuations[3].path().push_tail(
714                self.continuations[3].idx as int,
715            )
716            // PTE consistency
717            &&& self.continuations[2].entry_own.path.len()
718                == self.continuations[3].entry_own.node().tree_level + 1
719            &&& self.continuations[2].entry_own.match_pte(
720                self.continuations[3].entry_own.node().children_perm.value()[self.continuations[3].idx as int],
721                self.continuations[3].entry_own.node().level(),
722            )
723            &&& self.continuations[2].entry_own.parent_level
724                == self.continuations[3].entry_own.node().level()
725        }
726        &&& self.level <= 2 ==> {
727            &&& self.continuations.contains_key(1)
728            &&& self.continuations[1].inv()
729            &&& self.continuations[1].level() == 2
730            &&& self.continuations[1].entry_own.parent_level == 3
731            &&& self.in_locked_range() ==> pte_index_spec::<C>(self.va, (1 + 1) as PagingLevel)
732                == self.continuations[1].idx
733            &&& self.continuations[1].guard.inner.inner@.slot_vaddr()
734                != self.continuations[2].guard.inner.inner@.slot_vaddr()
735            &&& self.continuations[1].guard.inner.inner@.slot_vaddr()
736                != self.continuations[3].guard.inner.inner@.slot_vaddr()
737            // Path consistency: child path = parent path pushed with parent's index
738            &&& self.continuations[1].path() == self.continuations[2].path().push_tail(
739                self.continuations[2].idx as int,
740            )
741            // PTE consistency
742            &&& self.continuations[1].entry_own.path.len()
743                == self.continuations[2].entry_own.node().tree_level + 1
744            &&& self.continuations[1].entry_own.match_pte(
745                self.continuations[2].entry_own.node().children_perm.value()[self.continuations[2].idx as int],
746                self.continuations[2].entry_own.node().level(),
747            )
748            &&& self.continuations[1].entry_own.parent_level
749                == self.continuations[2].entry_own.node().level()
750        }
751        &&& self.level == 1 ==> {
752            &&& self.continuations.contains_key(0)
753            &&& self.continuations[0].inv()
754            &&& self.continuations[0].level() == 1
755            &&& self.continuations[0].entry_own.parent_level == 2
756            &&& self.in_locked_range() ==> pte_index_spec::<C>(self.va, (0 + 1) as PagingLevel)
757                == self.continuations[0].idx
758            &&& self.continuations[0].guard.inner.inner@.slot_vaddr()
759                != self.continuations[1].guard.inner.inner@.slot_vaddr()
760            &&& self.continuations[0].guard.inner.inner@.slot_vaddr()
761                != self.continuations[2].guard.inner.inner@.slot_vaddr()
762            &&& self.continuations[0].guard.inner.inner@.slot_vaddr()
763                != self.continuations[3].guard.inner.inner@.slot_vaddr()
764            // Path consistency: child path = parent path pushed with parent's index
765            &&& self.continuations[0].path() == self.continuations[1].path().push_tail(
766                self.continuations[1].idx as int,
767            )
768            // PTE consistency
769            &&& self.continuations[0].entry_own.path.len()
770                == self.continuations[1].entry_own.node().tree_level + 1
771            &&& self.continuations[0].entry_own.match_pte(
772                self.continuations[1].entry_own.node().children_perm.value()[self.continuations[1].idx as int],
773                self.continuations[1].entry_own.node().level(),
774            )
775            &&& self.continuations[0].entry_own.parent_level
776                == self.continuations[1].entry_own.node().level()
777        }
778    }
779}
780
781impl<'rcu, C: PageTableConfig> CursorOwner<'rcu, C> {
782    pub open spec fn node_unlocked(guards: Guards) -> (spec_fn(
783        EntryOwner<C>,
784        TreePath<NR_ENTRIES>,
785    ) -> bool) {
786        |owner: EntryOwner<C>, path: TreePath<NR_ENTRIES>|
787            owner.is_node() ==> guards.unlocked(owner.node().slot_vaddr())
788    }
789
790    pub open spec fn node_unlocked_except(guards: Guards, addr: usize) -> (spec_fn(
791        EntryOwner<C>,
792        TreePath<NR_ENTRIES>,
793    ) -> bool) {
794        |owner: EntryOwner<C>, path: TreePath<NR_ENTRIES>|
795            owner.is_node() ==> owner.node().slot_vaddr() != addr ==> guards.unlocked(
796                owner.node().slot_vaddr(),
797            )
798    }
799
800    pub open spec fn map_full_tree(
801        self,
802        f: spec_fn(EntryOwner<C>, TreePath<NR_ENTRIES>) -> bool,
803    ) -> bool {
804        forall|i: int|
805            #![trigger self.continuations[i]]
806            self.level - 1 <= i < NR_LEVELS ==> { self.continuations[i].map_children(f) }
807    }
808
809    pub open spec fn children_not_locked(self, guards: Guards) -> bool {
810        self.map_full_tree(Self::node_unlocked(guards))
811    }
812
813    pub proof fn lemma_children_not_locked_unroll(self, guards: Guards)
814        requires
815            self.children_not_locked(guards),
816        ensures
817            forall|i: int|
818                #![trigger self.continuations[i]]
819                self.level - 1 <= i < NR_LEVELS ==> self.continuations[i].map_children(
820                    Self::node_unlocked(guards),
821                ),
822    {
823    }
824
825    pub open spec fn only_current_locked(self, guards: Guards) -> bool {
826        self.map_full_tree(
827            Self::node_unlocked_except(guards, self.cur_entry_owner().node().slot_vaddr()),
828        )
829    }
830
831    pub proof fn lemma_never_drop_restores_children_not_locked(
832        self,
833        guard: PageTableGuard<'rcu, C>,
834        guards0: Guards,
835        guards1: Guards,
836    )
837        requires
838            self.inv(),
839            self.only_current_locked(guards0),
840            guards0.lock_held(guard.inner.inner@.slot_vaddr()),
841            guards1.guards == guards0.guards.remove(guard.inner.inner@.slot_vaddr()),
842            self.cur_entry_owner().is_node(),
843            guard.inner.inner@.slot_vaddr() == self.cur_entry_owner().node().slot_vaddr(),
844        ensures
845            self.children_not_locked(guards1),
846    {
847        let current_addr = self.cur_entry_owner().node().slot_vaddr();
848        let f = Self::node_unlocked_except(guards0, current_addr);
849        let g = Self::node_unlocked(guards1);
850
851        self.lemma_map_children_implies(f, g);
852    }
853
854    pub open spec fn nodes_locked(self, guards: Guards) -> bool {
855        forall|i: int|
856            #![trigger self.continuations[i]]
857            self.level - 1 <= i < self.guard_level ==> { self.continuations[i].node_locked(guards) }
858    }
859
860    pub open spec fn index(self) -> usize {
861        self.continuations[self.level - 1].idx
862    }
863
864    pub open spec fn inc_index(self) -> Self {
865        Self {
866            continuations: self.continuations.insert(
867                self.level - 1,
868                self.continuations[self.level - 1].inc_index(),
869            ),
870            va: vaddr_replace_pte_index_spec::<C>(
871                self.va,
872                self.level,
873                self.continuations[self.level - 1].inc_index().idx as int,
874            ),
875            popped_too_high: false,
876            ..self
877        }
878    }
879
880    /// Advances to the next slot boundary in one ghost update, without exposing an unaligned intermediate VA.
881    #[verifier::spinoff_prover]
882    pub proof fn do_inc_index_and_zero(tracked &mut self)
883        requires
884            old(self).inv(),
885            old(self).level <= old(self).guard_level,
886            old(self).in_barrier_va(),
887            old(self).continuations[old(self).level - 1].idx + 1 < NR_ENTRIES,
888            old(self).level == NR_LEVELS ==> (old(self).continuations[old(self).level - 1].idx + 1)
889                <= C::TOP_LEVEL_INDEX_RANGE().end,
890        ensures
891            final(self).inv(),
892            *final(self) == old(self).inc_index().zero_below_level(),
893    {
894        C::lemma_paging_consts_properties();
895        C::axiom_current_paging_consts_hardcoded();
896        lemma_page_table_managed_index_bounds::<C>(old(self).va);
897        lemma_inc_slot_indices::<C>(old(self).va, old(self).level);
898        old(self).lemma_inc_index_va();
899        old(self).inc_and_zero_increases_va();
900        lemma_align_down_indices::<C>(old(self).inc_index().va, old(self).level);
901        lemma_page_size_divides::<C>(old(self).level, C::NR_LEVELS());
902        lemma_nat_align_down_within_block(
903            old(self).va as nat,
904            page_size::<C>(old(self).level) as nat,
905            page_size::<C>(C::NR_LEVELS()) as nat,
906        );
907        self.popped_too_high = false;
908        let tracked mut cont = self.continuations.tracked_remove(self.level - 1);
909        cont.do_inc_index();
910        self.va = old(self).inc_index().zero_below_level().va;
911        lemma_page_table_managed_index_bounds::<C>(self.va);
912        self.continuations.tracked_insert(self.level - 1, cont);
913        assert(self.continuations == old(self).continuations.insert(self.level - 1, cont));
914        if self.in_barrier_va() {
915            old(self).lemma_barrier_va_bounds(self.va);
916        }
917    }
918
919    pub proof fn lemma_inv_continuation(self, i: int)
920        requires
921            self.inv(),
922            self.level - 1 <= i <= NR_LEVELS - 1,
923        ensures
924            self.continuations.contains_key(i),
925            self.continuations[i].inv(),
926            self.continuations[i].children.len() == NR_ENTRIES,
927    {
928    }
929
930    pub open spec fn view_mappings(self) -> Set<Mapping> {
931        self.continuations.filter_keys(|k| self.level - 1 <= k < NR_LEVELS).map_values(
932            |cont: CursorContinuation<'rcu, C>| cont.view_mappings(),
933        ).values().flatten()
934    }
935
936    pub broadcast proof fn lemma_view_mappings_contains(self)
937        requires
938            1 <= self.level <= NR_LEVELS,
939        ensures
940            #![trigger self.view_mappings()]
941            forall|m: Mapping| #[trigger]
942                self.view_mappings().contains(m) ==> exists|i: int|
943                    #![trigger self.continuations[i]]
944                    self.level - 1 <= i < NR_LEVELS
945                        && self.continuations[i].view_mappings().contains(m),
946    {
947        broadcast use vstd::map_lib::group_map_properties;
948
949    }
950
951    pub broadcast proof fn lemma_view_mappings_intro(self, m: Mapping, i: int)
952        requires
953            1 <= self.level <= NR_LEVELS,
954            self.level - 1 <= i < NR_LEVELS,
955            self.continuations.contains_key(i),
956            #[trigger] self.continuations[i].view_mappings().contains(m),
957        ensures
958            self.view_mappings().contains(m),
959    {
960        broadcast use vstd::map_lib::group_map_properties;
961
962        let filtered = self.continuations.filter_keys(|k| self.level - 1 <= k < NR_LEVELS);
963        let mapped = filtered.map_values(|cont: CursorContinuation<'rcu, C>| cont.view_mappings());
964        let values = mapped.values();
965        assert(values.contains(mapped[i]));
966    }
967
968    pub open spec fn as_page_table_owner(self) -> PageTableOwner<C> {
969        if self.level == 1 {
970            let l1 = self.continuations[0];
971            let l2 = self.continuations[1].restore(l1).0;
972            let l3 = self.continuations[2].restore(l2).0;
973            let l4 = self.continuations[3].restore(l3).0;
974            l4.as_page_table_owner()
975        } else if self.level == 2 {
976            let l2 = self.continuations[1];
977            let l3 = self.continuations[2].restore(l2).0;
978            let l4 = self.continuations[3].restore(l3).0;
979            l4.as_page_table_owner()
980        } else if self.level == 3 {
981            let l3 = self.continuations[2];
982            let l4 = self.continuations[3].restore(l3).0;
983            l4.as_page_table_owner()
984        } else {
985            let l4 = self.continuations[3];
986            l4.as_page_table_owner()
987        }
988    }
989
990    pub open spec fn cur_entry_owner(self) -> EntryOwner<C> {
991        self.cur_subtree().value()
992    }
993
994    pub open spec fn cur_subtree(self) -> OwnerSubtree<C> {
995        self.continuations[self.level - 1].children[self.index() as int]->0
996    }
997
998    /// Proves `clone_requires` for the item reconstructed from the current frame entry.
999    pub proof fn lemma_cur_frame_clone_requires(
1000        self,
1001        item: C::Item,
1002        pa: Paddr,
1003        level: PagingLevel,
1004        prop: PageProperty,
1005        regions: MetaRegionOwners,
1006    )
1007        requires
1008            self.inv(),
1009            regions.inv(),
1010            self.metaregion_sound(regions),
1011            self.cur_entry_owner().is_frame(),
1012            pa == self.cur_entry_owner().frame().mapped_pa,
1013            C::item_from_raw(pa, level, prop, C::item_into_raw(item).3) == item,
1014            C::item_into_raw(item).3@ == self.cur_entry_owner().frame_permission(),
1015            valid_frame_paddr(pa),
1016            C::raw_item_well_formed((pa, level, prop, C::item_into_raw(item).3)),
1017            // The recorded entry trackedness matches the item being cloned.
1018            (C::item_into_raw(item).3@ is Some) == self.cur_entry_owner().frame_is_tracked(),
1019            // Saturation aborts (Arc-style) via `inc_ref_count`'s diverging panic.
1020            C::item_into_raw(item).3@ is Some ==> (regions.slot_owner(pa).ref_count()
1021                < REF_COUNT_MAX || may_panic()),
1022        ensures
1023            item.clone_requires(regions),
1024    {
1025        let cont = self.continuations[self.level - 1];
1026        cont.lemma_map_children_unroll(
1027            PageTableOwner::<C>::metaregion_sound_pred(regions),
1028            cont.idx as int,
1029        );
1030        C::lemma_clone_requires_concrete(item, pa, level, prop, regions);
1031    }
1032
1033    /// Incrementing the ref count of the current frame preserves `regions.inv()` and
1034    /// `self.metaregion_sound(new_regions)`.
1035    pub proof fn lemma_clone_item_preserves_invariants(
1036        self,
1037        old_regions: MetaRegionOwners,
1038        new_regions: MetaRegionOwners,
1039        idx: int,
1040    )
1041        requires
1042            self.inv(),
1043            self.metaregion_sound(old_regions),
1044            old_regions.inv(),
1045            self.cur_entry_owner().is_frame(),
1046            idx == frame_to_index(self.cur_entry_owner().frame().mapped_pa),
1047            old_regions.slot_owners.contains_key(idx),
1048            new_regions.slot_owners.contains_key(idx),
1049            // rc at idx is incremented by 1
1050            new_regions.ref_count(idx) == old_regions.ref_count(idx) + 1,
1051            // All other inner_perms fields at idx are identical (same tracked object)
1052            new_regions.slot_owners[idx].ref_count_perm.id()
1053                == old_regions.slot_owners[idx].ref_count_perm.id(),
1054            new_regions.slot_owners[idx].metadata_perm.id()
1055                == old_regions.slot_owners[idx].metadata_perm.id(),
1056            new_regions.slot_owners[idx].metadata_perm.frac() + 1
1057                == old_regions.slot_owners[idx].metadata_perm.frac(),
1058            new_regions.slot_owners[idx].in_list_perm == old_regions.slot_owners[idx].in_list_perm,
1059            // Other MetaSlotOwner fields at idx unchanged
1060            new_regions.slot_owners[idx].paths_in_pt == old_regions.slot_owners[idx].paths_in_pt,
1061            new_regions.slot_owners[idx].slot_vaddr == old_regions.slot_owners[idx].slot_vaddr,
1062            new_regions.slot_owners[idx].usage == old_regions.slot_owners[idx].usage,
1063            // All other slot_owners unchanged
1064            new_regions.slot_owners.dom() == old_regions.slot_owners.dom(),
1065            forall|i: int|
1066                #![trigger new_regions.slot_owners[i]]
1067                i != idx && old_regions.slot_owners.contains_key(i) ==> new_regions.slot_owners[i]
1068                    == old_regions.slot_owners[i],
1069            // slots map unchanged
1070            new_regions.slots == old_regions.slots,
1071            new_regions.inv(),
1072            // obligation ledger unchanged (clone bumps a ref count only)
1073            // rc overflow guard: old rc is a normal shared count; the bumped rc fits
1074            // in the valid `[1, REF_COUNT_MAX]` range. The `<=` form (vs strict `<`)
1075            // matches what callers actually have: post-`clone_item`, the new rc is
1076            // bounded by the slot's `inv()` (which permits `rc == REF_COUNT_MAX`).
1077            0 < old_regions.ref_count(idx),
1078            old_regions.ref_count(idx) + 1 <= REF_COUNT_MAX,
1079        ensures
1080            new_regions.inv(),
1081            self.metaregion_sound(new_regions),
1082    {
1083        self.lemma_metaregion_slot_owners_rc_increment(old_regions, new_regions, idx);
1084    }
1085
1086    /// A new frame subtree at the current position has mappings equal to the singleton
1087    /// mapping covering the current slot range.
1088    pub proof fn lemma_new_child_mappings_eq_target(
1089        self,
1090        new_subtree: OwnerSubtree<C>,
1091        pa: Paddr,
1092        level: PagingLevel,
1093        prop: PageProperty,
1094    )
1095        requires
1096            self.inv(),
1097            self.in_barrier_va(),
1098            level == self.level,
1099            new_subtree.inv(),
1100            new_subtree.value().is_frame(),
1101            new_subtree.value().path == self.continuations[self.level - 1].path().push_tail(
1102                self.continuations[self.level - 1].idx as int,
1103            ),
1104            new_subtree.value().frame().mapped_pa == pa,
1105            new_subtree.value().frame().prop == prop,
1106        ensures
1107            PageTableOwner(new_subtree)@.mappings
1108                == set![Mapping {
1109                va_range: self@.cur_slot_range(page_size::<PagingConsts>(level)),
1110                pa_range: pa..(pa + page_size::<PagingConsts>(level)) as usize,
1111                page_size: page_size::<PagingConsts>(level),
1112                property: prop,
1113            }],
1114    {
1115        self.cur_va_in_subtree_range();
1116    }
1117
1118    /// VA interval covered by the locked node at `guard_level`, starting at `prefix`.
1119    /// Integer endpoints can represent the end of the address space.
1120    pub open spec fn locked_range(self) -> Range<int> {
1121        let size = page_size::<C>((self.guard_level + 1) as PagingLevel) as int;
1122        Range { start: self.prefix as int, end: self.prefix as int + size }
1123    }
1124
1125    /// Whether the current VA lies in the guard node's VA interval.
1126    pub open spec fn in_locked_range(self) -> bool {
1127        self.locked_range().start <= self.va < self.locked_range().end
1128    }
1129
1130    /// Whether the current VA belongs to the range requested when creating the cursor.
1131    pub open spec fn in_barrier_va(self) -> bool {
1132        self.barrier_va.start <= self.va < self.barrier_va.end
1133    }
1134
1135    /// Whether the current VA has reached or passed the requested range's end.
1136    pub open spec fn at_or_past_barrier_end(self) -> bool {
1137        self.va >= self.barrier_va.end
1138    }
1139
1140    /// Active cursor addresses select a root entry in the configured index range.
1141    pub proof fn lemma_in_barrier_va_top_index_bounds(self)
1142        requires
1143            self.inv(),
1144            self.in_barrier_va(),
1145            !self.popped_too_high,
1146        ensures
1147            C::TOP_LEVEL_INDEX_RANGE().start <= pte_index_spec::<C>(self.va, C::NR_LEVELS())
1148                < C::TOP_LEVEL_INDEX_RANGE().end,
1149    {
1150        self.lemma_barrier_va_bounds(self.va);
1151    }
1152
1153    pub proof fn lemma_in_barrier_va_level_le_guard_level(self)
1154        requires
1155            self.inv(),
1156            self.in_barrier_va(),
1157            !self.popped_too_high,
1158        ensures
1159            self.level <= self.guard_level,
1160    {
1161    }
1162
1163    /// The structural locked range spans the full guard node.
1164    pub proof fn lemma_locked_range_span(self)
1165        requires
1166            self.inv(),
1167        ensures
1168            self.locked_range().start == self.prefix,
1169            self.locked_range().end == self.prefix + page_size::<PagingConsts>(
1170                (self.guard_level + 1) as PagingLevel,
1171            ),
1172            self.prefix % page_size::<PagingConsts>((self.guard_level + 1) as PagingLevel) == 0,
1173    {
1174        C::lemma_paging_consts_properties();
1175        C::axiom_current_paging_consts_hardcoded();
1176        lemma_lower_indices_aligned::<C>(self.prefix, (self.guard_level + 1) as PagingLevel);
1177    }
1178
1179    pub proof fn lemma_prefix_aligned_to_guard_level(self)
1180        requires
1181            self.inv(),
1182        ensures
1183            self.prefix as nat % page_size::<PagingConsts>(self.guard_level) as nat == 0,
1184    {
1185        C::lemma_paging_consts_properties();
1186        C::axiom_current_paging_consts_hardcoded();
1187        lemma_lower_indices_aligned::<C>(self.prefix, self.guard_level);
1188    }
1189
1190    /// At the top guard level, the node determined by the cursor's upper address bits
1191    /// contains the entire locked range, even after the cursor leaves that range.
1192    pub proof fn lemma_in_node_holds_at_top(self, self_va: Vaddr, va: Vaddr, node_size: usize)
1193        requires
1194            self.inv(),
1195            self_va == self.va,
1196            self.guard_level == NR_LEVELS,
1197            node_size == page_size::<PagingConsts>((NR_LEVELS + 1) as PagingLevel),
1198            self.locked_range().start <= va < self.locked_range().end,
1199        ensures
1200            nat_align_down(self_va as nat, node_size as nat) <= va as nat,
1201            (va as nat) - nat_align_down(self_va as nat, node_size as nat) < node_size as nat,
1202    {
1203        C::lemma_paging_consts_properties();
1204        C::axiom_current_paging_consts_hardcoded();
1205        self.lemma_locked_range_span();
1206        let body_level = (C::NR_LEVELS() + 1) as PagingLevel;
1207        lemma_page_size_is_pow2_pte_index_bit_offset::<C>(body_level);
1208        lemma_page_size_next::<C>(self.guard_level);
1209        lemma_usize_shr_is_div(self_va, nr_page_table_coverage_bits_spec::<C>());
1210        lemma_fundamental_div_mod(self_va as int, node_size as int);
1211        lemma_mul_is_commutative(self_va as int / node_size as int, node_size as int);
1212        lemma_nat_align_down_sound(self_va as nat, node_size as nat);
1213        assert(nat_align_down(self_va as nat, node_size as nat) == vaddr_leading_part_spec::<C>(
1214            self_va,
1215        ));
1216
1217        lemma_vaddr_leading_part_is_align_down::<C>(self_va);
1218        lemma_vaddr_leading_part_is_align_down::<C>(self.prefix);
1219        lemma_lower_indices_aligned::<C>(self.prefix, body_level);
1220        lemma_usize_shr_is_div(self.prefix, nr_page_table_coverage_bits_spec::<C>());
1221        lemma_fundamental_div_mod(self.prefix as int, node_size as int);
1222        assert(nat_align_down(self_va as nat, node_size as nat) == self.prefix);
1223    }
1224
1225    /// Guard-level headroom bounds active slot arithmetic without excluding the final top-level slot.
1226    pub proof fn lemma_va_plus_page_size_no_overflow(self, level: PagingLevel)
1227        requires
1228            self.inv(),
1229            self.in_barrier_va(),
1230            1 <= level <= self.guard_level,
1231        ensures
1232            self.va + page_size::<PagingConsts>(level) <= usize::MAX,
1233            self.va + page_size::<PagingConsts>(level) < page_table_coverage_end_spec::<C>(),
1234    {
1235        C::lemma_paging_consts_properties();
1236        C::axiom_current_paging_consts_hardcoded();
1237        lemma_leading_bits_properties::<C>();
1238        lemma_page_size_is_pow2_pte_index_bit_offset::<C>((C::NR_LEVELS() + 1) as PagingLevel);
1239        lemma_page_size_monotone::<C>(level, self.guard_level);
1240        vstd::arithmetic::power2::lemma2_to64();
1241        vstd::layout::unsigned_int_max_values();
1242    }
1243
1244    pub proof fn lemma_locked_range_page_aligned(self)
1245        requires
1246            self.inv(),
1247        ensures
1248            self.locked_range().end % (PAGE_SIZE as int) == 0,
1249            self.locked_range().start % (PAGE_SIZE as int) == 0,
1250    {
1251        self.lemma_locked_range_span();
1252        lemma_page_size_base::<PagingConsts>();
1253        C::lemma_paging_consts_properties();
1254        C::axiom_current_paging_consts_hardcoded();
1255        lemma_page_size_divides::<PagingConsts>(1, (self.guard_level + 1) as PagingLevel);
1256        lemma_fundamental_div_mod(
1257            page_size::<PagingConsts>((self.guard_level + 1) as PagingLevel) as int,
1258            PAGE_SIZE as int,
1259        );
1260        assert(self.locked_range().start % (PAGE_SIZE as int) == 0);
1261        assert(page_size::<PagingConsts>((self.guard_level + 1) as PagingLevel) % PAGE_SIZE == 0);
1262        vstd::arithmetic::div_mod::lemma_add_mod_noop(
1263            self.prefix as int,
1264            page_size::<PagingConsts>((self.guard_level + 1) as PagingLevel) as int,
1265            PAGE_SIZE as int,
1266        );
1267    }
1268
1269    pub proof fn lemma_cur_subtree_inv(self)
1270        requires
1271            self.inv(),
1272        ensures
1273            self.cur_subtree().inv(),
1274    {
1275        let cont = self.continuations[self.level - 1];
1276        cont.lemma_inv_children_unroll(cont.idx as int)
1277    }
1278
1279    /// Constructs an absent subtree that fits the cursor's current slot.
1280    pub proof fn tracked_new_absent_subtree(self) -> (tracked res: OwnerSubtree<C>)
1281        requires
1282            self.inv(),
1283        ensures
1284            res.inv(),
1285            res.value().is_absent(),
1286            res.level() == self.continuations[self.level - 1].tree_level + 1,
1287            res.value().path.len() <= INC_LEVELS - 1,
1288            res.value().parent_level == self.continuations[self.level
1289                - 1].child().value().parent_level,
1290            res.value().path == self.continuations[self.level - 1].path().push_tail(
1291                self.continuations[self.level - 1].idx as int,
1292            ),
1293            res == OwnerSubtree::new_val(res.value(), res.level() as nat),
1294    {
1295        let cont = self.continuations[self.level - 1];
1296        self.lemma_inv_continuation(self.level - 1);
1297
1298        cont.lemma_inv_children_unroll(cont.idx as int);
1299        cont.lemma_inv_children_rel_unroll(cont.idx as int);
1300
1301        let tracked entry = EntryOwner::tracked_new_absent(self.cur_entry_owner().path, self.level);
1302
1303        let tracked subtree = OwnerSubtree::tracked_new_val(entry, cont.tree_level + 1);
1304        subtree
1305    }
1306
1307    /// If the current entry is absent, `!self@.present()`.
1308    pub proof fn lemma_cur_entry_absent_not_present(self)
1309        requires
1310            self.inv(),
1311            self.in_barrier_va(),
1312            self.cur_entry_owner().is_absent(),
1313        ensures
1314            !self@.present(),
1315    {
1316        let cur_va = self.cur_va();
1317
1318        assert forall|m: Mapping| self.view_mappings().contains(m) implies !(m.va_range.start
1319            <= cur_va < m.va_range.end) by {
1320            if m.va_range.start <= cur_va < m.va_range.end {
1321                self.mapping_covering_cur_va_from_cur_subtree(m);
1322            }
1323        };
1324
1325        let filtered = self@.mappings.filter(
1326            |m: Mapping| m.va_range.start <= self@.cur_va < m.va_range.end,
1327        );
1328        assert(filtered == set![]) by {};
1329    }
1330
1331    /// Generalises `lemma_cur_entry_absent_not_present` to any empty subtree.
1332    pub proof fn lemma_cur_subtree_empty_not_present(self)
1333        requires
1334            self.inv(),
1335            self.in_barrier_va(),
1336            PageTableOwner(self.cur_subtree()).view_rec(self.cur_subtree().value().path) =~= set![],
1337        ensures
1338            !self@.present(),
1339    {
1340        let cur_va = self.cur_va();
1341
1342        assert forall|m: Mapping| self.view_mappings().contains(m) implies !(m.va_range.start
1343            <= cur_va < m.va_range.end) by {
1344            if m.va_range.start <= cur_va < m.va_range.end {
1345                self.mapping_covering_cur_va_from_cur_subtree(m);
1346            }
1347        };
1348
1349        let filtered = self@.mappings.filter(
1350            |m: Mapping| m.va_range.start <= self@.cur_va < m.va_range.end,
1351        );
1352        assert(filtered == set![]) by {};
1353    }
1354
1355    pub proof fn lemma_cur_entry_frame_present(self)
1356        requires
1357            self.inv(),
1358            self.in_barrier_va(),
1359            self.cur_entry_owner().is_frame(),
1360        ensures
1361            self@.present(),
1362            self@.query(
1363                self.cur_entry_owner().frame().mapped_pa,
1364                page_size::<PagingConsts>(self.cur_entry_owner().parent_level),
1365                self.cur_entry_owner().frame().prop,
1366            ),
1367    {
1368        self.view_preserves_inv();
1369        let subtree = self.cur_subtree();
1370        let path = subtree.value().path;
1371        let frame = self.cur_entry_owner().frame();
1372        let pt_level = INC_LEVELS - path.len();
1373        let cont = self.continuations[self.level - 1];
1374
1375        let m = Mapping {
1376            va_range: Range {
1377                start: vaddr_of::<C>(path) as int,
1378                end: vaddr_of::<C>(path) + page_size::<PagingConsts>(pt_level as PagingLevel),
1379            },
1380            pa_range: Range {
1381                start: frame.mapped_pa,
1382                end: (frame.mapped_pa + page_size::<PagingConsts>(
1383                    pt_level as PagingLevel,
1384                )) as Paddr,
1385            },
1386            page_size: page_size::<PagingConsts>(pt_level as PagingLevel),
1387            property: frame.prop,
1388        };
1389        cont.lemma_view_mappings_intro(m, cont.idx as int);
1390        self.lemma_view_mappings_intro(m, self.level - 1);
1391        assert(m.va_range.start <= self@.cur_va < m.va_range.end) by {
1392            self.cur_va_in_subtree_range();
1393        };
1394
1395        let filtered = self@.mappings.filter(
1396            |m2: Mapping| m2.va_range.start <= self@.cur_va < m2.va_range.end,
1397        );
1398        lemma_set_contains_len(filtered, m);
1399        // Non-overlap makes the covering mapping unique, so `choose` returns this frame.
1400        let queried = self@.query_mapping();
1401        assert(filtered.contains(queried));
1402        assert(queried == m);
1403    }
1404
1405    /// The entry_own at each continuation level satisfies `metaregion_sound`.
1406    #[verifier::opaque]
1407    pub open spec fn path_metaregion_sound(self, regions: MetaRegionOwners) -> bool {
1408        forall|i: int|
1409            #![trigger self.continuations[i]]
1410            self.level - 1 <= i < NR_LEVELS ==> self.continuations[i].entry_own.metaregion_sound(
1411                regions,
1412            )
1413    }
1414
1415    pub open spec fn metaregion_sound(self, regions: MetaRegionOwners) -> bool {
1416        &&& self.map_full_tree(
1417            |entry_owner: EntryOwner<C>, path: TreePath<NR_ENTRIES>|
1418                entry_owner.metaregion_sound(regions),
1419        )
1420        &&& self.path_metaregion_sound(regions)
1421    }
1422
1423    pub proof fn lemma_metaregion_preserved(
1424        self,
1425        other: Self,
1426        regions0: MetaRegionOwners,
1427        regions1: MetaRegionOwners,
1428    )
1429        requires
1430            self.inv(),
1431            self.metaregion_sound(regions0),
1432            self.level == other.level,
1433            self.continuations =~= other.continuations,
1434            OwnerSubtree::implies(
1435                PageTableOwner::<C>::metaregion_sound_pred(regions0),
1436                PageTableOwner::<C>::metaregion_sound_pred(regions1),
1437            ),
1438        ensures
1439            other.metaregion_sound(regions1),
1440    {
1441        let f = PageTableOwner::metaregion_sound_pred(regions0);
1442        let g = PageTableOwner::metaregion_sound_pred(regions1);
1443
1444        assert forall|i: int| #![auto] self.level - 1 <= i < NR_LEVELS implies {
1445            other.continuations[i].map_children(g)
1446        } by {
1447            reveal(CursorContinuation::map_children);
1448            let cont = self.continuations[i];
1449            assert forall|j: int|
1450                0 <= j < NR_ENTRIES
1451                    && #[trigger] cont.children[j] is Some implies cont.children[j].unwrap().subtree_satisfies(
1452            cont.path().push_tail(j), g) by {
1453                cont.children[j].unwrap().lemma_subtree_satisfies_implies(
1454                    cont.path().push_tail(j),
1455                    f,
1456                    g,
1457                );
1458            };
1459        };
1460        assert(other.path_metaregion_sound(regions1)) by {
1461            reveal(CursorOwner::path_metaregion_sound);
1462            assert forall|i: int|
1463                #![trigger other.continuations[i]]
1464                self.level - 1 <= i
1465                    < NR_LEVELS implies other.continuations[i].entry_own.metaregion_sound(
1466                regions1,
1467            ) by {
1468                let eo = self.continuations[i].entry_own;
1469                assert(g(eo, self.continuations[i].path()));
1470            };
1471        };
1472    }
1473
1474    /// Transfers `metaregion_sound` when `slot_owners` is preserved.
1475    pub proof fn lemma_metaregion_slot_owners_preserved(
1476        self,
1477        regions0: MetaRegionOwners,
1478        regions1: MetaRegionOwners,
1479    )
1480        requires
1481            self.inv(),
1482            self.metaregion_sound(regions0),
1483            regions0.slot_owners =~= regions1.slot_owners,
1484            forall|k: int|
1485                regions0.slots.contains_key(k) ==> #[trigger] regions1.slots.contains_key(k),
1486            forall|k: int|
1487                regions0.slots.contains_key(k) ==> regions0.slots[k]
1488                    == #[trigger] regions1.slots[k],
1489        ensures
1490            self.metaregion_sound(regions1),
1491    {
1492        let f = PageTableOwner::<C>::metaregion_sound_pred(regions0);
1493        let g = PageTableOwner::<C>::metaregion_sound_pred(regions1);
1494        assert(OwnerSubtree::implies(f, g)) by {
1495            assert forall|entry: EntryOwner<C>, path: TreePath<NR_ENTRIES>|
1496                entry.inv() && f(entry, path) implies #[trigger] g(entry, path) by {
1497                entry.metaregion_sound_slot_owners_only(regions0, regions1);
1498            };
1499        };
1500        self.lemma_metaregion_preserved(self, regions0, regions1);
1501    }
1502
1503    pub proof fn lemma_metaregion_slot_owners_rc_increment(
1504        self,
1505        regions0: MetaRegionOwners,
1506        regions1: MetaRegionOwners,
1507        idx: int,
1508    )
1509        requires
1510            self.inv(),
1511            self.metaregion_sound(regions0),
1512            regions0.inv(),
1513            regions1.slots == regions0.slots,
1514            regions1.slot_owners.dom() == regions0.slot_owners.dom(),
1515            regions1.ref_count(idx) == regions0.ref_count(idx) + 1,
1516            regions1.slot_owners[idx].ref_count_perm.id()
1517                == regions0.slot_owners[idx].ref_count_perm.id(),
1518            regions1.slot_owners[idx].metadata_perm.id()
1519                == regions0.slot_owners[idx].metadata_perm.id(),
1520            regions1.slot_owners[idx].metadata_perm.frac() + 1
1521                == regions0.slot_owners[idx].metadata_perm.frac(),
1522            regions1.slot_owners[idx].in_list_perm == regions0.slot_owners[idx].in_list_perm,
1523            regions1.slot_owners[idx].paths_in_pt == regions0.slot_owners[idx].paths_in_pt,
1524            regions1.slot_owners[idx].slot_vaddr == regions0.slot_owners[idx].slot_vaddr,
1525            regions1.slot_owners[idx].usage == regions0.slot_owners[idx].usage,
1526            regions1.ref_count(idx) != REF_COUNT_UNUSED,
1527            // Bumped rc stays in the SHARED range (needed for the node branch).
1528            regions1.ref_count(idx) <= REF_COUNT_MAX,
1529            regions1.inv(),
1530            forall|i: int|
1531                #![trigger regions1.slot_owners[i]]
1532                i != idx && regions0.slot_owners.contains_key(i) ==> regions1.slot_owners[i]
1533                    == regions0.slot_owners[i],
1534        ensures
1535            self.metaregion_sound(regions1),
1536    {
1537        let f = PageTableOwner::<C>::metaregion_sound_pred(regions0);
1538        let g = PageTableOwner::<C>::metaregion_sound_pred(regions1);
1539        assert(OwnerSubtree::implies(f, g)) by {
1540            assert forall|entry: EntryOwner<C>, path: TreePath<NR_ENTRIES>|
1541                entry.inv() && f(entry, path) implies #[trigger] g(entry, path) by {
1542                if entry.is_frame() {
1543                    let pa = entry.frame().mapped_pa;
1544                    C::lemma_perm_well_formed_with_region_preserved(
1545                        pa,
1546                        Tracked(entry.frame_permission()),
1547                        regions0,
1548                        regions1,
1549                    );
1550                }
1551            };
1552        };
1553        self.lemma_metaregion_preserved(self, regions0, regions1);
1554    }
1555
1556    /// The continuation entry at `i` satisfies `metaregion_sound`.
1557    pub proof fn lemma_cont_entry_metaregion_at(self, regions: MetaRegionOwners, i: int)
1558        requires
1559            self.inv(),
1560            self.metaregion_sound(regions),
1561            self.level - 1 <= i < NR_LEVELS,
1562        ensures
1563            self.continuations[i].entry_own.metaregion_sound(regions),
1564    {
1565        reveal(CursorOwner::path_metaregion_sound);
1566    }
1567
1568    pub open spec fn new(
1569        owner_subtree: OwnerSubtree<C>,
1570        barrier_va: Range<Vaddr>,
1571        guard: PageTableGuard<'rcu, C>,
1572    ) -> Self {
1573        let va = barrier_va.start;
1574        let idx = pte_index_spec::<C>(va, C::NR_LEVELS());
1575        Self {
1576            level: C::NR_LEVELS(),
1577            continuations: Map::empty().insert(
1578                C::NR_LEVELS() - 1,
1579                CursorContinuation::new(owner_subtree, idx, guard),
1580            ),
1581            va,
1582            barrier_va,
1583            guard_level: C::NR_LEVELS(),
1584            prefix: nat_align_down(
1585                va as nat,
1586                page_size::<C>((C::NR_LEVELS() + 1) as PagingLevel) as nat,
1587            ) as Vaddr,
1588            popped_too_high: false,
1589        }
1590    }
1591
1592    pub proof fn tracked_new(
1593        tracked owner_subtree: OwnerSubtree<C>,
1594        barrier_va: Range<Vaddr>,
1595        guard: PageTableGuard<'rcu, C>,
1596    ) -> tracked Self
1597        returns
1598            Self::new(owner_subtree, barrier_va, guard),
1599    {
1600        let ghost va = barrier_va.start;
1601        let ghost idx = pte_index_spec::<C>(va, C::NR_LEVELS());
1602        let tracked continuation = CursorContinuation::tracked_new(owner_subtree, idx, guard);
1603        let tracked mut continuations = Map::tracked_empty();
1604        continuations.tracked_insert(C::NR_LEVELS() - 1, continuation);
1605        Self {
1606            level: C::NR_LEVELS(),
1607            continuations,
1608            va,
1609            barrier_va,
1610            guard_level: C::NR_LEVELS(),
1611            prefix: nat_align_down(
1612                va as nat,
1613                page_size::<C>((C::NR_LEVELS() + 1) as PagingLevel) as nat,
1614            ) as Vaddr,
1615            popped_too_high: false,
1616        }
1617    }
1618
1619    pub broadcast group group_lemmas {
1620        CursorOwner::lemma_view_mappings_contains,
1621        CursorOwner::lemma_view_mappings_intro,
1622    }
1623}
1624
1625pub ghost struct CursorView<C: PageTableConfig> {
1626    pub cur_va: Vaddr,
1627    pub mappings: Set<Mapping>,
1628    pub phantom: PhantomData<C>,
1629}
1630
1631impl<'rcu, C: PageTableConfig> View for CursorOwner<'rcu, C> {
1632    type V = CursorView<C>;
1633
1634    open spec fn view(&self) -> Self::V {
1635        CursorView { cur_va: self.cur_va(), mappings: self.view_mappings(), phantom: PhantomData }
1636    }
1637}
1638
1639impl<C: PageTableConfig> Inv for CursorView<C> {
1640    open spec fn inv(self) -> bool {
1641        &&& mappings_well_formed(
1642            self.mappings,
1643        )
1644        // Config-aware VA range: user page tables live in `[0, 2^47)`,
1645        // kernel page tables in `[0xffff_8000_…, usize::MAX]`, etc.
1646        // Managed bounds account for the configured upper bits and top-level index range,
1647        // so `Mapping::inv` can stay config-agnostic.
1648        &&& forall|m: Mapping|
1649            #![auto]
1650            self.mappings.contains(m) ==> {
1651                &&& page_table_managed_start_spec::<C>() <= m.va_range.start
1652                &&& m.va_range.end <= page_table_managed_end_spec::<C>()
1653            }
1654        &&& self.non_overlapping()
1655    }
1656}
1657
1658impl<C: PageTableConfig> CursorView<C> {
1659    /// Mappings in the view are non-overlapping. This is a consequence of the
1660    /// page table tree structure: distinct paths map to disjoint VA ranges.
1661    pub open spec fn non_overlapping(self) -> bool {
1662        mappings_disjoint_vaddrs(self.mappings)
1663    }
1664}
1665
1666/// Every mapping in a cursor's view has its VA range within the page
1667/// table's managed range.
1668pub proof fn lemma_view_in_vaddr_range<'rcu, C: PageTableConfig>(owner: &CursorOwner<'rcu, C>)
1669    requires
1670        owner.inv(),
1671    ensures
1672        forall|m: Mapping|
1673            #![auto]
1674            owner.view_mappings().contains(m) ==> {
1675                &&& page_table_managed_start_spec::<C>() <= m.va_range.start
1676                &&& m.va_range.end <= page_table_managed_end_spec::<C>()
1677            },
1678{
1679    C::lemma_paging_consts_properties();
1680    C::axiom_current_paging_consts_hardcoded();
1681    C::lemma_page_table_config_constant_properties();
1682    lemma_arch_specific_consts_properties::<C>();
1683
1684    lemma_page_table_managed_bounds::<C>();
1685    let end = C::TOP_LEVEL_INDEX_RANGE().end as int;
1686
1687    assert forall|m: Mapping| #[trigger] owner.view_mappings().contains(m) implies {
1688        &&& page_table_managed_start_spec::<C>() <= m.va_range.start
1689        &&& m.va_range.end <= page_table_managed_end_spec::<C>()
1690    } by {
1691        let i = choose|i: int|
1692            owner.level - 1 <= i < NR_LEVELS && (
1693            #[trigger] owner.continuations[i]).view_mappings().contains(m);
1694        let cont = owner.continuations[i];
1695        let j = choose|j: int|
1696            0 <= j < cont.children.len() && #[trigger] cont.children[j] is Some && PageTableOwner(
1697                cont.children[j].unwrap(),
1698            ).view_rec(cont.path().push_tail(j)).contains(m);
1699        let child = PageTableOwner(cont.children[j].unwrap());
1700        let p = cont.path().push_tail(j);
1701        let pidx = p[0] as int;
1702        child.lemma_view_rec_top_index_va_bound(p, m, end);
1703        lemma_mul_inequality(
1704            C::TOP_LEVEL_INDEX_RANGE().start as int,
1705            pidx,
1706            page_size::<C>(C::NR_LEVELS()) as int,
1707        );
1708    }
1709}
1710
1711/// Every mapping in a user cursor lies in `[0, 2^47)`, specializing the
1712/// generic cursor bound with the user configuration's address range.
1713pub proof fn lemma_view_in_vaddr_range_user<'rcu>(
1714    owner: &CursorOwner<'rcu, crate::mm::vm_space::UserPtConfig>,
1715)
1716    requires
1717        owner.inv(),
1718    ensures
1719        forall|m: Mapping|
1720            #![auto]
1721            owner.view_mappings().contains(m) ==> {
1722                &&& 0 <= m.va_range.start
1723                &&& m.va_range.end <= page_table_managed_end_spec::<
1724                    crate::mm::vm_space::UserPtConfig,
1725                >()
1726            },
1727{
1728    lemma_view_in_vaddr_range(owner);
1729    lemma_vaddr_range_spec_user();
1730}
1731
1732impl<'rcu, C: PageTableConfig> InvView for CursorOwner<'rcu, C> {
1733    proof fn view_preserves_inv(self) {
1734        self.view_non_overlapping();
1735        self.lemma_view_mapping_inv();
1736        lemma_view_in_vaddr_range::<C>(&self);
1737    }
1738}
1739
1740impl<'rcu, C: PageTableConfig> CursorOwner<'rcu, C> {
1741    /// The cursor's view has non-overlapping mappings.
1742    pub proof fn view_non_overlapping(self)
1743        requires
1744            self.inv(),
1745        ensures
1746            self@.non_overlapping(),
1747    {
1748        self.as_page_table_owner_view_non_overlapping();
1749    }
1750}
1751
1752impl<'rcu, C: PageTableConfig, A: InAtomicMode> Inv for Cursor<'rcu, C, A> {
1753    open spec fn inv(self) -> bool {
1754        // `level <= guard_level + 1` (not `<= guard_level`) admits the
1755        // transient "popped one above the guard" state: `pop_level` at
1756        // `level == guard_level` legitimately yields `level == guard_level
1757        // + 1` (real Rust does not panic there — the guard-node lock slot
1758        // is still `Some`). The next `pop_level` on such a cursor reads a
1759        // `None` path slot (`level > guard_level`, by `wf`) and diverges,
1760        // so the state never propagates further.
1761        &&& 1 <= self.level <= self.guard_level + 1
1762        &&& self.guard_level <= NR_LEVELS
1763        &&& self.va >= self.barrier_va.start
1764        &&& self.va % PAGE_SIZE == 0
1765        &&& self.barrier_va.start < self.barrier_va.end
1766        &&& self.barrier_va.start % PAGE_SIZE == 0
1767        &&& self.barrier_va.end % PAGE_SIZE == 0
1768        &&& is_valid_range_spec::<C>(self.barrier_va)
1769        &&& self.barrier_va.end + page_size::<C>(self.guard_level)
1770            <= page_table_coverage_end_spec::<C>()
1771        &&& self.barrier_va.end - self.barrier_va.start <= page_size::<C>(
1772            (self.guard_level + 1) as PagingLevel,
1773        )
1774    }
1775}
1776
1777impl<'rcu, C: PageTableConfig, A: InAtomicMode> OwnerOf for Cursor<'rcu, C, A> {
1778    type Owner = CursorOwner<'rcu, C>;
1779
1780    open spec fn wf(self, owner: Self::Owner) -> bool {
1781        &&& owner.va == self.va
1782        &&& owner.barrier_va == self.barrier_va
1783        &&& self.level == owner.level
1784        &&& owner.guard_level == self.guard_level
1785        &&& owner.locked_range().start <= self.barrier_va.start
1786        &&& self.barrier_va.end
1787            <= owner.locked_range().end
1788        // `path` holds lock guards only for levels in `[self.level,
1789        // self.guard_level]` (see the `Cursor.path` doc comment and
1790        // `locking.rs`: `lock_range` only locks the subtree rooted at
1791        // `guard_level`). The ghost `continuations` chain still extends above
1792        // `guard_level` up to the root, but those ancestor nodes are NOT
1793        // locked, so their `path` slots are `None` and are not tied to a
1794        // continuation guard.
1795        &&& self.level <= 4 ==> {
1796            &&& 4 <= self.guard_level ==> {
1797                &&& self.path[3] is Some
1798                &&& owner.continuations.contains_key(3)
1799                &&& owner.continuations[3].guard == self.path[3]->0
1800            }
1801            &&& 4 > self.guard_level ==> self.path[3] is None
1802        }
1803        &&& self.level <= 3 ==> {
1804            &&& 3 <= self.guard_level ==> {
1805                &&& self.path[2] is Some
1806                &&& owner.continuations.contains_key(2)
1807                &&& owner.continuations[2].guard == self.path[2]->0
1808            }
1809            &&& 3 > self.guard_level ==> self.path[2] is None
1810        }
1811        &&& self.level <= 2 ==> {
1812            &&& 2 <= self.guard_level ==> {
1813                &&& self.path[1] is Some
1814                &&& owner.continuations.contains_key(1)
1815                &&& owner.continuations[1].guard == self.path[1]->0
1816            }
1817            &&& 2 > self.guard_level ==> self.path[1] is None
1818        }
1819        &&& self.level == 1 ==> {
1820            // `1 <= self.guard_level` always holds (`inv` gives
1821            // `guard_level >= 1`), so this clause is equivalent to the
1822            // original level-1 case; the `None` branch is vacuous.
1823            &&& 1 <= self.guard_level ==> {
1824                &&& self.path[0] is Some
1825                &&& owner.continuations.contains_key(0)
1826                &&& owner.continuations[0].guard == self.path[0]->0
1827            }
1828            &&& 1 > self.guard_level ==> self.path[0] is None
1829        }
1830    }
1831}
1832
1833} // verus!