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}