diff --git a/src/main.rs b/src/main.rs index 965fc680..ae9f24fc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -11,8 +11,8 @@ entry_point!(kernel_main); #[cfg(not(test))] fn kernel_main(boot_info: &'static BootInfo) -> ! { use blog_os::interrupts::PICS; - use blog_os::memory::active_level_4_table; - use x86_64::{structures::paging::PageTable, VirtAddr}; + use blog_os::memory::translate_addr; + use x86_64::VirtAddr; println!("Hello World{}", "!"); @@ -21,24 +21,21 @@ fn kernel_main(boot_info: &'static BootInfo) -> ! { unsafe { PICS.lock().initialize() }; x86_64::instructions::interrupts::enable(); - let l4_table = unsafe { active_level_4_table(boot_info.physical_memory_offset) }; - for (i, entry) in l4_table.iter().enumerate() { - if !entry.is_unused() { - println!("L4 Entry {}: {:?}", i, entry); + let addresses = [ + // the identity-mapped vga buffer page + 0xb8000, + // some code page + 0x20010a, + // some stack page + 0x57ac_001f_fe48, + // virtual address mapped to physical address 0 + boot_info.physical_memory_offset, + ]; - // get the physical address from the entry and convert it - let phys = entry.frame().unwrap().start_address(); - let virt = phys.as_u64() + boot_info.physical_memory_offset; - let ptr = VirtAddr::new(virt).as_mut_ptr(); - let l3_table: &PageTable = unsafe { &*ptr }; - - // print non-empty entries of the level 3 table - for (i, entry) in l3_table.iter().enumerate() { - if !entry.is_unused() { - println!(" L3 Entry {}: {:?}", i, entry); - } - } - } + for &address in &addresses { + let virt = VirtAddr::new(address); + let phys = unsafe { translate_addr(virt, boot_info.physical_memory_offset) }; + println!("{:?} -> {:?}", virt, phys); } println!("It did not crash!"); diff --git a/src/memory.rs b/src/memory.rs index a7c85335..97fb34a7 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -1,4 +1,4 @@ -use x86_64::structures::paging::PageTable; +use x86_64::{structures::paging::PageTable, PhysAddr, VirtAddr}; /// Returns a mutable reference to the active level 4 table. /// @@ -17,3 +17,53 @@ pub unsafe fn active_level_4_table(physical_memory_offset: u64) -> &'static mut &mut *page_table_ptr // unsafe } + +/// Translates the given virtual address to the mapped physical address, or +/// `None` if the address is not mapped. +/// +/// This function is unsafe because the caller must guarantee that the +/// complete physical memory is mapped to virtual memory at the passed +/// `physical_memory_offset`. +pub unsafe fn translate_addr(addr: VirtAddr, physical_memory_offset: u64) -> Option { + translate_addr_inner(addr, physical_memory_offset) +} + +/// Private function that is called by `translate_addr`. +/// +/// This function is safe to limit the scope of `unsafe` because Rust treats +/// the whole body of unsafe functions as an unsafe block. This function must +/// only be reachable through `unsafe fn` from outside of this module. +fn translate_addr_inner(addr: VirtAddr, physical_memory_offset: u64) -> Option { + use x86_64::registers::control::Cr3; + use x86_64::structures::paging::page_table::FrameError; + + // read the active level 4 frame from the CR3 register + let (level_4_table_frame, _) = Cr3::read(); + + let table_indexes = [ + addr.p4_index(), + addr.p3_index(), + addr.p2_index(), + addr.p1_index(), + ]; + let mut frame = level_4_table_frame; + + // traverse the multi-level page table + for &index in &table_indexes { + // convert the frame into a page table reference + let virt = frame.start_address().as_u64() + physical_memory_offset; + let table_ptr: *const PageTable = VirtAddr::new(virt).as_ptr(); + let table = unsafe { &*table_ptr }; + + // read the page table entry and update `frame` + let entry = &table[index]; + frame = match entry.frame() { + Ok(frame) => frame, + Err(FrameError::FrameNotPresent) => return None, + Err(FrameError::HugeFrame) => panic!("huge pages not supported"), + }; + } + + // calculate the physical address by adding the page offset + Some(frame.start_address() + u64::from(addr.page_offset())) +}