ostd/mm/page_table/node/mod.rs
1// SPDX-License-Identifier: MPL-2.0
2//! This module defines page table node abstractions and the handle.
3//!
4//! The page table node is also frequently referred to as a page table in many architectural
5//! documentations. It is essentially a page that contains page table entries (PTEs) that map
6//! to child page tables nodes or mapped pages.
7//!
8//! This module leverages the page metadata to manage the page table pages, which makes it
9//! easier to provide the following guarantees:
10//!
11//! The page table node is not freed when it is still in use by:
12//! - a parent page table node,
13//! - or a handle to a page table node,
14//! - or a processor.
15//!
16//! This is implemented by using a reference counter in the page metadata. If the above
17//! conditions are not met, the page table node is ensured to be freed upon dropping the last
18//! reference.
19//!
20//! One can acquire exclusive access to a page table node using merely the physical address of
21//! the page table node. This is implemented by a lock in the page metadata. Here the
22//! exclusiveness is only ensured for kernel code, and the processor's MMU is able to access the
23//! page table node while a lock is held. So the modification to the PTEs should be done after
24//! the initialization of the entity that the PTE points to. This is taken care in this module.
25//!
26mod child;
27mod entry;
28
29#[path = "../../../../specs/mm/page_table/node/child.rs"]
30mod child_specs;
31#[path = "../../../../specs/mm/page_table/node/entry.rs"]
32mod entry_specs;
33
34pub use crate::specs::mm::page_table::node::{entry_owners::*, owners::*};
35pub use child::*;
36pub use entry::*;
37
38use vstd::cell::pcell_maybe_uninit;
39use vstd::prelude::*;
40
41use vstd::atomic::PAtomicU8;
42use vstd::simple_pptr::{self, PPtr};
43
44use vstd_extra::array_ptr;
45use vstd_extra::cast_ptr::*;
46use vstd_extra::ghost_tree::*;
47use vstd_extra::ownership::*;
48
49use crate::mm::frame::allocator::FrameAllocOptions;
50use crate::mm::frame::meta::MetaSlot;
51use crate::mm::frame::{frame_to_index, AnyFrameMeta, Frame};
52use crate::mm::kspace::VMALLOC_BASE_VADDR;
53use crate::mm::page_table::*;
54use crate::mm::{kspace::LINEAR_MAPPING_BASE_VADDR, paddr_to_vaddr, Paddr, Vaddr};
55use crate::specs::arch::kspace::FRAME_METADATA_RANGE;
56use crate::specs::mm::frame::mapping::{meta_to_frame, META_SLOT_SIZE};
57use crate::specs::mm::frame::meta_owners::{
58 MetaSlotOwner, MetaSlotStorage, Metadata, REF_COUNT_UNUSED,
59};
60use crate::specs::mm::frame::meta_region_owners::MetaRegionOwners;
61use crate::specs::mm::page_table::node::owners::*;
62
63use core::{marker::PhantomData, ops::Deref, sync::atomic::Ordering};
64
65use super::nr_subpage_per_huge;
66
67use crate::{
68 mm::{
69 page_table::{load_pte, store_pte},
70 // FrameAllocOptions, Infallible,
71 // VmReader,
72 },
73 specs::task::InAtomicMode,
74};
75
76verus! {
77
78/// The metadata of any kinds of page table pages.
79/// Make sure the the generic parameters don't effect the memory layout.
80pub struct PageTablePageMeta<C: PageTableConfig> {
81 /// The number of valid PTEs. It is mutable if the lock is held.
82 pub nr_children: pcell_maybe_uninit::PCell<u16>,
83 /// If the page table is detached from its parent.
84 ///
85 /// A page table can be detached from its parent while still being accessed,
86 /// since we use a RCU scheme to recycle page tables. If this flag is set,
87 /// it means that the parent is recycling the page table.
88 pub stray: pcell_maybe_uninit::PCell<bool>,
89 /// The level of the page table page. A page table page cannot be
90 /// referenced by page tables of different levels.
91 pub level: PagingLevel,
92 /// The lock for the page table page.
93 pub lock: PAtomicU8,
94 pub _phantom: core::marker::PhantomData<C>,
95}
96
97/// A smart pointer to a page table node.
98///
99/// This smart pointer is an owner of a page table node. Thus creating and
100/// dropping it will affect the reference count of the page table node. If
101/// dropped it as the last reference, the page table node and subsequent
102/// children will be freed.
103///
104/// [`PageTableNode`] is read-only. To modify the page table node, lock and use
105/// [`PageTableGuard`].
106pub type PageTableNode<C> = Frame<PageTablePageMeta<C>>;
107
108impl<C: PageTableConfig> AnyFrameMeta for PageTablePageMeta<C> {
109 fn on_drop(&mut self) {
110 }
111
112 fn is_untyped(&self) -> bool {
113 false
114 }
115
116 uninterp spec fn vtable_ptr(&self) -> usize;
117}
118
119#[verus_verify]
120impl<C: PageTableConfig> PageTableNode<C> {
121 /// Gets the level of a page table node.
122 /// # Verified Properties
123 /// ## Preconditions
124 /// - The node must be well-formed, and the caller must provide a permission token for its metadata.
125 /// ## Postconditions
126 /// - Returns the level of the node.
127 /// ## Safety
128 /// - We require the caller to provide a permission token to ensure that this function is only called on a valid page table node.
129 #[verus_spec(
130 with Tracked(perm) : Tracked<&PointsTo<MetaSlot, Metadata<PageTablePageMeta<C>>>>
131 )]
132 pub(super) fn level(&self) -> PagingLevel
133 requires
134 self.ptr.addr() == perm.addr(),
135 self.ptr.addr() == perm.points_to.addr(),
136 perm.is_init(),
137 perm.wf(&perm.inner_perms),
138 returns
139 perm.value().metadata.level,
140 {
141 #[verus_spec(with Tracked(perm))]
142 let meta = self.meta();
143 meta.level
144 }
145
146 /// Allocates a new empty page table node.
147 #[verus_spec(res =>
148 with Tracked(parent_owner): Tracked<&mut NodeOwner<C>>,
149 Tracked(regions): Tracked<&mut MetaRegionOwners>,
150 Tracked(guards): Tracked<&Guards<'rcu, C>>,
151 Ghost(idx): Ghost<usize>,
152 -> owner: Tracked<OwnerSubtree<C>>
153 requires
154 1 <= level < NR_LEVELS,
155 idx < NR_ENTRIES,
156 old(regions).inv(),
157 old(parent_owner).inv(),
158 ensures
159 regions.inv(),
160 parent_owner.inv(),
161 allocated_empty_node_owner(owner@, level),
162 res.ptr.addr() == owner@.value.node.unwrap().meta_perm.addr(),
163 guards.unlocked(owner@.value.node.unwrap().meta_perm.addr()),
164 MetaSlot::get_from_unused_spec(meta_to_frame(owner@.value.node.unwrap().meta_perm.addr()), false, *old(regions), *regions),
165 old(regions).slots.contains_key(frame_to_index(meta_to_frame(owner@.value.node.unwrap().meta_perm.addr()))),
166 owner@.value.relate_region(*regions),
167 owner@.value.in_scope,
168 owner@.value.match_pte(C::E::new_pt_spec(meta_to_frame(owner@.value.node.unwrap().meta_perm.addr())), level as PagingLevel),
169 *parent_owner == old(parent_owner).set_children_perm(idx, C::E::new_pt_spec(meta_to_frame(owner@.value.node.unwrap().meta_perm.addr()))),
170 )]
171 #[verifier::external_body]
172 pub fn alloc<'rcu>(level: PagingLevel) -> Self {
173 let tracked entry_owner = EntryOwner::new_absent(TreePath::new(Seq::empty()), level);
174
175 let tracked mut owner = OwnerSubtree::<C>::new_val_tracked(entry_owner, level as nat);
176 let meta = PageTablePageMeta::new(level);
177 let mut frame = FrameAllocOptions::new();
178 frame.zeroed(true);
179 let allocated_frame = frame.alloc_frame_with(meta).expect(
180 "Failed to allocate a page table node",
181 );
182 // The allocated frame is zeroed. Make sure zero is absent PTE.
183 //debug_assert_eq!(C::E::new_absent().as_usize(), 0);
184
185 proof_with!(|= Tracked(owner));
186
187 allocated_frame
188 }/*
189 /// Activates the page table assuming it is a root page table.
190 ///
191 /// Here we ensure not dropping an active page table by making a
192 /// processor a page table owner. When activating a page table, the
193 /// reference count of the last activated page table is decremented.
194 /// And that of the current page table is incremented.
195 ///
196 /// # Safety
197 ///
198 /// The caller must ensure that the page table to be activated has
199 /// proper mappings for the kernel and has the correct const parameters
200 /// matching the current CPU.
201 ///
202 /// # Panics
203 ///
204 /// Only top-level page tables can be activated using this function.
205 pub(crate) unsafe fn activate(&self) {
206 use crate::{
207 arch::mm::{activate_page_table, current_page_table_paddr},
208 mm::page_prop::CachePolicy,
209 };
210
211 assert_eq!(self.level(), C::NR_LEVELS());
212
213 let last_activated_paddr = current_page_table_paddr();
214 if last_activated_paddr == self.start_paddr() {
215 return;
216 }
217
218 // SAFETY: The safety is upheld by the caller.
219 unsafe { activate_page_table(self.clone().into_raw(), CachePolicy::Writeback) };
220
221 // Restore and drop the last activated page table.
222 // SAFETY: The physical address is valid and points to a forgotten page table node.
223 drop(unsafe { Self::from_raw(last_activated_paddr) });
224 }
225
226 /// Activates the (root) page table assuming it is the first activation.
227 ///
228 /// It will not try dropping the last activate page table. It is the same
229 /// with [`Self::activate()`] in other senses.
230 pub(super) unsafe fn first_activate(&self) {
231 use crate::{arch::mm::activate_page_table, mm::page_prop::CachePolicy};
232
233 // SAFETY: The safety is upheld by the caller.
234 unsafe { activate_page_table(self.clone().into_raw(), CachePolicy::Writeback) };
235 }*/
236
237}
238
239#[verus_verify]
240impl<'a, C: PageTableConfig> PageTableNodeRef<'a, C> {
241 pub open spec fn locks_preserved_except<'rcu>(addr: usize, guards0: Guards<'rcu, C>, guards1: Guards<'rcu, C>) -> bool {
242 &&& OwnerSubtree::implies(CursorOwner::node_unlocked(guards0), CursorOwner::node_unlocked_except(guards1, addr))
243 &&& forall |i: usize| guards0.lock_held(i) ==> guards1.lock_held(i)
244 &&& forall |i: usize| guards0.unlocked(i) && i != addr ==> guards1.unlocked(i)
245 }
246
247 /// Locks the page table node.
248 ///
249 /// An atomic mode guard is required to
250 /// 1. prevent deadlocks;
251 /// 2. provide a lifetime (`'rcu`) that the nodes are guaranteed to outlive.
252 /// # Verification Design
253 /// As of when we verified this library, we didn't have a spin lock implementation, so we axiomatize
254 /// what happens when it's successful.
255 #[verifier::external_body]
256 #[verus_spec(res =>
257 with Tracked(owner): Tracked<&NodeOwner<C>>,
258 Tracked(guards): Tracked<&mut Guards<'rcu, C>>
259 -> guard_perm: Tracked<GuardPerm<'rcu, C>>
260 requires
261 self.inner@.invariants(*owner),
262 old(guards).unlocked(owner.meta_perm.addr()),
263 ensures
264 guards.lock_held(owner.meta_perm.addr()),
265 Self::locks_preserved_except(owner.meta_perm.addr(), *old(guards), *guards),
266 res.addr() == guard_perm@.addr(),
267 owner.relate_guard_perm(guard_perm@),
268 )]
269 pub fn lock<'rcu, A: InAtomicMode>(self, _guard: &'rcu A) -> PPtr<PageTableGuard<'rcu, C>> where 'a: 'rcu {
270 unimplemented!()
271 }
272
273 /// Creates a new [`PageTableGuard`] without checking if the page table lock is held.
274 ///
275 /// # Safety
276 ///
277 /// This function must be called if this task logically holds the lock.
278 ///
279 /// Calling this function when a guard is already created is undefined behavior
280 /// unless that guard was already forgotten.
281 #[verus_spec(res =>
282 with Tracked(owner): Tracked<&NodeOwner<C>>,
283 Tracked(guards): Tracked<&mut Guards<'rcu, C>>
284 -> guard_perm: Tracked<GuardPerm<'rcu, C>>
285 requires
286 self.inner@.invariants(*owner),
287 old(guards).unlocked(owner.meta_perm.addr()),
288 ensures
289 guards.lock_held(owner.meta_perm.addr()),
290 Self::locks_preserved_except(owner.meta_perm.addr(), *old(guards), *guards),
291 res.addr() == guard_perm@.addr(),
292 owner.relate_guard_perm(guard_perm@),
293 )]
294 pub fn make_guard_unchecked<'rcu, A: InAtomicMode>(self, _guard: &'rcu A) -> PPtr<PageTableGuard<'rcu, C>> where 'a: 'rcu {
295 let guard = PageTableGuard { inner: self };
296 let (ptr, guard_perm) = PPtr::<PageTableGuard<C>>::new(guard);
297
298 proof {
299 let ghost guards0 = *guards;
300 guards.guards.tracked_insert(owner.meta_perm.addr(), None);
301 assert(owner.relate_guard_perm(guard_perm@));
302
303 assert(forall|other: EntryOwner<C>, path: TreePath<NR_ENTRIES>|
304 owner.inv() && CursorOwner::node_unlocked(guards0)(other, path)
305 ==> #[trigger] CursorOwner::node_unlocked_except(
306 *guards,
307 owner.meta_perm.addr(),
308 )(other, path));
309 }
310
311 proof_with!{|= guard_perm}
312 ptr
313 }
314}
315
316//}
317impl<'rcu, C: PageTableConfig> PageTableGuard<'rcu, C> {
318 /// Borrows an entry in the node at a given index.
319 ///
320 /// # Panics
321 ///
322 /// Panics if the index is not within the bound of
323 /// [`nr_subpage_per_huge<C>`].
324 #[verus_spec(res =>
325 with Tracked(owner): Tracked<&NodeOwner<C>>,
326 Tracked(child_owner): Tracked<&EntryOwner<C>>,
327 Tracked(guard_perm): Tracked<&GuardPerm<'rcu, C>>,
328 )]
329 pub fn entry(guard: PPtr<Self>, idx: usize) -> (res: Entry<'rcu, C>)
330 requires
331 owner.inv(),
332 !child_owner.in_scope,
333 child_owner.inv(),
334 owner.relate_guard_perm(*guard_perm),
335 guard_perm.addr() == guard.addr(),
336 child_owner.match_pte(
337 owner.children_perm.value()[idx as int],
338 child_owner.parent_level,
339 ),
340 // Panic condition
341 idx < NR_ENTRIES,
342 ensures
343 res.wf(*child_owner),
344 res.node.addr() == guard_perm.addr(),
345 res.idx == idx,
346 {
347 // assert!(idx < nr_subpage_per_huge::<C>());
348 // SAFETY: The index is within the bound.
349 #[verus_spec(with Tracked(child_owner), Tracked(owner), Tracked(guard_perm))]
350 Entry::new_at(guard, idx);
351 }
352
353 /// Gets the number of valid PTEs in a page table node.
354 /// # Verified Properties
355 /// ## Preconditions
356 /// - The node must be well-formed.
357 /// ## Postconditions
358 /// - Returns the number of valid PTEs in the node.
359 /// ## Safety
360 /// - We require the caller to provide a permission token to ensure that this function is only called on a valid page table node.
361 #[verus_spec(
362 with Tracked(owner) : Tracked<&NodeOwner<C>>
363 )]
364 pub fn nr_children(&self) -> (nr: u16)
365 requires
366 // Node invariants: owner well-formedness and node-owner consistency
367 self.inner.inner@.invariants(*owner),
368 returns
369 owner.meta_own.nr_children.value(),
370 {
371 // SAFETY: The lock is held so we have an exclusive access.
372 #[verus_spec(with Tracked(&owner.meta_perm))]
373 let meta = self.meta();
374
375 *meta.nr_children.borrow(Tracked(&owner.meta_own.nr_children))
376 }
377
378 /// Returns if the page table node is detached from its parent.
379 #[verus_spec(
380 with Tracked(meta_perm): Tracked<&'a PointsTo<MetaSlot, Metadata<PageTablePageMeta<C>>>>
381 )]
382 pub(super) fn stray_mut<'a>(&'a mut self) -> (res: &'a pcell_maybe_uninit::PCell<bool>)
383 requires
384 old(self).inner.inner@.ptr.addr() == meta_perm.addr(),
385 old(self).inner.inner@.ptr.addr() == meta_perm.points_to.addr(),
386 meta_perm.is_init(),
387 meta_perm.wf(&meta_perm.inner_perms),
388 ensures
389 res.id() == meta_perm.value().metadata.stray.id(),
390 *self == *old(self),
391 {
392 // SAFETY: The lock is held so we have an exclusive access.
393 #[verus_spec(with Tracked(meta_perm))]
394 let meta = self.meta();
395 &meta.stray
396 }
397
398 /// Reads a non-owning PTE at the given index.
399 ///
400 /// A non-owning PTE means that it does not account for a reference count
401 /// of the a page if the PTE points to a page. The original PTE still owns
402 /// the child page.
403 ///
404 /// # Safety
405 ///
406 /// The caller must ensure that the index is within the bound.
407 #[verus_spec(
408 with Tracked(owner): Tracked<&NodeOwner<C>>
409 )]
410 pub fn read_pte(&self, idx: usize) -> (pte: C::E)
411 requires
412 self.inner.inner@.invariants(*owner),
413 idx < NR_ENTRIES,
414 ensures
415 pte == owner.children_perm.value()[idx as int],
416 {
417 // debug_assert!(idx < nr_subpage_per_huge::<C>());
418 let ptr = vstd_extra::array_ptr::ArrayPtr::<C::E, NR_ENTRIES>::from_addr(
419 paddr_to_vaddr(
420 #[verus_spec(with Tracked(&owner.meta_perm.points_to))]
421 self.start_paddr(),
422 ),
423 );
424
425 // SAFETY:
426 // - The page table node is alive. The index is inside the bound, so the page table entry is valid.
427 // - All page table entries are aligned and accessed with atomic operations only.
428 #[verus_spec(with Tracked(&owner.children_perm))]
429 load_pte(ptr.add(idx), Ordering::Relaxed)
430 }
431
432 /// Writes a page table entry at a given index.
433 ///
434 /// This operation will leak the old child if the old PTE is present.
435 ///
436 /// # Safety
437 ///
438 /// The caller must ensure that:
439 /// 1. The index must be within the bound;
440 /// 2. The PTE must represent a valid [`Child`] whose level is compatible
441 /// with the page table node.
442 /// 3. The page table node will have the ownership of the [`Child`]
443 /// after this method.
444 #[verus_spec(
445 with Tracked(owner): Tracked<&mut NodeOwner<C>>
446 )]
447 pub fn write_pte(&mut self, idx: usize, pte: C::E)
448 requires
449 old(self).inner.inner@.invariants(*old(owner)),
450 idx < NR_ENTRIES,
451 ensures
452 owner.inv(),
453 owner.meta_perm.addr() == old(owner).meta_perm.addr(),
454 owner.level == old(owner).level,
455 owner.meta_own == old(owner).meta_own,
456 owner.meta_perm.points_to == old(owner).meta_perm.points_to,
457 owner.children_perm.value() == old(owner).children_perm.value().update(idx as int, pte),
458 *self == *old(self),
459 {
460 // debug_assert!(idx < nr_subpage_per_huge::<C>());
461 #[verusfmt::skip]
462 let ptr = vstd_extra::array_ptr::ArrayPtr::<C::E, NR_ENTRIES>::from_addr(
463 paddr_to_vaddr(
464 #[verus_spec(with Tracked(&owner.meta_perm.points_to))]
465 self.start_paddr()
466 ),
467 );
468
469 // SAFETY:
470 // - The page table node is alive. The index is inside the bound, so the page table entry is valid.
471 // - All page table entries are aligned and accessed with atomic operations only.
472 #[verus_spec(with Tracked(&mut owner.children_perm))]
473 store_pte(ptr.add(idx), pte, Ordering::Release)
474 }
475
476 /// Gets the mutable reference to the number of valid PTEs in the node.
477 #[verus_spec(
478 with Tracked(meta_perm): Tracked<&'a PointsTo<MetaSlot, Metadata<PageTablePageMeta<C>>>>
479 )]
480 fn nr_children_mut<'a>(&'a mut self) -> (res: &'a pcell_maybe_uninit::PCell<u16>)
481 requires
482 old(self).inner.inner@.ptr.addr() == meta_perm.addr(),
483 old(self).inner.inner@.ptr.addr() == meta_perm.points_to.addr(),
484 meta_perm.is_init(),
485 meta_perm.wf(&meta_perm.inner_perms),
486 ensures
487 res.id() == meta_perm.value().metadata.nr_children.id(),
488 *self == *old(self),
489 {
490 // SAFETY: The lock is held so we have an exclusive access.
491 #[verus_spec(with Tracked(meta_perm))]
492 let meta = self.meta();
493 &meta.nr_children
494 }
495}
496
497/*impl<C: PageTableConfig> Drop for PageTableGuard<'_, C> {
498 fn drop(&mut self) {
499 self.inner.meta().lock.store(0, Ordering::Release);
500 }
501}*/
502
503impl<C: PageTableConfig> PageTablePageMeta<C> {
504 pub fn new(level: PagingLevel) -> Self {
505 Self {
506 nr_children: pcell_maybe_uninit::PCell::new(0).0,
507 stray: pcell_maybe_uninit::PCell::new(false).0,
508 level,
509 lock: PAtomicU8::new(0).0,
510 _phantom: PhantomData,
511 }
512 }
513}
514
515} // verus!
516/* TODO: Come back after VMReader
517// FIXME: The safe APIs in the `page_table/node` module allow `Child::Frame`s with
518// arbitrary addresses to be stored in the page table nodes. Therefore, they may not
519// be valid `C::Item`s. The soundness of the following `on_drop` implementation must
520// be reasoned in conjunction with the `page_table/cursor` implementation.
521unsafe impl<C: PageTableConfig> AnyFrameMeta for PageTablePageMeta<C> {
522 fn on_drop(&mut self, reader: &mut VmReader<Infallible>) {
523 let nr_children = self.nr_children.get_mut();
524 if *nr_children == 0 {
525 return;
526 }
527
528 let level = self.level;
529 let range = if level == C::NR_LEVELS() {
530 C::TOP_LEVEL_INDEX_RANGE.clone()
531 } else {
532 0..nr_subpage_per_huge::<C>()
533 };
534
535 // Drop the children.
536 reader.skip(range.start * size_of::<C::E>());
537 for _ in range {
538 // Non-atomic read is OK because we have mutable access.
539 let pte = reader.read_once::<C::E>().unwrap();
540 if pte.is_present() {
541 let paddr = pte.paddr();
542 // As a fast path, we can ensure that the type of the child frame
543 // is `Self` if the PTE points to a child page table. Then we don't
544 // need to check the vtable for the drop method.
545 if !pte.is_last(level) {
546 // SAFETY: The PTE points to a page table node. The ownership
547 // of the child is transferred to the child then dropped.
548 drop(unsafe { Frame::<Self>::from_raw(paddr) });
549 } else {
550 // SAFETY: The PTE points to a mapped item. The ownership
551 // of the item is transferred here then dropped.
552 drop(unsafe { C::item_from_raw(paddr, level, pte.prop()) });
553 }
554 }
555 }
556 }
557}*/