Skip to main content

ostd/specs/mm/
vm_space.rs

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