use crate::boot::{self, AllocateType};
use crate::mem::memory_map::MemoryType;
use crate::proto::loaded_image::LoadedImage;
use core::alloc::{GlobalAlloc, Layout};
use core::ptr::{self, NonNull};
use core::sync::atomic::{AtomicU32, Ordering};
use uefi_raw::table::boot::PAGE_SIZE;
fn get_memory_type() -> MemoryType {
static MEMORY_TYPE: AtomicU32 = AtomicU32::new(MemoryType::RESERVED.0);
let memory_type = MEMORY_TYPE.load(Ordering::Acquire);
if memory_type == MemoryType::RESERVED.0 {
let memory_type = if let Ok(loaded_image) =
boot::open_protocol_exclusive::<LoadedImage>(boot::image_handle())
{
loaded_image.data_type()
} else {
MemoryType::LOADER_DATA
};
MEMORY_TYPE.store(memory_type.0, Ordering::Release);
memory_type
} else {
MemoryType(memory_type)
}
}
fn alloc_pool_aligned(memory_type: MemoryType, size: usize, align: usize) -> *mut u8 {
let full_alloc_ptr = boot::allocate_pool(memory_type, size + align);
let full_alloc_ptr = if let Ok(ptr) = full_alloc_ptr {
ptr.as_ptr()
} else {
return ptr::null_mut();
};
let mut offset = full_alloc_ptr.align_offset(align);
if offset == 0 {
offset = align;
}
unsafe {
let aligned_ptr = full_alloc_ptr.add(offset);
(aligned_ptr.cast::<*mut u8>()).sub(1).write(full_alloc_ptr);
aligned_ptr
}
}
const fn layout_allows_page_alloc_shortcut(layout: &Layout) -> bool {
layout.size().is_multiple_of(PAGE_SIZE) && layout.align() == PAGE_SIZE
}
#[derive(Debug)]
pub struct Allocator;
unsafe impl GlobalAlloc for Allocator {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
if !boot::are_boot_services_active() {
return ptr::null_mut();
}
let memory_type = get_memory_type();
let use_page_shortcut = layout_allows_page_alloc_shortcut(&layout);
match (use_page_shortcut, layout.align()) {
(true, _) => {
let count = layout.size().div_ceil(PAGE_SIZE);
boot::allocate_pages(AllocateType::AnyPages, memory_type, count)
.map(|ptr| ptr.as_ptr())
.unwrap_or(ptr::null_mut())
}
(false, 0..=8 ) => {
boot::allocate_pool(memory_type, layout.size())
.map(|ptr| ptr.as_ptr())
.unwrap_or(ptr::null_mut())
}
(false, 9..) => alloc_pool_aligned(memory_type, layout.size(), layout.align()),
}
}
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
let ptr = NonNull::new(ptr).unwrap();
let use_page_shortcut = layout_allows_page_alloc_shortcut(&layout);
match (use_page_shortcut, layout.align()) {
(true, _) => {
let count = layout.size().div_ceil(PAGE_SIZE);
unsafe { boot::free_pages(ptr, count).unwrap() }
}
(false, 0..=8 ) => {
unsafe { boot::free_pool(ptr) }.unwrap();
}
(false, 9..) => {
let ptr = ptr.as_ptr().cast::<*mut u8>();
let actual_alloc_ptr = unsafe { ptr.sub(1).read() };
let ptr = NonNull::new(actual_alloc_ptr).unwrap();
unsafe { boot::free_pool(ptr) }.unwrap();
}
}
}
}