Skip to main content

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

1use vstd::prelude::*;
2use vstd::set::{axiom_set_choose_len, axiom_set_intersect_finite};
3use vstd::set_lib::*;
4
5use vstd_extra::ghost_tree::*;
6use vstd_extra::ownership::*;
7
8use crate::mm::page_prop::PageProperty;
9use crate::mm::page_table::*;
10use crate::mm::{Paddr, PagingConstsTrait, PagingLevel, Vaddr};
11use crate::specs::arch::mm::{NR_ENTRIES, NR_LEVELS, PAGE_SIZE};
12use crate::specs::arch::paging_consts::PagingConsts;
13use crate::specs::mm::page_table::cursor::owners::*;
14use crate::specs::mm::page_table::owners::PageTableOwner;
15use crate::specs::mm::page_table::Mapping;
16use vstd_extra::arithmetic::*;
17
18use crate::mm::page_table::page_size_spec as page_size;
19
20verus! {
21
22impl<C: PageTableConfig> CursorView<C> {
23
24    /// After `split_if_mapped_huge_spec(new_size)`, a sub-mapping at `cur_va`
25    /// still exists.  The witness is `split_index(m, new_size, k)` where
26    /// `k = (cur_va - m.va_range.start) / new_size`.
27    pub proof fn split_if_mapped_huge_spec_preserves_present(v: Self, new_size: usize)
28        requires
29            v.inv(),
30            v.present(),
31            new_size > 0,
32            v.query_mapping().page_size > 0,
33            v.query_mapping().page_size % new_size == 0,
34        ensures
35            v.split_if_mapped_huge_spec(new_size).present(),
36    {
37        let cur_va = v.cur_va;
38        let m = v.query_mapping();
39        let ps = m.page_size;
40
41        assert(v.mappings.contains(m) && m.va_range.start <= cur_va && cur_va < m.va_range.end) by {
42            let f = v.mappings.filter(|m2: Mapping| m2.va_range.start <= v.cur_va < m2.va_range.end);
43            assert(f.finite()) by {
44                vstd::set::axiom_set_intersect_finite::<Mapping>(
45                    v.mappings,
46                    Set::new(|m2: Mapping| m2.va_range.start <= v.cur_va < m2.va_range.end));
47            };
48            vstd::set::axiom_set_choose_len(f);
49        };
50        assert(m.inv());
51        assert(m.va_range.start + ps == m.va_range.end);
52
53        let diff: int = cur_va as int - m.va_range.start as int;
54        let ki: int = diff / new_size as int;
55        vstd::arithmetic::div_mod::lemma_fundamental_div_mod(diff, new_size as int);
56        vstd::arithmetic::div_mod::lemma_mod_division_less_than_divisor(diff, new_size as int);
57        vstd::arithmetic::div_mod::lemma_div_pos_is_pos(diff, new_size as int);
58
59        vstd::arithmetic::div_mod::lemma_fundamental_div_mod(ps as int, new_size as int);
60        assert(ki < ps as int / new_size as int) by {
61            if ki >= ps as int / new_size as int {
62                vstd::arithmetic::mul::lemma_mul_inequality(
63                    ps as int / new_size as int, ki, new_size as int);
64            }
65        };
66
67        let sub = Self::split_index(m, new_size, ki as usize);
68
69        assert(ki * (new_size as int) >= 0) by {
70            vstd::arithmetic::mul::lemma_mul_nonnegative(ki, new_size as int);
71        };
72        assert((ki + 1) * (new_size as int) <= ps as int) by {
73            vstd::arithmetic::mul::lemma_mul_inequality(
74                ki + 1, ps as int / new_size as int, new_size as int);
75        };
76        assert(m.va_range.start as int + (ki + 1) * (new_size as int)
77            <= (m.va_range.end as int)) by {
78            vstd::arithmetic::mul::lemma_mul_is_commutative(ki + 1, new_size as int);
79            vstd::arithmetic::mul::lemma_mul_is_commutative(
80                ps as int / new_size as int, new_size as int);
81        };
82
83        assert(ki as usize as int == ki);
84        vstd::arithmetic::mul::lemma_mul_is_distributive_add(new_size as int, ki, 1 as int);
85        assert((cur_va as int) >= (m.va_range.start as int) + ki * (new_size as int));
86        assert((cur_va as int) < (m.va_range.start as int) + (ki + 1) * (new_size as int));
87        assert(sub.va_range.start <= cur_va);
88        assert(cur_va < sub.va_range.end);
89
90        let new_self = v.split_if_mapped_huge_spec(new_size);
91        let domain = Set::<int>::new(|n:int| 0 <= n < ps as int / new_size as int);
92        assert(domain.contains(ki));
93        assert(new_self.mappings.contains(sub));
94
95        let new_filter = new_self.mappings.filter(
96            |m2: Mapping| m2.va_range.start <= new_self.cur_va < m2.va_range.end);
97        assert(new_filter.contains(sub));
98        assert(new_self.mappings.finite()) by {
99            vstd::set::axiom_set_remove_finite(v.mappings, m);
100            let domain = Set::<int>::new(|n:int| 0 <= n < ps as int / new_size as int);
101            assert(domain =~= int::range_set(0int, ps as int / new_size as int));
102            vstd::set_lib::range_set_properties::<int>(0int, ps as int / new_size as int);
103            domain.lemma_map_finite(|n:int| Self::split_index(m, new_size, n as usize));
104            vstd::set::axiom_set_union_finite(
105                v.mappings.remove(m),
106                domain.map(|n:int| Self::split_index(m, new_size, n as usize)));
107        };
108        assert(new_filter.finite()) by {
109            vstd::set::axiom_set_intersect_finite::<Mapping>(
110                new_self.mappings,
111                Set::new(|m2: Mapping| m2.va_range.start <= new_self.cur_va < m2.va_range.end));
112        };
113        vstd::set::axiom_set_contains_len(new_filter, sub);
114    }
115
116    /// After `split_if_mapped_huge_spec(new_size)` on a valid view, the
117    /// mapping at `cur_va` has `page_size == new_size < m.page_size`.
118    ///
119    /// The sub-mapping `split_index(m, new_size, k)` has `page_size = new_size`.
120    /// No other mapping from the original view covers `cur_va` (non-overlapping),
121    /// so `query_mapping()` must return a sub-mapping with `page_size = new_size`.
122    pub proof fn split_if_mapped_huge_spec_decreases_page_size(v: Self, new_size: usize)
123        requires
124            v.inv(),
125            v.present(),
126            new_size > 0,
127            v.query_mapping().page_size > new_size,
128            v.query_mapping().page_size % new_size == 0,
129        ensures
130            v.split_if_mapped_huge_spec(new_size).present(),
131            v.split_if_mapped_huge_spec(new_size).query_mapping().page_size < v.query_mapping().page_size,
132    {
133        Self::split_if_mapped_huge_spec_preserves_present(v, new_size);
134
135        let cur_va = v.cur_va;
136        let m = v.query_mapping();
137        let new_self = v.split_if_mapped_huge_spec(new_size);
138        let m2 = new_self.query_mapping();
139        let ps = m.page_size;
140
141        assert(v.mappings.contains(m) && m.va_range.start <= cur_va && cur_va < m.va_range.end) by {
142            let f = v.mappings.filter(
143                |m2: Mapping| m2.va_range.start <= v.cur_va < m2.va_range.end);
144            assert(f.finite()) by {
145                vstd::set::axiom_set_intersect_finite::<Mapping>(
146                    v.mappings,
147                    Set::new(|m2: Mapping| m2.va_range.start <= v.cur_va < m2.va_range.end));
148            };
149            vstd::set::axiom_set_choose_len(f);
150        };
151
152        assert(new_self.mappings.contains(m2)
153            && m2.va_range.start <= cur_va && cur_va < m2.va_range.end) by {
154            let f = new_self.mappings.filter(
155                |m3: Mapping| m3.va_range.start <= new_self.cur_va < m3.va_range.end);
156            assert(new_self.mappings.finite()) by {
157                vstd::set::axiom_set_remove_finite(v.mappings, m);
158                let domain = Set::<int>::new(|n:int| 0 <= n < ps as int / new_size as int);
159                assert(domain =~= int::range_set(0int, ps as int / new_size as int));
160                vstd::set_lib::range_set_properties::<int>(0int, ps as int / new_size as int);
161                domain.lemma_map_finite(|n:int| Self::split_index(m, new_size, n as usize));
162                vstd::set::axiom_set_union_finite(
163                    v.mappings.remove(m),
164                    domain.map(|n:int| Self::split_index(m, new_size, n as usize)));
165            };
166            assert(f.finite()) by {
167                vstd::set::axiom_set_intersect_finite::<Mapping>(
168                    new_self.mappings,
169                    Set::new(|m3: Mapping| m3.va_range.start <= new_self.cur_va < m3.va_range.end));
170            };
171            vstd::set::axiom_set_choose_len(f);
172        };
173
174        if v.mappings.contains(m2) && m2 != m {
175            assert(m.va_range.end <= m2.va_range.start || m2.va_range.end <= m.va_range.start);
176            assert(false);
177        }
178
179        assert(!v.mappings.remove(m).contains(m2));
180        let new_mappings = Set::<int>::new(
181            |n:int| 0 <= n < ps as int / new_size as int
182        ).map(|n:int| Self::split_index(m, new_size, n as usize));
183        assert(new_mappings.contains(m2));
184        let k = choose|k: int| 0 <= k < ps as int / new_size as int
185            && #[trigger] Self::split_index(m, new_size, k as usize) == m2;
186        assert(m2.page_size == new_size);
187    }
188
189    /// `split_if_mapped_huge_spec` preserves `CursorView::inv()`.
190    ///
191    /// Requires: `v.inv()`, `v.present()`, the mapping at `cur_va` has
192    /// `page_size > new_size`, `page_size % new_size == 0`, and `new_size`
193    /// is itself a valid page size.
194    pub proof fn split_if_mapped_huge_spec_preserves_inv(v: Self, new_size: usize)
195        requires
196            v.inv(),
197            v.present(),
198            new_size > 0,
199            v.query_mapping().page_size > new_size,
200            v.query_mapping().page_size % new_size == 0,
201            set![4096usize, 2097152, 1073741824].contains(new_size),
202        ensures
203            v.split_if_mapped_huge_spec(new_size).inv(),
204    {
205        let cur_va = v.cur_va;
206        let m = v.query_mapping();
207        let ps = m.page_size;
208        let ns: int = new_size as int;
209        let new_self = v.split_if_mapped_huge_spec(new_size);
210        let count: int = ps as int / ns;
211        let domain = Set::<int>::new(|n: int| 0 <= n < count);
212        let new_mappings = domain.map(|n: int| Self::split_index(m, new_size, n as usize));
213
214        // --- Establish that m is in v.mappings and covers cur_va ---
215        assert(v.mappings.contains(m) && m.va_range.start <= cur_va && cur_va < m.va_range.end) by {
216            let f = v.mappings.filter(|m2: Mapping| m2.va_range.start <= v.cur_va < m2.va_range.end);
217            assert(f.finite()) by {
218                vstd::set::axiom_set_intersect_finite::<Mapping>(
219                    v.mappings,
220                    Set::new(|m2: Mapping| m2.va_range.start <= v.cur_va < m2.va_range.end));
221            };
222            vstd::set::axiom_set_choose_len(f);
223        };
224        assert(m.inv());
225
226        // --- (1) cur_va preserved ---
227
228        // --- (2) mappings.finite() ---
229        assert(new_self.mappings.finite()) by {
230            vstd::set::axiom_set_remove_finite(v.mappings, m);
231            assert(domain =~= int::range_set(0int, count));
232            vstd::set_lib::range_set_properties::<int>(0int, count);
233            domain.lemma_map_finite(|n: int| Self::split_index(m, new_size, n as usize));
234            vstd::set::axiom_set_union_finite(
235                v.mappings.remove(m), new_mappings);
236        };
237
238        // --- Helper: ps == count * ns (no remainder) ---
239        vstd::arithmetic::div_mod::lemma_fundamental_div_mod(ps as int, ns);
240        assert(ps as int == count * ns);
241
242        assert(m.va_range.start % new_size as int == 0) by {
243            vstd::arithmetic::mul::lemma_mul_is_commutative(count, ns);
244            vstd::arithmetic::div_mod::lemma_mod_mod(
245                m.va_range.start as int, ns, count);
246        };
247        assert(m.pa_range.start % new_size == 0) by {
248            vstd::arithmetic::mul::lemma_mul_is_commutative(count, ns);
249            vstd::arithmetic::div_mod::lemma_mod_mod(
250                m.pa_range.start as int, ns, count);
251        };
252
253        // --- (3) All mappings satisfy Mapping::inv() ---
254        assert forall|e: Mapping| new_self.mappings.contains(e) implies #[trigger] e.inv() by {
255            if v.mappings.remove(m).contains(e) {
256                assert(v.mappings.contains(e));
257            } else {
258                assert(new_mappings.contains(e));
259                let k = choose|k: int| 0 <= k < count
260                    && #[trigger] Self::split_index(m, new_size, k as usize) == e;
261                let sub = Self::split_index(m, new_size, k as usize);
262
263                // sub.page_size == new_size ∈ {4096, 2M, 1G}
264                assert(set![4096usize, 2097152, 1073741824].contains(sub.page_size));
265
266                // Alignment: (base + k * ns) % ns == base % ns == 0.
267                // Use lemma_mod_multiples_vanish: (ns * a + b) % ns == b % ns.
268                vstd::arithmetic::mul::lemma_mul_is_commutative(k, ns);
269                vstd::arithmetic::div_mod::lemma_mod_multiples_vanish(
270                    k, m.va_range.start as int, ns);
271                assert(sub.va_range.start % new_size as int == 0);
272                vstd::arithmetic::mul::lemma_mul_is_commutative(k + 1, ns);
273                vstd::arithmetic::div_mod::lemma_mod_multiples_vanish(
274                    k + 1, m.va_range.start as int, ns);
275                assert(sub.va_range.end % new_size as int == 0);
276
277                vstd::arithmetic::mul::lemma_mul_is_commutative(k, ns);
278                vstd::arithmetic::div_mod::lemma_mod_multiples_vanish(
279                    k, m.pa_range.start as int, ns);
280                assert(sub.pa_range.start % new_size == 0);
281                vstd::arithmetic::mul::lemma_mul_is_commutative(k + 1, ns);
282                vstd::arithmetic::div_mod::lemma_mod_multiples_vanish(
283                    k + 1, m.pa_range.start as int, ns);
284                assert(sub.pa_range.end % new_size == 0);
285
286                // Range spans exactly new_size.
287                vstd::arithmetic::mul::lemma_mul_is_distributive_add(ns, k, 1int);
288                assert(sub.va_range.start + new_size == sub.va_range.end);
289                assert(sub.pa_range.start + new_size == sub.pa_range.end);
290
291                // Bounds: sub is within m's range.
292                vstd::arithmetic::mul::lemma_mul_nonnegative(k, ns);
293                vstd::arithmetic::mul::lemma_mul_inequality(k + 1, count, ns);
294                // (k+1)*ns <= count*ns = ps, so m.start + (k+1)*ns <= m.start + ps = m.end.
295                assert(sub.va_range.start >= m.va_range.start);
296                assert(sub.va_range.end <= m.va_range.end);
297                assert(sub.va_range.start <= sub.va_range.end);
298
299                assert(sub.pa_range.start >= m.pa_range.start);
300                assert(sub.pa_range.end <= m.pa_range.end);
301                assert(sub.pa_range.start <= sub.pa_range.end);
302            }
303        };
304
305        // --- (4) Non-overlapping ---
306        assert forall|e1: Mapping, e2: Mapping|
307            #[trigger] new_self.mappings.contains(e1) &&
308            #[trigger] new_self.mappings.contains(e2) &&
309            e1 != e2
310        implies
311            e1.va_range.end <= e2.va_range.start || e2.va_range.end <= e1.va_range.start
312        by {
313            if v.mappings.remove(m).contains(e1) && v.mappings.remove(m).contains(e2) {
314                // Both from original: inherit from v.inv().
315                assert(v.mappings.contains(e1));
316                assert(v.mappings.contains(e2));
317            } else if v.mappings.remove(m).contains(e1) && new_mappings.contains(e2) {
318                // e1 from original (disjoint from m), e2 sub-mapping within m.
319                assert(v.mappings.contains(e1));
320                assert(e1 != m);
321                let k2 = choose|k: int| 0 <= k < count
322                    && #[trigger] Self::split_index(m, new_size, k as usize) == e2;
323                vstd::arithmetic::mul::lemma_mul_nonnegative(k2, ns);
324                vstd::arithmetic::mul::lemma_mul_inequality(k2 + 1, count, ns);
325                assert(e2.va_range.start >= m.va_range.start);
326                assert(e2.va_range.end <= m.va_range.end);
327            } else if new_mappings.contains(e1) && v.mappings.remove(m).contains(e2) {
328                // Symmetric case.
329                assert(v.mappings.contains(e2));
330                assert(e2 != m);
331                let k1 = choose|k: int| 0 <= k < count
332                    && #[trigger] Self::split_index(m, new_size, k as usize) == e1;
333                vstd::arithmetic::mul::lemma_mul_nonnegative(k1, ns);
334                vstd::arithmetic::mul::lemma_mul_inequality(k1 + 1, count, ns);
335                assert(e1.va_range.start >= m.va_range.start);
336                assert(e1.va_range.end <= m.va_range.end);
337            } else if new_mappings.contains(e1) && new_mappings.contains(e2) {
338                // Both sub-mappings with different indices.
339                let i = choose|k: int| 0 <= k < count
340                    && #[trigger] Self::split_index(m, new_size, k as usize) == e1;
341                let j = choose|k: int| 0 <= k < count
342                    && #[trigger] Self::split_index(m, new_size, k as usize) == e2;
343                vstd::arithmetic::mul::lemma_mul_nonnegative(i, ns);
344                vstd::arithmetic::mul::lemma_mul_nonnegative(j, ns);
345                if i < j {
346                    vstd::arithmetic::mul::lemma_mul_inequality(i + 1, j, ns);
347                    vstd::arithmetic::mul::lemma_mul_is_distributive_add(ns, i, 1int);
348                } else {
349                    vstd::arithmetic::mul::lemma_mul_inequality(j + 1, i, ns);
350                    vstd::arithmetic::mul::lemma_mul_is_distributive_add(ns, j, 1int);
351                }
352            }
353        };
354    }
355
356    /// `split_while_huge` only modifies `mappings`, not `cur_va`.
357    pub broadcast proof fn lemma_split_while_huge_preserves_cur_va(self, size: usize)
358        requires self.inv(), size >= PAGE_SIZE,
359        ensures #[trigger] self.split_while_huge(size).cur_va == self.cur_va
360        decreases if self.present() { self.query_mapping().page_size as int } else { 0 }
361    {
362        if self.present() {
363            let m = self.query_mapping();
364            if m.page_size > size {
365                let new_size = m.page_size / NR_ENTRIES;
366                let new_self = self.split_if_mapped_huge_spec(new_size);
367                // Establish m.inv() first.
368                let f = self.mappings.filter(|m2: Mapping| m2.va_range.start <= self.cur_va < m2.va_range.end);
369                vstd::set::axiom_set_intersect_finite::<Mapping>(
370                    self.mappings, Set::new(|m2: Mapping| m2.va_range.start <= self.cur_va < m2.va_range.end));
371                vstd::set::axiom_set_choose_len(f);
372                assert(m.inv());
373                assert(NR_ENTRIES == 512usize) by (compute_only);
374                // new_size is a valid page size (case split on m.page_size).
375                assert(set![4096usize, 2097152, 1073741824].contains(new_size)) by {
376                    if m.page_size == 2097152 { assert(2097152usize / 512 == 4096usize); }
377                    else if m.page_size == 1073741824 { assert(1073741824usize / 512 == 2097152usize); }
378                    else { assert(4096usize / 512 == 8usize); assert(false); } // 4096 case impossible: 8 not in set
379                };
380                assert(m.page_size % new_size == 0) by {
381                    if m.page_size == 2097152 { assert(2097152usize % 4096 == 0); }
382                    else { assert(1073741824usize % 2097152 == 0); }
383                };
384                Self::split_if_mapped_huge_spec_preserves_inv(self, new_size);
385
386                Self::split_if_mapped_huge_spec_decreases_page_size(self, new_size);
387                Self::lemma_split_while_huge_preserves_cur_va(new_self, size);
388            }
389        }
390    }
391
392    /// `split_while_huge` preserves `CursorView::inv()`.
393    pub proof fn lemma_split_while_huge_preserves_inv(self, size: usize)
394        requires self.inv(), size >= PAGE_SIZE,
395        ensures self.split_while_huge(size).inv(),
396        decreases if self.present() { self.query_mapping().page_size as int } else { 0 }
397    {
398        if self.present() {
399            let m = self.query_mapping();
400            if m.page_size > size {
401                let new_size = m.page_size / NR_ENTRIES;
402                let new_self = self.split_if_mapped_huge_spec(new_size);
403                // Establish m.inv() and call preserves_inv.
404                let f = self.mappings.filter(|m2: Mapping| m2.va_range.start <= self.cur_va < m2.va_range.end);
405                vstd::set::axiom_set_intersect_finite::<Mapping>(
406                    self.mappings, Set::new(|m2: Mapping| m2.va_range.start <= self.cur_va < m2.va_range.end));
407                vstd::set::axiom_set_choose_len(f);
408                assert(m.inv());
409                assert(NR_ENTRIES == 512usize) by (compute_only);
410                assert(set![4096usize, 2097152, 1073741824].contains(new_size)) by {
411                    if m.page_size == 2097152 { assert(2097152usize / 512 == 4096usize); }
412                    else if m.page_size == 1073741824 { assert(1073741824usize / 512 == 2097152usize); }
413                    else { assert(4096usize / 512 == 8usize); assert(false); }
414                };
415                assert(m.page_size % new_size == 0) by {
416                    if m.page_size == 2097152 { assert(2097152usize % 4096 == 0); }
417                    else { assert(1073741824usize % 2097152 == 0); }
418                };
419                Self::split_if_mapped_huge_spec_preserves_inv(self, new_size);
420                Self::split_if_mapped_huge_spec_decreases_page_size(self, new_size);
421                new_self.lemma_split_while_huge_preserves_inv(size);
422            }
423        }
424    }
425
426    /// Composition law for `split_while_huge`:
427    /// splitting to a finer target `s2 <= s1` is the same as first splitting to `s1` and then
428    /// further splitting to `s2`.
429    pub proof fn split_while_huge_compose(self, s1: usize, s2: usize)
430        requires
431            self.inv(),
432            s1 >= PAGE_SIZE,
433            s2 <= s1,
434        ensures
435            self.split_while_huge(s2) == self.split_while_huge(s1).split_while_huge(s2),
436        decreases if self.present() { self.query_mapping().page_size as int } else { 0 },
437    {
438        if !self.present() {
439            return;
440        }
441        let m = self.query_mapping();
442        if m.page_size <= s1 {
443            return;
444        }
445        let new_size = m.page_size / NR_ENTRIES;
446        let f = self.mappings.filter(
447            |m2: Mapping| m2.va_range.start <= self.cur_va < m2.va_range.end);
448        vstd::set::axiom_set_intersect_finite::<Mapping>(
449            self.mappings,
450            Set::new(|m2: Mapping| m2.va_range.start <= self.cur_va < m2.va_range.end));
451        vstd::set::axiom_set_choose_len(f);
452        assert(m.inv());
453        assert(NR_ENTRIES == 512usize) by (compute_only);
454        assert(set![4096usize, 2097152, 1073741824].contains(new_size)) by {
455            if m.page_size == 2097152 { assert(2097152usize / 512 == 4096usize); }
456            else { assert(1073741824usize / 512 == 2097152usize); }
457        };
458        assert(m.page_size % new_size == 0) by {
459            if m.page_size == 2097152 { assert(2097152usize % 4096 == 0); }
460            else { assert(1073741824usize % 2097152 == 0); }
461        };
462        Self::split_if_mapped_huge_spec_preserves_inv(self, new_size);
463        Self::split_if_mapped_huge_spec_decreases_page_size(self, new_size);
464        self.split_if_mapped_huge_spec(new_size).split_while_huge_compose(s1, s2);
465    }
466
467    /// When the current entry is absent or maps at `page_size <= size`, `split_while_huge(size)`
468    /// is a no-op.  Applying a second call with the same `size` therefore returns the same value.
469    pub proof fn split_while_huge_idempotent(self, size: usize)
470        requires
471            self.inv(),
472            size >= PAGE_SIZE,
473        ensures
474            self.split_while_huge(size).split_while_huge(size) == self.split_while_huge(size),
475    {
476        self.split_while_huge_compose(size, size);
477    }
478
479    /// When `split_while_huge(size)` is a no-op and the view is `present()`,
480    /// the mapping at `cur_va` already has `page_size <= size`.
481    pub proof fn split_while_huge_noop_implies_page_size_le(self, size: usize)
482        requires
483            self.inv(),
484            size >= PAGE_SIZE,
485            self.split_while_huge(size) == self,
486            self.present(),
487        ensures
488            self.query_mapping().page_size <= size,
489    {
490        let m = self.query_mapping();
491        if m.page_size > size {
492            let new_size = m.page_size / NR_ENTRIES;
493            let f = self.mappings.filter(
494                |m2: Mapping| m2.va_range.start <= self.cur_va < m2.va_range.end);
495            vstd::set::axiom_set_intersect_finite::<Mapping>(
496                self.mappings,
497                Set::new(|m2: Mapping| m2.va_range.start <= self.cur_va < m2.va_range.end));
498            vstd::set::axiom_set_choose_len(f);
499            assert(m.inv());
500            assert(NR_ENTRIES == 512usize) by (compute_only);
501            assert(set![4096usize, 2097152, 1073741824].contains(new_size)) by {
502                if m.page_size == 2097152 { assert(2097152usize / 512 == 4096usize); }
503                else if m.page_size == 1073741824 { assert(1073741824usize / 512 == 2097152usize); }
504                else { assert(4096usize / 512 == 8usize); assert(false); }
505            };
506            assert(m.page_size % new_size == 0) by {
507                if m.page_size == 2097152 { assert(2097152usize % 4096 == 0); }
508                else { assert(1073741824usize % 2097152 == 0); }
509            };
510            Self::split_if_mapped_huge_spec_preserves_inv(self, new_size);
511            Self::split_if_mapped_huge_spec_decreases_page_size(self, new_size);
512            let new_self = self.split_if_mapped_huge_spec(new_size);
513            new_self.split_while_huge_refinement(size, m);
514            assert(!new_self.mappings.contains(m)) by {
515                let new_mappings = Set::<int>::new(
516                    |n: int| 0 <= n < m.page_size as int / new_size as int
517                ).map(|n: int| Self::split_index(m, new_size, n as usize));
518                if new_mappings.contains(m) {
519                    let k = choose|k: int|
520                        0 <= k < m.page_size as int / new_size as int
521                        && #[trigger] Self::split_index(m, new_size, k as usize) == m;
522                }
523            };
524            let p = choose |p: Mapping| #[trigger] new_self.mappings.contains(p)
525                && p.va_range.start <= m.va_range.start
526                && m.va_range.end <= p.va_range.end
527                && m.pa_range.start == (p.pa_range.start + (m.va_range.start - p.va_range.start)) as Paddr
528                && m.property == p.property;
529            if self.mappings.contains(p) {
530                assert(p.va_range.start <= self.cur_va < p.va_range.end);
531            } else {
532                let new_mappings = Set::<int>::new(
533                    |n: int| 0 <= n < m.page_size as int / new_size as int
534                ).map(|n: int| Self::split_index(m, new_size, n as usize));
535                let k = choose|k: int|
536                    0 <= k < m.page_size as int / new_size as int
537                    && #[trigger] Self::split_index(m, new_size, k as usize) == p;
538                vstd::arithmetic::div_mod::lemma_fundamental_div_mod(
539                    m.page_size as int, new_size as int);
540                vstd::arithmetic::mul::lemma_mul_inequality(
541                    (k + 1) as int, m.page_size as int / new_size as int, new_size as int);
542                vstd::arithmetic::mul::lemma_mul_is_distributive_add_other_way(
543                    new_size as int, k, 1int);
544            }
545        }
546    }
547
548    /// When the mapping at `cur_va` is exactly one split-step above `size`
549    /// (i.e. `query_mapping().page_size / NR_ENTRIES == size`), one step of
550    /// `split_while_huge` equals `split_if_mapped_huge_spec`:
551    ///
552    /// `self.split_while_huge(size) == self.split_if_mapped_huge_spec(size)`
553    ///
554    /// This is because `split_while_huge` takes one step
555    /// `split_if_mapped_huge_spec(m.page_size / NR_ENTRIES)` = `split_if_mapped_huge_spec(size)`,
556    /// then the sub-mapping at `cur_va` has `page_size == size <= size`, so it stops.
557    pub proof fn split_while_huge_one_step(self, size: usize)
558        requires
559            self.inv(),
560            self.present(),
561            self.query_mapping().page_size > size,
562            self.query_mapping().page_size / NR_ENTRIES == size,
563            self.query_mapping().page_size % size == 0,
564            set![4096usize, 2097152, 1073741824].contains(size),
565        ensures
566            self.split_while_huge(size).mappings
567                =~= self.split_if_mapped_huge_spec(size).mappings,
568    {
569        let m = self.query_mapping();
570        let new_size = m.page_size / NR_ENTRIES;
571        let f0 = self.mappings.filter(
572            |m2: Mapping| m2.va_range.start <= self.cur_va < m2.va_range.end);
573        vstd::set::axiom_set_intersect_finite::<Mapping>(
574            self.mappings,
575            Set::new(|m2: Mapping| m2.va_range.start <= self.cur_va < m2.va_range.end));
576        vstd::set::axiom_set_choose_len(f0);
577        assert(m.inv());
578        assert(NR_ENTRIES == 512usize) by (compute_only);
579        Self::split_if_mapped_huge_spec_preserves_present(self, new_size);
580        Self::split_if_mapped_huge_spec_preserves_inv(self, new_size);
581        Self::split_if_mapped_huge_spec_decreases_page_size(self, new_size);
582        let new_self = self.split_if_mapped_huge_spec(new_size);
583
584        assert(new_self.query_mapping().page_size == new_size) by {
585            let new_m = new_self.query_mapping();
586            let f = new_self.mappings.filter(
587                |m2: Mapping| m2.va_range.start <= new_self.cur_va < m2.va_range.end);
588            vstd::set::axiom_set_intersect_finite::<Mapping>(
589                new_self.mappings,
590                Set::new(|m2: Mapping| m2.va_range.start <= new_self.cur_va < m2.va_range.end));
591            vstd::set::axiom_set_choose_len(f);
592            if self.mappings.contains(new_m) && new_m != m {
593                assert(false);
594            }
595            let new_mappings = Set::<int>::new(
596                |n: int| 0 <= n < m.page_size as int / new_size as int
597            ).map(|n: int| Self::split_index(m, new_size, n as usize));
598            assert(new_mappings.contains(new_m));
599        };
600        assert(new_self.split_while_huge(size) == new_self);
601    }
602
603    /// Locality of `split_if_mapped_huge_spec`: a mapping `m2` whose VA range
604    /// is disjoint from the mapping at `cur_va` is preserved.
605    pub proof fn split_if_mapped_huge_spec_locality(self, new_size: usize, m2: Mapping)
606        requires
607            self.inv(),
608            self.present(),
609            new_size > 0,
610            self.query_mapping().page_size % new_size == 0,
611            Mapping::disjoint_vaddrs(m2, self.query_mapping()),
612        ensures
613            self.split_if_mapped_huge_spec(new_size).mappings.contains(m2)
614                == self.mappings.contains(m2),
615    {
616        let m = self.query_mapping();
617        let size = m.page_size;
618        let new_mappings = Set::<int>::new(|n: int| 0 <= n < size / new_size)
619            .map(|n: int| Self::split_index(m, new_size, n as usize));
620
621        // Establish m covers cur_va (from present() + choose semantics).
622        let f = self.mappings.filter(
623            |m2: Mapping| m2.va_range.start <= self.cur_va < m2.va_range.end);
624        assert(f.finite()) by {
625            vstd::set::axiom_set_intersect_finite::<Mapping>(
626                self.mappings,
627                Set::new(|m2: Mapping| m2.va_range.start <= self.cur_va < m2.va_range.end));
628        };
629        vstd::set::axiom_set_choose_len(f);
630        assert(m.inv());
631
632        assert(!new_mappings.contains(m2)) by {
633            if new_mappings.contains(m2) {
634                let k = choose|k: int| 0 <= k < size as int / new_size as int
635                    && #[trigger] Self::split_index(m, new_size, k as usize) == m2;
636                vstd::arithmetic::div_mod::lemma_fundamental_div_mod(
637                    size as int, new_size as int);
638                vstd::arithmetic::mul::lemma_mul_inequality(
639                    (k + 1) as int, size as int / new_size as int, new_size as int);
640                vstd::arithmetic::mul::lemma_mul_is_distributive_add_other_way(
641                    new_size as int, k, 1int);
642                vstd::arithmetic::mul::lemma_mul_nonnegative(k, new_size as int);
643            }
644        };
645    }
646
647    /// Locality of `split_while_huge`: a mapping `m2` that is in `self.mappings`
648    /// and whose VA range does not contain `cur_va` is preserved.
649    ///
650    /// This is stronger than `split_if_mapped_huge_spec_locality` because it
651    /// handles the recursive case: each step only splits the mapping at `cur_va`,
652    /// and `m2` is disjoint from that mapping (by non-overlap invariant).
653    #[verifier::rlimit(80)]
654    pub proof fn split_while_huge_locality(self, size: usize, m2: Mapping)
655        requires
656            self.inv(),
657            size >= PAGE_SIZE,
658            self.mappings.contains(m2),
659            !(m2.va_range.start <= self.cur_va < m2.va_range.end),
660        ensures
661            self.split_while_huge(size).mappings.contains(m2),
662        decreases if self.present() { self.query_mapping().page_size as int } else { 0 },
663    {
664        if self.present() {
665            let m = self.query_mapping();
666            if m.page_size > size {
667                let new_size = m.page_size / NR_ENTRIES;
668                // Establish m covers cur_va.
669                let f = self.mappings.filter(
670                    |m3: Mapping| m3.va_range.start <= self.cur_va < m3.va_range.end);
671                assert(f.finite()) by {
672                    vstd::set::axiom_set_intersect_finite::<Mapping>(
673                        self.mappings,
674                        Set::new(|m3: Mapping| m3.va_range.start <= self.cur_va < m3.va_range.end));
675                };
676                vstd::set::axiom_set_choose_len(f);
677                assert(self.mappings.contains(m));
678                assert(m.va_range.start <= self.cur_va < m.va_range.end);
679                assert(m.inv());
680                // m2 != m and disjoint va_ranges (non-overlap invariant).
681                assert(Mapping::disjoint_vaddrs(m2, m));
682                // page_size % new_size == 0
683                assert(NR_ENTRIES == 512usize);
684                assert(m.page_size % new_size == 0) by {
685                    if m.page_size == 4096 { assert(4096usize % (4096usize / 512usize) == 0); }
686                    else if m.page_size == 2097152 { assert(2097152usize % (2097152usize / 512usize) == 0); }
687                    else { assert(1073741824usize % (1073741824usize / 512usize) == 0); }
688                };
689                assert(set![4096usize, 2097152, 1073741824].contains(new_size)) by {
690                    if m.page_size == 2097152 { assert(2097152usize / 512 == 4096usize); }
691                    else if m.page_size == 1073741824 { assert(1073741824usize / 512 == 2097152usize); }
692                    else { assert(4096usize / 512 == 8usize); assert(false); }
693                };
694                self.split_if_mapped_huge_spec_locality(new_size, m2);
695                let new_self = self.split_if_mapped_huge_spec(new_size);
696                Self::split_if_mapped_huge_spec_preserves_inv(self, new_size);
697                Self::split_if_mapped_huge_spec_decreases_page_size(self, new_size);
698                new_self.split_while_huge_locality(size, m2);
699            }
700        }
701    }
702
703    /// Converse locality: a mapping NOT in `self.mappings` and whose VA range
704    /// does not overlap any mapping in `self.mappings` that contains `cur_va`
705    /// is also NOT in `self.split_while_huge(size).mappings`.
706    ///
707    /// More precisely: if `m2 ∉ self.mappings` and `m2.va_range` is disjoint
708    /// from the range `[start, end)` of the mapping at `cur_va` (if present),
709    /// then `m2 ∉ self.split_while_huge(size).mappings`.
710    #[verifier::rlimit(120)]
711    pub proof fn split_while_huge_locality_absent(self, size: usize, m2: Mapping)
712        requires
713            self.inv(),
714            size >= PAGE_SIZE,
715            !self.mappings.contains(m2),
716            self.present() ==> Mapping::disjoint_vaddrs(m2, self.query_mapping()),
717        ensures
718            !self.split_while_huge(size).mappings.contains(m2),
719        decreases if self.present() { self.query_mapping().page_size as int } else { 0 },
720    {
721        if self.present() {
722            let m = self.query_mapping();
723            if m.page_size > size {
724                let new_size = m.page_size / NR_ENTRIES;
725                // Establish m covers cur_va and m.inv().
726                let f = self.mappings.filter(
727                    |m3: Mapping| m3.va_range.start <= self.cur_va < m3.va_range.end);
728                assert(f.finite()) by {
729                    vstd::set::axiom_set_intersect_finite::<Mapping>(
730                        self.mappings,
731                        Set::new(|m3: Mapping| m3.va_range.start <= self.cur_va < m3.va_range.end));
732                };
733                vstd::set::axiom_set_choose_len(f);
734                assert(self.mappings.contains(m));
735                // page_size % new_size == 0
736                assert(m.inv());
737                assert(m.page_size % new_size == 0) by {
738                    assert(set![4096usize, 2097152usize, 1073741824usize].contains(m.page_size));
739                    assert(4096usize % (4096usize / 512usize) == 0) by (compute_only);
740                    assert(2097152usize % (2097152usize / 512usize) == 0) by (compute_only);
741                    assert(1073741824usize % (1073741824usize / 512usize) == 0) by (compute_only);
742                };
743                assert(set![4096usize, 2097152, 1073741824].contains(new_size)) by {
744                    if m.page_size == 2097152 { assert(2097152usize / 512 == 4096usize); }
745                    else if m.page_size == 1073741824 { assert(1073741824usize / 512 == 2097152usize); }
746                    else { assert(4096usize / 512 == 8usize); assert(false); }
747                };
748                self.split_if_mapped_huge_spec_locality(new_size, m2);
749                let new_self = self.split_if_mapped_huge_spec(new_size);
750                Self::split_if_mapped_huge_spec_preserves_inv(self, new_size);
751                Self::split_if_mapped_huge_spec_preserves_present(self, new_size);
752                Self::split_if_mapped_huge_spec_decreases_page_size(self, new_size);
753                assert(new_self.present() ==>
754                    Mapping::disjoint_vaddrs(m2, new_self.query_mapping())) by {
755                    if new_self.present() {
756                        let new_m = new_self.query_mapping();
757                        let nf = new_self.mappings.filter(
758                            |m3: Mapping| m3.va_range.start <= new_self.cur_va < m3.va_range.end);
759                        vstd::set::axiom_set_intersect_finite::<Mapping>(
760                            new_self.mappings,
761                            Set::new(|m3: Mapping| m3.va_range.start <= new_self.cur_va < m3.va_range.end));
762                        vstd::set::axiom_set_choose_len(nf);
763                        if self.mappings.contains(new_m) && new_m != m {
764                            assert(false);
765                        }
766                        let new_mappings = Set::<int>::new(
767                            |n: int| 0 <= n < m.page_size as int / new_size as int
768                        ).map(|n: int| Self::split_index(m, new_size, n as usize));
769                        let k = choose|k: int|
770                            0 <= k < m.page_size as int / new_size as int
771                            && #[trigger] Self::split_index(m, new_size, k as usize) == new_m;
772                        vstd::arithmetic::div_mod::lemma_fundamental_div_mod(
773                            m.page_size as int, new_size as int);
774                        vstd::arithmetic::mul::lemma_mul_inequality(
775                            (k + 1) as int, m.page_size as int / new_size as int, new_size as int);
776                        vstd::arithmetic::mul::lemma_mul_is_distributive_add_other_way(
777                            new_size as int, k, 1int);
778                        vstd::arithmetic::mul::lemma_mul_nonnegative(k, new_size as int);
779                    }
780                };
781                new_self.split_while_huge_locality_absent(size, m2);
782            }
783        }
784    }
785
786    /// Refinement: every mapping in `split_while_huge(size).mappings` is either
787    /// from `self.mappings` or a sub-mapping of an entry in `self.mappings`.
788    /// Base lemma: every mapping in `split_if_mapped_huge_spec(new_size).mappings`
789    /// is either from the original mappings or a sub-mapping of `query_mapping()`.
790    pub proof fn split_if_mapped_huge_spec_refinement(self, new_size: usize, e: Mapping)
791        requires
792            self.inv(),
793            self.present(),
794            new_size > 0,
795            self.query_mapping().page_size > new_size,
796            self.query_mapping().page_size % new_size == 0,
797            self.split_if_mapped_huge_spec(new_size).mappings.contains(e),
798        ensures
799            self.mappings.contains(e)
800            || {
801                let parent = self.query_mapping();
802                &&& self.mappings.contains(parent)
803                &&& parent.va_range.start <= e.va_range.start
804                &&& e.va_range.end <= parent.va_range.end
805                &&& e.pa_range.start == (parent.pa_range.start + (e.va_range.start - parent.va_range.start)) as Paddr
806                &&& e.property == parent.property
807            },
808    {
809        let qm = self.query_mapping();
810        let ps = qm.page_size;
811        let ns: int = new_size as int;
812        let count: int = ps as int / ns;
813        let new_self = self.split_if_mapped_huge_spec(new_size);
814        let domain = Set::<int>::new(|n: int| 0 <= n < count);
815        let new_mappings = domain.map(|n: int| Self::split_index(qm, new_size, n as usize));
816
817        // Establish qm ∈ self.mappings.
818        let f = self.mappings.filter(|m2: Mapping| m2.va_range.start <= self.cur_va < m2.va_range.end);
819        vstd::set::axiom_set_intersect_finite::<Mapping>(
820            self.mappings,
821            Set::new(|m2: Mapping| m2.va_range.start <= self.cur_va < m2.va_range.end));
822        vstd::set::axiom_set_choose_len(f);
823        assert(self.mappings.contains(qm));
824        assert(qm.inv());
825
826        if self.mappings.remove(qm).contains(e) {
827            assert(self.mappings.contains(e));
828        } else {
829            assert(new_mappings.contains(e));
830            let k = choose|k: int| 0 <= k < count
831                && #[trigger] Self::split_index(qm, new_size, k as usize) == e;
832
833            vstd::arithmetic::div_mod::lemma_fundamental_div_mod(ps as int, ns);
834            assert(ps as int == count * ns);
835
836            vstd::arithmetic::mul::lemma_mul_nonnegative(k, ns);
837            vstd::arithmetic::mul::lemma_mul_inequality(k + 1, count, ns);
838
839            assert(k as usize as int == k);
840            let ku = k as usize;
841            assert(e == Self::split_index(qm, new_size, ku));
842            vstd::arithmetic::mul::lemma_mul_is_distributive_add(ns, k, 1int);
843
844            assert(e.va_range.start as int == qm.va_range.start as int + k * ns);
845            assert(e.va_range.start >= qm.va_range.start);
846            assert(e.va_range.end as int == qm.va_range.start as int + (k + 1) * ns);
847            assert(e.va_range.end <= qm.va_range.end);
848            assert(e.pa_range.start as int == qm.pa_range.start as int + k * ns);
849            assert(e.va_range.start - qm.va_range.start == k * ns);
850            assert(e.pa_range.start == (qm.pa_range.start + (e.va_range.start - qm.va_range.start)) as Paddr);
851            assert(e.property == qm.property);
852        }
853    }
854
855    pub proof fn split_while_huge_refinement(self, size: usize, m: Mapping)
856        requires
857            self.inv(),
858            size >= PAGE_SIZE,
859            self.split_while_huge(size).mappings.contains(m),
860        ensures
861            self.mappings.contains(m)
862            || exists |parent: Mapping| #[trigger] self.mappings.contains(parent)
863                && parent.va_range.start <= m.va_range.start
864                && m.va_range.end <= parent.va_range.end
865                && m.pa_range.start == (parent.pa_range.start + (m.va_range.start - parent.va_range.start)) as Paddr
866                && m.property == parent.property,
867        decreases if self.present() { self.query_mapping().page_size as int } else { 0 }
868    {
869        if self.present() {
870            let qm = self.query_mapping();
871            if qm.page_size > size {
872                let new_size = qm.page_size / NR_ENTRIES;
873                let new_self = self.split_if_mapped_huge_spec(new_size);
874
875                let f = self.mappings.filter(|m2: Mapping| m2.va_range.start <= self.cur_va < m2.va_range.end);
876                vstd::set::axiom_set_intersect_finite::<Mapping>(
877                    self.mappings,
878                    Set::new(|m2: Mapping| m2.va_range.start <= self.cur_va < m2.va_range.end));
879                vstd::set::axiom_set_choose_len(f);
880                assert(qm.inv());
881                assert(NR_ENTRIES == 512usize) by (compute_only);
882                assert(set![4096usize, 2097152, 1073741824].contains(new_size)) by {
883                    if qm.page_size == 2097152 { assert(2097152usize / 512 == 4096usize); }
884                    else if qm.page_size == 1073741824 { assert(1073741824usize / 512 == 2097152usize); }
885                    else { assert(4096usize / 512 == 8usize); assert(false); }
886                };
887                assert(qm.page_size % new_size == 0) by {
888                    if qm.page_size == 2097152 { assert(2097152usize % 4096 == 0); }
889                    else { assert(1073741824usize % 2097152 == 0); }
890                };
891                Self::split_if_mapped_huge_spec_preserves_inv(self, new_size);
892                Self::split_if_mapped_huge_spec_decreases_page_size(self, new_size);
893
894                new_self.split_while_huge_refinement(size, m);
895
896                if new_self.mappings.contains(m) {
897                    self.split_if_mapped_huge_spec_refinement(new_size, m);
898                } else {
899                    let p = choose|p: Mapping|
900                        #[trigger] new_self.mappings.contains(p)
901                        && p.va_range.start <= m.va_range.start
902                        && m.va_range.end <= p.va_range.end
903                        && m.pa_range.start == (p.pa_range.start + (m.va_range.start - p.va_range.start)) as Paddr
904                        && m.property == p.property;
905                    self.split_if_mapped_huge_spec_refinement(new_size, p);
906                    if !self.mappings.contains(p) {
907                        // TODO: chain the `as Paddr` equations through p.
908                        // In int arithmetic the substitution is immediate;
909                        // Verus needs an explicit no-overflow bridge to
910                        // strip the `as Paddr` casts. Defer.
911                        admit();
912                        assert(qm.va_range.start <= m.va_range.start);
913                        assert(m.va_range.end <= qm.va_range.end);
914                        assert(m.pa_range.start == (qm.pa_range.start + (m.va_range.start - qm.va_range.start)) as Paddr);
915                        assert(m.property == qm.property);
916                    }
917                }
918            }
919        }
920    }
921
922    /// `split_while_huge` produces a set disjoint from any set that was
923    /// already disjoint from `self.mappings`.
924    ///
925    /// Proof sketch: `split_while_huge` only adds sub-mappings of the entry at
926    /// `cur_va` and preserves all other entries from `self.mappings`.  If `other`
927    /// is disjoint from `self.mappings`, then entries preserved from `self.mappings`
928    /// aren't in `other`, and sub-mappings (with `va_range ⊂ query_mapping().va_range`)
929    /// can't equal any entry in `other` because `other` has no entry overlapping
930    /// the query mapping (which is in `self.mappings`).
931    pub proof fn split_while_huge_disjoint(self, size: usize, other: Set<Mapping>)
932        requires
933            self.inv(),
934            self.mappings.disjoint(other),
935        ensures
936            self.split_while_huge(size).mappings.disjoint(other),
937    { admit() }
938}
939
940
941impl<'rcu, C: PageTableConfig> CursorOwner<'rcu, C> {
942
943    /// When the current entry is a PT node at level `self.level`, any mapping at `cur_va` has
944    /// `page_size <= page_size(self.level - 1)`.  Therefore `split_while_huge` at
945    /// `page_size(self.level - 1)` does not split anything and is a no-op on the abstract view.
946    /// When present, the query_mapping is from the current subtree's view_rec.
947    proof fn query_mapping_from_subtree(self, qm: Mapping)
948        requires
949            self.inv(),
950            self@.inv(),
951            self@.present(),
952            qm == self@.query_mapping(),
953        ensures
954            PageTableOwner(self.cur_subtree()).view_rec(self.cur_subtree().value.path).contains(qm),
955    {
956        let f = self@.mappings.filter(|m2: Mapping| m2.va_range.start <= self@.cur_va < m2.va_range.end);
957        axiom_set_intersect_finite::<Mapping>(
958            self@.mappings, Set::new(|m2: Mapping| m2.va_range.start <= self@.cur_va < m2.va_range.end));
959        axiom_set_choose_len(f);
960        self.mapping_covering_cur_va_from_cur_subtree(qm);
961    }
962
963    pub proof fn split_while_huge_node_noop(self)
964        requires
965            self.inv(),
966            self.cur_entry_owner().is_node(),
967            self.level > 1,
968        ensures
969            self@.split_while_huge(page_size((self.level - 1) as PagingLevel)) == self@
970    {
971        self.view_preserves_inv();
972        if self@.present() {
973            self.cur_subtree_inv();
974            let subtree = self.cur_subtree();
975            let path = subtree.value.path;
976            let qm = self@.query_mapping();
977            self.query_mapping_from_subtree(qm);
978            let cont = self.continuations[self.level - 1];
979            cont.path().push_tail_property_len(cont.idx as usize);
980            PageTableOwner(subtree).view_rec_node_page_size_bound(path, qm);
981        }
982    }
983
984    /// When the current entry is absent, there is no mapping at `cur_va` in the abstract view,
985    /// so `split_while_huge` finds nothing to split and is a no-op for any target size.
986    pub proof fn split_while_huge_absent_noop(self, size: usize)
987        requires
988            self.inv(),
989            self.cur_entry_owner().is_absent(),
990        ensures
991            self@.split_while_huge(size) == self@,
992    {
993        self.view_preserves_inv();
994        self.cur_entry_absent_not_present();
995    }
996
997    pub proof fn split_while_huge_at_level_noop(self)
998        requires
999            self.inv(),
1000        ensures
1001            self@.split_while_huge(page_size(self.level as PagingLevel)) == self@
1002    {
1003        self.view_preserves_inv();
1004        if self@.present() {
1005            self.cur_subtree_inv();
1006            let subtree = self.cur_subtree();
1007            let path = subtree.value.path;
1008            let qm = self@.query_mapping();
1009            self.query_mapping_from_subtree(qm);
1010            let cont = self.continuations[self.level - 1];
1011            cont.path().push_tail_property_len(cont.idx as usize);
1012            PageTableOwner(subtree).view_rec_page_size_bound(path, qm);
1013        }
1014    }
1015
1016    /// After `map_branch_none` splits a huge frame at level `level_before_frame` and descends,
1017    /// the cursor view equals `owner0@.split_while_huge(page_size(level_before_frame - 1))`.
1018    ///
1019    /// Chain:
1020    ///   owner@ = owner_before_frame@.split_if_mapped_huge_spec(page_size(level_before_frame - 1))
1021    ///          = owner0@.split_while_huge(page_size(level_before_frame)).split_if_mapped_huge_spec(...)
1022    ///          = owner0@.split_while_huge(page_size(level_before_frame - 1))
1023    /// The last equality uses the fact that split_while_huge(L) on a frame of size page_size(L)
1024    /// takes exactly one split step to page_size(L-1), matching split_if_mapped_huge_spec.
1025    pub proof fn map_branch_frame_split_while_huge(
1026        self,
1027        owner0: Self,
1028        owner_before_frame: Self,
1029        level_before_frame: int,
1030    )
1031        requires
1032            self.inv(),
1033            owner0.inv(),
1034            owner_before_frame.inv(),
1035            1 <= level_before_frame - 1,
1036            level_before_frame <= NR_LEVELS,
1037            self.level == (level_before_frame - 1) as u8,
1038            owner_before_frame@ == owner0@.split_while_huge(
1039                page_size(level_before_frame as PagingLevel)),
1040            self@ == owner_before_frame@.split_if_mapped_huge_spec(
1041                page_size((level_before_frame - 1) as PagingLevel)),
1042        ensures
1043            self@ == owner0@.split_while_huge(page_size(self.level as PagingLevel))
1044    { admit() }
1045
1046    /// After split_if_mapped_huge + push_level, the mappings equal
1047    /// `old_view.split_while_huge(page_size(current_level))`.
1048    pub proof fn find_next_split_push_equals_split_while_huge(
1049        self,
1050        old_view: CursorView<C>,
1051    )
1052        requires
1053            self.inv(),
1054            old_view.inv(),
1055            self.cur_entry_owner().is_frame(),
1056            self@.cur_va == old_view.cur_va,
1057            old_view.present(),
1058            old_view.query_mapping().page_size > page_size(self.level as PagingLevel),
1059            old_view.query_mapping().page_size / NR_ENTRIES
1060                == page_size(self.level as PagingLevel),
1061            old_view.query_mapping().page_size
1062                % page_size(self.level as PagingLevel) == 0,
1063            self@.mappings =~= old_view.split_if_mapped_huge_spec(
1064                page_size(self.level as PagingLevel)).mappings,
1065        ensures
1066            self@.mappings =~= old_view.split_while_huge(
1067                page_size(self.level as PagingLevel)).mappings,
1068    {
1069        let ps = page_size(self.level as PagingLevel);
1070        let m = old_view.query_mapping();
1071        let f = old_view.mappings.filter(
1072            |m2: Mapping| m2.va_range.start <= old_view.cur_va < m2.va_range.end);
1073        vstd::set::axiom_set_intersect_finite::<Mapping>(
1074            old_view.mappings,
1075            Set::new(|m2: Mapping| m2.va_range.start <= old_view.cur_va < m2.va_range.end));
1076        vstd::set::axiom_set_choose_len(f);
1077        assert(m.inv());
1078        assert(NR_ENTRIES == 512usize) by (compute_only);
1079        crate::specs::mm::page_table::cursor::page_size_lemmas::lemma_page_size_spec_values();
1080        assert(set![4096usize, 2097152, 1073741824].contains(ps)) by {
1081            if m.page_size == 2097152 { assert(2097152usize / 512 == 4096usize); }
1082            else { assert(1073741824usize / 512 == 2097152usize); }
1083        };
1084        old_view.split_while_huge_one_step(ps);
1085    }
1086
1087    /// `split_while_huge` gives the same mappings for two `cur_va` values
1088    /// when no mapping starts between them and the `!present` case is a no-op.
1089    pub proof fn split_while_huge_cur_va_independent(
1090        v1: CursorView<C>,
1091        v2: CursorView<C>,
1092        size: usize,
1093    )
1094        requires
1095            v1.inv(),
1096            v2.inv(),
1097            v1.mappings =~= v2.mappings,
1098            v1.cur_va <= v2.cur_va,
1099            // No mapping starts in [v1.cur_va, v2.cur_va).
1100            v1.mappings.filter(|m: Mapping|
1101                v1.cur_va <= m.va_range.start && m.va_range.start < v2.cur_va)
1102                =~= Set::<Mapping>::empty(),
1103            // When v1 has no mapping at cur_va, any mapping at v2.cur_va is
1104            // already small enough that split_while_huge is a no-op on it too.
1105            // (At the call site this follows from: split_while_huge(v1) was a
1106            // no-op, so find_next found the mapping without splitting, meaning
1107            // its page_size <= size.)
1108            !v1.present() && v2.present() ==> v2.query_mapping().page_size <= size,
1109        ensures
1110            v1.split_while_huge(size).mappings =~= v2.split_while_huge(size).mappings,
1111    {
1112        if v1.cur_va == v2.cur_va {
1113            assert(v1 =~= v2);
1114            return;
1115        }
1116        if !v1.present() {
1117            return;
1118        }
1119        admit()
1120    }
1121}
1122
1123} // verus!