stuck in the middle of paging

This commit is contained in:
Jack Halford 2018-02-05 18:13:10 +01:00
parent 9a6ba1d03d
commit deb033ff50
13 changed files with 480 additions and 16 deletions

View file

@ -5,3 +5,10 @@ authors = ["Jack Halford <jack@crans.org>"]
[lib] [lib]
crate-type = ["staticlib"] crate-type = ["staticlib"]
[dependencies]
rlibc = "1.0"
volatile = "0.1.0"
spin = "0.4.5"
multiboot2 = "0.1.0"
bitflags = "0.9.1"

View file

@ -2,7 +2,7 @@ arch ?= x86_64
kernel := build/kernel-$(arch).bin kernel := build/kernel-$(arch).bin
iso := build/os-$(arch).iso iso := build/os-$(arch).iso
target ?= $(arch)-KFS target ?= $(arch)-kfs
rust_os := target/$(target)/debug/libkfs.a rust_os := target/$(target)/debug/libkfs.a
linker_script := src/arch/$(arch)/linker.ld linker_script := src/arch/$(arch)/linker.ld
@ -16,9 +16,10 @@ asm_object_files := $(patsubst src/arch/$(arch)/%.asm, \
all: $(kernel) all: $(kernel)
clean: clean:
@cargo clean
@rm -r build @rm -r build
run: $(iso) run:
@qemu-system-x86_64 -cdrom $(iso) @qemu-system-x86_64 -cdrom $(iso)
iso: $(iso) iso: $(iso)
@ -31,11 +32,10 @@ $(iso): $(kernel) $(grub.cfg)
rm -r build/isofiles rm -r build/isofiles
$(kernel): kernel $(asm_object_files) $(linker_script) $(kernel): kernel $(asm_object_files) $(linker_script)
@ld -n -T $(linker_script) -o $(kernel) \ @ld -n --gc-sections -T $(linker_script) -o $(kernel) $(asm_object_files) $(rust_os)
$(asm_object_files) $(rust_os)
kernel: kernel:
@xargo build --target $(target) @RUST_TARGET_PATH="$(shell pwd)" xargo build --target $(target)
# compile asm files # compile asm files
build/arch/$(arch)/%.o: src/arch/$(arch)/%.asm build/arch/$(arch)/%.o: src/arch/$(arch)/%.asm

View file

@ -6,6 +6,10 @@ bits 32
start: start:
mov esp, stack_top 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_multiboot
call check_cpuid call check_cpuid
call check_long_mode call check_long_mode
@ -86,14 +90,19 @@ check_long_mode:
jmp error jmp error
set_up_page_tables: 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 ; map first P4 entry to P3 table
mov eax, p3_table mov eax, p3_table
or eax, 0b11 ; present + writeable or eax, 0b11 ; present + writable
mov [p4_table], eax mov [p4_table], eax
; map first P3 entry to P2 table ; map first P3 entry to P2 table
mov eax, p2_table mov eax, p2_table
or eax, 0b11 ; present + writeable or eax, 0b11 ; present + writable
mov [p3_table], eax mov [p3_table], eax
mov ecx, 0 ;counter variable mov ecx, 0 ;counter variable
@ -142,7 +151,7 @@ p3_table:
p2_table: p2_table:
resb 4096 resb 4096
stack_bottom: stack_bottom:
resb 64 resb 4096 * 4
stack_top: stack_top:
section .rodata section .rodata

View file

@ -6,11 +6,19 @@ SECTIONS {
.boot : .boot :
{ {
/* ensure that the multiboot header is at the beginning */ /* ensure that the multiboot header is at the beginning */
*(.multiboot_header) KEEP(*(.multiboot_header))
} }
.text : .text :
{ {
*(.text) *(.text .text.*)
}
.rodata : {
*(.rodata .rodata.*)
}
.data.rel.ro : {
*(.data.rel.ro.local*) *(.data.rel.ro .data.rel.ro.*)
} }
} }

View file

@ -14,7 +14,7 @@ long_mode_start:
extern rust_main extern rust_main
call rust_main call rust_main
;print 'OKAY' to screen ;;print 'OKAY' to screen
mov rax, 0x2f592f412f4b2f4f ;mov rax, 0x2f592f412f4b2f4f
mov qword [0xb8000], rax ;mov qword [0xb8000], rax
hlt hlt

View file

@ -1,8 +1,71 @@
#![feature(lang_items)] #![feature(lang_items)]
#![feature(const_fn)]
#![feature(ptr_internals)]
#![no_std] #![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] #[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 = "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{}
}

View file

@ -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<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{ 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!()
}
}

View file

@ -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<Frame>;
fn deallocate_frame(&mut self, frame: Frame);
}

View file

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

View file

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

View file

@ -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<usize> for Table {
type Output = Entry;
fn index(&sef, index: usize) -> &Entry {
&self.entries[index]
}
}
impl IndexMut<usize> 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<usize> {
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
}
}
}

148
kernel-rs/src/vga_buffer.rs Normal file
View file

@ -0,0 +1,148 @@
use core::ptr::Unique;
use volatile::Volatile;
use core::fmt;
use spin::Mutex;
pub static WRITER: Mutex<Writer> = 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<ScreenChar>; BUFFER_WIDTH]; BUFFER_HEIGHT]
}
pub struct Writer {
column_position: usize,
color_code: ColorCode,
buffer: Unique<Buffer>,
}
#[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!("");
}
}

View file

@ -8,5 +8,6 @@
"arch": "x86_64", "arch": "x86_64",
"os": "none", "os": "none",
"disable-redzone": true, "disable-redzone": true,
"features": "-mmx,-sse,+soft-float" "features": "-mmx,-sse,+soft-float",
"panic-strategy": "abort"
} }