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
//! This module provides the [`PageLevel`] struct used to describe a single level in a page table
//! hierarchy. The full page table hierarchy is described by [`crate::format::PageFormat`].
use num_traits::{PrimInt, Unsigned};
/// Describes a single page level of the page hierarchy.
#[derive(Clone, Debug)]
pub struct PageLevel<PTE>
where
PTE: PrimInt + Unsigned,
{
/// The number of bits to shift right in the virtual address to get the index bits for this
/// page level.
pub shift_bits: usize,
/// The number of bits in the virtual address to extract as the index for this page level.
pub va_bits: usize,
/// The present bit in the PTE. The first mask is to select the relevants bits, the second is
/// what the value should be upon masking.
pub present_bit: (PTE, PTE),
/// The huge page bit in the PTE. If the current page level does not support huge pages, then
/// this should be set to zero. The first mask is to select the relevant bits, the second is
/// what the value should be upon masking.
pub huge_page_bit: (PTE, PTE),
/// The page table mask that should be set when allocating new page tables.
pub page_table_mask: PTE,
}
impl<PTE> PageLevel<PTE>
where
PTE: PrimInt + Unsigned,
{
/// Calculates the number of entries present in a page table for this page level.
pub fn entries(&self) -> usize {
1 << self.va_bits
}
/// Calculates the page size for this page level.
pub fn page_size(&self) -> usize {
1 << self.shift_bits
}
/// Calculates the shifted mask to select the appropriate bits from the virtual address.
pub fn mask(&self) -> usize {
((1 << self.va_bits) - 1) << self.shift_bits
}
/// Calculates the last virtual address within the same page for the current page level of the
/// given a virtual address. This can be used to retrieve the first address of the next page
/// for the current page level by simply adding one.
pub fn end(&self, addr: usize) -> usize {
addr | (self.page_size() - 1)
}
/// Calculates the PTE index of the given virtual address for the current page table level,
/// which can then be used to index into the page table to get the corresponding PTE for this
/// virtual address.
pub fn pte_index(&self, addr: usize) -> usize {
(addr >> self.shift_bits) & ((1 << self.va_bits) - 1)
}
/// Given a PTE, it checks if the PTE points to a present page or page table.
pub fn is_present(&self, pte: PTE) -> bool {
(pte & self.present_bit.0) == self.present_bit.1
}
/// Given a PTE, it checks if the PTE points to a huge page. Always returns `false` if the
/// current page level does not support huge pages.
pub fn is_huge_page(&self, pte: PTE) -> bool {
if self.huge_page_bit.0 != PTE::zero() {
let mask = self.present_bit.0 | self.huge_page_bit.0;
let value = self.present_bit.1 | self.huge_page_bit.1;
(pte & mask) == value
} else {
false
}
}
}