ostd/specs/mm/page_table/cursor/
cursor_fn_specs.rs1use vstd::prelude::*;
2
3use crate::specs::{
4 arch::PAGE_SIZE,
5 mm::{
6 frame::{mapping::frame_to_index, meta_region_owners::MetaRegionOwners},
7 page_table::{
8 cursor::owners::*, is_valid_range_spec,
9 vaddr_range_proofs::lemma_page_table_managed_index_bounds, *,
10 },
11 },
12 task::InAtomicMode,
13};
14
15use crate::arch::mm::PagingConsts;
16use crate::mm::{
17 PagingConstsTrait, PagingLevel, Vaddr,
18 frame::meta::{REF_COUNT_MAX, REF_COUNT_UNUSED},
19 page_table::*,
20};
21use core::ops::Range;
22
23verus! {
24
25impl<'rcu, C: PageTableConfig> CursorOwner<'rcu, C> {
26 pub proof fn lemma_barrier_va_bounds(self, va: Vaddr)
28 requires
29 self.inv(),
30 self.barrier_va.start <= va < self.barrier_va.end,
31 va % C::BASE_PAGE_SIZE() == 0,
32 ensures
33 self.locked_range().start <= va < self.locked_range().end,
34 vaddr_leading_part_spec::<C>(va) == page_table_coverage_start_spec::<C>(),
35 C::TOP_LEVEL_INDEX_RANGE().start <= pte_index_spec::<C>(va, C::NR_LEVELS())
36 < C::TOP_LEVEL_INDEX_RANGE().end,
37 {
38 C::lemma_paging_consts_properties();
39 C::axiom_current_paging_consts_hardcoded();
40 C::lemma_page_table_config_constant_properties();
41 self.lemma_locked_range_span();
42 lemma_same_node_vaddr_leading_part_match::<C>(
43 va,
44 self.prefix,
45 self.prefix,
46 self.guard_level,
47 );
48 lemma_page_table_managed_index_bounds::<C>(va);
49 }
50}
51
52impl<'rcu, C: PageTableConfig, A: InAtomicMode> Cursor<'rcu, C, A> {
54 pub open spec fn cursor_new_input_valid(va: Range<Vaddr>) -> bool {
56 &&& va.start < va.end
57 &&& va.start % C::BASE_PAGE_SIZE() == 0
58 &&& va.end % C::BASE_PAGE_SIZE() == 0
59 &&& is_valid_range_spec::<C>(va)
60 }
61
62 pub open spec fn cursor_new_success_conditions(va: Range<Vaddr>) -> bool {
64 let top = C::NR_LEVELS();
65 let end = page_table_coverage_end_spec::<C>();
66 let reserve_level = if top > 1 && C::HIGHEST_TRANSLATION_LEVEL() < top && end - page_size::<
68 C,
69 >(top) <= va.start {
70 (top - 1) as PagingLevel
71 } else {
72 top
73 };
74 &&& Self::cursor_new_input_valid(va)
75 &&& va.end + page_size::<C>(reserve_level) <= end
76 }
77
78 pub open spec fn invariants(
79 self,
80 owner: CursorOwner<'rcu, C>,
81 regions: MetaRegionOwners,
82 guards: Guards,
83 ) -> bool {
84 &&& self.va <= self.barrier_va.end
85 &&& owner.inv()
86 &&& self.inv()
87 &&& self.wf(owner)
88 &&& regions.inv()
89 &&& owner.children_not_locked(guards)
90 &&& owner.nodes_locked(guards)
91 &&& owner.metaregion_sound(regions)
92 &&& !owner.popped_too_high
93 }
94
95 pub open spec fn query_some_condition(self, owner: CursorOwner<'rcu, C>) -> bool {
96 owner@.present()
97 }
98
99 pub open spec fn query_panic_condition(
108 self,
109 owner: CursorOwner<'rcu, C>,
110 regions: MetaRegionOwners,
111 ) -> bool {
112 &&& self.barrier_va.start <= self.va < self.barrier_va.end
113 &&& PageTableOwner(owner.cur_subtree()).query_leaf(self.va) matches Some(entry) && {
114 &&& entry.is_frame()
115 &&& entry.frame_is_tracked()
116 &&& regions.ref_count(frame_to_index(entry.frame().mapped_pa)) >= REF_COUNT_MAX
117 }
118 }
119
120 pub open spec fn query_some_ensures(
121 self,
122 owner: CursorOwner<'rcu, C>,
123 res: PagesState<C>,
124 ) -> bool {
125 &&& owner.cur_va_range() == res.0
126 &&& res.1 is Some
127 &&& {
128 let qr = owner@.query_range();
129 owner@.query_item_spec(res.1->0) == Some(qr.start as Vaddr..qr.end as Vaddr)
130 }
131 }
132
133 pub open spec fn query_none_ensures(
134 self,
135 owner: CursorOwner<'rcu, C>,
136 res: PagesState<C>,
137 ) -> bool {
138 &&& res.1 is None
139 }
140
141 pub open spec fn jump_node_holds(self, lv: PagingLevel, va: Vaddr) -> bool {
147 let nstart = nat_align_down(
148 self.va as nat,
149 page_size::<PagingConsts>((lv + 1) as PagingLevel) as nat,
150 );
151 &&& nstart <= va as nat
152 &&& (va as nat) - nstart < page_size::<PagingConsts>((lv + 1) as PagingLevel) as nat
153 }
154
155 pub open spec fn jump_panic_condition(self, va: Vaddr) -> bool {
165 ||| va % PAGE_SIZE != 0
166 ||| (self.barrier_va.start <= va < self.barrier_va.end && forall|lv: PagingLevel|
167 #![trigger self.jump_node_holds(lv, va)]
168 self.level <= lv <= self.guard_level ==> !self.jump_node_holds(lv, va))
169 }
170
171 pub open spec fn find_next_panic_condition(self, len: usize) -> bool {
172 ||| len % PAGE_SIZE != 0
173 ||| self.va + len > self.barrier_va.end
174 }
175}
176
177impl<'rcu, C: PageTableConfig, A: InAtomicMode> CursorMut<'rcu, C, A> {
179 pub open spec fn map_panic_conditions(self, item: C::Item) -> bool {
181 ||| self.0.va >= self.0.barrier_va.end
182 ||| C::item_into_raw(item).1 > C::HIGHEST_TRANSLATION_LEVEL()
183 ||| C::item_into_raw(item).1 > self.0.guard_level
184 ||| self.0.va % page_size::<PagingConsts>(C::item_into_raw(item).1) != 0
185 ||| self.0.va + page_size::<PagingConsts>(C::item_into_raw(item).1) > self.0.barrier_va.end
186 }
187
188 pub open spec fn item_wf(self, item: C::Item, entry_owner: EntryOwner<C>) -> bool {
190 let (paddr, level, prop, _perm) = C::item_into_raw(item);
191 &&& C::item_well_formed(
192 item,
193 )
194 &&& level <= self.0.guard_level
196 &&& entry_owner.inv()
197 &&& (entry_owner.is_absent() || Child::Frame(paddr, level, prop).wf(entry_owner))
198 }
199
200 pub open spec fn item_not_mapped(item: C::Item, regions: MetaRegionOwners) -> bool {
201 let (pa, level, prop, perm) = C::item_into_raw(item);
202 let size = page_size::<PagingConsts>(level);
203 let range = pa..(pa + size) as usize;
204 perm@ is Some ==> regions.paddr_range_not_mapped(range)
205 }
206
207 pub open spec fn item_slot_in_regions_except_perm(
208 item: C::Item,
209 regions: MetaRegionOwners,
210 ) -> bool {
211 let (pa, _level, _prop, perm) = C::item_into_raw(item);
212 let idx = frame_to_index(pa);
213 &&& regions.contains(idx)
214 &&& perm@ is Some ==> {
215 &&& regions.slot_owners[idx].usage !is PageTable
216 &&& 0 < regions.ref_count(idx) <= REF_COUNT_MAX
217 }
218 }
219
220 pub closed spec fn item_slot_in_regions(item: C::Item, regions: MetaRegionOwners) -> bool {
221 let (pa, _level, _prop, perm) = C::item_into_raw(item);
222 &&& Self::item_slot_in_regions_except_perm(item, regions)
223 &&& C::perm_well_formed_with_region(pa, perm, regions)
224 }
225
226 pub proof fn lemma_item_slot_in_regions_facts(item: C::Item, regions: MetaRegionOwners)
227 ensures
228 Self::item_slot_in_regions(item, regions) <==> {
229 &&& Self::item_slot_in_regions_except_perm(item, regions)
230 &&& C::perm_well_formed_with_region(
231 C::item_into_raw(item).0,
232 C::item_into_raw(item).3,
233 regions,
234 )
235 },
236 {
237 }
238
239 pub proof fn lemma_item_slot_in_regions_preserved(
240 item: C::Item,
241 regions0: MetaRegionOwners,
242 regions1: MetaRegionOwners,
243 )
244 requires
245 Self::item_slot_in_regions(item, regions0),
246 Self::item_slot_in_regions_except_perm(item, regions1),
247 C::item_into_raw(item).3@ is Some ==> {
248 let idx = frame_to_index(C::item_into_raw(item).0);
249 &&& regions0.slots[idx] == regions1.slots[idx]
250 &&& regions0.slot_owners[idx].metadata_perm.id()
251 == regions1.slot_owners[idx].metadata_perm.id()
252 },
253 ensures
254 Self::item_slot_in_regions(item, regions1),
255 {
256 if C::item_into_raw(item).3@ is Some {
257 C::lemma_perm_well_formed_with_region_preserved(
258 C::item_into_raw(item).0,
259 C::item_into_raw(item).3,
260 regions0,
261 regions1,
262 );
263 } else {
264 C::lemma_none_perm_well_formed(C::item_into_raw(item).0, regions1);
265 }
266 }
267
268 pub proof fn lemma_all_item_slots_preserved(
269 regions0: MetaRegionOwners,
270 regions1: MetaRegionOwners,
271 )
272 requires
273 forall|idx: int| regions0.contains(idx) ==> #[trigger] regions1.contains(idx),
274 forall|idx: int|
275 regions0.contains(idx) && regions0.ref_count(idx) != REF_COUNT_UNUSED ==> {
276 &&& (#[trigger] regions1.slot_owners[idx]).same_permissions(
277 regions0.slot_owners[idx],
278 )
279 &&& regions1.slot_owners[idx].usage == regions0.slot_owners[idx].usage
280 &&& regions1.slot_owners[idx].slot_vaddr == regions0.slot_owners[idx].slot_vaddr
281 &&& regions1.slots[idx] == regions0.slots[idx]
282 },
283 ensures
284 forall|item: C::Item|
285 #![trigger Self::item_slot_in_regions(item, regions0)]
286 Self::item_slot_in_regions(item, regions0) ==> Self::item_slot_in_regions(
287 item,
288 regions1,
289 ),
290 {
291 assert forall|item: C::Item|
292 Self::item_slot_in_regions(
293 item,
294 regions0,
295 ) implies #[trigger] Self::item_slot_in_regions(item, regions1) by {
296 let (pa, _level, _prop, _perm) = C::item_into_raw(item);
297 if C::item_into_raw(item).3@ is Some {
298 Self::lemma_item_slot_in_regions_preserved(item, regions0, regions1);
299 } else {
300 C::lemma_none_perm_well_formed(pa, regions1);
301 }
302 };
303 }
304
305 pub open spec fn map_item_ensures(
306 self,
307 item: C::Item,
308 old_view: CursorView<C>,
309 new_view: CursorView<C>,
310 ) -> bool {
311 let (pa, level, prop, _perm) = C::item_into_raw(item);
312 new_view == old_view.map_spec(pa, page_size::<PagingConsts>(level), prop)
313 }
314}
315
316}