ostd/mm/vm_space.rs
1// SPDX-License-Identifier: MPL-2.0
2//! Virtual memory space management.
3//!
4//! The [`VmSpace`] struct is provided to manage the virtual memory space of a
5//! user. Cursors are used to traverse and modify over the virtual memory space
6//! concurrently. The VM space cursor [`self::Cursor`] is just a wrapper over
7//! the page table cursor, providing efficient, powerful concurrent accesses
8//! to the page table.
9use vstd::{pervasive::arbitrary, prelude::*, vpanic};
10use vstd_extra::{assert, assert_eq, ghost_tree::*, panic::may_panic, prelude::*};
11
12use crate::specs::{
13 arch::*,
14 mm::{
15 frame::{
16 meta_owners::{FracMetadataPerm, MetaSlotStorage},
17 meta_region_owners::MetaRegionOwners,
18 },
19 io::VmIoOwner,
20 page_table::{cursor::owners::CursorOwner, *},
21 tlb::TlbModel,
22 virt_mem::{MemView, VirtPtr},
23 },
24 task::InAtomicMode,
25};
26
27use crate::arch::mm::{PageTableEntry, PagingConsts, current_page_table_paddr};
28use crate::cpu::{AtomicCpuSet, CpuSet};
29use crate::error::Error;
30use crate::mm::frame::meta::mapping::meta_to_frame;
31use crate::mm::frame::untyped::UFrame;
32use crate::mm::frame::{Frame, MetaSlot};
33use crate::mm::kspace::KERNEL_PAGE_TABLE;
34use crate::mm::kspace::KernelPtConfig;
35use crate::mm::page_table::*;
36use crate::mm::tlb::*;
37use crate::mm::{
38 KERNEL_VADDR_RANGE,
39 page_table::{EntryOwner, PageTableFrag, PageTableGuard},
40};
41use crate::mm::{
42 MAX_USERSPACE_VADDR, Paddr, PagingConstsTrait, PagingLevel, Vaddr,
43 io::{Fallible, VmReader, VmWriter},
44 page_prop::PageProperty,
45};
46use crate::sync::RoArc;
47use alloc::sync::Arc;
48use alloc::vec::Vec;
49use core::marker::PhantomData;
50use core::{ops::Range, sync::atomic::Ordering};
51
52#[path = "../../specs/mm/vm_space.rs"]
53pub mod vm_space_specs;
54use vm_space_specs::*;
55
56verus! {
57
58/// A virtual address space for user-mode tasks, enabling safe manipulation of user-space memory.
59///
60/// The [`VmSpace`] type provides memory isolation guarantees between user-space and
61/// kernel-space. For example, given an arbitrary user-space pointer, one can read and
62/// write the memory location referred to by the user-space pointer without the risk of
63/// breaking the memory safety of the kernel space.
64///
65/// # Task Association Semantics
66///
67/// As far as OSTD is concerned, a [`VmSpace`] is not necessarily associated with a task. Once a
68/// [`VmSpace`] is activated (see [`VmSpace::activate`]), it remains activated until another
69/// [`VmSpace`] is activated **possibly by another task running on the same CPU**.
70///
71/// This means that it's up to the kernel to ensure that a task's [`VmSpace`] is always activated
72/// while the task is running. This can be done by using the injected post schedule handler
73/// (see [`inject_post_schedule_handler`]) to always activate the correct [`VmSpace`] after each
74/// context switch.
75///
76/// If the kernel otherwise decides not to ensure that the running task's [`VmSpace`] is always
77/// activated, the kernel must deal with race conditions when calling methods that require the
78/// [`VmSpace`] to be activated, e.g., [`UserMode::execute`], [`VmReader`] and [`VmWriter`].
79/// Otherwise, the behavior is unspecified, though it's guaranteed _not_ to compromise the kernel's
80/// memory safety.
81///
82/// # Memory Backing
83///
84/// A newly-created [`VmSpace`] is not backed by any physical memory pages. To
85/// provide memory pages for a [`VmSpace`], one can allocate and map physical
86/// memory ([`UFrame`]s) to the [`VmSpace`] using the cursor.
87///
88/// A [`VmSpace`] can also attach a page fault handler, which will be invoked to
89/// handle page faults generated from user space.
90///
91/// [`inject_post_schedule_handler`]: crate::task::inject_post_schedule_handler
92/// [`UserMode::execute`]: crate::user::UserMode::execute
93/// [`VmReader`]: crate::mm::io::VmWriter
94/// [`VmReader`]: crate::mm::io::VmReader
95/// # Verification Design
96///
97/// A [`VmSpace`] has a corresponding [`VmSpaceOwner`] object that is used to track its state,
98/// and against which its invariants are stated. The [`VmSpaceOwner`] catalogues the readers and writers
99/// that are associated with the [`VmSpace`], and the [`MemView`] which encodes the active page table and
100/// the subset of the TLB that covers the same virtual address space.
101/// All proofs about the correctness of the readers and writers are founded on the well-formedness of the [`MemView`]:
102///
103/// ```rust
104/// open spec fn mem_view_wf(self) -> bool {
105/// &&& self.mem_view is Some <==> self.mv_range@ is Some
106/// // This requires that TotalMapping (mvv) = mv ∪ writer mappings ∪ reader mappings
107/// &&& self.mem_view matches Some(remaining_view)
108/// ==> self.mv_range@ matches Some(total_view)
109/// ==> {
110/// &&& remaining_view.mappings_are_disjoint()
111/// &&& total_view.mappings_are_disjoint()
112/// // ======================
113/// // Remaining Consistency
114/// // ======================
115/// &&& remaining_view.mappings.subset_of(total_view.mappings)
116/// &&& remaining_view.memory.dom().subset_of(
117/// total_view.memory.dom(),
118/// )
119/// // =====================
120/// // Total View Consistency
121/// // =====================
122/// &&& forall|va: usize|
123/// remaining_view.addr_transl(va) == total_view.addr_transl(
124/// va,
125/// )
126/// // =====================
127/// // Writer correctness
128/// // =====================
129/// &&& forall|i: int|
130/// 0 <= i < self.writers.len() ==> {
131/// &&& self.writers[i].inv()
132/// }
133/// }
134/// }
135/// }
136/// ```
137pub struct VmSpace<'a> {
138 pub pt: PageTable<UserPtConfig>,
139 pub cpus: AtomicCpuSet,
140 pub _marker: PhantomData<&'a ()>,
141}
142
143type Result<A> = core::result::Result<A, Error>;
144
145#[verus_verify]
146impl<'a> VmSpace<'a> {
147 #[inline]
148 #[verus_spec(r =>
149 with
150 Tracked(regions): Tracked<&mut MetaRegionOwners>,
151 Tracked(guards): Tracked<&mut Guards>,
152 requires
153 old(regions).inv(),
154 )]
155 #[allow(private_interfaces)]
156 pub fn default<'rcu>() -> Self {
157 proof_with!(Tracked(regions), Tracked(guards));
158 Self::new()
159 }
160
161 /// Creates a new VM address space.
162 ///
163 /// This allocates a new user page table by duplicating the kernel page
164 /// table's top-level entries, and returns a [`VmSpace`] that wraps it.
165 ///
166 /// # Verified Properties
167 /// ## Preconditions
168 /// - **Safety Invariants**: The meta-region invariants must hold.
169 /// ## Postconditions
170 /// - The returned [`VmSpace`] instance satisfies the invariants of [`VmSpace`].
171 #[inline]
172 #[verus_spec(r =>
173 with
174 Tracked(regions): Tracked<&mut MetaRegionOwners>,
175 Tracked(guards): Tracked<&mut Guards>,
176 requires
177 old(regions).inv(),
178 ensures
179 final(regions).inv(),
180 )]
181 #[allow(private_interfaces)]
182 pub fn new<'rcu>() -> Self {
183 proof_decl! {
184 let tracked mut kernel_owner_opt: Option<&PageTableOwner<KernelPtConfig>> = None;
185 }
186 let kpt = {
187 #[verus_spec(with Tracked(&mut kernel_owner_opt), Tracked(regions), Tracked(guards))]
188 crate::mm::kspace::kvirt_area::get_kernel_page_table()
189 };
190 proof_decl! {
191 let tracked kernel_owner = kernel_owner_opt.tracked_take();
192 }
193 let pt = {
194 #[verus_spec(with Tracked(kernel_owner), Tracked(regions), Tracked(guards))]
195 kpt.create_user_page_table::<crate::specs::task::AnyAtomicGuard>()
196 };
197 Self { pt, cpus: AtomicCpuSet::new(CpuSet::new_empty()), _marker: PhantomData }
198 }
199
200 /// Gets an immutable cursor in the virtual address range.
201 ///
202 /// The cursor behaves like a lock guard, exclusively owning a sub-tree of
203 /// the page table, preventing others from creating a cursor in it. So be
204 /// sure to drop the cursor as soon as possible.
205 ///
206 /// The creation of the cursor may block if another cursor having an
207 /// overlapping range is alive.
208 ///
209 /// # Verified Properties
210 /// ## Preconditions
211 /// - **Safety Invariants**: The page table owner must be valid.
212 /// ## Postconditions
213 /// - When the virtual address range satisfies
214 /// [`cursor_new_success_conditions`](crate::mm::page_table::Cursor::cursor_new_success_conditions),
215 /// the result is `Ok` and a [`CursorOwner`] is returned.
216 #[verus_spec(r =>
217 with
218 Tracked(owner): Tracked<PageTableOwner<UserPtConfig>>,
219 Ghost(root_guard): Ghost<PageTableGuard<'a, UserPtConfig>>,
220 Tracked(regions): Tracked<&mut MetaRegionOwners>,
221 Tracked(guards): Tracked<&mut Guards>,
222 -> cursor_owner: Tracked<Option<CursorOwner<'a, UserPtConfig>>>,
223 requires
224 self.pt.relates_owner(owner, *old(regions)),
225 owner.0.value().node().relate_guard(root_guard),
226 va.end > 0,
227 ensures
228 crate::mm::page_table::Cursor::<UserPtConfig, G>::cursor_new_success_conditions(*va) ==> (r matches Ok(_) && cursor_owner@ matches Some(_)),
229 // On the success branch, the returned cursor owner satisfies
230 // its invariant. Follows from the underlying PT::cursor's
231 // ensures: r is Ok ⇒ cursor_new_success_conditions (by
232 // contrapositive of the !cond ⇒ Err clause) ⇒ invariants
233 // hold ⇒ cursor_owner.inv().
234 cursor_owner@ matches Some(c) ==> c.inv(),
235 )]
236 pub fn cursor<G: InAtomicMode>(&'a self, guard: &'a G, va: &Range<Vaddr>) -> Result<
237 Cursor<'a, G>,
238 > {
239 proof_decl! {
240 let tracked mut out_owner: Option<CursorOwner<'a, UserPtConfig>>;
241 }
242 match {
243 #[verus_spec(with Tracked(owner), Ghost(root_guard), Tracked(regions), Tracked(guards))]
244 self.pt.cursor(guard, va)
245 } {
246 Ok((pt_cursor, tracked_owner)) => {
247 proof! { out_owner = Some::<CursorOwner<'a, UserPtConfig>>(tracked_owner.get()); }
248 proof_with!(|= Tracked(out_owner));
249 Ok(Cursor(pt_cursor))
250 },
251 Err(e) => {
252 proof! { out_owner = None; }
253 proof_with!(|= Tracked(out_owner));
254 Err(Error::AccessDenied)
255 },
256 }
257 }
258
259 /// Gets a mutable cursor in the virtual address range.
260 ///
261 /// The same as [`Self::cursor`], the cursor behaves like a lock guard,
262 /// exclusively owning a sub-tree of the page table, preventing others
263 /// from creating a cursor in it. So be sure to drop the cursor as soon as
264 /// possible.
265 ///
266 /// The creation of the cursor may block if another cursor having an
267 /// overlapping range is alive. The modification to the mapping by the
268 /// cursor may also block or be overridden by the mapping of another cursor.
269 ///
270 /// # Verified Properties
271 /// ## Preconditions
272 /// - **Safety Invariants**: The page table owner must be valid.
273 /// ## Postconditions
274 /// - When the virtual address range satisfies
275 /// [`cursor_new_success_conditions`](crate::mm::page_table::Cursor::cursor_new_success_conditions),
276 /// the result is `Ok` and a [`CursorOwner`] is returned.
277 #[verus_spec(r =>
278 with
279 Tracked(owner): Tracked<PageTableOwner<UserPtConfig>>,
280 Ghost(root_guard): Ghost<PageTableGuard<'a, UserPtConfig>>,
281 Tracked(regions): Tracked<&mut MetaRegionOwners>,
282 Tracked(guards): Tracked<&mut Guards>
283 -> cursor_owner: Tracked<Option<CursorOwner<'a, UserPtConfig>>>,
284 requires
285 self.pt.relates_owner(owner, *old(regions)),
286 owner.0.value().node().relate_guard(root_guard),
287 va.end > 0,
288 ensures
289 crate::mm::page_table::Cursor::<UserPtConfig, G>::cursor_new_success_conditions(*va) ==> (r matches Ok(_) && cursor_owner@ matches Some(_)),
290 // See `cursor` above for the derivation.
291 cursor_owner@ matches Some(c) ==> c.inv(),
292 )]
293 pub fn cursor_mut<G: InAtomicMode>(&'a self, guard: &'a G, va: &Range<Vaddr>) -> Result<
294 CursorMut<'a, G>,
295 > {
296 proof_decl! {
297 let tracked mut out_owner: Option<CursorOwner<'a, UserPtConfig>>;
298 }
299 match {
300 #[verus_spec(with Tracked(owner), Ghost(root_guard), Tracked(regions), Tracked(guards))]
301 self.pt.cursor_mut(guard, va)
302 } {
303 Ok((pt_cursor, tracked_owner)) => {
304 proof! { out_owner = Some::<CursorOwner<'a, UserPtConfig>>(tracked_owner.get()); }
305 proof_with!(|= Tracked(out_owner));
306 Ok(CursorMut { pt_cursor, flusher: TlbFlusher::new(&self.cpus) })
307 },
308 Err(e) => {
309 proof! { out_owner = None; }
310 proof_with!(|= Tracked(out_owner));
311 Err(Error::AccessDenied)
312 },
313 }
314 }
315
316 /// Activates the page table on the current CPU.
317 #[verifier::external_body]
318 pub fn activate(this: &RoArc<Self>) {
319 // No support for CPU set semantics; skip now
320 // let preempt_guard = disable_preempt();
321 // let cpu = preempt_guard.current_cpu();
322 // let last_ptr = ACTIVATED_VM_SPACE.load();
323 // if last_ptr == Arc::as_ptr(self) {
324 // return;
325 // }
326 // // Record ourselves in the CPU set and the activated VM space pointer.
327 // // `Acquire` to ensure the modification to the PT is visible by this CPU.
328 // self.cpus.add(cpu, Ordering::Acquire);
329 // let self_ptr = Arc::into_raw(Arc::clone(self)) as *mut VmSpace;
330 // ACTIVATED_VM_SPACE.store(self_ptr);
331 // if !last_ptr.is_null() {
332 // // SAFETY: The pointer is cast from an `Arc` when it's activated
333 // // the last time, so it can be restored and only restored once.
334 // let last = unsafe { Arc::from_raw(last_ptr) };
335 // last.cpus.remove(cpu, Ordering::Relaxed);
336 // }
337 // self.pt.activate();
338 unimplemented!()
339 }
340
341 /// Creates a reader to read data from the user space of the current task.
342 ///
343 /// Returns [`Err`] if this [`VmSpace`] is not the user space of the current task
344 /// or the `vaddr` and `len` do not represent a valid user space memory range.
345 ///
346 /// # Verified Properties
347 /// ## Preconditions
348 /// - The [`VmSpaceOwner`] invariant must hold.
349 /// ## Postconditions
350 /// - When [`Self::reader_success_cond`] holds, the result is `Ok`.
351 /// - On success, the [`VmReader`] and its [`VmIoOwner`] are well-formed with no memory view.
352 /// ## Safety
353 /// - The function does not interact with the lower-level memory system directly.
354 /// By checking that the target (user) page table is not the active (kernel) one,
355 /// we ensure that the resulting reader cannot interact with kernel memory.
356 #[inline]
357 #[verus_spec(r =>
358 with
359 Tracked(owner): Tracked<&'a mut VmSpaceOwner>,
360 -> reader_owner: Tracked<Option<VmIoOwner>>,
361 requires
362 old(owner).inv(),
363 ensures
364 final(owner).inv(),
365 self.reader_success_cond(vaddr, len) ==> r is Ok && reader_owner@ is Some,
366 r is Ok && reader_owner@ is Some ==> {
367 &&& r.unwrap().wf(reader_owner@->0)
368 &&& reader_owner@->0.mem_view is None
369 &&& reader_owner@->0.inv()
370 },
371 // Range bound is necessary for success: the body's `checked_add`
372 // guard rejects any out-of-range request before constructing a
373 // reader. `nat` arithmetic subsumes the overflow case.
374 r is Ok ==> (vaddr as nat) + (len as nat) <= MAX_USERSPACE_VADDR as nat,
375 )]
376 pub fn reader(&self, vaddr: Vaddr, len: usize) -> Result<VmReader<'a, Fallible>> {
377 if current_page_table_paddr() != self.pt.root_paddr() {
378 proof_with!(|= Tracked(None));
379 Err(Error::AccessDenied)
380 } else if vaddr.checked_add(len).unwrap_or(usize::MAX) > MAX_USERSPACE_VADDR {
381 proof_with!(|= Tracked(None));
382 Err(Error::AccessDenied)
383 } else {
384 let ghost id = owner.new_vm_io_id();
385 proof_decl! {
386 let tracked mut vm_reader_owner: VmIoOwner;
387 }
388
389 // SAFETY: The memory range is in user space, as checked above.
390 let reader = unsafe {
391 proof_with!(Ghost(id) => Tracked(vm_reader_owner));
392 VmReader::from_user_space(VirtPtr::from_vaddr(vaddr, len), len)
393 };
394
395 proof_with!(|= Tracked(Some(vm_reader_owner)));
396 Ok(reader)
397 }
398 }
399
400 /// Creates a writer to write data to the user space of the current task.
401 ///
402 /// Returns [`Err`] if this [`VmSpace`] is not the user space of the current task
403 /// or the `vaddr` and `len` do not represent a valid user space memory range.
404 ///
405 /// # Verified Properties
406 /// ## Preconditions
407 /// - The [`VmSpaceOwner`] invariant must hold.
408 /// ## Postconditions
409 /// - When [`Self::writer_success_cond`] holds, the result is `Ok`.
410 /// - On success, the [`VmWriter`] and its [`VmIoOwner`] are well-formed with no memory view.
411 /// ## Safety
412 /// - The function does not interact with the lower-level memory system directly.
413 /// By checking that the target (user) page table is not the active (kernel) one,
414 /// we ensure that the resulting writer cannot interact with kernel memory.
415 #[inline]
416 #[verus_spec(r =>
417 with
418 Tracked(owner): Tracked<&mut VmSpaceOwner>,
419 -> writer_owner: Tracked<Option<VmIoOwner>>,
420 requires
421 old(owner).inv(),
422 ensures
423 final(owner).inv(),
424 self.writer_success_cond(vaddr, len) ==> r is Ok && writer_owner@ is Some,
425 r is Ok && writer_owner@ is Some ==> {
426 &&& r.unwrap().wf(writer_owner@->0)
427 &&& writer_owner@->0.mem_view is None
428 &&& writer_owner@->0.inv()
429 },
430 // Range bound is necessary for success: see `reader` above.
431 r is Ok ==> (vaddr as nat) + (len as nat) <= MAX_USERSPACE_VADDR as nat,
432 )]
433 pub fn writer(self, vaddr: Vaddr, len: usize) -> Result<VmWriter<'a, Fallible>> {
434 if current_page_table_paddr() != self.pt.root_paddr() {
435 proof_with!(|= Tracked(None));
436 Err(Error::AccessDenied)
437 } else if vaddr.checked_add(len).unwrap_or(usize::MAX) > MAX_USERSPACE_VADDR {
438 proof_with!(|= Tracked(None));
439 Err(Error::AccessDenied)
440 } else {
441 let ghost id = owner.new_vm_io_id();
442 proof_decl! {
443 let tracked mut vm_writer_owner: VmIoOwner;
444 }
445
446 // SAFETY: The memory range is in user space, as checked above.
447 let reader = unsafe {
448 proof_with!(Ghost(id) => Tracked(vm_writer_owner));
449 VmWriter::from_user_space(VirtPtr::from_vaddr(vaddr, len), len)
450 };
451
452 proof_with!(|= Tracked(Some(vm_writer_owner)));
453 Ok(reader)
454 }
455 }
456}
457
458/// The cursor for querying over the VM space without modifying it.
459///
460/// It exclusively owns a sub-tree of the page table, preventing others from
461/// reading or modifying the same sub-tree. Two read-only cursors can not be
462/// created from the same virtual address range either.
463pub struct Cursor<'a, A: InAtomicMode>(pub crate::mm::page_table::Cursor<'a, UserPtConfig, A>);
464
465#[verus_verify]
466impl<'rcu, A: InAtomicMode> Cursor<'rcu, A> {
467 /// Queries the mapping at the current virtual address.
468 ///
469 /// If the cursor is pointing to a valid virtual address that is locked,
470 /// it will return the virtual address range and the mapped item.
471 /// ## Preconditions
472 /// - All system invariants must hold
473 /// - **Liveness**: The function will return an error if the cursor is not within the locked range
474 /// ## Postconditions
475 /// - If there is a mapped item at the current virtual address ([`query_some_condition`]),
476 /// it is returned along with the virtual address range that it maps ([`query_success_ensures`]).
477 /// - The mapping that is returned corresponds to the abstract mapping given by [`query_item_spec`](CursorView::query_item_spec).
478 /// - If there is no mapped item at the current virtual address ([`query_none_condition`]),
479 /// it returns [`None`], and the virtual address range of the cursor's current position.
480 /// ## Safety
481 /// - This function preserves all memory invariants.
482 /// - The locking mechanism prevents data races.
483 #[verus_spec(r =>
484 with
485 Tracked(owner): Tracked<&mut CursorOwner<'rcu, UserPtConfig>>,
486 Tracked(regions): Tracked<&mut MetaRegionOwners>,
487 Tracked(guards): Tracked<&mut Guards>,
488 requires
489 old(self).0.invariants(*old(owner), *old(regions), *old(guards)),
490 // Out-of-range is a graceful `Err`; the sole panic is cloning
491 // the resolved leaf frame when *that specific slot* is
492 // saturated — precisely propagated from
493 // `Cursor::query_panic_condition`.
494 old(self).0.query_panic_condition(*old(owner), *old(regions)) ==> may_panic(),
495 ensures
496 final(self).0.invariants(*final(owner), *final(regions), *final(guards)),
497 !old(self).0.query_panic_condition(*old(owner), *old(regions)),
498 old(owner).in_locked_range() ==> r is Ok,
499 r matches Ok(state) ==>
500 final(self).0.query_some_condition(*final(owner)) ==>
501 final(self).0.query_some_ensures(*final(owner), state),
502 r matches Ok(state) ==>
503 !final(self).0.query_some_condition(*final(owner)) ==>
504 final(self).0.query_none_ensures(*final(owner), state),
505 old(owner)@.mappings == final(owner)@.mappings,
506 )]
507 pub fn query(&mut self) -> Result<(Range<Vaddr>, Option<MappedItem>)> {
508 Ok(
509 #[verus_spec(with Tracked(owner), Tracked(regions), Tracked(guards))]
510 self.0.query()?,
511 )
512 }
513
514 /// Moves the cursor forward to the next mapped virtual address.
515 ///
516 /// If there is mapped virtual address following the current address within
517 /// next `len` bytes, it will return that mapped address. In this case,
518 /// the cursor will stop at the mapped address.
519 ///
520 /// Otherwise, it will return `None`. And the cursor may stop at any
521 /// address after `len` bytes.
522 ///
523 /// # Verified Properties
524 /// ## Preconditions
525 /// - **Safety Invariants**: The page table cursor safety invariants
526 /// ([crate::mm::page_table::Cursor::invariants]) must hold before the call.
527 /// - **Liveness**: In order to avoid a panic, the length must be page-aligned and less than or equal to the remaining range of the cursor.
528 /// ## Postconditions
529 /// - **Safety Invariants**: Page table cursor safety invariants are preserved.
530 /// - **Correctness**: If there is a mapped address after the current address within the next `len` bytes,
531 /// it will move the cursor to the next mapped address and return it.
532 /// - **Correctness**: If the metadata region was well-formed before the call, it will be well-formed after.
533 /// ## Panics
534 /// This method panics if the length is longer than the remaining range of the cursor.
535 /// ## Safety
536 /// This function preserves all memory invariants.
537 /// Because it panics rather than move the cursor to an invalid address,
538 /// it ensures that the cursor is safe to use after the call.
539 #[verus_spec(res =>
540 with
541 Tracked(owner): Tracked<&mut CursorOwner<'rcu, UserPtConfig>>,
542 Tracked(regions): Tracked<&mut MetaRegionOwners>,
543 Tracked(guards): Tracked<&mut Guards>,
544 requires
545 old(self).0.invariants(*old(owner), *old(regions), *old(guards)),
546 old(self).0.find_next_panic_condition(len) ==> may_panic(),
547 ensures
548 !old(self).0.find_next_panic_condition(len),
549 final(self).0.invariants(*final(owner), *final(regions), *final(guards)),
550 res is Some ==> {
551 &&& res->0 == final(self).0.va
552 &&& final(owner).level <= final(owner).guard_level
553 &&& final(owner).in_locked_range()
554 },
555 )]
556 pub fn find_next(&mut self, len: usize) -> Option<Vaddr> {
557 #[verus_spec(with Tracked(owner), Tracked(regions), Tracked(guards))]
558 self.0.find_next(len)
559 }
560
561 // [FIXED] BUG FOUND BY FV: missing panic documentation. https://github.com/asterinas/asterinas/pull/3007
562 /// Jumps to the virtual address.
563 ///
564 /// If the target address is out of the range, this method will return `Err`.
565 ///
566 /// # Panics
567 ///
568 /// This method panics if the address has bad alignment.
569 ///
570 /// # Verified Properties
571 /// ## Preconditions
572 /// - **Safety Invariants**: The page table cursor safety invariants
573 /// ([crate::mm::page_table::Cursor::invariants]) must hold before the call.
574 /// - **Liveness**: The function will panic if the target `va` is not aligned
575 /// to the base page size.
576 /// ## Postconditions
577 /// - **Safety Invariants**: Page table cursor safety invariants are preserved.
578 /// - **Correctness**: If the target `va` is within the cursor's locked range,
579 /// the result will be `Ok` and the cursor's virtual address will be set to `va`.
580 /// - **Correctness**: If the target `va` is outside the locked range, the result is `Err`.
581 /// - **Correctness**: If the metadata region was well-formed before the call, it will be well-formed after.
582 /// ## Safety
583 /// This function preserves all memory invariants.
584 /// Because it throws an error rather than move the cursor to an invalid address,
585 /// it ensures that the cursor is safe to use after the call.
586 /// The locking mechanism prevents data races.
587 #[verus_spec(res =>
588 with
589 Tracked(owner): Tracked<&mut CursorOwner<'rcu, UserPtConfig>>,
590 Tracked(regions): Tracked<&mut MetaRegionOwners>,
591 Tracked(guards): Tracked<&mut Guards>,
592 requires
593 old(self).0.invariants(*old(owner), *old(regions), *old(guards)),
594 // `CursorMut::jump` diverges on a misaligned `va` and may panic
595 // in its `pop_level` repositioning ascent.
596 old(self).0.jump_panic_condition(va) ==> may_panic(),
597 ensures
598 !old(self).0.jump_panic_condition(va),
599 final(self).0.invariants(*final(owner), *final(regions), *final(guards)),
600 final(self).0.barrier_va.start <= va < final(self).0.barrier_va.end ==> {
601 &&& res is Ok
602 &&& final(self).0.va == va
603 },
604 !(final(self).0.barrier_va.start <= va < final(self).0.barrier_va.end) ==> res is Err,
605 )]
606 pub fn jump(&mut self, va: Vaddr) -> Result<()> {
607 (#[verus_spec(with Tracked(owner), Tracked(regions), Tracked(guards))]
608 self.0.jump(va))?;
609 Ok(())
610 }
611
612 /// Get the virtual address of the current slot.
613 #[verus_spec(
614 returns
615 self.0.va,
616 )]
617 pub fn virt_addr(&self) -> Vaddr {
618 self.0.virt_addr()
619 }
620}
621
622/// The cursor for modifying the mappings in VM space.
623///
624/// It exclusively owns a sub-tree of the page table, preventing others from
625/// reading or modifying the same sub-tree.
626pub struct CursorMut<'a, A: InAtomicMode> {
627 pub pt_cursor: crate::mm::page_table::CursorMut<'a, UserPtConfig, A>,
628 // We have a read lock so the CPU set in the flusher is always a superset
629 // of actual activated CPUs.
630 pub flusher: TlbFlusher<'a /*, DisabledPreemptGuard*/ >,
631}
632
633#[verus_verify]
634impl<'a, A: InAtomicMode> CursorMut<'a, A> {
635 /// Queries the mapping at the current virtual address.
636 ///
637 /// This is the same as [`Cursor::query`].
638 ///
639 /// If the cursor is pointing to a valid virtual address that is locked,
640 /// it will return the virtual address range and the mapped item.
641 /// ## Preconditions
642 /// - **Safety Invariants**: The page table cursor safety invariants
643 /// ([crate::mm::page_table::Cursor::invariants]) must hold before the call.
644 /// ## Postconditions
645 /// - **Safety Invariants**: Page table cursor safety invariants are preserved.
646 /// - **Correctness**: If the cursor is within the locked range, the result is `Ok`.
647 /// - **Correctness**: If there is a mapped item at the current virtual address ([`query_some_condition`]),
648 /// it is returned along with the virtual address range that it maps ([`query_success_ensures`]).
649 /// - **Correctness**: The mapping that is returned corresponds to the abstract mapping given by [`query_item_spec`](CursorView::query_item_spec).
650 /// - **Correctness**: If there is no mapped item at the current virtual address ([`query_none_condition`]),
651 /// it returns `None`, and the virtual address range of the cursor's current position.
652 /// - **Correctness**: If the metadata region was well-formed before the call, it will be well-formed after.
653 /// - **Safety**: The mappings in the page table are not affected.
654 /// - **Safety**: The soundness of individual entries are not affected.
655 /// ## Safety
656 /// - This function preserves all memory invariants.
657 /// - The locking mechanism prevents data races.
658 #[verus_spec(res =>
659 with
660 Tracked(owner): Tracked<&mut CursorOwner<'a, UserPtConfig>>,
661 Tracked(regions): Tracked<&mut MetaRegionOwners>,
662 Tracked(guards): Tracked<&mut Guards>,
663 requires
664 old(self).pt_cursor.0.invariants(*old(owner), *old(regions), *old(guards)),
665 // Out-of-range → graceful `Err`; the sole panic is cloning the
666 // resolved leaf frame when *that specific slot* is saturated —
667 // precisely propagated from `Cursor::query_panic_condition`.
668 old(self).pt_cursor.0.query_panic_condition(*old(owner), *old(regions))
669 ==> may_panic(),
670 ensures
671 final(self).pt_cursor.0.invariants(*final(owner), *final(regions), *final(guards)),
672 !old(self).pt_cursor.0.query_panic_condition(*old(owner), *old(regions)),
673 old(owner).in_locked_range() ==> res is Ok,
674 res matches Ok(state) ==>
675 final(self).pt_cursor.0.query_some_condition(*final(owner)) ==>
676 final(self).pt_cursor.0.query_some_ensures(*final(owner), state),
677 res matches Ok(state) ==>
678 !final(self).pt_cursor.0.query_some_condition(*final(owner)) ==>
679 final(self).pt_cursor.0.query_none_ensures(*final(owner), state),
680 old(owner)@.mappings == final(owner)@.mappings,
681 )]
682 pub fn query(&mut self) -> Result<(Range<Vaddr>, Option<MappedItem>)> {
683 Ok(
684 #[verus_spec(with Tracked(owner), Tracked(regions), Tracked(guards))]
685 self.pt_cursor.query()?,
686 )
687 }
688
689 /// Moves the cursor forward to the next mapped virtual address.
690 ///
691 /// This is the same as [`Cursor::find_next`].
692 ///
693 /// # Verified Properties
694 /// ## Preconditions
695 /// - **Safety Invariants**: The page table cursor safety invariants
696 /// ([crate::mm::page_table::Cursor::invariants]) must hold before the call.
697 /// - **Liveness**: In order to avoid a panic, the length must be page-aligned and less than or equal to the remaining range of the cursor.
698 /// ## Postconditions
699 /// - **Safety Invariants**: Page table cursor safety invariants are preserved.
700 /// - **Correctness**: If there is a mapped address after the current address within the next `len` bytes,
701 /// it will move the cursor to the next mapped address and return it.
702 /// - **Correctness**: If the metadata region was well-formed before the call, it will be well-formed after.
703 /// ## Panics
704 /// This method panics if the length is longer than the remaining range of the cursor.
705 /// ## Safety
706 /// This function preserves all memory invariants.
707 /// Because it panics rather than move the cursor to an invalid address,
708 /// it ensures that the cursor is safe to use after the call.
709 #[verus_spec(res =>
710 with
711 Tracked(owner): Tracked<&mut CursorOwner<'a, UserPtConfig>>,
712 Tracked(regions): Tracked<&mut MetaRegionOwners>,
713 Tracked(guards): Tracked<&mut Guards>,
714 requires
715 old(self).pt_cursor.0.invariants(*old(owner), *old(regions), *old(guards)),
716 old(self).pt_cursor.0.find_next_panic_condition(len) ==> may_panic(),
717 ensures
718 !old(self).pt_cursor.0.find_next_panic_condition(len),
719 final(self).pt_cursor.0.invariants(*final(owner), *final(regions), *final(guards)),
720 res is Some ==> {
721 &&& res->0 == final(self).pt_cursor.0.va
722 &&& final(owner).level <= final(owner).guard_level
723 &&& final(owner).in_locked_range()
724 },
725 )]
726 pub fn find_next(&mut self, len: usize) -> Option<Vaddr> {
727 #[verus_spec(with Tracked(owner), Tracked(regions), Tracked(guards))]
728 self.pt_cursor.find_next(len)
729 }
730
731 // [FIXED] BUG FOUND BY FV: missing panic documentation. https://github.com/asterinas/asterinas/pull/3007
732 /// Jump to the virtual address.
733 ///
734 /// This is the same as [`Cursor::jump`].
735 ///
736 /// # Panics
737 ///
738 /// This method panics if the address has bad alignment.
739 ///
740 /// # Verified Properties
741 /// ## Preconditions
742 /// - **Safety Invariants**: The page table cursor safety invariants
743 /// ([crate::mm::page_table::Cursor::invariants]) must hold before the call.
744 /// - **Liveness**: The function will panic if the target `va` is not aligned
745 /// to the base page size.
746 /// ## Postconditions
747 /// - **Safety Invariants**: Page table cursor safety invariants are preserved.
748 /// - **Correctness**: If the target `va` is within the cursor's locked range,
749 /// the result will be `Ok` and the cursor's virtual address will be set to `va`.
750 /// - **Correctness**: If the target `va` is outside the locked range, the result is `Err`.
751 /// - **Correctness**: If the metadata region was well-formed before the call, it will be well-formed after.
752 /// ## Panics
753 /// This method panics if the target address is not aligned to the page size.
754 /// ## Safety
755 /// This function preserves all memory invariants.
756 /// Because it throws an error rather than move the cursor to an invalid address,
757 /// it ensures that the cursor is safe to use after the call.
758 /// The locking mechanism prevents data races.
759 #[verus_spec(res =>
760 with
761 Tracked(owner): Tracked<&mut CursorOwner<'a, UserPtConfig>>,
762 Tracked(regions): Tracked<&mut MetaRegionOwners>,
763 Tracked(guards): Tracked<&mut Guards>
764 requires
765 old(self).pt_cursor.0.invariants(*old(owner), *old(regions), *old(guards)),
766 // `CursorMut::jump` diverges on a misaligned `va` and may panic
767 // in its `pop_level` repositioning ascent.
768 old(self).pt_cursor.0.jump_panic_condition(va) ==> may_panic(),
769 ensures
770 !old(self).pt_cursor.0.jump_panic_condition(va),
771 final(self).pt_cursor.0.invariants(*final(owner), *final(regions), *final(guards)),
772 final(self).pt_cursor.0.barrier_va.start <= va < final(self).pt_cursor.0.barrier_va.end ==> {
773 &&& res is Ok
774 &&& final(self).pt_cursor.0.va == va
775 },
776 !(final(self).pt_cursor.0.barrier_va.start <= va < final(self).pt_cursor.0.barrier_va.end) ==> res is Err,
777 )]
778 pub fn jump(&mut self, va: Vaddr) -> Result<()> {
779 (#[verus_spec(with Tracked(owner), Tracked(regions), Tracked(guards))]
780 self.pt_cursor.jump(va))?;
781 Ok(())
782 }
783
784 /// Get the virtual address of the current slot.
785 #[verus_spec(r =>
786 returns
787 self.pt_cursor.0.va,
788 )]
789 pub fn virt_addr(&self) -> Vaddr {
790 self.pt_cursor.virt_addr()
791 }
792
793 /// Get the dedicated TLB flusher for this cursor.
794 #[verus_spec(ret =>
795 ensures
796 *ret == old(self).flusher,
797 *final(ret) == final(self).flusher,
798 )]
799 pub fn flusher(&mut self) -> &mut TlbFlusher<'a> {
800 &mut self.flusher
801 }
802
803 /// Map a frame into the current slot.
804 ///
805 /// This method will bring the cursor to the next slot after the modification.
806 /// If there is an existing mapping at the current slot, it will be replaced
807 /// and the TLB will be flushed for that entry.
808 /// # Verified Properties
809 /// ## Preconditions
810 /// - **Safety Invariants**: The page table cursor safety invariants
811 /// ([`invariants`](crate::mm::page_table::Cursor::invariants)) and the TLB invariant
812 /// ([`TlbModel::inv`]) must hold before the call.
813 /// - **Liveness**: The cursor must not be past the end of its locked range,
814 /// and the frame's level must fit within the remaining range, or the function will panic.
815 /// - **Bookkeeping**: The frame must be well-formed with respect to its entry owner
816 /// ([`item_wf`](Self::item_wf)).
817 /// ## Postconditions
818 /// - **Safety Invariants**: Page table cursor safety invariants are preserved.
819 /// - **Correctness**: The page table view is updated with the new mapping
820 /// according to [`map_item_ensures`](Self::map_item_ensures).
821 /// - **Correctness**: If the metadata region was well-formed before the call
822 /// and the frame was not already mapped, it will be well-formed after.
823 /// ## Safety
824 /// - For soundness purposes, it doesn't matter if a frame is mapped multiple times
825 /// in the same page table. There is still a clear definition of the behavior.
826 #[verus_spec(
827 with
828 Tracked(cursor_owner): Tracked<&mut CursorOwner<'a, UserPtConfig>>,
829 Tracked(entry_owner): Tracked<EntryOwner<UserPtConfig>>,
830 Tracked(regions): Tracked<&mut MetaRegionOwners>,
831 Tracked(guards): Tracked<&mut Guards>,
832 Tracked(tlb_model): Tracked<&mut TlbModel>,
833 requires
834 old(tlb_model).inv(),
835 old(self).pt_cursor.0.invariants(*old(cursor_owner), *old(regions), *old(guards)),
836 old(self).item_wf(frame, prop, entry_owner, *old(regions)),
837 old(self).pt_cursor.map_panic_conditions(MappedItem { frame: frame, prop: prop }) ==> may_panic(),
838 ensures
839 !old(self).pt_cursor.map_panic_conditions(MappedItem { frame: frame, prop: prop }),
840 final(self).pt_cursor.0.invariants(*final(cursor_owner), *final(regions), *final(guards)),
841 old(self).map_item_ensures(
842 frame,
843 prop,
844 old(cursor_owner)@,
845 final(cursor_owner)@,
846 ),
847 )]
848 pub fn map(&mut self, frame: UFrame, prop: PageProperty) {
849 let start_va = self.virt_addr();
850 let item = MappedItem { frame: frame, prop: prop };
851
852 // SAFETY: It is safe to map untyped memory into the userspace.
853 let Err(frag) = (unsafe {
854 #[verus_spec(with Tracked(cursor_owner), Tracked(entry_owner), Tracked(regions), Tracked(guards))]
855 self.pt_cursor.map(item)
856 }) else {
857 return; // No mapping exists at the current address.
858 };
859
860 match frag {
861 PageTableFrag::Mapped { va, item } => {
862 //debug_assert_eq!(va, start_va);
863 let old_frame = item.frame;
864
865 #[verus_spec(with Tracked(tlb_model))]
866 self.flusher.issue_tlb_flush_with(
867 TlbFlushOp::Address(start_va),
868 old_frame.into_dyn(),
869 );
870 #[verus_spec(with Tracked(tlb_model))]
871 self.flusher.dispatch_tlb_flush();
872 },
873 PageTableFrag::StrayPageTable { .. } => {
874 assert(false) by {
875 assert(UserPtConfig::item_into_raw(item).1 == 1);
876 };
877 #[cfg(feature = "allow_panic")]
878 vpanic!("`UFrame` is base page sized but re-mapping out a child PT");
879 },
880 }
881 }
882
883 /// Clears the mapping starting from the current slot,
884 /// and returns the number of unmapped pages.
885 ///
886 /// This method will bring the cursor forward by `len` bytes in the virtual
887 /// address space after the modification.
888 ///
889 /// Already-absent mappings encountered by the cursor will be skipped. It
890 /// is valid to unmap a range that is not mapped.
891 ///
892 /// It must issue and dispatch a TLB flush after the operation. Otherwise,
893 /// the memory safety will be compromised. Please call this function less
894 /// to avoid the overhead of TLB flush. Using a large `len` is wiser than
895 /// splitting the operation into multiple small ones.
896 ///
897 /// # Verified Properties
898 /// ## Preconditions
899 /// - **Safety Invariants**: The page table cursor safety invariants
900 /// ([crate::mm::page_table::Cursor::invariants]) must hold before the call.
901 /// - **Safety Invariants**: The TLB invariant ([TlbModel::inv]) must hold.
902 /// - **Liveness**: In order to avoid a panic, the length must be page-aligned and less than or equal to the remaining range of the cursor.
903 /// ## Postconditions
904 /// - **Safety Invariants**: The page table cursor safety invariants are preserved.
905 /// - **Safety Invariants**: The TLB invariant is preserved.
906 /// - **Correctness**: Unmaps a range of virtual addresses from the current address up to `len` bytes
907 /// and returns the number of mappings that were removed.
908 /// - **Correctness**: If the metadata region was well-formed before the call, it will be well-formed after.
909 /// ## Panics
910 /// Panics if:
911 /// - the length is longer than the remaining range of the cursor;
912 /// - the length is not page-aligned.
913 /// ## Safety
914 /// - It is always sound to unmap pages. We flush unmapped pages from the TLB to ensure consistency.
915 /// - TODO: formalizing and proving that this function preserves TLB consistency would
916 /// be pretty straightforward and would be a nice addition to the correctness properties.
917 #[verus_spec(r =>
918 with
919 Tracked(cursor_owner): Tracked<&mut CursorOwner<'a, UserPtConfig>>,
920 Tracked(regions): Tracked<&mut MetaRegionOwners>,
921 Tracked(guards): Tracked<&mut Guards>,
922 Tracked(tlb_model): Tracked<&mut TlbModel>,
923 requires
924 old(self).pt_cursor.0.invariants(*old(cursor_owner), *old(regions), *old(guards)),
925 old(tlb_model).inv(),
926 old(self).pt_cursor.0.find_next_panic_condition(len) ==> may_panic(),
927 ensures
928 !old(self).pt_cursor.0.find_next_panic_condition(len),
929 final(self).pt_cursor.0.invariants(*final(cursor_owner), *final(regions), *final(guards)),
930 old(cursor_owner)@.unmap_spec(len, final(cursor_owner)@, r),
931 final(tlb_model).inv(),
932 )]
933 #[verifier::spinoff_prover]
934 pub fn unmap(&mut self, len: usize) -> usize {
935 proof {
936 cursor_owner.view_preserves_inv();
937 }
938
939 assert_eq!(len % PAGE_SIZE, 0);
940
941 // [KNOWN] BUG FOUND BY FV: `self.va + len` could overflow. For now assume that it doesn't. https://github.com/asterinas/asterinas/issues/3159
942 assume(self.pt_cursor.0.va + len <= usize::MAX);
943
944 assert!(self.virt_addr() + len <= self.pt_cursor.0.barrier_va.end);
945
946 assert(!self.pt_cursor.0.find_next_panic_condition(len));
947
948 let end_va = self.virt_addr() + len;
949 let mut num_unmapped: usize = 0;
950
951 let ghost start_va: Vaddr = cursor_owner@.cur_va;
952 // The "adjusted base" accumulates splits: starts as the split-at-boundaries
953 // version of start_mappings and gets updated when take_next splits huge pages.
954 let ghost mut adjusted_base: Set<Mapping> = cursor_owner@.mappings;
955 // Track the set of removed mappings explicitly (not as a VA range filter).
956 let ghost mut removed: Set<Mapping> = Set::empty();
957
958 proof {
959 // end_va <= barrier_va.end == locked_range().end. The cursor invariant
960 // bounds locked_range().end by `vaddr_range_spec::<C>().1 + 1`,
961 // and for UserPtConfig that evaluates to 2^47.
962 lemma_vaddr_range_spec_user();
963 assert((self.pt_cursor.0.va + len) % PAGE_SIZE as int == 0) by (compute);
964 }
965
966 #[verus_spec(
967 invariant
968 self.pt_cursor.0.va % PAGE_SIZE == 0,
969 end_va % PAGE_SIZE == 0,
970 end_va <= 0x0000_8000_0000_0000usize,
971 self.pt_cursor.0.invariants(*cursor_owner, *regions, *guards),
972 end_va <= self.pt_cursor.0.barrier_va.end,
973 tlb_model.inv(),
974 start_va <= cursor_owner@.cur_va,
975 // Split-aware invariant: adjusted_base tracks accumulated splits,
976 // removed tracks the explicitly removed set.
977 cursor_owner@.mappings == adjusted_base.difference(removed),
978 removed.subset_of(adjusted_base),
979 num_unmapped as nat == removed.len(),
980 crate::specs::mm::page_table::mapping_set_lemmas::wf_mapping_set(adjusted_base),
981 // Everything removed is in the [start, end) range.
982 forall |m: Mapping| #[trigger] removed.contains(m) ==>
983 start_va <= m.va_range.start < end_va,
984 // Per-config VA bound: every removed mapping fits within the
985 // user VA space, sourced from the cursor view prior to removal.
986 forall |m: Mapping| #[trigger] removed.contains(m) ==>
987 m.va_range.end <= 0x0000_8000_0000_0000_usize,
988 // Nothing in [start_va, end_va) with start < cursor_va remains,
989 // unless it is a sub-mapping of a boundary-straddling entry.
990 forall |m: Mapping| #![auto] adjusted_base.contains(m) && !removed.contains(m)
991 && start_va <= m.va_range.start && m.va_range.start < end_va ==>
992 m.va_range.start >= cursor_owner@.cur_va
993 || exists |parent: Mapping| #[trigger] old(cursor_owner)@.mappings.contains(parent)
994 && parent.va_range.start < start_va
995 && parent.va_range.start <= m.va_range.start
996 && m.va_range.end <= parent.va_range.end
997 && m.pa_range.start == (parent.pa_range.start + (m.va_range.start - parent.va_range.start)) as Paddr
998 && m.property == parent.property,
999 start_va == old(cursor_owner)@.cur_va,
1000 old(cursor_owner)@.inv(),
1001 // Locality: old mappings fully outside [start, end) survive in adjusted_base.
1002 // (Straddling mappings may be split — see refinement.)
1003 forall |m: Mapping| #[trigger] old(cursor_owner)@.mappings.contains(m)
1004 && (m.va_range.end <= start_va || m.va_range.start >= end_va)
1005 ==> #[trigger] adjusted_base.contains(m),
1006 // Refinement: every mapping in adjusted_base is either from the old view
1007 // or a sub-mapping of an old entry (from boundary splits).
1008 forall |m: Mapping| #[trigger] adjusted_base.contains(m) ==>
1009 old(cursor_owner)@.mappings.contains(m)
1010 || exists |parent: Mapping| #[trigger] old(cursor_owner)@.mappings.contains(parent)
1011 && parent.va_range.start <= m.va_range.start
1012 && m.va_range.end <= parent.va_range.end
1013 && m.pa_range.start == (parent.pa_range.start + (m.va_range.start - parent.va_range.start)) as Paddr
1014 && m.property == parent.property,
1015 invariant_except_break
1016 self.pt_cursor.0.va <= end_va,
1017 self.pt_cursor.0.va < end_va ==> cursor_owner.in_locked_range(),
1018 ensures
1019 self.pt_cursor.0.va >= end_va,
1020 decreases end_va - self.pt_cursor.0.va
1021 )]
1022 loop {
1023 let ghost prev_va: Vaddr = cursor_owner@.cur_va;
1024 let ghost prev_mappings: Set<Mapping> = cursor_owner@.mappings;
1025
1026 let ghost prev_view_inv: bool = cursor_owner@.inv();
1027 proof {
1028 cursor_owner.view_preserves_inv();
1029 // Per-config VA bound on prev_mappings — needed for
1030 // preserving the `removed`-end-bound loop invariant. The user
1031 // cursor's view lies in [0, 0x8000_0000_0000) by the PROVEN
1032 // `lemma_view_in_vaddr_range_user` (no longer the generic
1033 // axiom), which follows from `cursor_owner.inv()`'s isolation
1034 // (borrowed-kernel-half) clause.
1035 crate::specs::mm::page_table::cursor::owners::lemma_view_in_vaddr_range_user(
1036 cursor_owner,
1037 );
1038 lemma_vaddr_range_spec_user();
1039 }
1040
1041 // SAFETY: It is safe to un-map memory in the userspace.
1042 let Some(frag) = (unsafe {
1043 #[verus_spec(with Tracked(cursor_owner), Tracked(regions), Tracked(guards))]
1044 self.pt_cursor.take_next(end_va - self.virt_addr())
1045 }) else {
1046 proof {
1047 // At break: take_next returned None, so no mappings in [prev_va, end_va).
1048 // Any m with start >= prev_va leads to contradiction via the empty filter.
1049 assert forall|m: Mapping|
1050 #![auto]
1051 adjusted_base.contains(m) && !removed.contains(m) && start_va
1052 <= m.va_range.start && m.va_range.start
1053 < end_va implies m.va_range.start >= cursor_owner@.cur_va || exists|
1054 parent: Mapping,
1055 | #[trigger]
1056 old(cursor_owner)@.mappings.contains(parent) && parent.va_range.start
1057 < start_va && parent.va_range.start <= m.va_range.start
1058 && m.va_range.end <= parent.va_range.end && m.pa_range.start == (
1059 parent.pa_range.start + (m.va_range.start - parent.va_range.start)) as Paddr
1060 && m.property == parent.property by {
1061 if m.va_range.start >= prev_va {
1062 assert(prev_mappings.filter(
1063 |m2: Mapping| prev_va <= m2.va_range.start < end_va,
1064 ).contains(m));
1065 assert(false);
1066 }
1067 };
1068 }
1069 break;
1070 };
1071
1072 let ghost old_adjusted = adjusted_base;
1073 let ghost old_removed = removed;
1074
1075 let ghost frag_ghost = frag;
1076
1077 match frag {
1078 PageTableFrag::Mapped { va, item, .. } => {
1079 let frame = item.frame;
1080 proof {
1081 lemma_vaddr_range_spec_user();
1082 // `wf_mapping_set(removed)` from the wf adjusted_base
1083 // via subset; `va_range.end <= 2^47` for every removed
1084 // mapping is a loop invariant. Together they give
1085 // |removed| < usize::MAX, so num_unmapped + 1 fits.
1086 crate::specs::mm::page_table::mapping_set_lemmas::lemma_wf_subset(
1087 adjusted_base,
1088 removed,
1089 );
1090 crate::specs::mm::page_table::mapping_set_lemmas::lemma_mapping_set_cardinality_fits_usize(
1091 removed);
1092 }
1093 num_unmapped += 1;
1094 #[verus_spec(with Tracked(tlb_model))]
1095 self.flusher.issue_tlb_flush_with(TlbFlushOp::Address(va), frame.into_dyn());
1096 },
1097 PageTableFrag::StrayPageTable { pt, va, len, num_frames } => {
1098 proof {
1099 let ghost new_removed = old_removed.union(
1100 prev_mappings.filter(
1101 |m2: Mapping|
1102 frag_ghost->StrayPageTable_va <= m2.va_range.start
1103 < frag_ghost->StrayPageTable_va
1104 + frag_ghost->StrayPageTable_len,
1105 ),
1106 );
1107 assert(new_removed.subset_of(old_adjusted)) by {
1108 assert forall|m: Mapping|
1109 new_removed.contains(m) implies old_adjusted.contains(m) by {
1110 if prev_mappings.contains(m) {
1111 // m ∈ prev_mappings = old_adjusted \ old_removed ⊆ old_adjusted.
1112 }
1113 };
1114 };
1115 crate::specs::mm::page_table::mapping_set_lemmas::lemma_wf_subset(
1116 old_adjusted,
1117 new_removed,
1118 );
1119 lemma_vaddr_range_spec_user();
1120 crate::specs::mm::page_table::mapping_set_lemmas::lemma_mapping_set_cardinality_fits_usize(
1121 new_removed);
1122 // |new_removed| = |old_removed| + |subtree| (disjoint).
1123 assert(old_removed.disjoint(
1124 prev_mappings.filter(
1125 |m2: Mapping|
1126 frag_ghost->StrayPageTable_va <= m2.va_range.start
1127 < frag_ghost->StrayPageTable_va
1128 + frag_ghost->StrayPageTable_len,
1129 ),
1130 )) by {
1131 assert forall|m: Mapping|
1132 old_removed.contains(m) implies !prev_mappings.filter(
1133 |m2: Mapping|
1134 frag_ghost->StrayPageTable_va <= m2.va_range.start
1135 < frag_ghost->StrayPageTable_va
1136 + frag_ghost->StrayPageTable_len,
1137 ).contains(m) by {
1138 assert(!prev_mappings.contains(m));
1139 };
1140 };
1141 vstd::set_lib::lemma_set_disjoint_lens(
1142 old_removed,
1143 prev_mappings.filter(
1144 |m2: Mapping|
1145 frag_ghost->StrayPageTable_va <= m2.va_range.start
1146 < frag_ghost->StrayPageTable_va
1147 + frag_ghost->StrayPageTable_len,
1148 ),
1149 );
1150 }
1151 num_unmapped += num_frames;
1152 proof {
1153 assert(0x0000_8000_0000_0000usize < KERNEL_VADDR_RANGE.end as usize)
1154 by (compute_only);
1155 crate::specs::mm::page_table::cursor::page_size_lemmas::lemma_va_plus_page_size_no_overflow(
1156 va, len);
1157 }
1158 #[verus_spec(with Tracked(tlb_model))]
1159 self.flusher.issue_tlb_flush_with(TlbFlushOp::Range(va..va + len), pt);
1160 },
1161 }
1162
1163 proof {
1164 let ghost mm = match frag_ghost {
1165 PageTableFrag::Mapped { va: fv, item: fi, .. } => CursorView::<
1166 UserPtConfig,
1167 >::item_into_mapping(fv, fi),
1168 _ => arbitrary(),
1169 };
1170 let ghost sv = CursorView::<UserPtConfig> {
1171 cur_va: match frag_ghost {
1172 PageTableFrag::Mapped { va: fv, .. } => fv as Vaddr,
1173 _ => 0,
1174 },
1175 mappings: prev_mappings,
1176 phantom: PhantomData,
1177 };
1178 let ghost sm = sv.split_while_huge(mm.page_size).mappings;
1179 let ghost is_mapped = frag_ghost is Mapped;
1180 let ghost subtree = match frag_ghost {
1181 PageTableFrag::StrayPageTable { va: fv, len: fl, .. } => prev_mappings.filter(
1182 |m2: Mapping| fv <= m2.va_range.start < fv + fl,
1183 ),
1184 _ => Set::empty(),
1185 };
1186
1187 // Update ghost tracking variables.
1188 match frag_ghost {
1189 PageTableFrag::StrayPageTable { .. } => {
1190 removed = old_removed.union(subtree);
1191 },
1192 PageTableFrag::Mapped { .. } => {
1193 adjusted_base = sm.union(old_removed);
1194 removed = old_removed.union(set![mm]);
1195 },
1196 }
1197
1198 // Mapped-case setup: establish split_while_huge properties once.
1199 if is_mapped {
1200 assert forall|m: Mapping, x: Mapping| #[trigger]
1201 prev_mappings.contains(m) && #[trigger] old_removed.contains(
1202 x,
1203 ) implies Mapping::disjoint_vaddrs(m, x) by {
1204 assert(old_adjusted.contains(m));
1205 assert(old_adjusted.contains(x));
1206 };
1207 sv.split_while_huge_disjoint(mm.page_size, old_removed);
1208 sv.lemma_split_while_huge_preserves_inv(mm.page_size);
1209 }
1210 assert forall|m: Mapping| #[trigger] removed.contains(m) implies m.va_range.end
1211 <= 0x0000_8000_0000_0000_usize by {
1212 if !old_removed.contains(m) {
1213 if is_mapped {
1214 assert(m == mm);
1215 sv.split_while_huge_refinement(mm.page_size, mm);
1216 if !prev_mappings.contains(mm) {
1217 let parent = choose|p: Mapping| #[trigger]
1218 prev_mappings.contains(p) && p.va_range.start
1219 <= mm.va_range.start && mm.va_range.end <= p.va_range.end
1220 && mm.pa_range.start == (p.pa_range.start + (
1221 mm.va_range.start - p.va_range.start)) as Paddr && mm.property
1222 == p.property;
1223 }
1224 } else {
1225 assert(prev_mappings.contains(m));
1226 }
1227 }
1228 };
1229
1230 // Prove |removed| tracking (disjointness + cardinality).
1231
1232 match frag_ghost {
1233 PageTableFrag::StrayPageTable { .. } => {
1234 assert(old_removed.disjoint(subtree)) by {
1235 assert forall|e: Mapping|
1236 old_removed.contains(e) implies !subtree.contains(e) by {};
1237 };
1238 vstd::set_lib::lemma_set_disjoint_lens(old_removed, subtree);
1239 assert(removed == old_removed + subtree);
1240 },
1241 PageTableFrag::Mapped { .. } => {
1242 assert(old_removed.disjoint(set![mm])) by {
1243 assert forall|e: Mapping| #[trigger]
1244 old_removed.contains(e) implies !set![mm].contains(e) by {};
1245 };
1246 vstd::set_lib::lemma_set_disjoint_lens(old_removed, set![mm]);
1247 assert(removed == old_removed + set![mm]);
1248 vstd::set_lib::lemma_set_empty_equivalency_len(Set::<Mapping>::empty());
1249 vstd::set::lemma_set_insert_len(Set::<Mapping>::empty(), mm);
1250 },
1251 }
1252
1253 // Maintain wf_mapping_set(adjusted_base) — only changes in Mapped case.
1254 if is_mapped {
1255 crate::specs::mm::page_table::mapping_set_lemmas::lemma_wf_subset(
1256 old_adjusted,
1257 old_removed,
1258 );
1259 assert forall|m: Mapping, n: Mapping| #[trigger]
1260 sm.contains(m) && #[trigger] old_removed.contains(n) implies m.va_range.end
1261 <= n.va_range.start || n.va_range.end <= m.va_range.start by {
1262 sv.split_while_huge_refinement(mm.page_size, m);
1263 assert(!prev_mappings.contains(n));
1264 if prev_mappings.contains(m) {
1265 } else {
1266 let p = choose|p: Mapping| #[trigger]
1267 prev_mappings.contains(p) && p.va_range.start <= m.va_range.start
1268 && m.va_range.end <= p.va_range.end && m.pa_range.start == (
1269 p.pa_range.start + (m.va_range.start - p.va_range.start)) as Paddr
1270 && m.property == p.property;
1271 assert(old_adjusted.contains(p));
1272 }
1273 };
1274 crate::specs::mm::page_table::mapping_set_lemmas::lemma_wf_union(
1275 sm,
1276 old_removed,
1277 );
1278 }
1279 // Maintain mappings == adjusted_base \ removed.
1280
1281 assert forall|e: Mapping| #[trigger]
1282 adjusted_base.difference(removed).contains(e)
1283 <==> cursor_owner@.mappings.contains(e) by {};
1284
1285 assert(removed.subset_of(adjusted_base)) by {
1286 assert forall|e: Mapping| #[trigger]
1287 removed.contains(e) implies adjusted_base.contains(e) by {};
1288 };
1289
1290 // Maintain: not-yet-removed mappings in [start, end) are either
1291 // ahead of the cursor or sub-mappings of a boundary-straddling parent.
1292 assert forall|m: Mapping|
1293 #![auto]
1294 adjusted_base.contains(m) && !removed.contains(m) && start_va
1295 <= m.va_range.start && m.va_range.start < end_va implies m.va_range.start
1296 >= cursor_owner@.cur_va || exists|parent: Mapping| #[trigger]
1297 old(cursor_owner)@.mappings.contains(parent) && parent.va_range.start < start_va
1298 && parent.va_range.start <= m.va_range.start && m.va_range.end
1299 <= parent.va_range.end && m.pa_range.start == (parent.pa_range.start + (
1300 m.va_range.start - parent.va_range.start)) as Paddr && m.property
1301 == parent.property by {
1302 if m.va_range.start < cursor_owner@.cur_va {
1303 if m.va_range.start >= prev_va {
1304 // m was just processed — contradiction via empty filtered sets.
1305 match frag_ghost {
1306 PageTableFrag::StrayPageTable { va: frag_va, .. } => {
1307 if m.va_range.start >= frag_va {
1308 assert(cursor_owner@.mappings.filter(
1309 |m2: Mapping|
1310 frag_va <= m2.va_range.start < self.pt_cursor.0.va,
1311 ).contains(m));
1312 } else {
1313 assert(prev_mappings.filter(
1314 |m2: Mapping| prev_va <= m2.va_range.start < frag_va,
1315 ).contains(m));
1316 }
1317 },
1318 PageTableFrag::Mapped { va: frag_va, .. } => {
1319 if m.va_range.start >= (frag_va as usize) {
1320 assert(cursor_owner@.mappings.filter(
1321 |m2: Mapping|
1322 (frag_va as usize) <= m2.va_range.start
1323 < self.pt_cursor.0.va,
1324 ).contains(m));
1325 } else {
1326 assert(prev_mappings.filter(
1327 |m2: Mapping|
1328 prev_va <= m2.va_range.start < (frag_va as usize),
1329 ).contains(m));
1330 }
1331 },
1332 }
1333 assert(false);
1334 } else if is_mapped {
1335 // m.start < prev_va, m ∈ sm \ old_removed.
1336 assert(sm.contains(m));
1337 sv.split_while_huge_refinement(mm.page_size, m);
1338 if prev_mappings.contains(m) {
1339 // m ∈ old_adjusted \ old_removed — previous invariant applies directly.
1340 } else {
1341 // m is a sub-mapping of some p ∈ prev_mappings.
1342 let p = choose|p: Mapping| #[trigger]
1343 prev_mappings.contains(p) && p.va_range.start
1344 <= m.va_range.start && m.va_range.end <= p.va_range.end
1345 && m.pa_range.start == (p.pa_range.start + (m.va_range.start
1346 - p.va_range.start)) as Paddr && m.property == p.property;
1347 assert(old_adjusted.contains(p) && !old_removed.contains(p));
1348 if p.va_range.start < start_va {
1349 // p itself or its ancestor is the boundary parent.
1350 if !old(cursor_owner)@.mappings.contains(p) {
1351 let orig = choose|orig: Mapping| #[trigger]
1352 old(cursor_owner)@.mappings.contains(orig)
1353 && orig.va_range.start <= p.va_range.start
1354 && p.va_range.end <= orig.va_range.end
1355 && p.pa_range.start == (orig.pa_range.start + (
1356 p.va_range.start - orig.va_range.start)) as Paddr
1357 && p.property == orig.property;
1358 assert(orig.inv());
1359 assert(m.inv());
1360 crate::specs::mm::page_table::mapping_set_lemmas::lemma_sub_mapping_pa_compose(
1361 m, p, orig);
1362 }
1363 } else if p.va_range.start >= end_va {
1364 assert(false); // p.start <= m.start < end_va, contradiction.
1365 } else {
1366 // start_va <= p.start < end_va, p.start < prev_va.
1367 // Previous invariant gives boundary ancestor orig.
1368 let orig = choose|orig: Mapping| #[trigger]
1369 old(cursor_owner)@.mappings.contains(orig)
1370 && orig.va_range.start < start_va && orig.va_range.start
1371 <= p.va_range.start && p.va_range.end
1372 <= orig.va_range.end && p.pa_range.start == (
1373 orig.pa_range.start + (p.va_range.start
1374 - orig.va_range.start)) as Paddr && p.property
1375 == orig.property;
1376 assert(orig.inv());
1377 assert(m.inv());
1378 crate::specs::mm::page_table::mapping_set_lemmas::lemma_sub_mapping_pa_compose(
1379 m, p, orig);
1380 }
1381 }
1382 }
1383 }
1384 };
1385
1386 // Maintain: old mappings outside [start, end) survive in adjusted_base.
1387 if is_mapped {
1388 assert forall|m: Mapping|
1389 old(cursor_owner)@.mappings.contains(m) && (m.va_range.end <= start_va
1390 || m.va_range.start
1391 >= end_va) implies #[trigger] adjusted_base.contains(m) by {
1392 if m.va_range.end <= start_va {
1393 assert(m.inv());
1394 }
1395 sv.split_while_huge_locality(mm.page_size, m);
1396 };
1397
1398 // Maintain: refinement — every mapping in adjusted_base comes from
1399 // old mappings or is a sub-mapping of one.
1400 assert forall|m: Mapping| #[trigger] adjusted_base.contains(m) implies old(
1401 cursor_owner,
1402 )@.mappings.contains(m) || exists|parent: Mapping| #[trigger]
1403 old(cursor_owner)@.mappings.contains(parent) && parent.va_range.start
1404 <= m.va_range.start && m.va_range.end <= parent.va_range.end
1405 && m.pa_range.start == (parent.pa_range.start + (m.va_range.start
1406 - parent.va_range.start)) as Paddr && m.property == parent.property by {
1407 if !old_removed.contains(m) {
1408 sv.split_while_huge_refinement(mm.page_size, m);
1409 if !prev_mappings.contains(m) {
1410 let p = choose|p: Mapping| #[trigger]
1411 prev_mappings.contains(p) && p.va_range.start
1412 <= m.va_range.start && m.va_range.end <= p.va_range.end
1413 && m.pa_range.start == (p.pa_range.start + (m.va_range.start
1414 - p.va_range.start)) as Paddr && m.property == p.property;
1415 assert(old_adjusted.contains(p));
1416 if !old(cursor_owner)@.mappings.contains(p) {
1417 let orig = choose|orig: Mapping| #[trigger]
1418 old(cursor_owner)@.mappings.contains(orig)
1419 && orig.va_range.start <= p.va_range.start
1420 && p.va_range.end <= orig.va_range.end
1421 && p.pa_range.start == (orig.pa_range.start + (
1422 p.va_range.start - orig.va_range.start)) as Paddr
1423 && p.property == orig.property;
1424 assert(orig.inv());
1425 assert(m.inv());
1426 crate::specs::mm::page_table::mapping_set_lemmas::lemma_sub_mapping_pa_compose(
1427 m, p, orig);
1428 }
1429 }
1430 }
1431 }
1432 };
1433 }
1434 }
1435 proof {
1436 let old_view = old(cursor_owner)@;
1437 let new_view = cursor_owner@;
1438
1439 // Bridge from loop invariant to unmap_spec.
1440 let start = old_view.cur_va;
1441 let end = (old_view.cur_va + len) as Vaddr;
1442
1443 assert forall|m: Mapping|
1444 #![auto]
1445 old_view.mappings.contains(m) && (m.va_range.end <= start || m.va_range.start
1446 >= end) implies new_view.mappings.contains(m) by {
1447 assert(adjusted_base.contains(m));
1448 if m.va_range.end <= start {
1449 assert(m.inv());
1450 }
1451 };
1452
1453 assert forall|m: Mapping|
1454 new_view.mappings.contains(m) && start <= m.va_range.start < end implies exists|
1455 parent: Mapping,
1456 | #[trigger]
1457 old_view.mappings.contains(parent) && parent.va_range.start < start
1458 && parent.va_range.start <= m.va_range.start && m.va_range.end
1459 <= parent.va_range.end && m.pa_range.start == (parent.pa_range.start + (
1460 m.va_range.start - parent.va_range.start)) as Paddr && m.property
1461 == parent.property by {};
1462
1463 assert forall|m: Mapping|
1464 new_view.mappings.contains(m) implies old_view.mappings.contains(m) || exists|
1465 parent: Mapping,
1466 | #[trigger]
1467 old_view.mappings.contains(parent) && parent.va_range.start <= m.va_range.start
1468 && m.va_range.end <= parent.va_range.end && m.pa_range.start == (
1469 parent.pa_range.start + (m.va_range.start - parent.va_range.start)) as Paddr
1470 && m.property == parent.property by {};
1471 }
1472
1473 #[verus_spec(with Tracked(tlb_model))]
1474 self.flusher.dispatch_tlb_flush();
1475
1476 num_unmapped
1477 }
1478
1479 /// Applies the operation to the next slot of mapping within the range.
1480 ///
1481 /// The range to be found in is the current virtual address with the
1482 /// provided length.
1483 ///
1484 /// The function stops and yields the actually protected range if it has
1485 /// actually protected a page, no matter if the following pages are also
1486 /// required to be protected.
1487 ///
1488 /// It also makes the cursor moves forward to the next page after the
1489 /// protected one. If no mapped pages exist in the following range, the
1490 /// cursor will stop at the end of the range and return [`None`].
1491 ///
1492 /// Note that it will **NOT** flush the TLB after the operation. Please
1493 /// make the decision yourself on when and how to flush the TLB using
1494 /// [`Self::flusher`].
1495 ///
1496 /// # Verified Properties
1497 /// ## Preconditions
1498 /// - **Safety Invariants**: The page table cursor safety invariants
1499 /// ([`invariants`](crate::mm::page_table::Cursor::invariants)) and the
1500 /// meta-region invariants must hold before the call.
1501 /// - The cursor must be within the locked range and below the guard level.
1502 /// - The current entry must be a mapped frame (not absent or a page table node).
1503 /// - **Liveness**: The length must be page-aligned and within the remaining cursor range.
1504 /// ## Postconditions
1505 /// - **Correctness**: If the metadata region was well-formed before the call, it will be
1506 /// well-formed after.
1507 /// ## Panics
1508 /// Panics if the length is longer than the remaining range of the cursor.
1509 /// ## Safety
1510 /// - From a soundness perspective changing a userspace page's `prop` field is safe.
1511 #[verus_spec(r =>
1512 with
1513 Tracked(owner): Tracked<&mut CursorOwner<'a, UserPtConfig>>,
1514 Tracked(regions): Tracked<&mut MetaRegionOwners>,
1515 Tracked(guards): Tracked<&mut Guards>,
1516 requires
1517 old(self).pt_cursor.0.invariants(*old(owner), *old(regions), *old(guards)),
1518 forall |p: PageProperty| op.requires((p,)),
1519 // POTENTIALLY UNSOUND PATCH: trackedness preservation. For UserPtConfig
1520 // this is trivially true (tracked is constant). See `Entry::protect`.
1521 forall |pa: Paddr, level: PagingLevel, p_in: PageProperty, p_out: PageProperty,
1522 perm: Tracked<Option<<UserPtConfig as PageTableConfig>::Perm>>| #![auto]
1523 op.ensures((p_in,), p_out) ==> (
1524 UserPtConfig::item_into_raw(UserPtConfig::item_from_raw(
1525 pa, level, p_out, perm,
1526 )).3@ is Some
1527 ) == (perm@ is Some),
1528 forall |pa: Paddr, level: PagingLevel, p_in: PageProperty, p_out: PageProperty| #![auto]
1529 op.ensures((p_in,), p_out)
1530 && <PageTableEntry as PageTableEntryTrait>::new_page_req(pa, level, p_in) ==>
1531 <PageTableEntry as PageTableEntryTrait>::new_page_req(pa, level, p_out),
1532
1533 old(self).pt_cursor.0.find_next_panic_condition(len) ==> may_panic(),
1534 ensures
1535 !old(self).pt_cursor.0.find_next_panic_condition(len),
1536 final(self).pt_cursor.0.invariants(*final(owner), *final(regions), *final(guards)),
1537 final(self).pt_cursor.0.barrier_va == old(self).pt_cursor.0.barrier_va,
1538 )]
1539 pub fn protect_next(
1540 &mut self,
1541 len: usize,
1542 op: impl FnOnce(PageProperty) -> PageProperty,
1543 ) -> Option<Range<Vaddr>> {
1544 // SAFETY: It is safe to protect memory in the userspace.
1545 unsafe {
1546 #[verus_spec(with Tracked(owner), Tracked(regions), Tracked(guards))]
1547 self.pt_cursor.protect_next(len, op)
1548 }
1549 }
1550}
1551
1552/*cpu_local_cell! {
1553 /// The `Arc` pointer to the activated VM space on this CPU. If the pointer
1554 /// is NULL, it means that the activated page table is merely the kernel
1555 /// page table.
1556 // TODO: If we are enabling ASID, we need to maintain the TLB state of each
1557 // CPU, rather than merely the activated `VmSpace`. When ASID is enabled,
1558 // the non-active `VmSpace`s can still have their TLB entries in the CPU!
1559 static ACTIVATED_VM_SPACE: *const VmSpace = core::ptr::null();
1560}*/
1561
1562/*#[cfg(ktest)]
1563pub(super) fn get_activated_vm_space() -> *const VmSpace {
1564 ACTIVATED_VM_SPACE.load()
1565}*/
1566
1567/// The configuration for user page tables.
1568#[verifier::allow(autoderive_clone_without_spec)]
1569#[derive(Clone, Debug)]
1570pub struct UserPtConfig {}
1571
1572/// The item that can be mapped into the [`VmSpace`].
1573pub struct MappedItem {
1574 pub frame: UFrame,
1575 pub prop: PageProperty,
1576}
1577
1578#[verus_verify]
1579impl RCClone for MappedItem {
1580 open spec fn clone_requires(self, perm: MetaRegionOwners) -> bool {
1581 self.frame.clone_requires(perm)
1582 }
1583
1584 open spec fn clone_ensures(
1585 self,
1586 old_perm: MetaRegionOwners,
1587 new_perm: MetaRegionOwners,
1588 res: Self,
1589 ) -> bool {
1590 &&& self.prop == res.prop
1591 &&& self.frame.clone_ensures(old_perm, new_perm, res.frame)
1592 }
1593
1594 fn clone(&self, Tracked(perm): Tracked<&mut MetaRegionOwners>) -> (res: Self) {
1595 let frame = self.frame.clone(Tracked(perm));
1596 Self { frame, prop: self.prop }
1597 }
1598}
1599
1600// SAFETY: `item_into_raw` and `item_from_raw` are implemented correctly,
1601unsafe impl PageTableConfig for UserPtConfig {
1602 open spec fn TOP_LEVEL_INDEX_RANGE_spec() -> Range<usize> {
1603 0..256
1604 }
1605
1606 open spec fn TOP_LEVEL_CAN_UNMAP_spec() -> (b: bool) {
1607 true
1608 }
1609
1610 fn TOP_LEVEL_INDEX_RANGE() -> Range<usize> {
1611 0..256
1612 }
1613
1614 fn TOP_LEVEL_CAN_UNMAP() -> (b: bool) {
1615 true
1616 }
1617
1618 type E = PageTableEntry;
1619
1620 type C = PagingConsts;
1621
1622 type Item = MappedItem;
1623
1624 type Perm = (&'static vstd::simple_pptr::PointsTo<MetaSlot>, FracMetadataPerm);
1625
1626 open spec fn perm_well_formed_with_region(
1627 pa: Paddr,
1628 perm: Tracked<Option<Self::Perm>>,
1629 regions: MetaRegionOwners,
1630 ) -> bool {
1631 perm@ is Some ==> {
1632 let idx = crate::specs::mm::frame::mapping::frame_to_index(pa);
1633 let frame_perm = perm@->0;
1634 &&& frame_perm.0 == regions.slots[idx]
1635 &&& frame_perm.1.frac() == 1
1636 &&& frame_perm.1.id() == regions.slot_owners[idx].metadata_perm.id()
1637 &&& MetaSlot::perms_related(*frame_perm.0, frame_perm.1.resource())
1638 }
1639 }
1640
1641 proof fn lemma_none_perm_well_formed(pa: Paddr, regions: MetaRegionOwners) {
1642 }
1643
1644 open spec fn item_into_raw_spec(item: Self::Item) -> (
1645 Paddr,
1646 PagingLevel,
1647 PageProperty,
1648 Tracked<Option<Self::Perm>>,
1649 ) {
1650 (
1651 item.frame.start_paddr_spec(),
1652 1,
1653 item.prop,
1654 Tracked(Some((item.frame.tracked_slot_perm@, item.frame.tracked_metadata_perm@->0))),
1655 )
1656 }
1657
1658 #[verifier::external_body]
1659 fn item_into_raw(item: Self::Item) -> (res: (
1660 Paddr,
1661 PagingLevel,
1662 PageProperty,
1663 Tracked<Option<Self::Perm>>,
1664 )) {
1665 proof_decl! {
1666 let tracked frame_permission: FracMetadataPerm;
1667 }
1668 let MappedItem { frame, prop } = item;
1669 proof_decl! {
1670 let tracked slot_perm = *frame.tracked_slot_perm;
1671 }
1672 let level = frame.map_level();
1673 let paddr = frame.into_raw();
1674 proof_with!(=> Tracked(frame_permission));
1675 (paddr, level, prop, Tracked(Some((slot_perm, frame_permission))))
1676 }
1677
1678 open spec fn item_from_raw_spec(
1679 paddr: Paddr,
1680 _level: PagingLevel,
1681 prop: PageProperty,
1682 perm: Tracked<Option<Self::Perm>>,
1683 ) -> Self::Item {
1684 MappedItem {
1685 frame: UFrame {
1686 ptr: vstd::simple_pptr::PPtr(
1687 crate::mm::frame::meta::mapping::frame_to_meta(paddr),
1688 PhantomData,
1689 ),
1690 _marker: PhantomData,
1691 #[cfg(verus_keep_ghost_body)]
1692 tracked_slot_perm: Tracked((perm@->0).0),
1693 #[cfg(verus_keep_ghost_body)]
1694 tracked_metadata_perm: Tracked(Some((perm@->0).1)),
1695 },
1696 prop,
1697 }
1698 }
1699
1700 #[verifier::external_body]
1701 unsafe fn item_from_raw(
1702 paddr: Paddr,
1703 level: PagingLevel,
1704 prop: PageProperty,
1705 Tracked(perm): Tracked<Option<Self::Perm>>,
1706 ) -> Self::Item {
1707 let tracked (slot_perm, frame_permission) = perm.tracked_unwrap();
1708 proof_with!(Tracked(slot_perm), Tracked(frame_permission));
1709 let frame = unsafe { UFrame::from_raw(paddr) };
1710 MappedItem { frame, prop }
1711 }
1712
1713 proof fn lemma_item_into_raw_roundtrip(
1714 pa: Paddr,
1715 level: PagingLevel,
1716 prop: PageProperty,
1717 perm: Tracked<Option<Self::Perm>>,
1718 ) {
1719 broadcast use crate::specs::mm::frame::mapping::group_page_meta;
1720
1721 let item = Self::item_from_raw(pa, level, prop, perm);
1722 assert(Self::raw_item_well_formed((pa, level, prop, perm)));
1723 assert(item.frame.ptr.addr() == crate::mm::frame::meta::mapping::frame_to_meta(pa));
1724 crate::specs::mm::frame::mapping::lemma_paddr_to_meta_biinjective(pa);
1725 }
1726
1727 proof fn lemma_item_from_raw_roundtrip(
1728 item: Self::Item,
1729 paddr: Paddr,
1730 level: PagingLevel,
1731 prop: PageProperty,
1732 perm: Tracked<Option<Self::Perm>>,
1733 ) {
1734 broadcast use crate::specs::mm::frame::mapping::group_page_meta;
1735
1736 assert(Self::item_well_formed(item));
1737 crate::specs::mm::frame::mapping::lemma_meta_to_paddr_biinjective(item.frame.ptr.addr());
1738 }
1739
1740 open spec fn item_well_formed(item: Self::Item) -> bool {
1741 item.frame.inv()
1742 }
1743
1744 open spec fn raw_item_well_formed(
1745 item: (Paddr, PagingLevel, PageProperty, Tracked<Option<Self::Perm>>),
1746 ) -> bool {
1747 let (pa, level, _prop, perm) = item;
1748 &&& level == 1
1749 &&& perm@ is Some
1750 &&& (perm@->0).0.addr() == crate::mm::frame::meta::mapping::frame_to_meta(pa)
1751 &&& (perm@->0).0.is_init()
1752 &&& (perm@->0).1.frac() == 1
1753 &&& MetaSlot::perms_related(*(perm@->0).0, (perm@->0).1.resource())
1754 }
1755
1756 proof fn lemma_perm_well_formed_with_region_preserved(
1757 pa: Paddr,
1758 perm: Tracked<Option<Self::Perm>>,
1759 old_regions: MetaRegionOwners,
1760 new_regions: MetaRegionOwners,
1761 ) {
1762 if perm@ is Some {
1763 let idx = crate::specs::mm::frame::mapping::frame_to_index(pa);
1764 let frame_perm = perm@->0;
1765 assert(frame_perm == perm@->0);
1766 assert(frame_perm.0 == old_regions.slots[idx]);
1767 assert(frame_perm.0 == new_regions.slots[idx]);
1768 assert(frame_perm.1.id() == old_regions.slot_owners[idx].metadata_perm.id());
1769 assert(frame_perm.1.id() == new_regions.slot_owners[idx].metadata_perm.id());
1770 }
1771 }
1772
1773 proof fn lemma_raw_item_well_formed_preserved(
1774 pa: Paddr,
1775 level: PagingLevel,
1776 old_prop: PageProperty,
1777 new_prop: PageProperty,
1778 perm: Tracked<Option<Self::Perm>>,
1779 ) {
1780 }
1781
1782 proof fn lemma_raw_item_well_formed_split(
1783 pa: Paddr,
1784 level: PagingLevel,
1785 prop: PageProperty,
1786 child_pa: Paddr,
1787 child_idx: usize,
1788 perm: Tracked<Option<Self::Perm>>,
1789 ) {
1790 }
1791
1792 proof fn lemma_huge_raw_item_untracked(
1793 pa: Paddr,
1794 level: PagingLevel,
1795 prop: PageProperty,
1796 perm: Tracked<Option<Self::Perm>>,
1797 ) {
1798 }
1799
1800 proof fn lemma_item_from_raw_well_formed(
1801 pa: Paddr,
1802 level: PagingLevel,
1803 prop: PageProperty,
1804 perm: Tracked<Option<Self::Perm>>,
1805 ) {
1806 broadcast use crate::specs::mm::frame::mapping::group_page_meta;
1807
1808 let item = Self::item_from_raw(pa, level, prop, perm);
1809 crate::specs::mm::frame::mapping::lemma_meta_to_paddr_biinjective(item.frame.ptr.addr());
1810 }
1811
1812 proof fn lemma_clone_ensures_concrete(
1813 item: Self::Item,
1814 pa: Paddr,
1815 old_regions: MetaRegionOwners,
1816 new_regions: MetaRegionOwners,
1817 res: Self::Item,
1818 ) {
1819 use crate::specs::mm::frame::mapping::{frame_to_index, meta_to_index};
1820 let frame_idx = meta_to_index(item.frame.ptr.addr());
1821 assert(frame_to_index(pa) == frame_idx);
1822 assert(<MappedItem as RCClone>::clone_ensures(item, old_regions, new_regions, res));
1823 assert(item.frame.clone_ensures(old_regions, new_regions, res.frame));
1824 }
1825
1826 proof fn lemma_clone_requires_concrete(
1827 item: Self::Item,
1828 pa: Paddr,
1829 level: PagingLevel,
1830 prop: PageProperty,
1831 regions: MetaRegionOwners,
1832 ) {
1833 use crate::specs::mm::frame::mapping::{frame_to_index, meta_to_index};
1834 broadcast use crate::specs::mm::frame::mapping::group_page_meta;
1835
1836 let perm = Self::item_into_raw(item).3;
1837 Self::lemma_item_from_raw_well_formed(pa, level, prop, perm);
1838 regions.lemma_contains_valid_frame_paddr(pa);
1839 }
1840
1841 proof fn lemma_page_table_config_constant_requirements() {
1842 use vstd::arithmetic::power2::{lemma2_to64, lemma2_to64_rest, lemma_pow2_adds};
1843 use vstd_extra::prelude::lemma_usize_pow2_ilog2;
1844
1845 lemma2_to64();
1846 lemma2_to64_rest();
1847 vstd::layout::unsigned_int_max_values();
1848 lemma_usize_pow2_ilog2(12);
1849 lemma_usize_pow2_ilog2(9);
1850 lemma_pow2_adds(9, 39);
1851 PageTableEntry::lemma_layout();
1852 Self::C::lemma_paging_consts_properties();
1853 assert(Self::LEADING_BITS_spec() == 0usize);
1854 }
1855}
1856
1857} // verus!