ostd/mm/page_table/node/
entry.rs

1// SPDX-License-Identifier: MPL-2.0
2//! This module provides accessors to the page table entries in a node.
3use vstd::prelude::*;
4
5use vstd::simple_pptr::{self, PPtr, PointsTo};
6
7use vstd_extra::cast_ptr;
8use vstd_extra::drop_tracking::ManuallyDrop;
9use vstd_extra::ghost_tree::*;
10use vstd_extra::ownership::*;
11
12use crate::mm::frame::meta::mapping::{frame_to_index, frame_to_meta, meta_to_frame};
13use crate::mm::frame::{Frame, FrameRef};
14use crate::mm::page_table::*;
15use crate::mm::{Paddr, PagingConstsTrait, PagingLevel, Vaddr};
16use crate::specs::arch::mm::{NR_ENTRIES, NR_LEVELS, PAGE_SIZE};
17use crate::specs::arch::paging_consts::PagingConsts;
18use crate::specs::mm::frame::meta_owners::{MetaSlotOwner, REF_COUNT_UNUSED};
19use crate::specs::mm::frame::meta_region_owners::MetaRegionOwners;
20use crate::specs::mm::page_table::{PageTableOwner, INC_LEVELS};
21use crate::specs::task::InAtomicMode;
22
23use core::marker::PhantomData;
24use core::ops::Deref;
25
26use crate::{
27    mm::{nr_subpage_per_huge, page_prop::PageProperty},
28    //    sync::RcuDrop,
29    //    task::atomic_mode::InAtomicMode,
30};
31
32use super::*;
33
34verus! {
35
36/// A reference to a page table node.
37pub type PageTableNodeRef<'a, C> = FrameRef<'a, PageTablePageMeta<C>>;
38
39/// A guard that holds the lock of a page table node.
40pub struct PageTableGuard<'rcu, C: PageTableConfig> {
41    pub inner: PageTableNodeRef<'rcu, C>,
42}
43
44impl<'rcu, C: PageTableConfig> Deref for PageTableGuard<'rcu, C> {
45    type Target = PageTableNodeRef<'rcu, C>;
46
47    #[verus_spec(ensures returns self.inner)]
48    fn deref(&self) -> &Self::Target {
49        &self.inner
50    }
51}
52
53pub struct Entry<'rcu, C: PageTableConfig> {
54    /// The page table entry.
55    ///
56    /// We store the page table entry here to optimize the number of reads from
57    /// the node. We cannot hold a `&mut E` reference to the entry because that
58    /// other CPUs may modify the memory location for accessed/dirty bits. Such
59    /// accesses will violate the aliasing rules of Rust and cause undefined
60    /// behaviors.
61    ///
62    /// # Verification Design
63    /// The concrete value of a PTE is specific to the architecture and the page table configuration,
64    /// represented by the type `C::E`. We represent its value as an abstract [`EntryOwner`], which is
65    /// connected to the concrete value by `match_pte`. The `EntryOwner` is well-formed with respect to
66    /// `Entry` if it is related to the concrete value by `match_pte`.
67    ///
68    /// An `Entry` can be thought of as a mutable handle to the concrete value of the PTE.
69    /// The `node` field points (with a level of indirection) to the node that contains the entry,
70    /// `index` provides the offset, and the `pte` is current value. Only one `Entry` can exist for
71    /// a given node at any given time.
72    pub pte: C::E,
73    /// The index of the entry in the node.
74    pub idx: usize,
75    /// The node that contains the entry. In the original Rust this is a `&mut PageTableGuard`; the PPtr
76    /// type is handling the mutable reference because Verus does not yet support storing such a reference
77    /// in a struct.
78    pub node: PPtr<PageTableGuard<'rcu, C>>,
79}
80
81impl<'rcu, C: PageTableConfig> Entry<'rcu, C> {
82    pub open spec fn new_spec(pte: C::E, idx: usize, node: PPtr<PageTableGuard<'rcu, C>>) -> Self {
83        Self { pte, idx, node }
84    }
85
86    #[verifier::when_used_as_spec(new_spec)]
87    pub fn new(pte: C::E, idx: usize, node: PPtr<PageTableGuard<'rcu, C>>) -> Self {
88        Self { pte, idx, node }
89    }
90}
91
92#[verus_verify]
93impl<'a, 'rcu, C: PageTableConfig> Entry<'rcu, C> {
94    /// Returns if the entry does not map to anything.
95    #[verus_spec(r =>
96        with Tracked(owner): Tracked<&EntryOwner<C>>,
97        requires
98            self.wf(*owner),
99            owner.inv(),
100        returns owner.is_absent(),
101    )]
102    pub(in crate::mm) fn is_none(&self) -> bool {
103        !self.pte.is_present()
104    }
105
106    /// Returns if the entry maps to a page table node.
107    #[verus_spec(
108        with Tracked(owner): Tracked<EntryOwner<C>>,
109            Tracked(parent_owner): Tracked<&NodeOwner<C>>,
110            Tracked(guard_perm): Tracked<&GuardPerm<'rcu, C>>
111    )]
112    pub(in crate::mm) fn is_node(&self) -> bool
113        requires
114            owner.inv(),
115            self.wf(owner),
116            guard_perm.addr() == self.node.addr(),
117            parent_owner.relate_guard_perm(*guard_perm),
118            parent_owner.inv(),
119            parent_owner.level == owner.parent_level,
120        returns
121            owner.is_node(),
122    {
123        let guard = self.node.borrow(Tracked(guard_perm));
124
125        self.pte.is_present() && !self.pte.is_last(
126            #[verus_spec(with Tracked(&parent_owner.meta_perm))]
127            guard.level(),
128        )
129    }
130
131    /// Gets a reference to the child.
132    #[verus_spec(
133        with Tracked(owner): Tracked<&EntryOwner<C>>,
134            Tracked(parent_owner): Tracked<&NodeOwner<C>>,
135            Tracked(regions): Tracked<&mut MetaRegionOwners>,
136            Tracked(guard_perm): Tracked<&GuardPerm<'rcu, C>>
137    )]
138    pub(in crate::mm) fn to_ref(&self) -> (res: ChildRef<'rcu, C>)
139        requires
140            self.invariants(*owner, *old(regions)),
141            self.node_matching(*owner, *parent_owner, *guard_perm),
142            !owner.in_scope,
143        ensures
144            res.invariants(*owner, *old(regions)),
145            *regions =~= *old(regions),
146            Self::relate_region_preserved(*old(regions), *regions),
147    {
148        let guard = self.node.borrow(Tracked(guard_perm));
149
150        #[verus_spec(with Tracked(&parent_owner.meta_perm))]
151        let level = guard.level();
152
153        // SAFETY:
154        //  - The PTE outlives the reference (since we have `&self`).
155        //  - The level matches the current node.
156        #[verus_spec(with Tracked(regions), Tracked(owner))]
157        let res = ChildRef::from_pte(&self.pte, level);
158
159        res
160    }
161
162    /// Operates on the mapping properties of the entry.
163    ///
164    /// It only modifies the properties if the entry is present.
165    ///
166    /// # Verified Properties
167    /// ## Preconditions
168    /// - **Safety Invariants**: The entry must satisfy the relevant safety invariants.
169    /// - **Safety**: The caller must provide a valid guard permission matching `guard_perm`, and it must be guarding the
170    /// correct parent node.
171    /// - **Safety**: The entry must be a frame.
172    /// ## Postconditions
173    /// - **Safety Invariants**: The entry continues to satisfy the relevant safety invariants.
174    /// - **Safety**: The guard permission is preserved.
175    /// - **Correctness**: The entry's permissions are updated by `op`
176    /// ## Safety
177    /// - The entry is updated in place, only changing its properties, so the metadata is unchanged.
178    /// That's why we don't take a `MetaRegionOwners` argument. It means that all invariants will be preserved.
179    #[verus_spec(
180        with Tracked(owner) : Tracked<&mut EntryOwner<C>>,
181            Tracked(parent_owner): Tracked<&mut NodeOwner<C>>,
182            Tracked(guard_perm): Tracked<&mut GuardPerm<'rcu, C>>,
183    )]
184    pub(in crate::mm) fn protect(&mut self, op: impl FnOnce(PageProperty) -> PageProperty)
185        requires
186            old(owner).inv(),
187            old(self).wf(*old(owner)),
188            old(self).node_matching(*old(owner), *old(parent_owner), *old(guard_perm)),
189            op.requires((old(self).pte.prop(),)),
190            old(owner).is_frame(),
191        ensures
192            owner.inv(),
193            self.wf(*owner),
194            self.node_matching(*owner, *parent_owner, *guard_perm),
195            self.parent_perms_preserved(
196                *old(parent_owner),
197                *parent_owner,
198                *old(guard_perm),
199                *guard_perm,
200            ),
201            owner.is_frame(),
202            owner.frame.unwrap().mapped_pa == old(owner).frame.unwrap().mapped_pa,
203            old(self).pte.is_present() ==> op.ensures(
204                (old(owner).frame.unwrap().prop,),
205                owner.frame.unwrap().prop,
206            ),
207    {
208        let ghost pte0 = self.pte;
209
210        if !self.pte.is_present() {
211            return ;
212        }
213        let prop = self.pte.prop();
214        let new_prop = op(prop);
215
216        /*if prop == new_prop {
217            return;
218        }*/
219
220        proof {
221            self.pte.set_prop_properties(new_prop);
222        }
223        self.pte.set_prop(new_prop);
224
225        let mut guard = self.node.take(Tracked(guard_perm));
226
227        // SAFETY:
228        //  1. The index is within the bounds.
229        //  2. We replace the PTE with a new one, which differs only in
230        //     `PageProperty`, so the level still matches the current
231        //     page table node.
232        #[verus_spec(with Tracked(parent_owner))]
233        guard.write_pte(self.idx, self.pte);
234
235        proof {
236            let tracked mut frame_owner = owner.frame.tracked_take();
237            frame_owner.prop = new_prop;
238            owner.frame = Some(frame_owner);
239        }
240        assert(owner.match_pte(self.pte, owner.parent_level));
241
242        self.node.put(Tracked(guard_perm), guard);
243    }
244
245    /// Replaces the entry with a new child.
246    ///
247    /// The old child is returned.
248    ///
249    /// # Verified Properties
250    /// ## Preconditions
251    /// - **Safety Invariants**: Both old and new owners must satisfy the respective safety invariants for an [Entry](Entry::invariants)
252    /// and a [Child](Child::invariants).
253    /// - **Safety**: The caller must provide valid owners for all objects, and for the parent node where the entry
254    /// is being replaced. The parent node must have a valid guard permission.
255    /// - **Correctness**: The new child must be compatible with the old, for instance by having the same level.
256    /// ## Postconditions
257    /// - **Safety Invariants**: The old and new owners will satisfy the safety invariants for an [Entry](Entry::invariants)
258    /// and a [Child](Child::invariants), but they have changed positions.
259    /// - **Safety**: Safety properties that hold across the page table's tree structure are preserved
260    /// everywhere except for the entry being replaced.
261    /// - **Correctness**: The entry will match the argument, and the returned child will match the entry that was replaced.
262    /// ## Safety
263    /// - The invariants ensure that the entry is appropriately aligned and its index is within bounds.
264    /// - The transformation from child to entry ensures that the tree now owns the updated entry.
265    #[verus_spec(
266        with Tracked(regions) : Tracked<&mut MetaRegionOwners>,
267            Tracked(owner): Tracked<&mut EntryOwner<C>>,
268            Tracked(new_owner): Tracked<&mut EntryOwner<C>>,
269            Tracked(parent_owner): Tracked<&mut NodeOwner<C>>,
270            Tracked(guard_perm): Tracked<&mut GuardPerm<'rcu, C>>
271    )]
272    pub(in crate::mm) fn replace(&mut self, new_child: Child<C>) -> (res: Child<C>)
273        requires
274            old(self).invariants(*old(owner), *old(regions)),
275            new_child.invariants(*old(new_owner), *old(regions)),
276            old(self).node_matching(*old(owner), *old(parent_owner), *old(guard_perm)),
277            old(self).new_owner_compatible(new_child, *old(owner), *old(new_owner), *old(regions)),
278            !old(owner).in_scope,
279            Self::replace_panic_condition(*old(parent_owner), *old(new_owner)),
280        ensures
281            self.invariants(*new_owner, *regions),
282            res.invariants(*owner, *regions),
283            self.node_matching(*new_owner, *parent_owner, *guard_perm),
284            self.idx == old(self).idx,
285            *owner == old(owner).from_pte_owner_spec(),
286            *new_owner == old(new_owner).into_pte_owner_spec(),
287            Self::relate_region_neq_preserved(*old(owner), *new_owner, *old(regions), *regions),
288            Self::path_tracked_pred_preserved(*old(regions), *regions),
289            !new_owner.is_absent() ==> PageTableOwner::<C>::path_tracked_pred(*regions)(
290                *new_owner,
291                new_owner.path,
292            ),
293            self.parent_perms_preserved(
294                *old(parent_owner),
295                *parent_owner,
296                *guard_perm,
297                *old(guard_perm),
298            ),
299    {
300        let ghost new_idx = frame_to_index(new_owner.meta_slot_paddr().unwrap());
301        let ghost old_idx = frame_to_index(owner.meta_slot_paddr().unwrap());
302
303        let mut guard = self.node.take(Tracked(guard_perm));
304
305        /*
306        match &new_child {
307            Child::PageTable(node) => {
308                assert(node.level() == guard.level() - 1);
309            }
310            Child::Frame(_, level, _) => {
311                assert(*level == guard.level());
312            }
313            Child::None => {}
314        }
315        */
316
317        // SAFETY:
318        //  - The PTE is not referenced by other `ChildRef`s (since we have `&mut self`).
319        //  - The level matches the current node.
320        #[verus_spec(with Tracked(&parent_owner.meta_perm))]
321        let level = guard.level();
322
323        #[verus_spec(with Tracked(regions), Tracked(owner))]
324        let old_child = Child::from_pte(self.pte, level);
325
326        let ghost regions_after_from = *regions;
327
328        assert(new_owner.is_node() ==> regions.slots.contains_key(
329            frame_to_index(new_owner.meta_slot_paddr().unwrap()),
330        ));
331
332        if old_child.is_none() && !new_child.is_none() {
333            #[verus_spec(with Tracked(&parent_owner.meta_perm))]
334            let nr_children = guard.nr_children_mut();
335            let _tmp = nr_children.read(Tracked(&parent_owner.meta_own.nr_children));
336            assume(_tmp < NR_ENTRIES);
337            nr_children.write(Tracked(&mut parent_owner.meta_own.nr_children), _tmp + 1);
338        } else if !old_child.is_none() && new_child.is_none() {
339            #[verus_spec(with Tracked(&parent_owner.meta_perm))]
340            let nr_children = guard.nr_children_mut();
341            let _tmp = nr_children.read(Tracked(&parent_owner.meta_own.nr_children));
342            assume(_tmp > 0);
343            nr_children.write(Tracked(&mut parent_owner.meta_own.nr_children), _tmp - 1);
344        }
345        proof {
346            assert(owner.relate_region(*regions));
347        }
348
349        #[verus_spec(with Tracked(new_owner), Tracked(regions))]
350        let new_pte = new_child.into_pte();
351
352        let ghost regions_after_into = *regions;
353
354        proof {
355            assert(owner.relate_region(*regions));
356        }
357
358        // SAFETY:
359        //  1. The index is within the bounds.
360        //  2. The new PTE is a valid child whose level matches the current page table node.
361        //  3. The ownership of the child is passed to the page table node.
362        #[verus_spec(with Tracked(parent_owner))]
363        guard.write_pte(self.idx, new_pte);
364
365        self.node.put(Tracked(guard_perm), guard);
366
367        self.pte = new_pte;
368
369        proof {
370            if new_owner.is_node() || new_owner.is_frame() {
371                let paddr = new_owner.meta_slot_paddr().unwrap();
372                regions.inv_implies_correct_addr(paddr);
373
374                let tracked mut new_meta_slot = regions.slot_owners.tracked_remove(new_idx);
375                new_meta_slot.path_if_in_pt = Some(new_owner.get_path());
376                regions.slot_owners.tracked_insert(new_idx, new_meta_slot);
377            }
378            owner.in_scope = true;
379        }
380
381        assert(Self::relate_region_neq_preserved(*old(owner), *new_owner, *old(regions), *regions));
382        assert(Self::path_tracked_pred_preserved(*old(regions), *regions));
383
384        proof {
385            if new_owner.is_node() || new_owner.is_frame() {
386                let paddr = new_owner.meta_slot_paddr().unwrap();
387                regions.inv_implies_correct_addr(paddr);
388            }
389        }
390
391        old_child
392    }
393
394    /// Allocates a new child page table node and replaces the entry with it.
395    ///
396    /// If the old entry is not none, the operation will fail and return `None`.
397    /// Otherwise, the lock guard of the new child page table node is returned.
398    /// # Verified Properties
399    /// ## Preconditions
400    /// - **Safety Invariants**: The old node's root must satisfy the safety invariants for an [Entry](Entry::invariants)
401    /// and the caller must provide its parent node owner.
402    /// ## Postconditions
403    /// - **Safety Invariants**: If a new node is allocated, it will satisfy the safety invariants.
404    /// - **Safety**: If a new node is allocated, all other nodes have their invariants preserved.
405    /// - **Correctness**: A new node is allocated if and only if the old node is absent.
406    /// - **Correctness**: If the old node is present, the function retuns `None` and the state is unchanged.
407    /// ## Safety
408    /// - The invariants ensure that the entry is appropriately aligned and its index is within bounds.
409    /// - The invariants of the entire page table are preserved in both cases.
410    #[verus_spec(res =>
411        with Tracked(owner): Tracked<&mut OwnerSubtree<C>>,
412            Tracked(parent_owner): Tracked<&mut NodeOwner<C>>,
413            Tracked(regions): Tracked<&mut MetaRegionOwners>,
414            Tracked(guards): Tracked<&mut Guards<'rcu, C>>,
415            Tracked(parent_guard_perm): Tracked<&mut GuardPerm<'rcu, C>>
416            -> guard_perm: Tracked<Option<GuardPerm<'rcu, C>>>
417        requires
418            old(self).invariants(old(owner).value, *old(regions)),
419            old(owner).inv(),
420            old(self).node_matching(old(owner).value, *old(parent_owner), *old(parent_guard_perm)),
421            old(owner).level < INC_LEVELS - 1,
422        ensures
423            self.invariants(owner.value, *regions),
424            self.parent_perms_preserved(*old(parent_owner), *parent_owner, *old(parent_guard_perm), *parent_guard_perm),
425            old(owner).value.is_absent() && old(parent_owner).level > 1 ==> {
426                &&& res is Some
427                &&& owner.value.is_node()
428                &&& guard_perm@ is Some
429                &&& guard_perm@.unwrap().addr() == res.unwrap().addr()
430                &&& owner.level == old(owner).level
431                &&& owner.value.parent_level == old(owner).value.parent_level
432                &&& owner.value.node.unwrap().relate_guard_perm(guard_perm@.unwrap())
433                &&& guards.lock_held(owner.value.node.unwrap().meta_perm.addr())
434                &&& owner.value.path == old(owner).value.path
435                &&& owner.value.relate_region(*regions)
436                &&& OwnerSubtree::implies(
437                    CursorOwner::<'rcu, C>::node_unlocked(*old(guards)),
438                    CursorOwner::<'rcu, C>::node_unlocked_except(*guards, owner.value.node.unwrap().meta_perm.addr()))
439                &&& Self::relate_region_neq_preserved(old(owner).value, owner.value, *old(regions), *regions)
440                &&& Self::path_tracked_pred_preserved(*old(regions), *regions)
441                &&& owner.tree_predicate_map(owner.value.path,
442                    CursorOwner::<'rcu, C>::node_unlocked_except(*guards, owner.value.node.unwrap().meta_perm.addr()))
443                &&& owner.tree_predicate_map(owner.value.path, PageTableOwner::<C>::relate_region_pred(*regions))
444            },
445            !old(owner).value.is_absent() ==> {
446                &&& res is None
447                &&& *owner == *old(owner)
448            },
449            forall |i: usize| old(guards).lock_held(i) ==> guards.lock_held(i),
450            forall |i: usize| old(guards).unlocked(i) && i != owner.value.node.unwrap().meta_perm.addr() ==> guards.unlocked(i),
451    )]
452    pub(in crate::mm) fn alloc_if_none<A: InAtomicMode>(&mut self, guard: &'rcu A) -> (res: Option<
453        PPtr<PageTableGuard<'rcu, C>>,
454    >) {
455        let entry_is_present = self.pte.is_present();
456
457        let mut parent_guard = self.node.take(Tracked(parent_guard_perm));
458
459        #[verus_spec(with Tracked(&parent_owner.meta_perm))]
460        let level = parent_guard.level();
461
462        if entry_is_present || level <= 1 {
463            self.node.put(Tracked(parent_guard_perm), parent_guard);
464
465            proof_with!{|= Tracked(None)}
466            None
467        } else {
468            let tracked old_path = owner.value.get_path();
469
470            proof_decl! {
471                let tracked mut new_node_owner: Tracked<OwnerSubtree<C>>;
472            }
473
474            #[verus_spec(with Tracked(parent_owner), Tracked(regions), Tracked(guards), Ghost(self.idx) => Tracked(new_node_owner))]
475            let new_page = PageTableNode::<C>::alloc(level - 1);
476
477            proof {
478                let pte = C::E::new_pt_spec(
479                    meta_to_frame(new_node_owner.value.node.unwrap().meta_perm.addr()),
480                );
481                old(parent_owner).set_children_perm_axiom(self.idx, pte);
482                C::E::new_properties();
483                assert(!pte.is_last_spec(level as PagingLevel));
484            }
485
486            #[verus_spec(with Tracked(&new_node_owner.value.node.tracked_borrow().meta_perm.points_to))]
487            let paddr = new_page.start_paddr();
488
489            assert(new_node_owner.value.relate_region(*regions));
490
491            #[verus_spec(with Tracked(&mut new_node_owner.value), Tracked(regions))]
492            let new_pte = Child::PageTable(new_page).into_pte();
493            self.pte = new_pte;
494
495            proof {
496                // Preserve into_pte postcondition before we mutate owner
497                assert(new_node_owner.value.pte_invariants(self.pte, *regions));
498                assert(new_node_owner.value.match_pte(self.pte, new_node_owner.value.parent_level));
499                broadcast use crate::mm::frame::meta::mapping::group_page_meta;
500
501            }
502
503            #[verus_spec(with Tracked(regions), Tracked(&new_node_owner.value.node.tracked_borrow().meta_perm))]
504            let pt_ref = PageTableNodeRef::borrow_paddr(paddr);
505
506            proof_decl! {
507                let tracked mut guard_perm: Tracked<GuardPerm<'rcu, C>>;
508            }
509
510            // Lock before writing the PTE, so no one else can operate on it.
511            #[verus_spec(with Tracked(&new_node_owner.value.node.tracked_borrow()), Tracked(guards) => Tracked(guard_perm))]
512            let pt_lock_guard = pt_ref.lock(guard);
513
514            // SAFETY:
515            //  1. The index is within the bounds.
516            //  2. The new PTE is a child in `C` and at the correct paging level.
517            //  3. The ownership of the child is passed to the page table node.
518            #[verus_spec(with Tracked(parent_owner))]
519            parent_guard.write_pte(self.idx, self.pte);
520
521            #[verus_spec(with Tracked(&parent_owner.meta_perm))]
522            let nr_children = parent_guard.nr_children_mut();
523            let _tmp = nr_children.read(Tracked(&parent_owner.meta_own.nr_children));
524            assume(_tmp < NR_ENTRIES);
525            nr_children.write(Tracked(&mut parent_owner.meta_own.nr_children), _tmp + 1);
526
527            self.node.put(Tracked(parent_guard_perm), parent_guard);
528
529            proof {
530                owner.value = new_node_owner.value;
531                owner.value.parent_level = level as PagingLevel;
532                owner.value.path = old_path;
533                owner.children = new_node_owner.children;
534
535                let new_paddr = owner.value.meta_slot_paddr().unwrap();
536                regions.inv_implies_correct_addr(new_paddr);
537                let new_idx = frame_to_index(new_paddr);
538                let tracked mut new_meta_slot = regions.slot_owners.tracked_remove(new_idx);
539                new_meta_slot.path_if_in_pt = Some(owner.value.get_path());
540                regions.slot_owners.tracked_insert(new_idx, new_meta_slot);
541            }
542
543            proof_with!{|= Tracked(Some(guard_perm))}
544            Some(pt_lock_guard)
545        }
546    }
547
548    /// Splits the entry to smaller pages if it maps to a huge page.
549    ///
550    /// If the entry does map to a huge page, it is split into smaller pages
551    /// mapped by a child page table node. The new child page table node
552    /// is returned.
553    ///
554    /// If the entry does not map to a untracked huge page, the method returns
555    /// `None`.
556    /// # Verified Properties
557    /// ## Preconditions
558    /// - **Safety Invariants**: The old node's root must satisfy the safety invariants for an [Entry](Entry::invariants)
559    /// and the caller must provide its parent node owner.
560    /// ## Postconditions
561    /// - **Safety Invariants**: The node allocated in place of the split page satisfies the safety invariants.
562    /// - **Safety**: All other nodes have their invariants preserved.
563    #[verus_spec(res =>
564        with Tracked(owner) : Tracked<&mut OwnerSubtree<C>>,
565            Tracked(parent_owner): Tracked<&mut NodeOwner<C>>,
566            Tracked(regions): Tracked<&mut MetaRegionOwners>,
567            Tracked(guards): Tracked<&mut Guards<'rcu, C>>,
568            Tracked(guard_perm): Tracked<&mut GuardPerm<'rcu, C>>
569        requires
570            old(regions).inv(),
571            old(owner).inv(),
572            old(self).wf(old(owner).value),
573            old(self).node.addr() == old(guard_perm).addr(),
574            old(guard_perm).is_init(),
575            old(parent_owner).relate_guard_perm(*old(guard_perm)),
576            old(parent_owner).inv(),
577            old(parent_owner).level == old(owner).value.parent_level,
578            old(parent_owner).level < NR_LEVELS,
579        ensures
580            old(owner).value.is_frame() && old(parent_owner).level > 1 ==> {
581                &&& res is Some
582                &&& owner.value.is_node()
583                &&& owner.level == old(owner).level
584                &&& parent_owner.relate_guard_perm(*guard_perm)
585                &&& guards.guards.contains_key(res.unwrap().addr())
586                &&& guards.guards[res.unwrap().addr()] is Some
587                &&& guards.guards[res.unwrap().addr()].unwrap().pptr() == res.unwrap()
588                &&& owner.value.node.unwrap().relate_guard_perm(guards.guards[res.unwrap().addr()].unwrap())
589                &&& owner.value.node.unwrap().meta_perm.addr() == res.unwrap().addr()
590                &&& owner.value.path == old(owner).value.path
591                &&& owner.value.relate_region(*regions)
592                &&& OwnerSubtree::implies(
593                    CursorOwner::<'rcu, C>::node_unlocked(*old(guards)),
594                    CursorOwner::<'rcu, C>::node_unlocked(*guards))
595                &&& OwnerSubtree::implies(
596                    PageTableOwner::<C>::relate_region_pred(*old(regions)),
597                    PageTableOwner::<C>::relate_region_pred(*regions))
598                &&& owner.tree_predicate_map(owner.value.path,
599                    CursorOwner::<'rcu, C>::node_unlocked(*guards))
600                &&& owner.tree_predicate_map(owner.value.path,
601                    PageTableOwner::<C>::relate_region_pred(*regions))
602            },
603            !old(owner).value.is_frame() || old(parent_owner).level <= 1 ==> {
604                &&& res is None
605                &&& *owner == *old(owner)
606            },
607            owner.inv(),
608            regions.inv(),
609            parent_owner.inv(),
610            guard_perm.pptr() == old(guard_perm).pptr(),
611            guard_perm.value().inner.inner@.ptr.addr() == old(guard_perm).value().inner.inner@.ptr.addr(),
612            forall |i: usize| old(guards).lock_held(i) ==> guards.lock_held(i),
613            forall |i: usize| old(guards).unlocked(i) ==> guards.unlocked(i),
614    )]
615    pub(in crate::mm) fn split_if_mapped_huge<A: InAtomicMode>(&mut self, guard: &'rcu A) -> (res:
616        Option<PPtr<PageTableGuard<'rcu, C>>>) {
617        let mut node_guard = self.node.take(Tracked(guard_perm));
618
619        #[verus_spec(with Tracked(&parent_owner.meta_perm))]
620        let level = node_guard.level();
621
622        if !(self.pte.is_last(level) && level > 1) {
623            self.node.put(Tracked(guard_perm), node_guard);
624
625            return None;
626        }
627        let pa = self.pte.paddr();
628        let prop = self.pte.prop();
629
630        proof_decl!{
631            let tracked mut new_owner: OwnerSubtree<C>;
632        }
633
634        #[verus_spec(with Tracked(parent_owner), Tracked(regions), Tracked(guards), Ghost(self.idx) => Tracked(new_owner))]
635        let new_page = PageTableNode::<C>::alloc(level);
636
637        #[verus_spec(with Tracked(&new_owner.value.node.tracked_borrow().meta_perm.points_to))]
638        let paddr = new_page.start_paddr();
639
640        // This is a fudge because the `new_page` should be wrapped in an `RcuDrop` similar to a `NeverDrop`
641        assert(regions.slot_owners[frame_to_index(paddr)].raw_count == 1) by { admit() };
642
643        proof {
644            broadcast use crate::mm::frame::meta::mapping::group_page_meta;
645
646        }
647
648        #[verus_spec(with Tracked(regions), Tracked(&new_owner.value.node.tracked_borrow().meta_perm))]
649        let pt_ref = PageTableNodeRef::borrow_paddr(paddr);
650
651        proof_decl! {
652            let tracked mut new_guard_perm: Tracked<GuardPerm<'rcu, C>>;
653        }
654
655        // Lock before writing the PTE, so no one else can operate on it.
656        #[verus_spec(with Tracked(&new_owner.value.node.tracked_borrow()), Tracked(guards) => Tracked(new_guard_perm))]
657        let pt_lock_guard = pt_ref.lock(guard);
658
659        let ghost children_perm = new_owner.value.node.unwrap().children_perm;
660        let ghost new_owner_path = new_owner.value.path;
661        let ghost new_owner_meta_addr = new_owner.value.node.unwrap().meta_perm.addr();
662
663        for i in 0..nr_subpage_per_huge::<C>()
664            invariant
665                1 < level < NR_LEVELS,
666                owner.inv(),
667                regions.inv(),
668                parent_owner.inv(),
669                new_owner.value.is_node(),
670                new_owner.inv(),
671                new_owner.value.path == new_owner_path,
672                new_owner.value.node.unwrap().meta_perm.addr() == new_owner_meta_addr,
673                new_owner.value.node.unwrap().relate_guard_perm(new_guard_perm),
674                new_owner.value.node.unwrap().level == (level - 1) as PagingLevel,
675                pt_lock_guard.addr() == new_guard_perm.addr(),
676                forall|j: int|
677                    #![auto]
678                    0 <= j < NR_ENTRIES ==> {
679                        &&& new_owner.children[j] is Some
680                        &&& new_owner.children[j].unwrap().value.match_pte(
681                            new_owner.value.node.unwrap().children_perm.value()[j],
682                            new_owner.value.node.unwrap().level,
683                        )
684                        &&& new_owner.children[j].unwrap().value.parent_level
685                            == new_owner.value.node.unwrap().level
686                        &&& new_owner.children[j].unwrap().value.inv()
687                        &&& new_owner.children[j].unwrap().value.path == new_owner_path.push_tail(
688                            j as usize,
689                        )
690                    },
691                forall|j: int|
692                    #![auto]
693                    i <= j < NR_ENTRIES ==> {
694                        &&& new_owner.children[j].unwrap().value.is_absent()
695                        &&& !new_owner.children[j].unwrap().value.in_scope
696                        &&& new_owner.value.node.unwrap().children_perm.value()[j]
697                            == C::E::new_absent_spec()
698                    },
699                new_page.ptr.addr() == new_owner_meta_addr,
700        {
701            proof {
702                C::axiom_nr_subpage_per_huge_eq_nr_entries();
703            }
704            assert(i < NR_ENTRIES);
705
706            proof {
707                // Prove required facts while we still have new_owner.value.node available.
708                let ghost the_node = new_owner.value.node.unwrap();
709                assert(0 <= i < NR_ENTRIES);
710                assert(new_owner.children[i as int].unwrap().value.match_pte(
711                    the_node.children_perm.value()[i as int],
712                    new_owner.children[i as int].unwrap().value.parent_level,
713                ));
714                assert(the_node.relate_guard_perm(new_guard_perm));
715                assert(new_guard_perm.addr() == pt_lock_guard.addr());
716                assert(new_owner.children[i as int].unwrap().value.parent_level == the_node.level);
717                assert(!new_owner.children[i as int].unwrap().value.in_scope);
718
719                OwnerSubtree::child_some_properties(new_owner, i as usize);
720            }
721
722            let tracked mut new_owner_node = new_owner.value.node.tracked_take();
723
724            proof {
725                let ghost old_children_perm = new_owner_node.children_perm;
726            }
727
728            assert(pa + i * page_size((level - 1) as u8) < MAX_PADDR) by { admit() };
729            let small_pa = pa + i * page_size(level - 1);
730
731            assert(small_pa % PAGE_SIZE == 0) by { admit() };
732
733            let tracked child_owner = EntryOwner::new_frame(
734                small_pa,
735                new_owner.value.path.push_tail(i as usize),
736                (level - 1) as PagingLevel,
737                prop,
738            );
739
740            #[verus_spec(with Tracked(&new_owner_node), Tracked(&new_owner.children.tracked_borrow(i as int).tracked_borrow().value), Tracked(&new_guard_perm))]
741            let mut entry = PageTableGuard::entry(pt_lock_guard, i);
742
743            proof {
744                assert(entry.idx == i as usize);
745                let ghost child_before_remove = new_owner.child(i as usize).unwrap();
746                assert(child_before_remove.inv());
747            }
748            let tracked mut new_owner_child = new_owner.children.tracked_remove(
749                i as int,
750            ).tracked_unwrap();
751
752            proof {
753                assert(new_owner_child.value.match_pte(
754                    new_owner_node.children_perm.value()[i as int],
755                    new_owner_child.value.parent_level,
756                ));
757
758                let idx = frame_to_index(small_pa);
759                assert(regions.slot_owners[idx].path_if_in_pt is None) by { admit() };
760
761                assert(entry.node_matching(new_owner_child.value, new_owner_node, new_guard_perm))
762                    by {
763                    let pte = new_owner_node.children_perm.value()[i as int];
764                    assert(pte == C::E::new_absent_spec());
765                    crate::specs::arch::PageTableEntry::absent_pte_paddr_ok();
766                    EntryOwner::absent_match_pte(
767                        new_owner_child.value,
768                        pte,
769                        new_owner_child.value.parent_level,
770                    );
771                };
772            }
773
774            #[verus_spec(with Tracked(regions),
775                Tracked(&mut new_owner_child.value),
776                Tracked(&mut child_owner),
777                Tracked(&mut new_owner_node),
778                Tracked(&mut new_guard_perm))]
779            let old = entry.replace(Child::Frame(small_pa, level - 1, prop));
780
781            proof {
782                new_owner.value.node = Some(new_owner_node);
783                OwnerSubtree::set_value_property(new_owner_child, child_owner);
784                new_owner_child.value = child_owner;
785                new_owner.children.tracked_insert(i as int, Some(new_owner_child));
786
787                TreePath::push_tail_property_len(new_owner_path, i as usize);
788                OwnerSubtree::insert_preserves_inv(new_owner, i as usize, new_owner_child);
789            }
790        }
791
792        self.pte = (#[verus_spec(with Tracked(&mut new_owner.value), Tracked(regions))]
793        Child::PageTable(new_page).into_pte());
794
795        proof {
796            new_owner.level = owner.level;
797            *owner = new_owner;
798        }
799
800        let tracked mut node_owner = owner.value.node.tracked_take();
801
802        // SAFETY:
803        //  1. The index is within the bounds.
804        //  2. The new PTE is a child in `C` and at the correct paging level.
805        //  3. The ownership of the child is passed to the page table node.
806        #[verus_spec(with Tracked(&mut node_owner))]
807        node_guard.write_pte(self.idx, self.pte);
808
809        proof {
810            owner.value.node = Some(node_owner);
811        }
812
813        self.node.put(Tracked(guard_perm), node_guard);
814
815        Some(pt_lock_guard)
816    }
817
818    /// Create a new entry at the node with guard.
819    ///
820    /// # Verified Properties
821    /// ## Preconditions
822    /// - **Safety**: The caller must provide the owner of the entry and the parent node, and the entry
823    /// must match the parent node's PTE at the given index.
824    /// - **Safety**: The caller must provide a valid guard permission matching `guard`, and it must be guarding the
825    /// correct parent.
826    /// ## Postconditions
827    /// - **Correctness**: The resulting entry matches the owner.
828    /// ## Safety
829    /// - The precondition ensures that the index is within the bounds of the node.
830    /// - This function does not modify the actual entry or any other relevant structure, so it is safe to call.
831    /// Because we also require the guard to be correct, it will be safe to use the resulting `Entry` as a handle to the
832    /// underlying `PTE`.
833    #[verus_spec(
834        with Tracked(owner): Tracked<&EntryOwner<C>>,
835            Tracked(parent_owner): Tracked<&NodeOwner<C>>,
836            Tracked(guard_perm): Tracked<&GuardPerm<'rcu, C>>
837    )]
838    pub(in crate::mm) fn new_at(guard: PPtr<PageTableGuard<'rcu, C>>, idx: usize) -> (res: Self)
839        requires
840            owner.inv(),
841            !owner.in_scope,
842            parent_owner.inv(),
843            guard_perm.addr() == guard.addr(),
844            parent_owner.relate_guard_perm(*guard_perm),
845            idx < NR_ENTRIES,
846            owner.match_pte(parent_owner.children_perm.value()[idx as int], owner.parent_level),
847        ensures
848            res.wf(*owner),
849            res.node.addr() == guard_perm.addr(),
850            res.idx == idx,
851    {
852        // SAFETY: The index is within the bound.
853        let guard_val = guard.borrow(Tracked(guard_perm));
854        #[verus_spec(with Tracked(parent_owner))]
855        let pte = guard_val.read_pte(idx);
856        Self { pte, idx, node: guard }
857    }
858}
859
860} // verus!