mirror of
https://github.com/phil-opp/blog_os.git
synced 2025-12-16 22:37:49 +00:00
Delete code from first edition
This commit is contained in:
18
Cargo.toml
18
Cargo.toml
@@ -1,22 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
authors = ["Philipp Oppermann <dev@phil-opp.com>"]
|
authors = ["Philipp Oppermann <dev@phil-opp.com>"]
|
||||||
name = "blog_os"
|
name = "blog_os"
|
||||||
version = "0.1.0"
|
version = "0.2.0"
|
||||||
|
|
||||||
[dependencies]
|
[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