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