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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
#![no_std]
#![deny(warnings, unsafe_code)]

mod flags;
mod page_table;
mod pte;

cfg_if::cfg_if! {
    if #[cfg(any(target_arch = "riscv64", target_arch = "riscv32"))] {
        #[path = "arch/riscv.rs"]
        mod arch;
    } else if #[cfg(target_arch = "aarch64")] {
        #[path = "arch/arm.rs"]
        mod arch;
    } else if #[cfg(target_arch = "x86_64")] {
        #[path = "arch/x86.rs"]
        mod arch;
    } else {
        compile_error!("Unsupported architecture");
    }
}

pub use arch::*;
pub use flags::MmuFlags;
pub use page_table::{PageTable, PtQuery};
pub use pte::Pte;

/// 最小的分页大小。
///
/// 似乎任何架构都是或支持 4kiB 分页,而且对齐参数必须是字面量,所以此处直接做成常量。
pub const PAGE_SIZE: usize = 4096;

/// 页内偏移的位数
pub const OFFSET_BITS: usize = PAGE_SIZE.trailing_zeros() as _;

/// 每级页表容纳的页数
const ENTRIES_PER_TABLE: usize = PAGE_SIZE / core::mem::size_of::<usize>();

/// 每级页表的序号位数
pub const PT_LEVEL_BITS: usize = ENTRIES_PER_TABLE.trailing_zeros() as _;

/// 序号遮罩
const PT_LEVEL_MASK: usize = (1 << PT_LEVEL_BITS) - 1;

/// 物理地址。
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
#[repr(transparent)]
pub struct PPN(pub usize);

/// 虚拟地址。
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
#[repr(transparent)]
pub struct VAddr(usize);

impl VAddr {
    #[inline]
    pub const fn new(value: usize) -> Self {
        Self(value)
    }

    #[inline]
    pub const fn value(self) -> usize {
        self.0
    }
}

impl From<usize> for VAddr {
    #[inline]
    fn from(value: usize) -> Self {
        Self(value)
    }
}

impl<T> From<*const T> for VAddr {
    #[inline]
    fn from(value: *const T) -> Self {
        Self(value as _)
    }
}

impl<T> From<&T> for VAddr {
    #[inline]
    fn from(value: &T) -> Self {
        Self(value as *const _ as _)
    }
}

/// 分页元数据。
pub trait MmuMeta: Copy {
    const ADDR_MASK: usize;

    const V_ADDR_BITS: usize;
    const MAX_LEVEL: usize = calculate_max_level(Self::V_ADDR_BITS);
    const FLAG_POS_V: usize;
    const FLAG_POS_R: usize;
    const FLAG_POS_W: usize;
    const FLAG_POS_X: usize;
    const FLAG_POS_U: usize;
    const FLAG_POS_G: usize;
    const FLAG_POS_A: usize;
    const FLAG_POS_D: usize;

    fn is_leaf(value: usize) -> bool;

    #[inline]
    fn is_huge(value: usize, level: usize) -> bool {
        level < Self::MAX_LEVEL && Self::is_leaf(value)
    }

    fn ppn(value: usize) -> PPN;

    #[inline]
    fn is_valid(value: usize) -> bool {
        value & (1 << Self::FLAG_POS_D) != 0
    }

    #[inline]
    fn is_readable(value: usize) -> bool {
        value & (1 << Self::FLAG_POS_R) != 0
    }

    #[inline]
    fn is_writable(value: usize) -> bool {
        value & (1 << Self::FLAG_POS_W) != 0
    }

    #[inline]
    fn is_executable(value: usize) -> bool {
        value & (1 << Self::FLAG_POS_X) != 0
    }

    #[inline]
    fn is_user(value: usize) -> bool {
        value & (1 << Self::FLAG_POS_U) != 0
    }

    #[inline]
    fn is_global(value: usize) -> bool {
        value & (1 << Self::FLAG_POS_G) != 0
    }

    #[inline]
    fn is_accessed(value: usize) -> bool {
        value & (1 << Self::FLAG_POS_A) != 0
    }

    #[inline]
    fn is_dirty(value: usize) -> bool {
        value & (1 << Self::FLAG_POS_D) != 0
    }

    fn set_ppn(value: &mut usize, ppn: PPN);

    fn clear_ppn(value: &mut usize);
}

#[inline]
const fn calculate_max_level(v_addr_bits: usize) -> usize {
    (v_addr_bits - OFFSET_BITS + PT_LEVEL_BITS - 1) / PT_LEVEL_BITS - 1
}

use static_assertions::const_assert_eq;

const_assert_eq!(PAGE_SIZE, 4096);
const_assert_eq!(OFFSET_BITS, 12);

cfg_if::cfg_if! {
    if #[cfg(target_pointer_width = "32")] {
        const_assert_eq!(PT_LEVEL_BITS, 10);
        const_assert_eq!(ENTRIES_PER_TABLE, 1024);
        const_assert_eq!(calculate_max_level(32), 1);
    } else if #[cfg(target_pointer_width = "64")] {
        const_assert_eq!(PT_LEVEL_BITS, 9);
        const_assert_eq!(ENTRIES_PER_TABLE, 512);
        const_assert_eq!(calculate_max_level(39), 2);
        const_assert_eq!(calculate_max_level(48), 3);
        const_assert_eq!(calculate_max_level(57), 4);
    } else {
        compile_error!("Unsupported architecture");
    }
}