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