1use vstd::prelude::*;
14use vstd_extra::{ghost_tree::*, ownership::*};
15
16use crate::specs::{
17 arch::*,
18 mm::{
19 frame::{
20 mapping::frame_to_index, meta_owners::PageUsage, meta_region_owners::MetaRegionOwners,
21 },
22 page_table::{
23 Mapping,
24 cursor::owners::{CursorContinuation, CursorOwner},
25 node::entry_owners::EntryOwner,
26 owners::*,
27 },
28 },
29};
30
31use crate::arch::mm::PagingConsts;
32use crate::mm::{frame::meta::REF_COUNT_UNUSED, page_size, page_table::*};
33
34verus! {
35
36impl<'rcu, C: PageTableConfig> CursorOwner<'rcu, C> {
37 pub open spec fn no_node_at_idx(self, idx: int) -> bool {
45 &&& self.map_full_tree(
46 |e: EntryOwner<C>, _p: TreePath<NR_ENTRIES>|
47 e.is_node() && e.meta_slot_paddr() is Some ==> frame_to_index(
48 e.meta_slot_paddr()->0,
49 ) != idx,
50 )
51 &&& forall|i: int|
52 #![trigger self.continuations[i]]
53 self.level - 1 <= i < NR_LEVELS ==> {
54 let e = self.continuations[i].entry_own;
55 e.is_node() && e.meta_slot_paddr() is Some ==> frame_to_index(
56 e.meta_slot_paddr()->0,
57 ) != idx
58 }
59 }
60
61 pub proof fn and_map_full_tree(
67 self,
68 f: spec_fn(EntryOwner<C>, TreePath<NR_ENTRIES>) -> bool,
69 guard: spec_fn(EntryOwner<C>, TreePath<NR_ENTRIES>) -> bool,
70 )
71 requires
72 self.inv(),
73 self.map_full_tree(f),
74 self.map_full_tree(guard),
75 ensures
76 self.map_full_tree(|e: EntryOwner<C>, p: TreePath<NR_ENTRIES>| f(e, p) && guard(e, p)),
77 {
78 let combined = |e: EntryOwner<C>, p: TreePath<NR_ENTRIES>| f(e, p) && guard(e, p);
79 assert forall|i: int|
80 #![trigger self.continuations[i]]
81 self.level - 1 <= i < NR_LEVELS implies self.continuations[i].map_children(
82 combined,
83 ) by {
84 reveal(CursorContinuation::map_children);
85 let cont = self.continuations[i];
86 reveal(CursorContinuation::inv_children);
87 assert forall|j: int|
88 #![trigger cont.children[j]]
89 0 <= j < cont.children.len()
90 && cont.children[j] is Some implies cont.children[j].unwrap().subtree_satisfies(
91 cont.path().push_tail(j),
92 combined,
93 ) by {
94 cont.lemma_inv_children_unroll(j);
95 OwnerSubtree::lemma_subtree_satisfies_implies_and(
96 cont.children[j].unwrap(),
97 cont.path().push_tail(j),
98 f,
99 guard,
100 combined,
101 );
102 };
103 };
104 }
105
106 pub proof fn no_node_at_idx_from_slot_key(self, regions: MetaRegionOwners, changed_idx: int)
116 requires
117 self.inv(),
118 regions.inv(),
119 self.metaregion_sound(regions),
120 regions.contains(changed_idx),
121 regions.slot_owners[changed_idx].usage !is PageTable,
122 ensures
123 self.no_node_at_idx(changed_idx),
124 {
125 let msp = PageTableOwner::<C>::metaregion_sound_pred(regions);
126 let target = |e: EntryOwner<C>, _p: TreePath<NR_ENTRIES>|
127 e.is_node() && e.meta_slot_paddr() is Some ==> frame_to_index(
128 e.meta_slot_paddr().unwrap(),
129 ) != changed_idx;
130
131 self.lemma_map_children_implies(msp, target);
132
133 assert forall|i: int|
134 #![trigger self.continuations[i]]
135 self.level - 1 <= i < NR_LEVELS implies {
136 let e = self.continuations[i].entry_own;
137 e.is_node() && e.meta_slot_paddr() is Some ==> frame_to_index(
138 e.meta_slot_paddr().unwrap(),
139 ) != changed_idx
140 } by {
141 self.lemma_cont_entry_metaregion_at(regions, i);
142 let entry = self.continuations[i].entry_own;
143 if entry.is_node() && entry.meta_slot_paddr() is Some {
144 let idx = frame_to_index(entry.meta_slot_paddr().unwrap());
145 if idx == changed_idx {
146 assert(false);
147 }
148 }
149 };
150 }
151
152 pub proof fn metaregion_preserved_under_path_insert(
156 self,
157 regions0: MetaRegionOwners,
158 regions1: MetaRegionOwners,
159 changed_idx: int,
160 new_path: TreePath<NR_ENTRIES>,
161 )
162 requires
163 self.inv(),
164 self.metaregion_sound(regions0),
165 regions0.inv(),
166 regions0.contains(changed_idx),
167 regions1.slots == regions0.slots,
168 regions1.slot_owners.dom() =~= regions0.slot_owners.dom(),
169 forall|i: int|
170 #![trigger regions1.slot_owners[i]]
171 i != changed_idx ==> regions0.slot_owners[i] == regions1.slot_owners[i],
172 regions1.slot_owners[changed_idx].same_permissions(regions0.slot_owners[changed_idx]),
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 self.and_map_full_tree(f, guard);
192 assert(OwnerSubtree::implies(f_strong, g)) by {
193 assert forall|entry: EntryOwner<C>, path: TreePath<NR_ENTRIES>|
194 entry.inv() && f_strong(entry, path) implies #[trigger] g(entry, path) by {
195 if entry.is_frame() {
196 let pa = entry.frame().mapped_pa;
197 let idx = frame_to_index(pa);
198 assert(regions0.slots[idx] == regions1.slots[idx]);
199 assert(regions0.slot_owners[idx].metadata_perm.id()
200 == regions1.slot_owners[idx].metadata_perm.id());
201 C::lemma_perm_well_formed_with_region_preserved(
202 pa,
203 Tracked(entry.frame_permission()),
204 regions0,
205 regions1,
206 );
207 }
208 };
209 };
210 self.lemma_map_children_implies(f_strong, g);
211
212 reveal(CursorOwner::path_metaregion_sound);
213 assert forall|i: int|
214 #![trigger self.continuations[i]]
215 self.level - 1 <= i
216 < NR_LEVELS implies self.continuations[i].entry_own.metaregion_sound(regions1) by {
217 let cont_entry = self.continuations[i].entry_own;
218 if cont_entry.meta_slot_paddr() is Some {
219 cont_entry.lemma_metaregion_sound_one_slot_changed(regions0, regions1, changed_idx);
221 }
222 };
223 }
224
225 pub open spec fn path_removable_at_idx(
235 self,
236 idx: int,
237 removed_path: TreePath<NR_ENTRIES>,
238 ) -> bool {
239 &&& self.map_full_tree(
240 |e: EntryOwner<C>, _p: TreePath<NR_ENTRIES>|
241 e.meta_slot_paddr() is Some && frame_to_index(e.meta_slot_paddr()->0) == idx
242 ==> !e.is_node() && (e.is_frame() ==> e.path != removed_path),
243 )
244 &&& forall|i: int|
245 #![trigger self.continuations[i]]
246 self.level - 1 <= i < NR_LEVELS ==> {
247 let e = self.continuations[i].entry_own;
248 e.meta_slot_paddr() is Some && frame_to_index(e.meta_slot_paddr()->0) == idx
249 ==> !e.is_node() && (e.is_frame() ==> e.path != removed_path)
250 }
251 }
252
253 pub open spec fn no_frame_with_path(self, removed_path: TreePath<NR_ENTRIES>) -> bool {
260 &&& self.map_full_tree(
261 |e: EntryOwner<C>, _p: TreePath<NR_ENTRIES>| e.is_frame() ==> e.path != removed_path,
262 )
263 &&& forall|i: int|
264 #![trigger self.continuations[i]]
265 self.level - 1 <= i < NR_LEVELS ==> {
266 let e = self.continuations[i].entry_own;
267 e.is_frame() ==> e.path != removed_path
268 }
269 }
270
271 pub proof fn no_frame_with_path_from_no_view_mapping(self, removed_path: TreePath<NR_ENTRIES>)
285 requires
286 self.inv(),
287 forall|m: Mapping|
288 #![trigger self@.mappings.contains(m)]
289 self@.mappings.contains(m) ==> m.va_range.start != vaddr_of::<C>(removed_path),
290 ensures
291 self.no_frame_with_path(removed_path),
292 {
293 broadcast use CursorContinuation::group_lemmas;
294
295 let g = |e: EntryOwner<C>, _p: TreePath<NR_ENTRIES>|
296 e.is_frame() ==> e.path != removed_path;
297
298 assert forall|i: int|
301 #![trigger self.continuations[i]]
302 self.level - 1 <= i < NR_LEVELS implies self.continuations[i].map_children(g) by {
303 reveal(CursorContinuation::map_children);
304 self.lemma_inv_continuation(i);
305 let cont = self.continuations[i];
306 reveal(CursorContinuation::inv_children);
307 assert forall|j: int|
308 #![trigger cont.children[j]]
309 0 <= j < cont.children.len()
310 && cont.children[j] is Some implies cont.children[j].unwrap().subtree_satisfies(
311 cont.path().push_tail(j),
312 g,
313 ) by {
314 cont.lemma_inv_children_unroll(j);
315 cont.lemma_pt_inv_children_unroll(j);
316 cont.lemma_inv_children_rel_unroll(j);
317 let child = cont.children[j].unwrap();
318 let child_path = cont.path().push_tail(j);
319 PageTableOwner::<C>::lemma_pt_inv_implies_path_correct(child, child_path);
323 assert forall|m: Mapping|
325 #![trigger self@.mappings.contains(m)]
326 PageTableOwner(child).view_rec(child_path).contains(
327 m,
328 ) implies self@.mappings.contains(m) by {
329 self.lemma_view_mappings_intro(m, i);
330 };
331 PageTableOwner(child).lemma_no_frame_with_path_rec(
333 child_path,
334 removed_path,
335 self@.mappings,
336 );
337 };
338 };
339
340 assert forall|i: int|
342 #![trigger self.continuations[i]]
343 self.level - 1 <= i < NR_LEVELS implies {
344 let e = self.continuations[i].entry_own;
345 e.is_frame() ==> e.path != removed_path
346 } by {
347 self.lemma_inv_continuation(i);
348 };
349 }
350
351 pub proof fn path_removable_from_no_node_and_no_frame_path(
358 self,
359 idx: int,
360 removed_path: TreePath<NR_ENTRIES>,
361 )
362 requires
363 self.inv(),
364 self.no_node_at_idx(idx),
365 self.no_frame_with_path(removed_path),
366 ensures
367 self.path_removable_at_idx(idx, removed_path),
368 {
369 let nn = |e: EntryOwner<C>, _p: TreePath<NR_ENTRIES>|
370 e.is_node() && e.meta_slot_paddr() is Some ==> frame_to_index(
371 e.meta_slot_paddr().unwrap(),
372 ) != idx;
373 let nf = |e: EntryOwner<C>, _p: TreePath<NR_ENTRIES>|
374 e.is_frame() ==> e.path != removed_path;
375 let r = |e: EntryOwner<C>, _p: TreePath<NR_ENTRIES>|
376 e.meta_slot_paddr() is Some && frame_to_index(e.meta_slot_paddr().unwrap()) == idx
377 ==> !e.is_node() && (e.is_frame() ==> e.path != removed_path);
378
379 self.and_map_full_tree(nn, nf);
382 self.lemma_map_children_implies(
383 |e: EntryOwner<C>, p: TreePath<NR_ENTRIES>| nn(e, p) && nf(e, p),
384 r,
385 );
386
387 assert forall|i: int|
389 #![trigger self.continuations[i]]
390 self.level - 1 <= i < NR_LEVELS implies {
391 let e = self.continuations[i].entry_own;
392 e.meta_slot_paddr() is Some && frame_to_index(e.meta_slot_paddr().unwrap()) == idx
393 ==> !e.is_node() && (e.is_frame() ==> e.path != removed_path)
394 } by {
395 let e = self.continuations[i].entry_own;
396 }
397 }
398
399 pub proof fn metaregion_preserved_under_path_remove(
408 self,
409 regions0: MetaRegionOwners,
410 regions1: MetaRegionOwners,
411 changed_idx: int,
412 removed_path: TreePath<NR_ENTRIES>,
413 )
414 requires
415 self.inv(),
416 self.metaregion_sound(regions0),
417 regions0.inv(),
418 regions0.contains(changed_idx),
419 regions1.slots == regions0.slots,
420 regions1.slot_owners.dom() =~= regions0.slot_owners.dom(),
421 forall|i: int|
422 #![trigger regions1.slot_owners[i]]
423 i != changed_idx ==> regions0.slot_owners[i] == regions1.slot_owners[i],
424 regions1.slot_owners[changed_idx].same_permissions(regions0.slot_owners[changed_idx]),
425 regions1.slot_owners[changed_idx].slot_vaddr
426 == regions0.slot_owners[changed_idx].slot_vaddr,
427 regions1.slot_owners[changed_idx].usage == regions0.slot_owners[changed_idx].usage,
428 regions1.slot_owners[changed_idx].paths_in_pt
429 == regions0.slot_owners[changed_idx].paths_in_pt.remove(removed_path),
430 self.path_removable_at_idx(changed_idx, removed_path),
431 ensures
432 self.metaregion_sound(regions1),
433 {
434 let f = PageTableOwner::<C>::metaregion_sound_pred(regions0);
435 let g = PageTableOwner::<C>::metaregion_sound_pred(regions1);
436 let guard = |entry: EntryOwner<C>, _p: TreePath<NR_ENTRIES>|
437 entry.meta_slot_paddr() is Some && frame_to_index(entry.meta_slot_paddr().unwrap())
438 == changed_idx ==> !entry.is_node() && (entry.is_frame() ==> entry.path
439 != removed_path);
440 let f_strong = |entry: EntryOwner<C>, path: TreePath<NR_ENTRIES>|
441 f(entry, path) && guard(entry, path);
442
443 self.and_map_full_tree(f, guard);
444 assert(OwnerSubtree::implies(f_strong, g)) by {
445 assert forall|entry: EntryOwner<C>, path: TreePath<NR_ENTRIES>|
446 entry.inv() && f_strong(entry, path) implies #[trigger] g(entry, path) by {
447 if entry.is_frame() {
448 let pa = entry.frame().mapped_pa;
449 let idx = frame_to_index(pa);
450 assert(regions0.slots[idx] == regions1.slots[idx]);
451 assert(regions0.slot_owners[idx].metadata_perm.id()
452 == regions1.slot_owners[idx].metadata_perm.id());
453 C::lemma_perm_well_formed_with_region_preserved(
454 pa,
455 Tracked(entry.frame_permission()),
456 regions0,
457 regions1,
458 );
459 }
460 };
461 };
462 self.lemma_map_children_implies(f_strong, g);
463
464 reveal(CursorOwner::path_metaregion_sound);
465 assert forall|i: int|
466 #![trigger self.continuations[i]]
467 self.level - 1 <= i
468 < NR_LEVELS implies self.continuations[i].entry_own.metaregion_sound(regions1) by {
469 let cont_entry = self.continuations[i].entry_own;
470 if cont_entry.meta_slot_paddr() is Some {
471 let eidx = frame_to_index(cont_entry.meta_slot_paddr().unwrap());
472 if eidx != changed_idx {
473 cont_entry.lemma_metaregion_sound_one_slot_changed(
474 regions0,
475 regions1,
476 changed_idx,
477 );
478 }
479 }
480 };
481 }
482
483 pub proof fn take_next_remove_path_preserves_metaregion(
486 self,
487 owner_before_replace: Self,
488 regions0: MetaRegionOwners,
489 regions1: MetaRegionOwners,
490 removed_idx: int,
491 removed_path: TreePath<NR_ENTRIES>,
492 target: Mapping,
493 )
494 requires
495 self.inv(),
496 owner_before_replace.inv(),
497 owner_before_replace.in_barrier_va(),
498 self.metaregion_sound(regions0),
499 regions0.inv(),
500 regions0.contains(removed_idx),
501 regions0.slot_owners[removed_idx].usage !is PageTable,
502 self@.mappings == owner_before_replace@.mappings - PageTableOwner(
503 owner_before_replace.cur_subtree(),
504 )@.mappings,
505 PageTableOwner(owner_before_replace.cur_subtree())@.mappings == set![target],
506 owner_before_replace.cur_subtree().value().path == removed_path,
507 regions1.slots == regions0.slots,
508 regions1.slot_owners.dom() =~= regions0.slot_owners.dom(),
509 forall|i: int|
510 #![trigger regions1.slot_owners[i]]
511 i != removed_idx ==> regions0.slot_owners[i] == regions1.slot_owners[i],
512 regions1.slot_owners[removed_idx].same_permissions(regions0.slot_owners[removed_idx]),
513 regions1.slot_owners[removed_idx].slot_vaddr
514 == regions0.slot_owners[removed_idx].slot_vaddr,
515 regions1.slot_owners[removed_idx].usage == regions0.slot_owners[removed_idx].usage,
516 regions1.slot_owners[removed_idx].paths_in_pt
517 == regions0.slot_owners[removed_idx].paths_in_pt.remove(removed_path),
518 ensures
519 self.metaregion_sound(regions1),
520 {
521 self.no_node_at_idx_from_slot_key(regions0, removed_idx);
522
523 owner_before_replace.lemma_cur_subtree_eq_filtered_mappings_path();
524
525 let ghost sv = vaddr_of::<C>(removed_path) as int;
526 let ghost sz = page_size::<PagingConsts>(owner_before_replace.level) as int;
527 assert(sz > 0) by {
528 crate::specs::mm::page_table::cursor::page_size_lemmas::lemma_page_size_ge_base::<
529 PagingConsts,
530 >(owner_before_replace.level);
531 };
532 assert forall|mm: Mapping| #[trigger] self@.mappings.contains(mm) implies mm.va_range.start
533 != sv by {};
534
535 self.no_frame_with_path_from_no_view_mapping(removed_path);
536 self.path_removable_from_no_node_and_no_frame_path(removed_idx, removed_path);
537 self.metaregion_preserved_under_path_remove(regions0, regions1, removed_idx, removed_path);
538 }
539}
540
541}