Skip to main content

ostd/mm/frame/
meta.rs

1// SPDX-License-Identifier: MPL-2.0
2//! Metadata management of frames.
3//!
4//! You can picture a globally shared, static, gigantic array of metadata
5//! initialized for each frame.
6//! Each entry in this array holds the metadata for a single frame.
7//! There would be a dedicated small
8//! "heap" space in each slot for dynamic metadata. You can store anything as
9//! the metadata of a frame as long as it's [`Sync`].
10//!
11//! # Implementation
12//!
13//! The slots are placed in the metadata pages mapped to a certain virtual
14//! address in the kernel space. So finding the metadata of a frame often
15//! comes with no costs since the translation is a simple arithmetic operation.
16use vstd::atomic::{PAtomicU8, PAtomicU64, PermissionU64};
17use vstd::cell::pcell_maybe_uninit;
18use vstd::prelude::*;
19use vstd::simple_pptr::{self, PPtr};
20use vstd_extra::cast_ptr::*;
21use vstd_extra::ownership::*;
22use vstd_extra::panic::{may_panic, panic_diverge};
23use vstd_extra::prelude::*;
24
25use self::mapping::{META_SLOT_SIZE, frame_to_index, frame_to_meta, meta_addr, meta_to_frame};
26use crate::mm::io::{Infallible, VmReader};
27use crate::specs::mm::frame::meta_owners::*;
28use crate::specs::mm::frame::meta_region_owners::MetaRegionOwners;
29
30verus! {
31
32pub(crate) mod mapping {
33    use crate::mm::frame::MetaSlot;
34    use crate::mm::frame::meta::meta_slot_size;
35    use crate::mm::{PAGE_SIZE, Paddr, Vaddr};
36    use crate::specs::arch::kspace::FRAME_METADATA_RANGE;
37    use crate::specs::arch::mm::MAX_PADDR;
38    pub use crate::specs::mm::frame::mapping::*;
39    use vstd::prelude::*;
40
41    #[verifier::inline]
42    pub open spec fn frame_to_meta_spec(paddr: Paddr) -> Vaddr {
43        (FRAME_METADATA_RANGE.start + (paddr / PAGE_SIZE) * meta_slot_size()) as usize
44    }
45
46    #[verifier::inline]
47    pub open spec fn meta_to_frame_spec(vaddr: Vaddr) -> Paddr {
48        ((vaddr - FRAME_METADATA_RANGE.start) / META_SLOT_SIZE as int * PAGE_SIZE) as usize
49    }
50
51    /// Converts a physical address of a base frame to the virtual address of the metadata slot.
52    #[inline(always)]
53    #[verifier::when_used_as_spec(frame_to_meta_spec)]
54    pub fn frame_to_meta(paddr: Paddr) -> (res: Vaddr)
55        requires
56            paddr % PAGE_SIZE == 0,
57            paddr < MAX_PADDR,
58        ensures
59            res == frame_to_meta_spec(paddr),
60            res % META_SLOT_SIZE == 0,
61    {
62        let base = FRAME_METADATA_RANGE.start;
63        let offset = paddr / PAGE_SIZE;
64        base + offset * META_SLOT_SIZE
65    }
66
67    /// Converts a virtual address of the metadata slot to the physical address of the frame.
68    #[inline(always)]
69    #[verifier::when_used_as_spec(meta_to_frame_spec)]
70    pub fn meta_to_frame(vaddr: Vaddr) -> (res: Paddr)
71        requires
72            FRAME_METADATA_RANGE.start <= vaddr && vaddr < FRAME_METADATA_RANGE.end,
73            vaddr % META_SLOT_SIZE == 0,
74        ensures
75            res == meta_to_frame_spec(vaddr),
76            res % PAGE_SIZE == 0,
77    {
78        let base = FRAME_METADATA_RANGE.start;
79        let offset = (vaddr - base) / META_SLOT_SIZE;
80        offset * PAGE_SIZE
81    }
82
83}
84
85} // verus!
86use core::{
87    alloc::Layout,
88    any::Any,
89    cell::UnsafeCell,
90    fmt::Debug,
91    marker::PhantomData,
92    mem::{ManuallyDrop, MaybeUninit, align_of, size_of},
93    result::Result,
94    sync::atomic::{AtomicU8, AtomicU64, Ordering},
95};
96
97use align_ext::AlignExt;
98//use log::info;
99
100use crate::{
101    //    boot::memory_region::MemoryRegionType,
102    //    const_assert,
103    mm::{
104        MAX_NR_PAGES,
105        MAX_PADDR,
106        /*VmReader,*/ PAGE_SIZE,
107        /*Infallible,*/ Paddr,
108        PagingLevel,
109        //Segment,
110        Vaddr,
111        //        frame::allocator::{self, EarlyAllocatedFrameMeta},
112        paddr_to_vaddr,
113        //        page_table::boot_pt,
114        page_prop::{CachePolicy, PageFlags, PageProperty, PrivilegedPageFlags},
115    },
116    specs::arch::kspace::FRAME_METADATA_RANGE,
117    //    panic::abort,
118    //    util::ops::range_difference,
119};
120
121verus! {
122
123#[repr(C)]
124pub struct MetaSlot {
125    /// The metadata of a frame.
126    ///
127    /// It is placed at the beginning of a slot because:
128    ///  - the implementation can simply cast a `*const MetaSlot`
129    ///    to a `*const AnyFrameMeta` for manipulation;
130    ///  - if the metadata need special alignment, we can provide
131    ///    at most `PAGE_METADATA_ALIGN` bytes of alignment;
132    ///  - the subsequent fields can utilize the padding of the
133    ///    reference count to save space.
134    ///
135    /// # Verification Design
136    /// We model the metadata of the slot as a `MetaSlotStorage`, which is a tagged union of the different
137    /// types of metadata defined in the development.
138    pub storage: pcell_maybe_uninit::PCell<MetaSlotStorage>,
139    /// The reference count of the page.
140    ///
141    /// Specifically, the reference count has the following meaning:
142    ///  - `REF_COUNT_UNUSED`: The page is not in use.
143    ///  - `REF_COUNT_UNIQUE`: The page is owned by a [`UniqueFrame`].
144    ///  - `0`: The page is being constructed ([`Frame::from_unused`])
145    ///    or destructured ([`drop_last_in_place`]).
146    ///  - `1..REF_COUNT_MAX`: The page is in use.
147    ///  - `REF_COUNT_MAX..REF_COUNT_UNIQUE`: Illegal values to
148    ///    prevent the reference count from overflowing. Otherwise,
149    ///    overflowing the reference count will cause soundness issue.
150    pub ref_count: PAtomicU64,
151    /// The virtual table that indicates the type of the metadata. Currently we do not verify this because
152    /// of the dependency on the `dyn Trait` pattern. But we can revisit it now that `dyn Trait` is supported by Verus.
153    pub vtable_ptr: PPtr<usize>,
154    /// This is only accessed by [`crate::mm::frame::linked_list`].
155    /// It stores 0 if the frame is not in any list, otherwise it stores the
156    /// ID of the list.
157    ///
158    /// It is ugly but allows us to tell if a frame is in a specific list by
159    /// one relaxed read. Otherwise, if we store it conditionally in `storage`
160    /// we would have to ensure that the type is correct before the read, which
161    /// costs a synchronization.
162    pub in_list: PAtomicU64,
163}
164
165pub const REF_COUNT_UNUSED: u64 = u64::MAX;
166
167pub const REF_COUNT_UNIQUE: u64 = u64::MAX - 1;
168
169pub const REF_COUNT_MAX: u64 = i64::MAX as u64;
170
171type FrameMetaVtablePtr = core::ptr::DynMetadata<dyn AnyFrameMeta>;
172
173/// The error type for getting the frame from a physical address.
174#[derive(Debug)]
175pub enum GetFrameError {
176    /// The frame is in use.
177    InUse,
178    /// The frame is not in use.
179    Unused,
180    /// The frame is being initialized or destructed.
181    Busy,
182    /// The frame is private to an owner of [`UniqueFrame`].
183    ///
184    /// [`UniqueFrame`]: super::unique::UniqueFrame
185    Unique,
186    /// The provided physical address is out of bound.
187    OutOfBound,
188    /// The provided physical address is not aligned.
189    NotAligned,
190    /// Verification only: `compare_exchange` returned `Err`, retry
191    Retry,
192}
193
194pub open spec fn get_slot_spec(paddr: Paddr) -> (res: PPtr<MetaSlot>)
195    recommends
196        paddr % 4096 == 0,
197        paddr < MAX_PADDR,
198{
199    let slot = frame_to_meta(paddr);
200    PPtr(slot, PhantomData::<MetaSlot>)
201}
202
203/// Space-holder of the AnyFrameMeta virtual table.
204///
205/// Dyn-compatible: no `Self`-by-value, no associated types on dispatched
206/// methods, no dyn-incompatible supertrait. `vtable_ptr` is `Self: Sized`
207/// because it's only used statically (the runtime vtable pointer lives on
208/// the slot, not on the instance). Sites that need `Repr<MetaSlotStorage>`
209/// must spell it out — it was previously a supertrait.
210pub unsafe trait AnyFrameMeta {
211    /// Per-impl precondition for [`Self::on_drop`]. Default is `true`.
212    /// Impls that need richer caller-side invariants (e.g. the PT-node's
213    /// reader/region invariants) override this; the trait method's
214    /// `requires` clause calls it.
215    open spec fn on_drop_pre(
216        &self,
217        reader: VmReader<'_, Infallible>,
218        regions: MetaRegionOwners,
219        vm_io_owner: crate::specs::mm::io::VmIoOwner,
220    ) -> bool {
221        true
222    }
223
224    exec fn on_drop(
225        &mut self,
226        _reader: &mut VmReader<'_, Infallible>,
227        Tracked(_regions): Tracked<&mut MetaRegionOwners>,
228        Tracked(_vm_io_owner): Tracked<&mut crate::specs::mm::io::VmIoOwner>,
229    )
230        requires
231            old(_regions).inv(),
232            old(_reader).inv(),
233            old(_vm_io_owner).inv(),
234            old(_reader).wf(*old(_vm_io_owner)),
235            old(self).on_drop_pre(*old(_reader), *old(_regions), *old(_vm_io_owner)),
236        ensures
237            final(_regions).inv(),
238            final(_reader).inv(),
239            final(_vm_io_owner).inv(),
240            final(_reader).wf(*final(_vm_io_owner)),
241    {
242    }
243
244    exec fn is_untyped(&self) -> bool {
245        false
246    }
247
248    spec fn vtable_ptr(&self) -> usize where Self: Sized;
249}
250
251global layout MetaSlot is size == 64, align == 8;
252
253pub broadcast axiom fn size_of_meta_slot()
254    ensures
255        #![trigger size_of::<MetaSlot>()]
256        #![trigger align_of::<MetaSlot>()]
257        size_of::<MetaSlot>() == 64,
258        align_of::<MetaSlot>() == 8,
259;
260
261#[inline(always)]
262#[verifier::allow_in_spec]
263pub const fn meta_slot_size() -> (res: usize)
264    returns
265        64usize,
266{
267    size_of::<MetaSlot>()
268}
269
270pub open spec fn has_safe_slot(paddr: Paddr) -> bool {
271    &&& paddr % PAGE_SIZE == 0
272    &&& paddr < MAX_PADDR
273}
274
275/// Gets the reference to a metadata slot.
276/// # Verified Properties
277/// ## Preconditions
278/// `paddr` is the physical address of a frame, with a valid owner.
279/// ## Postconditions
280/// If `paddr` is aligned properly and in-bounds, the function returns a pointer to its metadata slot.
281/// ## Safety
282/// Verus ensures that the pointer will only be used when we have a permission object, so creating it is safe.
283#[verus_spec(res =>
284    ensures
285        has_safe_slot(paddr) <==> res is Ok,
286        res is Ok ==> res.unwrap().addr() == frame_to_meta(paddr),
287)]
288pub(super) fn get_slot(paddr: Paddr) -> Result<PPtr<MetaSlot>, GetFrameError> {
289    if paddr % PAGE_SIZE != 0 {
290        return Err(GetFrameError::NotAligned);
291    }
292    if paddr >= MAX_PADDR {
293        return Err(GetFrameError::OutOfBound);
294    }
295    let vaddr = frame_to_meta(paddr);
296    let ptr = PPtr::<MetaSlot>::from_addr(vaddr);
297
298    // SAFETY: `ptr` points to a valid `MetaSlot` that will never be
299    // mutably borrowed, so taking an immutable reference to it is safe.
300    Ok(ptr)
301}
302
303#[verus_verify]
304impl MetaSlot {
305    /// This is the equivalent of &self as *const as Vaddr, but we need to axiomatize it.
306    /// # Safety
307    /// It is safe to take the address of a pointer, but it may not be safe to use that
308    /// address for all purposes.
309    #[verifier::external_body]
310    #[verus_spec(
311        with
312            Tracked(perm): Tracked<&vstd::simple_pptr::PointsTo<MetaSlot>>,
313        requires
314            self == perm.value(),
315        returns
316            perm.addr(),
317    )]
318    fn addr_of(&self) -> Paddr {
319        unimplemented!()
320    }
321
322    /// Initializes the metadata slot of a frame assuming it is unused.
323    ///
324    /// If successful, the function returns a pointer to the metadata slot.
325    /// And the slot is initialized with the given metadata.
326    ///
327    /// The resulting reference count held by the returned pointer is
328    /// [`REF_COUNT_UNIQUE`] if `as_unique_ptr` is `true`, otherwise `1`.
329    ///
330    /// # Verified Properties
331    /// ## Preconditions
332    /// - **Safety Invariant**: Metaslot region invariants must hold.
333    /// - **Bookkeeping**: The slot permissions must be available in order to check the reference count.
334    /// This precondition is stronger than it needs to be; absent permissions correspond to error cases.
335    /// ## Postconditions
336    /// - **Safety Invariant**: Metaslot region invariants hold after the call.
337    /// - **Safety**: Other slots are not affected by the call.
338    /// - **Correctness**: If successful, the function returns a pointer to the metadata slot and a permission to the slot.
339    /// - **Correctness**: If successful, the slot is initialized with the given metadata.
340    /// ## Safety
341    /// - This function returns an error if `paddr` does not correspond to a valid slot or the slot is in use.
342    /// - Accesses to the slot itself are gated by atomic checks, avoiding data races.
343    #[verus_spec(res =>
344        with Tracked(regions): Tracked<&mut MetaRegionOwners>
345        requires
346            old(regions).inv(),
347        ensures
348            // Helper: on success, `regions.slots` loses the extracted slot
349            // (caller is responsible for re-parking it via `sync_slot_perm`
350            // to restore `regions.inv()`). On Err, regions is left intact
351            // and the inv is preserved.
352            res is Err ==> final(regions).inv(),
353            // On failure the slot perm/owner are re-parked unchanged: nothing
354            // was claimed, so the whole region state is intact.
355            res is Err ==> *final(regions) == *old(regions),
356            res matches Ok((res, perm)) ==> Self::get_from_unused_perm_spec(paddr, metadata, as_unique_ptr, res, perm@),
357            res matches Ok((res, perm)) ==> perm@.value().wf(
358                final(regions).slot_owners[frame_to_index(paddr)]),
359            // The returned perm is exactly the slot perm that was extracted
360            // from `regions.slots`. Lets callers re-park via `sync_slot_perm`
361            // and recover `final.slots == old.slots`.
362            res matches Ok((_, perm)) ==> perm@ == old(regions).slots[frame_to_index(paddr)],
363            res is Ok ==> Self::get_from_unused_spec(paddr, as_unique_ptr, *old(regions), *final(regions)),
364            // The extracted slot perm is handed back via the out-param, so it
365            // leaves `regions.slots` (caller re-parks it via `sync_slot_perm`).
366            res is Ok ==> Self::slot_perm_extracted_spec(paddr, *old(regions), *final(regions)),
367            !has_safe_slot(paddr) ==> res is Err,
368            // Linear-drop pilot: claiming an unused slot doesn't mint or
369            // redeem segment or frame obligations on any path.
370            final(regions).frame_obligations =~= old(regions).frame_obligations,
371    )]
372    pub(super) fn get_from_unused<M: AnyFrameMeta + Repr<MetaSlotStorage> + OwnerOf>(
373        paddr: Paddr,
374        metadata: M,
375        as_unique_ptr: bool,
376    ) -> Result<(PPtr<Self>, Tracked<vstd::simple_pptr::PointsTo<MetaSlot>>), GetFrameError> {
377        let slot = get_slot(paddr)?;
378
379        proof {
380            assert(has_safe_slot(paddr));
381            regions.inv_implies_correct_addr(paddr);
382        }
383
384        let tracked mut slot_own = regions.slot_owners.tracked_remove(frame_to_index(paddr));
385        let tracked mut slot_perm = regions.slots.tracked_remove(frame_to_index(paddr));
386
387        // `Acquire` pairs with the `Release` in `drop_last_in_place` and ensures the metadata
388        // initialization won't be reordered before this memory compare-and-exchange.
389        let last_ref_cnt = slot.borrow(Tracked(&slot_perm)).ref_count.compare_exchange(
390            Tracked(&mut slot_own.inner_perms.ref_count),
391            REF_COUNT_UNUSED,
392            0,
393        ).map_err(
394            |val|
395                match val {
396                    REF_COUNT_UNIQUE => GetFrameError::Unique,
397                    0 => GetFrameError::Busy,
398                    _ => GetFrameError::InUse,
399                },
400        );
401
402        if let Err(err) = last_ref_cnt {
403            proof {
404                let idx = frame_to_index(paddr);
405                regions.slot_owners.tracked_insert(idx, slot_own);
406                regions.slots.tracked_insert(idx, slot_perm);
407                // CAS failure leaves `ref_count` unchanged (value + id), so the
408                // re-parked slot is exactly the original — region state intact.
409                vstd_extra::auxiliary::axiom_permission_u64_ext_eq(
410                    regions.slot_owners[idx].inner_perms.ref_count,
411                    old(regions).slot_owners[idx].inner_perms.ref_count,
412                );
413                assert(regions.slot_owners[idx] == old(regions).slot_owners[idx]);
414                assert(regions.slot_owners =~= old(regions).slot_owners);
415                assert(regions.slots =~= old(regions).slots);
416                assert(*regions == *old(regions));
417            }
418
419            return Err(err);
420        }
421        // SAFETY: The slot now has a reference count of `0`, other threads will
422        // not access the metadata slot so it is safe to have a mutable reference.
423
424        unsafe {
425            #[verus_spec(with Tracked(&mut slot_own.inner_perms.storage), Tracked(&mut slot_own.inner_perms.vtable_ptr))]
426            slot.borrow(Tracked(&slot_perm)).write_meta(metadata)
427        };
428
429        if as_unique_ptr {
430            slot.borrow(Tracked(&slot_perm)).ref_count.store(
431                Tracked(&mut slot_own.inner_perms.ref_count),
432                REF_COUNT_UNIQUE,
433            );
434        } else {
435            slot.borrow(Tracked(&slot_perm)).ref_count.store(
436                Tracked(&mut slot_own.inner_perms.ref_count),
437                1,
438            );
439        }
440
441        proof {
442            slot_own.usage = PageUsage::Frame;
443            assert(slot_perm.value().wf(slot_own));
444            regions.slot_owners.tracked_insert(frame_to_index(paddr), slot_own);
445        }
446
447        Ok((slot, Tracked(slot_perm)))
448    }
449
450    /// The inner loop of `Self::get_from_in_use`.
451    /// # Verified Properties
452    /// ## Preconditions
453    /// - The permission must point to the slot.
454    /// - The permission must be initialized.
455    /// - **Liveness**: The reference count of the inner permissions must not be at the maximum.
456    /// ## Postconditions
457    /// - The reference count of the inner permissions is increased by one.
458    #[verus_spec(res =>
459        with Tracked(perm): Tracked<&vstd::simple_pptr::PointsTo<MetaSlot>>,
460            Tracked(inner_perms): Tracked<&mut MetadataInnerPerms>,
461        requires
462            perm.pptr() == slot,
463            perm.is_init(),
464            perm.value().ref_count.id() == old(inner_perms).ref_count.id(),
465            old(inner_perms).ref_count.value() >= REF_COUNT_MAX ==> may_panic(),
466        ensures
467            res is Ok ==> final(inner_perms).ref_count.value() == old(inner_perms).ref_count.value() + 1,
468            res is Ok ==> final(inner_perms).ref_count.value() <= REF_COUNT_MAX,
469            res is Ok ==> old(inner_perms).ref_count.value() > 0,
470            res matches Ok(ptr) ==> ptr == slot,
471            res is Err ==> final(inner_perms).ref_count.value() == old(inner_perms).ref_count.value(),
472            final(inner_perms).ref_count.id() == old(inner_perms).ref_count.id(),
473            final(inner_perms).storage == old(inner_perms).storage,
474            final(inner_perms).vtable_ptr == old(inner_perms).vtable_ptr,
475            final(inner_perms).in_list == old(inner_perms).in_list,
476    )]
477    fn get_from_in_use_loop(slot: PPtr<MetaSlot>) -> Result<PPtr<Self>, GetFrameError> {
478        match slot.borrow(Tracked(perm)).ref_count.load(Tracked(&mut inner_perms.ref_count)) {
479            REF_COUNT_UNUSED => {
480                return Err(GetFrameError::Unused);
481            },
482            REF_COUNT_UNIQUE => {
483                return Err(GetFrameError::Unique);
484            },
485            0 => {
486                return Err(GetFrameError::Busy);
487            },
488            last_ref_cnt => {
489                if last_ref_cnt >= REF_COUNT_MAX {
490                    // See `Self::inc_ref_count` for the explanation.
491                    vstd_extra::panic::panic_diverge();
492                }
493                // Using `Acquire` here to pair with `get_from_unused` or
494                // `<Frame<M> as From<UniqueFrame<M>>>::from` (who must be
495                // performed after writing the metadata).
496                //
497                // It ensures that the written metadata will be visible to us.
498
499                if slot.borrow(Tracked(perm)).ref_count.compare_exchange_weak(
500                    Tracked(&mut inner_perms.ref_count),
501                    last_ref_cnt,
502                    last_ref_cnt + 1,
503                ).is_ok() {
504                    return Ok(slot);
505                } else {
506                    return Err(GetFrameError::Retry);
507                }
508            },
509        }
510    }
511
512    /// Gets another owning pointer to the metadata slot from the given page.
513    /// # Verified Properties
514    /// ## Verification Design
515    /// To simplify the verification, we verify the loop body separately from the outer loop. We do not prove termination.
516    /// ## Preconditions
517    /// - **Safety Invariant**: Metaslot region invariants must hold.
518    /// - **Bookkeeping**: The slot permissions must be available in order to check the reference count.
519    /// This precondition is stronger than it needs to be; absent permissions correspond to error cases.
520    /// - **Liveness**: The reference count of the inner permissions must not be at the maximum, or the function will panic.
521    /// ## Postconditions
522    /// - **Safety**: Metaslot region invariants hold after the call.
523    /// - **Correctness**: If successful, the slot's reference count is increased by one.
524    /// - **Correctness**: If unsuccessful, the metaslot region remains unchanged.
525    /// ## Safety
526    /// The potential data race is avoided by the spin-lock.
527    #[verus_spec(res =>
528        with Tracked(regions): Tracked<&mut MetaRegionOwners>
529        requires
530            old(regions).inv(),
531            has_safe_slot(paddr) ==> old(regions).ref_count(frame_to_index(paddr)) >= REF_COUNT_MAX ==> may_panic(),
532        ensures
533            final(regions).inv(),
534            !has_safe_slot(paddr) ==> res is Err,
535            res is Ok ==> Self::get_from_in_use_success(paddr, *old(regions), *final(regions)),
536            res matches Ok(ptr) ==> ptr == old(regions).slots[frame_to_index(paddr)].pptr(),
537            res is Err ==> *final(regions) == *old(regions),
538            final(regions).frame_obligations =~= old(regions).frame_obligations,
539    )]
540    #[verifier::exec_allows_no_decreases_clause]
541    pub(super) fn get_from_in_use(paddr: Paddr) -> Result<PPtr<Self>, GetFrameError> {
542        let ghost regions0 = *regions;
543
544        let slot = get_slot(paddr)?;
545
546        proof {
547            assert(regions0 == *old(regions));
548            assert(has_safe_slot(paddr));
549            // `get_slot` succeeded ⟹ `has_safe_slot(paddr)`; with `regions.inv()`
550            // that recovers the slot facts the caller used to supply: the slot is
551            // present, its address is `frame_to_meta(paddr)`, and its inner-perm
552            // ref-count cell matches the slot's (via the per-slot `wf`).
553            broadcast use crate::mm::frame::meta::mapping::group_page_meta;
554
555            regions.inv_implies_correct_addr(paddr);
556        }
557
558        let tracked mut slot_own = regions.slot_owners.tracked_remove(frame_to_index(paddr));
559        let tracked slot_perm = regions.slots.tracked_borrow(frame_to_index(paddr));
560
561        let ghost pre = slot_own.inner_perms.ref_count.value();
562
563        loop
564            invariant
565                has_safe_slot(paddr),
566                slot_perm.addr() == slot.addr(),
567                slot_perm.is_init(),
568                slot_perm.value().ref_count.id() == slot_own.inner_perms.ref_count.id(),
569                slot_own.inner_perms.ref_count.value() == pre,
570                slot_own.inner_perms.ref_count.value() >= REF_COUNT_MAX ==> may_panic(),
571                regions0.slots.contains_key(frame_to_index(paddr)),
572                regions0.slot_owners.contains_key(frame_to_index(paddr)),
573                regions0.inv(),
574                regions0.slots[frame_to_index(paddr)] == *slot_perm,
575                slot_own.self_addr == regions0.slot_owners[frame_to_index(paddr)].self_addr,
576                slot_own.usage == regions0.slot_owners[frame_to_index(paddr)].usage,
577                slot_own.paths_in_pt == regions0.slot_owners[frame_to_index(paddr)].paths_in_pt,
578                FRAME_METADATA_RANGE.start <= slot_own.self_addr < FRAME_METADATA_RANGE.end,
579                slot_own.self_addr % META_SLOT_SIZE == 0,
580                slot_own.self_addr == slot_perm.addr(),
581                slot_perm.value().storage.id() == slot_own.inner_perms.storage.id(),
582                slot_perm.value().vtable_ptr == slot_own.inner_perms.vtable_ptr.pptr(),
583                slot_perm.value().in_list.id() == slot_own.inner_perms.in_list.id(),
584                slot_own.inner_perms.ref_count.id() == regions0.slot_owners[frame_to_index(
585                    paddr,
586                )].inner_perms.ref_count.id(),
587                slot_own.inner_perms.storage == regions0.slot_owners[frame_to_index(
588                    paddr,
589                )].inner_perms.storage,
590                slot_own.inner_perms.vtable_ptr == regions0.slot_owners[frame_to_index(
591                    paddr,
592                )].inner_perms.vtable_ptr,
593                slot_own.inner_perms.in_list == regions0.slot_owners[frame_to_index(
594                    paddr,
595                )].inner_perms.in_list,
596                // pre equals the original ref_count value
597                pre == regions0.slot_owners[frame_to_index(paddr)].inner_perms.ref_count.value(),
598                // regions0 equals old(regions)
599                regions0 == *old(regions),
600                // slot pptr matches what postcondition expects
601                slot == regions0.slots[frame_to_index(paddr)].pptr(),
602                // regions state: slot_owners has idx removed; slots borrowed (unchanged)
603                regions.slot_owners == regions0.slot_owners.remove(frame_to_index(paddr)),
604                regions.slots == regions0.slots,
605                // Linear-drop pilot: this path doesn't mint/redeem segment
606                // obligations, so the ledger is invariant.
607                regions.frame_obligations =~= regions0.frame_obligations,
608        {
609            match #[verus_spec(with Tracked(slot_perm), Tracked(&mut slot_own.inner_perms))]
610            Self::get_from_in_use_loop(slot) {
611                Err(GetFrameError::Retry) => {
612                    core::hint::spin_loop();
613                },
614                res => {
615                    proof {
616                        let idx = frame_to_index(paddr);
617
618                        assert(slot_own.inner_perms.ref_count.id()
619                            == regions0.slot_owners[idx].inner_perms.ref_count.id());
620
621                        let ghost orig = regions0.slot_owners[idx];
622                        assert(orig.inv());
623                        assert(pre == orig.inner_perms.ref_count.value());
624
625                        assert(slot_own.inner_perms.vtable_ptr == orig.inner_perms.vtable_ptr);
626
627                        if res is Ok {
628                            assert(slot_own.inner_perms.ref_count.value() == pre + 1);
629                            assert(slot_own.inner_perms.ref_count.value() <= REF_COUNT_MAX);
630                            assert(pre > 0);
631                            assert(0 < orig.inner_perms.ref_count.value());
632                            assert(orig.inner_perms.ref_count.value() <= REF_COUNT_MAX);
633                            assert(orig.inner_perms.vtable_ptr.is_init());
634                            assert(slot_own.inner_perms.vtable_ptr.is_init());
635                        } else {
636                            assert(slot_own.inner_perms.ref_count.value() == pre);
637                            assert(slot_own.inner_perms.ref_count.value()
638                                == orig.inner_perms.ref_count.value());
639                            assert(slot_own.inner_perms.ref_count.id()
640                                == orig.inner_perms.ref_count.id());
641                            assert(slot_own.inner_perms.storage == orig.inner_perms.storage);
642                            assert(slot_own.inner_perms.vtable_ptr == orig.inner_perms.vtable_ptr);
643                            assert(slot_own.inner_perms.in_list == orig.inner_perms.in_list);
644                        }
645                        assert(slot_own.inv());
646                        assert(slot_perm.value().wf(slot_own));
647                        assert(slot_own.self_addr == slot_perm.addr());
648
649                        if res is Err {
650                            assert(slot_own.inner_perms.ref_count.value() == pre);
651                            assert(slot_own.inner_perms.ref_count.id()
652                                == orig.inner_perms.ref_count.id());
653                            assert(slot_own.inner_perms.storage == orig.inner_perms.storage);
654                            assert(slot_own.inner_perms.vtable_ptr == orig.inner_perms.vtable_ptr);
655                            assert(slot_own.inner_perms.in_list == orig.inner_perms.in_list);
656                        }
657                        regions.slot_owners.tracked_insert(idx, slot_own);
658
659                        assert(regions.slot_owners.dom() =~= regions0.slot_owners.dom());
660                        assert(regions.slots =~= regions0.slots);
661
662                        assert forall|i: usize| i != idx implies #[trigger] regions.slot_owners[i]
663                            == regions0.slot_owners[i] by {};
664
665                        assert(regions.slot_owners[idx].inner_perms.ref_count.id()
666                            == regions0.slot_owners[idx].inner_perms.ref_count.id());
667                        assert(regions.slot_owners[idx].inner_perms.storage
668                            == regions0.slot_owners[idx].inner_perms.storage);
669                        assert(regions.slot_owners[idx].inner_perms.vtable_ptr
670                            == regions0.slot_owners[idx].inner_perms.vtable_ptr);
671                        assert(regions.slot_owners[idx].inner_perms.in_list
672                            == regions0.slot_owners[idx].inner_perms.in_list);
673                        assert(regions.slot_owners[idx].self_addr
674                            == regions0.slot_owners[idx].self_addr);
675                        assert(regions.slot_owners[idx].usage == regions0.slot_owners[idx].usage);
676
677                        // For ptr postcondition: slot_perm.pptr() == old(regions).slots[idx].pptr()
678                        assert(*slot_perm == regions0.slots[idx]);
679
680                        // For Err ==> *regions == *old(regions)
681                        if res is Err {
682                            // On Err, ref_count unchanged so slot_own == orig.
683                            // Use extensional equality axiom for PermissionU64.
684                            assert(regions.slot_owners[idx].inner_perms.ref_count.value()
685                                == regions0.slot_owners[idx].inner_perms.ref_count.value());
686                            assert(regions.slot_owners[idx].inner_perms.ref_count.id()
687                                == regions0.slot_owners[idx].inner_perms.ref_count.id());
688                            vstd_extra::auxiliary::axiom_permission_u64_ext_eq(
689                                regions.slot_owners[idx].inner_perms.ref_count,
690                                regions0.slot_owners[idx].inner_perms.ref_count,
691                            );
692                            assert(regions.slot_owners[idx] == regions0.slot_owners[idx]);
693                            assert(regions.slot_owners =~= regions0.slot_owners);
694                            assert(*regions == *old(regions));
695                        }
696                    }
697
698                    return res;
699                },
700            }
701        }
702    }
703
704    /// Increases the frame reference count by one.
705    ///
706    /// # Verified Properties
707    /// ## Preconditions
708    /// - **Bookkeeping**: The permission must match the reference count being updated.
709    /// - **Liveness**: The function will abort if the reference count is at the maximum.
710    /// ## Postconditions
711    /// - **Correctness**: The reference count is increased by one.
712    /// ## Safety
713    /// By requiring the caller to provide a permission for the reference count, we ensure that it already has a reference to the frame.
714    #[verus_spec(
715        with
716            Tracked(rc_perm): Tracked<&mut PermissionU64>,
717        requires
718            old(rc_perm).is_for(self.ref_count),
719            old(rc_perm).value() != REF_COUNT_UNUSED,
720            old(rc_perm).value() >= REF_COUNT_MAX ==> may_panic(),
721        ensures
722            final(rc_perm).value() == old(rc_perm).value() + 1,
723            old(rc_perm).value() < REF_COUNT_MAX,
724            final(rc_perm).id() == old(rc_perm).id(),
725    )]
726    pub(super) unsafe fn inc_ref_count(&self) {
727        let last_ref_cnt = self.ref_count.fetch_add(Tracked(rc_perm), 1);
728
729        if last_ref_cnt >= REF_COUNT_MAX {
730            // This follows the same principle as the `Arc::clone` implementation to prevent the
731            // reference count from overflowing. See also
732            // <https://doc.rust-lang.org/std/sync/struct.Arc.html#method.clone>.
733            panic_diverge();
734        }
735    }
736
737    /// Gets the corresponding frame's physical address.
738    ///
739    /// # Verified Properties
740    /// ## Preconditions
741    /// - **Safety**: The permission must point to a valid metadata slot.
742    /// - **Correctness**: The permission must point to the metadata slot.
743    /// ## Postconditions
744    /// - **Correctness**: The function returns the physical address of the frame.
745    /// ## Safety
746    /// The safety precondition requires that the permission points to a valid metadata slot.
747    /// This is an internal function, so it is fine to require the caller to verify this.
748    #[verus_spec(
749        with
750            Tracked(perm): Tracked<&vstd::simple_pptr::PointsTo<MetaSlot>>,
751        requires
752            perm.value() == self,
753            Self::frame_paddr_safety_cond(*perm),
754        returns
755            meta_to_frame(perm.addr()),
756    )]
757    pub(super) fn frame_paddr(&self) -> (pa: Paddr) {
758        proof_with!(Tracked(perm));
759        let addr = self.addr_of();
760        meta_to_frame(addr)
761    }
762
763    /*
764    /// Gets a dynamically typed pointer to the stored metadata.
765    ///
766    /// # Safety
767    ///
768    /// The caller should ensure that:
769    ///  - the stored metadata is initialized (by [`Self::write_meta`]) and valid.
770    ///
771    /// The returned pointer should not be dereferenced as mutable unless having
772    /// exclusive access to the metadata slot.
773
774    #[verifier::external_body]
775    pub(super) unsafe fn dyn_meta_ptr<M: AnyFrameMeta>(&self) -> PPtr<M> {
776        unimplemented!()
777
778        // SAFETY: The page metadata is valid to be borrowed immutably, since
779        // it will never be borrowed mutably after initialization.
780        let vtable_ptr = unsafe { *self.vtable_ptr.get() };
781
782        // SAFETY: The page metadata is initialized and valid.
783        let vtable_ptr = *unsafe { vtable_ptr.assume_init_ref() };
784
785        let meta_ptr: *mut dyn AnyFrameMeta =
786            core::ptr::from_raw_parts_mut(self as *const MetaSlot as *mut MetaSlot, vtable_ptr);
787
788        meta_ptr
789    }*/
790    /// Gets the stored metadata as type `M`.
791    ///
792    /// # Verified Properties
793    /// ## Preconditions
794    /// - **Safety**: The caller must provide an existing permission that matches the contents of the metadata slot.
795    /// ## Postconditions
796    /// - **Correctness**: The function returns a pointer to the stored metadata, of type `M`.
797    /// ## Safety
798    /// - Calling the method is always safe, but using the returned pointer could
799    /// be unsafe. Specifically, the dereferencer should ensure that:
800    ///  - the stored metadata is initialized (by [`Self::write_meta`]) and valid;
801    ///  - the initialized metadata is of type `M` (`Repr<M>::wf`);
802    ///  - the returned pointer should not be dereferenced as mutable unless having exclusive access to the metadata slot.
803    #[verus_spec(res =>
804        with
805            Tracked(perm): Tracked<&vstd::simple_pptr::PointsTo<MetaSlot>>,
806        requires
807            self == perm.value(),
808        ensures
809            res.ptr.addr() == perm.addr(),
810            res.addr() == perm.addr(),
811    )]
812    pub(super) fn as_meta_ptr<M: AnyFrameMeta + Repr<MetaSlotStorage>>(&self) -> ReprPtr<
813        MetaSlot,
814        Metadata<M>,
815    > {
816        proof_with!(Tracked(perm));
817        let addr = self.addr_of();
818
819        proof_with!(Tracked(perm));
820        self.cast_slot(addr)
821    }
822
823    /// Writes the metadata to the slot without reading or dropping the previous value.
824    ///
825    /// # Verification Design
826    /// This function is axiomatized for now because of trait constraints.
827    /// ## Preconditions
828    /// - The caller must provide the permission token to the metadata slot's storage.
829    /// ## Postconditions
830    /// - The permission is initialized to the new metadata.
831    /// ## Safety
832    /// The caller must have exclusive access to the metadata slot's storage in order to provide the permission token.
833    #[verus_spec(
834        with
835            Tracked(meta_perm): Tracked<&mut vstd::cell::pcell_maybe_uninit::PointsTo<MetaSlotStorage>>,
836            Tracked(vtable_perm): Tracked<&mut vstd::simple_pptr::PointsTo<usize>>,
837        requires
838            self.storage.id() == old(meta_perm).id(),
839            self.vtable_ptr == old(vtable_perm).pptr(),
840            old(vtable_perm).is_uninit(),
841        ensures
842            final(meta_perm).id() == old(meta_perm).id(),
843            final(meta_perm).is_init(),
844            final(vtable_perm).pptr() == old(vtable_perm).pptr(),
845            final(vtable_perm).is_init(),
846            Metadata::<M>::metadata_from_inner_perms(*final(meta_perm)) == metadata,
847    )]
848    pub(super) unsafe fn write_meta<M: AnyFrameMeta + Repr<MetaSlotStorage> + OwnerOf>(
849        &self,
850        metadata: M,
851    ) {
852        // SAFETY: Caller ensures that the access to the fields are exclusive.
853        //        let vtable_ptr = unsafe { &mut *self.vtable_ptr.get() };
854        //        vtable_ptr.write(core::ptr::metadata(&metadata as &dyn AnyFrameMeta));
855        self.vtable_ptr.put(Tracked(vtable_perm), 0);
856
857        // SAFETY:
858        // 1. `ptr` points to the metadata storage.
859        // 2. The size and the alignment of the metadata storage is large enough to hold `M`
860        //    (guaranteed by the const assertions above).
861        // 3. We have exclusive access to the metadata storage (guaranteed by the caller).
862        Metadata::<M>::write_metadata_into_storage(&self.storage, Tracked(meta_perm), metadata);
863    }
864
865    /// Drops the metadata and deallocates the frame.
866    ///
867    /// # Verified Properties
868    /// ## Preconditions
869    /// - **Safety Invariant**: The metadata slot must satisfy the safety invariants.
870    /// - **Safety**: The caller must provide an owner object for the metadata slot, which must include the permission for the
871    /// slot's `ref_count` field.
872    /// - **Safety**: The owner must satisfy [`drop_last_in_place_safety_cond`], which ensures that its reference count is 0
873    /// and it has no dangling raw pointers.
874    /// ## Postconditions
875    /// - **Safety**: The metadata slot satisfies the safety invariants after the call.
876    /// - **Correctness**: The reference count is set to `REF_COUNT_UNUSED` and the contents of the slot are uninitialized.
877    /// ## Safety
878    /// - By requiring the caller to provide an owner object, we ensure that it already has a reference to the frame.
879    /// - The safety precondition ensures that there are no dangling pointers, including raw pointer, guaranteeing temporal safety.
880    #[verus_spec(
881        with
882            Tracked(owner): Tracked<&mut MetaSlotOwner>,
883        requires
884            old(owner).inv(),
885            self.ref_count.id() == old(owner).inner_perms.ref_count.id(),
886            Self::drop_last_in_place_safety_cond(*old(owner)),
887        ensures
888            final(owner).inv(),
889            final(owner).inner_perms.ref_count.value() == REF_COUNT_UNUSED,
890            final(owner).inner_perms.ref_count.id() == old(owner).inner_perms.ref_count.id(),
891            final(owner).inner_perms.storage.id() == old(owner).inner_perms.storage.id(),
892            final(owner).inner_perms.storage.is_uninit(),
893            final(owner).inner_perms.vtable_ptr.is_uninit(),
894            final(owner).inner_perms.vtable_ptr.pptr() == old(owner).inner_perms.vtable_ptr.pptr(),
895            final(owner).inner_perms.in_list == old(owner).inner_perms.in_list,
896            final(owner).self_addr == old(owner).self_addr,
897            final(owner).usage == old(owner).usage,
898            final(owner).paths_in_pt == old(owner).paths_in_pt,
899    )]
900    pub(super) unsafe fn drop_last_in_place(&self) {
901        // This should be guaranteed as a safety requirement.
902        //        debug_assert_eq!(self.ref_count.load(Tracked(&*rc_perm)), 0);
903        // SAFETY: The caller ensures safety.
904        unsafe {
905            #[verus_spec(with Tracked(owner))]
906            self.drop_meta_in_place()
907        };
908
909        // `Release` pairs with the `Acquire` in `Frame::from_unused` and ensures
910        // `drop_meta_in_place` won't be reordered after this memory store.
911        self.ref_count.store(Tracked(&mut owner.inner_perms.ref_count), REF_COUNT_UNUSED);
912    }
913
914    /// Drops the metadata of a slot in place.
915    ///
916    /// After this operation, the metadata becomes uninitialized. Any access to the
917    /// metadata is undefined behavior unless it is re-initialized by [`Self::write_meta`].
918    ///
919    /// # Verification Design
920    /// This function is axiomatized because of its reliance on dynamic trait methods and `VmReader`.
921    /// The latter dependency makes it part of the "bootstrap gap".
922    /// Now that Verus better supports the `dyn Trait` pattern and we have verified `VmReader`, we can revisit it.
923    /// ## Preconditions
924    /// - The caller must provide an owner object for the metadata slot.
925    /// - The reference count must be 0
926    /// ## Safety
927    ///
928    /// The caller should ensure that:
929    ///  - the reference count is `0` (so we are the sole owner of the frame);
930    ///  - the metadata is initialized;
931    #[verifier::external_body]
932    #[verus_spec(
933        with
934            Tracked(slot_own): Tracked<&mut MetaSlotOwner>,
935        requires
936            old(slot_own).inner_perms.ref_count.value() == 0 || old(slot_own).inner_perms.ref_count.value() == REF_COUNT_UNIQUE,
937            old(slot_own).inner_perms.storage.is_init(),
938            old(slot_own).inner_perms.in_list.value() == 0,
939        ensures
940            final(slot_own).inner_perms.ref_count == old(slot_own).inner_perms.ref_count,
941            final(slot_own).inner_perms.storage.is_uninit(),
942            final(slot_own).inner_perms.storage.id() == old(slot_own).inner_perms.storage.id(),
943            final(slot_own).inner_perms.in_list == old(slot_own).inner_perms.in_list,
944            final(slot_own).inner_perms.vtable_ptr.is_uninit(),
945            final(slot_own).inner_perms.vtable_ptr.pptr() == old(slot_own).inner_perms.vtable_ptr.pptr(),
946            final(slot_own).self_addr == old(slot_own).self_addr,
947            final(slot_own).usage == old(slot_own).usage,
948            final(slot_own).paths_in_pt == old(slot_own).paths_in_pt,
949    )]
950    #[verifier::external_body]
951    pub(super) fn drop_meta_in_place(&self) {
952        // Smoke test for the dyn-dispatch shape — body kept `external_body`
953        // because (a) the args bundle isn't threaded through the call chain
954        // yet (Tracked::assume_new forges it here), (b) `VmReader`,
955        // `vtable_ptr.assume_init_read`, and `core::ptr::drop_in_place` have
956        // no Verus specs. Activates only the type-check; runtime behavior is
957        // axiomatic per the verus_spec ensures above.
958        let paddr = unimplemented!();
959        let _: Paddr = paddr;
960
961        // SAFETY: We have exclusive access to the frame metadata.
962        let vtable_ptr: *const core::ptr::DynMetadata<dyn AnyFrameMeta> = unimplemented!();
963        // SAFETY: The frame metadata is initialized and valid.
964        let vtable_ptr = unsafe { *vtable_ptr };
965
966        let storage_ptr: *mut () = unimplemented!();
967        let meta_ptr: *mut dyn AnyFrameMeta = core::ptr::from_raw_parts_mut(
968            storage_ptr,
969            vtable_ptr,
970        );
971
972        // SAFETY: The implementer of the frame metadata decides that if the
973        // frame is safe to be read or not.
974        let mut reader: VmReader<'_, Infallible> = unimplemented!();
975
976        // SAFETY: `ptr` points to the metadata storage which is valid to be
977        // mutably borrowed under `vtable_ptr` because the metadata is valid,
978        // the vtable is correct, and we have exclusive access.
979        let regions: Tracked<&mut MetaRegionOwners> = Tracked::assume_new();
980        let vm_io_owner: Tracked<&mut crate::specs::mm::io::VmIoOwner> = Tracked::assume_new();
981        unsafe {
982            // Invoke the custom `on_drop` handler.
983            (*meta_ptr).on_drop(&mut reader, regions, vm_io_owner);
984            // Drop the frame metadata.
985            core::ptr::drop_in_place(meta_ptr);
986        }
987    }
988}
989
990/// The metadata of frames that holds metadata of frames.
991#[derive(Debug, Default)]
992pub struct MetaPageMeta {}
993
994//impl_frame_meta_for!(MetaPageMeta);
995/*
996/// Initializes the metadata of all physical frames.
997///
998/// The function returns a list of `Frame`s containing the metadata.
999///
1000/// # Safety
1001///
1002/// This function should be called only once and only on the BSP,
1003/// before any APs are started.
1004pub(crate) unsafe fn init() -> Segment<MetaPageMeta> {
1005    let max_paddr = {
1006        let regions = &crate::boot::EARLY_INFO.get().unwrap().memory_regions;
1007        regions
1008            .iter()
1009            .filter(|r| r.typ() == MemoryRegionType::Usable)
1010            .map(|r| r.base() + r.len())
1011            .max()
1012            .unwrap()
1013    };
1014
1015    info!(
1016        "Initializing frame metadata for physical memory up to {:x}",
1017        max_paddr
1018    );
1019
1020    // In RISC-V, the boot page table has mapped the 512GB memory,
1021    // so we don't need to add temporary linear mapping.
1022    // In LoongArch, the DWM0 has mapped the whole memory,
1023    // so we don't need to add temporary linear mapping.
1024    #[cfg(target_arch = "x86_64")]
1025    add_temp_linear_mapping(max_paddr);
1026
1027    let tot_nr_frames = max_paddr / page_size::<PagingConsts>(1);
1028    let (nr_meta_pages, meta_pages) = alloc_meta_frames(tot_nr_frames);
1029
1030    // Map the metadata frames.
1031    boot_pt::with_borrow(|boot_pt| {
1032        for i in 0..nr_meta_pages {
1033            let frame_paddr = meta_pages + i * PAGE_SIZE;
1034            let vaddr = frame_to_meta::<PagingConsts>(0) + i * PAGE_SIZE;
1035            let prop = PageProperty {
1036                flags: PageFlags::RW,
1037                cache: CachePolicy::Writeback,
1038                priv_flags: PrivilegedPageFlags::GLOBAL,
1039            };
1040            // SAFETY: we are doing the metadata mappings for the kernel.
1041            unsafe { boot_pt.map_base_page(vaddr, frame_paddr / PAGE_SIZE, prop) };
1042        }
1043    })
1044    .unwrap();
1045
1046    // Now the metadata frames are mapped, we can initialize the metadata.
1047    super::MAX_PADDR.store(max_paddr, Ordering::Relaxed);
1048
1049    let meta_page_range = meta_pages..meta_pages + nr_meta_pages * PAGE_SIZE;
1050
1051    let (range_1, range_2) = allocator::EARLY_ALLOCATOR
1052        .lock()
1053        .as_ref()
1054        .unwrap()
1055        .allocated_regions();
1056    for r in range_difference(&range_1, &meta_page_range) {
1057        let early_seg = Segment::from_unused(r, |_| EarlyAllocatedFrameMeta).unwrap();
1058        let _ = ManuallyDrop::new(early_seg);
1059    }
1060    for r in range_difference(&range_2, &meta_page_range) {
1061        let early_seg = Segment::from_unused(r, |_| EarlyAllocatedFrameMeta).unwrap();
1062        let _ = ManuallyDrop::new(early_seg);
1063    }
1064
1065    mark_unusable_ranges();
1066
1067    Segment::from_unused(meta_page_range, |_| MetaPageMeta {}).unwrap()
1068}
1069
1070/// Returns whether the global frame allocator is initialized.
1071pub(in crate::mm) fn is_initialized() -> bool {
1072    // `init` sets it with relaxed ordering somewhere in the middle. But due
1073    // to the safety requirement of the `init` function, we can assume that
1074    // there is no race conditions.
1075    super::MAX_PADDR.load(Ordering::Relaxed) != 0
1076}
1077
1078fn alloc_meta_frames(tot_nr_frames: usize) -> (usize, Paddr) {
1079    let nr_meta_pages = tot_nr_frames
1080        .checked_mul(size_of::<MetaSlot>())
1081        .unwrap()
1082        .div_ceil(PAGE_SIZE);
1083    let start_paddr = allocator::early_alloc(
1084        Layout::from_size_align(nr_meta_pages * PAGE_SIZE, PAGE_SIZE).unwrap(),
1085    )
1086    .unwrap();
1087
1088    let slots = paddr_to_vaddr(start_paddr) as *mut MetaSlot;
1089
1090    // Initialize the metadata slots.
1091    for i in 0..tot_nr_frames {
1092        // SAFETY: The memory is successfully allocated with `tot_nr_frames`
1093        // slots so the index must be within the range.
1094        let slot = unsafe { slots.add(i) };
1095        // SAFETY: The memory is just allocated so we have exclusive access and
1096        // it's valid for writing.
1097        unsafe {
1098            slot.write(MetaSlot {
1099                storage: UnsafeCell::new(MetaSlotStorage::Empty([0; FRAME_METADATA_MAX_SIZE - 1])),
1100                ref_count: AtomicU64::new(REF_COUNT_UNUSED),
1101                vtable_ptr: UnsafeCell::new(MaybeUninit::uninit()),
1102                in_list: AtomicU64::new(0),
1103            })
1104        };
1105    }
1106
1107    (nr_meta_pages, start_paddr)
1108}
1109
1110/// Unusable memory metadata. Cannot be used for any purposes.
1111#[derive(Debug)]
1112pub struct UnusableMemoryMeta;
1113impl_frame_meta_for!(UnusableMemoryMeta);
1114
1115/// Reserved memory metadata. Maybe later used as I/O memory.
1116#[derive(Debug)]
1117pub struct ReservedMemoryMeta;
1118impl_frame_meta_for!(ReservedMemoryMeta);
1119
1120/// The metadata of physical pages that contains the kernel itself.
1121#[derive(Debug, Default)]
1122pub struct KernelMeta;
1123impl_frame_meta_for!(KernelMeta);
1124
1125macro_rules! mark_ranges {
1126    ($region: expr, $typ: expr) => {{
1127        debug_assert!($region.base() % PAGE_SIZE == 0);
1128        debug_assert!($region.len() % PAGE_SIZE == 0);
1129
1130        let seg = Segment::from_unused($region.base()..$region.end(), |_| $typ).unwrap();
1131        let _ = ManuallyDrop::new(seg);
1132    }};
1133}
1134
1135fn mark_unusable_ranges() {
1136    let regions = &crate::boot::EARLY_INFO.get().unwrap().memory_regions;
1137
1138    for region in regions
1139        .iter()
1140        .rev()
1141        .skip_while(|r| r.typ() != MemoryRegionType::Usable)
1142    {
1143        match region.typ() {
1144            MemoryRegionType::BadMemory => mark_ranges!(region, UnusableMemoryMeta),
1145            MemoryRegionType::Unknown => mark_ranges!(region, ReservedMemoryMeta),
1146            MemoryRegionType::NonVolatileSleep => mark_ranges!(region, UnusableMemoryMeta),
1147            MemoryRegionType::Reserved => mark_ranges!(region, ReservedMemoryMeta),
1148            MemoryRegionType::Kernel => mark_ranges!(region, KernelMeta),
1149            MemoryRegionType::Module => mark_ranges!(region, UnusableMemoryMeta),
1150            MemoryRegionType::Framebuffer => mark_ranges!(region, ReservedMemoryMeta),
1151            MemoryRegionType::Reclaimable => mark_ranges!(region, UnusableMemoryMeta),
1152            MemoryRegionType::Usable => {} // By default it is initialized as usable.
1153        }
1154    }
1155}
1156
1157/// Adds a temporary linear mapping for the metadata frames.
1158///
1159/// We only assume boot page table to contain 4G linear mapping. Thus if the
1160/// physical memory is huge we end up depleted of linear virtual memory for
1161/// initializing metadata.
1162#[cfg(target_arch = "x86_64")]
1163fn add_temp_linear_mapping(max_paddr: Paddr) {
1164    const PADDR4G: Paddr = 0x1_0000_0000;
1165
1166    if max_paddr <= PADDR4G {
1167        return;
1168    }
1169
1170    // TODO: We don't know if the allocator would allocate from low to high or
1171    // not. So we prepare all linear mappings in the boot page table. Hope it
1172    // won't drag the boot performance much.
1173    let end_paddr = max_paddr.align_up(PAGE_SIZE);
1174    let prange = PADDR4G..end_paddr;
1175    let prop = PageProperty {
1176        flags: PageFlags::RW,
1177        cache: CachePolicy::Writeback,
1178        priv_flags: PrivilegedPageFlags::GLOBAL,
1179    };
1180
1181    // SAFETY: we are doing the linear mapping for the kernel.
1182    unsafe {
1183        boot_pt::with_borrow(|boot_pt| {
1184            for paddr in prange.step_by(PAGE_SIZE) {
1185                let vaddr = LINEAR_MAPPING_BASE_VADDR + paddr;
1186                boot_pt.map_base_page(vaddr, paddr / PAGE_SIZE, prop);
1187            }
1188        })
1189        .unwrap();
1190    }
1191}
1192*/
1193} // verus!