Skip to main content

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

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