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_owners::MetaPerm, 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::{AtomicUsize, 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_owners[frame_to_index(pa)].inner_perms.ref_count.value()
373                    == old_regions.slot_owners[frame_to_index(pa)].inner_perms.ref_count.value() + 1
374                &&& new_regions.slot_owners[frame_to_index(pa)].inner_perms.ref_count.id()
375                    == old_regions.slot_owners[frame_to_index(pa)].inner_perms.ref_count.id()
376                &&& new_regions.slot_owners[frame_to_index(pa)].inner_perms.storage
377                    == old_regions.slot_owners[frame_to_index(pa)].inner_perms.storage
378                &&& new_regions.slot_owners[frame_to_index(pa)].inner_perms.vtable_ptr
379                    == old_regions.slot_owners[frame_to_index(pa)].inner_perms.vtable_ptr
380                &&& new_regions.slot_owners[frame_to_index(pa)].inner_perms.in_list
381                    == old_regions.slot_owners[frame_to_index(pa)].inner_perms.in_list
382                &&& new_regions.slot_owners[frame_to_index(pa)].paths_in_pt
383                    == old_regions.slot_owners[frame_to_index(pa)].paths_in_pt
384                &&& new_regions.slot_owners[frame_to_index(pa)].slot_vaddr
385                    == old_regions.slot_owners[frame_to_index(pa)].slot_vaddr
386                &&& new_regions.slot_owners[frame_to_index(pa)].usage
387                    == old_regions.slot_owners[frame_to_index(pa)].usage
388            },
389            !Self::tracked(item) ==> new_regions.slot_owners[frame_to_index(pa)]
390                == old_regions.slot_owners[frame_to_index(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.slots.contains_key(frame_to_index(pa)),
416            regions.slot_owners.contains_key(frame_to_index(pa)),
417            Self::tracked(item) ==> regions.slot_owners[frame_to_index(
418                pa,
419            )].inner_perms.ref_count.value() > 0,
420            // `rc != UNUSED` is needed only for tracked frames (untracked clone is a no-op).
421            Self::tracked(item) ==> regions.slot_owners[frame_to_index(
422                pa,
423            )].inner_perms.ref_count.value() != REF_COUNT_UNUSED,
424            // Saturation aborts (Arc-style) via `inc_ref_count`'s diverging panic.
425            Self::tracked(item) ==> (regions.slot_owners[frame_to_index(
426                pa,
427            )].inner_perms.ref_count.value() < REF_COUNT_MAX || may_panic()),
428        ensures
429            item.clone_requires(regions),
430    ;
431
432    /// The requirements of the page table configuration constants so that the memory management system can work correctly.
433    ///
434    /// NOTE: The postcondition is designed to be minimal, to actually be used in proofs, call `lemma_page_table_config_constant_properties`
435    /// instead to get all the properties that are derived from the requirements.
436    ///
437    /// FIXME: General architecture support. Move properties only relevant to paging constants to `PagingConstsTrait`.
438    proof fn lemma_page_table_config_constant_requirements()
439        ensures
440            core::mem::size_of::<Self::E>() == Self::C::PTE_SIZE(),
441            Self::TOP_LEVEL_INDEX_RANGE().start < Self::TOP_LEVEL_INDEX_RANGE().end,
442            Self::TOP_LEVEL_INDEX_RANGE().end <= pow2(
443                (Self::C::ADDRESS_WIDTH() - pte_index_bit_offset_spec::<Self::C>(
444                    Self::C::NR_LEVELS(),
445                )) as nat,
446            ),
447            Self::TOP_LEVEL_INDEX_RANGE().end * pow2(
448                pte_index_bit_offset_spec::<Self::C>(Self::C::NR_LEVELS()) as nat,
449            ) <= usize::MAX,
450            Self::LEADING_BITS_spec() != 0usize ==> (Self::C::VA_SIGN_EXT() && ((
451            Self::TOP_LEVEL_INDEX_RANGE().start * pow2(
452                pte_index_bit_offset_spec::<Self::C>(Self::C::NR_LEVELS()) as nat,
453            )) / (pow2((Self::C::ADDRESS_WIDTH() - 1) as nat) as int)) % 2 == 1),
454            (Self::C::VA_SIGN_EXT() && (((Self::TOP_LEVEL_INDEX_RANGE().start * pow2(
455                pte_index_bit_offset_spec::<Self::C>(Self::C::NR_LEVELS()) as nat,
456            )) / (pow2((Self::C::ADDRESS_WIDTH() - 1) as nat) as int)) % 2 == 1)) ==> {
457                &&& Self::LEADING_BITS_spec() * 0x1_0000_0000_0000int == 0x1_0000_0000_0000_0000int
458                    - pow2(Self::C::ADDRESS_WIDTH() as nat)
459            },
460            Self::LEADING_BITS_spec() < 0x1_0000_usize,
461            // FIXME: This property does not hold in general, `ADDRESS_WIDTH` can be wider.
462            pow2(
463                (Self::C::ADDRESS_WIDTH() - pte_index_bit_offset_spec::<Self::C>(
464                    Self::C::NR_LEVELS(),
465                )) as nat,
466            ) == NR_ENTRIES,
467    ;
468
469    // The derived properties of the page table config constants.
470    ///
471    /// NOTE: Implementations of `PageTableConfig` do not need to implement this lemma, the proof is automatically inherited from the default implementation.
472    proof fn lemma_page_table_config_constant_properties()
473        ensures
474    // Derived properties.
475
476            Self::TOP_LEVEL_INDEX_RANGE().end <= NR_ENTRIES,
477            // Copied from the postcondition of `lemma_page_table_config_constant_requirements`
478            // so that we only need to call this lemma in proofs.
479            core::mem::size_of::<Self::E>() == Self::C::PTE_SIZE(),
480            Self::TOP_LEVEL_INDEX_RANGE().start < Self::TOP_LEVEL_INDEX_RANGE().end,
481            Self::TOP_LEVEL_INDEX_RANGE().end <= pow2(
482                (Self::C::ADDRESS_WIDTH() - pte_index_bit_offset_spec::<Self::C>(
483                    Self::C::NR_LEVELS(),
484                )) as nat,
485            ),
486            Self::TOP_LEVEL_INDEX_RANGE().end * pow2(
487                pte_index_bit_offset_spec::<Self::C>(Self::C::NR_LEVELS()) as nat,
488            ) <= usize::MAX,
489            Self::LEADING_BITS_spec() != 0usize ==> (Self::C::VA_SIGN_EXT() && ((
490            Self::TOP_LEVEL_INDEX_RANGE().start * pow2(
491                pte_index_bit_offset_spec::<Self::C>(Self::C::NR_LEVELS()) as nat,
492            )) / (pow2((Self::C::ADDRESS_WIDTH() - 1) as nat) as int)) % 2 == 1),
493            (Self::C::VA_SIGN_EXT() && (((Self::TOP_LEVEL_INDEX_RANGE().start * pow2(
494                pte_index_bit_offset_spec::<Self::C>(Self::C::NR_LEVELS()) as nat,
495            )) / (pow2((Self::C::ADDRESS_WIDTH() - 1) as nat) as int)) % 2 == 1)) ==> {
496                &&& Self::LEADING_BITS_spec() * 0x1_0000_0000_0000int == 0x1_0000_0000_0000_0000int
497                    - pow2(Self::C::ADDRESS_WIDTH() as nat)
498            },
499            Self::LEADING_BITS_spec() < 0x1_0000_usize,
500            // FIXME: This property does not hold in general, `ADDRESS_WIDTH` can be wider.
501            pow2(
502                (Self::C::ADDRESS_WIDTH() - pte_index_bit_offset_spec::<Self::C>(
503                    Self::C::NR_LEVELS(),
504                )) as nat,
505            ) == NR_ENTRIES,
506    {
507        Self::C::lemma_paging_consts_properties();
508        Self::lemma_page_table_config_constant_requirements();
509    }
510}
511
512// Implement it so that we can comfortably use low level functions
513// like `page_size::<C>` without typing `C::C` everywhere.
514impl<C: PageTableConfig> PagingConstsTrait for C {
515    open spec fn BASE_PAGE_SIZE_spec() -> usize {
516        C::C::BASE_PAGE_SIZE_spec()
517    }
518
519    fn BASE_PAGE_SIZE() -> usize {
520        C::C::BASE_PAGE_SIZE()
521    }
522
523    open spec fn NR_LEVELS_spec() -> PagingLevel {
524        C::C::NR_LEVELS_spec()
525    }
526
527    fn NR_LEVELS() -> PagingLevel {
528        proof {
529            assert(Self::NR_LEVELS() == C::C::NR_LEVELS());
530        }
531        C::C::NR_LEVELS()
532    }
533
534    open spec fn HIGHEST_TRANSLATION_LEVEL_spec() -> PagingLevel {
535        C::C::HIGHEST_TRANSLATION_LEVEL_spec()
536    }
537
538    fn HIGHEST_TRANSLATION_LEVEL() -> PagingLevel {
539        C::C::HIGHEST_TRANSLATION_LEVEL()
540    }
541
542    open spec fn PTE_SIZE_spec() -> usize {
543        C::C::PTE_SIZE_spec()
544    }
545
546    fn PTE_SIZE() -> usize {
547        C::C::PTE_SIZE()
548    }
549
550    open spec fn ADDRESS_WIDTH_spec() -> usize {
551        C::C::ADDRESS_WIDTH_spec()
552    }
553
554    fn ADDRESS_WIDTH() -> usize {
555        C::C::ADDRESS_WIDTH()
556    }
557
558    open spec fn VA_SIGN_EXT_spec() -> bool {
559        C::C::VA_SIGN_EXT_spec()
560    }
561
562    fn VA_SIGN_EXT() -> bool {
563        C::C::VA_SIGN_EXT()
564    }
565
566    proof fn lemma_paging_consts_requirements() {
567        C::C::lemma_paging_consts_requirements();
568    }
569}
570
571/// Splits the address range into largest page table items.
572///
573/// Each of the returned items is a tuple of the physical address and the
574/// paging level. It is helpful when you want to map a physical address range
575/// into the provided virtual address.
576///
577/// For example, on x86-64, `C: PageTableConfig` may specify level 1 page as
578/// 4KiB, level 2 page as 2MiB, and level 3 page as 1GiB. Suppose that the
579/// supplied physical address range is from `0x3fdff000` to `0x80002000`,
580/// and the virtual address is also `0x3fdff000`, the following 5 items will
581/// be returned:
582///
583/// ```text
584/// 0x3fdff000                                                 0x80002000
585/// start                                                             end
586///   |----|----------------|--------------------------------|----|----|
587///    4KiB      2MiB                       1GiB              4KiB 4KiB
588/// ```
589///
590/// # Panics
591///
592/// Panics if:
593///  - any of `va`, `pa`, or `len` is not aligned to the base page size;
594///  - the range `va..(va + len)` is not valid for the page table.
595#[verifier::external_body]
596pub fn largest_pages<C: PageTableConfig>(
597    mut va: Vaddr,
598    mut pa: Paddr,
599    mut len: usize,
600) -> impl Iterator<Item = (Paddr, PagingLevel)> {
601    assert_eq!(va % C::BASE_PAGE_SIZE(), 0);
602    assert_eq!(pa % C::BASE_PAGE_SIZE(), 0);
603    assert_eq!(len % C::BASE_PAGE_SIZE(), 0);
604    assert!(is_valid_range::<C>(&(va..(va + len))));
605
606    core::iter::from_fn(
607        move ||
608            {
609                if len == 0 {
610                    return None;
611                }
612                let mut level = C::HIGHEST_TRANSLATION_LEVEL();
613                while page_size(level) > len || va % page_size(level) != 0 || pa % page_size(level)
614                    != 0 {
615                    level -= 1;
616                }
617
618                let item_start = pa;
619                va += page_size(level);
620                pa += page_size(level);
621                len -= page_size(level);
622
623                Some((item_start, level))
624            },
625    )
626}
627
628/// Gets the top-level index width, in bits, for the page table.
629fn top_level_index_width<C: PageTableConfig>() -> (ret: usize)
630    returns
631        top_level_index_width_spec::<C>(),
632{
633    proof {
634        C::lemma_paging_consts_properties();
635        C::lemma_page_table_config_constant_properties();
636    }
637
638    C::ADDRESS_WIDTH() - pte_index_bit_offset::<C>(C::NR_LEVELS())
639}
640
641fn pt_va_range_start<C: PageTableConfig>() -> (ret: Vaddr)
642    ensures
643        ret == C::TOP_LEVEL_INDEX_RANGE().start * pow2(
644            pte_index_bit_offset_spec::<C>(C::NR_LEVELS()) as nat,
645        ),
646{
647    proof {
648        C::lemma_paging_consts_properties();
649        let ghost idx_start = C::TOP_LEVEL_INDEX_RANGE().start;
650        let ghost offset = pte_index_bit_offset_spec::<C>(C::NR_LEVELS());
651        crate::specs::mm::page_table::vaddr_range_proofs::lemma_pt_va_range_start_shift_facts::<C>(
652            idx_start,
653            offset,
654        );
655        vstd::bits::lemma_usize_shl_is_mul(idx_start, offset);
656    }
657
658    C::TOP_LEVEL_INDEX_RANGE().start << pte_index_bit_offset::<C>(C::NR_LEVELS())
659}
660
661/// Concrete positional end of the VA range (inclusive):
662/// `(idx_range.end * 2^offset) - 1`, stated modulo `2^64` to match
663/// the inclusive-end spec. The verified configs prove the pre-subtraction
664/// product fits in `usize`, so the executable path can use an ordinary
665/// left shift followed by `wrapping_sub(1)`.
666fn pt_va_range_end<C: PageTableConfig>() -> (ret: Vaddr)
667    ensures
668        ret == (C::TOP_LEVEL_INDEX_RANGE().end * pow2(
669            pte_index_bit_offset_spec::<C>(C::NR_LEVELS()) as nat,
670        ) - 1) % 0x1_0000_0000_0000_0000int,
671{
672    let idx_end = C::TOP_LEVEL_INDEX_RANGE().end;
673    proof {
674        C::lemma_paging_consts_properties();
675    }
676    let offset = pte_index_bit_offset::<C>(C::NR_LEVELS());
677
678    proof {
679        crate::specs::mm::page_table::vaddr_range_proofs::lemma_pt_va_range_end_shift_facts::<C>(
680            idx_end,
681            offset,
682        );
683        vstd::bits::lemma_usize_shl_is_mul(idx_end, offset);
684    }
685
686    let shifted = idx_end << offset;
687    let ret = shifted.wrapping_sub(1);
688
689    proof {
690        assert(shifted == idx_end * pow2(offset as nat));
691        crate::specs::mm::page_table::vaddr_range_proofs::lemma_pt_va_range_end_wrapping_sub::<C>(
692            idx_end,
693            offset,
694            shifted,
695            ret,
696        );
697    }
698    ret
699}
700
701fn sign_bit_of_va<C: PageTableConfig>(va: Vaddr) -> (ret: bool)
702    ensures
703        ret == ((va as int / pow2((C::ADDRESS_WIDTH() - 1) as nat) as int) % 2 == 1),
704{
705    proof {
706        C::lemma_paging_consts_properties();
707        C::lemma_page_table_config_constant_properties();
708        vstd::bits::lemma_usize_shr_is_div(va, (C::ADDRESS_WIDTH() - 1) as usize);
709        vstd::bits::lemma_usize_low_bits_mask_is_mod(va >> (C::ADDRESS_WIDTH() - 1), 1);
710        vstd::bits::lemma_low_bits_mask_values();
711        vstd::arithmetic::power2::lemma2_to64();
712    }
713    (va >> (C::ADDRESS_WIDTH() - 1)) & 1 != 0
714}
715
716/// Apply the sign-extension OR to a positional value.
717///
718/// For any value `va` in `[0, 2^ADDRESS_WIDTH)`, the OR with
719/// `!0 ^ ((1 << ADDRESS_WIDTH) - 1)` is equivalent to adding
720/// `LEADING_BITS_spec() * 2^48`, because the mask's bits and `va`'s bits are
721/// disjoint.
722fn apply_sign_ext<C: PageTableConfig>(va: Vaddr) -> (ret: Vaddr)
723    requires
724        va < pow2(C::ADDRESS_WIDTH() as nat),
725        C::ADDRESS_WIDTH() < usize::BITS,
726        C::LEADING_BITS_spec() * 0x1_0000_0000_0000int == 0x1_0000_0000_0000_0000int - pow2(
727            C::ADDRESS_WIDTH() as nat,
728        ),
729    ensures
730        ret == va + C::LEADING_BITS_spec() * 0x1_0000_0000_0000int,
731{
732    let address_width = C::ADDRESS_WIDTH();
733    let low_bit = 1usize << address_width;
734    proof {
735        vstd::layout::unsigned_int_max_values();
736        vstd::bits::lemma_usize_pow2_no_overflow(address_width as nat);
737        vstd::bits::lemma_usize_shl_is_mul(1usize, address_width);
738    }
739    let low_mask = low_bit - 1;
740    let sign_ext_mask = !0 ^ low_mask;
741    let ret = va | sign_ext_mask;
742    proof {
743        assert(!0usize == 0xffff_ffff_ffff_ffffusize) by (compute_only);
744        assert(sign_ext_mask == usize::MAX - low_mask) by (bit_vector)
745            requires
746                sign_ext_mask == (!0usize ^ low_mask),
747                !0usize == 0xffff_ffff_ffff_ffffusize,
748                usize::MAX == 0xffff_ffff_ffff_ffffusize,
749        ;
750        assert(pow2(64) == 0x1_0000_0000_0000_0000nat) by {
751            vstd::arithmetic::power2::lemma2_to64();
752        };
753        assert(sign_ext_mask == 0x1_0000_0000_0000_0000int - pow2(address_width as nat));
754        assert(sign_ext_mask == C::LEADING_BITS_spec() * 0x1_0000_0000_0000int);
755
756        assert((va & sign_ext_mask) == 0usize) by (bit_vector)
757            requires
758                address_width < usize::BITS,
759                low_bit == 1usize << address_width,
760                low_mask == low_bit - 1,
761                sign_ext_mask == !0usize ^ low_mask,
762                va < low_bit,
763        ;
764        assert(ret == va + sign_ext_mask) by (bit_vector)
765            requires
766                ret == va | sign_ext_mask,
767                (va & sign_ext_mask) == 0usize,
768        ;
769    }
770    ret
771}
772
773/// Gets the managed virtual addresses range for the page table.
774///
775/// Returns a [`RangeInclusive`] because the end address, when the range
776/// reaches the top of the 64-bit address space (e.g. the canonical
777/// high-half kernel range ending at `usize::MAX`), would overflow the
778/// exclusive end of a [`Range<Vaddr>`].
779#[verusfmt::skip]
780fn vaddr_range<C: PageTableConfig>() -> (ret: RangeInclusive<Vaddr>)
781    returns
782        vaddr_range_spec::<C>(),
783{
784    let mut start = pt_va_range_start::<C>();
785    let mut end = pt_va_range_end::<C>();
786
787    proof {
788        C::lemma_paging_consts_properties();
789        C::lemma_page_table_config_constant_properties();
790        crate::specs::mm::page_table::vaddr_range_proofs::lemma_idx_times_pow2_bound::<C>(
791            start,
792            end,
793        );
794    }
795
796    if C::VA_SIGN_EXT() && sign_bit_of_va::<C>(pt_va_range_start::<C>()) {
797        start = apply_sign_ext::<C>(start);
798        end = apply_sign_ext::<C>(end);
799    }
800    start..=end
801}
802
803/// Checks if the given range is covered by the valid range of the page table.
804fn is_valid_range<C: PageTableConfig>(r: &Range<Vaddr>) -> bool
805    requires
806        r.end > 0,
807    returns
808        is_valid_range_spec::<C>(*r),
809{
810    let va_range = vaddr_range::<C>();
811    (r.start == 0 && r.end == 0) || (*va_range.start() <= r.start && r.end - 1 <= *va_range.end())
812}
813
814// Here are some const values that are determined by the paging constants.
815/// The number of virtual address bits used to index a PTE in a page.
816fn nr_pte_index_bits<C: PagingConstsTrait>() -> usize
817    returns
818        nr_pte_index_bits_spec::<C>(),
819{
820    proof {
821        C::lemma_paging_consts_properties();
822    }
823    nr_subpage_per_huge::<C>().ilog2() as usize
824}
825
826/// The index of a VA's PTE in a page table node at the given level.
827fn pte_index<C: PagingConstsTrait>(va: Vaddr, level: PagingLevel) -> (res: usize)
828    requires
829        1 <= level <= NR_LEVELS,
830    ensures
831        res == AbstractVaddr::from_vaddr(va).index[level - 1],
832{
833    proof {
834        let offset = pte_index_bit_offset_spec::<C>(level);
835        C::lemma_paging_consts_properties();
836        lemma_arch_specific_consts_properties::<C>();
837        assert(0 <= offset < usize::BITS) by (nonlinear_arith)
838            requires
839                1 <= level <= 4,
840                offset == 12 + 9 * (level - 1),
841        ;
842        lemma2_to64();
843        lemma2_to64_rest();
844        vstd::bits::lemma_usize_shr_is_div(va, pte_index_bit_offset_spec::<C>(level));
845        vstd::bits::lemma_low_bits_mask_values();
846        vstd::bits::lemma_usize_low_bits_mask_is_mod(
847            va >> pte_index_bit_offset_spec::<C>(level),
848            9,
849        );
850    }
851    (va >> pte_index_bit_offset::<C>(level)) & (nr_subpage_per_huge::<C>() - 1)
852}
853
854/// The bit offset of the entry offset part in a virtual address.
855///
856/// This function returns the bit offset of the least significant bit. Take
857/// x86-64 as an example, the `pte_index_bit_offset(2)` should return 21, which
858/// is 12 (the 4KiB in-page offset) plus 9 (index width in the level-1 table).
859fn pte_index_bit_offset<C: PagingConstsTrait>(level: PagingLevel) -> usize
860    requires
861        1 <= level <= NR_LEVELS,
862    returns
863        pte_index_bit_offset_spec::<C>(level),
864{
865    proof {
866        C::lemma_paging_consts_properties();
867        lemma_arch_specific_consts_properties::<C>();
868        assert(12 + 9 * (level - 1) <= 39) by (nonlinear_arith)
869            requires
870                1 <= level <= NR_LEVELS,
871                NR_LEVELS == 4,
872        ;
873    }
874    C::BASE_PAGE_SIZE().ilog2() as usize + nr_pte_index_bits::<C>() * (level as usize - 1)
875}
876
877/// A handle to a page table.
878/// A page table can track the lifetime of the mapped physical pages.
879pub struct PageTable<C: PageTableConfig> {
880    pub root: PageTableNode<C>,
881}
882
883/*
884impl PageTable<UserPtConfig> {
885    pub fn activate(&self) {
886        // SAFETY: The user mode page table is safe to activate since the kernel
887        // mappings are shared.
888        unsafe {
889            self.root.activate();
890        }
891    }
892}*/
893
894impl PageTable<KernelPtConfig> {
895    /// Create a new kernel page table.
896    #[verifier::external_body]
897    pub(crate) fn new_kernel_page_table() -> Self {
898        unimplemented!()/*        let kpt = Self::empty();
899
900        // Make shared the page tables mapped by the root table in the kernel space.
901        {
902        let preempt_guard = disable_preempt();
903        let mut root_node = kpt.root.borrow().lock(&preempt_guard);
904
905        for i in KernelPtConfig::TOP_LEVEL_INDEX_RANGE {
906            let mut root_entry = root_node.entry(i);
907            let _ = root_entry.alloc_if_none(&preempt_guard).unwrap();
908            }
909        }
910
911        kpt*/
912
913    }
914
915    /// Panic condition for [`Self::create_user_page_table`]:
916    /// Some kernel root entry at index `i` in `TOP_LEVEL_INDEX_RANGE` is
917    /// not a page table node (i.e., is absent or maps a huge frame).
918    pub open spec fn create_user_pt_panic_condition(root_owner: NodeOwner<KernelPtConfig>) -> bool {
919        exists|i: usize|
920            #![trigger root_owner.children_perm.value()[i as int]]
921            KernelPtConfig::TOP_LEVEL_INDEX_RANGE().start <= i
922                < KernelPtConfig::TOP_LEVEL_INDEX_RANGE().end && {
923                let pte = root_owner.children_perm.value()[i as int];
924                ||| !pte.is_present()
925                ||| pte.is_last(root_owner.level)
926            }
927    }
928
929    /// Create a new user page table.
930    ///
931    /// This should be the only way to create the user page table, that is to
932    /// duplicate the kernel page table with all the kernel mappings shared.
933    #[verus_spec(r =>
934        with Tracked(kernel_owner): Tracked<&PageTableOwner<KernelPtConfig>>,
935            Tracked(regions): Tracked<&mut MetaRegionOwners>,
936            Tracked(guards): Tracked<&mut Guards<'rcu>>,
937        requires
938            kernel_owner.inv(),
939            old(regions).inv(),
940            kernel_owner.0.value().is_node(),
941            !Self::create_user_pt_panic_condition(kernel_owner.0.value().node()),
942            // The kernel page table's root frame matches the tracked owner.
943            self.root.ptr.addr() == kernel_owner.0.value().node().meta_vaddr(),
944            // The kernel root entry is sound with respect to the meta regions.
945            kernel_owner.0.value().metaregion_sound(*old(regions)),
946            // The whole kernel page-table tree is sound: every entry's metaregion
947            // bookkeeping matches `old(regions)`. Needed to derive each child's
948            // soundness inside the loop body.
949            kernel_owner.metaregion_sound(*old(regions)),
950            // The kernel root is not currently locked.
951            old(guards).unlocked(kernel_owner.0.value().node().meta_vaddr()),
952        ensures
953            final(regions).inv(),
954    )]
955    pub(in crate::mm) fn create_user_page_table<'rcu, G: InAtomicMode + 'static>(
956        &'static self,
957    ) -> PageTable<UserPtConfig> {
958        let preempt_guard: &'rcu G = disable_preempt::<G>();
959
960        proof_decl! {
961            let tracked mut new_pt_owner: Option<PageTableOwner<UserPtConfig>> = None;
962        }
963        let ghost regions_before_alloc = *regions;
964        let new_pt: PageTable<UserPtConfig> = (
965        #[verus_spec(with Tracked(&mut new_pt_owner), Tracked(regions), Tracked(guards))]
966        PageTable::empty_with_owner());
967        let new_root = new_pt.root;
968        // Capture new_idx as a ghost BEFORE the tracked_take below empties new_pt_owner.
969        let ghost new_idx_g: int = crate::specs::mm::frame::mapping::frame_to_index(
970            new_pt_owner@.unwrap().0.value().meta_slot_paddr().unwrap(),
971        );
972        let ghost new_pt_owner_snap = new_pt_owner@.unwrap();
973        proof {
974            let kern_idx = crate::specs::mm::frame::mapping::frame_to_index(
975                kernel_owner.0.value().meta_slot_paddr().unwrap(),
976            );
977            let new_idx = new_idx_g;
978            crate::specs::mm::page_table::node::entry_owners::EntryOwner::<
979                KernelPtConfig,
980            >::lemma_active_entry_not_in_free_pool(
981                kernel_owner.0.value(),
982                regions_before_alloc,
983                new_idx,
984            );
985            assert(kern_idx != new_idx);
986            assert(regions.slot_owners[kern_idx] == regions_before_alloc.slot_owners[kern_idx]);
987            assert(kernel_owner.metaregion_sound(*regions));
988            assert(!regions.slots.contains_key(new_idx));
989        }
990
991        proof_decl! {
992            let tracked root_owner: &NodeOwner<KernelPtConfig>
993                = kernel_owner.0.tracked_borrow_value().tracked_borrow_node();
994            let tracked mut new_pt_owner_val: PageTableOwner<UserPtConfig>
995                = new_pt_owner.tracked_take();
996            let tracked mut new_node_owner: NodeOwner<UserPtConfig> = {
997                let tracked new_pt_value = new_pt_owner_val.0.tracked_borrow_mut_value();
998                new_pt_value.tracked_take_node()
999            };
1000            let tracked mut entry_owner: &EntryOwner<KernelPtConfig>;
1001        }
1002
1003        // Discharge borrow/lock preconditions for the kernel root from
1004        // kernel_owner.inv() + metaregion_sound + guards unlocked.
1005        proof {
1006            assert(kernel_owner.0.value().is_node());
1007            assert(kernel_owner.0.value().metaregion_sound(*regions));
1008        }
1009        let ghost regions_before_self_borrow: MetaRegionOwners = *regions;
1010        let mut root_node = {
1011            #[verus_spec(with Tracked(regions))]
1012            let root_ref = self.root.borrow();
1013            #[verus_spec(with Tracked(root_owner), Tracked(guards))]
1014            root_ref.lock(preempt_guard)
1015        };
1016        let ghost regions_after_kroot_borrow: MetaRegionOwners = *regions;
1017        let mut new_node: PageTableGuard<'rcu, UserPtConfig> = {
1018            #[verus_spec(with Tracked(regions))]
1019            let new_ref = new_root.borrow();
1020            #[verus_spec(with Tracked(&new_node_owner), Tracked(guards))]
1021            new_ref.lock(preempt_guard)
1022        };
1023        proof {
1024            let kern_idx = crate::specs::mm::frame::mapping::frame_to_index(
1025                kernel_owner.0.value().meta_slot_paddr().unwrap(),
1026            );
1027            assert(regions_before_self_borrow.slot_owners
1028                == regions_after_kroot_borrow.slot_owners);
1029            assert forall|k: int|
1030                regions_before_self_borrow.slots.contains_key(
1031                    k,
1032                ) implies regions_before_self_borrow.slots[k]
1033                == #[trigger] regions_after_kroot_borrow.slots[k] by {
1034                if k == kern_idx {
1035                    crate::specs::mm::page_table::node::entry_owners::EntryOwner::<
1036                        KernelPtConfig,
1037                    >::lemma_active_entry_not_in_free_pool(
1038                        kernel_owner.0.value(),
1039                        regions_before_self_borrow,
1040                        k,
1041                    );
1042                }
1043            };
1044            kernel_owner.metaregion_sound_preserved_slot_owners_eq(
1045                regions_before_self_borrow,
1046                regions_after_kroot_borrow,
1047            );
1048
1049            let new_idx = new_idx_g;
1050            assert(regions_before_alloc.slots.contains_key(new_idx));
1051            assert(kern_idx != new_idx) by {
1052                crate::specs::mm::page_table::node::entry_owners::EntryOwner::<
1053                    KernelPtConfig,
1054                >::lemma_active_entry_not_in_free_pool(
1055                    kernel_owner.0.value(),
1056                    regions_before_alloc,
1057                    new_idx,
1058                );
1059            };
1060
1061            assert(!regions_before_self_borrow.slots.contains_key(new_idx));
1062            assert(!regions_after_kroot_borrow.slots.contains_key(new_idx));
1063            assert forall|k: int|
1064                regions_after_kroot_borrow.slots.contains_key(
1065                    k,
1066                ) implies regions_after_kroot_borrow.slots[k] == #[trigger] regions.slots[k] by {
1067                if k != new_idx {
1068                    // borrow preserves slots[k] at k != self.index() == new_idx
1069                }
1070            };
1071            assert(kernel_owner.metaregion_sound(regions_before_alloc));
1072
1073            kernel_owner.0.lemma_subtree_satisfies_implies(
1074                kernel_owner.0.value().path,
1075                |
1076                    e: crate::specs::mm::page_table::node::entry_owners::EntryOwner<KernelPtConfig>,
1077                    p: vstd_extra::ghost_tree::TreePath<NR_ENTRIES>,
1078                |
1079                    e.is_frame() && e.parent_level > 1 ==> {
1080                        let pa = e.frame().mapped_pa;
1081                        let nr_pages = page_size(e.parent_level) / PAGE_SIZE;
1082                        forall|j: usize|
1083                            0 < j < nr_pages ==> {
1084                                let sub_idx =
1085                                    #[trigger] crate::specs::mm::frame::mapping::frame_to_index(
1086                                    (pa + j * PAGE_SIZE) as usize,
1087                                );
1088                                sub_idx != new_idx
1089                            }
1090                    },
1091                |
1092                    e: crate::specs::mm::page_table::node::entry_owners::EntryOwner<KernelPtConfig>,
1093                    p: vstd_extra::ghost_tree::TreePath<NR_ENTRIES>,
1094                |
1095                    e.is_frame() && e.parent_level > 1 ==> {
1096                        let pa = e.frame().mapped_pa;
1097                        let nr_pages = page_size(e.parent_level) / PAGE_SIZE;
1098                        forall|j: usize|
1099                            0 < j < nr_pages ==> {
1100                                let sub_idx =
1101                                    #[trigger] crate::specs::mm::frame::mapping::frame_to_index(
1102                                    (pa + j * PAGE_SIZE) as usize,
1103                                );
1104                                sub_idx != new_idx || (regions.slots.contains_key(sub_idx)
1105                                    && regions.slot_owners[sub_idx].inner_perms.ref_count.value()
1106                                    != REF_COUNT_UNUSED
1107                                    && regions.slot_owners[sub_idx].inner_perms.ref_count.value()
1108                                    > 0
1109                                    && regions.slot_owners[sub_idx].inner_perms.ref_count.value()
1110                                    <= REF_COUNT_MAX)
1111                            }
1112                    },
1113            );
1114            kernel_owner.metaregion_sound_preserved_one_slot_changed(
1115                regions_after_kroot_borrow,
1116                *regions,
1117                new_idx,
1118            );
1119        }
1120        let mut i: usize = KernelPtConfig::TOP_LEVEL_INDEX_RANGE().start;
1121        while i < KernelPtConfig::TOP_LEVEL_INDEX_RANGE().end
1122            invariant
1123                kernel_owner.inv(),
1124                kernel_owner.0.value().is_node(),
1125                regions.inv(),
1126                !Self::create_user_pt_panic_condition(kernel_owner.0.value().node()),
1127                i <= KernelPtConfig::TOP_LEVEL_INDEX_RANGE().end,
1128                KernelPtConfig::TOP_LEVEL_INDEX_RANGE().start <= i,
1129                // Lock postcondition for the kernel root.
1130                *root_owner == kernel_owner.0.value().node(),
1131                root_owner.relate_guard(root_node),
1132                // Tree-wide soundness of the kernel page table.
1133                kernel_owner.metaregion_sound(*regions),
1134                // The new node owner's invariants and guard relation.
1135                new_node_owner.inv(),
1136                new_node_owner.relate_guard(new_node),
1137                regions.slots.contains_key(new_node_owner.slot_index),
1138            decreases KernelPtConfig::TOP_LEVEL_INDEX_RANGE().end - i,
1139        {
1140            proof {
1141                let kern_node = kernel_owner.0.value().node();
1142                assert forall|j: usize|
1143                    #![trigger kern_node.children_perm.value()[j as int]]
1144                    KernelPtConfig::TOP_LEVEL_INDEX_RANGE().start <= j
1145                        < KernelPtConfig::TOP_LEVEL_INDEX_RANGE().end implies {
1146                    let pte = kern_node.children_perm.value()[j as int];
1147                    pte.is_present() && !pte.is_last(kern_node.level)
1148                } by {
1149                    let pte = kern_node.children_perm.value()[j as int];
1150                    if !pte.is_present() || pte.is_last(kern_node.level) {
1151                        assert(Self::create_user_pt_panic_condition(kern_node));
1152                    }
1153                }
1154
1155                kernel_owner.pt_inv_unroll(i as int);
1156                let tracked child_subtree: &OwnerSubtree<KernelPtConfig> =
1157                    kernel_owner.0.tracked_borrow_child(i as int);
1158                entry_owner = child_subtree.tracked_borrow_value();
1159                let kern_node = kernel_owner.0.value().node();
1160                assert(entry_owner.match_pte(
1161                    kern_node.children_perm.value()[i as int],
1162                    entry_owner.parent_level,
1163                ));
1164                assert(entry_owner.parent_level == kern_node.level);
1165                assert(child_subtree.inv());
1166                assert(entry_owner.inv());
1167                assert(root_owner.relate_guard(root_node));
1168
1169                kernel_owner.0.lemma_subtree_satisfies_unroll_once(
1170                    kernel_owner.0.value().path,
1171                    PageTableOwner::<KernelPtConfig>::metaregion_sound_pred(*regions),
1172                    i as int,
1173                );
1174                assert(child_subtree.subtree_satisfies(
1175                    kernel_owner.0.value().path.push_tail(i as int),
1176                    PageTableOwner::<KernelPtConfig>::metaregion_sound_pred(*regions),
1177                ));
1178                assert(entry_owner.metaregion_sound(*regions));
1179            }
1180
1181            #[verus_spec(with Tracked(root_owner), Tracked(entry_owner), Tracked(&*regions))]
1182            let root_entry = root_node.entry(i);
1183            let ghost pre_to_ref_regions: MetaRegionOwners = *regions;
1184            #[verus_spec(with Tracked(entry_owner), Tracked(root_owner), Tracked(regions))]
1185            let child = root_entry.to_ref();
1186
1187            proof {
1188                let kern_node = kernel_owner.0.value().node();
1189                let pte = kern_node.children_perm.value()[i as int];
1190
1191                assert(pte.is_present() && !pte.is_last(kern_node.level)) by {
1192                    if !pte.is_present() || pte.is_last(kern_node.level) {
1193                        assert(KernelPtConfig::TOP_LEVEL_INDEX_RANGE().start <= i
1194                            < KernelPtConfig::TOP_LEVEL_INDEX_RANGE().end);
1195                        assert(exists|j: usize|
1196                            KernelPtConfig::TOP_LEVEL_INDEX_RANGE().start <= j
1197                                < KernelPtConfig::TOP_LEVEL_INDEX_RANGE().end && {
1198                                let p = #[trigger] kern_node.children_perm.value()[j as int];
1199                                ||| !p.is_present()
1200                                ||| p.is_last(kern_node.level)
1201                            });
1202                        assert(Self::create_user_pt_panic_condition(kern_node));
1203                    }
1204                }
1205                // entry_owner.match_pte(pte, parent_level) + (present && !is_last)
1206                // ⟹ entry_owner.is_node().
1207                assert(entry_owner.is_node());
1208                // ChildRef::invariants(entry_owner, regions) gives child.wf(entry_owner).
1209                // For the Frame and None variants, wf requires is_frame() or is_absent(),
1210                // contradicting is_node(). Hence child must be PageTable.
1211                assert(child is PageTable);
1212                // to_ref's borrow_paddr preserves slot_owners exactly and only
1213                // grows `slots` (existing keys preserved). Use the tree-wide
1214                // preservation lemma.
1215                kernel_owner.metaregion_sound_preserved_slot_owners_eq(
1216                    pre_to_ref_regions,
1217                    *regions,
1218                );
1219            }
1220            let pt = match child {
1221                ChildRef::PageTable(pt) => pt,
1222                _ => vstd::pervasive::unreached(),
1223            };
1224
1225            let ghost entry_node_slot_idx = entry_owner.tracked_borrow_node().slot_index;
1226            let tracked entry_node_slot_perm = regions.slots.tracked_borrow(entry_node_slot_idx);
1227            #[verus_spec(with Tracked(entry_node_slot_perm))]
1228            let pt_addr = pt.start_paddr();
1229            let pte = PageTableEntry::new_pt(pt_addr);
1230
1231            proof {
1232                assert(regions.slots.contains_key(new_node_owner.slot_index));
1233            }
1234            unsafe {
1235                #[verus_spec(with Tracked(&mut new_node_owner), Tracked(&*regions))]
1236                new_node.write_pte(i, pte)
1237            };
1238
1239            i = i + 1;
1240        }
1241
1242        PageTable::<UserPtConfig> { root: new_root }
1243    }/*
1244    /// Protect the given virtual address range in the kernel page table.
1245    ///
1246    /// This method flushes the TLB entries when doing protection.
1247    ///
1248    /// # Safety
1249    ///
1250    /// The caller must ensure that the protection operation does not affect
1251    /// the memory safety of the kernel.
1252    pub unsafe fn protect_flush_tlb(
1253        &self,
1254        vaddr: &Range<Vaddr>,
1255        mut op: impl FnMut(&mut PageProperty),
1256    ) -> Result<(), PageTableError> {
1257        let preempt_guard = disable_preempt();
1258        let mut cursor = CursorMut::new(self, &preempt_guard, vaddr)?;
1259        // SAFETY: The safety is upheld by the caller.
1260        while let Some(range) =
1261            unsafe { cursor.protect_next(vaddr.end - cursor.virt_addr(), &mut op) }
1262        {
1263            crate::arch::mm::tlb_flush_addr(range.start);
1264        }
1265        Ok(())
1266    }*/
1267
1268}
1269
1270#[verus_verify]
1271impl<C: PageTableConfig> PageTable<C> {
1272    /// Relates this executable page-table handle to its tracked ownership tree.
1273    pub open spec fn relates_owner(
1274        &self,
1275        owner: PageTableOwner<C>,
1276        regions: MetaRegionOwners,
1277    ) -> bool {
1278        &&& owner.inv()
1279        &&& self.root.ptr.addr() == owner.0.value().node().meta_vaddr()
1280        &&& owner.metaregion_sound(regions)
1281    }
1282
1283    /// Create a new empty page table.
1284    ///
1285    /// Useful for the IOMMU page tables only.
1286    #[verifier::external_body]
1287    pub fn empty() -> Self {
1288        unimplemented!()
1289    }
1290
1291    /// Create a new empty page table together with its tracked ownership.
1292    #[verifier::external_body]
1293    #[verus_spec(r =>
1294        with Tracked(owner): Tracked<&mut Option<PageTableOwner<C>>>,
1295            Tracked(regions): Tracked<&mut MetaRegionOwners>,
1296            Tracked(guards): Tracked<&mut Guards<'rcu>>,
1297        requires
1298            old(regions).inv(),
1299        ensures
1300            final(owner)@ is Some,
1301            final(owner)@->0.inv(),
1302            (final(owner)@->0).0.value().is_node(),
1303            (final(owner)@->0).0.value().is_node(),
1304            r.root.ptr.addr() == (final(owner)@->0).0.value().node().meta_vaddr(),
1305            (final(owner)@->0).0.value().metaregion_sound(*final(regions)),
1306            final(regions).inv(),
1307            final(guards).unlocked((final(owner)@->0).0.value().node().meta_vaddr()),
1308            // Allocating a fresh node does not change the lock set, so any node
1309            // that was (un)locked before remains so.
1310            final(guards).guards == old(guards).guards,
1311            // The newly allocated slot was in the free pool before the call.
1312            old(regions).slots.contains_key(
1313                crate::specs::mm::frame::mapping::frame_to_index(
1314                    (final(owner)@->0).0.value().meta_slot_paddr()->0)),
1315            // After the alloc, the slot is removed from the free pool (now owned
1316            // by the new pt's NodeOwner).
1317            !final(regions).slots.contains_key(
1318                crate::specs::mm::frame::mapping::frame_to_index(
1319                    (final(owner)@->0).0.value().meta_slot_paddr()->0)),
1320            // Other slots and lock state are preserved.
1321            forall |i: int| #![trigger final(regions).slot_owners[i]]
1322                i != crate::specs::mm::frame::mapping::frame_to_index(
1323                    (final(owner)@->0).0.value().meta_slot_paddr()->0)
1324                ==> final(regions).slot_owners[i] == old(regions).slot_owners[i],
1325            forall |a: usize| old(guards).lock_held(a) ==> final(guards).lock_held(a),
1326            forall |idx: int| #![trigger final(regions).slot_owners[idx].paths_in_pt]
1327                final(regions).slot_owners[idx].paths_in_pt
1328                    == old(regions).slot_owners[idx].paths_in_pt,
1329            // Allocation preserves the soundness of the kernel page-table tree:
1330            // a fresh allocation cannot collide with any active node or frame entry
1331            // (the allocator returns a slot that wasn't in use). Stated as a
1332            // postcondition because deriving it requires a freshness axiom on the
1333            // underlying frame allocator.
1334            forall |kt: PageTableOwner<KernelPtConfig>|
1335                #![trigger kt.metaregion_sound(*final(regions))]
1336                kt.inv() && kt.metaregion_sound(*old(regions))
1337                ==> kt.metaregion_sound(*final(regions)),
1338            // Freshness: the new PT's slot index is not used (as a primary slot
1339            // or huge-frame sub-page slot) by any entry in any KernelPtConfig PT
1340            // tree that was sound before the alloc. Used to discharge the borrow
1341            // step that mutates `slot_owners[new_idx]`.
1342            forall |kt: PageTableOwner<KernelPtConfig>|
1343                #![trigger kt.metaregion_sound(*old(regions))]
1344                kt.inv() && kt.metaregion_sound(*old(regions)) ==>
1345                kt.0.subtree_satisfies(
1346                    kt.0.value().path,
1347                    |e: crate::specs::mm::page_table::node::entry_owners::EntryOwner<KernelPtConfig>,
1348                     p: vstd_extra::ghost_tree::TreePath<NR_ENTRIES>|
1349                        e.meta_slot_paddr() is Some
1350                            ==> crate::specs::mm::frame::mapping::frame_to_index(
1351                                e.meta_slot_paddr()->0) !=
1352                                crate::specs::mm::frame::mapping::frame_to_index(
1353                                    (final(owner)@->0).0.value().meta_slot_paddr()->0),
1354                ),
1355            // Sub-page freshness: for any huge frame entry in any pre-existing
1356            // sound KernelPtConfig tree, the new PT's slot index isn't a sub-page
1357            // slot of the huge frame either. Same allocator-freshness rationale.
1358            forall |kt: PageTableOwner<KernelPtConfig>|
1359                #![trigger kt.metaregion_sound(*old(regions))]
1360                kt.inv() && kt.metaregion_sound(*old(regions)) ==>
1361                kt.0.subtree_satisfies(
1362                    kt.0.value().path,
1363                    |e: crate::specs::mm::page_table::node::entry_owners::EntryOwner<KernelPtConfig>,
1364                     p: vstd_extra::ghost_tree::TreePath<NR_ENTRIES>|
1365                        e.is_frame() && e.parent_level > 1 ==> {
1366                            let pa = e.frame().mapped_pa;
1367                            let nr_pages = page_size(
1368                                e.parent_level) / PAGE_SIZE;
1369                            forall |j: usize| 0 < j < nr_pages ==> {
1370                                let sub_idx =
1371                                    #[trigger] crate::specs::mm::frame::mapping::frame_to_index(
1372                                        (pa + j * PAGE_SIZE) as usize);
1373                                sub_idx != crate::specs::mm::frame::mapping::frame_to_index(
1374                                    (final(owner)@->0).0.value().meta_slot_paddr()->0)
1375                            }
1376                        },
1377                ),
1378    )]
1379    pub fn empty_with_owner<'rcu>() -> Self {
1380        unimplemented!()
1381    }
1382
1383    #[verifier::external_body]
1384    pub(in crate::mm) unsafe fn first_activate_unchecked(&self) {
1385        unimplemented!()
1386        // SAFETY: The safety is upheld by the caller.
1387        //        unsafe { self.root.first_activate() };
1388
1389    }
1390
1391    pub uninterp spec fn root_paddr_spec(&self) -> Paddr;
1392
1393    /// The physical address of the root page table.
1394    ///
1395    /// Obtaining the physical address of the root page table is safe, however, using it or
1396    /// providing it to the hardware will be unsafe since the page table node may be dropped,
1397    /// resulting in UAF.
1398    #[verifier::external_body]
1399    #[verifier::when_used_as_spec(root_paddr_spec)]
1400    pub fn root_paddr(&self) -> (r: Paddr)
1401        returns
1402            self.root_paddr_spec(),
1403    {
1404        unimplemented!()
1405        //        self.root.start_paddr()
1406
1407    }
1408
1409    /// Query about the mapping of a single byte at the given virtual address.
1410    ///
1411    /// Note that this function may fail reflect an accurate result if there are
1412    /// cursors concurrently accessing the same virtual address range, just like what
1413    /// happens for the hardware MMU walk.
1414    #[cfg(ktest)]
1415    pub fn page_walk(&self, vaddr: Vaddr) -> Option<(Paddr, PageProperty)> {
1416        // SAFETY: The root node is a valid page table node so the address is valid.
1417        unsafe { page_walk::<C>(self.root_paddr(), vaddr) }
1418    }
1419
1420    /// Create a new cursor exclusively accessing the virtual address range for mapping.
1421    ///
1422    /// If another cursor is already accessing the range, the new cursor may wait until the
1423    /// previous cursor is dropped.
1424    #[verus_spec(r =>
1425        with Tracked(owner): Tracked<PageTableOwner<C>>,
1426            Ghost(root_guard): Ghost<PageTableGuard<'rcu, C>>,
1427            Tracked(regions): Tracked<&mut MetaRegionOwners>,
1428            Tracked(guards): Tracked<&mut Guards<'rcu>>
1429        requires
1430            self.relates_owner(owner, *old(regions)),
1431            owner.0.value().node().relate_guard(root_guard),
1432            // Per-config tightening; see `Cursor::new`.
1433            0 < va.end <= C::LOCKED_END_BOUND_spec(),
1434        ensures
1435            Cursor::<C, G>::cursor_new_success_conditions(*va) ==> {
1436                &&& r is Ok
1437                &&& r.unwrap().0.0.invariants(*r.unwrap().1, *final(regions), *final(guards))
1438                &&& r.unwrap().1.in_locked_range()
1439                &&& r.unwrap().0.0.level == r.unwrap().0.0.guard_level
1440                &&& r.unwrap().0.0.guard_level == NR_LEVELS as PagingLevel
1441                &&& r.unwrap().0.0.va < r.unwrap().0.0.barrier_va.end
1442                &&& r.unwrap().0.0.va == va.start
1443                &&& r.unwrap().0.0.barrier_va == *va
1444            },
1445            !Cursor::<C, G>::cursor_new_success_conditions(*va) ==> r is Err,
1446            forall |item: C::Item| #![trigger CursorMut::<'rcu, C, G>::item_not_mapped(item, *old(regions))]
1447                CursorMut::<'rcu, C, G>::item_not_mapped(item, *old(regions)) ==>
1448                CursorMut::<'rcu, C, G>::item_not_mapped(item, *final(regions)),
1449            // CursorMut::new inherits Cursor::new's weakened preservation:
1450            // PT-node allocations come from UNUSED slots, so any slot that
1451            // was already in use keeps its paths_in_pt.
1452            forall |idx: int| #![trigger final(regions).slot_owners[idx].paths_in_pt]
1453                old(regions).slot_owners[idx].inner_perms.ref_count.value()
1454                    != REF_COUNT_UNUSED
1455                ==> final(regions).slot_owners[idx].paths_in_pt
1456                        == old(regions).slot_owners[idx].paths_in_pt,
1457            forall|idx: int| #![trigger final(regions).slot_owners[idx]]
1458                old(regions).slot_owners.contains_key(idx)
1459                && old(regions).slot_owners[idx].inner_perms.ref_count.value()
1460                    != REF_COUNT_UNUSED
1461                ==> final(regions).slot_owners[idx].inner_perms.ref_count.value()
1462                        == old(regions).slot_owners[idx].inner_perms.ref_count.value()
1463                    && final(regions).slot_owners[idx].usage
1464                        == old(regions).slot_owners[idx].usage,
1465    )]
1466    pub fn cursor_mut<'rcu, G: InAtomicMode>(
1467        &'rcu self,
1468        guard: &'rcu G,
1469        va: &Range<Vaddr>,
1470    ) -> Result<(CursorMut<'rcu, C, G>, Tracked<CursorOwner<'rcu, C>>), PageTableError> {
1471        #[verus_spec(with Tracked(owner), Ghost(root_guard), Tracked(regions), Tracked(guards))]
1472        CursorMut::new(self, guard, va)
1473    }
1474
1475    /// Create a new cursor exclusively accessing the virtual address range for querying.
1476    ///
1477    /// If another cursor is already accessing the range, the new cursor may wait until the
1478    /// previous cursor is dropped. The modification to the mapping by the cursor may also
1479    /// block or be overridden by the mapping of another cursor.
1480    #[verus_spec(r =>
1481        with Tracked(owner): Tracked<PageTableOwner<C>>,
1482            Ghost(root_guard): Ghost<PageTableGuard<'rcu, C>>,
1483            Tracked(regions): Tracked<&mut MetaRegionOwners>,
1484            Tracked(guards): Tracked<&mut Guards<'rcu>>
1485        requires
1486            self.relates_owner(owner, *old(regions)),
1487            owner.0.value().node().relate_guard(root_guard),
1488            // Per-config tightening; see `Cursor::new`.
1489            0 < va.end <= C::LOCKED_END_BOUND_spec(),
1490        ensures
1491            Cursor::<C, G>::cursor_new_success_conditions(*va) ==> {
1492                &&& r is Ok
1493                &&& r.unwrap().0.invariants(*r.unwrap().1, *final(regions), *final(guards))
1494                &&& r.unwrap().1.in_locked_range()
1495                &&& r.unwrap().0.level == r.unwrap().0.guard_level
1496                &&& r.unwrap().0.va < r.unwrap().0.barrier_va.end
1497                &&& r.unwrap().0.va == va.start
1498                &&& r.unwrap().0.barrier_va == *va
1499                &&& r.unwrap().1@.as_page_table_owner() == owner
1500                &&& r.unwrap().1@.continuations[3].path() == owner.0.value().path
1501            },
1502            !Cursor::<C, G>::cursor_new_success_conditions(*va) ==> r is Err,
1503            forall|idx: int| #![trigger final(regions).slot_owners[idx].paths_in_pt]
1504                old(regions).slot_owners[idx].inner_perms.ref_count.value()
1505                    != REF_COUNT_UNUSED
1506                ==> final(regions).slot_owners[idx].paths_in_pt
1507                        == old(regions).slot_owners[idx].paths_in_pt,
1508            // Non-saturation preservation.
1509            (forall |i: int| #![trigger old(regions).slot_owners[i]]
1510                old(regions).slot_owners.contains_key(i)
1511                && old(regions).slot_owners[i].inner_perms.ref_count.value()
1512                    != REF_COUNT_UNUSED
1513                ==> old(regions).slot_owners[i].inner_perms.ref_count.value() + 1
1514                    < REF_COUNT_MAX)
1515            ==>
1516            (forall |i: int| #![trigger final(regions).slot_owners[i]]
1517                final(regions).slot_owners.contains_key(i)
1518                && final(regions).slot_owners[i].inner_perms.ref_count.value()
1519                    != REF_COUNT_UNUSED
1520                ==> final(regions).slot_owners[i].inner_perms.ref_count.value() + 1
1521                    < REF_COUNT_MAX),
1522            // Saturated-slot bridge (relayed from `Cursor::new`):
1523            // a slot at `>= REF_COUNT_MAX` before iff after, with the same
1524            // value. Used by `KVirtArea::query` to bridge inner-cursor
1525            // saturation back to the caller's snapshot.
1526            forall|idx: int| #![trigger final(regions).slot_owners[idx].inner_perms.ref_count.value()]
1527                final(regions).slot_owners[idx].inner_perms.ref_count.value()
1528                    >= REF_COUNT_MAX
1529                ==> old(regions).slot_owners[idx].inner_perms.ref_count.value()
1530                        == final(regions).slot_owners[idx].inner_perms.ref_count.value(),
1531            forall|idx: int| #![trigger old(regions).slot_owners[idx].inner_perms.ref_count.value()]
1532                old(regions).slot_owners[idx].inner_perms.ref_count.value()
1533                    >= REF_COUNT_MAX
1534                ==> final(regions).slot_owners[idx].inner_perms.ref_count.value()
1535                        == old(regions).slot_owners[idx].inner_perms.ref_count.value(),
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).value()[ptr.index as int] == new_val,
1858        final(perm).value() == old(perm).value().update(ptr.index as int, new_val),
1859        final(perm).addr() == old(perm).addr(),
1860        final(perm).is_init_all(),
1861)]
1862pub unsafe fn store_pte<E: PageTableEntryTrait>(
1863    ptr: vstd_extra::array_ptr::ArrayPtr<E, NR_ENTRIES>,
1864    new_val: E,
1865    ordering: Ordering,
1866);
1867
1868} // verus!