//! Paging subsystem. *Note: uses recursive mapping.* //! //! Extremely ripped off from Phil Oppermann's tutorials, because I don't feel like writing //! a paging system off the top of my head today. use core::ops::{Deref, DerefMut}; use multiboot2::BootInformation; use super::PAGE_SIZE; use super::{Frame, FrameAllocator}; mod entry; mod table; mod mapper; mod temporary_page; pub use self::entry::*; use self::table::{Table, Level4}; use self::temporary_page::TemporaryPage; use self::mapper::Mapper; /// Upper bound on entries per page table const ENTRY_COUNT: usize = 512; /// Helper type aliases used to make function signatures more expressive pub type PhysicalAddress = usize; pub type VirtualAddress = usize; pub struct ActivePageTable { mapper: Mapper } impl Deref for ActivePageTable { type Target = Mapper; fn deref(&self) -> &Mapper {&self.mapper} } impl DerefMut for ActivePageTable { fn deref_mut(&mut self) -> &mut Mapper {&mut self.mapper} } impl ActivePageTable { unsafe fn new() -> ActivePageTable { ActivePageTable { mapper: Mapper::new(), } } /// Executes a closure, with a different page table recursively mapped pub fn with(&mut self, table: &mut InactivePageTable, scratch_page: &mut TemporaryPage, f: F) where F: FnOnce(&mut Mapper) { use x86::shared::{tlb, control_regs}; { // Backup the original P4 pointer let backup = Frame::containing_address( unsafe {control_regs::cr3()} ); // Map a scratch page to the current p4 table let p4_table = scratch_page.map_table_frame(backup.clone(), self); // Overwrite main P4 recursive mapping self.p4_mut()[511].set(table.p4_frame.clone(), PRESENT | WRITABLE); unsafe {tlb::flush_all();} // flush *all* TLBs to prevent fuckiness // Execute f in context of the new page table f(self); // Restore the original pointer to P4 p4_table[511].set(backup, PRESENT | WRITABLE); unsafe {tlb::flush_all();} // prevent fuckiness } scratch_page.unmap(self); } /// Switches to a new [`InactivePageTable`], making it active. /// /// Note: We don't need to flush the TLB here, as the CPU automatically flushes /// the TLB when the P4 table is switched. pub fn switch(&mut self, new_table: InactivePageTable) -> InactivePageTable { use x86::shared::{control_regs}; let old_table = InactivePageTable { p4_frame: Frame::containing_address(unsafe {control_regs::cr3()}), }; unsafe { control_regs::cr3_write(new_table.p4_frame.start_address()); } old_table } } /// Owns an inactive P4 table. pub struct InactivePageTable { p4_frame: Frame, } impl InactivePageTable { pub fn new(frame: Frame, active_table: &mut ActivePageTable, temporary_page: &mut TemporaryPage) -> InactivePageTable { { let table = temporary_page.map_table_frame(frame.clone(), active_table); // zero the new inactive page table table.zero(); // set up a recursive mapping for this table table[511].set(frame.clone(), PRESENT | WRITABLE); } temporary_page.unmap(active_table); InactivePageTable { p4_frame: frame } } } /// A representation of a virtual page. #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] pub struct Page { index: usize, } impl Page { /// Retrieves the page containing a given virtual address. pub fn containing_address(address: VirtualAddress) -> Page { assert!(address < 0x0000_8000_0000_0000 || address >= 0xffff_8000_0000_0000, "invalid address: {:#x}", address); Page { index: address / PAGE_SIZE } } /// Returns the start (virtual) address of a page pub fn start_address(&self) -> VirtualAddress { self.index * PAGE_SIZE } pub fn range_inclusive(start: Page, end: Page) -> PageIter { PageIter { start: start, end: end, } } fn p4_index(&self) -> usize { (self.index >> 27) & 0o777 } fn p3_index(&self) -> usize { (self.index >> 18) & 0o777 } fn p2_index(&self) -> usize { (self.index >> 9) & 0o777 } fn p1_index(&self) -> usize { (self.index >> 0) & 0o777 } } pub struct PageIter { start: Page, end: Page, } impl Iterator for PageIter { type Item = Page; fn next(&mut self) -> Option { if self.start <= self.end { let frame = self.start.clone(); self.start.index += 1; Some(frame) } else { None } } } /// Remap the kernel pub fn remap_kernel(allocator: &mut A, boot_info: &BootInformation) -> ActivePageTable where A: FrameAllocator { let mut scratch_page = TemporaryPage::new(Page { index: 0xabadcafe }, allocator); let mut active_table = unsafe {ActivePageTable::new()}; let mut new_table = { let frame = allocator.alloc_frame() .expect("Attempted to allocate a frame for a new page table, but no frames are available!"); InactivePageTable::new(frame, &mut active_table, &mut scratch_page) }; active_table.with(&mut new_table, &mut scratch_page, |mapper| { let elf_sections_tag = boot_info.elf_sections_tag() .expect("ELF sections tag required!"); // -- Identity map the kernel sections for section in elf_sections_tag.sections() { if !section.is_allocated() { // section is not loaded to memory continue; } assert!(section.start_address() % PAGE_SIZE == 0, "ELF sections must be page-aligned!"); debug!("Mapping section at addr: {:#x}, size: {:#x}", section.addr, section.size); let flags = EntryFlags::from_elf_section_flags(section); let start_frame = Frame::containing_address(section.start_address()); let end_frame = Frame::containing_address(section.end_address() - 1); for frame in Frame::range_inclusive(start_frame, end_frame) { mapper.identity_map(frame, flags, allocator); } } // -- Identity map the VGA console buffer (it's only one frame long) let vga_buffer_frame = Frame::containing_address(0xb8000); mapper.identity_map(vga_buffer_frame, WRITABLE, allocator); // -- Identity map the multiboot info structure let multiboot_start = Frame::containing_address(boot_info.start_address()); let multiboot_end = Frame::containing_address(boot_info.end_address() - 1); for frame in Frame::range_inclusive(multiboot_start, multiboot_end) { mapper.identity_map(frame, PRESENT | WRITABLE, allocator); } }); let old_table = active_table.switch(new_table); info!("successfully switched to new page table."); // Create a guard page in place of the old P4 table's page let old_p4_page = Page::containing_address(old_table.p4_frame.start_address()); active_table.unmap(old_p4_page, allocator); info!("guard page established at {:#x}", old_p4_page.start_address()); active_table }