diff --git a/posts/DRAFT-allocating-frames.md b/posts/DRAFT-allocating-frames.md
index 393428c2..62ff0458 100644
--- a/posts/DRAFT-allocating-frames.md
+++ b/posts/DRAFT-allocating-frames.md
@@ -211,10 +211,10 @@ pub trait FrameAllocator {
This allows us to create another, more advanced frame allocator in the future.
### The Allocator
-Now we can put everything together and create the frame allocator. It looks like this:
+Now we can put everything together and create the actual frame allocator. It looks like this:
```rust
-use memory::Frame;
+use memory::{Frame, FrameAllocator};
use multiboot2::{MemoryAreaIter, MemoryArea};
pub struct AreaFrameAllocator {
@@ -227,7 +227,67 @@ pub struct AreaFrameAllocator {
multiboot_end: Frame,
}
```
-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.
+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`. When there are no areas left, all frames are used and `current_area` becomes `None`. The `{kernel, multiboot}_{start, end}` fields are used to avoid returning already used fields.
+
+To implement the `FrameAllocator` trait, we need to implement the `allocate_frame` and the `deallocate_frame` methods. The former looks like this:
+
+```rust
+fn allocate_frame(&mut self) -> Option {
+ if let Some(area) = self.current_area {
+ let frame = self.next_free_frame;
+
+ // the last frame of the current area
+ 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 {
+ // all frames of current area are used, switch to next area
+ self.choose_next_area();
+ } else if frame >= self.kernel_start && frame <= self.kernel_end {
+ // `frame` is used by the kernel
+ self.next_free_frame = Frame {
+ number: self.kernel_end.number + 1
+ };
+ } else if frame >= self.multiboot_start && frame <= self.multiboot_end {
+ // `frame` is used by the multiboot information structure
+ self.next_free_frame = Frame {
+ number: self.multiboot_end.number + 1
+ };
+ } else {
+ // frame is unused, increment `next_free_frame` and return it
+ self.next_free_frame.number += 1;
+ return Some(frame);
+ }
+ // `frame` was not valid, try it again with the updated `next_free_frame`
+ self.allocate_frame()
+ } else {
+ None // no free frames left
+ }
+}
+```
+The `choose_next_area` method isn't part of the trait and thus goes into an `impl AreaFrameAllocator` block:
+
+```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);
+}
+```
+This function chooses the area with the minimal base address that still has free frames, i.e. `next_free_frame` is smaller than its last frame. Note that we need to clone the iterator because the order of areas in the memory map isn't specified.
+
+We don't have a data structure to store free frames, so we can't implement `deallocate_frame` reasonably. Thus we use the `unimplemented` macro, which just panics when called:
+
+```rust
+fn deallocate_frame(&mut self, _frame: Frame) {
+ unimplemented!()
+}
+```
+
+Now we only need a constructor function:
```rust
pub fn new(kernel_start: usize, kernel_end: usize,
@@ -247,50 +307,7 @@ pub fn new(kernel_start: usize, kernel_end: usize,
allocator
}
```
-
-```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);
-}
-```
-
-```rust
-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()
- }
- }
-}
-```
+Note that we call `choose_next_area` manually here because `allocate_frame` returns `None` as soon as `current_area` is `None`.
## Remapping the Kernel Sections
We can use the ELF section tag to write a skeleton that remaps the kernel correctly:
diff --git a/src/memory/area_frame_allocator.rs b/src/memory/area_frame_allocator.rs
index 4a534f4d..d36b3705 100644
--- a/src/memory/area_frame_allocator.rs
+++ b/src/memory/area_frame_allocator.rs
@@ -43,27 +43,33 @@ impl AreaFrameAllocator {
impl FrameAllocator for AreaFrameAllocator {
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 let Some(area) = self.current_area {
+ let frame = self.next_free_frame;
- 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()
+ // the last frame of the current area
+ 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 {
+ // all frames of current area are used, switch to next area
+ self.choose_next_area();
+ } else if frame >= self.kernel_start && frame <= self.kernel_end {
+ // `frame` is used by the kernel
+ self.next_free_frame = Frame{ number: self.kernel_end.number + 1 };
+ } else if frame >= self.multiboot_start && frame <= self.multiboot_end {
+ // `frame` is used by the multiboot information structure
+ self.next_free_frame = Frame{ number: self.multiboot_end.number + 1 };
+ } else {
+ // frame is unused, increment `next_free_frame` and return it
+ self.next_free_frame.number += 1;
+ return Some(frame);
}
+ // `frame` was not valid, try it again with the updated `next_free_frame`
+ self.allocate_frame()
+ } else {
+ None // no free frames left
}
}