Skip to main content

ostd/specs/mm/page_table/node/
entry.rs

1use vstd::prelude::*;
2
3use vstd_extra::ghost_tree::*;
4use vstd_extra::ownership::*;
5
6use crate::mm::frame::meta::REF_COUNT_UNUSED;
7use crate::mm::page_table::*;
8use crate::specs::arch::NR_ENTRIES;
9use crate::specs::mm::frame::{mapping::frame_to_index, meta_region_owners::MetaRegionOwners};
10use crate::specs::mm::page_table::*;
11
12verus! {
13
14impl<'a, 'rcu, C: PageTableConfig> Entry<'a, 'rcu, C> {
15    pub open spec fn invariants(self, owner: EntryOwner<C>, regions: MetaRegionOwners) -> bool {
16        &&& owner.inv()
17        &&& regions.inv()
18        &&& self.wf(owner)
19        &&& owner.metaregion_sound(regions)
20    }
21
22    pub open spec fn node_matching(
23        self,
24        owner: EntryOwner<C>,
25        parent_owner: NodeOwner<C>,
26        guard: PageTableGuard<'rcu, C>,
27    ) -> bool {
28        &&& parent_owner.level == owner.parent_level
29        &&& parent_owner.inv()
30        &&& guard.inner.inner@.ptr.addr() == parent_owner.meta_addr_self()
31        &&& guard.inner.inner@.wf(parent_owner)
32        &&& owner.match_pte(parent_owner.children_perm.value()[self.idx as int], owner.parent_level)
33    }
34
35    pub open spec fn metaregion_sound_preserved(
36        regions0: MetaRegionOwners,
37        regions1: MetaRegionOwners,
38    ) -> bool {
39        OwnerSubtree::implies(
40            |entry: EntryOwner<C>, path: TreePath<NR_ENTRIES>| entry.metaregion_sound(regions0),
41            |entry: EntryOwner<C>, path: TreePath<NR_ENTRIES>| entry.metaregion_sound(regions1),
42        )
43    }
44
45    pub open spec fn replace_nonpanic_condition(
46        parent_owner: NodeOwner<C>,
47        new_owner: EntryOwner<C>,
48    ) -> bool {
49        if new_owner.is_node() {
50            parent_owner.level - 1 == new_owner.node().level
51        } else if new_owner.is_frame() {
52            parent_owner.level == new_owner.parent_level
53        } else {
54            true
55        }
56    }
57
58    pub open spec fn metaregion_sound_neq_preserved(
59        old_entry_owner: EntryOwner<C>,
60        new_entry_owner: EntryOwner<C>,
61        regions0: MetaRegionOwners,
62        regions1: MetaRegionOwners,
63    ) -> bool {
64        OwnerSubtree::implies(
65            |entry: EntryOwner<C>, path: TreePath<NR_ENTRIES>|
66                entry.meta_slot_paddr_neq(old_entry_owner) && entry.meta_slot_paddr_neq(
67                    new_entry_owner,
68                ) && entry.metaregion_sound(regions0),
69            |entry: EntryOwner<C>, path: TreePath<NR_ENTRIES>| entry.metaregion_sound(regions1),
70        )
71    }
72
73    /// When the new child is NOT a node, `into_pte` doesn't modify `raw_count`.
74    /// Only `paths_in_pt` changes at `new_idx`, which `metaregion_sound` doesn't inspect.
75    /// So entries with `paddr_neq(old_child)` preserve `metaregion_sound` — no
76    /// `paddr_neq(new_child)` needed.
77    pub open spec fn metaregion_sound_neq_old_preserved(
78        old_entry_owner: EntryOwner<C>,
79        regions0: MetaRegionOwners,
80        regions1: MetaRegionOwners,
81    ) -> bool {
82        OwnerSubtree::implies(
83            |entry: EntryOwner<C>, path: TreePath<NR_ENTRIES>|
84                entry.meta_slot_paddr_neq(old_entry_owner) && entry.metaregion_sound(regions0),
85            |entry: EntryOwner<C>, path: TreePath<NR_ENTRIES>| entry.metaregion_sound(regions1),
86        )
87    }
88
89    /// When `alloc_if_none` allocates a new node from an absent slot, all existing entries'
90    /// `metaregion_sound` is preserved in the new regions.
91    ///
92    /// Justification: `metaregion_sound_neq_preserved` gives preservation for entries whose paddr
93    /// differs from both old_child and new_child. Old_child is absent (paddr_neq trivially true).
94    pub proof fn alloc_if_none_metaregion_sound_preserved(
95        old_child: EntryOwner<C>,
96        new_child: EntryOwner<C>,
97        regions0: MetaRegionOwners,
98        regions1: MetaRegionOwners,
99    )
100        requires
101            old_child.is_absent(),
102            old_child.inv(),
103            new_child.is_node(),
104            regions0.inv(),
105            regions0.slots.contains_key(frame_to_index(new_child.meta_slot_paddr()->0)),
106            regions0.slot_owners[frame_to_index(
107                new_child.meta_slot_paddr()->0,
108            )].inner_perms.ref_count.value() == REF_COUNT_UNUSED,
109            // Allocator-pool / MMIO disjointness: the freshly-allocated node's
110            // paddr is non-MMIO. Rules out an MMIO-frame entry sitting at the
111            // same idx as the new node (delivered by `PageTableNode::alloc`).
112            !crate::specs::mm::frame::meta_owners::is_mmio_paddr(new_child.meta_slot_paddr()->0),
113            Self::metaregion_sound_neq_preserved(old_child, new_child, regions0, regions1),
114        ensures
115            Self::metaregion_sound_preserved(regions0, regions1),
116    {
117        broadcast use crate::specs::mm::frame::meta_owners::axiom_mmio_usage_iff_mmio_paddr;
118
119        let new_idx = frame_to_index(new_child.meta_slot_paddr().unwrap());
120        let f = PageTableOwner::<C>::metaregion_sound_pred(regions0);
121        let g = PageTableOwner::<C>::metaregion_sound_pred(regions1);
122
123        assert(old_child.meta_slot_paddr() is None);
124
125        assert forall|entry: EntryOwner<C>, path: TreePath<NR_ENTRIES>|
126            entry.inv() && f(entry, path) implies #[trigger] g(entry, path) by {
127            if entry.meta_slot_paddr() is Some && entry.is_node() {
128                EntryOwner::<C>::active_entry_not_in_free_pool(entry, regions0, new_idx);
129            }
130            // Frame entries colliding at new_idx are ruled out: either the
131            // slot's `usage != MMIO` (then `metaregion_sound` requires
132            // `rc != UNUSED`, contradicting the precondition), or the slot's
133            // `usage == MMIO` (then by axiom the paddr is MMIO, contradicting
134            // the allocator's non-MMIO guarantee).
135
136        };
137    }
138
139    pub open spec fn path_tracked_pred_preserved(
140        regions0: MetaRegionOwners,
141        regions1: MetaRegionOwners,
142    ) -> bool {
143        OwnerSubtree::implies(
144            PageTableOwner::<C>::path_tracked_pred(regions0),
145            PageTableOwner::<C>::path_tracked_pred(regions1),
146        )
147    }
148
149    pub open spec fn new_owner_compatible(
150        self,
151        new_child: Child<C>,
152        old_owner: EntryOwner<C>,
153        new_owner: EntryOwner<C>,
154        regions: MetaRegionOwners,
155    ) -> bool {
156        &&& old_owner.path == new_owner.path
157        &&& old_owner.parent_level == new_owner.parent_level
158        &&& new_owner.is_node() ==> {
159            &&& regions.slots.contains_key(frame_to_index(new_owner.meta_slot_paddr()->0))
160            &&& regions.slot_owners[frame_to_index(
161                new_owner.meta_slot_paddr()->0,
162            )].inner_perms.ref_count.value() != REF_COUNT_UNUSED
163        }
164    }
165
166    pub open spec fn parent_perms_preserved(
167        self,
168        parent_owner0: NodeOwner<C>,
169        parent_owner1: NodeOwner<C>,
170    ) -> bool {
171        &&& forall|i: int|
172            0 <= i < NR_ENTRIES ==> i != self.idx ==> parent_owner0.children_perm.value()[i]
173                == parent_owner1.children_perm.value()[i]
174        &&& parent_owner1.slot_index == parent_owner0.slot_index
175        &&& parent_owner1.level == parent_owner0.level
176        &&& parent_owner1.tree_level == parent_owner0.tree_level
177        &&& parent_owner1.meta_own.nr_children.id() == parent_owner0.meta_own.nr_children.id()
178        &&& parent_owner1.meta_own.stray == parent_owner0.meta_own.stray
179    }
180}
181
182} // verus!