ostd/mm/frame/mod.rs
1// SPDX-License-Identifier: MPL-2.0
2//! Frame (physical memory page) management.
3//!
4//! A frame is an aligned, contiguous range of bytes in physical memory. The
5//! sizes of base frames and huge frames (that are mapped as "huge pages") are
6//! architecture-dependent. A frame can be mapped to virtual address spaces
7//! using the page table.
8//!
9//! Frames can be accessed through frame handles, namely, [`Frame`]. A frame
10//! handle is a reference-counted pointer to a frame. When all handles to a
11//! frame are dropped, the frame is released and can be reused. Contiguous
12//! frames are managed with [`Segment`].
13//!
14//! There are various kinds of frames. The top-level grouping of frame kinds
15//! are "typed" frames and "untyped" frames. Typed frames host Rust objects
16//! that must follow the visibility, lifetime and borrow rules of Rust, thus
17//! not being able to be directly manipulated. Untyped frames are raw memory
18//! that can be manipulated directly. So only untyped frames can be
19//! - safely shared to external entities such as device drivers or user-space
20//! applications.
21//! - or directly manipulated with readers and writers that neglect Rust's
22//! "alias XOR mutability" rule.
23//!
24//! The kind of a frame is determined by the type of its metadata. Untyped
25//! frames have its metadata type that implements the [`AnyUFrameMeta`]
26//! trait, while typed frames don't.
27//!
28//! Frames can have dedicated metadata, which is implemented in the [`meta`]
29//! module. The reference count and usage of a frame are stored in the metadata
30//! as well, leaving the handle only a pointer to the metadata slot. Users
31//! can create custom metadata types by implementing the [`AnyFrameMeta`] trait.
32use vstd::{
33 assert_maps_equal, assert_sets_equal,
34 atomic::PermissionU64,
35 map::assert_maps_equal_internal,
36 prelude::*,
37 simple_pptr::{self, PPtr},
38 std_specs::cmp::PartialEqSpecImpl,
39};
40use vstd_extra::{cast_ptr::*, ownership::*, panic::may_panic};
41
42pub mod allocator;
43pub mod linked_list;
44pub mod meta;
45pub mod segment;
46pub mod unique;
47pub mod untyped;
48
49mod frame_ref;
50pub use frame_ref::FrameRef;
51
52#[cfg(ktest)]
53mod test;
54
55use crate::specs::{
56 arch::*,
57 mm::frame::{frame_specs::*, mapping::*, meta_owners::*, meta_region_owners::MetaRegionOwners},
58};
59
60use core::{
61 marker::PhantomData,
62 mem::ManuallyDrop,
63 sync::atomic::{AtomicUsize, Ordering},
64};
65
66//pub use allocator::GlobalFrameAllocator;
67use super::PagingLevel;
68use crate::mm::kspace::FRAME_METADATA_RANGE;
69use crate::mm::page_table::RCClone;
70use crate::mm::page_table::{PageTableConfig, PageTablePageMeta};
71use crate::mm::{
72 MAX_PADDR, Paddr, Vaddr,
73 frame::meta::{
74 META_SLOT_SIZE,
75 mapping::{frame_to_meta, meta_to_frame},
76 },
77 kspace::{LINEAR_MAPPING_BASE_VADDR, VMALLOC_BASE_VADDR},
78};
79pub use linked_list::{CursorMut, Link, LinkedList};
80pub use meta::{AnyFrameMeta, GetFrameError, MetaSlot};
81use meta::{REF_COUNT_MAX, REF_COUNT_UNIQUE, REF_COUNT_UNUSED, mapping};
82pub use segment::Segment;
83pub use unique::UniqueFrame;
84pub use untyped::{AnyUFrameMeta, UFrame};
85
86verus! {
87
88/*
89static MAX_PADDR: AtomicUsize = AtomicUsize::new(0);
90*/
91/// Returns the maximum physical address that is tracked by frame metadata.
92#[verifier::external_body]
93pub(in crate::mm) fn max_paddr() -> Paddr
94 returns
95 MAX_PADDR,
96{
97 // let max_paddr = MAX_PADDR.load(Ordering::Relaxed) as Paddr;
98 // debug_assert_ne!(max_paddr, 0);
99 // max_paddr
100 unimplemented!()
101}
102
103#[verifier::external_body]
104fn acquire_fence() {
105 core::sync::atomic::fence(Ordering::Acquire);
106}
107
108/// A smart pointer to a frame.
109///
110/// A frame is a contiguous range of bytes in physical memory. The [`Frame`]
111/// type is a smart pointer to a frame that is reference-counted.
112///
113/// Frames are associated with metadata. The type of the metadata `M` is
114/// determines the kind of the frame. If `M` implements [`AnyUFrameMeta`], the
115/// frame is a untyped frame. Otherwise, it is a typed frame.
116// #[repr(transparent)]
117pub struct Frame<M: ?Sized> {
118 pub ptr: PPtr<MetaSlot>,
119 pub _marker: PhantomData<M>,
120 /// The permission to access the `MetaSlot` fields.
121 #[cfg(verus_keep_ghost_body)]
122 pub tracked_slot_perm: Tracked<&'static simple_pptr::PointsTo<MetaSlot>>,
123 /// One fractional permission for the currently installed metadata.
124 #[cfg(verus_keep_ghost_body)]
125 pub tracked_metadata_perm: Tracked<Option<FracMetadataPerm>>,
126}
127
128#[verifier::external]
129unsafe impl<M: AnyFrameMeta + ?Sized> Send for Frame<M> {
130
131}
132
133#[verifier::external]
134unsafe impl<M: AnyFrameMeta + ?Sized> Sync for Frame<M> {
135
136}
137
138/*
139impl<M: AnyFrameMeta + ?Sized> core::fmt::Debug for Frame<M> {
140 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
141 write!(f, "Frame({:#x})", self.start_paddr())
142 }
143}
144*/
145
146verus!{
147
148impl<M: AnyFrameMeta + Repr<MetaSlotStorage> + ?Sized> PartialEqSpecImpl for Frame<M>{
149 open spec fn obeys_eq_spec() -> bool { true }
150
151 open spec fn eq_spec(&self, other: &Self) -> bool {
152 self.start_paddr_spec() == other.start_paddr_spec()
153 }
154}
155
156}
157
158#[verus_verify]
159impl<M: AnyFrameMeta + Repr<MetaSlotStorage> + ?Sized> PartialEq for Frame<M> {
160 fn eq(&self, other: &Self) -> bool {
161 proof!{
162 //FIXME: Add `ptr_inv` as type invariant when we fix visibility.
163 assume(self.ptr_inv());
164 assume(other.ptr_inv());
165 }
166 self.start_paddr() == other.start_paddr()
167 }
168}
169
170#[verus_verify]
171impl<M: AnyFrameMeta + Repr<MetaSlotStorage> + ?Sized> Eq for Frame<M> {
172
173}
174
175#[verus_verify]
176impl<M: AnyFrameMeta + Repr<MetaSlotStorage> + OwnerOf> Frame<M> {
177 /// Gets a [`Frame`] with a specific usage from a raw, unused page.
178 ///
179 /// The caller should provide the initial metadata of the page.
180 ///
181 /// If the provided frame is not truly unused at the moment, it will return
182 /// an error. If wanting to acquire a frame that is already in use, use
183 /// [`Frame::from_in_use`] instead.
184 /// # Verified Properties
185 /// ## Preconditions
186 /// - **Safety Invariant**: Metaslot region invariants must hold.
187 /// ## Postconditions
188 /// - **Safety Invariant**: Metaslot region invariants hold after the call.
189 /// - **Correctness**: If successful, the function returns a pointer to the metadata slot and a permission to the slot.
190 /// - **Correctness**: If successful, the slot is initialized with the given metadata.
191 /// - **Correctness**: If `paddr` does not have a corresponding metadata slot, the function returns an error.
192 /// - **Drop Bookkeeping**: If successful, the function returns a live frame, which is tracked correctly as needing to be dropped.
193 /// ## Safety
194 /// - This function returns an error if `paddr` does not correspond to a valid slot or the slot is in use.
195 #[verus_spec(r =>
196 with
197 Tracked(regions): Tracked<&mut MetaRegionOwners>,
198 Tracked(repr_perm): Tracked<&mut M::ReprPerm>
199 requires
200 old(regions).inv(),
201 ensures
202 final(regions).inv(),
203 r matches Ok(res) ==> {
204 &&& MetaSlot::get_from_unused_region_spec(
205 paddr, false, res.metadata_perm().usage(),
206 *old(regions), *final(regions))
207 &&& typed_meta_wf::<M>(res.slot_perm(), res.metadata_perm(), *final(repr_perm))
208 &&& M::from_repr_spec(res.metadata_perm().storage_perm.value(), *final(repr_perm)) == metadata
209 &&& res.inv()
210 &&& res.start_paddr_spec() == paddr
211 &&& res.wf_with_region(*final(regions))
212 },
213 r is Err ==> *final(regions) == *old(regions)
214 )]
215 pub fn from_unused(paddr: Paddr, metadata: M) -> Result<Self, GetFrameError> {
216 #[verus_spec(with
217 Tracked(regions),
218 Tracked(repr_perm) => Tracked(permissions)
219 )]
220 let from_unused = MetaSlot::get_from_unused(paddr, metadata, false);
221 if let Err(err) = from_unused {
222 Err(err)
223 } else {
224 proof_decl! {
225 lemma_paddr_to_meta_biinjective(paddr);
226 lemma_frame_to_meta_soundness(paddr);
227 let tracked slot_perm = regions.tracked_borrow_slot(paddr);
228 let ghost idx = frame_to_index(paddr);
229 assert(regions.slot_owners.contains_key(idx));
230 let tracked metadata_perm = permissions.tracked_unwrap().tracked_take_left();
231 }
232 let ptr = from_unused.unwrap();
233 Ok(
234 Self {
235 ptr,
236 _marker: PhantomData,
237 #[cfg(verus_keep_ghost_body)]
238 tracked_slot_perm: Tracked(slot_perm),
239 #[cfg(verus_keep_ghost_body)]
240 tracked_metadata_perm: Tracked(Some(metadata_perm)),
241 },
242 )
243 }
244 }
245
246 /// Gets the metadata of this page.
247 /// # Verified Properties
248 /// ## Preconditions
249 /// - The caller must have a valid permission for the frame.
250 /// ## Postconditions
251 /// - The function returns the borrowed metadata of the frame.
252 /// ## Safety
253 /// - By requiring the caller to provide a typed permission, we ensure that the metadata is of type `M`.
254 /// While a non-verified caller cannot be trusted to obey this interface, all functions that return a `Frame<M>` also
255 /// return an appropriate permission.
256 #[verus_spec(ret =>
257 with
258 Tracked(metadata_perm): Tracked<Option<&'a MetadataPerm>>,
259 Tracked(repr_perm): Tracked<&'a M::ReprPerm>,
260 requires
261 {
262 self.inv() && metadata_perm is None
263 && typed_meta_wf::<M>(self.slot_perm(), self.metadata_perm(), *repr_perm) ||
264 metadata_perm is Some
265 && self.external_meta_wf(*metadata_perm->0, *repr_perm)
266 },
267 ensures
268 {
269 let metadata_perm = if metadata_perm is Some {*metadata_perm -> 0} else
270 { self.metadata_perm() };
271 ret == typed_meta_value::<M>(metadata_perm, *repr_perm)
272 },
273 )]
274 pub fn meta<'a>(&'a self) -> &'a M {
275 // SAFETY: The type is tracked by the typed storage permission.
276 // unsafe { &*self.slot().as_meta_ptr::<M>() }
277 proof_decl! {
278 let tracked slot_perm = self.tracked_slot_perm.borrow();
279 let tracked metadata_perm = if metadata_perm is Some {
280 metadata_perm.tracked_borrow()
281 } else {
282 self.tracked_metadata_perm.tracked_borrow().tracked_borrow()
283 };
284 }
285 borrow_meta(
286 ReprPtr::<MetaSlotStorage, M>::from_pptr(PPtr::from_addr(self.ptr.addr())),
287 Tracked(slot_perm),
288 Tracked(metadata_perm),
289 Tracked(repr_perm),
290 )
291 }
292}
293
294#[verus_verify]
295impl<M: AnyFrameMeta + Repr<MetaSlotStorage>> Frame<M> {
296 /// Gets a dynamically typed [`Frame`] from a raw, in-use page.
297 ///
298 /// If the provided frame is not in use at the moment, it will return an error.
299 ///
300 /// The returned frame will have an extra reference count to the frame.
301 ///
302 /// # Verified Properties
303 /// ## Preconditions
304 /// - **Safety Invariant**: Metaslot region invariants must hold.
305 /// - *Termination*: The function may panic if `paddr` is a valid slot and its reference count is saturated.
306 /// ## Postconditions
307 /// - **Safety Invariant**: Metaslot region invariants hold after the call.
308 /// - **Correctness**: If successful, the function returns the frame at `paddr`.
309 /// - **Correctness**: If successful, the frame has an extra reference count.
310 /// - **Correctness**: If `paddr` does not have a valid metadata slot, the function returns an error.
311 /// - **Safety**: Frames other than the one at `paddr` are not affected by the call.
312 /// ## Safety
313 /// - If `paddr` is a valid frame address, it is safe to take a reference to the frame.
314 /// - If `paddr` is not a valid frame address, the function will return an error.
315 #[verus_spec(res =>
316 with Tracked(regions) : Tracked<&mut MetaRegionOwners>,
317 requires
318 old(regions).inv(),
319 valid_frame_paddr(paddr) ==> old(regions).ref_count(frame_to_index(paddr)) >= REF_COUNT_MAX ==> may_panic(),
320 ensures
321 final(regions).inv(),
322 res matches Ok(res) ==> {
323 &&& old(regions).inc_frame_reference_region_spec(paddr, *final(regions))
324 &&& res.inv()
325 &&& res.start_paddr_spec() == paddr
326 &&& res.wf_with_region(*final(regions))
327 },
328 res is Err ==> *old(regions) == *final(regions),
329 )]
330 pub fn from_in_use(paddr: Paddr) -> Result<Self, GetFrameError> {
331 proof_decl!{
332 let tracked frame_permission: Option<FracMetadataPerm>;
333 }
334 let res = #[verus_spec(with Tracked(regions) => Tracked(frame_permission))]
335 MetaSlot::get_from_in_use(paddr);
336 match res {
337 Ok(ptr) => {
338 proof {
339 regions.lemma_contains_valid_frame_paddr(paddr);
340 }
341 let tracked slot_perm = regions.tracked_borrow_slot(paddr);
342 Ok(
343 Self {
344 ptr,
345 _marker: PhantomData,
346 #[cfg(verus_keep_ghost_body)]
347 tracked_slot_perm: Tracked(slot_perm),
348 #[cfg(verus_keep_ghost_body)]
349 tracked_metadata_perm: Tracked(frame_permission),
350 },
351 )
352 },
353 Err(e) => Err(e),
354 }
355 }
356}
357
358#[verus_verify]
359impl<M: AnyFrameMeta + Repr<MetaSlotStorage> + ?Sized> Frame<M> {
360 /// Gets the physical address of the start of the frame.
361 /// # Verified Properties
362 /// ## Preconditions
363 /// - **Bookkeeping**: takes the permission for the frame's metadata slot.
364 /// ## Postconditions
365 /// - **Correctness**: returns the physical address of the frame.
366 /// ## Safety
367 /// The caller cannot obtain a frame that doesn't have a valid permission,
368 /// and this function does not mutate any state, so it is always sound to call.
369 #[verus_spec(
370 requires
371 self.ptr_inv(),
372 returns
373 self.start_paddr_spec(),
374 )]
375 pub fn start_paddr(&self) -> Paddr {
376 let slot = self.slot();
377
378 #[verus_spec(with self.tracked_slot_perm)]
379 slot.frame_paddr()
380 }
381
382 /// Gets the map level of this page.
383 ///
384 /// This is the level of the page table entry that maps the frame,
385 /// which determines the size of the frame.
386 ///
387 /// Currently, the level is always 1, which means the frame is a regular
388 /// page frame.
389 pub const fn map_level(&self) -> PagingLevel
390 returns
391 1u8,
392 {
393 1
394 }
395
396 /// Gets the size of this page in bytes.
397 pub const fn size(&self) -> usize
398 returns
399 PAGE_SIZE,
400 {
401 PAGE_SIZE
402 }
403
404 /* /// Gets the dynamically-typed metadata of this frame.
405 ///
406 /// If the type is known at compile time, use [`Frame::meta`] instead.
407 pub fn dyn_meta(&self) -> FrameMeta {
408 // SAFETY: The metadata is initialized and valid.
409 unsafe { &*self.slot().dyn_meta_ptr() }
410 }*/
411 /// Gets the reference count of the frame.
412 ///
413 /// It returns the number of all references to the frame, including all the
414 /// existing frame handles ([`Frame`], [`Frame<dyn AnyFrameMeta>`]), and all
415 /// the mappings in the page table that points to the frame.
416 ///
417 /// ## Safety
418 ///
419 /// The function is safe to call, but using it requires extra care. The
420 /// reference count can be changed by other threads at any time including
421 /// potentially between calling this method and acting on the result.
422 ///
423 /// # Verified Properties
424 /// ## Preconditions
425 /// - **Safety Invariant**: Metaslot region invariants must hold.
426 /// - **Bookkeeping**: The caller must have a valid and well-typed permission for the frame.
427 /// ## Postconditions
428 /// - **Correctness**: The function returns the reference count of the frame.
429 #[verus_spec(
430 with
431 Tracked(slot_own): Tracked<&MetaSlotOwner>,
432 requires
433 self.ptr_inv(),
434 self.tracked_slot_perm@.value().wf(*slot_own),
435 returns
436 slot_own.ref_count(),
437 )]
438 pub fn reference_count(&self) -> u64 {
439 let refcnt = self.slot().ref_count.load(Tracked(&slot_own.ref_count_perm));
440 refcnt
441 }
442
443 /// Borrows a reference from the given frame.
444 /// # Verified Properties
445 /// ## Preconditions
446 /// - **Safety Invariant**: Metaslot region invariants must hold.
447 /// ## Postconditions
448 /// - **Safety Invariant**: Metaslot region invariants hold after the call.
449 /// - **Correctness**: The function returns a reference to the frame.
450 /// - **Correctness**: The system context is unchanged.
451 #[verus_spec(res =>
452 requires
453 self.inv(),
454 ensures
455 res.inner@.slot_vaddr() == self.slot_vaddr(),
456 res.inner@.ptr_inv(),
457 res.inner@.slot_perm() == self.slot_perm(),
458 res.inner@.tracked_metadata_perm@ is None,
459 MetaSlot::perms_related(res.inner@.slot_perm(), self.metadata_perm()),
460 forall|repr_perm: M::ReprPerm|
461 #[trigger] typed_meta_wf::<M>(self.slot_perm(), self.metadata_perm(), repr_perm)
462 ==> res.inner@.external_meta_wf(self.metadata_perm(), repr_perm),
463 )]
464 pub fn borrow<'a>(&self) -> FrameRef<'a, M> {
465 let tracked slot_perm = *self.tracked_slot_perm.borrow();
466 let tracked metadata_perm = self.tracked_metadata_perm.borrow().tracked_borrow();
467 // SAFETY: Both the lifetime and the type matches `self`.
468 unsafe {
469 #[verus_spec(with Tracked(slot_perm), Tracked(metadata_perm))]
470 FrameRef::borrow_paddr(self.start_paddr())
471 }
472 }
473
474 /// Borrows a frame whose owning handle is represented by an external
475 /// counting permission, as is the case for page-table nodes stored raw in
476 /// PTEs.
477 #[verus_spec(res =>
478 with
479 Tracked(frame_permission): Tracked<&FracMetadataPerm>,
480 Tracked(regions): Tracked<&MetaRegionOwners>,
481 requires
482 self.ptr_inv(),
483 regions.inv(),
484 frame_permission.frac() == 1,
485 frame_permission.id() == regions.slot_owners[self.index()].metadata_perm.id(),
486 MetaSlot::perms_related(
487 *regions.slots[self.index()],
488 frame_permission.resource(),
489 ),
490 ensures
491 res.inner@.slot_vaddr() == self.slot_vaddr(),
492 res.inner@.ptr_inv(),
493 res.inner@.tracked_slot_perm@ == regions.slots[self.index()],
494 res.inner@.tracked_metadata_perm@ is None,
495 MetaSlot::perms_related(res.inner@.slot_perm(), frame_permission.resource()),
496 forall|repr_perm: M::ReprPerm|
497 #[trigger] typed_meta_wf::<M>(self.slot_perm(), frame_permission.resource(), repr_perm)
498 ==> res.inner@.external_meta_wf(frame_permission.resource(), repr_perm),
499 )]
500 pub(in crate::mm) fn borrow_with_permission<'a>(&self) -> FrameRef<'a, M> {
501 unsafe {
502 proof {
503 broadcast use group_page_meta;
504
505 regions.lemma_contains_valid_frame_paddr(self.start_paddr_spec());
506 }
507 let tracked slot_perm = regions.tracked_borrow_slot(self.start_paddr_spec());
508 #[verus_spec(with Tracked(slot_perm), Tracked(frame_permission))]
509 FrameRef::borrow_paddr(self.start_paddr())
510 }
511 }
512
513 /// Forgets the handle to the frame.
514 ///
515 /// This will result in the frame being leaked without calling the custom dropper.
516 ///
517 /// A physical address to the frame is returned in case the frame needs to be
518 /// restored using [`Frame::from_raw`] later. This is useful when some architectural
519 /// data structures need to hold the frame handle such as the page table.
520 ///
521 /// # Verified Properties
522 /// ## Preconditions
523 /// - **Safety**: The frame's invariant must be satisfied.
524 /// ## Postconditions
525 /// - **Correctness**: The function returns the physical address of the frame.
526 /// ## Safety
527 /// - We require the slot to be in use to ensure that a fresh frame handle will not be created until the raw frame is restored.
528 /// - The owner's raw count is incremented so that we can enforce the safety requirement on `Frame::from_raw`.
529 #[verus_spec(r =>
530 with
531 -> raw_permission: Tracked<FrameRawPerms>,
532 requires
533 self.inv(),
534 ensures
535 r == self.start_paddr_spec(),
536 raw_permission@.inv(),
537 )]
538 pub(in crate::mm) fn into_raw(mut self) -> Paddr {
539 broadcast use group_page_meta;
540
541 proof_decl!{
542 let tracked perm = FrameRawPerms {
543 slot_perm: &*self.tracked_slot_perm,
544 metadata_perm: self.tracked_metadata_perm.tracked_take(),
545 };
546 }
547
548 let this = ManuallyDrop::new(self);
549 proof_with!(|= Tracked(perm));
550 this.start_paddr()
551 }
552}
553
554#[verus_verify]
555impl<M: ?Sized> Frame<M> {
556 /// Gets the metadata slot of the frame.
557 ///
558 /// # Verified Properties
559 /// ## Preconditions
560 /// - **Safety**: The caller must have a valid permission for the frame.
561 /// ## Postconditions
562 /// - **Correctness**: The function returns a reference to the metadata slot of the frame.
563 /// ## Safety
564 /// - There is no way to mutably borrow the metadata slot, so taking an immutable reference is safe.
565 /// (The fields of the slot can be mutably borrowed, but not the slot itself.)
566 #[verus_spec(
567 requires
568 self.ptr_inv(),
569 returns
570 self.tracked_slot_perm@.value(),
571 )]
572 pub fn slot<'a>(&'a self) -> &'a MetaSlot {
573 // SAFETY: `ptr` points to a valid `MetaSlot` that will never be
574 // mutably borrowed, so taking an immutable reference to it is safe.
575 proof_decl! {
576 let tracked slot_perm = *self.tracked_slot_perm;
577 }
578 self.ptr.borrow(Tracked(slot_perm))
579 }
580}
581
582#[verus_verify]
583impl<M: AnyFrameMeta + Repr<MetaSlotStorage> + ?Sized> Frame<M> {
584 /// Restores a forgotten [`Frame`] from a physical address.
585 ///
586 /// # Safety
587 ///
588 /// The caller should only restore a `Frame` that was previously forgotten using
589 /// [`Frame::into_raw`].
590 ///
591 /// And the restoring operation should only be done once for a forgotten
592 /// [`Frame`]. Otherwise double-free will happen.
593 ///
594 /// Also, the caller ensures that the usage of the frame is correct. There's
595 /// no checking of the usage in this function.
596 #[verus_spec(r =>
597 with
598 Tracked(perm): Tracked<FrameRawPerms>,
599 requires
600 valid_frame_paddr(paddr),
601 perm.slot_vaddr() == frame_to_meta(paddr),
602 perm.inv(),
603 ensures
604 r.tracked_slot_perm@ == perm.slot_perm,
605 r.tracked_metadata_perm@ == Some(perm.metadata_perm),
606 r.start_paddr_spec() == paddr,
607 r.inv(),
608 )]
609 pub(in crate::mm) unsafe fn from_raw(paddr: Paddr) -> Self
610 no_unwind
611 {
612 // debug_assert!(paddr < max_paddr());
613 let vaddr = frame_to_meta(paddr);
614 // let ptr = vaddr as *const MetaSlot;
615 let ptr = PPtr(vaddr, PhantomData);
616
617 proof_with!{ tracked_slot_perm: Tracked(perm.slot_perm), tracked_metadata_perm: Tracked(Some(perm.metadata_perm)) }
618 Self { ptr, _marker: PhantomData }
619 }
620}
621
622#[verus_verify]
623impl<M: AnyFrameMeta + Repr<MetaSlotStorage>> RCClone for Frame<M> {
624 open spec fn clone_requires(self, regions: MetaRegionOwners) -> bool {
625 let paddr = self.start_paddr_spec();
626 let ref_count = regions.slot_owner(paddr).ref_count();
627 &&& self.inv()
628 &&& regions.inv()
629 &&& self.wf_with_region(regions)
630 &&& ref_count > 0
631 &&& ref_count != REF_COUNT_UNUSED
632 &&& ref_count >= REF_COUNT_MAX ==> may_panic()
633 }
634
635 open spec fn clone_ensures(
636 self,
637 pre: MetaRegionOwners,
638 post: MetaRegionOwners,
639 res: Self,
640 ) -> bool {
641 let idx = self.index();
642 &&& pre.inc_frame_reference_region_spec(self.start_paddr_spec(), post)
643 &&& post.inv()
644 &&& res.tracked_metadata_perm@ is Some
645 &&& res.tracked_metadata_perm@->0.frac() == 1
646 &&& res.tracked_metadata_perm@->0.id() == post.slot_owners[idx].metadata_perm.id()
647 &&& res.tracked_slot_perm@ == post.slots[idx]
648 &&& res.ptr == self.ptr
649 }
650
651 fn clone(&self, Tracked(regions): Tracked<&mut MetaRegionOwners>) -> Self {
652 proof_decl! {
653 regions.lemma_contains_valid_frame_paddr(self.start_paddr_spec());
654 let ghost paddr = self.start_paddr_spec();
655 let tracked slot_perm = regions.tracked_borrow_slot(paddr);
656 let tracked slot_own = regions.tracked_borrow_mut_slot_owner(paddr);
657 }
658
659 // SAFETY: We have already held a reference to the frame.
660 unsafe {
661 #[verus_spec(with Tracked(&mut slot_own.ref_count_perm))]
662 self.slot().inc_ref_count();
663 }
664
665 proof_decl!{
666 let tracked metadata_perm = slot_own.metadata_perm.split_one();
667 };
668
669 Self {
670 ptr: PPtr::<MetaSlot>::from_addr(self.ptr.0),
671 _marker: PhantomData,
672 #[cfg(verus_keep_ghost_body)]
673 tracked_slot_perm: Tracked(slot_perm),
674 #[cfg(verus_keep_ghost_body)]
675 tracked_metadata_perm: Tracked(Some(metadata_perm)),
676 }
677 }
678}
679
680/*
681impl<M: AnyFrameMeta + ?Sized> Drop for Frame<M> {
682 fn drop(&mut self) {
683 let last_ref_cnt = self.slot().ref_count.fetch_sub(1, Ordering::Release);
684 debug_assert!(last_ref_cnt != 0 && last_ref_cnt != REF_COUNT_UNUSED);
685
686 if last_ref_cnt == 1 {
687 // A fence is needed here with the same reasons stated in the implementation of
688 // `Arc::drop`: <https://doc.rust-lang.org/std/sync/struct.Arc.html#method.drop>.
689 core::sync::atomic::fence(Ordering::Acquire);
690
691 // SAFETY: this is the last reference and is about to be dropped.
692 unsafe { self.slot().drop_last_in_place() };
693
694 allocator::get_global_frame_allocator().dealloc(self.start_paddr(), PAGE_SIZE);
695 }
696 }
697}*/
698
699impl<M: ?Sized> Frame<M> {
700 pub fn drop(mut self, Tracked(regions): Tracked<&mut MetaRegionOwners>)
701 requires
702 self.inv(),
703 old(regions).inv(),
704 self.wf_with_region(*old(regions)),
705 old(regions).slot_owners[self.index()].ref_count() == 1 ==> old(
706 regions,
707 ).slot_owners[self.index()].paths_in_pt.is_empty(),
708 ensures
709 final(regions).inv(),
710 ({
711 let paddr = self.start_paddr_spec();
712 let old_owner = old(regions).slot_owner(paddr);
713 let new_owner = final(regions).slot_owner(paddr);
714 &&& *final(regions) =~= old(regions).insert_slot_owner(paddr, new_owner)
715 &&& new_owner.slot_vaddr == old_owner.slot_vaddr
716 &&& new_owner.usage == old_owner.usage
717 &&& new_owner.paths_in_pt == old_owner.paths_in_pt
718 &&& new_owner.metadata_perm.id() == old_owner.metadata_perm.id()
719 &&& old_owner.ref_count() == 1 ==> new_owner.ref_count() == REF_COUNT_UNUSED
720 &&& old_owner.ref_count() > 1 ==> new_owner.ref_count() == (old_owner.ref_count()
721 - 1) as u64
722 }),
723 {
724 proof_decl!{
725 let ghost idx = self.index();
726 let tracked slot_own = regions.tracked_borrow_mut_slot_owner(self.start_paddr_spec());
727 let tracked frame_permission = self.tracked_metadata_perm.tracked_take();
728 slot_own.metadata_perm.combine(frame_permission);
729 }
730
731 let last_ref_cnt = self.slot().ref_count.fetch_sub(
732 Tracked(&mut slot_own.ref_count_perm),
733 1,
734 );
735
736 if last_ref_cnt == 1 {
737 // A fence is needed here with the same reasons stated in the implementation of
738 // `Arc::drop`: <https://doc.rust-lang.org/std/sync/struct.Arc.html#method.drop>.
739 acquire_fence();
740 // SAFETY: this is the last reference and is about to be dropped.
741 unsafe {
742 #[verus_spec(with Tracked(slot_own))]
743 self.slot().drop_last_in_place()
744 };
745
746 // TODO: return page to allocator
747 // allocator::get_global_frame_allocator().dealloc(paddr, PAGE_SIZE);
748 }
749 proof {
750 assert_maps_equal!(
751 regions.slot_owners,
752 old(regions).slot_owners.insert(idx, regions.slot_owners[idx]),
753 i => {
754 if i != idx {
755 assert(regions.slot_owners[i] == old(regions).slot_owners[i]);
756 }
757 }
758 );
759 }
760 }
761}
762
763/*
764
765impl<M: AnyFrameMeta> TryFrom<Frame<dyn AnyFrameMeta>> for Frame<M> {
766 type Error = Frame<dyn AnyFrameMeta>;
767
768 /// Tries converting a [`Frame<dyn AnyFrameMeta>`] into the statically-typed [`Frame`].
769 ///
770 /// If the usage of the frame is not the same as the expected usage, it will
771 /// return the dynamic frame itself as is.
772 fn try_from(dyn_frame: Frame<dyn AnyFrameMeta>) -> Result<Self, Self::Error> {
773 if (dyn_frame.dyn_meta() as &dyn core::any::Any).is::<M>() {
774 // SAFETY: The metadata is coerceable and the struct is transmutable.
775 Ok(unsafe { core::mem::transmute::<Frame<dyn AnyFrameMeta>, Frame<M>>(dyn_frame) })
776 } else {
777 Err(dyn_frame)
778 }
779 }
780}*/
781
782/*impl<M: AnyFrameMeta> From<UFrame> for Frame<M> {
783 fn from(frame: UFrame) -> Self {
784 // SAFETY: The metadata is coerceable and the struct is transmutable.
785 unsafe { core::mem::transmute(frame) }
786 }
787}*/
788
789/*impl TryFrom<Frame<FrameMeta>> for UFrame {
790 type Error = Frame<FrameMeta>;
791}*/
792
793#[verifier::external]
794impl<M: AnyUFrameMeta> From<Frame<M>> for UFrame {
795 fn from(frame: Frame<M>) -> Self {
796 // SAFETY: The metadata is coerceable and the struct is transmutable.
797 unsafe { core::mem::transmute(frame) }
798 }
799}
800
801/*
802impl From<UFrame> for Frame<dyn AnyFrameMeta> {
803 fn from(frame: UFrame) -> Self {
804 // SAFETY: The metadata is coerceable and the struct is transmutable.
805 unsafe { core::mem::transmute(frame) }
806 }
807}
808
809impl TryFrom<Frame<dyn AnyFrameMeta>> for UFrame {
810 type Error = Frame<dyn AnyFrameMeta>;
811
812 /// Tries converting a [`Frame<dyn AnyFrameMeta>`] into [`UFrame`].
813 ///
814 /// If the usage of the frame is not the same as the expected usage, it will
815 /// return the dynamic frame itself as is.
816 fn try_from(dyn_frame: Frame<dyn AnyFrameMeta>) -> Result<Self, Self::Error> {
817 if dyn_frame.dyn_meta().is_untyped() {
818 // SAFETY: The metadata is coerceable and the struct is transmutable.
819 Ok(unsafe { core::mem::transmute::<Frame<dyn AnyFrameMeta>, UFrame>(dyn_frame) })
820 } else {
821 Err(dyn_frame)
822 }
823 }
824}*/
825
826/// Increases the reference count of the frame by one.
827///
828/// # Safety
829///
830/// The caller should ensure the following conditions:
831/// 1. The physical address must represent a valid frame;
832/// 2. The caller must have already held a reference to the frame.
833///
834/// # Verified Properties
835/// ## Preconditions
836/// - **Safety Invariant**: Metaslot region invariants must hold.
837/// - **Safety**: The physical address must represent a valid frame.
838/// ## Postconditions
839/// - **Safety Invariant**: Metaslot region invariants hold after the call.
840/// - **Correctness**: The reference count of the frame is increased by one.
841/// - **Safety**: Frames other than this one are not affected by the call.
842#[verus_spec(permission =>
843 with
844 Tracked(regions): Tracked<&mut MetaRegionOwners>,
845 requires
846 old(regions).inv(),
847 old(regions).contains(frame_to_index(paddr)),
848 valid_frame_paddr(paddr),
849 old(regions).slot_owner(paddr).ref_count() > 0,
850 old(regions).slot_owner(paddr).ref_count()
851 != REF_COUNT_UNUSED,
852 old(regions).slot_owner(paddr).ref_count()
853 >= REF_COUNT_MAX ==> may_panic(),
854 ensures
855 final(regions).inv(),
856 permission@.frac() == 1,
857 permission@.id() == final(regions).slot_owner(paddr).metadata_perm.id(),
858 permission@.resource() == old(regions).slot_owner(paddr).metadata_perm@,
859 old(regions).inc_frame_reference_region_spec(paddr, *final(regions)),
860)]
861pub(in crate::mm) unsafe fn inc_frame_ref_count(paddr: Paddr) -> (permission: Tracked<
862 FracMetadataPerm,
863>) {
864 let tracked perm = regions.tracked_borrow_slot(paddr);
865 let tracked slot_own = regions.tracked_borrow_mut_slot_owner(paddr);
866
867 let vaddr: Vaddr = frame_to_meta(paddr);
868 // SAFETY: `vaddr` points to a valid `MetaSlot` that will never be mutably borrowed, so taking
869 // an immutable reference to it is always safe.
870 let slot = PPtr::<MetaSlot>(vaddr, PhantomData);
871
872 unsafe {
873 #[verus_spec(with Tracked(&mut slot_own.ref_count_perm))]
874 slot.borrow(Tracked(perm)).inc_ref_count()
875 };
876 let tracked frame_permission = slot_own.metadata_perm.split_one();
877
878 Tracked(frame_permission)
879}
880
881/// A dynamically-typed frame is represented by a frame of the underlying metadata type,
882/// which can be cast from any other type.
883pub type DynFrame = Frame<MetaSlotStorage>;
884
885impl<M: AnyFrameMeta + Repr<MetaSlotStorage> + 'static> Frame<M> {
886 /// Erases the static metadata type, yielding a `Frame<dyn AnyFrameMeta>`.
887 ///
888 /// Inherent method rather than `From`/`Into` to avoid trait-inference
889 /// ambiguity at call sites that previously relied on the blanket
890 /// `From<T> for T` (e.g. `frame.into()` for `Frame<UFrame>`).
891 ///
892 /// Axiomatized (`external_body`) because the body is `transmute`, which
893 /// Verus has no built-in spec for.
894 #[verifier::external_body]
895 pub fn into_dyn(self) -> Frame<dyn AnyFrameMeta> {
896 // SAFETY: `Frame<M>` is `#[repr(transparent)]` over `PPtr<MetaSlot>`
897 // plus a zero-size `PhantomData<M>`. `Frame<dyn AnyFrameMeta>` has
898 // the same runtime layout (thin pointer + ZST phantom).
899 unsafe { core::mem::transmute(self) }
900 }
901}
902
903} // verus!