Skip to main content

ostd/mm/page_table/node/
child.rs

1// SPDX-License-Identifier: MPL-2.0
2//! This module specifies the type of the children of a page table node.
3use vstd::prelude::*;
4
5use crate::arch::mm::PagingConsts;
6use crate::mm::frame::Frame;
7use crate::mm::frame::meta::REF_COUNT_UNUSED;
8use crate::mm::frame::meta::mapping::{frame_to_meta, meta_to_frame};
9use crate::mm::page_table::*;
10use crate::specs::arch::*;
11use crate::specs::mm::frame::{
12    mapping::{frame_to_index, group_page_meta},
13    meta_region_owners::MetaRegionOwners,
14};
15
16use vstd_extra::cast_ptr::*;
17use vstd_extra::drop_tracking::*;
18use vstd_extra::ownership::*;
19
20use crate::specs::*;
21
22use crate::{
23    mm::{Paddr, PagingConstsTrait, PagingLevel, Vaddr, page_prop::PageProperty},
24    //    sync::RcuDrop,
25};
26
27use super::*;
28
29verus! {
30
31/// A page table entry that owns the child of a page table node if present.
32pub enum Child<C: PageTableConfig> {
33    /// A child page table node.
34    pub PageTable(PageTableNode<C>),
35    /// Physical address of a mapped physical frame.
36    ///
37    /// It is associated with the virtual page property and the level of the
38    /// mapping node, which decides the size of the frame.
39    pub Frame(Paddr, PagingLevel, PageProperty),
40    pub None,
41}
42
43#[verus_verify]
44impl<C: PageTableConfig> Child<C> {
45    /// Returns whether the child is not present.
46    #[vstd::contrib::auto_spec]
47    pub(in crate::mm) fn is_none(&self) -> (b: bool) {
48        matches!(self, Child::None)
49    }
50
51    /// Converts the child to a raw PTE value.
52    /// # Verified Properties
53    /// ## Preconditions
54    /// - **Safety Invariants**: all [safety invariants](Child::invariants) must hold on the child.
55    /// - **Safety**: the entry's must be marked as a child, which implies that it has a `raw_count` of 0.
56    /// ## Postconditions
57    /// - **Safety Invariants**: the `PTE`'s [safety invariants](EntryOwner::pte_invariants) are preserved.
58    /// - **Safety**: the entry's raw count is incremented by 1.
59    /// - **Safety**: No frame other than the target entry's (if applicable) is impacted by the call.
60    /// - **Correctness**: the `PTE` is equivalent to the original `Child`.
61    /// ## Safety
62    /// The `PTE` safety invariants ensure that the raw pointer to the entry is tracked correctly
63    /// so that we can guarantee the safety condition on `from_pte`.
64    #[verus_spec(res =>
65        with Tracked(owner): Tracked<&mut EntryOwner<C>>,
66             Tracked(regions): Tracked<&mut MetaRegionOwners>,
67        requires
68            self.invariants(*old(owner), *old(regions)),
69            self matches Child::PageTable(node) ==> old(regions).frame_obligations.count(
70                frame_to_index(meta_to_frame(node.ptr.addr())),
71            ) > 0,
72        ensures
73            final(owner).pte_invariants(res, *final(regions)),
74            *final(regions) == old(owner).into_pte_regions_spec(*old(regions)),
75            *final(owner) == old(owner).into_pte_owner_spec(),
76            old(owner).is_node() ==> res == C::E::new_pt_spec(
77                meta_to_frame(old(owner).node().meta_addr_self()),
78            ),
79    )]
80    pub fn into_pte(self) -> C::E {
81        proof {
82            C::E::lemma_page_table_entry_properties();
83        }
84
85        match self {
86            Child::PageTable(node) => {
87                let ghost node_owner = owner.node();
88                let ghost node_index = frame_to_index(meta_to_frame(node.ptr.addr()));
89
90                let tracked node_slot_perm = regions.slots.tracked_borrow(node_index);
91                #[verus_spec(with Tracked(node_slot_perm))]
92                let paddr = node.start_paddr();
93
94                let ghost fo0 = regions.frame_obligations;
95
96                let _ = ManuallyDrop::new(node, Tracked(regions));
97
98                proof {
99                    // `MD::new` removed one entry at `node_index`, matching
100                    // `into_pte_regions_spec`'s `.remove(index)`.
101                    let spec_regions = owner.into_pte_regions_spec(*old(regions));
102                }
103
104                C::E::new_pt(paddr)
105            },
106            Child::Frame(paddr, level, prop) => {
107                assume(C::E::new_page_req(paddr, level, prop));
108                C::E::new_page(paddr, level, prop)
109            },
110            Child::None => { C::E::new_absent() },
111        }
112    }
113
114    /// Converts a `PTE` to a `Child`.
115    ///
116    /// # Verified Properties
117    /// ## Preconditions
118    /// - **Safety Invariants**: the `PTE`'s [safety invariants](EntryOwner::pte_invariants) must hold.
119    /// - **Safety**: `level` must match the original level of the child.
120    /// ## Postconditions
121    /// - **Safety Invariants**: the [safety invariants](Child::invariants) are preserved.
122    /// - **Safety**: the `EntryOwner` is aware that it is tracking an entry in `Child` form.
123    /// - **Safety**: No frame other than the target entry's (if applicable) is impacted by the call.
124    /// - **Correctness**: the `Child` is equivalent to the original `PTE`.
125    /// ## Safety
126    /// The `PTE` safety invariants require that the `PTE` was previously obtained using [`Self::into_pte`]
127    /// (or another function that calls `ManuallyDrop::new`, which is sufficient for safety).
128    #[verus_spec(res =>
129        with Tracked(regions): Tracked<&mut MetaRegionOwners>,
130             Tracked(entry_own): Tracked<&mut EntryOwner<C>>,
131        requires
132            old(entry_own).pte_invariants(pte, *old(regions)),
133            level == old(entry_own).parent_level,
134        ensures
135            res.invariants(*final(entry_own), *final(regions)),
136            res == Child::<C>::from_pte_spec(pte, level, *final(regions)),
137            *final(entry_own) == old(entry_own).from_pte_owner_spec(),
138            *final(regions) == final(entry_own).from_pte_regions_spec(*old(regions)),
139    )]
140    pub unsafe fn from_pte(pte: C::E, level: PagingLevel) -> Self {
141        if !pte.is_present() {
142            return Child::None;
143        }
144        let paddr = pte.paddr();
145
146        if !pte.is_last(level) {
147            proof {
148                broadcast use group_page_meta;
149
150                regions.inv_implies_correct_addr(paddr);
151            }
152
153            proof_decl! {
154                let tracked from_raw_obl: vstd_extra::drop_tracking::DropObligation<usize>;
155            }
156
157            let node = unsafe {
158                proof_with!(
159                    Tracked(regions) => Tracked(from_raw_obl)
160                );
161                PageTableNode::from_raw(paddr)
162            };
163
164            proof {
165                // `from_raw_obl` is the freshly minted obligation token
166                // for this slot. It is silently dropped here; the
167                // corresponding `frame_obligations` entry persists and
168                // is consumed by `on_drop`'s teardown path (which mints
169                // its own token via the paired axiom when it calls
170                // `frame.drop`). Net effect over `from_pte` is +1 on
171                // the ledger, balancing the prior `-1` from
172                // `into_pte`'s `MD::new` consume.
173            }
174
175            return Child::PageTable(node);
176        }
177        Child::Frame(paddr, level, pte.prop())
178    }
179}
180
181/// A reference to the child of a page table node.
182/// # Verification Design
183/// If the child is itself a page table node, it is represented by a [`PageTableNodeRef`],
184/// because a reference to it must be treated as a potentially shared reference of the appropriate lifetime.
185/// By contrast, a mapped frame can be referenced by just carrying its values, and an absent one is just a simple tag.
186pub enum ChildRef<'a, C: PageTableConfig> {
187    /// A child page table node.
188    PageTable(PageTableNodeRef<'a, C>),
189    /// Physical address of a mapped physical frame.
190    ///
191    /// It is associated with the virtual page property and the level of the
192    /// mapping node, which decides the size of the frame.
193    Frame(Paddr, PagingLevel, PageProperty),
194    None,
195}
196
197#[verus_verify]
198impl<C: PageTableConfig> ChildRef<'_, C> {
199    /// Converts a PTE to a reference to a child.
200    ///
201    /// # Verified Properties
202    /// ## Preconditions
203    /// - **Safety Invariants**: the `PTE`'s [safety invariants](EntryOwner::pte_invariants) must hold.
204    /// - **Safety**: `level` must match the original level of the child.
205    /// ## Postconditions
206    /// - **Safety Invariants**: the [safety invariants](ChildRef::invariants) are preserved.
207    /// - **Correctness**: the `ChildRef` is equivalent to the original `PTE`.
208    /// - **Safety**: No frame other than the target entry's (if applicable) is impacted by the call.
209    /// ## Safety
210    /// - The `PTE` safety invariants require that the `PTE` was previously obtained using [`Self::from_pte`]
211    /// - The soundness of using the resulting `ChildRef` as a reference follows from `FrameRef` safety.
212    #[verus_spec(res =>
213        with Tracked(regions): Tracked<&mut MetaRegionOwners>,
214             Tracked(entry_owner): Tracked<&EntryOwner<C>>,
215        requires
216            entry_owner.pte_invariants(*pte, *old(regions)),
217            level == entry_owner.parent_level,
218        ensures
219            res.invariants(*entry_owner, *final(regions)),
220            final(regions).slot_owners == old(regions).slot_owners,
221            forall|k: usize|
222                old(regions).slots.contains_key(k) ==> #[trigger] final(regions).slots.contains_key(
223                    k,
224                ),
225            forall|k: usize|
226                old(regions).slots.contains_key(k) ==> old(regions).slots[k]
227                    == #[trigger] final(regions).slots[k],
228    )]
229    pub unsafe fn from_pte(pte: &C::E, level: PagingLevel) -> Self {
230        if !pte.is_present() {
231            return ChildRef::None;
232        }
233        let paddr = pte.paddr();
234
235        if !pte.is_last(level) {
236            proof {
237                broadcast use group_page_meta;
238
239                regions.inv_implies_correct_addr(paddr);
240            }
241
242            let node = unsafe {
243                #[verus_spec(with Tracked(regions))]
244                PageTableNodeRef::borrow_paddr(paddr)
245            };
246
247            proof {
248                // borrow_paddr postcondition gives raw_count == 1 and field-by-field preservation.
249                // Since raw_count was already 1 (entry is in PTE),
250                // slot_owners[idx] == old(slot_owners[idx]) follows field by field.
251                // slots: borrow_paddr inserts at borrow_idx. Prove existing keys preserved.
252                // The node's slot was NOT in old.slots: by active_entry_not_in_free_pool,
253                // a node entry's index can't equal any free-pool index.
254                let borrow_idx = frame_to_index(paddr);
255                let ghost entry_snap = *entry_owner;
256                assert(!old(regions).slots.contains_key(borrow_idx)) by {
257                    if old(regions).slots.contains_key(borrow_idx) {
258                        EntryOwner::<C>::active_entry_not_in_free_pool(
259                            entry_snap,
260                            *old(regions),
261                            borrow_idx,
262                        );
263                        // gives borrow_idx != borrow_idx — contradiction
264                    }
265                };
266                // Since borrow_idx was not in old.slots, insert preserves all old keys.
267                assert forall|k: usize| old(regions).slots.contains_key(k) implies old(
268                    regions,
269                ).slots[k] == #[trigger] regions.slots[k] by {
270                    // regions.slots == old.slots.insert(borrow_idx, _), and k != borrow_idx
271                };
272            }
273
274            return ChildRef::PageTable(node);
275        }
276        ChildRef::Frame(paddr, level, pte.prop())
277    }
278}
279
280} // verus!