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