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