Skip to main content

ostd/mm/frame/
segment.rs

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