Skip to content

Commit d0e5fc4

Browse files
committed
Added the ability to allocate sequential physical frames
There are some cases where we need to be able to allocate more than one frame, and for the full allocation to be contiguous, such as when using MMIO. The implementation has some false negatives, as it won't find sequential frames that span a boundary of the 64-bit boundary. However, it shouldn't ever return the wrong sequence of addresses. The implementation is fairly basic, but the similarly basic tests work, so hopefully it's fine. Improvements here would be very welcome. Signed-off-by: SlyMarbo <[email protected]>
1 parent 511b775 commit d0e5fc4

File tree

3 files changed

+90
-3
lines changed

3 files changed

+90
-3
lines changed

kernel/src/lib.rs

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -112,10 +112,14 @@ impl Bitmap {
112112
/// bits set to true.
113113
///
114114
pub fn new_set(num: usize) -> Self {
115-
Bitmap {
116-
num,
117-
bits: vec![!0u64; (num + 63) / 64],
115+
// Make sure we only set the bits we
116+
// have.
117+
let mut bitmap = Bitmap::new_unset(num);
118+
for i in 0..num {
119+
bitmap.set(i);
118120
}
121+
122+
bitmap
119123
}
120124

121125
/// new_unset returns a new bitmap with all
@@ -196,6 +200,27 @@ impl Bitmap {
196200
None
197201
}
198202

203+
// next_n_set returns the smallest i, such that
204+
// bits i to i+n-1 are set (true), or None if
205+
// no such i can be found..
206+
//
207+
pub fn next_n_set(&self, n: usize) -> Option<usize> {
208+
let mut mask = 0u64;
209+
for i in 0..n {
210+
mask |= 1 << i;
211+
}
212+
213+
for (i, values) in self.bits.iter().enumerate() {
214+
for j in 0..64 - n {
215+
if values & mask << j == mask << j {
216+
return Some(i * 64 + j);
217+
}
218+
}
219+
}
220+
221+
None
222+
}
223+
199224
// next_unset returns the smallest n, such that
200225
// bit n is unset (false), or None if all bits
201226
// are true.

kernel/src/memory/pmm/bitmap.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use crate::{println, Bitmap};
66
use alloc::vec::Vec;
77
use bootloader::bootinfo::{MemoryRegion, MemoryRegionType};
88
use core::slice::Iter;
9+
use x86_64::structures::paging::frame::PhysFrameRange;
910
use x86_64::structures::paging::{FrameAllocator, FrameDeallocator, PhysFrame, Size4KiB};
1011
use x86_64::PhysAddr;
1112

@@ -121,6 +122,29 @@ impl BitmapPool {
121122
}
122123
}
123124

125+
/// allocate_n_frames returns n sequential free frames,
126+
/// or None.
127+
///
128+
pub fn allocate_n_frames(&mut self, n: usize) -> Option<PhysFrameRange> {
129+
if n == 0 || self.free_frames == 0 {
130+
return None;
131+
}
132+
133+
match self.bitmap.next_n_set(n) {
134+
None => None,
135+
Some(index) => {
136+
for i in 0..n {
137+
self.bitmap.unset(index + i);
138+
}
139+
140+
self.free_frames -= n as u64;
141+
let start = self.frame_at(index);
142+
let end = self.frame_at(index + n);
143+
Some(PhysFrame::range(start, end))
144+
}
145+
}
146+
}
147+
124148
/// mark_frame_allocated marks the given frame as
125149
/// allocated.
126150
///
@@ -234,6 +258,20 @@ impl BitmapFrameAllocator {
234258
}
235259
}
236260

261+
/// allocate_n_frames returns n sequential free frames,
262+
/// or None.
263+
///
264+
pub fn allocate_n_frames(&mut self, n: usize) -> Option<PhysFrameRange> {
265+
for pool in self.pools.iter_mut() {
266+
if let Some(range) = pool.allocate_n_frames(n) {
267+
self.free_frames -= n as u64;
268+
return Some(range);
269+
}
270+
}
271+
272+
None
273+
}
274+
237275
/// mark_frame_allocated marks the given frame as
238276
/// already allocated.
239277
///

kernel/tests/heap_allocation.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ use core::panic::PanicInfo;
1313
use kernel::memory::pmm::BitmapFrameAllocator;
1414
use kernel::memory::KERNEL_HEAP;
1515
use kernel::Bitmap;
16+
use x86_64::structures::paging::frame::PhysFrameRange;
1617
use x86_64::structures::paging::{FrameAllocator, FrameDeallocator, PhysFrame};
1718
use x86_64::PhysAddr;
1819

@@ -197,6 +198,29 @@ fn bitmap_frame_allocator() {
197198
assert_eq!(alloc.allocate_frame(), None);
198199
assert_eq!(alloc.num_frames, 6u64);
199200
assert_eq!(alloc.free_frames, 0u64);
201+
202+
// Check that sequential allocations work correctly.
203+
204+
// Deallocate 2 non-sequential frames, expect None.
205+
unsafe { alloc.deallocate_frame(frame_for(0x5000)) };
206+
unsafe { alloc.deallocate_frame(frame_for(0x7000)) };
207+
assert_eq!(alloc.allocate_n_frames(2), None);
208+
209+
// Leave 2 sequential frames, check we get the right pair.
210+
// Note: we use PhysFrameRange, not PhysFrameRangeInclusive.
211+
assert_eq!(alloc.allocate_frame(), Some(frame_for(0x5000)));
212+
unsafe { alloc.deallocate_frame(frame_for(0x6000)) };
213+
assert_eq!(
214+
alloc.allocate_n_frames(2),
215+
Some(PhysFrameRange {
216+
start: frame_for(0x6000),
217+
end: frame_for(0x8000) // exclusive
218+
})
219+
);
220+
221+
// Check that we get nothing once we run out of frames.
222+
assert_eq!(alloc.num_frames, 6u64);
223+
assert_eq!(alloc.free_frames, 0u64);
200224
}
201225

202226
#[panic_handler]

0 commit comments

Comments
 (0)