Skip to main content

ostd/mm/page_table/
mod.rs

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