diff --git a/src/memory/mod.rs b/src/memory/mod.rs new file mode 100644 index 00000000..072d481f --- /dev/null +++ b/src/memory/mod.rs @@ -0,0 +1,77 @@ +use multiboot2::Multiboot; +use core::iter::range_inclusive; +use core::cmp::max; + +mod paging; + +pub fn init(multiboot: &Multiboot) { + // ATTENTION: we have a very small stack and no guard page + + let kernel_end = multiboot.elf_tag().unwrap().sections().map(|s| s.addr + s.size).max() + .unwrap() as usize; + let multiboot_end = multiboot as *const _ as usize + multiboot.total_size as usize; + let mut allocator = FrameAllocator::new(max(kernel_end, multiboot_end)); + let mut c = unsafe{paging::Controller::new(allocator)}; + + c.begin_new_table(); + c.identity_map(Page{number: 0xb8}, true, false); + + + for section in multiboot.elf_tag().unwrap().sections() { + let in_memory = section.flags & 0x2 != 0; + let writable = section.flags & 0x1 != 0; + let executable = section.flags & 0x4 != 0; + if !in_memory { + continue; + } + println!("section at 0x{:x}, allocated: {}, writable: {}, executable: {}", section.addr, + in_memory, writable, executable); + let start_page = Page::containing_address(section.addr as usize); + let end_page = Page::containing_address((section.addr + section.size) as usize); + for page in range_inclusive(start_page.number, end_page.number).map(|n| Page{number: n}) { + c.identity_map(page, writable, executable); + } + } + c.activate_new_table(); +} + +struct VirtualAddress(*const u8); + +struct FrameAllocator { + next_free_frame: usize, +} + +impl FrameAllocator { + fn new(kernel_end: usize) -> FrameAllocator { + assert!(kernel_end > 0x100000); + FrameAllocator { + next_free_frame: ((kernel_end - 1) >> 12) + 1, + } + } + + fn allocate_frame(&mut self) -> Option { + let page_number = self.next_free_frame; + self.next_free_frame += 1; + Some(Frame { + number: page_number + }) + } +} + +#[derive(PartialOrd, Ord, PartialEq, Eq, Clone, Copy)] +struct Frame { + number: usize, +} + +#[derive(PartialOrd, Ord, PartialEq, Eq, Clone, Copy)] +struct Page { + number: usize, +} + +impl Page { + fn containing_address(address: usize) -> Page { + Page { + number: address >> 12, + } + } +} diff --git a/src/memory/paging.rs b/src/memory/paging.rs new file mode 100644 index 00000000..21a51f4d --- /dev/null +++ b/src/memory/paging.rs @@ -0,0 +1,223 @@ +use super::{VirtualAddress, Frame}; +use core::ops::Deref; + +pub type FrameAllocator = super::FrameAllocator; +pub type Page = super::Page; + +pub const PAGE_SIZE: u64 = 4096; + +bitflags! { + flags PageTableFieldFlags: u64 { + const PRESENT = 1 << 0, + const WRITABLE = 1 << 1, + const USER_ACCESSIBLE = 1 << 2, + const WRITE_THROUGH = 1 << 3, + const NO_CACHE = 1 << 4, + const ACCESSED = 1 << 5, + const DIRTY = 1 << 6, + const OTHER1 = 1 << 9, + const OTHER2 = 1 << 10, + const NO_EXECUTE = 1 << 63, + } +} + +pub struct Controller { + allocator: FrameAllocator, +} + +impl Controller { + pub unsafe fn new(allocator: FrameAllocator) -> Controller { + Controller { + allocator: allocator, + } + } + + pub fn identity_map(&mut self, page: Page, writable: bool, executable: bool) { + let mut flags = PRESENT; + if writable { + flags = flags | WRITABLE; + } + if !executable { + flags = flags | NO_EXECUTE; + } + + page.map_to(Frame{number: page.number}, flags, &mut self.allocator) + } + + pub fn begin_new_table(&mut self) { + let new_p4_frame = self.allocator.allocate_frame().unwrap(); + let new_p4_page = &mut PageTablePage(Page{number: new_p4_frame.number}); + unsafe{new_p4_page.zero()}; + new_p4_page.field(511).set(new_p4_frame, PRESENT | WRITABLE); + + let old_p4_page = &mut PageTablePage(Page{number: 0o777_777_777_777}); + old_p4_page.field(511).set(new_p4_frame, PRESENT | WRITABLE); + + Self::flush_tlb(); + } + + pub fn activate_new_table(&mut self) { + unsafe { + let p4_address: u64 = { + let field = *(0xfffffffffffffff8 as *const u64); + field & !0xfff + }; + + asm!("mov cr3, $0" :: "r"(p4_address) :: "intel") + } + } + + fn flush_tlb() { + unsafe{asm!("mov rax, cr3 + mov cr3, rax" ::: "{rax}" : "intel")} + } +} + +// first allocated address starts on second P4-Page +pub const FIRST_PAGE : Page = Page { + number: 0o_001_000_000_000, +}; + +// a page containing a page table +struct PageTablePage(Page); + +struct PageIter(Page); + +struct PageTableField(*const u64); + +impl Page { + fn from_address(address: &VirtualAddress) -> Page { + Page { + number: address.0 as usize >> 12, + } + } + + pub fn start_address(&self) -> VirtualAddress { + if self.number >= 0o400_000_000_000 { + //sign extension + VirtualAddress(((self.number << 12) | 0o177777_000_000_000_000_0000) as *const u8) + } else { + VirtualAddress((self.number << 12) as *const u8) + } + } + + fn p4_index(&self) -> usize {(self.number >> 27) & 0o777} + fn p3_index(&self) -> usize {(self.number >> 18) & 0o777} + fn p2_index(&self) -> usize {(self.number >> 9) & 0o777} + fn p1_index(&self) -> usize {(self.number >> 0) & 0o777} + + fn p4_page(&self) -> PageTablePage { + PageTablePage(Page { + number: 0o_777_777_777_777, + }) + } + fn p3_page(&self) -> PageTablePage { + PageTablePage(Page { + number: 0o_777_777_777_000 | self.p4_index(), + }) + } + fn p2_page(&self) -> PageTablePage { + PageTablePage(Page { + number: 0o_777_777_000_000 | (self.p4_index() << 9) | self.p3_index(), + }) + } + fn p1_page(&self) -> PageTablePage { + PageTablePage(Page { + number: 0o_777_000_000_000 | (self.p4_index() << 18) | (self.p3_index() << 9) + | self.p2_index(), + }) + } + + pub fn map_to(&self, frame: Frame, flags: PageTableFieldFlags, allocator: &mut FrameAllocator) { + let p4_field = self.p4_page().field(self.p4_index()); + if p4_field.is_free() { + p4_field.set(allocator.allocate_frame().expect("no frame allocated"), PRESENT | WRITABLE); + unsafe{self.p3_page().zero()}; + } + let p3_field = self.p3_page().field(self.p3_index()); + if p3_field.is_free() { + p3_field.set(allocator.allocate_frame().expect("no frame allocated"), PRESENT | WRITABLE); + unsafe{self.p2_page().zero()}; + } + let p2_field = self.p2_page().field(self.p2_index()); + if p2_field.is_free() { + p2_field.set(allocator.allocate_frame().expect("no frame allocated"), PRESENT | WRITABLE); + unsafe{self.p1_page().zero()}; + } + let p1_field = self.p1_page().field(self.p1_index()); + //TODOassert!(p1_field.is_free()); + p1_field.set(frame, flags); + } + + unsafe fn zero(&self) { + let page = self.start_address().0 as *mut [u64; (PAGE_SIZE/64) as usize]; + *page = [0; (PAGE_SIZE/64) as usize]; + } + + pub fn next_pages(self) -> PageIter { + PageIter(self) + } +} + +impl Iterator for PageIter { + type Item = Page; + + fn next(&mut self) -> Option { + self.0.number += 1; + Some(self.0) + } +} + +impl PageTablePage { + fn field(&self, index: usize) -> PageTableField { + //print!("index: {} pointer: {:o}\n", index, self.0.start_address().0 as usize + (index * 8)); + PageTableField((self.0.start_address().0 as usize + (index * 8)) as *const u64) + } +} + +impl Deref for PageTablePage { + type Target = Page; + fn deref(&self) -> &Page { &self.0 } +} + +impl VirtualAddress { + pub fn page(&self) -> Page { + Page::from_address(self) + } + + fn page_offset(&self) -> u32 { + self.0 as u32 & 0xfff + } +} + +impl PageTableField { + fn is(&self, flags: PageTableFieldFlags) -> bool { + PageTableFieldFlags::from_bits_truncate(unsafe{*(self.0)}).contains(flags) + } + + fn add_flag(&self, flags: PageTableFieldFlags) { + unsafe{*(self.0 as *mut u64) |= flags.bits}; + } + + fn remove_flag(&self, flags: PageTableFieldFlags) { + unsafe{*(self.0 as *mut u64) &= !flags.bits}; + } + + fn is_free(&self) -> bool { + //TODO + let f = unsafe{*self.0}; + let free = f == 0; + free + } + + fn pointed_frame(&self) -> Frame { + Frame { + number: ((unsafe{(*self.0)} & 0x000fffff_fffff000) >> 12) as usize, + } + } + + fn set(&self, frame: Frame, flags: PageTableFieldFlags) { + let new = (((frame.number as u64) << 12) & 0x000fffff_fffff000) | flags.bits(); + unsafe{*(self.0 as *mut u64) = new}; + } +}