From ef1cc0ed4fa38877789b81d31a5366fdb40ddf25 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Thu, 14 Mar 2019 10:20:46 +0100 Subject: [PATCH 01/18] Reset code to post-09 branch for new 'Paging Implementation' post --- README.md | 8 +++--- src/lib.rs | 1 - src/main.rs | 18 ++++++------ src/memory.rs | 79 --------------------------------------------------- 4 files changed, 12 insertions(+), 94 deletions(-) delete mode 100644 src/memory.rs diff --git a/README.md b/README.md index a4087f75..d6de5af0 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ -# Blog OS (Advanced Paging) +# Blog OS (Introduction to Paging) -[![Build Status](https://travis-ci.org/phil-opp/blog_os.svg?branch=post-10)](https://travis-ci.org/phil-opp/blog_os/branches) +[![Build Status](https://travis-ci.org/phil-opp/blog_os.svg?branch=post-09)](https://travis-ci.org/phil-opp/blog_os/branches) -This repository contains the source code for the [Advanced Paging][post] post of the [Writing an OS in Rust](https://os.phil-opp.com) series. +This repository contains the source code for the [Introduction to Paging][post] post of the [Writing an OS in Rust](https://os.phil-opp.com) series. -[post]: https://os.phil-opp.com/advanced-paging/ +[post]: https://os.phil-opp.com/paging-introduction/ **Check out the [master branch](https://github.com/phil-opp/blog_os) for more information.** diff --git a/src/lib.rs b/src/lib.rs index 5974e73c..34debc4f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,7 +3,6 @@ pub mod gdt; pub mod interrupts; -pub mod memory; pub mod serial; pub mod vga_buffer; diff --git a/src/main.rs b/src/main.rs index a6493875..c10b0954 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,15 +3,13 @@ #![cfg_attr(test, allow(unused_imports))] use blog_os::println; -use bootloader::{bootinfo::BootInfo, entry_point}; use core::panic::PanicInfo; -entry_point!(kernel_main); - #[cfg(not(test))] -fn kernel_main(boot_info: &'static BootInfo) -> ! { +#[no_mangle] +pub extern "C" fn _start() -> ! { use blog_os::interrupts::PICS; - use blog_os::memory::{self, create_example_mapping}; + use x86_64::registers::control::Cr3; println!("Hello World{}", "!"); @@ -20,11 +18,11 @@ fn kernel_main(boot_info: &'static BootInfo) -> ! { unsafe { PICS.lock().initialize() }; x86_64::instructions::interrupts::enable(); - let mut recursive_page_table = unsafe { memory::init(boot_info.p4_table_addr as usize) }; - let mut frame_allocator = memory::init_frame_allocator(&boot_info.memory_map); - - create_example_mapping(&mut recursive_page_table, &mut frame_allocator); - unsafe { (0xdeadbeaf900 as *mut u64).write_volatile(0xf021f077f065f04e) }; + let (level_4_page_table, _) = Cr3::read(); + println!( + "Level 4 page table at: {:?}", + level_4_page_table.start_address() + ); println!("It did not crash!"); blog_os::hlt_loop(); diff --git a/src/memory.rs b/src/memory.rs deleted file mode 100644 index ddf643be..00000000 --- a/src/memory.rs +++ /dev/null @@ -1,79 +0,0 @@ -use bootloader::bootinfo::{MemoryMap, MemoryRegionType}; -use x86_64::structures::paging::{ - FrameAllocator, Mapper, Page, PageTable, PhysFrame, RecursivePageTable, Size4KiB, -}; -use x86_64::{PhysAddr, VirtAddr}; - -/// Creates a RecursivePageTable instance from the level 4 address. -/// -/// This function is unsafe because it can break memory safety if an invalid -/// address is passed. -pub unsafe fn init(level_4_table_addr: usize) -> RecursivePageTable<'static> { - /// Rust currently treats the whole body of unsafe functions as an unsafe - /// block, which makes it difficult to see which operations are unsafe. To - /// limit the scope of unsafe we use a safe inner function. - fn init_inner(level_4_table_addr: usize) -> RecursivePageTable<'static> { - let level_4_table_ptr = level_4_table_addr as *mut PageTable; - let level_4_table = unsafe { &mut *level_4_table_ptr }; - RecursivePageTable::new(level_4_table).unwrap() - } - - init_inner(level_4_table_addr) -} - -/// Create a FrameAllocator from the passed memory map -pub fn init_frame_allocator( - memory_map: &'static MemoryMap, -) -> BootInfoFrameAllocator> { - // get usable regions from memory map - let regions = memory_map - .iter() - .filter(|r| r.region_type == MemoryRegionType::Usable); - // map each region to its address range - let addr_ranges = regions.map(|r| r.range.start_addr()..r.range.end_addr()); - // transform to an iterator of frame start addresses - let frame_addresses = addr_ranges.flat_map(|r| r.into_iter().step_by(4096)); - // create `PhysFrame` types from the start addresses - let frames = frame_addresses.map(|addr| PhysFrame::containing_address(PhysAddr::new(addr))); - - BootInfoFrameAllocator { frames } -} - -pub fn create_example_mapping( - recursive_page_table: &mut RecursivePageTable, - frame_allocator: &mut impl FrameAllocator, -) { - use x86_64::structures::paging::PageTableFlags as Flags; - - let page: Page = Page::containing_address(VirtAddr::new(0xdeadbeaf000)); - let frame = PhysFrame::containing_address(PhysAddr::new(0xb8000)); - let flags = Flags::PRESENT | Flags::WRITABLE; - - let map_to_result = unsafe { recursive_page_table.map_to(page, frame, flags, frame_allocator) }; - map_to_result.expect("map_to failed").flush(); -} - -/// A FrameAllocator that always returns `None`. -pub struct EmptyFrameAllocator; - -impl FrameAllocator for EmptyFrameAllocator { - fn allocate_frame(&mut self) -> Option { - None - } -} - -pub struct BootInfoFrameAllocator -where - I: Iterator, -{ - frames: I, -} - -impl FrameAllocator for BootInfoFrameAllocator -where - I: Iterator, -{ - fn allocate_frame(&mut self) -> Option { - self.frames.next() - } -} From 59da6e56200c692e96e0e1c9455770da3f58c636 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Tue, 12 Mar 2019 17:52:57 +0100 Subject: [PATCH 02/18] Update `bootloader` to version 0.4.0 --- Cargo.lock | 22 ++++++++++++++++++---- Cargo.toml | 2 +- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f676a90a..83581011 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -31,7 +31,7 @@ name = "blog_os" version = "0.1.0" dependencies = [ "array-init 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "bootloader 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)", + "bootloader 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "pc-keyboard 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "pic8259_simple 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -43,13 +43,13 @@ dependencies = [ [[package]] name = "bootloader" -version = "0.3.12" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "fixedvec 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "font8x8 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", "usize_conversions 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "x86_64 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", + "x86_64 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "xmas-elf 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -297,6 +297,19 @@ dependencies = [ "ux 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "x86_64" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "array-init 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "bit_field 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "os_bootinfo 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "usize_conversions 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ux 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "x86_64" version = "0.5.2" @@ -328,7 +341,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum array-init 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "23589ecb866b460d3a0f1278834750268c607e8e28a1b982c907219f3178cd72" "checksum bit_field 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ed8765909f9009617974ab6b7d332625b320b33c326b1e9321382ef1999b5d56" "checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12" -"checksum bootloader 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)" = "2b498d7168288f3667f80aee93b4894e355dfce867803e1ccd5d9ee42a0b0e1a" +"checksum bootloader 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a0808c55da932b82d3ababdaa0caa3f18522c5d2d06309b98f73adda849a3f03" "checksum cc 1.0.30 (registry+https://github.com/rust-lang/crates.io-index)" = "d01c69d08ff207f231f07196e30f84c70f1c815b04f980f8b7b01ff01f05eb92" "checksum cpuio 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "22b8e308ccfc5acf3b82f79c0eac444cf6114cb2ac67a230ca6c177210068daa" "checksum fixedvec 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7c6c16d316ccdac21a4dd648e314e76facbbaf316e83ca137d0857a9c07419d0" @@ -364,6 +377,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" "checksum x86_64 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "2bd647af1614659e1febec1d681231aea4ebda4818bf55a578aff02f3e4db4b4" +"checksum x86_64 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "f9258d7e2dd25008d69e8c9e9ee37865887a5e1e3d06a62f1cb3f6c209e6f177" "checksum x86_64 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "de155d368de1d32afc8f90838bf81986e4bd43a0cd5fcd7f7e9c85cb8f51dc7c" "checksum xmas-elf 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "22678df5df766e8d1e5d609da69f0c3132d794edf6ab5e75e7abcd2270d4cf58" "checksum zero 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5f1bc8a6b2005884962297587045002d8cfb8dcec9db332f4ca216ddc5de82c5" diff --git a/Cargo.toml b/Cargo.toml index c3be061b..99a1d57c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,7 @@ authors = ["Philipp Oppermann "] edition = "2018" [dependencies] -bootloader = "0.3.12" +bootloader = "0.4.0" volatile = "0.2.3" spin = "0.4.9" uart_16550 = "0.1.0" From d5abc119f32da1dfd3f1adee0210b76e62ebe5fd Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Wed, 13 Mar 2019 11:04:37 +0100 Subject: [PATCH 03/18] Update Readme for `Paging Implementation` post --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index d6de5af0..7a6724cb 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ -# Blog OS (Introduction to Paging) +# Blog OS (Paging Implementation) -[![Build Status](https://travis-ci.org/phil-opp/blog_os.svg?branch=post-09)](https://travis-ci.org/phil-opp/blog_os/branches) +[![Build Status](https://travis-ci.org/phil-opp/blog_os.svg?branch=post-10)](https://travis-ci.org/phil-opp/blog_os/branches) -This repository contains the source code for the [Introduction to Paging][post] post of the [Writing an OS in Rust](https://os.phil-opp.com) series. +This repository contains the source code for the [Paging Implementation][post] post of the [Writing an OS in Rust](https://os.phil-opp.com) series. -[post]: https://os.phil-opp.com/paging-introduction/ +[post]: https://os.phil-opp.com/paging-implementation/ **Check out the [master branch](https://github.com/phil-opp/blog_os) for more information.** From e387c0b6b8fa5af22e6854f9e33f2531297608ab Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Wed, 13 Mar 2019 13:09:58 +0100 Subject: [PATCH 04/18] Create a memory::active_level_4_table function --- src/lib.rs | 1 + src/memory.rs | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+) create mode 100644 src/memory.rs diff --git a/src/lib.rs b/src/lib.rs index 34debc4f..5974e73c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,6 +3,7 @@ pub mod gdt; pub mod interrupts; +pub mod memory; pub mod serial; pub mod vga_buffer; diff --git a/src/memory.rs b/src/memory.rs new file mode 100644 index 00000000..a7c85335 --- /dev/null +++ b/src/memory.rs @@ -0,0 +1,19 @@ +use x86_64::structures::paging::PageTable; + +/// Returns a mutable reference to the active level 4 table. +/// +/// 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`. Also, this function must be only called once +/// to avoid aliasing `&mut` references (which is undefined behavior). +pub unsafe fn active_level_4_table(physical_memory_offset: u64) -> &'static mut PageTable { + use x86_64::{registers::control::Cr3, VirtAddr}; + + let (level_4_table_frame, _) = Cr3::read(); + + let phys = level_4_table_frame.start_address(); + let virt = VirtAddr::new(phys.as_u64() + physical_memory_offset); + let page_table_ptr: *mut PageTable = virt.as_mut_ptr(); + + &mut *page_table_ptr // unsafe +} From 7b7d19592f9e1c36b93e36056f87f8ac21ae2466 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Wed, 13 Mar 2019 13:50:54 +0100 Subject: [PATCH 05/18] Enable `map_physical_memory` feature of bootloader --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 99a1d57c..a43c8998 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,7 @@ authors = ["Philipp Oppermann "] edition = "2018" [dependencies] -bootloader = "0.4.0" +bootloader = { version = "0.4.0", features = ["map_physical_memory"]} volatile = "0.2.3" spin = "0.4.9" uart_16550 = "0.1.0" From e1ec5159b849eaf7068f40e39d70036b7e2fb270 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Wed, 13 Mar 2019 13:51:10 +0100 Subject: [PATCH 06/18] Add boot info argument and use entry_point macro --- src/main.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main.rs b/src/main.rs index c10b0954..98e3b337 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,11 +3,13 @@ #![cfg_attr(test, allow(unused_imports))] use blog_os::println; +use bootloader::{entry_point, BootInfo}; use core::panic::PanicInfo; +entry_point!(kernel_main); + #[cfg(not(test))] -#[no_mangle] -pub extern "C" fn _start() -> ! { +fn kernel_main(boot_info: &'static BootInfo) -> ! { use blog_os::interrupts::PICS; use x86_64::registers::control::Cr3; From 61683bccdab5b49000d3b81ddba805994c74ac9f Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Wed, 13 Mar 2019 13:52:16 +0100 Subject: [PATCH 07/18] Print non-empty level 4 table entries --- src/main.rs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/main.rs b/src/main.rs index 98e3b337..5445717a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -11,7 +11,7 @@ entry_point!(kernel_main); #[cfg(not(test))] fn kernel_main(boot_info: &'static BootInfo) -> ! { use blog_os::interrupts::PICS; - use x86_64::registers::control::Cr3; + use blog_os::memory::active_level_4_table; println!("Hello World{}", "!"); @@ -20,11 +20,12 @@ fn kernel_main(boot_info: &'static BootInfo) -> ! { unsafe { PICS.lock().initialize() }; x86_64::instructions::interrupts::enable(); - let (level_4_page_table, _) = Cr3::read(); - println!( - "Level 4 page table at: {:?}", - level_4_page_table.start_address() - ); + 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); + } + } println!("It did not crash!"); blog_os::hlt_loop(); From 7c30d62f33703e06541def54b186c2a6ce9b4d5a Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Wed, 13 Mar 2019 13:56:09 +0100 Subject: [PATCH 08/18] Also show non-empty level 3 table entries --- src/main.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/main.rs b/src/main.rs index 5445717a..965fc680 100644 --- a/src/main.rs +++ b/src/main.rs @@ -12,6 +12,7 @@ entry_point!(kernel_main); 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}; println!("Hello World{}", "!"); @@ -24,6 +25,19 @@ fn kernel_main(boot_info: &'static BootInfo) -> ! { for (i, entry) in l4_table.iter().enumerate() { if !entry.is_unused() { println!("L4 Entry {}: {:?}", i, entry); + + // 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); + } + } } } From 9335386928e88c4af6b6dc66bd03bcd276dbaa4d Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Wed, 13 Mar 2019 14:03:34 +0100 Subject: [PATCH 09/18] Add and test a `memory::translate_addr` function --- src/main.rs | 35 ++++++++++++++++------------------ src/memory.rs | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 67 insertions(+), 20 deletions(-) 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())) +} From 98b597665646322c01213e299f64a20e167cb616 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Wed, 13 Mar 2019 14:08:43 +0100 Subject: [PATCH 10/18] Create a memory::init function that initializes a MappedPageTable --- src/memory.rs | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/src/memory.rs b/src/memory.rs index 97fb34a7..be4433b8 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -1,4 +1,23 @@ -use x86_64::{structures::paging::PageTable, PhysAddr, VirtAddr}; +use x86_64::{ + structures::paging::{MappedPageTable, MapperAllSizes, PageTable, PhysFrame}, + PhysAddr, VirtAddr, +}; + +/// Initialize a new MappedPageTable. +/// +/// 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`. Also, this function must be only called once +/// to avoid aliasing `&mut` references (which is undefined behavior). +pub unsafe fn init(physical_memory_offset: u64) -> impl MapperAllSizes { + let level_4_table = active_level_4_table(physical_memory_offset); + let phys_to_virt = move |frame: PhysFrame| -> *mut PageTable { + let phys = frame.start_address().as_u64(); + let virt = VirtAddr::new(phys + physical_memory_offset); + virt.as_mut_ptr() + }; + MappedPageTable::new(level_4_table, phys_to_virt) +} /// Returns a mutable reference to the active level 4 table. /// @@ -6,7 +25,7 @@ use x86_64::{structures::paging::PageTable, PhysAddr, VirtAddr}; /// complete physical memory is mapped to virtual memory at the passed /// `physical_memory_offset`. Also, this function must be only called once /// to avoid aliasing `&mut` references (which is undefined behavior). -pub unsafe fn active_level_4_table(physical_memory_offset: u64) -> &'static mut PageTable { +unsafe fn active_level_4_table(physical_memory_offset: u64) -> &'static mut PageTable { use x86_64::{registers::control::Cr3, VirtAddr}; let (level_4_table_frame, _) = Cr3::read(); From cb4410c84ef1816e6b91f54937483cc33d0a0cdc Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Wed, 13 Mar 2019 14:12:28 +0100 Subject: [PATCH 11/18] Update kernel_main to use `MapperAllSizes::translate_addr` --- src/main.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main.rs b/src/main.rs index ae9f24fc..6a1fea0f 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::translate_addr; - use x86_64::VirtAddr; + use blog_os::memory; + use x86_64::{structures::paging::MapperAllSizes, VirtAddr}; println!("Hello World{}", "!"); @@ -21,6 +21,8 @@ fn kernel_main(boot_info: &'static BootInfo) -> ! { unsafe { PICS.lock().initialize() }; x86_64::instructions::interrupts::enable(); + let mapper = unsafe { memory::init(boot_info.physical_memory_offset) }; + let addresses = [ // the identity-mapped vga buffer page 0xb8000, @@ -34,7 +36,7 @@ fn kernel_main(boot_info: &'static BootInfo) -> ! { for &address in &addresses { let virt = VirtAddr::new(address); - let phys = unsafe { translate_addr(virt, boot_info.physical_memory_offset) }; + let phys = mapper.translate_addr(virt); println!("{:?} -> {:?}", virt, phys); } From b0e1527a95647a7dd84c20fc490d94933365c6ec Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Wed, 13 Mar 2019 14:12:56 +0100 Subject: [PATCH 12/18] Delete our `memory::translate_addr` function again --- src/memory.rs | 52 +-------------------------------------------------- 1 file changed, 1 insertion(+), 51 deletions(-) diff --git a/src/memory.rs b/src/memory.rs index be4433b8..d356dc92 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -1,6 +1,6 @@ use x86_64::{ structures::paging::{MappedPageTable, MapperAllSizes, PageTable, PhysFrame}, - PhysAddr, VirtAddr, + VirtAddr, }; /// Initialize a new MappedPageTable. @@ -36,53 +36,3 @@ unsafe fn active_level_4_table(physical_memory_offset: u64) -> &'static mut Page &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())) -} From 6146ccba2d8049ae1c5215b67face37b21cab0ae Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Wed, 13 Mar 2019 14:23:28 +0100 Subject: [PATCH 13/18] Add a `memory::create_example_mapping` function --- src/memory.rs | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/memory.rs b/src/memory.rs index d356dc92..5f40ff97 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -1,6 +1,9 @@ use x86_64::{ - structures::paging::{MappedPageTable, MapperAllSizes, PageTable, PhysFrame}, - VirtAddr, + structures::paging::{ + FrameAllocator, MappedPageTable, Mapper, MapperAllSizes, Page, PageTable, PhysFrame, + Size4KiB, + }, + PhysAddr, VirtAddr, }; /// Initialize a new MappedPageTable. @@ -36,3 +39,18 @@ unsafe fn active_level_4_table(physical_memory_offset: u64) -> &'static mut Page &mut *page_table_ptr // unsafe } + +/// Creates an example mapping for the given page to frame `0xb8000`. +pub fn create_example_mapping( + page: Page, + mapper: &mut impl Mapper, + frame_allocator: &mut impl FrameAllocator, +) { + use x86_64::structures::paging::PageTableFlags as Flags; + + let frame = PhysFrame::containing_address(PhysAddr::new(0xb8000)); + let flags = Flags::PRESENT | Flags::WRITABLE; + + let map_to_result = unsafe { mapper.map_to(page, frame, flags, frame_allocator) }; + map_to_result.expect("map_to failed").flush(); +} From 3e59283c1994f9a4a8135450c9ae35521b5a6514 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Wed, 13 Mar 2019 14:30:13 +0100 Subject: [PATCH 14/18] Create an `EmptyFrameAllocator` --- src/memory.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/memory.rs b/src/memory.rs index 5f40ff97..1e9dd9bf 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -54,3 +54,12 @@ pub fn create_example_mapping( let map_to_result = unsafe { mapper.map_to(page, frame, flags, frame_allocator) }; map_to_result.expect("map_to failed").flush(); } + +/// A FrameAllocator that always returns `None`. +pub struct EmptyFrameAllocator; + +impl FrameAllocator for EmptyFrameAllocator { + fn allocate_frame(&mut self) -> Option { + None + } +} From 770af27d75e080edd6f15e3c943a599618058d61 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Wed, 13 Mar 2019 14:31:45 +0100 Subject: [PATCH 15/18] Create a new mapping and write through it to the screen --- src/main.rs | 26 +++++++++----------------- 1 file changed, 9 insertions(+), 17 deletions(-) diff --git a/src/main.rs b/src/main.rs index 6a1fea0f..f883906b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -12,7 +12,7 @@ entry_point!(kernel_main); fn kernel_main(boot_info: &'static BootInfo) -> ! { use blog_os::interrupts::PICS; use blog_os::memory; - use x86_64::{structures::paging::MapperAllSizes, VirtAddr}; + use x86_64::{structures::paging::Page, VirtAddr}; println!("Hello World{}", "!"); @@ -21,24 +21,16 @@ fn kernel_main(boot_info: &'static BootInfo) -> ! { unsafe { PICS.lock().initialize() }; x86_64::instructions::interrupts::enable(); - let mapper = unsafe { memory::init(boot_info.physical_memory_offset) }; + let mut mapper = unsafe { memory::init(boot_info.physical_memory_offset) }; + let mut frame_allocator = memory::EmptyFrameAllocator; - 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, - ]; + // map a previously unmapped page + let page = Page::containing_address(VirtAddr::new(0x1000)); + memory::create_example_mapping(page, &mut mapper, &mut frame_allocator); - for &address in &addresses { - let virt = VirtAddr::new(address); - let phys = mapper.translate_addr(virt); - println!("{:?} -> {:?}", virt, phys); - } + // write the string `New!` to the screen through the new mapping + let page_ptr: *mut u64 = page.start_address().as_mut_ptr(); + unsafe { page_ptr.offset(400).write_volatile(0x_f021_f077_f065_f04e) }; println!("It did not crash!"); blog_os::hlt_loop(); From 763228c859d15c9608e9f954c8ebd7c2ae091daf Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Wed, 13 Mar 2019 14:39:51 +0100 Subject: [PATCH 16/18] Create a generic `BootInfoFrameAllocator` type --- src/memory.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/memory.rs b/src/memory.rs index 1e9dd9bf..6bd52009 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -63,3 +63,19 @@ impl FrameAllocator for EmptyFrameAllocator { None } } + +pub struct BootInfoFrameAllocator +where + I: Iterator, +{ + frames: I, +} + +impl FrameAllocator for BootInfoFrameAllocator +where + I: Iterator, +{ + fn allocate_frame(&mut self) -> Option { + self.frames.next() + } +} From a1bf5651fc5708623b39ff81ca61e61da3de7acc Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Wed, 13 Mar 2019 14:41:19 +0100 Subject: [PATCH 17/18] Create an `init_frame_allocator` function --- src/memory.rs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/memory.rs b/src/memory.rs index 6bd52009..0589fdd6 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -1,3 +1,4 @@ +use bootloader::bootinfo::{MemoryMap, MemoryRegionType}; use x86_64::{ structures::paging::{ FrameAllocator, MappedPageTable, Mapper, MapperAllSizes, Page, PageTable, PhysFrame, @@ -22,6 +23,24 @@ pub unsafe fn init(physical_memory_offset: u64) -> impl MapperAllSizes { MappedPageTable::new(level_4_table, phys_to_virt) } +/// Create a FrameAllocator from the passed memory map +pub fn init_frame_allocator( + memory_map: &'static MemoryMap, +) -> BootInfoFrameAllocator> { + // get usable regions from memory map + let regions = memory_map + .iter() + .filter(|r| r.region_type == MemoryRegionType::Usable); + // map each region to its address range + let addr_ranges = regions.map(|r| r.range.start_addr()..r.range.end_addr()); + // transform to an iterator of frame start addresses + let frame_addresses = addr_ranges.flat_map(|r| r.into_iter().step_by(4096)); + // create `PhysFrame` types from the start addresses + let frames = frame_addresses.map(|addr| PhysFrame::containing_address(PhysAddr::new(addr))); + + BootInfoFrameAllocator { frames } +} + /// Returns a mutable reference to the active level 4 table. /// /// This function is unsafe because the caller must guarantee that the From 9bf4ea73416cd70e1aefaac36b9032d7c23b0b50 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Wed, 13 Mar 2019 14:42:47 +0100 Subject: [PATCH 18/18] Use `BootInfoFrameAllocator` to create a `0xdeadbeaf000` mapping --- src/main.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main.rs b/src/main.rs index f883906b..d4d3269f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -22,10 +22,10 @@ fn kernel_main(boot_info: &'static BootInfo) -> ! { x86_64::instructions::interrupts::enable(); let mut mapper = unsafe { memory::init(boot_info.physical_memory_offset) }; - let mut frame_allocator = memory::EmptyFrameAllocator; + let mut frame_allocator = memory::init_frame_allocator(&boot_info.memory_map); // map a previously unmapped page - let page = Page::containing_address(VirtAddr::new(0x1000)); + let page = Page::containing_address(VirtAddr::new(0xdeadbeaf000)); memory::create_example_mapping(page, &mut mapper, &mut frame_allocator); // write the string `New!` to the screen through the new mapping