mirror of
https://github.com/phil-opp/blog_os.git
synced 2025-12-16 14:27:49 +00:00
Compare commits
33 Commits
first_edit
...
first_edit
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7f6576c9dc | ||
|
|
aa8028cf6c | ||
|
|
165054d12a | ||
|
|
58e90d497e | ||
|
|
238cc575c3 | ||
|
|
d2060e76f8 | ||
|
|
f651987666 | ||
|
|
eea8c10a97 | ||
|
|
e1d2af5ea7 | ||
|
|
a05db759d7 | ||
|
|
00bbd6fbc6 | ||
|
|
f1459a552c | ||
|
|
73d4390f27 | ||
|
|
0e3857ca50 | ||
|
|
3efe54169e | ||
|
|
2b9d880e48 | ||
|
|
91ffde4728 | ||
|
|
590b2fd1b0 | ||
|
|
93aff8cfa8 | ||
|
|
fab320271a | ||
|
|
7becaf5f30 | ||
|
|
3bbc2a0bdc | ||
|
|
c2d22af1c7 | ||
|
|
0ddd214a1b | ||
|
|
ad211de615 | ||
|
|
01f8c43ffb | ||
|
|
f2bbe43099 | ||
|
|
76550dcd95 | ||
|
|
c0d403abbe | ||
|
|
9dc998222a | ||
|
|
1f6633fe44 | ||
|
|
5f017124dd | ||
|
|
36369cfbe2 |
@@ -7,9 +7,16 @@ authors = ["Philipp Oppermann <dev@phil-opp.com>"]
|
||||
crate-type = ["staticlib"]
|
||||
|
||||
[dependencies]
|
||||
bit_field = "0.7.0"
|
||||
rlibc = "1.0"
|
||||
volatile = "0.1.0"
|
||||
spin = "0.4.5"
|
||||
multiboot2 = "0.1.0"
|
||||
bitflags = "0.7.0"
|
||||
x86_64 = "0.1.2"
|
||||
once = "0.3.3"
|
||||
linked_list_allocator = "0.4.2"
|
||||
|
||||
[dependencies.lazy_static]
|
||||
version = "0.2.4"
|
||||
features = ["spin_no_std"]
|
||||
|
||||
10
README.md
10
README.md
@@ -1,12 +1,14 @@
|
||||
# Blog OS (Remap the Kernel)
|
||||
[](https://travis-ci.org/phil-opp/blog_os/branches)
|
||||
# Blog OS (Double Faults)
|
||||
[](https://travis-ci.org/phil-opp/blog_os/branches)
|
||||
|
||||
This repository contains the source code for the [Remap the Kernel](http://os.phil-opp.com/remap-the-kernel.html) post of the [Writing an OS in Rust](http://os.phil-opp.com) series.
|
||||
This repository contains the source code for the [Double Faults](http://os.phil-opp.com/double-faults.html) post of the [Writing an OS in Rust](http://os.phil-opp.com) series.
|
||||
|
||||
**Check out the [master branch](https://github.com/phil-opp/blog_os) for more information.**
|
||||
|
||||
## Building
|
||||
You need to have `nasm`, `grub-mkrescue`, `xorriso`, `qemu`, and a nightly Rust compiler installed. Then you can run it using `make run`.
|
||||
You need to have `nasm`, `grub-mkrescue`, `xorriso`, `qemu`, a nightly Rust compiler, and [xargo] installed. Then you can run it using `make run`.
|
||||
|
||||
[xargo]: https://github.com/japaric/xargo
|
||||
|
||||
Please file an issue if you have any problems.
|
||||
|
||||
|
||||
2
Xargo.toml
Normal file
2
Xargo.toml
Normal file
@@ -0,0 +1,2 @@
|
||||
[target.x86_64-blog_os.dependencies]
|
||||
alloc = {}
|
||||
95
src/interrupts/gdt.rs
Normal file
95
src/interrupts/gdt.rs
Normal file
@@ -0,0 +1,95 @@
|
||||
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)
|
||||
}
|
||||
|
||||
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) };
|
||||
}
|
||||
|
||||
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 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! {
|
||||
flags DescriptorFlags: u64 {
|
||||
const CONFORMING = 1 << 42,
|
||||
const EXECUTABLE = 1 << 43,
|
||||
const USER_SEGMENT = 1 << 44,
|
||||
const PRESENT = 1 << 47,
|
||||
const LONG_MODE = 1 << 53,
|
||||
}
|
||||
}
|
||||
72
src/interrupts/mod.rs
Normal file
72
src/interrupts/mod.rs
Normal file
@@ -0,0 +1,72 @@
|
||||
use x86_64::VirtualAddress;
|
||||
use x86_64::structures::idt::{Idt, ExceptionStackFrame};
|
||||
use x86_64::structures::tss::TaskStateSegment;
|
||||
use memory::MemoryController;
|
||||
use spin::Once;
|
||||
|
||||
mod gdt;
|
||||
|
||||
lazy_static! {
|
||||
static ref IDT: Idt = {
|
||||
let mut idt = Idt::new();
|
||||
idt.breakpoint.set_handler_fn(breakpoint_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();
|
||||
|
||||
const DOUBLE_FAULT_IST_INDEX: usize = 0;
|
||||
|
||||
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;
|
||||
|
||||
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 breakpoint_handler(
|
||||
stack_frame: &mut ExceptionStackFrame)
|
||||
{
|
||||
println!("EXCEPTION: BREAKPOINT\n{:#?}", stack_frame);
|
||||
}
|
||||
|
||||
extern "x86-interrupt" fn double_fault_handler(
|
||||
stack_frame: &mut ExceptionStackFrame, _error_code: u64)
|
||||
{
|
||||
println!("\nEXCEPTION: DOUBLE FAULT\n{:#?}", stack_frame);
|
||||
loop {}
|
||||
}
|
||||
84
src/lib.rs
84
src/lib.rs
@@ -1,9 +1,17 @@
|
||||
#![feature(lang_items)]
|
||||
#![feature(const_fn)]
|
||||
#![feature(const_unique_new)]
|
||||
#![feature(alloc)]
|
||||
#![feature(const_unique_new, const_atomic_usize_new)]
|
||||
#![feature(unique)]
|
||||
#![feature(allocator_api)]
|
||||
#![feature(global_allocator)]
|
||||
#![feature(abi_x86_interrupt)]
|
||||
#![no_std]
|
||||
|
||||
|
||||
#[macro_use]
|
||||
extern crate alloc;
|
||||
|
||||
extern crate rlibc;
|
||||
extern crate volatile;
|
||||
extern crate spin;
|
||||
@@ -11,45 +19,55 @@ 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 fn rust_main(multiboot_information_address: usize) {
|
||||
use memory::FrameAllocator;
|
||||
|
||||
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) };
|
||||
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().map(|s| s.addr)
|
||||
.min().unwrap();
|
||||
let kernel_end = elf_sections_tag.sections().map(|s| s.addr + s.size)
|
||||
.max().unwrap();
|
||||
let multiboot_start = multiboot_information_address;
|
||||
let multiboot_end = multiboot_start + (boot_info.total_size as usize);
|
||||
|
||||
println!("kernel start: 0x{:x}, kernel end: 0x{:x}",
|
||||
kernel_start, kernel_end);
|
||||
println!("multiboot start: 0x{:x}, multiboot end: 0x{:x}",
|
||||
multiboot_start, multiboot_end);
|
||||
|
||||
let mut frame_allocator = memory::AreaFrameAllocator::new(
|
||||
kernel_start as usize, kernel_end as usize, multiboot_start,
|
||||
multiboot_end, memory_map_tag.memory_areas());
|
||||
|
||||
let boot_info = unsafe {
|
||||
multiboot2::load(multiboot_information_address)
|
||||
};
|
||||
enable_nxe_bit();
|
||||
enable_write_protect_bit();
|
||||
memory::remap_the_kernel(&mut frame_allocator, boot_info);
|
||||
println!("It did not crash!");
|
||||
|
||||
// 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_START + HEAP_SIZE);
|
||||
}
|
||||
|
||||
// initialize our IDT
|
||||
interrupts::init(&mut memory_controller);
|
||||
|
||||
for i in 0..10000 {
|
||||
format!("Some String");
|
||||
}
|
||||
|
||||
// invoke a breakpoint exception
|
||||
x86_64::instructions::interrupts::int3();
|
||||
|
||||
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 {}
|
||||
}
|
||||
|
||||
@@ -78,3 +96,11 @@ pub extern fn panic_fmt(fmt: core::fmt::Arguments, file: &'static str, line: u32
|
||||
println!(" {}", fmt);
|
||||
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();
|
||||
|
||||
62
src/memory/heap_allocator.rs
Normal file
62
src/memory/heap_allocator.rs
Normal file
@@ -0,0 +1,62 @@
|
||||
use alloc::heap::{Alloc, AllocErr, Layout};
|
||||
|
||||
use core::sync::atomic::{AtomicUsize, Ordering};
|
||||
|
||||
/// A simple allocator that allocates memory linearly and ignores freed memory.
|
||||
#[derive(Debug)]
|
||||
pub struct BumpAllocator {
|
||||
heap_start: usize,
|
||||
heap_end: usize,
|
||||
next: AtomicUsize,
|
||||
}
|
||||
|
||||
impl BumpAllocator {
|
||||
pub const fn new(heap_start: usize, heap_end: usize) -> Self {
|
||||
Self { heap_start, heap_end, next: AtomicUsize::new(heap_start) }
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<'a> Alloc for &'a BumpAllocator {
|
||||
unsafe fn alloc(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> {
|
||||
loop {
|
||||
// load current state of the `next` field
|
||||
let current_next = self.next.load(Ordering::Relaxed);
|
||||
let alloc_start = align_up(current_next, layout.align());
|
||||
let alloc_end = alloc_start.saturating_add(layout.size());
|
||||
|
||||
if alloc_end <= self.heap_end {
|
||||
// update the `next` pointer if it still has the value `current_next`
|
||||
let next_now = self.next.compare_and_swap(current_next, alloc_end,
|
||||
Ordering::Relaxed);
|
||||
if next_now == current_next {
|
||||
// next address was successfully updated, allocation succeeded
|
||||
return Ok(alloc_start as *mut u8);
|
||||
}
|
||||
} else {
|
||||
return Err(AllocErr::Exhausted{ request: layout })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn dealloc(&mut self, ptr: *mut u8, layout: Layout) {
|
||||
// do nothing, leak memory
|
||||
}
|
||||
}
|
||||
|
||||
/// Align downwards. Returns the greatest x with alignment `align`
|
||||
/// so that x <= addr. The alignment must be a power of 2.
|
||||
pub fn align_down(addr: usize, align: usize) -> usize {
|
||||
if align.is_power_of_two() {
|
||||
addr & !(align - 1)
|
||||
} else if align == 0 {
|
||||
addr
|
||||
} else {
|
||||
panic!("`align` must be a power of 2");
|
||||
}
|
||||
}
|
||||
|
||||
/// Align upwards. Returns the smallest x with alignment `align`
|
||||
/// so that x >= addr. The alignment must be a power of 2.
|
||||
pub fn align_up(addr: usize, align: usize) -> usize {
|
||||
align_down(addr + align - 1, align)
|
||||
}
|
||||
@@ -1,12 +1,70 @@
|
||||
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 {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,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct Frame {
|
||||
number: usize,
|
||||
@@ -56,3 +114,19 @@ pub trait FrameAllocator {
|
||||
fn allocate_frame(&mut self) -> Option<Frame>;
|
||||
fn deallocate_frame(&mut self, frame: Frame);
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
pub use self::entry::*;
|
||||
pub use self::mapper::Mapper;
|
||||
use core::ops::{Deref, DerefMut};
|
||||
use core::ops::{Deref, DerefMut, Add};
|
||||
use core::ptr::Unique;
|
||||
use memory::{PAGE_SIZE, Frame, FrameAllocator};
|
||||
use multiboot2::BootInformation;
|
||||
@@ -17,7 +17,7 @@ const ENTRY_COUNT: usize = 512;
|
||||
pub type PhysicalAddress = usize;
|
||||
pub type VirtualAddress = usize;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct Page {
|
||||
number: usize,
|
||||
}
|
||||
@@ -30,7 +30,7 @@ impl Page {
|
||||
Page { number: address / PAGE_SIZE }
|
||||
}
|
||||
|
||||
fn start_address(&self) -> usize {
|
||||
pub fn start_address(&self) -> usize {
|
||||
self.number * PAGE_SIZE
|
||||
}
|
||||
|
||||
@@ -46,6 +46,42 @@ impl Page {
|
||||
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 {
|
||||
@@ -145,6 +181,7 @@ impl InactivePageTable {
|
||||
}
|
||||
|
||||
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 },
|
||||
@@ -203,4 +240,6 @@ pub fn remap_the_kernel<A>(allocator: &mut A, boot_info: &BootInformation)
|
||||
);
|
||||
active_table.unmap(old_p4_page, allocator);
|
||||
println!("guard page at {:#x}", old_p4_page.start_address());
|
||||
|
||||
active_table
|
||||
}
|
||||
|
||||
79
src/memory/stack_allocator.rs
Normal file
79
src/memory/stack_allocator.rs
Normal file
@@ -0,0 +1,79 @@
|
||||
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
|
||||
}
|
||||
|
||||
pub fn bottom(&self) -> usize {
|
||||
self.bottom
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user