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
29pub use crate::specs::mm::page_table::node::{entry_owners::*, entry_view::*, owners::*};
30pub use child::*;
31pub use entry::*;
32
33use vstd::prelude::*;
34
35use vstd::atomic::PAtomicU8;
36use vstd::cell::PCell;
37use vstd::simple_pptr::{self, PPtr};
38
39use vstd_extra::array_ptr;
40use vstd_extra::cast_ptr::*;
41use vstd_extra::ghost_tree::*;
42use vstd_extra::ownership::*;
43
44use crate::mm::frame::allocator::FrameAllocOptions;
45use crate::mm::frame::meta::MetaSlot;
46use crate::mm::frame::{AnyFrameMeta, Frame, StoredPageTablePageMeta};
47use crate::mm::page_table::*;
48use crate::mm::{kspace::LINEAR_MAPPING_BASE_VADDR, paddr_to_vaddr, Paddr, Vaddr};
49use crate::specs::arch::kspace::FRAME_METADATA_RANGE;
50use crate::specs::arch::kspace::VMALLOC_BASE_VADDR;
51use crate::specs::mm::frame::mapping::{meta_to_frame, META_SLOT_SIZE};
52use crate::specs::mm::frame::meta_owners::MetaSlotOwner;
53use crate::specs::mm::frame::meta_region_owners::MetaRegionOwners;
54use crate::specs::mm::page_table::node::owners::*;
55
56use core::{marker::PhantomData, ops::Deref, sync::atomic::Ordering};
57
58use super::nr_subpage_per_huge;
59
60use crate::{
61    mm::{
62        page_table::{load_pte, store_pte},
63        //        FrameAllocOptions, Infallible,
64        //        VmReader,
65    },
66    specs::task::InAtomicMode,
67};
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<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<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
101impl<C: PageTableConfig> PageTablePageMeta<C> {
102    #[verifier::external_body]
103    pub fn get_stray(&self) -> PCell<bool>
104        returns
105            self.stray,
106    {
107        unimplemented!()
108    }
109
110    pub open spec fn into_spec(self) -> StoredPageTablePageMeta {
111        StoredPageTablePageMeta {
112            nr_children: self.nr_children,
113            stray: self.stray,
114            level: self.level,
115            lock: self.lock,
116        }
117    }
118
119    #[verifier::when_used_as_spec(into_spec)]
120    pub fn into(self) -> (res: StoredPageTablePageMeta)
121        ensures
122            res == self.into_spec(),
123    {
124        StoredPageTablePageMeta {
125            nr_children: self.nr_children,
126            stray: self.stray,
127            level: self.level,
128            lock: self.lock,
129        }
130    }
131}
132
133impl StoredPageTablePageMeta {
134    pub open spec fn into_spec<C: PageTableConfig>(self) -> PageTablePageMeta<C> {
135        PageTablePageMeta::<C> {
136            nr_children: self.nr_children,
137            stray: self.stray,
138            level: self.level,
139            lock: self.lock,
140            _phantom: PhantomData,
141        }
142    }
143
144    #[verifier::when_used_as_spec(into_spec)]
145    pub fn into<C: PageTableConfig>(self) -> (res: PageTablePageMeta<C>)
146        ensures
147            res == self.into_spec::<C>(),
148    {
149        PageTablePageMeta::<C> {
150            nr_children: self.nr_children,
151            stray: self.stray,
152            level: self.level,
153            lock: self.lock,
154            _phantom: PhantomData,
155        }
156    }
157}
158
159uninterp spec fn drop_tree_spec<C: PageTableConfig>(_page: Frame<PageTablePageMeta<C>>) -> Frame<
160    PageTablePageMeta<C>,
161>;
162
163#[verifier::external_body]
164extern "C" fn drop_tree<C: PageTableConfig>(_page: &mut Frame<PageTablePageMeta<C>>)
165    ensures
166        *_page == drop_tree_spec::<C>(*old(_page)),
167;
168
169impl<C: PageTableConfig> Repr<MetaSlot> for PageTablePageMeta<C> {
170    uninterp spec fn wf(r: MetaSlot) -> bool;
171
172    uninterp spec fn to_repr_spec(self) -> MetaSlot;
173
174    #[verifier::external_body]
175    fn to_repr(self) -> MetaSlot {
176        unimplemented!()
177    }
178
179    uninterp spec fn from_repr_spec(r: MetaSlot) -> Self;
180
181    #[verifier::external_body]
182    fn from_repr(r: MetaSlot) -> Self {
183        unimplemented!()
184    }
185
186    #[verifier::external_body]
187    fn from_borrowed<'a>(r: &'a MetaSlot) -> &'a Self {
188        unimplemented!()
189    }
190
191    proof fn from_to_repr(self) {
192        admit()
193    }
194
195    proof fn to_from_repr(r: MetaSlot) {
196        admit()
197    }
198
199    proof fn to_repr_wf(self) {
200        admit()
201    }
202}
203
204impl<C: PageTableConfig> AnyFrameMeta for PageTablePageMeta<C> {
205    fn on_drop(&mut self) {
206    }
207
208    fn is_untyped(&self) -> bool {
209        false
210    }
211
212    uninterp spec fn vtable_ptr(&self) -> usize;
213}
214
215#[verus_verify]
216impl<C: PageTableConfig> PageTableNode<C> {
217    #[verus_spec(
218        with Tracked(perm) : Tracked<&PointsTo<MetaSlot, PageTablePageMeta<C>>>
219    )]
220    pub fn level(&self) -> PagingLevel
221        requires
222            self.ptr.addr() == perm.addr(),
223            self.ptr.addr() == perm.points_to.addr(),
224            perm.is_init(),
225            perm.wf(),
226        returns
227            perm.value().level,
228    {
229        #[verus_spec(with Tracked(perm))]
230        let meta = self.meta();
231        meta.level
232    }
233
234    /// Allocates a new empty page table node.
235    #[verus_spec(res =>
236        with Tracked(regions): Tracked<&mut MetaRegionOwners>
237        -> owner: Tracked<OwnerSubtree<C>>
238        requires
239            level <= NR_LEVELS(),
240            old(regions).inv()
241        ensures
242            regions.inv()
243    )]
244    #[verifier::external_body]
245    pub fn alloc(level: PagingLevel) -> Self {
246        let tracked entry_owner = EntryOwner::new_absent(level);
247
248        let tracked mut owner = OwnerSubtree::<C>::new_val_tracked(entry_owner, level as nat);
249        let meta = PageTablePageMeta::new(level);
250        let mut frame = FrameAllocOptions::new();
251        frame.zeroed(true);
252        let allocated_frame = frame.alloc_frame_with(meta).expect(
253            "Failed to allocate a page table node",
254        );
255        // The allocated frame is zeroed. Make sure zero is absent PTE.
256        //debug_assert_eq!(C::E::new_absent().as_usize(), 0);
257
258        proof_with!(|= Tracked(owner));
259
260        allocated_frame
261    }/*
262    /// Activates the page table assuming it is a root page table.
263    ///
264    /// Here we ensure not dropping an active page table by making a
265    /// processor a page table owner. When activating a page table, the
266    /// reference count of the last activated page table is decremented.
267    /// And that of the current page table is incremented.
268    ///
269    /// # Safety
270    ///
271    /// The caller must ensure that the page table to be activated has
272    /// proper mappings for the kernel and has the correct const parameters
273    /// matching the current CPU.
274    ///
275    /// # Panics
276    ///
277    /// Only top-level page tables can be activated using this function.
278    pub(crate) unsafe fn activate(&self) {
279        use crate::{
280            arch::mm::{activate_page_table, current_page_table_paddr},
281            mm::page_prop::CachePolicy,
282        };
283
284        assert_eq!(self.level(), C::NR_LEVELS);
285
286        let last_activated_paddr = current_page_table_paddr();
287        if last_activated_paddr == self.start_paddr() {
288            return;
289        }
290
291        // SAFETY: The safety is upheld by the caller.
292        unsafe { activate_page_table(self.clone().into_raw(), CachePolicy::Writeback) };
293
294        // Restore and drop the last activated page table.
295        // SAFETY: The physical address is valid and points to a forgotten page table node.
296        drop(unsafe { Self::from_raw(last_activated_paddr) });
297    }
298
299    /// Activates the (root) page table assuming it is the first activation.
300    ///
301    /// It will not try dropping the last activate page table. It is the same
302    /// with [`Self::activate()`] in other senses.
303    pub(super) unsafe fn first_activate(&self) {
304        use crate::{arch::mm::activate_page_table, mm::page_prop::CachePolicy};
305
306        // SAFETY: The safety is upheld by the caller.
307        unsafe { activate_page_table(self.clone().into_raw(), CachePolicy::Writeback) };
308    }*/
309
310}
311
312#[verus_verify]
313impl<'a, C: PageTableConfig> PageTableNodeRef<'a, C> {
314    /// Locks the page table node.
315    ///
316    /// An atomic mode guard is required to
317    ///  1. prevent deadlocks;
318    ///  2. provide a lifetime (`'rcu`) that the nodes are guaranteed to outlive.
319    #[verifier::external_body]
320    #[verus_spec(
321        with Tracked(guards): Tracked<&mut Guards<'rcu, C>>
322        -> guard_perm: Tracked<GuardPerm<'rcu, C>>
323    )]
324    pub fn lock<'rcu, A: InAtomicMode>(self, _guard: &'rcu A) -> PPtr<PageTableGuard<'rcu, C>>
325        where 'a: 'rcu {
326        unimplemented!()
327    }
328
329    /// Creates a new [`PageTableGuard`] without checking if the page table lock is held.
330    ///
331    /// # Safety
332    ///
333    /// This function must be called if this task logically holds the lock.
334    ///
335    /// Calling this function when a guard is already created is undefined behavior
336    /// unless that guard was already forgotten.
337    #[verus_spec(res =>
338        with Tracked(owner): Tracked<&NodeOwner<C>>,
339            Tracked(guards): Tracked<&mut Guards<'rcu, C>>
340            -> guard_perm: Tracked<GuardPerm<'rcu, C>>
341        requires
342            owner.inv(),
343            self.inner@.wf(*owner),
344            !old(guards).guards.contains_key(owner.meta_perm.addr()),
345            old(guards).unlocked(owner.meta_perm.addr()),
346        ensures
347            guards.lock_held(owner.meta_perm.addr()),
348            OwnerSubtree::implies(CursorOwner::node_unlocked(*old(guards)),
349                CursorOwner::node_unlocked_except(*guards, owner.meta_perm.addr())),
350            forall |i: usize| old(guards).lock_held(i) ==> guards.lock_held(i),
351            res.addr() == guard_perm@.addr(),
352            owner.relate_guard_perm(guard_perm@),
353    )]
354    pub fn make_guard_unchecked<'rcu, A: InAtomicMode>(self, _guard: &'rcu A)
355    -> PPtr<PageTableGuard<'rcu, C>> where 'a: 'rcu {
356        let guard = PageTableGuard { inner: self };
357        let (ptr, guard_perm) = PPtr::<PageTableGuard<C>>::new(guard);
358
359        proof {
360            let ghost guards0 = *guards;
361            guards.guards.tracked_insert(owner.meta_perm.addr(), None);
362            assert(owner.relate_guard_perm(guard_perm@));
363
364            assert(forall|other: EntryOwner<C>, path: TreePath<CONST_NR_ENTRIES>| owner.inv() && CursorOwner::node_unlocked(guards0)(other, path)
365                ==> #[trigger] CursorOwner::node_unlocked_except(*guards, owner.meta_perm.addr())(other, path));
366        }
367
368        proof_with!{|= guard_perm}
369        ptr
370    }
371}
372
373//}
374impl<'rcu, C: PageTableConfig> PageTableGuard<'rcu, C> {
375    /// Borrows an entry in the node at a given index.
376    ///
377    /// # Panics
378    ///
379    /// Panics if the index is not within the bound of
380    /// [`nr_subpage_per_huge<C>`].
381    #[verus_spec(res =>
382        with Tracked(owner): Tracked<&NodeOwner<C>>,
383            Tracked(child_owner): Tracked<&EntryOwner<C>>,
384            Tracked(guard_perm): Tracked<&GuardPerm<'rcu, C>>,
385    )]
386    pub fn entry(guard: PPtr<Self>, idx: usize) -> (res: Entry<'rcu, C>)
387        requires
388            owner.inv(),
389            child_owner.inv(),
390            owner.relate_guard_perm(*guard_perm),
391            guard_perm.addr() == guard.addr(),
392            idx < NR_ENTRIES(),  // NR_ENTRIES == nr_subpage_per_huge::<C>()
393            child_owner.match_pte(owner.children_perm.value()[idx as int], child_owner.parent_level),
394        ensures
395            res.wf(*child_owner),
396            res.node.addr() == guard_perm.addr(),
397    {
398        //        assert!(idx < nr_subpage_per_huge::<C>());
399        // SAFETY: The index is within the bound.
400        #[verus_spec(with Tracked(child_owner), Tracked(owner), Tracked(guard_perm))]
401        Entry::new_at(guard, idx);
402    }
403
404    /// Gets the number of valid PTEs in the node.
405    #[rustc_allow_incoherent_impl]
406    #[verus_spec(
407        with Tracked(owner) : Tracked<&mut NodeOwner<C>>
408    )]
409    pub fn nr_children(&self) -> (nr: u16)
410        requires
411            self.inner.inner@.ptr.addr() == old(owner).meta_perm.addr(),
412            self.inner.inner@.ptr.addr() == old(owner).meta_perm.points_to.addr(),
413            old(owner).inv(),
414        ensures
415            *owner == *old(owner),
416    {
417        // SAFETY: The lock is held so we have an exclusive access.
418        #[verus_spec(with Tracked(&owner.meta_perm))]
419        let meta = self.meta();
420
421        *meta.nr_children.borrow(Tracked(&owner.meta_own.nr_children))
422    }
423
424    /// Returns if the page table node is detached from its parent.
425    #[rustc_allow_incoherent_impl]
426    #[verus_spec(
427        with Tracked(owner) : Tracked<EntryOwner<C>>
428    )]
429    pub fn stray_mut(&mut self) -> PCell<bool>
430        requires
431            owner.is_node(),
432            old(self).inner.inner@.ptr.addr() == owner.node.unwrap().meta_perm.addr(),
433            old(self).inner.inner@.ptr.addr() == owner.node.unwrap().meta_perm.points_to.addr(),
434            owner.inv(),
435    {
436        let tracked node_owner = owner.node.tracked_borrow();
437
438        // SAFETY: The lock is held so we have an exclusive access.
439        #[verus_spec(with Tracked(&node_owner.meta_perm))]
440        let meta = self.meta();
441
442        meta.get_stray()
443    }
444
445    /// Reads a non-owning PTE at the given index.
446    ///
447    /// A non-owning PTE means that it does not account for a reference count
448    /// of the a page if the PTE points to a page. The original PTE still owns
449    /// the child page.
450    ///
451    /// # Safety
452    ///
453    /// The caller must ensure that the index is within the bound.
454    #[rustc_allow_incoherent_impl]
455    #[verus_spec(
456        with Tracked(owner): Tracked<&NodeOwner<C>>
457    )]
458    pub fn read_pte(&self, idx: usize) -> (pte: C::E)
459        requires
460            self.inner.inner@.ptr.addr() == owner.meta_perm.addr(),
461            self.inner.inner@.ptr.addr() == owner.meta_perm.points_to.addr(),
462            owner.inv(),
463            meta_to_frame(owner.meta_perm.addr) < VMALLOC_BASE_VADDR()
464                - LINEAR_MAPPING_BASE_VADDR(),
465            FRAME_METADATA_RANGE().start <= owner.meta_perm.addr < FRAME_METADATA_RANGE().end,
466            owner.meta_perm.addr % META_SLOT_SIZE() == 0,
467            idx < NR_ENTRIES(),
468            owner.children_perm.addr() == paddr_to_vaddr(meta_to_frame(owner.meta_perm.addr)),
469        ensures
470            pte == owner.children_perm.value()[idx as int],
471    {
472        // debug_assert!(idx < nr_subpage_per_huge::<C>());
473        let ptr = vstd_extra::array_ptr::ArrayPtr::<C::E, CONST_NR_ENTRIES>::from_addr(
474            paddr_to_vaddr(
475                #[verus_spec(with Tracked(&owner.meta_perm.points_to))]
476                self.start_paddr(),
477            ),
478        );
479
480        // SAFETY:
481        // - The page table node is alive. The index is inside the bound, so the page table entry is valid.
482        // - All page table entries are aligned and accessed with atomic operations only.
483        #[verus_spec(with Tracked(&owner.children_perm))]
484        load_pte(ptr.add(idx), Ordering::Relaxed)
485    }
486
487    /// Writes a page table entry at a given index.
488    ///
489    /// This operation will leak the old child if the old PTE is present.
490    ///
491    /// # Safety
492    ///
493    /// The caller must ensure that:
494    ///  1. The index must be within the bound;
495    ///  2. The PTE must represent a valid [`Child`] whose level is compatible
496    ///     with the page table node.
497    ///  3. The page table node will have the ownership of the [`Child`]
498    ///     after this method.
499    #[rustc_allow_incoherent_impl]
500    #[verus_spec(
501        with Tracked(owner): Tracked<&mut NodeOwner<C>>
502    )]
503    #[verifier::external_body]
504    pub fn write_pte(&mut self, idx: usize, pte: C::E)
505        requires
506            old(owner).inv(),
507            meta_to_frame(old(owner).meta_perm.addr) < VMALLOC_BASE_VADDR()
508                - LINEAR_MAPPING_BASE_VADDR(),
509            idx < NR_ENTRIES(),
510        ensures
511            owner.inv(),
512            owner.meta_perm.addr() == old(owner).meta_perm.addr(),
513            owner.meta_own == old(owner).meta_own,
514            owner.meta_perm.points_to == old(owner).meta_perm.points_to,
515            *self == *old(self),
516    {
517        // debug_assert!(idx < nr_subpage_per_huge::<C>());
518        #[verusfmt::skip]
519        let ptr = vstd_extra::array_ptr::ArrayPtr::<C::E, CONST_NR_ENTRIES>::from_addr(
520            paddr_to_vaddr(
521                #[verus_spec(with Tracked(&owner.meta_perm.points_to))]
522                self.start_paddr()
523            ),
524        );
525
526        // SAFETY:
527        // - The page table node is alive. The index is inside the bound, so the page table entry is valid.
528        // - All page table entries are aligned and accessed with atomic operations only.
529        store_pte(ptr.add(idx), pte, Ordering::Release)
530    }
531
532    /// Gets the mutable reference to the number of valid PTEs in the node.
533    #[rustc_allow_incoherent_impl]
534    #[verus_spec(
535        with Tracked(meta_perm): Tracked<&'a PointsTo<MetaSlot, PageTablePageMeta<C>>>
536    )]
537    fn nr_children_mut<'a>(&'a mut self) -> (res: &'a PCell<u16>)
538        requires
539            old(self).inner.inner@.ptr.addr() == meta_perm.addr(),
540            old(self).inner.inner@.ptr.addr() == meta_perm.points_to.addr(),
541            meta_perm.is_init(),
542            meta_perm.wf(),
543        ensures
544            res.id() == meta_perm.value().nr_children.id(),
545            *self == *old(self),
546    {
547        // SAFETY: The lock is held so we have an exclusive access.
548        #[verus_spec(with Tracked(meta_perm))]
549        let meta = self.meta();
550        &meta.nr_children
551    }
552}
553
554/*impl<C: PageTableConfig> Drop for PageTableGuard<'_, C> {
555    fn drop(&mut self) {
556        self.inner.meta().lock.store(0, Ordering::Release);
557    }
558}*/
559
560impl<C: PageTableConfig> PageTablePageMeta<C> {
561    #[rustc_allow_incoherent_impl]
562    pub fn new(level: PagingLevel) -> Self {
563        Self {
564            nr_children: PCell::new(0).0,
565            stray: PCell::new(false).0,
566            level,
567            lock: PAtomicU8::new(0).0,
568            _phantom: PhantomData,
569        }
570    }
571}
572
573} // verus!
574/* TODO: Come back after VMReader
575// FIXME: The safe APIs in the `page_table/node` module allow `Child::Frame`s with
576// arbitrary addresses to be stored in the page table nodes. Therefore, they may not
577// be valid `C::Item`s. The soundness of the following `on_drop` implementation must
578// be reasoned in conjunction with the `page_table/cursor` implementation.
579unsafe impl<C: PageTableConfig> AnyFrameMeta for PageTablePageMeta<C> {
580    fn on_drop(&mut self, reader: &mut VmReader<Infallible>) {
581        let nr_children = self.nr_children.get_mut();
582        if *nr_children == 0 {
583            return;
584        }
585
586        let level = self.level;
587        let range = if level == C::NR_LEVELS {
588            C::TOP_LEVEL_INDEX_RANGE.clone()
589        } else {
590            0..nr_subpage_per_huge::<C>()
591        };
592
593        // Drop the children.
594        reader.skip(range.start * size_of::<C::E>());
595        for _ in range {
596            // Non-atomic read is OK because we have mutable access.
597            let pte = reader.read_once::<C::E>().unwrap();
598            if pte.is_present() {
599                let paddr = pte.paddr();
600                // As a fast path, we can ensure that the type of the child frame
601                // is `Self` if the PTE points to a child page table. Then we don't
602                // need to check the vtable for the drop method.
603                if !pte.is_last(level) {
604                    // SAFETY: The PTE points to a page table node. The ownership
605                    // of the child is transferred to the child then dropped.
606                    drop(unsafe { Frame::<Self>::from_raw(paddr) });
607                } else {
608                    // SAFETY: The PTE points to a mapped item. The ownership
609                    // of the item is transferred here then dropped.
610                    drop(unsafe { C::item_from_raw(paddr, level, pte.prop()) });
611                }
612            }
613        }
614    }
615}*/