mirror of
https://github.com/phil-opp/blog_os.git
synced 2025-12-16 14:27:49 +00:00
Delete code from first edition
This commit is contained in:
18
Cargo.toml
18
Cargo.toml
@@ -1,22 +1,6 @@
|
||||
[package]
|
||||
authors = ["Philipp Oppermann <dev@phil-opp.com>"]
|
||||
name = "blog_os"
|
||||
version = "0.1.0"
|
||||
version = "0.2.0"
|
||||
|
||||
[dependencies]
|
||||
bit_field = "0.7.0"
|
||||
bitflags = "0.9.1"
|
||||
multiboot2 = "0.1.0"
|
||||
once = "0.3.2"
|
||||
rlibc = "1.0"
|
||||
spin = "0.4.5"
|
||||
volatile = "0.1.0"
|
||||
x86_64 = "0.1.2"
|
||||
linked_list_allocator = "0.4.2"
|
||||
|
||||
[dependencies.lazy_static]
|
||||
features = ["spin_no_std"]
|
||||
version = "0.2.1"
|
||||
|
||||
[lib]
|
||||
crate-type = ["staticlib"]
|
||||
|
||||
82
Makefile
82
Makefile
@@ -1,82 +0,0 @@
|
||||
# Copyright 2016 Philipp Oppermann. See the README.md
|
||||
# file at the top-level directory of this distribution.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
# http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
# <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
# option. This file may not be copied, modified, or distributed
|
||||
# except according to those terms.
|
||||
|
||||
arch ?= x86_64
|
||||
target ?= $(arch)-blog_os
|
||||
kernel := build/kernel-$(arch).bin
|
||||
iso := build/os-$(arch).iso
|
||||
|
||||
rust_os := target/$(target)/debug/libblog_os.a
|
||||
linker_script := src/arch/$(arch)/linker.ld
|
||||
grub_cfg := src/arch/$(arch)/grub.cfg
|
||||
assembly_source_files := $(wildcard src/arch/$(arch)/*.asm)
|
||||
assembly_object_files := $(patsubst src/arch/$(arch)/%.asm, \
|
||||
build/arch/$(arch)/%.o, $(assembly_source_files))
|
||||
|
||||
# used by docker_* targets
|
||||
docker_image ?= blog_os
|
||||
tag ?= 0.1
|
||||
docker_cargo_volume ?= blogos-$(shell id -u)-$(shell id -g)-cargo
|
||||
docker_rustup_volume ?= blogos-$(shell id -u)-$(shell id -g)-rustup
|
||||
docker_args ?= -e LOCAL_UID=$(shell id -u) -e LOCAL_GID=$(shell id -g) -v $(docker_cargo_volume):/usr/local/cargo -v $(docker_rustup_volume):/usr/local/rustup -v $(shell pwd):$(shell pwd) -w $(shell pwd)
|
||||
docker_clean_args ?= $(docker_cargo_volume) $(docker_rustup_volume)
|
||||
|
||||
.PHONY: all clean run debug iso cargo gdb
|
||||
|
||||
all: $(kernel)
|
||||
|
||||
clean:
|
||||
@cargo clean
|
||||
@rm -rf build
|
||||
|
||||
run: $(iso)
|
||||
@qemu-system-x86_64 -cdrom $(iso) -s
|
||||
|
||||
debug: $(iso)
|
||||
@qemu-system-x86_64 -cdrom $(iso) -s -S
|
||||
|
||||
# docker_* targets
|
||||
|
||||
docker_build:
|
||||
@docker build docker/ -t $(docker_image):$(tag)
|
||||
|
||||
docker_iso:
|
||||
@docker run --rm $(docker_args) $(docker_image):$(tag) make iso
|
||||
|
||||
docker_run: docker_iso
|
||||
@qemu-system-x86_64 -cdrom $(iso) -s
|
||||
|
||||
docker_interactive:
|
||||
@docker run -it --rm $(docker_args) $(docker_image):$(tag)
|
||||
|
||||
docker_clean:
|
||||
@docker volume rm $(docker_clean_args)
|
||||
|
||||
gdb:
|
||||
@rust-os-gdb/bin/rust-gdb "build/kernel-x86_64.bin" -ex "target remote :1234"
|
||||
|
||||
iso: $(iso)
|
||||
|
||||
$(iso): $(kernel) $(grub_cfg)
|
||||
@mkdir -p build/isofiles/boot/grub
|
||||
@cp $(kernel) build/isofiles/boot/kernel.bin
|
||||
@cp $(grub_cfg) build/isofiles/boot/grub
|
||||
@grub-mkrescue -o $(iso) build/isofiles 2> /dev/null
|
||||
@rm -r build/isofiles
|
||||
|
||||
$(kernel): cargo $(rust_os) $(assembly_object_files) $(linker_script)
|
||||
@ld -n --gc-sections -T $(linker_script) -o $(kernel) $(assembly_object_files) $(rust_os)
|
||||
|
||||
cargo:
|
||||
@RUST_TARGET_PATH="$(shell pwd)" xargo build --target $(target)
|
||||
|
||||
# compile assembly files
|
||||
build/arch/$(arch)/%.o: src/arch/$(arch)/%.asm
|
||||
@mkdir -p $(shell dirname $@)
|
||||
@nasm -felf64 $< -o $@
|
||||
@@ -1,2 +0,0 @@
|
||||
[target.x86_64-blog_os.dependencies]
|
||||
alloc = {}
|
||||
2
libs/bump_allocator/.gitignore
vendored
2
libs/bump_allocator/.gitignore
vendored
@@ -1,2 +0,0 @@
|
||||
# Generated by Cargo
|
||||
/target/
|
||||
@@ -1,202 +0,0 @@
|
||||
; Copyright 2016 Philipp Oppermann. See the README.md
|
||||
; file at the top-level directory of this distribution.
|
||||
;
|
||||
; Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
; http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
; <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
; option. This file may not be copied, modified, or distributed
|
||||
; except according to those terms.
|
||||
|
||||
global start
|
||||
extern long_mode_start
|
||||
|
||||
section .text
|
||||
bits 32
|
||||
start:
|
||||
mov esp, stack_top
|
||||
; Move Multiboot info pointer to edi to pass it to the kernel. We must not
|
||||
; modify the `edi` register until the kernel it called.
|
||||
mov edi, ebx
|
||||
|
||||
call check_multiboot
|
||||
call check_cpuid
|
||||
call check_long_mode
|
||||
|
||||
call set_up_page_tables
|
||||
call enable_paging
|
||||
call set_up_SSE
|
||||
|
||||
; load the 64-bit GDT
|
||||
lgdt [gdt64.pointer]
|
||||
|
||||
jmp gdt64.code:long_mode_start
|
||||
|
||||
set_up_page_tables:
|
||||
; recursive map 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 + writable
|
||||
mov [p4_table], eax
|
||||
|
||||
; map first P3 entry to P2 table
|
||||
mov eax, p2_table
|
||||
or eax, 0b11 ; present + writable
|
||||
mov [p3_table], 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
|
||||
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, 512 ; if counter == 512, the whole P2 table is mapped
|
||||
jne .map_p2_table ; else map the next entry
|
||||
|
||||
ret
|
||||
|
||||
enable_paging:
|
||||
; load P4 to cr3 register (cpu uses this to access the P4 table)
|
||||
mov eax, p4_table
|
||||
mov cr3, eax
|
||||
|
||||
; enable PAE-flag in cr4 (Physical Address Extension)
|
||||
mov eax, cr4
|
||||
or eax, 1 << 5
|
||||
mov cr4, eax
|
||||
|
||||
; set the long mode bit in the EFER MSR (model specific register)
|
||||
mov ecx, 0xC0000080
|
||||
rdmsr
|
||||
or eax, 1 << 8
|
||||
wrmsr
|
||||
|
||||
; enable paging in the cr0 register
|
||||
mov eax, cr0
|
||||
or eax, 1 << 31
|
||||
mov cr0, eax
|
||||
|
||||
ret
|
||||
|
||||
; Prints `ERR: ` and the given error code to screen and hangs.
|
||||
; parameter: error code (in ascii) in al
|
||||
error:
|
||||
mov dword [0xb8000], 0x4f524f45
|
||||
mov dword [0xb8004], 0x4f3a4f52
|
||||
mov dword [0xb8008], 0x4f204f20
|
||||
mov byte [0xb800a], al
|
||||
hlt
|
||||
|
||||
; Throw error 0 if eax doesn't contain the Multiboot 2 magic value (0x36d76289).
|
||||
check_multiboot:
|
||||
cmp eax, 0x36d76289
|
||||
jne .no_multiboot
|
||||
ret
|
||||
.no_multiboot:
|
||||
mov al, "0"
|
||||
jmp error
|
||||
|
||||
; Throw error 1 if the CPU doesn't support the CPUID command.
|
||||
check_cpuid:
|
||||
; Check if CPUID is supported by attempting to flip the ID bit (bit 21) in
|
||||
; the FLAGS register. If we can flip it, CPUID is available.
|
||||
|
||||
; Copy FLAGS in to EAX via stack
|
||||
pushfd
|
||||
pop eax
|
||||
|
||||
; Copy to ECX as well for comparing later on
|
||||
mov ecx, eax
|
||||
|
||||
; Flip the ID bit
|
||||
xor eax, 1 << 21
|
||||
|
||||
; Copy EAX to FLAGS via the stack
|
||||
push eax
|
||||
popfd
|
||||
|
||||
; Copy FLAGS back to EAX (with the flipped bit if CPUID is supported)
|
||||
pushfd
|
||||
pop eax
|
||||
|
||||
; Restore FLAGS from the old version stored in ECX (i.e. flipping the ID bit
|
||||
; back if it was ever flipped).
|
||||
push ecx
|
||||
popfd
|
||||
|
||||
; Compare EAX and ECX. If they are equal then that means the bit wasn't
|
||||
; flipped, and CPUID isn't supported.
|
||||
cmp eax, ecx
|
||||
je .no_cpuid
|
||||
ret
|
||||
.no_cpuid:
|
||||
mov al, "1"
|
||||
jmp error
|
||||
|
||||
; Throw error 2 if the CPU doesn't support Long Mode.
|
||||
check_long_mode:
|
||||
; test if extended processor info in available
|
||||
mov eax, 0x80000000 ; implicit argument for cpuid
|
||||
cpuid ; get highest supported argument
|
||||
cmp eax, 0x80000001 ; it needs to be at least 0x80000001
|
||||
jb .no_long_mode ; if it's less, the CPU is too old for long mode
|
||||
|
||||
; use extended info to test if long mode is available
|
||||
mov eax, 0x80000001 ; argument for extended processor info
|
||||
cpuid ; returns various feature bits in ecx and edx
|
||||
test edx, 1 << 29 ; test if the LM-bit is set in the D-register
|
||||
jz .no_long_mode ; If it's not set, there is no long mode
|
||||
ret
|
||||
.no_long_mode:
|
||||
mov al, "2"
|
||||
jmp error
|
||||
|
||||
; Check for SSE and enable it. If it's not supported throw error "a".
|
||||
set_up_SSE:
|
||||
; check for SSE
|
||||
mov eax, 0x1
|
||||
cpuid
|
||||
test edx, 1<<25
|
||||
jz .no_SSE
|
||||
|
||||
; enable SSE
|
||||
mov eax, cr0
|
||||
and ax, 0xFFFB ; clear coprocessor emulation CR0.EM
|
||||
or ax, 0x2 ; set coprocessor monitoring CR0.MP
|
||||
mov cr0, eax
|
||||
mov eax, cr4
|
||||
or ax, 3 << 9 ; set CR4.OSFXSR and CR4.OSXMMEXCPT at the same time
|
||||
mov cr4, eax
|
||||
|
||||
ret
|
||||
.no_SSE:
|
||||
mov al, "a"
|
||||
jmp error
|
||||
|
||||
section .bss
|
||||
align 4096
|
||||
p4_table:
|
||||
resb 4096
|
||||
p3_table:
|
||||
resb 4096
|
||||
p2_table:
|
||||
resb 4096
|
||||
stack_bottom:
|
||||
resb 4096 * 4
|
||||
stack_top:
|
||||
|
||||
section .rodata
|
||||
gdt64:
|
||||
dq 0 ; zero entry
|
||||
.code: equ $ - gdt64 ; new
|
||||
dq (1<<44) | (1<<47) | (1<<43) | (1<<53) ; code segment
|
||||
.pointer:
|
||||
dw $ - gdt64 - 1
|
||||
dq gdt64
|
||||
@@ -1,16 +0,0 @@
|
||||
# Copyright 2016 Philipp Oppermann. See the README.md
|
||||
# file at the top-level directory of this distribution.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
# http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
# <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
# option. This file may not be copied, modified, or distributed
|
||||
# except according to those terms.
|
||||
|
||||
set timeout=0
|
||||
set default=0
|
||||
|
||||
menuentry "my os" {
|
||||
multiboot2 /boot/kernel.bin
|
||||
boot
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
/*
|
||||
Copyright 2016 Philipp Oppermann. See the README.md
|
||||
file at the top-level directory of this distribution.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
<LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
option. This file may not be copied, modified, or distributed
|
||||
except according to those terms.
|
||||
*/
|
||||
|
||||
ENTRY(start)
|
||||
|
||||
SECTIONS {
|
||||
. = 1M;
|
||||
|
||||
.rodata :
|
||||
{
|
||||
/* ensure that the multiboot header is at the beginning */
|
||||
KEEP(*(.multiboot_header))
|
||||
*(.rodata .rodata.*)
|
||||
. = ALIGN(4K);
|
||||
}
|
||||
|
||||
.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);
|
||||
}
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
; Copyright 2016 Philipp Oppermann. See the README.md
|
||||
; file at the top-level directory of this distribution.
|
||||
;
|
||||
; Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
; http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
; <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
; option. This file may not be copied, modified, or distributed
|
||||
; except according to those terms.
|
||||
|
||||
global long_mode_start
|
||||
extern rust_main
|
||||
|
||||
section .text
|
||||
bits 64
|
||||
long_mode_start:
|
||||
; load 0 into all data segment registers
|
||||
mov ax, 0
|
||||
mov ss, ax
|
||||
mov ds, ax
|
||||
mov es, ax
|
||||
mov fs, ax
|
||||
mov gs, ax
|
||||
|
||||
; call rust main (with multiboot pointer in rdi)
|
||||
call rust_main
|
||||
.os_returned:
|
||||
; rust main returned, print `OS returned!`
|
||||
mov rax, 0x4f724f204f534f4f
|
||||
mov [0xb8000], rax
|
||||
mov rax, 0x4f724f754f744f65
|
||||
mov [0xb8008], rax
|
||||
mov rax, 0x4f214f644f654f6e
|
||||
mov [0xb8010], rax
|
||||
hlt
|
||||
@@ -1,24 +0,0 @@
|
||||
; Copyright 2016 Philipp Oppermann. See the README.md
|
||||
; file at the top-level directory of this distribution.
|
||||
;
|
||||
; Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
; http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
; <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
; option. This file may not be copied, modified, or distributed
|
||||
; except according to those terms.
|
||||
|
||||
section .multiboot_header
|
||||
header_start:
|
||||
dd 0xe85250d6 ; magic number (multiboot 2)
|
||||
dd 0 ; architecture 0 (protected mode i386)
|
||||
dd header_end - header_start ; header length
|
||||
; checksum
|
||||
dd 0x100000000 - (0xe85250d6 + 0 + (header_end - header_start))
|
||||
|
||||
; insert optional multiboot tags here
|
||||
|
||||
; required end tag
|
||||
dw 0 ; type
|
||||
dw 0 ; flags
|
||||
dd 8 ; size
|
||||
header_end:
|
||||
@@ -1,95 +0,0 @@
|
||||
use x86_64::structures::tss::TaskStateSegment;
|
||||
use x86_64::structures::gdt::SegmentSelector;
|
||||
use x86_64::PrivilegeLevel;
|
||||
|
||||
pub struct Gdt {
|
||||
table: [u64; 8],
|
||||
next_free: usize,
|
||||
}
|
||||
|
||||
impl Gdt {
|
||||
pub fn new() -> Gdt {
|
||||
Gdt {
|
||||
table: [0; 8],
|
||||
next_free: 1,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_entry(&mut self, entry: Descriptor) -> SegmentSelector {
|
||||
let index = match entry {
|
||||
Descriptor::UserSegment(value) => self.push(value),
|
||||
Descriptor::SystemSegment(value_low, value_high) => {
|
||||
let index = self.push(value_low);
|
||||
self.push(value_high);
|
||||
index
|
||||
}
|
||||
};
|
||||
SegmentSelector::new(index as u16, PrivilegeLevel::Ring0)
|
||||
}
|
||||
|
||||
fn push(&mut self, value: u64) -> usize {
|
||||
if self.next_free < self.table.len() {
|
||||
let index = self.next_free;
|
||||
self.table[index] = value;
|
||||
self.next_free += 1;
|
||||
index
|
||||
} else {
|
||||
panic!("GDT full");
|
||||
}
|
||||
}
|
||||
|
||||
pub fn load(&'static self) {
|
||||
use x86_64::instructions::tables::{DescriptorTablePointer, lgdt};
|
||||
use core::mem::size_of;
|
||||
|
||||
let ptr = DescriptorTablePointer {
|
||||
base: self.table.as_ptr() as u64,
|
||||
limit: (self.table.len() * size_of::<u64>() - 1) as u16,
|
||||
};
|
||||
|
||||
unsafe { lgdt(&ptr) };
|
||||
}
|
||||
}
|
||||
|
||||
pub enum Descriptor {
|
||||
UserSegment(u64),
|
||||
SystemSegment(u64, u64),
|
||||
}
|
||||
|
||||
impl Descriptor {
|
||||
pub fn kernel_code_segment() -> Descriptor {
|
||||
let flags = USER_SEGMENT | PRESENT | EXECUTABLE | LONG_MODE;
|
||||
Descriptor::UserSegment(flags.bits())
|
||||
}
|
||||
|
||||
pub fn tss_segment(tss: &'static TaskStateSegment) -> Descriptor {
|
||||
use core::mem::size_of;
|
||||
use bit_field::BitField;
|
||||
|
||||
let ptr = tss as *const _ as u64;
|
||||
|
||||
let mut low = PRESENT.bits();
|
||||
// base
|
||||
low.set_bits(16..40, ptr.get_bits(0..24));
|
||||
low.set_bits(56..64, ptr.get_bits(24..32));
|
||||
// limit (the `-1` in needed since the bound is inclusive)
|
||||
low.set_bits(0..16, (size_of::<TaskStateSegment>() - 1) as u64);
|
||||
// type (0b1001 = available 64-bit tss)
|
||||
low.set_bits(40..44, 0b1001);
|
||||
|
||||
let mut high = 0;
|
||||
high.set_bits(0..32, ptr.get_bits(32..64));
|
||||
|
||||
Descriptor::SystemSegment(low, high)
|
||||
}
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
struct DescriptorFlags: u64 {
|
||||
const CONFORMING = 1 << 42;
|
||||
const EXECUTABLE = 1 << 43;
|
||||
const USER_SEGMENT = 1 << 44;
|
||||
const PRESENT = 1 << 47;
|
||||
const LONG_MODE = 1 << 53;
|
||||
}
|
||||
}
|
||||
@@ -1,120 +0,0 @@
|
||||
// Copyright 2016 Philipp Oppermann. See the README.md
|
||||
// file at the top-level directory of this distribution.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use memory::MemoryController;
|
||||
use x86_64::structures::tss::TaskStateSegment;
|
||||
use x86_64::structures::idt::{Idt, ExceptionStackFrame, PageFaultErrorCode};
|
||||
use spin::Once;
|
||||
|
||||
mod gdt;
|
||||
|
||||
const DOUBLE_FAULT_IST_INDEX: usize = 0;
|
||||
|
||||
lazy_static! {
|
||||
static ref IDT: Idt = {
|
||||
let mut idt = Idt::new();
|
||||
|
||||
idt.divide_by_zero.set_handler_fn(divide_by_zero_handler);
|
||||
idt.breakpoint.set_handler_fn(breakpoint_handler);
|
||||
idt.invalid_opcode.set_handler_fn(invalid_opcode_handler);
|
||||
idt.page_fault.set_handler_fn(page_fault_handler);
|
||||
|
||||
unsafe {
|
||||
idt.double_fault.set_handler_fn(double_fault_handler)
|
||||
.set_stack_index(DOUBLE_FAULT_IST_INDEX as u16);
|
||||
}
|
||||
|
||||
idt
|
||||
};
|
||||
}
|
||||
|
||||
static TSS: Once<TaskStateSegment> = Once::new();
|
||||
static GDT: Once<gdt::Gdt> = Once::new();
|
||||
|
||||
pub fn init(memory_controller: &mut MemoryController) {
|
||||
use x86_64::structures::gdt::SegmentSelector;
|
||||
use x86_64::instructions::segmentation::set_cs;
|
||||
use x86_64::instructions::tables::load_tss;
|
||||
use x86_64::VirtualAddress;
|
||||
|
||||
let double_fault_stack = memory_controller
|
||||
.alloc_stack(1)
|
||||
.expect("could not allocate double fault stack");
|
||||
|
||||
let tss = TSS.call_once(|| {
|
||||
let mut tss = TaskStateSegment::new();
|
||||
tss.interrupt_stack_table[DOUBLE_FAULT_IST_INDEX] =
|
||||
VirtualAddress(double_fault_stack.top());
|
||||
tss
|
||||
});
|
||||
|
||||
let mut code_selector = SegmentSelector(0);
|
||||
let mut tss_selector = SegmentSelector(0);
|
||||
let gdt = GDT.call_once(|| {
|
||||
let mut gdt = gdt::Gdt::new();
|
||||
code_selector = gdt.add_entry(gdt::Descriptor::kernel_code_segment());
|
||||
tss_selector = gdt.add_entry(gdt::Descriptor::tss_segment(&tss));
|
||||
gdt
|
||||
});
|
||||
gdt.load();
|
||||
|
||||
unsafe {
|
||||
// reload code segment register
|
||||
set_cs(code_selector);
|
||||
// load TSS
|
||||
load_tss(tss_selector);
|
||||
}
|
||||
|
||||
IDT.load();
|
||||
}
|
||||
|
||||
extern "x86-interrupt" fn divide_by_zero_handler(stack_frame: &mut ExceptionStackFrame) {
|
||||
println!("\nEXCEPTION: DIVIDE BY ZERO\n{:#?}", stack_frame);
|
||||
loop {}
|
||||
}
|
||||
|
||||
extern "x86-interrupt" fn breakpoint_handler(stack_frame: &mut ExceptionStackFrame) {
|
||||
println!(
|
||||
"\nEXCEPTION: BREAKPOINT at {:#x}\n{:#?}",
|
||||
stack_frame.instruction_pointer,
|
||||
stack_frame
|
||||
);
|
||||
}
|
||||
|
||||
extern "x86-interrupt" fn invalid_opcode_handler(stack_frame: &mut ExceptionStackFrame) {
|
||||
println!(
|
||||
"\nEXCEPTION: INVALID OPCODE at {:#x}\n{:#?}",
|
||||
stack_frame.instruction_pointer,
|
||||
stack_frame
|
||||
);
|
||||
loop {}
|
||||
}
|
||||
|
||||
extern "x86-interrupt" fn page_fault_handler(
|
||||
stack_frame: &mut ExceptionStackFrame,
|
||||
error_code: PageFaultErrorCode,
|
||||
) {
|
||||
use x86_64::registers::control_regs;
|
||||
println!(
|
||||
"\nEXCEPTION: PAGE FAULT while accessing {:#x}\nerror code: \
|
||||
{:?}\n{:#?}",
|
||||
control_regs::cr2(),
|
||||
error_code,
|
||||
stack_frame
|
||||
);
|
||||
loop {}
|
||||
}
|
||||
|
||||
extern "x86-interrupt" fn double_fault_handler(
|
||||
stack_frame: &mut ExceptionStackFrame,
|
||||
_error_code: u64,
|
||||
) {
|
||||
println!("\nEXCEPTION: DOUBLE FAULT\n{:#?}", stack_frame);
|
||||
loop {}
|
||||
}
|
||||
112
src/lib.rs
112
src/lib.rs
@@ -1,112 +0,0 @@
|
||||
// Copyright 2016 Philipp Oppermann. See the README.md
|
||||
// file at the top-level directory of this distribution.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
#![feature(lang_items)]
|
||||
#![feature(const_fn, unique)]
|
||||
#![feature(alloc)]
|
||||
#![feature(asm)]
|
||||
#![feature(naked_functions)]
|
||||
#![feature(abi_x86_interrupt)]
|
||||
#![feature(const_unique_new, const_atomic_usize_new)]
|
||||
#![feature(allocator_api)]
|
||||
#![feature(global_allocator)]
|
||||
#![no_std]
|
||||
|
||||
#[macro_use]
|
||||
extern crate alloc;
|
||||
extern crate rlibc;
|
||||
extern crate volatile;
|
||||
extern crate spin;
|
||||
extern crate multiboot2;
|
||||
#[macro_use]
|
||||
extern crate bitflags;
|
||||
extern crate x86_64;
|
||||
#[macro_use]
|
||||
extern crate once;
|
||||
extern crate linked_list_allocator;
|
||||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
extern crate bit_field;
|
||||
#[macro_use]
|
||||
mod vga_buffer;
|
||||
mod memory;
|
||||
mod interrupts;
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn rust_main(multiboot_information_address: usize) {
|
||||
// ATTENTION: we have a very small stack and no guard page
|
||||
vga_buffer::clear_screen();
|
||||
println!("Hello World{}", "!");
|
||||
|
||||
let boot_info = unsafe { multiboot2::load(multiboot_information_address) };
|
||||
enable_nxe_bit();
|
||||
enable_write_protect_bit();
|
||||
|
||||
// set up guard page and map the heap pages
|
||||
let mut memory_controller = memory::init(boot_info);
|
||||
|
||||
unsafe {
|
||||
HEAP_ALLOCATOR.lock().init(HEAP_START, HEAP_SIZE);
|
||||
}
|
||||
|
||||
// initialize our IDT
|
||||
interrupts::init(&mut memory_controller);
|
||||
|
||||
fn stack_overflow() {
|
||||
stack_overflow(); // for each recursion, the return address is pushed
|
||||
}
|
||||
|
||||
// trigger a stack overflow
|
||||
stack_overflow();
|
||||
|
||||
|
||||
println!("It did not crash!");
|
||||
loop {}
|
||||
}
|
||||
|
||||
fn enable_nxe_bit() {
|
||||
use x86_64::registers::msr::{IA32_EFER, rdmsr, wrmsr};
|
||||
|
||||
let nxe_bit = 1 << 11;
|
||||
unsafe {
|
||||
let efer = rdmsr(IA32_EFER);
|
||||
wrmsr(IA32_EFER, efer | nxe_bit);
|
||||
}
|
||||
}
|
||||
|
||||
fn enable_write_protect_bit() {
|
||||
use x86_64::registers::control_regs::{cr0, cr0_write, Cr0};
|
||||
|
||||
unsafe { cr0_write(cr0() | Cr0::WRITE_PROTECT) };
|
||||
}
|
||||
|
||||
#[cfg(not(test))]
|
||||
#[lang = "eh_personality"]
|
||||
#[no_mangle]
|
||||
pub extern "C" fn eh_personality() {}
|
||||
|
||||
#[cfg(not(test))]
|
||||
#[lang = "panic_fmt"]
|
||||
#[no_mangle]
|
||||
pub extern "C" fn panic_fmt(fmt: core::fmt::Arguments, file: &'static str, line: u32) -> ! {
|
||||
println!("\n\nPANIC in {} at line {}:", file, line);
|
||||
println!(" {}", fmt);
|
||||
loop {}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn _Unwind_Resume() -> ! { loop {} }
|
||||
|
||||
use linked_list_allocator::LockedHeap;
|
||||
|
||||
pub const HEAP_START: usize = 0o_000_001_000_000_0000;
|
||||
pub const HEAP_SIZE: usize = 100 * 1024; // 100 KiB
|
||||
|
||||
#[global_allocator]
|
||||
static HEAP_ALLOCATOR: LockedHeap = LockedHeap::empty();
|
||||
@@ -1,104 +0,0 @@
|
||||
// Copyright 2016 Philipp Oppermann. See the README.md
|
||||
// file at the top-level directory of this distribution.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use memory::{Frame, FrameAllocator};
|
||||
use multiboot2::{MemoryAreaIter, MemoryArea};
|
||||
|
||||
/// A frame allocator that uses the memory areas from the multiboot information structure as
|
||||
/// source. The {kernel, multiboot}_{start, end} fields are used to avoid returning memory that is
|
||||
/// already in use.
|
||||
///
|
||||
/// `kernel_end` and `multiboot_end` are _inclusive_ bounds.
|
||||
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!()
|
||||
}
|
||||
}
|
||||
@@ -1,154 +0,0 @@
|
||||
// Copyright 2016 Philipp Oppermann. See the README.md
|
||||
// file at the top-level directory of this distribution.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
pub use self::area_frame_allocator::AreaFrameAllocator;
|
||||
pub use self::paging::remap_the_kernel;
|
||||
pub use self::stack_allocator::Stack;
|
||||
use self::paging::PhysicalAddress;
|
||||
use multiboot2::BootInformation;
|
||||
|
||||
mod area_frame_allocator;
|
||||
pub mod heap_allocator;
|
||||
mod paging;
|
||||
mod stack_allocator;
|
||||
|
||||
pub const PAGE_SIZE: usize = 4096;
|
||||
|
||||
pub fn init(boot_info: &BootInformation) -> MemoryController {
|
||||
assert_has_not_been_called!("memory::init must be called only once");
|
||||
|
||||
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 = elf_sections_tag
|
||||
.sections()
|
||||
.filter(|s| s.is_allocated())
|
||||
.map(|s| s.addr)
|
||||
.min()
|
||||
.unwrap();
|
||||
let kernel_end = elf_sections_tag
|
||||
.sections()
|
||||
.filter(|s| s.is_allocated())
|
||||
.map(|s| s.addr + s.size)
|
||||
.max()
|
||||
.unwrap();
|
||||
|
||||
println!(
|
||||
"kernel start: {:#x}, kernel end: {:#x}",
|
||||
kernel_start,
|
||||
kernel_end
|
||||
);
|
||||
println!(
|
||||
"multiboot start: {:#x}, multiboot end: {:#x}",
|
||||
boot_info.start_address(),
|
||||
boot_info.end_address()
|
||||
);
|
||||
|
||||
let mut frame_allocator = AreaFrameAllocator::new(
|
||||
kernel_start as usize,
|
||||
kernel_end as usize,
|
||||
boot_info.start_address(),
|
||||
boot_info.end_address(),
|
||||
memory_map_tag.memory_areas(),
|
||||
);
|
||||
|
||||
let mut active_table = paging::remap_the_kernel(&mut frame_allocator, boot_info);
|
||||
|
||||
use self::paging::Page;
|
||||
use super::{HEAP_START, HEAP_SIZE};
|
||||
|
||||
let heap_start_page = Page::containing_address(HEAP_START);
|
||||
let heap_end_page = Page::containing_address(HEAP_START + HEAP_SIZE - 1);
|
||||
|
||||
for page in Page::range_inclusive(heap_start_page, heap_end_page) {
|
||||
active_table.map(page, paging::WRITABLE, &mut frame_allocator);
|
||||
}
|
||||
|
||||
let stack_allocator = {
|
||||
let stack_alloc_start = heap_end_page + 1;
|
||||
let stack_alloc_end = stack_alloc_start + 100;
|
||||
let stack_alloc_range = Page::range_inclusive(stack_alloc_start, stack_alloc_end);
|
||||
stack_allocator::StackAllocator::new(stack_alloc_range)
|
||||
};
|
||||
|
||||
MemoryController {
|
||||
active_table: active_table,
|
||||
frame_allocator: frame_allocator,
|
||||
stack_allocator: stack_allocator,
|
||||
}
|
||||
}
|
||||
|
||||
pub struct MemoryController {
|
||||
active_table: paging::ActivePageTable,
|
||||
frame_allocator: AreaFrameAllocator,
|
||||
stack_allocator: stack_allocator::StackAllocator,
|
||||
}
|
||||
|
||||
impl MemoryController {
|
||||
pub fn alloc_stack(&mut self, size_in_pages: usize) -> Option<Stack> {
|
||||
let &mut MemoryController {
|
||||
ref mut active_table,
|
||||
ref mut frame_allocator,
|
||||
ref mut stack_allocator,
|
||||
} = self;
|
||||
stack_allocator.alloc_stack(active_table, frame_allocator, size_in_pages)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct Frame {
|
||||
number: usize,
|
||||
}
|
||||
|
||||
impl Frame {
|
||||
fn containing_address(address: usize) -> Frame {
|
||||
Frame { number: address / PAGE_SIZE }
|
||||
}
|
||||
|
||||
fn start_address(&self) -> PhysicalAddress {
|
||||
self.number * PAGE_SIZE
|
||||
}
|
||||
|
||||
fn clone(&self) -> Frame {
|
||||
Frame { number: self.number }
|
||||
}
|
||||
|
||||
fn range_inclusive(start: Frame, end: Frame) -> FrameIter {
|
||||
FrameIter {
|
||||
start: start,
|
||||
end: end,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait FrameAllocator {
|
||||
fn allocate_frame(&mut self) -> Option<Frame>;
|
||||
fn deallocate_frame(&mut self, frame: Frame);
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
// Copyright 2016 Philipp Oppermann. See the README.md
|
||||
// file at the top-level directory of this distribution.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use memory::Frame;
|
||||
use multiboot2::ElfSection;
|
||||
|
||||
pub struct Entry(u64);
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
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 EntryFlags {
|
||||
pub fn from_elf_section_flags(section: &ElfSection) -> EntryFlags {
|
||||
use multiboot2::{ELF_SECTION_ALLOCATED, ELF_SECTION_WRITABLE, ELF_SECTION_EXECUTABLE};
|
||||
|
||||
let mut flags = EntryFlags::empty();
|
||||
|
||||
if section.flags().contains(ELF_SECTION_ALLOCATED) {
|
||||
// section is loaded to memory
|
||||
flags = flags | PRESENT;
|
||||
}
|
||||
if section.flags().contains(ELF_SECTION_WRITABLE) {
|
||||
flags = flags | WRITABLE;
|
||||
}
|
||||
if !section.flags().contains(ELF_SECTION_EXECUTABLE) {
|
||||
flags = flags | NO_EXECUTE;
|
||||
}
|
||||
|
||||
flags
|
||||
}
|
||||
}
|
||||
@@ -1,125 +0,0 @@
|
||||
// Copyright 2016 Philipp Oppermann. See the README.md
|
||||
// file at the top-level directory of this distribution.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use super::{VirtualAddress, PhysicalAddress, Page, ENTRY_COUNT};
|
||||
use super::entry::*;
|
||||
use super::table::{self, Table, Level4};
|
||||
use memory::{PAGE_SIZE, Frame, FrameAllocator};
|
||||
use core::ptr::Unique;
|
||||
|
||||
pub struct Mapper {
|
||||
p4: Unique<Table<Level4>>,
|
||||
}
|
||||
|
||||
impl Mapper {
|
||||
pub unsafe fn new() -> Mapper {
|
||||
Mapper { p4: Unique::new_unchecked(table::P4) }
|
||||
}
|
||||
|
||||
pub fn p4(&self) -> &Table<Level4> {
|
||||
unsafe { self.p4.as_ref() }
|
||||
}
|
||||
|
||||
pub fn p4_mut(&mut self) -> &mut Table<Level4> {
|
||||
unsafe { self.p4.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> {
|
||||
let p3 = self.p4().next_table(page.p4_index());
|
||||
|
||||
let huge_page = || {
|
||||
p3.and_then(|p3| {
|
||||
let p3_entry = &p3[page.p3_index()];
|
||||
// 1GiB page?
|
||||
if let Some(start_frame) = p3_entry.pointed_frame() {
|
||||
if p3_entry.flags().contains(HUGE_PAGE) {
|
||||
// address must be 1GiB aligned
|
||||
assert!(start_frame.number % (ENTRY_COUNT * ENTRY_COUNT) == 0);
|
||||
return Some(Frame {
|
||||
number: start_frame.number + page.p2_index() * ENTRY_COUNT +
|
||||
page.p1_index(),
|
||||
});
|
||||
}
|
||||
}
|
||||
if let Some(p2) = p3.next_table(page.p3_index()) {
|
||||
let p2_entry = &p2[page.p2_index()];
|
||||
// 2MiB page?
|
||||
if let Some(start_frame) = p2_entry.pointed_frame() {
|
||||
if p2_entry.flags().contains(HUGE_PAGE) {
|
||||
// address must be 2MiB aligned
|
||||
assert!(start_frame.number % ENTRY_COUNT == 0);
|
||||
return Some(Frame { number: start_frame.number + page.p1_index() });
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
})
|
||||
};
|
||||
|
||||
p3.and_then(|p3| p3.next_table(page.p3_index()))
|
||||
.and_then(|p2| 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 mut p3 = self.p4_mut().next_table_create(page.p4_index(), allocator);
|
||||
let mut p2 = p3.next_table_create(page.p3_index(), allocator);
|
||||
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 | 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,
|
||||
{
|
||||
use x86_64::VirtualAddress;
|
||||
use x86_64::instructions::tlb;
|
||||
|
||||
assert!(self.translate(page.start_address()).is_some());
|
||||
|
||||
let p1 = self.p4_mut()
|
||||
.next_table_mut(page.p4_index())
|
||||
.and_then(|p3| p3.next_table_mut(page.p3_index()))
|
||||
.and_then(|p2| p2.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();
|
||||
tlb::flush(VirtualAddress(page.start_address()));
|
||||
// TODO free p(1,2,3) table if empty
|
||||
// allocator.deallocate_frame(frame);
|
||||
}
|
||||
}
|
||||
@@ -1,248 +0,0 @@
|
||||
// Copyright 2016 Philipp Oppermann. See the README.md
|
||||
// file at the top-level directory of this distribution.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
pub use self::entry::*;
|
||||
use memory::{PAGE_SIZE, Frame, FrameAllocator};
|
||||
use self::temporary_page::TemporaryPage;
|
||||
pub use self::mapper::Mapper;
|
||||
use core::ops::{Add, Deref, DerefMut};
|
||||
use multiboot2::BootInformation;
|
||||
|
||||
mod entry;
|
||||
mod table;
|
||||
mod temporary_page;
|
||||
mod mapper;
|
||||
|
||||
const ENTRY_COUNT: usize = 512;
|
||||
|
||||
pub type PhysicalAddress = usize;
|
||||
pub type VirtualAddress = usize;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct Page {
|
||||
number: usize,
|
||||
}
|
||||
|
||||
impl Page {
|
||||
pub fn containing_address(address: VirtualAddress) -> Page {
|
||||
assert!(
|
||||
address < 0x0000_8000_0000_0000 || address >= 0xffff_8000_0000_0000,
|
||||
"invalid address: 0x{:x}",
|
||||
address
|
||||
);
|
||||
Page { number: address / PAGE_SIZE }
|
||||
}
|
||||
|
||||
pub fn start_address(&self) -> usize {
|
||||
self.number * PAGE_SIZE
|
||||
}
|
||||
|
||||
fn p4_index(&self) -> usize {
|
||||
(self.number >> 27) & 0o777
|
||||
}
|
||||
fn p3_index(&self) -> usize {
|
||||
(self.number >> 18) & 0o777
|
||||
}
|
||||
fn p2_index(&self) -> usize {
|
||||
(self.number >> 9) & 0o777
|
||||
}
|
||||
fn p1_index(&self) -> usize {
|
||||
(self.number >> 0) & 0o777
|
||||
}
|
||||
|
||||
pub fn range_inclusive(start: Page, end: Page) -> PageIter {
|
||||
PageIter {
|
||||
start: start,
|
||||
end: end,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Add<usize> for Page {
|
||||
type Output = Page;
|
||||
|
||||
fn add(self, rhs: usize) -> Page {
|
||||
Page { number: self.number + rhs }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct PageIter {
|
||||
start: Page,
|
||||
end: Page,
|
||||
}
|
||||
|
||||
impl Iterator for PageIter {
|
||||
type Item = Page;
|
||||
|
||||
fn next(&mut self) -> Option<Page> {
|
||||
if self.start <= self.end {
|
||||
let page = self.start;
|
||||
self.start.number += 1;
|
||||
Some(page)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
unsafe fn new() -> ActivePageTable {
|
||||
ActivePageTable { mapper: Mapper::new() }
|
||||
}
|
||||
|
||||
pub fn with<F>(
|
||||
&mut self,
|
||||
table: &mut InactivePageTable,
|
||||
temporary_page: &mut temporary_page::TemporaryPage, // new
|
||||
f: F,
|
||||
) where
|
||||
F: FnOnce(&mut Mapper),
|
||||
{
|
||||
use x86_64::registers::control_regs;
|
||||
use x86_64::instructions::tlb;
|
||||
|
||||
{
|
||||
let backup = Frame::containing_address(control_regs::cr3().0 as usize);
|
||||
|
||||
// map temporary_page to current p4 table
|
||||
let p4_table = temporary_page.map_table_frame(backup.clone(), self);
|
||||
|
||||
// overwrite recursive mapping
|
||||
self.p4_mut()[511].set(table.p4_frame.clone(), PRESENT | WRITABLE);
|
||||
tlb::flush_all();
|
||||
|
||||
// execute f in the new context
|
||||
f(self);
|
||||
|
||||
// restore recursive mapping to original p4 table
|
||||
p4_table[511].set(backup, PRESENT | WRITABLE);
|
||||
tlb::flush_all();
|
||||
}
|
||||
|
||||
temporary_page.unmap(self);
|
||||
}
|
||||
|
||||
pub fn switch(&mut self, new_table: InactivePageTable) -> InactivePageTable {
|
||||
use x86_64::PhysicalAddress;
|
||||
use x86_64::registers::control_regs;
|
||||
|
||||
let old_table = InactivePageTable {
|
||||
p4_frame: Frame::containing_address(control_regs::cr3().0 as usize),
|
||||
};
|
||||
unsafe {
|
||||
control_regs::cr3_write(PhysicalAddress(new_table.p4_frame.start_address() as u64));
|
||||
}
|
||||
old_table
|
||||
}
|
||||
}
|
||||
|
||||
pub struct InactivePageTable {
|
||||
p4_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();
|
||||
table[511].set(frame.clone(), PRESENT | WRITABLE);
|
||||
}
|
||||
temporary_page.unmap(active_table);
|
||||
|
||||
InactivePageTable { p4_frame: frame }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn remap_the_kernel<A>(allocator: &mut A, boot_info: &BootInformation) -> ActivePageTable
|
||||
where
|
||||
A: FrameAllocator,
|
||||
{
|
||||
let mut temporary_page = TemporaryPage::new(Page { number: 0xcafebabe }, 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");
|
||||
|
||||
// identity map the allocated kernel sections
|
||||
for section in elf_sections_tag.sections() {
|
||||
if !section.is_allocated() {
|
||||
// section is not loaded to memory
|
||||
continue;
|
||||
}
|
||||
|
||||
assert!(
|
||||
section.addr as usize % PAGE_SIZE == 0,
|
||||
"sections need to be page aligned"
|
||||
);
|
||||
println!(
|
||||
"mapping section at addr: {:#x}, size: {:#x}",
|
||||
section.addr,
|
||||
section.size
|
||||
);
|
||||
|
||||
let flags = EntryFlags::from_elf_section_flags(section);
|
||||
|
||||
let start_frame = Frame::containing_address(section.start_address());
|
||||
let end_frame = Frame::containing_address(section.end_address() - 1);
|
||||
for frame in Frame::range_inclusive(start_frame, end_frame) {
|
||||
mapper.identity_map(frame, flags, allocator);
|
||||
}
|
||||
}
|
||||
|
||||
// identity map the VGA text buffer
|
||||
let vga_buffer_frame = Frame::containing_address(0xb8000);
|
||||
mapper.identity_map(vga_buffer_frame, WRITABLE, allocator);
|
||||
|
||||
// identity map the multiboot info structure
|
||||
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, PRESENT, allocator);
|
||||
}
|
||||
});
|
||||
|
||||
let old_table = active_table.switch(new_table);
|
||||
println!("NEW TABLE!!!");
|
||||
|
||||
let old_p4_page = Page::containing_address(old_table.p4_frame.start_address());
|
||||
active_table.unmap(old_p4_page, allocator);
|
||||
println!("guard page at {:#x}", old_p4_page.start_address());
|
||||
|
||||
active_table
|
||||
}
|
||||
@@ -1,136 +0,0 @@
|
||||
// Copyright 2016 Philipp Oppermann. See the README.md
|
||||
// file at the top-level directory of this distribution.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use memory::paging::entry::*;
|
||||
use memory::paging::ENTRY_COUNT;
|
||||
use memory::FrameAllocator;
|
||||
use core::ops::{Index, IndexMut};
|
||||
use core::marker::PhantomData;
|
||||
|
||||
pub const P4: *mut Table<Level4> = 0xffffffff_fffff000 as *mut _;
|
||||
|
||||
pub struct Table<L: TableLevel> {
|
||||
entries: [Entry; ENTRY_COUNT],
|
||||
level: PhantomData<L>,
|
||||
}
|
||||
|
||||
impl<L> Table<L>
|
||||
where
|
||||
L: TableLevel,
|
||||
{
|
||||
pub fn zero(&mut self) {
|
||||
for entry in self.entries.iter_mut() {
|
||||
entry.set_unused();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Addresses are expected to be canonical (bits 48-63 must be the same as bit 47), otherwise the
|
||||
* CPU will #GP when we ask it to translate it.
|
||||
*/
|
||||
fn make_address_canonical(address : usize) -> usize {
|
||||
let sign_extension = 0o177777_000_000_000_000_0000 * ((address >> 47) & 0b1);
|
||||
(address & ((1 << 48) - 1)) | sign_extension
|
||||
}
|
||||
|
||||
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(PRESENT) && !entry_flags.contains(HUGE_PAGE) {
|
||||
let table_address = self as *const _ as usize;
|
||||
Some(make_address_canonical((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(HUGE_PAGE),
|
||||
"mapping code does not support huge pages"
|
||||
);
|
||||
let frame = allocator.allocate_frame().expect("no frames available");
|
||||
self.entries[index].set(frame, PRESENT | WRITABLE);
|
||||
self.next_table_mut(index).unwrap().zero();
|
||||
}
|
||||
self.next_table_mut(index).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl<L> Index<usize> for Table<L>
|
||||
where
|
||||
L: TableLevel,
|
||||
{
|
||||
type Output = Entry;
|
||||
|
||||
fn index(&self, index: usize) -> &Entry {
|
||||
&self.entries[index]
|
||||
}
|
||||
}
|
||||
|
||||
impl<L> IndexMut<usize> for Table<L>
|
||||
where
|
||||
L: TableLevel,
|
||||
{
|
||||
fn index_mut(&mut self, index: usize) -> &mut Entry {
|
||||
&mut self.entries[index]
|
||||
}
|
||||
}
|
||||
|
||||
pub trait TableLevel {}
|
||||
|
||||
pub enum Level4 {}
|
||||
#[allow(dead_code)]
|
||||
pub enum Level3 {}
|
||||
#[allow(dead_code)]
|
||||
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;
|
||||
}
|
||||
@@ -1,91 +0,0 @@
|
||||
// Copyright 2016 Philipp Oppermann. See the README.md
|
||||
// file at the top-level directory of this distribution.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
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::WRITABLE;
|
||||
|
||||
assert!(
|
||||
active_table.translate_page(self.page).is_none(),
|
||||
"temporary page is already mapped"
|
||||
);
|
||||
active_table.map_to(self.page, frame, WRITABLE, &mut self.allocator);
|
||||
self.page.start_address()
|
||||
}
|
||||
|
||||
/// 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>) }
|
||||
}
|
||||
|
||||
/// 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)
|
||||
}
|
||||
}
|
||||
|
||||
struct TinyAllocator([Option<Frame>; 3]);
|
||||
|
||||
impl TinyAllocator {
|
||||
fn new<A>(allocator: &mut A) -> TinyAllocator
|
||||
where
|
||||
A: FrameAllocator,
|
||||
{
|
||||
let mut f = || allocator.allocate_frame();
|
||||
let frames = [f(), f(), 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 allocator can hold only 3 frames.");
|
||||
}
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
use memory::paging::{self, Page, PageIter, ActivePageTable};
|
||||
use memory::{PAGE_SIZE, FrameAllocator};
|
||||
|
||||
pub struct StackAllocator {
|
||||
range: PageIter,
|
||||
}
|
||||
|
||||
impl StackAllocator {
|
||||
pub fn new(page_range: PageIter) -> StackAllocator {
|
||||
StackAllocator { range: page_range }
|
||||
}
|
||||
}
|
||||
|
||||
impl StackAllocator {
|
||||
pub fn alloc_stack<FA: FrameAllocator>(
|
||||
&mut self,
|
||||
active_table: &mut ActivePageTable,
|
||||
frame_allocator: &mut FA,
|
||||
size_in_pages: usize,
|
||||
) -> Option<Stack> {
|
||||
if size_in_pages == 0 {
|
||||
return None; /* a zero sized stack makes no sense */
|
||||
}
|
||||
|
||||
// clone the range, since we only want to change it on success
|
||||
let mut range = self.range.clone();
|
||||
|
||||
// try to allocate the stack pages and a guard page
|
||||
let guard_page = range.next();
|
||||
let stack_start = range.next();
|
||||
let stack_end = if size_in_pages == 1 {
|
||||
stack_start
|
||||
} else {
|
||||
// choose the (size_in_pages-2)th element, since index
|
||||
// starts at 0 and we already allocated the start page
|
||||
range.nth(size_in_pages - 2)
|
||||
};
|
||||
|
||||
match (guard_page, stack_start, stack_end) {
|
||||
(Some(_), Some(start), Some(end)) => {
|
||||
// success! write back updated range
|
||||
self.range = range;
|
||||
|
||||
// map stack pages to physical frames
|
||||
for page in Page::range_inclusive(start, end) {
|
||||
active_table.map(page, paging::WRITABLE, frame_allocator);
|
||||
}
|
||||
|
||||
// create a new stack
|
||||
let top_of_stack = end.start_address() + PAGE_SIZE;
|
||||
Some(Stack::new(top_of_stack, start.start_address()))
|
||||
}
|
||||
_ => None, /* not enough pages */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Stack {
|
||||
top: usize,
|
||||
bottom: usize,
|
||||
}
|
||||
|
||||
impl Stack {
|
||||
fn new(top: usize, bottom: usize) -> Stack {
|
||||
assert!(top > bottom);
|
||||
Stack {
|
||||
top: top,
|
||||
bottom: bottom,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn top(&self) -> usize {
|
||||
self.top
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn bottom(&self) -> usize {
|
||||
self.bottom
|
||||
}
|
||||
}
|
||||
@@ -1,150 +0,0 @@
|
||||
// Copyright 2016 Philipp Oppermann. See the README.md
|
||||
// file at the top-level directory of this distribution.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use core::ptr::Unique;
|
||||
use core::fmt;
|
||||
use spin::Mutex;
|
||||
use volatile::Volatile;
|
||||
|
||||
const BUFFER_HEIGHT: usize = 25;
|
||||
const BUFFER_WIDTH: usize = 80;
|
||||
|
||||
pub static WRITER: Mutex<Writer> = Mutex::new(Writer {
|
||||
column_position: 0,
|
||||
color_code: ColorCode::new(Color::LightGreen, 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();
|
||||
}
|
||||
|
||||
pub fn clear_screen() {
|
||||
for _ in 0..BUFFER_HEIGHT {
|
||||
println!("");
|
||||
}
|
||||
}
|
||||
|
||||
#[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,
|
||||
}
|
||||
|
||||
pub struct Writer {
|
||||
column_position: usize,
|
||||
color_code: ColorCode,
|
||||
buffer: Unique<Buffer>,
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Write for Writer {
|
||||
fn write_str(&mut self, s: &str) -> ::core::fmt::Result {
|
||||
for byte in s.bytes() {
|
||||
self.write_byte(byte)
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[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,
|
||||
}
|
||||
|
||||
struct Buffer {
|
||||
chars: [[Volatile<ScreenChar>; BUFFER_WIDTH]; BUFFER_HEIGHT],
|
||||
}
|
||||
Reference in New Issue
Block a user