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