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::MAX_PADDR;
12use crate::specs::arch::mm::{NR_ENTRIES, NR_LEVELS, PAGE_SIZE};
13use crate::specs::arch::paging_consts::PagingConsts;
14use crate::specs::mm::page_table::Mapping;
15use crate::specs::mm::page_table::cursor::owners::*;
16use crate::specs::mm::page_table::owners::PageTableOwner;
17use vstd_extra::arithmetic::*;
18
19use crate::mm::page_table::page_size_spec as page_size;
20
21verus! {
22
23impl<C: PageTableConfig> CursorView<C> {
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(
43                |m2: Mapping| m2.va_range.start <= v.cur_va < m2.va_range.end,
44            );
45            assert(f.finite()) by {
46                vstd::set::axiom_set_intersect_finite::<Mapping>(
47                    v.mappings,
48                    Set::new(|m2: Mapping| m2.va_range.start <= v.cur_va < m2.va_range.end),
49                );
50            };
51            vstd::set::axiom_set_choose_len(f);
52        };
53        assert(m.inv());
54        assert(m.va_range.start + ps == m.va_range.end);
55
56        let diff: int = cur_va as int - m.va_range.start as int;
57        let ki: int = diff / new_size as int;
58        vstd::arithmetic::div_mod::lemma_fundamental_div_mod(diff, new_size as int);
59        vstd::arithmetic::div_mod::lemma_mod_division_less_than_divisor(diff, new_size as int);
60        vstd::arithmetic::div_mod::lemma_div_pos_is_pos(diff, new_size as int);
61
62        vstd::arithmetic::div_mod::lemma_fundamental_div_mod(ps as int, new_size as int);
63        assert(ki < ps as int / new_size as int) by {
64            if ki >= ps as int / new_size as int {
65                vstd::arithmetic::mul::lemma_mul_inequality(
66                    ps as int / new_size as int,
67                    ki,
68                    new_size as int,
69                );
70            }
71        };
72
73        let sub = Self::split_index(m, new_size, ki as usize);
74
75        assert(ki * (new_size as int) >= 0) by {
76            vstd::arithmetic::mul::lemma_mul_nonnegative(ki, new_size as int);
77        };
78        assert((ki + 1) * (new_size as int) <= ps as int) by {
79            vstd::arithmetic::mul::lemma_mul_inequality(
80                ki + 1,
81                ps as int / new_size as int,
82                new_size as int,
83            );
84        };
85        assert(m.va_range.start as int + (ki + 1) * (new_size as int) <= (m.va_range.end as int))
86            by {
87            vstd::arithmetic::mul::lemma_mul_is_commutative(ki + 1, new_size as int);
88            vstd::arithmetic::mul::lemma_mul_is_commutative(
89                ps as int / new_size as int,
90                new_size as int,
91            );
92        };
93
94        assert(ki as usize as int == ki);
95        vstd::arithmetic::mul::lemma_mul_is_distributive_add(new_size as int, ki, 1 as int);
96        assert((cur_va as int) >= (m.va_range.start as int) + ki * (new_size as int));
97        assert((cur_va as int) < (m.va_range.start as int) + (ki + 1) * (new_size as int));
98        assert(sub.va_range.start <= cur_va);
99        assert(cur_va < sub.va_range.end);
100
101        let new_self = v.split_if_mapped_huge_spec(new_size);
102        let domain = Set::<int>::new(|n: int| 0 <= n < ps as int / new_size as int);
103        assert(domain.contains(ki));
104        assert(new_self.mappings.contains(sub));
105
106        let new_filter = new_self.mappings.filter(
107            |m2: Mapping| m2.va_range.start <= new_self.cur_va < m2.va_range.end,
108        );
109        assert(new_filter.contains(sub));
110        assert(new_self.mappings.finite()) by {
111            vstd::set::axiom_set_remove_finite(v.mappings, m);
112            let domain = Set::<int>::new(|n: int| 0 <= n < ps as int / new_size as int);
113            assert(domain =~= int::range_set(0int, ps as int / new_size as int));
114            vstd::set_lib::range_set_properties::<int>(0int, ps as int / new_size as int);
115            domain.lemma_map_finite(|n: int| Self::split_index(m, new_size, n as usize));
116            vstd::set::axiom_set_union_finite(
117                v.mappings.remove(m),
118                domain.map(|n: int| Self::split_index(m, new_size, n as usize)),
119            );
120        };
121        assert(new_filter.finite()) by {
122            vstd::set::axiom_set_intersect_finite::<Mapping>(
123                new_self.mappings,
124                Set::new(|m2: Mapping| m2.va_range.start <= new_self.cur_va < m2.va_range.end),
125            );
126        };
127        vstd::set::axiom_set_contains_len(new_filter, sub);
128    }
129
130    /// After `split_if_mapped_huge_spec(new_size)` on a valid view, the
131    /// mapping at `cur_va` has `page_size == new_size < m.page_size`.
132    ///
133    /// The sub-mapping `split_index(m, new_size, k)` has `page_size = new_size`.
134    /// No other mapping from the original view covers `cur_va` (non-overlapping),
135    /// so `query_mapping()` must return a sub-mapping with `page_size = new_size`.
136    pub proof fn split_if_mapped_huge_spec_decreases_page_size(v: Self, new_size: usize)
137        requires
138            v.inv(),
139            v.present(),
140            new_size > 0,
141            v.query_mapping().page_size > new_size,
142            v.query_mapping().page_size % new_size == 0,
143        ensures
144            v.split_if_mapped_huge_spec(new_size).present(),
145            v.split_if_mapped_huge_spec(new_size).query_mapping().page_size
146                < v.query_mapping().page_size,
147    {
148        Self::split_if_mapped_huge_spec_preserves_present(v, new_size);
149
150        let cur_va = v.cur_va;
151        let m = v.query_mapping();
152        let new_self = v.split_if_mapped_huge_spec(new_size);
153        let m2 = new_self.query_mapping();
154        let ps = m.page_size;
155
156        assert(v.mappings.contains(m) && m.va_range.start <= cur_va && cur_va < m.va_range.end) by {
157            let f = v.mappings.filter(
158                |m2: Mapping| m2.va_range.start <= v.cur_va < m2.va_range.end,
159            );
160            assert(f.finite()) by {
161                vstd::set::axiom_set_intersect_finite::<Mapping>(
162                    v.mappings,
163                    Set::new(|m2: Mapping| m2.va_range.start <= v.cur_va < m2.va_range.end),
164                );
165            };
166            vstd::set::axiom_set_choose_len(f);
167        };
168
169        assert(new_self.mappings.contains(m2) && m2.va_range.start <= cur_va && cur_va
170            < m2.va_range.end) by {
171            let f = new_self.mappings.filter(
172                |m3: Mapping| m3.va_range.start <= new_self.cur_va < m3.va_range.end,
173            );
174            assert(new_self.mappings.finite()) by {
175                vstd::set::axiom_set_remove_finite(v.mappings, m);
176                let domain = Set::<int>::new(|n: int| 0 <= n < ps as int / new_size as int);
177                assert(domain =~= int::range_set(0int, ps as int / new_size as int));
178                vstd::set_lib::range_set_properties::<int>(0int, ps as int / new_size as int);
179                domain.lemma_map_finite(|n: int| Self::split_index(m, new_size, n as usize));
180                vstd::set::axiom_set_union_finite(
181                    v.mappings.remove(m),
182                    domain.map(|n: int| Self::split_index(m, new_size, n as usize)),
183                );
184            };
185            assert(f.finite()) by {
186                vstd::set::axiom_set_intersect_finite::<Mapping>(
187                    new_self.mappings,
188                    Set::new(|m3: Mapping| m3.va_range.start <= new_self.cur_va < m3.va_range.end),
189                );
190            };
191            vstd::set::axiom_set_choose_len(f);
192        };
193
194        if v.mappings.contains(m2) && m2 != m {
195            assert(m.va_range.end <= m2.va_range.start || m2.va_range.end <= m.va_range.start);
196            assert(false);
197        }
198        assert(!v.mappings.remove(m).contains(m2));
199        let new_mappings = Set::<int>::new(|n: int| 0 <= n < ps as int / new_size as int).map(
200            |n: int| Self::split_index(m, new_size, n as usize),
201        );
202        assert(new_mappings.contains(m2));
203        let k = choose|k: int|
204            0 <= k < ps as int / new_size as int && #[trigger] Self::split_index(
205                m,
206                new_size,
207                k as usize,
208            ) == m2;
209        assert(m2.page_size == new_size);
210    }
211
212    /// `split_if_mapped_huge_spec` preserves `CursorView::inv()`.
213    ///
214    /// Requires: `v.inv()`, `v.present()`, the mapping at `cur_va` has
215    /// `page_size > new_size`, `page_size % new_size == 0`, and `new_size`
216    /// is itself a valid page size.
217    pub proof fn split_if_mapped_huge_spec_preserves_inv(v: Self, new_size: usize)
218        requires
219            v.inv(),
220            v.present(),
221            new_size > 0,
222            v.query_mapping().page_size > new_size,
223            v.query_mapping().page_size % new_size == 0,
224            set![4096usize, 2097152, 1073741824].contains(new_size),
225        ensures
226            v.split_if_mapped_huge_spec(new_size).inv(),
227    {
228        let cur_va = v.cur_va;
229        let m = v.query_mapping();
230        let ps = m.page_size;
231        let ns: int = new_size as int;
232        let new_self = v.split_if_mapped_huge_spec(new_size);
233        let count: int = ps as int / ns;
234        let domain = Set::<int>::new(|n: int| 0 <= n < count);
235        let new_mappings = domain.map(|n: int| Self::split_index(m, new_size, n as usize));
236
237        // --- Establish that m is in v.mappings and covers cur_va ---
238        assert(v.mappings.contains(m) && m.va_range.start <= cur_va && cur_va < m.va_range.end) by {
239            let f = v.mappings.filter(
240                |m2: Mapping| m2.va_range.start <= v.cur_va < m2.va_range.end,
241            );
242            assert(f.finite()) by {
243                vstd::set::axiom_set_intersect_finite::<Mapping>(
244                    v.mappings,
245                    Set::new(|m2: Mapping| m2.va_range.start <= v.cur_va < m2.va_range.end),
246                );
247            };
248            vstd::set::axiom_set_choose_len(f);
249        };
250        assert(m.inv());
251
252        // --- (1) cur_va preserved ---
253
254        // --- (2) mappings.finite() ---
255        assert(new_self.mappings.finite()) by {
256            vstd::set::axiom_set_remove_finite(v.mappings, m);
257            assert(domain =~= int::range_set(0int, count));
258            vstd::set_lib::range_set_properties::<int>(0int, count);
259            domain.lemma_map_finite(|n: int| Self::split_index(m, new_size, n as usize));
260            vstd::set::axiom_set_union_finite(v.mappings.remove(m), new_mappings);
261        };
262
263        // --- Helper: ps == count * ns (no remainder) ---
264        vstd::arithmetic::div_mod::lemma_fundamental_div_mod(ps as int, ns);
265        assert(ps as int == count * ns);
266
267        assert(m.va_range.start % new_size as int == 0) by {
268            vstd::arithmetic::mul::lemma_mul_is_commutative(count, ns);
269            vstd::arithmetic::div_mod::lemma_mod_mod(m.va_range.start as int, ns, count);
270        };
271        assert(m.pa_range.start % new_size == 0) by {
272            vstd::arithmetic::mul::lemma_mul_is_commutative(count, ns);
273            vstd::arithmetic::div_mod::lemma_mod_mod(m.pa_range.start as int, ns, count);
274        };
275
276        // --- (3) All mappings satisfy Mapping::inv() ---
277        assert forall|e: Mapping| new_self.mappings.contains(e) implies #[trigger] e.inv() by {
278            if v.mappings.remove(m).contains(e) {
279                assert(v.mappings.contains(e));
280            } else {
281                assert(new_mappings.contains(e));
282                let k = choose|k: int|
283                    0 <= k < count && #[trigger] Self::split_index(m, new_size, k as usize) == e;
284                let sub = Self::split_index(m, new_size, k as usize);
285
286                // sub.page_size == new_size ∈ {4096, 2M, 1G}
287                assert(set![4096usize, 2097152, 1073741824].contains(sub.page_size));
288
289                // Alignment: (base + k * ns) % ns == base % ns == 0.
290                // Use lemma_mod_multiples_vanish: (ns * a + b) % ns == b % ns.
291                vstd::arithmetic::mul::lemma_mul_is_commutative(k, ns);
292                vstd::arithmetic::div_mod::lemma_mod_multiples_vanish(
293                    k,
294                    m.va_range.start as int,
295                    ns,
296                );
297                assert(sub.va_range.start % new_size as int == 0);
298                vstd::arithmetic::mul::lemma_mul_is_commutative(k + 1, ns);
299                vstd::arithmetic::div_mod::lemma_mod_multiples_vanish(
300                    k + 1,
301                    m.va_range.start as int,
302                    ns,
303                );
304                assert(sub.va_range.end % new_size as int == 0);
305
306                vstd::arithmetic::mul::lemma_mul_is_commutative(k, ns);
307                vstd::arithmetic::div_mod::lemma_mod_multiples_vanish(
308                    k,
309                    m.pa_range.start as int,
310                    ns,
311                );
312                assert(sub.pa_range.start % new_size == 0);
313                vstd::arithmetic::mul::lemma_mul_is_commutative(k + 1, ns);
314                vstd::arithmetic::div_mod::lemma_mod_multiples_vanish(
315                    k + 1,
316                    m.pa_range.start as int,
317                    ns,
318                );
319                assert(sub.pa_range.end % new_size == 0);
320
321                // Range spans exactly new_size.
322                vstd::arithmetic::mul::lemma_mul_is_distributive_add(ns, k, 1int);
323                assert(sub.va_range.start + new_size == sub.va_range.end);
324                assert(sub.pa_range.start + new_size == sub.pa_range.end);
325
326                // Bounds: sub is within m's range.
327                vstd::arithmetic::mul::lemma_mul_nonnegative(k, ns);
328                vstd::arithmetic::mul::lemma_mul_inequality(k + 1, count, ns);
329                // (k+1)*ns <= count*ns = ps, so m.start + (k+1)*ns <= m.start + ps = m.end.
330                assert(sub.va_range.start >= m.va_range.start);
331                assert(sub.va_range.end <= m.va_range.end);
332                assert(sub.va_range.start <= sub.va_range.end);
333
334                assert(sub.pa_range.start >= m.pa_range.start);
335                assert(sub.pa_range.end <= m.pa_range.end);
336                assert(sub.pa_range.start <= sub.pa_range.end);
337            }
338        };
339
340        // --- (4) Non-overlapping ---
341        assert forall|e1: Mapping, e2: Mapping| #[trigger]
342            new_self.mappings.contains(e1) && #[trigger] new_self.mappings.contains(e2) && e1
343                != e2 implies e1.va_range.end <= e2.va_range.start || e2.va_range.end
344            <= e1.va_range.start by {
345            if v.mappings.remove(m).contains(e1) && v.mappings.remove(m).contains(e2) {
346                // Both from original: inherit from v.inv().
347                assert(v.mappings.contains(e1));
348                assert(v.mappings.contains(e2));
349            } else if v.mappings.remove(m).contains(e1) && new_mappings.contains(e2) {
350                // e1 from original (disjoint from m), e2 sub-mapping within m.
351                assert(v.mappings.contains(e1));
352                assert(e1 != m);
353                let k2 = choose|k: int|
354                    0 <= k < count && #[trigger] Self::split_index(m, new_size, k as usize) == e2;
355                vstd::arithmetic::mul::lemma_mul_nonnegative(k2, ns);
356                vstd::arithmetic::mul::lemma_mul_inequality(k2 + 1, count, ns);
357                assert(e2.va_range.start >= m.va_range.start);
358                assert(e2.va_range.end <= m.va_range.end);
359            } else if new_mappings.contains(e1) && v.mappings.remove(m).contains(e2) {
360                // Symmetric case.
361                assert(v.mappings.contains(e2));
362                assert(e2 != m);
363                let k1 = choose|k: int|
364                    0 <= k < count && #[trigger] Self::split_index(m, new_size, k as usize) == e1;
365                vstd::arithmetic::mul::lemma_mul_nonnegative(k1, ns);
366                vstd::arithmetic::mul::lemma_mul_inequality(k1 + 1, count, ns);
367                assert(e1.va_range.start >= m.va_range.start);
368                assert(e1.va_range.end <= m.va_range.end);
369            } else if new_mappings.contains(e1) && new_mappings.contains(e2) {
370                // Both sub-mappings with different indices.
371                let i = choose|k: int|
372                    0 <= k < count && #[trigger] Self::split_index(m, new_size, k as usize) == e1;
373                let j = choose|k: int|
374                    0 <= k < count && #[trigger] Self::split_index(m, new_size, k as usize) == e2;
375                vstd::arithmetic::mul::lemma_mul_nonnegative(i, ns);
376                vstd::arithmetic::mul::lemma_mul_nonnegative(j, ns);
377                if i < j {
378                    vstd::arithmetic::mul::lemma_mul_inequality(i + 1, j, ns);
379                    vstd::arithmetic::mul::lemma_mul_is_distributive_add(ns, i, 1int);
380                } else {
381                    vstd::arithmetic::mul::lemma_mul_inequality(j + 1, i, ns);
382                    vstd::arithmetic::mul::lemma_mul_is_distributive_add(ns, j, 1int);
383                }
384            }
385        };
386    }
387
388    /// `split_while_huge` only modifies `mappings`, not `cur_va`.
389    pub broadcast proof fn lemma_split_while_huge_preserves_cur_va(self, size: usize)
390        requires
391            self.inv(),
392            size >= PAGE_SIZE,
393        ensures
394            #[trigger] self.split_while_huge(size).cur_va == self.cur_va,
395        decreases
396                if self.present() {
397                    self.query_mapping().page_size as int
398                } else {
399                    0
400                },
401    {
402        if self.present() {
403            let m = self.query_mapping();
404            if m.page_size > size {
405                let new_size = m.page_size / NR_ENTRIES;
406                let new_self = self.split_if_mapped_huge_spec(new_size);
407                // Establish m.inv() first.
408                let f = self.mappings.filter(
409                    |m2: Mapping| m2.va_range.start <= self.cur_va < m2.va_range.end,
410                );
411                vstd::set::axiom_set_intersect_finite::<Mapping>(
412                    self.mappings,
413                    Set::new(|m2: Mapping| m2.va_range.start <= self.cur_va < m2.va_range.end),
414                );
415                vstd::set::axiom_set_choose_len(f);
416                assert(m.inv());
417                assert(NR_ENTRIES == 512usize) by (compute_only);
418                // new_size is a valid page size (case split on m.page_size).
419                assert(set![4096usize, 2097152, 1073741824].contains(new_size)) by {
420                    if m.page_size == 2097152 {
421                        assert(2097152usize / 512 == 4096usize);
422                    } else if m.page_size == 1073741824 {
423                        assert(1073741824usize / 512 == 2097152usize);
424                    } else {
425                        assert(4096usize / 512 == 8usize);
426                        assert(false);
427                    }  // 4096 case impossible: 8 not in set
428                };
429                assert(m.page_size % new_size == 0) by {
430                    if m.page_size == 2097152 {
431                        assert(2097152usize % 4096 == 0);
432                    } else {
433                        assert(1073741824usize % 2097152 == 0);
434                    }
435                };
436                Self::split_if_mapped_huge_spec_preserves_inv(self, new_size);
437
438                Self::split_if_mapped_huge_spec_decreases_page_size(self, new_size);
439                Self::lemma_split_while_huge_preserves_cur_va(new_self, size);
440            }
441        }
442    }
443
444    /// `split_while_huge` preserves `CursorView::inv()`.
445    pub proof fn lemma_split_while_huge_preserves_inv(self, size: usize)
446        requires
447            self.inv(),
448            size >= PAGE_SIZE,
449        ensures
450            self.split_while_huge(size).inv(),
451        decreases
452                if self.present() {
453                    self.query_mapping().page_size as int
454                } else {
455                    0
456                },
457    {
458        if self.present() {
459            let m = self.query_mapping();
460            if m.page_size > size {
461                let new_size = m.page_size / NR_ENTRIES;
462                let new_self = self.split_if_mapped_huge_spec(new_size);
463                // Establish m.inv() and call preserves_inv.
464                let f = self.mappings.filter(
465                    |m2: Mapping| m2.va_range.start <= self.cur_va < m2.va_range.end,
466                );
467                vstd::set::axiom_set_intersect_finite::<Mapping>(
468                    self.mappings,
469                    Set::new(|m2: Mapping| m2.va_range.start <= self.cur_va < m2.va_range.end),
470                );
471                vstd::set::axiom_set_choose_len(f);
472                assert(m.inv());
473                assert(NR_ENTRIES == 512usize) by (compute_only);
474                assert(set![4096usize, 2097152, 1073741824].contains(new_size)) by {
475                    if m.page_size == 2097152 {
476                        assert(2097152usize / 512 == 4096usize);
477                    } else if m.page_size == 1073741824 {
478                        assert(1073741824usize / 512 == 2097152usize);
479                    } else {
480                        assert(4096usize / 512 == 8usize);
481                        assert(false);
482                    }
483                };
484                assert(m.page_size % new_size == 0) by {
485                    if m.page_size == 2097152 {
486                        assert(2097152usize % 4096 == 0);
487                    } else {
488                        assert(1073741824usize % 2097152 == 0);
489                    }
490                };
491                Self::split_if_mapped_huge_spec_preserves_inv(self, new_size);
492                Self::split_if_mapped_huge_spec_decreases_page_size(self, new_size);
493                new_self.lemma_split_while_huge_preserves_inv(size);
494            }
495        }
496    }
497
498    /// Composition law for `split_while_huge`:
499    /// splitting to a finer target `s2 <= s1` is the same as first splitting to `s1` and then
500    /// further splitting to `s2`.
501    pub proof fn split_while_huge_compose(self, s1: usize, s2: usize)
502        requires
503            self.inv(),
504            s1 >= PAGE_SIZE,
505            s2 <= s1,
506        ensures
507            self.split_while_huge(s2) == self.split_while_huge(s1).split_while_huge(s2),
508        decreases
509                if self.present() {
510                    self.query_mapping().page_size as int
511                } else {
512                    0
513                },
514    {
515        if !self.present() {
516            return;
517        }
518        let m = self.query_mapping();
519        if m.page_size <= s1 {
520            return;
521        }
522        let new_size = m.page_size / NR_ENTRIES;
523        let f = self.mappings.filter(
524            |m2: Mapping| m2.va_range.start <= self.cur_va < m2.va_range.end,
525        );
526        vstd::set::axiom_set_intersect_finite::<Mapping>(
527            self.mappings,
528            Set::new(|m2: Mapping| m2.va_range.start <= self.cur_va < m2.va_range.end),
529        );
530        vstd::set::axiom_set_choose_len(f);
531        assert(m.inv());
532        assert(NR_ENTRIES == 512usize) by (compute_only);
533        assert(set![4096usize, 2097152, 1073741824].contains(new_size)) by {
534            if m.page_size == 2097152 {
535                assert(2097152usize / 512 == 4096usize);
536            } else {
537                assert(1073741824usize / 512 == 2097152usize);
538            }
539        };
540        assert(m.page_size % new_size == 0) by {
541            if m.page_size == 2097152 {
542                assert(2097152usize % 4096 == 0);
543            } else {
544                assert(1073741824usize % 2097152 == 0);
545            }
546        };
547        Self::split_if_mapped_huge_spec_preserves_inv(self, new_size);
548        Self::split_if_mapped_huge_spec_decreases_page_size(self, new_size);
549        self.split_if_mapped_huge_spec(new_size).split_while_huge_compose(s1, s2);
550    }
551
552    /// When the current entry is absent or maps at `page_size <= size`, `split_while_huge(size)`
553    /// is a no-op.  Applying a second call with the same `size` therefore returns the same value.
554    pub proof fn split_while_huge_idempotent(self, size: usize)
555        requires
556            self.inv(),
557            size >= PAGE_SIZE,
558        ensures
559            self.split_while_huge(size).split_while_huge(size) == self.split_while_huge(size),
560    {
561        self.split_while_huge_compose(size, size);
562    }
563
564    /// When `split_while_huge(size)` is a no-op and the view is `present()`,
565    /// the mapping at `cur_va` already has `page_size <= size`.
566    pub proof fn split_while_huge_noop_implies_page_size_le(self, size: usize)
567        requires
568            self.inv(),
569            size >= PAGE_SIZE,
570            self.split_while_huge(size) == self,
571            self.present(),
572        ensures
573            self.query_mapping().page_size <= size,
574    {
575        let m = self.query_mapping();
576        if m.page_size > size {
577            let new_size = m.page_size / NR_ENTRIES;
578            let f = self.mappings.filter(
579                |m2: Mapping| m2.va_range.start <= self.cur_va < m2.va_range.end,
580            );
581            vstd::set::axiom_set_intersect_finite::<Mapping>(
582                self.mappings,
583                Set::new(|m2: Mapping| m2.va_range.start <= self.cur_va < m2.va_range.end),
584            );
585            vstd::set::axiom_set_choose_len(f);
586            assert(m.inv());
587            assert(NR_ENTRIES == 512usize) by (compute_only);
588            assert(set![4096usize, 2097152, 1073741824].contains(new_size)) by {
589                if m.page_size == 2097152 {
590                    assert(2097152usize / 512 == 4096usize);
591                } else if m.page_size == 1073741824 {
592                    assert(1073741824usize / 512 == 2097152usize);
593                } else {
594                    assert(4096usize / 512 == 8usize);
595                    assert(false);
596                }
597            };
598            assert(m.page_size % new_size == 0) by {
599                if m.page_size == 2097152 {
600                    assert(2097152usize % 4096 == 0);
601                } else {
602                    assert(1073741824usize % 2097152 == 0);
603                }
604            };
605            Self::split_if_mapped_huge_spec_preserves_inv(self, new_size);
606            Self::split_if_mapped_huge_spec_decreases_page_size(self, new_size);
607            let new_self = self.split_if_mapped_huge_spec(new_size);
608            new_self.split_while_huge_refinement(size, m);
609            assert(!new_self.mappings.contains(m)) by {
610                let new_mappings = Set::<int>::new(
611                    |n: int| 0 <= n < m.page_size as int / new_size as int,
612                ).map(|n: int| Self::split_index(m, new_size, n as usize));
613                if new_mappings.contains(m) {
614                    let k = choose|k: int|
615                        0 <= k < m.page_size as int / new_size as int
616                            && #[trigger] Self::split_index(m, new_size, k as usize) == m;
617                }
618            };
619            let p = choose|p: Mapping| #[trigger]
620                new_self.mappings.contains(p) && p.va_range.start <= m.va_range.start
621                    && m.va_range.end <= p.va_range.end && m.pa_range.start == (p.pa_range.start + (
622                m.va_range.start - p.va_range.start)) as Paddr && m.property == p.property;
623            if self.mappings.contains(p) {
624                assert(p.va_range.start <= self.cur_va < p.va_range.end);
625            } else {
626                let new_mappings = Set::<int>::new(
627                    |n: int| 0 <= n < m.page_size as int / new_size as int,
628                ).map(|n: int| Self::split_index(m, new_size, n as usize));
629                let k = choose|k: int|
630                    0 <= k < m.page_size as int / new_size as int && #[trigger] Self::split_index(
631                        m,
632                        new_size,
633                        k as usize,
634                    ) == p;
635                vstd::arithmetic::div_mod::lemma_fundamental_div_mod(
636                    m.page_size as int,
637                    new_size as int,
638                );
639                vstd::arithmetic::mul::lemma_mul_inequality(
640                    (k + 1) as int,
641                    m.page_size as int / new_size as int,
642                    new_size as int,
643                );
644                vstd::arithmetic::mul::lemma_mul_is_distributive_add_other_way(
645                    new_size as int,
646                    k,
647                    1int,
648                );
649            }
650        }
651    }
652
653    /// When the mapping at `cur_va` is exactly one split-step above `size`
654    /// (i.e. `query_mapping().page_size / NR_ENTRIES == size`), one step of
655    /// `split_while_huge` equals `split_if_mapped_huge_spec`:
656    ///
657    /// `self.split_while_huge(size) == self.split_if_mapped_huge_spec(size)`
658    ///
659    /// This is because `split_while_huge` takes one step
660    /// `split_if_mapped_huge_spec(m.page_size / NR_ENTRIES)` = `split_if_mapped_huge_spec(size)`,
661    /// then the sub-mapping at `cur_va` has `page_size == size <= size`, so it stops.
662    pub proof fn split_while_huge_one_step(self, size: usize)
663        requires
664            self.inv(),
665            self.present(),
666            self.query_mapping().page_size > size,
667            self.query_mapping().page_size / NR_ENTRIES == size,
668            self.query_mapping().page_size % size == 0,
669            set![4096usize, 2097152, 1073741824].contains(size),
670        ensures
671            self.split_while_huge(size).mappings =~= self.split_if_mapped_huge_spec(size).mappings,
672    {
673        let m = self.query_mapping();
674        let new_size = m.page_size / NR_ENTRIES;
675        let f0 = self.mappings.filter(
676            |m2: Mapping| m2.va_range.start <= self.cur_va < m2.va_range.end,
677        );
678        vstd::set::axiom_set_intersect_finite::<Mapping>(
679            self.mappings,
680            Set::new(|m2: Mapping| m2.va_range.start <= self.cur_va < m2.va_range.end),
681        );
682        vstd::set::axiom_set_choose_len(f0);
683        assert(m.inv());
684        assert(NR_ENTRIES == 512usize) by (compute_only);
685        Self::split_if_mapped_huge_spec_preserves_present(self, new_size);
686        Self::split_if_mapped_huge_spec_preserves_inv(self, new_size);
687        Self::split_if_mapped_huge_spec_decreases_page_size(self, new_size);
688        let new_self = self.split_if_mapped_huge_spec(new_size);
689
690        assert(new_self.query_mapping().page_size == new_size) by {
691            let new_m = new_self.query_mapping();
692            let f = new_self.mappings.filter(
693                |m2: Mapping| m2.va_range.start <= new_self.cur_va < m2.va_range.end,
694            );
695            vstd::set::axiom_set_intersect_finite::<Mapping>(
696                new_self.mappings,
697                Set::new(|m2: Mapping| m2.va_range.start <= new_self.cur_va < m2.va_range.end),
698            );
699            vstd::set::axiom_set_choose_len(f);
700            if self.mappings.contains(new_m) && new_m != m {
701                assert(false);
702            }
703            let new_mappings = Set::<int>::new(
704                |n: int| 0 <= n < m.page_size as int / new_size as int,
705            ).map(|n: int| Self::split_index(m, new_size, n as usize));
706            assert(new_mappings.contains(new_m));
707        };
708        assert(new_self.split_while_huge(size) == new_self);
709    }
710
711    /// Locality of `split_if_mapped_huge_spec`: a mapping `m2` whose VA range
712    /// is disjoint from the mapping at `cur_va` is preserved.
713    pub proof fn split_if_mapped_huge_spec_locality(self, new_size: usize, m2: Mapping)
714        requires
715            self.inv(),
716            self.present(),
717            new_size > 0,
718            self.query_mapping().page_size % new_size == 0,
719            Mapping::disjoint_vaddrs(m2, self.query_mapping()),
720        ensures
721            self.split_if_mapped_huge_spec(new_size).mappings.contains(m2)
722                == self.mappings.contains(m2),
723    {
724        let m = self.query_mapping();
725        let size = m.page_size;
726        let new_mappings = Set::<int>::new(|n: int| 0 <= n < size / new_size).map(
727            |n: int| Self::split_index(m, new_size, n as usize),
728        );
729
730        // Establish m covers cur_va (from present() + choose semantics).
731        let f = self.mappings.filter(
732            |m2: Mapping| m2.va_range.start <= self.cur_va < m2.va_range.end,
733        );
734        assert(f.finite()) by {
735            vstd::set::axiom_set_intersect_finite::<Mapping>(
736                self.mappings,
737                Set::new(|m2: Mapping| m2.va_range.start <= self.cur_va < m2.va_range.end),
738            );
739        };
740        vstd::set::axiom_set_choose_len(f);
741        assert(m.inv());
742
743        assert(!new_mappings.contains(m2)) by {
744            if new_mappings.contains(m2) {
745                let k = choose|k: int|
746                    0 <= k < size as int / new_size as int && #[trigger] Self::split_index(
747                        m,
748                        new_size,
749                        k as usize,
750                    ) == m2;
751                vstd::arithmetic::div_mod::lemma_fundamental_div_mod(size as int, new_size as int);
752                vstd::arithmetic::mul::lemma_mul_inequality(
753                    (k + 1) as int,
754                    size as int / new_size as int,
755                    new_size as int,
756                );
757                vstd::arithmetic::mul::lemma_mul_is_distributive_add_other_way(
758                    new_size as int,
759                    k,
760                    1int,
761                );
762                vstd::arithmetic::mul::lemma_mul_nonnegative(k, new_size as int);
763            }
764        };
765    }
766
767    /// Locality of `split_while_huge`: a mapping `m2` that is in `self.mappings`
768    /// and whose VA range does not contain `cur_va` is preserved.
769    ///
770    /// This is stronger than `split_if_mapped_huge_spec_locality` because it
771    /// handles the recursive case: each step only splits the mapping at `cur_va`,
772    /// and `m2` is disjoint from that mapping (by non-overlap invariant).
773    #[verifier::rlimit(80)]
774    pub proof fn split_while_huge_locality(self, size: usize, m2: Mapping)
775        requires
776            self.inv(),
777            size >= PAGE_SIZE,
778            self.mappings.contains(m2),
779            !(m2.va_range.start <= self.cur_va < m2.va_range.end),
780        ensures
781            self.split_while_huge(size).mappings.contains(m2),
782        decreases
783                if self.present() {
784                    self.query_mapping().page_size as int
785                } else {
786                    0
787                },
788    {
789        if self.present() {
790            let m = self.query_mapping();
791            if m.page_size > size {
792                let new_size = m.page_size / NR_ENTRIES;
793                // Establish m covers cur_va.
794                let f = self.mappings.filter(
795                    |m3: Mapping| m3.va_range.start <= self.cur_va < m3.va_range.end,
796                );
797                assert(f.finite()) by {
798                    vstd::set::axiom_set_intersect_finite::<Mapping>(
799                        self.mappings,
800                        Set::new(|m3: Mapping| m3.va_range.start <= self.cur_va < m3.va_range.end),
801                    );
802                };
803                vstd::set::axiom_set_choose_len(f);
804                assert(self.mappings.contains(m));
805                assert(m.va_range.start <= self.cur_va < m.va_range.end);
806                assert(m.inv());
807                // m2 != m and disjoint va_ranges (non-overlap invariant).
808                assert(Mapping::disjoint_vaddrs(m2, m));
809                // page_size % new_size == 0
810                assert(NR_ENTRIES == 512usize);
811                assert(m.page_size % new_size == 0) by {
812                    if m.page_size == 4096 {
813                        assert(4096usize % (4096usize / 512usize) == 0);
814                    } else if m.page_size == 2097152 {
815                        assert(2097152usize % (2097152usize / 512usize) == 0);
816                    } else {
817                        assert(1073741824usize % (1073741824usize / 512usize) == 0);
818                    }
819                };
820                assert(set![4096usize, 2097152, 1073741824].contains(new_size)) by {
821                    if m.page_size == 2097152 {
822                        assert(2097152usize / 512 == 4096usize);
823                    } else if m.page_size == 1073741824 {
824                        assert(1073741824usize / 512 == 2097152usize);
825                    } else {
826                        assert(4096usize / 512 == 8usize);
827                        assert(false);
828                    }
829                };
830                self.split_if_mapped_huge_spec_locality(new_size, m2);
831                let new_self = self.split_if_mapped_huge_spec(new_size);
832                Self::split_if_mapped_huge_spec_preserves_inv(self, new_size);
833                Self::split_if_mapped_huge_spec_decreases_page_size(self, new_size);
834                new_self.split_while_huge_locality(size, m2);
835            }
836        }
837    }
838
839    /// Converse locality: a mapping NOT in `self.mappings` and whose VA range
840    /// does not overlap any mapping in `self.mappings` that contains `cur_va`
841    /// is also NOT in `self.split_while_huge(size).mappings`.
842    ///
843    /// More precisely: if `m2 ∉ self.mappings` and `m2.va_range` is disjoint
844    /// from the range `[start, end)` of the mapping at `cur_va` (if present),
845    /// then `m2 ∉ self.split_while_huge(size).mappings`.
846    #[verifier::rlimit(120)]
847    pub proof fn split_while_huge_locality_absent(self, size: usize, m2: Mapping)
848        requires
849            self.inv(),
850            size >= PAGE_SIZE,
851            !self.mappings.contains(m2),
852            self.present() ==> Mapping::disjoint_vaddrs(m2, self.query_mapping()),
853        ensures
854            !self.split_while_huge(size).mappings.contains(m2),
855        decreases
856                if self.present() {
857                    self.query_mapping().page_size as int
858                } else {
859                    0
860                },
861    {
862        if self.present() {
863            let m = self.query_mapping();
864            if m.page_size > size {
865                let new_size = m.page_size / NR_ENTRIES;
866                // Establish m covers cur_va and m.inv().
867                let f = self.mappings.filter(
868                    |m3: Mapping| m3.va_range.start <= self.cur_va < m3.va_range.end,
869                );
870                assert(f.finite()) by {
871                    vstd::set::axiom_set_intersect_finite::<Mapping>(
872                        self.mappings,
873                        Set::new(|m3: Mapping| m3.va_range.start <= self.cur_va < m3.va_range.end),
874                    );
875                };
876                vstd::set::axiom_set_choose_len(f);
877                assert(self.mappings.contains(m));
878                // page_size % new_size == 0
879                assert(m.inv());
880                assert(m.page_size % new_size == 0) by {
881                    assert(set![4096usize, 2097152usize, 1073741824usize].contains(m.page_size));
882                    assert(4096usize % (4096usize / 512usize) == 0) by (compute_only);
883                    assert(2097152usize % (2097152usize / 512usize) == 0) by (compute_only);
884                    assert(1073741824usize % (1073741824usize / 512usize) == 0) by (compute_only);
885                };
886                assert(set![4096usize, 2097152, 1073741824].contains(new_size)) by {
887                    if m.page_size == 2097152 {
888                        assert(2097152usize / 512 == 4096usize);
889                    } else if m.page_size == 1073741824 {
890                        assert(1073741824usize / 512 == 2097152usize);
891                    } else {
892                        assert(4096usize / 512 == 8usize);
893                        assert(false);
894                    }
895                };
896                self.split_if_mapped_huge_spec_locality(new_size, m2);
897                let new_self = self.split_if_mapped_huge_spec(new_size);
898                Self::split_if_mapped_huge_spec_preserves_inv(self, new_size);
899                Self::split_if_mapped_huge_spec_preserves_present(self, new_size);
900                Self::split_if_mapped_huge_spec_decreases_page_size(self, new_size);
901                assert(new_self.present() ==> Mapping::disjoint_vaddrs(
902                    m2,
903                    new_self.query_mapping(),
904                )) by {
905                    if new_self.present() {
906                        let new_m = new_self.query_mapping();
907                        let nf = new_self.mappings.filter(
908                            |m3: Mapping| m3.va_range.start <= new_self.cur_va < m3.va_range.end,
909                        );
910                        vstd::set::axiom_set_intersect_finite::<Mapping>(
911                            new_self.mappings,
912                            Set::new(
913                                |m3: Mapping|
914                                    m3.va_range.start <= new_self.cur_va < m3.va_range.end,
915                            ),
916                        );
917                        vstd::set::axiom_set_choose_len(nf);
918                        if self.mappings.contains(new_m) && new_m != m {
919                            assert(false);
920                        }
921                        let new_mappings = Set::<int>::new(
922                            |n: int| 0 <= n < m.page_size as int / new_size as int,
923                        ).map(|n: int| Self::split_index(m, new_size, n as usize));
924                        let k = choose|k: int|
925                            0 <= k < m.page_size as int / new_size as int
926                                && #[trigger] Self::split_index(m, new_size, k as usize) == new_m;
927                        vstd::arithmetic::div_mod::lemma_fundamental_div_mod(
928                            m.page_size as int,
929                            new_size as int,
930                        );
931                        vstd::arithmetic::mul::lemma_mul_inequality(
932                            (k + 1) as int,
933                            m.page_size as int / new_size as int,
934                            new_size as int,
935                        );
936                        vstd::arithmetic::mul::lemma_mul_is_distributive_add_other_way(
937                            new_size as int,
938                            k,
939                            1int,
940                        );
941                        vstd::arithmetic::mul::lemma_mul_nonnegative(k, new_size as int);
942                    }
943                };
944                new_self.split_while_huge_locality_absent(size, m2);
945            }
946        }
947    }
948
949    /// Refinement: every mapping in `split_while_huge(size).mappings` is either
950    /// from `self.mappings` or a sub-mapping of an entry in `self.mappings`.
951    /// Base lemma: every mapping in `split_if_mapped_huge_spec(new_size).mappings`
952    /// is either from the original mappings or a sub-mapping of `query_mapping()`.
953    pub proof fn split_if_mapped_huge_spec_refinement(self, new_size: usize, e: Mapping)
954        requires
955            self.inv(),
956            self.present(),
957            new_size > 0,
958            self.query_mapping().page_size > new_size,
959            self.query_mapping().page_size % new_size == 0,
960            self.split_if_mapped_huge_spec(new_size).mappings.contains(e),
961        ensures
962            self.mappings.contains(e) || {
963                let parent = self.query_mapping();
964                &&& self.mappings.contains(parent)
965                &&& parent.va_range.start <= e.va_range.start
966                &&& e.va_range.end <= parent.va_range.end
967                &&& e.pa_range.start == (parent.pa_range.start + (e.va_range.start
968                    - parent.va_range.start)) as Paddr
969                &&& e.property == parent.property
970            },
971    {
972        let qm = self.query_mapping();
973        let ps = qm.page_size;
974        let ns: int = new_size as int;
975        let count: int = ps as int / ns;
976        let new_self = self.split_if_mapped_huge_spec(new_size);
977        let domain = Set::<int>::new(|n: int| 0 <= n < count);
978        let new_mappings = domain.map(|n: int| Self::split_index(qm, new_size, n as usize));
979
980        // Establish qm ∈ self.mappings.
981        let f = self.mappings.filter(
982            |m2: Mapping| m2.va_range.start <= self.cur_va < m2.va_range.end,
983        );
984        vstd::set::axiom_set_intersect_finite::<Mapping>(
985            self.mappings,
986            Set::new(|m2: Mapping| m2.va_range.start <= self.cur_va < m2.va_range.end),
987        );
988        vstd::set::axiom_set_choose_len(f);
989        assert(self.mappings.contains(qm));
990        assert(qm.inv());
991
992        if self.mappings.remove(qm).contains(e) {
993            assert(self.mappings.contains(e));
994        } else {
995            assert(new_mappings.contains(e));
996            let k = choose|k: int|
997                0 <= k < count && #[trigger] Self::split_index(qm, new_size, k as usize) == e;
998
999            vstd::arithmetic::div_mod::lemma_fundamental_div_mod(ps as int, ns);
1000            assert(ps as int == count * ns);
1001
1002            vstd::arithmetic::mul::lemma_mul_nonnegative(k, ns);
1003            vstd::arithmetic::mul::lemma_mul_inequality(k + 1, count, ns);
1004
1005            assert(k as usize as int == k);
1006            let ku = k as usize;
1007            assert(e == Self::split_index(qm, new_size, ku));
1008            vstd::arithmetic::mul::lemma_mul_is_distributive_add(ns, k, 1int);
1009
1010            assert(e.va_range.start as int == qm.va_range.start as int + k * ns);
1011            assert(e.va_range.start >= qm.va_range.start);
1012            assert(e.va_range.end as int == qm.va_range.start as int + (k + 1) * ns);
1013            assert(e.va_range.end <= qm.va_range.end);
1014            assert(e.pa_range.start as int == qm.pa_range.start as int + k * ns);
1015            assert(e.va_range.start - qm.va_range.start == k * ns);
1016            assert(e.pa_range.start == (qm.pa_range.start + (e.va_range.start
1017                - qm.va_range.start)) as Paddr);
1018            assert(e.property == qm.property);
1019        }
1020    }
1021
1022    pub proof fn split_while_huge_refinement(self, size: usize, m: Mapping)
1023        requires
1024            self.inv(),
1025            size >= PAGE_SIZE,
1026            self.split_while_huge(size).mappings.contains(m),
1027        ensures
1028            self.mappings.contains(m) || exists|parent: Mapping| #[trigger]
1029                self.mappings.contains(parent) && parent.va_range.start <= m.va_range.start
1030                    && m.va_range.end <= parent.va_range.end && m.pa_range.start == (
1031                parent.pa_range.start + (m.va_range.start - parent.va_range.start)) as Paddr
1032                    && m.property == parent.property,
1033        decreases
1034                if self.present() {
1035                    self.query_mapping().page_size as int
1036                } else {
1037                    0
1038                },
1039    {
1040        if self.present() {
1041            let qm = self.query_mapping();
1042            if qm.page_size > size {
1043                let new_size = qm.page_size / NR_ENTRIES;
1044                let new_self = self.split_if_mapped_huge_spec(new_size);
1045
1046                let f = self.mappings.filter(
1047                    |m2: Mapping| m2.va_range.start <= self.cur_va < m2.va_range.end,
1048                );
1049                vstd::set::axiom_set_intersect_finite::<Mapping>(
1050                    self.mappings,
1051                    Set::new(|m2: Mapping| m2.va_range.start <= self.cur_va < m2.va_range.end),
1052                );
1053                vstd::set::axiom_set_choose_len(f);
1054                assert(qm.inv());
1055                assert(NR_ENTRIES == 512usize) by (compute_only);
1056                assert(set![4096usize, 2097152, 1073741824].contains(new_size)) by {
1057                    if qm.page_size == 2097152 {
1058                        assert(2097152usize / 512 == 4096usize);
1059                    } else if qm.page_size == 1073741824 {
1060                        assert(1073741824usize / 512 == 2097152usize);
1061                    } else {
1062                        assert(4096usize / 512 == 8usize);
1063                        assert(false);
1064                    }
1065                };
1066                assert(qm.page_size % new_size == 0) by {
1067                    if qm.page_size == 2097152 {
1068                        assert(2097152usize % 4096 == 0);
1069                    } else {
1070                        assert(1073741824usize % 2097152 == 0);
1071                    }
1072                };
1073                Self::split_if_mapped_huge_spec_preserves_inv(self, new_size);
1074                Self::split_if_mapped_huge_spec_decreases_page_size(self, new_size);
1075
1076                new_self.split_while_huge_refinement(size, m);
1077
1078                if new_self.mappings.contains(m) {
1079                    self.split_if_mapped_huge_spec_refinement(new_size, m);
1080                } else {
1081                    let p = choose|p: Mapping| #[trigger]
1082                        new_self.mappings.contains(p) && p.va_range.start <= m.va_range.start
1083                            && m.va_range.end <= p.va_range.end && m.pa_range.start == (
1084                        p.pa_range.start + (m.va_range.start - p.va_range.start)) as Paddr
1085                            && m.property == p.property;
1086                    self.split_if_mapped_huge_spec_refinement(new_size, p);
1087                    if !self.mappings.contains(p) {
1088                        assert(qm.va_range.start <= m.va_range.start);
1089                        assert(m.va_range.end <= qm.va_range.end);
1090                        // Extract m.inv() and p.inv() via inv preservation of
1091                        // split_while_huge / split_if_mapped_huge_spec.
1092                        new_self.lemma_split_while_huge_preserves_inv(size);
1093                        assert(new_self.split_while_huge(size).inv());
1094                        assert(m.inv());
1095                        assert(new_self.inv());
1096                        assert(p.inv());
1097                        // Mapping::inv gives `start + page_size == end`.
1098                        assert(m.va_range.start + m.page_size == m.va_range.end);
1099                        assert(qm.va_range.start + qm.page_size == qm.va_range.end);
1100                        // m's va offset within qm: at most qm.page_size - m.page_size <= qm.page_size.
1101                        assert(m.va_range.start - qm.va_range.start <= qm.page_size - m.page_size);
1102                        // pa-side chain in int arithmetic.
1103                        assert(qm.pa_range.start + qm.page_size == qm.pa_range.end);
1104                        assert(qm.pa_range.end <= MAX_PADDR);
1105                        assert(p.pa_range.start as int == qm.pa_range.start as int + (
1106                        p.va_range.start - qm.va_range.start));
1107                        assert(m.pa_range.start as int == p.pa_range.start as int + (
1108                        m.va_range.start - p.va_range.start));
1109                        assert(m.pa_range.start as int == qm.pa_range.start as int + (
1110                        m.va_range.start - qm.va_range.start));
1111                        // No-overflow: sum <= qm.pa_range.end <= MAX_PADDR <= usize::MAX.
1112                        assert(qm.pa_range.start as int + (m.va_range.start - qm.va_range.start)
1113                            <= qm.pa_range.end as int);
1114                        assert(m.pa_range.start == (qm.pa_range.start + (m.va_range.start
1115                            - qm.va_range.start)) as Paddr);
1116                        assert(m.property == qm.property);
1117                    }
1118                }
1119            }
1120        }
1121    }
1122
1123    /// `split_while_huge` produces a set disjoint from any set that is
1124    /// pairwise VA-disjoint from `self.mappings`.
1125    ///
1126    /// Set-disjointness of `other` from `self.mappings` is **not** sufficient
1127    /// (counterexample: `other = {split_index(m, k, 0)}` collides with the
1128    /// first sub-mapping after one split step). We need VA-disjointness so
1129    /// that sub-mappings — which lie strictly inside the parent's va_range
1130    /// and hence have distinct va_ranges from anything in `other` — cannot
1131    /// equal any element of `other`.
1132    pub proof fn split_while_huge_disjoint(self, size: usize, other: Set<Mapping>)
1133        requires
1134            self.inv(),
1135            size >= PAGE_SIZE,
1136            forall|m: Mapping, x: Mapping| #[trigger]
1137                self.mappings.contains(m) && #[trigger] other.contains(x)
1138                    ==> Mapping::disjoint_vaddrs(m, x),
1139        ensures
1140            self.split_while_huge(size).mappings.disjoint(other),
1141        decreases
1142                if self.present() {
1143                    self.query_mapping().page_size as int
1144                } else {
1145                    0
1146                },
1147    {
1148        if !self.present() {
1149            // split_while_huge is a no-op; disjointness is by va-disjoint hypothesis.
1150            assert forall|x: Mapping|
1151                #![trigger other.contains(x)]
1152                other.contains(x) implies !self.split_while_huge(size).mappings.contains(x) by {
1153                if self.mappings.contains(x) {
1154                    assert(Mapping::disjoint_vaddrs(x, x));
1155                    assert(x.inv());
1156                }
1157            };
1158            return;
1159        }
1160        let m = self.query_mapping();
1161        if m.page_size <= size {
1162            assert forall|x: Mapping|
1163                #![trigger other.contains(x)]
1164                other.contains(x) implies !self.split_while_huge(size).mappings.contains(x) by {
1165                if self.mappings.contains(x) {
1166                    assert(Mapping::disjoint_vaddrs(x, x));
1167                    assert(x.inv());
1168                }
1169            };
1170            return;
1171        }
1172        let new_size = m.page_size / NR_ENTRIES;
1173
1174        // Standard scaffolding: m ∈ self.mappings, m covers cur_va, m.inv().
1175        let f = self.mappings.filter(
1176            |m2: Mapping| m2.va_range.start <= self.cur_va < m2.va_range.end,
1177        );
1178        vstd::set::axiom_set_intersect_finite::<Mapping>(
1179            self.mappings,
1180            Set::new(|m2: Mapping| m2.va_range.start <= self.cur_va < m2.va_range.end),
1181        );
1182        vstd::set::axiom_set_choose_len(f);
1183        assert(self.mappings.contains(m));
1184        assert(m.inv());
1185
1186        assert(NR_ENTRIES == 512usize) by (compute_only);
1187        assert(set![4096usize, 2097152, 1073741824].contains(new_size)) by {
1188            if m.page_size == 2097152 {
1189                assert(2097152usize / 512 == 4096usize);
1190            } else if m.page_size == 1073741824 {
1191                assert(1073741824usize / 512 == 2097152usize);
1192            } else {
1193                assert(4096usize / 512 == 8usize);
1194                assert(false);
1195            }
1196        };
1197        assert(m.page_size % new_size == 0) by {
1198            if m.page_size == 2097152 {
1199                assert(2097152usize % 4096 == 0);
1200            } else {
1201                assert(1073741824usize % 2097152 == 0);
1202            }
1203        };
1204        Self::split_if_mapped_huge_spec_preserves_inv(self, new_size);
1205        Self::split_if_mapped_huge_spec_decreases_page_size(self, new_size);
1206        let new_self = self.split_if_mapped_huge_spec(new_size);
1207
1208        // Recursive call: establish va-disjoint hypothesis for new_self.
1209        assert forall|m2: Mapping, x: Mapping| #[trigger]
1210            new_self.mappings.contains(m2) && #[trigger] other.contains(
1211                x,
1212            ) implies Mapping::disjoint_vaddrs(m2, x) by {
1213            if self.mappings.contains(m2) {
1214                // Existing mapping preserved by split: va-disjoint by hypothesis.
1215            } else {
1216                // m2 is a sub-mapping of m, with va_range ⊆ m.va_range.
1217                let new_mappings = Set::<int>::new(
1218                    |n: int| 0 <= n < m.page_size as int / new_size as int,
1219                ).map(|n: int| Self::split_index(m, new_size, n as usize));
1220                assert(new_mappings.contains(m2));
1221                let k = choose|k: int|
1222                    0 <= k < m.page_size as int / new_size as int && #[trigger] Self::split_index(
1223                        m,
1224                        new_size,
1225                        k as usize,
1226                    ) == m2;
1227                vstd::arithmetic::div_mod::lemma_fundamental_div_mod(
1228                    m.page_size as int,
1229                    new_size as int,
1230                );
1231                vstd::arithmetic::mul::lemma_mul_inequality(
1232                    (k + 1) as int,
1233                    m.page_size as int / new_size as int,
1234                    new_size as int,
1235                );
1236                vstd::arithmetic::mul::lemma_mul_is_distributive_add_other_way(
1237                    new_size as int,
1238                    k,
1239                    1int,
1240                );
1241                vstd::arithmetic::mul::lemma_mul_nonnegative(k, new_size as int);
1242                // m2.va_range ⊆ m.va_range; m.va_range va-disjoint from x.va_range.
1243                assert(Mapping::disjoint_vaddrs(m, x));
1244            }
1245        };
1246
1247        new_self.split_while_huge_disjoint(size, other);
1248    }
1249}
1250
1251impl<'rcu, C: PageTableConfig> CursorOwner<'rcu, C> {
1252    /// When the current entry is a PT node at level `self.level`, any mapping at `cur_va` has
1253    /// `page_size <= page_size(self.level - 1)`.  Therefore `split_while_huge` at
1254    /// `page_size(self.level - 1)` does not split anything and is a no-op on the abstract view.
1255    /// When present, the query_mapping is from the current subtree's view_rec.
1256    proof fn query_mapping_from_subtree(self, qm: Mapping)
1257        requires
1258            self.inv(),
1259            self.in_locked_range(),
1260            self@.inv(),
1261            self@.present(),
1262            qm == self@.query_mapping(),
1263        ensures
1264            PageTableOwner(self.cur_subtree()).view_rec(self.cur_subtree().value.path).contains(qm),
1265    {
1266        let f = self@.mappings.filter(
1267            |m2: Mapping| m2.va_range.start <= self@.cur_va < m2.va_range.end,
1268        );
1269        axiom_set_intersect_finite::<Mapping>(
1270            self@.mappings,
1271            Set::new(|m2: Mapping| m2.va_range.start <= self@.cur_va < m2.va_range.end),
1272        );
1273        axiom_set_choose_len(f);
1274        self.mapping_covering_cur_va_from_cur_subtree(qm);
1275    }
1276
1277    pub proof fn split_while_huge_node_noop(self)
1278        requires
1279            self.inv(),
1280            self.in_locked_range(),
1281            self.cur_entry_owner().is_node(),
1282            self.level > 1,
1283        ensures
1284            self@.split_while_huge(page_size((self.level - 1) as PagingLevel)) == self@,
1285    {
1286        self.view_preserves_inv();
1287        if self@.present() {
1288            self.cur_subtree_inv();
1289            let subtree = self.cur_subtree();
1290            let path = subtree.value.path;
1291            let qm = self@.query_mapping();
1292            self.query_mapping_from_subtree(qm);
1293            let cont = self.continuations[self.level - 1];
1294            cont.path().push_tail_property_len(cont.idx as usize);
1295            PageTableOwner(subtree).view_rec_node_page_size_bound(path, qm);
1296        }
1297    }
1298
1299    /// When the current entry is absent, there is no mapping at `cur_va` in the abstract view,
1300    /// so `split_while_huge` finds nothing to split and is a no-op for any target size.
1301    pub proof fn split_while_huge_absent_noop(self, size: usize)
1302        requires
1303            self.inv(),
1304            self.in_locked_range(),
1305            self.cur_entry_owner().is_absent(),
1306        ensures
1307            self@.split_while_huge(size) == self@,
1308    {
1309        self.view_preserves_inv();
1310        self.cur_entry_absent_not_present();
1311    }
1312
1313    pub proof fn split_while_huge_at_level_noop(self)
1314        requires
1315            self.inv(),
1316            self.in_locked_range(),
1317        ensures
1318            self@.split_while_huge(page_size(self.level as PagingLevel)) == self@,
1319    {
1320        self.view_preserves_inv();
1321        if self@.present() {
1322            self.cur_subtree_inv();
1323            let subtree = self.cur_subtree();
1324            let path = subtree.value.path;
1325            let qm = self@.query_mapping();
1326            self.query_mapping_from_subtree(qm);
1327            let cont = self.continuations[self.level - 1];
1328            cont.path().push_tail_property_len(cont.idx as usize);
1329            PageTableOwner(subtree).view_rec_page_size_bound(path, qm);
1330        }
1331    }
1332
1333    /// After `map_branch_none` splits a huge frame at level `level_before_frame` and descends,
1334    /// the cursor view equals `owner0@.split_while_huge(page_size(level_before_frame - 1))`.
1335    ///
1336    /// Chain:
1337    ///   owner@ = owner_before_frame@.split_if_mapped_huge_spec(page_size(level_before_frame - 1))
1338    ///          = owner0@.split_while_huge(page_size(level_before_frame)).split_if_mapped_huge_spec(...)
1339    ///          = owner0@.split_while_huge(page_size(level_before_frame - 1))
1340    /// The last equality uses the fact that split_while_huge(L) on a frame of size page_size(L)
1341    /// takes exactly one split step to page_size(L-1), matching split_if_mapped_huge_spec.
1342    pub proof fn map_branch_frame_split_while_huge(
1343        self,
1344        owner0: Self,
1345        owner_before_frame: Self,
1346        level_before_frame: int,
1347    )
1348        requires
1349            self.inv(),
1350            owner0.inv(),
1351            owner_before_frame.inv(),
1352            1 <= level_before_frame - 1,
1353            level_before_frame <= NR_LEVELS,
1354            self.level == (level_before_frame - 1) as u8,
1355            owner_before_frame@ == owner0@.split_while_huge(
1356                page_size(level_before_frame as PagingLevel),
1357            ),
1358            self@ == owner_before_frame@.split_if_mapped_huge_spec(
1359                page_size((level_before_frame - 1) as PagingLevel),
1360            ),
1361            // The mapping at cur_va in owner_before_frame is exactly the
1362            // frame at the level being split: present, with page_size equal
1363            // to page_size(level_before_frame). Both follow from being in
1364            // the ChildRef::Frame branch at level `level_before_frame`.
1365            owner_before_frame@.present(),
1366            owner_before_frame@.query_mapping().page_size == page_size(
1367                level_before_frame as PagingLevel,
1368            ),
1369    {
1370        owner0.view_preserves_inv();
1371        owner_before_frame.view_preserves_inv();
1372        let s_top = page_size(level_before_frame as PagingLevel);
1373        let s_low = page_size((level_before_frame - 1) as PagingLevel);
1374
1375        // page_size(L) >= PAGE_SIZE; page_size(L) > page_size(L-1);
1376        // page_size(L) / NR_ENTRIES == page_size(L-1); page_size(L) % page_size(L-1) == 0;
1377        // page_size(L-1) ∈ {4K, 2M, 1G}.
1378        crate::specs::mm::page_table::cursor::page_size_lemmas::lemma_page_size_spec_values();
1379        crate::specs::mm::page_table::cursor::page_size_lemmas::lemma_page_size_ge_page_size(
1380            level_before_frame as PagingLevel,
1381        );
1382        assert(NR_ENTRIES == 512usize) by (compute_only);
1383
1384        // Compose: owner0.split_while_huge(s_low)
1385        //         == owner0.split_while_huge(s_top).split_while_huge(s_low)
1386        //         == owner_before_frame.split_while_huge(s_low)
1387        owner0@.split_while_huge_compose(s_top, s_low);
1388
1389        // One step: owner_before_frame.split_while_huge(s_low)
1390        //         == owner_before_frame.split_if_mapped_huge_spec(s_low)
1391        owner_before_frame@.split_while_huge_one_step(s_low);
1392    }
1393
1394    /// After split_if_mapped_huge + push_level, the mappings equal
1395    /// `old_view.split_while_huge(page_size(current_level))`.
1396    pub proof fn find_next_split_push_equals_split_while_huge(self, old_view: CursorView<C>)
1397        requires
1398            self.inv(),
1399            old_view.inv(),
1400            self.cur_entry_owner().is_frame(),
1401            self@.cur_va == old_view.cur_va,
1402            old_view.present(),
1403            old_view.query_mapping().page_size > page_size(self.level as PagingLevel),
1404            old_view.query_mapping().page_size / NR_ENTRIES == page_size(self.level as PagingLevel),
1405            old_view.query_mapping().page_size % page_size(self.level as PagingLevel) == 0,
1406            self@.mappings =~= old_view.split_if_mapped_huge_spec(
1407                page_size(self.level as PagingLevel),
1408            ).mappings,
1409        ensures
1410            self@.mappings =~= old_view.split_while_huge(
1411                page_size(self.level as PagingLevel),
1412            ).mappings,
1413    {
1414        let ps = page_size(self.level as PagingLevel);
1415        let m = old_view.query_mapping();
1416        let f = old_view.mappings.filter(
1417            |m2: Mapping| m2.va_range.start <= old_view.cur_va < m2.va_range.end,
1418        );
1419        vstd::set::axiom_set_intersect_finite::<Mapping>(
1420            old_view.mappings,
1421            Set::new(|m2: Mapping| m2.va_range.start <= old_view.cur_va < m2.va_range.end),
1422        );
1423        vstd::set::axiom_set_choose_len(f);
1424        assert(m.inv());
1425        assert(NR_ENTRIES == 512usize) by (compute_only);
1426        crate::specs::mm::page_table::cursor::page_size_lemmas::lemma_page_size_spec_values();
1427        assert(set![4096usize, 2097152, 1073741824].contains(ps)) by {
1428            if m.page_size == 2097152 {
1429                assert(2097152usize / 512 == 4096usize);
1430            } else {
1431                assert(1073741824usize / 512 == 2097152usize);
1432            }
1433        };
1434        old_view.split_while_huge_one_step(ps);
1435    }
1436
1437    /// `split_while_huge` gives the same mappings for two `cur_va` values
1438    /// when no mapping starts between them and the `!present` case is a no-op.
1439    ///
1440    /// The `v1.cur_va < v2.cur_va ==> !v1.present()` precondition rules out
1441    /// the genuinely hard case where v1's query mapping spans v2.cur_va but
1442    /// gets split inconsistently — at the call site this is supplied by
1443    /// `find_next_impl`'s ensures (`final.va > old.va ==> !old(owner)@.present()`).
1444    pub proof fn split_while_huge_cur_va_independent(
1445        v1: CursorView<C>,
1446        v2: CursorView<C>,
1447        size: usize,
1448    )
1449        requires
1450            v1.inv(),
1451            v2.inv(),
1452            v1.mappings =~= v2.mappings,
1453            v1.cur_va <= v2.cur_va,
1454            // No mapping starts in [v1.cur_va, v2.cur_va).
1455            v1.mappings.filter(
1456                |m: Mapping| v1.cur_va <= m.va_range.start && m.va_range.start < v2.cur_va,
1457            ) =~= Set::<Mapping>::empty(),
1458            // When v1 has no mapping at cur_va, any mapping at v2.cur_va is
1459            // already small enough that split_while_huge is a no-op on it too.
1460            // (At the call site this follows from: split_while_huge(v1) was a
1461            // no-op, so find_next found the mapping without splitting, meaning
1462            // its page_size <= size.)
1463            !v1.present() && v2.present() ==> v2.query_mapping().page_size <= size,
1464            // When the cursor advances strictly forward, the original cur_va
1465            // had no mapping. Supplied by `find_next_impl`'s ensures.
1466            v1.cur_va < v2.cur_va ==> !v1.present(),
1467        ensures
1468            v1.split_while_huge(size).mappings =~= v2.split_while_huge(size).mappings,
1469    {
1470        if v1.cur_va == v2.cur_va {
1471            assert(v1 =~= v2);
1472        }
1473    }
1474}
1475
1476} // verus!