use super::global::SYSTEM_TABLE;
use super::MemoryType;
use core::alloc::{GlobalAlloc, Layout};
use core::ffi::c_void;
use core::ptr;
#[allow(clippy::module_name_repetitions)]
pub struct BootAllocator;
impl BootAllocator {
const UEFI_ALIGNMENT: usize = 8;
const VOID_PTR_LAYOUT: Layout = Layout::new::<*const c_void>();
fn extend_layout_for_alignment(orig_layout: Layout) -> (Layout, usize) {
let (saved_ptr_layout, saved_ptr_offset) = orig_layout
.extend(Self::VOID_PTR_LAYOUT)
.expect("Could not construct extended layout for alignment structure");
let adjusted_size = saved_ptr_layout.size() + saved_ptr_layout.align();
let adjusted_layout =
Layout::from_size_align(adjusted_size, saved_ptr_layout.align())
.expect("Could not construct extended layout for alignable allocation");
(adjusted_layout, saved_ptr_offset)
}
unsafe fn alloc(layout: Layout) -> *mut u8 {
let system_table = SYSTEM_TABLE.expect("System table not initialized");
let boot_services =
(*system_table).boot_services.expect("Boot services unavailable");
let (adjusted_layout, saved_ptr_offset) =
if layout.align() <= Self::UEFI_ALIGNMENT {
(layout, 0)
} else {
Self::extend_layout_for_alignment(layout)
};
let mut buffer: *mut c_void = ptr::null_mut();
(boot_services.allocate_pool)(
MemoryType::LoaderData,
adjusted_layout.size(),
&mut buffer,
)
.into_result()
.expect("allocate_pool() failed");
if adjusted_layout.align() > Self::UEFI_ALIGNMENT {
let orig_address = buffer as usize;
let align_offset = orig_address % adjusted_layout.align();
if align_offset != 0 {
let shifted_ptr = orig_address + (adjusted_layout.align() - align_offset);
buffer = shifted_ptr as *mut c_void;
}
let saved_ptr_ptr = (buffer as usize + saved_ptr_offset) as *mut *mut c_void;
*saved_ptr_ptr = orig_address as *mut c_void;
}
buffer.cast()
}
unsafe fn dealloc(buffer: *mut u8, layout: Layout) {
let system_table = SYSTEM_TABLE.expect("System table not initialized");
let boot_services =
(*system_table).boot_services.expect("Boot services unavailable");
let original_ptr: *mut c_void = if layout.align() <= Self::UEFI_ALIGNMENT {
buffer.cast()
} else {
let (_, saved_ptr_offset) = Self::extend_layout_for_alignment(layout);
let saved_ptr_ptr = (buffer as usize + saved_ptr_offset) as *mut *mut c_void;
*saved_ptr_ptr
};
(boot_services.free_pool)(original_ptr)
.into_result()
.expect("Could not free memory");
}
}
unsafe impl GlobalAlloc for BootAllocator {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
BootAllocator::alloc(layout)
}
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
BootAllocator::dealloc(ptr, layout);
}
}