diff --git a/src/arch/x86_64/memory/area_frame_allocator.rs b/src/arch/x86_64/memory/area_frame_allocator.rs new file mode 100644 index 0000000..1f73ac0 --- /dev/null +++ b/src/arch/x86_64/memory/area_frame_allocator.rs @@ -0,0 +1,89 @@ +use arch::x86_64::memory::{Frame, FrameAllocator}; +use multiboot2::{MemoryArea, MemoryAreaIter}; + +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 FrameAllocator for AreaFrameAllocator { + fn alloc_frame(&mut self) -> Option { + if let Some(area) = self.current_area { + // This is the next frame up for allocation + let frame = Frame {index: self.next_free_frame.index}; + + // Calculate the current area's last frame + let current_area_last_frame = { + let addr = area.base_addr + area.length - 1; + Frame::containing_address(addr as usize) + }; + + // Check if the frame we're considering is OK; if it is, we'll return it, + // if not, we'll update the frame we're looking at and try again. + if frame > current_area_last_frame { + // If the frame we wanted to allocate is past the end of the current frame, + // switch to the next area + self.next_area(); + } else if frame >= self.kernel_start && frame <= self.kernel_end { + // The frame under consideration is used by the kernel, + // so jump over the kernel code area. + self.next_free_frame = self.kernel_end.next_frame(); + } else if frame >= self.multiboot_start && frame <= self.multiboot_end { + // The frame under consideration is used by Multiboot, + // so jump over the multiboot area. + self.next_free_frame = self.multiboot_end.next_frame(); + } else { + // Frame is unused! + self.next_free_frame.index += 1; // We'll consider the next frame next time we need to alloc + return Some(frame); + } + // The frame we were looking at wasn't valid; try again with our updated `next_free_frame` + return self.alloc_frame(); + } else { + None // no free frames left! + } + } + + fn dealloc_frame(&mut self, frame: Frame) { + unimplemented!(); + } +} + +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(0x0), + 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.next_area(); + allocator + } + + fn 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; + } + } + } +} diff --git a/src/arch/x86_64/memory/mod.rs b/src/arch/x86_64/memory/mod.rs new file mode 100644 index 0000000..35e6853 --- /dev/null +++ b/src/arch/x86_64/memory/mod.rs @@ -0,0 +1,40 @@ +//! Memory management for x86_64 platforms. +//! +//! Heavly inspired/lovingly ripped off from Phil Oppermann's [os.phil-opp.com](http://os.phil-opp.com/). + +mod area_frame_allocator; +mod paging; + +pub use self::area_frame_allocator::AreaFrameAllocator; + +/// The physical size of each frame. +pub const PAGE_SIZE: usize = 4096; + +/// A representation of a frame in physical memory. +#[derive(PartialEq, Eq, PartialOrd, Ord, Debug)] +pub struct Frame { + index: usize, +} + +impl Frame { + /// Retrieves the frame containing a particular physical address. + fn containing_address(address: usize) -> Frame { + Frame {index: address/PAGE_SIZE} + } + + /// Returns the frame after this one. + fn next_frame(&self) -> Frame { + Frame {index: self.index + 1} + } + + fn start_address(&self) -> usize { + self.index * PAGE_SIZE + } +} + +/// A trait which can be implemented by any frame allocator, to make the frame allocation system +/// pluggable. +pub trait FrameAllocator { + fn alloc_frame(&mut self) -> Option; + fn dealloc_frame(&mut self, frame: Frame); +} diff --git a/src/arch/x86_64/mod.rs b/src/arch/x86_64/mod.rs index 91f2d96..80f2bd6 100644 --- a/src/arch/x86_64/mod.rs +++ b/src/arch/x86_64/mod.rs @@ -1 +1,2 @@ -pub mod device; \ No newline at end of file +pub mod device; +pub mod memory; diff --git a/src/lib.rs b/src/lib.rs index 5e98e38..05b0048 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,6 +9,8 @@ extern crate log; extern crate rlibc; extern crate spin; extern crate volatile; +#[macro_use] +extern crate bitflags; extern crate x86; extern crate multiboot2;