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 proof {
442 axiom_mmio_usage_iff_mmio_paddr(*slot_own);
443 }
444
445 // `Acquire` pairs with the `Release` in `drop_last_in_place` and ensures the metadata
446 // initialization won't be reordered before this memory compare-and-exchange.
447 let last_ref_cnt = slot.ref_count.compare_exchange(
448 Tracked(&mut slot_own.ref_count_perm),
449 REF_COUNT_UNUSED,
450 0,
451 ).map_err(
452 |val|
453 match val {
454 REF_COUNT_UNIQUE => GetFrameError::Unique,
455 0 => GetFrameError::Busy,
456 _ => GetFrameError::InUse,
457 },
458 );
459
460 if let Err(err) = last_ref_cnt {
461 proof {
462 vstd_extra::auxiliary::axiom_permission_u64_ext_eq(
463 regions.slot_owner(paddr).ref_count_perm,
464 old(regions).slot_owner(paddr).ref_count_perm,
465 );
466 }
467
468 return #[verus_spec(with |= Tracked(None))]
469 Err(err);
470 }
471 let tracked mut metadata_perm = slot_own.metadata_perm.take_resource();
472
473 // SAFETY: The slot now has a reference count of `0`, other threads will
474 // not access the metadata slot so it is safe to have a mutable reference.
475 unsafe {
476 #[verus_spec(with
477 Tracked(&mut metadata_perm),
478 Tracked(repr_perm)
479 )]
480 slot.write_meta(metadata)
481 };
482
483 proof {
484 slot_own.usage = PageUsage::Frame;
485 axiom_mmio_usage_iff_mmio_paddr(*slot_own);
486 slot_own.metadata_perm.put_resource(metadata_perm);
487 }
488
489 if as_unique_ptr {
490 // No one can create a `Frame` instance directly from the page
491 // address, so `Relaxed` is fine here.
492 slot.ref_count.store(Tracked(&mut slot_own.ref_count_perm), REF_COUNT_UNIQUE);
493 } else {
494 // `Release` is used to ensure that the metadata initialization
495 // won't be reordered after this memory store.
496 slot.ref_count.store(Tracked(&mut slot_own.ref_count_perm), 1);
497 }
498 let tracked perm = if as_unique_ptr {
499 Some(Sum::Right(slot_own.metadata_perm.take_resource()))
500 } else {
501 Some(Sum::Left(slot_own.metadata_perm.split_one()))
502 };
503 // Ok(slot as *const MetaSlot)
504 proof_with!(|= Tracked(perm));
505 Ok(PPtr::from_addr(frame_to_meta(paddr)))
506 }
507
508 /// Gets another owning pointer to the metadata slot from the given page.
509 /// # Verified Properties
510 /// We do not prove termination.
511 /// ## Preconditions
512 /// - **Safety Invariant**: Metaslot region invariants must hold.
513 /// - **Bookkeeping**: The slot permissions must be available in order to check the reference count.
514 /// This precondition is stronger than it needs to be; absent permissions correspond to error cases.
515 /// - **Liveness**: The reference count of the inner permissions must not be at the maximum, or the function will panic.
516 /// ## Postconditions
517 /// - **Safety**: Metaslot region invariants hold after the call.
518 /// - **Correctness**: If successful, the slot's reference count is increased by one.
519 /// - **Correctness**: If unsuccessful, the metaslot region remains unchanged.
520 /// ## Safety
521 /// The potential data race is avoided by the spin-lock.
522 #[verus_spec(res =>
523 with
524 Tracked(regions): Tracked<&mut MetaRegionOwners>,
525 -> permission: Tracked<Option<FracMetadataPerm>>,
526 requires
527 old(regions).inv(),
528 valid_frame_paddr(paddr) ==> old(regions).ref_count(frame_to_index(paddr)) >= REF_COUNT_MAX ==> may_panic(),
529 ensures
530 final(regions).inv(),
531 res matches Ok(ptr) ==> {
532 &&& Self::get_from_in_use_success_spec(paddr, *old(regions), *final(regions), permission@->0)
533 &&& valid_frame_paddr(paddr)
534 &&& permission@ is Some
535 &&& ptr == old(regions).slots[frame_to_index(paddr)].pptr()
536 },
537 res is Err ==> {
538 &&& *final(regions) == *old(regions)
539 &&& permission@ is None
540 }
541 )]
542 #[verifier::exec_allows_no_decreases_clause]
543 #[verifier::loop_isolation(false)]
544 pub(super) fn get_from_in_use(paddr: Paddr) -> Result<PPtr<Self>, GetFrameError> {
545 proof_decl! {
546 if valid_frame_paddr(paddr) {
547 regions.lemma_contains_valid_frame_paddr(paddr);
548 }
549 let tracked slot_perm = if valid_frame_paddr(paddr) {
550 Some(regions.tracked_borrow_slot(paddr))
551 } else {
552 None
553 };
554 }
555 let slot = match #[verus_spec(with Tracked(slot_perm))]
556 get_slot(paddr) {
557 Ok(slot) => slot,
558 Err(err) => {
559 return #[verus_spec(with |= Tracked(None))]
560 Err(err);
561 },
562 };
563
564 loop
565 invariant
566 *regions == *old(regions),
567 {
568 let tracked slot_own = regions.tracked_borrow_mut_slot_owner(paddr);
569
570 match slot.ref_count.load(Tracked(&mut slot_own.ref_count_perm)) {
571 REF_COUNT_UNUSED => {
572 return #[verus_spec(with |= Tracked(None))]
573 Err(GetFrameError::Unused);
574 },
575 REF_COUNT_UNIQUE => {
576 return #[verus_spec(with |= Tracked(None))]
577 Err(GetFrameError::Unique);
578 },
579 0 => {
580 return #[verus_spec(with |= Tracked(None))]
581 Err(GetFrameError::Busy);
582 },
583 last_ref_cnt => {
584 if last_ref_cnt >= REF_COUNT_MAX {
585 // See `Self::inc_ref_count` for the explanation.
586 vstd_extra::panic::panic_diverge();
587 }
588 // Using `Acquire` here to pair with `get_from_unused` or
589 // `<Frame<M> as From<UniqueFrame<M>>>::from` (who must be
590 // performed after writing the metadata).
591 //
592 // It ensures that the written metadata will be visible to us.
593
594 if slot.ref_count.compare_exchange_weak(
595 Tracked(&mut slot_own.ref_count_perm),
596 last_ref_cnt,
597 last_ref_cnt + 1,
598 ).is_ok() {
599 let tracked permission = slot_own.metadata_perm.split_one();
600 return #[verus_spec(with |= Tracked(Some(permission)))]
601 Ok(PPtr::from_addr(frame_to_meta(paddr)));
602 }
603 proof {
604 vstd_extra::auxiliary::axiom_permission_u64_ext_eq(
605 slot_own.ref_count_perm,
606 old(regions).slot_owner(paddr).ref_count_perm,
607 );
608 }
609
610 },
611 }
612 core::hint::spin_loop();
613 }
614 }
615
616 /// Increases the frame reference count by one.
617 ///
618 /// # Verified Properties
619 /// ## Preconditions
620 /// - **Bookkeeping**: The permission must match the reference count being updated.
621 /// - **Liveness**: The function will abort if the reference count is at the maximum.
622 /// ## Postconditions
623 /// - **Correctness**: The reference count is increased by one.
624 /// ## Safety
625 /// By requiring the caller to provide a permission for the reference count, we ensure that it already has a reference to the frame.
626 #[verus_spec(
627 with
628 Tracked(rc_perm): Tracked<&mut PermissionU64>,
629 requires
630 old(rc_perm).is_for(self.ref_count),
631 old(rc_perm).value() != REF_COUNT_UNUSED,
632 old(rc_perm).value() >= REF_COUNT_MAX ==> may_panic(),
633 ensures
634 final(rc_perm).value() == old(rc_perm).value() + 1,
635 old(rc_perm).value() < REF_COUNT_MAX,
636 final(rc_perm).id() == old(rc_perm).id(),
637 )]
638 pub(super) unsafe fn inc_ref_count(&self) {
639 let last_ref_cnt = self.ref_count.fetch_add(Tracked(rc_perm), 1);
640
641 if last_ref_cnt >= REF_COUNT_MAX {
642 // This follows the same principle as the `Arc::clone` implementation to prevent the
643 // reference count from overflowing. See also
644 // <https://doc.rust-lang.org/std/sync/struct.Arc.html#method.clone>.
645 panic_diverge();
646 }
647 }
648
649 /// Gets the corresponding frame's physical address.
650 ///
651 /// # Verified Properties
652 /// ## Preconditions
653 /// - **Safety**: The permission must point to a valid metadata slot.
654 /// - **Correctness**: The permission must point to the metadata slot.
655 /// ## Postconditions
656 /// - **Correctness**: The function returns the physical address of the frame.
657 /// ## Safety
658 /// The safety precondition requires that the permission points to a valid metadata slot.
659 /// This is an internal function, so it is fine to require the caller to verify this.
660 #[verus_spec(
661 with
662 Tracked(perm): Tracked<&PointsTo<MetaSlot>>,
663 requires
664 perm.value() == self,
665 Self::frame_paddr_safety_cond(*perm),
666 returns
667 meta_to_frame(perm.addr()),
668 )]
669 pub(super) fn frame_paddr(&self) -> (pa: Paddr) {
670 proof_with!(Tracked(perm));
671 let addr = self.addr_of();
672 meta_to_frame(addr)
673 }
674
675 /*
676 /// Gets a dynamically typed pointer to the stored metadata.
677 ///
678 /// # Safety
679 ///
680 /// The caller should ensure that:
681 /// - the stored metadata is initialized (by [`Self::write_meta`]) and valid.
682 ///
683 /// The returned pointer should not be dereferenced as mutable unless having
684 /// exclusive access to the metadata slot.
685
686 #[verifier::external_body]
687 pub(super) unsafe fn dyn_meta_ptr<M: AnyFrameMeta>(&self) -> PPtr<M> {
688 unimplemented!()
689
690 // SAFETY: The page metadata is valid to be borrowed immutably, since
691 // it will never be borrowed mutably after initialization.
692 let vtable_ptr = unsafe { *self.vtable_ptr.get() };
693
694 // SAFETY: The page metadata is initialized and valid.
695 let vtable_ptr = *unsafe { vtable_ptr.assume_init_ref() };
696
697 let meta_ptr: *mut dyn AnyFrameMeta =
698 core::ptr::from_raw_parts_mut(self as *const MetaSlot as *mut MetaSlot, vtable_ptr);
699
700 meta_ptr
701 }
702
703 /// Gets the stored metadata as type `M`.
704 ///
705 /// Calling the method should be safe, but using the returned pointer would
706 /// be unsafe. Specifically, the derefernecer should ensure that:
707 /// - the stored metadata is initialized (by [`Self::write_meta`]) and
708 /// valid;
709 /// - the initialized metadata is of type `M`;
710 /// - the returned pointer should not be dereferenced as mutable unless
711 /// having exclusive access to the metadata slot.
712 pub(super) fn as_meta_ptr<M: AnyFrameMeta>(&self) -> *mut M {
713 self.storage.get() as *mut M
714 }
715
716 */
717 /// Writes the metadata to the slot without reading or dropping the previous value.
718 ///
719 /// # Safety
720 ///
721 /// The caller should have exclusive access to the metadata slot's fields.
722 ///
723 /// # Verification Design
724 /// This function is axiomatized for now because of trait constraints.
725 /// ## Preconditions
726 /// - The caller must provide the permission token to the metadata slot's storage.
727 /// ## Postconditions
728 /// - The permission is initialized to the new metadata.
729 /// ## Safety
730 /// The caller must have exclusive access to the metadata slot's storage in order to provide the permission token.
731 #[verus_spec(
732 with
733 Tracked(metadata_perm): Tracked<&mut MetadataPerm>,
734 Tracked(repr_perm): Tracked<&mut M::ReprPerm>,
735 requires
736 self.storage.id() == old(metadata_perm).storage_perm.id(),
737 self.vtable_ptr == old(metadata_perm).vtable_ptr_perm.pptr(),
738 old(metadata_perm).vtable_ptr_perm.is_uninit(),
739 ensures
740 final(metadata_perm).storage_perm.id() == old(metadata_perm).storage_perm.id(),
741 final(metadata_perm).storage_perm.is_init(),
742 final(metadata_perm).vtable_ptr_perm.pptr() == old(metadata_perm).vtable_ptr_perm.pptr(),
743 final(metadata_perm).vtable_ptr_perm.is_init(),
744 <M as Repr<MetaSlotStorage>>::wf(
745 final(metadata_perm).storage_perm.value(),
746 *final(repr_perm),
747 ),
748 M::from_repr_spec(
749 final(metadata_perm).storage_perm.value(),
750 *final(repr_perm),
751 ) == metadata,
752 )]
753 pub(super) unsafe fn write_meta<M: AnyFrameMeta + Repr<MetaSlotStorage> + OwnerOf>(
754 &self,
755 metadata: M,
756 ) {
757 // SAFETY: Caller ensures that the access to the fields are exclusive.
758 // let vtable_ptr = unsafe { &mut *self.vtable_ptr.get() };
759 // vtable_ptr.write(core::ptr::metadata(&metadata as &dyn AnyFrameMeta));
760 self.vtable_ptr.put(Tracked(&mut metadata_perm.vtable_ptr_perm), 0);
761
762 // SAFETY:
763 // 1. `ptr` points to the metadata storage.
764 // 2. The size and the alignment of the metadata storage is large enough to hold `M`
765 // (guaranteed by the const assertions above).
766 // 3. We have exclusive access to the metadata storage (guaranteed by the caller).
767 // unsafe { ptr.cast::<M>().write(metadata) };
768 write_metadata_into_storage(
769 &self.storage,
770 Tracked(metadata_perm),
771 Tracked(repr_perm),
772 metadata,
773 );
774 }
775
776 /// Drops the metadata and deallocates the frame.
777 ///
778 /// # Safety
779 ///
780 /// The caller should ensure that:
781 /// - the reference count is `0` (so we are the sole owner of the frame);
782 /// - the metadata is initialized;
783 ///
784 /// # Verified Properties
785 /// ## Preconditions
786 /// - **Safety Invariant**: The metadata slot must satisfy the safety invariants.
787 /// - **Safety**: The caller must provide an owner object for the metadata slot, which must include the permission for the
788 /// slot's `ref_count` field.
789 /// - **Safety**: The owner must satisfy [`drop_last_in_place_safety_cond`], which ensures that its reference count is 0
790 /// and it has no dangling raw pointers.
791 /// ## Postconditions
792 /// - **Safety**: The metadata slot satisfies the safety invariants after the call.
793 /// - **Correctness**: The reference count is set to `REF_COUNT_UNUSED` and the contents of the slot are uninitialized.
794 /// ## Safety
795 /// - By requiring the caller to provide an owner object, we ensure that it already has a reference to the frame.
796 /// - The safety precondition ensures that there are no dangling pointers, including raw pointer, guaranteeing temporal safety.
797 #[verus_spec(
798 with
799 Tracked(owner): Tracked<&mut MetaSlotOwner>,
800 requires
801 old(owner).inv(),
802 self.ref_count.id() == old(owner).ref_count_perm.id(),
803 self.in_list.id() == old(owner).in_list_perm.id(),
804 self.storage.id() == old(owner).storage_perm().id(),
805 self.vtable_ptr == old(owner).vtable_ptr_perm().pptr(),
806 Self::drop_last_in_place_safety_cond(*old(owner)),
807 ensures
808 final(owner).inv(),
809 final(owner).ref_count() == REF_COUNT_UNUSED,
810 final(owner).ref_count_perm.id() == old(owner).ref_count_perm.id(),
811 final(owner).metadata_perm.id() == old(owner).metadata_perm.id(),
812 final(owner).storage_perm().id() == old(owner).storage_perm().id(),
813 final(owner).storage_perm().is_uninit(),
814 final(owner).vtable_ptr_perm().is_uninit(),
815 final(owner).vtable_ptr_perm().pptr() == old(owner).vtable_ptr_perm().pptr(),
816 final(owner).in_list_perm == old(owner).in_list_perm,
817 final(owner).slot_vaddr == old(owner).slot_vaddr,
818 final(owner).usage == old(owner).usage,
819 final(owner).paths_in_pt == old(owner).paths_in_pt,
820 )]
821 pub(super) unsafe fn drop_last_in_place(&self) {
822 // This should be guaranteed as a safety requirement.
823 // debug_assert_eq!(self.ref_count.load(Ordering::Relaxed), 0);
824 let tracked mut metadata_perm = owner.metadata_perm.take_resource();
825 // SAFETY: The caller ensures safety.
826 unsafe {
827 #[verus_spec(with
828 Tracked(&owner.ref_count_perm),
829 Tracked(&owner.in_list_perm),
830 Tracked(&mut metadata_perm)
831 )]
832 self.drop_meta_in_place()
833 };
834 proof {
835 owner.metadata_perm.put_resource(metadata_perm);
836 }
837
838 // `Release` pairs with the `Acquire` in `Frame::from_unused` and ensures
839 // `drop_meta_in_place` won't be reordered after this memory store.
840 self.ref_count.store(Tracked(&mut owner.ref_count_perm), REF_COUNT_UNUSED);
841 }
842
843 /// Drops the metadata of a slot in place.
844 ///
845 /// After this operation, the metadata becomes uninitialized. Any access to the
846 /// metadata is undefined behavior unless it is re-initialized by [`Self::write_meta`].
847 ///
848 /// # Verification Design
849 /// This function is axiomatized because of its reliance on dynamic trait methods and `VmReader`.
850 /// The latter dependency makes it part of the "bootstrap gap".
851 /// Now that Verus better supports the `dyn Trait` pattern and we have verified `VmReader`, we can revisit it.
852 /// ## Preconditions
853 /// - The caller must provide the concrete permissions for the metadata slot.
854 /// - The reference count must be `0` or [`REF_COUNT_UNIQUE`].
855 /// ## Safety
856 ///
857 /// The caller should ensure that:
858 /// - the reference count grants exclusive access (`0` or [`REF_COUNT_UNIQUE`]);
859 /// - the metadata is initialized;
860 #[verifier::external_body]
861 #[verus_spec(
862 with
863 Tracked(ref_count_perm): Tracked<&PermissionU64>,
864 Tracked(in_list_perm): Tracked<&PermissionU64>,
865 Tracked(metadata_perm): Tracked<&mut MetadataPerm>,
866 requires
867 self.ref_count.id() == ref_count_perm.id(),
868 ref_count_perm.value() == 0 || ref_count_perm.value() == REF_COUNT_UNIQUE,
869 self.in_list.id() == in_list_perm.id(),
870 in_list_perm.value() == 0,
871 old(metadata_perm).storage_perm.is_init(),
872 old(metadata_perm).storage_perm.id() == self.storage.id(),
873 old(metadata_perm).vtable_ptr_perm.is_init(),
874 old(metadata_perm).vtable_ptr_perm.pptr() == self.vtable_ptr,
875 ensures
876 final(metadata_perm).storage_perm.is_uninit(),
877 final(metadata_perm).storage_perm.id() == old(metadata_perm).storage_perm.id(),
878 final(metadata_perm).vtable_ptr_perm.is_uninit(),
879 final(metadata_perm).vtable_ptr_perm.pptr()
880 == old(metadata_perm).vtable_ptr_perm.pptr(),
881 )]
882 #[verifier::external_body]
883 pub(super) unsafe fn drop_meta_in_place(&self) {
884 let paddr = self.frame_paddr();
885
886 // SAFETY: We have exclusive access to the frame metadata.
887 let vtable_ptr: *const core::ptr::DynMetadata<dyn AnyFrameMeta> = unimplemented!();
888 // SAFETY: The frame metadata is initialized and valid.
889 let vtable_ptr = unsafe { *vtable_ptr };
890
891 let storage_ptr: *mut () = unimplemented!();
892 let meta_ptr: *mut dyn AnyFrameMeta = core::ptr::from_raw_parts_mut(
893 storage_ptr,
894 vtable_ptr,
895 );
896
897 // SAFETY: The implementer of the frame metadata decides that if the
898 // frame is safe to be read or not.
899 let mut reader: VmReader<'_, Infallible> = unimplemented!();
900
901 // SAFETY: `ptr` points to the metadata storage which is valid to be
902 // mutably borrowed under `vtable_ptr` because the metadata is valid,
903 // the vtable is correct, and we have exclusive access.
904 let regions: Tracked<&mut MetaRegionOwners> = Tracked::assume_new();
905 let vm_io_owner: Tracked<&mut crate::specs::mm::io::VmIoOwner> = Tracked::assume_new();
906 unsafe {
907 // Invoke the custom `on_drop` handler.
908 (*meta_ptr).on_drop(&mut reader, regions, vm_io_owner);
909 // Drop the frame metadata.
910 core::ptr::drop_in_place(meta_ptr);
911 }
912 }
913}
914
915/// The metadata of frames that holds metadata of frames.
916#[derive(Debug, Default)]
917pub struct MetaPageMeta {}
918
919//impl_frame_meta_for!(MetaPageMeta);
920/*
921/// Initializes the metadata of all physical frames.
922///
923/// The function returns a list of `Frame`s containing the metadata.
924///
925/// # Safety
926///
927/// This function should be called only once and only on the BSP,
928/// before any APs are started.
929pub(crate) unsafe fn init() -> Segment<MetaPageMeta> {
930 let max_paddr = {
931 let regions = &crate::boot::EARLY_INFO.get().unwrap().memory_regions;
932 regions
933 .iter()
934 .filter(|r| r.typ() == MemoryRegionType::Usable)
935 .map(|r| r.base() + r.len())
936 .max()
937 .unwrap()
938 };
939
940 info!(
941 "Initializing frame metadata for physical memory up to {:x}",
942 max_paddr
943 );
944
945 // In RISC-V, the boot page table has mapped the 512GB memory,
946 // so we don't need to add temporary linear mapping.
947 // In LoongArch, the DWM0 has mapped the whole memory,
948 // so we don't need to add temporary linear mapping.
949 #[cfg(target_arch = "x86_64")]
950 add_temp_linear_mapping(max_paddr);
951
952 let tot_nr_frames = max_paddr / page_size::<PagingConsts>(1);
953 let (nr_meta_pages, meta_pages) = alloc_meta_frames(tot_nr_frames);
954
955 // Map the metadata frames.
956 boot_pt::with_borrow(|boot_pt| {
957 for i in 0..nr_meta_pages {
958 let frame_paddr = meta_pages + i * PAGE_SIZE;
959 let vaddr = frame_to_meta::<PagingConsts>(0) + i * PAGE_SIZE;
960 let prop = PageProperty {
961 flags: PageFlags::RW,
962 cache: CachePolicy::Writeback,
963 priv_flags: PrivilegedPageFlags::GLOBAL,
964 };
965 // SAFETY: we are doing the metadata mappings for the kernel.
966 unsafe { boot_pt.map_base_page(vaddr, frame_paddr / PAGE_SIZE, prop) };
967 }
968 })
969 .unwrap();
970
971 // Now the metadata frames are mapped, we can initialize the metadata.
972 super::MAX_PADDR.store(max_paddr, Ordering::Relaxed);
973
974 let meta_page_range = meta_pages..meta_pages + nr_meta_pages * PAGE_SIZE;
975
976 let (range_1, range_2) = allocator::EARLY_ALLOCATOR
977 .lock()
978 .as_ref()
979 .unwrap()
980 .allocated_regions();
981 for r in range_difference(&range_1, &meta_page_range) {
982 let early_seg = Segment::from_unused(r, |_| EarlyAllocatedFrameMeta).unwrap();
983 let _ = ManuallyDrop::new(early_seg);
984 }
985 for r in range_difference(&range_2, &meta_page_range) {
986 let early_seg = Segment::from_unused(r, |_| EarlyAllocatedFrameMeta).unwrap();
987 let _ = ManuallyDrop::new(early_seg);
988 }
989
990 mark_unusable_ranges();
991
992 Segment::from_unused(meta_page_range, |_| MetaPageMeta {}).unwrap()
993}
994
995/// Returns whether the global frame allocator is initialized.
996pub(in crate::mm) fn is_initialized() -> bool {
997 // `init` sets it with relaxed ordering somewhere in the middle. But due
998 // to the safety requirement of the `init` function, we can assume that
999 // there is no race conditions.
1000 super::MAX_PADDR.load(Ordering::Relaxed) != 0
1001}
1002
1003fn alloc_meta_frames(tot_nr_frames: usize) -> (usize, Paddr) {
1004 let nr_meta_pages = tot_nr_frames
1005 .checked_mul(size_of::<MetaSlot>())
1006 .unwrap()
1007 .div_ceil(PAGE_SIZE);
1008 let start_paddr = allocator::early_alloc(
1009 Layout::from_size_align(nr_meta_pages * PAGE_SIZE, PAGE_SIZE).unwrap(),
1010 )
1011 .unwrap();
1012
1013 let slots = paddr_to_vaddr(start_paddr) as *mut MetaSlot;
1014
1015 // Initialize the metadata slots.
1016 for i in 0..tot_nr_frames {
1017 // SAFETY: The memory is successfully allocated with `tot_nr_frames`
1018 // slots so the index must be within the range.
1019 let slot = unsafe { slots.add(i) };
1020 // SAFETY: The memory is just allocated so we have exclusive access and
1021 // it's valid for writing.
1022 unsafe {
1023 slot.write(MetaSlot {
1024 storage: UnsafeCell::new(MetaSlotStorage::Empty([0; FRAME_METADATA_MAX_SIZE - 1])),
1025 ref_count: AtomicU64::new(REF_COUNT_UNUSED),
1026 vtable_ptr: UnsafeCell::new(MaybeUninit::uninit()),
1027 in_list: AtomicU64::new(0),
1028 })
1029 };
1030 }
1031
1032 (nr_meta_pages, start_paddr)
1033}
1034
1035/// Unusable memory metadata. Cannot be used for any purposes.
1036#[derive(Debug)]
1037pub struct UnusableMemoryMeta;
1038impl_frame_meta_for!(UnusableMemoryMeta);
1039
1040/// Reserved memory metadata. Maybe later used as I/O memory.
1041#[derive(Debug)]
1042pub struct ReservedMemoryMeta;
1043impl_frame_meta_for!(ReservedMemoryMeta);
1044
1045/// The metadata of physical pages that contains the kernel itself.
1046#[derive(Debug, Default)]
1047pub struct KernelMeta;
1048impl_frame_meta_for!(KernelMeta);
1049
1050macro_rules! mark_ranges {
1051 ($region: expr, $typ: expr) => {{
1052 debug_assert!($region.base() % PAGE_SIZE == 0);
1053 debug_assert!($region.len() % PAGE_SIZE == 0);
1054
1055 let seg = Segment::from_unused($region.base()..$region.end(), |_| $typ).unwrap();
1056 let _ = ManuallyDrop::new(seg);
1057 }};
1058}
1059
1060fn mark_unusable_ranges() {
1061 let regions = &crate::boot::EARLY_INFO.get().unwrap().memory_regions;
1062
1063 for region in regions
1064 .iter()
1065 .rev()
1066 .skip_while(|r| r.typ() != MemoryRegionType::Usable)
1067 {
1068 match region.typ() {
1069 MemoryRegionType::BadMemory => mark_ranges!(region, UnusableMemoryMeta),
1070 MemoryRegionType::Unknown => mark_ranges!(region, ReservedMemoryMeta),
1071 MemoryRegionType::NonVolatileSleep => mark_ranges!(region, UnusableMemoryMeta),
1072 MemoryRegionType::Reserved => mark_ranges!(region, ReservedMemoryMeta),
1073 MemoryRegionType::Kernel => mark_ranges!(region, KernelMeta),
1074 MemoryRegionType::Module => mark_ranges!(region, UnusableMemoryMeta),
1075 MemoryRegionType::Framebuffer => mark_ranges!(region, ReservedMemoryMeta),
1076 MemoryRegionType::Reclaimable => mark_ranges!(region, UnusableMemoryMeta),
1077 MemoryRegionType::Usable => {} // By default it is initialized as usable.
1078 }
1079 }
1080}
1081
1082/// Adds a temporary linear mapping for the metadata frames.
1083///
1084/// We only assume boot page table to contain 4G linear mapping. Thus if the
1085/// physical memory is huge we end up depleted of linear virtual memory for
1086/// initializing metadata.
1087#[cfg(target_arch = "x86_64")]
1088fn add_temp_linear_mapping(max_paddr: Paddr) {
1089 const PADDR4G: Paddr = 0x1_0000_0000;
1090
1091 if max_paddr <= PADDR4G {
1092 return;
1093 }
1094
1095 // TODO: We don't know if the allocator would allocate from low to high or
1096 // not. So we prepare all linear mappings in the boot page table. Hope it
1097 // won't drag the boot performance much.
1098 let end_paddr = max_paddr.align_up(PAGE_SIZE);
1099 let prange = PADDR4G..end_paddr;
1100 let prop = PageProperty {
1101 flags: PageFlags::RW,
1102 cache: CachePolicy::Writeback,
1103 priv_flags: PrivilegedPageFlags::GLOBAL,
1104 };
1105
1106 // SAFETY: we are doing the linear mapping for the kernel.
1107 unsafe {
1108 boot_pt::with_borrow(|boot_pt| {
1109 for paddr in prange.step_by(PAGE_SIZE) {
1110 let vaddr = LINEAR_MAPPING_BASE_VADDR + paddr;
1111 boot_pt.map_base_page(vaddr, paddr / PAGE_SIZE, prop);
1112 }
1113 })
1114 .unwrap();
1115 }
1116}
1117*/
1118} // verus!