1use crate::{
9 arch::xshim::{PageTableFlags, PhysAddr, PhysFrame as X86PhysFrame, Size4KiB, VirtAddr},
10 x86_crate_shim::{
11 registers::control::Cr3,
12 structures::paging::{
13 FrameAllocator as X86FrameAllocator, Mapper, OffsetPageTable, Page, PageTable,
14 Translate,
15 },
16 },
17};
18
19use crate::{
20 memory::frame::{FrameAllocOptions, FramePurpose},
21 sync::SpinLock,
22};
23
24pub struct BuddyFrameAllocator;
54
55#[cfg(target_arch = "x86_64")]
60unsafe impl X86FrameAllocator<Size4KiB> for BuddyFrameAllocator {
61 fn allocate_frame(&mut self) -> Option<X86PhysFrame<Size4KiB>> {
62 let token = unsafe { crate::sync::IrqDisabledToken::token_from_trusted_context() };
68
69 let frame = FrameAllocOptions::new()
76 .purpose(FramePurpose::PageTable)
77 .allocate(&token)
78 .ok()?;
79
80 X86PhysFrame::from_start_address(frame.start_address).ok()
81 }
82}
83#[cfg(not(target_arch = "x86_64"))]
84impl crate::arch::x86_64::structures::paging::FrameAllocator<crate::arch::xshim::Size4KiB>
85 for BuddyFrameAllocator
86{
87 fn allocate_frame(
88 &mut self,
89 ) -> Option<crate::arch::xshim::PhysFrame<crate::arch::xshim::Size4KiB>> {
90 None }
92}
93
94static mut PAGING_READY: bool = false;
96
97static mut KERNEL_CR3: PhysAddr = PhysAddr::new_truncate(0);
99
100static KERNEL_PT_LOCK: SpinLock<()> = SpinLock::new(());
106
107pub fn is_initialized() -> bool {
109 unsafe { *(&raw const PAGING_READY) }
110}
111
112pub fn init(hhdm_offset: u64) {
119 let phys_offset = VirtAddr::new(hhdm_offset);
120 let (level_4_frame, _flags) = Cr3::read();
121 let level_4_phys = level_4_frame.start_address().as_u64();
122 let level_4_virt = phys_offset + level_4_phys;
123
124 unsafe {
128 let kcr3 = &raw mut KERNEL_CR3;
129 *kcr3 = level_4_frame.start_address();
130 let ready = &raw mut PAGING_READY;
131 *ready = true;
132 }
133
134 log::info!(
135 "Paging initialized: CR3={:#x}, HHDM={:#x}, L4 table @ {:#x}",
136 level_4_phys,
137 hhdm_offset,
138 level_4_virt.as_u64(),
139 );
140}
141
142pub fn map_all_ram(memory_regions: &[crate::boot::entry::MemoryRegion]) {
155 use crate::boot::entry::MemoryKind;
156
157 for region in memory_regions {
158 if matches!(region.kind, MemoryKind::Free | MemoryKind::Reclaim) {
159 log::debug!(
160 "Mapping RAM region to HHDM: phys=0x{:x}..0x{:x}",
161 region.base,
162 region.base + region.size
163 );
164 ensure_hhdm_range(
165 region.base,
166 region.size,
167 PageTableFlags::PRESENT | PageTableFlags::WRITABLE | PageTableFlags::NO_EXECUTE,
168 );
169 }
170 }
171}
172
173pub fn map_page(
177 page: Page<Size4KiB>,
178 frame: X86PhysFrame<Size4KiB>,
179 flags: PageTableFlags,
180) -> Result<(), &'static str> {
181 if !is_initialized() {
182 return Err("Paging not initialized");
183 }
184 let phys_offset = VirtAddr::new(crate::memory::hhdm_offset());
185 let (level_4_frame, _) = Cr3::read();
186 let level_4_virt = phys_offset + level_4_frame.start_address().as_u64();
187 let mapper = unsafe { &mut *level_4_virt.as_mut_ptr::<PageTable>() };
189 let mut mapper = unsafe { OffsetPageTable::new(mapper, phys_offset) };
190 let mut allocator = BuddyFrameAllocator;
191
192 unsafe {
193 mapper
194 .map_to(page, frame, flags, &mut allocator)
195 .map_err(|_| "Failed to map page")?
196 .flush();
197 }
198 Ok(())
199}
200
201pub fn map_page_kernel(
210 page: Page<Size4KiB>,
211 frame: X86PhysFrame<Size4KiB>,
212 flags: PageTableFlags,
213) -> Result<(), &'static str> {
214 if !is_initialized() {
215 return Err("Paging not initialized");
216 }
217 let _guard = KERNEL_PT_LOCK.lock();
218 let kernel_cr3 = unsafe { *(&raw const KERNEL_CR3) };
220 let phys_offset = VirtAddr::new(crate::memory::hhdm_offset());
221 let level_4_virt = phys_offset + kernel_cr3.as_u64();
222 let mapper = unsafe { &mut *level_4_virt.as_mut_ptr::<PageTable>() };
224 let mut mapper = unsafe { OffsetPageTable::new(mapper, phys_offset) };
225 let mut allocator = BuddyFrameAllocator;
226
227 unsafe {
228 mapper
229 .map_to(page, frame, flags, &mut allocator)
230 .map_err(|_| "Failed to map page (kernel)")?
231 .flush();
232 }
233 Ok(())
234}
235
236pub fn unmap_page(page: Page<Size4KiB>) -> Result<X86PhysFrame<Size4KiB>, &'static str> {
238 if !is_initialized() {
239 return Err("Paging not initialized");
240 }
241 let phys_offset = VirtAddr::new(crate::memory::hhdm_offset());
242 let (level_4_frame, _) = Cr3::read();
243 let level_4_virt = phys_offset + level_4_frame.start_address().as_u64();
244 let mapper = unsafe { &mut *level_4_virt.as_mut_ptr::<PageTable>() };
246 let mut mapper = unsafe { OffsetPageTable::new(mapper, phys_offset) };
247 let (frame, flush) = mapper.unmap(page).map_err(|_| "Failed to unmap page")?;
248 flush.flush();
249 Ok(frame)
250}
251
252pub fn unmap_page_kernel(page: Page<Size4KiB>) -> Result<X86PhysFrame<Size4KiB>, &'static str> {
258 if !is_initialized() {
259 return Err("Paging not initialized");
260 }
261 let _guard = KERNEL_PT_LOCK.lock();
262 let kernel_cr3 = unsafe { *(&raw const KERNEL_CR3) };
264 let phys_offset = VirtAddr::new(crate::memory::hhdm_offset());
265 let level_4_virt = phys_offset + kernel_cr3.as_u64();
266 let mapper = unsafe { &mut *level_4_virt.as_mut_ptr::<PageTable>() };
268 let mut mapper = unsafe { OffsetPageTable::new(mapper, phys_offset) };
269 let (frame, flush) = mapper
270 .unmap(page)
271 .map_err(|_| "Failed to unmap page (kernel)")?;
272 flush.flush();
273 Ok(frame)
274}
275
276pub fn translate(addr: VirtAddr) -> Option<PhysAddr> {
280 if !is_initialized() {
281 return None;
282 }
283 let phys_offset = VirtAddr::new(crate::memory::hhdm_offset());
284 let (level_4_frame, _) = Cr3::read();
285 let level_4_virt = phys_offset + level_4_frame.start_address().as_u64();
286 let mapper = unsafe { &mut *level_4_virt.as_mut_ptr::<PageTable>() };
288 let mapper = unsafe { OffsetPageTable::new(mapper, phys_offset) };
289 mapper.translate_addr(addr)
290}
291
292fn translate_via_active_page_tables(addr: VirtAddr) -> Option<PhysAddr> {
293 let hhdm = crate::memory::hhdm_offset();
294 let (level_4_frame, _) = Cr3::read();
295
296 unsafe {
297 let l4_ptr = (level_4_frame.start_address().as_u64() + hhdm) as *const u64;
298 let l4e = *l4_ptr.add(((addr.as_u64() >> 39) & 0x1FF) as usize);
299 if l4e & 1 == 0 {
300 return None;
301 }
302
303 let l3_ptr = ((l4e & 0x000F_FFFF_FFFF_F000) + hhdm) as *const u64;
304 let l3e = *l3_ptr.add(((addr.as_u64() >> 30) & 0x1FF) as usize);
305 if l3e & 1 == 0 {
306 return None;
307 }
308 if l3e & 0x80 != 0 {
309 return Some(PhysAddr::new(
310 (l3e & 0x000F_FFFF_C000_0000) + (addr.as_u64() & 0x3FFF_FFFF),
311 ));
312 }
313
314 let l2_ptr = ((l3e & 0x000F_FFFF_FFFF_F000) + hhdm) as *const u64;
315 let l2e = *l2_ptr.add(((addr.as_u64() >> 21) & 0x1FF) as usize);
316 if l2e & 1 == 0 {
317 return None;
318 }
319 if l2e & 0x80 != 0 {
320 return Some(PhysAddr::new(
321 (l2e & 0x000F_FFFF_FFE0_0000) + (addr.as_u64() & 0x1F_FFFF),
322 ));
323 }
324
325 let l1_ptr = ((l2e & 0x000F_FFFF_FFFF_F000) + hhdm) as *const u64;
326 let l1e = *l1_ptr.add(((addr.as_u64() >> 12) & 0x1FF) as usize);
327 if l1e & 1 == 0 {
328 return None;
329 }
330
331 Some(PhysAddr::new(
332 (l1e & 0x000F_FFFF_FFFF_F000) + (addr.as_u64() & 0xFFF),
333 ))
334 }
335}
336
337pub fn is_hhdm_range_mapped_now(phys_base: u64, size: u64) -> bool {
343 if size == 0 {
344 return true;
345 }
346
347 let start = phys_base & !0xFFF;
348 let end = phys_base.saturating_add(size).saturating_add(0xFFF) & !0xFFF;
349
350 crate::e9_mark!(b'*');
351 let mut phys = start;
352 while phys < end {
353 let virt = VirtAddr::new(crate::memory::phys_to_virt(phys));
354 let mapped = match translate_via_active_page_tables(virt) {
355 Some(m) => m,
356 None => return false,
357 };
358 if mapped.as_u64() != phys {
359 return false;
360 }
361 phys = phys.saturating_add(4096);
362 }
363 crate::e9_mark!(b'+');
364 true
365}
366
367pub fn active_page_table() -> PhysAddr {
369 let (frame, _) = Cr3::read();
370 frame.start_address()
371}
372
373pub fn kernel_l4_phys() -> PhysAddr {
378 unsafe { *(&raw const KERNEL_CR3) }
380}
381
382pub fn ensure_identity_map(phys_addr: u64) {
388 let virt_addr = crate::memory::phys_to_virt(phys_addr);
389 let page = Page::<Size4KiB>::containing_address(VirtAddr::new(virt_addr));
390 let frame = X86PhysFrame::<Size4KiB>::containing_address(PhysAddr::new(phys_addr));
391
392 if translate(VirtAddr::new(virt_addr)).is_none() {
393 log::debug!(
394 "Identity mapping missing page: {:#x} -> {:#x}",
395 phys_addr,
396 virt_addr
397 );
398 let flags = uncached_hhdm_flags();
399 if let Err(e) = map_page(page, frame, flags) {
400 log::error!("Failed to identity map {:#x}: {}", phys_addr, e);
401 }
402 }
403}
404
405pub fn ensure_identity_map_range(phys_base: u64, size: u64) {
410 ensure_hhdm_range(phys_base, size, uncached_hhdm_flags());
411}
412
413fn uncached_hhdm_flags() -> PageTableFlags {
414 let flags = PageTableFlags::PRESENT
415 | PageTableFlags::WRITABLE
416 | PageTableFlags::NO_EXECUTE
417 | PageTableFlags::NO_CACHE;
418 #[cfg(target_arch = "x86_64")]
419 let flags = flags | PageTableFlags::WRITE_THROUGH; flags
421}
422
423fn ensure_hhdm_range(phys_base: u64, size: u64, flags: PageTableFlags) {
426 if size == 0 || !is_initialized() {
427 return;
428 }
429
430 let page_size = 4096u64;
431 let start = phys_base & !(page_size - 1);
432 let end = (phys_base.saturating_add(size).saturating_add(page_size - 1)) & !(page_size - 1);
433 if start >= end {
434 return;
435 }
436
437 let phys_offset = VirtAddr::new(crate::memory::hhdm_offset());
438 let (level_4_frame, _) = Cr3::read();
439 let level_4_virt = phys_offset + level_4_frame.start_address().as_u64();
440
441 let l4_table = unsafe { &mut *level_4_virt.as_mut_ptr::<PageTable>() };
443 let mut mapper = unsafe { OffsetPageTable::new(l4_table, phys_offset) };
444 let mut allocator = BuddyFrameAllocator;
445
446 let mut mapped_count: u64 = 0;
447 let mut p = start;
448 while p < end {
449 let virt = VirtAddr::new(crate::memory::phys_to_virt(p));
450 if mapper.translate_addr(virt).is_none() {
452 let page = Page::<Size4KiB>::containing_address(virt);
453 let frame = X86PhysFrame::<Size4KiB>::containing_address(PhysAddr::new(p));
454 match unsafe { mapper.map_to(page, frame, flags, &mut allocator) } {
456 Ok(flush) => {
457 flush.flush();
458 mapped_count += 1;
459 }
460 Err(_) => {
461 log::error!("ensure_identity_map_range: failed to map {:#x}", p);
462 }
463 }
464 }
465 p = p.saturating_add(page_size);
466 }
467
468 if mapped_count > 0 {
469 log::debug!(
470 "Identity mapped {} pages: phys {:#x}..{:#x}",
471 mapped_count,
472 start,
473 end,
474 );
475 }
476}
477
478#[cfg(target_arch = "x86_64")]
485pub unsafe fn retire_uefi_identity_code() {
486 const ADDRESS: u64 = 0x000F_FFFF_FFFF_F000;
487 const NX: u64 = 1 << 63;
488 unsafe fn retire(table_phys: u64, level: u8) {
489 let table = crate::memory::phys_to_virt(table_phys) as *mut u64;
490 for index in 0..512 {
491 let slot = unsafe { table.add(index) };
492 let entry = unsafe { slot.read_volatile() };
493 if entry & 1 == 0 {
494 continue;
495 }
496 if level == 1 || entry & (1 << 7) != 0 {
497 if entry & NX == 0 {
498 unsafe { slot.write_volatile(entry | NX | 2) };
499 }
500 } else {
501 unsafe { retire(entry & ADDRESS, level - 1) };
502 }
503 }
504 }
505 let cr3: u64;
506 unsafe {
507 core::arch::asm!("mov {}, cr3", out(reg) cr3, options(nomem, nostack, preserves_flags))
508 };
509 let root = crate::memory::phys_to_virt(cr3 & ADDRESS) as *const u64;
510 let identity = unsafe { root.read_volatile() };
511 if identity & 1 != 0 {
512 unsafe { retire(identity & ADDRESS, 3) };
513 }
514 unsafe { core::arch::asm!("mov cr3, {}", in(reg) cr3, options(nostack, preserves_flags)) };
515}
516
517#[cfg(target_arch = "x86_64")]
521pub fn set_trampoline_execution(physical: u64, executable: bool) -> Result<(), &'static str> {
522 if physical != 0x8000 {
523 return Err("unexpected AP trampoline page");
524 }
525 let _guard = KERNEL_PT_LOCK.lock();
526 let (frame, _) = Cr3::read();
527 let mut table = frame.start_address().as_u64();
528 const ADDRESS: u64 = 0x000F_FFFF_FFFF_F000;
529 for shift in [39, 30, 21, 12] {
530 let slot = (crate::memory::phys_to_virt(table) as *mut u64)
531 .wrapping_add(((physical >> shift) & 511) as usize);
532 let entry = unsafe { slot.read_volatile() };
533 if entry & 1 == 0 {
534 return Err("missing AP identity page");
535 }
536 if shift != 12 && entry & (1 << 7) != 0 {
537 return if executable && entry & (1 << 63) == 0 {
540 Ok(())
541 } else {
542 Err("AP identity mapping requires a 4 KiB leaf")
543 };
544 }
545 if shift == 12 {
546 if entry & ADDRESS != physical {
547 return Err("AP identity mapping collision");
548 }
549 unsafe {
550 let updated = if executable {
551 entry & !((1 << 63) | 2)
552 } else {
553 entry | (1 << 63)
554 }; slot.write_volatile(updated);
556 core::arch::asm!("invlpg [{}]", in(reg) physical, options(nostack, preserves_flags));
557 }
558 return Ok(());
559 }
560 table = entry & ADDRESS;
561 }
562 Err("invalid AP identity mapping")
563}