mirror of
https://github.com/phil-opp/blog_os.git
synced 2025-12-16 22:37:49 +00:00
Add an AreaFrameAllocator
This commit is contained in:
@@ -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
|
```rust
|
||||||
struct AreaFrameAllocator {
|
use memory::Frame;
|
||||||
first_used_frame: Frame,
|
use multiboot2::{MemoryAreaIter, MemoryArea};
|
||||||
last_used_frame: Frame,
|
|
||||||
current_area: Option<MemoryArea>,
|
pub struct AreaFrameAllocator {
|
||||||
|
next_free_frame: Frame,
|
||||||
|
current_area: Option<&'static MemoryArea>,
|
||||||
areas: MemoryAreaIter,
|
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
|
```rust
|
||||||
TODO
|
pub fn allocate_frame(&mut self) -> Option<Frame> {
|
||||||
|
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
|
## Remapping the Kernel Sections
|
||||||
We can use the ELF section tag to write a skeleton that remaps the kernel correctly:
|
We can use the ELF section tag to write a skeleton that remaps the kernel correctly:
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
#![feature(no_std, lang_items)]
|
#![feature(no_std, lang_items)]
|
||||||
#![feature(const_fn, unique, core_str_ext)]
|
#![feature(const_fn, unique, core_str_ext, iter_cmp)]
|
||||||
#![no_std]
|
#![no_std]
|
||||||
|
|
||||||
extern crate rlibc;
|
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!("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);
|
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{}
|
loop{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
67
src/memory/area_frame_allocator.rs
Normal file
67
src/memory/area_frame_allocator.rs
Normal file
@@ -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<Frame> {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,3 +1,7 @@
|
|||||||
|
pub use self::area_frame_allocator::AreaFrameAllocator;
|
||||||
|
|
||||||
|
mod area_frame_allocator;
|
||||||
|
|
||||||
pub const PAGE_SIZE: usize = 4096;
|
pub const PAGE_SIZE: usize = 4096;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
|||||||
Reference in New Issue
Block a user