1use vstd::prelude::*;
16
17use vstd_extra::ghost_tree::*;
18use vstd_extra::ownership::*;
19
20use crate::mm::frame::meta::mapping::frame_to_index;
21use crate::mm::page_table::*;
22use crate::specs::arch::mm::PAGE_SIZE;
23use crate::specs::arch::mm::{NR_ENTRIES, NR_LEVELS};
24use crate::specs::mm::frame::meta_owners::REF_COUNT_UNUSED;
25use crate::specs::mm::frame::meta_region_owners::MetaRegionOwners;
26use crate::specs::mm::page_table::Mapping;
27use crate::specs::mm::page_table::cursor::owners::{CursorContinuation, CursorOwner};
28use crate::specs::mm::page_table::node::entry_owners::EntryOwner;
29use crate::specs::mm::page_table::owners::{OwnerSubtree, PageTableOwner, vaddr_of};
30
31verus! {
32
33impl<'rcu, C: PageTableConfig> CursorOwner<'rcu, C> {
34 pub open spec fn no_node_at_idx(self, idx: usize) -> bool {
42 &&& self.map_full_tree(
43 |e: EntryOwner<C>, _p: TreePath<NR_ENTRIES>|
44 e.is_node() && e.meta_slot_paddr() is Some ==> frame_to_index(
45 e.meta_slot_paddr().unwrap(),
46 ) != idx,
47 )
48 &&& forall|i: int|
49 #![trigger self.continuations[i]]
50 self.level - 1 <= i < NR_LEVELS ==> {
51 let e = self.continuations[i].entry_own;
52 e.is_node() && e.meta_slot_paddr() is Some ==> frame_to_index(
53 e.meta_slot_paddr().unwrap(),
54 ) != idx
55 }
56 }
57
58 pub proof fn and_map_full_tree(
64 self,
65 f: spec_fn(EntryOwner<C>, TreePath<NR_ENTRIES>) -> bool,
66 guard: spec_fn(EntryOwner<C>, TreePath<NR_ENTRIES>) -> bool,
67 )
68 requires
69 self.inv(),
70 self.map_full_tree(f),
71 self.map_full_tree(guard),
72 ensures
73 self.map_full_tree(|e: EntryOwner<C>, p: TreePath<NR_ENTRIES>| f(e, p) && guard(e, p)),
74 {
75 let combined = |e: EntryOwner<C>, p: TreePath<NR_ENTRIES>| f(e, p) && guard(e, p);
76 assert forall|i: int|
77 #![trigger self.continuations[i]]
78 self.level - 1 <= i < NR_LEVELS implies self.continuations[i].map_children(
79 combined,
80 ) by {
81 let cont = self.continuations[i];
82 reveal(CursorContinuation::inv_children);
83 assert forall|j: int|
84 #![trigger cont.children[j]]
85 0 <= j < cont.children.len()
86 && cont.children[j] is Some implies cont.children[j].unwrap().tree_predicate_map(
87 cont.path().push_tail(j as usize), combined) by {
88 cont.inv_children_unroll(j);
89 OwnerSubtree::map_implies_and(
90 cont.children[j].unwrap(),
91 cont.path().push_tail(j as usize),
92 f,
93 guard,
94 combined,
95 );
96 };
97 };
98 }
99
100 pub proof fn no_node_at_idx_from_slot_key(self, regions: MetaRegionOwners, changed_idx: usize)
113 requires
114 self.inv(),
115 regions.inv(),
116 self.metaregion_sound(regions),
117 regions.slots.contains_key(changed_idx),
118 ensures
119 self.no_node_at_idx(changed_idx),
120 {
121 let msp = PageTableOwner::<C>::metaregion_sound_pred(regions);
122 let target = |e: EntryOwner<C>, _p: TreePath<NR_ENTRIES>|
123 e.is_node() && e.meta_slot_paddr() is Some ==> frame_to_index(
124 e.meta_slot_paddr().unwrap(),
125 ) != changed_idx;
126
127 assert(OwnerSubtree::implies(msp, target)) by {
128 assert forall|entry: EntryOwner<C>, path: TreePath<NR_ENTRIES>|
129 entry.inv() && msp(entry, path) implies #[trigger] target(entry, path) by {
130 if entry.is_node() && entry.meta_slot_paddr() is Some {
131 EntryOwner::<C>::active_entry_not_in_free_pool(entry, regions, changed_idx);
132 }
133 };
134 };
135 self.map_children_implies(msp, target);
136
137 assert forall|i: int|
138 #![trigger self.continuations[i]]
139 self.level - 1 <= i < NR_LEVELS implies {
140 let e = self.continuations[i].entry_own;
141 e.is_node() && e.meta_slot_paddr() is Some ==> frame_to_index(
142 e.meta_slot_paddr().unwrap(),
143 ) != changed_idx
144 } by {
145 let entry = self.continuations[i].entry_own;
146 if entry.is_node() && entry.meta_slot_paddr() is Some {
147 EntryOwner::<C>::active_entry_not_in_free_pool(entry, regions, changed_idx);
148 }
149 };
150 }
151
152 pub proof fn metaregion_preserved_under_path_insert(
156 self,
157 regions0: MetaRegionOwners,
158 regions1: MetaRegionOwners,
159 changed_idx: usize,
160 new_path: TreePath<NR_ENTRIES>,
161 )
162 requires
163 self.inv(),
164 self.metaregion_sound(regions0),
165 regions0.inv(),
166 regions0.slot_owners.contains_key(changed_idx),
167 regions1.slots == regions0.slots,
168 regions1.slot_owners.dom() =~= regions0.slot_owners.dom(),
169 forall|i: usize|
170 #![trigger regions1.slot_owners[i]]
171 i != changed_idx ==> regions0.slot_owners[i] == regions1.slot_owners[i],
172 regions1.slot_owners[changed_idx].inner_perms
173 == regions0.slot_owners[changed_idx].inner_perms,
174 regions1.slot_owners[changed_idx].self_addr
175 == regions0.slot_owners[changed_idx].self_addr,
176 regions1.slot_owners[changed_idx].usage == regions0.slot_owners[changed_idx].usage,
177 regions1.slot_owners[changed_idx].paths_in_pt
178 == regions0.slot_owners[changed_idx].paths_in_pt.insert(new_path),
179 self.no_node_at_idx(changed_idx),
180 ensures
181 self.metaregion_sound(regions1),
182 {
183 let f = PageTableOwner::<C>::metaregion_sound_pred(regions0);
184 let g = PageTableOwner::<C>::metaregion_sound_pred(regions1);
185 let guard = |entry: EntryOwner<C>, _p: TreePath<NR_ENTRIES>|
186 entry.is_node() && entry.meta_slot_paddr() is Some ==> frame_to_index(
187 entry.meta_slot_paddr().unwrap(),
188 ) != changed_idx;
189 let f_strong = |entry: EntryOwner<C>, path: TreePath<NR_ENTRIES>|
190 f(entry, path) && guard(entry, path);
191
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.meta_slot_paddr() is Some {
196 let eidx = frame_to_index(entry.meta_slot_paddr().unwrap());
197 if eidx != changed_idx {
198 assert(entry.is_frame() && entry.parent_level > 1 ==> {
204 let pa = entry.frame.unwrap().mapped_pa;
205 let nr_pages = page_size(entry.parent_level) / PAGE_SIZE;
206 forall|j: usize|
207 0 < j < nr_pages ==> {
208 let sub_idx = #[trigger] frame_to_index(
209 (pa + j * PAGE_SIZE) as usize,
210 );
211 sub_idx != changed_idx || regions1.slot_owners[sub_idx].usage
212 == crate::specs::mm::frame::meta_owners::PageUsage::MMIO
213 || (regions1.slots.contains_key(sub_idx)
214 && regions1.slot_owners[sub_idx].inner_perms.ref_count.value()
215 != REF_COUNT_UNUSED
216 && regions1.slot_owners[sub_idx].inner_perms.ref_count.value()
217 > 0)
218 }
219 });
220 entry.metaregion_sound_one_slot_changed(regions0, regions1, changed_idx);
221 } else {
222 assert(!entry.is_node());
223 assert(entry.is_frame());
224 if entry.parent_level > 1 {
225 assert(entry.frame_sub_pages_valid(regions1));
226 }
227 }
228 }
229 };
230 };
231
232 self.and_map_full_tree(f, guard);
233 self.map_children_implies(f_strong, g);
234
235 assert forall|i: int|
236 #![trigger self.continuations[i]]
237 self.level - 1 <= i
238 < NR_LEVELS implies self.continuations[i].entry_own.metaregion_sound(regions1) by {
239 let cont_entry = self.continuations[i].entry_own;
240 if cont_entry.meta_slot_paddr() is Some {
241 assert(cont_entry.is_frame() && cont_entry.parent_level > 1 ==> {
243 let pa = cont_entry.frame.unwrap().mapped_pa;
244 let nr_pages = page_size(cont_entry.parent_level) / PAGE_SIZE;
245 forall|j: usize|
246 0 < j < nr_pages ==> {
247 let sub_idx = #[trigger] frame_to_index((pa + j * PAGE_SIZE) as usize);
248 sub_idx != changed_idx || regions1.slot_owners[sub_idx].usage
249 == crate::specs::mm::frame::meta_owners::PageUsage::MMIO || (
250 regions1.slots.contains_key(sub_idx)
251 && regions1.slot_owners[sub_idx].inner_perms.ref_count.value()
252 != REF_COUNT_UNUSED
253 && regions1.slot_owners[sub_idx].inner_perms.ref_count.value() > 0)
254 }
255 });
256 cont_entry.metaregion_sound_one_slot_changed(regions0, regions1, changed_idx);
257 }
258 };
259 }
260
261 pub open spec fn path_removable_at_idx(
271 self,
272 idx: usize,
273 removed_path: TreePath<NR_ENTRIES>,
274 ) -> bool {
275 &&& self.map_full_tree(
276 |e: EntryOwner<C>, _p: TreePath<NR_ENTRIES>|
277 e.meta_slot_paddr() is Some && frame_to_index(e.meta_slot_paddr().unwrap()) == idx
278 ==> !e.is_node() && (e.is_frame() ==> e.path != removed_path),
279 )
280 &&& forall|i: int|
281 #![trigger self.continuations[i]]
282 self.level - 1 <= i < NR_LEVELS ==> {
283 let e = self.continuations[i].entry_own;
284 e.meta_slot_paddr() is Some && frame_to_index(e.meta_slot_paddr().unwrap()) == idx
285 ==> !e.is_node() && (e.is_frame() ==> e.path != removed_path)
286 }
287 }
288
289 pub open spec fn no_frame_with_path(self, removed_path: TreePath<NR_ENTRIES>) -> bool {
296 &&& self.map_full_tree(
297 |e: EntryOwner<C>, _p: TreePath<NR_ENTRIES>| e.is_frame() ==> e.path != removed_path,
298 )
299 &&& forall|i: int|
300 #![trigger self.continuations[i]]
301 self.level - 1 <= i < NR_LEVELS ==> {
302 let e = self.continuations[i].entry_own;
303 e.is_frame() ==> e.path != removed_path
304 }
305 }
306
307 pub proof fn no_frame_with_path_from_no_view_mapping(self, removed_path: TreePath<NR_ENTRIES>)
321 requires
322 self.inv(),
323 forall|m: Mapping|
324 #![trigger self@.mappings.contains(m)]
325 self@.mappings.contains(m) ==> m.va_range.start != vaddr_of::<C>(
326 removed_path,
327 ) as int,
328 ensures
329 self.no_frame_with_path(removed_path),
330 {
331 let g = |e: EntryOwner<C>, _p: TreePath<NR_ENTRIES>|
332 e.is_frame() ==> e.path != removed_path;
333
334 assert forall|i: int|
337 #![trigger self.continuations[i]]
338 self.level - 1 <= i < NR_LEVELS implies self.continuations[i].map_children(g) by {
339 self.inv_continuation(i);
340 let cont = self.continuations[i];
341 reveal(CursorContinuation::inv_children);
342 assert forall|j: int|
343 #![trigger cont.children[j]]
344 0 <= j < cont.children.len()
345 && cont.children[j] is Some implies cont.children[j].unwrap().tree_predicate_map(
346 cont.path().push_tail(j as usize), g) by {
347 cont.inv_children_unroll(j);
348 cont.pt_inv_children_unroll(j);
349 cont.inv_children_rel_unroll(j);
350 let child = cont.children[j].unwrap();
351 let child_path = cont.path().push_tail(j as usize);
352 assert(child.value.path == child_path);
355 assert(child_path.inv());
356 PageTableOwner::<C>::pt_inv_implies_path_correct(child, child_path);
358 assert forall|m: Mapping|
360 #![trigger self@.mappings.contains(m)]
361 PageTableOwner(child).view_rec(child_path).contains(
362 m,
363 ) implies self@.mappings.contains(m) by {
364 assert(cont.view_mappings().contains(m));
365 assert(self.view_mappings().contains(m));
366 };
367 PageTableOwner(child).no_frame_with_path_rec(
369 child_path,
370 removed_path,
371 self@.mappings,
372 );
373 };
374 };
375
376 assert forall|i: int|
378 #![trigger self.continuations[i]]
379 self.level - 1 <= i < NR_LEVELS implies {
380 let e = self.continuations[i].entry_own;
381 e.is_frame() ==> e.path != removed_path
382 } by {
383 self.inv_continuation(i);
384 };
385
386 assert(self.no_frame_with_path(removed_path));
387 }
388
389 pub proof fn path_removable_from_no_node_and_no_frame_path(
396 self,
397 idx: usize,
398 removed_path: TreePath<NR_ENTRIES>,
399 )
400 requires
401 self.inv(),
402 self.no_node_at_idx(idx),
403 self.no_frame_with_path(removed_path),
404 ensures
405 self.path_removable_at_idx(idx, removed_path),
406 {
407 let nn = |e: EntryOwner<C>, _p: TreePath<NR_ENTRIES>|
408 e.is_node() && e.meta_slot_paddr() is Some ==> frame_to_index(
409 e.meta_slot_paddr().unwrap(),
410 ) != idx;
411 let nf = |e: EntryOwner<C>, _p: TreePath<NR_ENTRIES>|
412 e.is_frame() ==> e.path != removed_path;
413 let r = |e: EntryOwner<C>, _p: TreePath<NR_ENTRIES>|
414 e.meta_slot_paddr() is Some && frame_to_index(e.meta_slot_paddr().unwrap()) == idx
415 ==> !e.is_node() && (e.is_frame() ==> e.path != removed_path);
416
417 assert(OwnerSubtree::implies(
419 |e: EntryOwner<C>, p: TreePath<NR_ENTRIES>| nn(e, p) && nf(e, p),
420 r,
421 )) by {
422 assert forall|e: EntryOwner<C>, p: TreePath<NR_ENTRIES>|
423 e.inv() && nn(e, p) && nf(e, p) implies #[trigger] r(e, p) by {}
424 };
425 self.and_map_full_tree(nn, nf);
427 self.map_children_implies(
428 |e: EntryOwner<C>, p: TreePath<NR_ENTRIES>| nn(e, p) && nf(e, p),
429 r,
430 );
431
432 assert forall|i: int|
434 #![trigger self.continuations[i]]
435 self.level - 1 <= i < NR_LEVELS implies {
436 let e = self.continuations[i].entry_own;
437 e.meta_slot_paddr() is Some && frame_to_index(e.meta_slot_paddr().unwrap()) == idx
438 ==> !e.is_node() && (e.is_frame() ==> e.path != removed_path)
439 } by {
440 let e = self.continuations[i].entry_own;
441 assert(nn(e, e.path));
442 assert(nf(e, e.path));
443 }
444 }
445
446 pub proof fn metaregion_preserved_under_path_remove(
456 self,
457 regions0: MetaRegionOwners,
458 regions1: MetaRegionOwners,
459 changed_idx: usize,
460 removed_path: TreePath<NR_ENTRIES>,
461 )
462 requires
463 self.inv(),
464 self.metaregion_sound(regions0),
465 regions0.inv(),
466 regions0.slot_owners.contains_key(changed_idx),
467 regions1.slots == regions0.slots,
468 regions1.slot_owners.dom() =~= regions0.slot_owners.dom(),
469 forall|i: usize|
470 #![trigger regions1.slot_owners[i]]
471 i != changed_idx ==> regions0.slot_owners[i] == regions1.slot_owners[i],
472 regions1.slot_owners[changed_idx].inner_perms
473 == regions0.slot_owners[changed_idx].inner_perms,
474 regions1.slot_owners[changed_idx].self_addr
475 == regions0.slot_owners[changed_idx].self_addr,
476 regions1.slot_owners[changed_idx].usage == regions0.slot_owners[changed_idx].usage,
477 regions1.slot_owners[changed_idx].paths_in_pt
478 == regions0.slot_owners[changed_idx].paths_in_pt.remove(removed_path),
479 self.path_removable_at_idx(changed_idx, removed_path),
480 ensures
481 self.metaregion_sound(regions1),
482 {
483 let f = PageTableOwner::<C>::metaregion_sound_pred(regions0);
484 let g = PageTableOwner::<C>::metaregion_sound_pred(regions1);
485 let guard = |entry: EntryOwner<C>, _p: TreePath<NR_ENTRIES>|
486 entry.meta_slot_paddr() is Some && frame_to_index(entry.meta_slot_paddr().unwrap())
487 == changed_idx ==> !entry.is_node() && (entry.is_frame() ==> entry.path
488 != removed_path);
489 let f_strong = |entry: EntryOwner<C>, path: TreePath<NR_ENTRIES>|
490 f(entry, path) && guard(entry, path);
491
492 assert(OwnerSubtree::implies(f_strong, g)) by {
493 assert forall|entry: EntryOwner<C>, path: TreePath<NR_ENTRIES>|
494 entry.inv() && f_strong(entry, path) implies #[trigger] g(entry, path) by {
495 if entry.meta_slot_paddr() is Some {
496 let eidx = frame_to_index(entry.meta_slot_paddr().unwrap());
497 if eidx != changed_idx {
498 assert(entry.is_frame() && entry.parent_level > 1 ==> {
503 let pa = entry.frame.unwrap().mapped_pa;
504 let nr_pages = page_size(entry.parent_level) / PAGE_SIZE;
505 forall|j: usize|
506 0 < j < nr_pages ==> {
507 let sub_idx = #[trigger] frame_to_index(
508 (pa + j * PAGE_SIZE) as usize,
509 );
510 sub_idx != changed_idx || regions1.slot_owners[sub_idx].usage
511 == crate::specs::mm::frame::meta_owners::PageUsage::MMIO
512 || (regions1.slots.contains_key(sub_idx)
513 && regions1.slot_owners[sub_idx].inner_perms.ref_count.value()
514 != REF_COUNT_UNUSED
515 && regions1.slot_owners[sub_idx].inner_perms.ref_count.value()
516 > 0)
517 }
518 });
519 entry.metaregion_sound_one_slot_changed(regions0, regions1, changed_idx);
520 } else {
521 assert(!entry.is_node());
528 if entry.is_frame() {
529 assert(entry.path != removed_path);
530 assert(regions0.slot_owners[changed_idx].paths_in_pt.contains(
531 entry.path,
532 ));
533 assert(regions1.slot_owners[changed_idx].paths_in_pt.contains(
534 entry.path,
535 ));
536 entry.frame_sub_pages_valid_preserved_at_own_slot(regions0, regions1);
537 }
538 assert(entry.metaregion_sound(regions1));
539 }
540 }
541 };
542 };
543
544 self.and_map_full_tree(f, guard);
545 self.map_children_implies(f_strong, g);
546
547 assert forall|i: int|
548 #![trigger self.continuations[i]]
549 self.level - 1 <= i
550 < NR_LEVELS implies self.continuations[i].entry_own.metaregion_sound(regions1) by {
551 let cont_entry = self.continuations[i].entry_own;
552 if cont_entry.meta_slot_paddr() is Some {
553 let eidx = frame_to_index(cont_entry.meta_slot_paddr().unwrap());
554 if eidx != changed_idx {
555 assert(cont_entry.is_frame() && cont_entry.parent_level > 1 ==> {
556 let pa = cont_entry.frame.unwrap().mapped_pa;
557 let nr_pages = page_size(cont_entry.parent_level) / PAGE_SIZE;
558 forall|j: usize|
559 0 < j < nr_pages ==> {
560 let sub_idx = #[trigger] frame_to_index(
561 (pa + j * PAGE_SIZE) as usize,
562 );
563 sub_idx != changed_idx || regions1.slot_owners[sub_idx].usage
564 == crate::specs::mm::frame::meta_owners::PageUsage::MMIO || (
565 regions1.slots.contains_key(sub_idx)
566 && regions1.slot_owners[sub_idx].inner_perms.ref_count.value()
567 != REF_COUNT_UNUSED
568 && regions1.slot_owners[sub_idx].inner_perms.ref_count.value()
569 > 0)
570 }
571 });
572 cont_entry.metaregion_sound_one_slot_changed(regions0, regions1, changed_idx);
573 } else {
574 assert(!cont_entry.is_node());
575 if cont_entry.is_frame() {
576 assert(cont_entry.path != removed_path);
577 assert(regions0.slot_owners[changed_idx].paths_in_pt.contains(
578 cont_entry.path,
579 ));
580 assert(regions1.slot_owners[changed_idx].paths_in_pt.contains(
581 cont_entry.path,
582 ));
583 cont_entry.frame_sub_pages_valid_preserved_at_own_slot(regions0, regions1);
584 }
585 assert(cont_entry.metaregion_sound(regions1));
586 }
587 }
588 };
589 }
590}
591
592}