1use vstd::{
2 arithmetic::{
3 div_mod::{
4 lemma_div_denominator, lemma_fundamental_div_mod, lemma_mod_adds,
5 lemma_mod_equivalence, lemma_mod_multiples_basic,
6 },
7 mul::{lemma_mul_inequality, lemma_mul_is_associative, lemma_mul_is_commutative},
8 power2::{lemma2_to64, lemma2_to64_rest, pow2},
9 },
10 bits::lemma_usize_shr_is_div,
11 prelude::*,
12 seq::*,
13 seq_lib::*,
14 set::lemma_set_choose_len,
15 set_lib::*,
16};
17use vstd_extra::{
18 arithmetic::nat_align_down, drop_tracking::*, ghost_tree::*, ownership::*,
19 prelude::TreeNodeValue,
20};
21
22use crate::arch::mm::lemma_nr_subpage_per_huge_eq_nr_entries;
23
24use crate::specs::{
25 arch::*,
26 mm::{
27 frame::{mapping::frame_to_index, meta_region_owners::MetaRegionOwners},
28 page_table::{
29 cursor::page_size_lemmas::lemma_page_size_spec_values,
30 vaddr_range_proofs::lemma_leading_bits_properties, *,
31 },
32 },
33};
34
35use crate::arch::mm::PagingConsts;
36use crate::mm::{
37 Paddr, PagingConstsTrait, PagingLevel, Vaddr,
38 frame::meta::{REF_COUNT_MAX, REF_COUNT_UNIQUE, REF_COUNT_UNUSED},
39 page_size, page_size_spec,
40 page_table::{EntryOwner, EntryOwnerKind, PageTableEntryTrait, PageTableGuard},
41};
42use core::ops::{Deref, Range};
43
44verus! {
45
46broadcast use group_ghost_tree_lemmas;
47
48#[verifier::inline]
49pub open spec fn vaddr_make<const L: usize>(idx: int, offset: usize) -> usize
50 recommends
51 0 < L,
52 idx < L,
53 offset < NR_ENTRIES,
54{
55 (page_size_spec::<PagingConsts>((L - idx) as PagingLevel) * offset) as usize
56}
57
58pub open spec fn rec_vaddr(path: TreePath<NR_ENTRIES>, idx: int) -> usize
59 decreases path.len() - idx,
60 when 0 <= idx <= path.len()
61{
62 if idx == path.len() {
63 0
64 } else {
65 let offset = path[idx] as usize;
66 (vaddr_make::<NR_LEVELS>(idx, offset) + rec_vaddr(path, idx + 1)) as usize
67 }
68}
69
70pub open spec fn vaddr(path: TreePath<NR_ENTRIES>) -> usize {
71 rec_vaddr(path, 0)
72}
73
74pub open spec fn vaddr_of<C: PageTableConfig>(path: TreePath<NR_ENTRIES>) -> usize {
76 (vaddr(path) + page_table_coverage_start_spec::<C>()) as usize
77}
78
79#[verifier::spinoff_prover]
83pub proof fn lemma_vaddr_strict_bound(path: TreePath<NR_ENTRIES>)
84 requires
85 path.inv(),
86 path.len() <= INC_LEVELS - 1,
87 ensures
88 vaddr(path) < 0x1_0000_0000_0000int,
89{
90 lemma_rec_vaddr_suffix_bound(path, 0);
91 lemma_page_size_ge_base::<PagingConsts>((INC_LEVELS - path.len()) as PagingLevel);
92 lemma_page_size_spec_values();
93}
94
95pub proof fn lemma_vaddr_top_index_cell(path: TreePath<NR_ENTRIES>)
97 requires
98 path.inv(),
99 1 <= path.len() <= INC_LEVELS - 1,
100 ensures
101 (path[0]) * page_size::<PagingConsts>(NR_LEVELS as PagingLevel) <= vaddr(path),
102 vaddr(path) + page_size::<PagingConsts>((INC_LEVELS - path.len()) as PagingLevel) <= (
103 path[0] + 1) * page_size::<PagingConsts>(NR_LEVELS as PagingLevel),
104{
105 broadcast use TreePath::lemma_index_satisfies_elem_inv;
106
107 lemma_rec_vaddr_suffix_bound(path, 1);
108 lemma_rec_vaddr_unfold(path, 0);
109 lemma_page_size_next::<PagingConsts>(NR_LEVELS as PagingLevel);
110 lemma_nr_subpage_per_huge_eq_nr_entries();
111 lemma_page_size_spec_values();
112 let i0 = path[0];
113 lemma_vaddr_make_eq_page_size(0, i0);
114}
115
116pub proof fn lemma_vaddr_of_eq_int<C: PageTableConfig>(path: TreePath<NR_ENTRIES>)
120 requires
121 path.inv(),
122 path.len() <= INC_LEVELS - 1,
123 ensures
124 vaddr_of::<C>(path) == vaddr(path) + page_table_coverage_start_spec::<C>(),
125{
126 C::lemma_page_table_config_constant_properties();
127 lemma_leading_bits_properties::<C>();
128 C::lemma_paging_consts_properties();
129 C::axiom_current_paging_consts_hardcoded();
130 lemma_vaddr_strict_bound(path);
131 lemma_arch_specific_consts_properties::<C>();
132 vstd::arithmetic::power2::lemma2_to64();
133 assert(nr_page_table_coverage_bits_spec::<C>() == 48);
134 assert(leading_bits_spec::<C>() < 65536);
135 let body_level = (C::NR_LEVELS() + 1) as PagingLevel;
136 lemma_page_size_is_pow2_pte_index_bit_offset::<C>(body_level);
137 lemma_page_size_spec_values();
138}
139
140pub proof fn lemma_vaddr_path_aligned<C: PageTableConfig>(path: TreePath<NR_ENTRIES>, va: Vaddr)
142 requires
143 path.inv(),
144 path.len() <= C::NR_LEVELS(),
145 forall|k: int|
146 0 <= k < path.len() ==> #[trigger] path[k] == pte_index_spec::<C>(
147 va,
148 (C::NR_LEVELS() - k) as PagingLevel,
149 ),
150 ensures
151 vaddr(path) + vaddr_leading_part_spec::<C>(va) == nat_align_down(
152 va as nat,
153 page_size::<C>((C::NR_LEVELS() - path.len() + 1) as PagingLevel) as nat,
154 ),
155 decreases path.len(),
156{
157 C::lemma_paging_consts_properties();
158 C::axiom_current_paging_consts_hardcoded();
159 let level = (C::NR_LEVELS() - path.len() + 1) as PagingLevel;
160 lemma_page_size_is_pow2_pte_index_bit_offset::<C>(level);
161 let size = page_size::<C>(level) as int;
162 lemma_fundamental_div_mod(va as int, size);
163 vstd::arithmetic::div_mod::lemma_mod_bound(va as int, size);
164 vstd::arithmetic::div_mod::lemma_div_pos_is_pos(va as int, size);
165 if path.len() == 0 {
166 lemma_usize_shr_is_div(va, nr_page_table_coverage_bits_spec::<C>());
167 lemma_mul_is_commutative(size, va as int / size);
168 } else {
169 let (index, parent) = path.pop_tail();
170 path.lemma_pop_tail_preserves_inv();
171 path.lemma_index_satisfies_elem_inv(path.len() - 1);
172 assert(0 <= index < NR_ENTRIES);
173 assert(parent.push_tail(index) == path);
174 assert forall|k: int| 0 <= k < parent.len() implies #[trigger] parent[k]
175 == pte_index_spec::<C>(va, (C::NR_LEVELS() - k) as PagingLevel) by {
176 assert(parent[k] == path[k]);
177 };
178 lemma_vaddr_path_aligned::<C>(parent, va);
179 PageTableOwner::<C>::lemma_vaddr_push_tail_eq(parent, index);
180 lemma_page_size_next::<C>(level);
181 lemma_pte_index_spec_is_div_mod::<C>(va, level);
182
183 let fanout = nr_subpage_per_huge::<C>() as int;
184 let quotient = va as int / size;
185 let parent_size = page_size::<C>((level + 1) as PagingLevel) as int;
186 lemma_div_denominator(va as int, size, fanout);
187 lemma_fundamental_div_mod(quotient, fanout);
188 lemma_fundamental_div_mod(va as int, parent_size);
189 vstd::arithmetic::div_mod::lemma_mod_bound(va as int, parent_size);
190 vstd::arithmetic::div_mod::lemma_div_pos_is_pos(va as int, parent_size);
191 assert(vaddr(parent) + vaddr_leading_part_spec::<C>(va) == nat_align_down(
192 va as nat,
193 parent_size as nat,
194 ));
195 assert(vaddr(path) == vaddr(parent) + index * size);
196 assert(index == pte_index_spec::<C>(va, level));
197 assert(quotient == fanout * (va as int / parent_size) + index);
198 assert(size * quotient == parent_size * (va as int / parent_size) + size * index)
199 by (nonlinear_arith)
200 requires
201 quotient == fanout * (va as int / parent_size) + index,
202 parent_size == size * fanout,
203 ;
204 }
205}
206
207pub proof fn sibling_paths_disjoint<C: PageTableConfig>(
214 prefix: TreePath<NR_ENTRIES>,
215 j: int,
216 k: int,
217 size: usize,
218)
219 requires
220 prefix.inv(),
221 prefix.len() < INC_LEVELS - 1,
222 0 <= j,
223 0 <= k,
224 j < NR_ENTRIES,
225 k < NR_ENTRIES,
226 j != k,
227 size == page_size::<PagingConsts>((INC_LEVELS - prefix.len() - 1) as PagingLevel),
228 ensures
229 vaddr(prefix.push_tail(j)) + size <= vaddr(prefix.push_tail(k)) || vaddr(
230 prefix.push_tail(k),
231 ) + size <= vaddr(prefix.push_tail(j)),
232{
233 PageTableOwner::<C>::lemma_vaddr_push_tail_eq(prefix, j);
234 PageTableOwner::<C>::lemma_vaddr_push_tail_eq(prefix, k);
235 let s = size as int;
236 let vp = vaddr(prefix) as int;
237 let vj = vaddr(prefix.push_tail(j)) as int;
238 let vk = vaddr(prefix.push_tail(k)) as int;
239 if j < k {
240 assert(vj + s <= vk) by (nonlinear_arith)
241 requires
242 vj == vp + j * s,
243 vk == vp + k * s,
244 j < k,
245 s >= 0,
246 ;
247 } else {
248 assert(vk + s <= vj) by (nonlinear_arith)
249 requires
250 vj == vp + j * s,
251 vk == vp + k * s,
252 k < j,
253 s >= 0,
254 ;
255 }
256}
257
258impl<C: PageTableConfig, const L: usize> TreeNodeValue<L> for EntryOwner<C> {
259 open spec fn default(lv: nat) -> Self {
260 Self {
261 kind: EntryOwnerKind::Absent,
262 path: TreePath::new(Seq::empty()),
263 parent_level: (INC_LEVELS - lv) as PagingLevel,
264 }
265 }
266
267 proof fn lemma_default_preserves_inv() {
268 }
269
270 open spec fn la_inv(self, lv: nat) -> bool {
271 self.is_node() ==> lv < L - 1
272 }
273
274 proof fn lemma_default_preserves_la_inv() {
275 }
276
277 open spec fn rel_children(self, i: int, child: Option<Self>) -> bool {
280 true
281 }
282
283 proof fn lemma_default_preserves_rel_children(self, lv: nat) {
284 }
285}
286
287pub const INC_LEVELS: usize = NR_LEVELS + 1;
288
289pub type OwnerSubtree<C> = TreeNode<EntryOwner<C>, NR_ENTRIES, INC_LEVELS>;
299
300pub open spec fn allocated_empty_node_owner<C: PageTableConfig>(
309 owner: OwnerSubtree<C>,
310 level: PagingLevel,
311) -> bool {
312 &&& owner.inv()
313 &&& owner.value().is_node()
314 &&& owner.value().path == TreePath::<NR_ENTRIES>::new(Seq::empty())
315 &&& owner.value().parent_level == (level + 1) as PagingLevel
316 &&& owner.value().node().level()
317 == level
318 &&& owner.level() == (INC_LEVELS - level - 1) as nat
323 &&& owner.value().node().inv()
324 &&& !owner.value().node().children_perm.value().all(|child: C::E| child.is_present())
325 &&& forall|i: int|
326 0 <= i < NR_ENTRIES ==> {
327 &&& #[trigger] owner.has_child(i)
328 &&& owner.child(i).value().is_absent()
329 &&& owner.child(i).value().inv()
330 &&& owner.child(i).value().path == owner.value().path.push_tail(i)
331 }
332 &&& forall|i: int|
333 #![auto]
334 0 <= i < NR_ENTRIES ==> owner.child(i).value().match_pte(
335 owner.value().node().children_perm.value()[i],
336 owner.child(i).value().parent_level,
337 )
338 &&& forall|i: int|
339 #![auto]
340 0 <= i < NR_ENTRIES ==> owner.child(i).value().parent_level
341 == owner.value().node().level()
342 &&& forall|j: int|
347 0 <= j < NR_ENTRIES ==> #[trigger] owner.value().node().children_perm.value()[j]
348 == C::E::new_absent_spec()
349}
350
351pub open spec fn allocated_empty_node_grandchildren_none<C: PageTableConfig>(
359 owner: OwnerSubtree<C>,
360) -> bool {
361 forall|i: int, j: int|
362 0 <= i < NR_ENTRIES && 0 <= j < NR_ENTRIES ==> !#[trigger] owner.child(i).has_child(j)
363}
364
365proof fn tracked_rebase_freshly_allocated_children_at<C: PageTableConfig>(
366 tracked owner: &mut OwnerSubtree<C>,
367 new_path: TreePath<NR_ENTRIES>,
368 i: usize,
369)
370 requires
371 i <= NR_ENTRIES,
372 old(owner).inv(),
373 old(owner).children().len() == NR_ENTRIES,
374 new_path.inv(),
375 forall|j: int| i <= j < NR_ENTRIES ==> (#[trigger] old(owner).children()[j]) is Some,
376 ensures
377 final(owner).inv(),
378 final(owner).value() == old(owner).value(),
379 final(owner).level() == old(owner).level(),
380 final(owner).children().len() == NR_ENTRIES,
381 forall|j: int|
382 0 <= j < i ==> (#[trigger] final(owner).children()[j]) == old(owner).children()[j],
383 forall|j: int|
384 i <= j < NR_ENTRIES ==> {
385 let c_old = old(owner).child(j);
386 let c_new = (#[trigger] final(owner).children()[j])->0;
387 &&& final(owner).has_child(j)
388 &&& c_new.value() == EntryOwner { path: new_path.push_tail(j), ..c_old.value() }
389 &&& c_new.level() == c_old.level()
390 &&& c_new.children() == c_old.children()
391 &&& c_new.value().is_absent() == c_old.value().is_absent()
392 },
393 decreases NR_ENTRIES - i,
394{
395 if i < NR_ENTRIES {
396 {
397 let tracked child = owner.tracked_borrow_mut_child(i as int);
398 let before = *child;
399 assert(before.inv());
400 {
401 let tracked child_value = child.tracked_borrow_mut_value();
402 child_value.path = new_path.push_tail(i as int);
403 }
404 assert(child.value().inv());
405 assert(child.inv_node());
406 assert(child.inv_children());
407 assert forall|j: int|
408 0 <= j < NR_ENTRIES && (#[trigger] child.children()[j]) is Some implies child.child(
409 j,
410 ).inv() by {
411 assert(before.child(j).inv());
412 };
413 assert(child.inv());
414 }
415 assert(owner.inv());
416 tracked_rebase_freshly_allocated_children_at(owner, new_path, (i + 1) as usize);
417 }
418}
419
420pub proof fn tracked_rebase_freshly_allocated_children<C: PageTableConfig>(
430 tracked owner: &mut OwnerSubtree<C>,
431 new_path: TreePath<NR_ENTRIES>,
432)
433 requires
434 old(owner).inv(),
435 old(owner).children().len() == NR_ENTRIES,
436 new_path.inv(),
437 forall|i: int| 0 <= i < NR_ENTRIES ==> (#[trigger] old(owner).children()[i]) is Some,
438 ensures
439 final(owner).inv(),
440 final(owner).value() == old(owner).value(),
441 final(owner).level() == old(owner).level(),
442 final(owner).children().len() == NR_ENTRIES,
443 forall|i: int|
444 0 <= i < NR_ENTRIES ==> {
445 let c_old = old(owner).child(i);
446 let c_new = (#[trigger] final(owner).children()[i])->0;
447 &&& final(owner).has_child(i)
448 &&& c_new.value() == EntryOwner { path: new_path.push_tail(i), ..c_old.value() }
449 &&& c_new.level() == c_old.level()
450 &&& c_new.children() == c_old.children()
451 &&& c_new.value().is_absent() == c_old.value().is_absent()
452 },
453 allocated_empty_node_grandchildren_none(*old(owner))
454 ==> allocated_empty_node_grandchildren_none(*final(owner)),
455{
456 tracked_rebase_freshly_allocated_children_at(owner, new_path, 0);
457 if allocated_empty_node_grandchildren_none(*old(owner)) {
458 assert forall|i: int, j: int|
459 0 <= i < NR_ENTRIES && 0 <= j < NR_ENTRIES implies !#[trigger] owner.child(i).has_child(
460 j,
461 ) by {
462 assert(!old(owner).child(i).has_child(j));
463 assert(owner.child(i).children() == old(owner).child(i).children());
464 };
465 }
466}
467
468pub proof fn lemma_fresh_node_subtree_satisfies<C: PageTableConfig>(
477 node: OwnerSubtree<C>,
478 path: TreePath<NR_ENTRIES>,
479 f: spec_fn(EntryOwner<C>, TreePath<NR_ENTRIES>) -> bool,
480)
481 requires
482 node.inv(),
483 node.level() < INC_LEVELS - 1,
484 f(node.value(), path),
485 forall|i: int| 0 <= i < NR_ENTRIES ==> #[trigger] node.has_child(i),
486 forall|i: int, j: int|
487 0 <= i < NR_ENTRIES && 0 <= j < NR_ENTRIES ==> !#[trigger] node.child(i).has_child(j),
488 forall|i: int|
489 0 <= i < NR_ENTRIES ==> #[trigger] f(node.child(i).value(), path.push_tail(i)),
490 ensures
491 node.subtree_satisfies(path, f),
492{
493 assert forall|i: int|
494 0 <= i < node.children().len() && #[trigger] node.has_child(i) implies node.child(
495 i,
496 ).subtree_satisfies(path.push_tail(i), f) by {
497 assert(node.child(i).inv());
498 };
499}
500
501pub tracked struct PageTableOwner<C: PageTableConfig>(pub OwnerSubtree<C>);
508
509impl<C: PageTableConfig> PageTableOwner<C> {
510 #[verifier::opaque]
512 pub open spec fn query_leaf(self, va: Vaddr) -> Option<EntryOwner<C>>
513 decreases INC_LEVELS - self.0.level(),
514 when self.0.inv()
515 {
516 if self.0.value().is_frame() {
517 Some(self.0.value())
518 } else if self.0.value().is_node() && self.0.level() < INC_LEVELS - 1 {
519 let idx = pte_index_spec::<C>(va, self.0.value().node().level());
520 if 0 <= idx < NR_ENTRIES && self.0.has_child(idx as int) {
521 PageTableOwner(self.0.child(idx as int)).query_leaf(va)
522 } else {
523 None
524 }
525 } else {
526 None
527 }
528 }
529
530 pub open spec fn pt_edge_at(parent: OwnerSubtree<C>, i: int) -> bool {
532 &&& parent.has_child(i)
533 &&& parent.child(i).value().path.len() == parent.value().node().tree_level
534 + 1
535 &&& (parent.child(i).value().match_pte(
543 parent.value().node().children_perm.value()[i],
544 parent.value().node().level(),
545 ) || (parent.value().node().level() == NR_LEVELS && leading_bits_spec::<C>() == 0
546 && parent.child(i).value().borrowed_match_pte(
547 parent.value().node().children_perm.value()[i],
548 parent.value().node().level(),
549 )))
550 &&& parent.child(i).value().path == parent.value().path.push_tail(i)
551 &&& parent.child(i).value().parent_level == parent.value().node().level()
552 }
553
554 #[verifier::opaque]
558 pub open spec fn pt_inv_at_depth(self, depth: nat) -> bool
559 decreases depth,
560 {
561 if depth == 0 {
562 true
563 } else if self.0.value().is_node() {
564 forall|i: int|
565 #![trigger self.0.has_child(i)]
566 0 <= i < NR_ENTRIES ==> Self::pt_edge_at(self.0, i) && PageTableOwner(
567 self.0.child(i),
568 ).pt_inv_at_depth((depth - 1) as nat)
569 } else {
570 forall|i: int|
571 #![trigger self.0.has_child(i)]
572 0 <= i < NR_ENTRIES ==> !self.0.has_child(i)
573 }
574 }
575
576 pub open spec fn pt_inv(self) -> bool {
581 &&& self.0.inv()
582 &&& self.pt_inv_at_depth((INC_LEVELS - self.0.level()) as nat)
583 }
584
585 pub proof fn pt_inv_unroll(self, i: int)
586 requires
587 self.pt_inv(),
588 self.0.value().is_node(),
589 0 <= i < NR_ENTRIES,
590 ensures
591 Self::pt_edge_at(self.0, i),
592 PageTableOwner(self.0.child(i)).pt_inv(),
593 {
594 reveal(PageTableOwner::pt_inv_at_depth);
597 let depth = (INC_LEVELS - self.0.level()) as nat;
598 assert(<EntryOwner<C> as TreeNodeValue<INC_LEVELS>>::la_inv(
599 self.0.value(),
600 self.0.level(),
601 ));
602 }
603
604 pub proof fn pt_inv_non_node(self, i: int)
605 requires
606 self.pt_inv(),
607 !self.0.value().is_node(),
608 0 <= i < NR_ENTRIES,
609 ensures
610 !self.0.has_child(i),
611 {
612 reveal(PageTableOwner::pt_inv_at_depth);
613 }
614
615 pub proof fn lemma_non_node_pt_inv_at_depth(self, depth: nat)
618 requires
619 !self.0.value().is_node(),
620 forall|j: int| 0 <= j < NR_ENTRIES ==> !#[trigger] self.0.has_child(j),
621 ensures
622 self.pt_inv_at_depth(depth),
623 decreases depth,
624 {
625 reveal(PageTableOwner::pt_inv_at_depth);
626 }
627
628 #[verifier::spinoff_prover]
630 pub proof fn lemma_fresh_node_pt_inv(owner: OwnerSubtree<C>)
631 requires
632 owner.inv(),
633 owner.value().is_node(),
634 owner.children().len() == NR_ENTRIES,
635 forall|i: int|
636 0 <= i < NR_ENTRIES ==> {
637 &&& #[trigger] owner.has_child(i)
638 &&& !owner.child(i).value().is_node()
639 &&& owner.child(i).value().path.len() == owner.value().node().tree_level + 1
640 &&& owner.child(i).value().match_pte(
641 owner.value().node().children_perm.value()[i],
642 owner.value().node().level(),
643 )
644 &&& owner.child(i).value().path == owner.value().path.push_tail(i)
645 &&& owner.child(i).value().parent_level == owner.value().node().level()
646 },
647 allocated_empty_node_grandchildren_none(owner),
648 ensures
649 PageTableOwner(owner).pt_inv(),
650 {
651 reveal(PageTableOwner::pt_inv_at_depth);
652 let depth = (INC_LEVELS - owner.level()) as nat;
653 assert forall|i: int| 0 <= i < NR_ENTRIES implies #[trigger] owner.has_child(i)
657 && Self::pt_edge_at(owner, i) && PageTableOwner(owner.child(i)).pt_inv_at_depth(
658 (depth - 1) as nat,
659 ) by {
660 let child = owner.child(i);
661 assert(owner.has_child(i));
662 PageTableOwner(child).lemma_non_node_pt_inv_at_depth((depth - 1) as nat);
663 };
664 }
665
666 pub proof fn lemma_split_node_view(self, old: Self)
668 requires
669 self.0.inv(),
670 old.0.inv(),
671 self.0.value().is_node(),
672 old.0.value().is_frame(),
673 self.0.value().path == old.0.value().path,
674 old.0.value().path.len() == old.0.level(),
675 old.0.level() + old.0.value().parent_level == INC_LEVELS,
676 1 < old.0.value().parent_level < NR_LEVELS,
677 forall|i: int|
678 0 <= i < NR_ENTRIES ==> {
679 let child = self.0.child(i).value();
680 &&& #[trigger] self.0.has_child(i)
681 &&& child.is_frame()
682 &&& child.path == old.0.value().path.push_tail(i)
683 &&& child.parent_level == old.0.value().parent_level - 1
684 &&& child.frame().prop == old.0.value().frame().prop
685 &&& child.frame().mapped_pa == old.0.value().frame().mapped_pa + i
686 * page_size::<PagingConsts>((old.0.value().parent_level - 1) as PagingLevel)
687 },
688 ensures
689 self.view_rec(self.0.value().path) == Set::<int>::range(0, NR_ENTRIES as int).map(
690 |i: int|
691 CursorView::<C>::split_index(
692 old.view_rec(old.0.value().path).choose(),
693 page_size::<PagingConsts>((old.0.value().parent_level - 1) as PagingLevel),
694 i as usize,
695 ),
696 ),
697 {
698 let path = old.0.value().path;
699 let level = old.0.value().parent_level;
700 let size = page_size::<PagingConsts>((level - 1) as PagingLevel);
701 let huge = Mapping {
702 va_range: vaddr_of::<C>(path) as int..vaddr_of::<C>(path) + page_size::<PagingConsts>(
703 level,
704 ),
705 pa_range: old.0.value().frame().mapped_pa..(old.0.value().frame().mapped_pa
706 + page_size::<PagingConsts>(level)) as usize,
707 page_size: page_size::<PagingConsts>(level),
708 property: old.0.value().frame().prop,
709 };
710 assert(old.view_rec(path) == set![huge]);
711 lemma_set_choose_len(set![huge]);
712 assert(old.view_rec(path).choose() == huge);
713 let pieces = Set::<int>::range(0, NR_ENTRIES as int).map(
714 |i: int| CursorView::<C>::split_index(huge, size, i as usize),
715 );
716 self.lemma_view_rec_contains(path);
717 assert forall|i: int| 0 <= i < NR_ENTRIES implies #[trigger] PageTableOwner(
718 self.0.child(i),
719 ).view_rec(path.push_tail(i))
720 == set![CursorView::<C>::split_index(huge, size, i as usize)] by {
721 assert(self.0.has_child(i));
722 assert(self.0.child(i).inv());
723 assert(self.0.child(i).value().is_frame());
724 Self::lemma_vaddr_push_tail_eq(path, i);
725 lemma_vaddr_of_eq_int::<C>(path);
726 lemma_vaddr_of_eq_int::<C>(path.push_tail(i));
727 assert(vaddr_of::<C>(path.push_tail(i)) == vaddr_of::<C>(path) + i * size);
728 vstd::arithmetic::mul::lemma_mul_is_distributive_add_other_way(size as int, i, 1);
729 let child = self.0.child(i);
730 let piece = CursorView::<C>::split_index(huge, size, i as usize);
731 assert(child.value().path == path.push_tail(i));
732 assert(child.value().frame().mapped_pa == huge.pa_range.start + i * size);
733 let child_mapping = Mapping {
734 va_range: vaddr_of::<C>(path.push_tail(i)) as int..vaddr_of::<C>(path.push_tail(i))
735 + size,
736 pa_range: child.value().frame().mapped_pa..(child.value().frame().mapped_pa
737 + size) as usize,
738 page_size: size,
739 property: child.value().frame().prop,
740 };
741 assert(PageTableOwner(child).view_rec(path.push_tail(i)) == set![child_mapping]);
742 };
743 assert forall|m: Mapping|
744 self.view_rec(path).contains(m) implies #[trigger] pieces.contains(m) by {
745 let i = self.view_rec_contains_choose(path, m);
746 assert(self.0.has_child(i));
747 assert(m == CursorView::<C>::split_index(huge, size, i as usize));
748 assert(Set::<int>::range(0, NR_ENTRIES as int).contains(i));
749 assert(pieces.contains(CursorView::<C>::split_index(huge, size, i as usize)));
750 };
751 assert forall|m: Mapping| #[trigger] pieces.contains(m) implies self.view_rec(
752 path,
753 ).contains(m) by {
754 let i = choose|i: int|
755 0 <= i < NR_ENTRIES && m == #[trigger] CursorView::<C>::split_index(
756 huge,
757 size,
758 i as usize,
759 );
760 assert(self.0.has_child(i));
761 assert(PageTableOwner(self.0.child(i)).view_rec(path.push_tail(i)).contains(m));
762 self.lemma_view_rec_contains_intro(path, m, i);
763 };
764 }
765
766 pub open spec fn view_rec_node_children(self, path: TreePath<NR_ENTRIES>) -> Seq<Set<Mapping>>
767 decreases INC_LEVELS - path.len(), 0nat,
768 when self.0.inv() && path.len() < INC_LEVELS - 1
769 {
770 self.0.children().map(
771 |i, child: Option<OwnerSubtree<C>>|
772 if child is Some {
773 PageTableOwner(child->0).view_rec(path.push_tail(i))
774 } else {
775 Set::empty()
776 },
777 )
778 }
779
780 pub open spec fn view_rec(self, path: TreePath<NR_ENTRIES>) -> Set<Mapping>
781 decreases INC_LEVELS - path.len(), 1nat,
782 when self.0.inv() && path.len() <= INC_LEVELS - 1
783 {
784 if self.0.value().is_frame() {
785 let va = vaddr_of::<C>(path);
786 let pt_level = INC_LEVELS - path.len();
787 let page_size = page_size::<PagingConsts>(pt_level as PagingLevel);
788
789 set![Mapping {
790 va_range: Range { start: va as int, end: va + page_size },
791 pa_range: Range {
792 start: self.0.value().frame().mapped_pa,
793 end: (self.0.value().frame().mapped_pa + page_size) as Paddr,
794 },
795 page_size: page_size,
796 property: self.0.value().frame().prop,
797 }]
798 } else if self.0.value().is_node() && path.len() < INC_LEVELS - 1 {
799 self.view_rec_node_children(path).to_set().flatten()
800 } else {
801 set![]
802 }
803 }
804
805 pub broadcast proof fn lemma_view_rec_contains_intro(
806 self,
807 path: TreePath<NR_ENTRIES>,
808 m: Mapping,
809 i: int,
810 )
811 requires
812 self.0.inv(),
813 path.len() < INC_LEVELS - 1,
814 self.0.value().is_node(),
815 0 <= i < self.0.children().len(),
816 self.0.has_child(i),
817 #[trigger] PageTableOwner(self.0.children()[i]->0).view_rec(path.push_tail(i)).contains(
818 m,
819 ),
820 ensures
821 self.view_rec(path).contains(m),
822 {
823 broadcast use group_seq_properties;
824
825 let mapped = self.view_rec_node_children(path);
826 assert(mapped.to_set().contains(mapped[i]));
827 }
828
829 pub broadcast proof fn lemma_view_rec_contains(self, path: TreePath<NR_ENTRIES>)
830 requires
831 self.0.inv(),
832 path.len() < INC_LEVELS - 1,
833 self.0.value().is_node(),
834 ensures
835 #![trigger self.view_rec(path)]
836 forall|m: Mapping|
837 #![trigger self.view_rec(path).contains(m)]
838 self.view_rec(path).contains(m) ==> exists|i: int|
839 #![trigger self.0.children()[i]]
840 0 <= i < self.0.children().len() && self.0.children()[i] is Some
841 && PageTableOwner(self.0.children()[i]->0).view_rec(
842 path.push_tail(i),
843 ).contains(m),
844 {
845 broadcast use group_seq_properties;
846
847 }
848
849 pub proof fn view_rec_contains_choose(self, path: TreePath<NR_ENTRIES>, m: Mapping) -> (i: int)
850 requires
851 self.0.inv(),
852 path.len() < INC_LEVELS - 1,
853 self.view_rec(path).contains(m),
854 self.0.value().is_node(),
855 ensures
856 0 <= i < self.0.children().len() && self.0.children()[i] is Some && PageTableOwner(
857 self.0.children()[i]->0,
858 ).view_rec(path.push_tail(i)).contains(m),
859 {
860 broadcast use PageTableOwner::group_lemmas;
861
862 choose|i: int|
863 #![auto]
864 0 <= i < self.0.children().len() && self.0.children()[i] is Some && PageTableOwner(
865 self.0.children()[i].unwrap(),
866 ).view_rec(path.push_tail(i)).contains(m)
867 }
868
869 pub proof fn lemma_vaddr_push_tail_eq(path: TreePath<NR_ENTRIES>, i: int)
871 requires
872 path.inv(),
873 path.len() < INC_LEVELS - 1,
874 0 <= i < NR_ENTRIES,
875 ensures
876 vaddr(path.push_tail(i)) == vaddr(path) + i * page_size::<PagingConsts>(
877 (INC_LEVELS - path.len() - 1) as PagingLevel,
878 ),
879 vaddr(path) + (i + 1) * page_size::<PagingConsts>(
880 (INC_LEVELS - path.len() - 1) as PagingLevel,
881 ) <= usize::MAX,
882 {
883 broadcast use {
884 TreePath::lemma_push_tail_len,
885 TreePath::lemma_push_tail_preserves_inv,
886 TreePath::lemma_index_satisfies_elem_inv,
887 };
888
889 let pt = path.push_tail(i);
890 lemma_rec_vaddr_push_tail(path, i, 0);
891 lemma_rec_vaddr_suffix_bound(pt, 0);
892 let entry_size = page_size::<PagingConsts>(
893 (INC_LEVELS - path.len() - 1) as PagingLevel,
894 ) as int;
895 assert(vaddr(path) + (i + 1) * entry_size == vaddr(pt) + entry_size) by (nonlinear_arith)
896 requires
897 vaddr(pt) == vaddr(path) + i * entry_size,
898 ;
899 }
900
901 pub proof fn lemma_view_rec_vaddr_range(self, path: TreePath<NR_ENTRIES>, m: Mapping)
902 requires
903 self.pt_inv(),
904 path.inv(),
905 path.len() <= INC_LEVELS - 1,
906 path.len() == self.0.level(),
907 self.0.value().parent_level == (INC_LEVELS - self.0.level()) as PagingLevel,
908 self.view_rec(path).contains(m),
909 ensures
910 vaddr_of::<C>(path) <= m.va_range.start,
911 m.va_range.start < m.va_range.end,
912 m.va_range.end <= vaddr_of::<C>(path) + page_size::<PagingConsts>(
913 (INC_LEVELS - path.len()) as PagingLevel,
914 ),
915 decreases INC_LEVELS - path.len(),
916 {
917 broadcast use PageTableOwner::group_lemmas;
918
919 lemma_page_size_spec_values();
920 if self.0.value().is_frame() {
921 let frame = self.0.value().frame();
922 let pt_level = (INC_LEVELS - path.len()) as PagingLevel;
923 let expected = Mapping {
924 va_range: Range {
925 start: vaddr_of::<C>(path) as int,
926 end: vaddr_of::<C>(path) + page_size::<PagingConsts>(pt_level),
927 },
928 pa_range: Range {
929 start: frame.mapped_pa,
930 end: (frame.mapped_pa + page_size::<PagingConsts>(pt_level)) as Paddr,
931 },
932 page_size: page_size::<PagingConsts>(pt_level),
933 property: frame.prop,
934 };
935 } else if self.0.value().is_node() && path.len() < INC_LEVELS - 1 {
936 let i = choose|i: int|
937 #![trigger self.0.children()[i]]
938 0 <= i < self.0.children().len() && self.0.children()[i] is Some && PageTableOwner(
939 self.0.children()[i].unwrap(),
940 ).view_rec(path.push_tail(i)).contains(m);
941 self.pt_inv_unroll(i);
942 let child = PageTableOwner(self.0.children()[i].unwrap());
943 child.lemma_view_rec_vaddr_range(path.push_tail(i), m);
944 Self::lemma_vaddr_push_tail_eq(path, i);
945
946 let child_ps = page_size::<PagingConsts>(
947 (INC_LEVELS - path.len() - 1) as PagingLevel,
948 ) as int;
949 assert((i + 1) * child_ps <= NR_ENTRIES * child_ps) by (nonlinear_arith)
950 requires
951 0 <= i < NR_ENTRIES,
952 child_ps >= 0,
953 ;
954 lemma_vaddr_of_eq_int::<C>(path.push_tail(i));
955 assert(i * child_ps + child_ps == (i + 1) * child_ps) by (nonlinear_arith);
956 }
957 }
958
959 pub proof fn lemma_view_rec_top_index_va_bound(
962 self,
963 path: TreePath<NR_ENTRIES>,
964 m: Mapping,
965 t: int,
966 )
967 requires
968 self.pt_inv(),
969 path.inv(),
970 1 <= path.len() <= INC_LEVELS - 1,
971 path.len() == self.0.level(),
972 self.0.value().parent_level == (INC_LEVELS - self.0.level()) as PagingLevel,
973 path[0] < t,
974 self.view_rec(path).contains(m),
975 ensures
976 (path[0]) * page_size::<C>(C::NR_LEVELS()) + page_table_coverage_start_spec::<C>()
977 <= m.va_range.start,
978 m.va_range.start < m.va_range.end,
979 m.va_range.end <= t * page_size::<C>(C::NR_LEVELS()) + page_table_coverage_start_spec::<
980 C,
981 >(),
982 {
983 self.lemma_view_rec_vaddr_range(path, m);
984 lemma_vaddr_of_eq_int::<C>(path);
985 lemma_vaddr_top_index_cell(path);
986 C::lemma_paging_consts_properties();
987 C::axiom_current_paging_consts_hardcoded();
988 lemma_arch_specific_consts_properties::<C>();
989 lemma_page_size_is_pow2_pte_index_bit_offset::<C>(C::NR_LEVELS());
990 lemma_page_size_spec_values();
991 lemma_mul_inequality(path[0] + 1, t, page_size::<C>(C::NR_LEVELS()) as int);
992 }
993
994 pub proof fn lemma_pt_inv_implies_path_correct(
999 subtree: OwnerSubtree<C>,
1000 path: TreePath<NR_ENTRIES>,
1001 )
1002 requires
1003 PageTableOwner(subtree).pt_inv(),
1004 subtree.value().path == path,
1005 ensures
1006 subtree.subtree_satisfies(path, Self::path_correct_pred()),
1007 decreases INC_LEVELS - subtree.level(),
1008 {
1009 if subtree.level() < INC_LEVELS - 1 {
1011 assert forall|i: int|
1012 0 <= i < subtree.children().len() && (
1013 #[trigger] subtree.children()[i]) is Some implies subtree.children()[i].unwrap().subtree_satisfies(
1014 path.push_tail(i), Self::path_correct_pred()) by {
1015 if subtree.value().is_node() {
1016 PageTableOwner(subtree).pt_inv_unroll(i);
1017 Self::lemma_pt_inv_implies_path_correct(
1019 subtree.children()[i].unwrap(),
1020 path.push_tail(i),
1021 );
1022 } else {
1023 PageTableOwner(subtree).pt_inv_non_node(i);
1025 }
1026 };
1027 }
1028 }
1029
1030 pub proof fn lemma_no_frame_with_path_rec(
1040 self,
1041 path: TreePath<NR_ENTRIES>,
1042 removed_path: TreePath<NR_ENTRIES>,
1043 ambient: Set<Mapping>,
1044 )
1045 requires
1046 self.pt_inv(),
1047 path.inv(),
1048 path.len() <= INC_LEVELS - 1,
1049 path.len() == self.0.level(),
1050 self.0.value().parent_level == (INC_LEVELS - self.0.level()) as PagingLevel,
1051 self.0.value().path == path,
1052 self.0.subtree_satisfies(path, Self::path_correct_pred()),
1053 forall|mm: Mapping|
1054 #![trigger self.view_rec(path).contains(mm)]
1055 self.view_rec(path).contains(mm) ==> ambient.contains(mm),
1056 forall|mm: Mapping|
1057 #![trigger ambient.contains(mm)]
1058 ambient.contains(mm) ==> mm.va_range.start != vaddr_of::<C>(removed_path),
1059 ensures
1060 self.0.subtree_satisfies(
1061 path,
1062 |e: EntryOwner<C>, _p: TreePath<NR_ENTRIES>|
1063 e.is_frame() ==> e.path != removed_path,
1064 ),
1065 decreases INC_LEVELS - path.len(),
1066 {
1067 broadcast use PageTableOwner::group_lemmas;
1068
1069 let g = |e: EntryOwner<C>, _p: TreePath<NR_ENTRIES>|
1070 e.is_frame() ==> e.path != removed_path;
1071
1072 if self.0.value().is_frame() {
1074 let frame = self.0.value().frame();
1075 let pt_level = (INC_LEVELS - path.len()) as PagingLevel;
1076 let expected = Mapping {
1077 va_range: Range {
1078 start: vaddr_of::<C>(path) as int,
1079 end: vaddr_of::<C>(path) + page_size::<PagingConsts>(pt_level),
1080 },
1081 pa_range: Range {
1082 start: frame.mapped_pa,
1083 end: (frame.mapped_pa + page_size::<PagingConsts>(pt_level)) as Paddr,
1084 },
1085 page_size: page_size::<PagingConsts>(pt_level),
1086 property: frame.prop,
1087 };
1088 assert(self.view_rec(path).contains(expected));
1089
1090 }
1091 if self.0.level() < INC_LEVELS - 1 {
1092 assert forall|i: int|
1093 0 <= i < self.0.children().len() && (
1094 #[trigger] self.0.children()[i]) is Some implies self.0.children()[i].unwrap().subtree_satisfies(
1095 path.push_tail(i), g) by {
1096 if self.0.value().is_node() {
1097 self.pt_inv_unroll(i);
1098 let child = PageTableOwner(self.0.children()[i].unwrap());
1099 child.lemma_no_frame_with_path_rec(path.push_tail(i), removed_path, ambient);
1102 } else {
1103 PageTableOwner(self.0).pt_inv_non_node(i);
1104 }
1105 };
1106 }
1107 }
1108
1109 pub proof fn lemma_view_rec_disjoint_vaddrs(
1110 self,
1111 path: TreePath<NR_ENTRIES>,
1112 m1: Mapping,
1113 m2: Mapping,
1114 )
1115 requires
1116 self.pt_inv(),
1117 path.inv(),
1118 path.len() <= INC_LEVELS - 1,
1119 path.len() == self.0.level(),
1120 self.0.value().parent_level == (INC_LEVELS - self.0.level()) as PagingLevel,
1121 self.view_rec(path).contains(m1),
1122 self.view_rec(path).contains(m2),
1123 m1 != m2,
1124 ensures
1125 m1.va_range.end <= m2.va_range.start || m2.va_range.end <= m1.va_range.start,
1126 decreases INC_LEVELS - path.len(),
1127 {
1128 broadcast use PageTableOwner::group_lemmas;
1129 broadcast use group_set_properties;
1130
1131 if self.0.value().is_frame() {
1132 } else if self.0.value().is_node() {
1133 let i1 = self.view_rec_contains_choose(path, m1);
1134 let i2 = self.view_rec_contains_choose(path, m2);
1135
1136 if i1 == i2 {
1137 self.pt_inv_unroll(i1);
1138 PageTableOwner(self.0.children()[i1].unwrap()).lemma_view_rec_disjoint_vaddrs(
1139 path.push_tail(i1),
1140 m1,
1141 m2,
1142 );
1143 } else {
1144 self.pt_inv_unroll(i1);
1145 self.pt_inv_unroll(i2);
1146 let child_ps = page_size::<PagingConsts>(
1147 (INC_LEVELS - path.len() - 1) as PagingLevel,
1148 );
1149 PageTableOwner(self.0.children()[i1].unwrap()).lemma_view_rec_vaddr_range(
1150 path.push_tail(i1),
1151 m1,
1152 );
1153 PageTableOwner(self.0.children()[i2].unwrap()).lemma_view_rec_vaddr_range(
1154 path.push_tail(i2),
1155 m2,
1156 );
1157 if i1 < i2 {
1158 sibling_paths_disjoint::<C>(path, i1, i2, child_ps);
1159 } else {
1160 sibling_paths_disjoint::<C>(path, i2, i1, child_ps);
1161 }
1162 lemma_vaddr_of_eq_int::<C>(path.push_tail(i1));
1166 lemma_vaddr_of_eq_int::<C>(path.push_tail(i2));
1167 }
1168 }
1169 }
1170
1171 pub proof fn lemma_view_rec_mapping_page_size(self, path: TreePath<NR_ENTRIES>)
1173 requires
1174 self.pt_inv(),
1175 path.len() <= INC_LEVELS - 1,
1176 path.len() == self.0.level(),
1177 self.0.value().parent_level == (INC_LEVELS - self.0.level()) as PagingLevel,
1178 ensures
1179 forall|m: Mapping| #[trigger]
1180 self.view_rec(path).contains(m)
1181 ==> set![4096usize, 2097152usize, 1073741824usize].contains(m.page_size),
1182 decreases INC_LEVELS - path.len(),
1183 {
1184 broadcast use PageTableOwner::group_lemmas;
1185
1186 if self.0.value().is_frame() {
1187 lemma_page_size_spec_values();
1188 } else if self.0.value().is_node() && path.len() < INC_LEVELS - 1 {
1189 assert forall|m: Mapping| #[trigger]
1190 self.view_rec(path).contains(
1191 m,
1192 ) implies set![4096usize, 2097152usize, 1073741824usize].contains(m.page_size) by {
1193 let i = choose|i: int|
1194 #![trigger self.0.children()[i]]
1195 0 <= i < self.0.children().len() && self.0.children()[i] is Some
1196 && PageTableOwner(self.0.children()[i].unwrap()).view_rec(
1197 path.push_tail(i),
1198 ).contains(m);
1199 self.pt_inv_unroll(i);
1200 let child = self.0.children()[i].unwrap();
1201 PageTableOwner(child).lemma_view_rec_mapping_page_size(path.push_tail(i));
1202 };
1203 }
1204 }
1205
1206 #[verifier::rlimit(200)]
1213 proof fn lemma_vaddr_path_alignment_and_bound(path: TreePath<NR_ENTRIES>)
1214 requires
1215 path.inv(),
1216 path.len() <= INC_LEVELS - 1,
1217 1 <= INC_LEVELS - path.len() <= NR_LEVELS,
1218 ensures
1219 vaddr(path) % page_size::<PagingConsts>((INC_LEVELS - path.len()) as PagingLevel) == 0,
1220 vaddr(path) + page_size::<PagingConsts>((INC_LEVELS - path.len()) as PagingLevel)
1221 <= usize::MAX,
1222 {
1223 lemma_rec_vaddr_aligned(path, 0);
1224 lemma_rec_vaddr_suffix_bound(path, 0);
1225 }
1226
1227 pub proof fn lemma_view_rec_mapping_inv(self, path: TreePath<NR_ENTRIES>)
1229 requires
1230 self.pt_inv(),
1231 path.inv(),
1232 path.len() <= INC_LEVELS - 1,
1233 path.len() == self.0.level(),
1234 self.0.value().parent_level == (INC_LEVELS - self.0.level()) as PagingLevel,
1235 ensures
1236 forall|m: Mapping| #[trigger] self.view_rec(path).contains(m) ==> m.inv(),
1237 decreases INC_LEVELS - path.len(),
1238 {
1239 broadcast use PageTableOwner::group_lemmas;
1240
1241 if self.0.value().is_frame() {
1242 lemma_page_size_spec_values();
1243 let frame = self.0.value().frame();
1244 let pt_level = (INC_LEVELS - path.len()) as PagingLevel;
1245 Self::lemma_vaddr_path_alignment_and_bound(path);
1246 let m = Mapping {
1247 va_range: Range {
1248 start: vaddr_of::<C>(path) as int,
1249 end: vaddr_of::<C>(path) + page_size::<PagingConsts>(pt_level),
1250 },
1251 pa_range: Range {
1252 start: frame.mapped_pa,
1253 end: (frame.mapped_pa + page_size::<PagingConsts>(pt_level)) as Paddr,
1254 },
1255 page_size: page_size::<PagingConsts>(pt_level),
1256 property: frame.prop,
1257 };
1258
1259 let ps = page_size::<PagingConsts>(pt_level) as int;
1260 vstd_extra::arithmetic::lemma_mod_0_add(frame.mapped_pa as int, ps, ps);
1261 lemma_vaddr_of_eq_int::<C>(path);
1262 C::lemma_page_table_config_constant_properties();
1263 C::lemma_paging_consts_properties();
1264 C::axiom_current_paging_consts_hardcoded();
1265 lemma_arch_specific_consts_properties::<C>();
1266 lemma_leading_bits_properties::<C>();
1267 assert(nr_page_table_coverage_bits_spec::<C>() == 48);
1268 lemma_vaddr_strict_bound(path);
1269 let lb = leading_bits_spec::<C>() as int;
1270 lemma2_to64();
1271 lemma2_to64_rest();
1272 assert(lb < 65536);
1273 let limit = page_size::<PagingConsts>(INC_LEVELS as PagingLevel) as int;
1274 assert(pow2(nr_page_table_coverage_bits_spec::<C>() as nat) == limit);
1275 lemma_page_size_divides::<PagingConsts>(pt_level, INC_LEVELS as PagingLevel);
1276 assert(limit % ps == 0);
1277 lemma_fundamental_div_mod(limit, ps);
1278 let limit_ratio = limit / ps;
1279 assert(limit == limit_ratio * ps);
1280 lemma_mul_is_associative(lb, limit_ratio, ps);
1281 lemma_mod_multiples_basic(lb * limit_ratio, ps);
1282 assert(lb * limit == (lb * limit_ratio) * ps);
1283 assert((lb * limit) % ps == 0);
1284 lemma_mod_adds(vaddr(path) as int, lb * limit, ps);
1285 let v = vaddr(path) as int;
1286 lemma_mod_equivalence(limit, v, ps);
1287 let diff = limit - v;
1288 let q = diff / ps;
1289 lemma_fundamental_div_mod(diff, ps);
1290 assert(q >= 1) by (nonlinear_arith)
1291 requires
1292 diff > 0,
1293 ps > 0,
1294 diff == ps * q,
1295 ;
1296 lemma_mul_inequality(1, q, ps);
1297 lemma_mul_inequality(lb, 0xffffint, limit);
1298 assert(v + ps <= limit) by (nonlinear_arith)
1299 requires
1300 q >= 1,
1301 ps > 0,
1302 diff == ps * q,
1303 diff == limit - v,
1304 ;
1305 assert(0xffffint * limit + limit == 0x1_0000_0000_0000_0000int);
1306 assert(lb * limit + v + ps <= 0x1_0000_0000_0000_0000int);
1307 lemma_mod_0_add(m.va_range.start, ps, ps);
1308 assert(set![4096, 2097152, 1073741824].contains(m.page_size));
1309 assert(m.pa_range.start <= m.pa_range.end <= MAX_PADDR);
1310 assert forall|m2: Mapping| #[trigger]
1311 self.view_rec(path).contains(m2) implies m2.inv() by {};
1312 } else if self.0.value().is_node() && path.len() < INC_LEVELS - 1 {
1313 assert forall|m: Mapping| #[trigger]
1314 self.view_rec(path).contains(m) implies m.inv() by {
1315 let i = choose|i: int|
1316 #![trigger self.0.children()[i]]
1317 0 <= i < self.0.children().len() && self.0.children()[i] is Some
1318 && PageTableOwner(self.0.children()[i].unwrap()).view_rec(
1319 path.push_tail(i),
1320 ).contains(m);
1321 self.pt_inv_unroll(i);
1322 let child = self.0.children()[i].unwrap();
1323 PageTableOwner(child).lemma_view_rec_mapping_inv(path.push_tail(i));
1324 };
1325 }
1326 }
1327
1328 pub proof fn lemma_view_rec_absent_empty(self, path: TreePath<NR_ENTRIES>)
1330 requires
1331 self.0.inv(),
1332 self.0.value().is_absent(),
1333 path.len() <= INC_LEVELS - 1,
1334 ensures
1335 self.view_rec(path) == set![],
1336 {
1337 }
1338
1339 pub proof fn lemma_view_rec_nr_children_zero_empty(self, path: TreePath<NR_ENTRIES>)
1350 requires
1351 self.pt_inv(),
1352 self.0.value().is_node(),
1353 self.0.value().node().meta_own.nr_children.value() == 0,
1354 self.0.value().node().count_consistent(),
1355 path.len() <= INC_LEVELS - 1,
1356 path.len() == self.0.level(),
1357 ensures
1358 self.view_rec(path) == set![],
1359 {
1360 if path.len() < INC_LEVELS - 1 {
1361 let cp = self.0.value().node().children_perm.value();
1362 self.lemma_view_rec_contains(path);
1364 assert forall|m: Mapping| self.view_rec(path).contains(m) implies false by {
1365 let i = choose|i: int|
1366 #![trigger self.0.children()[i]]
1367 0 <= i < self.0.children().len() && self.0.children()[i] is Some
1368 && PageTableOwner(self.0.children()[i]->0).view_rec(
1369 path.push_tail(i),
1370 ).contains(m);
1371 self.pt_inv_unroll(i);
1372 if cp[i].is_present() {
1374 crate::specs::mm::page_table::node::owners::lemma_count_present_upto_present(
1375 cp,
1376 cp.len() as int,
1377 i,
1378 );
1379 }
1380 };
1386 }
1387 }
1388
1389 pub open spec fn metaregion_sound_pred(regions: MetaRegionOwners) -> (spec_fn(
1390 EntryOwner<C>,
1391 TreePath<NR_ENTRIES>,
1392 ) -> bool) {
1393 |entry: EntryOwner<C>, path: TreePath<NR_ENTRIES>| entry.metaregion_sound(regions)
1394 }
1395
1396 pub open spec fn metaregion_sound(self, regions: MetaRegionOwners) -> bool
1397 decreases INC_LEVELS - self.0.level(),
1398 when self.0.inv()
1399 {
1400 self.0.subtree_satisfies(self.0.value().path, Self::metaregion_sound_pred(regions))
1401 }
1402
1403 pub proof fn lemma_metaregion_sound_preserved_slot_owners_eq(
1408 self,
1409 r0: MetaRegionOwners,
1410 r1: MetaRegionOwners,
1411 )
1412 requires
1413 self.inv(),
1414 self.metaregion_sound(r0),
1415 r0.slot_owners == r1.slot_owners,
1416 forall|k: int| r0.slots.contains_key(k) ==> #[trigger] r1.slots.contains_key(k),
1417 forall|k: int| r0.slots.contains_key(k) ==> r0.slots[k] == #[trigger] r1.slots[k],
1418 ensures
1419 self.metaregion_sound(r1),
1420 {
1421 Self::lemma_metaregion_sound_preserved_slot_owners_eq_subtree(
1422 self.0,
1423 self.0.value().path,
1424 r0,
1425 r1,
1426 );
1427 }
1428
1429 pub proof fn lemma_metaregion_sound_preserved_slot_owners_eq_subtree(
1431 subtree: OwnerSubtree<C>,
1432 path: TreePath<NR_ENTRIES>,
1433 r0: MetaRegionOwners,
1434 r1: MetaRegionOwners,
1435 )
1436 requires
1437 subtree.inv(),
1438 subtree.subtree_satisfies(path, Self::metaregion_sound_pred(r0)),
1439 r0.slot_owners == r1.slot_owners,
1440 forall|k: int| r0.slots.contains_key(k) ==> #[trigger] r1.slots.contains_key(k),
1441 forall|k: int| r0.slots.contains_key(k) ==> r0.slots[k] == #[trigger] r1.slots[k],
1442 ensures
1443 subtree.subtree_satisfies(path, Self::metaregion_sound_pred(r1)),
1444 decreases INC_LEVELS - subtree.level(),
1445 {
1446 subtree.value().metaregion_sound_slot_owners_only(r0, r1);
1447 if subtree.level() < INC_LEVELS - 1 {
1449 assert forall|i: int|
1450 #![trigger subtree.has_child(i)]
1451 0 <= i < NR_ENTRIES && subtree.has_child(i) implies subtree.child(
1452 i,
1453 ).subtree_satisfies(path.push_tail(i), Self::metaregion_sound_pred(r1)) by {
1454 Self::lemma_metaregion_sound_preserved_slot_owners_eq_subtree(
1455 subtree.child(i),
1456 path.push_tail(i),
1457 r0,
1458 r1,
1459 );
1460 }
1461 }
1462 }
1463
1464 pub proof fn lemma_metaregion_sound_preserved_one_slot_changed_subtree(
1465 subtree: OwnerSubtree<C>,
1466 path: TreePath<NR_ENTRIES>,
1467 r0: MetaRegionOwners,
1468 r1: MetaRegionOwners,
1469 changed_idx: int,
1470 )
1471 requires
1472 subtree.inv(),
1473 subtree.subtree_satisfies(path, Self::metaregion_sound_pred(r0)),
1474 forall|i: int|
1475 #![trigger r1.slot_owners[i]]
1476 i != changed_idx ==> r0.slot_owners[i] == r1.slot_owners[i],
1477 r0.slot_owners.dom() == r1.slot_owners.dom(),
1478 forall|k: int| r0.slots.contains_key(k) ==> #[trigger] r1.slots.contains_key(k),
1479 forall|k: int| r0.slots.contains_key(k) ==> r0.slots[k] == #[trigger] r1.slots[k],
1480 subtree.subtree_satisfies(
1481 path,
1482 |e: EntryOwner<C>, p: TreePath<NR_ENTRIES>|
1483 e.meta_slot_paddr() is Some ==> frame_to_index(e.meta_slot_paddr()->0)
1484 != changed_idx,
1485 ),
1486 ensures
1487 subtree.subtree_satisfies(path, Self::metaregion_sound_pred(r1)),
1488 decreases INC_LEVELS - subtree.level(),
1489 {
1490 subtree.value().lemma_metaregion_sound_one_slot_changed(r0, r1, changed_idx);
1491 if subtree.level() < INC_LEVELS - 1 {
1492 assert forall|i: int|
1493 #![trigger subtree.has_child(i)]
1494 0 <= i < NR_ENTRIES && subtree.has_child(i) implies subtree.child(
1495 i,
1496 ).subtree_satisfies(path.push_tail(i), Self::metaregion_sound_pred(r1)) by {
1497 Self::lemma_metaregion_sound_preserved_one_slot_changed_subtree(
1498 subtree.child(i),
1499 path.push_tail(i),
1500 r0,
1501 r1,
1502 changed_idx,
1503 );
1504 }
1505 }
1506 }
1507
1508 pub open spec fn path_tracked_pred(regions: MetaRegionOwners) -> spec_fn(
1511 EntryOwner<C>,
1512 TreePath<NR_ENTRIES>,
1513 ) -> bool {
1514 |entry: EntryOwner<C>, path: TreePath<NR_ENTRIES>|
1515 {
1516 entry.is_node() && entry.meta_slot_paddr() is Some ==> {
1518 &&& regions.slot_owners.contains_key(frame_to_index(entry.meta_slot_paddr()->0))
1519 &&& regions.slot_owner(entry.meta_slot_paddr()->0).paths_in_pt
1520 == set![entry.path]
1521 }
1522 }
1523 }
1524
1525 pub open spec fn path_correct_pred() -> spec_fn(EntryOwner<C>, TreePath<NR_ENTRIES>) -> bool {
1526 |entry: EntryOwner<C>, path: TreePath<NR_ENTRIES>| { entry.path == path }
1527 }
1528
1529 pub proof fn lemma_view_rec_page_size_bound(self, path: TreePath<NR_ENTRIES>, m: Mapping)
1532 requires
1533 self.0.inv(),
1534 path.len() <= INC_LEVELS - 1,
1535 self.view_rec(path).contains(m),
1536 ensures
1537 m.page_size <= page_size::<PagingConsts>((INC_LEVELS - path.len()) as PagingLevel),
1538 decreases INC_LEVELS - path.len(),
1539 {
1540 broadcast use PageTableOwner::group_lemmas;
1541
1542 if self.0.value().is_node() && path.len() < INC_LEVELS - 1 {
1543 let i = choose|i: int|
1544 #![trigger self.0.children()[i]]
1545 0 <= i < self.0.children().len() && self.0.children()[i] is Some && PageTableOwner(
1546 self.0.children()[i].unwrap(),
1547 ).view_rec(path.push_tail(i)).contains(m);
1548 PageTableOwner(self.0.children()[i].unwrap()).lemma_view_rec_page_size_bound(
1549 path.push_tail(i),
1550 m,
1551 );
1552 lemma_page_size_monotone::<PagingConsts>(
1553 (INC_LEVELS - path.len() - 1) as PagingLevel,
1554 (INC_LEVELS - path.len()) as PagingLevel,
1555 );
1556 }
1557 }
1558
1559 pub proof fn lemma_view_rec_node_page_size_bound(self, path: TreePath<NR_ENTRIES>, m: Mapping)
1562 requires
1563 self.0.inv(),
1564 self.0.value().is_node(),
1565 path.len() < INC_LEVELS - 1,
1566 self.view_rec(path).contains(m),
1567 ensures
1568 m.page_size <= page_size::<PagingConsts>(
1569 ((INC_LEVELS - path.len()) - 1) as PagingLevel,
1570 ),
1571 decreases INC_LEVELS - path.len(),
1572 {
1573 broadcast use PageTableOwner::group_lemmas;
1574
1575 let i = choose|i: int|
1576 #![trigger self.0.children()[i]]
1577 0 <= i < self.0.children().len() && self.0.children()[i] is Some && PageTableOwner(
1578 self.0.children()[i].unwrap(),
1579 ).view_rec(path.push_tail(i)).contains(m);
1580 PageTableOwner(self.0.children()[i].unwrap()).lemma_view_rec_page_size_bound(
1581 path.push_tail(i),
1582 m,
1583 );
1584 }
1585
1586 pub open spec fn is_prefix_of<const N: usize>(prefix: TreePath<N>, path: TreePath<N>) -> bool {
1588 &&& prefix.len() <= path.len()
1589 &&& forall|i: int| 0 <= i < prefix.len() ==> prefix[i] == path[i]
1590 }
1591
1592 pub proof fn lemma_neq_old_from_path_disjoint(
1595 subtree: OwnerSubtree<C>,
1596 path_j: TreePath<NR_ENTRIES>,
1597 old_entry: EntryOwner<C>,
1598 regions: MetaRegionOwners,
1599 )
1600 requires
1601 subtree.inv(),
1602 subtree.value().path == path_j,
1603 path_j.len() == subtree.level(),
1604 path_j.inv(),
1605 path_j.len() <= INC_LEVELS - 1,
1606 subtree.subtree_satisfies(path_j, Self::metaregion_sound_pred(regions)),
1607 subtree.subtree_satisfies(path_j, Self::path_correct_pred()),
1608 old_entry.is_node(),
1609 old_entry.meta_slot_paddr() is Some,
1610 regions.slot_owner(old_entry.meta_slot_paddr()->0).paths_in_pt == set![old_entry.path],
1611 !Self::is_prefix_of(path_j, old_entry.path),
1612 ensures
1613 subtree.subtree_satisfies(
1614 path_j,
1615 |e: EntryOwner<C>, p: TreePath<NR_ENTRIES>| e.meta_slot_paddr_neq(old_entry),
1616 ),
1617 decreases INC_LEVELS - subtree.level(),
1618 {
1619 let f_sound = Self::metaregion_sound_pred(regions);
1620 let f_path = Self::path_correct_pred();
1621 let g = |e: EntryOwner<C>, p: TreePath<NR_ENTRIES>| e.meta_slot_paddr_neq(old_entry);
1622
1623 assert(g(subtree.value(), path_j)) by {
1624 assert(f_sound(subtree.value(), path_j));
1625 assert(f_path(subtree.value(), path_j));
1626 let entry = subtree.value();
1627 if (entry.is_node() || (entry.is_frame() && entry.frame_is_tracked()))
1628 && entry.meta_slot_paddr() is Some && entry.meta_slot_paddr()->0
1629 == old_entry.meta_slot_paddr()->0 {
1630 let idx = frame_to_index(entry.meta_slot_paddr()->0);
1631 let old_idx = frame_to_index(old_entry.meta_slot_paddr()->0);
1632 assert(idx == old_idx);
1633 if entry.is_node() {
1634 assert(regions.slot_owners[idx].paths_in_pt == set![entry.path]);
1635 assert(regions.slot_owners[idx].paths_in_pt.contains(entry.path));
1636 } else {
1637 assert(entry.is_frame());
1638 assert(regions.slot_owners[idx].paths_in_pt.contains(entry.path));
1639 }
1640 assert(set![old_entry.path].contains(entry.path));
1641 assert(entry.path == old_entry.path);
1642 assert(Self::is_prefix_of(path_j, old_entry.path));
1643 }
1644 };
1645
1646 if subtree.level() < INC_LEVELS - 1 {
1647 assert forall|i: int|
1648 0 <= i < subtree.children().len() && (
1649 #[trigger] subtree.children()[i]) is Some implies subtree.children()[i].unwrap().subtree_satisfies(
1650 path_j.push_tail(i), g) by {
1651 let child = subtree.child(i);
1652 let child_path = path_j.push_tail(i);
1653 subtree.lemma_subtree_satisfies_unroll_once(path_j, f_sound, i);
1654 subtree.lemma_subtree_satisfies_unroll_once(path_j, f_path, i);
1655 Self::lemma_neq_old_from_path_disjoint(child, child_path, old_entry, regions);
1656 };
1657 }
1658 }
1659
1660 pub broadcast group group_lemmas {
1661 PageTableOwner::lemma_view_rec_contains,
1662 PageTableOwner::lemma_view_rec_contains_intro,
1663 }
1664}
1665
1666impl<C: PageTableConfig> Inv for PageTableOwner<C> {
1667 open spec fn inv(self) -> bool {
1668 &&& self.0.inv()
1669 &&& self.pt_inv_at_depth((INC_LEVELS - self.0.level()) as nat)
1670 &&& self.0.value().is_node()
1671 &&& self.0.value().path.len() <= INC_LEVELS - 1
1672 &&& self.0.value().path.inv()
1673 &&& self.0.value().path.len() == self.0.level()
1674 &&& self.0.value().parent_level == (INC_LEVELS - self.0.level()) as PagingLevel
1675 &&& self.0.value().node().tree_level == self.0.value().path.len()
1676 }
1677}
1678
1679impl<C: PageTableConfig> View for PageTableOwner<C> {
1680 type V = PageTableView;
1681
1682 open spec fn view(&self) -> <Self as View>::V {
1683 let mappings = self.view_rec(self.0.value().path);
1684 PageTableView { mappings }
1685 }
1686}
1687
1688proof fn lemma_vaddr_make_eq_page_size(idx: int, offset: int)
1691 requires
1692 0 <= idx < NR_LEVELS,
1693 0 <= offset < NR_ENTRIES,
1694 ensures
1695 vaddr_make::<NR_LEVELS>(idx, offset as usize) == offset * page_size::<PagingConsts>(
1696 (NR_LEVELS - idx) as PagingLevel,
1697 ),
1698{
1699 let entry_size = page_size::<PagingConsts>((NR_LEVELS - idx) as PagingLevel) as int;
1700 let parent_size = page_size::<PagingConsts>((INC_LEVELS - idx) as PagingLevel) as int;
1701 lemma_page_size_next::<PagingConsts>((NR_LEVELS - idx) as PagingLevel);
1702 lemma_nr_subpage_per_huge_eq_nr_entries();
1703 vstd::arithmetic::mul::lemma_mul_is_commutative(NR_ENTRIES as int, entry_size);
1704 assert(offset * entry_size <= NR_ENTRIES * entry_size) by (nonlinear_arith)
1705 requires
1706 0 <= offset <= NR_ENTRIES,
1707 0 <= entry_size,
1708 ;
1709 assert(NR_ENTRIES * entry_size == parent_size);
1710 assert(parent_size <= usize::MAX);
1711 assert(offset as usize == offset);
1712 assert(page_size_spec::<PagingConsts>((NR_LEVELS - idx) as PagingLevel) == entry_size);
1713 assert(vaddr_make::<NR_LEVELS>(idx, offset as usize) == (entry_size * offset) as usize);
1714 assert(entry_size * offset == offset * entry_size) by (nonlinear_arith);
1715}
1716
1717proof fn lemma_rec_vaddr_suffix_bound(path: TreePath<NR_ENTRIES>, idx: int)
1720 requires
1721 path.inv(),
1722 0 <= idx <= path.len() <= INC_LEVELS - 1,
1723 ensures
1724 rec_vaddr(path, idx) + page_size::<PagingConsts>((INC_LEVELS - path.len()) as PagingLevel)
1725 <= page_size::<PagingConsts>((INC_LEVELS - idx) as PagingLevel),
1726 decreases path.len() - idx,
1727{
1728 if idx == path.len() {
1729 assert(rec_vaddr(path, idx) == 0);
1730 } else {
1731 path.lemma_index_satisfies_elem_inv(idx);
1732 lemma_rec_vaddr_suffix_bound(path, idx + 1);
1733 lemma_page_size_next::<PagingConsts>((NR_LEVELS - idx) as PagingLevel);
1734 lemma_nr_subpage_per_huge_eq_nr_entries();
1735
1736 let entry_size = page_size::<PagingConsts>((NR_LEVELS - idx) as PagingLevel) as int;
1737 let mapped_page_size = page_size::<PagingConsts>(
1738 (INC_LEVELS - path.len()) as PagingLevel,
1739 ) as int;
1740 lemma_vaddr_make_eq_page_size(idx, path[idx]);
1741 assert(path[idx] + 1 <= NR_ENTRIES);
1742 assert((path[idx] + 1) * entry_size <= NR_ENTRIES * entry_size) by (nonlinear_arith)
1743 requires
1744 0 <= path[idx] + 1 <= NR_ENTRIES,
1745 0 <= entry_size,
1746 ;
1747 assert(entry_size * path[idx] + rec_vaddr(path, idx + 1) + mapped_page_size <= (path[idx]
1748 + 1) * entry_size) by (nonlinear_arith)
1749 requires
1750 rec_vaddr(path, idx + 1) + mapped_page_size <= entry_size,
1751 0 <= entry_size,
1752 ;
1753 assert(entry_size * path[idx] + rec_vaddr(path, idx + 1) <= usize::MAX);
1754 assert(rec_vaddr(path, idx) == entry_size * path[idx] + rec_vaddr(path, idx + 1));
1755 assert(rec_vaddr(path, idx) + mapped_page_size <= (path[idx] + 1) * entry_size);
1756 }
1757}
1758
1759proof fn lemma_rec_vaddr_unfold(path: TreePath<NR_ENTRIES>, idx: int)
1761 requires
1762 path.inv(),
1763 0 <= idx < path.len() <= INC_LEVELS - 1,
1764 ensures
1765 rec_vaddr(path, idx) == path[idx] * page_size::<PagingConsts>(
1766 (NR_LEVELS - idx) as PagingLevel,
1767 ) + rec_vaddr(path, idx + 1),
1768{
1769 path.lemma_index_satisfies_elem_inv(idx);
1770 lemma_rec_vaddr_suffix_bound(path, idx + 1);
1771 lemma_vaddr_make_eq_page_size(idx, path[idx]);
1772 lemma_page_size_next::<PagingConsts>((NR_LEVELS - idx) as PagingLevel);
1773 lemma_nr_subpage_per_huge_eq_nr_entries();
1774
1775 let entry_size = page_size::<PagingConsts>((NR_LEVELS - idx) as PagingLevel) as int;
1776 let parent_size = page_size::<PagingConsts>((INC_LEVELS - idx) as PagingLevel) as int;
1777 assert(path[idx] + 1 <= NR_ENTRIES);
1778 assert(path[idx] * entry_size + rec_vaddr(path, idx + 1) <= (path[idx] + 1) * entry_size)
1779 by (nonlinear_arith)
1780 requires
1781 rec_vaddr(path, idx + 1) <= entry_size,
1782 0 <= entry_size,
1783 ;
1784 assert((path[idx] + 1) * entry_size <= NR_ENTRIES * entry_size) by (nonlinear_arith)
1785 requires
1786 0 <= path[idx] + 1 <= NR_ENTRIES,
1787 0 <= entry_size,
1788 ;
1789 assert(NR_ENTRIES * entry_size == parent_size);
1790 assert(parent_size <= usize::MAX);
1791 assert(path[idx] * entry_size + rec_vaddr(path, idx + 1) <= usize::MAX);
1792}
1793
1794proof fn lemma_rec_vaddr_aligned(path: TreePath<NR_ENTRIES>, idx: int)
1797 requires
1798 path.inv(),
1799 0 <= idx <= path.len() <= INC_LEVELS - 1,
1800 ensures
1801 rec_vaddr(path, idx) % page_size::<PagingConsts>((INC_LEVELS - path.len()) as PagingLevel)
1802 == 0,
1803 decreases path.len() - idx,
1804{
1805 let mapped_level = (INC_LEVELS - path.len()) as PagingLevel;
1806 let mapped_page_size = page_size::<PagingConsts>(mapped_level) as int;
1807 lemma_page_size_ge_base::<PagingConsts>(mapped_level);
1808 assert(0 < mapped_page_size);
1809 if idx == path.len() {
1810 assert(rec_vaddr(path, idx) == 0);
1811 assert(0usize % page_size::<PagingConsts>(mapped_level) == 0);
1812 } else {
1813 path.lemma_index_satisfies_elem_inv(idx);
1814 lemma_rec_vaddr_aligned(path, idx + 1);
1815 lemma_rec_vaddr_unfold(path, idx);
1816
1817 let entry_level = (NR_LEVELS - idx) as PagingLevel;
1818 let entry_size = page_size::<PagingConsts>(entry_level) as int;
1819 lemma_page_size_divides::<PagingConsts>(mapped_level, entry_level);
1820 assert(entry_size % mapped_page_size == 0);
1821 lemma_fundamental_div_mod(entry_size, mapped_page_size);
1822 let entry_ratio = entry_size / mapped_page_size;
1823 assert(entry_size == entry_ratio * mapped_page_size);
1824 lemma_mod_multiples_basic(path[idx] * entry_ratio, mapped_page_size);
1825 lemma_mul_is_associative(path[idx], entry_ratio, mapped_page_size);
1826 assert(path[idx] * entry_size == (path[idx] * entry_ratio) * mapped_page_size);
1827 assert((path[idx] * entry_size) % mapped_page_size == 0);
1828 lemma_mod_0_add(path[idx] * entry_size, rec_vaddr(path, idx + 1) as int, mapped_page_size);
1829 assert((path[idx] * entry_size + rec_vaddr(path, idx + 1)) % mapped_page_size == 0);
1830 assert((rec_vaddr(path, idx) as int) % mapped_page_size == 0);
1831 assert(rec_vaddr(path, idx) % page_size::<PagingConsts>(mapped_level) == ((rec_vaddr(
1832 path,
1833 idx,
1834 ) as int) % mapped_page_size) as usize);
1835 }
1836}
1837
1838proof fn lemma_rec_vaddr_push_tail(path: TreePath<NR_ENTRIES>, i: int, idx: int)
1840 requires
1841 path.inv(),
1842 0 <= idx <= path.len() < INC_LEVELS - 1,
1843 0 <= i < NR_ENTRIES,
1844 ensures
1845 rec_vaddr(path.push_tail(i), idx) == rec_vaddr(path, idx) + i * page_size::<PagingConsts>(
1846 (NR_LEVELS - path.len()) as PagingLevel,
1847 ),
1848 decreases path.len() - idx,
1849{
1850 let pt = path.push_tail(i);
1851 path.lemma_push_tail_len(i);
1852 path.lemma_push_tail_index(i);
1853 path.lemma_push_tail_preserves_inv(i);
1854 if idx == path.len() {
1855 assert(rec_vaddr(path, idx) == 0);
1856 lemma_rec_vaddr_unfold(pt, idx);
1857 assert(rec_vaddr(pt, idx + 1) == 0);
1858 } else {
1859 lemma_rec_vaddr_push_tail(path, i, idx + 1);
1860 lemma_rec_vaddr_unfold(path, idx);
1861 lemma_rec_vaddr_unfold(pt, idx);
1862 }
1863}
1864
1865}