use core::fmt;
use core::sync::atomic::{AtomicUsize, Ordering};
use alloc::vec::Vec;
use x86_64::PhysAddr;
use x86_64::structures::paging::{FrameAllocator, PageSize, PhysFrame, Size4KiB};
pub struct BumpFrameAllocator {
start: usize,
end: usize,
next: AtomicUsize,
}
impl BumpFrameAllocator {
pub const unsafe fn new(start: usize, end: usize) -> Self {
BumpFrameAllocator {
start,
end,
next: AtomicUsize::new(start),
}
}
}
unsafe impl FrameAllocator<Size4KiB> for BumpFrameAllocator {
fn allocate_frame(&mut self) -> Option<PhysFrame<Size4KiB>> {
let frame_size = Size4KiB::SIZE as usize;
let current = self.next.load(Ordering::Relaxed);
let aligned = (current + frame_size - 1) & !(frame_size - 1);
if aligned + frame_size <= self.end {
self.next.store(aligned + frame_size, Ordering::Relaxed);
Some(PhysFrame::<Size4KiB>::containing_address(PhysAddr::new(
aligned as u64,
)))
} else {
None
}
}
}
impl fmt::Debug for BumpFrameAllocator {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("BumpFrameAllocator")
.field("start", &self.start)
.field("end", &self.end)
.field("next", &self.next.load(Ordering::Relaxed))
.finish()
}
}
pub struct FreeListFrameAllocator {
free_list: Vec<PhysFrame<Size4KiB>>,
}
impl FreeListFrameAllocator {
pub unsafe fn new(start: usize, end: usize) -> Self {
let mut free_list = Vec::new();
let frame_size = Size4KiB::SIZE as usize;
let mut addr = (start + frame_size - 1) & !(frame_size - 1);
while addr + frame_size <= end {
free_list.push(PhysFrame::<Size4KiB>::containing_address(PhysAddr::new(
addr as u64,
)));
addr += frame_size;
}
FreeListFrameAllocator { free_list }
}
pub unsafe fn new_static(
start: usize,
end: usize,
backing: &mut [PhysFrame<Size4KiB>],
) -> (Self, usize) {
let mut count = 0;
let frame_size = Size4KiB::SIZE as usize;
let mut addr = (start + frame_size - 1) & !(frame_size - 1);
let max = backing.len();
while addr + frame_size <= end && count < max {
backing[count] = PhysFrame::<Size4KiB>::containing_address(PhysAddr::new(addr as u64));
addr += frame_size;
count += 1;
}
let free_list = Vec::from(&backing[..count]);
(FreeListFrameAllocator { free_list }, count)
}
pub unsafe fn reset(&mut self, start: usize, end: usize) {
self.free_list.clear();
let frame_size = Size4KiB::SIZE as usize;
let mut addr = (start + frame_size - 1) & !(frame_size - 1);
while addr + frame_size <= end {
self.free_list
.push(PhysFrame::<Size4KiB>::containing_address(PhysAddr::new(
addr as u64,
)));
addr += frame_size;
}
}
pub fn alloc_frame(&mut self) -> Option<PhysFrame<Size4KiB>> {
self.free_list.pop()
}
pub fn free_frame(&mut self, frame: PhysFrame<Size4KiB>) {
self.free_list.push(frame);
}
}
unsafe impl FrameAllocator<Size4KiB> for FreeListFrameAllocator {
fn allocate_frame(&mut self) -> Option<PhysFrame<Size4KiB>> {
self.free_list.pop()
}
}
#[cfg(feature = "spin_lock")]
pub struct LockedFreeListFrameAllocator {
inner: spin::Mutex<FreeListFrameAllocator>,
}
#[cfg(feature = "spin_lock")]
impl LockedFreeListFrameAllocator {
pub unsafe fn new(start: usize, end: usize) -> Self {
LockedFreeListFrameAllocator {
inner: spin::Mutex::new(unsafe { FreeListFrameAllocator::new(start, end) }),
}
}
pub unsafe fn new_static(
start: usize,
end: usize,
backing: &mut [PhysFrame<Size4KiB>],
) -> (Self, usize) {
let (alloc, count) = unsafe { FreeListFrameAllocator::new_static(start, end, backing) };
(
LockedFreeListFrameAllocator {
inner: spin::Mutex::new(alloc),
},
count,
)
}
pub unsafe fn init(start: usize, end: usize) -> Self {
LockedFreeListFrameAllocator {
inner: spin::Mutex::new(unsafe { FreeListFrameAllocator::new(start, end) }),
}
}
pub const fn empty() -> Self {
LockedFreeListFrameAllocator {
inner: spin::Mutex::new(FreeListFrameAllocator {
free_list: Vec::new(),
}),
}
}
pub fn lock(&'_ self) -> spin::MutexGuard<'_, FreeListFrameAllocator> {
self.inner.lock()
}
pub fn alloc_frame(&self) -> Option<PhysFrame<Size4KiB>> {
self.inner.lock().alloc_frame()
}
pub fn free_frame(&self, frame: PhysFrame<Size4KiB>) {
self.inner.lock().free_frame(frame)
}
}
#[cfg(feature = "spin_lock")]
unsafe impl FrameAllocator<Size4KiB> for LockedFreeListFrameAllocator {
fn allocate_frame(&mut self) -> Option<PhysFrame<Size4KiB>> {
FrameAllocator::allocate_frame(&mut *self.inner.lock())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn physframe_alignment() {
let addr = 0x12345;
let frame = PhysFrame::<Size4KiB>::containing_address(PhysAddr::new(addr as u64));
let frame_size = Size4KiB::SIZE as usize;
assert_eq!(frame.start_address().as_u64() % frame_size as u64, 0);
let addr_val = addr as u64;
let frame_start = frame.start_address().as_u64();
let frame_limit = frame_start + frame_size as u64;
assert!(addr_val >= frame_start);
assert!(addr_val < frame_limit);
}
#[test]
fn physframe_zero_address() {
let frame = PhysFrame::<Size4KiB>::containing_address(PhysAddr::new(0));
assert_eq!(frame.start_address().as_u64(), 0);
}
#[test]
fn physframe_unaligned_address() {
let frame_size = Size4KiB::SIZE as usize;
let addr = frame_size * 5 + 123;
let frame = PhysFrame::<Size4KiB>::containing_address(PhysAddr::new(addr as u64));
assert_eq!(frame.start_address().as_u64(), (frame_size * 5) as u64);
let addr_val = addr as u64;
let frame_start = frame.start_address().as_u64();
let frame_limit = frame_start + frame_size as u64;
assert!(addr_val >= frame_start);
assert!(addr_val < frame_limit);
}
#[test]
fn bump_allocator_basic() {
let start = 0x10000;
let frame_size = Size4KiB::SIZE as usize;
let end = start + 3 * frame_size;
let mut alloc = unsafe { BumpFrameAllocator::new(start, end) };
let f1 = FrameAllocator::allocate_frame(&mut alloc);
let f2 = FrameAllocator::allocate_frame(&mut alloc);
let f3 = FrameAllocator::allocate_frame(&mut alloc);
assert!(f1.is_some() && f2.is_some() && f3.is_some());
assert_ne!(f1, f2);
assert_ne!(f2, f3);
assert_ne!(f1, f3);
assert!(FrameAllocator::allocate_frame(&mut alloc).is_none());
}
#[test]
fn bump_allocator_no_reuse() {
let start = 0x20000;
let frame_size = Size4KiB::SIZE as usize;
let end = start + 2 * frame_size;
let mut alloc = unsafe { BumpFrameAllocator::new(start, end) };
let f1 = FrameAllocator::allocate_frame(&mut alloc).unwrap();
let f2 = FrameAllocator::allocate_frame(&mut alloc).unwrap();
assert_ne!(f1, f2, "Bump allocator must not reuse frames");
}
#[test]
fn bump_allocator_zero_region() {
let mut alloc = unsafe { BumpFrameAllocator::new(0, 0) };
assert!(FrameAllocator::allocate_frame(&mut alloc).is_none());
}
#[test]
fn bump_allocator_unaligned_start() {
let start = 0x12345;
let frame_size = Size4KiB::SIZE as usize;
let end = start + 2 * frame_size;
let mut alloc = unsafe { BumpFrameAllocator::new(start, end) };
let f1 = FrameAllocator::allocate_frame(&mut alloc);
assert!(f1.is_some());
assert_eq!(f1.unwrap().start_address().as_u64() % frame_size as u64, 0);
}
#[test]
fn bump_allocator_exhaustion() {
let start = 0x80000;
let frame_size = Size4KiB::SIZE as usize;
let end = start + frame_size;
let mut alloc = unsafe { BumpFrameAllocator::new(start, end) };
let f1 = FrameAllocator::allocate_frame(&mut alloc);
let f2 = FrameAllocator::allocate_frame(&mut alloc);
assert!(f1.is_some());
assert!(f2.is_none());
}
#[test]
fn freelist_allocator_basic() {
let start = 0x30000;
let frame_size = Size4KiB::SIZE as usize;
let end = start + 2 * frame_size;
let mut alloc = unsafe { FreeListFrameAllocator::new(start, end) };
let f1 = FrameAllocator::allocate_frame(&mut alloc);
let f2 = FrameAllocator::allocate_frame(&mut alloc);
assert!(f1.is_some() && f2.is_some());
assert_ne!(f1, f2);
assert!(FrameAllocator::allocate_frame(&mut alloc).is_none());
}
#[test]
fn freelist_allocator_reuse() {
let start = 0x40000;
let frame_size = Size4KiB::SIZE as usize;
let end = start + 2 * frame_size;
let mut alloc = unsafe { FreeListFrameAllocator::new(start, end) };
let f1 = FrameAllocator::allocate_frame(&mut alloc).unwrap();
alloc.free_frame(f1);
let f2 = FrameAllocator::allocate_frame(&mut alloc).unwrap();
assert_eq!(f1, f2, "Freelist should reuse deallocated frames");
}
#[test]
fn freelist_allocator_double_free() {
let start = 0x50000;
let frame_size = Size4KiB::SIZE as usize;
let end = start + frame_size;
let mut alloc = unsafe { FreeListFrameAllocator::new(start, end) };
let f = FrameAllocator::allocate_frame(&mut alloc).unwrap();
alloc.free_frame(f);
alloc.free_frame(f); assert!(FrameAllocator::allocate_frame(&mut alloc).is_some());
assert!(FrameAllocator::allocate_frame(&mut alloc).is_some());
assert!(FrameAllocator::allocate_frame(&mut alloc).is_none());
}
#[test]
fn freelist_allocator_alignment() {
let start = 0x12345;
let frame_size = Size4KiB::SIZE as usize;
let end = start + 3 * frame_size;
let mut alloc = unsafe { FreeListFrameAllocator::new(start, end) };
let aligned_start = (start + frame_size - 1) & !(frame_size - 1);
let n_frames = if end > aligned_start {
(end - aligned_start) / frame_size
} else {
0
};
let frame_size = Size4KiB::SIZE as usize;
for _ in 0..n_frames {
let f = FrameAllocator::allocate_frame(&mut alloc).unwrap();
assert_eq!(f.start_address().as_u64() % frame_size as u64, 0);
}
assert!(FrameAllocator::allocate_frame(&mut alloc).is_none());
}
#[test]
fn freelist_allocator_zero_region() {
let mut alloc = unsafe { FreeListFrameAllocator::new(0, 0) };
assert!(FrameAllocator::allocate_frame(&mut alloc).is_none());
}
#[test]
fn freelist_allocator_unaligned_start() {
let start = 0x12345;
let frame_size = Size4KiB::SIZE as usize;
let end = start + 2 * frame_size;
let mut alloc = unsafe { FreeListFrameAllocator::new(start, end) };
let f1 = FrameAllocator::allocate_frame(&mut alloc);
assert!(f1.is_some());
assert_eq!(f1.unwrap().start_address().as_u64() % frame_size as u64, 0);
}
#[test]
fn freelist_allocator_stress_many_frames() {
let start = 0x100000;
let n = 100;
let frame_size = Size4KiB::SIZE as usize;
let end = start + n * frame_size;
let mut alloc = unsafe { FreeListFrameAllocator::new(start, end) };
let mut frames = Vec::new();
for _ in 0..n {
let f = FrameAllocator::allocate_frame(&mut alloc);
assert!(f.is_some());
frames.push(f.unwrap());
}
assert!(FrameAllocator::allocate_frame(&mut alloc).is_none());
for f in &frames {
alloc.free_frame(*f);
}
let mut seen = Vec::new();
for _ in 0..n {
let f = FrameAllocator::allocate_frame(&mut alloc);
assert!(f.is_some());
let f = f.unwrap();
assert!(!seen.contains(&f));
seen.push(f);
}
assert!(FrameAllocator::allocate_frame(&mut alloc).is_none());
}
#[test]
fn freelist_allocator_reuse_order() {
let start = 0x200000;
let frame_size = Size4KiB::SIZE as usize;
let end = start + 2 * frame_size;
let mut alloc = unsafe { FreeListFrameAllocator::new(start, end) };
let f1 = FrameAllocator::allocate_frame(&mut alloc).unwrap();
let f2 = FrameAllocator::allocate_frame(&mut alloc).unwrap();
alloc.free_frame(f1);
alloc.free_frame(f2);
let r1 = FrameAllocator::allocate_frame(&mut alloc).unwrap();
let r2 = FrameAllocator::allocate_frame(&mut alloc).unwrap();
assert_eq!(r1, f2);
assert_eq!(r2, f1);
}
#[cfg(feature = "spin_lock")]
#[test]
fn locked_freelist_allocator_basic() {
let start = 0x60000;
let frame_size = Size4KiB::SIZE as usize;
let end = start + 2 * frame_size;
let alloc = unsafe { LockedFreeListFrameAllocator::init(start, end) };
let mut guard = alloc.lock();
let f1 = guard.alloc_frame();
let f2 = guard.alloc_frame();
assert!(f1.is_some() && f2.is_some());
assert_ne!(f1, f2);
assert!(guard.alloc_frame().is_none());
}
#[cfg(feature = "spin_lock")]
#[test]
fn locked_freelist_allocator_reuse() {
let start = 0x70000;
let frame_size = Size4KiB::SIZE as usize;
let end = start + 2 * frame_size;
let alloc = unsafe { LockedFreeListFrameAllocator::init(start, end) };
let mut guard = alloc.lock();
let f1 = guard.alloc_frame().unwrap();
guard.free_frame(f1);
let f2 = guard.alloc_frame().unwrap();
assert_eq!(f1, f2, "LockedFreelist should reuse deallocated frames");
}
#[cfg(feature = "spin_lock")]
#[test]
fn locked_freelist_allocator_empty() {
let alloc = LockedFreeListFrameAllocator::empty();
let mut guard = alloc.lock();
assert!(guard.alloc_frame().is_none());
}
}