1use vstd::prelude::*;
16
17use vstd_extra::{ghost_tree::*, ownership::*};
18
19use crate::specs::{
20 arch::*,
21 mm::{
22 frame::{
23 mapping::frame_to_index, meta_owners::PageUsage, meta_region_owners::MetaRegionOwners,
24 },
25 page_table::{
26 Mapping,
27 cursor::owners::{CursorContinuation, CursorOwner},
28 node::entry_owners::EntryOwner,
29 owners::{OwnerSubtree, PageTableOwner, vaddr_of},
30 },
31 },
32};
33
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 let cont = self.continuations[i];
87 reveal(CursorContinuation::inv_children);
88 assert forall|j: int|
89 #![trigger cont.children[j]]
90 0 <= j < cont.children.len()
91 && cont.children[j] is Some implies cont.children[j].unwrap().subtree_satisfies(
92 cont.path().push_tail(j),
93 combined,
94 ) by {
95 cont.inv_children_unroll(j);
96 OwnerSubtree::lemma_subtree_satisfies_implies_and(
97 cont.children[j].unwrap(),
98 cont.path().push_tail(j),
99 f,
100 guard,
101 combined,
102 );
103 };
104 };
105 }
106
107 pub proof fn no_node_at_idx_from_slot_key(self, regions: MetaRegionOwners, changed_idx: int)
117 requires
118 self.inv(),
119 regions.inv(),
120 self.metaregion_sound(regions),
121 regions.contains(changed_idx),
122 regions.slot_owners[changed_idx].usage !is PageTable,
123 ensures
124 self.no_node_at_idx(changed_idx),
125 {
126 let msp = PageTableOwner::<C>::metaregion_sound_pred(regions);
127 let target = |e: EntryOwner<C>, _p: TreePath<NR_ENTRIES>|
128 e.is_node() && e.meta_slot_paddr() is Some ==> frame_to_index(
129 e.meta_slot_paddr().unwrap(),
130 ) != changed_idx;
131
132 self.map_children_implies(msp, target);
133
134 assert forall|i: int|
135 #![trigger self.continuations[i]]
136 self.level - 1 <= i < NR_LEVELS implies {
137 let e = self.continuations[i].entry_own;
138 e.is_node() && e.meta_slot_paddr() is Some ==> frame_to_index(
139 e.meta_slot_paddr().unwrap(),
140 ) != changed_idx
141 } by {
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 self.map_children_implies(f_strong, g);
193
194 assert forall|i: int|
195 #![trigger self.continuations[i]]
196 self.level - 1 <= i
197 < NR_LEVELS implies self.continuations[i].entry_own.metaregion_sound(regions1) by {
198 let cont_entry = self.continuations[i].entry_own;
199 if cont_entry.meta_slot_paddr() is Some {
200 cont_entry.metaregion_sound_one_slot_changed(regions0, regions1, changed_idx);
202 }
203 };
204 }
205
206 pub open spec fn path_removable_at_idx(
216 self,
217 idx: int,
218 removed_path: TreePath<NR_ENTRIES>,
219 ) -> bool {
220 &&& self.map_full_tree(
221 |e: EntryOwner<C>, _p: TreePath<NR_ENTRIES>|
222 e.meta_slot_paddr() is Some && frame_to_index(e.meta_slot_paddr()->0) == idx
223 ==> !e.is_node() && (e.is_frame() ==> e.path != removed_path),
224 )
225 &&& forall|i: int|
226 #![trigger self.continuations[i]]
227 self.level - 1 <= i < NR_LEVELS ==> {
228 let e = self.continuations[i].entry_own;
229 e.meta_slot_paddr() is Some && frame_to_index(e.meta_slot_paddr()->0) == idx
230 ==> !e.is_node() && (e.is_frame() ==> e.path != removed_path)
231 }
232 }
233
234 pub open spec fn no_frame_with_path(self, removed_path: TreePath<NR_ENTRIES>) -> bool {
241 &&& self.map_full_tree(
242 |e: EntryOwner<C>, _p: TreePath<NR_ENTRIES>| 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.is_frame() ==> e.path != removed_path
249 }
250 }
251
252 pub proof fn no_frame_with_path_from_no_view_mapping(self, removed_path: TreePath<NR_ENTRIES>)
266 requires
267 self.inv(),
268 forall|m: Mapping|
269 #![trigger self@.mappings.contains(m)]
270 self@.mappings.contains(m) ==> m.va_range.start != vaddr_of::<C>(removed_path),
271 ensures
272 self.no_frame_with_path(removed_path),
273 {
274 broadcast use CursorContinuation::group_lemmas;
275
276 let g = |e: EntryOwner<C>, _p: TreePath<NR_ENTRIES>|
277 e.is_frame() ==> e.path != removed_path;
278
279 assert forall|i: int|
282 #![trigger self.continuations[i]]
283 self.level - 1 <= i < NR_LEVELS implies self.continuations[i].map_children(g) by {
284 self.inv_continuation(i);
285 let cont = self.continuations[i];
286 reveal(CursorContinuation::inv_children);
287 assert forall|j: int|
288 #![trigger cont.children[j]]
289 0 <= j < cont.children.len()
290 && cont.children[j] is Some implies cont.children[j].unwrap().subtree_satisfies(
291 cont.path().push_tail(j),
292 g,
293 ) by {
294 cont.inv_children_unroll(j);
295 cont.pt_inv_children_unroll(j);
296 cont.inv_children_rel_unroll(j);
297 let child = cont.children[j].unwrap();
298 let child_path = cont.path().push_tail(j);
299 PageTableOwner::<C>::pt_inv_implies_path_correct(child, child_path);
303 assert forall|m: Mapping|
305 #![trigger self@.mappings.contains(m)]
306 PageTableOwner(child).view_rec(child_path).contains(
307 m,
308 ) implies self@.mappings.contains(m) by {
309 self.lemma_view_mappings_intro(m, i);
310 };
311 PageTableOwner(child).no_frame_with_path_rec(
313 child_path,
314 removed_path,
315 self@.mappings,
316 );
317 };
318 };
319
320 assert forall|i: int|
322 #![trigger self.continuations[i]]
323 self.level - 1 <= i < NR_LEVELS implies {
324 let e = self.continuations[i].entry_own;
325 e.is_frame() ==> e.path != removed_path
326 } by {
327 self.inv_continuation(i);
328 };
329 }
330
331 pub proof fn path_removable_from_no_node_and_no_frame_path(
338 self,
339 idx: int,
340 removed_path: TreePath<NR_ENTRIES>,
341 )
342 requires
343 self.inv(),
344 self.no_node_at_idx(idx),
345 self.no_frame_with_path(removed_path),
346 ensures
347 self.path_removable_at_idx(idx, removed_path),
348 {
349 let nn = |e: EntryOwner<C>, _p: TreePath<NR_ENTRIES>|
350 e.is_node() && e.meta_slot_paddr() is Some ==> frame_to_index(
351 e.meta_slot_paddr().unwrap(),
352 ) != idx;
353 let nf = |e: EntryOwner<C>, _p: TreePath<NR_ENTRIES>|
354 e.is_frame() ==> e.path != removed_path;
355 let r = |e: EntryOwner<C>, _p: TreePath<NR_ENTRIES>|
356 e.meta_slot_paddr() is Some && frame_to_index(e.meta_slot_paddr().unwrap()) == idx
357 ==> !e.is_node() && (e.is_frame() ==> e.path != removed_path);
358
359 self.and_map_full_tree(nn, nf);
362 self.map_children_implies(
363 |e: EntryOwner<C>, p: TreePath<NR_ENTRIES>| nn(e, p) && nf(e, p),
364 r,
365 );
366
367 assert forall|i: int|
369 #![trigger self.continuations[i]]
370 self.level - 1 <= i < NR_LEVELS implies {
371 let e = self.continuations[i].entry_own;
372 e.meta_slot_paddr() is Some && frame_to_index(e.meta_slot_paddr().unwrap()) == idx
373 ==> !e.is_node() && (e.is_frame() ==> e.path != removed_path)
374 } by {
375 let e = self.continuations[i].entry_own;
376 }
377 }
378
379 pub proof fn metaregion_preserved_under_path_remove(
389 self,
390 regions0: MetaRegionOwners,
391 regions1: MetaRegionOwners,
392 changed_idx: int,
393 removed_path: TreePath<NR_ENTRIES>,
394 )
395 requires
396 self.inv(),
397 self.metaregion_sound(regions0),
398 regions0.inv(),
399 regions0.contains(changed_idx),
400 regions1.slots == regions0.slots,
401 regions1.slot_owners.dom() =~= regions0.slot_owners.dom(),
402 forall|i: int|
403 #![trigger regions1.slot_owners[i]]
404 i != changed_idx ==> regions0.slot_owners[i] == regions1.slot_owners[i],
405 regions1.slot_owners[changed_idx].same_permissions(regions0.slot_owners[changed_idx]),
406 regions1.slot_owners[changed_idx].slot_vaddr
407 == regions0.slot_owners[changed_idx].slot_vaddr,
408 regions1.slot_owners[changed_idx].usage == regions0.slot_owners[changed_idx].usage,
409 regions1.slot_owners[changed_idx].paths_in_pt
410 == regions0.slot_owners[changed_idx].paths_in_pt.remove(removed_path),
411 self.path_removable_at_idx(changed_idx, removed_path),
412 ensures
413 self.metaregion_sound(regions1),
414 {
415 let f = PageTableOwner::<C>::metaregion_sound_pred(regions0);
416 let g = PageTableOwner::<C>::metaregion_sound_pred(regions1);
417 let guard = |entry: EntryOwner<C>, _p: TreePath<NR_ENTRIES>|
418 entry.meta_slot_paddr() is Some && frame_to_index(entry.meta_slot_paddr().unwrap())
419 == changed_idx ==> !entry.is_node() && (entry.is_frame() ==> entry.path
420 != removed_path);
421 let f_strong = |entry: EntryOwner<C>, path: TreePath<NR_ENTRIES>|
422 f(entry, path) && guard(entry, path);
423
424 self.and_map_full_tree(f, guard);
425 self.map_children_implies(f_strong, g);
426
427 assert forall|i: int|
428 #![trigger self.continuations[i]]
429 self.level - 1 <= i
430 < NR_LEVELS implies self.continuations[i].entry_own.metaregion_sound(regions1) by {
431 let cont_entry = self.continuations[i].entry_own;
432 if cont_entry.meta_slot_paddr() is Some {
433 let eidx = frame_to_index(cont_entry.meta_slot_paddr().unwrap());
434 if eidx != changed_idx {
435 cont_entry.metaregion_sound_one_slot_changed(regions0, regions1, changed_idx);
436 } else {
437 if cont_entry.is_frame() {
438 cont_entry.frame_sub_pages_valid_preserved_at_own_slot(regions0, regions1);
439 }
440 }
441 }
442 };
443 }
444
445 pub proof fn take_next_remove_path_preserves_metaregion(
448 self,
449 owner_before_replace: Self,
450 regions0: MetaRegionOwners,
451 regions1: MetaRegionOwners,
452 removed_idx: int,
453 removed_path: TreePath<NR_ENTRIES>,
454 target: Mapping,
455 )
456 requires
457 self.inv(),
458 owner_before_replace.inv(),
459 owner_before_replace.in_locked_range(),
460 self.metaregion_sound(regions0),
461 regions0.inv(),
462 regions0.contains(removed_idx),
463 regions0.slot_owners[removed_idx].usage !is PageTable,
464 self@.mappings == owner_before_replace@.mappings - PageTableOwner(
465 owner_before_replace.cur_subtree(),
466 )@.mappings,
467 PageTableOwner(owner_before_replace.cur_subtree())@.mappings == set![target],
468 owner_before_replace.cur_subtree().value().path == removed_path,
469 regions1.slots == regions0.slots,
470 regions1.slot_owners.dom() =~= regions0.slot_owners.dom(),
471 forall|i: int|
472 #![trigger regions1.slot_owners[i]]
473 i != removed_idx ==> regions0.slot_owners[i] == regions1.slot_owners[i],
474 regions1.slot_owners[removed_idx].same_permissions(regions0.slot_owners[removed_idx]),
475 regions1.slot_owners[removed_idx].slot_vaddr
476 == regions0.slot_owners[removed_idx].slot_vaddr,
477 regions1.slot_owners[removed_idx].usage == regions0.slot_owners[removed_idx].usage,
478 regions1.slot_owners[removed_idx].paths_in_pt
479 == regions0.slot_owners[removed_idx].paths_in_pt.remove(removed_path),
480 ensures
481 self.metaregion_sound(regions1),
482 {
483 self.no_node_at_idx_from_slot_key(regions0, removed_idx);
484
485 owner_before_replace.cur_subtree_eq_filtered_mappings_path();
486
487 let ghost sv = vaddr_of::<C>(removed_path) as int;
488 let ghost sz = page_size(owner_before_replace.level) as int;
489 assert(sz > 0) by {
490 crate::specs::mm::page_table::cursor::page_size_lemmas::lemma_page_size_ge_page_size(
491 owner_before_replace.level,
492 );
493 };
494 assert forall|mm: Mapping| #[trigger] self@.mappings.contains(mm) implies mm.va_range.start
495 != sv by {};
496
497 self.no_frame_with_path_from_no_view_mapping(removed_path);
498 self.path_removable_from_no_node_and_no_frame_path(removed_idx, removed_path);
499 self.metaregion_preserved_under_path_remove(regions0, regions1, removed_idx, removed_path);
500 }
501}
502
503}