Skip to content

Commit 0393c4d

Browse files
SNoAndhiroki-chen
andauthored
Node (and Entry/Child) documentation (#341)
* `ghost` into `tracked` for API compatibility * Support for `Once` * Update * vmio: initial layout * vmio: fix two issues with verus updates * stage * Fix `replace` to use parent node owner * Switch `FramePerm` to `MetaPerm` * Wrapper around `ManuallyDrop::new()` for cases when it is taking on the role of `into_raw` * vmio: more APIs * Fixup remaining proofs after `NeverDrop` change * Closing various unneessary admits in `cursor.rs` * Starting virtual pointer work * More cursor work * minor updates * Fix merge * Fix a verification error * Remove a file * `replace_cur_entry` * Update dv * Starting on virt_ptr * Migrating out of aster_common: vm_space * Migrate from aster_common: io, untyped, pod * Move specs into main ostd tree * Move `aster_common` into `ostd`, just in its own directory for now. * start vmspace stuff * Virtual pointer * Specification for memcpy * Move `page_table/cursor` out of legacy `aster_common` directory. * Moved `page_table/node` from legacy `aster_common` directory * Moved `page_table` from legacy `aster_common` * Moving cursor ownership stuff into `specs` * Ready to work on `cursor_steps` lemmas * Remove one lemma that shouldn't hold * Move owner/view files into specs directory * Migrate `frame` out of legacy `aster_common` * Eliminated `extern_const` declarations because we can just use consts now that `aster_common` is gone * Finished merging the legacy `aster_common` stuff over, some proofs did break in the process * Some proofs related to consts now need manual intervention; also fixed some consts that got changed somehow. * Revert "Some proofs related to consts now need manual intervention; also fixed some consts that got changed somehow." This reverts commit 82141a9. * Revert "Eliminated `extern_const` declarations because we can just use consts now that `aster_common` is gone" This reverts commit 5c0fa3d. Turns out `extern_const` is much easier to prove with even if it's not necessary. * merge * merge commits * minor tweaks * Move more proof definitions into `specs` * Admit some of the active proofs to push to public repo * Reconfigured `PageTableOwner` with new `TreeNodeView` implementation * CursorOwner changes * New structure for `move_forward` proof * Move `move_forward` work * stage * vmspace: tried experiment * Working on `map`, `unmap`, `split...` * Comment out manual trigger so `do_inc_index` will verify * Remove empty `aster_common` file * Remove the last trace of `aster_common`, yay! * Formatting * Cursor progress * Update vm to use `VirtPtr` * suppress warnings * fix some proofs * improve IO owner invariants and overlap specs * lemma for split * refine IO invariants and complete missing proofs * refine some API designs * prove `dispose_writer` * more proofs * Removing admits * Beginnings of memory model work * Move virtual pointer stuff to `virt_mem` in `ostd`, because it is really part of the ostd verification rather than a generic library * sync dv * fix broken proofs * verify `dispose_reader` * Use `NeverDrop` in place of `ManuallyDrop` * Starting on `Node::alloc` * refactor the tracked design * Getting guard_perm out of EntryOwner, phase 1 * Remove `guard_perm` from `EntryOwner` phase 2 * Finished removing `guard_perm` from `NodeOwner` and removing the extraneous `NodeEntryOwner` wrapper. * Re-verifying things that broke due to moving `guard_perm` * Reverifiying things that changed due to moving `guard_perm` * Reducing admits in cursor * Removing admits * More admits * More admits * Relate `pte` with `EntryOwner` (equivalent to how `Entry` is related * Structure of `split_if_mapped_huge` * Change `virt_mem_newer` to use separate frames * refine some ill-formed specs and proofs * Add the operations to manipulate the page table and TLB, assuming that the `GlobalMemView` is complete (no floating `MemView` objects) * update * `protect_next` uncommented * Some proofs and trigger updates * Working on virtual memory example; includes changes to `virt_mem_newer` * Example with mapping a page * Split up pre- and post-conditions of `map` for readability * Tweaking `map` doc example * Working on `guard_perm` assumptions. * update * revert toolchain * prove `activate_writer` * Assorted admits * `CursorView` updated for new specs * Working on some fiddling conditions about locked nodes * Fix breaking change in `layout` * Update dv * Relating page table nodes to their regions * Fix the performance issue by moving the branches of `map` out into their own functions. * Uniqueness of pt guards, stage 1: pt metadata carries correct path * Added predicate map over trees to deal with some challenging reasoning about all nodes * Added predicate map over trees * Working more on inline docs for `vmspace` specs, with hyperlinks to `page_table_cursor_specs.rs` * Big step toward using Set<Mapping> everywhere * Clearing admits * finish vm io * fix warnings and fmt * fix triggers * Admitpocalypse * Assorted fixes * `split_if_mapped_huge` * `query` using view spec * Fixed issue with the `map` top level proof. Resulted in adding some admits that will need to get fixed. * Page life cycle criteria for nodes * Mostly work on `jump` admits. * Add missing `res` * Finalize non-duplication theorem, but still need to figure out how best to present it * First stab at the top-level memory document * Version numbers in `Cargo.lock` * Top-level theorems: now with more TLB * Delete old `virt_mem.rs` * Simplifying `vm_space` specs * Documented `linked_list` * Fixed some lingering admits in `linked_list` * Working on `vm_space` documentation * Assorted fixes * More in-depth tlb modeling, for `unmap` * Beginning to separate out safety conditions from correctness * `UniqueFrame` documentation, shifting preconditions to separate safety from correctness * Working on docs and reorganizing `vm_space` * Finished `vm_space` documentation (at least for now) * Make `vm_space_specs` public and not re-exported, for cleaner docs. * Bullet points for multiple conditions in docs * Fixed `MetaSlot` vs. `MetaSlotStorage` abstraction issue. `MetaSlotStorage` can be modeled as a cell again! * `Repr` now supports underlying datatypes with their own permissions, so it's able to realistically apply to `MetaSlot`s now. * documentation and pretty * Constructing inner perms from meta perm * Documentation of `node/mod.rs` * Using the new ReprPtr more widely * Rename `NeverDrop` to `ManuallyDrop` and use it in all cases * Eliminated `dropped_slots` * Documented `meta` except the "drop" related functions * Done documenting `meta.rs` * Simplifying preconitions in `Frame` * prettier and triggers * `PCell` migration * documenting `vm_space` * documenting the lifecycle of vmspaces * Working on `Frame<M>` documentation * Finish `frame/mod.rs` documentation * Remove `vstd_extra::ManuallyDrop` and some duplicate imports * Remove duplicat imports * Fine-tune some documentation * API update for `VmSpace` * prettier * fix a typo * sync dv * sync dv * More documentation for `VmSpace` * Inline docs for `Child` * Reorganizing `Entry` pre- and post-conditions, reducing extraneous assertions. * Add postcondition to `alloc` * add doc for vm_space utilities * fix errors * Still cleaning up node preconds * Node documentation. Also fixed a problem with allocating nodes during map * Document `protect` as welll --------- Co-authored-by: Hiroki <haobin.chen@certik.com>
1 parent 15d08f8 commit 0393c4d

20 files changed

Lines changed: 1043 additions & 859 deletions

File tree

ostd/specs/arch/x86_64/page_table_entry.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,4 +319,12 @@ impl PageTableEntryTrait for PageTableEntry {
319319

320320
}
321321

322+
impl PageTableEntry {
323+
/// Absent (zero) PTE has well-formed paddr for match_pte.
324+
pub axiom fn absent_pte_paddr_ok()
325+
ensures
326+
Self::new_absent_spec().paddr_spec() % crate::specs::arch::mm::PAGE_SIZE == 0,
327+
Self::new_absent_spec().paddr_spec() < crate::specs::arch::mm::MAX_PADDR;
328+
}
329+
322330
}

ostd/specs/mm/frame/frame_specs.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ impl<'a, M: AnyFrameMeta> Frame<M> {
2323
&&& regions.slot_owners[frame_to_index(paddr)].raw_count == 1
2424
&&& regions.slot_owners[frame_to_index(paddr)].self_addr == frame_to_meta(paddr)
2525
&&& has_safe_slot(paddr) // TODO: this should actually imply the first condition
26-
&&& !regions.slots.contains_key(frame_to_index(paddr)) // Whomever called `into_raw` should hold the permission.
2726
&&& regions.inv()
2827
}
2928

