mmu code mostly written, needs a lot of debugging still
This commit is contained in:
parent
d491624274
commit
3041cec87d
14 changed files with 622 additions and 81 deletions
|
|
@ -9,5 +9,4 @@ crate-type = ["staticlib"]
|
||||||
[dependencies]
|
[dependencies]
|
||||||
rlibc = "1.0"
|
rlibc = "1.0"
|
||||||
bitflags = "1.0.1"
|
bitflags = "1.0.1"
|
||||||
# spin = "0.4.5"
|
|
||||||
multiboot2 = { path = "multiboot2-elf64" }
|
multiboot2 = { path = "multiboot2-elf64" }
|
||||||
|
|
|
||||||
|
|
@ -4,12 +4,61 @@ extern x86_start
|
||||||
section .text
|
section .text
|
||||||
bits 32
|
bits 32
|
||||||
start:
|
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
|
push ebx
|
||||||
|
|
||||||
call check_multiboot
|
call check_multiboot
|
||||||
|
|
||||||
|
call set_up_page_tables
|
||||||
|
; call enable_paging
|
||||||
|
|
||||||
lgdt [GDTR.ptr] ; load the new gdt
|
lgdt [GDTR.ptr] ; load the new gdt
|
||||||
jmp GDTR.gdt_cs:x86_start
|
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:
|
error:
|
||||||
mov dword [0xb8000], 0x4f524f45
|
mov dword [0xb8000], 0x4f524f45
|
||||||
|
|
@ -21,14 +70,6 @@ HALT:
|
||||||
hlt
|
hlt
|
||||||
jmp HALT
|
jmp HALT
|
||||||
|
|
||||||
check_multiboot:
|
|
||||||
cmp eax, 0x36d76289
|
|
||||||
jne .no_multiboot
|
|
||||||
ret
|
|
||||||
.no_multiboot:
|
|
||||||
mov al, "0"
|
|
||||||
jmp error
|
|
||||||
|
|
||||||
section .gdt
|
section .gdt
|
||||||
GDTR:
|
GDTR:
|
||||||
; http://tuttlem.github.io/2014/07/11/a-gdt-primer.html
|
; http://tuttlem.github.io/2014/07/11/a-gdt-primer.html
|
||||||
|
|
@ -88,6 +129,12 @@ GDTR:
|
||||||
DD .gdt_top ; pointer to top of gdt
|
DD .gdt_top ; pointer to top of gdt
|
||||||
|
|
||||||
section .bss
|
section .bss
|
||||||
|
align 4096
|
||||||
|
p2_table:
|
||||||
|
resb 4096
|
||||||
|
p1_table:
|
||||||
|
resb 4096
|
||||||
stack_bottom:
|
stack_bottom:
|
||||||
resb 4096 * 16
|
resb 4096 * 4
|
||||||
stack_top:
|
stack_top:
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,10 +13,38 @@ SECTIONS {
|
||||||
|
|
||||||
. = 1M;
|
. = 1M;
|
||||||
/* ensure that the multiboot header is at the beginning */
|
/* ensure that the multiboot header is at the beginning */
|
||||||
.multiboot : { KEEP(*(.multiboot)) }
|
.multiboot :
|
||||||
.text : { *(.text .text.*) }
|
{
|
||||||
.rodata : { *(.rodata .rodata.*) }
|
KEEP(*(.multiboot))
|
||||||
.data : { *(.data.rel.ro.local*) *(.data.rel.ro .data.rel.ro.*) *(.data.*) }
|
. = ALIGN(4K);
|
||||||
.debug : { *(.debug_*) }
|
}
|
||||||
.bss : {*(.bss .bss.*)}
|
|
||||||
}
|
.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);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ extern crate core;
|
||||||
use acpi;
|
use acpi;
|
||||||
use cpuio;
|
use cpuio;
|
||||||
use context;
|
use context;
|
||||||
|
use memory;
|
||||||
// use multiboot2;
|
// use multiboot2;
|
||||||
use core::char;
|
use core::char;
|
||||||
use vga::*;
|
use vga::*;
|
||||||
|
|
@ -18,6 +19,7 @@ fn dispatch(command: &str) -> Result <(), &'static str> {
|
||||||
"sections" => self::mb2_sections(),
|
"sections" => self::mb2_sections(),
|
||||||
"shutdown" | "halt" | "q" => self::shutdown(),
|
"shutdown" | "halt" | "q" => self::shutdown(),
|
||||||
"stack" => self::print_stack(),
|
"stack" => self::print_stack(),
|
||||||
|
"test" => self::test(),
|
||||||
_ => Err("Command unknown. (h|help for help)"),
|
_ => Err("Command unknown. (h|help for help)"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -45,6 +47,12 @@ fn help() -> Result <(), &'static str> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn test() -> Result<(), &'static str>
|
||||||
|
{
|
||||||
|
memory::test_paging(context::frame_allocator());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Reboot the kernel
|
/// Reboot the kernel
|
||||||
///
|
///
|
||||||
/// If reboot failed, will loop on a halt cmd
|
/// If reboot failed, will loop on a halt cmd
|
||||||
|
|
|
||||||
|
|
@ -6,10 +6,6 @@ pub static mut CONTEXT: Option<Context> = None;
|
||||||
|
|
||||||
pub struct Context {
|
pub struct Context {
|
||||||
pub current_term: u8,
|
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 boot_info: multiboot2::BootInformation,
|
||||||
pub frame_allocator: memory::AreaFrameAllocator,
|
pub frame_allocator: memory::AreaFrameAllocator,
|
||||||
pub vga1: vga::Writer,
|
pub vga1: vga::Writer,
|
||||||
|
|
@ -41,10 +37,6 @@ impl Context
|
||||||
|
|
||||||
Context {
|
Context {
|
||||||
current_term: 0,
|
current_term: 0,
|
||||||
multiboot_start,
|
|
||||||
multiboot_end,
|
|
||||||
kernel_start,
|
|
||||||
kernel_end,
|
|
||||||
boot_info,
|
boot_info,
|
||||||
frame_allocator,
|
frame_allocator,
|
||||||
vga1,
|
vga1,
|
||||||
|
|
@ -52,31 +44,41 @@ impl Context
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init_screen() {
|
pub fn init_screen() {
|
||||||
set_color!(White, Cyan);
|
set_color!(White, Cyan);
|
||||||
print!("{}{}{}{}{}{}{}{}{}{}{}{}{}{}",
|
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!();
|
set_color!();
|
||||||
context().vga1.prompt();
|
context().vga1.prompt();
|
||||||
context().vga2.prompt();
|
context().vga2.prompt();
|
||||||
context().vga1.flush();
|
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() {
|
pub fn switch_term() {
|
||||||
context().current_term = {
|
context().current_term = {
|
||||||
if context().current_term == 0 { 1 }
|
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 {
|
fn context() -> &'static mut Context {
|
||||||
unsafe {
|
unsafe {
|
||||||
match CONTEXT {
|
match CONTEXT {
|
||||||
|
|
@ -107,4 +105,7 @@ fn context() -> &'static mut Context {
|
||||||
|
|
||||||
pub fn init(multiboot_info_addr: usize) {
|
pub fn init(multiboot_info_addr: usize) {
|
||||||
unsafe { CONTEXT = Some(Context::new(multiboot_info_addr)) };
|
unsafe { CONTEXT = Some(Context::new(multiboot_info_addr)) };
|
||||||
|
|
||||||
|
// memory::remap_the_kernel(frame_allocator(), boot_info());
|
||||||
|
self::init_screen();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
//! project hosted at (https://github.com/jzck/kernel)
|
//! project hosted on [github](https://github.com/jzck/kernel)
|
||||||
|
|
||||||
#![no_std]
|
#![no_std]
|
||||||
#![feature(lang_items)]
|
#![feature(lang_items)]
|
||||||
|
|
@ -22,17 +22,15 @@ pub mod console;
|
||||||
pub mod cpuio;
|
pub mod cpuio;
|
||||||
/// ACPI self-content module
|
/// ACPI self-content module
|
||||||
pub mod acpi;
|
pub mod acpi;
|
||||||
/// simple area frame allocator implementation
|
/// physical frame allocator + paging module
|
||||||
pub mod memory;
|
pub mod memory;
|
||||||
|
/// a few x86 instruction wrappers
|
||||||
// use vga::{Color, ColorCode};
|
pub mod x86;
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern fn kmain(multiboot_info_addr: usize) -> ! {
|
pub extern fn kmain(multiboot_info_addr: usize) -> ! {
|
||||||
context::init(multiboot_info_addr);
|
context::init(multiboot_info_addr);
|
||||||
context::init_screen();
|
|
||||||
acpi::init().unwrap();
|
acpi::init().unwrap();
|
||||||
|
|
||||||
loop { keyboard::kbd_callback(); }
|
loop { keyboard::kbd_callback(); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -42,13 +40,12 @@ pub extern fn eh_personality() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[lang = "panic_fmt"] #[no_mangle]
|
#[lang = "panic_fmt"] #[no_mangle]
|
||||||
pub extern fn panic_fmt(
|
pub extern fn panic_fmt(fmt: core::fmt::Arguments, file: &'static str, line: u32)
|
||||||
fmt: core::fmt::Arguments, file: &'static str, line: u32
|
|
||||||
)
|
|
||||||
-> ! {
|
-> ! {
|
||||||
println!("PANIC: {}", fmt);
|
println!("PANIC: {}", fmt);
|
||||||
println!("FILE: {}", file);
|
println!("FILE: {}", file);
|
||||||
println!("LINE: {}", line);
|
println!("LINE: {}", line);
|
||||||
|
flush!();
|
||||||
loop {}
|
loop {}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,8 @@ mod area_allocator;
|
||||||
mod paging;
|
mod paging;
|
||||||
|
|
||||||
pub use self::area_allocator::*;
|
pub use self::area_allocator::*;
|
||||||
|
pub use self::paging::test_paging;
|
||||||
|
pub use self::paging::remap_the_kernel;
|
||||||
use self::paging::PhysicalAddress;
|
use self::paging::PhysicalAddress;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
|
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
|
@ -19,9 +21,39 @@ impl Frame {
|
||||||
fn start_address(&self) -> PhysicalAddress {
|
fn start_address(&self) -> PhysicalAddress {
|
||||||
self.number * PAGE_SIZE
|
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 {
|
pub trait FrameAllocator {
|
||||||
fn allocate_frame(&mut self) -> Option<Frame>;
|
fn allocate_frame(&mut self) -> Option<Frame>;
|
||||||
fn deallocate_frame(&mut self, frame: Frame);
|
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<Frame> {
|
||||||
|
if self.start <= self.end {
|
||||||
|
let frame = self.start.clone();
|
||||||
|
self.start.number += 1;
|
||||||
|
Some(frame)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,21 +1,6 @@
|
||||||
use memory::Frame;
|
use memory::Frame;
|
||||||
|
|
||||||
pub struct Entry(u64);
|
pub struct Entry(u32);
|
||||||
|
|
||||||
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 {
|
impl Entry {
|
||||||
pub fn is_unused(&self) -> bool {
|
pub fn is_unused(&self) -> bool {
|
||||||
|
|
@ -33,15 +18,30 @@ impl Entry {
|
||||||
pub fn pointed_frame(&self) -> Option<Frame> {
|
pub fn pointed_frame(&self) -> Option<Frame> {
|
||||||
if self.flags().contains(EntryFlags::PRESENT) {
|
if self.flags().contains(EntryFlags::PRESENT) {
|
||||||
Some(Frame::containing_address(
|
Some(Frame::containing_address(
|
||||||
// actual addr is bits 12-51
|
self.0 as usize & 0xffff_f000))
|
||||||
self.0 as usize & 0x000fffff_fffff000))
|
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set(&mut self, frame: Frame, flags: EntryFlags) {
|
pub fn set(&mut self, frame: Frame, flags: EntryFlags) {
|
||||||
assert!(frame.start_address() & !0x000fffff_fffff000 == 0);
|
assert!(frame.start_address() & !0xffff_f000 == 0);
|
||||||
self.0 = (frame.start_address() as u64) | flags.bits();
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
85
kernel-rs/src/memory/paging/mapper.rs
Normal file
85
kernel-rs/src/memory/paging/mapper.rs
Normal file
|
|
@ -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<Table<Level2>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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<Level2> {
|
||||||
|
unsafe { self.p2.as_ref() }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn p2_mut(&mut self) -> &mut Table<Level2> {
|
||||||
|
unsafe { self.p2.as_mut() }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn translate(&self, virtual_address: VirtualAddress) -> Option<PhysicalAddress>
|
||||||
|
{
|
||||||
|
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<Frame> {
|
||||||
|
|
||||||
|
// 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<A>(&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<A>(&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<A>(&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<A>(&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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,13 +1,202 @@
|
||||||
|
#![allow(dead_code)]
|
||||||
|
|
||||||
mod entry;
|
mod entry;
|
||||||
mod table;
|
mod table;
|
||||||
|
mod temporary_page;
|
||||||
|
mod mapper;
|
||||||
|
|
||||||
use memory::PAGE_SIZE;
|
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 PhysicalAddress = usize;
|
||||||
pub type VirtualAddress = usize;
|
pub type VirtualAddress = usize;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub struct Page {
|
pub struct Page {
|
||||||
number: usize,
|
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<F>(&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<A>(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<A>(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!();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,21 @@
|
||||||
use memory::paging::entry::*;
|
use memory::*;
|
||||||
use memory::paging::ENTRY_COUNT;
|
use memory::paging::*;
|
||||||
|
|
||||||
use core::ops::{Index, IndexMut};
|
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<Level2> = 0xffff_f000 as *mut _;
|
||||||
|
|
||||||
|
pub struct Table<L: TableLevel> {
|
||||||
entries: [Entry; ENTRY_COUNT],
|
entries: [Entry; ENTRY_COUNT],
|
||||||
|
level: PhantomData<L>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Table {
|
impl<L> Table<L> where L: TableLevel
|
||||||
|
{
|
||||||
pub fn zero(&mut self) {
|
pub fn zero(&mut self) {
|
||||||
for entry in self.entries.iter_mut() {
|
for entry in self.entries.iter_mut() {
|
||||||
entry.set_unused();
|
entry.set_unused();
|
||||||
|
|
@ -15,7 +23,46 @@ impl Table {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Index<usize> for Table {
|
impl<L> Table<L> where L: HierarchicalLevel
|
||||||
|
{
|
||||||
|
fn next_table_address(&self, index: usize) -> Option<usize> {
|
||||||
|
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<L::NextLevel>> {
|
||||||
|
self.next_table_address(index)
|
||||||
|
.map(|address| unsafe { &*(address as *const _) })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn next_table_mut(&mut self, index: usize) -> Option<&mut Table<L::NextLevel>> {
|
||||||
|
self.next_table_address(index)
|
||||||
|
.map(|address| unsafe { &mut *(address as *mut _) })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn next_table_create<A>(&mut self,
|
||||||
|
index: usize,
|
||||||
|
allocator: &mut A) -> &mut Table<L::NextLevel>
|
||||||
|
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<L> Index<usize> for Table<L> where L: TableLevel
|
||||||
|
{
|
||||||
type Output = Entry;
|
type Output = Entry;
|
||||||
|
|
||||||
fn index(&self, index: usize) -> &Entry {
|
fn index(&self, index: usize) -> &Entry {
|
||||||
|
|
@ -23,8 +70,26 @@ impl Index<usize> for Table {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IndexMut<usize> for Table {
|
impl<L> IndexMut<usize> for Table<L> where L: TableLevel
|
||||||
|
{
|
||||||
fn index_mut(&mut self, index: usize) -> &mut Entry {
|
fn index_mut(&mut self, index: usize) -> &mut Entry {
|
||||||
&mut self.entries[index]
|
&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; }
|
||||||
|
|
|
||||||
79
kernel-rs/src/memory/paging/temporary_page.rs
Normal file
79
kernel-rs/src/memory/paging/temporary_page.rs
Normal file
|
|
@ -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<A>(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<Level1> {
|
||||||
|
unsafe { &mut *(self.map(frame, active_table) as *mut Table<Level1>) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct TinyAllocator([Option<Frame>; 1]);
|
||||||
|
|
||||||
|
impl TinyAllocator {
|
||||||
|
fn new<A>(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<Frame> {
|
||||||
|
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.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -25,7 +25,7 @@ macro_rules! println {
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! flush {
|
macro_rules! flush {
|
||||||
() => (context::current_term().flush());
|
() => ($crate::context::current_term().flush());
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! set_color {
|
macro_rules! set_color {
|
||||||
|
|
|
||||||
11
kernel-rs/src/x86/mod.rs
Normal file
11
kernel-rs/src/x86/mod.rs
Normal file
|
|
@ -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");
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue