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;
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    //    PodOnce,
29    Vaddr,
30};
31
32use crate::specs::mm::page_table::*;
33
34use crate::specs::arch::mm::*;
35use crate::specs::arch::paging_consts::PagingConsts;
36use crate::specs::mm::page_table::cursor::*;
37use crate::specs::task::InAtomicMode;
38
39use crate::specs::mm::frame::meta_region_owners::MetaRegionOwners;
40use vstd_extra::ownership::Inv;
41
42mod node;
43pub use node::*;
44mod cursor;
45
46pub(crate) use cursor::*;
47
48#[cfg(ktest)]
49mod test;
50
51//pub(crate) mod boot_pt;
52
53verus! {
54
55#[derive(Clone, Copy, PartialEq, Eq, Debug)]
56pub enum PageTableError {
57    /// The provided virtual address range is invalid.
58    InvalidVaddrRange(Vaddr, Vaddr),
59    /// The provided virtual address is invalid.
60    InvalidVaddr(Vaddr),
61    /// Using virtual address not aligned.
62    UnalignedVaddr,
63}
64
65pub trait RCClone: Sized {
66    spec fn clone_requires(self, slot_perm: simple_pptr::PointsTo<MetaSlot>, rc_perm: PermissionU64) -> bool;
67
68    fn clone(&self, Tracked(slot_perm): Tracked<&simple_pptr::PointsTo<MetaSlot>>, Tracked(rc_perm): Tracked<&mut PermissionU64>) -> (res: Self)
69        requires
70            self.clone_requires(*slot_perm, *old(rc_perm)),
71            old(rc_perm).is_for(slot_perm.value().ref_count),
72            old(rc_perm).value() < u64::MAX,
73        ensures
74            res == *self,
75            rc_perm.value() == old(rc_perm).value() + 1,
76            rc_perm.id() == old(rc_perm).id(),
77    ;
78}
79
80/// The configurations of a page table.
81///
82/// It abstracts away both the usage and the architecture specifics from the
83/// general page table implementation. For examples:
84///  - the managed virtual address range;
85///  - the trackedness of physical mappings;
86///  - the PTE layout;
87///  - the number of page table levels, etc.
88///
89/// # Safety
90///
91/// The implementor must ensure that the `item_into_raw` and `item_from_raw`
92/// are implemented correctly so that:
93///  - `item_into_raw` consumes the ownership of the item;
94///  - if the provided raw form matches the item that was consumed by
95///    `item_into_raw`, `item_from_raw` restores the exact item that was
96///    consumed by `item_into_raw`.
97pub unsafe trait PageTableConfig: Clone + Debug + Send + Sync + 'static {
98    /// The index range at the top level (`C::NR_LEVELS()`) page table.
99    ///
100    /// When configured with this value, the [`PageTable`] instance will only
101    /// be allowed to manage the virtual address range that is covered by
102    /// this range. The range can be smaller than the actual allowed range
103    /// specified by the hardware MMU (limited by `C::ADDRESS_WIDTH`).
104    spec fn TOP_LEVEL_INDEX_RANGE_spec() -> Range<usize>;
105
106    fn TOP_LEVEL_INDEX_RANGE() -> (r: Range<usize>)
107        ensures
108            r == Self::TOP_LEVEL_INDEX_RANGE_spec(),
109    ;
110
111    /// If we can remove the top-level page table entries.
112    ///
113    /// This is for the kernel page table, whose second-top-level page
114    /// tables need `'static` lifetime to be shared with user page tables.
115    /// Other page tables do not need to set this to `false`.
116    open spec fn TOP_LEVEL_CAN_UNMAP_spec() -> bool {
117        true
118    }
119
120    fn TOP_LEVEL_CAN_UNMAP() -> (b: bool)
121        ensures
122            b == Self::TOP_LEVEL_CAN_UNMAP_spec(),
123    ;
124
125    /// The type of the page table entry.
126    type E: PageTableEntryTrait;
127
128    /// The paging constants.
129    type C: PagingConstsTrait;
130
131    proof fn axiom_nr_subpage_per_huge_eq_nr_entries()
132        ensures
133            Self::C::BASE_PAGE_SIZE() / Self::C::PTE_SIZE() == NR_ENTRIES,
134    ;
135
136    /// The item that can be mapped into the virtual memory space using the
137    /// page table.
138    ///
139    /// Usually, this item is a [`crate::mm::Frame`], which we call a "tracked"
140    /// frame. The page table can also do "untracked" mappings that only maps
141    /// to certain physical addresses without tracking the ownership of the
142    /// mapped physical frame. The user of the page table APIs can choose by
143    /// defining this type and the corresponding methods [`item_into_raw`] and
144    /// [`item_from_raw`].
145    ///
146    /// [`item_from_raw`]: PageTableConfig::item_from_raw
147    /// [`item_into_raw`]: PageTableConfig::item_into_raw
148    type Item: RCClone;
149
150    /// Consumes the item and returns the physical address, the paging level,
151    /// and the page property.
152    ///
153    /// The ownership of the item will be consumed, i.e., the item will be
154    /// forgotten after this function is called.
155    spec fn item_into_raw_spec(item: Self::Item) -> (Paddr, PagingLevel, PageProperty);
156
157    #[verifier::when_used_as_spec(item_into_raw_spec)]
158    fn item_into_raw(item: Self::Item) -> (res: (Paddr, PagingLevel, PageProperty))
159        ensures
160            1 <= res.1 <= NR_LEVELS,
161            res == Self::item_into_raw_spec(item),
162    ;
163
164    /// Restores the item from the physical address and the paging level.
165    ///
166    /// There could be transformations after [`PageTableConfig::item_into_raw`]
167    /// and before [`PageTableConfig::item_from_raw`], which include:
168    ///  - splitting and coalescing the items, for example, splitting one item
169    ///    into 512 `level - 1` items with and contiguous physical addresses;
170    ///  - protecting the items, for example, changing the page property.
171    ///
172    /// Splitting and coalescing maintains ownership rules, i.e., if one
173    /// physical address is within the range of one item, after splitting/
174    /// coalescing, there should be exactly one item that contains the address.
175    ///
176    /// # Safety
177    ///
178    /// The caller must ensure that:
179    ///  - the physical address and the paging level represent a page table
180    ///    item or part of it (as described above);
181    ///  - either the ownership of the item is properly transferred to the
182    ///    return value, or the return value is wrapped in a
183    ///    [`core::mem::ManuallyDrop`] that won't outlive the original item.
184    ///
185    /// A concrete trait implementation may require the caller to ensure that
186    ///  - the [`super::PageFlags::AVAIL1`] flag is the same as that returned
187    ///    from [`PageTableConfig::item_into_raw`].
188    spec fn item_from_raw_spec(paddr: Paddr, level: PagingLevel, prop: PageProperty) -> Self::Item;
189
190    #[verifier::when_used_as_spec(item_from_raw_spec)]
191    fn item_from_raw(paddr: Paddr, level: PagingLevel, prop: PageProperty) -> Self::Item
192        returns
193            Self::item_from_raw_spec(paddr, level, prop),
194    ;
195
196    proof fn item_roundtrip(item: Self::Item, paddr: Paddr, level: PagingLevel, prop: PageProperty)
197        ensures
198            Self::item_into_raw_spec(item) == (paddr, level, prop) <==> Self::item_from_raw_spec(
199                paddr,
200                level,
201                prop,
202            ) == item,
203    ;
204}
205
206// Implement it so that we can comfortably use low level functions
207// like `page_size::<C>` without typing `C::C` everywhere.
208impl<C: PageTableConfig> PagingConstsTrait for C {
209    open spec fn BASE_PAGE_SIZE_spec() -> usize {
210        C::C::BASE_PAGE_SIZE_spec()
211    }
212
213    fn BASE_PAGE_SIZE() -> usize {
214        C::C::BASE_PAGE_SIZE()
215    }
216
217    open spec fn NR_LEVELS_spec() -> PagingLevel {
218        C::C::NR_LEVELS_spec()
219    }
220
221    fn NR_LEVELS() -> PagingLevel {
222        C::C::NR_LEVELS()
223    }
224
225    open spec fn HIGHEST_TRANSLATION_LEVEL_spec() -> PagingLevel {
226        C::C::HIGHEST_TRANSLATION_LEVEL_spec()
227    }
228
229    fn HIGHEST_TRANSLATION_LEVEL() -> PagingLevel {
230        C::C::HIGHEST_TRANSLATION_LEVEL()
231    }
232
233    open spec fn PTE_SIZE_spec() -> usize {
234        C::C::PTE_SIZE_spec()
235    }
236
237    fn PTE_SIZE() -> usize {
238        C::C::PTE_SIZE()
239    }
240
241    open spec fn ADDRESS_WIDTH_spec() -> usize {
242        C::C::ADDRESS_WIDTH_spec()
243    }
244
245    fn ADDRESS_WIDTH() -> usize {
246        C::C::ADDRESS_WIDTH()
247    }
248
249    open spec fn VA_SIGN_EXT_spec() -> bool {
250        C::C::VA_SIGN_EXT_spec()
251    }
252
253    fn VA_SIGN_EXT() -> bool {
254        C::C::VA_SIGN_EXT()
255    }
256
257    proof fn lemma_BASE_PAGE_SIZE_properties()
258        ensures
259            0 < Self::BASE_PAGE_SIZE_spec(),
260            is_pow2(Self::BASE_PAGE_SIZE_spec() as int),
261    {
262        C::C::lemma_BASE_PAGE_SIZE_properties();
263    }
264
265    proof fn lemma_PTE_SIZE_properties()
266        ensures
267            0 < Self::PTE_SIZE_spec() <= Self::BASE_PAGE_SIZE(),
268            is_pow2(Self::PTE_SIZE_spec() as int),
269    {
270        C::C::lemma_PTE_SIZE_properties();
271    }
272}
273
274/// The interface for defining architecture-specific page table entries.
275///
276/// Note that a default PTE should be a PTE that points to nothing.
277pub trait PageTableEntryTrait: Clone + Copy + Debug + Sized + Send + Sync + 'static {
278    spec fn default_spec() -> Self;
279
280    /// For implement `Default` trait.
281    #[verifier::when_used_as_spec(default_spec)]
282    fn default() -> (res: Self)
283        ensures
284            res == Self::default_spec(),
285    ;
286
287    /// Create a set of new invalid page table flags that indicates an absent page.
288    ///
289    /// Note that currently the implementation requires an all zero PTE to be an absent PTE.
290    spec fn new_absent_spec() -> Self;
291
292    #[verifier(when_used_as_spec(new_absent_spec))]
293    fn new_absent() -> (res: Self)
294        ensures
295            res == Self::new_absent_spec(),
296            res.paddr() % PAGE_SIZE == 0,
297            res.paddr() < MAX_PADDR,
298    ;
299
300    /// If the flags are present with valid mappings.
301    spec fn is_present_spec(&self) -> bool;
302
303    #[verifier::when_used_as_spec(is_present_spec)]
304    fn is_present(&self) -> (res: bool)
305        ensures
306            res == self.is_present_spec(),
307    ;
308
309    /// Create a new PTE with the given physical address and flags that map to a page.
310    spec fn new_page_spec(paddr: Paddr, level: PagingLevel, prop: PageProperty) -> Self;
311
312    #[verifier::when_used_as_spec(new_page_spec)]
313    fn new_page(paddr: Paddr, level: PagingLevel, prop: PageProperty) -> (res: Self)
314        requires
315            paddr % PAGE_SIZE == 0,
316            paddr < MAX_PADDR,
317        ensures
318            res == Self::new_page_spec(paddr, level, prop),
319            res.paddr() == paddr,
320            res.paddr() % PAGE_SIZE == 0,
321            res.paddr() < MAX_PADDR,
322    ;
323
324    /// Create a new PTE that map to a child page table.
325    spec fn new_pt_spec(paddr: Paddr) -> Self;
326
327    #[verifier::when_used_as_spec(new_pt_spec)]
328    fn new_pt(paddr: Paddr) -> (res: Self)
329        requires
330            paddr % PAGE_SIZE == 0,
331            paddr < MAX_PADDR,
332        ensures
333            res == Self::new_pt_spec(paddr),
334            res.paddr() == paddr,
335            res.paddr() % PAGE_SIZE == 0,
336            res.paddr() < MAX_PADDR,
337    ;
338
339    proof fn new_properties()
340        ensures
341            !Self::new_absent_spec().is_present(),
342            forall|level: PagingLevel|
343                #![trigger Self::new_absent_spec().is_last(level)]
344                1 < level ==> !Self::new_absent_spec().is_last(level),
345            forall|paddr: Paddr, level: PagingLevel, prop: PageProperty|
346                #![trigger Self::new_page_spec(paddr, level, prop)]
347                {
348                    &&& Self::new_page_spec(paddr, level, prop).is_present()
349                    &&& Self::new_page_spec(paddr, level, prop).paddr() == paddr
350                    &&& Self::new_page_spec(paddr, level, prop).prop() == prop
351                    &&& Self::new_page_spec(paddr, level, prop).is_last(level)
352                },
353            forall|paddr: Paddr|
354                #![trigger Self::new_pt_spec(paddr)]
355                {
356                    &&& Self::new_pt_spec(paddr).is_present()
357                    &&& Self::new_pt_spec(paddr).paddr() == paddr
358                    &&& forall|level: PagingLevel| !Self::new_pt_spec(paddr).is_last(level)
359                },
360    ;
361
362    /// Get the physical address from the PTE.
363    /// The physical address recorded in the PTE is either:
364    /// - the physical address of the next level page table;
365    /// - or the physical address of the page it maps to.
366    spec fn paddr_spec(&self) -> Paddr;
367
368    #[verifier::when_used_as_spec(paddr_spec)]
369    fn paddr(&self) -> (res: Paddr)
370        ensures
371            res == self.paddr_spec(),
372    ;
373
374    spec fn prop_spec(&self) -> PageProperty;
375
376    #[verifier::when_used_as_spec(prop_spec)]
377    fn prop(&self) -> (res: PageProperty)
378        ensures
379            res == self.prop_spec(),
380    ;
381
382    /// Set the page property of the PTE.
383    ///
384    /// This will be only done if the PTE is present. If not, this method will
385    /// do nothing.
386    spec fn set_prop_spec(&self, prop: PageProperty) -> Self;
387
388    fn set_prop(&mut self, prop: PageProperty)
389        ensures
390            old(self).set_prop_spec(prop) == *self,
391    ;
392
393    proof fn set_prop_properties(self, prop: PageProperty)
394        ensures
395            self.set_prop_spec(prop).prop() == prop,
396            self.set_prop_spec(prop).paddr() == self.paddr(),
397            self.is_present() ==> self.set_prop_spec(prop).is_present(),
398            forall|level: PagingLevel|
399                #![trigger self.is_last(level)]
400                self.is_last(level) ==> self.set_prop_spec(prop).is_last(level),
401    ;
402
403    /// If the PTE maps a page rather than a child page table.
404    ///
405    /// The level of the page table the entry resides is given since architectures
406    /// like amd64 only uses a huge bit in intermediate levels.
407    spec fn is_last_spec(&self, level: PagingLevel) -> bool;
408
409    #[verifier::when_used_as_spec(is_last_spec)]
410    fn is_last(&self, level: PagingLevel) -> (b: bool)
411        ensures
412            b == self.is_last_spec(level),
413    ;
414
415    /// Converts the PTE into its corresponding `usize` value.
416    spec fn as_usize_spec(self) -> usize;
417
418    #[verifier::external_body]
419    #[verifier::when_used_as_spec(as_usize_spec)]
420    fn as_usize(self) -> (res: usize)
421        ensures
422            res == self.as_usize_spec(),
423    {
424        unimplemented!()
425        // SAFETY: `Self` is `Pod` and has the same memory representation as `usize`.
426        //        unsafe { transmute_unchecked(self) }
427
428    }
429
430    /// Converts a usize `pte_raw` into a PTE.
431    #[verifier::external_body]
432    fn from_usize(pte_raw: usize) -> Self {
433        unimplemented!()
434        // SAFETY: `Self` is `Pod` and has the same memory representation as `usize`.
435        //        unsafe { transmute_unchecked(pte_raw) }
436
437    }
438}
439
440/// A handle to a page table.
441/// A page table can track the lifetime of the mapped physical pages.
442pub struct PageTable<C: PageTableConfig> {
443    pub root: PageTableNode<C>,
444}
445
446#[verifier::inline]
447pub open spec fn nr_pte_index_bits_spec<C: PagingConstsTrait>() -> usize {
448    nr_subpage_per_huge::<C>().ilog2() as usize
449}
450
451/// The number of virtual address bits used to index a PTE in a page.
452#[inline(always)]
453#[verifier::when_used_as_spec(nr_pte_index_bits_spec)]
454pub fn nr_pte_index_bits<C: PagingConstsTrait>() -> (res: usize)
455    ensures
456        res == nr_pte_index_bits_spec::<C>(),
457{
458    proof {
459        lemma_nr_subpage_per_huge_bounded::<C>();
460    }
461    nr_subpage_per_huge::<C>().ilog2() as usize
462}
463
464pub proof fn lemma_nr_pte_index_bits_bounded<C: PagingConstsTrait>()
465    ensures
466        0 <= nr_pte_index_bits::<C>() <= C::BASE_PAGE_SIZE().ilog2(),
467{
468    lemma_nr_subpage_per_huge_bounded::<C>();
469    let nr = nr_subpage_per_huge::<C>();
470    assert(1 <= nr <= C::BASE_PAGE_SIZE());
471    let bits = nr.ilog2();
472    assert(0 <= bits) by {
473        lemma_usize_ilog2_ordered(1, nr);
474    }
475    assert(bits <= C::BASE_PAGE_SIZE().ilog2()) by {
476        lemma_usize_ilog2_ordered(nr, C::BASE_PAGE_SIZE());
477    }
478}
479
480/// Splits the address range into largest page table items.
481///
482/// Each of the returned items is a tuple of the physical address and the
483/// paging level. It is helpful when you want to map a physical address range
484/// into the provided virtual address.
485///
486/// For example, on x86-64, `C: PageTableConfig` may specify level 1 page as
487/// 4KiB, level 2 page as 2MiB, and level 3 page as 1GiB. Suppose that the
488/// supplied physical address range is from `0x3fdff000` to `0x80002000`,
489/// and the virtual address is also `0x3fdff000`, the following 5 items will
490/// be returned:
491///
492/// ```text
493/// 0x3fdff000                                                 0x80002000
494/// start                                                             end
495///   |----|----------------|--------------------------------|----|----|
496///    4KiB      2MiB                       1GiB              4KiB 4KiB
497/// ```
498///
499/// # Panics
500///
501/// Panics if:
502///  - any of `va`, `pa`, or `len` is not aligned to the base page size;
503///  - the range `va..(va + len)` is not valid for the page table.
504#[verifier::external_body]
505pub fn largest_pages<C: PageTableConfig>(
506    mut va: Vaddr,
507    mut pa: Paddr,
508    mut len: usize,
509) -> impl Iterator<Item = (Paddr, PagingLevel)> {
510    assert_eq!(va % C::BASE_PAGE_SIZE(), 0);
511    assert_eq!(pa % C::BASE_PAGE_SIZE(), 0);
512    assert_eq!(len % C::BASE_PAGE_SIZE(), 0);
513    assert!(is_valid_range::<C>(&(va..(va + len))));
514
515    core::iter::from_fn(
516        move ||
517            {
518                if len == 0 {
519                    return None;
520                }
521                let mut level = C::HIGHEST_TRANSLATION_LEVEL();
522                while page_size(level) > len || va % page_size(level) != 0 || pa % page_size(level)
523                    != 0 {
524                    level -= 1;
525                }
526
527                let item_start = pa;
528                va += page_size(level);
529                pa += page_size(level);
530                len -= page_size(level);
531
532                Some((item_start, level))
533            },
534    )
535}
536
537/// Gets the managed virtual addresses range for the page table.
538///
539/// It returns a [`RangeInclusive`] because the end address, if being
540/// [`Vaddr::MAX`], overflows [`Range<Vaddr>`].
541#[verifier::external_body]
542fn top_level_index_width<C: PageTableConfig>() -> usize {
543    C::ADDRESS_WIDTH() - pte_index_bit_offset::<C>(C::NR_LEVELS())
544}
545
546#[verifier::external_body]
547fn pt_va_range_start<C: PageTableConfig>() -> Vaddr {
548    C::TOP_LEVEL_INDEX_RANGE().start << pte_index_bit_offset::<C>(C::NR_LEVELS())
549}
550
551#[verifier::external_body]
552fn pt_va_range_end<C: PageTableConfig>() -> Vaddr {
553    C::TOP_LEVEL_INDEX_RANGE().end.unbounded_shl(
554        pte_index_bit_offset::<C>(C::NR_LEVELS()) as u32,
555    ).wrapping_sub(1)  // Inclusive end.
556
557}
558
559#[verifier::external_body]
560fn sign_bit_of_va<C: PageTableConfig>(va: Vaddr) -> bool {
561    (va >> (C::ADDRESS_WIDTH() - 1)) & 1 != 0
562}
563
564#[verifier::inline]
565pub open spec fn pte_index_bit_offset_spec<C: PagingConstsTrait>(level: PagingLevel) -> int {
566    (C::BASE_PAGE_SIZE().ilog2() as int) + (nr_pte_index_bits::<C>() as int) * (level as int - 1)
567}
568
569/// Spec for the managed virtual address range (exclusive end).
570/// For configs without VA_SIGN_EXT (e.g. UserPtConfig) or when the base range has sign bit 0.
571/// Configs with sign extension (e.g. KernelPtConfig) use a wrapped range in exec;
572/// we use an axiom to connect that case.
573#[verifier::inline]
574pub open spec fn vaddr_range_spec<C: PageTableConfig>() -> Range<Vaddr> {
575    let idx_range = C::TOP_LEVEL_INDEX_RANGE_spec();
576    let offset = pte_index_bit_offset_spec::<C::C>(C::NR_LEVELS()) as nat;
577    let start = (idx_range.start as int) * pow2(offset);
578    let end_inclusive = (idx_range.end as int) * pow2(offset) - 1;
579    (start as Vaddr)..((end_inclusive + 1) as Vaddr)
580}
581
582/// Spec for whether a range is within the page table's managed address space.
583#[verifier::inline]
584pub open spec fn is_valid_range_spec<C: PageTableConfig>(r: &Range<Vaddr>) -> bool {
585    let va_range = vaddr_range_spec::<C>();
586    (r.start == 0 && r.end == 0)
587        || (va_range.start <= r.start && r.end > 0 && r.end - 1 <= va_range.end - 1)
588}
589
590#[verifier::external_body]
591fn vaddr_range<C: PageTableConfig>() -> (ret: Range<Vaddr>)
592    ensures
593        ret == vaddr_range_spec::<C>(),
594{
595    /*    const {
596        assert!(C::TOP_LEVEL_INDEX_RANGE().start < C::TOP_LEVEL_INDEX_RANGE().end);
597        assert!(top_level_index_width::<C>() <= nr_pte_index_bits::<C>(),);
598        assert!(C::TOP_LEVEL_INDEX_RANGE().start < 1 << top_level_index_width::<C>());
599        assert!(C::TOP_LEVEL_INDEX_RANGE().end <= 1 << top_level_index_width::<C>());
600    };*/
601    let mut start = pt_va_range_start::<C>();
602    let mut end = pt_va_range_end::<C>();
603
604    /*    const {
605        assert!(
606            !C::VA_SIGN_EXT()
607                || sign_bit_of_va::<C>(pt_va_range_start::<C>())
608                    == sign_bit_of_va::<C>(pt_va_range_end::<C>()),
609            "The sign bit of both range endpoints must be the same if sign extension is enabled"
610        )
611    }*/
612
613    if C::VA_SIGN_EXT() && sign_bit_of_va::<C>(pt_va_range_start::<C>()) {
614        start |= !0 ^ ((1 << C::ADDRESS_WIDTH()) - 1);
615        end |= !0 ^ ((1 << C::ADDRESS_WIDTH()) - 1);
616    }
617    start..end + 1
618}
619
620/// Checks if the given range is covered by the valid range of the page table.
621#[verifier::external_body]
622fn is_valid_range<C: PageTableConfig>(r: &Range<Vaddr>) -> (ret: bool)
623    ensures
624        ret == is_valid_range_spec::<C>(r),
625{
626    let va_range = vaddr_range::<C>();
627    (r.start == 0 && r.end == 0) || (va_range.start <= r.start && r.end - 1 <= va_range.end)
628}
629
630// Here are some const values that are determined by the paging constants.
631/// The index of a VA's PTE in a page table node at the given level.
632#[verifier::external_body]
633fn pte_index<C: PagingConstsTrait>(va: Vaddr, level: PagingLevel) -> (res: usize)
634    ensures
635        res == AbstractVaddr::from_vaddr(va).index[level - 1],
636{
637    (va >> pte_index_bit_offset::<C>(level)) & (nr_subpage_per_huge::<C>() - 1)
638}
639
640/// The bit offset of the entry offset part in a virtual address.
641///
642/// This function returns the bit offset of the least significant bit. Take
643/// x86-64 as an example, the `pte_index_bit_offset(2)` should return 21, which
644/// is 12 (the 4KiB in-page offset) plus 9 (index width in the level-1 table).
645#[verifier::external_body]
646fn pte_index_bit_offset<C: PagingConstsTrait>(level: PagingLevel) -> usize {
647    C::BASE_PAGE_SIZE().ilog2() as usize + nr_pte_index_bits::<C>() * (level as usize - 1)
648}
649
650/* TODO: stub out UserPtConfig
651
652impl PageTable<UserPtConfig> {
653    pub fn activate(&self) {
654        // SAFETY: The user mode page table is safe to activate since the kernel
655        // mappings are shared.
656        unsafe {
657            self.root.activate();
658        }
659    }
660}*/
661
662/* TODO: return here after kspace
663impl PageTable<KernelPtConfig> {
664    /// Create a new kernel page table.
665    pub(crate) fn new_kernel_page_table() -> Self {
666        let kpt = Self::empty();
667
668        // Make shared the page tables mapped by the root table in the kernel space.
669        {
670            let preempt_guard = disable_preempt();
671            let mut root_node = kpt.root.borrow().lock(&preempt_guard);
672
673            for i in KernelPtConfig::TOP_LEVEL_INDEX_RANGE {
674                let mut root_entry = root_node.entry(i);
675                let _ = root_entry.alloc_if_none(&preempt_guard).unwrap();
676            }
677        }
678
679        kpt
680    }
681
682    /// Create a new user page table.
683    ///
684    /// This should be the only way to create the user page table, that is to
685    /// duplicate the kernel page table with all the kernel mappings shared.
686    pub(in crate::mm) fn create_user_page_table(&'static self) -> PageTable<UserPtConfig> {
687        let new_root = PageTableNode::alloc(PagingConsts::NR_LEVELS);
688
689        let preempt_guard = disable_preempt();
690        let mut root_node = self.root.borrow().lock(&preempt_guard);
691        let mut new_node = new_root.borrow().lock(&preempt_guard);
692
693        const {
694            assert!(!KernelPtConfig::TOP_LEVEL_CAN_UNMAP);
695            assert!(
696                UserPtConfig::TOP_LEVEL_INDEX_RANGE.end
697                    <= KernelPtConfig::TOP_LEVEL_INDEX_RANGE.start
698            );
699        }
700
701        for i in KernelPtConfig::TOP_LEVEL_INDEX_RANGE {
702            let root_entry = root_node.entry(i);
703            let child = root_entry.to_ref();
704            let ChildRef::PageTable(pt) = child else {
705                panic!("The kernel page table doesn't contain shared nodes");
706            };
707
708            // We do not add additional reference count specifically for the
709            // shared kernel page tables. It requires user page tables to
710            // outlive the kernel page table, which is trivially true.
711            // See also `<PageTablePageMeta as AnyFrameMeta>::on_drop`.
712            let pt_addr = pt.start_paddr();
713            let pte = PageTableEntry::new_pt(pt_addr);
714            // SAFETY: The index is within the bounds and the PTE is at the
715            // correct paging level. However, neither it's a `UserPtConfig`
716            // child nor the node has the ownership of the child. It is
717            // still safe because `UserPtConfig::TOP_LEVEL_INDEX_RANGE`
718            // guarantees that the cursor won't access it.
719            unsafe { new_node.write_pte(i, pte) };
720        }
721        drop(new_node);
722
723        PageTable::<UserPtConfig> { root: new_root }
724    }
725
726    /// Protect the given virtual address range in the kernel page table.
727    ///
728    /// This method flushes the TLB entries when doing protection.
729    ///
730    /// # Safety
731    ///
732    /// The caller must ensure that the protection operation does not affect
733    /// the memory safety of the kernel.
734    pub unsafe fn protect_flush_tlb(
735        &self,
736        vaddr: &Range<Vaddr>,
737        mut op: impl FnMut(&mut PageProperty),
738    ) -> Result<(), PageTableError> {
739        let preempt_guard = disable_preempt();
740        let mut cursor = CursorMut::new(self, &preempt_guard, vaddr)?;
741        // SAFETY: The safety is upheld by the caller.
742        while let Some(range) =
743            unsafe { cursor.protect_next(vaddr.end - cursor.virt_addr(), &mut op) }
744        {
745            crate::arch::mm::tlb_flush_addr(range.start);
746        }
747        Ok(())
748    }
749}*/
750
751impl<C: PageTableConfig> PageTable<C> {
752    pub uninterp spec fn root_paddr_spec(&self) -> Paddr;
753
754    /// Create a new empty page table.
755    ///
756    /// Useful for the IOMMU page tables only.
757    #[verifier::external_body]
758    pub fn empty() -> Self {
759        unimplemented!()/*        PageTable {
760            root: PageTableNode::alloc(C::NR_LEVELS()),
761        }*/
762
763    }
764
765    #[verifier::external_body]
766    pub(in crate::mm) unsafe fn first_activate_unchecked(&self) {
767        unimplemented!()
768        // SAFETY: The safety is upheld by the caller.
769        //        unsafe { self.root.first_activate() };
770
771    }
772
773    /// The physical address of the root page table.
774    ///
775    /// Obtaining the physical address of the root page table is safe, however, using it or
776    /// providing it to the hardware will be unsafe since the page table node may be dropped,
777    /// resulting in UAF.
778    #[verifier::external_body]
779    #[verifier::when_used_as_spec(root_paddr_spec)]
780    pub fn root_paddr(&self) -> (r: Paddr)
781        returns
782            self.root_paddr_spec(),
783    {
784        unimplemented!()
785        //        self.root.start_paddr()
786
787    }
788
789    /// Query about the mapping of a single byte at the given virtual address.
790    ///
791    /// Note that this function may fail reflect an accurate result if there are
792    /// cursors concurrently accessing the same virtual address range, just like what
793    /// happens for the hardware MMU walk.
794    #[cfg(ktest)]
795    pub fn page_walk(&self, vaddr: Vaddr) -> Option<(Paddr, PageProperty)> {
796        // SAFETY: The root node is a valid page table node so the address is valid.
797        unsafe { page_walk::<C>(self.root_paddr(), vaddr) }
798    }
799
800    /// Create a new cursor exclusively accessing the virtual address range for mapping.
801    ///
802    /// If another cursor is already accessing the range, the new cursor may wait until the
803    /// previous cursor is dropped.
804    #[verus_spec(r =>
805        with Tracked(owner): Tracked<PageTableOwner<C>>,
806            Tracked(guard_perm): Tracked<GuardPerm<'rcu, C>>,
807            Tracked(regions): Tracked<&mut MetaRegionOwners>,
808            Tracked(guards): Tracked<&mut Guards<'rcu, C>>
809        requires
810        ensures
811            Cursor::<C, G>::cursor_new_success_conditions(va) ==> {
812                &&& r is Ok
813                &&& r.unwrap().0.inner.invariants(*r.unwrap().1, *regions, *guards)
814                &&& r.unwrap().1.in_locked_range()
815                &&& r.unwrap().0.inner.level < r.unwrap().0.inner.guard_level
816                &&& r.unwrap().0.inner.guard_level == NR_LEVELS as PagingLevel
817                &&& r.unwrap().0.inner.va < r.unwrap().0.inner.barrier_va.end
818                &&& r.unwrap().0.inner.va == va.start
819                &&& r.unwrap().0.inner.barrier_va == *va
820            },
821            forall |item: C::Item| #![trigger CursorMut::<'rcu, C, G>::item_not_mapped(item, *old(regions))]
822                CursorMut::<'rcu, C, G>::item_not_mapped(item, *old(regions)) ==>
823                CursorMut::<'rcu, C, G>::item_not_mapped(item, *regions),
824            // cursor_mut only locks page-table node slots; path_if_in_pt is unchanged for all slots.
825            forall |idx: usize| #![auto]
826                (*regions).slot_owners[idx].path_if_in_pt == (*old(regions)).slot_owners[idx].path_if_in_pt,
827    )]
828    #[verifier::external_body]
829    pub fn cursor_mut<'rcu, G: InAtomicMode>(
830        &'rcu self,
831        guard: &'rcu G,
832        va: &Range<Vaddr>,
833    ) -> Result<(CursorMut<'rcu, C, G>, Tracked<CursorOwner<'rcu, C>>), PageTableError> {
834        #[verus_spec(with Tracked(owner), Tracked(guard_perm), Tracked(regions), Tracked(guards))]
835        CursorMut::new(self, guard, va)
836    }
837
838    /// Create a new cursor exclusively accessing the virtual address range for querying.
839    ///
840    /// If another cursor is already accessing the range, the new cursor may wait until the
841    /// previous cursor is dropped. The modification to the mapping by the cursor may also
842    /// block or be overridden by the mapping of another cursor.
843    #[verus_spec(r =>
844        with Tracked(owner): Tracked<PageTableOwner<C>>,
845            Tracked(guard_perm): Tracked<GuardPerm<'rcu, C>>,
846            Tracked(regions): Tracked<&mut MetaRegionOwners>,
847            Tracked(guards): Tracked<&mut Guards<'rcu, C>>
848        requires
849            owner.inv(),
850        ensures
851            Cursor::<C, G>::cursor_new_success_conditions(va) ==> r is Ok
852    )]
853    pub fn cursor<'rcu, G: InAtomicMode>(&'rcu self, guard: &'rcu G, va: &Range<Vaddr>)
854    -> Result<(Cursor<'rcu, C, G>, Tracked<CursorOwner<'rcu, C>>), PageTableError> {
855        #[verus_spec(with Tracked(owner), Tracked(guard_perm), Tracked(regions), Tracked(guards))]
856        Cursor::new(self, guard, va)
857    }
858    
859    /*
860    /// Create a new reference to the same page table.
861    /// The caller must ensure that the kernel page table is not copied.
862    /// This is only useful for IOMMU page tables. Think twice before using it in other cases.
863    pub unsafe fn shallow_copy(&self) -> Self {
864        PageTable {
865            root: self.root.clone(),
866        }
867    }
868    */
869
870}
871
872/// A software emulation of the MMU address translation process.
873///
874/// This method returns the physical address of the given virtual address and
875/// the page property if a valid mapping exists for the given virtual address.
876///
877/// # Safety
878///
879/// The caller must ensure that the `root_paddr` is a pointer to a valid root
880/// page table node.
881///
882/// # Notes on the page table use-after-free problem
883///
884/// Neither the hardware MMU nor the software page walk method acquires the page
885/// table locks while reading. They can enter a to-be-recycled page table node
886/// and read the page table entries after the node is recycled and reused.
887///
888/// For the hardware MMU page walk, we mitigate this problem by dropping the page
889/// table nodes only after the TLBs have been flushed on all the CPUs that
890/// activate the page table.
891///
892/// For the software page walk, we only need to disable preemption at the beginning
893/// since the page table nodes won't be recycled in the RCU critical section.
894#[cfg(ktest)]
895pub(super) unsafe fn page_walk<C: PageTableConfig>(root_paddr: Paddr, vaddr: Vaddr) -> Option<
896    (Paddr, PageProperty),
897> {
898    use super::paddr_to_vaddr;
899
900    let _rcu_guard = disable_preempt();
901
902    let mut pt_addr = paddr_to_vaddr(root_paddr);
903    #[verusfmt::skip]
904    for cur_level in (1..= C::NR_LEVELS()).rev() {
905        let offset = pte_index::<C>(vaddr, cur_level);
906        // SAFETY:
907        //  - The page table node is alive because (1) the root node is alive and
908        //    (2) all child nodes cannot be recycled because we're in the RCU critical section.
909        //  - The index is inside the bound, so the page table entry is valid.
910        //  - All page table entries are aligned and accessed with atomic operations only.
911        let cur_pte = unsafe { load_pte((pt_addr as *mut C::E).add(offset), Ordering::Acquire) };
912
913        if !cur_pte.is_present() {
914            return None;
915        }
916        if cur_pte.is_last(cur_level) {
917            debug_assert!(cur_level <= C::HIGHEST_TRANSLATION_LEVEL);
918            return Some(
919                (cur_pte.paddr() + (vaddr & (page_size::<C>(cur_level) - 1)), cur_pte.prop()),
920            );
921        }
922        pt_addr = paddr_to_vaddr(cur_pte.paddr());
923    }
924
925    unreachable!("All present PTEs at the level 1 must be last-level PTEs");
926}
927
928/// Loads a page table entry with an atomic instruction.
929///
930/// # Verification Design
931/// ## Preconditions
932/// - The pointer must be a valid pointer to the array that represents the page table node.
933/// - The array must be initialized at the target index.
934/// ## Postconditions
935/// - The value is loaded from the array at the given index.
936/// ## Safety
937/// - We require the caller to provide a permission token to ensure that this function is only called on a valid array
938/// and the pointer is in bounds.
939/// - Like an `AtomicUsize::load` in normal Rust, this function assumes that the value being loaded is an integer
940/// (and therefore can be safely cloned). We model the PTE as an abstract type, but in all actual implementations it is an
941/// integer. Importantly, it does not include any data that is unsafe to duplicate.
942#[verifier::external_body]
943#[verus_spec(
944    with Tracked(perm): Tracked<&vstd_extra::array_ptr::PointsTo<E, NR_ENTRIES>>
945    requires
946        perm.is_init(ptr.index as int),
947        perm.addr() == ptr.addr(),
948        0 <= ptr.index < NR_ENTRIES,
949    returns
950        perm.value()[ptr.index as int],
951)]
952pub fn load_pte<E: PageTableEntryTrait>(
953    ptr: vstd_extra::array_ptr::ArrayPtr<E, NR_ENTRIES>,
954    ordering: Ordering,
955) -> (pte: E) {
956    unimplemented!()
957}
958
959/// Stores a page table entry with an atomic instruction.
960///
961/// # Verification Design
962/// We axiomatize this function as a store operation in the array that represents the page table node.
963/// ## Preconditions
964/// - The pointer must be a valid pointer to the array that represents the page table node.
965/// - The array must be initialized so that the verifier knows that it remains initialized after the store.
966/// ## Postconditions
967/// - The new value is stored in the array at the given index.
968/// ## Safety
969/// - We require the caller to provide a permission token to ensure that this function is only called on a valid array
970/// and the pointer is in bounds.
971#[verifier::external_body]
972#[verus_spec(
973    with Tracked(perm): Tracked<&mut vstd_extra::array_ptr::PointsTo<E, NR_ENTRIES>>
974    requires
975        old(perm).addr() == ptr.addr(),
976        0 <= ptr.index < NR_ENTRIES,
977        old(perm).is_init_all(),
978    ensures
979        perm.value()[ptr.index as int] == new_val,
980        perm.value() == old(perm).value().update(ptr.index as int, new_val),
981        perm.addr() == old(perm).addr(),
982        perm.is_init_all(),
983)]
984pub fn store_pte<E: PageTableEntryTrait>(
985    ptr: vstd_extra::array_ptr::ArrayPtr<E, NR_ENTRIES>,
986    new_val: E,
987    ordering: Ordering,
988);
989
990} // verus!