Skip to main content

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

1use vstd::{prelude::*, set::lemma_set_choose_len, set_lib::*};
2use vstd_extra::{arithmetic::*, ghost_tree::*, ownership::*};
3
4use crate::specs::{
5    arch::{MAX_PADDR, NR_ENTRIES, NR_LEVELS, PAGE_SIZE},
6    mm::page_table::{Mapping, cursor::owners::*, owners::PageTableOwner, vaddr_range_spec},
7};
8
9use crate::arch::mm::PagingConsts;
10use crate::mm::{
11    Paddr, PagingConstsTrait, PagingLevel, Vaddr, page_prop::PageProperty, page_size, page_table::*,
12};
13
14verus! {
15
16broadcast use group_ghost_tree_lemmas;
17
18impl<C: PageTableConfig> CursorView<C> {
19    proof fn lemma_split_index_inv(m: Mapping, new_size: usize, k: int)
20        requires
21            m.inv(),
22            new_size > 0,
23            m.page_size % new_size == 0,
24            set![4096usize, 2097152, 1073741824].contains(new_size),
25            m.va_range.start % new_size as int == 0,
26            0 <= k < m.page_size as int / new_size as int,
27        ensures
28            Self::split_index(m, new_size, k as usize).inv(),
29            m.va_range.start <= Self::split_index(m, new_size, k as usize).va_range.start,
30            Self::split_index(m, new_size, k as usize).va_range.end <= m.va_range.end,
31    {
32        let ps = m.page_size as int;
33        let ns = new_size as int;
34        let count = ps / ns;
35        let sub = Self::split_index(m, new_size, k as usize);
36
37        vstd::arithmetic::div_mod::lemma_fundamental_div_mod(ps, ns);
38        assert(ps == count * ns) by {
39            vstd::arithmetic::mul::lemma_mul_is_commutative(ns, count);
40        };
41        vstd::arithmetic::mul::lemma_mul_inequality(k + 1, count, ns);
42        vstd::arithmetic::mul::lemma_mul_is_distributive_add_other_way(ns, k, 1);
43
44        vstd::arithmetic::div_mod::lemma_mod_mod(m.pa_range.start as int, ns, count);
45        assert((m.pa_range.start as int) % ns == 0);
46        vstd::arithmetic::div_mod::lemma_mod_multiples_basic(k, ns);
47        vstd::arithmetic::div_mod::lemma_mod_multiples_basic(k + 1, ns);
48        vstd_extra::arithmetic::lemma_mod_0_add(m.pa_range.start as int, k * ns, ns);
49        vstd_extra::arithmetic::lemma_mod_0_add(m.pa_range.start as int, (k + 1) * ns, ns);
50        vstd_extra::arithmetic::lemma_mod_0_add(m.va_range.start, k * ns, ns);
51        vstd_extra::arithmetic::lemma_mod_0_add(m.va_range.start, (k + 1) * ns, ns);
52
53        assert(sub.inv());
54    }
55
56    /// After `split_if_mapped_huge_spec(new_size)`, a sub-mapping at `cur_va`
57    /// still exists.  The witness is `split_index(m, new_size, k)` where
58    /// `k = (cur_va - m.va_range.start) / new_size`.
59    pub proof fn split_if_mapped_huge_spec_preserves_present(v: Self, new_size: usize)
60        requires
61            v.inv(),
62            v.present(),
63            new_size > 0,
64            v.query_mapping().page_size > 0,
65            v.query_mapping().page_size % new_size == 0,
66        ensures
67            v.split_if_mapped_huge_spec(new_size).present(),
68    {
69        let cur_va = v.cur_va;
70        let m = v.query_mapping();
71        let ps = m.page_size;
72
73        assert(v.mappings.contains(m) && m.va_range.start <= cur_va && cur_va < m.va_range.end) by {
74            let f = v.mappings.filter(
75                |m2: Mapping| m2.va_range.start <= v.cur_va < m2.va_range.end,
76            );
77            vstd::set::lemma_set_choose_len(f);
78        };
79        assert(m.inv());
80
81        let diff: int = cur_va - m.va_range.start;
82        let ki: int = diff / new_size as int;
83        vstd::arithmetic::div_mod::lemma_fundamental_div_mod(diff, new_size as int);
84
85        vstd::arithmetic::div_mod::lemma_fundamental_div_mod(ps as int, new_size as int);
86        assert(ki < ps as int / new_size as int) by {
87            if ki >= ps as int / new_size as int {
88                vstd::arithmetic::mul::lemma_mul_inequality(
89                    ps as int / new_size as int,
90                    ki,
91                    new_size as int,
92                );
93            }
94        };
95
96        let sub = Self::split_index(m, new_size, ki as usize);
97
98        assert(ki * new_size >= 0);
99        assert((ki + 1) * new_size <= ps) by {
100            vstd::arithmetic::mul::lemma_mul_inequality(
101                ki + 1,
102                ps as int / new_size as int,
103                new_size as int,
104            );
105        };
106
107        vstd::arithmetic::mul::lemma_mul_is_distributive_add(new_size as int, ki, 1 as int);
108
109        let new_self = v.split_if_mapped_huge_spec(new_size);
110        let domain = Set::<int>::range(0int, ps as int / new_size as int);
111        assert(domain.contains(ki));
112
113        let new_filter = new_self.mappings.filter(
114            |m2: Mapping| m2.va_range.start <= new_self.cur_va < m2.va_range.end,
115        );
116        vstd::set::lemma_set_contains_len(new_filter, sub);
117    }
118
119    /// After `split_if_mapped_huge_spec(new_size)` on a valid view, the
120    /// mapping at `cur_va` has `page_size == new_size < m.page_size`.
121    ///
122    /// The sub-mapping `split_index(m, new_size, k)` has `page_size = new_size`.
123    /// No other mapping from the original view covers `cur_va` (non-overlapping),
124    /// so `query_mapping()` must return a sub-mapping with `page_size = new_size`.
125    pub proof fn split_if_mapped_huge_spec_decreases_page_size(v: Self, new_size: usize)
126        requires
127            v.inv(),
128            v.present(),
129            new_size > 0,
130            v.query_mapping().page_size > new_size,
131            v.query_mapping().page_size % new_size == 0,
132        ensures
133            v.split_if_mapped_huge_spec(new_size).present(),
134            v.split_if_mapped_huge_spec(new_size).query_mapping().page_size
135                < v.query_mapping().page_size,
136    {
137        Self::split_if_mapped_huge_spec_preserves_present(v, new_size);
138
139        let cur_va = v.cur_va;
140        let m = v.query_mapping();
141        let new_self = v.split_if_mapped_huge_spec(new_size);
142        let m2 = new_self.query_mapping();
143        let ps = m.page_size;
144
145        assert(v.mappings.contains(m) && m.va_range.start <= cur_va && cur_va < m.va_range.end) by {
146            let f = v.mappings.filter(
147                |m2: Mapping| m2.va_range.start <= v.cur_va < m2.va_range.end,
148            );
149            vstd::set::lemma_set_choose_len(f);
150        };
151
152        assert(new_self.mappings.contains(m2) && m2.va_range.start <= cur_va && cur_va
153            < 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            );
157            vstd::set::lemma_set_choose_len(f);
158        };
159
160        if v.mappings.contains(m2) && m2 != m {
161            assert(false);
162        }
163        let new_mappings = Set::<int>::range(0int, ps as int / new_size as int).map(
164            |n: int| Self::split_index(m, new_size, n as usize),
165        );
166        let k = choose|k: int|
167            0 <= k < ps as int / new_size as int && #[trigger] Self::split_index(
168                m,
169                new_size,
170                k as usize,
171            ) == m2;
172    }
173
174    /// `split_if_mapped_huge_spec` preserves `CursorView::inv()`.
175    ///
176    /// Requires: `v.inv()`, `v.present()`, the mapping at `cur_va` has
177    /// `page_size > new_size`, `page_size % new_size == 0`, and `new_size`
178    /// is itself a valid page size.
179    #[verifier::rlimit(80)]
180    #[verifier::spinoff_prover]
181    pub proof fn split_if_mapped_huge_spec_preserves_inv(v: Self, new_size: usize)
182        requires
183            v.inv(),
184            v.present(),
185            new_size > 0,
186            v.query_mapping().page_size > new_size,
187            v.query_mapping().page_size % new_size == 0,
188            set![4096usize, 2097152, 1073741824].contains(new_size),
189        ensures
190            v.split_if_mapped_huge_spec(new_size).inv(),
191    {
192        let cur_va = v.cur_va;
193        let m = v.query_mapping();
194        let ps = m.page_size;
195        let ns: int = new_size as int;
196        let count: int = ps as int / ns;
197
198        // Establish that m is in v.mappings and covers cur_va.
199        assert(v.mappings.contains(m) && m.va_range.start <= cur_va && cur_va < m.va_range.end) by {
200            let f = v.mappings.filter(
201                |m2: Mapping| m2.va_range.start <= v.cur_va < m2.va_range.end,
202            );
203            vstd::set::lemma_set_choose_len(f);
204        };
205        assert(m.inv());
206        assert(m.va_range.start % new_size as int == 0) by {
207            vstd::arithmetic::mul::lemma_mul_is_commutative(count, ns);
208            vstd::arithmetic::div_mod::lemma_mod_mod(m.va_range.start as int, ns, count);
209        };
210
211        let domain = Set::<int>::range(0int, count);
212        let new_mappings = domain.map(|n: int| Self::split_index(m, new_size, n as usize));
213        let new_self = v.split_if_mapped_huge_spec(new_size);
214
215        assert forall|m2: Mapping| #[trigger] new_self.mappings.contains(m2) implies m2.inv() by {
216            if v.mappings.contains(m2) && m2 != m {
217            } else {
218                let k = choose|k: int|
219                    0 <= k < count && #[trigger] Self::split_index(m, new_size, k as usize) == m2;
220                Self::lemma_split_index_inv(m, new_size, k);
221            }
222        };
223
224        assert forall|m2: Mapping| #[trigger] new_self.mappings.contains(m2) implies {
225            &&& vaddr_range_spec::<C>().start <= m2.va_range.start
226            &&& m2.va_range.end <= vaddr_range_spec::<C>().end + 1
227        } by {
228            if v.mappings.contains(m2) && m2 != m {
229            } else {
230                let k = choose|k: int|
231                    0 <= k < count && #[trigger] Self::split_index(m, new_size, k as usize) == m2;
232                Self::lemma_split_index_inv(m, new_size, k);
233            }
234        };
235    }
236
237    /// `split_while_huge` only modifies `mappings`, not `cur_va`.
238    pub broadcast proof fn lemma_split_while_huge_preserves_cur_va(self, size: usize)
239        requires
240            self.inv(),
241            size >= PAGE_SIZE,
242        ensures
243            #[trigger] self.split_while_huge(size).cur_va == self.cur_va,
244        decreases
245                if self.present() {
246                    self.query_mapping().page_size as int
247                } else {
248                    0
249                },
250    {
251        if self.present() {
252            let m = self.query_mapping();
253            if m.page_size > size {
254                let new_size = m.page_size / NR_ENTRIES;
255                let new_self = self.split_if_mapped_huge_spec(new_size);
256                // Establish m.inv() first.
257                let f = self.mappings.filter(
258                    |m2: Mapping| m2.va_range.start <= self.cur_va < m2.va_range.end,
259                );
260                vstd::set::lemma_set_choose_len(f);
261                assert(m.inv());
262                // new_size is a valid page size (case split on m.page_size).
263                assert(set![4096usize, 2097152, 1073741824].contains(new_size)) by {
264                    if m.page_size != 2097152 && m.page_size != 1073741824 {
265                        assert(false);
266                    }
267                };
268                Self::split_if_mapped_huge_spec_preserves_inv(self, new_size);
269
270                Self::split_if_mapped_huge_spec_decreases_page_size(self, new_size);
271                Self::lemma_split_while_huge_preserves_cur_va(new_self, size);
272            }
273        }
274    }
275
276    /// `split_while_huge` preserves `CursorView::inv()`.
277    pub proof fn lemma_split_while_huge_preserves_inv(self, size: usize)
278        requires
279            self.inv(),
280            size >= PAGE_SIZE,
281        ensures
282            self.split_while_huge(size).inv(),
283        decreases
284                if self.present() {
285                    self.query_mapping().page_size as int
286                } else {
287                    0
288                },
289    {
290        if self.present() {
291            let m = self.query_mapping();
292            if m.page_size > size {
293                let new_size = m.page_size / NR_ENTRIES;
294                let new_self = self.split_if_mapped_huge_spec(new_size);
295                // Establish m.inv() and call preserves_inv.
296                let f = self.mappings.filter(
297                    |m2: Mapping| m2.va_range.start <= self.cur_va < m2.va_range.end,
298                );
299                vstd::set::lemma_set_choose_len(f);
300                assert(m.inv());
301                assert(set![4096usize, 2097152, 1073741824].contains(new_size)) by {
302                    if m.page_size != 2097152 && m.page_size != 1073741824 {
303                        assert(false);
304                    }
305                };
306                Self::split_if_mapped_huge_spec_preserves_inv(self, new_size);
307                Self::split_if_mapped_huge_spec_decreases_page_size(self, new_size);
308                new_self.lemma_split_while_huge_preserves_inv(size);
309            }
310        }
311    }
312
313    /// Composition law for `split_while_huge`:
314    /// splitting to a finer target `s2 <= s1` is the same as first splitting to `s1` and then
315    /// further splitting to `s2`.
316    pub proof fn split_while_huge_compose(self, s1: usize, s2: usize)
317        requires
318            self.inv(),
319            s1 >= PAGE_SIZE,
320            s2 <= s1,
321        ensures
322            self.split_while_huge(s2) == self.split_while_huge(s1).split_while_huge(s2),
323        decreases
324                if self.present() {
325                    self.query_mapping().page_size as int
326                } else {
327                    0
328                },
329    {
330        if !self.present() {
331            return;
332        }
333        let m = self.query_mapping();
334        if m.page_size <= s1 {
335            return;
336        }
337        let new_size = m.page_size / NR_ENTRIES;
338        let f = self.mappings.filter(
339            |m2: Mapping| m2.va_range.start <= self.cur_va < m2.va_range.end,
340        );
341        vstd::set::lemma_set_choose_len(f);
342        assert(m.inv());
343        assert(set![4096usize, 2097152, 1073741824].contains(new_size)) by {
344            if m.page_size == 2097152 {
345            } else {
346            }
347        };
348        Self::split_if_mapped_huge_spec_preserves_inv(self, new_size);
349        Self::split_if_mapped_huge_spec_decreases_page_size(self, new_size);
350        self.split_if_mapped_huge_spec(new_size).split_while_huge_compose(s1, s2);
351    }
352
353    /// When `split_while_huge(size)` is a no-op and the view is `present()`,
354    /// the mapping at `cur_va` already has `page_size <= size`.
355    pub proof fn split_while_huge_noop_implies_page_size_le(self, size: usize)
356        requires
357            self.inv(),
358            size >= PAGE_SIZE,
359            self.split_while_huge(size) == self,
360            self.present(),
361        ensures
362            self.query_mapping().page_size <= size,
363    {
364        let m = self.query_mapping();
365        if m.page_size > size {
366            let new_size = m.page_size / NR_ENTRIES;
367            let f = self.mappings.filter(
368                |m2: Mapping| m2.va_range.start <= self.cur_va < m2.va_range.end,
369            );
370            vstd::set::lemma_set_choose_len(f);
371            assert(m.inv());
372            assert(set![4096usize, 2097152, 1073741824].contains(new_size)) by {
373                if m.page_size == 2097152 {
374                } else if m.page_size == 1073741824 {
375                } else {
376                    assert(false);
377                }
378            };
379            Self::split_if_mapped_huge_spec_preserves_inv(self, new_size);
380            let new_self = self.split_if_mapped_huge_spec(new_size);
381            new_self.split_while_huge_refinement(size, m);
382            let p = choose|p: Mapping| #[trigger]
383                new_self.mappings.contains(p) && p.va_range.start <= m.va_range.start
384                    && m.va_range.end <= p.va_range.end && m.pa_range.start == (p.pa_range.start + (
385                m.va_range.start - p.va_range.start)) as Paddr && m.property == p.property;
386            if self.mappings.contains(p) {
387            } else {
388                let new_mappings = Set::<int>::range(
389                    0int,
390                    m.page_size as int / new_size as int,
391                ).map(|n: int| Self::split_index(m, new_size, n as usize));
392                let k = choose|k: int|
393                    0 <= k < m.page_size as int / new_size as int && #[trigger] Self::split_index(
394                        m,
395                        new_size,
396                        k as usize,
397                    ) == p;
398            }
399        }
400    }
401
402    /// When the mapping at `cur_va` is exactly one split-step above `size`
403    /// (i.e. `query_mapping().page_size / NR_ENTRIES == size`), one step of
404    /// `split_while_huge` equals `split_if_mapped_huge_spec`:
405    ///
406    /// `self.split_while_huge(size) == self.split_if_mapped_huge_spec(size)`
407    ///
408    /// This is because `split_while_huge` takes one step
409    /// `split_if_mapped_huge_spec(m.page_size / NR_ENTRIES)` = `split_if_mapped_huge_spec(size)`,
410    /// then the sub-mapping at `cur_va` has `page_size == size <= size`, so it stops.
411    pub proof fn split_while_huge_one_step(self, size: usize)
412        requires
413            self.inv(),
414            self.present(),
415            self.query_mapping().page_size > size,
416            self.query_mapping().page_size / NR_ENTRIES == size,
417            self.query_mapping().page_size % size == 0,
418            set![4096usize, 2097152, 1073741824].contains(size),
419        ensures
420            self.split_while_huge(size).mappings == self.split_if_mapped_huge_spec(size).mappings,
421    {
422        let m = self.query_mapping();
423        let new_size = m.page_size / NR_ENTRIES;
424        let f0 = self.mappings.filter(
425            |m2: Mapping| m2.va_range.start <= self.cur_va < m2.va_range.end,
426        );
427        vstd::set::lemma_set_choose_len(f0);
428        Self::split_if_mapped_huge_spec_preserves_inv(self, new_size);
429        Self::split_if_mapped_huge_spec_decreases_page_size(self, new_size);
430        let new_self = self.split_if_mapped_huge_spec(new_size);
431
432        assert(new_self.query_mapping().page_size == new_size) by {
433            let new_m = new_self.query_mapping();
434            let f = new_self.mappings.filter(
435                |m2: Mapping| m2.va_range.start <= new_self.cur_va < m2.va_range.end,
436            );
437            vstd::set::lemma_set_choose_len(f);
438            if self.mappings.contains(new_m) && new_m != m {
439                assert(false);
440            }
441            let new_mappings = Set::<int>::range(0int, m.page_size as int / new_size as int).map(
442                |n: int| Self::split_index(m, new_size, n as usize),
443            );
444        };
445        assert(new_self.split_while_huge(size) == new_self);
446    }
447
448    /// Locality of `split_while_huge`: a mapping `m2` that is in `self.mappings`
449    /// and whose VA range does not contain `cur_va` is preserved.
450    #[verifier::rlimit(80)]
451    pub proof fn split_while_huge_locality(self, size: usize, m2: Mapping)
452        requires
453            self.inv(),
454            size >= PAGE_SIZE,
455            self.mappings.contains(m2),
456            !(m2.va_range.start <= self.cur_va < m2.va_range.end),
457        ensures
458            self.split_while_huge(size).mappings.contains(m2),
459        decreases
460                if self.present() {
461                    self.query_mapping().page_size as int
462                } else {
463                    0
464                },
465    {
466        if self.present() {
467            let m = self.query_mapping();
468            if m.page_size > size {
469                let new_size = m.page_size / NR_ENTRIES;
470                // Establish m covers cur_va.
471                let f = self.mappings.filter(
472                    |m3: Mapping| m3.va_range.start <= self.cur_va < m3.va_range.end,
473                );
474                vstd::set::lemma_set_choose_len(f);
475                assert(m.inv());
476                // m2 != m and disjoint va_ranges (non-overlap invariant).
477                assert(set![4096usize, 2097152, 1073741824].contains(new_size)) by {
478                    if m.page_size != 2097152 && m.page_size != 1073741824 {
479                        assert(false);
480                    }
481                };
482                let new_self = self.split_if_mapped_huge_spec(new_size);
483                Self::split_if_mapped_huge_spec_preserves_inv(self, new_size);
484                Self::split_if_mapped_huge_spec_decreases_page_size(self, new_size);
485                new_self.split_while_huge_locality(size, m2);
486            }
487        }
488    }
489
490    /// Refinement: every mapping in `split_while_huge(size).mappings` is either
491    /// from `self.mappings` or a sub-mapping of an entry in `self.mappings`.
492    /// Base lemma: every mapping in `split_if_mapped_huge_spec(new_size).mappings`
493    /// is either from the original mappings or a sub-mapping of `query_mapping()`.
494    pub proof fn split_if_mapped_huge_spec_refinement(self, new_size: usize, e: Mapping)
495        requires
496            self.inv(),
497            self.present(),
498            new_size > 0,
499            self.query_mapping().page_size > new_size,
500            self.query_mapping().page_size % new_size == 0,
501            self.split_if_mapped_huge_spec(new_size).mappings.contains(e),
502        ensures
503            self.mappings.contains(e) || {
504                let parent = self.query_mapping();
505                &&& self.mappings.contains(parent)
506                &&& parent.va_range.start <= e.va_range.start
507                &&& e.va_range.end <= parent.va_range.end
508                &&& e.pa_range.start == (parent.pa_range.start + (e.va_range.start
509                    - parent.va_range.start)) as Paddr
510                &&& e.property == parent.property
511            },
512    {
513        let qm = self.query_mapping();
514        let ps = qm.page_size;
515        let ns: int = new_size as int;
516        let count: int = ps as int / ns;
517        let new_self = self.split_if_mapped_huge_spec(new_size);
518        let domain = Set::<int>::range(0int, count);
519        let new_mappings = domain.map(|n: int| Self::split_index(qm, new_size, n as usize));
520
521        // Establish qm ∈ self.mappings.
522        let f = self.mappings.filter(
523            |m2: Mapping| m2.va_range.start <= self.cur_va < m2.va_range.end,
524        );
525        vstd::set::lemma_set_choose_len(f);
526        assert(qm.inv());
527
528        if self.mappings.remove(qm).contains(e) {
529        } else {
530            let k = choose|k: int|
531                0 <= k < count && #[trigger] Self::split_index(qm, new_size, k as usize) == e;
532
533            vstd::arithmetic::div_mod::lemma_fundamental_div_mod(ps as int, ns);
534
535            vstd::arithmetic::mul::lemma_mul_inequality(k + 1, count, ns);
536        }
537    }
538
539    pub proof fn split_while_huge_refinement(self, size: usize, m: Mapping)
540        requires
541            self.inv(),
542            size >= PAGE_SIZE,
543            self.split_while_huge(size).mappings.contains(m),
544        ensures
545            self.mappings.contains(m) || exists|parent: Mapping| #[trigger]
546                self.mappings.contains(parent) && parent.va_range.start <= m.va_range.start
547                    && m.va_range.end <= parent.va_range.end && m.pa_range.start == (
548                parent.pa_range.start + (m.va_range.start - parent.va_range.start)) as Paddr
549                    && m.property == parent.property,
550        decreases
551                if self.present() {
552                    self.query_mapping().page_size as int
553                } else {
554                    0
555                },
556    {
557        if self.present() {
558            let qm = self.query_mapping();
559            if qm.page_size > size {
560                let new_size = qm.page_size / NR_ENTRIES;
561                let new_self = self.split_if_mapped_huge_spec(new_size);
562
563                let f = self.mappings.filter(
564                    |m2: Mapping| m2.va_range.start <= self.cur_va < m2.va_range.end,
565                );
566                vstd::set::lemma_set_choose_len(f);
567                assert(qm.inv());
568                assert(set![4096usize, 2097152, 1073741824].contains(new_size)) by {
569                    if qm.page_size != 2097152 && qm.page_size != 1073741824 {
570                        assert(false);
571                    }
572                };
573                Self::split_if_mapped_huge_spec_preserves_inv(self, new_size);
574                Self::split_if_mapped_huge_spec_decreases_page_size(self, new_size);
575
576                new_self.split_while_huge_refinement(size, m);
577
578                if new_self.mappings.contains(m) {
579                } else {
580                    let p = choose|p: Mapping| #[trigger]
581                        new_self.mappings.contains(p) && p.va_range.start <= m.va_range.start
582                            && m.va_range.end <= p.va_range.end && m.pa_range.start == (
583                        p.pa_range.start + (m.va_range.start - p.va_range.start)) as Paddr
584                            && m.property == p.property;
585                    if !self.mappings.contains(p) {
586                    }
587                }
588            }
589        }
590    }
591
592    /// `split_while_huge` produces a set disjoint from any set that is
593    /// pairwise VA-disjoint from `self.mappings`.
594    ///
595    /// Set-disjointness of `other` from `self.mappings` is **not** sufficient
596    /// (counterexample: `other = {split_index(m, k, 0)}` collides with the
597    /// first sub-mapping after one split step). We need VA-disjointness so
598    /// that sub-mappings — which lie strictly inside the parent's va_range
599    /// and hence have distinct va_ranges from anything in `other` — cannot
600    /// equal any element of `other`.
601    pub proof fn split_while_huge_disjoint(self, size: usize, other: Set<Mapping>)
602        requires
603            self.inv(),
604            size >= PAGE_SIZE,
605            forall|m: Mapping, x: Mapping| #[trigger]
606                self.mappings.contains(m) && #[trigger] other.contains(x)
607                    ==> Mapping::disjoint_vaddrs(m, x),
608        ensures
609            self.split_while_huge(size).mappings.disjoint(other),
610        decreases
611                if self.present() {
612                    self.query_mapping().page_size as int
613                } else {
614                    0
615                },
616    {
617        if !self.present() {
618            // split_while_huge is a no-op; disjointness is by va-disjoint hypothesis.
619            assert forall|x: Mapping|
620                #![trigger other.contains(x)]
621                other.contains(x) implies !self.split_while_huge(size).mappings.contains(x) by {
622                if self.mappings.contains(x) {
623                    assert(x.inv());
624                }
625            };
626            return;
627        }
628        let m = self.query_mapping();
629        if m.page_size <= size {
630            assert forall|x: Mapping|
631                #![trigger other.contains(x)]
632                other.contains(x) implies !self.split_while_huge(size).mappings.contains(x) by {
633                if self.mappings.contains(x) {
634                    assert(x.inv());
635                }
636            };
637            return;
638        }
639        let new_size = m.page_size / NR_ENTRIES;
640
641        // Standard scaffolding: m ∈ self.mappings, m covers cur_va, m.inv().
642        let f = self.mappings.filter(
643            |m2: Mapping| m2.va_range.start <= self.cur_va < m2.va_range.end,
644        );
645        vstd::set::lemma_set_choose_len(f);
646        assert(m.inv());
647
648        assert(set![4096usize, 2097152, 1073741824].contains(new_size)) by {
649            if m.page_size == 2097152 {
650            } else if m.page_size == 1073741824 {
651            } else {
652                assert(false);
653            }
654        };
655        assert(m.page_size % new_size == 0) by {
656            assert(2097152usize % (2097152usize / 512usize) == 0) by (compute_only);
657            assert(1073741824usize % (1073741824usize / 512usize) == 0) by (compute_only);
658        };
659        assert(m.page_size > new_size) by {
660            assert(2097152usize > 2097152usize / 512usize) by (compute_only);
661            assert(1073741824usize > 1073741824usize / 512usize) by (compute_only);
662        };
663        Self::split_if_mapped_huge_spec_preserves_inv(self, new_size);
664        Self::split_if_mapped_huge_spec_decreases_page_size(self, new_size);
665        let new_self = self.split_if_mapped_huge_spec(new_size);
666
667        // Recursive call: establish va-disjoint hypothesis for new_self.
668        assert forall|m2: Mapping, x: Mapping| #[trigger]
669            new_self.mappings.contains(m2) && #[trigger] other.contains(
670                x,
671            ) implies Mapping::disjoint_vaddrs(m2, x) by {
672            if self.mappings.contains(m2) {
673                // Existing mapping preserved by split: va-disjoint by hypothesis.
674            } else {
675                // m2 is a sub-mapping of m, so its va_range is contained in m.va_range,
676                // and disjointness from x follows from disjointness of m and x.
677                Self::split_if_mapped_huge_spec_refinement(self, new_size, m2);
678                assert(Mapping::disjoint_vaddrs(m, x));
679            }
680        };
681
682        new_self.split_while_huge_disjoint(size, other);
683    }
684}
685
686impl<'rcu, C: PageTableConfig> CursorOwner<'rcu, C> {
687    /// When the current entry is a PT node at level `self.level`, any mapping at `cur_va` has
688    /// `page_size <= page_size(self.level - 1)`.  Therefore `split_while_huge` at
689    /// `page_size(self.level - 1)` does not split anything and is a no-op on the abstract view.
690    /// When present, the query_mapping is from the current subtree's view_rec.
691    proof fn query_mapping_from_subtree(self, qm: Mapping)
692        requires
693            self.inv(),
694            self.in_locked_range(),
695            self@.inv(),
696            self@.present(),
697            qm == self@.query_mapping(),
698        ensures
699            PageTableOwner(self.cur_subtree()).view_rec(self.cur_subtree().value().path).contains(
700                qm,
701            ),
702    {
703        let f = self@.mappings.filter(
704            |m2: Mapping| m2.va_range.start <= self@.cur_va < m2.va_range.end,
705        );
706        lemma_set_choose_len(f);
707        self.mapping_covering_cur_va_from_cur_subtree(qm);
708    }
709
710    pub proof fn split_while_huge_node_noop(self)
711        requires
712            self.inv(),
713            self.in_locked_range(),
714            self.cur_entry_owner().is_node(),
715            self.level > 1,
716        ensures
717            self@.split_while_huge(page_size::<PagingConsts>((self.level - 1) as PagingLevel))
718                == self@,
719    {
720        self.view_preserves_inv();
721        if self@.present() {
722            let subtree = self.cur_subtree();
723            let path = subtree.value().path;
724            let qm = self@.query_mapping();
725            self.query_mapping_from_subtree(qm);
726            PageTableOwner(subtree).view_rec_node_page_size_bound(path, qm);
727        }
728    }
729
730    /// When the current entry is absent, there is no mapping at `cur_va` in the abstract view,
731    /// so `split_while_huge` finds nothing to split and is a no-op for any target size.
732    pub proof fn split_while_huge_absent_noop(self, size: usize)
733        requires
734            self.inv(),
735            self.in_locked_range(),
736            self.cur_entry_owner().is_absent(),
737        ensures
738            self@.split_while_huge(size) == self@,
739    {
740        self.view_preserves_inv();
741        self.lemma_cur_entry_absent_not_present();
742    }
743
744    pub proof fn split_while_huge_at_level_noop(self)
745        requires
746            self.inv(),
747            self.in_locked_range(),
748        ensures
749            self@.split_while_huge(page_size::<PagingConsts>(self.level as PagingLevel)) == self@,
750    {
751        self.view_preserves_inv();
752        if self@.present() {
753            self.lemma_cur_subtree_inv();
754            let subtree = self.cur_subtree();
755            let path = subtree.value().path;
756            let qm = self@.query_mapping();
757            self.query_mapping_from_subtree(qm);
758            PageTableOwner(subtree).view_rec_page_size_bound(path, qm);
759        }
760    }
761
762    /// After split_if_mapped_huge + push_level, the mappings equal
763    /// `old_view.split_while_huge(page_size(current_level))`.
764    pub proof fn find_next_split_push_equals_split_while_huge(self, old_view: CursorView<C>)
765        requires
766            self.inv(),
767            old_view.inv(),
768            self.cur_entry_owner().is_frame(),
769            self@.cur_va == old_view.cur_va,
770            old_view.present(),
771            old_view.query_mapping().page_size > page_size::<PagingConsts>(
772                self.level as PagingLevel,
773            ),
774            old_view.query_mapping().page_size / NR_ENTRIES == page_size::<PagingConsts>(
775                self.level as PagingLevel,
776            ),
777            old_view.query_mapping().page_size % page_size::<PagingConsts>(
778                self.level as PagingLevel,
779            ) == 0,
780            self@.mappings =~= old_view.split_if_mapped_huge_spec(
781                page_size::<PagingConsts>(self.level as PagingLevel),
782            ).mappings,
783        ensures
784            self@.mappings == old_view.split_while_huge(
785                page_size::<PagingConsts>(self.level as PagingLevel),
786            ).mappings,
787    {
788        let ps = page_size::<PagingConsts>(self.level as PagingLevel);
789        let m = old_view.query_mapping();
790        let f = old_view.mappings.filter(
791            |m2: Mapping| m2.va_range.start <= old_view.cur_va < m2.va_range.end,
792        );
793        crate::specs::mm::page_table::cursor::page_size_lemmas::lemma_page_size_spec_values();
794        old_view.split_while_huge_one_step(ps);
795    }
796}
797
798} // verus!