1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
//! x

#![no_std]
#![deny(warnings, unstable_features, missing_docs)]

mod addr;
mod flags;
mod pte;
mod table;

cfg_if::cfg_if! {
    if #[cfg(any(target_arch = "riscv64", target_arch = "riscv32"))] {
        #[path = "arch/riscv.rs"]
        mod arch;
    } else if #[cfg(target_arch = "aarch64")] {
        #[path = "arch/arm.rs"]
        mod arch;
    } else if #[cfg(target_arch = "x86_64")] {
        #[path = "arch/x86.rs"]
        mod arch;
    } else {
        compile_error!("Unsupported architecture");
    }
}

pub use addr::*;
pub use arch::*;
pub use flags::VmFlags;
pub use pte::Pte;
pub use table::*;

/// 地址转换单元元数据。
pub trait MmuMeta {
    /// 物理地址位数,用于计算物理页号形式。
    const P_ADDR_BITS: usize;

    /// 页内偏移的位数。
    const PAGE_BITS: usize;

    /// 各级页内虚地址位数位数。
    const LEVEL_BITS: &'static [usize];

    /// 物理页号在 PTE 中的位置。
    const PPN_POS: usize;

    /// 判断页表项是否有效。
    #[inline]
    fn is_valid(flags: usize) -> bool {
        // 一般都用最低位表示页表有效
        flags & 1 == 1
    }

    /// 如果页表项指向物理页,则返回 `true`。
    ///
    /// # NOTE
    ///
    /// 为了分散开销,这个方法的实现不会判断 PTE 是否 valid。
    fn is_leaf(flags: usize) -> bool;

    /// 格式化特性位。
    #[inline]
    fn fmt_flags(f: &mut core::fmt::Formatter, flags: usize) -> core::fmt::Result {
        write!(f, "{flags:018x}")
    }
}

/// 页式虚存元数据。
pub trait VmMeta: 'static + MmuMeta + Copy + Ord + core::hash::Hash + core::fmt::Debug {
    /// 虚拟页号位数,用于裁剪或扩展正确的虚址。
    const V_ADDR_BITS: usize = Self::PAGE_BITS + const_sum(0, Self::LEVEL_BITS);

    /// 页表最大级别。
    const MAX_LEVEL: usize = Self::LEVEL_BITS.len() - 1;

    /// 页表项中的物理页号掩码。
    const PPN_MASK: usize = ppn_mask::<Self>();

    /// `level` 级页表容纳的总页数。
    #[inline]
    fn pages_in_table(level: usize) -> usize {
        1 << Self::LEVEL_BITS[..=level].iter().sum::<usize>()
    }

    /// `level` 级页表容纳的总字节数。
    #[inline]
    fn bytes_in_table(level: usize) -> usize {
        1 << (Self::LEVEL_BITS[..=level].iter().sum::<usize>() + Self::PAGE_BITS)
    }

    /// `level` 级页容纳的总字节数。
    #[inline]
    fn bytes_in_page(level: usize) -> usize {
        1 << (Self::LEVEL_BITS[..level].iter().sum::<usize>() + Self::PAGE_BITS)
    }

    /// 判断页表项指向的是一个大于 0 级(4 kiB)的物理页。
    ///
    /// # NOTE
    ///
    /// 为了零开销抽象,这个方法的实现可能不会判断 PTE 是否 valid。
    #[inline]
    fn is_huge(value: usize, level: usize) -> bool {
        level != 0 && Self::is_leaf(value)
    }

    /// 从 PTE 中获得 PPN。
    #[inline]
    fn ppn(value: usize) -> PPN<Self> {
        PPN::new((value & Self::PPN_MASK) >> Self::PPN_POS)
    }

    /// 设置页表项的 ppn。
    #[inline]
    fn set_ppn(value: &mut usize, ppn: PPN<Self>) {
        *value |= (ppn.val() << Self::PPN_POS) & Self::PPN_MASK;
    }

    /// 清除页表项中的 ppn。
    #[inline]
    fn clear_ppn(value: &mut usize) {
        *value &= !Self::PPN_MASK;
    }
}

/// 自动实现。
impl<T: 'static + MmuMeta + Copy + Ord + core::hash::Hash + core::fmt::Debug> VmMeta for T {}

/// 生成一个 `bits` 位的掩码。
#[inline]
const fn mask(bits: usize) -> usize {
    (1 << bits) - 1
}

/// 计算 pte 中 ppn 的掩码。
#[inline]
const fn ppn_mask<Meta: MmuMeta>() -> usize {
    let m0: usize = !mask(Meta::PPN_POS);
    let m1: usize = mask(Meta::PPN_POS + Meta::P_ADDR_BITS - Meta::PAGE_BITS);
    m0 & m1
}

/// 递归求和,可以用在编译期。
#[inline]
const fn const_sum(val: usize, bits: &[usize]) -> usize {
    match bits {
        [] => val,
        [n, tail @ ..] => const_sum(val + *n, tail),
    }
}

#[cfg(test)]
mod test_meta {
    #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
    pub(crate) struct Sv39;

    impl super::MmuMeta for Sv39 {
        const P_ADDR_BITS: usize = 56;
        const PAGE_BITS: usize = 12;
        const LEVEL_BITS: &'static [usize] = &[9; 3];
        const PPN_POS: usize = 10;

        #[inline]
        fn is_leaf(value: usize) -> bool {
            const MASK: usize = 0b1110;
            value & MASK != 0
        }
    }

    #[test]
    fn test_pages() {
        use super::VmMeta;

        assert_eq!(Sv39::pages_in_table(0), 512);
        assert_eq!(Sv39::pages_in_table(1), 512 * 512);
        assert_eq!(Sv39::pages_in_table(2), 512 * 512 * 512);

        assert_eq!(Sv39::bytes_in_table(0), 4096 * 512);
        assert_eq!(Sv39::bytes_in_table(1), 4096 * 512 * 512);
        assert_eq!(Sv39::bytes_in_table(2), 4096 * 512 * 512 * 512);
    }
}