1use vstd::prelude::*;
16
17use vstd_extra::ghost_tree::*;
18use vstd_extra::ownership::*;
19
20use crate::mm::frame::meta::REF_COUNT_UNUSED;
21use crate::mm::page_size;
22use crate::mm::page_table::*;
23use crate::specs::arch::*;
24use crate::specs::mm::frame::{mapping::frame_to_index, meta_region_owners::MetaRegionOwners};
25use crate::specs::mm::page_table::Mapping;
26use crate::specs::mm::page_table::cursor::owners::{CursorContinuation, CursorOwner};
27use crate::specs::mm::page_table::node::entry_owners::EntryOwner;
28use crate::specs::mm::page_table::owners::{OwnerSubtree, PageTableOwner, vaddr_of};
29
30verus! {
31
32impl<'rcu, C: PageTableConfig> CursorOwner<'rcu, C> {
33 pub open spec fn no_node_at_idx(self, idx: usize) -> bool {
41 &&& self.map_full_tree(
42 |e: EntryOwner<C>, _p: TreePath<NR_ENTRIES>|
43 e.is_node() && e.meta_slot_paddr() is Some ==> frame_to_index(
44 e.meta_slot_paddr()->0,
45 ) != idx,
46 )
47 &&& forall|i: int|
48 #![trigger self.continuations[i]]
49 self.level - 1 <= i < NR_LEVELS ==> {
50 let e = self.continuations[i].entry_own;
51 e.is_node() && e.meta_slot_paddr() is Some ==> frame_to_index(
52 e.meta_slot_paddr()->0,
53 ) != idx
54 }
55 }
56
57 pub proof fn and_map_full_tree(
63 self,
64 f: spec_fn(EntryOwner<C>, TreePath<NR_ENTRIES>) -> bool,
65 guard: spec_fn(EntryOwner<C>, TreePath<NR_ENTRIES>) -> bool,
66 )
67 requires
68 self.inv(),
69 self.map_full_tree(f),
70 self.map_full_tree(guard),
71 ensures
72 self.map_full_tree(|e: EntryOwner<C>, p: TreePath<NR_ENTRIES>| f(e, p) && guard(e, p)),
73 {
74 let combined = |e: EntryOwner<C>, p: TreePath<NR_ENTRIES>| f(e, p) && guard(e, p);
75 assert forall|i: int|
76 #![trigger self.continuations[i]]
77 self.level - 1 <= i < NR_LEVELS implies self.continuations[i].map_children(
78 combined,
79 ) by {
80 let cont = self.continuations[i];
81 reveal(CursorContinuation::inv_children);
82 assert forall|j: int|
83 #![trigger cont.children[j]]
84 0 <= j < cont.children.len()
85 && cont.children[j] is Some implies cont.children[j].unwrap().tree_predicate_map(
86 cont.path().push_tail(j as usize), combined) by {
87 cont.inv_children_unroll(j);
88 OwnerSubtree::map_implies_and(
89 cont.children[j].unwrap(),
90 cont.path().push_tail(j as usize),
91 f,
92 guard,
93 combined,
94 );
95 };
96 };
97 }
98
99 pub proof fn no_node_at_idx_from_slot_key(self, regions: MetaRegionOwners, changed_idx: usize)
112 requires
113 self.inv(),
114 regions.inv(),
115 self.metaregion_sound(regions),
116 regions.slots.contains_key(changed_idx),
117 ensures
118 self.no_node_at_idx(changed_idx),
119 {
120 let msp = PageTableOwner::<C>::metaregion_sound_pred(regions);
121 let target = |e: EntryOwner<C>, _p: TreePath<NR_ENTRIES>|
122 e.is_node() && e.meta_slot_paddr() is Some ==> frame_to_index(
123 e.meta_slot_paddr().unwrap(),
124 ) != changed_idx;
125
126 assert(OwnerSubtree::implies(msp, target)) by {
127 assert forall|entry: EntryOwner<C>, path: TreePath<NR_ENTRIES>|
128 entry.inv() && msp(entry, path) implies #[trigger] target(entry, path) by {
129 if entry.is_node() && entry.meta_slot_paddr() is Some {
130 EntryOwner::<C>::active_entry_not_in_free_pool(entry, regions, changed_idx);
131 }
132 };
133 };
134 self.map_children_implies(msp, target);
135
136 assert forall|i: int|
137 #![trigger self.continuations[i]]
138 self.level - 1 <= i < NR_LEVELS implies {
139 let e = self.continuations[i].entry_own;
140 e.is_node() && e.meta_slot_paddr() is Some ==> frame_to_index(
141 e.meta_slot_paddr().unwrap(),
142 ) != changed_idx
143 } by {
144 let entry = self.continuations[i].entry_own;
145 if entry.is_node() && entry.meta_slot_paddr() is Some {
146 EntryOwner::<C>::active_entry_not_in_free_pool(entry, regions, changed_idx);
147 }
148 };
149 }
150
151 pub proof fn metaregion_preserved_under_path_insert(
155 self,
156 regions0: MetaRegionOwners,
157 regions1: MetaRegionOwners,
158 changed_idx: usize,
159 new_path: TreePath<NR_ENTRIES>,
160 )
161 requires
162 self.inv(),
163 self.metaregion_sound(regions0),
164 regions0.inv(),
165 regions0.slot_owners.contains_key(changed_idx),
166 regions1.slots == regions0.slots,
167 regions1.slot_owners.dom() =~= regions0.slot_owners.dom(),
168 forall|i: usize|
169 #![trigger regions1.slot_owners[i]]
170 i != changed_idx ==> regions0.slot_owners[i] == regions1.slot_owners[i],
171 regions1.slot_owners[changed_idx].inner_perms
172 == regions0.slot_owners[changed_idx].inner_perms,
173 regions1.slot_owners[changed_idx].slot_vaddr
174 == regions0.slot_owners[changed_idx].slot_vaddr,
175 regions1.slot_owners[changed_idx].usage == regions0.slot_owners[changed_idx].usage,
176 regions1.slot_owners[changed_idx].paths_in_pt
177 == regions0.slot_owners[changed_idx].paths_in_pt.insert(new_path),
178 self.no_node_at_idx(changed_idx),
179 ensures
180 self.metaregion_sound(regions1),
181 {
182 let f = PageTableOwner::<C>::metaregion_sound_pred(regions0);
183 let g = PageTableOwner::<C>::metaregion_sound_pred(regions1);
184 let guard = |entry: EntryOwner<C>, _p: TreePath<NR_ENTRIES>|
185 entry.is_node() && entry.meta_slot_paddr() is Some ==> frame_to_index(
186 entry.meta_slot_paddr().unwrap(),
187 ) != changed_idx;
188 let f_strong = |entry: EntryOwner<C>, path: TreePath<NR_ENTRIES>|
189 f(entry, path) && guard(entry, path);
190
191 assert(OwnerSubtree::implies(f_strong, g)) by {
192 assert forall|entry: EntryOwner<C>, path: TreePath<NR_ENTRIES>|
193 entry.inv() && f_strong(entry, path) implies #[trigger] g(entry, path) by {
194 if entry.meta_slot_paddr() is Some {
195 let eidx = frame_to_index(entry.meta_slot_paddr().unwrap());
196 if eidx != changed_idx {
197 assert(entry.is_frame() && entry.parent_level > 1 ==> {
203 let pa = entry.frame().mapped_pa;
204 let nr_pages = page_size(entry.parent_level) / PAGE_SIZE;
205 forall|j: usize|
206 0 < j < nr_pages ==> {
207 let sub_idx = #[trigger] frame_to_index(
208 (pa + j * PAGE_SIZE) as usize,
209 );
210 sub_idx != changed_idx
211 || regions1.slot_owners[sub_idx].usage is MMIO || (
212 regions1.slots.contains_key(sub_idx)
213 && regions1.slot_owners[sub_idx].inner_perms.ref_count.value()
214 != REF_COUNT_UNUSED
215 && regions1.slot_owners[sub_idx].inner_perms.ref_count.value()
216 > 0)
217 }
218 });
219 entry.metaregion_sound_one_slot_changed(regions0, regions1, changed_idx);
220 } else {
221 if entry.parent_level > 1 {
222 }
223 }
224 }
225 };
226 };
227
228 self.and_map_full_tree(f, guard);
229 self.map_children_implies(f_strong, g);
230
231 assert forall|i: int|
232 #![trigger self.continuations[i]]
233 self.level - 1 <= i
234 < NR_LEVELS implies self.continuations[i].entry_own.metaregion_sound(regions1) by {
235 let cont_entry = self.continuations[i].entry_own;
236 if cont_entry.meta_slot_paddr() is Some {
237 assert(cont_entry.is_frame() && cont_entry.parent_level > 1 ==> {
239 let pa = cont_entry.frame().mapped_pa;
240 let nr_pages = page_size(cont_entry.parent_level) / PAGE_SIZE;
241 forall|j: usize|
242 0 < j < nr_pages ==> {
243 let sub_idx = #[trigger] frame_to_index((pa + j * PAGE_SIZE) as usize);
244 sub_idx != changed_idx || regions1.slot_owners[sub_idx].usage is MMIO
245 || (regions1.slots.contains_key(sub_idx)
246 && regions1.slot_owners[sub_idx].inner_perms.ref_count.value()
247 != REF_COUNT_UNUSED
248 && regions1.slot_owners[sub_idx].inner_perms.ref_count.value() > 0)
249 }
250 });
251 cont_entry.metaregion_sound_one_slot_changed(regions0, regions1, changed_idx);
252 }
253 };
254 }
255
256 pub open spec fn path_removable_at_idx(
266 self,
267 idx: usize,
268 removed_path: TreePath<NR_ENTRIES>,
269 ) -> bool {
270 &&& self.map_full_tree(
271 |e: EntryOwner<C>, _p: TreePath<NR_ENTRIES>|
272 e.meta_slot_paddr() is Some && frame_to_index(e.meta_slot_paddr()->0) == idx
273 ==> !e.is_node() && (e.is_frame() ==> e.path != removed_path),
274 )
275 &&& forall|i: int|
276 #![trigger self.continuations[i]]
277 self.level - 1 <= i < NR_LEVELS ==> {
278 let e = self.continuations[i].entry_own;
279 e.meta_slot_paddr() is Some && frame_to_index(e.meta_slot_paddr()->0) == idx
280 ==> !e.is_node() && (e.is_frame() ==> e.path != removed_path)
281 }
282 }
283
284 pub open spec fn no_frame_with_path(self, removed_path: TreePath<NR_ENTRIES>) -> bool {
291 &&& self.map_full_tree(
292 |e: EntryOwner<C>, _p: TreePath<NR_ENTRIES>| e.is_frame() ==> e.path != removed_path,
293 )
294 &&& forall|i: int|
295 #![trigger self.continuations[i]]
296 self.level - 1 <= i < NR_LEVELS ==> {
297 let e = self.continuations[i].entry_own;
298 e.is_frame() ==> e.path != removed_path
299 }
300 }
301
302 pub proof fn no_frame_with_path_from_no_view_mapping(self, removed_path: TreePath<NR_ENTRIES>)
316 requires
317 self.inv(),
318 forall|m: Mapping|
319 #![trigger self@.mappings.contains(m)]
320 self@.mappings.contains(m) ==> m.va_range.start != vaddr_of::<C>(removed_path),
321 ensures
322 self.no_frame_with_path(removed_path),
323 {
324 broadcast use CursorContinuation::group_lemmas;
325
326 let g = |e: EntryOwner<C>, _p: TreePath<NR_ENTRIES>|
327 e.is_frame() ==> e.path != removed_path;
328
329 assert forall|i: int|
332 #![trigger self.continuations[i]]
333 self.level - 1 <= i < NR_LEVELS implies self.continuations[i].map_children(g) by {
334 self.inv_continuation(i);
335 let cont = self.continuations[i];
336 reveal(CursorContinuation::inv_children);
337 assert forall|j: int|
338 #![trigger cont.children[j]]
339 0 <= j < cont.children.len()
340 && cont.children[j] is Some implies cont.children[j].unwrap().tree_predicate_map(
341 cont.path().push_tail(j as usize), g) by {
342 cont.inv_children_unroll(j);
343 cont.pt_inv_children_unroll(j);
344 cont.inv_children_rel_unroll(j);
345 let child = cont.children[j].unwrap();
346 let child_path = cont.path().push_tail(j as usize);
347 PageTableOwner::<C>::pt_inv_implies_path_correct(child, child_path);
351 assert forall|m: Mapping|
353 #![trigger self@.mappings.contains(m)]
354 PageTableOwner(child).view_rec(child_path).contains(
355 m,
356 ) implies self@.mappings.contains(m) by {
357 self.lemma_view_mappings_intro(m, i);
358 };
359 PageTableOwner(child).no_frame_with_path_rec(
361 child_path,
362 removed_path,
363 self@.mappings,
364 );
365 };
366 };
367
368 assert forall|i: int|
370 #![trigger self.continuations[i]]
371 self.level - 1 <= i < NR_LEVELS implies {
372 let e = self.continuations[i].entry_own;
373 e.is_frame() ==> e.path != removed_path
374 } by {
375 self.inv_continuation(i);
376 };
377 }
378
379 pub proof fn path_removable_from_no_node_and_no_frame_path(
386 self,
387 idx: usize,
388 removed_path: TreePath<NR_ENTRIES>,
389 )
390 requires
391 self.inv(),
392 self.no_node_at_idx(idx),
393 self.no_frame_with_path(removed_path),
394 ensures
395 self.path_removable_at_idx(idx, removed_path),
396 {
397 let nn = |e: EntryOwner<C>, _p: TreePath<NR_ENTRIES>|
398 e.is_node() && e.meta_slot_paddr() is Some ==> frame_to_index(
399 e.meta_slot_paddr().unwrap(),
400 ) != idx;
401 let nf = |e: EntryOwner<C>, _p: TreePath<NR_ENTRIES>|
402 e.is_frame() ==> e.path != removed_path;
403 let r = |e: EntryOwner<C>, _p: TreePath<NR_ENTRIES>|
404 e.meta_slot_paddr() is Some && frame_to_index(e.meta_slot_paddr().unwrap()) == idx
405 ==> !e.is_node() && (e.is_frame() ==> e.path != removed_path);
406
407 assert(OwnerSubtree::implies(
409 |e: EntryOwner<C>, p: TreePath<NR_ENTRIES>| nn(e, p) && nf(e, p),
410 r,
411 )) by {
412 assert forall|e: EntryOwner<C>, p: TreePath<NR_ENTRIES>|
413 e.inv() && nn(e, p) && nf(e, p) implies #[trigger] r(e, p) by {}
414 };
415 self.and_map_full_tree(nn, nf);
417 self.map_children_implies(
418 |e: EntryOwner<C>, p: TreePath<NR_ENTRIES>| nn(e, p) && nf(e, p),
419 r,
420 );
421
422 assert forall|i: int|
424 #![trigger self.continuations[i]]
425 self.level - 1 <= i < NR_LEVELS implies {
426 let e = self.continuations[i].entry_own;
427 e.meta_slot_paddr() is Some && frame_to_index(e.meta_slot_paddr().unwrap()) == idx
428 ==> !e.is_node() && (e.is_frame() ==> e.path != removed_path)
429 } by {
430 let e = self.continuations[i].entry_own;
431 }
432 }
433
434 pub proof fn metaregion_preserved_under_path_remove(
444 self,
445 regions0: MetaRegionOwners,
446 regions1: MetaRegionOwners,
447 changed_idx: usize,
448 removed_path: TreePath<NR_ENTRIES>,
449 )
450 requires
451 self.inv(),
452 self.metaregion_sound(regions0),
453 regions0.inv(),
454 regions0.slot_owners.contains_key(changed_idx),
455 regions1.slots == regions0.slots,
456 regions1.slot_owners.dom() =~= regions0.slot_owners.dom(),
457 forall|i: usize|
458 #![trigger regions1.slot_owners[i]]
459 i != changed_idx ==> regions0.slot_owners[i] == regions1.slot_owners[i],
460 regions1.slot_owners[changed_idx].inner_perms
461 == regions0.slot_owners[changed_idx].inner_perms,
462 regions1.slot_owners[changed_idx].slot_vaddr
463 == regions0.slot_owners[changed_idx].slot_vaddr,
464 regions1.slot_owners[changed_idx].usage == regions0.slot_owners[changed_idx].usage,
465 regions1.slot_owners[changed_idx].paths_in_pt
466 == regions0.slot_owners[changed_idx].paths_in_pt.remove(removed_path),
467 self.path_removable_at_idx(changed_idx, removed_path),
468 ensures
469 self.metaregion_sound(regions1),
470 {
471 let f = PageTableOwner::<C>::metaregion_sound_pred(regions0);
472 let g = PageTableOwner::<C>::metaregion_sound_pred(regions1);
473 let guard = |entry: EntryOwner<C>, _p: TreePath<NR_ENTRIES>|
474 entry.meta_slot_paddr() is Some && frame_to_index(entry.meta_slot_paddr().unwrap())
475 == changed_idx ==> !entry.is_node() && (entry.is_frame() ==> entry.path
476 != removed_path);
477 let f_strong = |entry: EntryOwner<C>, path: TreePath<NR_ENTRIES>|
478 f(entry, path) && guard(entry, path);
479
480 assert(OwnerSubtree::implies(f_strong, g)) by {
481 assert forall|entry: EntryOwner<C>, path: TreePath<NR_ENTRIES>|
482 entry.inv() && f_strong(entry, path) implies #[trigger] g(entry, path) by {
483 if entry.meta_slot_paddr() is Some {
484 let eidx = frame_to_index(entry.meta_slot_paddr().unwrap());
485 if eidx != changed_idx {
486 assert(entry.is_frame() && entry.parent_level > 1 ==> {
491 let pa = entry.frame().mapped_pa;
492 let nr_pages = page_size(entry.parent_level) / PAGE_SIZE;
493 forall|j: usize|
494 0 < j < nr_pages ==> {
495 let sub_idx = #[trigger] frame_to_index(
496 (pa + j * PAGE_SIZE) as usize,
497 );
498 sub_idx != changed_idx
499 || regions1.slot_owners[sub_idx].usage is MMIO || (
500 regions1.slots.contains_key(sub_idx)
501 && regions1.slot_owners[sub_idx].inner_perms.ref_count.value()
502 != REF_COUNT_UNUSED
503 && regions1.slot_owners[sub_idx].inner_perms.ref_count.value()
504 > 0)
505 }
506 });
507 entry.metaregion_sound_one_slot_changed(regions0, regions1, changed_idx);
508 } else {
509 if entry.is_frame() {
516 assert(regions0.slot_owners[changed_idx].paths_in_pt.contains(
517 entry.path,
518 ));
519 assert(regions1.slot_owners[changed_idx].paths_in_pt.contains(
520 entry.path,
521 ));
522 entry.frame_sub_pages_valid_preserved_at_own_slot(regions0, regions1);
523 }
524 }
525 }
526 };
527 };
528
529 self.and_map_full_tree(f, guard);
530 self.map_children_implies(f_strong, g);
531
532 assert forall|i: int|
533 #![trigger self.continuations[i]]
534 self.level - 1 <= i
535 < NR_LEVELS implies self.continuations[i].entry_own.metaregion_sound(regions1) by {
536 let cont_entry = self.continuations[i].entry_own;
537 if cont_entry.meta_slot_paddr() is Some {
538 let eidx = frame_to_index(cont_entry.meta_slot_paddr().unwrap());
539 if eidx != changed_idx {
540 assert(cont_entry.is_frame() && cont_entry.parent_level > 1 ==> {
541 let pa = cont_entry.frame().mapped_pa;
542 let nr_pages = page_size(cont_entry.parent_level) / PAGE_SIZE;
543 forall|j: usize|
544 0 < j < nr_pages ==> {
545 let sub_idx = #[trigger] frame_to_index(
546 (pa + j * PAGE_SIZE) as usize,
547 );
548 sub_idx != changed_idx
549 || regions1.slot_owners[sub_idx].usage is MMIO || (
550 regions1.slots.contains_key(sub_idx)
551 && regions1.slot_owners[sub_idx].inner_perms.ref_count.value()
552 != REF_COUNT_UNUSED
553 && regions1.slot_owners[sub_idx].inner_perms.ref_count.value()
554 > 0)
555 }
556 });
557 cont_entry.metaregion_sound_one_slot_changed(regions0, regions1, changed_idx);
558 } else {
559 if cont_entry.is_frame() {
560 assert(regions0.slot_owners[changed_idx].paths_in_pt.contains(
561 cont_entry.path,
562 ));
563 assert(regions1.slot_owners[changed_idx].paths_in_pt.contains(
564 cont_entry.path,
565 ));
566 cont_entry.frame_sub_pages_valid_preserved_at_own_slot(regions0, regions1);
567 }
568 }
569 }
570 };
571 }
572
573 pub proof fn take_next_remove_path_preserves_metaregion(
576 self,
577 owner_before_replace: Self,
578 regions0: MetaRegionOwners,
579 regions1: MetaRegionOwners,
580 removed_idx: usize,
581 removed_path: TreePath<NR_ENTRIES>,
582 target: Mapping,
583 )
584 requires
585 self.inv(),
586 owner_before_replace.inv(),
587 owner_before_replace.in_locked_range(),
588 self.metaregion_sound(regions0),
589 regions0.inv(),
590 regions0.slot_owners.contains_key(removed_idx),
591 regions0.slots.contains_key(removed_idx),
592 self@.mappings == owner_before_replace@.mappings - PageTableOwner(
593 owner_before_replace.cur_subtree(),
594 )@.mappings,
595 PageTableOwner(owner_before_replace.cur_subtree())@.mappings == set![target],
596 owner_before_replace.cur_subtree().value.path == removed_path,
597 regions1.slots == regions0.slots,
598 regions1.slot_owners.dom() =~= regions0.slot_owners.dom(),
599 forall|i: usize|
600 #![trigger regions1.slot_owners[i]]
601 i != removed_idx ==> regions0.slot_owners[i] == regions1.slot_owners[i],
602 regions1.slot_owners[removed_idx].inner_perms
603 == regions0.slot_owners[removed_idx].inner_perms,
604 regions1.slot_owners[removed_idx].slot_vaddr
605 == regions0.slot_owners[removed_idx].slot_vaddr,
606 regions1.slot_owners[removed_idx].usage == regions0.slot_owners[removed_idx].usage,
607 regions1.slot_owners[removed_idx].paths_in_pt
608 == regions0.slot_owners[removed_idx].paths_in_pt.remove(removed_path),
609 ensures
610 self.metaregion_sound(regions1),
611 {
612 self.no_node_at_idx_from_slot_key(regions0, removed_idx);
613
614 owner_before_replace.cur_subtree_eq_filtered_mappings_path();
615 let ghost obr_subtree = PageTableOwner(owner_before_replace.cur_subtree())@.mappings;
616
617 let ghost sv = vaddr_of::<C>(removed_path) as int;
618 let ghost sz = page_size(owner_before_replace.level) as int;
619 assert(obr_subtree == owner_before_replace@.mappings.filter(
620 |mm: Mapping| sv <= mm.va_range.start < sv + sz,
621 ));
622 assert(sz > 0) by {
623 crate::specs::mm::page_table::cursor::page_size_lemmas::lemma_page_size_ge_page_size(
624 owner_before_replace.level,
625 );
626 };
627 assert forall|mm: Mapping| #[trigger] self@.mappings.contains(mm) implies mm.va_range.start
628 != sv by {
629 if mm.va_range.start == sv {
630 assert(owner_before_replace@.mappings.filter(
631 |m2: Mapping| sv <= m2.va_range.start < sv + sz,
632 ).contains(mm));
633 }
634 };
635
636 self.no_frame_with_path_from_no_view_mapping(removed_path);
637 self.path_removable_from_no_node_and_no_frame_path(removed_idx, removed_path);
638 self.metaregion_preserved_under_path_remove(regions0, regions1, removed_idx, removed_path);
639 }
640}
641
642}