From deb033ff50e70652e28b8d2309feb3addb00bd5e Mon Sep 17 00:00:00 2001 From: Jack Halford Date: Mon, 5 Feb 2018 18:13:10 +0100 Subject: [PATCH] stuck in the middle of paging --- kernel-rs/Cargo.toml | 7 + kernel-rs/Makefile | 10 +- kernel-rs/src/arch/x86_64/boot.asm | 15 +- kernel-rs/src/arch/x86_64/linker.ld | 12 +- kernel-rs/src/arch/x86_64/long_mode_init.asm | 6 +- kernel-rs/src/lib.rs | 67 ++++++++- kernel-rs/src/memory/area_frame_allocator.rs | 92 ++++++++++++ kernel-rs/src/memory/mod.rs | 28 ++++ kernel-rs/src/memory/paging/entry.rs | 47 ++++++ kernel-rs/src/memory/paging/mod.rs | 10 ++ kernel-rs/src/memory/paging/table.rs | 51 +++++++ kernel-rs/src/vga_buffer.rs | 148 +++++++++++++++++++ kernel-rs/x86_64-KFS.json | 3 +- 13 files changed, 480 insertions(+), 16 deletions(-) create mode 100644 kernel-rs/src/memory/area_frame_allocator.rs create mode 100644 kernel-rs/src/memory/mod.rs create mode 100644 kernel-rs/src/memory/paging/entry.rs create mode 100644 kernel-rs/src/memory/paging/mod.rs create mode 100644 kernel-rs/src/memory/paging/table.rs create mode 100644 kernel-rs/src/vga_buffer.rs diff --git a/kernel-rs/Cargo.toml b/kernel-rs/Cargo.toml index 765e83c8..7f432ba4 100644 --- a/kernel-rs/Cargo.toml +++ b/kernel-rs/Cargo.toml @@ -5,3 +5,10 @@ authors = ["Jack Halford "] [lib] crate-type = ["staticlib"] + +[dependencies] +rlibc = "1.0" +volatile = "0.1.0" +spin = "0.4.5" +multiboot2 = "0.1.0" +bitflags = "0.9.1" diff --git a/kernel-rs/Makefile b/kernel-rs/Makefile index b43759b2..881368fc 100644 --- a/kernel-rs/Makefile +++ b/kernel-rs/Makefile @@ -2,7 +2,7 @@ arch ?= x86_64 kernel := build/kernel-$(arch).bin iso := build/os-$(arch).iso -target ?= $(arch)-KFS +target ?= $(arch)-kfs rust_os := target/$(target)/debug/libkfs.a linker_script := src/arch/$(arch)/linker.ld @@ -16,9 +16,10 @@ asm_object_files := $(patsubst src/arch/$(arch)/%.asm, \ all: $(kernel) clean: + @cargo clean @rm -r build -run: $(iso) +run: @qemu-system-x86_64 -cdrom $(iso) iso: $(iso) @@ -31,11 +32,10 @@ $(iso): $(kernel) $(grub.cfg) rm -r build/isofiles $(kernel): kernel $(asm_object_files) $(linker_script) - @ld -n -T $(linker_script) -o $(kernel) \ - $(asm_object_files) $(rust_os) + @ld -n --gc-sections -T $(linker_script) -o $(kernel) $(asm_object_files) $(rust_os) kernel: - @xargo build --target $(target) + @RUST_TARGET_PATH="$(shell pwd)" xargo build --target $(target) # compile asm files build/arch/$(arch)/%.o: src/arch/$(arch)/%.asm diff --git a/kernel-rs/src/arch/x86_64/boot.asm b/kernel-rs/src/arch/x86_64/boot.asm index e4037e21..2cb30b12 100644 --- a/kernel-rs/src/arch/x86_64/boot.asm +++ b/kernel-rs/src/arch/x86_64/boot.asm @@ -6,6 +6,10 @@ bits 32 start: mov esp, stack_top + ; Move multiboot info pointer to edi + ; which is the first argument for the rust main + mov edi, ebx + call check_multiboot call check_cpuid call check_long_mode @@ -86,14 +90,19 @@ check_long_mode: jmp error set_up_page_tables: + ; map P4 511th byte recursively to P4 + mov eax, p4_table + or eax, 0b11 ; present + writable + mov [p4_table + 511 * 8], eax + ; map first P4 entry to P3 table mov eax, p3_table - or eax, 0b11 ; present + writeable + or eax, 0b11 ; present + writable mov [p4_table], eax ; map first P3 entry to P2 table mov eax, p2_table - or eax, 0b11 ; present + writeable + or eax, 0b11 ; present + writable mov [p3_table], eax mov ecx, 0 ;counter variable @@ -142,7 +151,7 @@ p3_table: p2_table: resb 4096 stack_bottom: - resb 64 + resb 4096 * 4 stack_top: section .rodata diff --git a/kernel-rs/src/arch/x86_64/linker.ld b/kernel-rs/src/arch/x86_64/linker.ld index 89751965..06648ad5 100644 --- a/kernel-rs/src/arch/x86_64/linker.ld +++ b/kernel-rs/src/arch/x86_64/linker.ld @@ -6,11 +6,19 @@ SECTIONS { .boot : { /* ensure that the multiboot header is at the beginning */ - *(.multiboot_header) + KEEP(*(.multiboot_header)) } .text : { - *(.text) + *(.text .text.*) + } + + .rodata : { + *(.rodata .rodata.*) + } + + .data.rel.ro : { + *(.data.rel.ro.local*) *(.data.rel.ro .data.rel.ro.*) } } diff --git a/kernel-rs/src/arch/x86_64/long_mode_init.asm b/kernel-rs/src/arch/x86_64/long_mode_init.asm index 5b693dd1..e89e7955 100644 --- a/kernel-rs/src/arch/x86_64/long_mode_init.asm +++ b/kernel-rs/src/arch/x86_64/long_mode_init.asm @@ -14,7 +14,7 @@ long_mode_start: extern rust_main call rust_main - ;print 'OKAY' to screen - mov rax, 0x2f592f412f4b2f4f - mov qword [0xb8000], rax + ;;print 'OKAY' to screen + ;mov rax, 0x2f592f412f4b2f4f + ;mov qword [0xb8000], rax hlt diff --git a/kernel-rs/src/lib.rs b/kernel-rs/src/lib.rs index e65b4ff0..11c52c83 100755 --- a/kernel-rs/src/lib.rs +++ b/kernel-rs/src/lib.rs @@ -1,8 +1,71 @@ #![feature(lang_items)] +#![feature(const_fn)] +#![feature(ptr_internals)] #![no_std] +extern crate rlibc; +extern crate volatile; +extern crate spin; +extern crate multiboot2; +#[macro_use] extern crate bitflags; +#[macro_use] mod vga_buffer; +mod memory; + #[no_mangle] -pub extern fn rust_main() {} +pub extern fn rust_main(multiboot_information_address: usize) { + use memory::FrameAllocator; + + vga_buffer::clear_screen(); + println!("Hello World {}", "!"); + + let boot_info = unsafe{ multiboot2::load(multiboot_information_address)}; + + let memory_map_tag = boot_info.memory_map_tag() + .expect("Memory map tag required"); + println!("memory areas:"); + for area in memory_map_tag.memory_areas() { + println!(" start: 0x{:x}, length: 0x{:x}", + area.base_addr, area.length); + } + + let elf_sections_tag = boot_info.elf_sections_tag() + .expect("ELF sections tag required"); + println!("kernel sections:"); + for section in elf_sections_tag.sections() { + println!(" addr: 0x{:x}, length: 0x{:x}, flags: 0x{:x}", + section.addr, section.size, section.flags); + } + + let kernel_start = elf_sections_tag.sections().map(|s| s.addr) + .min().unwrap(); + let kernel_end = elf_sections_tag.sections().map(|s| s.addr + s.size) + .max().unwrap(); + + let multiboot_start = multiboot_information_address; + let multiboot_end = multiboot_start + (boot_info.total_size as usize); + + let mut frame_allocator = memory::AreaFrameAllocator::new( + kernel_start as usize, kernel_end as usize, multiboot_start, + multiboot_end, memory_map_tag.memory_areas() + ); + + for i in 0.. { + if let None = frame_allocator.allocate_frame() { + println!("allocated {} frames", i); + break; + } + } + + loop{}; +} #[lang = "eh_personality"] #[no_mangle] pub extern fn eh_personality() {} -#[lang = "panic_fmt"] #[no_mangle] pub extern fn panic_fmt() -> ! {loop{}} + +#[lang = "panic_fmt"] +#[no_mangle] +pub extern fn panic_fmt(fmt: core::fmt::Arguments, file: &'static str, + line: u32) -> ! { + println!("\n\nPANIC in {} at line {}:", file, line); + println!(" {}", fmt); + loop{} +} diff --git a/kernel-rs/src/memory/area_frame_allocator.rs b/kernel-rs/src/memory/area_frame_allocator.rs new file mode 100644 index 00000000..123aa879 --- /dev/null +++ b/kernel-rs/src/memory/area_frame_allocator.rs @@ -0,0 +1,92 @@ +use memory::{Frame, FrameAllocator}; +use multiboot2::{MemoryAreaIter, MemoryArea}; + +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: usize, + kernel_end: usize, + multiboot_start: usize, + multiboot_end: usize, + memory_areas: MemoryAreaIter + ) -> AreaFrameAllocator { + let mut allocator = AreaFrameAllocator { + next_free_frame: Frame::containing_address(0), + current_area: None, + areas: memory_areas, + kernel_start: Frame::containing_address(kernel_start), + kernel_end: Frame::containing_address(kernel_end), + multiboot_start: Frame::containing_address(multiboot_start), + multiboot_end: Frame::containing_address(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(address as usize) >= self.next_free_frame + }).min_by_key(|area| area.base_addr); + + if let Some(area) = self.current_area { + let start_frame = Frame::containing_address(area.base_addr as usize); + if self.next_free_frame < start_frame { + self.next_free_frame = start_frame; + } + } + } +} + +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{ number: self.next_free_frame.number }; + + // the last frame of the current area + let current_area_last_frame = { + let address = area.base_addr + area.length - 1; + Frame::containing_address(address as usize) + }; + + 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 { + number: self.kernel_end.number + 1 + }; + } else if frame >= self.multiboot_start && frame <= self.multiboot_end { + // 'frame' is used by the multiboot information structure + self.next_free_frame = Frame { + number: self.multiboot_end.number + 1 + }; + } else { + // 'frame' is unused, increment next_free_frame and return it; + self.next_free_frame.number += 1; + 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 + } + } + + fn deallocate_frame(&mut self, frame: Frame) { + unimplemented!() + } + +} diff --git a/kernel-rs/src/memory/mod.rs b/kernel-rs/src/memory/mod.rs new file mode 100644 index 00000000..a1358219 --- /dev/null +++ b/kernel-rs/src/memory/mod.rs @@ -0,0 +1,28 @@ +pub use self::area_frame_allocator::AreaFrameAllocator; +use self::paging::PhysicalAddress; + +mod area_frame_allocator; +mod paging; + +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] +pub struct Frame { + number: usize, +} + +pub const PAGE_SIZE: usize = 4096; + + +impl Frame { + fn containing_address(address: usize) -> Frame { + Frame{ number: address / PAGE_SIZE } + } + + fn start_address(&self) -> PhysicalAddress { + self.number * PAGE_SIZE + } +} + +pub trait FrameAllocator { + fn allocate_frame(&mut self) -> Option; + fn deallocate_frame(&mut self, frame: Frame); +} diff --git a/kernel-rs/src/memory/paging/entry.rs b/kernel-rs/src/memory/paging/entry.rs new file mode 100644 index 00000000..18415098 --- /dev/null +++ b/kernel-rs/src/memory/paging/entry.rs @@ -0,0 +1,47 @@ +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; + } +} + +impl Entry { + pub fn is_unused(&self) -> bool { + self.0 == 0; + } + + pub fn set_unused(&mut self) { + self.0 = 0; + } + + pub fn flags(&self) -> EntryFlags { + EntryFlags::from_bits_truncate(self.0) + } + + pub fn pointed_frame(&self) -> Option { + if self.flags().contains(PRESENT) { + Some(Frame::containing_address( + self.0 as usize & 0x000fffff_fffff000 + )) + } 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(); + } +} diff --git a/kernel-rs/src/memory/paging/mod.rs b/kernel-rs/src/memory/paging/mod.rs new file mode 100644 index 00000000..17896565 --- /dev/null +++ b/kernel-rs/src/memory/paging/mod.rs @@ -0,0 +1,10 @@ +use memory::PAGE_SIZE; + +const ENTRY_COUNT: usize = 512; + +pub type PhysicalAddress = usize; +pub type VirtualAddress = usize; + +pub struct Page { + number: usize, +} diff --git a/kernel-rs/src/memory/paging/table.rs b/kernel-rs/src/memory/paging/table.rs new file mode 100644 index 00000000..e8b1244d --- /dev/null +++ b/kernel-rs/src/memory/paging/table.rs @@ -0,0 +1,51 @@ +use memory::paging::entry::* +use memory::paging::ENTRY_COUNT; +use core::ops::{Index, IndexMut}; + +pub const P4: *mut Table = 0xffffffff_fffff000 as *mut _; + +pub struct Table { + entries: [Entry; ENTRY_COUNT], +} + +impl Index for Table { + type Output = Entry; + + fn index(&sef, index: usize) -> &Entry { + &self.entries[index] + } +} + +impl IndexMut for Table { + fn index_mut(&mut self, index: usize) -> &mut Entry { + &mut self.entries[index] + } +} + +impl Table { + pub fn zero(&mut self) { + for entry in self.entries.iter_mut() { + entry.set_unused(); + } + } + + 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(&self, index: usize) -> Option<&mut Table> { + self.next_table_address(index) + .map(|address| unsafe { &*(address as *mut _) }) + } + + fn next_table_address(&self, index: usize) -> Option { + let entry_flags = self[index].flags(); + if entry_flags.contains(PRESENT) && !entry_flags.contains(HUGE_PAGE) { + let table_address = self as *const _ as usize; + Some((table_address << 9) | (index << 12)) + } else { + None + } + } +} diff --git a/kernel-rs/src/vga_buffer.rs b/kernel-rs/src/vga_buffer.rs new file mode 100644 index 00000000..394ded69 --- /dev/null +++ b/kernel-rs/src/vga_buffer.rs @@ -0,0 +1,148 @@ +use core::ptr::Unique; +use volatile::Volatile; +use core::fmt; +use spin::Mutex; + +pub static WRITER: Mutex = Mutex::new(Writer { + column_position: 0, + color_code: ColorCode::new(Color::LightRed, Color::Black), + buffer: unsafe { Unique::new_unchecked(0xb8000 as *mut _) }, +}); + +macro_rules! println { + ($fmt:expr) => (print!(concat!($fmt, "\n"))); + ($fmt:expr, $($arg:tt)*) => (print!(concat!($fmt, "\n"), $($arg)*)); +} + +macro_rules! print { + ($($arg:tt)*) => ({ + $crate::vga_buffer::print(format_args!($($arg)*)); + }); +} + +pub fn print(args: fmt::Arguments) { + use core::fmt::Write; + WRITER.lock().write_fmt(args).unwrap(); +} + +impl fmt::Write for Writer { + fn write_str(&mut self, s: &str) -> fmt::Result { + for byte in s.bytes() { + self.write_byte(byte) + } + Ok(()) + } +} + +#[allow(dead_code)] +#[derive(Debug, Clone, Copy)] +#[repr(u8)] +pub enum Color { + Black = 0, + Blue = 1, + Green = 2, + Cyan = 3, + Red = 4, + Magenta = 5, + Brown = 6, + LightGray = 7, + DarkGray = 8, + LightBlue = 9, + LightGreen = 10, + LightCyan = 11, + LightRed = 12, + Pink = 13, + Yellow = 14, + White = 15, +} + +#[derive(Debug, Clone, Copy)] +struct ColorCode(u8); + +impl ColorCode { + const fn new(foreground: Color, background: Color) -> ColorCode { + ColorCode((background as u8) << 4 | (foreground as u8)) + } +} + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +struct ScreenChar { + ascii_character: u8, + color_code: ColorCode, +} + +const BUFFER_HEIGHT: usize = 25; +const BUFFER_WIDTH: usize = 80; + +struct Buffer { + chars: [[Volatile; BUFFER_WIDTH]; BUFFER_HEIGHT] +} + +pub struct Writer { + column_position: usize, + color_code: ColorCode, + buffer: Unique, +} + +#[allow(dead_code)] +impl Writer { + pub fn write_byte(&mut self, byte: u8) { + match byte { + b'\n' => self.new_line(), + byte => { + if self.column_position >= BUFFER_WIDTH { + self.new_line(); + } + + let row = BUFFER_HEIGHT - 1; + let col = self.column_position; + + let color_code = self.color_code; + self.buffer().chars[row][col].write(ScreenChar { + ascii_character: byte, + color_code: color_code, + }); + self.column_position += 1; + } + } + } + + pub fn write_str(&mut self, s: &str) { + for byte in s.bytes() { + self.write_byte(byte) + } + } + + fn buffer(&mut self) -> &mut Buffer { + unsafe{ self.buffer.as_mut()} + } + + fn new_line(&mut self) { + for row in 1..BUFFER_HEIGHT { + for col in 0..BUFFER_WIDTH { + let buffer = self.buffer(); + let character = buffer.chars[row][col].read(); + buffer.chars[row - 1][col].write(character); + } + } + self.clear_row(BUFFER_HEIGHT - 1); + self.column_position = 0; + } + + fn clear_row(&mut self, row: usize) { + let blank = ScreenChar { + ascii_character: b' ', + color_code: self.color_code, + }; + for col in 0..BUFFER_WIDTH { + self.buffer().chars[row][col].write(blank); + } + } +} + +pub fn clear_screen() { + for _ in 0..BUFFER_HEIGHT { + println!(""); + } +} diff --git a/kernel-rs/x86_64-KFS.json b/kernel-rs/x86_64-KFS.json index ab1cb664..d2fa22e1 100644 --- a/kernel-rs/x86_64-KFS.json +++ b/kernel-rs/x86_64-KFS.json @@ -8,5 +8,6 @@ "arch": "x86_64", "os": "none", "disable-redzone": true, - "features": "-mmx,-sse,+soft-float" + "features": "-mmx,-sse,+soft-float", + "panic-strategy": "abort" }