Skip to main content

ostd/mm/page_table/
mod.rs

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