Dynamic Memory Allocation in UEFI Boot Services
In bare-metal UEFI (Unified Extensible Firmware Interface)
programming, standard C runtime environments do not exist natively,
making traditional functions like malloc() and
free() unavailable. Instead, developers interact directly
with the UEFI System Table to perform dynamic memory management through
UEFI Boot Services. This article explores how developers allocate,
manage, and free dynamic memory using UEFI Boot Services APIs such as
AllocatePool, FreePool,
AllocatePages, and FreePages, while addressing
memory types, alignment, and lifecycle constraints.
The Role of UEFI Boot Services
When developing UEFI applications or drivers via the EDK II (EFI
Development Kit II) framework, dynamic memory management is facilitated
by the Global Boot Services pointer (gBS). The firmware
manages all system RAM prior to the operating system loading and exposes
functions to request arbitrary buffers or page-aligned blocks.
Unlike standard application-level memory allocation, UEFI requires
developers to specify the memory type being allocated. This
designation determines how the memory is treated and whether it persists
after the operating system takes control via
ExitBootServices().
Pool Allocation: The
malloc Equivalent
For variable-sized, arbitrary byte allocations similar to
malloc(), developers use
gBS->AllocatePool(). Releasing this memory is
accomplished using gBS->FreePool().
Syntax and Parameters
EFI_STATUS
EFIAPI
AllocatePool (
IN EFI_MEMORY_TYPE PoolType,
IN UINTN Size,
OUT VOID **Buffer
);
EFI_STATUS
EFIAPI
FreePool (
IN VOID *Buffer
);PoolType: Identifies the intended purpose of the memory block. Common types include:EfiBootServicesData: General-purpose memory used during the pre-boot phase. It is released back to the OS afterExitBootServices()is called.EfiRuntimeServicesData: Memory that remains intact and accessible even after the OS kernel has booted.EfiLoaderData: Memory reserved for loading executable images such as OS loaders or UEFI drivers.
Size: The number of bytes to allocate.Buffer: A pointer to a pointer that receives the base address of the allocated block.
Example Implementation
#include <Uefi.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/UefiLib.h>
EFI_STATUS
AllocateExampleBuffer (VOID)
{
EFI_STATUS Status;
UINT8 *Buffer = NULL;
UINTN BufferSize = 1024; // 1 KB
// Allocate memory from the boot services pool
Status = gBS->AllocatePool (
EfiBootServicesData,
BufferSize,
(VOID **)&Buffer
);
if (EFI_ERROR (Status)) {
return Status;
}
// Use the allocated buffer
Buffer[0] = 0xAA;
// Free the allocated memory
Status = gBS->FreePool (Buffer);
return Status;
}Unlike free(), which does not typically return a status
code, FreePool() returns an EFI_STATUS (such
as EFI_INVALID_PARAMETER if an invalid pointer is passed),
allowing developers to directly verify deallocation success.
Page Allocation: Coarse-Grained Management
When an allocation requires page alignment (typically 4 KB
boundaries) or physical address constraints—often necessary for Direct
Memory Access (DMA) or loading executable images—developers use
gBS->AllocatePages() and
gBS->FreePages().
Syntax and Types
EFI_STATUS
EFIAPI
AllocatePages (
IN EFI_ALLOCATE_TYPE Type,
IN EFI_MEMORY_TYPE MemoryType,
IN UINTN Pages,
IN OUT EFI_PHYSICAL_ADDRESS *Memory
);
EFI_STATUS
EFIAPI
FreePages (
IN EFI_PHYSICAL_ADDRESS Memory,
IN UINTN Pages
);Type: Dictates address placement constraints:AllocateAnyPages: Allocates any available pages that fit the requirement.AllocateMaxAddress: Allocates pages at or below a specified maximum physical address.AllocateAddress: Allocates pages starting at a specific physical address (useful for legacy hardware constraints).
Pages: The number of 4 KB pages requested.Memory: ForAllocateAnyPages, this is an output parameter receiving the base address. For fixed types, it is an input containing the target boundary or address.
Critical
Differences Between malloc and UEFI Allocation
- Explicit Memory Lifecycles: Standard
malloccreates heap space tied to the lifecycle of the user-space process. In UEFI, developers must choose whether data persists into runtime OS operations or gets purged atExitBootServices(). - Granular Error Reporting: UEFI memory APIs return
explicit error codes, such as
EFI_OUT_OF_RESOURCESorEFI_NOT_FOUND, making it straightforward to diagnose failure modes compared to a simpleNULLreturn frommalloc. - Alignment Guarantees:
AllocatePages()guarantees 4 KB alignment by default, removing the need for auxiliary alignment functions likeposix_memalign()or_aligned_malloc(). - No Implicit Garbage Collection: If an application
exits without calling
FreePool(), memory allocated with types likeEfiRuntimeServicesDatacan remain reserved, potentially reducing the usable memory pool for the incoming operating system.