#![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];
const PPN_POS: usize;
const VALID_FLAG: usize = 1;
#[inline]
fn is_valid(flags: usize) -> bool {
flags & Self::VALID_FLAG == Self::VALID_FLAG
}
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>();
#[inline]
fn pages_in_table(level: usize) -> usize {
1 << Self::LEVEL_BITS[..=level].iter().sum::<usize>()
}
#[inline]
fn bytes_in_table(level: usize) -> usize {
1 << (Self::LEVEL_BITS[..=level].iter().sum::<usize>() + Self::PAGE_BITS)
}
#[inline]
fn bytes_in_page(level: usize) -> usize {
1 << (Self::LEVEL_BITS[..level].iter().sum::<usize>() + Self::PAGE_BITS)
}
#[inline]
fn is_huge(value: usize, level: usize) -> bool {
level != 0 && Self::is_leaf(value)
}
#[inline]
fn ppn(value: usize) -> PPN<Self> {
PPN::new((value & Self::PPN_MASK) >> Self::PPN_POS)
}
#[inline]
fn set_ppn(value: &mut usize, ppn: PPN<Self>) {
*value |= (ppn.val() << Self::PPN_POS) & Self::PPN_MASK;
}
#[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 {}
#[inline]
const fn mask(bits: usize) -> usize {
(1 << bits) - 1
}
#[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);
}
}