Skip to main content

ostd/mm/frame/
segment.rs

1// SPDX-License-Identifier: MPL-2.0
2//! A contiguous range of frames.
3use vstd::prelude::*;
4use vstd::simple_pptr;
5use vstd_extra::assert;
6use vstd_extra::cast_ptr::*;
7use vstd_extra::drop_tracking::*;
8use vstd_extra::ownership::*;
9use vstd_extra::panic::may_panic;
10use vstd_extra::prelude::*;
11
12use crate::mm::page_table::RCClone;
13use crate::mm::{Paddr, PagingLevel, Vaddr, paddr_to_vaddr};
14use crate::specs::arch::kspace::FRAME_METADATA_RANGE;
15use crate::specs::arch::mm::{MAX_NR_PAGES, MAX_PADDR, PAGE_SIZE};
16use crate::specs::mm::frame::meta_owners::*;
17use crate::specs::mm::frame::meta_region_owners::MetaRegionOwners;
18use crate::specs::mm::frame::segment::*;
19use crate::specs::mm::virt_mem::MemView;
20
21use core::{fmt::Debug, /*mem::ManuallyDrop,*/ ops::Range};
22
23use super::meta::mapping::{frame_to_index, frame_to_meta, meta_addr};
24use super::{AnyFrameMeta, GetFrameError, MetaSlot};
25use crate::mm::frame::{Frame, has_safe_slot, untyped::AnyUFrameMeta};
26
27verus! {
28
29/// A contiguous range of homogeneous physical memory frames.
30///
31/// This is a handle to multiple contiguous frames. It will be more lightweight
32/// than owning an array of frame handles.
33///
34/// The ownership is achieved by the reference counting mechanism of frames.
35/// When constructing a [`Segment`], the frame handles are created then
36/// forgotten, leaving the reference count. When dropping a it, the frame
37/// handles are restored and dropped, decrementing the reference count.
38///
39/// All the metadata of the frames are homogeneous, i.e., they are of the same
40/// type.
41#[repr(transparent)]
42pub struct Segment<M: AnyFrameMeta + ?Sized> {
43    /// The physical address range of the segment.
44    pub range: Range<Paddr>,
45    /// Marker for the metadata type.
46    pub _marker: core::marker::PhantomData<M>,
47}
48
49/*
50impl<M: AnyFrameMeta + ?Sized> Debug for Segment<M> {
51    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
52        write!(f, "Segment({:#x}..{:#x})", self.range.start, self.range.end)
53    }
54}
55*/
56
57/*impl<M: AnyFrameMeta + ?Sized> Drop for Segment<M> {
58    fn drop(&mut self) {
59        for paddr in self.range.clone().step_by(PAGE_SIZE) {
60            // SAFETY: for each frame there would be a forgotten handle
61            // when creating the `Segment` object.
62            drop(unsafe { Frame::<M>::from_raw(paddr) });
63        }
64    }
65}*/
66
67/// A contiguous range of homogeneous untyped physical memory frames that have any metadata.
68///
69/// In other words, the metadata of the frames are of the same type, and they
70/// are untyped, but the type of metadata is not known at compile time. An
71/// [`USegment`] as a parameter accepts any untyped segments.
72///
73/// The usage of this frame will not be changed while this object is alive.
74pub type USegment = Segment<dyn AnyUFrameMeta>;
75
76/* impl<M: AnyFrameMeta + ?Sized> Clone for Segment<M> {
77    fn clone(&self) -> Self {
78        for paddr in self.range.clone().step_by(PAGE_SIZE) {
79            // SAFETY: for each frame there would be a forgotten handle
80            // when creating the `Segment` object, so we already have
81            // reference counts for the frames.
82            unsafe { inc_frame_ref_count(paddr) };
83        }
84        Self {
85            range: self.range.clone(),
86            _marker: core::marker::PhantomData,
87        }
88    }
89} */
90
91impl<M: AnyFrameMeta + Repr<MetaSlotStorage> + OwnerOf> RCClone for Segment<M> {
92    open spec fn clone_requires(self, perm: MetaRegionOwners) -> bool {
93        &&& self.inv()
94        &&& perm.inv()
95        &&& forall|pa: Paddr|
96            #![trigger frame_to_index(pa)]
97            (self.start_paddr() <= pa < self.end_paddr() && pa % PAGE_SIZE == 0) ==> {
98                let idx = frame_to_index(pa);
99                &&& perm.slots.contains_key(idx)
100                &&& has_safe_slot(pa)
101                &&& perm.slot_owners[idx].inner_perms.ref_count.value() > 0
102                &&& perm.slot_owners[idx].inner_perms.ref_count.value() + 1
103                    < super::meta::REF_COUNT_MAX
104                &&& !MetaSlot::inc_ref_count_panic_cond(perm.slot_owners[idx].inner_perms.ref_count)
105            }
106    }
107
108    open spec fn clone_ensures(
109        self,
110        old_perm: MetaRegionOwners,
111        new_perm: MetaRegionOwners,
112        res: Self,
113    ) -> bool {
114        &&& res.range() == self.range()
115        &&& res.inv()
116        &&& new_perm.inv()
117        // `Segment::clone` bumps each page's refcount via
118        // `inc_frame_ref_count` (which preserves the ledger), not through
119        // `Frame::clone`, so it is net-zero on `frame_obligations`. (The
120        // trait no longer hardcodes this; each impl states its own effect.)
121        &&& new_perm.frame_obligations =~= old_perm.frame_obligations
122    }
123
124    fn clone(&self, Tracked(perm): Tracked<&mut MetaRegionOwners>) -> (res: Self) {
125        let mut paddr = self.range.start;
126
127        let ghost old_perm = *perm;
128        loop
129            invariant
130                perm.inv(),
131                self.inv(),
132                perm.slots =~= old_perm.slots,
133                perm.slot_owners.dom() =~= old_perm.slot_owners.dom(),
134                // Linear-drop pilot: cloning a Segment doesn't mint or
135                // redeem its obligation — the per-frame ref-count bump is
136                // an Arc-style operation.
137                perm.frame_obligations =~= old_perm.frame_obligations,
138                self.range.start <= paddr <= self.range.end,
139                paddr % PAGE_SIZE == 0,
140                paddr <= MAX_PADDR,
141                forall|pa: Paddr|
142                    #![trigger frame_to_index(pa)]
143                    (paddr <= pa < self.range.end && pa % PAGE_SIZE == 0) ==> {
144                        let idx = frame_to_index(pa);
145                        &&& perm.slots.contains_key(idx)
146                        &&& has_safe_slot(pa)
147                        &&& perm.slot_owners[idx].inner_perms.ref_count.value() > 0
148                        &&& perm.slot_owners[idx].inner_perms.ref_count.value() + 1
149                            < super::meta::REF_COUNT_MAX
150                        &&& !MetaSlot::inc_ref_count_panic_cond(
151                            perm.slot_owners[idx].inner_perms.ref_count,
152                        )
153                    },
154            decreases self.range.end - paddr,
155        {
156            if paddr >= self.range.end {
157                break;
158            }
159            proof {
160                assert(paddr + PAGE_SIZE <= self.range.end);
161                assert(paddr + PAGE_SIZE <= MAX_PADDR);
162            }
163
164            unsafe {
165                #[verus_spec(with Tracked(perm))]
166                crate::mm::frame::inc_frame_ref_count(paddr)
167            };
168
169            paddr = paddr + PAGE_SIZE;
170        }
171
172        Self { range: self.range.start..self.range.end, _marker: core::marker::PhantomData }
173    }
174}
175
176#[verus_verify]
177impl<M: AnyFrameMeta + Repr<MetaSlotStorage> + OwnerOf> Segment<M> {
178    /// Creates a new [`Segment`] from unused frames.
179    ///
180    /// The caller must provide a closure to initialize metadata for all the frames.
181    /// The closure receives the physical address of the frame and returns the
182    /// metadata, which is similar to [`core::array::from_fn`].
183    ///
184    /// It returns an error if:
185    ///  - any of the frames cannot be created with a specific reason.
186    ///
187    /// # Verified Properties
188    /// ## Preconditions
189    /// - the metadata function must be well-formed and valid for all frames in the range;
190    /// - the metadata function must ensure that the frames can be created and owned by the segment;
191    /// - for any frame created via the closure `metadata_fn`, the corresponding slot in `regions`
192    ///   must be unused and not dropped in the owner ([`MetaRegionOwners`]).
193    ///
194    /// Range constraints (alignment, `range.end <= MAX_PADDR`, non-emptiness) are runtime-checked
195    /// in the body — see the postconditions below for the corresponding error variants.
196    /// ## Postconditions
197    /// - if the result is `Ok`, the returned segment satisfies its invariant with the owner,
198    ///   has the same physical address range as the input, and the owner is `Some`;
199    /// - if the input range is misaligned, the result is `Err(NotAligned)`;
200    /// - if the input range exceeds `MAX_PADDR`, the result is `Err(OutOfBound)`;
201    /// - if the input is aligned and within `MAX_PADDR` and the function terminated,
202    ///   then `range.start < range.end` (the runtime `assert!` would otherwise diverge).
203    #[verus_spec(r =>
204        with
205            Tracked(regions): Tracked<&mut MetaRegionOwners>,
206                -> owner: Tracked<Option<SegmentOwner<M>>>,
207        requires
208            old(regions).inv(),
209            forall|paddr_in: Paddr|
210                (range.start <= paddr_in < range.end && paddr_in % PAGE_SIZE == 0) ==> {
211                    &&& metadata_fn.requires((paddr_in,))
212                },
213            forall|paddr_in: Paddr, paddr_out: Paddr, m: M|
214                metadata_fn.ensures((paddr_in,), (paddr_out, m)) ==> paddr_in == paddr_out,
215            !(range.end <= MAX_PADDR ==> range.start < range.end) ==> may_panic(),
216        ensures
217            final(regions).inv(),
218            r is Err ==> final(regions).frame_obligations =~= old(regions).frame_obligations,
219            (range.start % PAGE_SIZE != 0 || range.end % PAGE_SIZE != 0)
220                ==> r == Err::<Self, _>(GetFrameError::NotAligned),
221            (range.start % PAGE_SIZE == 0 && range.end % PAGE_SIZE == 0 && range.end > MAX_PADDR)
222                ==> r == Err::<Self, _>(GetFrameError::OutOfBound),
223            r is Ok ==> range.end <= MAX_PADDR ==> range.start < range.end,
224            r matches Ok(seg) ==> {
225                &&& seg.start_paddr() == range.start
226                &&& seg.end_paddr() == range.end
227                &&& seg.start_paddr() < seg.end_paddr()
228                &&& crate::specs::mm::frame::segment::seg_obligations_minted(
229                    *old(regions),
230                    *final(regions),
231                    range.start,
232                    crate::specs::mm::frame::segment::seg_nframes(range),
233                )
234                &&& owner@ matches Some(owner) && {
235                    &&& seg.inv()
236                    &&& seg.wf(&owner)
237                }
238                &&& forall|paddr: Paddr|
239                    #![trigger frame_to_index(paddr)]
240                    (range.start <= paddr < range.end && paddr % PAGE_SIZE == 0)
241                        ==> final(regions).slots.contains_key(frame_to_index(paddr))
242            },
243    )]
244    pub fn from_unused(range: Range<Paddr>, metadata_fn: impl Fn(Paddr) -> (Paddr, M)) -> (res:
245        Result<Self, GetFrameError>) {
246        proof_decl! {
247            let tracked mut owner: Option<SegmentOwner<M>> = None;
248            let tracked mut addrs = Seq::<usize>::tracked_empty();
249        }
250
251        if range.start % PAGE_SIZE != 0 || range.end % PAGE_SIZE != 0 {
252            return {
253                proof_with!(|= Tracked(owner));
254                Err(GetFrameError::NotAligned)
255            };
256        }
257        if range.end > MAX_PADDR {
258            return {
259                proof_with!(|= Tracked(owner));
260                Err(GetFrameError::OutOfBound)
261            };
262        }
263        assert!(range.start < range.end);
264
265        let mut segment = Self {
266            range: range.start..range.start,
267            _marker: core::marker::PhantomData,
268        };
269
270        let mut i = 0;
271        let addr_len = (range.end - range.start) / PAGE_SIZE;
272
273        while i < addr_len
274            invariant
275                i <= addr_len,
276                i as int == addrs.len(),
277                range.start % PAGE_SIZE == 0,
278                range.end % PAGE_SIZE == 0,
279                range.end <= MAX_PADDR,
280                range.start <= range.start + i * PAGE_SIZE <= range.end,
281                range.end == range.start + addr_len * PAGE_SIZE,
282                addr_len == (range.end - range.start) / PAGE_SIZE as int,
283                i <= addr_len,
284                forall|paddr_in: Paddr|
285                    (range.start + i * PAGE_SIZE <= paddr_in < range.end && paddr_in % PAGE_SIZE
286                        == 0) ==> {
287                        &&& metadata_fn.requires((paddr_in,))
288                    },
289                forall|paddr_in: Paddr, paddr_out: Paddr, m: M|
290                    range.start + i * PAGE_SIZE <= paddr_in < range.end && paddr_in % PAGE_SIZE == 0
291                        && metadata_fn.ensures((paddr_in,), (paddr_out, m)) ==> paddr_in
292                        == paddr_out,
293                forall|j: int|
294                    #![trigger addrs[j]]
295                    0 <= j < addrs.len() as int ==> {
296                        let idx = frame_to_index(addrs[j]);
297                        &&& regions.slots.contains_key(idx)
298                        &&& regions.slot_owners.contains_key(idx)
299                        &&& regions.slot_owners[idx].self_addr == meta_addr(idx)
300                        &&& regions.slot_owners[idx].inner_perms.ref_count.value() > 0
301                        &&& regions.slot_owners[idx].inner_perms.ref_count.value()
302                            <= crate::mm::frame::meta::REF_COUNT_MAX
303                        &&& regions.slot_owners[idx].paths_in_pt.is_empty()
304                        &&& addrs[j] % PAGE_SIZE == 0
305                        &&& addrs[j] < MAX_PADDR
306                        &&& addrs[j] == range.start + (j as u64) * PAGE_SIZE
307                    },
308                regions.inv(),
309                regions.frame_obligations =~= old(regions).frame_obligations,
310                regions.slot_owners.dom() =~= old(regions).slot_owners.dom(),
311                segment.range.start == range.start,
312                segment.range.end == range.start + i * PAGE_SIZE,
313            ensures
314                i == addr_len,
315            decreases addr_len - i,
316        {
317            let paddr_in = range.start + i * PAGE_SIZE;
318            let (paddr, meta) = metadata_fn(paddr_in);
319
320            let ghost regions_pre = *regions;
321            let res = #[verus_spec(with Tracked(regions))]
322            Frame::<M>::from_unused(paddr, meta);
323            let frame = match res {
324                Ok(f) => f,
325                Err(e) => {
326                    let mut p = range.start;
327                    let ghost mut k: int = 0;
328                    while p < segment.range.end
329                        invariant
330                            regions.inv(),
331                            regions.frame_obligations =~= old(regions).frame_obligations,
332                            regions.slot_owners.dom() =~= old(regions).slot_owners.dom(),
333                            range.start % PAGE_SIZE == 0,
334                            i as int == addrs.len(),
335                            segment.range.end == range.start + i * PAGE_SIZE,
336                            segment.range.end <= MAX_PADDR,
337                            range.start <= p <= segment.range.end,
338                            p == range.start + k * PAGE_SIZE,
339                            p % PAGE_SIZE == 0,
340                            0 <= k <= i,
341                            forall|j: int|
342                                #![trigger addrs[j]]
343                                k <= j < addrs.len() as int ==> {
344                                    let idx = frame_to_index(addrs[j]);
345                                    &&& regions.slots.contains_key(idx)
346                                    &&& regions.slot_owners.contains_key(idx)
347                                    &&& regions.slot_owners[idx].self_addr == meta_addr(idx)
348                                    &&& regions.slot_owners[idx].inner_perms.ref_count.value() > 0
349                                    &&& regions.slot_owners[idx].inner_perms.ref_count.value()
350                                        <= crate::mm::frame::meta::REF_COUNT_MAX
351                                    &&& regions.slot_owners[idx].paths_in_pt.is_empty()
352                                    &&& addrs[j] % PAGE_SIZE == 0
353                                    &&& addrs[j] < MAX_PADDR
354                                    &&& addrs[j] == range.start + (j as u64) * PAGE_SIZE
355                                },
356                        decreases segment.range.end - p,
357                    {
358                        let ghost reclaim_pre = *regions;
359                        let ghost idx_k = frame_to_index(p);
360                        proof {
361                            broadcast use crate::mm::frame::meta::mapping::group_page_meta;
362
363                            assert(addrs[k] == p);
364                            assert(meta_addr(idx_k) == frame_to_meta(p));
365                            assert(regions.slots.contains_key(idx_k));
366                        }
367                        proof_decl! {
368                            let tracked from_raw_obl: vstd_extra::drop_tracking::DropObligation<
369                                usize,
370                            >;
371                        }
372                        let frame = unsafe {
373                            #[verus_spec(with Tracked(regions) => Tracked(from_raw_obl))]
374                            Frame::<M>::from_raw(p)
375                        };
376                        proof {
377                            broadcast use crate::mm::frame::meta::mapping::group_page_meta;
378
379                            assert(regions.slots[idx_k].pptr() == frame.ptr);
380                        }
381                        frame.drop(Tracked(regions), Tracked(from_raw_obl));
382                        proof {
383                            assert forall|j: int|
384                                #![trigger addrs[j]]
385                                (k + 1) <= j < addrs.len() as int implies ({
386                                let idx = frame_to_index(addrs[j]);
387                                &&& regions.slots.contains_key(idx)
388                                &&& regions.slot_owners.contains_key(idx)
389                                &&& regions.slot_owners[idx] == reclaim_pre.slot_owners[idx]
390                            }) by {
391                                assert(addrs[j] != p);
392                                crate::mm::frame::meta::mapping::lemma_frame_to_index_injective(
393                                    addrs[j],
394                                    p,
395                                );
396                            };
397                        }
398                        p = p + PAGE_SIZE;
399                        proof {
400                            k = k + 1;
401                        }
402                    }
403                    return {
404                        proof_with!(|= Tracked(owner));
405                        Err(e)
406                    };
407                },
408            };
409
410            let _ = ManuallyDrop::new(frame, Tracked(regions));
411            segment.range.end = paddr + PAGE_SIZE;
412            proof {
413                broadcast use crate::mm::frame::meta::mapping::group_page_meta;
414
415                regions.inv_implies_correct_addr(paddr);
416                let idx = frame_to_index(paddr);
417                axiom_mmio_usage_iff_mmio_paddr(regions.slot_owners[idx]);
418                axiom_mmio_usage_iff_mmio_paddr(regions_pre.slot_owners[idx]);
419                assert(regions_pre.slot_owners[idx].paths_in_pt.is_empty());
420                assert(regions.slot_owners[idx].paths_in_pt
421                    =~= regions_pre.slot_owners[idx].paths_in_pt);
422                assert(regions.frame_obligations =~= regions_pre.frame_obligations);
423                addrs.tracked_push(paddr);
424            }
425
426            i += 1;
427        }
428
429        proof {
430            // Per-frame migration: record one forgotten reference per frame in
431            // `frame_obligations`. The construction loop above is net-zero on
432            // the ledger (each `Frame::from_unused` mint is cancelled by its
433            // `ManuallyDrop`); this post-loop pass records the segment's
434            // retained per-frame references, replacing the old single
435            // range-keyed `obligations` entry.
436            // The construction loop preserved `frame_obligations`, so the mint
437            // helper's exact delta (stated against the pre-mint state) telescopes
438            // to the function's entry state for the postcondition.
439            assert(regions.frame_obligations =~= old(regions).frame_obligations);
440            crate::specs::mm::frame::segment::tracked_mint_seg_obligations(
441                regions,
442                range.start,
443                addr_len as int,
444            );
445            assert(addr_len as int == crate::specs::mm::frame::segment::seg_nframes(range));
446            owner = Some(SegmentOwner { range, _marker: core::marker::PhantomData });
447
448            assert forall|addr: usize|
449                #![trigger frame_to_index(addr)]
450                range.start <= addr < range.end && addr % PAGE_SIZE == 0 implies {
451                regions.slots.contains_key(frame_to_index(addr))
452            } by {
453                let j = (addr - range.start) / PAGE_SIZE as int;
454                assert(0 <= j < addrs.len() as int);
455                assert(addrs[j as int] == addr);
456            }
457        }
458
459        proof_with!(|= Tracked(owner));
460        Ok(segment)
461    }
462
463    /// Restores the [`Segment`] from the raw physical address range.
464    ///
465    /// # Verified Properties
466    /// ## Preconditions
467    /// - the meta region must satisfy its invariant;
468    /// - the segment-to-be (with the supplied `range`) must satisfy its invariant
469    ///   ([`Self::inv`]) and the well-formedness relation with `owner` ([`Self::wf`]);
470    /// - `owner` must relate correctly to `regions`.
471    ///
472    /// Following the [`UniqueFrame::from_raw`] pattern, the contract here ties
473    /// `range` and `owner` together up front so that the postcondition can
474    /// directly advertise `r.inv()` and `r.wf(&owner)` for the caller.
475    ///
476    /// ## Postconditions
477    /// - the returned segment satisfies its invariant and is well-formed with `owner`;
478    /// - the returned segment has the same physical address range as the input;
479    /// - the meta region is unchanged (preserving the relation with `owner`).
480    ///
481    /// # Safety
482    ///
483    /// The range must be a forgotten [`Segment`] that matches the type `M`.
484    /// The caller must provide the permissions that were returned by [`Self::into_raw`].
485    #[verus_spec(r =>
486        with
487            Tracked(regions): Tracked<&mut MetaRegionOwners>,
488            Tracked(owner): Tracked<SegmentOwner<M>>,
489        requires
490            old(regions).inv(),
491            owner.inv(),
492            range == owner.range,
493            owner.relate_regions(*old(regions)),
494        ensures
495            r.range() == range,
496            r.inv(),
497            r.wf(&owner),
498            final(regions).inv(),
499            *final(regions) =~= *old(regions),
500    )]
501    pub(crate) unsafe fn from_raw(range: Range<Paddr>) -> Self {
502        Self { range, _marker: core::marker::PhantomData }
503    }
504}
505
506#[verus_verify]
507impl<M: AnyFrameMeta + ?Sized> Segment<M> {
508    /// Gets the start physical address of the contiguous frames.
509    #[verus_verify(dual_spec)]
510    #[verus_spec(
511        returns
512            self.start_paddr(),
513    )]
514    pub fn start_paddr(&self) -> Paddr {
515        self.range.start
516    }
517
518    /// Gets the end physical address of the contiguous frames.
519    #[verus_verify(dual_spec)]
520    #[verus_spec(
521        returns
522            self.end_paddr(),
523    )]
524    pub fn end_paddr(&self) -> Paddr {
525        self.range.end
526    }
527
528    /// Gets the length in bytes of the contiguous frames.
529    #[verus_verify(dual_spec)]
530    #[verus_spec(r =>
531        requires
532            self.inv(),
533        ensures
534            r == self.end_paddr() - self.start_paddr(),
535        returns
536            self.size()
537    )]
538    pub fn size(&self) -> usize {
539        self.range.end - self.range.start
540    }
541
542    pub open spec fn range(&self) -> Range<Paddr> {
543        self.start_paddr()..self.end_paddr()
544    }
545}
546
547#[verus_verify]
548impl<M: AnyFrameMeta + Repr<MetaSlotStorage> + OwnerOf> Segment<M> {
549    /// Splits the frames into two at the given byte offset from the start.
550    ///
551    /// The resulting frames cannot be empty. So the offset cannot be neither
552    /// zero nor the length of the frames.
553    ///
554    /// # Verified Properties
555    /// ## Preconditions
556    /// - the segment must satisfy its invariant ([`Self::inv`]) and the well-formedness
557    ///   relation with the owner ([`Self::wf`]);
558    /// - the offset must be aligned and within bounds;
559    /// - the meta region must be valid and the owner must relate to it.
560    ///
561    /// ## Postconditions
562    /// - the resulting segments satisfy their invariants with the corresponding owners;
563    /// - they match [`Self::split_spec`];
564    /// - the original owner's permissions are split between the two new owners;
565    /// - both halves continue to relate correctly to `regions` (which is unchanged).
566    #[verus_spec(r =>
567        with
568            Tracked(owner): Tracked<SegmentOwner<M>>,
569            Tracked(regions): Tracked<&mut MetaRegionOwners>,
570                -> result_owners: (Tracked<SegmentOwner<M>>, Tracked<SegmentOwner<M>>),
571        requires
572            self.invariants(&owner, *old(regions)),
573            offset % PAGE_SIZE != 0 ==> may_panic(),
574            !(0 < offset && offset < self.size()) ==> may_panic(),
575        ensures
576            final(regions).slots =~= old(regions).slots,
577            final(regions).slot_owners =~= old(regions).slot_owners,
578            final(regions).frame_obligations =~= old(regions).frame_obligations,
579            (r.0, r.1) == self.split_spec(offset),
580            r.0.invariants(&result_owners.0@, *final(regions)),
581            r.1.invariants(&result_owners.1@, *final(regions)),
582    )]
583    pub fn split(self, offset: usize) -> (Self, Self) {
584        assert!(offset % PAGE_SIZE == 0);
585        assert!(0 < offset && offset < self.size());
586
587        let ghost old_regions = *regions;
588
589        let old = ManuallyDrop::new(self, Tracked(regions));
590        let at = old.range.start + offset;
591
592        let ghost old_start = old@.start_paddr();
593        let ghost old_end = old@.end_paddr();
594
595        let tracked frame_own1 = SegmentOwner {
596            range: old_start..at,
597            _marker: core::marker::PhantomData,
598        };
599        let tracked frame_own2 = SegmentOwner {
600            range: at..old_end,
601            _marker: core::marker::PhantomData,
602        };
603        proof {
604            assert(*regions =~= old_regions);
605            assert(regions.slot_owners =~= old_regions.slot_owners);
606            assert(regions.frame_obligations =~= old_regions.frame_obligations);
607            assert forall|i: int|
608                #![trigger frame_to_index((frame_own1.range.start + i * PAGE_SIZE) as usize)]
609                0 <= i < crate::specs::mm::frame::segment::seg_nframes(frame_own1.range) implies {
610                let idx = frame_to_index((frame_own1.range.start + i * PAGE_SIZE) as usize);
611                &&& regions.frame_obligations.count(idx) >= 1
612                &&& regions.slot_owners.contains_key(idx)
613                &&& regions.slots.contains_key(idx)
614                &&& regions.slot_owners[idx].self_addr == meta_addr(idx)
615                &&& regions.slot_owners[idx].inner_perms.ref_count.value() > 0
616                &&& regions.slot_owners[idx].inner_perms.ref_count.value()
617                    <= crate::mm::frame::meta::REF_COUNT_MAX
618                &&& regions.slot_owners[idx].paths_in_pt.is_empty()
619                &&& regions.slot_owners[idx].usage
620                    == crate::specs::mm::frame::meta_owners::PageUsage::Frame
621            } by {
622                owner.relate_regions_at(old_regions, i);
623            }
624            assert forall|i: int|
625                #![trigger frame_to_index((frame_own2.range.start + i * PAGE_SIZE) as usize)]
626                0 <= i < crate::specs::mm::frame::segment::seg_nframes(frame_own2.range) implies {
627                let idx = frame_to_index((frame_own2.range.start + i * PAGE_SIZE) as usize);
628                &&& regions.frame_obligations.count(idx) >= 1
629                &&& regions.slot_owners.contains_key(idx)
630                &&& regions.slots.contains_key(idx)
631                &&& regions.slot_owners[idx].self_addr == meta_addr(idx)
632                &&& regions.slot_owners[idx].inner_perms.ref_count.value() > 0
633                &&& regions.slot_owners[idx].inner_perms.ref_count.value()
634                    <= crate::mm::frame::meta::REF_COUNT_MAX
635                &&& regions.slot_owners[idx].paths_in_pt.is_empty()
636                &&& regions.slot_owners[idx].usage
637                    == crate::specs::mm::frame::meta_owners::PageUsage::Frame
638            } by {
639                owner.relate_regions_at(old_regions, i + (offset / PAGE_SIZE) as int);
640            }
641
642            assert forall|i: int, j: int|
643                #![trigger frame_to_index((frame_own1.range.start + i * PAGE_SIZE) as usize),
644                    frame_to_index((frame_own1.range.start + j * PAGE_SIZE) as usize)]
645                0 <= i < j < crate::specs::mm::frame::segment::seg_nframes(
646                    frame_own1.range,
647                ) implies frame_to_index((frame_own1.range.start + i * PAGE_SIZE) as usize)
648                != frame_to_index((frame_own1.range.start + j * PAGE_SIZE) as usize) by {
649                owner.relate_regions_distinct(old_regions, i, j);
650            }
651            assert forall|i: int, j: int|
652                #![trigger frame_to_index((frame_own2.range.start + i * PAGE_SIZE) as usize),
653                    frame_to_index((frame_own2.range.start + j * PAGE_SIZE) as usize)]
654                0 <= i < j < crate::specs::mm::frame::segment::seg_nframes(
655                    frame_own2.range,
656                ) implies frame_to_index((frame_own2.range.start + i * PAGE_SIZE) as usize)
657                != frame_to_index((frame_own2.range.start + j * PAGE_SIZE) as usize) by {
658                owner.relate_regions_distinct(
659                    old_regions,
660                    i + (offset / PAGE_SIZE) as int,
661                    j + (offset / PAGE_SIZE) as int,
662                );
663            }
664        }
665        proof_with!(|= (Tracked(frame_own1), Tracked(frame_own2)));
666        (
667            Self { range: old.range.start..at, _marker: core::marker::PhantomData },
668            Self { range: at..old.range.end, _marker: core::marker::PhantomData },
669        )
670    }
671
672    /// Precise panic condition for [`Self::slice`]. `slice` diverges iff:
673    ///  - the slice range is misaligned, reversed, or out of the segment's
674    ///    bounds (the diverging `assert!`s at the top of `slice`), or
675    ///  - **the specific per-frame slot that `slice` bumps** is already
676    ///    saturated (`inc_ref_count` would overflow). Unlike `query` which
677    ///    clones one item, `slice` bumps one refcount per page in the
678    ///    slice range, so the saturation disjunct is an *exists* over those
679    ///    specific paddrs `self.range.start + j * PAGE_SIZE` for
680    ///    `j ∈ [range.start/PAGE_SIZE, range.end/PAGE_SIZE)`.
681    pub open spec fn page_in_range_saturated(
682        self,
683        range: &Range<usize>,
684        regions: MetaRegionOwners,
685    ) -> bool {
686        exists|j: int|
687            #![trigger frame_to_index((self.range.start + j * PAGE_SIZE) as usize)]
688            (range.start as int) / (PAGE_SIZE as int) <= j < (range.end as int) / (PAGE_SIZE as int)
689                && regions.slot_owners[frame_to_index(
690                (self.start_paddr() + j * PAGE_SIZE) as usize,
691            )].inner_perms.ref_count.value() >= REF_COUNT_MAX
692    }
693
694    /// Gets an extra handle to the frames in the byte offset range.
695    ///
696    /// The sliced byte offset range in indexed by the offset from the start of
697    /// the contiguous frames. The resulting frames holds extra reference counts.
698    ///
699    /// # Verified Properties
700    /// ## Postconditions
701    /// - the resulting slice's range matches the slicing range and is in-bounds
702    ///   (the in-bounds check follows from the diverging `assert!` in the body);
703    /// - `regions` preserves invariants and key domains. Per-frame state — e.g.
704    ///   that a hypothetical `sub_owner.relate_regions(regions)` would hold —
705    ///   is *not* exposed; threading that through the per-frame ref-count bump
706    ///   loop would require a much heavier proof. Mirrors [`Segment::clone`].
707    ///
708    /// See also [`vstd::seq::Seq::subrange`].
709    #[verus_spec(r =>
710        with
711            Tracked(owner): Tracked<&SegmentOwner<M>>,
712            Tracked(regions): Tracked<&mut MetaRegionOwners>,
713        requires
714            self.invariants(owner, *old(regions)),
715            range.start % PAGE_SIZE != 0 ==> may_panic(),
716            range.end % PAGE_SIZE != 0 ==> may_panic(),
717            range.start > range.end ==> may_panic(),
718            self.range.start as int + range.end as int > self.range.end as int ==> may_panic(),
719            self.page_in_range_saturated(range, *old(regions)) ==> may_panic(),
720        ensures
721            range.start % PAGE_SIZE == 0,
722            range.end % PAGE_SIZE == 0,
723            range.start <= range.end,
724            self.range.start as int + range.end as int <= self.range.end as int,
725            !self.page_in_range_saturated(range, *old(regions)),
726            r.inv(),
727            r.start_paddr() == self.start_paddr() + range.start,
728            r.end_paddr() == self.start_paddr() + range.end,
729            r.end_paddr() <= self.end_paddr(),
730            final(regions).inv(),
731            final(regions).slots =~= old(regions).slots,
732            final(regions).slot_owners.dom() =~= old(regions).slot_owners.dom(),
733            final(regions).frame_obligations =~= old(regions).frame_obligations,
734    )]
735    #[verifier::rlimit(8000)]
736    pub fn slice(&self, range: &Range<usize>) -> Self {
737        // KNOWN BUG: potential overflows https://github.com/asterinas/asterinas/issues/3165
738        assume(self.range.start + range.start <= usize::MAX);
739        assume(self.range.start + range.end <= usize::MAX);
740
741        assert!(range.start % PAGE_SIZE == 0 && range.end % PAGE_SIZE == 0);
742        let start = self.range.start + range.start;
743        let end = self.range.start + range.end;
744        assert!(start <= end && end <= self.range.end);
745
746        let mut paddr = start;
747        let ghost addr_len = (end - start) / PAGE_SIZE as int;
748        let ghost first_perm_idx: int = (range.start / PAGE_SIZE) as int;
749        let ghost last_perm_idx: int = (range.end / PAGE_SIZE) as int;
750        let ghost old_regions = *regions;
751        let ghost mut i: int = 0;
752        loop
753            invariant
754                self.page_in_range_saturated(range, *old(regions)) ==> may_panic(),
755                old_regions == *old(regions),
756                regions.inv(),
757                regions.slots =~= old_regions.slots,
758                regions.slot_owners.dom() =~= old_regions.slot_owners.dom(),
759                regions.frame_obligations =~= old_regions.frame_obligations,
760                start == self.range.start + range.start,
761                end == self.range.start + range.end,
762                start <= end <= self.range.end,
763                start % PAGE_SIZE == 0,
764                end % PAGE_SIZE == 0,
765                paddr == (start + i * PAGE_SIZE) as usize,
766                paddr <= end,
767                0 <= i <= addr_len,
768                addr_len == (end - start) / PAGE_SIZE as int,
769                paddr < end <==> i < addr_len,
770                self.inv(),
771                self.wf(&owner),
772                owner.inv(),
773                owner.relate_regions(old_regions),
774                first_perm_idx == range.start / PAGE_SIZE,
775                last_perm_idx == range.end / PAGE_SIZE,
776                first_perm_idx + addr_len as int == last_perm_idx,
777                first_perm_idx + i <= last_perm_idx,
778                forall|j: int|
779                    #![trigger frame_to_index((self.range.start + j * PAGE_SIZE) as usize)]
780                    first_perm_idx + i <= j < last_perm_idx ==> (
781                    *regions).slot_owners[frame_to_index(
782                        (self.range.start + j * PAGE_SIZE) as usize,
783                    )] == old_regions.slot_owners[frame_to_index(
784                        (self.range.start + j * PAGE_SIZE) as usize,
785                    )],
786                forall|j: int|
787                    #![trigger frame_to_index((self.range.start + j * PAGE_SIZE) as usize)]
788                    first_perm_idx <= j < first_perm_idx + i
789                        ==> old_regions.slot_owners[frame_to_index(
790                        (self.range.start + j * PAGE_SIZE) as usize,
791                    )].inner_perms.ref_count.value() < REF_COUNT_MAX,
792            ensures
793                i == addr_len,
794            decreases addr_len - i,
795        {
796            if paddr >= end {
797                break;
798            }
799            let ghost slot_idx: usize = frame_to_index(paddr);
800            let ghost perm_idx: int = first_perm_idx + i;
801
802            proof {
803                owner.relate_regions_at(old_regions, perm_idx);
804                assert(regions.slot_owners[slot_idx] == old_regions.slot_owners[slot_idx]);
805            }
806
807            unsafe {
808                #[verus_spec(with Tracked(regions))]
809                crate::mm::frame::inc_frame_ref_count(paddr)
810            };
811
812            paddr = paddr + PAGE_SIZE;
813
814            proof {
815                i = i + 1;
816                assert forall|j: int|
817                    #![trigger frame_to_index((self.range.start + j * PAGE_SIZE) as usize)]
818                    first_perm_idx + i <= j < last_perm_idx implies (
819                *regions).slot_owners[frame_to_index((self.range.start + j * PAGE_SIZE) as usize)]
820                    == old_regions.slot_owners[frame_to_index(
821                    (self.range.start + j * PAGE_SIZE) as usize,
822                )] by {
823                    let _ = frame_to_index((self.range.start + perm_idx * PAGE_SIZE) as usize);
824                    let _ = frame_to_index((self.range.start + j * PAGE_SIZE) as usize);
825                };
826            }
827        }
828
829        Self { range: start..end, _marker: core::marker::PhantomData }
830    }
831
832    /// Forgets the [`Segment`] and gets a raw range of physical addresses.
833    ///
834    /// The segment's permissions are returned to the caller via `frame_perms`.
835    /// The caller is responsible for holding onto the permissions and providing
836    /// them back when restoring the segment with [`Self::from_raw`].
837    ///
838    /// # Verified Properties
839    /// ## Preconditions
840    /// - the segment must satisfy the invariant with the owner;
841    /// - the meta region in `regions` must satisfy the invariant;
842    /// - the owner must relate correctly to `regions`.
843    ///
844    /// ## Postconditions
845    /// - the returned physical address range matches the segment's range;
846    /// - the meta region is unchanged (preserving the relation with the returned owner).
847    #[verus_spec(r =>
848        with
849            Tracked(regions): Tracked<&mut MetaRegionOwners>,
850            Tracked(owner): Tracked<SegmentOwner<M>>,
851                -> frame_perms: Tracked<SegmentOwner<M>>,
852        requires
853            self.invariants(&owner, *old(regions)),
854        ensures
855            r == self.range(),
856            final(regions).inv(),
857            *final(regions) =~= *old(regions),
858            frame_perms@ == owner,
859    )]
860    pub(crate) fn into_raw(self) -> Range<Paddr> {
861        let range = self.range.clone();
862        let _ = ManuallyDrop::new(self, Tracked(regions));
863
864        proof_with!(|= Tracked(owner));
865        range
866    }
867
868    /// Gets the next frame in the segment.
869    ///
870    /// This is the verified counterpart of [`Iterator::next`] for `Segment<M>`.
871    /// The trait method is `external_body` because Verus's `core::iter::Iterator`
872    /// support can't thread `Tracked` metadata through the trait method's
873    /// fixed signature.
874    ///
875    /// # Verified Properties
876    /// ## Preconditions
877    /// - segment, owner, and meta regions must satisfy their respective invariants;
878    /// - the segment is well-formed with the owner;
879    /// - the owner relates correctly to `regions` (forgotten reference, slot
880    ///   present and consistent, etc. — see [`SegmentOwner::relate_regions`]).
881    ///
882    /// All the per-frame coherence conditions previously listed inline are now
883    /// consequences of `owner.relate_regions(regions)` instantiated at `i = 0`.
884    ///
885    /// ## Postconditions
886    /// - if the result is [`None`], then the segment has been exhausted;
887    /// - if the result is [`Some`], then the segment is advanced by one frame and
888    ///   the returned frame is the next frame, with its slot restored to `regions`;
889    /// - the new owner continues to relate correctly to the new regions.
890    #[verus_spec(res =>
891        with
892            Tracked(regions): Tracked<&mut MetaRegionOwners>,
893            Tracked(owner): Tracked<&mut SegmentOwner<M>>
894        requires
895            old(self).invariants(old(owner), *old(regions)),
896        ensures
897            final(regions).inv(),
898            final(self).inv(),
899            final(owner).relate_regions(*final(regions)),
900            match res {
901                None => final(self).start_paddr() == old(self).end_paddr(),
902                Some(pair) => {
903                    &&& final(self).start_paddr() == old(self).start_paddr() + PAGE_SIZE
904                    &&& pair.0.paddr() == old(self).start_paddr()
905                    &&& final(regions).slots.contains_key(frame_to_index(pair.0.paddr()))
906                    &&& pair.1@.value() == frame_to_index(pair.0.paddr())
907                    &&& pair.0.inv_with_regions(*final(regions))
908                    &&& final(regions).frame_obligations.count(frame_to_index(pair.0.paddr())) >= 1
909                },
910            },
911    )]
912    pub fn next(&mut self) -> Option<
913        (Frame<M>, Tracked<vstd_extra::drop_tracking::DropObligation<usize>>),
914    > {
915        if self.range.start < self.range.end {
916            proof {
917                owner.relate_regions_at(*old(regions), 0);
918            }
919            proof_decl! {
920                let tracked from_raw_obl: vstd_extra::drop_tracking::DropObligation<usize>;
921            }
922
923            let frame = unsafe {
924                #[verus_spec(with Tracked(regions) => Tracked(from_raw_obl))]
925                Frame::<M>::from_raw(self.range.start)
926            };
927
928            proof {
929                let new_range = ((self.range.start + PAGE_SIZE) as usize)..self.range.end;
930                let tracked redeem_tok = vstd_extra::drop_tracking::DropObligation::tracked_mint(
931                    frame.index(),
932                );
933                regions.tracked_redeem_frame_obligation(redeem_tok);
934                assert(regions.frame_obligations =~= old(regions).frame_obligations);
935                owner.range = new_range;
936
937                assert forall|i: int|
938                    #![trigger frame_to_index((owner.range.start + i * PAGE_SIZE) as usize)]
939                    0 <= i < crate::specs::mm::frame::segment::seg_nframes(owner.range) implies {
940                    let idx = frame_to_index((owner.range.start + i * PAGE_SIZE) as usize);
941                    &&& regions.frame_obligations.count(idx) >= 1
942                    &&& regions.slot_owners.contains_key(idx)
943                    &&& regions.slots.contains_key(idx)
944                    &&& regions.slot_owners[idx].self_addr == meta_addr(idx)
945                    &&& regions.slot_owners[idx].inner_perms.ref_count.value() > 0
946                    &&& regions.slot_owners[idx].inner_perms.ref_count.value()
947                        <= crate::mm::frame::meta::REF_COUNT_MAX
948                    &&& regions.slot_owners[idx].paths_in_pt.is_empty()
949                    &&& regions.slot_owners[idx].usage
950                        == crate::specs::mm::frame::meta_owners::PageUsage::Frame
951                } by {
952                    old(owner).relate_regions_at(*old(regions), i + 1);
953                    old(owner).relate_regions_distinct(*old(regions), 0, i + 1);
954                }
955                assert forall|i: int, j: int|
956                    #![trigger frame_to_index((owner.range.start + i * PAGE_SIZE) as usize),
957                        frame_to_index((owner.range.start + j * PAGE_SIZE) as usize)]
958                    0 <= i < j < crate::specs::mm::frame::segment::seg_nframes(
959                        owner.range,
960                    ) implies frame_to_index((owner.range.start + i * PAGE_SIZE) as usize)
961                    != frame_to_index((owner.range.start + j * PAGE_SIZE) as usize) by {
962                    old(owner).relate_regions_distinct(*old(regions), i + 1, j + 1);
963                }
964                // The yielded frame is a valid live handle against the new
965                // regions, so the caller can use or drop it.
966                broadcast use crate::mm::frame::meta::mapping::group_page_meta;
967
968                assert(regions.slots[frame.index()].pptr() == frame.ptr);
969                assert(frame.inv_with_regions(*regions));
970            }
971
972            self.range.start = self.range.start + PAGE_SIZE;
973            Some((frame, Tracked(from_raw_obl)))
974        } else {
975            None
976        }
977    }
978
979    /// Returns the number of pages of the contiguous frames.
980    #[verifier::inline]
981    pub open spec fn nrpage_spec(&self) -> usize
982        recommends
983            self.inv(),
984    {
985        self.size() / PAGE_SIZE
986    }
987
988    /// Splits the contiguous frames into two at the given byte offset from the start in spec mode.
989    pub closed spec fn split_spec(self, offset: usize) -> (Self, Self)
990        recommends
991            self.inv(),
992            offset % PAGE_SIZE == 0,
993            0 < offset < self.size(),
994    {
995        let at = (self.start_paddr() + offset) as usize;
996        let idx = at / PAGE_SIZE;
997        (
998            Self { range: self.start_paddr()..at, _marker: core::marker::PhantomData },
999            Self { range: at..self.end_paddr(), _marker: core::marker::PhantomData },
1000        )
1001    }
1002}
1003
1004#[verus_verify]
1005impl<M: AnyFrameMeta + Repr<MetaSlotStorage>> From<Frame<M>> for Segment<M> {
1006    /// Converts a single [`Frame`] into a one-page [`Segment`] by forgetting
1007    /// the frame and recording its paddr range. Symmetric to vostd's
1008    /// `From<Frame<M>> for Segment<M>`.
1009    //
1010    // Trusted at the trait boundary: the `From::from` signature can't thread
1011    // `Tracked` metadata to bump the frame's `raw_count` via the verified
1012    // `vstd_extra::drop_tracking::ManuallyDrop`, so we use `core::mem`'s
1013    // version. Same trust pattern as the `Iterator` impl.
1014    #[verifier::external_body]
1015    fn from(frame: Frame<M>) -> Self {
1016        let pa = frame.start_paddr();
1017        let _ = core::mem::ManuallyDrop::new(frame);
1018        Self { range: pa..(pa + PAGE_SIZE), _marker: core::marker::PhantomData }
1019    }
1020}
1021
1022impl<M: AnyFrameMeta + Repr<MetaSlotStorage> + OwnerOf> Iterator for Segment<M> {
1023    type Item = Frame<M>;
1024
1025    /// Gets the next frame in the segment.
1026    //
1027    // Verus's `core::iter::Iterator` support doesn't allow threading `Tracked`
1028    // metadata through the trait method's fixed signature, so the verified
1029    // `next` lives as an inherent method on `Segment<M>` and the trait body
1030    // is trusted at the trait boundary.
1031    #[verifier::external_body]
1032    fn next(&mut self) -> Option<Self::Item> {
1033        if self.range.start < self.range.end {
1034            // SAFETY: each frame in the range was a forgotten handle when
1035            // creating the `Segment` object.
1036            let frame = unsafe { Frame::<M>::from_raw(self.range.start) };
1037            self.range.start = self.range.start + PAGE_SIZE;
1038            Some(frame)
1039        } else {
1040            None
1041        }
1042    }
1043}
1044
1045impl<M: AnyFrameMeta + Repr<MetaSlotStorage>> Segment<M> {
1046    /// Verified drop: iterates over each frame in the segment, decrements its
1047    /// reference count, and (when last ref) tears down the metadata.
1048    ///
1049    /// Per-frame linear-drop: before the teardown loop, this redeems the one
1050    /// `frame_obligations` entry the segment retained per frame (minted by
1051    /// `Segment::from_unused`). Failing to drop the segment leaves those
1052    /// per-frame counts outstanding and breaks
1053    /// [`MetaRegionOwners::clean_inv`] at the enclosing function's exit.
1054    #[verus_spec(
1055        with Tracked(owner): Tracked<SegmentOwner<M>>,
1056            Tracked(regions): Tracked<&mut MetaRegionOwners>
1057        requires
1058            self.invariants(&owner, *old(regions)),
1059            forall|i: int|
1060                #![trigger frame_to_index((self.range.start + i * PAGE_SIZE) as usize)]
1061                0 <= i < crate::specs::mm::frame::segment::seg_nframes(self.range) ==> {
1062                    let idx = frame_to_index((self.range.start + i * PAGE_SIZE) as usize);
1063                    old(regions).slot_owners[idx].inner_perms.ref_count.value() == 1 ==> {
1064                        &&& old(regions).slot_owners[idx].inner_perms.storage.is_init()
1065                        &&& old(regions).slot_owners[idx].inner_perms.in_list.value() == 0
1066                    }
1067                },
1068        ensures
1069            final(regions).inv(),
1070    )]
1071    pub fn drop(self) {
1072        let tracked owner = owner;
1073        let ghost old_owner = owner;
1074        let ghost n = crate::specs::mm::frame::segment::seg_nframes(self.range);
1075        let mut paddr = self.range.start;
1076
1077        let ghost mut k: int = 0;
1078
1079        let ghost old_owner = owner;
1080
1081        assert forall|i: int| #![trigger frame_idx_at(self.range.start, i)] 0 <= i < n implies {
1082            let idx = frame_idx_at(self.range.start, i);
1083            old(regions).slot_owners[idx].inner_perms.ref_count.value() == 1 ==> {
1084                &&& old(regions).slot_owners[idx].inner_perms.storage.is_init()
1085                &&& old(regions).slot_owners[idx].inner_perms.in_list.value() == 0
1086            }
1087        } by {};
1088
1089        proof {
1090            assert(old_owner.range == self.range);
1091            assert forall|i: int|
1092                #![trigger frame_to_index((self.range.start + i * PAGE_SIZE) as usize)]
1093                0 <= i < n implies old(regions).frame_obligations.count(
1094                frame_to_index((self.range.start + i * PAGE_SIZE) as usize),
1095            ) >= 1 by {
1096                old_owner.relate_regions_at(*old(regions), i);
1097            };
1098            assert forall|i: int, j: int|
1099                #![trigger frame_to_index((self.range.start + i * PAGE_SIZE) as usize),
1100                    frame_to_index((self.range.start + j * PAGE_SIZE) as usize)]
1101                0 <= i < j < n implies frame_to_index((self.range.start + i * PAGE_SIZE) as usize)
1102                != frame_to_index((self.range.start + j * PAGE_SIZE) as usize) by {
1103                old_owner.relate_regions_distinct(*old(regions), i, j);
1104            };
1105            crate::specs::mm::frame::segment::tracked_redeem_seg_obligations(
1106                regions,
1107                self.range.start,
1108                n,
1109            );
1110        }
1111
1112        loop
1113            invariant
1114                regions.inv(),
1115                self.inv(),
1116                self.range.start <= paddr <= self.range.end,
1117                paddr == (self.range.start + k * PAGE_SIZE) as usize,
1118                paddr % PAGE_SIZE == 0,
1119                paddr <= MAX_PADDR,
1120                0 <= k <= n,
1121                n == (self.range.end - self.range.start) as int / PAGE_SIZE as int,
1122                paddr < self.range.end <==> k < n,
1123                forall|j: int|
1124                    #![trigger frame_to_index((self.range.start + j * PAGE_SIZE) as usize)]
1125                    k <= j < n ==> {
1126                        let idx = frame_to_index((self.range.start + j * PAGE_SIZE) as usize);
1127                        &&& regions.slot_owners.contains_key(idx)
1128                        &&& regions.slots.contains_key(idx)
1129                        &&& regions.slot_owners[idx] == old(regions).slot_owners[idx]
1130                    },
1131                forall|j: int|
1132                    #![trigger frame_idx_at(self.range.start, j)]
1133                    k <= j < n ==> regions.slot_owners.contains_key(
1134                        frame_idx_at(self.range.start, j),
1135                    ) && regions.slot_owners[frame_idx_at(self.range.start, j)] == old(
1136                        regions,
1137                    ).slot_owners[frame_idx_at(self.range.start, j)],
1138                regions.slot_owners.dom() =~= old(regions).slot_owners.dom(),
1139                self.wf(&old_owner),
1140                old_owner.inv(),
1141                old(regions).inv(),
1142                old_owner.relate_regions(*old(regions)),
1143                old_owner.range == self.range,
1144                forall|i: int|
1145                    #![trigger frame_to_index((self.range.start + i * PAGE_SIZE) as usize)]
1146                    0 <= i < n ==> {
1147                        let idx = frame_to_index((self.range.start + i * PAGE_SIZE) as usize);
1148                        old(regions).slot_owners[idx].inner_perms.ref_count.value() == 1 ==> {
1149                            &&& old(regions).slot_owners[idx].inner_perms.storage.is_init()
1150                            &&& old(regions).slot_owners[idx].inner_perms.in_list.value() == 0
1151                        }
1152                    },
1153            decreases n - k,
1154        {
1155            if paddr >= self.range.end {
1156                break;
1157            }
1158            proof {
1159                old_owner.relate_regions_at(*old(regions), k);
1160            }
1161
1162            proof_decl! {
1163                let tracked from_raw_obl: vstd_extra::drop_tracking::DropObligation<usize>;
1164            }
1165
1166            // SAFETY: each segment frame holds a forgotten reference;
1167            // `from_raw` mints the obligation and `frame.drop` consumes
1168            // it directly. The old "redeem-then-mint-then-drop" dance
1169            // is gone — `from_raw`'s freshly minted obligation feeds
1170            // straight into `frame.drop`.
1171            let frame = unsafe {
1172                #[verus_spec(with Tracked(regions) => Tracked(from_raw_obl))]
1173                Frame::<M>::from_raw(paddr)
1174            };
1175
1176            frame.drop(Tracked(regions), Tracked(from_raw_obl));
1177
1178            proof {
1179                assert forall|j: int|
1180                    #![trigger frame_to_index((self.range.start + j * PAGE_SIZE) as usize)]
1181                    (k + 1) <= j < n implies {
1182                    let idx = frame_to_index((self.range.start + j * PAGE_SIZE) as usize);
1183                    &&& regions.slot_owners.contains_key(idx)
1184                    &&& regions.slots.contains_key(idx)
1185                    &&& regions.slot_owners[idx] == old(regions).slot_owners[idx]
1186                } by {
1187                    old_owner.relate_regions_distinct(*old(regions), k, j);
1188                };
1189            }
1190
1191            paddr = paddr + PAGE_SIZE;
1192
1193            proof {
1194                k = k + 1;
1195            }
1196        }
1197    }
1198}
1199
1200/*impl<M: AnyFrameMeta> TryFrom<Segment<dyn AnyFrameMeta>> for Segment<M> {
1201    type Error = Segment<dyn AnyFrameMeta>;
1202
1203    open spec fn clone_ensures(
1204        self,
1205        old_perm: MetaRegionOwners,
1206        new_perm: MetaRegionOwners,
1207        res: Self,
1208    ) -> bool {
1209        &&& res.range == self.range
1210        &&& res.inv()
1211        &&& new_perm.inv()
1212    }
1213
1214    fn clone(&self, Tracked(perm): Tracked<&mut MetaRegionOwners>) -> (res: Self) {
1215        let mut paddr = self.range.start;
1216
1217        let ghost old_perm = *perm;
1218        loop
1219            invariant
1220                perm.inv(),
1221                self.inv(),
1222                perm.slots =~= old_perm.slots,
1223                perm.slot_owners.dom() =~= old_perm.slot_owners.dom(),
1224                // Linear-drop pilot: cloning a Segment doesn't mint or
1225                // redeem its obligation — the per-frame ref-count bump is
1226                // an Arc-style operation.
1227                perm.frame_obligations =~= old_perm.frame_obligations,
1228                self.range.start <= paddr <= self.range.end,
1229                paddr % PAGE_SIZE == 0,
1230                paddr <= MAX_PADDR,
1231                forall|pa: Paddr|
1232                    #![trigger frame_to_index(pa)]
1233                    (paddr <= pa < self.range.end && pa % PAGE_SIZE == 0) ==> {
1234                        let idx = frame_to_index(pa);
1235                        &&& perm.slots.contains_key(idx)
1236                        &&& has_safe_slot(pa)
1237                        &&& perm.slot_owners[idx].inner_perms.ref_count.value() > 0
1238                        &&& perm.slot_owners[idx].inner_perms.ref_count.value() + 1
1239                            < super::meta::REF_COUNT_MAX
1240                        &&& !MetaSlot::inc_ref_count_panic_cond(
1241                            perm.slot_owners[idx].inner_perms.ref_count,
1242                        )
1243                    },
1244            decreases self.range.end - paddr,
1245        {
1246            if paddr >= self.range.end {
1247                break;
1248            }
1249            proof {
1250                assert(paddr + PAGE_SIZE <= self.range.end);
1251                assert(paddr + PAGE_SIZE <= MAX_PADDR);
1252            }
1253
1254            #[verus_spec(with Tracked(perm))]
1255            crate::mm::frame::inc_frame_ref_count(paddr);
1256
1257            paddr = paddr + PAGE_SIZE;
1258        }
1259        // Since segments are homogeneous, we can safely assume that the rest
1260        // of the frames are of the same type. We just debug-check here.
1261        #[cfg(debug_assertions)]
1262        {
1263            for paddr in seg.range.clone().step_by(PAGE_SIZE) {
1264                let frame = unsafe { Frame::<dyn AnyFrameMeta>::from_raw(paddr) };
1265                let frame = ManuallyDrop::new(frame);
1266                debug_assert!((frame.dyn_meta() as &dyn core::any::Any).is::<M>());
1267            }
1268        }
1269        // SAFETY: The metadata is coerceable and the struct is transmutable.
1270        Ok(unsafe { core::mem::transmute::<Segment<dyn AnyFrameMeta>, Segment<M>>(seg) })
1271    }
1272}
1273
1274impl<M: AnyUFrameMeta> From<Segment<M>> for USegment {
1275    fn from(seg: Segment<M>) -> Self {
1276        // SAFETY: The metadata is coerceable and the struct is transmutable.
1277        unsafe { core::mem::transmute(seg) }
1278    }
1279}
1280
1281impl TryFrom<Segment<dyn AnyFrameMeta>> for USegment {
1282    type Error = Segment<dyn AnyFrameMeta>;
1283
1284    /// Try converting a [`Segment<dyn AnyFrameMeta>`] into [`USegment`].
1285    ///
1286    /// If the usage of the page is not the same as the expected usage, it will
1287    /// return the dynamic page itself as is.
1288    fn try_from(seg: Segment<dyn AnyFrameMeta>) -> core::result::Result<Self, Self::Error> {
1289        // SAFETY: for each page there would be a forgotten handle
1290        // when creating the `Segment` object.
1291        let first_frame = unsafe { Frame::<dyn AnyFrameMeta>::from_raw(seg.range.start) };
1292        let first_frame = ManuallyDrop::new(first_frame);
1293        if !first_frame.dyn_meta().is_untyped() {
1294            return Err(seg);
1295        }
1296        // Since segments are homogeneous, we can safely assume that the rest
1297        // of the frames are of the same type. We just debug-check here.
1298        #[cfg(debug_assertions)]
1299        {
1300            for paddr in seg.range.clone().step_by(PAGE_SIZE) {
1301                let frame = unsafe { Frame::<dyn AnyFrameMeta>::from_raw(paddr) };
1302                let frame = ManuallyDrop::new(frame);
1303                debug_assert!(frame.dyn_meta().is_untyped());
1304            }
1305        }
1306        // SAFETY: The metadata is coerceable and the struct is transmutable.
1307        Ok(unsafe { core::mem::transmute::<Segment<dyn AnyFrameMeta>, USegment>(seg) })
1308    }
1309} */
1310
1311impl<M: AnyFrameMeta + ?Sized> Inv for Segment<M> {
1312    /// The invariant of a [`Segment`]:
1313    ///
1314    /// - the physical addresses of the frames are aligned and within bounds.
1315    /// - the range is well-formed, i.e., the start is less than or equal to the end.
1316    open spec fn inv(self) -> bool {
1317        &&& self.start_paddr() % PAGE_SIZE == 0
1318        &&& self.end_paddr() % PAGE_SIZE == 0
1319        &&& self.start_paddr() <= self.end_paddr() <= MAX_PADDR
1320    }
1321}
1322
1323} // verus!