Skip to content

Commit

Permalink
Fix blocking and recursion in StackFrameAllocator
Browse files Browse the repository at this point in the history
This commit fixes the blocking in StackFrameAllocator. There is also
blocking in `remap_the_kernel()`, and that was fixed using a temporary
change. While fixing things I added more verbose comments to some
functions and errors in the operating system. `test_paging` has been
moved from being a dead function to being commented out, it will be
returned to the codebase when the memory module is fixed.

The biggest changes in this commit are the changes to `TinyAllocator`.
Now almost all of it's functions are generic using closures which makes
it easier to use in other parts of the codebase.

Finally I have created some changes in the `vga_buffer` module. These
are mostly whitespace fixes, however I have improved functionality in
some parts.
  • Loading branch information
Calvin Lee committed Nov 11, 2016
1 parent 80553d1 commit 3f05746
Show file tree
Hide file tree
Showing 8 changed files with 200 additions and 105 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
*.rlib

#cargo
/.cargo/
/target/
Cargo.lock

Expand Down
26 changes: 13 additions & 13 deletions src/memory/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
#![allow(dead_code,unused_variables)]

use multiboot2::BootInformation;
use spin::Mutex;

pub use self::area_frame_iter::AreaFrameIter;
use self::stack_frame_allocator::StackFrameAllocator;
Expand All @@ -35,15 +34,6 @@ const HEAP_START: usize = 0o000_001_000_0000;
/// The size of the kernel heap
const HEAP_SIZE: usize = 100 * 1024;

lazy_static! {
/// A representation of the level 4 page table.
static ref ACTIVE_TABLE: Mutex<paging::ActivePageTable> = {
unsafe {
Mutex::new( paging::ActivePageTable::new() )
}
};
}

/// Initializes memory to a defined state.
///
/// It first finds, and prints out, the kernel start and finish. Then it
Expand Down Expand Up @@ -87,8 +77,10 @@ pub fn init(boot_info: &BootInformation) {
memory_map_tag.memory_areas()))
};

// TODO remove active table argument
paging::remap_the_kernel(&mut ACTIVE_TABLE.lock(), &mut frame_allocator, boot_info);
// TODO FIXME replace with static active table
let mut temp_active_table = unsafe {paging::ActivePageTable::new()};

paging::remap_the_kernel(&mut temp_active_table, &mut frame_allocator, boot_info);

use self::paging::Page;
use hole_list_allocator;
Expand All @@ -97,7 +89,7 @@ pub fn init(boot_info: &BootInformation) {
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.lock().map(page, paging::WRITABLE, &mut frame_allocator);
temp_active_table.map(page, paging::WRITABLE, &mut frame_allocator);
}

