Support for RMM

This commit is contained in:
Jeremy Soller
2020-11-27 16:49:39 +00:00
parent 8b27de416b
commit f5ac405db6
21 changed files with 350 additions and 684 deletions
-151
View File
@@ -1,151 +0,0 @@
//! # Bump frame allocator
//! Some code was borrowed from [Phil Opp's Blog](http://os.phil-opp.com/allocating-frames.html)
use crate::paging::PhysicalAddress;
use super::{Frame, FrameAllocator, MemoryArea, MemoryAreaIter};
use syscall::{PartialAllocStrategy, PhysallocFlags};
pub struct BumpAllocator {
next_free_frame: Frame,
current_area: Option<&'static MemoryArea>,
areas: MemoryAreaIter,
kernel_start: Frame,
kernel_end: Frame
}
impl BumpAllocator {
pub fn new(kernel_start: usize, kernel_end: usize, memory_areas: MemoryAreaIter) -> Self {
let mut allocator = Self {
next_free_frame: Frame::containing_address(PhysicalAddress::new(0)),
current_area: None,
areas: memory_areas,
kernel_start: Frame::containing_address(PhysicalAddress::new(kernel_start)),
kernel_end: Frame::containing_address(PhysicalAddress::new(kernel_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(PhysicalAddress::new(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(PhysicalAddress::new(area.base_addr as usize));
if self.next_free_frame < start_frame {
self.next_free_frame = start_frame;
}
}
}
}
impl FrameAllocator for BumpAllocator {
#[allow(unused)]
fn set_noncore(&mut self, noncore: bool) {}
fn free_frames(&self) -> usize {
let mut count = 0;
for area in self.areas.clone() {
let start_frame = Frame::containing_address(PhysicalAddress::new(area.base_addr as usize));
let end_frame = Frame::containing_address(PhysicalAddress::new((area.base_addr + area.length - 1) as usize));
for frame in Frame::range_inclusive(start_frame, end_frame) {
if frame >= self.kernel_start && frame <= self.kernel_end {
// Inside of kernel range
} else if frame >= self.next_free_frame {
// Frame is in free range
count += 1;
} else {
// Inside of used range
}
}
}
count
}
fn used_frames(&self) -> usize {
let mut count = 0;
for area in self.areas.clone() {
let start_frame = Frame::containing_address(PhysicalAddress::new(area.base_addr as usize));
let end_frame = Frame::containing_address(PhysicalAddress::new((area.base_addr + area.length - 1) as usize));
for frame in Frame::range_inclusive(start_frame, end_frame) {
if frame >= self.kernel_start && frame <= self.kernel_end {
// Inside of kernel range
count += 1
} else if frame >= self.next_free_frame {
// Frame is in free range
} else {
count += 1;
}
}
}
count
}
fn allocate_frames3(&mut self, count: usize, flags: PhysallocFlags, strategy: Option<PartialAllocStrategy>, min: usize) -> Option<(Frame, usize)> {
// TODO: Comply with flags and allocation strategies better.
if count == 0 {
return None;
} else if let Some(area) = self.current_area {
let space32 = flags.contains(PhysallocFlags::SPACE_32);
let partial_alloc = flags.contains(PhysallocFlags::PARTIAL_ALLOC);
let mut actual_size = count;
// "Clone" the frame to return it if it's free. Frame doesn't
// implement Clone, but we can construct an identical frame.
let start_frame = Frame { number: self.next_free_frame.number };
let mut end_frame = Frame { number: self.next_free_frame.number + (count - 1) };
let min_end_frame = if partial_alloc { Frame { number: self.next_free_frame.number + (min - 1) } } else { Frame { number: self.next_free_frame.number + (count - 1) } };
// the last frame of the current area
let current_area_last_frame = {
let address = area.base_addr + area.length - 1;
Frame::containing_address(PhysicalAddress::new(address as usize))
};
if end_frame > current_area_last_frame && min_end_frame > current_area_last_frame {
// all frames of current area are used, switch to next area
self.choose_next_area();
return self.allocate_frames3(count, flags, strategy, min)
} else if partial_alloc {
end_frame = Frame { number: self.next_free_frame.number + (min - 1) };
actual_size = min;
}
if space32 && end_frame.start_address().get() + super::PAGE_SIZE >= 0x1_0000_0000 {
// assuming that the bump allocator always advances, and that the memory map is sorted,
// when allocating in 32-bit space we can only return None when the free range was
// outside 0x0000_0000-0xFFFF_FFFF.
//
// we don't want to skip an entire memory region just because one 32-bit allocation failed.
return None;
}
if (start_frame >= self.kernel_start && start_frame <= self.kernel_end)
|| (end_frame >= self.kernel_start && end_frame <= self.kernel_end) {
// `frame` is used by the kernel
self.next_free_frame = Frame {
number: self.kernel_end.number + 1
};
// `frame` was not valid, try it again with the updated `next_free_frame`
return self.allocate_frames3(count, flags, strategy, min)
}
// frame is unused, increment `next_free_frame` and return it
self.next_free_frame.number += actual_size;
return Some((start_frame, actual_size));
} else {
None // no free memory areas left, and thus no frames left
}
}
fn deallocate_frames(&mut self, _frame: Frame, _count: usize) {
//panic!("BumpAllocator::deallocate_frame: not supported: {:?}", frame);
}
}
+29 -113
View File
@@ -1,34 +1,15 @@
//! # Memory management
//! Some code was borrowed from [Phil Opp's Blog](http://os.phil-opp.com/allocating-frames.html)
use crate::log::info;
use crate::arch::rmm::FRAME_ALLOCATOR;
pub use crate::paging::{PAGE_SIZE, PhysicalAddress};
use self::bump::BumpAllocator;
use self::recycle::RecycleAllocator;
use spin::Mutex;
use rmm::{
FrameAllocator,
FrameCount,
};
use syscall::{PartialAllocStrategy, PhysallocFlags};
pub mod bump;
pub mod recycle;
/// The current memory map. It's size is maxed out to 512 entries, due to it being
/// from 0x500 to 0x5000 (800 is the absolute total)
static mut MEMORY_MAP: [MemoryArea; 512] = [MemoryArea { base_addr: 0, length: 0, _type: 0, acpi: 0 }; 512];
/// Memory does not exist
pub const MEMORY_AREA_NULL: u32 = 0;
/// Memory is free to use
pub const MEMORY_AREA_FREE: u32 = 1;
/// Memory is reserved
pub const MEMORY_AREA_RESERVED: u32 = 2;
/// Memory is used by ACPI, and can be reclaimed
pub const MEMORY_AREA_ACPI: u32 = 3;
/// A memory map area
#[derive(Copy, Clone, Debug, Default)]
#[repr(packed)]
@@ -39,101 +20,50 @@ pub struct MemoryArea {
pub acpi: u32
}
#[derive(Clone)]
pub struct MemoryAreaIter {
_type: u32,
i: usize
}
impl MemoryAreaIter {
fn new(_type: u32) -> Self {
MemoryAreaIter {
_type,
i: 0
}
}
}
impl Iterator for MemoryAreaIter {
type Item = &'static MemoryArea;
fn next(&mut self) -> Option<Self::Item> {
while self.i < unsafe { MEMORY_MAP.len() } {
let entry = unsafe { &MEMORY_MAP[self.i] };
self.i += 1;
if entry._type == self._type {
return Some(entry);
}
}
None
}
}
static ALLOCATOR: Mutex<Option<RecycleAllocator<BumpAllocator>>> = Mutex::new(None);
/// Init memory module
/// Must be called once, and only once,
pub unsafe fn init(kernel_start: usize, kernel_end: usize) {
// Copy memory map from bootloader location
for (i, entry) in MEMORY_MAP.iter_mut().enumerate() {
*entry = *(0x500 as *const MemoryArea).add(i);
if entry._type != MEMORY_AREA_NULL {
info!("{:X?}", entry);
}
}
*ALLOCATOR.lock() = Some(RecycleAllocator::new(BumpAllocator::new(kernel_start, kernel_end, MemoryAreaIter::new(MEMORY_AREA_FREE))));
}
/// Init memory module after core
/// Must be called once, and only once,
pub unsafe fn init_noncore() {
if let Some(ref mut allocator) = *ALLOCATOR.lock() {
allocator.set_noncore(true)
} else {
panic!("frame allocator not initialized");
}
}
/// Get the number of frames available
pub fn free_frames() -> usize {
if let Some(ref allocator) = *ALLOCATOR.lock() {
allocator.free_frames()
} else {
panic!("frame allocator not initialized");
unsafe {
FRAME_ALLOCATOR.usage().free().data()
}
}
/// Get the number of frames used
pub fn used_frames() -> usize {
if let Some(ref allocator) = *ALLOCATOR.lock() {
allocator.used_frames()
} else {
panic!("frame allocator not initialized");
unsafe {
FRAME_ALLOCATOR.usage().used().data()
}
}
/// Allocate a range of frames
pub fn allocate_frames(count: usize) -> Option<Frame> {
if let Some(ref mut allocator) = *ALLOCATOR.lock() {
allocator.allocate_frames(count)
} else {
panic!("frame allocator not initialized");
unsafe {
FRAME_ALLOCATOR.allocate(FrameCount::new(count)).map(|phys| {
Frame::containing_address(PhysicalAddress::new(phys.data()))
})
}
}
pub fn allocate_frames_complex(count: usize, flags: PhysallocFlags, strategy: Option<PartialAllocStrategy>, min: usize) -> Option<(Frame, usize)> {
if let Some(ref mut allocator) = *ALLOCATOR.lock() {
allocator.allocate_frames3(count, flags, strategy, min)
} else {
panic!("frame allocator not initialized");
if count == min && flags == PhysallocFlags::SPACE_64 && strategy.is_none() {
return allocate_frames(count).map(|frame| (frame, count));
}
println!(
"!!!! allocate_frames_complex not implemented for count {}, flags {:?}, strategy {:?}, min {}",
count,
flags,
strategy,
min
);
return None;
}
/// Deallocate a range of frames frame
pub fn deallocate_frames(frame: Frame, count: usize) {
if let Some(ref mut allocator) = *ALLOCATOR.lock() {
allocator.deallocate_frames(frame, count)
} else {
panic!("frame allocator not initialized");
unsafe {
FRAME_ALLOCATOR.free(
rmm::PhysicalAddress::new(frame.start_address().get()),
FrameCount::new(count)
);
}
}
@@ -188,17 +118,3 @@ impl Iterator for FrameIter {
}
}
}
pub trait FrameAllocator {
fn set_noncore(&mut self, noncore: bool);
fn free_frames(&self) -> usize;
fn used_frames(&self) -> usize;
fn allocate_frames(&mut self, size: usize) -> Option<Frame> {
self.allocate_frames2(size, PhysallocFlags::SPACE_64)
}
fn allocate_frames2(&mut self, size: usize, flags: PhysallocFlags) -> Option<Frame> {
self.allocate_frames3(size, flags, None, size).map(|(s, _)| s)
}
fn allocate_frames3(&mut self, size: usize, flags: PhysallocFlags, strategy: Option<PartialAllocStrategy>, min: usize) -> Option<(Frame, usize)>;
fn deallocate_frames(&mut self, frame: Frame, size: usize);
}
-153
View File
@@ -1,153 +0,0 @@
//! Recycle allocator
//! Uses freed frames if possible, then uses inner allocator
use alloc::vec::Vec;
use crate::paging::PhysicalAddress;
use super::{Frame, FrameAllocator};
use syscall::{PartialAllocStrategy, PhysallocFlags};
struct Range {
base: usize,
count: usize,
}
pub struct RecycleAllocator<T: FrameAllocator> {
inner: T,
noncore: bool,
free: Vec<Range>,
}
impl<T: FrameAllocator> RecycleAllocator<T> {
pub fn new(inner: T) -> Self {
Self {
inner,
noncore: false,
free: Vec::new(),
}
}
fn free_count(&self) -> usize {
self.free.len()
}
fn merge(&mut self, address: usize, count: usize) -> bool {
for i in 0 .. self.free.len() {
let changed = {
let free = &mut self.free[i];
if address + count * super::PAGE_SIZE == free.base {
free.base = address;
free.count += count;
true
} else if free.base + free.count * super::PAGE_SIZE == address {
free.count += count;
true
} else {
false
}
};
if changed {
//TODO: Use do not use recursion
let Range { base: address, count } = self.free[i];
if self.merge(address, count) {
self.free.remove(i);
}
return true;
}
}
false
}
fn try_recycle(&mut self, count: usize, flags: PhysallocFlags, strategy: Option<PartialAllocStrategy>, min: usize) -> Option<(usize, usize)> {
let space32 = flags.contains(PhysallocFlags::SPACE_32);
let partial_alloc = flags.contains(PhysallocFlags::PARTIAL_ALLOC);
let mut actual_size = count;
let mut current_optimal_index = None;
let mut current_optimal = self.free.first()?;
for (free_range_index, free_range) in self.free.iter().enumerate().skip(1) {
// Later entries can be removed faster
if space32 && free_range.base + count * super::PAGE_SIZE >= 0x1_0000_0000 {
// We need a 32-bit physical address and this range is outside that address
// space.
continue;
}
if free_range.count < count {
if partial_alloc && free_range.count >= min && matches!(strategy, Some(PartialAllocStrategy::Greedy)) {
// The free range does not fit the entire requested range, but is still
// at least as large as the minimum range. When using the "greedy"
// strategy, we return immediately.
current_optimal_index = Some(free_range_index);
actual_size = free_range.count;
break;
}
// Range has to fit if we want the entire frame requested.
continue;
}
if free_range.count > current_optimal.count {
// Skip this free range if it wasn't smaller than the old one; we do want to use
// the smallest range possible to reduce fragmentation as much as possible.
continue;
}
// We found a range that fit.
current_optimal_index = Some(free_range_index);
current_optimal = free_range;
}
current_optimal_index.map(|idx| (actual_size, idx))
}
}
impl<T: FrameAllocator> FrameAllocator for RecycleAllocator<T> {
fn set_noncore(&mut self, noncore: bool) {
self.noncore = noncore;
}
fn free_frames(&self) -> usize {
self.inner.free_frames() + self.free_count()
}
fn used_frames(&self) -> usize {
self.inner.used_frames() - self.free_count()
}
fn allocate_frames3(&mut self, count: usize, flags: PhysallocFlags, strategy: Option<PartialAllocStrategy>, min: usize) -> Option<(Frame, usize)> {
// TODO: Cover all different strategies.
if let Some((actual_size, free_range_idx_to_use)) = self.try_recycle(count, flags, strategy, min) {
let (address, remove) = {
let free_range = &mut self.free[free_range_idx_to_use];
free_range.count -= actual_size;
(free_range.base + free_range.count * super::PAGE_SIZE, free_range.count == 0)
};
if remove {
self.free.remove(free_range_idx_to_use);
}
//println!("Restoring frame {:?}, {}", frame, count);
Some((Frame::containing_address(PhysicalAddress::new(address)), actual_size))
} else {
//println!("No saved frames {}", count);
self.inner.allocate_frames3(count, flags, strategy, min)
}
}
fn deallocate_frames(&mut self, frame: Frame, count: usize) {
if self.noncore {
let address = frame.start_address().get();
if ! self.merge(address, count) {
self.free.push(Range { base: address, count });
}
} else {
//println!("Could not save frame {:?}, {}", frame, count);
self.inner.deallocate_frames(frame, count);
}
}
}