page_table_generic/
lib.rs

1#![cfg_attr(not(test), no_std)]
2#![feature(pointer_is_aligned_to)]
3
4use core::{alloc::Layout, fmt::Debug, ptr::NonNull};
5
6mod align;
7pub mod err;
8mod iter;
9mod page_table_entry;
10mod table;
11
12pub use table::PageTableRef;
13
14bitflags::bitflags! {
15    /// Generic page table entry flags that indicate the corresponding mapped
16    /// memory region permissions and attributes.
17    #[repr(transparent)]
18    #[derive(Clone, Copy, PartialEq, Eq)]
19    pub struct AccessSetting: u8 {
20        const Read = 1;
21        const Write = 1 << 2;
22        const Execute = 1 << 3;
23    }
24}
25
26impl AccessSetting {
27    pub fn readable(&self) -> bool {
28        self.contains(AccessSetting::Read)
29    }
30
31    pub fn writable(&self) -> bool {
32        self.contains(AccessSetting::Write)
33    }
34
35    pub fn executable(&self) -> bool {
36        self.contains(AccessSetting::Execute)
37    }
38}
39
40#[repr(u8)]
41#[derive(Debug, Clone, Copy, PartialEq, Eq)]
42pub enum CacheSetting {
43    Normal,
44    Device,
45    NonCache,
46}
47
48#[repr(C)]
49#[derive(Debug, Clone, Copy)]
50pub struct MapConfig {
51    pub vaddr: *const u8,
52    pub paddr: usize,
53    pub setting: PTESetting,
54}
55
56impl MapConfig {
57    pub fn new(
58        vaddr: *const u8,
59        paddr: usize,
60        privilege_access: AccessSetting,
61        cache_setting: CacheSetting,
62    ) -> Self {
63        Self {
64            vaddr,
65            paddr,
66            setting: PTESetting {
67                cache_setting,
68                privilege_access,
69                user_access: AccessSetting::empty(),
70                is_global: true,
71            },
72        }
73    }
74
75    pub fn set_user_access(&mut self, access: AccessSetting) -> Self {
76        self.setting.user_access = access;
77        *self
78    }
79}
80
81#[repr(C)]
82#[derive(Clone, Copy, PartialEq, Eq)]
83pub struct PTESetting {
84    pub is_global: bool,
85    pub privilege_access: AccessSetting,
86    pub user_access: AccessSetting,
87    pub cache_setting: CacheSetting,
88}
89
90impl Default for PTESetting {
91    fn default() -> Self {
92        Self {
93            is_global: Default::default(),
94            privilege_access: AccessSetting::empty(),
95            user_access: AccessSetting::empty(),
96            cache_setting: CacheSetting::Normal,
97        }
98    }
99}
100
101#[repr(C)]
102#[derive(Default, Clone, PartialEq, Eq)]
103pub struct PTEGeneric {
104    pub paddr: usize,
105    pub is_block: bool,
106    pub is_valid: bool,
107    pub setting: PTESetting,
108}
109
110impl PTEGeneric {
111    pub(crate) fn new(paddr: usize, is_block: bool, setting: PTESetting) -> Self {
112        Self {
113            paddr,
114            is_valid: true,
115            is_block,
116            setting,
117        }
118    }
119
120    pub fn valid(&self) -> bool {
121        self.is_valid
122    }
123}
124
125impl Debug for PTEGeneric {
126    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
127        write!(
128            f,
129            "PTE PA:{:#p} Block: {:>5} {:?}",
130            self.paddr as *const u8, self.is_block, self.setting
131        )
132    }
133}
134
135pub trait PTEArch: Sync + Send + Clone + Copy + 'static {
136    fn page_size() -> usize;
137    fn level() -> usize;
138    fn new_pte(config: PTEGeneric) -> usize;
139    fn read_pte(pte: usize) -> PTEGeneric;
140}
141
142pub trait Access {
143    /// offset = virtual address - physical address.
144    fn va_offset(&self) -> usize;
145    /// Alloc memory for a page table entry.
146    ///
147    /// # Safety
148    ///
149    /// should be deallocated by [`dealloc`].
150    unsafe fn alloc(&mut self, layout: Layout) -> Option<NonNull<u8>>;
151    /// dealloc memory for a page table entry.
152    ///
153    /// # Safety
154    ///
155    /// ptr must be allocated by [`alloc`].
156    unsafe fn dealloc(&mut self, ptr: NonNull<u8>, layout: Layout);
157}
158
159pub struct PTEInfo {
160    pub level: usize,
161    pub vaddr: *const u8,
162    pub pte: PTEGeneric,
163}
164
165impl Debug for PTESetting {
166    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
167        macro_rules! field {
168            ($i:expr,$e:ident,$name:expr) => {
169                if $i.contains(AccessSetting::$e) {
170                    f.write_str($name)?
171                } else {
172                    f.write_str("-")?
173                }
174            };
175        }
176
177        f.write_str("P")?;
178        field!(self.privilege_access, Read, "R");
179        field!(self.privilege_access, Write, "W");
180        field!(self.privilege_access, Execute, "X");
181        f.write_str(" | U")?;
182        field!(self.user_access, Read, "R");
183        field!(self.user_access, Write, "W");
184        field!(self.user_access, Execute, "X");
185        f.write_str(", ")?;
186        f.write_fmt(format_args!("{:?}", self.cache_setting))?;
187        f.write_str(")")
188    }
189}