Skip to main content

ostd/mm/kspace/
mod.rs

1// SPDX-License-Identifier: MPL-2.0
2//! Kernel memory space management.
3//!
4//! The kernel memory space is currently managed as follows, if the
5//! address width is 48 bits (with 47 bits kernel space).
6//!
7//! TODO: the cap of linear mapping (the start of vm alloc) are raised
8//! to workaround for high IO in TDX. We need actual vm alloc API to have
9//! a proper fix.
10//!
11//! ```text
12//! +-+ <- the highest used address (0xffff_ffff_ffff_0000)
13//! | |         For the kernel code, 1 GiB.
14//! +-+ <- 0xffff_ffff_8000_0000
15//! | |
16//! | |         Unused hole.
17//! +-+ <- 0xffff_e100_0000_0000
18//! | |         For frame metadata, 1 TiB.
19//! +-+ <- 0xffff_e000_0000_0000
20//! | |         For [`KVirtArea`], 32 TiB.
21//! +-+ <- the middle of the higher half (0xffff_c000_0000_0000)
22//! | |
23//! | |
24//! | |
25//! | |         For linear mappings, 64 TiB.
26//! | |         Mapped physical addresses are untracked.
27//! | |
28//! | |
29//! | |
30//! +-+ <- the base of high canonical address (0xffff_8000_0000_0000)
31//! ```
32//!
33//! If the address width is (according to [`crate::arch::mm::PagingConsts`])
34//! 39 bits or 57 bits, the memory space just adjust proportionally.
35use core::ops::Range;
36use vstd::atomic::PermissionU64;
37use vstd::prelude::*;
38use vstd::simple_pptr::PointsTo;
39
40//use log::info;
41use crate::sync::{OnceImpl, TrivialPred};
42pub(crate) mod kvirt_area;
43#[cfg(ktest)]
44mod test;
45
46use super::{
47    Paddr, PagingConstsTrait, Vaddr,
48    frame::{
49        Frame, Segment,
50        meta::{AnyFrameMeta, MetaPageMeta, MetaSlot, mapping},
51    },
52    page_prop::{CachePolicy, PageFlags, PageProperty, PrivilegedPageFlags},
53    page_table::{PageTable, PageTableConfig},
54};
55use crate::mm::frame::DynFrame;
56use crate::mm::page_table::RCClone;
57use crate::specs::arch::*;
58use crate::specs::mm::frame::meta_region_owners::MetaRegionOwners;
59use crate::specs::mm::frame::{
60    mapping::group_page_meta,
61    meta_owners::{MetaPerm, MetaSlotStorage},
62};
63use crate::{
64    arch::mm::{PageTableEntry, PagingConsts},
65    boot::memory_region::MemoryRegionType,
66    mm::{PagingLevel, largest_pages},
67    //task::disable_preempt,
68};
69
70use vstd_extra::ownership::*;
71
72verus! {
73
74/// The shortest supported address width is 39 bits. And the literal
75/// values are written for 48 bits address width. Adjust the values
76/// by arithmetic left shift.
77pub const ADDR_WIDTH_SHIFT: isize = 48 - 48;
78
79/// Start of the kernel address space.
80/// This is the _lowest_ address of the x86-64's _high_ canonical addresses.
81#[cfg(not(target_arch = "loongarch64"))]
82pub const KERNEL_BASE_VADDR: Vaddr = 0xffff_8000_0000_0000 << ADDR_WIDTH_SHIFT;
83
84#[cfg(target_arch = "loongarch64")]
85pub const KERNEL_BASE_VADDR: Vaddr = 0x9000_0000_0000_0000 << ADDR_WIDTH_SHIFT;
86
87/// End of the kernel address space (non inclusive).
88pub const KERNEL_END_VADDR: Vaddr = 0xffff_ffff_ffff_0000 << ADDR_WIDTH_SHIFT;
89
90/*
91/// The kernel code is linear mapped to this address.
92///
93/// FIXME: This offset should be randomly chosen by the loader or the
94/// boot compatibility layer. But we disabled it because OSTD
95/// doesn't support relocatable kernel yet.
96pub fn kernel_loaded_offset() -> usize {
97    KERNEL_CODE_BASE_VADDR
98}*/
99
100#[cfg(target_arch = "x86_64")]
101const KERNEL_CODE_BASE_VADDR: usize = 0xffff_ffff_8000_0000 << ADDR_WIDTH_SHIFT;
102
103#[cfg(target_arch = "riscv64")]
104const KERNEL_CODE_BASE_VADDR: usize = 0xffff_ffff_0000_0000 << ADDR_WIDTH_SHIFT;
105
106#[cfg(target_arch = "loongarch64")]
107const KERNEL_CODE_BASE_VADDR: usize = 0x9000_0000_0000_0000 << ADDR_WIDTH_SHIFT;
108
109pub const FRAME_METADATA_CAP_VADDR: Vaddr = 0xffff_e100_0000_0000 << ADDR_WIDTH_SHIFT;
110
111pub const FRAME_METADATA_BASE_VADDR: Vaddr = 0xffff_e000_0000_0000 << ADDR_WIDTH_SHIFT;
112
113pub const FRAME_METADATA_RANGE: Range<Vaddr> = 0xffff_e000_0000_0000..0xffff_e100_0000_0000;
114
115pub const VMALLOC_BASE_VADDR: Vaddr = 0xffff_c000_0000_0000 << ADDR_WIDTH_SHIFT;
116
117pub const VMALLOC_VADDR_RANGE: Range<Vaddr> = VMALLOC_BASE_VADDR..FRAME_METADATA_BASE_VADDR;
118
119/// The base address of the linear mapping of all physical
120/// memory in the kernel address space.
121pub const LINEAR_MAPPING_BASE_VADDR: Vaddr = 0xffff_8000_0000_0000 << ADDR_WIDTH_SHIFT;
122
123pub const LINEAR_MAPPING_VADDR_RANGE: Range<Vaddr> = LINEAR_MAPPING_BASE_VADDR..VMALLOC_BASE_VADDR;
124
125/*
126#[cfg(not(target_arch = "loongarch64"))]
127pub const LINEAR_MAPPING_BASE_VADDR: Vaddr = 0xffff_8000_0000_0000 << ADDR_WIDTH_SHIFT;
128#[cfg(target_arch = "loongarch64")]
129pub const LINEAR_MAPPING_BASE_VADDR: Vaddr = 0x9000_0000_0000_0000 << ADDR_WIDTH_SHIFT;
130pub const LINEAR_MAPPING_VADDR_RANGE: Range<Vaddr> = LINEAR_MAPPING_BASE_VADDR..VMALLOC_BASE_VADDR;
131*/
132
133/// Convert physical address to virtual address using offset, only available inside `ostd`
134pub open spec fn paddr_to_vaddr_spec(pa: Paddr) -> usize {
135    (pa + LINEAR_MAPPING_BASE_VADDR) as usize
136}
137
138#[verifier::when_used_as_spec(paddr_to_vaddr_spec)]
139pub fn paddr_to_vaddr(pa: Paddr) -> usize
140    requires
141        pa + LINEAR_MAPPING_BASE_VADDR < usize::MAX,
142    returns
143        paddr_to_vaddr_spec(pa),
144{
145    //debug_assert!(pa < VMALLOC_BASE_VADDR - LINEAR_MAPPING_BASE_VADDR);
146    pa + LINEAR_MAPPING_BASE_VADDR
147}
148
149/// The kernel page table instance.
150///
151/// It manages the kernel mapping of all address spaces by sharing the kernel part. And it
152/// is unlikely to be activated.
153#[allow(private_interfaces)]
154pub exec static KERNEL_PAGE_TABLE: OnceImpl<PageTable<KernelPtConfig>, TrivialPred> = OnceImpl::new(
155    Ghost(TrivialPred),
156);
157
158#[verifier::allow(autoderive_clone_without_spec)]
159#[derive(Clone, Debug)]
160pub(crate) struct KernelPtConfig {}
161
162// We use the first available PTE bit to mark the frame as tracked.
163// SAFETY: `item_into_raw` and `item_from_raw` are implemented correctly,
164unsafe impl PageTableConfig for KernelPtConfig {
165    open spec fn TOP_LEVEL_INDEX_RANGE_spec() -> Range<usize> {
166        256..512
167    }
168
169    open spec fn LEADING_BITS_spec() -> usize {
170        0xffff
171    }
172
173    proof fn lemma_page_table_config_constant_requirements() {
174        use crate::mm::nr_subpage_per_huge;
175        use crate::mm::page_table::{nr_pte_index_bits, pte_index_bit_offset_spec};
176        use vstd::arithmetic::power2::{lemma2_to64, lemma2_to64_rest, lemma_pow2_adds, pow2};
177        use vstd_extra::prelude::lemma_usize_pow2_ilog2;
178
179        lemma2_to64();
180        lemma2_to64_rest();
181        assert(usize::BITS == 64) by (compute);
182        vstd::layout::unsigned_int_max_values();
183        lemma_usize_pow2_ilog2(12);
184        lemma_usize_pow2_ilog2(9);
185        lemma_pow2_adds(9, 39);
186        lemma_pow2_adds(8, 39);
187        assert(nr_subpage_per_huge::<PagingConsts>() == 512_usize);
188        assert(nr_pte_index_bits::<PagingConsts>() == 9_usize);
189        assert(PagingConsts::BASE_PAGE_SIZE().ilog2() == 12u32);
190        assert(pte_index_bit_offset_spec::<PagingConsts>(4) == 39);
191        assert(256 * pow2(39) == pow2(47));
192        assert((256 * pow2(39) as int) / (pow2(47) as int) == 1);
193        assert(pte_index_bit_offset_spec::<Self::C>(Self::C::NR_LEVELS()) == 39);
194        assert(Self::TOP_LEVEL_INDEX_RANGE_spec().start * (pow2(
195            pte_index_bit_offset_spec::<Self::C>(Self::C::NR_LEVELS()) as nat,
196        )) == pow2(47));
197        assert(((Self::TOP_LEVEL_INDEX_RANGE_spec().start * pow2(
198            pte_index_bit_offset_spec::<Self::C>(Self::C::NR_LEVELS()) as nat,
199        )) / (pow2((Self::C::ADDRESS_WIDTH() - 1) as nat) as int)) == 1);
200        assert(((Self::TOP_LEVEL_INDEX_RANGE_spec().start * pow2(
201            pte_index_bit_offset_spec::<Self::C>(Self::C::NR_LEVELS()) as nat,
202        )) / (pow2((Self::C::ADDRESS_WIDTH() - 1) as nat) as int)) % 2 == 1);
203        lemma_pow2_adds(16, 48);
204        assert(Self::LEADING_BITS_spec() * 0x1_0000_0000_0000int == 0x1_0000_0000_0000_0000int
205            - pow2(Self::C::ADDRESS_WIDTH() as nat));
206    }
207
208    fn TOP_LEVEL_INDEX_RANGE() -> (r: Range<usize>) {
209        256..512
210    }
211
212    open spec fn TOP_LEVEL_CAN_UNMAP_spec() -> bool {
213        false
214    }
215
216    fn TOP_LEVEL_CAN_UNMAP() -> (b: bool) {
217        false
218    }
219
220    // The kvirt allocator (the only source of kernel-PT cursor ranges) caps
221    // `range.end` at FRAME_METADATA_BASE_VADDR — see `kvirt_alloc_range_bounds`.
222    open spec fn LOCKED_END_BOUND_spec() -> int {
223        FRAME_METADATA_BASE_VADDR as int
224    }
225
226    type E = PageTableEntry;
227
228    type C = PagingConsts;
229
230    type Item = MappedItem;
231
232    uninterp spec fn item_into_raw_spec(item: Self::Item) -> (Paddr, PagingLevel, PageProperty);
233
234    //    #[verifier::when_used_as_spec(item_into_raw_spec)]
235    #[verifier::external_body]
236    fn item_into_raw(item: Self::Item) -> (res: (Paddr, PagingLevel, PageProperty))
237        ensures
238            1 <= res.1 <= crate::specs::arch::NR_LEVELS,
239            res == Self::item_into_raw_spec(item),
240    {
241        match item {
242            MappedItem::Tracked(frame, mut prop) => {
243                debug_assert!(!prop.flags.contains(PageFlags::AVAIL1()));
244                prop.flags = prop.flags | PageFlags::AVAIL1();
245                let level = frame.map_level();
246                let paddr = frame.into_raw();
247                (paddr, level, prop)
248            },
249            MappedItem::Untracked(pa, level, mut prop) => {
250                debug_assert!(!prop.flags.contains(PageFlags::AVAIL1()));
251                prop.flags = prop.flags - PageFlags::AVAIL1();
252                (pa, level, prop)
253            },
254        }
255    }
256
257    uninterp spec fn item_from_raw_spec(
258        paddr: Paddr,
259        level: PagingLevel,
260        prop: PageProperty,
261    ) -> Self::Item;
262
263    //#[verifier::when_used_as_spec(item_from_raw_spec)]
264    #[verifier::external_body]
265    unsafe fn item_from_raw(paddr: Paddr, level: PagingLevel, prop: PageProperty) -> (res:
266        Self::Item)
267        ensures
268            res == Self::item_from_raw_spec(paddr, level, prop),
269    {
270        if prop.flags.contains(PageFlags::AVAIL1()) {
271            debug_assert_eq!(level, 1);
272            // SAFETY: The caller ensures safety.
273            let frame = unsafe { Frame::<MetaSlotStorage>::from_raw(paddr) };
274            MappedItem::Tracked(frame, prop)
275        } else {
276            MappedItem::Untracked(paddr, level, prop)
277        }
278    }
279
280    proof fn lemma_pte_size_eq_size_of() {
281        PageTableEntry::lemma_layout();
282    }
283
284    proof fn lemma_pte_walk_fills_page() {
285        Self::lemma_page_table_config_constant_properties();
286        Self::lemma_pte_size_eq_size_of();
287    }
288
289    proof fn lemma_pte_align_divides_size() {
290        PageTableEntry::lemma_layout();
291    }
292
293    axiom fn item_roundtrip(item: Self::Item, paddr: Paddr, level: PagingLevel, prop: PageProperty);
294
295    open spec fn tracked(item: Self::Item) -> bool {
296        // Tracked items hold a reference; clone bumps rc. Untracked items
297        // (MMIO frames) are not ref-counted; clone is a no-op.
298        item is Tracked
299    }
300
301    open spec fn item_well_formed(item: Self::Item) -> bool {
302        match item {
303            MappedItem::Tracked(frame, _) => frame.inv(),
304            MappedItem::Untracked(_, _, _) => true,
305        }
306    }
307
308    proof fn item_from_raw_well_formed(pa: Paddr, level: PagingLevel, prop: PageProperty) {
309        broadcast use group_page_meta;
310
311        let item = Self::item_from_raw_spec(pa, level, prop);
312        if prop.flags.contains(crate::mm::page_prop::PageFlags::AVAIL1()) {
313            // Tracked branch: derive `frame.inv()` from
314            //   - `item_from_raw_spec_tracked_ptr`: frame.ptr.addr() == frame_to_meta(pa).
315            //   - `lemma_frame_to_meta_soundness` (broadcast): frame_to_meta(pa) is
316            //     META_SLOT_SIZE-aligned and within FRAME_METADATA_RANGE.
317            Self::item_from_raw_spec_tracked_ptr(pa, level, prop);
318            // Now item is `MappedItem::Tracked(frame, _)` with the address fact.
319            match item {
320                MappedItem::Tracked(frame, _) => {
321                    assert(frame.ptr.addr() == crate::mm::frame::meta::mapping::frame_to_meta(pa));
322                    // frame.inv() unfolds to (alignment + range), both from the lemma.
323                    assert(frame.inv());
324                },
325                MappedItem::Untracked(_, _, _) => {
326                    // Excluded by item_from_raw_spec_tracked_ptr.
327                    assert(false);
328                },
329            }
330        } else {
331            // Untracked branch: item_well_formed is `true`.
332            Self::item_from_raw_spec_untracked_variant(pa, level, prop);
333        }
334    }
335
336    proof fn clone_ensures_concrete(
337        item: Self::Item,
338        pa: Paddr,
339        old_regions: MetaRegionOwners,
340        new_regions: MetaRegionOwners,
341        res: Self::Item,
342    ) {
343        // `MappedItem::clone_ensures` case-analyzes:
344        //   - Tracked: `frame.clone_ensures(old, new, res_frame)` — gives per-field facts at
345        //     `frame_to_index(meta_to_frame(frame.ptr.addr()))`. Bridge to `frame_to_index(pa)`.
346        //   - Untracked: `old == new`. Then trait's `rc + 1` ensures becomes contradictory.
347        //     This case is ASSUMED unreachable (clone_item only runs on Tracked items in
348        //     the verified call chain).
349        // Force the trait method's open-spec body to unfold by asserting the UFCS form.
350        assert(<MappedItem as RCClone>::clone_ensures(item, old_regions, new_regions, res));
351        match (item, res) {
352            (MappedItem::Tracked(frame, prop_actual), MappedItem::Tracked(res_frame, _)) => {
353                use crate::mm::frame::meta::mapping::meta_to_frame;
354                use crate::specs::mm::frame::mapping::frame_to_index;
355                Self::item_into_raw_spec_tracked_pa(frame, prop_actual);
356                let frame_idx = frame_to_index(meta_to_frame(frame.ptr.addr()));
357                assert(pa == meta_to_frame(frame.ptr.addr()));
358                assert(frame_to_index(pa) == frame_idx);
359                assert(frame.clone_ensures(old_regions, new_regions, res_frame));
360                // Canonical: the cloned frame minted one obligation at its slot.
361                assert(new_regions.frame_obligations == old_regions.frame_obligations.insert(
362                    frame_idx,
363                ));
364            },
365            (MappedItem::Untracked(_, _, _), _) => {
366                // clone_ensures for Untracked is `old == new`; the trait's
367                // `!tracked ==> slot unchanged` ensures follows directly, and
368                // the per-frame ledger is preserved (net-zero clone).
369                assert(old_regions == new_regions);
370            },
371            _ => {
372                // res == item by precondition; if item is Tracked, res is Tracked too.
373                // The mismatched-tag arm is unreachable.
374                assert(res == item);
375            },
376        }
377    }
378
379    proof fn clone_requires_concrete(
380        item: Self::Item,
381        pa: Paddr,
382        level: PagingLevel,
383        prop: PageProperty,
384        regions: MetaRegionOwners,
385    ) {
386        // `MappedItem::clone_requires` case-analyzes:
387        //   - Tracked: `frame.clone_requires(regions)` — needs `frame.inv()` and the
388        //     slot facts at `frame_to_index(meta_to_frame(frame.ptr.addr()))`.
389        //   - Untracked: `regions.inv()`. Trivially satisfied from precondition.
390        // Discharge `frame.inv()` via the trait-level structural well-formedness method.
391        Self::item_from_raw_well_formed(pa, level, prop);
392        match item {
393            MappedItem::Tracked(frame, prop_actual) => {
394                use crate::mm::frame::meta::mapping::meta_to_frame;
395                use crate::specs::mm::frame::mapping::frame_to_index;
396                Self::item_into_raw_spec_tracked_pa(frame, prop_actual);
397                Self::item_roundtrip(item, pa, level, prop);
398                assert(meta_to_frame(frame.ptr.addr()) == pa);
399                assert(frame_to_index(meta_to_frame(frame.ptr.addr())) == frame_to_index(pa));
400                // `Self::item_well_formed(item)` unfolds to `frame.inv()` for the
401                // Tracked variant.
402                assert(frame.inv());
403            },
404            MappedItem::Untracked(_, _, _) => {
405                // clone_requires for Untracked is just `regions.inv()` which we have.
406            },
407        }
408    }
409}
410
411impl KernelPtConfig {
412    /// The spec agrees with the exec, which ensures 1 <= level <= NR_LEVELS.
413    pub proof fn item_into_raw_spec_level_bounds(item: MappedItem)
414        requires
415            item matches MappedItem::Untracked(_, level, _) ==> 1 <= level <= NR_LEVELS,
416        ensures
417            1 <= KernelPtConfig::item_into_raw_spec(item).1 <= crate::specs::arch::NR_LEVELS,
418    {
419        match item {
420            MappedItem::Tracked(_, _) => {
421                Self::item_into_raw_spec_tracked_level(item);
422                // item_into_raw_spec(item).1 == 1; 1 <= 1 <= NR_LEVELS always.
423                assert(1 <= NR_LEVELS);
424            },
425            MappedItem::Untracked(pa, level, prop) => {
426                Self::item_into_raw_spec_untracked(pa, level, prop);
427                // item_into_raw_spec(item).1 == level, bounded by precondition.
428            },
429        }
430    }
431
432    /// Tracked frames use 4K pages (level 1). Used to prove alignment in map_frames.
433    pub axiom fn item_into_raw_spec_tracked_level(item: MappedItem)
434        requires
435            matches!(item, MappedItem::Tracked(_, _)),
436        ensures
437            KernelPtConfig::item_into_raw_spec(item).1 == 1,
438    ;
439
440    /// For untracked items, `item_into_raw_spec` preserves PA, level, and prop.
441    /// This is correct when the AVAIL1 bit is not set in `prop`, which is assumed
442    /// for untracked MMIO frames (enforced by the debug_assert in the exec).
443    pub axiom fn item_into_raw_spec_untracked(pa: Paddr, level: PagingLevel, prop: PageProperty)
444        ensures
445            KernelPtConfig::item_into_raw_spec(MappedItem::Untracked(pa, level, prop)).0 == pa,
446            KernelPtConfig::item_into_raw_spec(MappedItem::Untracked(pa, level, prop)).1 == level,
447            KernelPtConfig::item_into_raw_spec(MappedItem::Untracked(pa, level, prop)).2 == prop,
448    ;
449
450    /// For tracked items, the physical address from item_into_raw_spec equals
451    /// the frame's metadata-derived physical address.
452    pub axiom fn item_into_raw_spec_tracked_pa(frame: DynFrame, prop: PageProperty)
453        ensures
454            KernelPtConfig::item_into_raw_spec(MappedItem::Tracked(frame, prop)).0
455                == crate::mm::frame::meta::mapping::meta_to_frame(frame.ptr.addr()),
456    ;
457
458    /// For tracked items, item_into_raw_spec returns the same `prop` that was passed in.
459    pub axiom fn item_into_raw_spec_tracked_prop(frame: DynFrame, prop: PageProperty)
460        ensures
461            KernelPtConfig::item_into_raw_spec(MappedItem::Tracked(frame, prop)).2 == prop,
462    ;
463
464    /// Structural shape of `item_from_raw_spec` for the Tracked branch: the reconstructed
465    /// frame's pointer is `frame_to_meta(pa)`. This mirrors the exec body of `item_from_raw`,
466    /// which calls `Frame::from_raw(pa)` whose ensures gives `r.ptr.addr() == frame_to_meta(pa)`.
467    /// Once we have this address shape, `Frame::inv()` follows from `lemma_frame_to_meta_soundness`.
468    pub axiom fn item_from_raw_spec_tracked_ptr(pa: Paddr, level: PagingLevel, prop: PageProperty)
469        requires
470            has_safe_slot(pa),
471            prop.flags.contains(crate::mm::page_prop::PageFlags::AVAIL1()),
472        ensures
473            match KernelPtConfig::item_from_raw_spec(pa, level, prop) {
474                MappedItem::Tracked(frame, _) => frame.ptr.addr()
475                    == crate::mm::frame::meta::mapping::frame_to_meta(pa),
476                MappedItem::Untracked(_, _, _) => false,
477            },
478    ;
479
480    /// For untracked items, `item_from_raw_spec` returns the Untracked variant.
481    /// Mirrors the exec body's branch on `prop.flags.contains(AVAIL1)`.
482    pub axiom fn item_from_raw_spec_untracked_variant(
483        pa: Paddr,
484        level: PagingLevel,
485        prop: PageProperty,
486    )
487        requires
488            !prop.flags.contains(crate::mm::page_prop::PageFlags::AVAIL1()),
489        ensures
490            matches!(KernelPtConfig::item_from_raw_spec(pa, level, prop),
491                MappedItem::Untracked(_, _, _)),
492    ;
493
494    /// For KernelPtConfig (x86_64): HIGHEST_TRANSLATION_LEVEL = 2 < NR_LEVELS = 4.
495    pub proof fn lemma_kernel_htl_lt_nr_levels()
496        ensures
497            KernelPtConfig::HIGHEST_TRANSLATION_LEVEL() < NR_LEVELS,
498    {
499        assert(KernelPtConfig::HIGHEST_TRANSLATION_LEVEL() == 2);
500        assert(NR_LEVELS == 4usize);
501    }
502}
503
504/*
505#[derive(Clone, Debug, PartialEq, Eq)]
506pub(crate) enum MappedItem {
507    Tracked(Frame<dyn AnyFrameMeta>, PageProperty),
508    Untracked(Paddr, PagingLevel, PageProperty),
509}
510*/
511
512pub enum MappedItem {
513    Tracked(DynFrame, PageProperty),
514    Untracked(Paddr, PagingLevel, PageProperty),
515}
516
517impl RCClone for MappedItem {
518    open spec fn clone_requires(self, perm: MetaRegionOwners) -> bool {
519        match self {
520            MappedItem::Tracked(frame, _) => frame.clone_requires(perm),
521            MappedItem::Untracked(_, _, _) => perm.inv(),
522        }
523    }
524
525    open spec fn clone_ensures(
526        self,
527        old_perm: MetaRegionOwners,
528        new_perm: MetaRegionOwners,
529        res: Self,
530    ) -> bool {
531        match (self, res) {
532            (
533                MappedItem::Tracked(frame, _),
534                MappedItem::Tracked(res_frame, _),
535            ) => frame.clone_ensures(old_perm, new_perm, res_frame),
536            (MappedItem::Untracked(_, _, _), _) => old_perm == new_perm,
537            _ => true,
538        }
539    }
540
541    #[verifier::external_body]
542    fn clone(&self, Tracked(perm): Tracked<&mut MetaRegionOwners>) -> (res: Self) {
543        unimplemented!();
544    }
545}
546
547} // verus!
548// /// Initializes the kernel page table.
549// ///
550// /// This function should be called after:
551// ///  - the page allocator and the heap allocator are initialized;
552// ///  - the memory regions are initialized.
553// ///
554// /// This function should be called before:
555// ///  - any initializer that modifies the kernel page table.
556// pub fn init_kernel_page_table(meta_pages: Segment<MetaPageMeta>) {
557//     info!("Initializing the kernel page table");
558//     // Start to initialize the kernel page table.
559//     let kpt = PageTable::<KernelPtConfig>::new_kernel_page_table();
560//     let preempt_guard = disable_preempt();
561//     // In LoongArch64, we don't need to do linear mappings for the kernel because of DMW0.
562//     #[cfg(not(target_arch = "loongarch64"))]
563//     // Do linear mappings for the kernel.
564//     {
565//         let max_paddr = crate::mm::frame::max_paddr();
566//         let from = LINEAR_MAPPING_BASE_VADDR..LINEAR_MAPPING_BASE_VADDR + max_paddr;
567//         let prop = PageProperty {
568//             flags: PageFlags::RW,
569//             cache: CachePolicy::Writeback,
570//             priv_flags: PrivilegedPageFlags::GLOBAL,
571//         };
572//         let mut cursor = kpt.cursor_mut(&preempt_guard, &from).unwrap();
573//         for (pa, level) in largest_pages::<KernelPtConfig>(from.start, 0, max_paddr) {
574//             // SAFETY: we are doing the linear mapping for the kernel.
575//             unsafe { cursor.map(MappedItem::Untracked(pa, level, prop)) }
576//                 .expect("Kernel linear address space is mapped twice");
577//         }
578//     }
579//     // Map the metadata pages.
580//     {
581//         let start_va = mapping::frame_to_meta::<PagingConsts>(0);
582//         let from = start_va..start_va + meta_pages.size();
583//         let prop = PageProperty {
584//             flags: PageFlags::RW,
585//             cache: CachePolicy::Writeback,
586//             priv_flags: PrivilegedPageFlags::GLOBAL,
587//         };
588//         let mut cursor = kpt.cursor_mut(&preempt_guard, &from).unwrap();
589//         // We use untracked mapping so that we can benefit from huge pages.
590//         // We won't unmap them anyway, so there's no leaking problem yet.
591//         // TODO: support tracked huge page mapping.
592//         let pa_range = meta_pages.into_raw();
593//         for (pa, level) in
594//             largest_pages::<KernelPtConfig>(from.start, pa_range.start, pa_range.len())
595//         {
596//             // SAFETY: We are doing the metadata mappings for the kernel.
597//             unsafe { cursor.map(MappedItem::Untracked(pa, level, prop)) }
598//                 .expect("Frame metadata address space is mapped twice");
599//         }
600//     }
601//     // In LoongArch64, we don't need to do linear mappings for the kernel code because of DMW0.
602//     #[cfg(not(target_arch = "loongarch64"))]
603//     // Map for the kernel code itself.
604//     // TODO: set separated permissions for each segments in the kernel.
605//     {
606//         let regions = &crate::boot::EARLY_INFO.get().unwrap().memory_regions;
607//         let region = regions
608//             .iter()
609//             .find(|r| r.typ() == MemoryRegionType::Kernel)
610//             .unwrap();
611//         let offset = kernel_loaded_offset();
612//         let from = region.base() + offset..region.end() + offset;
613//         let prop = PageProperty {
614//             flags: PageFlags::RWX,
615//             cache: CachePolicy::Writeback,
616//             priv_flags: PrivilegedPageFlags::GLOBAL,
617//         };
618//         let mut cursor = kpt.cursor_mut(&preempt_guard, &from).unwrap();
619//         for (pa, level) in largest_pages::<KernelPtConfig>(from.start, region.base(), from.len()) {
620//             // SAFETY: we are doing the kernel code mapping.
621//             unsafe { cursor.map(MappedItem::Untracked(pa, level, prop)) }
622//                 .expect("Kernel code mapped twice");
623//         }
624//     }
625//     KERNEL_PAGE_TABLE.call_once(|| kpt);
626// }
627// /// Activates the kernel page table.
628// ///
629// /// # Safety
630// ///
631// /// This function should only be called once per CPU.
632// pub unsafe fn activate_kernel_page_table() {
633//     let kpt = KERNEL_PAGE_TABLE
634//         .get()
635//         .expect("The kernel page table is not initialized yet");
636//     // SAFETY: the kernel page table is initialized properly.
637//     unsafe {
638//         kpt.first_activate_unchecked();
639//         crate::arch::mm::tlb_flush_all_including_global();
640//     }
641//     // SAFETY: the boot page table is OK to be dismissed now since
642//     // the kernel page table is activated just now.
643//     unsafe {
644//         crate::mm::page_table::boot_pt::dismiss();
645//     }
646// }