1use vstd::{modes::tracked_swap, prelude::*, simple_pptr::PointsTo};
2use vstd_extra::{ghost_tree::*, ownership::*};
3
4use crate::specs::{
5 arch::*,
6 mm::{
7 frame::{mapping::*, meta_owners::PageUsage, meta_region_owners::MetaRegionOwners},
8 page_table::*,
9 },
10};
11
12use crate::arch::mm::PagingConsts;
13use crate::mm::{
14 Paddr, PagingConstsTrait, PagingLevel, Vaddr,
15 frame::meta::{
16 MetaSlot, REF_COUNT_MAX, REF_COUNT_UNIQUE, REF_COUNT_UNUSED, mapping::meta_to_frame,
17 },
18 page_prop::PageProperty,
19 page_table::*,
20};
21use core::marker::PhantomData;
22
23verus! {
24
25pub tracked struct FrameEntryOwner<C: PageTableConfig> {
31 pub ghost mapped_pa: usize,
32 pub ghost prop: PageProperty,
33 pub permission: Option<C::Perm>,
34}
35
36pub tracked enum EntryOwnerKind<C: PageTableConfig> {
37 Node(NodeOwner<C>),
38 Frame(FrameEntryOwner<C>),
39 Borrowed(ghost Set<Mapping>),
49 Absent,
50}
51
52pub tracked struct EntryOwner<C: PageTableConfig> {
53 pub kind: EntryOwnerKind<C>,
54 pub ghost path: TreePath<NR_ENTRIES>,
55 pub ghost parent_level: PagingLevel,
56}
57
58impl<C: PageTableConfig> EntryOwner<C> {
59 #[verifier::inline]
60 pub open spec fn is_node(self) -> bool {
61 self.kind is Node
62 }
63
64 #[verifier::inline]
65 pub open spec fn is_frame(self) -> bool {
66 self.kind is Frame
67 }
68
69 #[verifier::inline]
70 pub open spec fn is_absent(self) -> bool {
71 self.kind is Absent
72 }
73
74 #[verifier::inline]
75 pub open spec fn is_borrowed(self) -> bool {
76 self.kind is Borrowed
77 }
78
79 pub open spec fn node(self) -> NodeOwner<C> {
80 self.kind->Node_0
81 }
82
83 pub open spec fn frame(self) -> FrameEntryOwner<C> {
84 self.kind->Frame_0
85 }
86
87 pub open spec fn frame_permission(self) -> Option<C::Perm>
88 recommends
89 self.is_frame(),
90 {
91 self.frame().permission
92 }
93
94 pub open spec fn frame_is_tracked(self) -> bool
95 recommends
96 self.is_frame(),
97 {
98 self.frame_permission() is Some
99 }
100
101 pub open spec fn borrowed(self) -> Set<Mapping> {
102 self.kind->Borrowed_0
103 }
104
105 pub open spec fn new_absent(path: TreePath<NR_ENTRIES>, parent_level: PagingLevel) -> Self {
106 EntryOwner { kind: EntryOwnerKind::Absent, path, parent_level }
107 }
108
109 pub open spec fn new_frame(
110 paddr: Paddr,
111 path: TreePath<NR_ENTRIES>,
112 parent_level: PagingLevel,
113 prop: PageProperty,
114 permission: Option<C::Perm>,
115 ) -> Self {
116 EntryOwner {
117 kind: EntryOwnerKind::Frame(FrameEntryOwner { mapped_pa: paddr, prop, permission }),
118 path,
119 parent_level,
120 }
121 }
122
123 pub open spec fn new_node(node: NodeOwner<C>, path: TreePath<NR_ENTRIES>) -> Self {
124 EntryOwner {
125 kind: EntryOwnerKind::Node(node),
126 path,
127 parent_level: (node.level() + 1) as PagingLevel,
128 }
129 }
130
131 pub proof fn tracked_new_absent(
132 path: TreePath<NR_ENTRIES>,
133 parent_level: PagingLevel,
134 ) -> tracked Self
135 returns
136 Self::new_absent(path, parent_level),
137 {
138 Self { kind: EntryOwnerKind::Absent, path, parent_level }
139 }
140
141 pub proof fn tracked_take_node(tracked &mut self) -> (tracked res: NodeOwner<C>)
142 requires
143 old(self).kind is Node,
144 ensures
145 res == old(self).node(),
146 *final(self) == (EntryOwner { kind: EntryOwnerKind::Absent, ..*old(self) }),
147 {
148 let tracked mut tmp = EntryOwnerKind::Absent;
149 tracked_swap(&mut self.kind, &mut tmp);
150 match tmp {
151 EntryOwnerKind::Node(node) => node,
152 _ => { proof_from_false() },
153 }
154 }
155
156 pub proof fn tracked_put_node(tracked &mut self, tracked node: NodeOwner<C>)
157 ensures
158 *final(self) == (EntryOwner { kind: EntryOwnerKind::Node(node), ..*old(self) }),
159 {
160 self.kind = EntryOwnerKind::Node(node);
161 }
162
163 pub proof fn tracked_borrow_node(tracked &self) -> (tracked res: &NodeOwner<C>)
164 requires
165 self.kind is Node,
166 ensures
167 *res == self.node(),
168 {
169 match self.kind {
170 EntryOwnerKind::Node(ref node) => node,
171 _ => { proof_from_false() },
172 }
173 }
174
175 pub proof fn tracked_borrow_mut_node(tracked &mut self) -> (tracked res: &mut NodeOwner<C>)
176 requires
177 old(self).kind is Node,
178 ensures
179 *res == old(self).node(),
180 *final(self) == (EntryOwner { kind: EntryOwnerKind::Node(*final(res)), ..*old(self) }),
181 {
182 match self.kind {
183 EntryOwnerKind::Node(ref mut node) => node,
184 _ => { proof_from_false() },
185 }
186 }
187
188 pub proof fn tracked_set_frame_prop(tracked &mut self, prop: PageProperty)
189 requires
190 old(self).kind is Frame,
191 ensures
192 *final(self) == (EntryOwner {
193 kind: EntryOwnerKind::Frame(FrameEntryOwner { prop, ..old(self).frame() }),
194 ..*old(self)
195 }),
196 {
197 let tracked mut old_kind = EntryOwnerKind::Absent;
198 tracked_swap(&mut self.kind, &mut old_kind);
199 match old_kind {
200 EntryOwnerKind::Frame(frame) => {
201 let ghost mapped_pa = frame.mapped_pa;
202 let tracked permission = frame.permission;
203 self.kind = EntryOwnerKind::Frame(FrameEntryOwner { mapped_pa, prop, permission });
204 },
205 _ => proof_from_false(),
206 }
207 }
208
209 pub proof fn tracked_take_frame_permission(tracked &mut self) -> (tracked res: Option<C::Perm>)
210 requires
211 old(self).is_frame(),
212 ensures
213 res == old(self).frame_permission(),
214 *final(self) == (EntryOwner {
215 kind: EntryOwnerKind::Frame(
216 FrameEntryOwner { permission: None, ..old(self).frame() },
217 ),
218 ..*old(self)
219 }),
220 {
221 let tracked mut old_kind = EntryOwnerKind::Absent;
222 tracked_swap(&mut self.kind, &mut old_kind);
223 match old_kind {
224 EntryOwnerKind::Frame(frame) => {
225 let ghost mapped_pa = frame.mapped_pa;
226 let ghost prop = frame.prop;
227 let tracked permission = frame.permission;
228 self.kind = EntryOwnerKind::Frame(
229 FrameEntryOwner { mapped_pa, prop, permission: None },
230 );
231 permission
232 },
233 _ => proof_from_false(),
234 }
235 }
236
237 pub proof fn tracked_put_frame_permission(
238 tracked &mut self,
239 tracked permission: Option<C::Perm>,
240 )
241 requires
242 old(self).is_frame(),
243 old(self).frame_permission() is None,
244 ensures
245 *final(self) == (EntryOwner {
246 kind: EntryOwnerKind::Frame(FrameEntryOwner { permission, ..old(self).frame() }),
247 ..*old(self)
248 }),
249 {
250 let tracked mut old_kind = EntryOwnerKind::Absent;
251 tracked_swap(&mut self.kind, &mut old_kind);
252 match old_kind {
253 EntryOwnerKind::Frame(frame) => {
254 let ghost mapped_pa = frame.mapped_pa;
255 let ghost prop = frame.prop;
256 self.kind = EntryOwnerKind::Frame(FrameEntryOwner { mapped_pa, prop, permission });
257 },
258 _ => proof_from_false(),
259 }
260 }
261
262 pub proof fn tracked_new_frame(
263 paddr: Paddr,
264 path: TreePath<NR_ENTRIES>,
265 parent_level: PagingLevel,
266 prop: PageProperty,
267 tracked permission: Option<C::Perm>,
268 ) -> tracked Self
269 returns
270 Self::new_frame(paddr, path, parent_level, prop, permission),
271 {
272 Self {
273 kind: EntryOwnerKind::Frame(FrameEntryOwner { mapped_pa: paddr, prop, permission }),
274 path,
275 parent_level,
276 }
277 }
278
279 pub proof fn tracked_new_untracked_frame(
287 paddr: Paddr,
288 parent_level: PagingLevel,
289 prop: PageProperty,
290 ) -> (tracked res: Self)
291 requires
292 valid_frame_paddr(paddr),
293 1 <= parent_level < NR_LEVELS,
294 paddr % page_size::<PagingConsts>(parent_level) == 0,
295 paddr + page_size::<PagingConsts>(parent_level) <= MAX_PADDR,
296 C::raw_item_well_formed((paddr, parent_level, prop, Tracked(None))),
297 C::E::new_page_req(paddr, parent_level, prop),
298 ensures
299 res.is_frame(),
300 res.frame().mapped_pa == paddr,
301 res.frame().prop == prop,
302 !res.frame_is_tracked(),
303 res.parent_level == parent_level,
304 res.path.inv(),
305 res.inv(),
306 crate::mm::page_table::Child::<C>::Frame(paddr, parent_level, prop).wf(res),
307 {
308 Self {
309 kind: EntryOwnerKind::Frame(
310 FrameEntryOwner { mapped_pa: paddr, prop, permission: None },
311 ),
312 path: TreePath(Seq::empty()),
313 parent_level,
314 }
315 }
316
317 pub open spec fn match_pte(self, pte: C::E, parent_level: PagingLevel) -> bool {
318 &&& valid_frame_paddr(pte.paddr())
319 &&& !pte.is_present() ==> {
320 &&& self.is_absent()
321 &&& parent_level > 1 ==> !pte.is_last(parent_level)
322 }
323 &&& pte.is_present() && !pte.is_last(parent_level) ==> {
324 &&& self.is_node()
325 &&& meta_to_frame(self.node().slot_vaddr()) == pte.paddr()
326 }
327 &&& pte.is_present() && pte.is_last(parent_level) ==> {
328 &&& self.is_frame()
329 &&& self.frame().mapped_pa == pte.paddr()
330 &&& self.frame().prop == pte.prop()
331 }
332 }
333
334 pub open spec fn borrowed_match_pte(self, pte: C::E, parent_level: PagingLevel) -> bool {
342 &&& self.is_borrowed()
343 &&& valid_frame_paddr(pte.paddr())
344 &&& pte.is_present()
345 &&& !pte.is_last(parent_level)
346 }
347
348 pub proof fn huge_frame_split_child_at(self, regions: MetaRegionOwners, idx: usize)
349 requires
350 self.inv(),
351 self.is_frame(),
352 regions.inv(),
353 1 < self.parent_level < NR_LEVELS,
354 idx < NR_ENTRIES,
355 ensures
356 self.frame().mapped_pa + idx * page_size::<PagingConsts>(
357 (self.parent_level - 1) as PagingLevel,
358 ) < MAX_PADDR,
359 ((self.frame().mapped_pa + idx * page_size::<PagingConsts>(
360 (self.parent_level - 1) as PagingLevel,
361 )) as Paddr) % page_size::<PagingConsts>((self.parent_level - 1) as PagingLevel) == 0,
362 ((self.frame().mapped_pa + idx * page_size::<PagingConsts>(
363 (self.parent_level - 1) as PagingLevel,
364 )) as Paddr) + page_size::<PagingConsts>((self.parent_level - 1) as PagingLevel)
365 <= MAX_PADDR,
366 ((self.frame().mapped_pa + idx * page_size::<PagingConsts>(
367 (self.parent_level - 1) as PagingLevel,
368 )) as Paddr) % PAGE_SIZE == 0,
369 {
370 let pa = self.frame().mapped_pa;
371 let child_pa = (pa + idx * page_size::<PagingConsts>(
372 (self.parent_level - 1) as PagingLevel,
373 )) as Paddr;
374 crate::specs::mm::page_table::cursor::page_size_lemmas::lemma_page_size_spec_values();
375 vstd_extra::external::ilog2::lemma_usize_ilog2_to32();
376 crate::specs::mm::page_table::cursor::page_size_lemmas::lemma_page_size_base::<
377 PagingConsts,
378 >();
379 vstd::arithmetic::power2::lemma2_to64();
380 if self.parent_level == 2 {
381 crate::specs::mm::page_table::cursor::page_size_lemmas::lemma_page_size_divides::<
382 PagingConsts,
383 >(1, 2);
384 assert(child_pa + page_size::<PagingConsts>(1) <= MAX_PADDR) by {
385 assert(idx * 4096 + 4096 <= 2097152);
386 };
387 } else {
388 crate::specs::mm::page_table::cursor::page_size_lemmas::lemma_va_align_page_size::<
389 PagingConsts,
390 >(pa, 2);
391 vstd::arithmetic::div_mod::lemma_mod_multiples_basic(
392 idx as int,
393 page_size::<PagingConsts>(2) as int,
394 );
395 vstd::arithmetic::div_mod::lemma_add_mod_noop(
396 pa as int,
397 (idx * page_size::<PagingConsts>(2)) as int,
398 page_size::<PagingConsts>(2) as int,
399 );
400 }
401 }
402
403 pub open spec fn metaregion_sound(self, regions: MetaRegionOwners) -> bool {
404 if self.is_node() {
405 let idx = frame_to_index(self.meta_slot_paddr()->0);
406 &&& regions.ref_count(idx) != REF_COUNT_UNUSED
407 &&& 0 < regions.ref_count(idx) <= REF_COUNT_MAX
408 &&& regions.slot_owners[idx].slot_vaddr == self.node().slot_vaddr()
409 &&& regions.slots[idx].value().wf(regions.slot_owners[idx])
410 &&& regions.slot_owners[idx].paths_in_pt == set![self.path]
411 &&& self.node().metaregion_sound_node(regions)
412 } else if self.is_frame() {
413 let idx = frame_to_index(self.meta_slot_paddr()->0);
414 &&& regions.slots.contains_key(idx)
415 &&& regions.slots[idx].addr() == index_to_meta(idx)
416 &&& regions.slots[idx].is_init()
417 &&& regions.slots[idx].value().wf(regions.slot_owners[idx])
418 &&& self.frame_is_tracked() ==> {
419 &&& regions.slot_owners[idx].usage !is PageTable
420 &&& 0 < regions.ref_count(idx) <= REF_COUNT_MAX
421 &&& regions.slot_owners[idx].paths_in_pt.contains(self.path)
422 }
423 &&& C::perm_well_formed_with_region(
424 self.frame().mapped_pa,
425 Tracked(self.frame_permission()),
426 regions,
427 )
428 } else {
429 true
430 }
431 }
432
433 pub proof fn lemma_active_entry_not_in_free_pool(
436 entry: Self,
437 regions: MetaRegionOwners,
438 free_idx: int,
439 )
440 requires
441 regions.inv(),
442 entry.inv(),
443 entry.is_node(),
444 entry.metaregion_sound(regions),
445 regions.slots.contains_key(free_idx),
446 regions.ref_count(free_idx) == REF_COUNT_UNUSED,
447 ensures
448 frame_to_index(entry.meta_slot_paddr()->0) != free_idx,
449 {
450 let idx = frame_to_index(entry.meta_slot_paddr().unwrap());
451 if idx == free_idx {
452 assert(false);
453 }
454 }
455
456 pub open spec fn meta_slot_paddr(self) -> Option<Paddr> {
457 if self.is_node() {
458 Some(meta_to_frame(self.node().slot_vaddr()))
459 } else if self.is_frame() {
460 Some(self.frame().mapped_pa)
461 } else {
462 None
463 }
464 }
465
466 pub open spec fn meta_slot_paddr_neq(self, other: Self) -> bool {
468 (self.is_node() || (self.is_frame() && self.frame_is_tracked())) && (other.is_node() || (
469 other.is_frame() && other.frame_is_tracked())) ==> {
470 self.meta_slot_paddr() is Some ==> other.meta_slot_paddr() is Some
471 ==> self.meta_slot_paddr()->0 != other.meta_slot_paddr()->0
472 }
473 }
474
475 pub proof fn lemma_untracked_frame_metaregion_sound(self, regions: MetaRegionOwners)
477 requires
478 self.inv(),
479 self.is_frame(),
480 !self.frame_is_tracked(),
481 regions.inv(),
482 ensures
483 self.metaregion_sound(regions),
484 {
485 regions.lemma_contains_valid_frame_paddr(self.frame().mapped_pa);
486 C::lemma_none_perm_well_formed(self.frame().mapped_pa, regions);
487 }
488
489 pub proof fn metaregion_sound_slot_owners_only(self, r0: MetaRegionOwners, r1: MetaRegionOwners)
493 requires
494 self.inv(),
495 self.metaregion_sound(r0),
496 r0.slot_owners == r1.slot_owners,
497 forall|k: int| r0.slots.contains_key(k) ==> #[trigger] r1.slots.contains_key(k),
498 forall|k: int| r0.slots.contains_key(k) ==> r0.slots[k] == #[trigger] r1.slots[k],
499 ensures
500 self.metaregion_sound(r1),
501 {
502 if self.is_frame() {
503 let pa = self.frame().mapped_pa;
504 let idx = frame_to_index(pa);
505 assert(r0.slots[idx] == r1.slots[idx]);
506 assert(r0.slot_owners[idx].metadata_perm.id()
507 == r1.slot_owners[idx].metadata_perm.id());
508 C::lemma_perm_well_formed_with_region_preserved(
509 pa,
510 Tracked(self.frame_permission()),
511 r0,
512 r1,
513 );
514 }
515 }
516
517 pub proof fn lemma_metaregion_sound_frame_paths_changed(
519 self,
520 r0: MetaRegionOwners,
521 r1: MetaRegionOwners,
522 )
523 requires
524 self.inv(),
525 self.metaregion_sound(r0),
526 r0.inv(),
527 r1.inv(),
528 r0.slots == r1.slots,
529 forall|i: int|
530 #![trigger r1.slot_owners[i]]
531 r0.slot_owners.contains_key(i) ==> {
532 &&& r1.slot_owners[i].same_permissions(r0.slot_owners[i])
533 &&& r1.slot_owners[i].usage == r0.slot_owners[i].usage
534 &&& r1.slot_owners[i].slot_vaddr == r0.slot_owners[i].slot_vaddr
535 &&& r0.slot_owners[i].paths_in_pt.subset_of(r1.slot_owners[i].paths_in_pt)
536 &&& r0.slot_owners[i].usage is PageTable ==> r1.slot_owners[i].paths_in_pt
537 == r0.slot_owners[i].paths_in_pt
538 },
539 ensures
540 self.metaregion_sound(r1),
541 {
542 if self.is_frame() {
543 C::lemma_perm_well_formed_with_region_preserved(
544 self.frame().mapped_pa,
545 Tracked(self.frame_permission()),
546 r0,
547 r1,
548 );
549 }
550 }
551
552 pub proof fn metaregion_sound_frame_prop_changed(self, other: Self, regions: MetaRegionOwners)
556 requires
557 regions.inv(),
558 self.inv(),
559 other.inv(),
560 self.is_frame(),
561 other.is_frame(),
562 self.metaregion_sound(regions),
563 other.frame().mapped_pa == self.frame().mapped_pa,
564 other.path == self.path,
565 other.parent_level == self.parent_level,
566 other.frame_permission() == self.frame_permission(),
567 other.frame_is_tracked() == self.frame_is_tracked(),
568 ensures
569 other.metaregion_sound(regions),
570 {
571 }
572
573 pub proof fn lemma_metaregion_sound_one_slot_changed(
576 self,
577 r0: MetaRegionOwners,
578 r1: MetaRegionOwners,
579 changed_idx: int,
580 )
581 requires
582 self.inv(),
583 self.metaregion_sound(r0),
584 forall|i: int|
585 #![trigger r1.slot_owners[i]]
586 i != changed_idx ==> r0.slot_owners[i] == r1.slot_owners[i],
587 r0.slot_owners.dom() =~= r1.slot_owners.dom(),
588 forall|k: int| r0.slots.contains_key(k) ==> #[trigger] r1.slots.contains_key(k),
590 forall|k: int| r0.slots.contains_key(k) ==> r0.slots[k] == #[trigger] r1.slots[k],
591 self.is_node() || (self.is_frame() && self.frame_is_tracked())
592 ==> self.meta_slot_paddr() is Some ==> frame_to_index(self.meta_slot_paddr()->0)
593 != changed_idx,
594 self.meta_slot_paddr() is Some && frame_to_index(self.meta_slot_paddr()->0)
595 == changed_idx ==> r1.inv(),
596 ensures
597 self.metaregion_sound(r1),
598 {
599 if self.is_frame() && !self.frame_is_tracked() && frame_to_index(self.meta_slot_paddr()->0)
600 == changed_idx {
601 self.lemma_untracked_frame_metaregion_sound(r1);
602 } else if self.meta_slot_paddr() is Some {
603 let idx = frame_to_index(self.meta_slot_paddr()->0);
604 assert(idx != changed_idx);
605 assert(r0.slot_owners[idx] == r1.slot_owners[idx]);
606 assert(r0.slots.contains_key(idx));
607 assert(r1.slots.contains_key(idx));
608 assert(r0.slots[idx] == r1.slots[idx]);
609 if self.is_frame() {
610 assert(r0.slot_owners[idx].metadata_perm.id()
611 == r1.slot_owners[idx].metadata_perm.id());
612 C::lemma_perm_well_formed_with_region_preserved(
613 self.frame().mapped_pa,
614 Tracked(self.frame_permission()),
615 r0,
616 r1,
617 );
618 }
619 if self.is_node() {
620 assert(self.node().meta_wf(r1));
621 assert(self.node().metaregion_sound_node(r1));
622 }
623 }
624 }
625}
626
627impl<C: PageTableConfig> Inv for EntryOwner<C> {
628 open spec fn inv(self) -> bool {
630 &&& self.is_node() ==> {
631 &&& self.node().inv()
632 &&& self.parent_level == self.node().level() + 1
633 }
634 &&& self.is_frame() ==> {
635 &&& 1 <= self.parent_level < NR_LEVELS
638 &&& valid_frame_paddr(self.frame().mapped_pa)
639 &&& self.frame().mapped_pa % page_size::<PagingConsts>(self.parent_level) == 0
640 &&& self.frame().mapped_pa + page_size::<PagingConsts>(self.parent_level) <= MAX_PADDR
641 &&& C::raw_item_well_formed(
642 (
643 self.frame().mapped_pa,
644 self.parent_level,
645 self.frame().prop,
646 Tracked(self.frame_permission()),
647 ),
648 )
649 &&& C::E::new_page_req(self.frame().mapped_pa, self.parent_level, self.frame().prop)
650 }
651 &&& self.path.inv()
652 }
653}
654
655impl<'a, 'rcu, C: PageTableConfig> OwnerOf for Entry<'a, 'rcu, C> {
656 type Owner = EntryOwner<C>;
657
658 open spec fn wf(self, owner: Self::Owner) -> bool {
659 &&& self.idx < NR_ENTRIES
660 &&& owner.match_pte(self.pte, owner.parent_level)
661 }
662}
663
664}