page_table/
lib.rs

1//! x
2
3#![no_std]
4#![deny(warnings, unstable_features, missing_docs)]
5
6mod addr;
7mod flags;
8mod pte;
9mod table;
10
11cfg_if::cfg_if! {
12    if #[cfg(any(target_arch = "riscv64", target_arch = "riscv32"))] {
13        #[path = "arch/riscv.rs"]
14        mod arch;
15    } else if #[cfg(target_arch = "aarch64")] {
16        #[path = "arch/arm.rs"]
17        mod arch;
18    } else if #[cfg(target_arch = "x86_64")] {
19        #[path = "arch/x86.rs"]
20        mod arch;
21    } else {
22        compile_error!("Unsupported architecture");
23    }
24}
25
26pub use addr::*;
27pub use arch::*;
28pub use flags::VmFlags;
29pub use pte::Pte;
30pub use table::*;
31
32/// 地址转换单元元数据。
33pub trait MmuMeta {
34    /// 物理地址位数,用于计算物理页号形式。
35    const P_ADDR_BITS: usize;
36
37    /// 页内偏移的位数。
38    const PAGE_BITS: usize;
39
40    /// 各级页内虚地址位数位数。
41    const LEVEL_BITS: &'static [usize];
42
43    /// 物理页号在 PTE 中的位置。
44    const PPN_POS: usize;
45
46    /// 表示页表项有效的标志位。
47    ///
48    /// 一般就是最低位。
49    const VALID_FLAG: usize = 1;
50
51    /// 判断页表项是否有效。
52    #[inline]
53    fn is_valid(flags: usize) -> bool {
54        flags & Self::VALID_FLAG == Self::VALID_FLAG
55    }
56
57    /// 如果页表项指向物理页,则返回 `true`。
58    ///
59    /// # NOTE
60    ///
61    /// 为了分散开销,这个方法的实现不会判断页表项是否有效。
62    fn is_leaf(flags: usize) -> bool;
63
64    /// 格式化特性位。
65    #[inline]
66    fn fmt_flags(f: &mut core::fmt::Formatter, flags: usize) -> core::fmt::Result {
67        write!(f, "{flags:018x}")
68    }
69}
70
71/// 页式虚存元数据。
72pub trait VmMeta: 'static + MmuMeta + Copy + Ord + core::hash::Hash + core::fmt::Debug {
73    /// 虚拟页号位数,用于裁剪或扩展正确的虚址。
74    const V_ADDR_BITS: usize = Self::PAGE_BITS + const_sum(0, Self::LEVEL_BITS);
75
76    /// 页表最大级别。
77    const MAX_LEVEL: usize = Self::LEVEL_BITS.len() - 1;
78
79    /// 页表项中的物理页号掩码。
80    const PPN_MASK: usize = ppn_mask::<Self>();
81
82    /// `level` 级页表容纳的总页数。
83    #[inline]
84    fn pages_in_table(level: usize) -> usize {
85        1 << Self::LEVEL_BITS[..=level].iter().sum::<usize>()
86    }
87
88    /// `level` 级页表容纳的总字节数。
89    #[inline]
90    fn bytes_in_table(level: usize) -> usize {
91        1 << (Self::LEVEL_BITS[..=level].iter().sum::<usize>() + Self::PAGE_BITS)
92    }
93
94    /// `level` 级页容纳的总字节数。
95    #[inline]
96    fn bytes_in_page(level: usize) -> usize {
97        1 << (Self::LEVEL_BITS[..level].iter().sum::<usize>() + Self::PAGE_BITS)
98    }
99
100    /// 判断页表项指向的是一个大于 0 级(4 kiB)的物理页。
101    ///
102    /// # NOTE
103    ///
104    /// 为了零开销抽象,这个方法的实现可能不会判断 PTE 是否 valid。
105    #[inline]
106    fn is_huge(value: usize, level: usize) -> bool {
107        level != 0 && Self::is_leaf(value)
108    }
109
110    /// 从 PTE 中获得 PPN。
111    #[inline]
112    fn ppn(value: usize) -> PPN<Self> {
113        PPN::new((value & Self::PPN_MASK) >> Self::PPN_POS)
114    }
115
116    /// 设置页表项的 ppn。
117    #[inline]
118    fn set_ppn(value: &mut usize, ppn: PPN<Self>) {
119        *value |= (ppn.val() << Self::PPN_POS) & Self::PPN_MASK;
120    }
121
122    /// 清除页表项中的 ppn。
123    #[inline]
124    fn clear_ppn(value: &mut usize) {
125        *value &= !Self::PPN_MASK;
126    }
127}
128
129/// 自动实现。
130impl<T: 'static + MmuMeta + Copy + Ord + core::hash::Hash + core::fmt::Debug> VmMeta for T {}
131
132/// 生成一个 `bits` 位的掩码。
133#[inline]
134const fn mask(bits: usize) -> usize {
135    (1 << bits) - 1
136}
137
138/// 计算 pte 中 ppn 的掩码。
139#[inline]
140const fn ppn_mask<Meta: MmuMeta>() -> usize {
141    let m0: usize = !mask(Meta::PPN_POS);
142    let m1: usize = mask(Meta::PPN_POS + Meta::P_ADDR_BITS - Meta::PAGE_BITS);
143    m0 & m1
144}
145
146/// 递归求和,可以用在编译期。
147#[inline]
148const fn const_sum(val: usize, bits: &[usize]) -> usize {
149    match bits {
150        [] => val,
151        [n, tail @ ..] => const_sum(val + *n, tail),
152    }
153}
154
155#[cfg(test)]
156mod test_meta {
157    #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
158    pub(crate) struct Sv39;
159
160    impl super::MmuMeta for Sv39 {
161        const P_ADDR_BITS: usize = 56;
162        const PAGE_BITS: usize = 12;
163        const LEVEL_BITS: &'static [usize] = &[9; 3];
164        const PPN_POS: usize = 10;
165
166        #[inline]
167        fn is_leaf(value: usize) -> bool {
168            const MASK: usize = 0b1110;
169            value & MASK != 0
170        }
171    }
172
173    #[test]
174    fn test_pages() {
175        use super::VmMeta;
176
177        assert_eq!(Sv39::pages_in_table(0), 512);
178        assert_eq!(Sv39::pages_in_table(1), 512 * 512);
179        assert_eq!(Sv39::pages_in_table(2), 512 * 512 * 512);
180
181        assert_eq!(Sv39::bytes_in_table(0), 4096 * 512);
182        assert_eq!(Sv39::bytes_in_table(1), 4096 * 512 * 512);
183        assert_eq!(Sv39::bytes_in_table(2), 4096 * 512 * 512 * 512);
184    }
185}