From 3041cec87dcb93cb94031531fea2ff2303a444ef Mon Sep 17 00:00:00 2001 From: Jack Halford Date: Wed, 7 Mar 2018 19:15:19 +0100 Subject: [PATCH] mmu code mostly written, needs a lot of debugging still --- kernel-rs/Cargo.toml | 1 - kernel-rs/src/arch/x86/boot.asm | 67 +++++- kernel-rs/src/arch/x86/linker.ld | 42 +++- kernel-rs/src/console.rs | 8 + kernel-rs/src/context.rs | 53 ++--- kernel-rs/src/lib.rs | 15 +- kernel-rs/src/memory/mod.rs | 32 +++ kernel-rs/src/memory/paging/entry.rs | 40 ++-- kernel-rs/src/memory/paging/mapper.rs | 85 ++++++++ kernel-rs/src/memory/paging/mod.rs | 191 +++++++++++++++++- kernel-rs/src/memory/paging/table.rs | 77 ++++++- kernel-rs/src/memory/paging/temporary_page.rs | 79 ++++++++ kernel-rs/src/vga/mod.rs | 2 +- kernel-rs/src/x86/mod.rs | 11 + 14 files changed, 622 insertions(+), 81 deletions(-) create mode 100644 kernel-rs/src/memory/paging/mapper.rs create mode 100644 kernel-rs/src/memory/paging/temporary_page.rs create mode 100644 kernel-rs/src/x86/mod.rs diff --git a/kernel-rs/Cargo.toml b/kernel-rs/Cargo.toml index f854812c..68a70731 100644 --- a/kernel-rs/Cargo.toml +++ b/kernel-rs/Cargo.toml @@ -9,5 +9,4 @@ crate-type = ["staticlib"] [dependencies] rlibc = "1.0" bitflags = "1.0.1" -# spin = "0.4.5" multiboot2 = { path = "multiboot2-elf64" } diff --git a/kernel-rs/src/arch/x86/boot.asm b/kernel-rs/src/arch/x86/boot.asm index 2b8b5995..b0e9c208 100644 --- a/kernel-rs/src/arch/x86/boot.asm +++ b/kernel-rs/src/arch/x86/boot.asm @@ -4,12 +4,61 @@ extern x86_start section .text bits 32 start: + ; our stack, located in bss, linker.ld puts bss at the end of the binary + mov esp, stack_top + ; multiboot information pointer push ebx call check_multiboot + + call set_up_page_tables + ; call enable_paging + lgdt [GDTR.ptr] ; load the new gdt jmp GDTR.gdt_cs:x86_start - ; jmp x86_start + +check_multiboot: + cmp eax, 0x36d76289 + jne .no_multiboot + ret +.no_multiboot: + mov al, "0" + jmp error + +set_up_page_tables: + ; map P2 table recursively + mov eax, p2_table + or eax, 0b11 ; present + writable + mov [p2_table + 1023 * 8], eax + + ; map each P2 entry to a huge 2MiB page + mov ecx, 0 ; counter variable + +.map_p2_table: + ; map ecx-th P2 entry to a huge page that starts at address 2MiB*ecx + mov eax, 0x200000 ; 2MiB + ; mov eax, 0x2 ; WRITABLE, not PRESENT + mul ecx ; start address of ecx-th page + or eax, 0b10000011 ; present + writable + huge + mov [p2_table + ecx * 8], eax ; map ecx-th entry + + inc ecx ; increase counter + cmp ecx, 1023 ; if counter == 1023, the whole P2 table is mapped + jne .map_p2_table ; else map the next entry + + ret + +enable_paging: + ; load P2 to cr3 register (cpu uses this to access the P2 table) + mov eax, p2_table + mov cr3, eax + + ; enable paging in the cr0 register + mov eax, cr0 + or eax, 1 << 31 + mov cr0, eax + + ret error: mov dword [0xb8000], 0x4f524f45 @@ -21,14 +70,6 @@ HALT: hlt jmp HALT -check_multiboot: - cmp eax, 0x36d76289 - jne .no_multiboot - ret -.no_multiboot: - mov al, "0" - jmp error - section .gdt GDTR: ; http://tuttlem.github.io/2014/07/11/a-gdt-primer.html @@ -88,6 +129,12 @@ GDTR: DD .gdt_top ; pointer to top of gdt section .bss +align 4096 +p2_table: + resb 4096 +p1_table: + resb 4096 stack_bottom: - resb 4096 * 16 + resb 4096 * 4 stack_top: + diff --git a/kernel-rs/src/arch/x86/linker.ld b/kernel-rs/src/arch/x86/linker.ld index 15716e72..27f2ae00 100644 --- a/kernel-rs/src/arch/x86/linker.ld +++ b/kernel-rs/src/arch/x86/linker.ld @@ -13,10 +13,38 @@ SECTIONS { . = 1M; /* ensure that the multiboot header is at the beginning */ - .multiboot : { KEEP(*(.multiboot)) } - .text : { *(.text .text.*) } - .rodata : { *(.rodata .rodata.*) } - .data : { *(.data.rel.ro.local*) *(.data.rel.ro .data.rel.ro.*) *(.data.*) } - .debug : { *(.debug_*) } - .bss : {*(.bss .bss.*)} -} + .multiboot : + { + KEEP(*(.multiboot)) + . = ALIGN(4K); + } + + .text : + { + *(.text .text.*) + . = ALIGN(4K); + } + + .rodata : + { + *(.rodata .rodata.*) + . = ALIGN(4K); + } + + .data : + { + *(.data.rel.ro.local*) *(.data.rel.ro .data.rel.ro.*) *(.data.*) + . = ALIGN(4K); + } + + .debug : + { + *(.debug_*) + . = ALIGN(4K); + } + + .bss : + { + *(.bss .bss.*)} + . = ALIGN(4K); + } diff --git a/kernel-rs/src/console.rs b/kernel-rs/src/console.rs index d8e9fefc..ebc033c9 100644 --- a/kernel-rs/src/console.rs +++ b/kernel-rs/src/console.rs @@ -4,6 +4,7 @@ extern crate core; use acpi; use cpuio; use context; +use memory; // use multiboot2; use core::char; use vga::*; @@ -18,6 +19,7 @@ fn dispatch(command: &str) -> Result <(), &'static str> { "sections" => self::mb2_sections(), "shutdown" | "halt" | "q" => self::shutdown(), "stack" => self::print_stack(), + "test" => self::test(), _ => Err("Command unknown. (h|help for help)"), } } @@ -45,6 +47,12 @@ fn help() -> Result <(), &'static str> { Ok(()) } +fn test() -> Result<(), &'static str> +{ + memory::test_paging(context::frame_allocator()); + Ok(()) +} + /// Reboot the kernel /// /// If reboot failed, will loop on a halt cmd diff --git a/kernel-rs/src/context.rs b/kernel-rs/src/context.rs index 64eea470..03f810ad 100644 --- a/kernel-rs/src/context.rs +++ b/kernel-rs/src/context.rs @@ -6,10 +6,6 @@ pub static mut CONTEXT: Option = None; pub struct Context { pub current_term: u8, - pub multiboot_start: usize, - pub multiboot_end: usize, - pub kernel_start: usize, - pub kernel_end: usize, pub boot_info: multiboot2::BootInformation, pub frame_allocator: memory::AreaFrameAllocator, pub vga1: vga::Writer, @@ -41,10 +37,6 @@ impl Context Context { current_term: 0, - multiboot_start, - multiboot_end, - kernel_start, - kernel_end, boot_info, frame_allocator, vga1, @@ -52,31 +44,41 @@ impl Context } } + + } pub fn init_screen() { set_color!(White, Cyan); print!("{}{}{}{}{}{}{}{}{}{}{}{}{}{}", - format_args!("{: ^80}", r#" ,--, "#), - format_args!("{: ^80}", r#" ,--.'| ,----, "#), - format_args!("{: ^80}", r#" ,--, | : .' .' \ "#), - format_args!("{: ^80}", r#",---.'| : ' ,----,' | "#), - format_args!("{: ^80}", r#"; : | | ; | : . ; "#), - format_args!("{: ^80}", r#"| | : _' | ; |.' / "#), - format_args!("{: ^80}", r#": : |.' | `----'/ ; "#), - format_args!("{: ^80}", r#"| ' ' ; : / ; / "#), - format_args!("{: ^80}", r#"\ \ .'. | ; / /-, "#), - format_args!("{: ^80}", r#" `---`: | ' / / /.`| "#), - format_args!("{: ^80}", r#" ' ; |./__; : "#), - format_args!("{: ^80}", r#" | : ;| : .' "#), - format_args!("{: ^80}", r#" ' ,/ ; | .' "#), - format_args!("{: ^80}", r#" '--' `---' "#)); + format_args!("{: ^80}", r#" ,--, "#), + format_args!("{: ^80}", r#" ,--.'| ,----, "#), + format_args!("{: ^80}", r#" ,--, | : .' .' \ "#), + format_args!("{: ^80}", r#",---.'| : ' ,----,' | "#), + format_args!("{: ^80}", r#"; : | | ; | : . ; "#), + format_args!("{: ^80}", r#"| | : _' | ; |.' / "#), + format_args!("{: ^80}", r#": : |.' | `----'/ ; "#), + format_args!("{: ^80}", r#"| ' ' ; : / ; / "#), + format_args!("{: ^80}", r#"\ \ .'. | ; / /-, "#), + format_args!("{: ^80}", r#" `---`: | ' / / /.`| "#), + format_args!("{: ^80}", r#" ' ; |./__; : "#), + format_args!("{: ^80}", r#" | : ;| : .' "#), + format_args!("{: ^80}", r#" ' ,/ ; | .' "#), + format_args!("{: ^80}", r#" '--' `---' "#)); set_color!(); context().vga1.prompt(); context().vga2.prompt(); context().vga1.flush(); } +pub fn frame_allocator() -> &'static mut memory::AreaFrameAllocator { + &mut context().frame_allocator +} + +pub fn boot_info() -> &'static multiboot2::BootInformation { + &context().boot_info +} + pub fn switch_term() { context().current_term = { if context().current_term == 0 { 1 } @@ -92,10 +94,6 @@ pub fn current_term() -> &'static mut vga::Writer{ } } -pub fn boot_info() -> &'static multiboot2::BootInformation { - &context().boot_info -} - fn context() -> &'static mut Context { unsafe { match CONTEXT { @@ -107,4 +105,7 @@ fn context() -> &'static mut Context { pub fn init(multiboot_info_addr: usize) { unsafe { CONTEXT = Some(Context::new(multiboot_info_addr)) }; + + // memory::remap_the_kernel(frame_allocator(), boot_info()); + self::init_screen(); } diff --git a/kernel-rs/src/lib.rs b/kernel-rs/src/lib.rs index 93c08533..786bd74e 100644 --- a/kernel-rs/src/lib.rs +++ b/kernel-rs/src/lib.rs @@ -1,4 +1,4 @@ -//! project hosted at (https://github.com/jzck/kernel) +//! project hosted on [github](https://github.com/jzck/kernel) #![no_std] #![feature(lang_items)] @@ -22,17 +22,15 @@ pub mod console; pub mod cpuio; /// ACPI self-content module pub mod acpi; -/// simple area frame allocator implementation +/// physical frame allocator + paging module pub mod memory; - -// use vga::{Color, ColorCode}; +/// a few x86 instruction wrappers +pub mod x86; #[no_mangle] pub extern fn kmain(multiboot_info_addr: usize) -> ! { context::init(multiboot_info_addr); - context::init_screen(); acpi::init().unwrap(); - loop { keyboard::kbd_callback(); } } @@ -42,13 +40,12 @@ pub extern fn eh_personality() { } #[lang = "panic_fmt"] #[no_mangle] -pub extern fn panic_fmt( - fmt: core::fmt::Arguments, file: &'static str, line: u32 - ) +pub extern fn panic_fmt(fmt: core::fmt::Arguments, file: &'static str, line: u32) -> ! { println!("PANIC: {}", fmt); println!("FILE: {}", file); println!("LINE: {}", line); + flush!(); loop {} } diff --git a/kernel-rs/src/memory/mod.rs b/kernel-rs/src/memory/mod.rs index 68b3abd9..51629c66 100644 --- a/kernel-rs/src/memory/mod.rs +++ b/kernel-rs/src/memory/mod.rs @@ -4,6 +4,8 @@ mod area_allocator; mod paging; pub use self::area_allocator::*; +pub use self::paging::test_paging; +pub use self::paging::remap_the_kernel; use self::paging::PhysicalAddress; #[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] @@ -19,9 +21,39 @@ impl Frame { fn start_address(&self) -> PhysicalAddress { self.number * PAGE_SIZE } + + fn clone(&self) ->Frame { + Frame { number: self.number } + } + + fn range_inclusive(start: Frame, end: Frame) -> FrameIter { + FrameIter { + start, + end, + } + } } pub trait FrameAllocator { fn allocate_frame(&mut self) -> Option; fn deallocate_frame(&mut self, frame: Frame); } + +struct FrameIter { + start: Frame, + end: Frame, +} + +impl Iterator for FrameIter { + type Item = Frame; + + fn next(&mut self) -> Option { + if self.start <= self.end { + let frame = self.start.clone(); + self.start.number += 1; + Some(frame) + } else { + None + } + } +} diff --git a/kernel-rs/src/memory/paging/entry.rs b/kernel-rs/src/memory/paging/entry.rs index ae2b7355..0f2803c7 100644 --- a/kernel-rs/src/memory/paging/entry.rs +++ b/kernel-rs/src/memory/paging/entry.rs @@ -1,21 +1,6 @@ use memory::Frame; -pub struct Entry(u64); - -bitflags! { - pub struct EntryFlags: 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 HUGE_PAGE = 1 << 7; - const GLOBAL = 1 << 8; - const NO_EXECUTE = 1 << 63; - } -} +pub struct Entry(u32); impl Entry { pub fn is_unused(&self) -> bool { @@ -33,15 +18,30 @@ impl Entry { pub fn pointed_frame(&self) -> Option { if self.flags().contains(EntryFlags::PRESENT) { Some(Frame::containing_address( - // actual addr is bits 12-51 - self.0 as usize & 0x000fffff_fffff000)) + self.0 as usize & 0xffff_f000)) } else { None } } pub fn set(&mut self, frame: Frame, flags: EntryFlags) { - assert!(frame.start_address() & !0x000fffff_fffff000 == 0); - self.0 = (frame.start_address() as u64) | flags.bits(); + assert!(frame.start_address() & !0xffff_f000 == 0); + self.0 = (frame.start_address() as u32) | flags.bits(); + } +} + +bitflags! { + pub struct EntryFlags: u32 { + 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 HUGE_PAGE = 1 << 7; + const GLOBAL = 1 << 8; + // LONG MODE + // const NO_EXECUTE = 1 << 63; } } diff --git a/kernel-rs/src/memory/paging/mapper.rs b/kernel-rs/src/memory/paging/mapper.rs new file mode 100644 index 00000000..652a1589 --- /dev/null +++ b/kernel-rs/src/memory/paging/mapper.rs @@ -0,0 +1,85 @@ +use super::{VirtualAddress, PhysicalAddress, Page, ENTRY_COUNT}; +use super::entry::*; +use super::table::{self, Table, Level2, Level1}; +use memory::{PAGE_SIZE, Frame, FrameAllocator}; +use core::ptr::Unique; + +pub struct Mapper { + p2: Unique>, +} + +impl Mapper { + pub unsafe fn new() -> Mapper { + Mapper { + p2: Unique::new_unchecked(table::P2), + } + } + + // the remaining mapping methods, all public + pub fn p2(&self) -> &Table { + unsafe { self.p2.as_ref() } + } + + pub fn p2_mut(&mut self) -> &mut Table { + unsafe { self.p2.as_mut() } + } + + pub fn translate(&self, virtual_address: VirtualAddress) -> Option + { + let offset = virtual_address % PAGE_SIZE; + self.translate_page(Page::containing_address(virtual_address)) + .map(|frame| frame.number * PAGE_SIZE + offset) + } + + pub fn translate_page(&self, page: Page) -> Option { + + // huge page handler, unimplemented + let huge_page = || { + unimplemented!() + }; + + self.p2().next_table(page.p2_index()) + .and_then(|p1| p1[page.p1_index()].pointed_frame()) + .or_else(huge_page) + } + + + pub fn map_to(&mut self, page: Page, frame: Frame, flags: EntryFlags, + allocator: &mut A) + where A: FrameAllocator + { + let p2 = self.p2_mut(); + let mut p1 = p2.next_table_create(page.p2_index(), allocator); + + assert!(p1[page.p1_index()].is_unused()); + p1[page.p1_index()].set(frame, flags | EntryFlags::PRESENT); + } + + pub fn map(&mut self, page: Page, flags: EntryFlags, allocator: &mut A) + where A: FrameAllocator + { + let frame = allocator.allocate_frame().expect("out of memory"); + self.map_to(page, frame, flags, allocator) + } + + pub fn identity_map(&mut self, frame: Frame, flags: EntryFlags, allocator: &mut A) + where A: FrameAllocator + { + let page = Page::containing_address(frame.start_address()); + self.map_to(page, frame, flags, allocator); + } + + pub fn unmap(&mut self, page: Page, allocator: &mut A) + where A: FrameAllocator + { + assert!(self.translate(page.start_address()).is_some()); + + let p1 = self.p2_mut() + .next_table_mut(page.p2_index()) + .expect("mapping code does not support huge pages"); + let frame = p1[page.p1_index()].pointed_frame().unwrap(); + p1[page.p1_index()].set_unused(); + // TODO flush the tlb + allocator.deallocate_frame(frame); + } +} diff --git a/kernel-rs/src/memory/paging/mod.rs b/kernel-rs/src/memory/paging/mod.rs index a4fa45aa..6a12207a 100644 --- a/kernel-rs/src/memory/paging/mod.rs +++ b/kernel-rs/src/memory/paging/mod.rs @@ -1,13 +1,202 @@ +#![allow(dead_code)] + mod entry; mod table; +mod temporary_page; +mod mapper; use memory::PAGE_SIZE; +use memory::*; +use self::mapper::Mapper; +use self::temporary_page::TemporaryPage; +use core::ops::{Deref, DerefMut}; +use multiboot2::BootInformation; +use x86; -const ENTRY_COUNT: usize = 512; +pub use self::entry::*; +pub use self::table::*; + +// x86 non PAE has 1024 entries per table +const ENTRY_COUNT: usize = 1024; pub type PhysicalAddress = usize; pub type VirtualAddress = usize; +#[derive(Debug, Clone, Copy)] pub struct Page { number: usize, } + +impl Page { + pub fn containing_address(address: VirtualAddress) -> Page { + // assert!(address < 0x0000_8000_0000_0000 || + // address >= 0xffff_8000_0000_0000, + // "invalid addres: 0x{:x}", address); + Page { number: address / PAGE_SIZE } + } + + fn start_address(&self) -> usize { + self.number * PAGE_SIZE + } + + fn p2_index(&self) -> usize { + (self.number >> 9) & 0o777 + } + + fn p1_index(&self) -> usize { + (self.number >> 0) & 0o777 + } +} + +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 { + pub unsafe fn new() -> ActivePageTable { + ActivePageTable { + mapper: Mapper::new(), + } + } + + pub fn with(&mut self, + table: &mut InactivePageTable, + temporary_page: &mut temporary_page::TemporaryPage, + f: F) + where F: FnOnce(&mut Mapper) + { + self.p2_mut()[ENTRY_COUNT -1].set(table.p2_frame.clone(), EntryFlags::PRESENT | EntryFlags::WRITABLE); + + //TODO tlb flush all + + // execute f in the nex context + f(self); + + // TODO restore recursive mapping to original p2 table + } + + pub fn switch(&mut self, new_table: InactivePageTable) -> InactivePageTable { + + let p2_frame = Frame::containing_address(x86::cr3() as usize); + let old_table = InactivePageTable { + p2_frame, + }; + + unsafe { + let frame = Frame::containing_address(new_table.p2_frame.start_address()); + x86::cr3_write(frame.start_address()); + } + + old_table + } +} + +pub struct InactivePageTable { + p2_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); + table.zero(); + // set up recursive mapping for the table + table[ENTRY_COUNT - 1].set(frame.clone(), EntryFlags::PRESENT | EntryFlags:: WRITABLE) + } + temporary_page.unmap(active_table); + InactivePageTable { p2_frame: frame } + } +} + +pub fn remap_the_kernel(allocator: &mut A, boot_info: &BootInformation) + where A: FrameAllocator +{ + let mut temporary_page = TemporaryPage::new(Page { number: 0xcafe }, + allocator); + + let mut active_table = unsafe { ActivePageTable::new() }; + let mut new_table = { + let frame = allocator.allocate_frame().expect("no more frames"); + InactivePageTable::new(frame, &mut active_table, &mut temporary_page) + }; + + active_table.with(&mut new_table, &mut temporary_page, |mapper| { + let elf_sections_tag = boot_info.elf_sections_tag() + .expect("Memory map tag required"); + + for section in elf_sections_tag.sections() { + use self::entry::EntryFlags; + + if !section.is_allocated() { + //section is not loaded to memory + continue; + } + assert!(section.start_address() % PAGE_SIZE as u64 == 0, + "sections need to be page aligned"); + + println!("mapping section at addr: {:#x}, size: {:#x}", + section.start_address(), section.size()); + + let flags = EntryFlags::WRITABLE; //TODO use real section flags + + let start_frame = Frame::containing_address(section.start_address() as usize); + let end_frame = Frame::containing_address(section.end_address() as usize - 1); + for frame in Frame::range_inclusive(start_frame, end_frame) { + mapper.identity_map(frame, flags, allocator); + } + } + + let vga_buffer_frame = Frame::containing_address(0xb8000); + mapper.identity_map(vga_buffer_frame, EntryFlags::WRITABLE, allocator); + + 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, EntryFlags::PRESENT, allocator); + } + }); + + let old_table = active_table.switch(new_table); + println!("new table!"); +} + +pub fn test_paging(allocator: &mut A) + where A: FrameAllocator +{ + let mut page_table = unsafe { ActivePageTable::new() }; + + let addr = 0xffff_f000; + let page = Page::containing_address(addr); + let frame = allocator.allocate_frame().expect("no more frames"); + // println!("None = {:?}, map to {:?}", + // page_table.translate(addr), + // frame); + println!("check 0"); + flush!(); + page_table.map_to(page, frame, EntryFlags::empty(), allocator); + println!("check 1"); + flush!(); + println!("Some = {:?}", page_table.translate(addr)); + flush!(); + println!("next free frame: {:?}", allocator.allocate_frame()); + flush!(); + +} diff --git a/kernel-rs/src/memory/paging/table.rs b/kernel-rs/src/memory/paging/table.rs index 5a22f689..28289a03 100644 --- a/kernel-rs/src/memory/paging/table.rs +++ b/kernel-rs/src/memory/paging/table.rs @@ -1,13 +1,21 @@ -use memory::paging::entry::*; -use memory::paging::ENTRY_COUNT; +use memory::*; +use memory::paging::*; use core::ops::{Index, IndexMut}; +use core::marker::PhantomData; -pub struct Table { +// virtual address of P2 because its recursively mapped +// see protected mode Non-PAE +// https://wiki.osdev.org/Page_Tables +pub const P2: *mut Table = 0xffff_f000 as *mut _; + +pub struct Table { entries: [Entry; ENTRY_COUNT], + level: PhantomData, } -impl Table { +impl Table where L: TableLevel +{ pub fn zero(&mut self) { for entry in self.entries.iter_mut() { entry.set_unused(); @@ -15,7 +23,46 @@ impl Table { } } -impl Index for Table { +impl Table where L: HierarchicalLevel +{ + fn next_table_address(&self, index: usize) -> Option { + let entry_flags = self[index].flags(); + if entry_flags.contains(EntryFlags::PRESENT) && !entry_flags.contains(EntryFlags::HUGE_PAGE) { + let table_address = self as *const _ as usize; + Some((table_address << 9) | (index << 12)) + } else { + None + } + } + + pub fn next_table(&self, index: usize) -> Option<&Table> { + self.next_table_address(index) + .map(|address| unsafe { &*(address as *const _) }) + } + + pub fn next_table_mut(&mut self, index: usize) -> Option<&mut Table> { + self.next_table_address(index) + .map(|address| unsafe { &mut *(address as *mut _) }) + } + + pub fn next_table_create(&mut self, + index: usize, + allocator: &mut A) -> &mut Table + where A: FrameAllocator + { + if self.next_table(index).is_none() { + // assert!(!self.entries[index].flags().contains(EntryFlags::HUGE_PAGE), + // "mapping code does not support huge pages"); + let frame = allocator.allocate_frame().expect("no frames available"); + self.entries[index].set(frame, EntryFlags::PRESENT | EntryFlags::WRITABLE); + self.next_table_mut(index).expect("no next next table 1").zero() + } + self.next_table_mut(index).expect("no next table 2") + } +} + +impl Index for Table where L: TableLevel +{ type Output = Entry; fn index(&self, index: usize) -> &Entry { @@ -23,8 +70,26 @@ impl Index for Table { } } -impl IndexMut for Table { +impl IndexMut for Table where L: TableLevel +{ fn index_mut(&mut self, index: usize) -> &mut Entry { &mut self.entries[index] } } + +pub trait TableLevel {} + +pub enum Level4 {} +pub enum Level3 {} +pub enum Level2 {} +pub enum Level1 {} + +impl TableLevel for Level4 {} +impl TableLevel for Level3 {} +impl TableLevel for Level2 {} +impl TableLevel for Level1 {} + +pub trait HierarchicalLevel: TableLevel { type NextLevel: TableLevel; } +impl HierarchicalLevel for Level4 { type NextLevel = Level3; } +impl HierarchicalLevel for Level3 { type NextLevel = Level2; } +impl HierarchicalLevel for Level2 { type NextLevel = Level1; } diff --git a/kernel-rs/src/memory/paging/temporary_page.rs b/kernel-rs/src/memory/paging/temporary_page.rs new file mode 100644 index 00000000..77ef6440 --- /dev/null +++ b/kernel-rs/src/memory/paging/temporary_page.rs @@ -0,0 +1,79 @@ +use super::{Page, ActivePageTable, VirtualAddress}; +use super::table::{Table, Level1}; +use memory::{Frame, FrameAllocator}; + +pub struct TemporaryPage { + page: Page, + allocator: TinyAllocator, +} + +impl TemporaryPage { + pub fn new(page: Page, allocator: &mut A) -> TemporaryPage + where A: FrameAllocator + { + TemporaryPage { + page: page, + allocator: TinyAllocator::new(allocator), + } + } + + /// Maps the temporary page to the given frame in the active table. + /// Returns the start address of the temporary page. + pub fn map(&mut self, frame: Frame, active_table: &mut ActivePageTable) + -> VirtualAddress + { + use super::entry::EntryFlags; + + assert!(active_table.translate_page(self.page).is_none(), + "temporary page is already mapped"); + active_table.map_to(self.page, frame, EntryFlags::WRITABLE, &mut self.allocator); + self.page.start_address() + } + + /// Unmaps the temporary page in the active table. + pub fn unmap(&mut self, active_table: &mut ActivePageTable) { + active_table.unmap(self.page, &mut self.allocator) + } + + /// Maps the temporary page to the given page table frame in the active + /// table. Returns a reference to the now mapped table. + pub fn map_table_frame(&mut self, + frame: Frame, + active_table: &mut ActivePageTable) + -> &mut Table { + unsafe { &mut *(self.map(frame, active_table) as *mut Table) } + } +} + +struct TinyAllocator([Option; 1]); + +impl TinyAllocator { + fn new(allocator: &mut A) -> TinyAllocator + where A: FrameAllocator + { + let mut f = || allocator.allocate_frame(); + let frames = [f()]; + TinyAllocator(frames) + } +} + +impl FrameAllocator for TinyAllocator { + fn allocate_frame(&mut self) -> Option { + for frame_option in &mut self.0 { + if frame_option.is_some() { + return frame_option.take(); + } + } + None + } + + fn deallocate_frame(&mut self, frame: Frame) { + for frame_option in &mut self.0 { + if frame_option.is_none() { + *frame_option = Some(frame); + return; + } + } + panic!("Tiny allcoator can only hold 1 frame."); + } +} diff --git a/kernel-rs/src/vga/mod.rs b/kernel-rs/src/vga/mod.rs index 8bc3ebf6..0a09fe1b 100644 --- a/kernel-rs/src/vga/mod.rs +++ b/kernel-rs/src/vga/mod.rs @@ -25,7 +25,7 @@ macro_rules! println { } macro_rules! flush { - () => (context::current_term().flush()); + () => ($crate::context::current_term().flush()); } macro_rules! set_color { diff --git a/kernel-rs/src/x86/mod.rs b/kernel-rs/src/x86/mod.rs new file mode 100644 index 00000000..46a9b41b --- /dev/null +++ b/kernel-rs/src/x86/mod.rs @@ -0,0 +1,11 @@ +//! x86 (32 bit) only + +pub fn cr3() -> usize { + let ret: usize; + unsafe { asm!("mov %cr3, $0" : "=r" (ret)) }; + ret +} + +pub unsafe fn cr3_write(val: usize) { + asm!("mov $0, %cr3" :: "r" (val) : "memory"); +}