Skip to main content

ostd/mm/page_table/
mod.rs

1// SPDX-License-Identifier: MPL-2.0
2use vstd::arithmetic::power2::*;
3use vstd::prelude::*;
4use vstd::simple_pptr;
5use vstd::std_specs::clone::*;
6use vstd_extra::assert;
7use vstd_extra::panic::may_panic;
8use vstd_extra::prelude::*;
9
10use crate::specs::arch::*;
11use crate::specs::mm::page_table::{cursor::*, *};
12use crate::specs::task::InAtomicMode;
13
14use crate::mm::frame::meta::{REF_COUNT_MAX, REF_COUNT_UNIQUE, REF_COUNT_UNUSED};
15use crate::mm::kspace::kvirt_area::disable_preempt;
16use crate::specs::mm::{
17    frame::{mapping::frame_to_index, meta_region_owners::MetaRegionOwners},
18    page_table::{
19        is_valid_range_spec, nr_pte_index_bits_spec, pte_index_bit_offset_spec,
20        top_level_index_width_spec, vaddr_range_spec,
21    },
22};
23
24use core::{
25    fmt::Debug,
26    intrinsics::transmute_unchecked,
27    ops::{Range, RangeInclusive},
28    sync::atomic::Ordering,
29};
30
31use super::{
32    Paddr, PagingConstsTrait, PagingLevel, PodOnce, Vaddr,
33    kspace::KernelPtConfig,
34    nr_subpage_per_huge,
35    page_prop::{CachePolicy, PageProperty},
36    page_size,
37    vm_space::UserPtConfig,
38};
39
40use crate::{
41    //task::{atomic_mode::AsAtomicModeGuard, disable_preempt},
42    Pod,
43    arch::mm::{PageTableEntry, PagingConsts},
44};
45
46mod node;
47pub use node::*;
48mod cursor;
49
50pub(crate) use cursor::*;
51
52#[cfg(ktest)]
53mod test;
54
55//pub(crate) mod boot_pt;
56
57verus! {
58
59#[derive(Clone, Copy, PartialEq, Eq, Debug)]
60pub enum PageTableError {
61    /// The provided virtual address range is invalid.
62    InvalidVaddrRange(Vaddr, Vaddr),
63    /// The provided virtual address is invalid.
64    InvalidVaddr(Vaddr),
65    /// Using virtual address not aligned.
66    UnalignedVaddr,
67}
68
69pub trait RCClone: Sized {
70    spec fn clone_requires(self, perm: MetaRegionOwners) -> bool;
71
72    spec fn clone_ensures(
73        self,
74        old_perm: MetaRegionOwners,
75        new_perm: MetaRegionOwners,
76        res: Self,
77    ) -> bool;
78
79    fn clone(&self, Tracked(perm): Tracked<&mut MetaRegionOwners>) -> (res: Self)
80        requires
81            self.clone_requires(*old(perm)),
82        ensures
83    // RCClone::clone` doesn't mint/redeem segment obligations.
84    // The per-frame `frame_obligations` effect is left to each impl's `clone_ensures`
85
86            res == *self,
87            self.clone_ensures(*old(perm), *final(perm), res),
88            final(perm).inv(),
89            final(perm).slots == old(perm).slots,
90            final(perm).slot_owners.dom() == old(perm).slot_owners.dom(),
91    ;
92}
93
94/// The configurations of a page table.
95///
96/// It abstracts away both the usage and the architecture specifics from the
97/// general page table implementation. For examples:
98///  - the managed virtual address range;
99///  - the trackedness of physical mappings;
100///  - the PTE layout;
101///  - the number of page table levels, etc.
102///
103/// # Safety
104///
105/// The implementor must ensure that the `item_into_raw` and `item_from_raw`
106/// are implemented correctly so that:
107///  - `item_into_raw` consumes the ownership of the item;
108///  - if the provided raw form matches the item that was consumed by
109///    `item_into_raw`, `item_from_raw` restores the exact item that was
110///    consumed by `item_into_raw`.
111pub unsafe trait PageTableConfig: Clone + Debug + Send + Sync + 'static {
112    spec fn TOP_LEVEL_INDEX_RANGE_spec() -> Range<usize>;
113
114    /// The index range at the top level (`C::NR_LEVELS()`) page table.
115    ///
116    /// When configured with this value, the [`PageTable`] instance will only
117    /// be allowed to manage the virtual address range that is covered by
118    /// this range. The range can be smaller than the actual allowed range
119    /// specified by the hardware MMU (limited by `C::ADDRESS_WIDTH`).
120    #[verifier::when_used_as_spec(TOP_LEVEL_INDEX_RANGE_spec)]
121    fn TOP_LEVEL_INDEX_RANGE() -> Range<usize>
122        returns
123            Self::TOP_LEVEL_INDEX_RANGE(),
124    ;
125
126    /// VERIFICATION only: The leading bits `[48, 64)` of every virtual address managed by this
127    /// config.
128    ///
129    /// Concretely, a mapping `m` in this page table has
130    /// `m.va_range.start / 2^48 == LEADING_BITS_spec()`. For non-sign-extended
131    /// configurations (e.g. `UserPtConfig`) this is `0`. For x86-64 kernel
132    /// PT it is `0xffff` (sign-extended high half). The type is wide enough
133    /// to carry arbitrary bit patterns, so the model can accommodate future
134    /// configurations that place their managed range at a non-canonical
135    /// fixed offset.
136    ///
137    /// Combined with `TOP_LEVEL_INDEX_RANGE`, this fully determines
138    /// the managed VA range, computed as
139    /// [`vaddr_range_spec::<Self>`]. Callers that previously used
140    /// `VADDR_RANGE_spec()` should use `vaddr_range_spec::<C>()`
141    /// directly — the inclusive `(start, end_inclusive)` form avoids the
142    /// `end == usize::MAX + 1` overflow that plagues `Range<Vaddr>` for
143    /// sign-extended kernel configurations.
144    open spec fn LEADING_BITS_spec() -> usize {
145        0
146    }
147
148    open spec fn TOP_LEVEL_CAN_UNMAP_spec() -> bool {
149        true
150    }
151
152    /// If we can remove the top-level page table entries.
153    ///
154    /// This is for the kernel page table, whose second-top-level page
155    /// tables need `'static` lifetime to be shared with user page tables.
156    /// Other page tables do not need to set this to `false`.
157    #[verifier::when_used_as_spec(TOP_LEVEL_CAN_UNMAP_spec)]
158    fn TOP_LEVEL_CAN_UNMAP() -> bool
159        returns
160            Self::TOP_LEVEL_CAN_UNMAP(),
161    ;
162
163    /// VERIFICATION only: Upper bound on `locked_range().end` for cursors of this config.
164    ///
165    /// May be tighter than the structural `vaddr_range_spec().1 + 1`
166    /// when the actual sources of cursor ranges (e.g. the kvirt allocator
167    /// for `KernelPtConfig`) draw from a sub-window of the configured VA
168    /// range. `KernelPtConfig` overrides this to `FRAME_METADATA_BASE_VADDR`,
169    /// which the `kvirt_alloc_range_bounds` axiom enforces. This bound is
170    /// what allows the cursor's `move_forward` proof to discharge
171    /// `prefix.idx[NR_LEVELS - 1] + 1 < NR_ENTRIES` at the top-level
172    /// boundary — the structural bound only gives `<= NR_ENTRIES` for
173    /// configurations whose `TOP_LEVEL_INDEX_RANGE.end == NR_ENTRIES`.
174    ///
175    /// Default: `usize::MAX + 1` (no tightening over the structural bound).
176    open spec fn LOCKED_END_BOUND_spec() -> int {
177        0x1_0000_0000_0000_0000int
178    }
179
180    /// The type of the page table entry.
181    type E: PageTableEntryTrait;
182
183    /// The paging constants.
184    type C: PagingConstsTrait;
185
186    /// The item that can be mapped into the virtual memory space using the
187    /// page table.
188    ///
189    /// Usually, this item is a [`crate::mm::Frame`], which we call a "tracked"
190    /// frame. The page table can also do "untracked" mappings that only maps
191    /// to certain physical addresses without tracking the ownership of the
192    /// mapped physical frame. The user of the page table APIs can choose by
193    /// defining this type and the corresponding methods [`item_into_raw`] and
194    /// [`item_from_raw`].
195    ///
196    /// [`item_from_raw`]: PageTableConfig::item_from_raw
197    /// [`item_into_raw`]: PageTableConfig::item_into_raw
198    type Item: RCClone;
199
200    spec fn item_into_raw_spec(item: Self::Item) -> (Paddr, PagingLevel, PageProperty);
201
202    /// Consumes the item and returns the physical address, the paging level,
203    /// and the page property.
204    ///
205    /// The ownership of the item will be consumed, i.e., the item will be
206    /// forgotten after this function is called.
207    #[verifier::when_used_as_spec(item_into_raw_spec)]
208    fn item_into_raw(item: Self::Item) -> ((paddr, level, prop): (Paddr, PagingLevel, PageProperty))
209        requires
210            Self::item_well_formed(item),
211        ensures
212            1 <= level <= NR_LEVELS,
213            valid_frame_paddr(paddr),
214            paddr % page_size(level) == 0,
215            paddr + page_size(level) <= MAX_PADDR,
216            Self::raw_item_well_formed(paddr, level, prop),
217            Self::E::new_page_req(paddr, level, prop),
218        returns
219            Self::item_into_raw_spec(item),
220    ;
221
222    spec fn item_from_raw_spec(paddr: Paddr, level: PagingLevel, prop: PageProperty) -> Self::Item;
223
224    /// Restores the item from the physical address and the paging level.
225    ///
226    /// There could be transformations after [`PageTableConfig::item_into_raw`]
227    /// and before [`PageTableConfig::item_from_raw`], which include:
228    ///  - splitting and coalescing the items, for example, splitting one item
229    ///    into 512 `level - 1` items with and contiguous physical addresses;
230    ///  - protecting the items, for example, changing the page property.
231    ///
232    /// Splitting and coalescing maintains ownership rules, i.e., if one
233    /// physical address is within the range of one item, after splitting/
234    /// coalescing, there should be exactly one item that contains the address.
235    ///
236    /// # Safety
237    ///
238    /// The caller must ensure that:
239    ///  - the physical address and the paging level represent a page table
240    ///    item or part of it (as described above);
241    ///  - either the ownership of the item is properly transferred to the
242    ///    return value, or the return value is wrapped in a
243    ///    [`core::mem::ManuallyDrop`] that won't outlive the original item.
244    ///
245    /// A concrete trait implementation may require the caller to ensure that
246    ///  - the [`super::PageFlags::AVAIL1`] flag is the same as that returned
247    ///    from [`PageTableConfig::item_into_raw`].
248    #[verifier::when_used_as_spec(item_from_raw_spec)]
249    unsafe fn item_from_raw(paddr: Paddr, level: PagingLevel, prop: PageProperty) -> (res:
250        Self::Item)
251        requires
252            valid_frame_paddr(paddr),
253            Self::raw_item_well_formed(paddr, level, prop),
254        ensures
255            Self::item_well_formed(res),
256        returns
257            Self::item_from_raw_spec(paddr, level, prop),
258    ;
259
260    /// Whether cloning this item bumps a slot's refcount. For ref-counted items
261    /// (e.g. `MappedItem::Tracked`), `true`; for items where clone is a no-op
262    /// (e.g. `MappedItem::Untracked` for kernel MMIO frames), `false`.
263    spec fn tracked(item: Self::Item) -> bool;
264
265    /// Per-config predicate that captures the structural well-formedness an item
266    /// reconstructed via [`PageTableConfig::item_from_raw`] must satisfy. This may include both
267    /// ownership invariants and restrictions on raw-only property bits.
268    spec fn item_well_formed(item: Self::Item) -> bool;
269
270    /// Per-config predicate that captures the well-formedness of raw properties
271    /// produced via [`PageTableConfig::item_into_raw`] must satisfy.
272    spec fn raw_item_well_formed(pa: Paddr, level: PagingLevel, prop: PageProperty) -> bool;
273
274    /// Changing properties without changing trackedness preserves a canonical raw item.
275    proof fn lemma_raw_item_well_formed_preserved(
276        pa: Paddr,
277        level: PagingLevel,
278        old_prop: PageProperty,
279        new_prop: PageProperty,
280    )
281        requires
282            valid_frame_paddr(pa),
283            Self::raw_item_well_formed(pa, level, old_prop),
284            Self::tracked(Self::item_from_raw(pa, level, new_prop)) == Self::tracked(
285                Self::item_from_raw(pa, level, old_prop),
286            ),
287        ensures
288            Self::raw_item_well_formed(pa, level, new_prop),
289    ;
290
291    /// Splitting a canonical huge-page raw item yields canonical child raw items.
292    proof fn lemma_raw_item_well_formed_split(
293        pa: Paddr,
294        level: PagingLevel,
295        prop: PageProperty,
296        child_pa: Paddr,
297        child_idx: usize,
298    )
299        requires
300            valid_frame_paddr(pa),
301            Self::raw_item_well_formed(pa, level, prop),
302            Self::E::new_page_req(pa, level, prop),
303            level > 1,
304            child_idx < NR_ENTRIES,
305            child_pa == pa + child_idx * page_size((level - 1) as PagingLevel),
306        ensures
307            Self::raw_item_well_formed(child_pa, (level - 1) as PagingLevel, prop),
308            Self::E::new_page_req(child_pa, (level - 1) as PagingLevel, prop),
309    ;
310
311    /// The item produced by [`PageTableConfig::item_from_raw`] is well-formed.
312    proof fn lemma_item_from_raw_well_formed(pa: Paddr, level: PagingLevel, prop: PageProperty)
313        requires
314            valid_frame_paddr(pa),
315            Self::raw_item_well_formed(pa, level, prop),
316        ensures
317            Self::item_well_formed(Self::item_from_raw(pa, level, prop)),
318    ;
319
320    /// Re-encoding a canonical raw item preserves the complete raw representation.
321    proof fn lemma_item_into_raw_roundtrip(pa: Paddr, level: PagingLevel, prop: PageProperty)
322        requires
323            valid_frame_paddr(pa),
324            Self::raw_item_well_formed(pa, level, prop),
325        ensures
326            Self::item_into_raw(Self::item_from_raw(pa, level, prop)) == (pa, level, prop),
327    ;
328
329    /// Decoding the raw representation produced from a well-formed item restores that item.
330    proof fn lemma_item_from_raw_roundtrip(
331        item: Self::Item,
332        pa: Paddr,
333        level: PagingLevel,
334        prop: PageProperty,
335    )
336        requires
337            valid_frame_paddr(pa),
338            Self::item_well_formed(item),
339            Self::item_into_raw(item) == (pa, level, prop),
340        ensures
341            Self::item_from_raw(pa, level, prop) == item,
342    ;
343
344    /// Proves that `clone_ensures` for `Self::Item` implies concrete per-field
345    /// properties on `MetaRegionOwners`. Each `PageTableConfig` implementor proves
346    /// this by unfolding its `MappedItem::clone_ensures` → `Frame::clone_ensures`.
347    /// Proves that after `clone`, the slot at `frame_to_index(pa)` has the expected
348    /// per-field properties. Implementors unfold their `MappedItem::clone_ensures` to
349    /// `Frame::clone_ensures` and connect `pa` to the frame's internal pointer address.
350    proof fn lemma_clone_ensures_concrete(
351        item: Self::Item,
352        pa: Paddr,
353        old_regions: MetaRegionOwners,
354        new_regions: MetaRegionOwners,
355        res: Self::Item,
356    )
357        requires
358            item.clone_ensures(old_regions, new_regions, res),
359            Self::item_into_raw_spec(item).0 == pa,
360            res == item,
361            new_regions.inv(),
362            new_regions.slots =~= old_regions.slots,
363            new_regions.slot_owners.dom() =~= old_regions.slot_owners.dom(),
364        ensures
365    // Other slots always unchanged.
366
367            forall|i: int|
368                i != frame_to_index(pa) ==> (#[trigger] new_regions.slot_owners[i]
369                    == old_regions.slot_owners[i]),
370            // The frame's slot: bumped if the item is ref-counted, otherwise unchanged.
371            Self::tracked(item) ==> {
372                &&& new_regions.slot_owner(pa).ref_count() == old_regions.slot_owner(pa).ref_count()
373                    + 1
374                &&& new_regions.slot_owner(pa).ref_count_perm.id() == old_regions.slot_owner(
375                    pa,
376                ).ref_count_perm.id()
377                &&& new_regions.slot_owner(pa).storage_perm() == old_regions.slot_owner(
378                    pa,
379                ).storage_perm()
380                &&& new_regions.slot_owner(pa).vtable_ptr_perm() == old_regions.slot_owner(
381                    pa,
382                ).vtable_ptr_perm()
383                &&& new_regions.slot_owner(pa).in_list_perm == old_regions.slot_owner(
384                    pa,
385                ).in_list_perm
386                &&& new_regions.slot_owner(pa).paths_in_pt == old_regions.slot_owner(pa).paths_in_pt
387                &&& new_regions.slot_owner(pa).slot_vaddr == old_regions.slot_owner(pa).slot_vaddr
388                &&& new_regions.slot_owner(pa).usage == old_regions.slot_owner(pa).usage
389            },
390            !Self::tracked(item) ==> new_regions.slot_owner(pa) == old_regions.slot_owner(pa),
391            // Canonical model: a tracked clone MINTS one per-frame obligation
392            // at the slot (`Frame::clone`); an untracked clone is net-zero.
393            Self::tracked(item) ==> new_regions.frame_obligations
394                == old_regions.frame_obligations.insert(frame_to_index(pa)),
395            !Self::tracked(item) ==> new_regions.frame_obligations == old_regions.frame_obligations,
396    ;
397
398    /// Proves `item.clone_requires(regions)` from the concrete frame-slot facts
399    /// delivered by `metaregion_sound` plus the non-saturation bound propagated
400    /// from `Cursor::query`. Implementors unfold their `MappedItem::clone_requires`
401    /// to `Frame::clone_requires` and connect `pa` to the frame's internal pointer
402    /// address.
403    proof fn lemma_clone_requires_concrete(
404        item: Self::Item,
405        pa: Paddr,
406        level: PagingLevel,
407        prop: PageProperty,
408        regions: MetaRegionOwners,
409    )
410        requires
411            regions.inv(),
412            Self::item_from_raw_spec(pa, level, prop) == item,
413            Self::raw_item_well_formed(pa, level, prop),
414            valid_frame_paddr(pa),
415            regions.contains(frame_to_index(pa)),
416            Self::tracked(item) ==> regions.slot_owner(pa).ref_count() > 0,
417            // `rc != UNUSED` is needed only for tracked frames (untracked clone is a no-op).
418            Self::tracked(item) ==> regions.slot_owner(pa).ref_count() != REF_COUNT_UNUSED,
419            // Saturation aborts (Arc-style) via `inc_ref_count`'s diverging panic.
420            Self::tracked(item) ==> (regions.slot_owner(pa).ref_count() < REF_COUNT_MAX
421                || may_panic()),
422        ensures
423            item.clone_requires(regions),
424    ;
425
426    /// The requirements of the page table configuration constants so that the memory management system can work correctly.
427    ///
428    /// NOTE: The postcondition is designed to be minimal, to actually be used in proofs, call `lemma_page_table_config_constant_properties`
429    /// instead to get all the properties that are derived from the requirements.
430    ///
431    /// FIXME: General architecture support. Move properties only relevant to paging constants to `PagingConstsTrait`.
432    proof fn lemma_page_table_config_constant_requirements()
433        ensures
434            core::mem::size_of::<Self::E>() == Self::C::PTE_SIZE(),
435            Self::TOP_LEVEL_INDEX_RANGE().start < Self::TOP_LEVEL_INDEX_RANGE().end,
436            Self::TOP_LEVEL_INDEX_RANGE().end <= pow2(
437                (Self::C::ADDRESS_WIDTH() - pte_index_bit_offset_spec::<Self::C>(
438                    Self::C::NR_LEVELS(),
439                )) as nat,
440            ),
441            Self::TOP_LEVEL_INDEX_RANGE().end * pow2(
442                pte_index_bit_offset_spec::<Self::C>(Self::C::NR_LEVELS()) as nat,
443            ) <= usize::MAX,
444            Self::LEADING_BITS_spec() != 0usize ==> (Self::C::VA_SIGN_EXT() && ((
445            Self::TOP_LEVEL_INDEX_RANGE().start * pow2(
446                pte_index_bit_offset_spec::<Self::C>(Self::C::NR_LEVELS()) as nat,
447            )) / (pow2((Self::C::ADDRESS_WIDTH() - 1) as nat) as int)) % 2 == 1),
448            (Self::C::VA_SIGN_EXT() && (((Self::TOP_LEVEL_INDEX_RANGE().start * pow2(
449                pte_index_bit_offset_spec::<Self::C>(Self::C::NR_LEVELS()) as nat,
450            )) / (pow2((Self::C::ADDRESS_WIDTH() - 1) as nat) as int)) % 2 == 1)) ==> {
451                &&& Self::LEADING_BITS_spec() * 0x1_0000_0000_0000int == 0x1_0000_0000_0000_0000int
452                    - pow2(Self::C::ADDRESS_WIDTH() as nat)
453            },
454            Self::LEADING_BITS_spec() < 0x1_0000_usize,
455            // FIXME: This property does not hold in general, `ADDRESS_WIDTH` can be wider.
456            pow2(
457                (Self::C::ADDRESS_WIDTH() - pte_index_bit_offset_spec::<Self::C>(
458                    Self::C::NR_LEVELS(),
459                )) as nat,
460            ) == NR_ENTRIES,
461    ;
462
463    // The derived properties of the page table config constants.
464    ///
465    /// NOTE: Implementations of `PageTableConfig` do not need to implement this lemma, the proof is automatically inherited from the default implementation.
466    proof fn lemma_page_table_config_constant_properties()
467        ensures
468    // Derived properties.
469
470            Self::TOP_LEVEL_INDEX_RANGE().end <= NR_ENTRIES,
471            // Copied from the postcondition of `lemma_page_table_config_constant_requirements`
472            // so that we only need to call this lemma in proofs.
473            core::mem::size_of::<Self::E>() == Self::C::PTE_SIZE(),
474            Self::TOP_LEVEL_INDEX_RANGE().start < Self::TOP_LEVEL_INDEX_RANGE().end,
475            Self::TOP_LEVEL_INDEX_RANGE().end <= pow2(
476                (Self::C::ADDRESS_WIDTH() - pte_index_bit_offset_spec::<Self::C>(
477                    Self::C::NR_LEVELS(),
478                )) as nat,
479            ),
480            Self::TOP_LEVEL_INDEX_RANGE().end * pow2(
481                pte_index_bit_offset_spec::<Self::C>(Self::C::NR_LEVELS()) as nat,
482            ) <= usize::MAX,
483            Self::LEADING_BITS_spec() != 0usize ==> (Self::C::VA_SIGN_EXT() && ((
484            Self::TOP_LEVEL_INDEX_RANGE().start * pow2(
485                pte_index_bit_offset_spec::<Self::C>(Self::C::NR_LEVELS()) as nat,
486            )) / (pow2((Self::C::ADDRESS_WIDTH() - 1) as nat) as int)) % 2 == 1),
487            (Self::C::VA_SIGN_EXT() && (((Self::TOP_LEVEL_INDEX_RANGE().start * pow2(
488                pte_index_bit_offset_spec::<Self::C>(Self::C::NR_LEVELS()) as nat,
489            )) / (pow2((Self::C::ADDRESS_WIDTH() - 1) as nat) as int)) % 2 == 1)) ==> {
490                &&& Self::LEADING_BITS_spec() * 0x1_0000_0000_0000int == 0x1_0000_0000_0000_0000int
491                    - pow2(Self::C::ADDRESS_WIDTH() as nat)
492            },
493            Self::LEADING_BITS_spec() < 0x1_0000_usize,
494            // FIXME: This property does not hold in general, `ADDRESS_WIDTH` can be wider.
495            pow2(
496                (Self::C::ADDRESS_WIDTH() - pte_index_bit_offset_spec::<Self::C>(
497                    Self::C::NR_LEVELS(),
498                )) as nat,
499            ) == NR_ENTRIES,
500    {
501        Self::C::lemma_paging_consts_properties();
502        Self::lemma_page_table_config_constant_requirements();
503    }
504}
505
506// Implement it so that we can comfortably use low level functions
507// like `page_size::<C>` without typing `C::C` everywhere.
508impl<C: PageTableConfig> PagingConstsTrait for C {
509    open spec fn BASE_PAGE_SIZE_spec() -> usize {
510        C::C::BASE_PAGE_SIZE_spec()
511    }
512
513    fn BASE_PAGE_SIZE() -> usize {
514        C::C::BASE_PAGE_SIZE()
515    }
516
517    open spec fn NR_LEVELS_spec() -> PagingLevel {
518        C::C::NR_LEVELS_spec()
519    }
520
521    fn NR_LEVELS() -> PagingLevel {
522        proof {
523            assert(Self::NR_LEVELS() == C::C::NR_LEVELS());
524        }
525        C::C::NR_LEVELS()
526    }
527
528    open spec fn HIGHEST_TRANSLATION_LEVEL_spec() -> PagingLevel {
529        C::C::HIGHEST_TRANSLATION_LEVEL_spec()
530    }
531
532    fn HIGHEST_TRANSLATION_LEVEL() -> PagingLevel {
533        C::C::HIGHEST_TRANSLATION_LEVEL()
534    }
535
536    open spec fn PTE_SIZE_spec() -> usize {
537        C::C::PTE_SIZE_spec()
538    }
539
540    fn PTE_SIZE() -> usize {
541        C::C::PTE_SIZE()
542    }
543
544    open spec fn ADDRESS_WIDTH_spec() -> usize {
545        C::C::ADDRESS_WIDTH_spec()
546    }
547
548    fn ADDRESS_WIDTH() -> usize {
549        C::C::ADDRESS_WIDTH()
550    }
551
552    open spec fn VA_SIGN_EXT_spec() -> bool {
553        C::C::VA_SIGN_EXT_spec()
554    }
555
556    fn VA_SIGN_EXT() -> bool {
557        C::C::VA_SIGN_EXT()
558    }
559
560    proof fn lemma_paging_consts_requirements() {
561        C::C::lemma_paging_consts_requirements();
562    }
563}
564
565/// Splits the address range into largest page table items.
566///
567/// Each of the returned items is a tuple of the physical address and the
568/// paging level. It is helpful when you want to map a physical address range
569/// into the provided virtual address.
570///
571/// For example, on x86-64, `C: PageTableConfig` may specify level 1 page as
572/// 4KiB, level 2 page as 2MiB, and level 3 page as 1GiB. Suppose that the
573/// supplied physical address range is from `0x3fdff000` to `0x80002000`,
574/// and the virtual address is also `0x3fdff000`, the following 5 items will
575/// be returned:
576///
577/// ```text
578/// 0x3fdff000                                                 0x80002000
579/// start                                                             end
580///   |----|----------------|--------------------------------|----|----|
581///    4KiB      2MiB                       1GiB              4KiB 4KiB
582/// ```
583///
584/// # Panics
585///
586/// Panics if:
587///  - any of `va`, `pa`, or `len` is not aligned to the base page size;
588///  - the range `va..(va + len)` is not valid for the page table.
589#[verifier::external_body]
590pub fn largest_pages<C: PageTableConfig>(
591    mut va: Vaddr,
592    mut pa: Paddr,
593    mut len: usize,
594) -> impl Iterator<Item = (Paddr, PagingLevel)> {
595    assert_eq!(va % C::BASE_PAGE_SIZE(), 0);
596    assert_eq!(pa % C::BASE_PAGE_SIZE(), 0);
597    assert_eq!(len % C::BASE_PAGE_SIZE(), 0);
598    assert!(is_valid_range::<C>(&(va..(va + len))));
599
600    core::iter::from_fn(
601        move ||
602            {
603                if len == 0 {
604                    return None;
605                }
606                let mut level = C::HIGHEST_TRANSLATION_LEVEL();
607                while page_size(level) > len || va % page_size(level) != 0 || pa % page_size(level)
608                    != 0 {
609                    level -= 1;
610                }
611
612                let item_start = pa;
613                va += page_size(level);
614                pa += page_size(level);
615                len -= page_size(level);
616
617                Some((item_start, level))
618            },
619    )
620}
621
622/// Gets the top-level index width, in bits, for the page table.
623fn top_level_index_width<C: PageTableConfig>() -> (ret: usize)
624    returns
625        top_level_index_width_spec::<C>(),
626{
627    proof {
628        C::lemma_paging_consts_properties();
629        C::lemma_page_table_config_constant_properties();
630    }
631
632    C::ADDRESS_WIDTH() - pte_index_bit_offset::<C>(C::NR_LEVELS())
633}
634
635fn pt_va_range_start<C: PageTableConfig>() -> (ret: Vaddr)
636    ensures
637        ret == C::TOP_LEVEL_INDEX_RANGE().start * pow2(
638            pte_index_bit_offset_spec::<C>(C::NR_LEVELS()) as nat,
639        ),
640{
641    proof {
642        C::lemma_paging_consts_properties();
643        let ghost idx_start = C::TOP_LEVEL_INDEX_RANGE().start;
644        let ghost offset = pte_index_bit_offset_spec::<C>(C::NR_LEVELS());
645        crate::specs::mm::page_table::vaddr_range_proofs::lemma_pt_va_range_start_shift_facts::<C>(
646            idx_start,
647            offset,
648        );
649        vstd::bits::lemma_usize_shl_is_mul(idx_start, offset);
650    }
651
652    C::TOP_LEVEL_INDEX_RANGE().start << pte_index_bit_offset::<C>(C::NR_LEVELS())
653}
654
655/// Concrete positional end of the VA range (inclusive):
656/// `(idx_range.end * 2^offset) - 1`, stated modulo `2^64` to match
657/// the inclusive-end spec. The verified configs prove the pre-subtraction
658/// product fits in `usize`, so the executable path can use an ordinary
659/// left shift followed by `wrapping_sub(1)`.
660fn pt_va_range_end<C: PageTableConfig>() -> (ret: Vaddr)
661    ensures
662        ret == (C::TOP_LEVEL_INDEX_RANGE().end * pow2(
663            pte_index_bit_offset_spec::<C>(C::NR_LEVELS()) as nat,
664        ) - 1) % 0x1_0000_0000_0000_0000int,
665{
666    let idx_end = C::TOP_LEVEL_INDEX_RANGE().end;
667    proof {
668        C::lemma_paging_consts_properties();
669    }
670    let offset = pte_index_bit_offset::<C>(C::NR_LEVELS());
671
672    proof {
673        crate::specs::mm::page_table::vaddr_range_proofs::lemma_pt_va_range_end_shift_facts::<C>(
674            idx_end,
675            offset,
676        );
677        vstd::bits::lemma_usize_shl_is_mul(idx_end, offset);
678    }
679
680    let shifted = idx_end << offset;
681    let ret = shifted.wrapping_sub(1);
682
683    proof {
684        assert(shifted == idx_end * pow2(offset as nat));
685        crate::specs::mm::page_table::vaddr_range_proofs::lemma_pt_va_range_end_wrapping_sub::<C>(
686            idx_end,
687            offset,
688            shifted,
689            ret,
690        );
691    }
692    ret
693}
694
695fn sign_bit_of_va<C: PageTableConfig>(va: Vaddr) -> (ret: bool)
696    ensures
697        ret == ((va as int / pow2((C::ADDRESS_WIDTH() - 1) as nat) as int) % 2 == 1),
698{
699    proof {
700        C::lemma_paging_consts_properties();
701        C::lemma_page_table_config_constant_properties();
702        vstd::bits::lemma_usize_shr_is_div(va, (C::ADDRESS_WIDTH() - 1) as usize);
703        vstd::bits::lemma_usize_low_bits_mask_is_mod(va >> (C::ADDRESS_WIDTH() - 1), 1);
704        vstd::bits::lemma_low_bits_mask_values();
705        vstd::arithmetic::power2::lemma2_to64();
706    }
707    (va >> (C::ADDRESS_WIDTH() - 1)) & 1 != 0
708}
709
710/// Apply the sign-extension OR to a positional value.
711///
712/// For any value `va` in `[0, 2^ADDRESS_WIDTH)`, the OR with
713/// `!0 ^ ((1 << ADDRESS_WIDTH) - 1)` is equivalent to adding
714/// `LEADING_BITS_spec() * 2^48`, because the mask's bits and `va`'s bits are
715/// disjoint.
716fn apply_sign_ext<C: PageTableConfig>(va: Vaddr) -> (ret: Vaddr)
717    requires
718        va < pow2(C::ADDRESS_WIDTH() as nat),
719        C::ADDRESS_WIDTH() < usize::BITS,
720        C::LEADING_BITS_spec() * 0x1_0000_0000_0000int == 0x1_0000_0000_0000_0000int - pow2(
721            C::ADDRESS_WIDTH() as nat,
722        ),
723    ensures
724        ret == va + C::LEADING_BITS_spec() * 0x1_0000_0000_0000int,
725{
726    let address_width = C::ADDRESS_WIDTH();
727    let low_bit = 1usize << address_width;
728    proof {
729        vstd::layout::unsigned_int_max_values();
730        vstd::bits::lemma_usize_pow2_no_overflow(address_width as nat);
731        vstd::bits::lemma_usize_shl_is_mul(1usize, address_width);
732    }
733    let low_mask = low_bit - 1;
734    let sign_ext_mask = !0 ^ low_mask;
735    let ret = va | sign_ext_mask;
736    proof {
737        assert(!0usize == 0xffff_ffff_ffff_ffffusize) by (compute_only);
738        assert(sign_ext_mask == usize::MAX - low_mask) by (bit_vector)
739            requires
740                sign_ext_mask == (!0usize ^ low_mask),
741                !0usize == 0xffff_ffff_ffff_ffffusize,
742                usize::MAX == 0xffff_ffff_ffff_ffffusize,
743        ;
744        assert(pow2(64) == 0x1_0000_0000_0000_0000nat) by {
745            vstd::arithmetic::power2::lemma2_to64();
746        };
747        assert(sign_ext_mask == 0x1_0000_0000_0000_0000int - pow2(address_width as nat));
748        assert(sign_ext_mask == C::LEADING_BITS_spec() * 0x1_0000_0000_0000int);
749
750        assert((va & sign_ext_mask) == 0usize) by (bit_vector)
751            requires
752                address_width < usize::BITS,
753                low_bit == 1usize << address_width,
754                low_mask == low_bit - 1,
755                sign_ext_mask == !0usize ^ low_mask,
756                va < low_bit,
757        ;
758        assert(ret == va + sign_ext_mask) by (bit_vector)
759            requires
760                ret == va | sign_ext_mask,
761                (va & sign_ext_mask) == 0usize,
762        ;
763    }
764    ret
765}
766
767/// Gets the managed virtual addresses range for the page table.
768///
769/// Returns a [`RangeInclusive`] because the end address, when the range
770/// reaches the top of the 64-bit address space (e.g. the canonical
771/// high-half kernel range ending at `usize::MAX`), would overflow the
772/// exclusive end of a [`Range<Vaddr>`].
773#[verusfmt::skip]
774fn vaddr_range<C: PageTableConfig>() -> (ret: RangeInclusive<Vaddr>)
775    ensures
776        ret@ == vaddr_range_spec::<C>(),
777{
778    let mut start = pt_va_range_start::<C>();
779    let mut end = pt_va_range_end::<C>();
780
781    proof {
782        C::lemma_paging_consts_properties();
783        C::lemma_page_table_config_constant_properties();
784        crate::specs::mm::page_table::vaddr_range_proofs::lemma_idx_times_pow2_bound::<C>(
785            start,
786            end,
787        );
788    }
789
790    if C::VA_SIGN_EXT() && sign_bit_of_va::<C>(pt_va_range_start::<C>()) {
791        start = apply_sign_ext::<C>(start);
792        end = apply_sign_ext::<C>(end);
793    }
794    start..=end
795}
796
797/// Checks if the given range is covered by the valid range of the page table.
798fn is_valid_range<C: PageTableConfig>(r: &Range<Vaddr>) -> bool
799    requires
800        r.end > 0,
801    returns
802        is_valid_range_spec::<C>(*r),
803{
804    let va_range = vaddr_range::<C>();
805    (r.start == 0 && r.end == 0) || (*va_range.start() <= r.start && r.end - 1 <= *va_range.end())
806}
807
808// Here are some const values that are determined by the paging constants.
809/// The number of virtual address bits used to index a PTE in a page.
810fn nr_pte_index_bits<C: PagingConstsTrait>() -> usize
811    returns
812        nr_pte_index_bits_spec::<C>(),
813{
814    proof {
815        C::lemma_paging_consts_properties();
816    }
817    nr_subpage_per_huge::<C>().ilog2() as usize
818}
819
820/// The index of a VA's PTE in a page table node at the given level.
821fn pte_index<C: PagingConstsTrait>(va: Vaddr, level: PagingLevel) -> (res: usize)
822    requires
823        1 <= level <= NR_LEVELS,
824    ensures
825        res == AbstractVaddr::from_vaddr(va).index[level - 1],
826{
827    proof {
828        let offset = pte_index_bit_offset_spec::<C>(level);
829        C::lemma_paging_consts_properties();
830        lemma_arch_specific_consts_properties::<C>();
831        assert(0 <= offset < usize::BITS) by (nonlinear_arith)
832            requires
833                1 <= level <= 4,
834                offset == 12 + 9 * (level - 1),
835        ;
836        lemma2_to64();
837        lemma2_to64_rest();
838        vstd::bits::lemma_usize_shr_is_div(va, pte_index_bit_offset_spec::<C>(level));
839        vstd::bits::lemma_low_bits_mask_values();
840        vstd::bits::lemma_usize_low_bits_mask_is_mod(
841            va >> pte_index_bit_offset_spec::<C>(level),
842            9,
843        );
844    }
845    (va >> pte_index_bit_offset::<C>(level)) & (nr_subpage_per_huge::<C>() - 1)
846}
847
848/// The bit offset of the entry offset part in a virtual address.
849///
850/// This function returns the bit offset of the least significant bit. Take
851/// x86-64 as an example, the `pte_index_bit_offset(2)` should return 21, which
852/// is 12 (the 4KiB in-page offset) plus 9 (index width in the level-1 table).
853fn pte_index_bit_offset<C: PagingConstsTrait>(level: PagingLevel) -> usize
854    requires
855        1 <= level <= NR_LEVELS,
856    returns
857        pte_index_bit_offset_spec::<C>(level),
858{
859    proof {
860        C::lemma_paging_consts_properties();
861        lemma_arch_specific_consts_properties::<C>();
862        assert(12 + 9 * (level - 1) <= 39) by (nonlinear_arith)
863            requires
864                1 <= level <= NR_LEVELS,
865                NR_LEVELS == 4,
866        ;
867    }
868    C::BASE_PAGE_SIZE().ilog2() as usize + nr_pte_index_bits::<C>() * (level as usize - 1)
869}
870
871/// A handle to a page table.
872/// A page table can track the lifetime of the mapped physical pages.
873pub struct PageTable<C: PageTableConfig> {
874    pub root: PageTableNode<C>,
875}
876
877/*
878impl PageTable<UserPtConfig> {
879    pub fn activate(&self) {
880        // SAFETY: The user mode page table is safe to activate since the kernel
881        // mappings are shared.
882        unsafe {
883            self.root.activate();
884        }
885    }
886}*/
887
888impl PageTable<KernelPtConfig> {
889    /// Create a new kernel page table.
890    #[verifier::external_body]
891    pub(crate) fn new_kernel_page_table() -> Self {
892        unimplemented!()/*        let kpt = Self::empty();
893
894        // Make shared the page tables mapped by the root table in the kernel space.
895        {
896        let preempt_guard = disable_preempt();
897        let mut root_node = kpt.root.borrow().lock(&preempt_guard);
898
899        for i in KernelPtConfig::TOP_LEVEL_INDEX_RANGE {
900            let mut root_entry = root_node.entry(i);
901            let _ = root_entry.alloc_if_none(&preempt_guard).unwrap();
902            }
903        }
904
905        kpt*/
906
907    }
908
909    /// Panic condition for [`Self::create_user_page_table`]:
910    /// Some kernel root entry at index `i` in `TOP_LEVEL_INDEX_RANGE` is
911    /// not a page table node (i.e., is absent or maps a huge frame).
912    pub open spec fn create_user_pt_panic_condition(root_owner: NodeOwner<KernelPtConfig>) -> bool {
913        exists|i: usize|
914            #![trigger root_owner.children_perm.value()[i as int]]
915            KernelPtConfig::TOP_LEVEL_INDEX_RANGE().start <= i
916                < KernelPtConfig::TOP_LEVEL_INDEX_RANGE().end && {
917                let pte = root_owner.children_perm.value()[i as int];
918                ||| !pte.is_present()
919                ||| pte.is_last(root_owner.level)
920            }
921    }
922
923    /// Create a new user page table.
924    ///
925    /// This should be the only way to create the user page table, that is to
926    /// duplicate the kernel page table with all the kernel mappings shared.
927    #[verus_spec(r =>
928        with Tracked(kernel_owner): Tracked<&PageTableOwner<KernelPtConfig>>,
929            Tracked(regions): Tracked<&mut MetaRegionOwners>,
930            Tracked(guards): Tracked<&mut Guards<'rcu>>,
931        requires
932            kernel_owner.inv(),
933            old(regions).inv(),
934            kernel_owner.0.value().is_node(),
935            !Self::create_user_pt_panic_condition(kernel_owner.0.value().node()),
936            // The kernel page table's root frame matches the tracked owner.
937            self.root.ptr.addr() == kernel_owner.0.value().node().meta_vaddr(),
938            // The kernel root entry is sound with respect to the meta regions.
939            kernel_owner.0.value().metaregion_sound(*old(regions)),
940            // The whole kernel page-table tree is sound: every entry's metaregion
941            // bookkeeping matches `old(regions)`. Needed to derive each child's
942            // soundness inside the loop body.
943            kernel_owner.metaregion_sound(*old(regions)),
944            // The kernel root is not currently locked.
945            old(guards).unlocked(kernel_owner.0.value().node().meta_vaddr()),
946        ensures
947            final(regions).inv(),
948    )]
949    pub(in crate::mm) fn create_user_page_table<'rcu, G: InAtomicMode + 'static>(
950        &'static self,
951    ) -> PageTable<UserPtConfig> {
952        let preempt_guard: &'rcu G = disable_preempt::<G>();
953
954        proof_decl! {
955            let tracked mut new_pt_owner: Option<PageTableOwner<UserPtConfig>> = None;
956        }
957        let ghost regions_before_alloc = *regions;
958        let new_pt: PageTable<UserPtConfig> = (
959        #[verus_spec(with Tracked(&mut new_pt_owner), Tracked(regions), Tracked(guards))]
960        PageTable::empty_with_owner());
961        let new_root = new_pt.root;
962        // Capture new_idx as a ghost BEFORE the tracked_take below empties new_pt_owner.
963        let ghost new_idx_g: int = crate::specs::mm::frame::mapping::frame_to_index(
964            new_pt_owner@.unwrap().0.value().meta_slot_paddr().unwrap(),
965        );
966        let ghost new_pt_owner_snap = new_pt_owner@.unwrap();
967        proof {
968            let kern_idx = crate::specs::mm::frame::mapping::frame_to_index(
969                kernel_owner.0.value().meta_slot_paddr().unwrap(),
970            );
971            let new_idx = new_idx_g;
972            crate::specs::mm::page_table::node::entry_owners::EntryOwner::<
973                KernelPtConfig,
974            >::lemma_active_entry_not_in_free_pool(
975                kernel_owner.0.value(),
976                regions_before_alloc,
977                new_idx,
978            );
979            assert(kern_idx != new_idx);
980            assert(regions.slot_owners[kern_idx] == regions_before_alloc.slot_owners[kern_idx]);
981            assert(kernel_owner.metaregion_sound(*regions));
982            assert(!regions.contains(new_idx));
983        }
984
985        proof_decl! {
986            let tracked root_owner: &NodeOwner<KernelPtConfig>
987                = kernel_owner.0.tracked_borrow_value().tracked_borrow_node();
988            let tracked mut new_pt_owner_val: PageTableOwner<UserPtConfig>
989                = new_pt_owner.tracked_take();
990            let tracked mut new_node_owner: NodeOwner<UserPtConfig> = {
991                let tracked new_pt_value = new_pt_owner_val.0.tracked_borrow_mut_value();
992                new_pt_value.tracked_take_node()
993            };
994            let tracked mut entry_owner: &EntryOwner<KernelPtConfig>;
995        }
996
997        // Discharge borrow/lock preconditions for the kernel root from
998        // kernel_owner.inv() + metaregion_sound + guards unlocked.
999        proof {
1000            assert(kernel_owner.0.value().is_node());
1001            assert(kernel_owner.0.value().metaregion_sound(*regions));
1002        }
1003        let ghost regions_before_self_borrow: MetaRegionOwners = *regions;
1004        let mut root_node = {
1005            #[verus_spec(with Tracked(regions))]
1006            let root_ref = self.root.borrow();
1007            #[verus_spec(with Tracked(root_owner), Tracked(guards))]
1008            root_ref.lock(preempt_guard)
1009        };
1010        let ghost regions_after_kroot_borrow: MetaRegionOwners = *regions;
1011        let mut new_node: PageTableGuard<'rcu, UserPtConfig> = {
1012            #[verus_spec(with Tracked(regions))]
1013            let new_ref = new_root.borrow();
1014            #[verus_spec(with Tracked(&new_node_owner), Tracked(guards))]
1015            new_ref.lock(preempt_guard)
1016        };
1017        proof {
1018            let kern_idx = crate::specs::mm::frame::mapping::frame_to_index(
1019                kernel_owner.0.value().meta_slot_paddr().unwrap(),
1020            );
1021            assert(regions_before_self_borrow.slot_owners
1022                == regions_after_kroot_borrow.slot_owners);
1023            assert forall|k: int|
1024                regions_before_self_borrow.contains(k) implies regions_before_self_borrow.slots[k]
1025                == #[trigger] regions_after_kroot_borrow.slots[k] by {
1026                if k == kern_idx {
1027                    crate::specs::mm::page_table::node::entry_owners::EntryOwner::<
1028                        KernelPtConfig,
1029                    >::lemma_active_entry_not_in_free_pool(
1030                        kernel_owner.0.value(),
1031                        regions_before_self_borrow,
1032                        k,
1033                    );
1034                }
1035            };
1036            kernel_owner.metaregion_sound_preserved_slot_owners_eq(
1037                regions_before_self_borrow,
1038                regions_after_kroot_borrow,
1039            );
1040
1041            let new_idx = new_idx_g;
1042            assert(regions_before_alloc.contains(new_idx));
1043            assert(kern_idx != new_idx) by {
1044                crate::specs::mm::page_table::node::entry_owners::EntryOwner::<
1045                    KernelPtConfig,
1046                >::lemma_active_entry_not_in_free_pool(
1047                    kernel_owner.0.value(),
1048                    regions_before_alloc,
1049                    new_idx,
1050                );
1051            };
1052
1053            assert(!regions_before_self_borrow.contains(new_idx));
1054            assert(!regions_after_kroot_borrow.contains(new_idx));
1055            assert forall|k: int|
1056                regions_after_kroot_borrow.contains(k) implies regions_after_kroot_borrow.slots[k]
1057                == #[trigger] regions.slots[k] by {};
1058            assert(kernel_owner.metaregion_sound(regions_before_alloc));
1059
1060            kernel_owner.0.lemma_subtree_satisfies_implies(
1061                kernel_owner.0.value().path,
1062                |
1063                    e: crate::specs::mm::page_table::node::entry_owners::EntryOwner<KernelPtConfig>,
1064                    p: vstd_extra::ghost_tree::TreePath<NR_ENTRIES>,
1065                |
1066                    e.is_frame() && e.parent_level > 1 ==> {
1067                        let pa = e.frame().mapped_pa;
1068                        let nr_pages = page_size(e.parent_level) / PAGE_SIZE;
1069                        forall|j: usize|
1070                            0 < j < nr_pages ==> {
1071                                let sub_idx =
1072                                    #[trigger] crate::specs::mm::frame::mapping::frame_to_index(
1073                                    (pa + j * PAGE_SIZE) as usize,
1074                                );
1075                                sub_idx != new_idx
1076                            }
1077                    },
1078                |
1079                    e: crate::specs::mm::page_table::node::entry_owners::EntryOwner<KernelPtConfig>,
1080                    p: vstd_extra::ghost_tree::TreePath<NR_ENTRIES>,
1081                |
1082                    e.is_frame() && e.parent_level > 1 ==> {
1083                        let pa = e.frame().mapped_pa;
1084                        let nr_pages = page_size(e.parent_level) / PAGE_SIZE;
1085                        forall|j: usize|
1086                            0 < j < nr_pages ==> {
1087                                let sub_idx =
1088                                    #[trigger] crate::specs::mm::frame::mapping::frame_to_index(
1089                                    (pa + j * PAGE_SIZE) as usize,
1090                                );
1091                                sub_idx != new_idx || (regions.contains(sub_idx)
1092                                    && regions.slot_owners[sub_idx].ref_count() != REF_COUNT_UNUSED
1093                                    && regions.slot_owners[sub_idx].ref_count() > 0
1094                                    && regions.slot_owners[sub_idx].ref_count() <= REF_COUNT_MAX)
1095                            }
1096                    },
1097            );
1098            kernel_owner.metaregion_sound_preserved_one_slot_changed(
1099                regions_after_kroot_borrow,
1100                *regions,
1101                new_idx,
1102            );
1103        }
1104        let mut i: usize = KernelPtConfig::TOP_LEVEL_INDEX_RANGE().start;
1105        while i < KernelPtConfig::TOP_LEVEL_INDEX_RANGE().end
1106            invariant
1107                kernel_owner.inv(),
1108                kernel_owner.0.value().is_node(),
1109                regions.inv(),
1110                !Self::create_user_pt_panic_condition(kernel_owner.0.value().node()),
1111                i <= KernelPtConfig::TOP_LEVEL_INDEX_RANGE().end,
1112                KernelPtConfig::TOP_LEVEL_INDEX_RANGE().start <= i,
1113                // Lock postcondition for the kernel root.
1114                *root_owner == kernel_owner.0.value().node(),
1115                root_owner.relate_guard(root_node),
1116                // Tree-wide soundness of the kernel page table.
1117                kernel_owner.metaregion_sound(*regions),
1118                // The new node owner's invariants and guard relation.
1119                new_node_owner.inv(),
1120                new_node_owner.relate_guard(new_node),
1121                regions.contains(new_node_owner.slot_index),
1122            decreases KernelPtConfig::TOP_LEVEL_INDEX_RANGE().end - i,
1123        {
1124            proof {
1125                let kern_node = kernel_owner.0.value().node();
1126                assert forall|j: usize|
1127                    #![trigger kern_node.children_perm.value()[j as int]]
1128                    KernelPtConfig::TOP_LEVEL_INDEX_RANGE().start <= j
1129                        < KernelPtConfig::TOP_LEVEL_INDEX_RANGE().end implies {
1130                    let pte = kern_node.children_perm.value()[j as int];
1131                    pte.is_present() && !pte.is_last(kern_node.level)
1132                } by {
1133                    let pte = kern_node.children_perm.value()[j as int];
1134                    if !pte.is_present() || pte.is_last(kern_node.level) {
1135                        assert(Self::create_user_pt_panic_condition(kern_node));
1136                    }
1137                }
1138
1139                kernel_owner.pt_inv_unroll(i as int);
1140                let tracked child_subtree: &OwnerSubtree<KernelPtConfig> =
1141                    kernel_owner.0.tracked_borrow_child(i as int);
1142                entry_owner = child_subtree.tracked_borrow_value();
1143                let kern_node = kernel_owner.0.value().node();
1144                assert(entry_owner.match_pte(
1145                    kern_node.children_perm.value()[i as int],
1146                    entry_owner.parent_level,
1147                ));
1148                assert(entry_owner.parent_level == kern_node.level);
1149                assert(child_subtree.inv());
1150                assert(entry_owner.inv());
1151                assert(root_owner.relate_guard(root_node));
1152
1153                kernel_owner.0.lemma_subtree_satisfies_unroll_once(
1154                    kernel_owner.0.value().path,
1155                    PageTableOwner::<KernelPtConfig>::metaregion_sound_pred(*regions),
1156                    i as int,
1157                );
1158                assert(child_subtree.subtree_satisfies(
1159                    kernel_owner.0.value().path.push_tail(i as int),
1160                    PageTableOwner::<KernelPtConfig>::metaregion_sound_pred(*regions),
1161                ));
1162                assert(entry_owner.metaregion_sound(*regions));
1163            }
1164
1165            #[verus_spec(with Tracked(root_owner), Tracked(entry_owner), Tracked(&*regions))]
1166            let root_entry = root_node.entry(i);
1167            let ghost pre_to_ref_regions: MetaRegionOwners = *regions;
1168            #[verus_spec(with Tracked(entry_owner), Tracked(root_owner), Tracked(regions))]
1169            let child = root_entry.to_ref();
1170
1171            proof {
1172                let kern_node = kernel_owner.0.value().node();
1173                let pte = kern_node.children_perm.value()[i as int];
1174
1175                assert(pte.is_present() && !pte.is_last(kern_node.level)) by {
1176                    if !pte.is_present() || pte.is_last(kern_node.level) {
1177                        assert(KernelPtConfig::TOP_LEVEL_INDEX_RANGE().start <= i
1178                            < KernelPtConfig::TOP_LEVEL_INDEX_RANGE().end);
1179                        assert(exists|j: usize|
1180                            KernelPtConfig::TOP_LEVEL_INDEX_RANGE().start <= j
1181                                < KernelPtConfig::TOP_LEVEL_INDEX_RANGE().end && {
1182                                let p = #[trigger] kern_node.children_perm.value()[j as int];
1183                                ||| !p.is_present()
1184                                ||| p.is_last(kern_node.level)
1185                            });
1186                        assert(Self::create_user_pt_panic_condition(kern_node));
1187                    }
1188                }
1189                // entry_owner.match_pte(pte, parent_level) + (present && !is_last)
1190                // ⟹ entry_owner.is_node().
1191                assert(entry_owner.is_node());
1192                // ChildRef::invariants(entry_owner, regions) gives child.wf(entry_owner).
1193                // For the Frame and None variants, wf requires is_frame() or is_absent(),
1194                // contradicting is_node(). Hence child must be PageTable.
1195                assert(child is PageTable);
1196                // to_ref's borrow_paddr preserves slot_owners exactly and only
1197                // grows `slots` (existing keys preserved). Use the tree-wide
1198                // preservation lemma.
1199                kernel_owner.metaregion_sound_preserved_slot_owners_eq(
1200                    pre_to_ref_regions,
1201                    *regions,
1202                );
1203            }
1204            let pt = match child {
1205                ChildRef::PageTable(pt) => pt,
1206                _ => vstd::pervasive::unreached(),
1207            };
1208
1209            let ghost entry_node_slot_idx = entry_owner.tracked_borrow_node().slot_index;
1210            let tracked entry_node_slot_perm = regions.slots.tracked_borrow(entry_node_slot_idx);
1211            #[verus_spec(with Tracked(entry_node_slot_perm))]
1212            let pt_addr = pt.start_paddr();
1213            let pte = PageTableEntry::new_pt(pt_addr);
1214
1215            proof {
1216                assert(regions.contains(new_node_owner.slot_index));
1217            }
1218            unsafe {
1219                #[verus_spec(with Tracked(&mut new_node_owner), Tracked(&*regions))]
1220                new_node.write_pte(i, pte)
1221            };
1222
1223            i = i + 1;
1224        }
1225
1226        PageTable::<UserPtConfig> { root: new_root }
1227    }/*
1228    /// Protect the given virtual address range in the kernel page table.
1229    ///
1230    /// This method flushes the TLB entries when doing protection.
1231    ///
1232    /// # Safety
1233    ///
1234    /// The caller must ensure that the protection operation does not affect
1235    /// the memory safety of the kernel.
1236    pub unsafe fn protect_flush_tlb(
1237        &self,
1238        vaddr: &Range<Vaddr>,
1239        mut op: impl FnMut(&mut PageProperty),
1240    ) -> Result<(), PageTableError> {
1241        let preempt_guard = disable_preempt();
1242        let mut cursor = CursorMut::new(self, &preempt_guard, vaddr)?;
1243        // SAFETY: The safety is upheld by the caller.
1244        while let Some(range) =
1245            unsafe { cursor.protect_next(vaddr.end - cursor.virt_addr(), &mut op) }
1246        {
1247            crate::arch::mm::tlb_flush_addr(range.start);
1248        }
1249        Ok(())
1250    }*/
1251
1252}
1253
1254#[verus_verify]
1255impl<C: PageTableConfig> PageTable<C> {
1256    /// Relates this executable page-table handle to its tracked ownership tree.
1257    pub open spec fn relates_owner(
1258        &self,
1259        owner: PageTableOwner<C>,
1260        regions: MetaRegionOwners,
1261    ) -> bool {
1262        &&& owner.inv()
1263        &&& self.root.ptr.addr() == owner.0.value().node().meta_vaddr()
1264        &&& owner.metaregion_sound(regions)
1265    }
1266
1267    /// Create a new empty page table.
1268    ///
1269    /// Useful for the IOMMU page tables only.
1270    #[verifier::external_body]
1271    pub fn empty() -> Self {
1272        unimplemented!()
1273    }
1274
1275    /// Create a new empty page table together with its tracked ownership.
1276    #[verifier::external_body]
1277    #[verus_spec(r =>
1278        with Tracked(owner): Tracked<&mut Option<PageTableOwner<C>>>,
1279            Tracked(regions): Tracked<&mut MetaRegionOwners>,
1280            Tracked(guards): Tracked<&mut Guards<'rcu>>,
1281        requires
1282            old(regions).inv(),
1283        ensures
1284            final(owner)@ is Some,
1285            final(owner)@->0.inv(),
1286            (final(owner)@->0).0.value().is_node(),
1287            (final(owner)@->0).0.value().is_node(),
1288            r.root.ptr.addr() == (final(owner)@->0).0.value().node().meta_vaddr(),
1289            (final(owner)@->0).0.value().metaregion_sound(*final(regions)),
1290            final(regions).inv(),
1291            final(guards).unlocked((final(owner)@->0).0.value().node().meta_vaddr()),
1292            // Allocating a fresh node does not change the lock set, so any node
1293            // that was (un)locked before remains so.
1294            final(guards).guards == old(guards).guards,
1295            // The newly allocated slot was in the free pool before the call.
1296            old(regions).contains(
1297                crate::specs::mm::frame::mapping::frame_to_index(
1298                    (final(owner)@->0).0.value().meta_slot_paddr()->0)),
1299            // After the alloc, the slot is removed from the free pool (now owned
1300            // by the new pt's NodeOwner).
1301            !final(regions).contains(
1302                crate::specs::mm::frame::mapping::frame_to_index(
1303                    (final(owner)@->0).0.value().meta_slot_paddr()->0)),
1304            // Other slots and lock state are preserved.
1305            forall |i: int| #![trigger final(regions).slot_owners[i]]
1306                i != crate::specs::mm::frame::mapping::frame_to_index(
1307                    (final(owner)@->0).0.value().meta_slot_paddr()->0)
1308                ==> final(regions).slot_owners[i] == old(regions).slot_owners[i],
1309            forall |a: usize| old(guards).lock_held(a) ==> final(guards).lock_held(a),
1310            forall |idx: int| #![trigger final(regions).slot_owners[idx].paths_in_pt]
1311                final(regions).slot_owners[idx].paths_in_pt
1312                    == old(regions).slot_owners[idx].paths_in_pt,
1313            // Allocation preserves the soundness of the kernel page-table tree:
1314            // a fresh allocation cannot collide with any active node or frame entry
1315            // (the allocator returns a slot that wasn't in use). Stated as a
1316            // postcondition because deriving it requires a freshness axiom on the
1317            // underlying frame allocator.
1318            forall |kt: PageTableOwner<KernelPtConfig>|
1319                #![trigger kt.metaregion_sound(*final(regions))]
1320                kt.inv() && kt.metaregion_sound(*old(regions))
1321                ==> kt.metaregion_sound(*final(regions)),
1322            // Freshness: the new PT's slot index is not used (as a primary slot
1323            // or huge-frame sub-page slot) by any entry in any KernelPtConfig PT
1324            // tree that was sound before the alloc. Used to discharge the borrow
1325            // step that mutates `slot_owners[new_idx]`.
1326            forall |kt: PageTableOwner<KernelPtConfig>|
1327                #![trigger kt.metaregion_sound(*old(regions))]
1328                kt.inv() && kt.metaregion_sound(*old(regions)) ==>
1329                kt.0.subtree_satisfies(
1330                    kt.0.value().path,
1331                    |e: crate::specs::mm::page_table::node::entry_owners::EntryOwner<KernelPtConfig>,
1332                     p: vstd_extra::ghost_tree::TreePath<NR_ENTRIES>|
1333                        e.meta_slot_paddr() is Some
1334                            ==> crate::specs::mm::frame::mapping::frame_to_index(
1335                                e.meta_slot_paddr()->0) !=
1336                                crate::specs::mm::frame::mapping::frame_to_index(
1337                                    (final(owner)@->0).0.value().meta_slot_paddr()->0),
1338                ),
1339            // Sub-page freshness: for any huge frame entry in any pre-existing
1340            // sound KernelPtConfig tree, the new PT's slot index isn't a sub-page
1341            // slot of the huge frame either. Same allocator-freshness rationale.
1342            forall |kt: PageTableOwner<KernelPtConfig>|
1343                #![trigger kt.metaregion_sound(*old(regions))]
1344                kt.inv() && kt.metaregion_sound(*old(regions)) ==>
1345                kt.0.subtree_satisfies(
1346                    kt.0.value().path,
1347                    |e: crate::specs::mm::page_table::node::entry_owners::EntryOwner<KernelPtConfig>,
1348                     p: vstd_extra::ghost_tree::TreePath<NR_ENTRIES>|
1349                        e.is_frame() && e.parent_level > 1 ==> {
1350                            let pa = e.frame().mapped_pa;
1351                            let nr_pages = page_size(
1352                                e.parent_level) / PAGE_SIZE;
1353                            forall |j: usize| 0 < j < nr_pages ==> {
1354                                let sub_idx =
1355                                    #[trigger] crate::specs::mm::frame::mapping::frame_to_index(
1356                                        (pa + j * PAGE_SIZE) as usize);
1357                                sub_idx != crate::specs::mm::frame::mapping::frame_to_index(
1358                                    (final(owner)@->0).0.value().meta_slot_paddr()->0)
1359                            }
1360                        },
1361                ),
1362    )]
1363    pub fn empty_with_owner<'rcu>() -> Self {
1364        unimplemented!()
1365    }
1366
1367    #[verifier::external_body]
1368    pub(in crate::mm) unsafe fn first_activate_unchecked(&self) {
1369        unimplemented!()
1370        // SAFETY: The safety is upheld by the caller.
1371        //        unsafe { self.root.first_activate() };
1372
1373    }
1374
1375    pub uninterp spec fn root_paddr_spec(&self) -> Paddr;
1376
1377    /// The physical address of the root page table.
1378    ///
1379    /// Obtaining the physical address of the root page table is safe, however, using it or
1380    /// providing it to the hardware will be unsafe since the page table node may be dropped,
1381    /// resulting in UAF.
1382    #[verifier::external_body]
1383    #[verifier::when_used_as_spec(root_paddr_spec)]
1384    pub fn root_paddr(&self) -> (r: Paddr)
1385        returns
1386            self.root_paddr_spec(),
1387    {
1388        unimplemented!()
1389        //        self.root.start_paddr()
1390
1391    }
1392
1393    /// Query about the mapping of a single byte at the given virtual address.
1394    ///
1395    /// Note that this function may fail reflect an accurate result if there are
1396    /// cursors concurrently accessing the same virtual address range, just like what
1397    /// happens for the hardware MMU walk.
1398    #[cfg(ktest)]
1399    pub fn page_walk(&self, vaddr: Vaddr) -> Option<(Paddr, PageProperty)> {
1400        // SAFETY: The root node is a valid page table node so the address is valid.
1401        unsafe { page_walk::<C>(self.root_paddr(), vaddr) }
1402    }
1403
1404    /// Create a new cursor exclusively accessing the virtual address range for mapping.
1405    ///
1406    /// If another cursor is already accessing the range, the new cursor may wait until the
1407    /// previous cursor is dropped.
1408    #[verus_spec(r =>
1409        with Tracked(owner): Tracked<PageTableOwner<C>>,
1410            Ghost(root_guard): Ghost<PageTableGuard<'rcu, C>>,
1411            Tracked(regions): Tracked<&mut MetaRegionOwners>,
1412            Tracked(guards): Tracked<&mut Guards<'rcu>>
1413        requires
1414            self.relates_owner(owner, *old(regions)),
1415            owner.0.value().node().relate_guard(root_guard),
1416            // Per-config tightening; see `Cursor::new`.
1417            0 < va.end <= C::LOCKED_END_BOUND_spec(),
1418        ensures
1419            Cursor::<C, G>::cursor_new_success_conditions(*va) ==> {
1420                &&& r is Ok
1421                &&& r.unwrap().0.0.invariants(*r.unwrap().1, *final(regions), *final(guards))
1422                &&& r.unwrap().1.in_locked_range()
1423                &&& r.unwrap().0.0.level == r.unwrap().0.0.guard_level
1424                &&& r.unwrap().0.0.guard_level == NR_LEVELS as PagingLevel
1425                &&& r.unwrap().0.0.va < r.unwrap().0.0.barrier_va.end
1426                &&& r.unwrap().0.0.va == va.start
1427                &&& r.unwrap().0.0.barrier_va == *va
1428            },
1429            !Cursor::<C, G>::cursor_new_success_conditions(*va) ==> r is Err,
1430            forall |item: C::Item| #![trigger CursorMut::<'rcu, C, G>::item_not_mapped(item, *old(regions))]
1431                CursorMut::<'rcu, C, G>::item_not_mapped(item, *old(regions)) ==>
1432                CursorMut::<'rcu, C, G>::item_not_mapped(item, *final(regions)),
1433            // CursorMut::new inherits Cursor::new's weakened preservation:
1434            // PT-node allocations come from UNUSED slots, so any slot that
1435            // was already in use keeps its paths_in_pt.
1436            forall |idx: int| #![trigger final(regions).slot_owners[idx].paths_in_pt]
1437                old(regions).slot_owners[idx].ref_count()
1438                    != REF_COUNT_UNUSED
1439                ==> final(regions).slot_owners[idx].paths_in_pt
1440                        == old(regions).slot_owners[idx].paths_in_pt,
1441            forall|idx: int| #![trigger final(regions).slot_owners[idx]]
1442                old(regions).contains(idx)
1443                && old(regions).slot_owners[idx].ref_count()
1444                    != REF_COUNT_UNUSED
1445                ==> final(regions).slot_owners[idx].ref_count()
1446                        == old(regions).slot_owners[idx].ref_count()
1447                    && final(regions).slot_owners[idx].usage
1448                        == old(regions).slot_owners[idx].usage,
1449    )]
1450    pub fn cursor_mut<'rcu, G: InAtomicMode>(
1451        &'rcu self,
1452        guard: &'rcu G,
1453        va: &Range<Vaddr>,
1454    ) -> Result<(CursorMut<'rcu, C, G>, Tracked<CursorOwner<'rcu, C>>), PageTableError> {
1455        #[verus_spec(with Tracked(owner), Ghost(root_guard), Tracked(regions), Tracked(guards))]
1456        CursorMut::new(self, guard, va)
1457    }
1458
1459    /// Create a new cursor exclusively accessing the virtual address range for querying.
1460    ///
1461    /// If another cursor is already accessing the range, the new cursor may wait until the
1462    /// previous cursor is dropped. The modification to the mapping by the cursor may also
1463    /// block or be overridden by the mapping of another cursor.
1464    #[verus_spec(r =>
1465        with Tracked(owner): Tracked<PageTableOwner<C>>,
1466            Ghost(root_guard): Ghost<PageTableGuard<'rcu, C>>,
1467            Tracked(regions): Tracked<&mut MetaRegionOwners>,
1468            Tracked(guards): Tracked<&mut Guards<'rcu>>
1469        requires
1470            self.relates_owner(owner, *old(regions)),
1471            owner.0.value().node().relate_guard(root_guard),
1472            // Per-config tightening; see `Cursor::new`.
1473            0 < va.end <= C::LOCKED_END_BOUND_spec(),
1474        ensures
1475            Cursor::<C, G>::cursor_new_success_conditions(*va) ==> {
1476                &&& r is Ok
1477                &&& r.unwrap().0.invariants(*r.unwrap().1, *final(regions), *final(guards))
1478                &&& r.unwrap().1.in_locked_range()
1479                &&& r.unwrap().0.level == r.unwrap().0.guard_level
1480                &&& r.unwrap().0.va < r.unwrap().0.barrier_va.end
1481                &&& r.unwrap().0.va == va.start
1482                &&& r.unwrap().0.barrier_va == *va
1483                &&& r.unwrap().1@.as_page_table_owner() == owner
1484                &&& r.unwrap().1@.continuations[3].path() == owner.0.value().path
1485            },
1486            !Cursor::<C, G>::cursor_new_success_conditions(*va) ==> r is Err,
1487            forall|idx: int| #![trigger final(regions).slot_owners[idx].paths_in_pt]
1488                old(regions).slot_owners[idx].ref_count()
1489                    != REF_COUNT_UNUSED
1490                ==> final(regions).slot_owners[idx].paths_in_pt
1491                        == old(regions).slot_owners[idx].paths_in_pt,
1492            // Non-saturation preservation.
1493            (forall |i: int| #![trigger old(regions).slot_owners[i]]
1494                old(regions).contains(i)
1495                && old(regions).slot_owners[i].ref_count()
1496                    != REF_COUNT_UNUSED
1497                ==> old(regions).slot_owners[i].ref_count() + 1
1498                    < REF_COUNT_MAX)
1499            ==>
1500            (forall |i: int| #![trigger final(regions).slot_owners[i]]
1501                final(regions).contains(i)
1502                && final(regions).slot_owners[i].ref_count()
1503                    != REF_COUNT_UNUSED
1504                ==> final(regions).slot_owners[i].ref_count() + 1
1505                    < REF_COUNT_MAX),
1506            // Saturated-slot bridge (relayed from `Cursor::new`):
1507            // a slot at `>= REF_COUNT_MAX` before iff after, with the same
1508            // value. Used by `KVirtArea::query` to bridge inner-cursor
1509            // saturation back to the caller's snapshot.
1510            forall|idx: int| #![trigger final(regions).slot_owners[idx].ref_count()]
1511                final(regions).slot_owners[idx].ref_count()
1512                    >= REF_COUNT_MAX
1513                ==> old(regions).slot_owners[idx].ref_count()
1514                        == final(regions).slot_owners[idx].ref_count(),
1515            forall|idx: int| #![trigger old(regions).slot_owners[idx].ref_count()]
1516                old(regions).slot_owners[idx].ref_count()
1517                    >= REF_COUNT_MAX
1518                ==> final(regions).slot_owners[idx].ref_count()
1519                        == old(regions).slot_owners[idx].ref_count(),
1520    )]
1521    pub fn cursor<'rcu, G: InAtomicMode>(&'rcu self, guard: &'rcu G, va: &Range<Vaddr>) -> Result<
1522        (Cursor<'rcu, C, G>, Tracked<CursorOwner<'rcu, C>>),
1523        PageTableError,
1524    > {
1525        #[verus_spec(with Tracked(owner), Ghost(root_guard), Tracked(regions), Tracked(guards))]
1526        Cursor::new(self, guard, va)
1527    }/*
1528    /// Create a new reference to the same page table.
1529    /// The caller must ensure that the kernel page table is not copied.
1530    /// This is only useful for IOMMU page tables. Think twice before using it in other cases.
1531    pub unsafe fn shallow_copy(&self) -> Self {
1532        PageTable {
1533            root: self.root.clone(),
1534        }
1535    }
1536    */
1537
1538}
1539
1540/// A software emulation of the MMU address translation process.
1541///
1542/// This method returns the physical address of the given virtual address and
1543/// the page property if a valid mapping exists for the given virtual address.
1544///
1545/// # Safety
1546///
1547/// The caller must ensure that the `root_paddr` is a pointer to a valid root
1548/// page table node.
1549///
1550/// # Notes on the page table use-after-free problem
1551///
1552/// Neither the hardware MMU nor the software page walk method acquires the page
1553/// table locks while reading. They can enter a to-be-recycled page table node
1554/// and read the page table entries after the node is recycled and reused.
1555///
1556/// For the hardware MMU page walk, we mitigate this problem by dropping the page
1557/// table nodes only after the TLBs have been flushed on all the CPUs that
1558/// activate the page table.
1559///
1560/// For the software page walk, we only need to disable preemption at the beginning
1561/// since the page table nodes won't be recycled in the RCU critical section.
1562#[cfg(ktest)]
1563pub(super) unsafe fn page_walk<C: PageTableConfig>(root_paddr: Paddr, vaddr: Vaddr) -> Option<
1564    (Paddr, PageProperty),
1565> {
1566    use super::paddr_to_vaddr;
1567
1568    let _rcu_guard = disable_preempt();
1569
1570    let mut pt_addr = paddr_to_vaddr(root_paddr);
1571    #[verusfmt::skip]
1572    for cur_level in (1..= C::NR_LEVELS()).rev() {
1573        let offset = pte_index::<C>(vaddr, cur_level);
1574        // SAFETY:
1575        //  - The page table node is alive because (1) the root node is alive and
1576        //    (2) all child nodes cannot be recycled because we're in the RCU critical section.
1577        //  - The index is inside the bound, so the page table entry is valid.
1578        //  - All page table entries are aligned and accessed with atomic operations only.
1579        let cur_pte = unsafe { load_pte((pt_addr as *mut C::E).add(offset), Ordering::Acquire) };
1580
1581        if !cur_pte.is_present() {
1582            return None;
1583        }
1584        if cur_pte.is_last(cur_level) {
1585            debug_assert!(cur_level <= C::HIGHEST_TRANSLATION_LEVEL);
1586            return Some(
1587                (cur_pte.paddr() + (vaddr & (page_size::<C>(cur_level) - 1)), cur_pte.prop()),
1588            );
1589        }
1590        pt_addr = paddr_to_vaddr(cur_pte.paddr());
1591    }
1592
1593    unreachable!("All present PTEs at the level 1 must be last-level PTEs");
1594}
1595
1596/// A trait that abstracts architecture-specific page table entries (PTEs).
1597///
1598/// Note that a default PTE should be a PTE that points to nothing.
1599pub trait PageTableEntryTrait:
1600    Clone + Copy + Debug + Default + Sized + Pod + PodOnce + Send + Sync + 'static {
1601    spec fn new_absent_spec() -> Self;
1602
1603    /// Create a set of new invalid page table flags that indicates an absent page.
1604    ///
1605    /// Note that currently the implementation requires an all zero PTE to be an absent PTE.
1606    #[verifier(when_used_as_spec(new_absent_spec))]
1607    fn new_absent() -> (res: Self)
1608        ensures
1609            valid_frame_paddr(res.paddr()),
1610            !res.is_present(),
1611        returns
1612            Self::new_absent(),
1613    ;
1614
1615    spec fn is_present_spec(&self) -> bool;
1616
1617    /// Returns if the PTE points to something.
1618    ///
1619    /// For PTEs created by [`Self::new_absent`], this method should return
1620    /// false. For PTEs created by [`Self::new_page`] or [`Self::new_pt`]
1621    /// and modified with [`Self::set_prop`], this method should return true.
1622    #[verifier::when_used_as_spec(is_present_spec)]
1623    fn is_present(&self) -> bool
1624        returns
1625            self.is_present_spec(),
1626    ;
1627
1628    spec fn new_page_spec(paddr: Paddr, level: PagingLevel, prop: PageProperty) -> Self;
1629
1630    /// The preconditions for creating a new page-mapping PTE.
1631    spec fn new_page_req(paddr: Paddr, level: PagingLevel, prop: PageProperty) -> bool;
1632
1633    /// Creates a new PTE that maps to a page.
1634    #[verifier::when_used_as_spec(new_page_spec)]
1635    fn new_page(paddr: Paddr, level: PagingLevel, prop: PageProperty) -> (res: Self)
1636        requires
1637            paddr < MAX_PADDR,
1638            Self::new_page_req(paddr, level, prop),
1639        ensures
1640            res.paddr() == paddr & !((PAGE_SIZE - 1) as usize),
1641            paddr % PAGE_SIZE == 0 ==> res.paddr() == paddr,
1642            valid_frame_paddr(res.paddr()),
1643            res.is_present(),
1644            res.is_last(level),
1645            res.prop() == prop,
1646        returns
1647            Self::new_page(paddr, level, prop),
1648    ;
1649
1650    spec fn new_pt_spec(paddr: Paddr) -> Self;
1651
1652    /// Create a new PTE that map to a child page table.
1653    #[verifier::when_used_as_spec(new_pt_spec)]
1654    fn new_pt(paddr: Paddr) -> (res: Self)
1655        requires
1656            paddr < MAX_PADDR,
1657        ensures
1658            res.paddr() == paddr & !((PAGE_SIZE - 1) as usize),
1659            paddr % PAGE_SIZE == 0 ==> res.paddr() == paddr,
1660            valid_frame_paddr(res.paddr()),
1661            res.is_present(),
1662            forall|level: PagingLevel| !res.is_last(level),
1663        returns
1664            Self::new_pt(paddr),
1665    ;
1666
1667    /// Returns the physical address from the PTE.
1668    ///
1669    /// The physical address recorded in the PTE is either:
1670    /// - the physical address of the next-level page table, or
1671    /// - the physical address of the page that the PTE maps to.
1672    spec fn paddr_spec(&self) -> Paddr;
1673
1674    #[verifier::when_used_as_spec(paddr_spec)]
1675    fn paddr(&self) -> (res: Paddr)
1676        ensures
1677            valid_frame_paddr(res),
1678        returns
1679            self.paddr(),
1680    ;
1681
1682    spec fn prop_spec(&self) -> PageProperty;
1683
1684    #[verifier::when_used_as_spec(prop_spec)]
1685    fn prop(&self) -> PageProperty
1686        returns
1687            self.prop(),
1688    ;
1689
1690    /// The preconditions for setting the page property of a PTE.
1691    spec fn set_prop_req(self, prop: PageProperty) -> bool;
1692
1693    fn set_prop(&mut self, prop: PageProperty)
1694        requires
1695            old(self).set_prop_req(prop),
1696        ensures
1697            !old(self).is_present() ==> *old(self) == *final(self),
1698            old(self).is_present() ==> {
1699                &&& final(self).prop() == prop
1700                &&& final(self).paddr() == old(self).paddr()
1701                &&& final(self).is_present()
1702                &&& forall|level: PagingLevel|
1703                    #![trigger old(self).is_last(level)]
1704                    old(self).is_last(level) ==> final(self).is_last(level)
1705            },
1706    ;
1707
1708    spec fn is_last_spec(&self, level: PagingLevel) -> bool;
1709
1710    /// Returns if the PTE maps a page rather than a child page table.
1711    ///
1712    /// The method needs to know the level of the page table where the PTE resides,
1713    /// since architectures like x86-64 have a huge bit only in intermediate levels.
1714    #[verifier::when_used_as_spec(is_last_spec)]
1715    fn is_last(&self, level: PagingLevel) -> bool
1716        returns
1717            self.is_last_spec(level),
1718    ;
1719
1720    spec fn as_usize_spec(self) -> usize;
1721
1722    /// Converts the PTE into a raw `usize` value.
1723    #[verifier::external_body]
1724    #[verifier::when_used_as_spec(as_usize_spec)]
1725    fn as_usize(self) -> usize
1726        returns
1727            self.as_usize(),
1728    {
1729        unimplemented!()
1730        // const { assert!(size_of::<Self>() == size_of::<usize>()) };
1731        // SAFETY: `Self` is `Pod` and has the same memory representation as `usize`.
1732        // unsafe { transmute_unchecked(self) }
1733
1734    }
1735
1736    /// Converts the raw `usize` value into a PTE.
1737    #[verifier::external_body]
1738    fn from_usize(pte_raw: usize) -> Self {
1739        unimplemented!()
1740        // const { assert!(size_of::<Self>() == size_of::<usize>()) };
1741        // SAFETY: `Self` is `Pod` and has the same memory representation as `usize`.
1742        // unsafe { transmute_unchecked(pte_raw) }
1743
1744    }
1745
1746    /// Absent (zero) PTE has well-formed paddr for match_pte.
1747    proof fn lemma_page_table_entry_properties()
1748        ensures
1749            core::mem::size_of::<Self>() == core::mem::size_of::<usize>(),
1750            core::mem::size_of::<Self>() % core::mem::align_of::<Self>() == 0,
1751            core::mem::align_of::<Self>() > 0,
1752            valid_frame_paddr(Self::new_absent().paddr()),
1753            !Self::new_absent().is_present(),
1754            forall|level: PagingLevel|
1755                #![trigger Self::new_absent().is_last(level)]
1756                1 < level ==> !Self::new_absent().is_last(level),
1757            forall|paddr: Paddr, level: PagingLevel, prop: PageProperty|
1758                #![trigger Self::new_page(paddr, level, prop)]
1759                Self::new_page_req(paddr, level, prop) && (prop.cache is Writeback
1760                    || prop.cache is Writethrough || prop.cache is Uncacheable) ==> {
1761                    &&& Self::new_page(paddr, level, prop).is_present()
1762                    &&& (paddr < MAX_PADDR ==> Self::new_page(paddr, level, prop).paddr() == paddr
1763                        & !((PAGE_SIZE - 1) as usize))
1764                    &&& (paddr < MAX_PADDR && paddr % PAGE_SIZE == 0 ==> Self::new_page(
1765                        paddr,
1766                        level,
1767                        prop,
1768                    ).paddr() == paddr)
1769                    &&& Self::new_page(paddr, level, prop).prop() == prop
1770                    &&& Self::new_page(paddr, level, prop).is_last(level)
1771                },
1772            forall|paddr: Paddr|
1773                #![trigger Self::new_pt(paddr)]
1774                {
1775                    &&& Self::new_pt(paddr).is_present()
1776                    &&& (paddr < MAX_PADDR ==> Self::new_pt(paddr).paddr() == paddr & !((PAGE_SIZE
1777                        - 1) as usize))
1778                    &&& (paddr < MAX_PADDR && paddr % PAGE_SIZE == 0 ==> Self::new_pt(paddr).paddr()
1779                        == paddr)
1780                    &&& forall|level: PagingLevel| !Self::new_pt(paddr).is_last(level)
1781                },
1782    ;
1783
1784    proof fn lemma_paddr_is_page_aligned(self)
1785        ensures
1786            self.paddr() % PAGE_SIZE == 0,
1787    ;
1788}
1789
1790/// Loads a page table entry with an atomic instruction.
1791///
1792/// # Verification Design
1793/// ## Preconditions
1794/// - The pointer must be a valid pointer to the array that represents the page table node.
1795/// - The array must be initialized at the target index.
1796/// ## Postconditions
1797/// - The value is loaded from the array at the given index.
1798/// ## Safety
1799/// - We require the caller to provide a permission token to ensure that this function is only called on a valid array
1800/// and the pointer is in bounds.
1801/// - Like an `AtomicUsize::load` in normal Rust, this function assumes that the value being loaded is an integer
1802/// (and therefore can be safely cloned). We model the PTE as an abstract type, but in all actual implementations it is an
1803/// integer. Importantly, it does not include any data that is unsafe to duplicate.
1804#[verifier::external_body]
1805#[verus_spec(
1806    with Tracked(perm): Tracked<&vstd_extra::array_ptr::PointsTo<E, NR_ENTRIES>>
1807    requires
1808        perm.is_init(ptr.index as int),
1809        perm.addr() == ptr.addr(),
1810        0 <= ptr.index < NR_ENTRIES,
1811    returns
1812        perm.value()[ptr.index as int],
1813)]
1814pub unsafe fn load_pte<E: PageTableEntryTrait>(
1815    ptr: vstd_extra::array_ptr::ArrayPtr<E, NR_ENTRIES>,
1816    ordering: Ordering,
1817) -> (pte: E) {
1818    unimplemented!()
1819}
1820
1821/// Stores a page table entry with an atomic instruction.
1822///
1823/// # Verification Design
1824/// We axiomatize this function as a store operation in the array that represents the page table node.
1825/// ## Preconditions
1826/// - The pointer must be a valid pointer to the array that represents the page table node.
1827/// - The array must be initialized so that the verifier knows that it remains initialized after the store.
1828/// ## Postconditions
1829/// - The new value is stored in the array at the given index.
1830/// ## Safety
1831/// - We require the caller to provide a permission token to ensure that this function is only called on a valid array
1832/// and the pointer is in bounds.
1833#[verifier::external_body]
1834#[verus_spec(
1835    with Tracked(perm): Tracked<&mut vstd_extra::array_ptr::PointsTo<E, NR_ENTRIES>>
1836    requires
1837        old(perm).addr() == ptr.addr(),
1838        0 <= ptr.index < NR_ENTRIES,
1839        old(perm).is_init_all(),
1840    ensures
1841        final(perm).wf(),
1842        final(perm).value()[ptr.index as int] == new_val,
1843        final(perm).value() == old(perm).value().update(ptr.index as int, new_val),
1844        final(perm).addr() == old(perm).addr(),
1845        final(perm).is_init_all(),
1846)]
1847pub unsafe fn store_pte<E: PageTableEntryTrait>(
1848    ptr: vstd_extra::array_ptr::ArrayPtr<E, NR_ENTRIES>,
1849    new_val: E,
1850    ordering: Ordering,
1851);
1852
1853} // verus!