unsafe {
Expand Down Expand Up @@ -158,4 +150,12 @@ impl Iterator for FrameIter {
pub trait FrameAllocator {
fn allocate_frame(&mut self) -> Option<Frame>;
fn deallocate_frame(&mut self, frame: Frame);

fn transfer_frames<A>(&mut self, other: &mut A)
where A: FrameAllocator
{
while let Some(frame) = other.allocate_frame() {
self.deallocate_frame(frame);
}
}
}
2 changes: 1 addition & 1 deletion src/memory/paging/mapper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ impl Mapper {
/// # Safety
/// The page table must be recursively mapped, if it is not any methods using
/// the Mapper will most likely produce undefined behaviour.
pub unsafe fn new() -> Mapper {
pub const unsafe fn new() -> Mapper {
Mapper { p4: Unique::new(table::P4) }
}

Expand Down
95 changes: 62 additions & 33 deletions src/memory/paging/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,11 @@
use core::ops::{Deref, DerefMut};

use multiboot2::{BootInformation, StringTable};
use spin::Mutex;

pub use self::entry::*;
pub use self::mapper::Mapper;
use self::temporary_page::TemporaryPage;
pub use self::temporary_page::{TemporaryPage, TinyAllocator};
use memory::{PAGE_SIZE, Frame, FrameAllocator};

/// An entry in the page table.
Expand All @@ -28,7 +29,23 @@ mod mapper;
/// How many entries are in each table.
const ENTRY_COUNT: usize = 512;

/// This is the _only_ ActivePageTable that should be used in the system. Any others
/// would violate the assumptions of `Unique`.
pub static ACTIVE_TABLE: Mutex<ActivePageTable> = Mutex::new(unsafe {
ActivePageTable::new()
});

// TODO FIXME Make conversions between the PhysicalAddress and VirtualAddress types
// unsafe. All addresses in this module have to be explicitly physical or virtual.
//
// Possibly make physical and virtual traits and apply them to numbers, pointers,
// etc.?
/// A _physical_ address on the machine. *_These should only be known to the kernel_*.
/// The userspace should never recieve a physical addresss.
pub type PhysicalAddress = usize;
/// A _virtual_ address on the machine. _All_ pointers are of this type. It is
/// undefined behaviour to convert a virtual to a physical address except through
/// the specific page table methods.
pub type VirtualAddress = usize;

/// A representation of a virtual page.
Expand Down Expand Up @@ -137,7 +154,7 @@ impl ActivePageTable {
/// # Safety
/// The page table must be recursively mapped, if it is not any methods using
/// the active page table will most likely produce undefined behaviour.
pub unsafe fn new() -> ActivePageTable {
pub const unsafe fn new() -> ActivePageTable {
ActivePageTable { mapper: Mapper::new() }
}

Expand Down Expand Up @@ -300,37 +317,49 @@ pub fn remap_the_kernel<A>(active_table: &mut ActivePageTable,
println!("New guard page at {:#x}", old_p4_page.start_address());
}

pub fn test_paging<A>(allocator: &mut A)
//TODO Replace allocator field with static allocator for public interface.
pub fn map_to<A>(page: Page,
frame: Frame,
flags: EntryFlags,
allocator: &mut A)
where A: FrameAllocator
{
let mut page_table = unsafe { ActivePageTable::new() };

// Address 0 is mapped
println!("Some = {:?}", page_table.translate(0));
// Second P1 entry
println!("Some = {:?}", page_table.translate(4096));
// Second P2 entry
println!("Some = {:?}", page_table.translate(4096 * 512));
// 300th P2 entry
println!("Some = {:?}", page_table.translate(4096 * 512 * 300));
// Second P3 entry
println!("None = {:?}", page_table.translate(4096 * 512 * 512));
// Last entry
println!("Some = {:?}", page_table.translate(4096 * 512 * 512 - 1));

// Test map_to
let addr = 4096 * 512 * 512 * 42; // 42th p3 entry
let page = Page::containing_address(addr);
let frame = allocator.allocate_frame()
.expect("No more frames :(");
println!("None = {:?}, map to {:?}",
page_table.translate(addr),
frame);
page_table.map_to(page, frame, EntryFlags::empty(), allocator);
println!("Some = {:?}", page_table.translate(addr));
println!("Next free frame: {:?}", allocator.allocate_frame());

// Test unmap
page_table.unmap(Page::containing_address(addr), allocator);
println!("None = {:?}", page_table.translate(addr));
let mut temp_alloc = TinyAllocator::new(|| allocator.allocate_frame());

ACTIVE_TABLE.lock().map_to(page, frame, flags, &mut temp_alloc);

temp_alloc.drop(allocator);
}

//pub fn test_paging<A>(page_table: &mut ActivePageTable, allocator: &mut A)
// where A: FrameAllocator
//{
// // Address 0 is mapped
// println!("Some = {:?}", page_table.translate(0));
// // Second P1 entry
// println!("Some = {:?}", page_table.translate(4096));
// // Second P2 entry
// println!("Some = {:?}", page_table.translate(4096 * 512));
// // 300th P2 entry
// println!("Some = {:?}", page_table.translate(4096 * 512 * 300));
// // Second P3 entry
// println!("None = {:?}", page_table.translate(4096 * 512 * 512));
// // Last entry
// println!("Some = {:?}", page_table.translate(4096 * 512 * 512 - 1));
//
// // Test map_to
// let addr = 4096 * 512 * 512 * 12; // 12th p3 entry
// let page = Page::containing_address(addr);
// let frame = allocator.allocate_frame()
// .expect("No more frames :(");
// println!("None = {:?}, map to {:?}",
// page_table.translate(addr),
// frame);
// map_to(page, frame, EntryFlags::empty(), allocator);
// println!("Some = {:?}", page_table.translate(addr));
// println!("Next free frame: {:?}", allocator.allocate_frame());
//
// // Test unmap
// unmap(Page::containing_address(addr), allocator);
// println!("None = {:?}", page_table.translate(addr));
//}
2 changes: 1 addition & 1 deletion src/memory/paging/table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

use memory::paging::entry::*;
use memory::paging::ENTRY_COUNT;
use memory::paging::{VirtualAddress, PhysicalAddress};
use memory::paging::VirtualAddress;
use memory::FrameAllocator;

use core::marker::PhantomData;
Expand Down
62 changes: 51 additions & 11 deletions src/memory/paging/temporary_page.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ impl TemporaryPage {
{
TemporaryPage {
page: page,
allocator: TinyAllocator::new(allocator),
allocator: TinyAllocator::new(|| allocator.allocate_frame()),
}
}

Expand Down Expand Up @@ -68,29 +68,69 @@ impl TemporaryPage {

/// A `FrameAllocator` that can allocate up to three frames: enough for a p3, p2 and
/// p1 table.
struct TinyAllocator([Option<Frame>; 3]);
pub struct TinyAllocator([Option<Frame>; 3]);

impl TinyAllocator {
/// Creates a new TinyAllocator using the current allocator.
fn new<A>(allocator: &mut A) -> TinyAllocator
where A: FrameAllocator
/// Constructs a new TinyAllocator using the given closure.
pub fn new<F>(mut f: F) -> TinyAllocator
where F: FnMut() -> Option<Frame>
{
let mut f = || allocator.allocate_frame();
TinyAllocator([f(), f(), f()])
}

/// This is a hack that drops the `TinyAllocator` without leaking frames. The
/// drop trait cannot be used because this function needs a `FrameAllocator`
fn drop<A>(mut self, allocator: &mut A)
where A: FrameAllocator
/// Constructs an empty TinyAllocator.
pub const fn empty() -> TinyAllocator {
TinyAllocator([None, None, None])
}

/// Replaces all `None` fields of the allocator with new `Frames` from the given
/// closure.
pub fn refill<F>(&mut self, mut f: F)
where F: FnMut() -> Frame
{
for frame_option in &mut self.0 {
if frame_option.is_none() {
*frame_option = Some(f());
}
}
}

/// Flushes each frame in the allocator to the given closure.
pub fn flush<F>(&mut self, mut f: F)
where F: FnMut(Frame)
{
for frame_option in &mut self.0 {
if let Some(ref frame) = *frame_option {
allocator.deallocate_frame(frame.clone());
// Cloning is safe in this context because the original is
// immediatly destroyed after the clone.
f(frame.clone());
}
*frame_option = None;
}
}

/// Returns `true` if the allocator is full.
pub fn is_full(&self) -> bool {
for frame_option in &self.0 {
if frame_option.is_none() {
return false;
}
}
true
}

/// Returns `true` if the allocator is empty.
pub fn is_empty(&self) -> bool {
self.0 == [None, None, None]
}

/// This is a hack that drops the `TinyAllocator` without leaking frames. The
/// drop trait cannot be used because this function needs a `FrameAllocator`
pub fn drop<A>(&mut self, allocator: &mut A)
where A: FrameAllocator
{
allocator.transfer_frames(self);
}
}

impl FrameAllocator for TinyAllocator {
Expand Down
Loading

0 comments on commit 3f05746

Please sign in to comment.