From 273b0048512383131dae343b792cd25b5e672323 Mon Sep 17 00:00:00 2001 From: Julien CLEMENT Date: Tue, 7 Dec 2021 19:00:34 +0100 Subject: [PATCH] feat(paging): add frame allocator and mapping test Signed-off-by: Julien CLEMENT --- Cargo.lock | 18 ++++++- Cargo.toml | 1 + Makefile | 2 +- src/boot/boot.asm | 9 +++- src/interrupts.rs | 28 +++++++++-- src/lib.rs | 35 ++++++++++++- src/linker.ld | 55 ++++++++++++++------ src/memory/frame_allocator.rs | 94 +++++++++++++++++++++++++++++++++++ src/memory/mod.rs | 7 +++ src/memory/paging/mod.rs | 33 ++++++++++++ 10 files changed, 257 insertions(+), 25 deletions(-) create mode 100644 src/memory/frame_allocator.rs create mode 100644 src/memory/mod.rs create mode 100644 src/memory/paging/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 3e329a0..da8af61 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,6 +8,12 @@ version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcb6dd1c2376d2e096796e234a70e17e94cc2d5d54ff8ce42b28cef1d0d359a4" +[[package]] +name = "bitflags" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8dead7461c1127cf637931a1e50934eb6eee8bff2f74433ac7909e9afcee04a3" + [[package]] name = "bitflags" version = "1.3.2" @@ -19,6 +25,7 @@ name = "julios" version = "0.1.0" dependencies = [ "lazy_static", + "multiboot2", "pc-keyboard", "pic8259", "spin", @@ -35,6 +42,15 @@ dependencies = [ "spin", ] +[[package]] +name = "multiboot2" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b209a4456a3cc81fb69ad318ed6d47af81a90b829701f151354e3994d8b216d4" +dependencies = [ + "bitflags 0.4.0", +] + [[package]] name = "pc-keyboard" version = "0.5.1" @@ -75,6 +91,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbc6ed1ed2cd4536b083c34041aff7b84448ee25ac4aa5e9d54802ce226f9815" dependencies = [ "bit_field", - "bitflags", + "bitflags 1.3.2", "volatile 0.4.4", ] diff --git a/Cargo.toml b/Cargo.toml index 2846c1b..3335063 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,7 @@ spin = "0.5.2" x86_64 = "0.14.2" pic8259 = "0.10.1" pc-keyboard = "0.5.0" +multiboot2 = "0.1.0" [dependencies.lazy_static] version = "1.0" diff --git a/Makefile b/Makefile index 345464a..6babcfa 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ GRUB_CFG = grub/grub.cfg all: $(ISO) run: $(ISO) - qemu-system-x86_64 -cdrom $< -serial stdio + qemu-system-x86_64 -cdrom $< -serial stdio -m 8G debug: $(ISO) bochs -q diff --git a/src/boot/boot.asm b/src/boot/boot.asm index 05ea5f4..5b355c7 100644 --- a/src/boot/boot.asm +++ b/src/boot/boot.asm @@ -15,6 +15,7 @@ bits 32 _start: mov esp, stack_top + mov edi, ebx call check_multiboot call check_cpuid @@ -27,6 +28,11 @@ _start: jmp gdt64.code:long_mode_start set_up_page_tables: + ; recursively map p4 + mov eax, p4_table + or eax, 0b11 ; present + writeable + mov [p4_table + 511 * 8], eax + ; map first P4 entry to P3 table mov eax, p3_table or eax, 0b11 ; present + writable @@ -163,9 +169,8 @@ p3_table: p2_table: resb 4096 -section .stack stack_bottom: - resb 0x800000 + resb 4 * 4096 stack_top: diff --git a/src/interrupts.rs b/src/interrupts.rs index 0dbcdaa..bb06b40 100644 --- a/src/interrupts.rs +++ b/src/interrupts.rs @@ -1,4 +1,5 @@ use crate::gdt; +use crate::hlt_loop; use crate::vga::{self, Color, ColorCode}; use crate::{print, println}; use lazy_static::lazy_static; @@ -6,6 +7,7 @@ use pc_keyboard::{layouts, DecodedKey, HandleControl, Keyboard, ScancodeSet1}; use pic8259::ChainedPics; use spin::{self, Mutex}; use x86_64::instructions::port::Port; +use x86_64::structures::idt::PageFaultErrorCode; use x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame}; pub const PIC_1_OFFSET: u8 = 32; @@ -37,6 +39,7 @@ lazy_static! { static ref IDT: InterruptDescriptorTable = { let mut idt = InterruptDescriptorTable::new(); idt.breakpoint.set_handler_fn(breakpoint_handler); + idt.page_fault.set_handler_fn(page_fault_handler); unsafe { idt.double_fault .set_handler_fn(double_fault_handler) @@ -66,6 +69,22 @@ extern "x86-interrupt" fn breakpoint_handler(stack_frame: InterruptStackFrame) { vga::change_color(color); } +extern "x86-interrupt" fn page_fault_handler( + stack_frame: InterruptStackFrame, + error_code: PageFaultErrorCode, +) { + let color: vga::ColorCode = vga::get_color(); + vga::change_color(ColorCode::new(Color::LightRed, Color::Black)); + use x86_64::registers::control::Cr2; + + println!("EXCEPTION: PAGE FAULT"); + println!("Accessed Address: {:?}", Cr2::read()); + println!("Error Code: {:?}", error_code); + println!("{:#?}", stack_frame); + vga::change_color(color); + hlt_loop(); +} + extern "x86-interrupt" fn double_fault_handler( stack_frame: InterruptStackFrame, _error_code: u64, @@ -74,7 +93,7 @@ extern "x86-interrupt" fn double_fault_handler( } extern "x86-interrupt" fn timer_interrupt_handler(_stack_frame: InterruptStackFrame) { - print!("."); + // print!("."); unsafe { PICS.lock() .notify_end_of_interrupt(InterruptIndex::Timer.as_u8()); @@ -83,10 +102,9 @@ extern "x86-interrupt" fn timer_interrupt_handler(_stack_frame: InterruptStackFr extern "x86-interrupt" fn keyboard_interrupt_handler(_stack_frame: InterruptStackFrame) { lazy_static! { - static ref KEYBOARD: Mutex> = - Mutex::new(Keyboard::new(layouts::Us104Key, ScancodeSet1, - HandleControl::Ignore) - ); + static ref KEYBOARD: Mutex> = Mutex::new( + Keyboard::new(layouts::Us104Key, ScancodeSet1, HandleControl::Ignore) + ); } let mut keyboard = KEYBOARD.lock(); diff --git a/src/lib.rs b/src/lib.rs index 8c2ddcb..2e47eca 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,9 +4,12 @@ mod gdt; mod interrupts; +mod memory; mod serial; mod vga; +extern crate multiboot2; + use core::panic::PanicInfo; use vga::{Color, ColorCode}; @@ -31,11 +34,39 @@ pub fn init() { vga::change_color(ColorCode::new(Color::LightGreen, Color::Black)); } +fn get_frame_allocator(multiboot_info_addr: usize) -> memory::AreaFrameAllocator { + let boot_info = unsafe { multiboot2::load(multiboot_info_addr) }; + let memory_map_tag = boot_info.memory_map_tag().expect("Memory map tag required"); + + let elf_sections_tag = boot_info + .elf_sections_tag() + .expect("Elf-sections tag required"); + + let kernel_start: u64 = elf_sections_tag.sections().map(|s| s.addr).min().unwrap(); + let kernel_end: u64 = elf_sections_tag + .sections() + .map(|s| s.addr + s.size) + .max() + .unwrap(); + + let multiboot_start: u64 = multiboot_info_addr as u64; + let multiboot_end: u64 = multiboot_start + (boot_info.total_size as u64); + + memory::AreaFrameAllocator::new( kernel_start, kernel_end, multiboot_start, + multiboot_end, memory_map_tag.memory_areas()) +} + #[no_mangle] -pub extern "C" fn julios_main() -> ! { +pub extern "C" fn julios_main(multiboot_info_addr: usize) -> ! { + let boot_info = unsafe { multiboot2::load(multiboot_info_addr) }; + + let mut frame_allocator = get_frame_allocator(multiboot_info_addr); + + memory::kernel_remap(&mut frame_allocator, boot_info); + init(); println!("***JuliOS V0.1.0***"); serial_println!("Hello serial"); - + memory::paging::test_paging(&mut frame_allocator); panic!("Kernel end of flow"); } diff --git a/src/linker.ld b/src/linker.ld index 4300d1e..8c4fed2 100644 --- a/src/linker.ld +++ b/src/linker.ld @@ -1,26 +1,53 @@ ENTRY(_start) - SECTIONS { . = 1M; - .boot : + .rodata : { /* ensure that the multiboot header is at the beginning */ - *(.multiboot_header) - } - - .stack : - { - *(.stack) - } - - .bss : - { - *(.bss) + KEEP(*(.multiboot_header)) + *(.rodata .rodata.*) + . = ALIGN(4K); } .text : { - *(.text) + *(.text .text.*) + . = ALIGN(4K); + } + + .data : + { + *(.data .data.*) + . = ALIGN(4K); + } + + .bss : + { + *(.bss .bss.*) + . = ALIGN(4K); + } + + .got : + { + *(.got) + . = ALIGN(4K); + } + + .got.plt : + { + *(.got.plt) + . = ALIGN(4K); + } + + .data.rel.ro : ALIGN(4K) { + *(.data.rel.ro.local*) *(.data.rel.ro .data.rel.ro.*) + . = ALIGN(4K); + } + + .gcc_except_table : ALIGN(4K) { + *(.gcc_except_table) + . = ALIGN(4K); } } + diff --git a/src/memory/frame_allocator.rs b/src/memory/frame_allocator.rs new file mode 100644 index 0000000..33d5f50 --- /dev/null +++ b/src/memory/frame_allocator.rs @@ -0,0 +1,94 @@ +pub use super::PAGE_SIZE; +use multiboot2::{MemoryArea, MemoryAreaIter}; +pub use x86_64::structures::paging::{ + frame::PhysFrame as Frame, FrameAllocator, FrameDeallocator, Size4KiB, +}; +use x86_64::PhysAddr; + +pub struct AreaFrameAllocator { + next_free_frame: Frame, + current_area: Option<&'static MemoryArea>, + areas: MemoryAreaIter, + kernel_start: Frame, + kernel_end: Frame, + multiboot_start: Frame, + multiboot_end: Frame, +} + +impl AreaFrameAllocator { + pub fn new(kernel_start: u64, kernel_end: u64, multiboot_start: u64, multiboot_end: u64, memory_areas: MemoryAreaIter) -> AreaFrameAllocator { + let mut allocator = AreaFrameAllocator { + next_free_frame: Frame::containing_address(PhysAddr::new(0)), + current_area: None, + areas: memory_areas, + kernel_start: Frame::containing_address(PhysAddr::new(kernel_start)), + kernel_end: Frame::containing_address(PhysAddr::new(kernel_end)), + multiboot_start: Frame::containing_address(PhysAddr::new(multiboot_start)), + multiboot_end: Frame::containing_address(PhysAddr::new(multiboot_end)), + }; + allocator.choose_next_area(); + allocator + } + + fn choose_next_area(&mut self) { + self.current_area = self + .areas + .clone() + .filter(|area| { + let address = area.base_addr + area.length - 1; + Frame::containing_address(PhysAddr::new(address)) >= self.next_free_frame + }) + .min_by_key(|area| area.base_addr); + + if let Some(area) = self.current_area { + let start_frame = Frame::containing_address(PhysAddr::new(area.base_addr)); + if self.next_free_frame < start_frame { + self.next_free_frame = start_frame; + } + } + } +} + +unsafe impl FrameAllocator for AreaFrameAllocator { + fn allocate_frame(&mut self) -> Option { + if let Some(area) = self.current_area { + // "Clone" the frame to return it if it's free. Frame doesn't + // implement Clone, but we can construct an identical frame. + let frame = Frame::containing_address(self.next_free_frame.start_address()); + + // the last frame of the current area + let current_area_last_frame = { + let address = area.base_addr + area.length - 1; + Frame::containing_address(PhysAddr::new(address)) + }; + + if frame > current_area_last_frame { + // all frames of current area are used, switch to next area + self.choose_next_area(); + } else if frame >= self.kernel_start && frame <= self.kernel_end { + // `frame` is used by the kernel + self.next_free_frame = + Frame::containing_address(self.kernel_end.start_address() + PAGE_SIZE); + } else if frame >= self.multiboot_start && frame <= self.multiboot_end { + // `frame` is used by the multiboot information structure + self.next_free_frame = + Frame::containing_address(self.multiboot_end.start_address() + PAGE_SIZE); + } else { + // frame is unused, increment `next_free_frame` and return it + self.next_free_frame = + Frame::containing_address(self.next_free_frame.start_address() + PAGE_SIZE); + return Some(frame); + } + // `frame` was not valid, try it again with the updated `next_free_frame` + self.allocate_frame() + } else { + None // no free frames left + } + } +} + +impl FrameDeallocator for AreaFrameAllocator { + unsafe fn deallocate_frame(&mut self, _frame: Frame) { + unimplemented!() + } +} diff --git a/src/memory/mod.rs b/src/memory/mod.rs new file mode 100644 index 0000000..a261cd0 --- /dev/null +++ b/src/memory/mod.rs @@ -0,0 +1,7 @@ +pub use self::frame_allocator::AreaFrameAllocator; +pub use paging::kernel_remap; + +pub mod frame_allocator; +pub mod paging; + +pub const PAGE_SIZE: usize = 4096; diff --git a/src/memory/paging/mod.rs b/src/memory/paging/mod.rs new file mode 100644 index 0000000..b124aab --- /dev/null +++ b/src/memory/paging/mod.rs @@ -0,0 +1,33 @@ +use multiboot2::BootInformation; +use x86_64::structures::paging::{FrameAllocator, Size4KiB, PageTable, RecursivePageTable, Page, PageTableFlags as Flags, Mapper}; +use crate::println; +use x86_64::VirtAddr; + +pub const P4: *mut PageTable = 0o177777_777_777_777_777_0000 as *mut _; + +pub fn kernel_remap(_allocator: &mut A, _boot_info: &BootInformation) + where A: FrameAllocator +{ +} + +pub fn test_paging(allocator: &mut A) + where A: FrameAllocator +{ + let mut page_table = unsafe { RecursivePageTable::new(&mut *P4).expect("Could not create Page Table") }; + + let addr = 42 * 512 * 512 * 4096; // 42th P3 entry + let page = Page::containing_address(VirtAddr::new(addr)); + let frame = allocator.allocate_frame().expect("no more frames"); + println!("None = , map to {:?}", frame); + unsafe { page_table.map_to(page, frame, Flags::PRESENT, allocator).expect("Could not map").flush() }; + println!("next free frame: {:?}", allocator.allocate_frame()); + + let page_ptr: *mut u8 = page.start_address().as_mut_ptr(); + let frame_ptr: *mut u8 = frame.start_address().as_u64() as *mut u8; + + unsafe { + println!("Page: {:#?}, Frame: {:#?}", *page_ptr, *frame_ptr); + *frame_ptr = 42; + println!("Page: {:#?}, Frame: {:#?}", *page_ptr, *frame_ptr); + } +}