Skip to main content

ostd/specs/mm/embedding/
kvirt_store.rs

1//! Kernel-side one-step-soundness store for the kernel virtual area API
2//! ([`crate::mm::kspace::kvirt_area::KVirtArea`]) — the *kernel* analog of
3//! [`super::VmStore`].
4//!
5//! # Why a separate, kernel store
6//!
7//! [`super::VmStore`] models a caller of the **user** `VmSpace` API: it
8//! holds a `Map<VmSpaceId, VmSpaceOwner>`, each wrapping an
9//! `OwnerSubtree<UserPtConfig>`, plus user-space cursors
10//! (`CursorOwner<'rcu, UserPtConfig>`). `KVirtArea` instead maps frames
11//! into the single, global **kernel** page table
12//! ([`crate::mm::kspace::KERNEL_PAGE_TABLE`], a `KernelPtConfig` table),
13//! so the kernel store differs in three ways:
14//!
15//!   - **one** page table, not a map: a single
16//!     [`PageTableOwner<KernelPtConfig>`] (`kernel_pt`) rather than
17//!     `Map<VmSpaceId, _>`;
18//!   - it tracks the allocated **kernel virtual areas**
19//!     (`kvirt_areas`) — each `KVirtArea` is just a `Range<Vaddr>`
20//!     handle, since its `KVirtAreaOwner` is consumed into the page table
21//!     at construction (`map_frames`/`map_untracked_frames` take it by
22//!     value);
23//!   - cursors and owners are over `KernelPtConfig`, not `UserPtConfig`
24//!     (added when the mutating ops land).
25//!
26//! # Roadmap
27//!
28//! Landed: the store + invariant. Next: `step_query` (read-only) over
29//! `kernel_pt`, then `map_frames` / `map_untracked_frames` (the
30//! mapping-creating ops; their exec ensures may need strengthening
31//! first). Accounting (the `rc == H + P + cover_count` equation
32//! [`super::VmStore`] carries) is deferred — `KVirtArea` is
33//! mapping-focused, not reference-count-focused.
34use core::ops::Range;
35
36use vstd::prelude::*;
37use vstd_extra::prelude::Inv;
38
39use crate::specs::{
40    arch::PAGE_SIZE,
41    mm::{
42        frame::meta_region_owners::MetaRegionOwners,
43        page_table::{node::Guards, *},
44    },
45};
46
47use crate::mm::{
48    Vaddr,
49    kspace::{
50        FRAME_METADATA_BASE_VADDR, KERNEL_BASE_VADDR, KernelPtConfig, MappedItem,
51        kvirt_area::{KVirtArea, KVirtAreaOwner},
52    },
53    page_table::PageTableGuard,
54};
55
56verus! {
57
58/// Logical identifier for an allocated [`KVirtArea`] in the store.
59pub type KVirtId = int;
60
61/// One-step-soundness store for the kernel virtual area API. Holds the
62/// shared `regions`, the single global kernel page table `kernel_pt`,
63/// and the set of allocated kernel virtual areas (each a `Range<Vaddr>`
64/// handle — see the module docs).
65///
66/// The kernel analog of [`super::VmStore`]; see the module documentation
67/// for how it differs (one global PT vs. a map of user `VmSpace`s).
68pub tracked struct KVmStore {
69    pub regions: MetaRegionOwners,
70    pub kernel_pt: PageTableOwner<KernelPtConfig>,
71    pub kvirt_areas: Map<KVirtId, Range<Vaddr>>,
72}
73
74impl KVmStore {
75    /// The store's top-level invariant.
76    ///
77    /// Mirrors the page-table-soundness portion of
78    /// [`super::VmStore::structural_inv`] (the kernel PT relates to the
79    /// region map via [`PageTableOwner::metaregion_sound`]) and adds the
80    /// per-area range well-formedness that [`KVirtArea::inv`] enforces at
81    /// the handle level (within the kernel VMALLOC bounds, page-aligned,
82    /// ordered).
83    pub open spec fn inv(self) -> bool {
84        &&& self.regions.inv()
85        &&& self.kernel_pt.inv()
86        // The global kernel page table relates to the shared region map —
87        // every present page-table node sits at a sound region slot, just
88        // as `VmStore::inv` requires of each cursor's owner.
89        &&& self.kernel_pt.metaregion_sound(
90            self.regions,
91        )
92        // Each allocated area is a well-formed kernel virtual range:
93        // within `[KERNEL_BASE_VADDR, FRAME_METADATA_BASE_VADDR]` (the
94        // VMALLOC region the real allocator draws from), page-aligned,
95        // and ordered — exactly the handle-level facts `KVirtArea::inv`
96        // guarantees at construction and every op preserves.
97        &&& forall|id: KVirtId| #[trigger]
98            self.kvirt_areas.dom().contains(id) ==> {
99                let r = self.kvirt_areas[id];
100                &&& KERNEL_BASE_VADDR <= r.start
101                &&& r.end <= FRAME_METADATA_BASE_VADDR
102                &&& r.start % PAGE_SIZE == 0
103                &&& r.end % PAGE_SIZE == 0
104                &&& r.start <= r.end
105            }
106    }
107}
108
109} // verus!