Skip to main content

ax_page_table_multiarch/
lib.rs

1#![cfg_attr(not(test), no_std)]
2#![cfg_attr(doc, feature(doc_cfg))]
3#![doc = include_str!("../README.md")]
4
5#[macro_use]
6extern crate log;
7
8mod arch;
9#[cfg(any(target_pointer_width = "32", doc, docsrs))]
10mod bits32;
11#[cfg(any(target_pointer_width = "64", doc, docsrs))]
12mod bits64;
13
14use core::fmt::Debug;
15
16use arrayvec::ArrayVec;
17use ax_memory_addr::{MemoryAddr, PAGE_SIZE_4K, PhysAddr, VirtAddr};
18#[doc(no_inline)]
19pub use ax_page_table_entry::{GenericPTE, MappingFlags};
20
21#[cfg(any(target_pointer_width = "32", doc, docsrs))]
22pub use self::{
23    arch::*,
24    bits32::{PageTable32, PageTable32Cursor},
25};
26#[cfg(any(target_pointer_width = "64", doc, docsrs))]
27pub use self::{
28    arch::*,
29    bits64::{PageTable64, PageTable64Cursor},
30};
31
32/// The error type for page table operation failures.
33#[derive(Debug, PartialEq, Clone, Copy)]
34pub enum PagingError {
35    /// Cannot allocate memory.
36    NoMemory,
37    /// The address is not aligned to the page size.
38    NotAligned,
39    /// The mapping is not present.
40    NotMapped,
41    /// The mapping is already present.
42    AlreadyMapped,
43    /// The page table entry represents a huge page, but the target physical
44    /// frame is 4K in size.
45    MappedToHugePage,
46}
47
48#[cfg(feature = "ax-errno")]
49impl From<PagingError> for ax_errno::AxErrorKind {
50    fn from(value: PagingError) -> Self {
51        match value {
52            PagingError::NoMemory => ax_errno::AxErrorKind::NoMemory,
53            _ => ax_errno::AxErrorKind::InvalidInput,
54        }
55    }
56}
57
58/// The specialized `Result` type for page table operations.
59pub type PagingResult<T = ()> = Result<T, PagingError>;
60
61/// The **architecture-dependent** metadata that must be provided for
62/// [`PageTable64`].
63pub trait PagingMetaData: Sync + Send {
64    /// The number of levels of the hardware page table.
65    const LEVELS: usize;
66    /// The maximum number of bits of physical address.
67    const PA_MAX_BITS: usize;
68    /// The maximum number of bits of virtual address.
69    const VA_MAX_BITS: usize;
70
71    /// The maximum physical address.
72    const PA_MAX_ADDR: usize = (1 << Self::PA_MAX_BITS) - 1;
73
74    /// The virtual address to be translated in this page table.
75    ///
76    /// This associated type allows more flexible use of page tables structs
77    /// like [`PageTable64`], for example, to implement EPTs.
78    type VirtAddr: MemoryAddr;
79    // (^)it can be converted from/to usize and it's trivially copyable
80
81    /// Whether a given physical address is valid.
82    #[inline]
83    fn paddr_is_valid(paddr: usize) -> bool {
84        paddr <= Self::PA_MAX_ADDR // default
85    }
86
87    /// Whether a given virtual address is valid.
88    #[inline]
89    fn vaddr_is_valid(vaddr: usize) -> bool {
90        // default: top bits sign extended
91        let top_mask = usize::MAX << (Self::VA_MAX_BITS - 1);
92        (vaddr & top_mask) == 0 || (vaddr & top_mask) == top_mask
93    }
94
95    /// Flushes the TLB.
96    ///
97    /// If `vaddr` is [`None`], flushes the entire TLB. Otherwise, flushes the
98    /// TLB entry at the given virtual address.
99    fn flush_tlb(vaddr: Option<Self::VirtAddr>);
100}
101
102/// The low-level **OS-dependent** helpers that must be provided for
103/// [`PageTable64`].
104pub trait PagingHandler: Sized {
105    /// Request to allocate a 4K-sized physical frame.
106    fn alloc_frame() -> Option<PhysAddr> {
107        Self::alloc_frames(1, PAGE_SIZE_4K)
108    }
109    /// Allocate `num` contiguous physical frames, with the starting physical
110    /// address aligned to `align` bytes (must be a power of two, and must be
111    /// a multiple of 4K).
112    fn alloc_frames(num: usize, align: usize) -> Option<PhysAddr>;
113    /// Request to free a allocated physical frame.
114    fn dealloc_frame(paddr: PhysAddr) {
115        Self::dealloc_frames(paddr, 1)
116    }
117    /// Free `num` contiguous physical frames starting from the given physical
118    /// address. The `num` must be the same as that used in allocation.
119    fn dealloc_frames(paddr: PhysAddr, num: usize);
120    /// Returns a virtual address that maps to the given physical address.
121    ///
122    /// Used to access the physical memory directly in page table
123    /// implementation.
124    fn phys_to_virt(paddr: PhysAddr) -> VirtAddr;
125}
126
127/// The page sizes supported by the hardware page table.
128#[repr(usize)]
129#[derive(Debug, Copy, Clone, Eq, PartialEq)]
130pub enum PageSize {
131    /// Size of 4 kilobytes (2<sup>12</sup> bytes).
132    Size4K = 0x1000,
133    /// Size of 1 megabytes (2<sup>20</sup> bytes).
134    Size1M = 0x10_0000,
135    /// Size of 2 megabytes (2<sup>21</sup> bytes).
136    Size2M = 0x20_0000,
137    /// Size of 1 gigabytes (2<sup>30</sup> bytes).
138    Size1G = 0x4000_0000,
139}
140
141impl PageSize {
142    /// Whether this page size is considered huge (larger than 4K).
143    pub const fn is_huge(self) -> bool {
144        matches!(self, Self::Size1G | Self::Size2M | Self::Size1M)
145    }
146
147    /// Checks whether a given address or size is aligned to the page size.
148    pub const fn is_aligned(self, addr_or_size: usize) -> bool {
149        ax_memory_addr::is_aligned(addr_or_size, self as usize)
150    }
151
152    /// Returns the offset of the address within the page size.
153    pub const fn align_offset(self, addr: usize) -> usize {
154        ax_memory_addr::align_offset(addr, self as usize)
155    }
156}
157
158impl From<PageSize> for usize {
159    #[inline]
160    fn from(size: PageSize) -> usize {
161        size as usize
162    }
163}
164
165// TODO: tune threshold; employ a more advanced data structure
166const SMALL_FLUSH_THRESHOLD: usize = 32;
167
168enum TlbFlusher<M: PagingMetaData> {
169    None,
170    Array(ArrayVec<M::VirtAddr, SMALL_FLUSH_THRESHOLD>),
171    Full,
172}