1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104
//! This module implements Rust's global allocator interface using UEFI's memory allocation functions.
//!
//! Enabling the `alloc` optional feature in your app will allow you to use Rust's higher-level data structures,
//! like boxes, vectors, hash maps, linked lists and so on.
//!
//! # Usage
//!
//! Call the `init` function with a reference to the boot services table.
//! Failure to do so before calling a memory allocating function will panic.
//!
//! Call the `exit_boot_services` function before exiting UEFI boot services.
//! Failure to do so will turn subsequent allocation into undefined behaviour.
use core::alloc::{GlobalAlloc, Layout};
use core::ptr::{self, NonNull};
use crate::prelude::*;
use crate::table::boot::{BootServices, MemoryType};
/// Reference to the boot services table, used to call the pool memory allocation functions.
///
/// The inner pointer is only safe to dereference if UEFI boot services have not been
/// exited by the host application yet.
static mut BOOT_SERVICES: Option<NonNull<BootServices>> = None;
/// Initializes the allocator.
///
/// # Safety
///
/// This function is unsafe because you _must_ make sure that exit_boot_services
/// will be called when UEFI boot services will be exited.
pub unsafe fn init(boot_services: &BootServices) {
BOOT_SERVICES = NonNull::new(boot_services as *const _ as *mut _);
}
/// Access the boot services
fn boot_services() -> NonNull<BootServices> {
unsafe { BOOT_SERVICES.expect("Boot services are unavailable or have been exited") }
}
/// Notify the allocator library that boot services are not safe to call anymore
///
/// You must arrange for this function to be called on exit from UEFI boot services
pub fn exit_boot_services() {
unsafe {
BOOT_SERVICES = None;
}
}
/// Allocator which uses the UEFI pool allocation functions.
///
/// Only valid for as long as the UEFI boot services are available.
pub struct Allocator;
#[allow(clippy::cast_ptr_alignment)]
unsafe impl GlobalAlloc for Allocator {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
let mem_ty = MemoryType::LOADER_DATA;
let size = layout.size();
let align = layout.align();
if align > 8 {
// allocate more space for alignment
let ptr = if let Ok(ptr) = boot_services()
.as_ref()
.allocate_pool(mem_ty, size + align)
.warning_as_error()
{
ptr
} else {
return ptr::null_mut();
};
// calculate align offset
let mut offset = ptr.align_offset(align);
if offset == 0 {
offset = align;
}
let return_ptr = ptr.add(offset);
// store allocated pointer before the struct
(return_ptr as *mut *mut u8).sub(1).write(ptr);
return_ptr
} else {
boot_services()
.as_ref()
.allocate_pool(mem_ty, size)
.warning_as_error()
.unwrap_or(ptr::null_mut())
}
}
unsafe fn dealloc(&self, mut ptr: *mut u8, layout: Layout) {
if layout.align() > 8 {
ptr = (ptr as *const *mut u8).sub(1).read();
}
boot_services()
.as_ref()
.free_pool(ptr)
.warning_as_error()
.unwrap();
}
}
#[global_allocator]
static ALLOCATOR: Allocator = Allocator;