Support for RMM
This commit is contained in:
@@ -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
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user