feat(paging): add frame allocator and mapping test

Signed-off-by: Julien CLEMENT <julien.clement@epita.fr>
This commit is contained in:
Julien CLEMENT 2021-12-07 19:00:34 +01:00
parent 62be6d047e
commit 273b004851
10 changed files with 257 additions and 25 deletions

18
Cargo.lock generated
View File

@ -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",
]

View File

@ -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"

View File

@ -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

View File

@ -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:

View File

@ -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<Keyboard<layouts::Us104Key, ScancodeSet1>> =
Mutex::new(Keyboard::new(layouts::Us104Key, ScancodeSet1,
HandleControl::Ignore)
);
static ref KEYBOARD: Mutex<Keyboard<layouts::Us104Key, ScancodeSet1>> = Mutex::new(
Keyboard::new(layouts::Us104Key, ScancodeSet1, HandleControl::Ignore)
);
}
let mut keyboard = KEYBOARD.lock();

View File

@ -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");
}

View File

@ -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);
}
}

View File

@ -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<Size4KiB> for AreaFrameAllocator {
fn allocate_frame(&mut self) -> Option<Frame> {
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<Size4KiB> for AreaFrameAllocator {
unsafe fn deallocate_frame(&mut self, _frame: Frame) {
unimplemented!()
}
}

7
src/memory/mod.rs Normal file
View File

@ -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;

33
src/memory/paging/mod.rs Normal file
View File

@ -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<A>(_allocator: &mut A, _boot_info: &BootInformation)
where A: FrameAllocator<Size4KiB>
{
}
pub fn test_paging<A>(allocator: &mut A)
where A: FrameAllocator<Size4KiB>
{
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);
}
}