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