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                if k != new_idx {
1059                    // borrow preserves slots[k] at k != self.index() == new_idx
1060                }
1061            };
1062            assert(kernel_owner.metaregion_sound(regions_before_alloc));
1063
1064            kernel_owner.0.lemma_subtree_satisfies_implies(
1065                kernel_owner.0.value().path,
1066                |
1067                    e: crate::specs::mm::page_table::node::entry_owners::EntryOwner<KernelPtConfig>,
1068                    p: vstd_extra::ghost_tree::TreePath<NR_ENTRIES>,
1069                |
1070                    e.is_frame() && e.parent_level > 1 ==> {
1071                        let pa = e.frame().mapped_pa;
1072                        let nr_pages = page_size(e.parent_level) / PAGE_SIZE;
1073                        forall|j: usize|
1074                            0 < j < nr_pages ==> {
1075                                let sub_idx =
1076                                    #[trigger] crate::specs::mm::frame::mapping::frame_to_index(
1077                                    (pa + j * PAGE_SIZE) as usize,
1078                                );
1079                                sub_idx != new_idx
1080                            }
1081                    },
1082                |
1083                    e: crate::specs::mm::page_table::node::entry_owners::EntryOwner<KernelPtConfig>,
1084                    p: vstd_extra::ghost_tree::TreePath<NR_ENTRIES>,
1085                |
1086                    e.is_frame() && e.parent_level > 1 ==> {
1087                        let pa = e.frame().mapped_pa;
1088                        let nr_pages = page_size(e.parent_level) / PAGE_SIZE;
1089                        forall|j: usize|
1090                            0 < j < nr_pages ==> {
1091                                let sub_idx =
1092                                    #[trigger] crate::specs::mm::frame::mapping::frame_to_index(
1093                                    (pa + j * PAGE_SIZE) as usize,
1094                                );
1095                                sub_idx != new_idx || (regions.contains(sub_idx)
1096                                    && regions.slot_owners[sub_idx].ref_count() != REF_COUNT_UNUSED
1097                                    && regions.slot_owners[sub_idx].ref_count() > 0
1098                                    && regions.slot_owners[sub_idx].ref_count() <= REF_COUNT_MAX)
1099                            }
1100                    },
1101            );
1102            kernel_owner.metaregion_sound_preserved_one_slot_changed(
1103                regions_after_kroot_borrow,
1104                *regions,
1105                new_idx,
1106            );
1107        }
1108        let mut i: usize = KernelPtConfig::TOP_LEVEL_INDEX_RANGE().start;
1109        while i < KernelPtConfig::TOP_LEVEL_INDEX_RANGE().end
1110            invariant
1111                kernel_owner.inv(),
1112                kernel_owner.0.value().is_node(),
1113                regions.inv(),
1114                !Self::create_user_pt_panic_condition(kernel_owner.0.value().node()),
1115                i <= KernelPtConfig::TOP_LEVEL_INDEX_RANGE().end,
1116                KernelPtConfig::TOP_LEVEL_INDEX_RANGE().start <= i,
1117                // Lock postcondition for the kernel root.
1118                *root_owner == kernel_owner.0.value().node(),
1119                root_owner.relate_guard(root_node),
1120                // Tree-wide soundness of the kernel page table.
1121                kernel_owner.metaregion_sound(*regions),
1122                // The new node owner's invariants and guard relation.
1123                new_node_owner.inv(),
1124                new_node_owner.relate_guard(new_node),
1125                regions.contains(new_node_owner.slot_index),
1126            decreases KernelPtConfig::TOP_LEVEL_INDEX_RANGE().end - i,
1127        {
1128            proof {
1129                let kern_node = kernel_owner.0.value().node();
1130                assert forall|j: usize|
1131                    #![trigger kern_node.children_perm.value()[j as int]]
1132                    KernelPtConfig::TOP_LEVEL_INDEX_RANGE().start <= j
1133                        < KernelPtConfig::TOP_LEVEL_INDEX_RANGE().end implies {
1134                    let pte = kern_node.children_perm.value()[j as int];
1135                    pte.is_present() && !pte.is_last(kern_node.level)
1136                } by {
1137                    let pte = kern_node.children_perm.value()[j as int];
1138                    if !pte.is_present() || pte.is_last(kern_node.level) {
1139                        assert(Self::create_user_pt_panic_condition(kern_node));
1140                    }
1141                }
1142
1143                kernel_owner.pt_inv_unroll(i as int);
1144                let tracked child_subtree: &OwnerSubtree<KernelPtConfig> =
1145                    kernel_owner.0.tracked_borrow_child(i as int);
1146                entry_owner = child_subtree.tracked_borrow_value();
1147                let kern_node = kernel_owner.0.value().node();
1148                assert(entry_owner.match_pte(
1149                    kern_node.children_perm.value()[i as int],
1150                    entry_owner.parent_level,
1151                ));
1152                assert(entry_owner.parent_level == kern_node.level);
1153                assert(child_subtree.inv());
1154                assert(entry_owner.inv());
1155                assert(root_owner.relate_guard(root_node));
1156
1157                kernel_owner.0.lemma_subtree_satisfies_unroll_once(
1158                    kernel_owner.0.value().path,
1159                    PageTableOwner::<KernelPtConfig>::metaregion_sound_pred(*regions),
1160                    i as int,
1161                );
1162                assert(child_subtree.subtree_satisfies(
1163                    kernel_owner.0.value().path.push_tail(i as int),
1164                    PageTableOwner::<KernelPtConfig>::metaregion_sound_pred(*regions),
1165                ));
1166                assert(entry_owner.metaregion_sound(*regions));
1167            }
1168
1169            #[verus_spec(with Tracked(root_owner), Tracked(entry_owner), Tracked(&*regions))]
1170            let root_entry = root_node.entry(i);
1171            let ghost pre_to_ref_regions: MetaRegionOwners = *regions;
1172            #[verus_spec(with Tracked(entry_owner), Tracked(root_owner), Tracked(regions))]
1173            let child = root_entry.to_ref();
1174
1175            proof {
1176                let kern_node = kernel_owner.0.value().node();
1177                let pte = kern_node.children_perm.value()[i as int];
1178
1179                assert(pte.is_present() && !pte.is_last(kern_node.level)) by {
1180                    if !pte.is_present() || pte.is_last(kern_node.level) {
1181                        assert(KernelPtConfig::TOP_LEVEL_INDEX_RANGE().start <= i
1182                            < KernelPtConfig::TOP_LEVEL_INDEX_RANGE().end);
1183                        assert(exists|j: usize|
1184                            KernelPtConfig::TOP_LEVEL_INDEX_RANGE().start <= j
1185                                < KernelPtConfig::TOP_LEVEL_INDEX_RANGE().end && {
1186                                let p = #[trigger] kern_node.children_perm.value()[j as int];
1187                                ||| !p.is_present()
1188                                ||| p.is_last(kern_node.level)
1189                            });
1190                        assert(Self::create_user_pt_panic_condition(kern_node));
1191                    }
1192                }
1193                // entry_owner.match_pte(pte, parent_level) + (present && !is_last)
1194                // ⟹ entry_owner.is_node().
1195                assert(entry_owner.is_node());
1196                // ChildRef::invariants(entry_owner, regions) gives child.wf(entry_owner).
1197                // For the Frame and None variants, wf requires is_frame() or is_absent(),
1198                // contradicting is_node(). Hence child must be PageTable.
1199                assert(child is PageTable);
1200                // to_ref's borrow_paddr preserves slot_owners exactly and only
1201                // grows `slots` (existing keys preserved). Use the tree-wide
1202                // preservation lemma.
1203                kernel_owner.metaregion_sound_preserved_slot_owners_eq(
1204                    pre_to_ref_regions,
1205                    *regions,
1206                );
1207            }
1208            let pt = match child {
1209                ChildRef::PageTable(pt) => pt,
1210                _ => vstd::pervasive::unreached(),
1211            };
1212
1213            let ghost entry_node_slot_idx = entry_owner.tracked_borrow_node().slot_index;
1214            let tracked entry_node_slot_perm = regions.slots.tracked_borrow(entry_node_slot_idx);
1215            #[verus_spec(with Tracked(entry_node_slot_perm))]
1216            let pt_addr = pt.start_paddr();
1217            let pte = PageTableEntry::new_pt(pt_addr);
1218
1219            proof {
1220                assert(regions.contains(new_node_owner.slot_index));
1221            }
1222            unsafe {
1223                #[verus_spec(with Tracked(&mut new_node_owner), Tracked(&*regions))]
1224                new_node.write_pte(i, pte)
1225            };
1226
1227            i = i + 1;
1228        }
1229
1230        PageTable::<UserPtConfig> { root: new_root }
1231    }/*
1232    /// Protect the given virtual address range in the kernel page table.
1233    ///
1234    /// This method flushes the TLB entries when doing protection.
1235    ///
1236    /// # Safety
1237    ///
1238    /// The caller must ensure that the protection operation does not affect
1239    /// the memory safety of the kernel.
1240    pub unsafe fn protect_flush_tlb(
1241        &self,
1242        vaddr: &Range<Vaddr>,
1243        mut op: impl FnMut(&mut PageProperty),
1244    ) -> Result<(), PageTableError> {
1245        let preempt_guard = disable_preempt();
1246        let mut cursor = CursorMut::new(self, &preempt_guard, vaddr)?;
1247        // SAFETY: The safety is upheld by the caller.
1248        while let Some(range) =
1249            unsafe { cursor.protect_next(vaddr.end - cursor.virt_addr(), &mut op) }
1250        {
1251            crate::arch::mm::tlb_flush_addr(range.start);
1252        }
1253        Ok(())
1254    }*/
1255
1256}
1257
1258#[verus_verify]
1259impl<C: PageTableConfig> PageTable<C> {
1260    /// Relates this executable page-table handle to its tracked ownership tree.
1261    pub open spec fn relates_owner(
1262        &self,
1263        owner: PageTableOwner<C>,
1264        regions: MetaRegionOwners,
1265    ) -> bool {
1266        &&& owner.inv()
1267        &&& self.root.ptr.addr() == owner.0.value().node().meta_vaddr()
1268        &&& owner.metaregion_sound(regions)
1269    }
1270
1271    /// Create a new empty page table.
1272    ///
1273    /// Useful for the IOMMU page tables only.
1274    #[verifier::external_body]
1275    pub fn empty() -> Self {
1276        unimplemented!()
1277    }
1278
1279    /// Create a new empty page table together with its tracked ownership.
1280    #[verifier::external_body]
1281    #[verus_spec(r =>
1282        with Tracked(owner): Tracked<&mut Option<PageTableOwner<C>>>,
1283            Tracked(regions): Tracked<&mut MetaRegionOwners>,
1284            Tracked(guards): Tracked<&mut Guards<'rcu>>,
1285        requires
1286            old(regions).inv(),
1287        ensures
1288            final(owner)@ is Some,
1289            final(owner)@->0.inv(),
1290            (final(owner)@->0).0.value().is_node(),
1291            (final(owner)@->0).0.value().is_node(),
1292            r.root.ptr.addr() == (final(owner)@->0).0.value().node().meta_vaddr(),
1293            (final(owner)@->0).0.value().metaregion_sound(*final(regions)),
1294            final(regions).inv(),
1295            final(guards).unlocked((final(owner)@->0).0.value().node().meta_vaddr()),
1296            // Allocating a fresh node does not change the lock set, so any node
1297            // that was (un)locked before remains so.
1298            final(guards).guards == old(guards).guards,
1299            // The newly allocated slot was in the free pool before the call.
1300            old(regions).contains(
1301                crate::specs::mm::frame::mapping::frame_to_index(
1302                    (final(owner)@->0).0.value().meta_slot_paddr()->0)),
1303            // After the alloc, the slot is removed from the free pool (now owned
1304            // by the new pt's NodeOwner).
1305            !final(regions).contains(
1306                crate::specs::mm::frame::mapping::frame_to_index(
1307                    (final(owner)@->0).0.value().meta_slot_paddr()->0)),
1308            // Other slots and lock state are preserved.
1309            forall |i: int| #![trigger final(regions).slot_owners[i]]
1310                i != crate::specs::mm::frame::mapping::frame_to_index(
1311                    (final(owner)@->0).0.value().meta_slot_paddr()->0)
1312                ==> final(regions).slot_owners[i] == old(regions).slot_owners[i],
1313            forall |a: usize| old(guards).lock_held(a) ==> final(guards).lock_held(a),
1314            forall |idx: int| #![trigger final(regions).slot_owners[idx].paths_in_pt]
1315                final(regions).slot_owners[idx].paths_in_pt
1316                    == old(regions).slot_owners[idx].paths_in_pt,
1317            // Allocation preserves the soundness of the kernel page-table tree:
1318            // a fresh allocation cannot collide with any active node or frame entry
1319            // (the allocator returns a slot that wasn't in use). Stated as a
1320            // postcondition because deriving it requires a freshness axiom on the
1321            // underlying frame allocator.
1322            forall |kt: PageTableOwner<KernelPtConfig>|
1323                #![trigger kt.metaregion_sound(*final(regions))]
1324                kt.inv() && kt.metaregion_sound(*old(regions))
1325                ==> kt.metaregion_sound(*final(regions)),
1326            // Freshness: the new PT's slot index is not used (as a primary slot
1327            // or huge-frame sub-page slot) by any entry in any KernelPtConfig PT
1328            // tree that was sound before the alloc. Used to discharge the borrow
1329            // step that mutates `slot_owners[new_idx]`.
1330            forall |kt: PageTableOwner<KernelPtConfig>|
1331                #![trigger kt.metaregion_sound(*old(regions))]
1332                kt.inv() && kt.metaregion_sound(*old(regions)) ==>
1333                kt.0.subtree_satisfies(
1334                    kt.0.value().path,
1335                    |e: crate::specs::mm::page_table::node::entry_owners::EntryOwner<KernelPtConfig>,
1336                     p: vstd_extra::ghost_tree::TreePath<NR_ENTRIES>|
1337                        e.meta_slot_paddr() is Some
1338                            ==> crate::specs::mm::frame::mapping::frame_to_index(
1339                                e.meta_slot_paddr()->0) !=
1340                                crate::specs::mm::frame::mapping::frame_to_index(
1341                                    (final(owner)@->0).0.value().meta_slot_paddr()->0),
1342                ),
1343            // Sub-page freshness: for any huge frame entry in any pre-existing
1344            // sound KernelPtConfig tree, the new PT's slot index isn't a sub-page
1345            // slot of the huge frame either. Same allocator-freshness rationale.
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.is_frame() && e.parent_level > 1 ==> {
1354                            let pa = e.frame().mapped_pa;
1355                            let nr_pages = page_size(
1356                                e.parent_level) / PAGE_SIZE;
1357                            forall |j: usize| 0 < j < nr_pages ==> {
1358                                let sub_idx =
1359                                    #[trigger] crate::specs::mm::frame::mapping::frame_to_index(
1360                                        (pa + j * PAGE_SIZE) as usize);
1361                                sub_idx != crate::specs::mm::frame::mapping::frame_to_index(
1362                                    (final(owner)@->0).0.value().meta_slot_paddr()->0)
1363                            }
1364                        },
1365                ),
1366    )]
1367    pub fn empty_with_owner<'rcu>() -> Self {
1368        unimplemented!()
1369    }
1370
1371    #[verifier::external_body]
1372    pub(in crate::mm) unsafe fn first_activate_unchecked(&self) {
1373        unimplemented!()
1374        // SAFETY: The safety is upheld by the caller.
1375        //        unsafe { self.root.first_activate() };
1376
1377    }
1378
1379    pub uninterp spec fn root_paddr_spec(&self) -> Paddr;
1380
1381    /// The physical address of the root page table.
1382    ///
1383    /// Obtaining the physical address of the root page table is safe, however, using it or
1384    /// providing it to the hardware will be unsafe since the page table node may be dropped,
1385    /// resulting in UAF.
1386    #[verifier::external_body]
1387    #[verifier::when_used_as_spec(root_paddr_spec)]
1388    pub fn root_paddr(&self) -> (r: Paddr)
1389        returns
1390            self.root_paddr_spec(),
1391    {
1392        unimplemented!()
1393        //        self.root.start_paddr()
1394
1395    }
1396
1397    /// Query about the mapping of a single byte at the given virtual address.
1398    ///
1399    /// Note that this function may fail reflect an accurate result if there are
1400    /// cursors concurrently accessing the same virtual address range, just like what
1401    /// happens for the hardware MMU walk.
1402    #[cfg(ktest)]
1403    pub fn page_walk(&self, vaddr: Vaddr) -> Option<(Paddr, PageProperty)> {
1404        // SAFETY: The root node is a valid page table node so the address is valid.
1405        unsafe { page_walk::<C>(self.root_paddr(), vaddr) }
1406    }
1407
1408    /// Create a new cursor exclusively accessing the virtual address range for mapping.
1409    ///
1410    /// If another cursor is already accessing the range, the new cursor may wait until the
1411    /// previous cursor is dropped.
1412    #[verus_spec(r =>
1413        with Tracked(owner): Tracked<PageTableOwner<C>>,
1414            Ghost(root_guard): Ghost<PageTableGuard<'rcu, C>>,
1415            Tracked(regions): Tracked<&mut MetaRegionOwners>,
1416            Tracked(guards): Tracked<&mut Guards<'rcu>>
1417        requires
1418            self.relates_owner(owner, *old(regions)),
1419            owner.0.value().node().relate_guard(root_guard),
1420            // Per-config tightening; see `Cursor::new`.
1421            0 < va.end <= C::LOCKED_END_BOUND_spec(),
1422        ensures
1423            Cursor::<C, G>::cursor_new_success_conditions(*va) ==> {
1424                &&& r is Ok
1425                &&& r.unwrap().0.0.invariants(*r.unwrap().1, *final(regions), *final(guards))
1426                &&& r.unwrap().1.in_locked_range()
1427                &&& r.unwrap().0.0.level == r.unwrap().0.0.guard_level
1428                &&& r.unwrap().0.0.guard_level == NR_LEVELS as PagingLevel
1429                &&& r.unwrap().0.0.va < r.unwrap().0.0.barrier_va.end
1430                &&& r.unwrap().0.0.va == va.start
1431                &&& r.unwrap().0.0.barrier_va == *va
1432            },
1433            !Cursor::<C, G>::cursor_new_success_conditions(*va) ==> r is Err,
1434            forall |item: C::Item| #![trigger CursorMut::<'rcu, C, G>::item_not_mapped(item, *old(regions))]
1435                CursorMut::<'rcu, C, G>::item_not_mapped(item, *old(regions)) ==>
1436                CursorMut::<'rcu, C, G>::item_not_mapped(item, *final(regions)),
1437            // CursorMut::new inherits Cursor::new's weakened preservation:
1438            // PT-node allocations come from UNUSED slots, so any slot that
1439            // was already in use keeps its paths_in_pt.
1440            forall |idx: int| #![trigger final(regions).slot_owners[idx].paths_in_pt]
1441                old(regions).slot_owners[idx].ref_count()
1442                    != REF_COUNT_UNUSED
1443                ==> final(regions).slot_owners[idx].paths_in_pt
1444                        == old(regions).slot_owners[idx].paths_in_pt,
1445            forall|idx: int| #![trigger final(regions).slot_owners[idx]]
1446                old(regions).contains(idx)
1447                && old(regions).slot_owners[idx].ref_count()
1448                    != REF_COUNT_UNUSED
1449                ==> final(regions).slot_owners[idx].ref_count()
1450                        == old(regions).slot_owners[idx].ref_count()
1451                    && final(regions).slot_owners[idx].usage
1452                        == old(regions).slot_owners[idx].usage,
1453    )]
1454    pub fn cursor_mut<'rcu, G: InAtomicMode>(
1455        &'rcu self,
1456        guard: &'rcu G,
1457        va: &Range<Vaddr>,
1458    ) -> Result<(CursorMut<'rcu, C, G>, Tracked<CursorOwner<'rcu, C>>), PageTableError> {
1459        #[verus_spec(with Tracked(owner), Ghost(root_guard), Tracked(regions), Tracked(guards))]
1460        CursorMut::new(self, guard, va)
1461    }
1462
1463    /// Create a new cursor exclusively accessing the virtual address range for querying.
1464    ///
1465    /// If another cursor is already accessing the range, the new cursor may wait until the
1466    /// previous cursor is dropped. The modification to the mapping by the cursor may also
1467    /// block or be overridden by the mapping of another cursor.
1468    #[verus_spec(r =>
1469        with Tracked(owner): Tracked<PageTableOwner<C>>,
1470            Ghost(root_guard): Ghost<PageTableGuard<'rcu, C>>,
1471            Tracked(regions): Tracked<&mut MetaRegionOwners>,
1472            Tracked(guards): Tracked<&mut Guards<'rcu>>
1473        requires
1474            self.relates_owner(owner, *old(regions)),
1475            owner.0.value().node().relate_guard(root_guard),
1476            // Per-config tightening; see `Cursor::new`.
1477            0 < va.end <= C::LOCKED_END_BOUND_spec(),
1478        ensures
1479            Cursor::<C, G>::cursor_new_success_conditions(*va) ==> {
1480                &&& r is Ok
1481                &&& r.unwrap().0.invariants(*r.unwrap().1, *final(regions), *final(guards))
1482                &&& r.unwrap().1.in_locked_range()
1483                &&& r.unwrap().0.level == r.unwrap().0.guard_level
1484                &&& r.unwrap().0.va < r.unwrap().0.barrier_va.end
1485                &&& r.unwrap().0.va == va.start
1486                &&& r.unwrap().0.barrier_va == *va
1487                &&& r.unwrap().1@.as_page_table_owner() == owner
1488                &&& r.unwrap().1@.continuations[3].path() == owner.0.value().path
1489            },
1490            !Cursor::<C, G>::cursor_new_success_conditions(*va) ==> r is Err,
1491            forall|idx: int| #![trigger final(regions).slot_owners[idx].paths_in_pt]
1492                old(regions).slot_owners[idx].ref_count()
1493                    != REF_COUNT_UNUSED
1494                ==> final(regions).slot_owners[idx].paths_in_pt
1495                        == old(regions).slot_owners[idx].paths_in_pt,
1496            // Non-saturation preservation.
1497            (forall |i: int| #![trigger old(regions).slot_owners[i]]
1498                old(regions).contains(i)
1499                && old(regions).slot_owners[i].ref_count()
1500                    != REF_COUNT_UNUSED
1501                ==> old(regions).slot_owners[i].ref_count() + 1
1502                    < REF_COUNT_MAX)
1503            ==>
1504            (forall |i: int| #![trigger final(regions).slot_owners[i]]
1505                final(regions).contains(i)
1506                && final(regions).slot_owners[i].ref_count()
1507                    != REF_COUNT_UNUSED
1508                ==> final(regions).slot_owners[i].ref_count() + 1
1509                    < REF_COUNT_MAX),
1510            // Saturated-slot bridge (relayed from `Cursor::new`):
1511            // a slot at `>= REF_COUNT_MAX` before iff after, with the same
1512            // value. Used by `KVirtArea::query` to bridge inner-cursor
1513            // saturation back to the caller's snapshot.
1514            forall|idx: int| #![trigger final(regions).slot_owners[idx].ref_count()]
1515                final(regions).slot_owners[idx].ref_count()
1516                    >= REF_COUNT_MAX
1517                ==> old(regions).slot_owners[idx].ref_count()
1518                        == final(regions).slot_owners[idx].ref_count(),
1519            forall|idx: int| #![trigger old(regions).slot_owners[idx].ref_count()]
1520                old(regions).slot_owners[idx].ref_count()
1521                    >= REF_COUNT_MAX
1522                ==> final(regions).slot_owners[idx].ref_count()
1523                        == old(regions).slot_owners[idx].ref_count(),
1524    )]
1525    pub fn cursor<'rcu, G: InAtomicMode>(&'rcu self, guard: &'rcu G, va: &Range<Vaddr>) -> Result<
1526        (Cursor<'rcu, C, G>, Tracked<CursorOwner<'rcu, C>>),
1527        PageTableError,
1528    > {
1529        #[verus_spec(with Tracked(owner), Ghost(root_guard), Tracked(regions), Tracked(guards))]
1530        Cursor::new(self, guard, va)
1531    }/*
1532    /// Create a new reference to the same page table.
1533    /// The caller must ensure that the kernel page table is not copied.
1534    /// This is only useful for IOMMU page tables. Think twice before using it in other cases.
1535    pub unsafe fn shallow_copy(&self) -> Self {
1536        PageTable {
1537            root: self.root.clone(),
1538        }
1539    }
1540    */
1541
1542}
1543
1544/// A software emulation of the MMU address translation process.
1545///
1546/// This method returns the physical address of the given virtual address and
1547/// the page property if a valid mapping exists for the given virtual address.
1548///
1549/// # Safety
1550///
1551/// The caller must ensure that the `root_paddr` is a pointer to a valid root
1552/// page table node.
1553///
1554/// # Notes on the page table use-after-free problem
1555///
1556/// Neither the hardware MMU nor the software page walk method acquires the page
1557/// table locks while reading. They can enter a to-be-recycled page table node
1558/// and read the page table entries after the node is recycled and reused.
1559///
1560/// For the hardware MMU page walk, we mitigate this problem by dropping the page
1561/// table nodes only after the TLBs have been flushed on all the CPUs that
1562/// activate the page table.
1563///
1564/// For the software page walk, we only need to disable preemption at the beginning
1565/// since the page table nodes won't be recycled in the RCU critical section.
1566#[cfg(ktest)]
1567pub(super) unsafe fn page_walk<C: PageTableConfig>(root_paddr: Paddr, vaddr: Vaddr) -> Option<
1568    (Paddr, PageProperty),
1569> {
1570    use super::paddr_to_vaddr;
1571
1572    let _rcu_guard = disable_preempt();
1573
1574    let mut pt_addr = paddr_to_vaddr(root_paddr);
1575    #[verusfmt::skip]
1576    for cur_level in (1..= C::NR_LEVELS()).rev() {
1577        let offset = pte_index::<C>(vaddr, cur_level);
1578        // SAFETY:
1579        //  - The page table node is alive because (1) the root node is alive and
1580        //    (2) all child nodes cannot be recycled because we're in the RCU critical section.
1581        //  - The index is inside the bound, so the page table entry is valid.
1582        //  - All page table entries are aligned and accessed with atomic operations only.
1583        let cur_pte = unsafe { load_pte((pt_addr as *mut C::E).add(offset), Ordering::Acquire) };
1584
1585        if !cur_pte.is_present() {
1586            return None;
1587        }
1588        if cur_pte.is_last(cur_level) {
1589            debug_assert!(cur_level <= C::HIGHEST_TRANSLATION_LEVEL);
1590            return Some(
1591                (cur_pte.paddr() + (vaddr & (page_size::<C>(cur_level) - 1)), cur_pte.prop()),
1592            );
1593        }
1594        pt_addr = paddr_to_vaddr(cur_pte.paddr());
1595    }
1596
1597    unreachable!("All present PTEs at the level 1 must be last-level PTEs");
1598}
1599
1600/// A trait that abstracts architecture-specific page table entries (PTEs).
1601///
1602/// Note that a default PTE should be a PTE that points to nothing.
1603pub trait PageTableEntryTrait:
1604    Clone + Copy + Debug + Default + Sized + Pod + PodOnce + Send + Sync + 'static {
1605    spec fn new_absent_spec() -> Self;
1606
1607    /// Create a set of new invalid page table flags that indicates an absent page.
1608    ///
1609    /// Note that currently the implementation requires an all zero PTE to be an absent PTE.
1610    #[verifier(when_used_as_spec(new_absent_spec))]
1611    fn new_absent() -> (res: Self)
1612        ensures
1613            valid_frame_paddr(res.paddr()),
1614            !res.is_present(),
1615        returns
1616            Self::new_absent(),
1617    ;
1618
1619    spec fn is_present_spec(&self) -> bool;
1620
1621    /// Returns if the PTE points to something.
1622    ///
1623    /// For PTEs created by [`Self::new_absent`], this method should return
1624    /// false. For PTEs created by [`Self::new_page`] or [`Self::new_pt`]
1625    /// and modified with [`Self::set_prop`], this method should return true.
1626    #[verifier::when_used_as_spec(is_present_spec)]
1627    fn is_present(&self) -> bool
1628        returns
1629            self.is_present_spec(),
1630    ;
1631
1632    spec fn new_page_spec(paddr: Paddr, level: PagingLevel, prop: PageProperty) -> Self;
1633
1634    /// The preconditions for creating a new page-mapping PTE.
1635    spec fn new_page_req(paddr: Paddr, level: PagingLevel, prop: PageProperty) -> bool;
1636
1637    /// Creates a new PTE that maps to a page.
1638    #[verifier::when_used_as_spec(new_page_spec)]
1639    fn new_page(paddr: Paddr, level: PagingLevel, prop: PageProperty) -> (res: Self)
1640        requires
1641            paddr < MAX_PADDR,
1642            Self::new_page_req(paddr, level, prop),
1643        ensures
1644            res.paddr() == paddr & !((PAGE_SIZE - 1) as usize),
1645            paddr % PAGE_SIZE == 0 ==> res.paddr() == paddr,
1646            valid_frame_paddr(res.paddr()),
1647            res.is_present(),
1648            res.is_last(level),
1649            res.prop() == prop,
1650        returns
1651            Self::new_page(paddr, level, prop),
1652    ;
1653
1654    spec fn new_pt_spec(paddr: Paddr) -> Self;
1655
1656    /// Create a new PTE that map to a child page table.
1657    #[verifier::when_used_as_spec(new_pt_spec)]
1658    fn new_pt(paddr: Paddr) -> (res: Self)
1659        requires
1660            paddr < MAX_PADDR,
1661        ensures
1662            res.paddr() == paddr & !((PAGE_SIZE - 1) as usize),
1663            paddr % PAGE_SIZE == 0 ==> res.paddr() == paddr,
1664            valid_frame_paddr(res.paddr()),
1665            res.is_present(),
1666            forall|level: PagingLevel| !res.is_last(level),
1667        returns
1668            Self::new_pt(paddr),
1669    ;
1670
1671    /// Returns the physical address from the PTE.
1672    ///
1673    /// The physical address recorded in the PTE is either:
1674    /// - the physical address of the next-level page table, or
1675    /// - the physical address of the page that the PTE maps to.
1676    spec fn paddr_spec(&self) -> Paddr;
1677
1678    #[verifier::when_used_as_spec(paddr_spec)]
1679    fn paddr(&self) -> (res: Paddr)
1680        ensures
1681            valid_frame_paddr(res),
1682        returns
1683            self.paddr(),
1684    ;
1685
1686    spec fn prop_spec(&self) -> PageProperty;
1687
1688    #[verifier::when_used_as_spec(prop_spec)]
1689    fn prop(&self) -> PageProperty
1690        returns
1691            self.prop(),
1692    ;
1693
1694    /// The preconditions for setting the page property of a PTE.
1695    spec fn set_prop_req(self, prop: PageProperty) -> bool;
1696
1697    fn set_prop(&mut self, prop: PageProperty)
1698        requires
1699            old(self).set_prop_req(prop),
1700        ensures
1701            !old(self).is_present() ==> *old(self) == *final(self),
1702            old(self).is_present() ==> {
1703                &&& final(self).prop() == prop
1704                &&& final(self).paddr() == old(self).paddr()
1705                &&& final(self).is_present()
1706                &&& forall|level: PagingLevel|
1707                    #![trigger old(self).is_last(level)]
1708                    old(self).is_last(level) ==> final(self).is_last(level)
1709            },
1710    ;
1711
1712    spec fn is_last_spec(&self, level: PagingLevel) -> bool;
1713
1714    /// Returns if the PTE maps a page rather than a child page table.
1715    ///
1716    /// The method needs to know the level of the page table where the PTE resides,
1717    /// since architectures like x86-64 have a huge bit only in intermediate levels.
1718    #[verifier::when_used_as_spec(is_last_spec)]
1719    fn is_last(&self, level: PagingLevel) -> bool
1720        returns
1721            self.is_last_spec(level),
1722    ;
1723
1724    spec fn as_usize_spec(self) -> usize;
1725
1726    /// Converts the PTE into a raw `usize` value.
1727    #[verifier::external_body]
1728    #[verifier::when_used_as_spec(as_usize_spec)]
1729    fn as_usize(self) -> usize
1730        returns
1731            self.as_usize(),
1732    {
1733        unimplemented!()
1734        // const { assert!(size_of::<Self>() == size_of::<usize>()) };
1735        // SAFETY: `Self` is `Pod` and has the same memory representation as `usize`.
1736        // unsafe { transmute_unchecked(self) }
1737
1738    }
1739
1740    /// Converts the raw `usize` value into a PTE.
1741    #[verifier::external_body]
1742    fn from_usize(pte_raw: usize) -> Self {
1743        unimplemented!()
1744        // const { assert!(size_of::<Self>() == size_of::<usize>()) };
1745        // SAFETY: `Self` is `Pod` and has the same memory representation as `usize`.
1746        // unsafe { transmute_unchecked(pte_raw) }
1747
1748    }
1749
1750    /// Absent (zero) PTE has well-formed paddr for match_pte.
1751    proof fn lemma_page_table_entry_properties()
1752        ensures
1753            core::mem::size_of::<Self>() == core::mem::size_of::<usize>(),
1754            core::mem::size_of::<Self>() % core::mem::align_of::<Self>() == 0,
1755            core::mem::align_of::<Self>() > 0,
1756            valid_frame_paddr(Self::new_absent().paddr()),
1757            !Self::new_absent().is_present(),
1758            forall|level: PagingLevel|
1759                #![trigger Self::new_absent().is_last(level)]
1760                1 < level ==> !Self::new_absent().is_last(level),
1761            forall|paddr: Paddr, level: PagingLevel, prop: PageProperty|
1762                #![trigger Self::new_page(paddr, level, prop)]
1763                Self::new_page_req(paddr, level, prop) && (prop.cache is Writeback
1764                    || prop.cache is Writethrough || prop.cache is Uncacheable) ==> {
1765                    &&& Self::new_page(paddr, level, prop).is_present()
1766                    &&& (paddr < MAX_PADDR ==> Self::new_page(paddr, level, prop).paddr() == paddr
1767                        & !((PAGE_SIZE - 1) as usize))
1768                    &&& (paddr < MAX_PADDR && paddr % PAGE_SIZE == 0 ==> Self::new_page(
1769                        paddr,
1770                        level,
1771                        prop,
1772                    ).paddr() == paddr)
1773                    &&& Self::new_page(paddr, level, prop).prop() == prop
1774                    &&& Self::new_page(paddr, level, prop).is_last(level)
1775                },
1776            forall|paddr: Paddr|
1777                #![trigger Self::new_pt(paddr)]
1778                {
1779                    &&& Self::new_pt(paddr).is_present()
1780                    &&& (paddr < MAX_PADDR ==> Self::new_pt(paddr).paddr() == paddr & !((PAGE_SIZE
1781                        - 1) as usize))
1782                    &&& (paddr < MAX_PADDR && paddr % PAGE_SIZE == 0 ==> Self::new_pt(paddr).paddr()
1783                        == paddr)
1784                    &&& forall|level: PagingLevel| !Self::new_pt(paddr).is_last(level)
1785                },
1786    ;
1787
1788    proof fn lemma_paddr_is_page_aligned(self)
1789        ensures
1790            self.paddr() % PAGE_SIZE == 0,
1791    ;
1792}
1793
1794/// Loads a page table entry with an atomic instruction.
1795///
1796/// # Verification Design
1797/// ## Preconditions
1798/// - The pointer must be a valid pointer to the array that represents the page table node.
1799/// - The array must be initialized at the target index.
1800/// ## Postconditions
1801/// - The value is loaded from the array at the given index.
1802/// ## Safety
1803/// - We require the caller to provide a permission token to ensure that this function is only called on a valid array
1804/// and the pointer is in bounds.
1805/// - Like an `AtomicUsize::load` in normal Rust, this function assumes that the value being loaded is an integer
1806/// (and therefore can be safely cloned). We model the PTE as an abstract type, but in all actual implementations it is an
1807/// integer. Importantly, it does not include any data that is unsafe to duplicate.
1808#[verifier::external_body]
1809#[verus_spec(
1810    with Tracked(perm): Tracked<&vstd_extra::array_ptr::PointsTo<E, NR_ENTRIES>>
1811    requires
1812        perm.is_init(ptr.index as int),
1813        perm.addr() == ptr.addr(),
1814        0 <= ptr.index < NR_ENTRIES,
1815    returns
1816        perm.value()[ptr.index as int],
1817)]
1818pub unsafe fn load_pte<E: PageTableEntryTrait>(
1819    ptr: vstd_extra::array_ptr::ArrayPtr<E, NR_ENTRIES>,
1820    ordering: Ordering,
1821) -> (pte: E) {
1822    unimplemented!()
1823}
1824
1825/// Stores a page table entry with an atomic instruction.
1826///
1827/// # Verification Design
1828/// We axiomatize this function as a store operation in the array that represents the page table node.
1829/// ## Preconditions
1830/// - The pointer must be a valid pointer to the array that represents the page table node.
1831/// - The array must be initialized so that the verifier knows that it remains initialized after the store.
1832/// ## Postconditions
1833/// - The new value is stored in the array at the given index.
1834/// ## Safety
1835/// - We require the caller to provide a permission token to ensure that this function is only called on a valid array
1836/// and the pointer is in bounds.
1837#[verifier::external_body]
1838#[verus_spec(
1839    with Tracked(perm): Tracked<&mut vstd_extra::array_ptr::PointsTo<E, NR_ENTRIES>>
1840    requires
1841        old(perm).addr() == ptr.addr(),
1842        0 <= ptr.index < NR_ENTRIES,
1843        old(perm).is_init_all(),
1844    ensures
1845        final(perm).wf(),
1846        final(perm).value()[ptr.index as int] == new_val,
1847        final(perm).value() == old(perm).value().update(ptr.index as int, new_val),
1848        final(perm).addr() == old(perm).addr(),
1849        final(perm).is_init_all(),
1850)]
1851pub unsafe fn store_pte<E: PageTableEntryTrait>(
1852    ptr: vstd_extra::array_ptr::ArrayPtr<E, NR_ENTRIES>,
1853    new_val: E,
1854    ordering: Ordering,
1855);
1856
1857} // verus!