Skip to main content

ostd/specs/mm/page_table/
vaddr_range_proofs.rs

1// SPDX-License-Identifier: MPL-2.0
2//! Bounded-arithmetic proofs for `vaddr_range`'s body.
3//!
4//! Factored into a leaf module because the `nonlinear_arith` tactic
5//! triggers a Verus internal panic when used in `page_table/mod.rs`
6//! alongside `largest_pages` (which has an `impl Iterator` return type).
7//! Same workaround pattern as the older `vaddr_range_bv_lemmas`.
8use vstd::prelude::*;
9
10use vstd::arithmetic::power2::{lemma_pow2_adds, lemma_pow2_pos, pow2};
11
12use crate::specs::mm::page_table::pte_index_bit_offset_spec;
13
14use crate::mm::{PagingConstsTrait, Vaddr, page_table::PageTableConfig};
15
16verus! {
17
18/// Facts needed to turn `idx.start << offset` into
19/// `idx.start * 2^offset` for `pt_va_range_start`.
20pub proof fn lemma_pt_va_range_start_shift_facts<C: PageTableConfig>(
21    idx_start: usize,
22    offset: usize,
23)
24    requires
25        idx_start == C::TOP_LEVEL_INDEX_RANGE().start,
26        offset == pte_index_bit_offset_spec::<C>(C::NR_LEVELS()),
27    ensures
28        offset < usize::BITS,
29        idx_start * pow2(offset as nat) <= usize::MAX,
30        offset == pte_index_bit_offset_spec::<C>(C::NR_LEVELS()),
31{
32    C::lemma_paging_consts_properties();
33    C::lemma_page_table_config_constant_properties();
34    vstd::layout::unsigned_int_max_values();
35
36    let off = pte_index_bit_offset_spec::<C>(C::NR_LEVELS()) as nat;
37    let aw = C::ADDRESS_WIDTH() as nat;
38    let top_w = (aw - off) as nat;
39    lemma_pow2_adds(top_w, off);
40    lemma_pow2_pos(off);
41    lemma_pow2_pos(top_w);
42    lemma_pow2_pos(aw);
43
44    let i_start = C::TOP_LEVEL_INDEX_RANGE().start as int;
45    let p_off = pow2(off) as int;
46    let p_top = pow2(top_w) as int;
47
48    assert(i_start * p_off < p_top * p_off) by (nonlinear_arith)
49        requires
50            i_start < p_top,
51            p_off > 0,
52    ;
53
54    if aw < 64 {
55        vstd::arithmetic::power2::lemma_pow2_strictly_increases(aw, 64);
56    }
57}
58
59/// Facts needed to turn `idx.end << offset` into
60/// `idx.end * 2^offset` for `pt_va_range_end`.
61pub proof fn lemma_pt_va_range_end_shift_facts<C: PageTableConfig>(idx_end: usize, offset: usize)
62    requires
63        idx_end == C::TOP_LEVEL_INDEX_RANGE().end,
64        offset == pte_index_bit_offset_spec::<C>(C::NR_LEVELS()),
65    ensures
66        offset < usize::BITS,
67        idx_end * pow2(offset as nat) <= usize::MAX,
68        0 < idx_end * pow2(offset as nat),
69        offset == pte_index_bit_offset_spec::<C>(C::NR_LEVELS()),
70{
71    C::lemma_paging_consts_properties();
72    C::lemma_page_table_config_constant_properties();
73    lemma_pow2_pos(offset as nat);
74
75    let i_end = C::TOP_LEVEL_INDEX_RANGE().end as int;
76    let p_off = pow2(offset as nat) as int;
77    assert(i_end * p_off > 0) by (nonlinear_arith)
78        requires
79            i_end > 0,
80            p_off > 0,
81    ;
82}
83
84/// Connect `wrapping_sub(1)` to the modulo end-of-range spec after the
85/// shift result is known to be non-zero.
86pub proof fn lemma_pt_va_range_end_wrapping_sub<C: PageTableConfig>(
87    idx_end: usize,
88    offset: usize,
89    shifted: usize,
90    ret: usize,
91)
92    requires
93        idx_end == C::TOP_LEVEL_INDEX_RANGE().end,
94        offset == pte_index_bit_offset_spec::<C>(C::NR_LEVELS()),
95        shifted == idx_end * pow2(offset as nat),
96        ret == vstd::wrapping::usize_specs::wrapping_sub(shifted, 1usize),
97    ensures
98        ret == (C::TOP_LEVEL_INDEX_RANGE().end * pow2(
99            pte_index_bit_offset_spec::<C>(C::NR_LEVELS()) as nat,
100        ) - 1) % 0x1_0000_0000_0000_0000int,
101{
102    lemma_pt_va_range_end_shift_facts::<C>(idx_end, offset);
103    vstd::layout::unsigned_int_max_values();
104}
105
106/// Two-in-one: `start = idx.start * 2^off < 2^ADDRESS_WIDTH` and
107/// `end = (idx.end * 2^off - 1) % 2^64 < 2^ADDRESS_WIDTH`.
108///
109/// The first follows from `idx.start < 2^(ADDRESS_WIDTH - off)`. The
110/// second from `idx.end <= 2^(ADDRESS_WIDTH - off)` plus the no-wrap
111/// condition: `idx.end * 2^off <= 2^ADDRESS_WIDTH ≤ 2^64`, so the `% 2^64`
112/// is a no-op when the value is positive.
113pub proof fn lemma_idx_times_pow2_bound<C: PageTableConfig>(start: Vaddr, end: Vaddr)
114    requires
115        start == C::TOP_LEVEL_INDEX_RANGE().start * (pow2(
116            pte_index_bit_offset_spec::<C>(C::NR_LEVELS()) as nat,
117        ) as int),
118        end == (C::TOP_LEVEL_INDEX_RANGE().end * (pow2(
119            pte_index_bit_offset_spec::<C>(C::NR_LEVELS()) as nat,
120        ) as int) - 1) % 0x1_0000_0000_0000_0000int,
121    ensures
122        start < pow2(C::ADDRESS_WIDTH() as nat),
123        end < pow2(C::ADDRESS_WIDTH() as nat),
124        // For the end-of-range arithmetic ensures of `vaddr_range`:
125        end == C::TOP_LEVEL_INDEX_RANGE().end * pow2(
126            pte_index_bit_offset_spec::<C>(C::NR_LEVELS()) as nat,
127        ) - 1,
128{
129    C::lemma_paging_consts_properties();
130    C::lemma_page_table_config_constant_properties();
131    let off = pte_index_bit_offset_spec::<C>(C::NR_LEVELS()) as nat;
132    let aw = C::ADDRESS_WIDTH() as nat;
133    let top_w = (aw - off) as nat;
134    lemma_pow2_adds(top_w, off);
135    lemma_pow2_pos(off);
136    lemma_pow2_pos(top_w);
137    lemma_pow2_pos(aw);
138    // Constants for clarity.
139    let i_start = C::TOP_LEVEL_INDEX_RANGE().start as int;
140    let i_end = C::TOP_LEVEL_INDEX_RANGE().end as int;
141    let p_off = pow2(off) as int;
142    let p_top = pow2(top_w) as int;
143    let p_aw = pow2(aw) as int;
144    // start < p_top * p_off = p_aw.
145    assert(start < (p_top * p_off)) by (nonlinear_arith)
146        requires
147            start == i_start * p_off,
148            i_start < p_top,
149            p_off > 0,
150    ;
151    // end pre-wrap value.
152    let e_pre = i_end * p_off;
153    assert(e_pre <= p_top * p_off) by (nonlinear_arith)
154        requires
155            e_pre == i_end * p_off,
156            i_end <= p_top,
157            p_off > 0,
158    ;
159    // i_end > 0 — from `lemma_page_table_config_constant_properties`'s
160    // `idx.start < idx.end` plus `idx.start >= 0` (usize).
161    assert(e_pre > 0) by (nonlinear_arith)
162        requires
163            e_pre == i_end * p_off,
164            i_end > 0,
165            p_off > 0,
166    ;
167}
168
169} // verus!