@@ -46,6 +45,9 @@ impl<'a, M: AnyFrameMeta> Frame<M> {
4645
&&& forall|i: usize|
4746
#![trigger new_regions.slot_owners[i], old_regions.slot_owners[i]]
4847
i != frame_to_index(paddr) ==> new_regions.slot_owners[i] == old_regions.slot_owners[i]
48+
&&& forall|i: usize|
49+
i != frame_to_index(paddr) ==>
50+
new_regions.slots.contains_key(i) == old_regions.slots.contains_key(i)
4951
&&& r.ptr.addr() == frame_to_meta(paddr)
5052
&&& r.paddr() == paddr
5153
}

ostd/specs/mm/page_table/cursor/owners.rs

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -390,10 +390,6 @@ impl<'rcu, C: PageTableConfig> Inv for CursorOwner<'rcu, C> {
390390
&&& self.va.index[0] == self.continuations[0].idx
391391
&&& self.continuations[0].guard_perm.value().inner.inner@.ptr.addr() !=
392392
self.continuations[1].guard_perm.value().inner.inner@.ptr.addr()
393-
&&& self.continuations[0].guard_perm.value().inner.inner@.ptr.addr() !=
394-
self.continuations[2].guard_perm.value().inner.inner@.ptr.addr()
395-
&&& self.continuations[0].guard_perm.value().inner.inner@.ptr.addr() !=
396-
self.continuations[3].guard_perm.value().inner.inner@.ptr.addr()
397393
}
398394
}
399395
}
@@ -907,6 +903,23 @@ impl<'rcu, C: PageTableConfig> CursorOwner<'rcu, C> {
907903
self.in_locked_range(),
908904
{ admit() }
909905

906+
/// When in_locked_range and !popped_too_high, level < guard_level (from inv), hence level < NR_LEVELS.
907+
pub proof fn in_locked_range_level_lt_nr_levels(self)
908+
requires
909+
self.inv(),
910+
self.in_locked_range(),
911+
!self.popped_too_high,
912+
ensures
913+
self.level < NR_LEVELS,
914+
{
915+
assert(self.above_locked_range() ==> self.va.to_vaddr() >= self.locked_range().end);
916+
assert(self.in_locked_range() ==> self.va.to_vaddr() < self.locked_range().end);
917+
assert(self.in_locked_range() ==> !self.above_locked_range());
918+
assert(!self.popped_too_high ==> self.level < self.guard_level || self.above_locked_range());
919+
assert(self.level < self.guard_level);
920+
assert(self.guard_level <= NR_LEVELS);
921+
}
922+
910923
/// If in_locked_range() and level < guard_level, then:
911924
/// - va.align_down(page_size(level+1)) >= locked_range().start
912925
/// - va.align_down(page_size(level+1)) + page_size(level+1) <= locked_range().end

ostd/specs/mm/page_table/cursor/page_table_cursor_specs.rs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use crate::specs::arch::mm::{NR_ENTRIES, NR_LEVELS, PAGE_SIZE};
1212
use crate::specs::arch::paging_consts::PagingConsts;
1313
use crate::specs::mm::page_table::cursor::owners::*;
1414
use crate::specs::mm::page_table::*;
15+
use vstd_extra::arithmetic::*;
1516

1617
use core::ops::Range;
1718

@@ -61,6 +62,14 @@ impl<C: PageTableConfig> CursorView<C> {
6162
self.query_mapping().va_range
6263
}
6364

65+
/// The VA range of the current slot when mapping a page of the given size.
66+
/// Works for both present and absent mappings: when present, equals query_range() for
67+
/// a mapping of that size; when absent, returns the aligned range that would be mapped.
68+
pub open spec fn cur_slot_range(self, size: usize) -> Range<Vaddr> {
69+
let start = nat_align_down(self.cur_va as nat, size as nat) as Vaddr;
70+
start..(start as nat + size as nat) as Vaddr
71+
}
72+
6473
/// This predicate specifies the behavior of the `query` method. It states that the current item
6574
/// in the page table matches the given item, mapped at the resulting virtual address range.
6675
pub open spec fn query_item_spec(self, item: C::Item) -> Option<Range<Vaddr>>
@@ -175,7 +184,7 @@ impl<C: PageTableConfig> CursorView<C> {
175184
/// a new large mapping.
176185
pub open spec fn map_spec(self, paddr: Paddr, size: usize, prop: PageProperty) -> Self {
177186
let new = Mapping {
178-
va_range: self.query_range(),
187+
va_range: self.cur_slot_range(size),
179188
pa_range: paddr..(paddr + size) as usize,
180189
page_size: size,
181190
property: prop,
@@ -191,7 +200,7 @@ impl<C: PageTableConfig> CursorView<C> {
191200
///
192201
pub open spec fn map_simple(self, paddr: Paddr, size: usize, prop: PageProperty) -> Self {
193202
let new = Mapping {
194-
va_range: self.query_range(),
203+
va_range: self.cur_slot_range(size),
195204
pa_range: paddr..(paddr + size) as usize,
196205
page_size: size,
197206
property: prop,
Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
use vstd::prelude::*;
2+
3+
use vstd_extra::ownership::*;
4+
5+
use crate::mm::frame::*;
6+
use crate::mm::page_prop::PageProperty;
7+
use crate::mm::page_table::*;
8+
use crate::mm::{Paddr, PagingConstsTrait, PagingLevel, Vaddr};
9+
use crate::specs::arch::mm::{NR_ENTRIES, NR_LEVELS, PAGE_SIZE};
10+
use crate::specs::arch::paging_consts::PagingConsts;
11+
use crate::specs::mm::frame::meta_owners::MetaSlotOwner;
12+
use crate::specs::mm::frame::meta_region_owners::MetaRegionOwners;
13+
14+
verus! {
15+
16+
impl<C: PageTableConfig> OwnerOf for Child<C> {
17+
type Owner = EntryOwner<C>;
18+
19+
open spec fn wf(self, owner: Self::Owner) -> bool {
20+
match self {
21+
Self::PageTable(node) => {
22+
&&& owner.is_node()
23+
&&& node.ptr.addr() == owner.node.unwrap().meta_perm.addr()
24+
&&& node.index() == frame_to_index(meta_to_frame(node.ptr.addr()))
25+
},
26+
Self::Frame(paddr, level, prop) => {
27+
&&& owner.is_frame()
28+
&&& owner.frame.unwrap().mapped_pa == paddr
29+
&&& owner.frame.unwrap().prop == prop
30+
&&& level == owner.parent_level
31+
},
32+
Self::None => owner.is_absent(),
33+
}
34+
}
35+
}
36+
37+
38+
impl<'a, C: PageTableConfig> OwnerOf for ChildRef<'a, C> {
39+
type Owner = EntryOwner<C>;
40+
41+
open spec fn wf(self, owner: Self::Owner) -> bool {
42+
match self {
43+
Self::PageTable(node) => {
44+
&&& owner.is_node()
45+
&&& node.inner.0.ptr.addr() == owner.node.unwrap().meta_perm.addr()
46+
},
47+
Self::Frame(paddr, level, prop) => {
48+
&&& owner.is_frame()
49+
&&& owner.frame.unwrap().mapped_pa == paddr
50+
&&& owner.frame.unwrap().prop == prop
51+
},
52+
Self::None => owner.is_absent(),
53+
}
54+
}
55+
}
56+
57+
impl<C: PageTableConfig> Child<C> {
58+
59+
pub open spec fn get_node(self) -> Option<PageTableNode<C>> {
60+
match self {
61+
Self::PageTable(node) => Some(node),
62+
_ => None,
63+
}
64+
}
65+
66+
pub open spec fn get_frame_tuple(self) -> Option<(Paddr, PagingLevel, PageProperty)> {
67+
match self {
68+
Self::Frame(paddr, level, prop) => Some((paddr, level, prop)),
69+
_ => None,
70+
}
71+
}
72+
73+
pub open spec fn into_pte_frame_spec(self, tuple: (Paddr, PagingLevel, PageProperty)) -> C::E {
74+
let (paddr, level, prop) = tuple;
75+
C::E::new_page_spec(paddr, level, prop)
76+
}
77+
78+
79+
pub open spec fn into_pte_none_spec(self) -> C::E {
80+
C::E::new_absent_spec()
81+
}
82+
83+
84+
pub open spec fn from_pte_spec(pte: C::E, level: PagingLevel, regions: MetaRegionOwners) -> Self {
85+
if !pte.is_present() {
86+
Self::None
87+
} else if pte.is_last(level) {
88+
Self::Frame(pte.paddr(), level, pte.prop())
89+
} else {
90+
Self::PageTable(PageTableNode::from_raw_spec(pte.paddr()))
91+
}
92+
}
93+
94+
pub open spec fn from_pte_frame_spec(pte: C::E, level: PagingLevel) -> Self {
95+
Self::Frame(pte.paddr(), level, pte.prop())
96+
}
97+
98+
99+
pub open spec fn from_pte_pt_spec(paddr: Paddr, regions: MetaRegionOwners) -> Self {
100+
Self::PageTable(PageTableNode::from_raw_spec(paddr))
101+
}
102+
103+
pub open spec fn invariants(self, owner: EntryOwner<C>, regions: MetaRegionOwners) -> bool {
104+
&&& owner.inv()
105+
&&& regions.inv()
106+
&&& self.wf(owner)
107+
&&& owner.relate_region(regions)
108+
&&& owner.in_scope
109+
}
110+
}
111+
112+
impl<C: PageTableConfig> ChildRef<'_, C> {
113+
pub open spec fn invariants(self, owner: EntryOwner<C>, regions: MetaRegionOwners) -> bool {
114+
&&& owner.inv()
115+
&&& regions.inv()
116+
&&& self.wf(owner)
117+
&&& owner.relate_region(regions)
118+
&&& !owner.in_scope
119+
}
120+
}
121+
122+
impl<C: PageTableConfig> EntryOwner<C> {
123+
124+
pub open spec fn from_pte_regions_spec(self, regions: MetaRegionOwners) -> MetaRegionOwners {
125+
if self.is_node() {
126+
let index = frame_to_index(self.meta_slot_paddr().unwrap());
127+
let old_slot = regions.slot_owners[index];
128+
let new_slot = MetaSlotOwner {
129+
raw_count: 0usize,
130+
..old_slot
131+
};
132+
MetaRegionOwners {
133+
slots: regions.slots.insert(index, self.node.unwrap().meta_perm.points_to),
134+
slot_owners: regions.slot_owners.insert(index, new_slot),
135+
}
136+
} else {
137+
regions
138+
}
139+
}
140+
141+
pub open spec fn into_pte_regions_spec(self, regions: MetaRegionOwners) -> MetaRegionOwners {
142+
if self.is_node() {
143+
let index = frame_to_index(self.meta_slot_paddr().unwrap());
144+
let old_slot = regions.slot_owners[index];
145+
let new_slot = MetaSlotOwner {
146+
raw_count: (old_slot.raw_count + 1) as usize,
147+
..old_slot
148+
};
149+
MetaRegionOwners {
150+
slots: regions.slots,
151+
slot_owners: regions.slot_owners.insert(index, new_slot),
152+
..regions
153+
}
154+
} else {
155+
regions
156+
}
157+
}
158+
159+
pub open spec fn into_pte_owner_spec(self) -> EntryOwner<C> {
160+
EntryOwner {
161+
in_scope: false,
162+
..self
163+
}
164+
}
165+
166+
pub open spec fn from_pte_owner_spec(self) -> EntryOwner<C> {
167+
EntryOwner {
168+
in_scope: true,
169+
..self
170+
}
171+
}
172+
173+
/// This is equivalent to the other `invariants` relations, combining the `inv` predicates for each
174+
/// object and the well-formedness relations between them.
175+
pub open spec fn pte_invariants(self, pte: C::E, regions: MetaRegionOwners) -> bool {
176+
&&& self.inv()
177+
&&& regions.inv()
178+
&&& self.match_pte(pte, self.parent_level)
179+
&&& self.relate_region(regions)
180+
&&& !self.in_scope
181+
}
182+
183+
}
184+
185+
} // verus!

0 commit comments

Comments
 (0)