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