Skip to main content

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

1//! Tree-predicate lifting, tree entry level constraints, and tree membership
2//! lemmas for `CursorContinuation` and `CursorOwner`.
3use vstd::{
4    arithmetic::div_mod::{lemma_small_mod, lemma_sub_mod_noop},
5    prelude::*,
6};
7use vstd_extra::{arithmetic::lemma_nat_align_down_sound, ghost_tree::*, ownership::*};
8
9use crate::specs::{
10    arch::{NR_ENTRIES, NR_LEVELS, PAGE_SIZE},
11    mm::page_table::{
12        cursor::owners::{CursorContinuation, CursorOwner},
13        node::entry_owners::EntryOwner,
14        owners::*,
15    },
16};
17
18use crate::arch::mm::PagingConsts;
19use crate::mm::{Paddr, PagingLevel, Vaddr, page_prop::PageProperty, page_size, page_table::*};
20use core::ops::Range;
21
22verus! {
23
24// ─── Tree predicate lifting (CursorContinuation) ───────────────────
25impl<'rcu, C: PageTableConfig> CursorContinuation<'rcu, C> {
26    /// Lift map_children(f) to map_children(g) when implies(f, g).
27    pub proof fn map_children_lift(
28        self,
29        f: spec_fn(EntryOwner<C>, TreePath<NR_ENTRIES>) -> bool,
30        g: spec_fn(EntryOwner<C>, TreePath<NR_ENTRIES>) -> bool,
31    )
32        requires
33            self.inv(),
34            self.map_children(f),
35            OwnerSubtree::implies(f, g),
36        ensures
37            self.map_children(g),
38    {
39        reveal(CursorContinuation::map_children);
40        assert forall|j: int|
41            #![auto]
42            0 <= j < self.children.len()
43                && self.children[j] is Some implies self.children[j].unwrap().subtree_satisfies(
44            self.path().push_tail(j),
45            g,
46        ) by {
47            self.lemma_inv_children_unroll(j);
48            OwnerSubtree::lemma_subtree_satisfies_implies(
49                self.children[j].unwrap(),
50                self.path().push_tail(j),
51                f,
52                g,
53            );
54        };
55    }
56
57    /// Lift map_children from f to g when siblings (j != idx) come from
58    /// cont0 which had map_children(f), and the child at idx (if present)
59    /// already satisfies g.
60    pub proof fn map_children_lift_skip_idx(
61        self,
62        cont0: Self,
63        idx: int,
64        f: spec_fn(EntryOwner<C>, TreePath<NR_ENTRIES>) -> bool,
65        g: spec_fn(EntryOwner<C>, TreePath<NR_ENTRIES>) -> bool,
66    )
67        requires
68            0 <= idx < NR_ENTRIES,
69            OwnerSubtree::implies(f, g),
70            cont0.inv(),
71            cont0.map_children(f),
72            self.path() == cont0.path(),
73            self.children.len() == cont0.children.len(),
74            forall|j: int|
75                #![auto]
76                0 <= j < NR_ENTRIES && j != idx ==> self.children[j] == cont0.children[j],
77            self.children[idx] is Some ==> self.children[idx]->0.subtree_satisfies(
78                self.path().push_tail(idx as int),
79                g,
80            ),
81        ensures
82            self.map_children(g),
83    {
84        reveal(CursorContinuation::map_children);
85        assert forall|j: int|
86            #![auto]
87            0 <= j < self.children.len()
88                && self.children[j] is Some implies self.children[j].unwrap().subtree_satisfies(
89            self.path().push_tail(j),
90            g,
91        ) by {
92            if j != idx {
93                cont0.lemma_inv_children_unroll(j);
94                OwnerSubtree::lemma_subtree_satisfies_implies(
95                    cont0.children[j].unwrap(),
96                    cont0.path().push_tail(j),
97                    f,
98                    g,
99                );
100            }
101        };
102    }
103
104    pub proof fn as_subtree_restore(self, child: Self)
105        requires
106            self.inv(),
107            child.inv(),
108            self.all_but_index_some(),
109            child.all_some(),
110        ensures
111            self.restore(child).0.as_subtree() == self.put_child(child.as_subtree()).as_subtree(),
112    {
113        assert(self.put_child(child.as_subtree()).children == self.children.update(
114            self.idx as int,
115            Some(child.as_subtree()),
116        ));
117    }
118}
119
120// ─── Tree predicate lifting (CursorOwner) ──────────────────────────
121impl<'rcu, C: PageTableConfig> CursorOwner<'rcu, C> {
122    pub proof fn map_children_implies(
123        self,
124        f: spec_fn(EntryOwner<C>, TreePath<NR_ENTRIES>) -> bool,
125        g: spec_fn(EntryOwner<C>, TreePath<NR_ENTRIES>) -> bool,
126    )
127        requires
128            self.inv(),
129            OwnerSubtree::implies(f, g),
130            forall|i: int|
131                #![trigger self.continuations[i]]
132                self.level - 1 <= i < NR_LEVELS ==> self.continuations[i].map_children(f),
133        ensures
134            forall|i: int|
135                #![trigger self.continuations[i]]
136                self.level - 1 <= i < NR_LEVELS ==> self.continuations[i].map_children(g),
137    {
138        assert forall|i: int|
139            #![trigger self.continuations[i]]
140            self.level - 1 <= i < NR_LEVELS implies self.continuations[i].map_children(g) by {
141            reveal(CursorContinuation::map_children);
142            let cont = self.continuations[i];
143            reveal(CursorContinuation::inv_children);
144            assert forall|j: int|
145                #![trigger cont.children[j]]
146                0 <= j < cont.children.len()
147                    && cont.children[j] is Some implies cont.children[j].unwrap().subtree_satisfies(
148                cont.path().push_tail(j),
149                g,
150            ) by {
151                cont.lemma_inv_children_unroll(j);
152                OwnerSubtree::lemma_subtree_satisfies_implies(
153                    cont.children[j].unwrap(),
154                    cont.path().push_tail(j),
155                    f,
156                    g,
157                );
158            }
159        }
160    }
161
162    // ─── Tree entry level constraints ──────────────────────────
163    pub proof fn cur_entry_node_implies_level_gt_1(self)
164        requires
165            self.inv(),
166            self.cur_entry_owner().is_node(),
167        ensures
168            self.level > 1,
169    {
170        self.lemma_cur_subtree_inv();
171    }
172
173    /// A frame entry at the cursor's current level that doesn't fit the aligned range
174    /// `[cur_va, end)` must be at level > 1.
175    /// Justification: At level 1, `page_size(1) == BASE_PAGE_SIZE`. Since the cursor VA
176    /// and `end` are BASE_PAGE_SIZE-aligned and `cur_va < end`, we have
177    /// `cur_va + page_size(1) <= end`, so a level-1 frame always fits. Therefore
178    /// `!cur_entry_fits_range` implies `level > 1`.
179    #[verifier::spinoff_prover]
180    pub proof fn frame_not_fits_implies_level_gt_1(
181        self,
182        cur_entry_fits_range: bool,
183        cur_va: Vaddr,
184        end: Vaddr,
185    )
186        requires
187            self.inv(),
188            self.cur_entry_owner().is_frame(),
189            !cur_entry_fits_range,
190            cur_va < end,
191            // cur_va is the cursor's current VA (from the call site: cur_va == self.va).
192            cur_va == self.cur_va(),
193            // Definition: cur_entry_fits_range iff cur_va is at start of entry AND entry end <= end.
194            cur_entry_fits_range == (cur_va == self.cur_va_range().start && self.cur_va_range().end
195                <= end),
196            // cur_va and end are PAGE_SIZE-aligned
197            cur_va as nat % PAGE_SIZE as nat == 0,
198            end as nat % PAGE_SIZE as nat == 0,
199        ensures
200            self.level > 1,
201    {
202        if self.level == 1 {
203            crate::specs::mm::page_table::cursor::page_size_lemmas::lemma_page_size_base::<
204                PagingConsts,
205            >();
206            lemma_nat_align_down_sound(cur_va as nat, PAGE_SIZE as nat);
207            lemma_sub_mod_noop(end as int, cur_va as int, PAGE_SIZE as int);
208            if end - cur_va < PAGE_SIZE {
209                lemma_small_mod((end - cur_va) as nat, PAGE_SIZE as nat);
210                assert(false);
211            }
212        }
213    }
214}
215
216} // verus!