Skip to main content

ostd/mm/page_table/node/
mod.rs

1// SPDX-License-Identifier: MPL-2.0
2//! This module defines page table node abstractions and the handle.
3//!
4//! The page table node is also frequently referred to as a page table in many architectural
5//! documentations. It is essentially a page that contains page table entries (PTEs) that map
6//! to child page tables nodes or mapped pages.
7//!
8//! This module leverages the page metadata to manage the page table pages, which makes it
9//! easier to provide the following guarantees:
10//!
11//! The page table node is not freed when it is still in use by:
12//!    - a parent page table node,
13//!    - or a handle to a page table node,
14//!    - or a processor.
15//!
16//! This is implemented by using a reference counter in the page metadata. If the above
17//! conditions are not met, the page table node is ensured to be freed upon dropping the last
18//! reference.
19//!
20//! One can acquire exclusive access to a page table node using merely the physical address of
21//! the page table node. This is implemented by a lock in the page metadata. Here the
22//! exclusiveness is only ensured for kernel code, and the processor's MMU is able to access the
23//! page table node while a lock is held. So the modification to the PTEs should be done after
24//! the initialization of the entity that the PTE points to. This is taken care in this module.
25//!
26mod child;
27mod entry;
28
29#[path = "../../../../specs/mm/page_table/node/child.rs"]
30mod child_specs;
31#[path = "../../../../specs/mm/page_table/node/entry.rs"]
32mod entry_specs;
33
34use vstd::{atomic::PAtomicU8, cell::pcell_maybe_uninit, prelude::*, simple_pptr::PPtr};
35use vstd_extra::{array_ptr, cast_ptr::*, ghost_tree::*, ownership::*};
36
37pub use crate::specs::mm::page_table::node::{entry_owners::*, owners::*};
38use crate::specs::mm::{
39    frame::{mapping::*, meta_owners::*, meta_region_owners::MetaRegionOwners},
40    page_table::node::owners::*,
41};
42
43use super::{PageTableConfig, PageTableEntryTrait, nr_subpage_per_huge};
44use crate::mm::frame::{
45    allocator::FrameAllocOptions,
46    meta::{
47        META_SLOT_SIZE, MetaSlot, REF_COUNT_MAX,
48        mapping::{frame_to_meta, meta_to_frame},
49    },
50};
51use crate::mm::page_table::*;
52use crate::mm::{Paddr, Vaddr};
53use crate::{
54    mm::{
55        PagingConstsTrait,
56        PagingLevel,
57        //        FrameAllocOptions, Infallible,
58        //        VmReader,
59        frame::{Frame, FrameRef, meta::AnyFrameMeta},
60        paddr_to_vaddr,
61        page_table::{load_pte, store_pte},
62    },
63    specs::task::InAtomicMode,
64};
65pub use child::*;
66use core::{marker::PhantomData, ops::Deref, sync::atomic::Ordering};
67pub use entry::*;
68
69verus! {
70
71/// The metadata of any kinds of page table pages.
72/// Make sure the the generic parameters don't effect the memory layout.
73pub struct PageTablePageMeta<C: PageTableConfig> {
74    /// The number of valid PTEs. It is mutable if the lock is held.
75    pub nr_children: pcell_maybe_uninit::PCell<u16>,
76    /// If the page table is detached from its parent.
77    ///
78    /// A page table can be detached from its parent while still being accessed,
79    /// since we use a RCU scheme to recycle page tables. If this flag is set,
80    /// it means that the parent is recycling the page table.
81    pub stray: pcell_maybe_uninit::PCell<bool>,
82    /// The level of the page table page. A page table page cannot be
83    /// referenced by page tables of different levels.
84    pub level: PagingLevel,
85    /// The lock for the page table page.
86    pub lock: PAtomicU8,
87    pub _phantom: core::marker::PhantomData<C>,
88}
89
90/// A smart pointer to a page table node.
91///
92/// This smart pointer is an owner of a page table node. Thus creating and
93/// dropping it will affect the reference count of the page table node. If
94/// dropped it as the last reference, the page table node and subsequent
95/// children will be freed.
96///
97/// [`PageTableNode`] is read-only. To modify the page table node, lock and use
98/// [`PageTableGuard`].
99pub type PageTableNode<C> = Frame<PageTablePageMeta<C>>;
100
101unsafe impl<C: PageTableConfig> AnyFrameMeta for PageTablePageMeta<C> {
102    /// Caller invariants the PT-node `on_drop` body relies on:
103    /// - Reader well-formedness + `vm_io_owner` matching + read view
104    ///   initialized + at least `PAGE_SIZE` bytes remaining for the
105    ///   PT-node walk.
106    /// - Global region table invariant.
107    /// - Embedding ([`child_perms_embedding`]): for every paddr in
108    ///   `child_perms.dom()`, the slot and perm match `from_raw` /
109    ///   `VerifiedDrop::drop`'s expected shape.
110    /// - Walk coverage ([`walk_coverage_from_view`]): for every present
111    ///   non-last PTE in the page bytes, `frame_to_index(pte.paddr()) ∈
112    ///   child_perms.dom()`.
113    /// - Walk uniqueness ([`walk_uniqueness_from_view`]): distinct PTE
114    ///   positions with present non-last PTEs have distinct paddrs.
115    ///
116    /// Coverage and uniqueness retain the memory-safety premises needed by the
117    /// trusted destructor body, without storing item permissions in
118    /// [`VmIoOwner`](crate::specs::mm::io::VmIoOwner).
119    open spec fn on_drop_pre(
120        &self,
121        reader: crate::mm::VmReader<'_, crate::mm::Infallible>,
122        regions: crate::specs::mm::frame::meta_region_owners::MetaRegionOwners,
123        vm_io_owner: crate::specs::mm::io::VmIoOwner,
124    ) -> bool {
125        &&& reader.inv()
126        &&& reader.wf(vm_io_owner)
127        &&& reader.remain_spec() >= crate::specs::arch::PAGE_SIZE
128        &&& reader.cursor.vaddr % core::mem::align_of::<C::E>() == 0
129        &&& vm_io_owner.inv()
130        &&& vm_io_owner.read_view_initialized()
131        &&& regions.inv()
132        &&& Self::child_perms_embedding(regions, vstd::set::Set::empty())
133        &&& self.walk_coverage_from_view(reader, vm_io_owner.read_view_of(), regions.slots.dom())
134        &&& self.walk_uniqueness_from_view(reader, vm_io_owner.read_view_of())
135    }
136
137    /// Drops the children of a page-table node.
138    ///
139    /// The permission handoff for recursively destroying page-table entries is
140    /// not modeled yet. Keep that trusted boundary local to this destructor;
141    /// `VmIoOwner` only supplies the memory view used by `reader`.
142    #[verifier::external_body]
143    fn on_drop(
144        &mut self,
145        reader: &mut crate::mm::VmReader<'_, crate::mm::Infallible>,
146        Tracked(regions): Tracked<
147            &mut crate::specs::mm::frame::meta_region_owners::MetaRegionOwners,
148        >,
149        Tracked(_vm_io_owner): Tracked<&mut crate::specs::mm::io::VmIoOwner>,
150    ) {
151        let level = self.level;
152        let range = if level == C::NR_LEVELS() {
153            C::TOP_LEVEL_INDEX_RANGE()
154        } else {
155            0..nr_subpage_per_huge::<C>()
156        };
157
158        reader.skip_in_place(range.start * core::mem::size_of::<C::E>());
159
160        for _ in range {
161            // Non-atomic read is OK because we have mutable access.
162            let pte = reader.read_once::<C::E>().unwrap();
163            if pte.is_present() {
164                let paddr = pte.paddr();
165                // As a fast path, we can ensure that the type of the child frame
166                // is `Self` if the PTE points to a child page table. Then we don't
167                // need to check the vtable for the drop method.
168                if !pte.is_last(level) {
169                    // SAFETY: The PTE points to a page table node. The ownership
170                    // of the child is transferred to the child then dropped.
171                    let frame = unsafe { Frame::<Self>::from_raw(paddr) };
172                    frame.drop(Tracked(regions));
173                } else {
174                    proof_decl! {
175                        let tracked item_perm: Option<C::Perm>;
176                    }
177                    // SAFETY: The PTE points to a mapped item. The ownership
178                    // of the item is transferred here then dropped.
179                    let _item = unsafe {
180                        C::item_from_raw(paddr, level, pte.prop(), Tracked(item_perm))
181                    };
182                }
183            }
184        }
185    }
186
187    fn is_untyped(&self) -> bool {
188        false
189    }
190
191    uninterp spec fn vtable_ptr(&self) -> usize;
192}
193
194#[verus_verify]
195impl<C: PageTableConfig> PageTableNode<C> {
196    /// Gets the level of a page table node.
197    /// # Verified Properties
198    /// ## Preconditions
199    /// - The node must be well-formed, and the caller must provide a permission token for its metadata.
200    /// ## Postconditions
201    /// - Returns the level of the node.
202    /// ## Safety
203    /// - We require the caller to provide a permission token to ensure that this function is only called on a valid page table node.
204    #[verus_spec(
205        with
206            Tracked(owner): Tracked<&NodeOwner<C>>,
207    )]
208    pub(super) fn level(&self) -> PagingLevel
209        requires
210            self.external_meta_wf(owner.frame_permission.resource(), ()),
211        returns
212            owner.level(),
213    {
214        #[verus_spec(with
215            Tracked(Some(owner.tracked_borrow_metadata_perm())),
216            Tracked(&())
217        )]
218        let meta = self.meta();
219        meta.level
220    }
221
222    /// Allocates a new empty page table node.
223    #[verus_spec(res =>
224        with Tracked(regions): Tracked<&mut MetaRegionOwners>,
225             Tracked(guards): Tracked<&Guards>,
226                 -> owner: Tracked<OwnerSubtree<C>>,
227        requires
228            1 <= level < NR_LEVELS,
229            old(regions).inv(),
230        ensures
231            final(regions).inv(),
232            allocated_empty_node_owner(owner@, level),
233            allocated_empty_node_grandchildren_none(owner@),
234            res.slot_vaddr() == owner@.value().node().slot_vaddr(),
235            res.external_meta_wf(owner@.value().node().frame_permission.resource(), ()),
236            res.tracked_slot_perm@ == final(regions).slots[owner@.value().node().slot_index],
237            guards.unlocked(owner@.value().node().slot_vaddr()),
238            MetaSlot::claim_unused_slot_region_spec(
239                meta_to_frame(owner@.value().node().slot_vaddr()),
240                false, *old(regions), *final(regions)),
241
242            old(regions).contains(meta_to_index(owner@.value().node().slot_vaddr())),
243
244            final(regions).slot_owners[owner@.value().node().slot_index].paths_in_pt.is_empty(),
245            owner@.value().match_pte(C::E::new_pt_spec(meta_to_frame(owner@.value().node().slot_vaddr())), (level + 1) as PagingLevel),
246            final(regions).contains(owner@.value().node().slot_index),
247            owner@.value().node().metaregion_sound_node(*final(regions)),
248    )]
249    #[verifier::external_body]
250    pub fn alloc<'rcu>(level: PagingLevel) -> Self {
251        let tracked entry_owner = EntryOwner::tracked_new_absent(
252            TreePath::new(Seq::empty()),
253            level,
254        );
255
256        let tracked mut owner = OwnerSubtree::<C>::tracked_new_val(entry_owner, level as nat);
257        let meta = PageTablePageMeta::new(level);
258        let mut frame = FrameAllocOptions::new();
259        frame.zeroed(true);
260        let allocated_frame = frame.alloc_frame_with(meta).expect(
261            "Failed to allocate a page table node",
262        );
263        // The allocated frame is zeroed. Make sure zero is absent PTE.
264        //debug_assert_eq!(C::E::new_absent().as_usize(), 0);
265
266        proof_with!(|= Tracked(owner));
267
268        allocated_frame
269    }/*
270    /// Activates the page table assuming it is a root page table.
271    ///
272    /// Here we ensure not dropping an active page table by making a
273    /// processor a page table owner. When activating a page table, the
274    /// reference count of the last activated page table is decremented.
275    /// And that of the current page table is incremented.
276    ///
277    /// # Safety
278    ///
279    /// The caller must ensure that the page table to be activated has
280    /// proper mappings for the kernel and has the correct const parameters
281    /// matching the current CPU.
282    ///
283    /// # Panics
284    ///
285    /// Only top-level page tables can be activated using this function.
286    pub(crate) unsafe fn activate(&self) {
287        use crate::{
288            arch::mm::{activate_page_table, current_page_table_paddr},
289            mm::page_prop::CachePolicy,
290        };
291
292        #[cfg(feature = "allow_panic")]
293        assert_eq!(self.level(), C::NR_LEVELS());
294
295        let last_activated_paddr = current_page_table_paddr();
296        if last_activated_paddr == self.start_paddr() {
297            return;
298        }
299
300        // SAFETY: The safety is upheld by the caller.
301        unsafe { activate_page_table(self.clone().into_raw(), CachePolicy::Writeback) };
302
303        // Restore and drop the last activated page table.
304        // SAFETY: The physical address is valid and points to a forgotten page table node.
305        drop(unsafe { Self::from_raw(last_activated_paddr) });
306    }
307
308    /// Activates the (root) page table assuming it is the first activation.
309    ///
310    /// It will not try dropping the last activate page table. It is the same
311    /// with [`Self::activate()`] in other senses.
312    pub(super) unsafe fn first_activate(&self) {
313        use crate::{arch::mm::activate_page_table, mm::page_prop::CachePolicy};
314
315        // SAFETY: The safety is upheld by the caller.
316        unsafe { activate_page_table(self.clone().into_raw(), CachePolicy::Writeback) };
317    }*/
318
319}
320
321#[verus_verify]
322impl<'a, C: PageTableConfig> PageTableNodeRef<'a, C> {
323    pub open spec fn locks_preserved_except<'rcu>(
324        addr: usize,
325        guards0: Guards,
326        guards1: Guards,
327    ) -> bool {
328        &&& OwnerSubtree::implies(
329            CursorOwner::<'rcu, C>::node_unlocked(guards0),
330            CursorOwner::<'rcu, C>::node_unlocked_except(guards1, addr),
331        )
332        &&& forall|i: usize| guards0.lock_held(i) ==> guards1.lock_held(i)
333        &&& forall|i: usize| guards0.unlocked(i) && i != addr ==> guards1.unlocked(i)
334    }
335
336    /// Locks the page table node.
337    ///
338    /// An atomic mode guard is required to
339    ///  1. prevent deadlocks;
340    ///  2. provide a lifetime (`'rcu`) that the nodes are guaranteed to outlive.
341    /// # Verification Design
342    /// As of when we verified this library, we didn't have a spin lock implementation, so we axiomatize
343    /// what happens when it's successful.
344    #[verifier::external_body]
345    #[verus_spec(res =>
346        with Tracked(owner): Tracked<&NodeOwner<C>>,
347            Tracked(guards): Tracked<&mut Guards>
348        requires
349            self.inner@.invariants(*owner),
350            self.inner@.external_meta_wf(owner.frame_permission.resource(), ()),
351            old(guards).unlocked(owner.slot_vaddr()),
352        ensures
353            final(guards).lock_held(owner.slot_vaddr()),
354            Self::locks_preserved_except(owner.slot_vaddr(), *old(guards), *final(guards)),
355            owner.relate_guard(res),
356    )]
357    pub fn lock<'rcu, A: InAtomicMode>(self, _guard: &'rcu A) -> PageTableGuard<'rcu, C> where
358        'a: 'rcu,
359     {
360        unimplemented!()
361    }
362
363    /// Creates a new [`PageTableGuard`] without checking if the page table lock is held.
364    ///
365    /// # Safety
366    ///
367    /// This function must be called if this task logically holds the lock.
368    ///
369    /// Calling this function when a guard is already created is undefined behavior
370    /// unless that guard was already forgotten.
371    #[verus_spec(res =>
372        with Tracked(owner): Tracked<&NodeOwner<C>>,
373             Tracked(guards): Tracked<&mut Guards>,
374        requires
375            self.inner@.invariants(*owner),
376            self.inner@.external_meta_wf(owner.frame_permission.resource(), ()),
377            old(guards).unlocked(owner.slot_vaddr()),
378        ensures
379            final(guards).lock_held(owner.slot_vaddr()),
380            Self::locks_preserved_except(owner.slot_vaddr(), *old(guards), *final(guards)),
381            owner.relate_guard(res),
382    )]
383    pub unsafe fn make_guard_unchecked<'rcu, A: InAtomicMode>(
384        self,
385        _guard: &'rcu A,
386    ) -> PageTableGuard<'rcu, C> where 'a: 'rcu {
387        let guard = PageTableGuard { inner: self };
388
389        proof {
390            guards.guards = guards.guards.insert(owner.slot_vaddr());
391        }
392
393        guard
394    }
395}
396
397impl<'rcu, C: PageTableConfig> PageTableGuard<'rcu, C> {
398    /// Borrows an entry in the node at a given index.
399    ///
400    /// # Panics
401    ///
402    /// Panics if the index is not within the bound of
403    /// [`nr_subpage_per_huge<C>`].
404    #[verus_spec(res =>
405        with Tracked(owner): Tracked<&NodeOwner<C>>,
406             Tracked(child_owner): Tracked<&EntryOwner<C>>,
407             Tracked(regions): Tracked<&MetaRegionOwners>,
408        requires
409            owner.inv(),
410            child_owner.inv(),
411            owner.relate_guard(*old(self)),
412            child_owner.match_pte(
413                owner.children_perm.value()[idx as int],
414                child_owner.parent_level,
415            ),
416            regions.inv(),
417            regions.contains(owner.slot_index),
418            // Panic condition
419            idx < NR_ENTRIES,
420        ensures
421            res.wf(*child_owner),
422            res.idx == idx,
423            *res.node == *old(self),
424            *final(self) == *final(res.node),
425            owner.relate_guard(*res.node),
426    )]
427    pub fn entry<'a>(&'a mut self, idx: usize) -> Entry<'a, 'rcu, C> {
428        #[cfg(feature = "allow_panic")]
429        assert!(idx < nr_subpage_per_huge::<C>());
430        // SAFETY: The index is within the bound. `Entry::new_at` returns an
431        // entry whose node is the guard value we were handed.
432        unsafe {
433            #[verus_spec(with Tracked(child_owner), Tracked(owner), Tracked(regions))]
434            Entry::new_at(self, idx)
435        }
436    }
437
438    /// Gets the number of valid PTEs in a page table node.
439    /// # Verified Properties
440    /// ## Preconditions
441    /// - The node must be well-formed.
442    /// ## Postconditions
443    /// - Returns the number of valid PTEs in the node.
444    /// ## Safety
445    /// - We require the caller to provide a permission token to ensure that this function is only called on a valid page table node.
446    #[verus_spec(nr =>
447        with Tracked(owner) : Tracked<&NodeOwner<C>>,
448        requires
449            owner.meta_own.nr_children.id() == owner.meta_value().nr_children.id(),
450            self.inner.inner@.invariants(*owner),
451            self.inner.inner@.external_meta_wf(owner.frame_permission.resource(), ()),
452        returns
453            owner.meta_own.nr_children.value(),
454    )]
455    pub fn nr_children(&self) -> u16 {
456        #[verus_spec(with
457            Tracked(Some(owner.tracked_borrow_metadata_perm())),
458            Tracked(&())
459        )]
460        let meta = self.meta();
461
462        *meta.nr_children.borrow(Tracked(&owner.meta_own.nr_children))
463    }
464
465    /// Returns if the page table node is detached from its parent.
466    #[verus_spec(res =>
467        with
468            Tracked(metadata_perms): Tracked<&'a MetadataPerm>,
469            Tracked(repr_perm): Tracked<&'a ()>,
470            Ghost(stray_id): Ghost<vstd::cell::CellId>,
471        requires
472            old(self).inner.inner@.external_meta_wf(*metadata_perms, *repr_perm),
473            typed_meta_value::<PageTablePageMeta<C>>(
474                *metadata_perms,
475                *repr_perm,
476            ).stray.id()
477                == stray_id,
478        ensures
479            res.id() == stray_id,
480            *final(self) == *old(self),
481    )]
482    pub(super) fn stray_mut<'a>(&'a mut self) -> &'a pcell_maybe_uninit::PCell<bool> {
483        // SAFETY: The lock is held so we have an exclusive access.
484        #[verus_spec(with
485            Tracked(Some(metadata_perms)),
486            Tracked(repr_perm)
487        )]
488        let meta = self.meta();
489        &meta.stray
490    }
491
492    /// Reads a non-owning PTE at the given index.
493    ///
494    /// A non-owning PTE means that it does not account for a reference count
495    /// of the a page if the PTE points to a page. The original PTE still owns
496    /// the child page.
497    ///
498    /// # Safety
499    ///
500    /// The caller must ensure that the index is within the bound.
501    #[verus_spec(
502        with Tracked(owner): Tracked<&NodeOwner<C>>,
503             Tracked(regions): Tracked<&MetaRegionOwners>,
504        requires
505            self.inner.inner@.invariants(*owner),
506            regions.inv(),
507            regions.contains(owner.slot_index),
508            idx < NR_ENTRIES,
509        returns
510            owner.children_perm.value()[idx as int],
511    )]
512    pub unsafe fn read_pte(&self, idx: usize) -> C::E {
513        // debug_assert!(idx < nr_subpage_per_huge::<C>());
514        let ptr = vstd_extra::array_ptr::ArrayPtr::<C::E, NR_ENTRIES>::from_addr(
515            paddr_to_vaddr(self.start_paddr()),
516        );
517
518        // SAFETY:
519        // - The page table node is alive. The index is inside the bound, so the page table entry is valid.
520        // - All page table entries are aligned and accessed with atomic operations only.
521        unsafe {
522            #[verus_spec(with Tracked(&owner.children_perm))]
523            load_pte(ptr.add(idx), Ordering::Relaxed)
524        }
525    }
526
527    /// Writes a page table entry at a given index.
528    ///
529    /// This operation will leak the old child if the old PTE is present.
530    ///
531    /// # Safety
532    ///
533    /// The caller must ensure that:
534    ///  1. The index must be within the bound;
535    ///  2. The PTE must represent a valid [`Child`] whose level is compatible
536    ///     with the page table node.
537    ///  3. The page table node will have the ownership of the [`Child`]
538    ///     after this method.
539    #[verus_spec(
540        with Tracked(owner): Tracked<&mut NodeOwner<C>>,
541             Tracked(regions): Tracked<&MetaRegionOwners>,
542        requires
543            old(self).inner.inner@.invariants(*old(owner)),
544            regions.inv(),
545            regions.contains(old(owner).slot_index),
546            idx < NR_ENTRIES,
547        ensures
548            final(owner).inv(),
549            final(owner).level() == old(owner).level(),
550            final(owner).meta_own == old(owner).meta_own,
551            final(owner).frame_permission == old(owner).frame_permission,
552            final(owner).slot_index == old(owner).slot_index,
553            final(owner).tree_level == old(owner).tree_level,
554            final(owner).children_perm.value() == old(owner).children_perm.value().update(
555                idx as int,
556                pte,
557            ),
558            *final(self) == *old(self),
559    )]
560    pub unsafe fn write_pte(&mut self, idx: usize, pte: C::E) {
561        // debug_assert!(idx < nr_subpage_per_huge::<C>());
562        #[verusfmt::skip]
563        let ptr = vstd_extra::array_ptr::ArrayPtr::<C::E, NR_ENTRIES>::from_addr(
564            paddr_to_vaddr(self.start_paddr())
565        );
566
567        // SAFETY:
568        // - The page table node is alive. The index is inside the bound, so the page table entry is valid.
569        // - All page table entries are aligned and accessed with atomic operations only.
570        unsafe {
571            #[verus_spec(with Tracked(&mut owner.children_perm))]
572            store_pte(ptr.add(idx), pte, Ordering::Release)
573        }
574        proof {
575            assert(owner.children_perm.wf());
576        }
577    }
578
579    /// Gets the mutable reference to the number of valid PTEs in the node.
580    #[verus_spec(res =>
581        with
582            Tracked(metadata_perms): Tracked<&'a MetadataPerm>,
583            Ghost(nr_children_id): Ghost<vstd::cell::CellId>,
584        requires
585            old(self).inner.inner@.external_meta_wf(*metadata_perms, ()),
586            typed_meta_value::<PageTablePageMeta<C>>(
587                *metadata_perms,
588                (),
589            ).nr_children.id()
590                == nr_children_id,
591        ensures
592            res.id() == nr_children_id,
593            *final(self) == *old(self),
594    )]
595    fn nr_children_mut<'a>(&'a mut self) -> &'a pcell_maybe_uninit::PCell<u16> {
596        // SAFETY: The lock is held so we have an exclusive access.
597        #[verus_spec(with
598            Tracked(Some(metadata_perms)),
599            Tracked(&())
600        )]
601        let meta = self.meta();
602        &meta.nr_children
603    }
604}
605
606/*impl<C: PageTableConfig> Drop for PageTableGuard<'_, C> {
607    fn drop(&mut self) {
608        self.inner.meta().lock.store(0, Ordering::Release);
609    }
610}*/
611
612impl<C: PageTableConfig> PageTablePageMeta<C> {
613    pub fn new(level: PagingLevel) -> Self {
614        Self {
615            nr_children: pcell_maybe_uninit::PCell::new(0).0,
616            stray: pcell_maybe_uninit::PCell::new(false).0,
617            level,
618            lock: PAtomicU8::new(0).0,
619            _phantom: PhantomData,
620        }
621    }
622
623    /// The PTE value that `read_once::<C::E>` would produce at cursor `c`
624    /// against the given memory view. Linked to `read_once` via
625    /// `pod_bytes(v) == read_view.read_bytes(...)` (strengthened ensures)
626    /// and the checked byte-decoding model.
627    pub open spec fn walk_pte_at_view(view: crate::specs::mm::virt_mem::MemView, c: usize) -> C::E {
628        ostd_pod::decode_pod::<C::E>(view.read_bytes(c, core::mem::size_of::<C::E>()))
629    }
630
631    /// Single-cursor projection of [`walk_coverage_from_view`]. Extracting
632    /// the forall body to a named predicate lets the body invoke
633    /// [`lemma_coverage_at`] for one specific `c` instead of relying on
634    /// auto-trigger matching across the loop invariant's `forall|c|`.
635    pub open spec fn walk_coverage_at(
636        self,
637        view: crate::specs::mm::virt_mem::MemView,
638        dom: vstd::set::Set<int>,
639        c: usize,
640    ) -> bool {
641        let pte = Self::walk_pte_at_view(view, c);
642        pte.is_present() && !pte.is_last(self.level) ==> dom.contains(frame_to_index(pte.paddr()))
643    }
644
645    /// Instantiate [`walk_coverage_from_view`]'s forall at one cursor.
646    pub proof fn lemma_coverage_at(
647        self,
648        reader: crate::mm::VmReader<'_, crate::mm::Infallible>,
649        view: crate::specs::mm::virt_mem::MemView,
650        dom: vstd::set::Set<int>,
651        c: usize,
652    )
653        requires
654            self.walk_coverage_from_view(reader, view, dom),
655            reader.cursor.vaddr <= c,
656            c + core::mem::size_of::<C::E>() <= reader.cursor.vaddr + reader.remain_spec(),
657            (c - reader.cursor.vaddr) % core::mem::size_of::<C::E>() as int == 0,
658        ensures
659            self.walk_coverage_at(view, dom, c),
660    {
661    }
662
663    /// Instantiate [`walk_uniqueness_from_view`]'s forall at one cursor pair.
664    pub proof fn lemma_uniqueness_at_pair(
665        self,
666        reader: crate::mm::VmReader<'_, crate::mm::Infallible>,
667        view: crate::specs::mm::virt_mem::MemView,
668        c1: usize,
669        c2: usize,
670    )
671        requires
672            self.walk_uniqueness_from_view(reader, view),
673            reader.cursor.vaddr <= c1,
674            c1 + core::mem::size_of::<C::E>() <= reader.cursor.vaddr + reader.remain_spec(),
675            (c1 - reader.cursor.vaddr) % core::mem::size_of::<C::E>() as int == 0,
676            reader.cursor.vaddr <= c2,
677            c2 + core::mem::size_of::<C::E>() <= reader.cursor.vaddr + reader.remain_spec(),
678            (c2 - reader.cursor.vaddr) % core::mem::size_of::<C::E>() as int == 0,
679            c1 != c2,
680            Self::walk_pte_at_view(view, c1).is_present(),
681            !Self::walk_pte_at_view(view, c1).is_last(self.level),
682            Self::walk_pte_at_view(view, c2).is_present(),
683            !Self::walk_pte_at_view(view, c2).is_last(self.level),
684        ensures
685            Self::walk_pte_at_view(view, c1).paddr() != Self::walk_pte_at_view(view, c2).paddr(),
686    {
687    }
688
689    /// Caller-side dom-membership obligation: every present non-last PTE
690    /// position in the walk (over `view`) has its child-frame index in
691    /// `dom`. Phrased over a frozen `(view, dom)` pair so the body can
692    /// carry it as a loop invariant against an entry-state snapshot
693    /// while `vm_io_owner` advances per iteration.
694    pub open spec fn walk_coverage_from_view(
695        self,
696        reader: crate::mm::VmReader<'_, crate::mm::Infallible>,
697        view: crate::specs::mm::virt_mem::MemView,
698        dom: vstd::set::Set<int>,
699    ) -> bool {
700        forall|c: usize|
701            #![trigger Self::walk_pte_at_view(view, c)]
702            reader.cursor.vaddr <= c && c + core::mem::size_of::<C::E>() <= reader.cursor.vaddr
703                + reader.remain_spec() && (c - reader.cursor.vaddr) % core::mem::size_of::<
704                C::E,
705            >() as int == 0 ==> {
706                let pte = Self::walk_pte_at_view(view, c);
707                pte.is_present() && !pte.is_last(self.level) ==> dom.contains(
708                    frame_to_index(pte.paddr()),
709                )
710            }
711    }
712
713    /// Caller-side uniqueness obligation: distinct cursor positions with
714    /// present non-last PTEs (in `view`) map to distinct paddrs.
715    pub open spec fn walk_uniqueness_from_view(
716        self,
717        reader: crate::mm::VmReader<'_, crate::mm::Infallible>,
718        view: crate::specs::mm::virt_mem::MemView,
719    ) -> bool {
720        forall|c1: usize, c2: usize|
721            #![trigger Self::walk_pte_at_view(view, c1), Self::walk_pte_at_view(view, c2)]
722            reader.cursor.vaddr <= c1 && c1 + core::mem::size_of::<C::E>() <= reader.cursor.vaddr
723                + reader.remain_spec() && (c1 - reader.cursor.vaddr) % core::mem::size_of::<
724                C::E,
725            >() as int == 0 && reader.cursor.vaddr <= c2 && c2 + core::mem::size_of::<C::E>()
726                <= reader.cursor.vaddr + reader.remain_spec() && (c2 - reader.cursor.vaddr)
727                % core::mem::size_of::<C::E>() as int == 0 && c1 != c2 ==> {
728                let pte1 = Self::walk_pte_at_view(view, c1);
729                let pte2 = Self::walk_pte_at_view(view, c2);
730                pte1.is_present() && !pte1.is_last(self.level) && pte2.is_present()
731                    && !pte2.is_last(self.level) ==> pte1.paddr() != pte2.paddr()
732            }
733    }
734
735    /// Caller-side shape obligation: every paddr in `child_perms.dom()`
736    /// has a slot perm matching the shape `from_raw` + `VerifiedDrop::drop`
737    /// expect (init, alignment, refcount within bounds, last-reference
738    /// shape when refcount == 1).
739    pub open spec fn child_perms_embedding(
740        regions: crate::specs::mm::frame::meta_region_owners::MetaRegionOwners,
741        excluded: vstd::set::Set<int>,
742    ) -> bool {
743        forall|paddr: crate::mm::Paddr|
744            #![trigger regions.slot_owner(paddr)]
745            regions.slots.dom().contains(frame_to_index(paddr)) && !excluded.contains(
746                frame_to_index(paddr),
747            ) ==> {
748                let idx = frame_to_index(paddr);
749                let so = regions.slot_owners[idx];
750                &&& <Frame<Self>>::from_raw_requires(regions, paddr)
751                &&& 0 < so.ref_count() <= REF_COUNT_MAX
752                &&& so.storage_perm().is_init()
753                &&& so.ref_count() == 1 ==> {
754                    &&& so.in_list_perm.value() == 0
755                    &&& so.paths_in_pt.is_empty()
756                }
757            }
758    }
759}
760
761} // verus!