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