1#![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
32pub trait MmuMeta {
34 const P_ADDR_BITS: usize;
36
37 const PAGE_BITS: usize;
39
40 const LEVEL_BITS: &'static [usize];
42
43 const PPN_POS: usize;
45
46 const VALID_FLAG: usize = 1;
50
51 #[inline]
53 fn is_valid(flags: usize) -> bool {
54 flags & Self::VALID_FLAG == Self::VALID_FLAG
55 }
56
57 fn is_leaf(flags: usize) -> bool;
63
64 #[inline]
66 fn fmt_flags(f: &mut core::fmt::Formatter, flags: usize) -> core::fmt::Result {
67 write!(f, "{flags:018x}")
68 }
69}
70
71pub trait VmMeta: 'static + MmuMeta + Copy + Ord + core::hash::Hash + core::fmt::Debug {
73 const V_ADDR_BITS: usize = Self::PAGE_BITS + const_sum(0, Self::LEVEL_BITS);
75
76 const MAX_LEVEL: usize = Self::LEVEL_BITS.len() - 1;
78
79 const PPN_MASK: usize = ppn_mask::<Self>();
81
82 #[inline]
84 fn pages_in_table(level: usize) -> usize {
85 1 << Self::LEVEL_BITS[..=level].iter().sum::<usize>()
86 }
87
88 #[inline]
90 fn bytes_in_table(level: usize) -> usize {
91 1 << (Self::LEVEL_BITS[..=level].iter().sum::<usize>() + Self::PAGE_BITS)
92 }
93
94 #[inline]
96 fn bytes_in_page(level: usize) -> usize {
97 1 << (Self::LEVEL_BITS[..level].iter().sum::<usize>() + Self::PAGE_BITS)
98 }
99
100 #[inline]
106 fn is_huge(value: usize, level: usize) -> bool {
107 level != 0 && Self::is_leaf(value)
108 }
109
110 #[inline]
112 fn ppn(value: usize) -> PPN<Self> {
113 PPN::new((value & Self::PPN_MASK) >> Self::PPN_POS)
114 }
115
116 #[inline]
118 fn set_ppn(value: &mut usize, ppn: PPN<Self>) {
119 *value |= (ppn.val() << Self::PPN_POS) & Self::PPN_MASK;
120 }
121
122 #[inline]
124 fn clear_ppn(value: &mut usize) {
125 *value &= !Self::PPN_MASK;
126 }
127}
128
129impl<T: 'static + MmuMeta + Copy + Ord + core::hash::Hash + core::fmt::Debug> VmMeta for T {}
131
132#[inline]
134const fn mask(bits: usize) -> usize {
135 (1 << bits) - 1
136}
137
138#[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#[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}