1use vstd::{
2 arithmetic::{
3 div_mod::{lemma_div_denominator, lemma_fundamental_div_mod},
4 mul::lemma_mul_is_commutative,
5 power2::pow2,
6 },
7 bits::lemma_usize_shr_is_div,
8 prelude::*,
9 seq::*,
10 seq_lib::*,
11 set_lib::*,
12};
13use vstd_extra::{
14 arithmetic::nat_align_down, drop_tracking::*, ghost_tree::*, ownership::*,
15 prelude::TreeNodeValue,
16};
17
18use crate::specs::{
19 arch::*,
20 mm::{
21 frame::{mapping::frame_to_index, meta_region_owners::MetaRegionOwners},
22 page_table::{cursor::page_size_lemmas::lemma_page_size_spec_values, *},
23 },
24};
25
26use crate::arch::mm::PagingConsts;
27use crate::mm::{
28 Paddr, PagingConstsTrait, PagingLevel, Vaddr,
29 frame::meta::{REF_COUNT_MAX, REF_COUNT_UNIQUE, REF_COUNT_UNUSED},
30 page_size,
31 page_table::{EntryOwner, EntryOwnerKind, PageTableEntryTrait, PageTableGuard},
32};
33use core::ops::{Deref, Range};
34
35verus! {
36
37broadcast use group_ghost_tree_lemmas;
38
39#[verifier::inline]
40pub open spec fn vaddr_shift_bits<const L: usize>(idx: int) -> nat
41 recommends
42 0 < L,
43 idx < L,
44{
45 (12 + 9 * (L - 1 - idx)) as nat
46}
47
48#[verifier::inline]
49pub open spec fn vaddr_shift<const L: usize>(idx: int) -> usize
50 recommends
51 0 < L,
52 idx < L,
53{
54 pow2(vaddr_shift_bits::<L>(idx)) as usize
55}
56
57#[verifier::inline]
58pub open spec fn vaddr_make<const L: usize>(idx: int, offset: usize) -> usize
59 recommends
60 0 < L,
61 idx < L,
62 0 <= offset < 512,
63{
64 (vaddr_shift::<L>(idx) * offset) as usize
65}
66
67pub open spec fn rec_vaddr(path: TreePath<NR_ENTRIES>, idx: int) -> usize
68 decreases path.len() - idx,
69 when 0 <= idx <= path.len()
70{
71 if idx == path.len() {
72 0
73 } else {
74 let offset = path[idx] as usize;
75 (vaddr_make::<NR_LEVELS>(idx, offset) + rec_vaddr(path, idx + 1)) as usize
76 }
77}
78
79pub open spec fn vaddr(path: TreePath<NR_ENTRIES>) -> usize {
80 rec_vaddr(path, 0)
81}
82
83pub open spec fn vaddr_at(path: TreePath<NR_ENTRIES>, leading_bits: int) -> usize {
90 (vaddr(path) + leading_bits * 0x1_0000_0000_0000int) as usize
91}
92
93pub open spec fn vaddr_of<C: PageTableConfig>(path: TreePath<NR_ENTRIES>) -> usize {
99 vaddr_at(path, C::LEADING_BITS_spec() as int)
100}
101
102#[verifier::spinoff_prover]
106pub proof fn lemma_vaddr_strict_bound(path: TreePath<NR_ENTRIES>)
107 requires
108 path.inv(),
109 path.len() <= INC_LEVELS - 1,
110 ensures
111 vaddr(path) < 0x1_0000_0000_0000int,
112{
113 vstd::arithmetic::power2::lemma2_to64();
114 vstd::arithmetic::power2::lemma2_to64_rest();
115 if path.len() == 0 {
116 } else if path.len() == 1 {
117 let i0 = path[0];
118 assert(rec_vaddr(path, 1) == 0);
119 } else if path.len() == 2 {
120 let i0 = path[0];
121 let i1 = path[1];
122 assert(rec_vaddr(path, 2) == 0);
123 assert(rec_vaddr(path, 1) == vaddr_make::<NR_LEVELS>(1, i1 as usize) as usize);
124 } else if path.len() == 3 {
125 let i0 = path[0];
126 let i1 = path[1];
127 let i2 = path[2];
128 assert(rec_vaddr(path, 3) == 0);
129 assert(rec_vaddr(path, 2) == vaddr_make::<NR_LEVELS>(2, i2 as usize) as usize);
130 assert(rec_vaddr(path, 1) == (vaddr_make::<NR_LEVELS>(1, i1 as usize) + vaddr_make::<
131 NR_LEVELS,
132 >(2, i2 as usize)) as usize);
133 } else {
134 let i0 = path[0];
135 let i1 = path[1];
136 let i2 = path[2];
137 let i3 = path[3];
138 assert(rec_vaddr(path, 4) == 0);
139 assert(rec_vaddr(path, 3) == vaddr_make::<NR_LEVELS>(3, i3 as usize) as usize);
140 assert(rec_vaddr(path, 2) == (vaddr_make::<NR_LEVELS>(2, i2 as usize) + vaddr_make::<
141 NR_LEVELS,
142 >(3, i3 as usize)) as usize);
143 assert(rec_vaddr(path, 1) == (vaddr_make::<NR_LEVELS>(1, i1 as usize) + vaddr_make::<
144 NR_LEVELS,
145 >(2, i2 as usize) + vaddr_make::<NR_LEVELS>(3, i3 as usize)) as usize);
146 assert(rec_vaddr(path, 0) == (vaddr_make::<NR_LEVELS>(0, i0 as usize) + vaddr_make::<
147 NR_LEVELS,
148 >(1, i1 as usize) + vaddr_make::<NR_LEVELS>(2, i2 as usize) + vaddr_make::<NR_LEVELS>(
149 3,
150 i3 as usize,
151 )) as usize);
152 assert(0x80_0000_0000usize * i0 + 0x4000_0000usize * i1 + 0x20_0000usize * i2 + 0x1000usize
153 * i3 < 0x1_0000_0000_0000int) by (nonlinear_arith)
154 requires
155 i0 < 512,
156 i1 < 512,
157 i2 < 512,
158 i3 < 512,
159 ;
160 }
161}
162
163#[verifier::spinoff_prover]
167pub proof fn lemma_vaddr_top_index_cell(path: TreePath<NR_ENTRIES>)
168 requires
169 path.inv(),
170 1 <= path.len() <= INC_LEVELS - 1,
171 ensures
172 (path[0]) * 0x80_0000_0000int <= vaddr(path),
173 vaddr(path) + page_size::<PagingConsts>((INC_LEVELS - path.len()) as PagingLevel) <= (
174 path[0] + 1) * 0x80_0000_0000int,
175{
176 broadcast use TreePath::lemma_index_satisfies_elem_inv;
177
178 lemma_page_size_spec_values();
179 vstd::arithmetic::power2::lemma2_to64();
180 vstd::arithmetic::power2::lemma2_to64_rest();
181 let i0 = path[0];
182 if path.len() == 1 {
183 assert(rec_vaddr(path, 1) == 0);
184 } else if path.len() == 2 {
185 let i1 = path[1];
186 assert(rec_vaddr(path, 2) == 0);
187 assert(rec_vaddr(path, 1) == vaddr_make::<NR_LEVELS>(1, i1 as usize) as usize);
188 } else if path.len() == 3 {
189 let i1 = path[1];
190 let i2 = path[2];
191 assert(rec_vaddr(path, 3) == 0);
192 assert(rec_vaddr(path, 2) == vaddr_make::<NR_LEVELS>(2, i2 as usize) as usize);
193 assert(rec_vaddr(path, 1) == (vaddr_make::<NR_LEVELS>(1, i1 as usize) + vaddr_make::<
194 NR_LEVELS,
195 >(2, i2 as usize)) as usize);
196 } else {
197 let i1 = path[1];
198 let i2 = path[2];
199 let i3 = path[3];
200 assert(rec_vaddr(path, 4) == 0);
201 assert(rec_vaddr(path, 3) == vaddr_make::<NR_LEVELS>(3, i3 as usize) as usize);
202 assert(rec_vaddr(path, 2) == (vaddr_make::<NR_LEVELS>(2, i2 as usize) + vaddr_make::<
203 NR_LEVELS,
204 >(3, i3 as usize)) as usize);
205 assert(rec_vaddr(path, 1) == (vaddr_make::<NR_LEVELS>(1, i1 as usize) + vaddr_make::<
206 NR_LEVELS,
207 >(2, i2 as usize) + vaddr_make::<NR_LEVELS>(3, i3 as usize)) as usize);
208 assert(0x80_0000_0000int * i0 + 0x4000_0000int * i1 + 0x20_0000int * i2 + 0x1000int * i3
209 + 0x1000int <= (i0 + 1) * 0x80_0000_0000int) by (nonlinear_arith)
210 requires
211 i1 < 512,
212 i2 < 512,
213 i3 < 512,
214 ;
215 }
216}
217
218pub proof fn lemma_vaddr_of_eq_int<C: PageTableConfig>(path: TreePath<NR_ENTRIES>)
222 requires
223 path.inv(),
224 path.len() <= INC_LEVELS - 1,
225 ensures
226 vaddr_of::<C>(path) == vaddr(path) + C::LEADING_BITS_spec() as int * 0x1_0000_0000_0000int,
227 vaddr_of::<C>(path) == vaddr(path) + C::LEADING_BITS_spec() as int * pow2(
228 page_table_vaddr_bits_spec::<C>() as nat,
229 ),
230{
231 C::lemma_page_table_config_constant_properties();
232 C::lemma_paging_consts_properties();
233 lemma_vaddr_strict_bound(path);
234 let body_level = (C::NR_LEVELS() + 1) as PagingLevel;
235 lemma_page_size_is_pow2_pte_index_bit_offset::<C>(body_level);
236 lemma_page_size_spec_values();
237}
238
239pub proof fn lemma_vaddr_path_aligned<C: PageTableConfig>(path: TreePath<NR_ENTRIES>, va: Vaddr)
241 requires
242 path.inv(),
243 path.len() <= C::NR_LEVELS(),
244 forall|k: int|
245 0 <= k < path.len() ==> #[trigger] path[k] == pte_index_spec::<C>(
246 va,
247 (C::NR_LEVELS() - k) as PagingLevel,
248 ),
249 ensures
250 vaddr(path) + vaddr_upper_part_spec::<C>(va) == nat_align_down(
251 va as nat,
252 page_size::<C>((C::NR_LEVELS() - path.len() + 1) as PagingLevel) as nat,
253 ),
254 decreases path.len(),
255{
256 C::lemma_paging_consts_properties();
257 let level = (C::NR_LEVELS() - path.len() + 1) as PagingLevel;
258 lemma_page_size_is_pow2_pte_index_bit_offset::<C>(level);
259 let size = page_size::<C>(level) as int;
260 lemma_fundamental_div_mod(va as int, size);
261 vstd::arithmetic::div_mod::lemma_mod_bound(va as int, size);
262 vstd::arithmetic::div_mod::lemma_div_pos_is_pos(va as int, size);
263 if path.len() == 0 {
264 lemma_usize_shr_is_div(va, page_table_vaddr_bits_spec::<C>());
265 lemma_mul_is_commutative(size, va as int / size);
266 } else {
267 let (index, parent) = path.pop_tail();
268 path.lemma_pop_tail_preserves_inv();
269 path.lemma_index_satisfies_elem_inv(path.len() - 1);
270 assert(parent.push_tail(index) == path);
271 assert forall|k: int| 0 <= k < parent.len() implies #[trigger] parent[k]
272 == pte_index_spec::<C>(va, (C::NR_LEVELS() - k) as PagingLevel) by {
273 assert(parent[k] == path[k]);
274 };
275 lemma_vaddr_path_aligned::<C>(parent, va);
276 PageTableOwner::<C>::lemma_vaddr_push_tail_eq(parent, index);
277 lemma_page_size_next::<C>(level);
278 lemma_pte_index_spec_is_div_mod::<C>(va, level);
279
280 let fanout = nr_subpage_per_huge::<C>() as int;
281 let quotient = va as int / size;
282 let parent_size = page_size::<C>((level + 1) as PagingLevel) as int;
283 lemma_div_denominator(va as int, size, fanout);
284 lemma_fundamental_div_mod(quotient, fanout);
285 lemma_fundamental_div_mod(va as int, parent_size);
286 vstd::arithmetic::div_mod::lemma_mod_bound(va as int, parent_size);
287 vstd::arithmetic::div_mod::lemma_div_pos_is_pos(va as int, parent_size);
288 assert(vaddr(parent) + vaddr_upper_part_spec::<C>(va) == nat_align_down(
289 va as nat,
290 parent_size as nat,
291 ));
292 assert(vaddr(path) == vaddr(parent) + index * size);
293 assert(index == pte_index_spec::<C>(va, level));
294 assert(quotient == fanout * (va as int / parent_size) + index);
295 assert(size * quotient == parent_size * (va as int / parent_size) + size * index)
296 by (nonlinear_arith)
297 requires
298 quotient == fanout * (va as int / parent_size) + index,
299 parent_size == size * fanout,
300 ;
301 }
302}
303
304pub proof fn sibling_paths_disjoint<C: PageTableConfig>(
311 prefix: TreePath<NR_ENTRIES>,
312 j: int,
313 k: int,
314 size: usize,
315)
316 requires
317 prefix.inv(),
318 prefix.len() < INC_LEVELS - 1,
319 0 <= j,
320 0 <= k,
321 j < NR_ENTRIES,
322 k < NR_ENTRIES,
323 j != k,
324 size == page_size::<PagingConsts>((INC_LEVELS - prefix.len() - 1) as PagingLevel),
325 ensures
326 vaddr(prefix.push_tail(j)) + size <= vaddr(prefix.push_tail(k)) || vaddr(
327 prefix.push_tail(k),
328 ) + size <= vaddr(prefix.push_tail(j)),
329{
330 PageTableOwner::<C>::lemma_vaddr_push_tail_eq(prefix, j);
331 PageTableOwner::<C>::lemma_vaddr_push_tail_eq(prefix, k);
332 let s = size as int;
333 let vp = vaddr(prefix) as int;
334 let vj = vaddr(prefix.push_tail(j)) as int;
335 let vk = vaddr(prefix.push_tail(k)) as int;
336 if j < k {
337 assert(vj + s <= vk) by (nonlinear_arith)
338 requires
339 vj == vp + j * s,
340 vk == vp + k * s,
341 j < k,
342 s >= 0,
343 ;
344 } else {
345 assert(vk + s <= vj) by (nonlinear_arith)
346 requires
347 vj == vp + j * s,
348 vk == vp + k * s,
349 k < j,
350 s >= 0,
351 ;
352 }
353}
354
355impl<C: PageTableConfig, const L: usize> TreeNodeValue<L> for EntryOwner<C> {
356 open spec fn default(lv: nat) -> Self {
357 Self {
358 kind: EntryOwnerKind::Absent,
359 path: TreePath::new(Seq::empty()),
360 parent_level: (INC_LEVELS - lv) as PagingLevel,
361 }
362 }
363
364 proof fn lemma_default_preserves_inv() {
365 }
366
367 open spec fn la_inv(self, lv: nat) -> bool {
368 self.is_node() ==> lv < L - 1
369 }
370
371 proof fn lemma_default_preserves_la_inv() {
372 }
373
374 open spec fn rel_children(self, i: int, child: Option<Self>) -> bool {
377 true
378 }
379
380 proof fn lemma_default_preserves_rel_children(self, lv: nat) {
381 }
382}
383
384pub const INC_LEVELS: usize = NR_LEVELS + 1;
385
386pub type OwnerSubtree<C> = TreeNode<EntryOwner<C>, NR_ENTRIES, INC_LEVELS>;
396
397pub open spec fn allocated_empty_node_owner<C: PageTableConfig>(
406 owner: OwnerSubtree<C>,
407 level: PagingLevel,
408) -> bool {
409 &&& owner.inv()
410 &&& owner.value().is_node()
411 &&& owner.value().path == TreePath::<NR_ENTRIES>::new(Seq::empty())
412 &&& owner.value().parent_level == (level + 1) as PagingLevel
413 &&& owner.value().node().level()
414 == level
415 &&& owner.level() == (INC_LEVELS - level - 1) as nat
420 &&& owner.value().node().inv()
421 &&& !owner.value().node().children_perm.value().all(|child: C::E| child.is_present())
422 &&& forall|i: int|
423 0 <= i < NR_ENTRIES ==> {
424 &&& #[trigger] owner.has_child(i)
425 &&& owner.child(i).value().is_absent()
426 &&& owner.child(i).value().inv()
427 &&& owner.child(i).value().path == owner.value().path.push_tail(i)
428 }
429 &&& forall|i: int|
430 #![auto]
431 0 <= i < NR_ENTRIES ==> owner.child(i).value().match_pte(
432 owner.value().node().children_perm.value()[i],
433 owner.child(i).value().parent_level,
434 )
435 &&& forall|i: int|
436 #![auto]
437 0 <= i < NR_ENTRIES ==> owner.child(i).value().parent_level
438 == owner.value().node().level()
439 &&& forall|j: int|
444 0 <= j < NR_ENTRIES ==> #[trigger] owner.value().node().children_perm.value()[j]
445 == C::E::new_absent_spec()
446}
447
448pub open spec fn allocated_empty_node_grandchildren_none<C: PageTableConfig>(
456 owner: OwnerSubtree<C>,
457) -> bool {
458 forall|i: int, j: int|
459 0 <= i < NR_ENTRIES && 0 <= j < NR_ENTRIES ==> !#[trigger] owner.child(i).has_child(j)
460}
461
462pub proof fn rebase_freshly_allocated_children_at<C: PageTableConfig>(
467 tracked owner: &mut OwnerSubtree<C>,
468 new_path: TreePath<NR_ENTRIES>,
469 i: usize,
470)
471 requires
472 i <= NR_ENTRIES,
473 old(owner).children().len() == NR_ENTRIES,
474 new_path.inv(),
475 forall|j: int| i <= j < NR_ENTRIES ==> (#[trigger] old(owner).children()[j]) is Some,
476 ensures
477 final(owner).value() == old(owner).value(),
478 final(owner).level() == old(owner).level(),
479 final(owner).children().len() == NR_ENTRIES,
480 forall|j: int|
481 0 <= j < i ==> (#[trigger] final(owner).children()[j]) == old(owner).children()[j],
482 forall|j: int|
483 i <= j < NR_ENTRIES ==> {
484 let c_old = old(owner).child(j);
485 let c_new = (#[trigger] final(owner).children()[j])->0;
486 &&& final(owner).has_child(j)
487 &&& c_new.value() == EntryOwner { path: new_path.push_tail(j), ..c_old.value() }
488 &&& c_new.level() == c_old.level()
489 &&& c_new.children() == c_old.children()
490 },
491 decreases NR_ENTRIES - i,
492{
493 if i < NR_ENTRIES {
494 let tracked mut child_value = owner.tracked_borrow_mut_child(
495 i as int,
496 ).tracked_borrow_mut_value();
497 child_value.path = new_path.push_tail(i as int);
498 rebase_freshly_allocated_children_at(owner, new_path, (i + 1) as usize);
499 }
500}
501
502pub proof fn rebase_freshly_allocated_children<C: PageTableConfig>(
512 tracked owner: &mut OwnerSubtree<C>,
513 new_path: TreePath<NR_ENTRIES>,
514)
515 requires
516 old(owner).children().len() == NR_ENTRIES,
517 new_path.inv(),
518 forall|i: int| 0 <= i < NR_ENTRIES ==> (#[trigger] old(owner).children()[i]) is Some,
519 ensures
520 final(owner).value() == old(owner).value(),
521 final(owner).level() == old(owner).level(),
522 final(owner).children().len() == NR_ENTRIES,
523 forall|i: int|
524 0 <= i < NR_ENTRIES ==> {
525 let c_old = old(owner).child(i);
526 let c_new = (#[trigger] final(owner).children()[i])->0;
527 &&& final(owner).has_child(i)
528 &&& c_new.value() == EntryOwner { path: new_path.push_tail(i), ..c_old.value() }
529 &&& c_new.level() == c_old.level()
530 &&& c_new.children() == c_old.children()
531 },
532{
533 rebase_freshly_allocated_children_at(owner, new_path, 0);
534}
535
536pub proof fn fresh_node_subtree_satisfies<C: PageTableConfig>(
545 node: OwnerSubtree<C>,
546 path: TreePath<NR_ENTRIES>,
547 f: spec_fn(EntryOwner<C>, TreePath<NR_ENTRIES>) -> bool,
548)
549 requires
550 node.inv(),
551 node.level() < INC_LEVELS - 1,
552 f(node.value(), path),
553 forall|i: int| 0 <= i < NR_ENTRIES ==> #[trigger] node.has_child(i),
554 forall|i: int, j: int|
555 0 <= i < NR_ENTRIES && 0 <= j < NR_ENTRIES ==> !#[trigger] node.child(i).has_child(j),
556 forall|i: int|
557 0 <= i < NR_ENTRIES ==> #[trigger] f(node.child(i).value(), path.push_tail(i)),
558 ensures
559 node.subtree_satisfies(path, f),
560{
561 assert forall|i: int|
562 0 <= i < node.children().len() && #[trigger] node.has_child(i) implies node.child(
563 i,
564 ).subtree_satisfies(path.push_tail(i), f) by {
565 assert(node.child(i).inv());
568 };
569}
570
571pub tracked struct PageTableOwner<C: PageTableConfig>(pub OwnerSubtree<C>);
578
579impl<C: PageTableConfig> PageTableOwner<C> {
580 pub open spec fn pt_edge_at(parent: OwnerSubtree<C>, i: int) -> bool {
582 &&& parent.has_child(i)
583 &&& parent.child(i).value().path.len() == parent.value().node().tree_level
584 + 1
585 &&& (parent.child(i).value().match_pte(
593 parent.value().node().children_perm.value()[i],
594 parent.value().node().level(),
595 ) || (parent.value().node().level() == NR_LEVELS && C::LEADING_BITS_spec() == 0
596 && parent.child(i).value().borrowed_match_pte(
597 parent.value().node().children_perm.value()[i],
598 parent.value().node().level(),
599 )))
600 &&& parent.child(i).value().path == parent.value().path.push_tail(i)
601 &&& parent.child(i).value().parent_level == parent.value().node().level()
602 }
603
604 #[verifier::opaque]
608 pub open spec fn pt_inv_at_depth(self, depth: nat) -> bool
609 decreases depth,
610 {
611 if depth == 0 {
612 true
613 } else if self.0.value().is_node() {
614 forall|i: int|
615 #![trigger self.0.has_child(i)]
616 0 <= i < NR_ENTRIES ==> Self::pt_edge_at(self.0, i) && PageTableOwner(
617 self.0.child(i),
618 ).pt_inv_at_depth((depth - 1) as nat)
619 } else {
620 forall|i: int|
621 #![trigger self.0.has_child(i)]
622 0 <= i < NR_ENTRIES ==> !self.0.has_child(i)
623 }
624 }
625
626 pub open spec fn pt_inv(self) -> bool {
631 &&& self.0.inv()
632 &&& self.pt_inv_at_depth((INC_LEVELS - self.0.level()) as nat)
633 }
634
635 pub proof fn pt_inv_unroll(self, i: int)
636 requires
637 self.pt_inv(),
638 self.0.value().is_node(),
639 0 <= i < NR_ENTRIES,
640 ensures
641 Self::pt_edge_at(self.0, i),
642 PageTableOwner(self.0.child(i)).pt_inv(),
643 {
644 reveal(PageTableOwner::pt_inv_at_depth);
647 let depth = (INC_LEVELS - self.0.level()) as nat;
648 assert(<EntryOwner<C> as TreeNodeValue<INC_LEVELS>>::la_inv(
649 self.0.value(),
650 self.0.level(),
651 ));
652 }
653
654 pub proof fn pt_inv_non_node(self, i: int)
655 requires
656 self.pt_inv(),
657 !self.0.value().is_node(),
658 0 <= i < NR_ENTRIES,
659 ensures
660 !self.0.has_child(i),
661 {
662 reveal(PageTableOwner::pt_inv_at_depth);
663 }
664
665 pub proof fn non_node_pt_inv_at_depth(self, depth: nat)
669 requires
670 !self.0.value().is_node(),
671 forall|j: int| 0 <= j < NR_ENTRIES ==> !#[trigger] self.0.has_child(j),
672 ensures
673 self.pt_inv_at_depth(depth),
674 decreases depth,
675 {
676 reveal(PageTableOwner::pt_inv_at_depth);
677 }
678
679 #[verifier::spinoff_prover]
688 pub proof fn allocated_empty_node_pt_inv(owner: OwnerSubtree<C>)
689 requires
690 owner.inv(),
691 owner.value().is_node(),
692 owner.children().len() == NR_ENTRIES,
693 forall|i: int|
694 0 <= i < NR_ENTRIES ==> {
695 &&& #[trigger] owner.has_child(i)
696 &&& owner.child(i).value().is_absent()
697 &&& owner.child(i).value().path.len() == owner.value().node().tree_level + 1
698 &&& owner.child(i).value().match_pte(
699 owner.value().node().children_perm.value()[i],
700 owner.value().node().level(),
701 )
702 &&& owner.child(i).value().path == owner.value().path.push_tail(i)
703 &&& owner.child(i).value().parent_level == owner.value().node().level()
704 },
705 allocated_empty_node_grandchildren_none(owner),
706 ensures
707 PageTableOwner(owner).pt_inv(),
708 {
709 reveal(PageTableOwner::pt_inv_at_depth);
710 let depth = (INC_LEVELS - owner.level()) as nat;
711 assert forall|i: int| 0 <= i < NR_ENTRIES implies #[trigger] owner.has_child(i)
715 && Self::pt_edge_at(owner, i) && PageTableOwner(owner.child(i)).pt_inv_at_depth(
716 (depth - 1) as nat,
717 ) by {
718 let child = owner.child(i);
719 assert(owner.has_child(i));
720 PageTableOwner(child).non_node_pt_inv_at_depth((depth - 1) as nat);
721 };
722 }
723
724 pub open spec fn view_rec_node_children(self, path: TreePath<NR_ENTRIES>) -> Seq<Set<Mapping>>
725 decreases INC_LEVELS - path.len(), 0nat,
726 when self.0.inv() && path.len() < INC_LEVELS - 1
727 {
728 self.0.children().map(
729 |i, child: Option<OwnerSubtree<C>>|
730 if child is Some {
731 PageTableOwner(child->0).view_rec(path.push_tail(i))
732 } else {
733 Set::empty()
734 },
735 )
736 }
737
738 pub open spec fn view_rec(self, path: TreePath<NR_ENTRIES>) -> Set<Mapping>
739 decreases INC_LEVELS - path.len(), 1nat,
740 when self.0.inv() && path.len() <= INC_LEVELS - 1
741 {
742 if self.0.value().is_frame() {
743 let va = vaddr_of::<C>(path);
744 let pt_level = INC_LEVELS - path.len();
745 let page_size = page_size::<PagingConsts>(pt_level as PagingLevel);
746
747 set![Mapping {
748 va_range: Range { start: va as int, end: va + page_size },
749 pa_range: Range {
750 start: self.0.value().frame().mapped_pa,
751 end: (self.0.value().frame().mapped_pa + page_size) as Paddr,
752 },
753 page_size: page_size,
754 property: self.0.value().frame().prop,
755 }]
756 } else if self.0.value().is_node() && path.len() < INC_LEVELS - 1 {
757 self.view_rec_node_children(path).to_set().flatten()
758 } else {
759 set![]
760 }
761 }
762
763 pub broadcast proof fn lemma_view_rec_contains_intro(
764 self,
765 path: TreePath<NR_ENTRIES>,
766 m: Mapping,
767 i: int,
768 )
769 requires
770 self.0.inv(),
771 path.len() < INC_LEVELS - 1,
772 self.0.value().is_node(),
773 0 <= i < self.0.children().len(),
774 self.0.has_child(i),
775 #[trigger] PageTableOwner(self.0.children()[i]->0).view_rec(path.push_tail(i)).contains(
776 m,
777 ),
778 ensures
779 self.view_rec(path).contains(m),
780 {
781 broadcast use vstd::seq_lib::group_seq_properties;
782
783 let mapped = self.view_rec_node_children(path);
784 assert(mapped.to_set().contains(mapped[i]));
785 }
786
787 pub broadcast proof fn lemma_view_rec_contains(self, path: TreePath<NR_ENTRIES>)
788 requires
789 self.0.inv(),
790 path.len() < INC_LEVELS - 1,
791 self.0.value().is_node(),
792 ensures
793 #![trigger self.view_rec(path)]
794 forall|m: Mapping|
795 #![trigger self.view_rec(path).contains(m)]
796 self.view_rec(path).contains(m) ==> exists|i: int|
797 #![trigger self.0.children()[i]]
798 0 <= i < self.0.children().len() && self.0.children()[i] is Some
799 && PageTableOwner(self.0.children()[i]->0).view_rec(
800 path.push_tail(i),
801 ).contains(m),
802 {
803 broadcast use vstd::seq_lib::group_seq_properties;
804
805 }
806
807 pub proof fn view_rec_contains_choose(self, path: TreePath<NR_ENTRIES>, m: Mapping) -> (i: int)
808 requires
809 self.0.inv(),
810 path.len() < INC_LEVELS - 1,
811 self.view_rec(path).contains(m),
812 self.0.value().is_node(),
813 ensures
814 0 <= i < self.0.children().len() && self.0.children()[i] is Some && PageTableOwner(
815 self.0.children()[i]->0,
816 ).view_rec(path.push_tail(i)).contains(m),
817 {
818 broadcast use PageTableOwner::group_lemmas;
819
820 choose|i: int|
821 #![auto]
822 0 <= i < self.0.children().len() && self.0.children()[i] is Some && PageTableOwner(
823 self.0.children()[i].unwrap(),
824 ).view_rec(path.push_tail(i)).contains(m)
825 }
826
827 #[verifier::spinoff_prover]
829 #[verifier::rlimit(200)]
830 pub proof fn lemma_vaddr_push_tail_eq(path: TreePath<NR_ENTRIES>, i: int)
831 requires
832 path.inv(),
833 path.len() < INC_LEVELS - 1,
834 0 <= i < NR_ENTRIES,
835 ensures
836 vaddr(path.push_tail(i)) == vaddr(path) + i * page_size::<PagingConsts>(
837 (INC_LEVELS - path.len() - 1) as PagingLevel,
838 ),
839 vaddr(path) + (i + 1) * page_size::<PagingConsts>(
840 (INC_LEVELS - path.len() - 1) as PagingLevel,
841 ) <= usize::MAX,
842 {
843 broadcast use {
844 TreePath::lemma_push_tail_len,
845 TreePath::lemma_push_tail_preserves_inv,
846 TreePath::lemma_index_satisfies_elem_inv,
847 };
848
849 lemma_page_size_spec_values();
850 lemma_vaddr_strict_bound(path);
851 vstd::arithmetic::power2::lemma2_to64();
852 vstd::arithmetic::power2::lemma2_to64_rest();
853 let pt = path.push_tail(i);
854 if path.len() >= 1 {
855 }
856 if path.len() == 0 {
857 assert(rec_vaddr(pt, 1) == 0);
858 assert(vaddr_make::<NR_LEVELS>(0, i as usize) == 0x80_0000_0000usize * i) by (compute);
859 assert(0x80_0000_0000usize * (i + 1) <= usize::MAX) by (nonlinear_arith)
860 requires
861 i < 512,
862 ;
863 } else if path.len() == 1 {
864 let i0 = path[0];
865 assert(vaddr_make::<NR_LEVELS>(0, i0 as usize) == 0x80_0000_0000usize * i0);
866 assert(rec_vaddr(pt, 2) == 0);
867 } else if path.len() == 2 {
868 let i0 = path[0];
869 let i1 = path[1];
870 assert(rec_vaddr(path, 2) == 0);
871 assert(rec_vaddr(path, 1) == vaddr_make::<NR_LEVELS>(1, i1 as usize) as usize);
872 assert(rec_vaddr(pt, 3) == 0);
873 assert(rec_vaddr(pt, 2) == vaddr_make::<NR_LEVELS>(2, i as usize) as usize);
874 assert(rec_vaddr(pt, 1) == (vaddr_make::<NR_LEVELS>(1, i1 as usize) + vaddr_make::<
875 NR_LEVELS,
876 >(2, i as usize)) as usize);
877 } else {
878 let i0 = path[0];
879 let i1 = path[1];
880 let i2 = path[2];
881 assert(rec_vaddr(path, 3) == 0);
882 assert(rec_vaddr(path, 2) == vaddr_make::<NR_LEVELS>(2, i2 as usize) as usize);
883 assert(rec_vaddr(path, 1) == (vaddr_make::<NR_LEVELS>(1, i1 as usize) + vaddr_make::<
884 NR_LEVELS,
885 >(2, i2 as usize)) as usize);
886 assert(vaddr_make::<NR_LEVELS>(1, i1 as usize) == 0x4000_0000usize * i1) by (compute);
887 assert(rec_vaddr(pt, 4) == 0);
888 assert(rec_vaddr(pt, 3) == vaddr_make::<NR_LEVELS>(3, i as usize) as usize);
889 assert(rec_vaddr(pt, 2) == (vaddr_make::<NR_LEVELS>(2, i2 as usize) + vaddr_make::<
890 NR_LEVELS,
891 >(3, i as usize)) as usize);
892 assert(rec_vaddr(pt, 1) == (vaddr_make::<NR_LEVELS>(1, i1 as usize) + vaddr_make::<
893 NR_LEVELS,
894 >(2, i2 as usize) + vaddr_make::<NR_LEVELS>(3, i as usize)) as usize);
895 assert(vaddr_make::<NR_LEVELS>(3, i as usize) == 0x1000usize * i) by (compute);
896 assert(0x80_0000_0000usize * i0 + 0x4000_0000usize * i1 + 0x20_0000usize * i2
897 + 0x1000usize * (i + 1) <= usize::MAX) by (nonlinear_arith)
898 requires
899 i0 < 512,
900 i1 < 512,
901 i2 < 512,
902 i < 512,
903 ;
904 }
905 }
906
907 pub proof fn view_rec_vaddr_range(self, path: TreePath<NR_ENTRIES>, m: Mapping)
908 requires
909 self.pt_inv(),
910 path.inv(),
911 path.len() <= INC_LEVELS - 1,
912 path.len() == self.0.level(),
913 self.0.value().parent_level == (INC_LEVELS - self.0.level()) as PagingLevel,
914 self.view_rec(path).contains(m),
915 ensures
916 vaddr_of::<C>(path) <= m.va_range.start,
917 m.va_range.start < m.va_range.end,
918 m.va_range.end <= vaddr_of::<C>(path) + page_size::<PagingConsts>(
919 (INC_LEVELS - path.len()) as PagingLevel,
920 ),
921 decreases INC_LEVELS - path.len(),
922 {
923 broadcast use PageTableOwner::group_lemmas;
924
925 lemma_page_size_spec_values();
926 if self.0.value().is_frame() {
927 let frame = self.0.value().frame();
928 let pt_level = (INC_LEVELS - path.len()) as PagingLevel;
929 let expected = Mapping {
930 va_range: Range {
931 start: vaddr_of::<C>(path) as int,
932 end: vaddr_of::<C>(path) + page_size::<PagingConsts>(pt_level),
933 },
934 pa_range: Range {
935 start: frame.mapped_pa,
936 end: (frame.mapped_pa + page_size::<PagingConsts>(pt_level)) as Paddr,
937 },
938 page_size: page_size::<PagingConsts>(pt_level),
939 property: frame.prop,
940 };
941 } else if self.0.value().is_node() && path.len() < INC_LEVELS - 1 {
942 let i = choose|i: int|
943 #![trigger self.0.children()[i]]
944 0 <= i < self.0.children().len() && self.0.children()[i] is Some && PageTableOwner(
945 self.0.children()[i].unwrap(),
946 ).view_rec(path.push_tail(i)).contains(m);
947 self.pt_inv_unroll(i);
948 let child = PageTableOwner(self.0.children()[i].unwrap());
949 child.view_rec_vaddr_range(path.push_tail(i), m);
950 Self::lemma_vaddr_push_tail_eq(path, i);
951
952 let child_ps = page_size::<PagingConsts>(
953 (INC_LEVELS - path.len() - 1) as PagingLevel,
954 ) as int;
955 assert((i + 1) * child_ps <= 512 * child_ps) by (nonlinear_arith)
956 requires
957 0 <= i < 512,
958 child_ps >= 0,
959 ;
960 lemma_vaddr_of_eq_int::<C>(path.push_tail(i));
961 assert(i * child_ps + child_ps == (i + 1) * child_ps) by (nonlinear_arith);
962 }
963 }
964
965 pub proof fn view_rec_top_index_va_bound(self, path: TreePath<NR_ENTRIES>, m: Mapping, t: int)
971 requires
972 self.pt_inv(),
973 path.inv(),
974 1 <= path.len() <= INC_LEVELS - 1,
975 path.len() == self.0.level(),
976 self.0.value().parent_level == (INC_LEVELS - self.0.level()) as PagingLevel,
977 path[0] < t,
978 self.view_rec(path).contains(m),
979 ensures
980 (path[0]) * 0x80_0000_0000int + C::LEADING_BITS_spec() * 0x1_0000_0000_0000int
981 <= m.va_range.start,
982 m.va_range.start < m.va_range.end,
983 m.va_range.end <= t * 0x80_0000_0000int + C::LEADING_BITS_spec()
984 * 0x1_0000_0000_0000int,
985 {
986 self.view_rec_vaddr_range(path, m);
987 lemma_vaddr_of_eq_int::<C>(path);
988 lemma_vaddr_top_index_cell(path);
989 }
996
997 pub proof fn pt_inv_implies_path_correct(subtree: OwnerSubtree<C>, path: TreePath<NR_ENTRIES>)
1005 requires
1006 PageTableOwner(subtree).pt_inv(),
1007 subtree.value().path == path,
1008 ensures
1009 subtree.subtree_satisfies(path, Self::path_correct_pred()),
1010 decreases INC_LEVELS - subtree.level(),
1011 {
1012 if subtree.level() < INC_LEVELS - 1 {
1014 assert forall|i: int|
1015 0 <= i < subtree.children().len() && (
1016 #[trigger] subtree.children()[i]) is Some implies subtree.children()[i].unwrap().subtree_satisfies(
1017 path.push_tail(i), Self::path_correct_pred()) by {
1018 if subtree.value().is_node() {
1019 PageTableOwner(subtree).pt_inv_unroll(i);
1020 Self::pt_inv_implies_path_correct(
1022 subtree.children()[i].unwrap(),
1023 path.push_tail(i),
1024 );
1025 } else {
1026 PageTableOwner(subtree).pt_inv_non_node(i);
1028 }
1029 };
1030 }
1031 }
1032
1033 pub proof fn no_frame_with_path_rec(
1043 self,
1044 path: TreePath<NR_ENTRIES>,
1045 removed_path: TreePath<NR_ENTRIES>,
1046 ambient: Set<Mapping>,
1047 )
1048 requires
1049 self.pt_inv(),
1050 path.inv(),
1051 path.len() <= INC_LEVELS - 1,
1052 path.len() == self.0.level(),
1053 self.0.value().parent_level == (INC_LEVELS - self.0.level()) as PagingLevel,
1054 self.0.value().path == path,
1055 self.0.subtree_satisfies(path, Self::path_correct_pred()),
1056 forall|mm: Mapping|
1057 #![trigger self.view_rec(path).contains(mm)]
1058 self.view_rec(path).contains(mm) ==> ambient.contains(mm),
1059 forall|mm: Mapping|
1060 #![trigger ambient.contains(mm)]
1061 ambient.contains(mm) ==> mm.va_range.start != vaddr_of::<C>(removed_path),
1062 ensures
1063 self.0.subtree_satisfies(
1064 path,
1065 |e: EntryOwner<C>, _p: TreePath<NR_ENTRIES>|
1066 e.is_frame() ==> e.path != removed_path,
1067 ),
1068 decreases INC_LEVELS - path.len(),
1069 {
1070 broadcast use PageTableOwner::group_lemmas;
1071
1072 let g = |e: EntryOwner<C>, _p: TreePath<NR_ENTRIES>|
1073 e.is_frame() ==> e.path != removed_path;
1074
1075 if self.0.value().is_frame() {
1077 let frame = self.0.value().frame();
1078 let pt_level = (INC_LEVELS - path.len()) as PagingLevel;
1079 let expected = Mapping {
1080 va_range: Range {
1081 start: vaddr_of::<C>(path) as int,
1082 end: vaddr_of::<C>(path) + page_size::<PagingConsts>(pt_level),
1083 },
1084 pa_range: Range {
1085 start: frame.mapped_pa,
1086 end: (frame.mapped_pa + page_size::<PagingConsts>(pt_level)) as Paddr,
1087 },
1088 page_size: page_size::<PagingConsts>(pt_level),
1089 property: frame.prop,
1090 };
1091 assert(self.view_rec(path).contains(expected));
1092
1093 }
1094 if self.0.level() < INC_LEVELS - 1 {
1095 assert forall|i: int|
1096 0 <= i < self.0.children().len() && (
1097 #[trigger] self.0.children()[i]) is Some implies self.0.children()[i].unwrap().subtree_satisfies(
1098 path.push_tail(i), g) by {
1099 if self.0.value().is_node() {
1100 self.pt_inv_unroll(i);
1101 let child = PageTableOwner(self.0.children()[i].unwrap());
1102 child.no_frame_with_path_rec(path.push_tail(i), removed_path, ambient);
1105 } else {
1106 PageTableOwner(self.0).pt_inv_non_node(i);
1107 }
1108 };
1109 }
1110 }
1111
1112 pub proof fn view_rec_disjoint_vaddrs(
1113 self,
1114 path: TreePath<NR_ENTRIES>,
1115 m1: Mapping,
1116 m2: Mapping,
1117 )
1118 requires
1119 self.pt_inv(),
1120 path.inv(),
1121 path.len() <= INC_LEVELS - 1,
1122 path.len() == self.0.level(),
1123 self.0.value().parent_level == (INC_LEVELS - self.0.level()) as PagingLevel,
1124 self.view_rec(path).contains(m1),
1125 self.view_rec(path).contains(m2),
1126 m1 != m2,
1127 ensures
1128 m1.va_range.end <= m2.va_range.start || m2.va_range.end <= m1.va_range.start,
1129 decreases INC_LEVELS - path.len(),
1130 {
1131 broadcast use PageTableOwner::group_lemmas;
1132 broadcast use group_set_properties;
1133
1134 if self.0.value().is_frame() {
1135 } else if self.0.value().is_node() {
1136 let i1 = self.view_rec_contains_choose(path, m1);
1137 let i2 = self.view_rec_contains_choose(path, m2);
1138
1139 if i1 == i2 {
1140 self.pt_inv_unroll(i1);
1141 PageTableOwner(self.0.children()[i1].unwrap()).view_rec_disjoint_vaddrs(
1142 path.push_tail(i1),
1143 m1,
1144 m2,
1145 );
1146 } else {
1147 self.pt_inv_unroll(i1);
1148 self.pt_inv_unroll(i2);
1149 let child_ps = page_size::<PagingConsts>(
1150 (INC_LEVELS - path.len() - 1) as PagingLevel,
1151 );
1152 PageTableOwner(self.0.children()[i1].unwrap()).view_rec_vaddr_range(
1153 path.push_tail(i1),
1154 m1,
1155 );
1156 PageTableOwner(self.0.children()[i2].unwrap()).view_rec_vaddr_range(
1157 path.push_tail(i2),
1158 m2,
1159 );
1160 if i1 < i2 {
1161 sibling_paths_disjoint::<C>(path, i1, i2, child_ps);
1162 } else {
1163 sibling_paths_disjoint::<C>(path, i2, i1, child_ps);
1164 }
1165 lemma_vaddr_of_eq_int::<C>(path.push_tail(i1));
1169 lemma_vaddr_of_eq_int::<C>(path.push_tail(i2));
1170 }
1171 }
1172 }
1173
1174 pub proof fn view_rec_mapping_page_size(self, path: TreePath<NR_ENTRIES>)
1181 requires
1182 self.pt_inv(),
1183 path.len() <= INC_LEVELS - 1,
1184 path.len() == self.0.level(),
1185 self.0.value().parent_level == (INC_LEVELS - self.0.level()) as PagingLevel,
1186 ensures
1187 forall|m: Mapping| #[trigger]
1188 self.view_rec(path).contains(m)
1189 ==> set![4096usize, 2097152usize, 1073741824usize].contains(m.page_size),
1190 decreases INC_LEVELS - path.len(),
1191 {
1192 broadcast use PageTableOwner::group_lemmas;
1193
1194 if self.0.value().is_frame() {
1195 lemma_page_size_spec_values();
1196 } else if self.0.value().is_node() && path.len() < INC_LEVELS - 1 {
1197 assert forall|m: Mapping| #[trigger]
1198 self.view_rec(path).contains(
1199 m,
1200 ) implies set![4096usize, 2097152usize, 1073741824usize].contains(m.page_size) by {
1201 let i = choose|i: int|
1202 #![trigger self.0.children()[i]]
1203 0 <= i < self.0.children().len() && self.0.children()[i] is Some
1204 && PageTableOwner(self.0.children()[i].unwrap()).view_rec(
1205 path.push_tail(i),
1206 ).contains(m);
1207 self.pt_inv_unroll(i);
1208 let child = self.0.children()[i].unwrap();
1209 PageTableOwner(child).view_rec_mapping_page_size(path.push_tail(i));
1210 };
1211 }
1212 }
1213
1214 #[verifier::spinoff_prover]
1221 #[verifier::rlimit(200)]
1222 proof fn lemma_vaddr_path_alignment_and_bound(path: TreePath<NR_ENTRIES>)
1223 requires
1224 path.inv(),
1225 path.len() <= INC_LEVELS - 1,
1226 1 <= INC_LEVELS - path.len() <= NR_LEVELS,
1227 ensures
1228 vaddr(path) % page_size::<PagingConsts>((INC_LEVELS - path.len()) as PagingLevel) == 0,
1229 vaddr(path) + page_size::<PagingConsts>((INC_LEVELS - path.len()) as PagingLevel)
1230 <= usize::MAX,
1231 {
1232 lemma_page_size_spec_values();
1233 vstd::arithmetic::power2::lemma2_to64();
1234 vstd::arithmetic::power2::lemma2_to64_rest();
1235 broadcast use TreePath::lemma_index_satisfies_elem_inv;
1236 if path.len() == 0 {
1248 assert(rec_vaddr(path, 0) == 0);
1249 } else if path.len() == 1 {
1250 let i0 = path[0];
1251 assert(rec_vaddr(path, 1) == 0);
1252 assert(rec_vaddr(path, 0) == (vaddr_make::<NR_LEVELS>(0, i0 as usize) + rec_vaddr(
1253 path,
1254 1,
1255 )) as usize);
1256 } else if path.len() == 2 {
1257 let i0 = path[0];
1258 let i1 = path[1];
1259 assert(rec_vaddr(path, 2) == 0);
1260 assert(rec_vaddr(path, 1) == (vaddr_make::<NR_LEVELS>(1, i1 as usize) + rec_vaddr(
1261 path,
1262 2,
1263 )) as usize);
1264 let s = 0x80_0000_0000usize * i0 + 0x4000_0000usize * i1;
1265 } else if path.len() == 3 {
1266 let i0 = path[0];
1267 let i1 = path[1];
1268 let i2 = path[2];
1269 assert(rec_vaddr(path, 3) == 0);
1270 assert(rec_vaddr(path, 2) == (vaddr_make::<NR_LEVELS>(2, i2 as usize) + rec_vaddr(
1271 path,
1272 3,
1273 )) as usize);
1274 assert(rec_vaddr(path, 0) == (vaddr_make::<NR_LEVELS>(0, i0 as usize) + rec_vaddr(
1275 path,
1276 1,
1277 )) as usize);
1278 let s = 0x80_0000_0000usize * i0 + 0x4000_0000usize * i1 + 0x20_0000usize * i2;
1279 assert(rec_vaddr(path, 0) == s);
1280 assert(s % 0x20_0000 == 0) by (nonlinear_arith)
1281 requires
1282 s == 0x80_0000_0000 * i0 + 0x4000_0000 * i1 + 0x20_0000 * i2,
1283 ;
1284 } else {
1285 assert(path.len() == 4);
1286 let i0 = path[0];
1287 let i1 = path[1];
1288 let i2 = path[2];
1289 let i3 = path[3];
1290 assert(rec_vaddr(path, 4) == 0);
1291 assert(rec_vaddr(path, 3) == (vaddr_make::<NR_LEVELS>(3, i3 as usize) + rec_vaddr(
1292 path,
1293 4,
1294 )) as usize);
1295 assert(rec_vaddr(path, 1) == (vaddr_make::<NR_LEVELS>(1, i1 as usize) + rec_vaddr(
1296 path,
1297 2,
1298 )) as usize);
1299 assert(rec_vaddr(path, 0) == (vaddr_make::<NR_LEVELS>(0, i0 as usize) + rec_vaddr(
1300 path,
1301 1,
1302 )) as usize);
1303 let s = (0x80_0000_0000usize * i0 + 0x4000_0000usize * i1 + 0x20_0000usize * i2
1304 + 0x1000usize * i3) as int;
1305 assert(s + 0x1000 <= usize::MAX) by (nonlinear_arith)
1306 requires
1307 s == 0x80_0000_0000 * i0 + 0x4000_0000 * i1 + 0x20_0000 * i2 + 0x1000 * i3,
1308 i0 < 512,
1309 i1 < 512,
1310 i2 < 512,
1311 i3 < 512,
1312 ;
1313 }
1314 }
1315
1316 pub proof fn view_rec_mapping_inv(self, path: TreePath<NR_ENTRIES>)
1325 requires
1326 self.pt_inv(),
1327 path.inv(),
1328 path.len() <= INC_LEVELS - 1,
1329 path.len() == self.0.level(),
1330 self.0.value().parent_level == (INC_LEVELS - self.0.level()) as PagingLevel,
1331 ensures
1332 forall|m: Mapping| #[trigger] self.view_rec(path).contains(m) ==> m.inv(),
1333 decreases INC_LEVELS - path.len(),
1334 {
1335 broadcast use PageTableOwner::group_lemmas;
1336
1337 if self.0.value().is_frame() {
1338 lemma_page_size_spec_values();
1339 let frame = self.0.value().frame();
1340 let pt_level = (INC_LEVELS - path.len()) as PagingLevel;
1341 Self::lemma_vaddr_path_alignment_and_bound(path);
1342 let m = Mapping {
1343 va_range: Range {
1344 start: vaddr_of::<C>(path) as int,
1345 end: vaddr_of::<C>(path) + page_size::<PagingConsts>(pt_level),
1346 },
1347 pa_range: Range {
1348 start: frame.mapped_pa,
1349 end: (frame.mapped_pa + page_size::<PagingConsts>(pt_level)) as Paddr,
1350 },
1351 page_size: page_size::<PagingConsts>(pt_level),
1352 property: frame.prop,
1353 };
1354
1355 let ps = page_size::<PagingConsts>(pt_level) as int;
1356 vstd_extra::arithmetic::lemma_mod_0_add(frame.mapped_pa as int, ps, ps);
1357 lemma_vaddr_of_eq_int::<C>(path);
1358 C::lemma_page_table_config_constant_properties();
1359 lemma_vaddr_strict_bound(path);
1360 let lb = C::LEADING_BITS_spec() as int;
1361 vstd::arithmetic::power2::lemma2_to64_rest();
1362 assert(lb * 0x1_0000_0000_0000int % ps == 0) by (nonlinear_arith)
1366 requires
1367 lb >= 0,
1368 (ps == 0x1000int || ps == 0x20_0000int || ps == 0x4000_0000int),
1369 ;
1370 vstd::arithmetic::div_mod::lemma_mod_adds(
1371 vaddr(path) as int,
1372 lb * 0x1_0000_0000_0000int,
1373 ps,
1374 );
1375 let v = vaddr(path) as int;
1378 let limit = 0x1_0000_0000_0000int;
1379 assert(limit % ps == 0) by {
1380 if ps == 0x1000int {
1381 assert(0x1_0000_0000_0000int % 0x1000int == 0) by (compute_only);
1382 } else if ps == 0x20_0000int {
1383 assert(0x1_0000_0000_0000int % 0x20_0000int == 0) by (compute_only);
1384 } else {
1385 assert(0x1_0000_0000_0000int % 0x4000_0000int == 0) by (compute_only);
1386 }
1387 };
1388 vstd::arithmetic::div_mod::lemma_mod_equivalence(limit, v, ps);
1389 let diff = limit - v;
1390 let q = diff / ps;
1391 vstd::arithmetic::div_mod::lemma_fundamental_div_mod(diff, ps);
1392 assert(q >= 1) by (nonlinear_arith)
1393 requires
1394 diff > 0,
1395 ps > 0,
1396 diff == ps * q,
1397 ;
1398 vstd::arithmetic::mul::lemma_mul_inequality(1, q, ps);
1399 vstd::arithmetic::mul::lemma_mul_inequality(lb, 0xffffint, 0x1_0000_0000_0000int);
1400 assert(v + ps <= limit) by (nonlinear_arith)
1401 requires
1402 q >= 1,
1403 ps > 0,
1404 diff == ps * q,
1405 diff == limit - v,
1406 ;
1407 assert(0xffffint * 0x1_0000_0000_0000int + 0x1_0000_0000_0000int
1408 == 0x1_0000_0000_0000_0000int) by (compute_only);
1409 assert(lb * limit + v + ps <= 0x1_0000_0000_0000_0000int);
1410 vstd_extra::arithmetic::lemma_mod_0_add(m.va_range.start, ps, ps);
1411 assert(set![4096, 2097152, 1073741824].contains(m.page_size));
1412 assert(m.pa_range.start <= m.pa_range.end <= MAX_PADDR);
1413 assert forall|m2: Mapping| #[trigger]
1414 self.view_rec(path).contains(m2) implies m2.inv() by {};
1415 } else if self.0.value().is_node() && path.len() < INC_LEVELS - 1 {
1416 assert forall|m: Mapping| #[trigger]
1417 self.view_rec(path).contains(m) implies m.inv() by {
1418 let i = choose|i: int|
1419 #![trigger self.0.children()[i]]
1420 0 <= i < self.0.children().len() && self.0.children()[i] is Some
1421 && PageTableOwner(self.0.children()[i].unwrap()).view_rec(
1422 path.push_tail(i),
1423 ).contains(m);
1424 self.pt_inv_unroll(i);
1425 let child = self.0.children()[i].unwrap();
1426 PageTableOwner(child).view_rec_mapping_inv(path.push_tail(i));
1427 };
1428 }
1429 }
1430
1431 pub proof fn view_rec_absent_empty(self, path: TreePath<NR_ENTRIES>)
1433 requires
1434 self.0.inv(),
1435 self.0.value().is_absent(),
1436 path.len() <= INC_LEVELS - 1,
1437 ensures
1438 self.view_rec(path) == set![],
1439 {
1440 }
1441
1442 pub proof fn view_rec_nr_children_zero_empty(self, path: TreePath<NR_ENTRIES>)
1453 requires
1454 self.pt_inv(),
1455 self.0.value().is_node(),
1456 self.0.value().node().meta_own.nr_children.value() == 0,
1457 self.0.value().node().count_consistent(),
1458 path.len() <= INC_LEVELS - 1,
1459 path.len() == self.0.level(),
1460 ensures
1461 self.view_rec(path) == set![],
1462 {
1463 if path.len() < INC_LEVELS - 1 {
1464 let cp = self.0.value().node().children_perm.value();
1465 self.lemma_view_rec_contains(path);
1467 assert forall|m: Mapping| self.view_rec(path).contains(m) implies false by {
1468 let i = choose|i: int|
1469 #![trigger self.0.children()[i]]
1470 0 <= i < self.0.children().len() && self.0.children()[i] is Some
1471 && PageTableOwner(self.0.children()[i]->0).view_rec(
1472 path.push_tail(i),
1473 ).contains(m);
1474 self.pt_inv_unroll(i);
1475 if cp[i].is_present() {
1477 crate::specs::mm::page_table::node::owners::lemma_count_present_upto_present(
1478 cp,
1479 cp.len() as int,
1480 i,
1481 );
1482 }
1483 };
1489 }
1490 }
1491
1492 pub open spec fn metaregion_sound_pred(regions: MetaRegionOwners) -> (spec_fn(
1493 EntryOwner<C>,
1494 TreePath<NR_ENTRIES>,
1495 ) -> bool) {
1496 |entry: EntryOwner<C>, path: TreePath<NR_ENTRIES>| entry.metaregion_sound(regions)
1497 }
1498
1499 pub open spec fn metaregion_sound(self, regions: MetaRegionOwners) -> bool
1500 decreases INC_LEVELS - self.0.level(),
1501 when self.0.inv()
1502 {
1503 self.0.subtree_satisfies(self.0.value().path, Self::metaregion_sound_pred(regions))
1504 }
1505
1506 pub proof fn metaregion_sound_preserved_slot_owners_eq(
1511 self,
1512 r0: MetaRegionOwners,
1513 r1: MetaRegionOwners,
1514 )
1515 requires
1516 self.inv(),
1517 self.metaregion_sound(r0),
1518 r0.slot_owners == r1.slot_owners,
1519 forall|k: int| r0.slots.contains_key(k) ==> #[trigger] r1.slots.contains_key(k),
1520 forall|k: int| r0.slots.contains_key(k) ==> r0.slots[k] == #[trigger] r1.slots[k],
1521 ensures
1522 self.metaregion_sound(r1),
1523 {
1524 Self::metaregion_sound_preserved_slot_owners_eq_subtree(
1525 self.0,
1526 self.0.value().path,
1527 r0,
1528 r1,
1529 );
1530 }
1531
1532 pub proof fn metaregion_sound_preserved_slot_owners_eq_subtree(
1534 subtree: OwnerSubtree<C>,
1535 path: TreePath<NR_ENTRIES>,
1536 r0: MetaRegionOwners,
1537 r1: MetaRegionOwners,
1538 )
1539 requires
1540 subtree.inv(),
1541 subtree.subtree_satisfies(path, Self::metaregion_sound_pred(r0)),
1542 r0.slot_owners == r1.slot_owners,
1543 forall|k: int| r0.slots.contains_key(k) ==> #[trigger] r1.slots.contains_key(k),
1544 forall|k: int| r0.slots.contains_key(k) ==> r0.slots[k] == #[trigger] r1.slots[k],
1545 ensures
1546 subtree.subtree_satisfies(path, Self::metaregion_sound_pred(r1)),
1547 decreases INC_LEVELS - subtree.level(),
1548 {
1549 subtree.value().metaregion_sound_slot_owners_only(r0, r1);
1550 if subtree.level() < INC_LEVELS - 1 {
1552 assert forall|i: int|
1553 #![trigger subtree.has_child(i)]
1554 0 <= i < NR_ENTRIES && subtree.has_child(i) implies subtree.child(
1555 i,
1556 ).subtree_satisfies(path.push_tail(i), Self::metaregion_sound_pred(r1)) by {
1557 Self::metaregion_sound_preserved_slot_owners_eq_subtree(
1558 subtree.child(i),
1559 path.push_tail(i),
1560 r0,
1561 r1,
1562 );
1563 }
1564 }
1565 }
1566
1567 pub proof fn metaregion_sound_preserved_one_slot_changed(
1573 self,
1574 r0: MetaRegionOwners,
1575 r1: MetaRegionOwners,
1576 changed_idx: int,
1577 )
1578 requires
1579 self.inv(),
1580 self.metaregion_sound(r0),
1581 forall|i: int|
1582 #![trigger r1.slot_owners[i]]
1583 i != changed_idx ==> r0.slot_owners[i] == r1.slot_owners[i],
1584 r0.slot_owners.dom() == r1.slot_owners.dom(),
1585 forall|k: int| r0.slots.contains_key(k) ==> #[trigger] r1.slots.contains_key(k),
1586 forall|k: int| r0.slots.contains_key(k) ==> r0.slots[k] == #[trigger] r1.slots[k],
1587 self.0.subtree_satisfies(
1589 self.0.value().path,
1590 |e: EntryOwner<C>, p: TreePath<NR_ENTRIES>|
1591 e.meta_slot_paddr() is Some ==> frame_to_index(e.meta_slot_paddr()->0)
1592 != changed_idx,
1593 ),
1594 self.0.subtree_satisfies(
1598 self.0.value().path,
1599 |e: EntryOwner<C>, p: TreePath<NR_ENTRIES>|
1600 e.is_frame() && e.parent_level > 1 ==> {
1601 let pa = e.frame().mapped_pa;
1602 let nr_pages = page_size::<PagingConsts>(e.parent_level) / PAGE_SIZE;
1603 forall|j: usize|
1604 0 < j < nr_pages ==> {
1605 let sub_idx = #[trigger] frame_to_index(
1606 (pa + j * PAGE_SIZE) as usize,
1607 );
1608 sub_idx != changed_idx || (r1.slots.contains_key(sub_idx)
1609 && r1.ref_count(sub_idx) != REF_COUNT_UNUSED && r1.ref_count(
1610 sub_idx,
1611 ) > 0 && r1.ref_count(sub_idx) <= REF_COUNT_MAX)
1612 }
1613 },
1614 ),
1615 ensures
1616 self.metaregion_sound(r1),
1617 {
1618 Self::metaregion_sound_preserved_one_slot_changed_subtree(
1619 self.0,
1620 self.0.value().path,
1621 r0,
1622 r1,
1623 changed_idx,
1624 );
1625 }
1626
1627 pub proof fn metaregion_sound_preserved_one_slot_changed_subtree(
1628 subtree: OwnerSubtree<C>,
1629 path: TreePath<NR_ENTRIES>,
1630 r0: MetaRegionOwners,
1631 r1: MetaRegionOwners,
1632 changed_idx: int,
1633 )
1634 requires
1635 subtree.inv(),
1636 subtree.subtree_satisfies(path, Self::metaregion_sound_pred(r0)),
1637 forall|i: int|
1638 #![trigger r1.slot_owners[i]]
1639 i != changed_idx ==> r0.slot_owners[i] == r1.slot_owners[i],
1640 r0.slot_owners.dom() == r1.slot_owners.dom(),
1641 forall|k: int| r0.slots.contains_key(k) ==> #[trigger] r1.slots.contains_key(k),
1642 forall|k: int| r0.slots.contains_key(k) ==> r0.slots[k] == #[trigger] r1.slots[k],
1643 subtree.subtree_satisfies(
1644 path,
1645 |e: EntryOwner<C>, p: TreePath<NR_ENTRIES>|
1646 e.meta_slot_paddr() is Some ==> frame_to_index(e.meta_slot_paddr()->0)
1647 != changed_idx,
1648 ),
1649 subtree.subtree_satisfies(
1650 path,
1651 |e: EntryOwner<C>, p: TreePath<NR_ENTRIES>|
1652 e.is_frame() && e.parent_level > 1 ==> {
1653 let pa = e.frame().mapped_pa;
1654 let nr_pages = page_size::<PagingConsts>(e.parent_level) / PAGE_SIZE;
1655 forall|j: usize|
1656 0 < j < nr_pages ==> {
1657 let sub_idx = #[trigger] frame_to_index(
1658 (pa + j * PAGE_SIZE) as usize,
1659 );
1660 sub_idx != changed_idx || (r1.slots.contains_key(sub_idx)
1661 && r1.ref_count(sub_idx) != REF_COUNT_UNUSED && r1.ref_count(
1662 sub_idx,
1663 ) > 0 && r1.ref_count(sub_idx) <= REF_COUNT_MAX)
1664 }
1665 },
1666 ),
1667 ensures
1668 subtree.subtree_satisfies(path, Self::metaregion_sound_pred(r1)),
1669 decreases INC_LEVELS - subtree.level(),
1670 {
1671 subtree.value().metaregion_sound_one_slot_changed(r0, r1, changed_idx);
1672 if subtree.level() < INC_LEVELS - 1 {
1673 assert forall|i: int|
1674 #![trigger subtree.has_child(i)]
1675 0 <= i < NR_ENTRIES && subtree.has_child(i) implies subtree.child(
1676 i,
1677 ).subtree_satisfies(path.push_tail(i), Self::metaregion_sound_pred(r1)) by {
1678 Self::metaregion_sound_preserved_one_slot_changed_subtree(
1679 subtree.child(i),
1680 path.push_tail(i),
1681 r0,
1682 r1,
1683 changed_idx,
1684 );
1685 }
1686 }
1687 }
1688
1689 pub open spec fn path_tracked_pred(regions: MetaRegionOwners) -> spec_fn(
1692 EntryOwner<C>,
1693 TreePath<NR_ENTRIES>,
1694 ) -> bool {
1695 |entry: EntryOwner<C>, path: TreePath<NR_ENTRIES>|
1696 {
1697 entry.is_node() && entry.meta_slot_paddr() is Some ==> {
1699 &&& regions.slot_owners.contains_key(frame_to_index(entry.meta_slot_paddr()->0))
1700 &&& regions.slot_owner(entry.meta_slot_paddr()->0).paths_in_pt
1701 == set![entry.path]
1702 }
1703 }
1704 }
1705
1706 pub open spec fn path_correct_pred() -> spec_fn(EntryOwner<C>, TreePath<NR_ENTRIES>) -> bool {
1707 |entry: EntryOwner<C>, path: TreePath<NR_ENTRIES>| { entry.path == path }
1708 }
1709
1710 pub open spec fn not_in_scope_pred() -> spec_fn(EntryOwner<C>, TreePath<NR_ENTRIES>) -> bool {
1711 |entry: EntryOwner<C>, _path: TreePath<NR_ENTRIES>| true
1712 }
1713
1714 pub proof fn tree_not_in_scope(subtree: OwnerSubtree<C>, path: TreePath<NR_ENTRIES>)
1716 requires
1717 subtree.inv(),
1718 ensures
1719 subtree.subtree_satisfies(path, Self::not_in_scope_pred()),
1720 decreases INC_LEVELS - subtree.level(),
1721 {
1722 if subtree.level() < INC_LEVELS - 1 {
1724 assert forall|i: int|
1725 0 <= i < subtree.children().len() && (
1726 #[trigger] subtree.children()[i]) is Some implies subtree.children()[i].unwrap().subtree_satisfies(
1727 path.push_tail(i), Self::not_in_scope_pred()) by {
1728 Self::tree_not_in_scope(subtree.children()[i].unwrap(), path.push_tail(i));
1729 };
1730 }
1731 }
1732
1733 pub proof fn view_rec_page_size_bound(self, path: TreePath<NR_ENTRIES>, m: Mapping)
1736 requires
1737 self.0.inv(),
1738 path.len() <= INC_LEVELS - 1,
1739 self.view_rec(path).contains(m),
1740 ensures
1741 m.page_size <= page_size::<PagingConsts>((INC_LEVELS - path.len()) as PagingLevel),
1742 decreases INC_LEVELS - path.len(),
1743 {
1744 broadcast use PageTableOwner::group_lemmas;
1745
1746 if self.0.value().is_node() && path.len() < INC_LEVELS - 1 {
1747 let i = choose|i: int|
1748 #![trigger self.0.children()[i]]
1749 0 <= i < self.0.children().len() && self.0.children()[i] is Some && PageTableOwner(
1750 self.0.children()[i].unwrap(),
1751 ).view_rec(path.push_tail(i)).contains(m);
1752 PageTableOwner(self.0.children()[i].unwrap()).view_rec_page_size_bound(
1753 path.push_tail(i),
1754 m,
1755 );
1756 lemma_page_size_monotone::<PagingConsts>(
1757 (INC_LEVELS - path.len() - 1) as PagingLevel,
1758 (INC_LEVELS - path.len()) as PagingLevel,
1759 );
1760 }
1761 }
1762
1763 pub proof fn view_rec_node_page_size_bound(self, path: TreePath<NR_ENTRIES>, m: Mapping)
1766 requires
1767 self.0.inv(),
1768 self.0.value().is_node(),
1769 path.len() < INC_LEVELS - 1,
1770 self.view_rec(path).contains(m),
1771 ensures
1772 m.page_size <= page_size::<PagingConsts>(
1773 ((INC_LEVELS - path.len()) - 1) as PagingLevel,
1774 ),
1775 decreases INC_LEVELS - path.len(),
1776 {
1777 broadcast use PageTableOwner::group_lemmas;
1778
1779 let i = choose|i: int|
1780 #![trigger self.0.children()[i]]
1781 0 <= i < self.0.children().len() && self.0.children()[i] is Some && PageTableOwner(
1782 self.0.children()[i].unwrap(),
1783 ).view_rec(path.push_tail(i)).contains(m);
1784 PageTableOwner(self.0.children()[i].unwrap()).view_rec_page_size_bound(
1785 path.push_tail(i),
1786 m,
1787 );
1788 }
1789
1790 pub open spec fn is_prefix_of<const N: usize>(prefix: TreePath<N>, path: TreePath<N>) -> bool {
1792 &&& prefix.len() <= path.len()
1793 &&& forall|i: int| 0 <= i < prefix.len() ==> prefix[i] == path[i]
1794 }
1795
1796 pub proof fn neq_old_from_path_disjoint(
1799 subtree: OwnerSubtree<C>,
1800 path_j: TreePath<NR_ENTRIES>,
1801 old_entry: EntryOwner<C>,
1802 regions: MetaRegionOwners,
1803 )
1804 requires
1805 subtree.inv(),
1806 subtree.value().path == path_j,
1807 path_j.len() == subtree.level(),
1808 path_j.inv(),
1809 path_j.len() <= INC_LEVELS - 1,
1810 subtree.subtree_satisfies(path_j, Self::metaregion_sound_pred(regions)),
1811 subtree.subtree_satisfies(path_j, Self::path_correct_pred()),
1812 old_entry.is_node(),
1813 old_entry.meta_slot_paddr() is Some,
1814 regions.slot_owner(old_entry.meta_slot_paddr()->0).paths_in_pt == set![old_entry.path],
1815 !Self::is_prefix_of(path_j, old_entry.path),
1816 ensures
1817 subtree.subtree_satisfies(
1818 path_j,
1819 |e: EntryOwner<C>, p: TreePath<NR_ENTRIES>| e.meta_slot_paddr_neq(old_entry),
1820 ),
1821 decreases INC_LEVELS - subtree.level(),
1822 {
1823 let f_sound = Self::metaregion_sound_pred(regions);
1824 let f_path = Self::path_correct_pred();
1825 let g = |e: EntryOwner<C>, p: TreePath<NR_ENTRIES>| e.meta_slot_paddr_neq(old_entry);
1826
1827 assert(g(subtree.value(), path_j)) by {
1828 assert(f_sound(subtree.value(), path_j));
1829 assert(f_path(subtree.value(), path_j));
1830 let entry = subtree.value();
1831 if entry.meta_slot_paddr() is Some && entry.meta_slot_paddr()->0
1832 == old_entry.meta_slot_paddr()->0 {
1833 let idx = frame_to_index(entry.meta_slot_paddr()->0);
1834 let old_idx = frame_to_index(old_entry.meta_slot_paddr()->0);
1835 assert(idx == old_idx);
1836 if entry.is_node() {
1837 assert(regions.slot_owners[idx].paths_in_pt == set![entry.path]);
1838 assert(regions.slot_owners[idx].paths_in_pt.contains(entry.path));
1839 } else {
1840 assert(entry.is_frame());
1841 assert(regions.slot_owners[idx].paths_in_pt.contains(entry.path));
1842 }
1843 assert(set![old_entry.path].contains(entry.path));
1844 assert(entry.path == old_entry.path);
1845 assert(Self::is_prefix_of(path_j, old_entry.path));
1846 }
1847 };
1848
1849 if subtree.level() < INC_LEVELS - 1 {
1850 assert forall|i: int|
1851 0 <= i < subtree.children().len() && (
1852 #[trigger] subtree.children()[i]) is Some implies subtree.children()[i].unwrap().subtree_satisfies(
1853 path_j.push_tail(i), g) by {
1854 let child = subtree.child(i);
1855 let child_path = path_j.push_tail(i);
1856 subtree.lemma_subtree_satisfies_unroll_once(path_j, f_sound, i);
1857 subtree.lemma_subtree_satisfies_unroll_once(path_j, f_path, i);
1858 Self::neq_old_from_path_disjoint(child, child_path, old_entry, regions);
1859 };
1860 }
1861 }
1862
1863 pub broadcast group group_lemmas {
1864 PageTableOwner::lemma_view_rec_contains,
1865 PageTableOwner::lemma_view_rec_contains_intro,
1866 }
1867}
1868
1869impl<C: PageTableConfig> Inv for PageTableOwner<C> {
1870 open spec fn inv(self) -> bool {
1871 &&& self.0.inv()
1872 &&& self.pt_inv_at_depth((INC_LEVELS - self.0.level()) as nat)
1873 &&& self.0.value().is_node()
1874 &&& self.0.value().path.len() <= INC_LEVELS - 1
1875 &&& self.0.value().path.inv()
1876 &&& self.0.value().path.len() == self.0.level()
1877 &&& self.0.value().parent_level == (INC_LEVELS - self.0.level()) as PagingLevel
1878 &&& self.0.value().node().tree_level == self.0.value().path.len()
1879 &&& self.0.subtree_satisfies(self.0.value().path, Self::path_correct_pred())
1880 }
1881}
1882
1883impl<C: PageTableConfig> View for PageTableOwner<C> {
1884 type V = PageTableView;
1885
1886 open spec fn view(&self) -> <Self as View>::V {
1887 let mappings = self.view_rec(self.0.value().path);
1888 PageTableView { mappings }
1889 }
1890}
1891
1892}