page_walker/
level.rs

1//! This module provides the [`PageLevel`] struct used to describe a single level in a page table
2//! hierarchy. The full page table hierarchy is described by [`crate::format::PageFormat`].
3
4use num_traits::{PrimInt, Unsigned};
5
6/// Describes a single page level of the page hierarchy.
7#[derive(Clone, Debug)]
8pub struct PageLevel<PTE>
9where
10    PTE: PrimInt + Unsigned,
11{
12    /// The number of bits to shift right in the virtual address to get the index bits for this
13    /// page level.
14    pub shift_bits: usize,
15    /// The number of bits in the virtual address to extract as the index for this page level.
16    pub va_bits: usize,
17    /// The present bit in the PTE. The first mask is to select the relevants bits, the second is
18    /// what the value should be upon masking.
19    pub present_bit: (PTE, PTE),
20    /// The huge page bit in the PTE. If the current page level does not support huge pages, then
21    /// this should be set to zero. The first mask is to select the relevant bits, the second is
22    /// what the value should be upon masking.
23    pub huge_page_bit: (PTE, PTE),
24    /// The page table mask that should be set when allocating new page tables.
25    pub page_table_mask: PTE,
26}
27
28impl<PTE> PageLevel<PTE>
29where
30    PTE: PrimInt + Unsigned,
31{
32    /// Calculates the number of entries present in a page table for this page level.
33    pub fn entries(&self) -> usize {
34        1 << self.va_bits
35    }
36
37    /// Calculates the page size for this page level.
38    pub fn page_size(&self) -> usize {
39        1 << self.shift_bits
40    }
41
42    /// Calculates the shifted mask to select the appropriate bits from the virtual address.
43    pub fn mask(&self) -> usize {
44        ((1 << self.va_bits) - 1) << self.shift_bits
45    }
46
47    /// Calculates the last virtual address within the same page for the current page level of the
48    /// given a virtual address. This can be used to retrieve the first address of the next page
49    /// for the current page level by simply adding one.
50    pub fn end(&self, addr: usize) -> usize {
51        addr | (self.page_size() - 1)
52    }
53
54    /// Calculates the PTE index of the given virtual address for the current page table level,
55    /// which can then be used to index into the page table to get the corresponding PTE for this
56    /// virtual address.
57    pub fn pte_index(&self, addr: usize) -> usize {
58        (addr >> self.shift_bits) & ((1 << self.va_bits) - 1)
59    }
60
61    /// Given a PTE, it checks if the PTE points to a present page or page table.
62    pub fn is_present(&self, pte: PTE) -> bool {
63        (pte & self.present_bit.0) == self.present_bit.1
64    }
65
66    /// Given a PTE, it checks if the PTE points to a huge page. Always returns `false` if the
67    /// current page level does not support huge pages.
68    pub fn is_huge_page(&self, pte: PTE) -> bool {
69        if self.huge_page_bit.0 != PTE::zero() {
70            let mask = self.present_bit.0 | self.huge_page_bit.0;
71            let value = self.present_bit.1 | self.huge_page_bit.1;
72
73            (pte & mask) == value
74        } else {
75            false
76        }
77    }
78}