From a2047bc70a0baeb32df5767564ccd96d6b36f123 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Sat, 14 Nov 2015 15:41:11 +0100 Subject: [PATCH] Add an AreaFrameAllocator --- posts/DRAFT-allocating-frames.md | 83 ++++++++++++++++++++++++++---- src/lib.rs | 5 +- src/memory/area_frame_allocator.rs | 67 ++++++++++++++++++++++++ src/memory/mod.rs | 4 ++ 4 files changed, 148 insertions(+), 11 deletions(-) create mode 100644 src/memory/area_frame_allocator.rs diff --git a/posts/DRAFT-allocating-frames.md b/posts/DRAFT-allocating-frames.md index de15f421..addceb8c 100644 --- a/posts/DRAFT-allocating-frames.md +++ b/posts/DRAFT-allocating-frames.md @@ -200,24 +200,87 @@ impl Frame { } ``` -The allocator struct looks like this: +### The Allocator +Now we can put everything together and create the frame allocator. It looks like this: ```rust -struct AreaFrameAllocator { - first_used_frame: Frame, - last_used_frame: Frame, - current_area: Option, +use memory::Frame; +use multiboot2::{MemoryAreaIter, MemoryArea}; + +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, } ``` -TODO +The `next_free_frame` field is a simple counter that is increased every time we return a frame. The `current_area` field holds the memory area that contains `next_free_frame`. If `next_free_frame` leaves this area, we will look for the next one in `areas`. The `{kernel, multiboot}_{start, end}` fields are used to avoid returning already used fields. -To allocate a frame we try to find one in the current area and update the first/last used bounds. If we can't find one, we look for the new area with the minimal start address, that still contains free frames. If the current area is `None`, there are no free frames left. +```rust +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 +} +``` -TODO +```rust +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(|area| area.base_addr); +} +``` -### Unit Tests -TODO +```rust +pub fn allocate_frame(&mut self) -> Option { + match self.current_area { + None => None, + Some(area) => { + let frame = self.next_free_frame; + 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 { + self.choose_next_area() + } else if frame >= self.kernel_start && + frame <= self.kernel_end + { + self.next_free_frame = Frame { + number: self.kernel_end.number + 1 + } + } else if frame >= self.multiboot_start && + frame <= self.multiboot_end + { + self.next_free_frame = Frame { + number: self.multiboot_end.number + 1 + } + } else { + self.next_free_frame.number += 1; + return Some(frame); + } + self.allocate_frame() + } + } +} +``` ## Remapping the Kernel Sections We can use the ELF section tag to write a skeleton that remaps the kernel correctly: diff --git a/src/lib.rs b/src/lib.rs index 723510b2..4c0ee4c1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,7 +13,7 @@ // limitations under the License. #![feature(no_std, lang_items)] -#![feature(const_fn, unique, core_str_ext)] +#![feature(const_fn, unique, core_str_ext, iter_cmp)] #![no_std] extern crate rlibc; @@ -54,6 +54,9 @@ pub extern fn rust_main(multiboot_information_address: 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()); + loop{} } diff --git a/src/memory/area_frame_allocator.rs b/src/memory/area_frame_allocator.rs new file mode 100644 index 00000000..ade79217 --- /dev/null +++ b/src/memory/area_frame_allocator.rs @@ -0,0 +1,67 @@ +use memory::Frame; +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 + } + + pub fn allocate_frame(&mut self) -> Option { + match self.current_area { + None => None, + Some(area) => { + let frame = self.next_free_frame; + 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 { + self.choose_next_area() + } else if frame >= self.kernel_start && frame <= self.kernel_end { + self.next_free_frame = Frame{ number: self.kernel_end.number + 1 } + } else if frame >= self.multiboot_start && frame <= self.multiboot_end { + self.next_free_frame = Frame{ number: self.multiboot_end.number + 1 } + } else { + self.next_free_frame.number += 1; + return Some(frame); + } + self.allocate_frame() + } + } + } + + 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(|area| area.base_addr); + } +} diff --git a/src/memory/mod.rs b/src/memory/mod.rs index b60d5022..496244c4 100644 --- a/src/memory/mod.rs +++ b/src/memory/mod.rs @@ -1,3 +1,7 @@ +pub use self::area_frame_allocator::AreaFrameAllocator; + +mod area_frame_allocator; + pub const PAGE_SIZE: usize = 4096; #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]