Skip to main content

ostd/specs/mm/
vm_space.rs

1use core::ops::Range;
2use vstd::pervasive::proof_from_false;
3use vstd::prelude::*;
4
5use vstd_extra::ownership::*;
6
7use crate::mm::frame::untyped::UFrame;
8use crate::mm::io::{VmReader, VmWriter};
9use crate::mm::page_prop::PageProperty;
10use crate::mm::page_table::*;
11use crate::mm::vm_space::{Cursor, CursorMut, MappedItem, UserPtConfig, VmSpace};
12use crate::mm::{MAX_USERSPACE_VADDR, Paddr, PagingConstsTrait, PagingLevel, Vaddr};
13use crate::specs::arch::mm::{NR_LEVELS, current_page_table_paddr_spec};
14use crate::specs::mm::frame::meta_region_owners::MetaRegionOwners;
15use crate::specs::mm::io::{VmIoMemView, VmIoOwner};
16use crate::specs::mm::page_table::cursor::CursorView;
17use crate::specs::mm::page_table::cursor::owners::CursorOwner;
18use crate::specs::mm::page_table::node::entry_owners::EntryOwner;
19use crate::specs::mm::page_table::{Guards, Mapping, OwnerSubtree, PageTableOwner, PageTableView};
20use crate::specs::mm::tlb::TlbModel;
21use crate::specs::mm::virt_mem::{FrameContents, MemView};
22use crate::specs::task::InAtomicMode;
23
24verus! {
25
26/// This struct is used for reading/writing memories represented by the
27/// [`VmReader`] or [`VmWriter`]. We also require a valid `vmspace_owner`
28/// must be present in this struct to ensure that the reader/writer is
29/// not created out of thin air.
30pub tracked struct VmIoPermission {
31    pub vmio_owner: VmIoOwner,
32    pub vmspace_owner: VmSpaceOwner,
33}
34
35/// A tracked struct for reasoning about verification-only properties of a [`VmSpace`].
36///
37/// This struct serves as a bookkeeper for all _active_ readers/writers within a specific
38/// virtual memory space. It maintains a holistic view of the memory range covered by the
39/// VM space it is tracking using a [`Ghost<MemView>`]. It also maintains a [`Tracked<MemView>`]
40/// for the current memories it is holding permissions for, which is a subset of the total
41/// memory range.
42///
43/// The management of each reader/writer and their corresponding memory views and permissions
44/// must talk to this struct's APIs for properly taking and returning permissions, which ensures
45/// the consistency of the overall VM space so that, e.g., no memory-aliasing will occur during
46/// the lifetime of the readers/writers, and that no reader/writer will be created out of thin air
47/// without the permission from the VM space owner (or the verification rejects invalid requests).
48///
49/// # Lifecycle of a reader/writer
50///
51/// We briefly introduce how we manage the lifecycle of a reader/writer under the management of
52/// [`VmSpaceOwner`] in this section. In the first place we require that whenever the reader or
53/// writer is being used to read or write memory, a matching permission called [`VmIoOwner`] must
54/// be present. Thus the key is to properly manage the creation and deletion of the [`VmIoOwner`].
55///
56/// 1. **Creation**: To create a new reader/writer, we first check if the new reader/writer can be created
57///   under the current VM space owner using the APIs [`Self::can_create_reader`] and [`Self::can_create_writer`].
58///   Interestingly, this creates an empty reader/writer that doesn't hold any memory view yet.
59/// 2. **Activation**: This is the magic step where we assign a memory view to the reader/writer. Via
60///  [`Self::activate_reader`] and [`Self::activate_writer`],
61///   the permissions will be moved from the VM space owner to the reader/writer. Note that readers do not
62///   need owned permissions so they just borrow the memory view from the VM space owner, while writers need to
63///   take the ownership of the memory view from the VM space owner.
64///   After this step, the reader/writer is fully activated and can be used to read/write memory.
65/// 3. **Disposal**: After the reader/writer finishes the reading/writing operation, we can dispose it via
66///   [`Self::dispose_reader`] and [`Self::dispose_writer`]. This step is the reverse of activation, where the
67///   permissions will be moved back from the reader/writer to the VM space owner. After this step, the reader/writer
68///   is considered disposed but re-usable as long as it is properly activated again before use.
69/// 4. **Removal**: If we know for sure that the reader/writer will never be used again, we can remove it from the
70///    active list via [`Self::remove_reader`] and [`Self::remove_writer`].
71pub tracked struct VmSpaceOwner {
72    /// The owner of the page table of this VM space.
73    pub page_table_owner: OwnerSubtree<UserPtConfig>,
74    /// Whether this VM space is currently active.
75    pub active: bool,
76    /// Active readers for this VM space.
77    pub readers: Seq<VmIoOwner>,
78    /// Active writers for this VM space.
79    pub writers: Seq<VmIoOwner>,
80    /// The "actual" memory view of this VM space where some
81    /// of the mappings may be  transferred to the writers.
82    pub mem_view: Option<MemView>,
83    /// This is the holistic view of the memory range covered by this VM space owner.
84    pub mv_range: Ghost<Option<MemView>>,
85    /// Whether we allow shared reading.
86    pub shared_reader: bool,
87}
88
89impl<'a> Inv for VmSpaceOwner {
90    /// Defines the invariant for `VmSpaceOwner`.
91    ///
92    /// This specification ensures the consistency of the VM space, particularly
93    /// regarding memory access permissions and overlapping ranges.
94    ///
95    /// # Invariants
96    /// 1. **Recursion**: The underlying `page_table_owner` must satisfy its own invariant.
97    /// 2. **Finiteness**: The sets of readers and writers must be finite.
98    /// 3. **Active State Consistency**: If the VM space is marked as `active`:
99    ///    - **ID Separation**: A handle ID cannot be both a reader and a writer simultaneously.
100    ///    - **Element Validity**: All stored `VmIoOwner` instances must be valid and
101    ///                             their stored ID must match their map key.
102    ///    - **Memory Isolation (Read-Write)**: No Reader memory range may overlap with any Writer memory range.
103    ///    - **Memory Isolation (Write-Write)**: No Writer memory range may overlap with any other Writer memory range.
104    ///    - **Conditional Read Isolation**: If `shared_reader` is set, Readers must be mutually disjoint (cannot overlap).
105    open spec fn inv(self) -> bool {
106        &&& self.page_table_owner.inv()
107        &&& self.active ==> {
108            &&& self.mem_view_wf()
109            &&& self.mem_view matches Some(mem_view) ==> {
110                // Readers and writers are valid.
111                &&& forall|i: int|
112                    #![trigger self.readers[i]]
113                    0 <= i < self.readers.len() as int ==> {
114                        &&& self.readers[i].inv()
115                    }
116                &&& forall|i: int|
117                    #![trigger self.writers[i]]
118                    0 <= i < self.writers.len() as int ==> {
119                        &&& self.writers[i].inv()
120                    }
121                    // --- Memory Range Overlap Checks ---
122                    // Readers do not overlap with other readers, and writers do not overlap with other writers.
123                &&& forall|i, j: int|
124                    #![trigger self.readers[i], self.writers[j]]
125                    0 <= i < self.readers.len() as int && 0 <= j < self.writers.len() as int ==> {
126                        let r = self.readers[i];
127                        let w = self.writers[j];
128                        r.disjoint(w)
129                    }
130                &&& !self.shared_reader ==> forall|i, j: int|
131                    #![trigger self.readers[i], self.readers[j]]
132                    0 <= i < self.readers.len() as int && 0 <= j < self.readers.len() as int && i
133                        != j ==> {
134                        let r1 = self.readers[i];
135                        let r2 = self.readers[j];
136                        r1.disjoint(r2)
137                    }
138                &&& forall|i, j: int|
139                    #![trigger self.writers[i], self.writers[j]]
140                    0 <= i < self.writers.len() as int && 0 <= j < self.writers.len() as int && i
141                        != j ==> {
142                        let w1 = self.writers[i];
143                        let w2 = self.writers[j];
144                        w1.disjoint(w2)
145                    }
146            }
147        }
148    }
149}
150
151impl<'a> VmSpaceOwner {
152    /// This specification function ensures that the `mem_view` (remaining view),
153    /// `mv_range` (total view), and the views held by active readers and writers
154    /// maintain a consistent global state.
155    ///
156    /// The key properties include:
157    ///
158    /// ### 1. Existence Invariants
159    /// * `mem_view` is present if and only if `mv_range` is present.
160    ///
161    /// ### 2. Structural Integrity
162    /// * Both the remaining view and the total view must have finite memory mappings.
163    /// * Internal mappings within each view must be disjoint (no overlapping address ranges).
164    ///
165    /// ### 3. Global Consistency
166    /// * **Subset Relation**: The remaining view's mappings and memory domain must be subsets of the total view.
167    /// * **Translation Equality**: For any virtual address (VA), the address translation in the remaining view must match the translation in the total view.
168    ///
169    /// ### 4. Writer Invariants (Exclusive Ownership)
170    /// * **Type Verification**: Every writer must hold a `WriteView`.
171    /// * **Translation Consistency**: Writer translations must match the total view.
172    /// * **Mutual Exclusion**: If a writer translates a VA, that VA must not exist in the remaining view's translation table or memory domain.
173    /// * **Mapping Isolation**: Writer mappings must be disjoint from the remaining view's mappings and must be a subset of the total view's mappings.
174    ///
175    /// ### 5. Reader Invariants (Shared Consistency)
176    /// * **Type Verification**: Every reader must hold a `ReadView`.
177    /// * **Translation Consistency**: Reader translations must be consistent with the total view.
178    pub open spec fn mem_view_wf(self) -> bool {
179        &&& self.mem_view is Some
180            <==> self.mv_range@ is Some
181        // This requires that TotalMapping (mvv) = mv ∪ writer mappings ∪ reader mappings
182        &&& self.mem_view matches Some(remaining_view) ==> self.mv_range@ matches Some(total_view)
183            ==> {
184            &&& remaining_view.mappings_are_disjoint()
185            &&& remaining_view.mappings.finite()
186            &&& total_view.mappings_are_disjoint()
187            &&& total_view.mappings.finite()
188            // ======================
189            // Remaining Consistency
190            // ======================
191            &&& remaining_view.mappings.subset_of(total_view.mappings)
192            &&& remaining_view.memory.dom().subset_of(
193                total_view.memory.dom(),
194            )
195            // =====================
196            // Total View Consistency
197            // =====================
198            &&& forall|va: usize|
199                #![trigger remaining_view.addr_transl(va)]
200                #![trigger total_view.addr_transl(va)]
201                remaining_view.addr_transl(va) == total_view.addr_transl(
202                    va,
203                )
204            // =====================
205            // Writer correctness
206            // =====================
207            &&& forall|i: int|
208                #![trigger self.writers[i]]
209                0 <= i < self.writers.len() as int ==> {
210                    let writer = self.writers[i];
211
212                    &&& writer.mem_view matches Some(VmIoMemView::WriteView(writer_mv)) && {
213                        &&& forall|va: usize|
214                            #![trigger writer_mv.addr_transl(va)]
215                            #![trigger total_view.addr_transl(va)]
216                            #![trigger remaining_view.addr_transl(va)]
217                            #![trigger remaining_view.memory.contains_key(va)]
218                            {
219                                // We do not enforce that the range must be the same as the
220                                // memory view it holds as the writer may not consume all the
221                                // memory in its range.
222                                //
223                                // So we cannot directly reason on `self.range` here; we need
224                                // to instead ensure that the memory view it holds is consistent
225                                // with the total view and remaining view.
226                                &&& writer_mv.mappings.finite()
227                                &&& writer_mv.addr_transl(va) == total_view.addr_transl(va)
228                                &&& writer_mv.addr_transl(va) matches Some(_) ==> {
229                                    &&& remaining_view.addr_transl(va) is None
230                                    &&& !remaining_view.memory.contains_key(va)
231                                }
232                            }
233                        &&& writer_mv.mappings.disjoint(remaining_view.mappings)
234                        &&& writer_mv.mappings.subset_of(total_view.mappings)
235                        &&& writer_mv.memory.dom().subset_of(total_view.memory.dom())
236                    }
237                }
238                // =====================
239                // Reader correctness
240                // =====================
241            &&& forall|i: int|
242                #![trigger self.readers[i]]
243                0 <= i < self.readers.len() as int ==> {
244                    let reader = self.readers[i];
245
246                    &&& reader.mem_view matches Some(VmIoMemView::ReadView(reader_mv)) && {
247                        forall|va: usize|
248                            #![trigger reader_mv.addr_transl(va)]
249                            #![trigger total_view.addr_transl(va)]
250                            {
251                                // For readers there is no need to check remaining_view
252                                // because it is borrowed from remaining_view directly.
253                                &&& reader_mv.mappings.finite()
254                                &&& reader_mv.addr_transl(va) == total_view.addr_transl(va)
255                            }
256                    }
257                }
258        }
259    }
260
261    /// Determines whether a new reader can be safely instantiated within the VM address space.
262    ///
263    /// This specification function enforces memory isolation by ensuring that the
264    /// requested memory range does not intersect with the domain of any active writer.
265    pub open spec fn can_create_reader(&self, vaddr: Vaddr, len: usize) -> bool
266        recommends
267            self.inv(),
268    {
269        &&& forall|i: int|
270            #![trigger self.writers[i]]
271            0 <= i < self.writers.len() ==> !self.writers[i].overlaps_with_range(
272                vaddr..(vaddr + len) as usize,
273            )
274    }
275
276    /// Checks if we can create a new writer under this VM space owner.
277    ///
278    /// Similar to [`can_create_reader`], but checks both active readers and writers.
279    pub open spec fn can_create_writer(&self, vaddr: Vaddr, len: usize) -> bool
280        recommends
281            self.inv(),
282    {
283        &&& forall|i: int|
284            #![trigger self.readers[i]]
285            0 <= i < self.readers.len() ==> !self.readers[i].overlaps_with_range(
286                vaddr..(vaddr + len) as usize,
287            )
288        &&& forall|i: int|
289            #![trigger self.writers[i]]
290            0 <= i < self.writers.len() ==> !self.writers[i].overlaps_with_range(
291                vaddr..(vaddr + len) as usize,
292            )
293    }
294
295    /// Generates a new unique ID for VM IO owners.
296    ///
297    /// This assumes that we always generate a fresh ID that is not used by any existing
298    /// readers or writers. This should be safe as the ID space is unbounded and only used
299    /// to reason about different VM IO owners in verification.
300    pub uninterp spec fn new_vm_io_id(&self) -> nat
301        recommends
302            self.inv(),
303    ;
304
305    /// Activates the given reader to read data from the user space of the current task.
306    /// # Verified Properties
307    /// ## Preconditions
308    /// - The [`VmSpace`] invariants must hold with respect to the [`VmSpaceOwner`], which must be active.
309    /// - The reader must be well-formed with respect to the [`VmSpaceOwner`].
310    /// - The reader's virtual address range must be mapped within the [`VmSpaceOwner`]'s memory view.
311    /// ## Postconditions
312    /// - The reader will be added to the [`VmSpace`]'s readers list.
313    /// - The reader will be activated with a view of its virtual address range taken from the [`VmSpaceOwner`]'s memory view.
314    /// ## Safety
315    /// - The function preserves all memory invariants.
316    /// - The [`MemView`] invariants ensure that the reader has a consistent view of memory.
317    /// - The [`VmSpaceOwner`] invariants ensure that the viewed memory is owned exclusively by this [`VmSpace`].
318    #[inline(always)]
319    #[verus_spec(r =>
320        requires
321            old(self).mem_view matches Some(mv) &&
322                forall |va: usize|
323                #![auto]
324                    old(owner_r).range.start <= va < old(owner_r).range.end ==>
325                        mv.addr_transl(va) is Some
326            ,
327            old(self).inv(),
328            old(self).active,
329            reader.wf(*old(owner_r)),
330            old(owner_r).mem_view is None,
331            reader.inv(),
332        ensures
333            reader.wf(*final(owner_r)),
334            final(owner_r).mem_view == Some(VmIoMemView::ReadView(old(self).mem_view@.unwrap().borrow_at(
335                old(owner_r).range.start,
336                (old(owner_r).range.end - old(owner_r).range.start) as usize,
337            ))),
338    )]
339    pub proof fn activate_reader(
340        tracked &'a mut self,
341        reader: &'a VmReader<'a>,
342        tracked owner_r: &'a mut VmIoOwner,
343    ) {
344        let tracked mv = match self.mem_view {
345            Some(ref mv) => mv,
346            _ => { proof_from_false() },
347        };
348        let tracked borrowed_mv = mv.tracked_borrow_at(
349            owner_r.range.start,
350            (owner_r.range.end - owner_r.range.start) as usize,
351        );
352
353        owner_r.mem_view = Some(VmIoMemView::ReadView(borrowed_mv));
354
355        assert forall|va: usize|
356            #![auto]
357            owner_r.range.start <= va < owner_r.range.end implies borrowed_mv.addr_transl(
358            va,
359        ) is Some by {
360            if owner_r.range.start <= va && va < owner_r.range.end {
361                assert(borrowed_mv.mappings =~= mv.mappings.filter(
362                    |m: Mapping|
363                        m.va_range.start < (owner_r.range.end) && m.va_range.end
364                            > owner_r.range.start,
365                ));
366                let o_borrow_mv = borrowed_mv.mappings.filter(
367                    |m: Mapping| m.va_range.start <= va < m.va_range.end,
368                );
369                let o_mv = mv.mappings.filter(|m: Mapping| m.va_range.start <= va < m.va_range.end);
370                assert(mv.addr_transl(va) is Some);
371                assert(o_mv.len() > 0);
372                let m = o_mv.choose();
373                vstd::set::axiom_set_choose_len(o_mv);
374                assert(o_mv.contains(m));
375                assert(o_borrow_mv.contains(m));
376                assert(o_borrow_mv.len() > 0);
377            }
378        }
379
380    }
381
382    /// Activates the given writer to write data to the user space of the current task.
383    /// # Verified Properties
384    /// ## Preconditions
385    /// - The [`VmSpace`] invariants must hold with respect to the [`VmSpaceOwner`], which must be active.
386    /// - The writer must be well-formed with respect to the `[VmSpaceOwner`].
387    /// - The writer's virtual address range must be mapped within the [`VmSpaceOwner`]'s memory view.
388    /// ## Postconditions
389    /// - The writer will be added to the [`VmSpace`]'s writers list.
390    /// - The writer will be activated with a view of its virtual address range taken from the [`VmSpaceOwner`]'s memory view.
391    /// ## Safety
392    /// - The function preserves all memory invariants.
393    /// - The [`MemView`] invariants ensure that the writer has a consistent view of memory.
394    /// - The [`VmSpaceOwner`] invariants ensure that the viewed memory is owned exclusively by
395    ///   this [`VmSpace`].
396    #[inline(always)]
397    #[verus_spec(r =>
398        requires
399            old(self).mem_view matches Some(mv) &&
400                forall |va: usize|
401                #![auto]
402                    old(owner_w).range.start <= va < old(owner_w).range.end ==>
403                        mv.addr_transl(va) is Some
404            ,
405            old(self).inv(),
406            old(self).active,
407            writer.wf(*old(owner_w)),
408            old(owner_w).mem_view is None,
409            writer.inv(),
410        ensures
411            writer.wf(*final(owner_w)),
412            final(owner_w).mem_view == Some(VmIoMemView::WriteView(old(self).mem_view@.unwrap().split(
413                old(owner_w).range.start,
414                (old(owner_w).range.end - old(owner_w).range.start) as usize,
415            ).0)),
416    )]
417    pub proof fn activate_writer(
418        tracked &mut self,
419        writer: &'a VmWriter<'a>,
420        tracked owner_w: &'a mut VmIoOwner,
421    ) {
422        let tracked mut mv = self.mem_view.tracked_take();
423        let ghost old_mv = mv;
424        let tracked (lhs, rhs) = mv.tracked_split(
425            owner_w.range.start,
426            (owner_w.range.end - owner_w.range.start) as usize,
427        );
428
429        owner_w.mem_view = Some(VmIoMemView::WriteView(lhs));
430        self.mem_view = Some(rhs);
431
432        assert forall|va: usize|
433            #![auto]
434            owner_w.range.start <= va < owner_w.range.end implies lhs.addr_transl(va) is Some by {
435            if owner_w.range.start <= va && va < owner_w.range.end {
436                assert(lhs.mappings =~= old_mv.mappings.filter(
437                    |m: Mapping|
438                        m.va_range.start < (owner_w.range.end) && m.va_range.end
439                            > owner_w.range.start,
440                ));
441                let o_lhs = lhs.mappings.filter(
442                    |m: Mapping| m.va_range.start <= va < m.va_range.end,
443                );
444                let o_mv = old_mv.mappings.filter(
445                    |m: Mapping| m.va_range.start <= va < m.va_range.end,
446                );
447
448                assert(old_mv.addr_transl(va) is Some);
449                assert(o_mv.len() > 0);
450                broadcast use vstd::set::axiom_set_choose_len;
451
452                let m = o_mv.choose();
453                assert(o_mv.contains(m));
454                assert(m.va_range.start <= va < m.va_range.end);
455                assert(o_lhs.contains(m));
456                assert(o_lhs.len() > 0);
457            }
458        }
459
460    }
461
462    /// Removes the given reader from the active readers list.
463    ///
464    /// # Verified Properties
465    /// ## Preconditions
466    /// - The [`VmSpace`] invariants must hold with respect to the [`VmSpaceOwner`], which must be active.
467    /// - The index `idx` must be a valid index into the active readers list.
468    /// ## Postconditions
469    /// - The reader at index `idx` will be removed from the active readers list.
470    /// - The invariants of the [`VmSpaceOwner`] will still hold after the removal
471    pub proof fn remove_reader(tracked &mut self, idx: int)
472        requires
473            old(self).inv(),
474            old(self).active,
475            old(self).mem_view is Some,
476            0 <= idx < old(self).readers.len() as int,
477        ensures
478            final(self).inv(),
479            final(self).active == old(self).active,
480            final(self).shared_reader == old(self).shared_reader,
481            final(self).readers == old(self).readers.remove(idx),
482    {
483        self.readers.tracked_remove(idx);
484    }
485
486    /// Removes the given writer from the active writers list.
487    ///
488    /// # Verified Properties
489    /// ## Preconditions
490    /// - The [`VmSpace`] invariants must hold with respect to the [`VmSpaceOwner`], which must be active.
491    /// - The index `idx` must be a valid index into the active writers list.
492    /// ## Postconditions
493    /// - The writer at index `idx` will be removed from the active writers list.
494    /// - The memory view held by the removed writer will be returned to the VM space owner,
495    ///   ensuring that the overall memory view remains consistent.
496    /// - The invariants of the [`VmSpaceOwner`] will still hold after the removal.
497    pub proof fn remove_writer(tracked &mut self, idx: usize)
498        requires
499            old(self).inv(),
500            old(self).active,
501            old(self).mem_view is Some,
502            old(self).mv_range@ is Some,
503            0 <= idx < old(self).writers.len() as int,
504        ensures
505            final(self).inv(),
506            final(self).active == old(self).active,
507            final(self).shared_reader == old(self).shared_reader,
508            final(self).writers == old(self).writers.remove(idx as int),
509    {
510        let tracked writer = self.writers.tracked_remove(idx as int);
511
512        let tracked mv = match writer.mem_view {
513            Some(VmIoMemView::WriteView(mv)) => mv,
514            _ => { proof_from_false() },
515        };
516
517        let tracked mut remaining = self.mem_view.tracked_take();
518        let ghost old_remaining = remaining;
519        remaining.tracked_join(mv);
520        self.mem_view = Some(remaining);
521
522        assert(self.mem_view_wf()) by {
523            let ghost total_view = self.mv_range@.unwrap();
524
525            assert(remaining.mappings =~= old_remaining.mappings.union(mv.mappings));
526            assert(remaining.memory =~= old_remaining.memory.union_prefer_right(mv.memory));
527            assert(self.mv_range == old(self).mv_range);
528            assert(self.mem_view == Some(remaining));
529
530            assert forall|va: usize|
531                #![auto]
532                { remaining.addr_transl(va) == total_view.addr_transl(va) } by {
533                let r_mappings = remaining.mappings.filter(
534                    |m: Mapping| m.va_range.start <= va < m.va_range.end,
535                );
536                let t_mappings = total_view.mappings.filter(
537                    |m: Mapping| m.va_range.start <= va < m.va_range.end,
538                );
539                let w_mappings = mv.mappings.filter(
540                    |m: Mapping| m.va_range.start <= va < m.va_range.end,
541                );
542
543                assert(r_mappings.subset_of(t_mappings));
544                assert(w_mappings.subset_of(t_mappings));
545
546                if r_mappings.len() > 0 {
547                    let r = r_mappings.choose();
548                    vstd::set::axiom_set_choose_len(r_mappings);
549                    assert(r_mappings.contains(r));
550                    assert(t_mappings.contains(r));
551                    assert(t_mappings.len() > 0);
552                }
553            }
554
555            assert forall|i: int|
556                #![trigger self.writers[i]]
557                0 <= i < self.writers.len() as int implies {
558                let other_writer = self.writers[i];
559
560                &&& other_writer.mem_view matches Some(VmIoMemView::WriteView(writer_mv))
561                    && writer_mv.mappings.disjoint(remaining.mappings)
562            } by {
563                let other_writer = self.writers[i];
564
565                assert(old(self).inv());
566                let writer_mv = match other_writer.mem_view {
567                    Some(VmIoMemView::WriteView(mv)) => mv,
568                    _ => { proof_from_false() },
569                };
570
571                assert(exists|i: int|
572                    0 <= i < old(self).writers.len() as int ==> #[trigger] old(self).writers[i]
573                        == other_writer);
574                assert(exists|i: int|
575                    0 <= i < old(self).writers.len() as int ==> #[trigger] old(self).writers[i]
576                        == writer);
577                assert(mv.mappings.disjoint(writer_mv.mappings));
578            }
579        }
580    }
581
582    /// Disposes the given reader, releasing its ownership on the memory range.
583    ///
584    /// This does not mean that the owner is discarded; it indicates that someone
585    /// who finishes the reading operation can let us reclaim the permission.
586    /// The deletion of the reader is done via another API [`VmSpaceOwner::remove_reader`].
587    ///
588    /// Typically this API is called in two scenarios:
589    ///
590    /// 1. The reader has been created and we immediately move the ownership into us.
591    /// 2. The reader has finished the reading and need to return the ownership back.
592    pub proof fn dispose_reader(tracked &mut self, tracked owner: VmIoOwner)
593        requires
594            owner.inv(),
595            old(self).inv(),
596            old(self).active,
597            old(self).mv_range@ matches Some(total_view) && owner.mem_view matches Some(
598                VmIoMemView::ReadView(mv),
599            ) && old(self).mem_view matches Some(remaining) && mv.mappings.finite() && {
600                forall|va: usize|
601                    #![auto]
602                    {
603                        &&& total_view.addr_transl(va) == mv.addr_transl(va)
604                        &&& mv.mappings.finite()
605                    }
606            },
607            forall|i: int|
608                #![trigger old(self).writers[i]]
609                0 <= i < old(self).writers.len() ==> old(self).writers[i].disjoint(owner),
610            forall|i: int|
611                #![trigger old(self).readers[i]]
612                0 <= i < old(self).readers.len() ==> old(self).readers[i].disjoint(owner),
613        ensures
614            final(self).inv(),
615            final(self).active == old(self).active,
616            final(self).shared_reader == old(self).shared_reader,
617            owner.range.start < owner.range.end ==> final(self).readers == old(self).readers.push(
618                owner,
619            ),
620    {
621        if owner.range.start < owner.range.end {
622            // Return the memory view back to the vm space owner.
623            self.readers.tracked_push(owner);
624        }
625    }
626
627    /// Disposes the given writer, releasing its ownership on the memory range.
628    ///
629    /// This does not mean that the owner is discarded; it indicates that someone
630    /// who finishes the writing operation can let us reclaim the permission.
631    ///
632    /// The deletion of the writer is through another API [`VmSpaceOwner::remove_writer`].
633    pub proof fn dispose_writer(tracked &mut self, tracked owner: VmIoOwner)
634        requires
635            old(self).inv(),
636            old(self).active,
637            owner.inv(),
638            old(self).mv_range@ matches Some(total_view) && owner.mem_view matches Some(
639                VmIoMemView::WriteView(mv),
640            ) && old(self).mem_view matches Some(remaining) && mv.mappings.finite() && {
641                &&& forall|va: usize|
642                    #![auto]
643                    {
644                        &&& mv.addr_transl(va) == total_view.addr_transl(va)
645                        &&& mv.addr_transl(va) matches Some(_) ==> {
646                            &&& remaining.addr_transl(va) is None
647                            &&& !remaining.memory.contains_key(va)
648                        }
649                    }
650                &&& mv.mappings.disjoint(remaining.mappings)
651                &&& mv.mappings.subset_of(total_view.mappings)
652                &&& mv.memory.dom().subset_of(total_view.memory.dom())
653            },
654            forall|i: int|
655                #![trigger old(self).writers[i]]
656                0 <= i < old(self).writers.len() as int ==> old(self).writers[i].disjoint(owner),
657            forall|i: int|
658                #![trigger old(self).readers[i]]
659                0 <= i < old(self).readers.len() as int ==> old(self).readers[i].disjoint(owner),
660        ensures
661            final(self).inv(),
662            final(self).active == old(self).active,
663            final(self).shared_reader == old(self).shared_reader,
664            owner.range.start < owner.range.end ==> final(self).writers == old(self).writers.push(
665                owner,
666            ),
667    {
668        // If the writer has consumed all the memory, nothing to do;
669        // just discard the writer and return the permission back to
670        // the vm space owner.
671        match owner.mem_view {
672            Some(VmIoMemView::WriteView(ref writer_mv)) => {
673                if owner.range.start < owner.range.end {
674                    self.writers.tracked_push(owner);
675                }
676            },
677            _ => {
678                assert(false);
679            },
680        }
681    }
682}
683
684impl<'a> VmSpace<'a> {
685    pub open spec fn reader_success_cond(self, vaddr: Vaddr, len: usize) -> bool {
686        &&& vaddr != 0 && len > 0 && vaddr + len <= MAX_USERSPACE_VADDR
687        &&& current_page_table_paddr_spec() == self.pt.root_paddr_spec()
688    }
689
690    pub open spec fn writer_requires(
691        &self,
692        vm_owner: VmSpaceOwner,
693        vaddr: Vaddr,
694        len: usize,
695    ) -> bool {
696        &&& vm_owner.inv()
697    }
698
699    pub open spec fn writer_success_cond(self, vaddr: Vaddr, len: usize) -> bool {
700        &&& vaddr != 0 && len > 0 && vaddr + len <= MAX_USERSPACE_VADDR
701        &&& current_page_table_paddr_spec() == self.pt.root_paddr_spec()
702    }
703}
704
705impl<'rcu, A: InAtomicMode> Cursor<'rcu, A> {
706    pub open spec fn query_success_requires(self) -> bool {
707        self.0.barrier_va.start <= self.0.va < self.0.barrier_va.end
708    }
709
710    pub open spec fn query_success_ensures(
711        self,
712        view: CursorView<UserPtConfig>,
713        range: Range<Vaddr>,
714        item: Option<MappedItem>,
715    ) -> bool {
716        if view.present() {
717            &&& item is Some
718            &&& view.query_item_spec(item.unwrap()) == Some(range)
719        } else {
720            &&& range.start == self.0.va
721            &&& item is None
722        }
723    }
724}
725
726impl<'a, A: InAtomicMode> CursorMut<'a, A> {
727    pub open spec fn map_cursor_requires(
728        self,
729        cursor_owner: CursorOwner<'a, UserPtConfig>,
730    ) -> bool {
731        &&& cursor_owner.in_locked_range()
732        &&& self.pt_cursor.0.level < self.pt_cursor.0.guard_level
733        &&& self.pt_cursor.0.va < self.pt_cursor.0.barrier_va.end
734    }
735
736    pub open spec fn item_wf(
737        self,
738        frame: UFrame,
739        prop: PageProperty,
740        entry_owner: EntryOwner<UserPtConfig>,
741        regions: MetaRegionOwners,
742    ) -> bool {
743        let item = MappedItem { frame: frame, prop: prop };
744        let (paddr, level, prop0) = UserPtConfig::item_into_raw_spec(item);
745        &&& prop == prop0
746        &&& entry_owner.frame.unwrap().mapped_pa == paddr
747        &&& entry_owner.frame.unwrap().prop == prop
748        &&& level <= UserPtConfig::HIGHEST_TRANSLATION_LEVEL()
749        &&& 1 <= level <= NR_LEVELS
750        &&& level < self.pt_cursor.0.guard_level
751        &&& Child::Frame(paddr, level, prop0).wf(entry_owner)
752        &&& self.pt_cursor.0.va + page_size(level) <= self.pt_cursor.0.barrier_va.end
753        &&& entry_owner.inv()
754        &&& self.pt_cursor.0.va % page_size(level) == 0
755        &&& crate::mm::page_table::CursorMut::<'a, UserPtConfig, A>::item_slot_in_regions(
756            item,
757            regions,
758        )
759    }
760
761    pub open spec fn map_item_ensures(
762        self,
763        frame: UFrame,
764        prop: PageProperty,
765        old_cursor_view: CursorView<UserPtConfig>,
766        cursor_view: CursorView<UserPtConfig>,
767    ) -> bool {
768        let item = MappedItem { frame: frame, prop: prop };
769        let (paddr, level, prop0) = UserPtConfig::item_into_raw_spec(item);
770        cursor_view == old_cursor_view.map_spec(paddr, page_size(level), prop)
771    }
772}
773
774} // verus!