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