1#![allow(dead_code)]
3extern crate alloc;
4use alloc::boxed::Box;
5
6#[repr(C, align(4096))]
8struct PageTables {
9 l1_table: [u64; 512], l2_table_0: [u64; 512], l2_table_1: [u64; 512], }
13
14static mut TABLES: PageTables = PageTables {
15 l1_table: [0; 512],
16 l2_table_0: [0; 512],
17 l2_table_1: [0; 512],
18};
19
20pub const DEVICE_REGION_START: usize = 0x3800_0000;
31
32pub fn init() {
34 crate::info!("[MMU] Initializing MMU (4KB Page Size, Identity Mapping)...");
35
36 unsafe {
37 for i in 0..512 {
39 TABLES.l1_table[i] = 0;
40 TABLES.l2_table_0[i] = 0;
41 TABLES.l2_table_1[i] = 0;
42 }
43
44 let l2_phys_addr_0 = &TABLES.l2_table_0 as *const u64 as u64;
46 let l2_phys_addr_1 = &TABLES.l2_table_1 as *const u64 as u64;
47 TABLES.l1_table[0] = (l2_phys_addr_0 & 0x0000_FFFF_FFFF_F000) | 0b11;
48 TABLES.l1_table[1] = (l2_phys_addr_1 & 0x0000_FFFF_FFFF_F000) | 0b11;
49
50 for i in 0..1024 {
52 let phys_block_addr = (i as u64) * 0x0020_0000;
53
54 let mut desc = phys_block_addr & 0x0000_FFFF_FFFF_F000;
55 desc |= 1 << 10; desc |= 3 << 8; desc |= 0 << 6; if phys_block_addr >= DEVICE_REGION_START as u64 {
64 desc |= 1 << 2; desc |= 1 << 54; desc |= 1 << 53; desc |= 0b01; } else {
70 desc |= 0 << 2; desc |= 0b01; }
74
75 if i < 512 {
76 TABLES.l2_table_0[i] = desc;
77 } else {
78 TABLES.l2_table_1[i - 512] = desc;
79 }
80 }
81
82 let mair: u64 = 0xFF | (0x04 << 8);
86 core::arch::asm!("msr mair_el1, {}", in(reg) mair);
87
88 let tcr: u64 = (3 << 12) | (1 << 10) | (1 << 8) | 25 | (1 << 32); core::arch::asm!("msr tcr_el1, {}", in(reg) tcr);
100
101 let l1_phys_addr = &TABLES.l1_table as *const u64 as u64;
103 core::arch::asm!("msr ttbr0_el1, {}", in(reg) l1_phys_addr);
104
105 core::arch::asm!("tlbi vmalle1is", "dsb sy", "isb", options(nostack));
107
108 let mut sctlr: u64;
112 core::arch::asm!("mrs {}, sctlr_el1", out(reg) sctlr);
113 sctlr |= 1 << 0; sctlr &= !(1 << 2); sctlr |= 1 << 12; core::arch::asm!("msr sctlr_el1, {}", in(reg) sctlr);
117
118 core::arch::asm!("isb", "dsb sy", options(nostack));
120
121 clean_dcache_range(
124 &TABLES as *const PageTables as usize,
125 core::mem::size_of::<PageTables>(),
126 );
127
128 core::arch::asm!("mrs {}, sctlr_el1", out(reg) sctlr);
130 sctlr |= 1 << 2; core::arch::asm!("msr sctlr_el1, {}", in(reg) sctlr);
132
133 core::arch::asm!("isb", "dsb sy", options(nostack));
135 }
136
137 crate::info!("[MMU] MMU and Caching successfully enabled!");
138}
139
140pub fn init_secondary() {
142 unsafe {
143 let mair: u64 = 0xFF | (0x04 << 8);
144 core::arch::asm!("msr mair_el1, {}", in(reg) mair);
145
146 let tcr: u64 = (3 << 12) | (1 << 10) | (1 << 8) | 25 | (1 << 32);
148 core::arch::asm!("msr tcr_el1, {}", in(reg) tcr);
149
150 let l1_phys_addr = &TABLES.l1_table as *const u64 as u64;
151 core::arch::asm!("msr ttbr0_el1, {}", in(reg) l1_phys_addr);
152
153 let mut sctlr: u64;
154 core::arch::asm!("mrs {}, sctlr_el1", out(reg) sctlr);
155 sctlr |= 1 << 0;
156 sctlr &= !(1 << 2); sctlr |= 1 << 12;
158 core::arch::asm!("msr sctlr_el1, {}", in(reg) sctlr);
159
160 core::arch::asm!("isb", "dsb sy", options(nostack));
161
162 core::arch::asm!("mrs {}, sctlr_el1", out(reg) sctlr);
164 sctlr |= 1 << 2;
165 core::arch::asm!("msr sctlr_el1, {}", in(reg) sctlr);
166
167 core::arch::asm!("isb", "dsb sy", options(nostack));
168 }
169}
170
171pub fn get_kernel_page_table() -> u64 {
172 unsafe { core::ptr::addr_of!(TABLES.l1_table) as *const u64 as u64 }
173}
174
175pub fn create_process_page_table() -> u64 {
176 get_kernel_page_table()
177}
178
179pub fn create_user_page_table() -> u64 {
180 create_page_table_internal(false)
181}
182
183fn create_page_table_internal(is_kernel: bool) -> u64 {
184 unsafe {
185 let mut pt = Box::new(PageTables {
186 l1_table: [0; 512],
187 l2_table_0: [0; 512],
188 l2_table_1: [0; 512],
189 });
190
191 for i in 0..512 {
193 pt.l1_table[i] = TABLES.l1_table[i];
194 pt.l2_table_0[i] = TABLES.l2_table_0[i];
195 pt.l2_table_1[i] = TABLES.l2_table_1[i];
196 }
197
198 if !is_kernel {
200 for i in 0..1024 {
201 let phys_block_addr = (i as u64) * 0x0020_0000;
202 let table = if i < 512 {
203 &mut pt.l2_table_0
204 } else {
205 &mut pt.l2_table_1
206 };
207 let idx = if i < 512 { i } else { i - 512 };
208
209 if phys_block_addr >= DEVICE_REGION_START as u64 {
210 let mut desc = phys_block_addr & 0x0000_FFFF_FFFF_F000;
215 desc |= 1 << 10; desc |= 3 << 8; desc |= 0 << 6; desc |= 1 << 2; desc |= 1 << 54; desc |= 1 << 53; desc |= 0b01; table[idx] = desc;
223 } else if phys_block_addr < 0x0060_0000 {
224 let mut desc = phys_block_addr & 0x0000_FFFF_FFFF_F000;
231 desc |= 1 << 10; desc |= 3 << 8; desc |= 0 << 6; desc |= 0 << 2; desc |= 0b01; table[idx] = desc;
237 } else {
238 let mut desc = phys_block_addr & 0x0000_FFFF_FFFF_F000;
241 desc |= 1 << 10; desc |= 3 << 8; desc |= 1 << 6; desc |= 0 << 2; desc |= 0b01; table[idx] = desc;
247 }
248 }
249 }
250
251 let l2_phys_addr_0 = pt.l2_table_0.as_ptr() as u64;
252 let l2_phys_addr_1 = pt.l2_table_1.as_ptr() as u64;
253 pt.l1_table[0] = (l2_phys_addr_0 & 0x0000_FFFF_FFFF_F000) | 0b11;
254 pt.l1_table[1] = (l2_phys_addr_1 & 0x0000_FFFF_FFFF_F000) | 0b11;
255
256 let raw_ptr = Box::into_raw(pt);
257
258 clean_dcache_range(raw_ptr as usize, core::mem::size_of::<PageTables>());
260
261 raw_ptr as u64
262 }
263}
264
265pub fn map_page(pt_addr: u64, virt_addr: u64, phys_addr: u64) {
266 unsafe {
267 let pt = &mut *(pt_addr as *mut PageTables);
268 let l2_index_global = (virt_addr / 0x0020_0000) as usize; let mut desc = phys_addr & 0x0000_FFFF_FFFF_F000;
271 desc |= 1 << 10; desc |= 3 << 8; desc |= 0 << 6; desc |= 0 << 2; desc |= 0b01; let entry_addr;
278 if l2_index_global < 512 {
279 pt.l2_table_0[l2_index_global] = desc;
280 entry_addr = &pt.l2_table_0[l2_index_global] as *const u64 as usize;
281 } else {
282 pt.l2_table_1[l2_index_global - 512] = desc;
283 entry_addr = &pt.l2_table_1[l2_index_global - 512] as *const u64 as usize;
284 }
285
286 clean_dcache_range(entry_addr, 8);
288 }
289}
290
291pub fn clean_dcache_range(addr: usize, size: usize) {
293 let cache_line_size = 64; let start = addr & !(cache_line_size - 1);
295 let end = addr + size;
296 let mut current = start;
297
298 while current < end {
299 unsafe {
300 core::arch::asm!(
301 "dc civac, {}",
302 in(reg) current,
303 options(nostack)
304 );
305 }
306 current += cache_line_size;
307 }
308
309 unsafe {
310 core::arch::asm!("dsb sy", "isb", options(nostack));
311 }
312}
313
314pub fn invalidate_dcache_range(addr: usize, size: usize) {
315 let cache_line_size = 64; let start = addr & !(cache_line_size - 1);
317 let end = addr + size;
318 let mut current = start;
319
320 while current < end {
321 unsafe {
322 core::arch::asm!(
323 "dc ivac, {}",
324 in(reg) current,
325 options(nostack)
326 );
327 }
328 current += cache_line_size;
329 }
330
331 unsafe {
332 core::arch::asm!("dsb sy", "isb", options(nostack));
333 }
334}