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
34pub use crate::specs::mm::page_table::node::{entry_owners::*, owners::*};
35pub use child::*;
36pub use entry::*;
37
38use vstd::cell::pcell_maybe_uninit;
39use vstd::prelude::*;
40
41use vstd::atomic::PAtomicU8;
42use vstd_extra::array_ptr;
43use vstd_extra::cast_ptr::*;
44use vstd_extra::drop_tracking::{Drop as VerifiedDrop, TrackDrop};
45use vstd_extra::ghost_tree::*;
46use vstd_extra::ownership::*;
47
48use crate::mm::frame::{
49    allocator::FrameAllocOptions,
50    meta::{
51        META_SLOT_SIZE, MetaSlot, REF_COUNT_MAX, REF_COUNT_UNUSED,
52        mapping::{frame_to_meta, meta_to_frame},
53    },
54};
55
56use crate::mm::page_table::*;
57use crate::mm::{Paddr, Vaddr};
58use crate::specs::mm::{
59    frame::{
60        mapping::{frame_to_index, lemma_frame_to_index_injective, meta_to_index},
61        meta_owners::{
62            MetaSlotOwner, MetaSlotStorage, MetadataPerms, typed_meta_value, typed_meta_wf,
63        },
64        meta_region_owners::MetaRegionOwners,
65    },
66    page_table::node::owners::*,
67};
68
69use core::{marker::PhantomData, ops::Deref, sync::atomic::Ordering};
70
71use super::{PageTableConfig, PageTableEntryTrait, nr_subpage_per_huge};
72
73use crate::{
74    mm::{
75        PagingConstsTrait,
76        PagingLevel,
77        //        FrameAllocOptions, Infallible,
78        //        VmReader,
79        frame::{Frame, FrameRef, meta::AnyFrameMeta},
80        paddr_to_vaddr,
81        page_table::{load_pte, store_pte},
82    },
83    specs::task::InAtomicMode,
84};
85
86verus! {
87
88/// The metadata of any kinds of page table pages.
89/// Make sure the the generic parameters don't effect the memory layout.
90pub struct PageTablePageMeta<C: PageTableConfig> {
91    /// The number of valid PTEs. It is mutable if the lock is held.
92    pub nr_children: pcell_maybe_uninit::PCell<u16>,
93    /// If the page table is detached from its parent.
94    ///
95    /// A page table can be detached from its parent while still being accessed,
96    /// since we use a RCU scheme to recycle page tables. If this flag is set,
97    /// it means that the parent is recycling the page table.
98    pub stray: pcell_maybe_uninit::PCell<bool>,
99    /// The level of the page table page. A page table page cannot be
100    /// referenced by page tables of different levels.
101    pub level: PagingLevel,
102    /// The lock for the page table page.
103    pub lock: PAtomicU8,
104    pub _phantom: core::marker::PhantomData<C>,
105}
106
107/// A smart pointer to a page table node.
108///
109/// This smart pointer is an owner of a page table node. Thus creating and
110/// dropping it will affect the reference count of the page table node. If
111/// dropped it as the last reference, the page table node and subsequent
112/// children will be freed.
113///
114/// [`PageTableNode`] is read-only. To modify the page table node, lock and use
115/// [`PageTableGuard`].
116pub type PageTableNode<C> = Frame<PageTablePageMeta<C>>;
117
118unsafe impl<C: PageTableConfig> AnyFrameMeta for PageTablePageMeta<C> {
119    /// Caller invariants the PT-node `on_drop` body relies on:
120    /// - Reader well-formedness + `vm_io_owner` matching + read view
121    ///   initialized + at least `PAGE_SIZE` bytes remaining for the
122    ///   PT-node walk.
123    /// - Global region table invariant.
124    /// - Embedding ([`child_perms_embedding`]): for every paddr in
125    ///   `child_perms.dom()`, the slot and perm match `from_raw` /
126    ///   `VerifiedDrop::drop`'s expected shape.
127    /// - Walk coverage ([`walk_coverage_from_view`]): for every present
128    ///   non-last PTE in the page bytes, `frame_to_index(pte.paddr()) ∈
129    ///   child_perms.dom()`.
130    /// - Walk uniqueness ([`walk_uniqueness_from_view`]): distinct PTE
131    ///   positions with present non-last PTEs have distinct paddrs.
132    ///
133    /// The body now discharges the dom-membership obligation in full via
134    /// byte-level chaining (`decode_pod` + `read_once`'s strengthened
135    /// ensures + the byte-preservation loop invariant) plus the two
136    /// walk-* preconditions; see [`lemma_coverage_at`] and
137    /// [`lemma_uniqueness_at_pair`].
138    open spec fn on_drop_pre(
139        &self,
140        reader: crate::mm::VmReader<'_, crate::mm::Infallible>,
141        regions: crate::specs::mm::frame::meta_region_owners::MetaRegionOwners,
142        vm_io_owner: crate::specs::mm::io::VmIoOwner,
143    ) -> bool {
144        &&& reader.inv()
145        &&& reader.wf(vm_io_owner)
146        &&& reader.remain_spec() >= crate::specs::arch::PAGE_SIZE
147        &&& reader.cursor.vaddr % core::mem::align_of::<C::E>() == 0
148        &&& vm_io_owner.inv()
149        &&& vm_io_owner.read_view_initialized()
150        &&& regions.inv()
151        &&& Self::child_perms_embedding(regions, vstd::set::Set::empty())
152        &&& self.walk_coverage_from_view(reader, vm_io_owner.read_view_of(), regions.slots.dom())
153        &&& self.walk_items_well_formed_from_view(reader, vm_io_owner.read_view_of())
154        &&& self.walk_uniqueness_from_view(reader, vm_io_owner.read_view_of())
155    }
156
157    /// Drops the children of a page-table node: walks each present PTE and
158    /// drops the referenced child page-table-node frame or mapped item.
159    #[verifier::spinoff_prover]
160    fn on_drop(
161        &mut self,
162        reader: &mut crate::mm::VmReader<'_, crate::mm::Infallible>,
163        Tracked(regions): Tracked<
164            &mut crate::specs::mm::frame::meta_region_owners::MetaRegionOwners,
165        >,
166        Tracked(vm_io_owner): Tracked<&mut crate::specs::mm::io::VmIoOwner>,
167    ) {
168        let level = self.level;
169        let range = if level == C::NR_LEVELS() {
170            C::TOP_LEVEL_INDEX_RANGE()
171        } else {
172            0..nr_subpage_per_huge::<C>()
173        };
174
175        proof {
176            C::lemma_paging_consts_properties();
177            C::lemma_page_table_config_constant_properties();
178            vstd::arithmetic::mul::lemma_mul_inequality(
179                range.start as int,
180                NR_ENTRIES as int,
181                core::mem::size_of::<C::E>() as int,
182            );
183        }
184
185        let ghost size_of_e: int = core::mem::size_of::<C::E>() as int;
186        let ghost align_of_e: int = core::mem::align_of::<C::E>() as int;
187        let ghost pre_skip_cursor: int = reader.cursor.vaddr as int;
188
189        let ghost initial_view: crate::specs::mm::virt_mem::MemView = vm_io_owner.read_view_of();
190        let ghost initial_dom: vstd::set::Set<int> = regions.slots.dom();
191        let ghost initial_reader: crate::mm::VmReader<'_, crate::mm::Infallible> = *reader;
192
193        #[verus_spec(with Tracked(vm_io_owner))]
194        reader.skip_in_place(range.start * core::mem::size_of::<C::E>());
195
196        proof {
197            C::E::lemma_page_table_entry_properties();
198            let k = size_of_e / align_of_e;
199            vstd::arithmetic::div_mod::lemma_fundamental_div_mod(size_of_e, align_of_e);
200            vstd::arithmetic::mul::lemma_mul_is_commutative(align_of_e, k);
201            vstd::arithmetic::mul::lemma_mul_is_associative(range.start as int, k, align_of_e);
202            vstd::arithmetic::div_mod::lemma_mod_multiples_basic(range.start * k, align_of_e);
203            vstd::arithmetic::div_mod::lemma_mod_adds(
204                pre_skip_cursor,
205                range.start * size_of_e,
206                align_of_e,
207            );
208        }
209
210        let ghost post_skip_remain: int = reader.remain_spec() as int;
211        let ghost range_start: int = range.start as int;
212        let ghost range_end: int = range.end as int;
213        let n_iters: usize = range.end - range.start;
214        let mut iter_count: usize = 0;
215        let ghost mut removed_indices: vstd::set::Set<int> = vstd::set::Set::empty();
216
217        proof {
218            C::lemma_page_table_config_constant_properties();
219            C::lemma_paging_consts_properties();
220            vstd::arithmetic::mul::lemma_mul_is_distributive_sub_other_way(
221                size_of_e,
222                NR_ENTRIES as int,
223                range_start,
224            );
225            vstd::arithmetic::mul::lemma_mul_inequality(
226                range_end - range_start,
227                NR_ENTRIES - range_start,
228                size_of_e,
229            );
230        }
231
232        while iter_count < n_iters
233            invariant
234                reader.inv(),
235                reader.wf(*vm_io_owner),
236                vm_io_owner.inv(),
237                vm_io_owner.read_view_initialized(),
238                regions.inv(),
239                reader.cursor.vaddr as int % align_of_e == 0,
240                size_of_e == core::mem::size_of::<C::E>(),
241                align_of_e == core::mem::align_of::<C::E>(),
242                size_of_e % align_of_e == 0,
243                align_of_e > 0,
244                size_of_e > 0,
245                iter_count <= n_iters,
246                n_iters == range_end - range_start,
247                // Verus loses non-negativity of `range_start` / `range_end`
248                // across the loop boundary; pin it via these invariants so
249                // `lemma_mul_nonnegative` preconditions discharge in the body.
250                0 <= range_start,
251                range_start <= range_end,
252                range_end <= NR_ENTRIES,
253                reader.remain_spec() == post_skip_remain - iter_count * size_of_e,
254                post_skip_remain >= (range_end - range_start) * size_of_e,
255                regions.slots.dom() == initial_dom,
256                Self::child_perms_embedding(*regions, removed_indices),
257                self.walk_coverage_from_view(initial_reader, initial_view, initial_dom),
258                self.walk_items_well_formed_from_view(initial_reader, initial_view),
259                self.walk_uniqueness_from_view(initial_reader, initial_view),
260                // Without this, Verus treats `self.level` as potentially
261                // mutated by `&mut self` and the level-comparison facts go
262                // missing inside walk_coverage / walk_uniqueness instances.
263                self.level == level,
264                reader.end == initial_reader.end,
265                reader.cursor.vaddr == initial_reader.cursor.vaddr + range_start * size_of_e
266                    + iter_count * size_of_e,
267                forall|i: usize|
268                    #![trigger initial_view.addr_transl(i)]
269                    initial_reader.cursor.vaddr <= i < initial_reader.end.vaddr ==> {
270                        &&& initial_view.addr_transl(i) is Some
271                        &&& initial_view.memory.contains_key(initial_view.addr_transl(i).unwrap().0)
272                    },
273                forall|va: usize|
274                    #![trigger vm_io_owner.read_view_of().read(va)]
275                    reader.cursor.vaddr <= va < initial_reader.end.vaddr ==> {
276                        &&& initial_view.addr_transl(va) == vm_io_owner.read_view_of().addr_transl(
277                            va,
278                        )
279                        &&& initial_view.read(va) == vm_io_owner.read_view_of().read(va)
280                    },
281                removed_indices.subset_of(initial_dom),
282                // Witness past iter for each removed idx — the discharge
283                // proof picks it up via `choose|j|` and invokes
284                // `walk_uniqueness` at (current_cursor, witness_cursor).
285                forall|idx: int| #[trigger]
286                    removed_indices.contains(idx) ==> exists|j: int|
287                        #![trigger Self::walk_pte_at_view(
288                            initial_view,
289                            (initial_reader.cursor.vaddr
290                                + range_start * size_of_e
291                                + j * size_of_e) as usize,
292                        )]
293                        0 <= j < iter_count && {
294                            let cj = (initial_reader.cursor.vaddr + range_start * size_of_e + j
295                                * size_of_e) as usize;
296                            let pte_j = Self::walk_pte_at_view(initial_view, cj);
297                            &&& pte_j.is_present()
298                            &&& !pte_j.is_last(self.level)
299                            &&& idx == frame_to_index(pte_j.paddr())
300                        },
301            decreases n_iters - iter_count,
302        {
303            proof {
304                vstd::arithmetic::mul::lemma_mul_is_distributive_sub(
305                    size_of_e,
306                    range_end - range_start,
307                    iter_count as int,
308                );
309                vstd::arithmetic::mul::lemma_mul_inequality(
310                    1,
311                    range_end - range_start - iter_count,
312                    size_of_e,
313                );
314            }
315            let ghost cursor_pre_read: usize = reader.cursor.vaddr;
316            let ghost pre_view: crate::specs::mm::virt_mem::MemView = vm_io_owner.read_view_of();
317            proof {
318                crate::specs::mm::virt_mem::MemView::lemma_read_bytes_eq_pointwise(
319                    pre_view,
320                    initial_view,
321                    cursor_pre_read,
322                    core::mem::size_of::<C::E>(),
323                );
324            }
325            let pte = #[verus_spec(with Tracked(vm_io_owner))]
326            reader.read_once::<C::E>();
327            let pte = pte.unwrap();
328            proof {
329                ostd_pod::lemma_decode_pod_inverse::<C::E>(pte);
330                vstd::arithmetic::mul::lemma_mul_nonnegative(range_start, size_of_e);
331                vstd::arithmetic::mul::lemma_mul_nonnegative(iter_count as int, size_of_e);
332            }
333            if pte.is_present() {
334                let paddr = pte.paddr();
335                if !pte.is_last(level) {
336                    proof {
337                        vstd::arithmetic::mul::lemma_mul_is_distributive_add_other_way(
338                            size_of_e,
339                            range_start,
340                            iter_count as int,
341                        );
342                        vstd::arithmetic::div_mod::lemma_mod_multiples_basic(
343                            range_start + iter_count,
344                            size_of_e,
345                        );
346                        Self::lemma_coverage_at(
347                            *self,
348                            initial_reader,
349                            initial_view,
350                            initial_dom,
351                            cursor_pre_read,
352                        );
353                        broadcast use lemma_frame_to_index_injective;
354
355                        assert forall|idx: int| #[trigger] removed_indices.contains(idx) implies idx
356                            != frame_to_index(pte.paddr()) by {
357                            let j = choose|j: int|
358                                #![trigger Self::walk_pte_at_view(
359                                    initial_view,
360                                    (initial_reader.cursor.vaddr
361                                        + range_start * size_of_e
362                                        + j * size_of_e) as usize,
363                                )]
364                                0 <= j < iter_count && {
365                                    let cj = (initial_reader.cursor.vaddr + range_start * size_of_e
366                                        + j * size_of_e) as usize;
367                                    let pte_j = Self::walk_pte_at_view(initial_view, cj);
368                                    &&& pte_j.is_present()
369                                    &&& !pte_j.is_last(self.level)
370                                    &&& idx == frame_to_index(pte_j.paddr())
371                                };
372                            let cj: usize = (initial_reader.cursor.vaddr + range_start * size_of_e
373                                + j * size_of_e) as usize;
374                            let pte_j = Self::walk_pte_at_view(initial_view, cj);
375                            vstd::arithmetic::mul::lemma_mul_nonnegative(range_start, size_of_e);
376                            vstd::arithmetic::mul::lemma_mul_nonnegative(j, size_of_e);
377                            vstd::arithmetic::mul::lemma_mul_strict_inequality(
378                                j,
379                                iter_count as int,
380                                size_of_e,
381                            );
382                            vstd::arithmetic::mul::lemma_mul_is_distributive_add_other_way(
383                                size_of_e,
384                                range_start,
385                                j,
386                            );
387                            vstd::arithmetic::mul::lemma_mul_inequality(
388                                range_start + j + 1,
389                                range_end,
390                                size_of_e,
391                            );
392                            vstd::arithmetic::mul::lemma_mul_is_distributive_sub_other_way(
393                                size_of_e,
394                                range_end,
395                                range_start,
396                            );
397                            vstd::arithmetic::div_mod::lemma_mod_multiples_basic(
398                                range_start + j,
399                                size_of_e,
400                            );
401                            Self::lemma_uniqueness_at_pair(
402                                *self,
403                                initial_reader,
404                                initial_view,
405                                cursor_pre_read,
406                                cj,
407                            );
408                            pte.lemma_paddr_is_page_aligned();
409                            pte_j.lemma_paddr_is_page_aligned();
410                        };
411                    }
412                    proof {
413                        removed_indices = removed_indices.insert(frame_to_index(paddr));
414                        assert({
415                            let cj = (initial_reader.cursor.vaddr + range_start * size_of_e
416                                + iter_count * size_of_e) as usize;
417                            let pte_j = Self::walk_pte_at_view(initial_view, cj);
418                            &&& cj == cursor_pre_read
419                            &&& pte_j == pte
420                            &&& pte_j.is_present()
421                            &&& !pte_j.is_last(self.level)
422                            &&& frame_to_index(paddr) == frame_to_index(pte_j.paddr())
423                        });
424                    }
425                    proof_decl! {
426                        let tracked from_raw_obl: vstd_extra::drop_tracking::DropObligation<int>;
427                    }
428                    let frame = unsafe {
429                        #[verus_spec(with Tracked(regions) => Tracked(from_raw_obl))]
430                        Frame::<Self>::from_raw(paddr)
431                    };
432                    // `from_raw` minted the obligation; `frame.drop`
433                    // consumes it directly. No redeem dance needed.
434                    VerifiedDrop::drop(frame, Tracked(regions), Tracked(from_raw_obl));
435                } else {
436                    // SAFETY: The PTE points to a mapped item. The ownership
437                    // of the item is transferred here then dropped.
438                    proof {
439                        vstd::arithmetic::mul::lemma_mul_is_distributive_add_other_way(
440                            size_of_e,
441                            range_start,
442                            iter_count as int,
443                        );
444                        vstd::arithmetic::div_mod::lemma_mod_multiples_basic(
445                            range_start + iter_count,
446                            size_of_e,
447                        );
448                        Self::lemma_item_well_formed_at(
449                            *self,
450                            initial_reader,
451                            initial_view,
452                            cursor_pre_read,
453                        );
454                        assert(C::raw_item_well_formed(paddr, level, pte.prop()));
455                    }
456                    let _item = unsafe { C::item_from_raw(paddr, level, pte.prop()) };
457                }
458            }
459            proof {
460                vstd::arithmetic::div_mod::lemma_mod_adds(
461                    reader.cursor.vaddr - size_of_e,
462                    size_of_e,
463                    align_of_e,
464                );
465            }
466            let ghost iter_count_old: int = iter_count as int;
467            iter_count = iter_count + 1;
468            proof {
469                vstd::arithmetic::mul::lemma_mul_is_distributive_add_other_way(
470                    size_of_e,
471                    iter_count_old,
472                    1,
473                );
474            }
475        }
476    }
477
478    fn is_untyped(&self) -> bool {
479        false
480    }
481
482    uninterp spec fn vtable_ptr(&self) -> usize;
483}
484
485#[verus_verify]
486impl<C: PageTableConfig> PageTableNode<C> {
487    /// Gets the level of a page table node.
488    /// # Verified Properties
489    /// ## Preconditions
490    /// - The node must be well-formed, and the caller must provide a permission token for its metadata.
491    /// ## Postconditions
492    /// - Returns the level of the node.
493    /// ## Safety
494    /// - We require the caller to provide a permission token to ensure that this function is only called on a valid page table node.
495    #[verus_spec(
496        with
497            Tracked(owner): Tracked<&NodeOwner<C>>,
498            Tracked(regions): Tracked<&MetaRegionOwners>
499    )]
500    pub(super) fn level(&self) -> PagingLevel
501        requires
502            self.ptr.addr() == regions.slots[owner.slot_index].addr(),
503            owner.metaregion_sound_node(*regions),
504        returns
505            owner.level,
506    {
507        let tracked points_to = regions.slots.tracked_borrow(owner.slot_index);
508        let tracked slot_owner = regions.slot_owners.tracked_borrow(owner.slot_index);
509        #[verus_spec(with
510            Tracked(points_to),
511            Tracked(&slot_owner.metadata_perm),
512            Tracked(&())
513        )]
514        let meta = self.meta();
515        meta.level
516    }
517
518    /// Allocates a new empty page table node.
519    #[verus_spec(res =>
520        with Tracked(parent_owner): Tracked<&mut NodeOwner<C>>,
521             Tracked(regions): Tracked<&mut MetaRegionOwners>,
522             Tracked(guards): Tracked<&Guards<'rcu>>,
523             Ghost(idx): Ghost<usize>,
524                 -> owner: Tracked<OwnerSubtree<C>>,
525        requires
526            1 <= level < NR_LEVELS,
527            idx < NR_ENTRIES,
528            old(regions).inv(),
529            old(parent_owner).inv(),
530        ensures
531            final(regions).inv(),
532            final(parent_owner).inv(),
533            allocated_empty_node_owner(owner@, level),
534            allocated_empty_node_grandchildren_none(owner@),
535            res.ptr.addr() == owner@.value().node().meta_vaddr(),
536            guards.unlocked(owner@.value().node().meta_vaddr()),
537            MetaSlot::get_node_from_unused_spec(meta_to_frame(owner@.value().node().meta_vaddr()), *old(regions), *final(regions)),
538            MetaSlot::slot_perm_reparked_spec(meta_to_frame(owner@.value().node().meta_vaddr()), *old(regions), *final(regions)),
539
540            final(regions).frame_obligations == old(regions).frame_obligations.insert(
541                meta_to_index(owner@.value().node().meta_vaddr())),
542            old(regions).contains(meta_to_index(owner@.value().node().meta_vaddr())),
543
544            !crate::specs::mm::frame::meta_owners::is_mmio_paddr(
545                meta_to_frame(owner@.value().node().meta_vaddr())),
546            owner@.value().metaregion_sound(*final(regions)),
547            forall|i: int|
548                #[trigger] old(regions).slot_owners[i].ref_count() != REF_COUNT_UNUSED
549                ==> i != meta_to_index(owner@.value().node().meta_vaddr()),
550            owner@.value().match_pte(C::E::new_pt_spec(meta_to_frame(owner@.value().node().meta_vaddr())), level as PagingLevel),
551            final(parent_owner).meta_own == old(parent_owner).meta_own,
552            final(parent_owner).slot_index == old(parent_owner).slot_index,
553            final(parent_owner).level == old(parent_owner).level,
554            final(parent_owner).tree_level == old(parent_owner).tree_level,
555            final(parent_owner).children_perm.addr() == old(parent_owner).children_perm.addr(),
556            final(parent_owner).children_perm.value() == old(parent_owner).children_perm.value().update(
557                idx as int,
558                C::E::new_pt_spec(meta_to_frame(owner@.value().node().meta_vaddr())),
559            ),
560            final(regions).contains(owner@.value().node().slot_index),
561            owner@.value().node().metaregion_sound_node(*final(regions)),
562    )]
563    #[verifier::external_body]
564    pub fn alloc<'rcu>(level: PagingLevel) -> Self {
565        let tracked entry_owner = EntryOwner::tracked_new_absent(
566            TreePath::new(Seq::empty()),
567            level,
568        );
569
570        let tracked mut owner = OwnerSubtree::<C>::tracked_new_val(entry_owner, level as nat);
571        let meta = PageTablePageMeta::new(level);
572        let mut frame = FrameAllocOptions::new();
573        frame.zeroed(true);
574        let allocated_frame = frame.alloc_frame_with(meta).expect(
575            "Failed to allocate a page table node",
576        );
577        // The allocated frame is zeroed. Make sure zero is absent PTE.
578        //debug_assert_eq!(C::E::new_absent().as_usize(), 0);
579
580        proof_with!(|= Tracked(owner));
581
582        allocated_frame
583    }/*
584    /// Activates the page table assuming it is a root page table.
585    ///
586    /// Here we ensure not dropping an active page table by making a
587    /// processor a page table owner. When activating a page table, the
588    /// reference count of the last activated page table is decremented.
589    /// And that of the current page table is incremented.
590    ///
591    /// # Safety
592    ///
593    /// The caller must ensure that the page table to be activated has
594    /// proper mappings for the kernel and has the correct const parameters
595    /// matching the current CPU.
596    ///
597    /// # Panics
598    ///
599    /// Only top-level page tables can be activated using this function.
600    pub(crate) unsafe fn activate(&self) {
601        use crate::{
602            arch::mm::{activate_page_table, current_page_table_paddr},
603            mm::page_prop::CachePolicy,
604        };
605
606        #[cfg(feature = "allow_panic")]
607        assert_eq!(self.level(), C::NR_LEVELS());
608
609        let last_activated_paddr = current_page_table_paddr();
610        if last_activated_paddr == self.start_paddr() {
611            return;
612        }
613
614        // SAFETY: The safety is upheld by the caller.
615        unsafe { activate_page_table(self.clone().into_raw(), CachePolicy::Writeback) };
616
617        // Restore and drop the last activated page table.
618        // SAFETY: The physical address is valid and points to a forgotten page table node.
619        drop(unsafe { Self::from_raw(last_activated_paddr) });
620    }
621
622    /// Activates the (root) page table assuming it is the first activation.
623    ///
624    /// It will not try dropping the last activate page table. It is the same
625    /// with [`Self::activate()`] in other senses.
626    pub(super) unsafe fn first_activate(&self) {
627        use crate::{arch::mm::activate_page_table, mm::page_prop::CachePolicy};
628
629        // SAFETY: The safety is upheld by the caller.
630        unsafe { activate_page_table(self.clone().into_raw(), CachePolicy::Writeback) };
631    }*/
632
633}
634
635#[verus_verify]
636impl<'a, C: PageTableConfig> PageTableNodeRef<'a, C> {
637    pub open spec fn locks_preserved_except<'rcu>(
638        addr: usize,
639        guards0: Guards<'rcu>,
640        guards1: Guards<'rcu>,
641    ) -> bool {
642        &&& OwnerSubtree::implies(
643            CursorOwner::<'rcu, C>::node_unlocked(guards0),
644            CursorOwner::<'rcu, C>::node_unlocked_except(guards1, addr),
645        )
646        &&& forall|i: usize| guards0.lock_held(i) ==> guards1.lock_held(i)
647        &&& forall|i: usize| guards0.unlocked(i) && i != addr ==> guards1.unlocked(i)
648    }
649
650    /// Locks the page table node.
651    ///
652    /// An atomic mode guard is required to
653    ///  1. prevent deadlocks;
654    ///  2. provide a lifetime (`'rcu`) that the nodes are guaranteed to outlive.
655    /// # Verification Design
656    /// As of when we verified this library, we didn't have a spin lock implementation, so we axiomatize
657    /// what happens when it's successful.
658    #[verifier::external_body]
659    #[verus_spec(res =>
660        with Tracked(owner): Tracked<&NodeOwner<C>>,
661            Tracked(guards): Tracked<&mut Guards<'rcu>>
662        requires
663            self.inner@.invariants(*owner),
664            old(guards).unlocked(owner.meta_vaddr()),
665        ensures
666            final(guards).lock_held(owner.meta_vaddr()),
667            Self::locks_preserved_except(owner.meta_vaddr(), *old(guards), *final(guards)),
668            owner.relate_guard(res),
669    )]
670    pub fn lock<'rcu, A: InAtomicMode>(self, _guard: &'rcu A) -> PageTableGuard<'rcu, C> where
671        'a: 'rcu,
672     {
673        unimplemented!()
674    }
675
676    /// Creates a new [`PageTableGuard`] without checking if the page table lock is held.
677    ///
678    /// # Safety
679    ///
680    /// This function must be called if this task logically holds the lock.
681    ///
682    /// Calling this function when a guard is already created is undefined behavior
683    /// unless that guard was already forgotten.
684    #[verus_spec(res =>
685        with Tracked(owner): Tracked<&NodeOwner<C>>,
686             Tracked(guards): Tracked<&mut Guards<'rcu>>,
687        requires
688            self.inner@.invariants(*owner),
689            old(guards).unlocked(owner.meta_vaddr()),
690        ensures
691            final(guards).lock_held(owner.meta_vaddr()),
692            Self::locks_preserved_except(owner.meta_vaddr(), *old(guards), *final(guards)),
693            owner.relate_guard(res),
694    )]
695    pub unsafe fn make_guard_unchecked<'rcu, A: InAtomicMode>(
696        self,
697        _guard: &'rcu A,
698    ) -> PageTableGuard<'rcu, C> where 'a: 'rcu {
699        let guard = PageTableGuard { inner: self };
700
701        proof {
702            let ghost guards0 = *guards;
703            guards.guards = guards.guards.insert(owner.meta_vaddr());
704
705        }
706
707        guard
708    }
709}
710
711impl<'rcu, C: PageTableConfig> PageTableGuard<'rcu, C> {
712    /// Borrows an entry in the node at a given index.
713    ///
714    /// # Panics
715    ///
716    /// Panics if the index is not within the bound of
717    /// [`nr_subpage_per_huge<C>`].
718    #[verus_spec(res =>
719        with Tracked(owner): Tracked<&NodeOwner<C>>,
720             Tracked(child_owner): Tracked<&EntryOwner<C>>,
721             Tracked(regions): Tracked<&MetaRegionOwners>,
722        requires
723            owner.inv(),
724            child_owner.inv(),
725            owner.relate_guard(*old(self)),
726            child_owner.match_pte(
727                owner.children_perm.value()[idx as int],
728                child_owner.parent_level,
729            ),
730            regions.inv(),
731            regions.contains(owner.slot_index),
732            // Panic condition
733            idx < NR_ENTRIES,
734        ensures
735            res.wf(*child_owner),
736            res.idx == idx,
737            *res.node == *old(self),
738            *final(self) == *final(res.node),
739            owner.relate_guard(*res.node),
740    )]
741    pub fn entry<'a>(&'a mut self, idx: usize) -> Entry<'a, 'rcu, C> {
742        #[cfg(feature = "allow_panic")]
743        assert!(idx < nr_subpage_per_huge::<C>());
744        // SAFETY: The index is within the bound. `Entry::new_at` returns an
745        // entry whose node is the guard value we were handed.
746        unsafe {
747            #[verus_spec(with Tracked(child_owner), Tracked(owner), Tracked(regions))]
748            Entry::new_at(self, idx)
749        }
750    }
751
752    /// Gets the number of valid PTEs in a page table node.
753    /// # Verified Properties
754    /// ## Preconditions
755    /// - The node must be well-formed.
756    /// ## Postconditions
757    /// - Returns the number of valid PTEs in the node.
758    /// ## Safety
759    /// - We require the caller to provide a permission token to ensure that this function is only called on a valid page table node.
760    #[verus_spec(nr =>
761        with Tracked(owner) : Tracked<&NodeOwner<C>>,
762             Tracked(regions): Tracked<&MetaRegionOwners>,
763        requires
764            self.inner.inner@.invariants(*owner),
765            regions.inv(),
766            owner.metaregion_sound_node(*regions),
767        returns
768            owner.meta_own.nr_children.value(),
769    )]
770    pub fn nr_children(&self) -> u16 {
771        let tracked points_to = regions.slots.tracked_borrow(owner.slot_index);
772        let tracked slot_owner = regions.slot_owners.tracked_borrow(owner.slot_index);
773        #[verus_spec(with
774            Tracked(points_to),
775            Tracked(&slot_owner.metadata_perm),
776            Tracked(&())
777        )]
778        let meta = self.meta();
779
780        *meta.nr_children.borrow(Tracked(&owner.meta_own.nr_children))
781    }
782
783    /// Returns if the page table node is detached from its parent.
784    #[verus_spec(res =>
785        with
786            Tracked(points_to): Tracked<&'a vstd::simple_pptr::PointsTo<MetaSlot>>,
787            Tracked(metadata_perms): Tracked<&'a MetadataPerms>,
788            Tracked(repr_perm): Tracked<&'a ()>,
789            Ghost(stray_id): Ghost<vstd::cell::CellId>,
790        requires
791            old(self).inner.inner@.ptr.addr() == points_to.addr(),
792            typed_meta_wf::<PageTablePageMeta<C>>(*points_to, *metadata_perms, *repr_perm),
793            typed_meta_value::<PageTablePageMeta<C>>(*metadata_perms, *repr_perm).stray.id()
794                == stray_id,
795        ensures
796            res.id() == stray_id,
797            *final(self) == *old(self),
798    )]
799    pub(super) fn stray_mut<'a>(&'a mut self) -> &'a pcell_maybe_uninit::PCell<bool> {
800        // SAFETY: The lock is held so we have an exclusive access.
801        #[verus_spec(with
802            Tracked(points_to),
803            Tracked(metadata_perms),
804            Tracked(repr_perm)
805        )]
806        let meta = self.meta();
807        &meta.stray
808    }
809
810    /// Reads a non-owning PTE at the given index.
811    ///
812    /// A non-owning PTE means that it does not account for a reference count
813    /// of the a page if the PTE points to a page. The original PTE still owns
814    /// the child page.
815    ///
816    /// # Safety
817    ///
818    /// The caller must ensure that the index is within the bound.
819    #[verus_spec(pte =>
820        with Tracked(owner): Tracked<&NodeOwner<C>>,
821             Tracked(regions): Tracked<&MetaRegionOwners>,
822        requires
823            self.inner.inner@.invariants(*owner),
824            regions.inv(),
825            regions.contains(owner.slot_index),
826            idx < NR_ENTRIES,
827        ensures
828            pte == owner.children_perm.value()[idx as int],
829    )]
830    pub unsafe fn read_pte(&self, idx: usize) -> C::E {
831        // debug_assert!(idx < nr_subpage_per_huge::<C>());
832        let tracked owner_slot_perm = regions.slots.tracked_borrow(owner.slot_index);
833        let ptr = vstd_extra::array_ptr::ArrayPtr::<C::E, NR_ENTRIES>::from_addr(
834            paddr_to_vaddr(
835                #[verus_spec(with Tracked(owner_slot_perm))]
836                self.start_paddr(),
837            ),
838        );
839
840        // SAFETY:
841        // - The page table node is alive. The index is inside the bound, so the page table entry is valid.
842        // - All page table entries are aligned and accessed with atomic operations only.
843        unsafe {
844            #[verus_spec(with Tracked(&owner.children_perm))]
845            load_pte(ptr.add(idx), Ordering::Relaxed)
846        }
847    }
848
849    /// Writes a page table entry at a given index.
850    ///
851    /// This operation will leak the old child if the old PTE is present.
852    ///
853    /// # Safety
854    ///
855    /// The caller must ensure that:
856    ///  1. The index must be within the bound;
857    ///  2. The PTE must represent a valid [`Child`] whose level is compatible
858    ///     with the page table node.
859    ///  3. The page table node will have the ownership of the [`Child`]
860    ///     after this method.
861    #[verus_spec(
862        with Tracked(owner): Tracked<&mut NodeOwner<C>>,
863             Tracked(regions): Tracked<&MetaRegionOwners>,
864        requires
865            old(self).inner.inner@.invariants(*old(owner)),
866            regions.inv(),
867            regions.contains(old(owner).slot_index),
868            idx < NR_ENTRIES,
869        ensures
870            final(owner).inv(),
871            final(owner).level == old(owner).level,
872            final(owner).meta_own == old(owner).meta_own,
873            final(owner).slot_index == old(owner).slot_index,
874            final(owner).children_perm.value() == old(owner).children_perm.value().update(
875                idx as int,
876                pte,
877            ),
878            *final(self) == *old(self),
879    )]
880    pub unsafe fn write_pte(&mut self, idx: usize, pte: C::E) {
881        // debug_assert!(idx < nr_subpage_per_huge::<C>());
882        let tracked owner_slot_perm = regions.slots.tracked_borrow(owner.slot_index);
883        #[verusfmt::skip]
884        let ptr = vstd_extra::array_ptr::ArrayPtr::<C::E, NR_ENTRIES>::from_addr(
885            paddr_to_vaddr(
886                #[verus_spec(with Tracked(owner_slot_perm))]
887                self.start_paddr()
888            ),
889        );
890
891        // SAFETY:
892        // - The page table node is alive. The index is inside the bound, so the page table entry is valid.
893        // - All page table entries are aligned and accessed with atomic operations only.
894        unsafe {
895            #[verus_spec(with Tracked(&mut owner.children_perm))]
896            store_pte(ptr.add(idx), pte, Ordering::Release)
897        }
898        proof {
899            assert(owner.children_perm.wf());
900        }
901    }
902
903    /// Gets the mutable reference to the number of valid PTEs in the node.
904    #[verus_spec(res =>
905        with
906            Tracked(points_to): Tracked<&'a vstd::simple_pptr::PointsTo<MetaSlot>>,
907            Tracked(metadata_perms): Tracked<&'a MetadataPerms>,
908            Ghost(nr_children_id): Ghost<vstd::cell::CellId>,
909        requires
910            old(self).inner.inner@.ptr.addr() == points_to.addr(),
911            typed_meta_wf::<PageTablePageMeta<C>>(*points_to, *metadata_perms, ()),
912            typed_meta_value::<PageTablePageMeta<C>>(*metadata_perms, ()).nr_children.id()
913                == nr_children_id,
914        ensures
915            res.id() == nr_children_id,
916            *final(self) == *old(self),
917    )]
918    fn nr_children_mut<'a>(&'a mut self) -> &'a pcell_maybe_uninit::PCell<u16> {
919        // SAFETY: The lock is held so we have an exclusive access.
920        #[verus_spec(with
921            Tracked(points_to),
922            Tracked(metadata_perms),
923            Tracked(&())
924        )]
925        let meta = self.meta();
926        &meta.nr_children
927    }
928}
929
930/*impl<C: PageTableConfig> Drop for PageTableGuard<'_, C> {
931    fn drop(&mut self) {
932        self.inner.meta().lock.store(0, Ordering::Release);
933    }
934}*/
935
936impl<C: PageTableConfig> PageTablePageMeta<C> {
937    pub fn new(level: PagingLevel) -> Self {
938        Self {
939            nr_children: pcell_maybe_uninit::PCell::new(0).0,
940            stray: pcell_maybe_uninit::PCell::new(false).0,
941            level,
942            lock: PAtomicU8::new(0).0,
943            _phantom: PhantomData,
944        }
945    }
946
947    /// The PTE value that `read_once::<C::E>` would produce at cursor `c`
948    /// against the given memory view. Linked to `read_once` via
949    /// `pod_bytes(v) == read_view.read_bytes(...)` (strengthened ensures)
950    /// + [`lemma_decode_pod_inverse`].
951    pub open spec fn walk_pte_at_view(view: crate::specs::mm::virt_mem::MemView, c: usize) -> C::E {
952        ostd_pod::decode_pod::<C::E>(view.read_bytes(c, core::mem::size_of::<C::E>()))
953    }
954
955    /// Single-cursor projection of [`walk_coverage_from_view`]. Extracting
956    /// the forall body to a named predicate lets the body invoke
957    /// [`lemma_coverage_at`] for one specific `c` instead of relying on
958    /// auto-trigger matching across the loop invariant's `forall|c|`.
959    pub open spec fn walk_coverage_at(
960        self,
961        view: crate::specs::mm::virt_mem::MemView,
962        dom: vstd::set::Set<int>,
963        c: usize,
964    ) -> bool {
965        let pte = Self::walk_pte_at_view(view, c);
966        pte.is_present() && !pte.is_last(self.level) ==> dom.contains(frame_to_index(pte.paddr()))
967    }
968
969    /// Instantiate [`walk_coverage_from_view`]'s forall at one cursor.
970    pub proof fn lemma_coverage_at(
971        self,
972        reader: crate::mm::VmReader<'_, crate::mm::Infallible>,
973        view: crate::specs::mm::virt_mem::MemView,
974        dom: vstd::set::Set<int>,
975        c: usize,
976    )
977        requires
978            self.walk_coverage_from_view(reader, view, dom),
979            reader.cursor.vaddr <= c,
980            c + core::mem::size_of::<C::E>() <= reader.cursor.vaddr + reader.remain_spec(),
981            (c - reader.cursor.vaddr) % core::mem::size_of::<C::E>() as int == 0,
982        ensures
983            self.walk_coverage_at(view, dom, c),
984    {
985    }
986
987    /// Instantiate [`walk_items_well_formed_from_view`]'s forall at one cursor.
988    pub proof fn lemma_item_well_formed_at(
989        self,
990        reader: crate::mm::VmReader<'_, crate::mm::Infallible>,
991        view: crate::specs::mm::virt_mem::MemView,
992        c: usize,
993    )
994        requires
995            self.walk_items_well_formed_from_view(reader, view),
996            reader.cursor.vaddr <= c,
997            c + core::mem::size_of::<C::E>() <= reader.cursor.vaddr + reader.remain_spec(),
998            (c - reader.cursor.vaddr) % core::mem::size_of::<C::E>() as int == 0,
999        ensures
1000            ({
1001                let pte = Self::walk_pte_at_view(view, c);
1002                pte.is_present() && pte.is_last(self.level) ==> C::raw_item_well_formed(
1003                    pte.paddr(),
1004                    self.level,
1005                    pte.prop(),
1006                )
1007            }),
1008    {
1009    }
1010
1011    /// Instantiate [`walk_uniqueness_from_view`]'s forall at one cursor pair.
1012    pub proof fn lemma_uniqueness_at_pair(
1013        self,
1014        reader: crate::mm::VmReader<'_, crate::mm::Infallible>,
1015        view: crate::specs::mm::virt_mem::MemView,
1016        c1: usize,
1017        c2: usize,
1018    )
1019        requires
1020            self.walk_uniqueness_from_view(reader, view),
1021            reader.cursor.vaddr <= c1,
1022            c1 + core::mem::size_of::<C::E>() <= reader.cursor.vaddr + reader.remain_spec(),
1023            (c1 - reader.cursor.vaddr) % core::mem::size_of::<C::E>() as int == 0,
1024            reader.cursor.vaddr <= c2,
1025            c2 + core::mem::size_of::<C::E>() <= reader.cursor.vaddr + reader.remain_spec(),
1026            (c2 - reader.cursor.vaddr) % core::mem::size_of::<C::E>() as int == 0,
1027            c1 != c2,
1028            Self::walk_pte_at_view(view, c1).is_present(),
1029            !Self::walk_pte_at_view(view, c1).is_last(self.level),
1030            Self::walk_pte_at_view(view, c2).is_present(),
1031            !Self::walk_pte_at_view(view, c2).is_last(self.level),
1032        ensures
1033            Self::walk_pte_at_view(view, c1).paddr() != Self::walk_pte_at_view(view, c2).paddr(),
1034    {
1035    }
1036
1037    /// Caller-side dom-membership obligation: every present non-last PTE
1038    /// position in the walk (over `view`) has its child-frame index in
1039    /// `dom`. Phrased over a frozen `(view, dom)` pair so the body can
1040    /// carry it as a loop invariant against an entry-state snapshot
1041    /// while `vm_io_owner` advances per iteration.
1042    pub open spec fn walk_coverage_from_view(
1043        self,
1044        reader: crate::mm::VmReader<'_, crate::mm::Infallible>,
1045        view: crate::specs::mm::virt_mem::MemView,
1046        dom: vstd::set::Set<int>,
1047    ) -> bool {
1048        forall|c: usize|
1049            #![trigger Self::walk_pte_at_view(view, c)]
1050            reader.cursor.vaddr <= c && c + core::mem::size_of::<C::E>() <= reader.cursor.vaddr
1051                + reader.remain_spec() && (c - reader.cursor.vaddr) % core::mem::size_of::<
1052                C::E,
1053            >() as int == 0 ==> {
1054                let pte = Self::walk_pte_at_view(view, c);
1055                pte.is_present() && !pte.is_last(self.level) ==> dom.contains(
1056                    frame_to_index(pte.paddr()),
1057                )
1058            }
1059    }
1060
1061    /// Every present leaf PTE encountered by the drop walk contains a canonical
1062    /// raw item for the node's paging level.
1063    pub open spec fn walk_items_well_formed_from_view(
1064        self,
1065        reader: crate::mm::VmReader<'_, crate::mm::Infallible>,
1066        view: crate::specs::mm::virt_mem::MemView,
1067    ) -> bool {
1068        forall|c: usize|
1069            #![trigger Self::walk_pte_at_view(view, c)]
1070            reader.cursor.vaddr <= c && c + core::mem::size_of::<C::E>() <= reader.cursor.vaddr
1071                + reader.remain_spec() && (c - reader.cursor.vaddr) % core::mem::size_of::<
1072                C::E,
1073            >() as int == 0 ==> {
1074                let pte = Self::walk_pte_at_view(view, c);
1075                pte.is_present() && pte.is_last(self.level) ==> C::raw_item_well_formed(
1076                    pte.paddr(),
1077                    self.level,
1078                    pte.prop(),
1079                )
1080            }
1081    }
1082
1083    /// Caller-side uniqueness obligation: distinct cursor positions with
1084    /// present non-last PTEs (in `view`) map to distinct paddrs.
1085    pub open spec fn walk_uniqueness_from_view(
1086        self,
1087        reader: crate::mm::VmReader<'_, crate::mm::Infallible>,
1088        view: crate::specs::mm::virt_mem::MemView,
1089    ) -> bool {
1090        forall|c1: usize, c2: usize|
1091            #![trigger Self::walk_pte_at_view(view, c1), Self::walk_pte_at_view(view, c2)]
1092            reader.cursor.vaddr <= c1 && c1 + core::mem::size_of::<C::E>() <= reader.cursor.vaddr
1093                + reader.remain_spec() && (c1 - reader.cursor.vaddr) % core::mem::size_of::<
1094                C::E,
1095            >() as int == 0 && reader.cursor.vaddr <= c2 && c2 + core::mem::size_of::<C::E>()
1096                <= reader.cursor.vaddr + reader.remain_spec() && (c2 - reader.cursor.vaddr)
1097                % core::mem::size_of::<C::E>() as int == 0 && c1 != c2 ==> {
1098                let pte1 = Self::walk_pte_at_view(view, c1);
1099                let pte2 = Self::walk_pte_at_view(view, c2);
1100                pte1.is_present() && !pte1.is_last(self.level) && pte2.is_present()
1101                    && !pte2.is_last(self.level) ==> pte1.paddr() != pte2.paddr()
1102            }
1103    }
1104
1105    /// Caller-side shape obligation: every paddr in `child_perms.dom()`
1106    /// has a slot perm matching the shape `from_raw` + `VerifiedDrop::drop`
1107    /// expect (init, alignment, refcount within bounds, last-reference
1108    /// shape when refcount == 1).
1109    pub open spec fn child_perms_embedding(
1110        regions: crate::specs::mm::frame::meta_region_owners::MetaRegionOwners,
1111        excluded: vstd::set::Set<int>,
1112    ) -> bool {
1113        forall|paddr: crate::mm::Paddr|
1114            #![trigger regions.slot_owners[frame_to_index(paddr)]]
1115            regions.slots.dom().contains(frame_to_index(paddr)) && !excluded.contains(
1116                frame_to_index(paddr),
1117            ) ==> {
1118                let idx = frame_to_index(paddr);
1119                let so = regions.slot_owners[idx];
1120                &&& <Frame<Self>>::from_raw_requires_safety(
1121                    regions,
1122                    paddr,
1123                )
1124                // Borrow-protocol transition: `raw_count` is dormant.
1125                &&& 0 < so.ref_count() <= REF_COUNT_MAX
1126                &&& so.ref_count() == 1 ==> {
1127                    &&& so.storage_perm().is_init()
1128                    &&& so.in_list_perm.value() == 0
1129                    &&& so.paths_in_pt.is_empty()
1130                }
1131                // Borrow-protocol redesign: in steady state between
1132                // `into_pte`'s consume and `on_drop`'s `from_raw`-mint,
1133                // the per-child `frame_obligations` count is 0.
1134
1135            }
1136    }
1137}
1138
1139} // verus!