5.1.1 Memory Allocation Services
The AllocatePool()
and FreePool()
boot services are used by UEFI drivers to
allocate and free small buffers that are guaranteed to be aligned on an 8-byte
boundary. These services are ideal for allocating and freeing data structures.
The AllocatePages()
and FreePages()
boot services are used by UEFI drivers
to allocate and free larger buffers that are guaranteed to be aligned on a 4 KB
boundary. These services allow buffers to be allocated at any available
address, at specific addresses, or below a specific address.
5.1.1.1 Critical considerations for allocating memory
UEFI drivers should not make assumptions about the organization of system
memory. Because of this, allocating from specific addresses or below specific
addresses is strongly discouraged. The AllocatePool()
service does not
allow the caller to specify a preferred address, so this service is safe to use
and does not impact the compatibility of a UEFI Driver on different platforms.
The AllocatePages()
service does have a mode that allows a specific address
to be specified or a range of addresses to be specified. The allocation type of
AllocateAnyPages
is safe to use and increases the compatibility of UEFI
Drivers on different platforms. The allocation types of AllocateMaxAddress
and AllocateAddress
may reduce platform compatibility, so their use is
discouraged.
Caution: Although the Allocate services allow for specific memory allocation, do not allocate specific addresses in a UEFI driver. Allocating buffers at a specific address could result in errors, including a catastrophic failure on some platforms. Memory allocation in UEFI drivers should be done dynamically.
TIP: Always check function return codes to verify if a memory allocation request succeeded or not before accessing the allocated buffer.
Key points:
To prevent memory leaks, every allocation operation must have a corresponding free operation. It is important to note that some UEFI services allocate buffers for the caller and expect the caller to free those buffers.
Buffers above 4 GB may be allocated if there is system memory is present above 4 GB. As a result, all UEFI drivers must be aware that pointers may contain address values above 4 GB, and care must be taken never to strip the upper address bits.
Structures and values placed in allocated buffers must be naturally aligned to maximize compatibility with all CPU architectures.
Never use an allocated buffer for DMA without mapping it through an I/O Protocol. For example, the
Map()
andUnMap()
services in the PCI I/O Protocol.
Refer to Chapter 4 for general porting considerations covering more memory allocation details for 32-bit and 64-bit architectures.
5.1.1.2 Do not directly allocate a memory buffer for DMA access
A UEFI driver must never directly allocate a memory buffer for DMA access. The UEFI driver cannot know enough about the system architecture to predict what system memory areas are available for DMA or if CPU caches are coherent with DMA operations. Instead, a UEFI driver must use the services provided by the I/O protocol for the bus to allocate and free buffers required for DMA operations. There should also be services to initiate and complete DMA transactions. For example, the PCI Root Bridge I/O Protocol and PCI I/O Protocol both provide services for PCI DMA operations. As additional I/O bus types with DMA capabilities are introduced, new protocols that abstract the DMA services must be provided.
5.1.1.3 Allocating and freeing buffers
UEFI boot service drivers typically allocate and free buffers of type
EfiBootServicesData
. UEFI runtime drivers typically allocate and free buffers
of type EfiRuntimeServicesData
. OS Loaders typically allocate and free
buffers of type EfiLoaderData
.
Most drivers that follow the UEFI driver model allocate private context
structures in their Driver Binding Protocol Start()
function and free them in
their Driver Binding Protocol Stop()
function. UEFI drivers may also
dynamically allocate and free buffers as different I/O operations are performed.
5.1.1.4 Code examples for AllocatePool() and FreePool()
The following code fragment shows how the UEFI Boot Service AllocatePool()
and
FreePool()
can be used to allocate and free a buffer for a data structure
from EfiBootServicesData
memory. The EDK II library
UefiBootServicesTableLib
provides global variables for the UEFI System Table,
the UEFI Boot Services Table, and the Image Handle for the currently executing
driver. In this example, the global variable for the UEFI Boot Services Table,
called gBS,
is used to call the UEFI Boot Services AllocatePool()
and
FreePool().
Example 16-Allocate and free pool using UEFI Boot Services Table
#include <Uefi.h>
#include <Library/UefiBootServicesTableLib.h>
EFI_STATUS Status;
EXAMPLE_DEVICE *Device;
//
// Allocate a buffer for a data structure
//
Status = gBS->AllocatePool (
EfiBootServicesData,
sizeof (EXAMPLE_DEVICE),
(VOID **)&Device
);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Free the allocated buffer
//
Status = gBS->FreePool (Device);
if (EFI_ERROR (Status)) {
return Status;
}
The code fragment below shows exactly the same functionality as Example 16,
above, but uses EDK II library MemoryAllocationLib
to simplify the
implementation. The MemoryAllocationLib
function AllocatePool()
allocates memory of type
EfiBootServicesData
. If memory of type EfiRuntimeServicesData
is required,
then the MemoryAllocationLib
function AllocateRuntimePool()
should be used.
Example 17-Allocate and free pool using MemoryAllocationLib
#include <Uefi.h>
#include <Library/MemoryAllocationLib.h>
EXAMPLE_DEVICE *Device;
//
// Allocate a buffer for a data structure
//
Device = (EXAMPLE_DEVICE *)AllocatePool (sizeof (EXAMPLE_DEVICE));
if (Device == NULL) {
return EFI_OUT_OF_RESOURCES;
}
//
// Free the allocated buffer
//
FreePool (Device);
In many cases, when a structure is allocated, it is useful to clear the buffer
to a known state with zeros. The following code fragment in Example 18 expands
on Example 17, above, showing how the EDK II library MemoryAllocationLib
can
be used to allocate and clear a buffer in a single call.
Example 18-Allocate and clear pool using MemoryAllocationLib
#include <Uefi.h>
#include <Library/MemoryAllocationLib.h>
EXAMPLE_DEVICE *Device;
//
// Allocate and clear a buffer for a data structure
//
Device = (EXAMPLE_DEVICE *)AllocateZeroPool (sizeof (EXAMPLE_DEVICE));
if (Device == NULL) {
return EFI_OUT_OF_RESOURCES;
}
//
// Free the allocated buffer
//
FreePool (Device);
Complex structures that require many fields to be initialized after the
structure is allocated may increase the size of the UEFI driver if the fields
are initialized one by one. The EDK II library MemoryAllocationLib
provides
an additional allocation method that makes use of a template structure to
reduce code size.
The concept is that a template structure can be declared as a global variable
with all the fields pre-initialized to the required values. It takes less space
to store just the data than it does to store the instructions and data to
initialize all the fields one by one. This technique may be useful for UEFI
Drivers that produce new protocols for each device the UEFI Driver manages.
Example 19, below, expands on the above Example 18 showing how the EDK II library
MemoryAllocationLib
is used to allocate and initialize a buffer from a template
structure in a single call.
Example 19-Allocate and initialize pool using MemoryAllocationLib
#include <Uefi.h>
#include <Library/MemoryAllocationLib.h>
EXAMPLE_DEVICE gExampleDeviceTemplate = {
EXAMPLE_PRIVATE_DATA_SIGNATURE,
//
// Other device specific fields
//
};
EXAMPLE_DEVICE *Device;
//
// Allocate and initialize a buffer for a data structure
//
Device = (EXAMPLE_DEVICE *)AllocateCopyPool (
sizeof (EXAMPLE_DEVICE), &gExampleDeviceTemplate
);
if (Device == NULL) {
return EFI_OUT_OF_RESOURCES;
}
//
// Free the allocated buffer
//
FreePool (Device);
5.1.1.5 Code examples for AllocatePages() and FreePages()
The following code fragment shows how the UEFI Boot Services AllocatePages()
and FreePages()
are used to allocate and free a 16KB buffer for a data structure
from EfiBootServicesData
memory. The EDK II library UefiBootServicesTableLib
provides global variables for the UEFI System Table, the UEFI Boot Services Table,
and the Image Handle for the currently executing driver. In this example, the global
variable for the UEFI Boot Services Table, called gBS
, is used to call the
UEFI Boot Services AllocatePages()
and FreePages().
Example 20-Allocate and free pages using UEFI Boot Services Table
#include <Uefi.h>
#include <Library/UefiBootServicesTableLib.h>
EFI_STATUS Status;
EFI_PHYSICAL_ADDRESS PhysicalBuffer;
UINTN Pages;
VOID *Buffer;
//
// Allocate the number of pages to hold Size bytes and
// return in PhysicalBuffer
//
Pages = EFI_SIZE_TO_PAGES (SIZE_16KB);
Status = gBS->AllocatePages (
AllocateAnyPages,
EfiBootServicesData,
Pages,
&PhysicalBuffer
);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Convert the physical address to a pointer.
// This method works for all support CPU architectures.
//
Buffer = (VOID *)(UINTN)PhysicalBuffer;
//
// Free the allocated buffer
//
Status = gBS->FreePages (PhysicalBuffer, Pages);
if (EFI_ERROR (Status)) {
return Status;
}
The code fragment in Example 21, below, shows the same functionality as Example
20, above, but uses the EDK II library MemoryAllocationLib
to simplify the
implementation.
The MemoryAllocationLib
function AllocatePages()
allocates memory of type
EfiBootServicesData
. If memory of type EfiRuntimeServicesData
is required,
the MemoryAllocationLib
function AllocateRuntimePages()
should be used.
Example 21-Allocate and free pages using MemoryAllocationLib
#include <Uefi.h>
#include <Library/MemoryAllocationLib.h>
EXAMPLE_DEVICE *Device;
UINTN Pages;
//
// Allocate a buffer for a data structure
//
Pages = EFI_SIZE_TO_PAGES (sizeof (EXAMPLE_DEVICE));
Device = (EXAMPLE_DEVICE *)AllocatePages (Pages);
if (Device == NULL) {
return EFI_OUT_OF_RESOURCES;
}
//
// Free the allocated buffer
//
FreePages (Device, Pages);
In some rare circumstances, a UEFI Driver may be required to allocate a buffer
with a specific alignment. AllocatePool()
provides 8-byte alignment.
AllocatePages()
provides 4KB alignment. If an alignment above 4KB is
required, the preferred technique is to allocate a large buffer through
AllocatePages()
, find the portion of the allocated buffer that meets the
required alignment, and free the unused portions. EDK II library
MemoryAllocationLib
provides the function called AllocateAlignedPages()
that implements this technique. The code fragment in the example below
allocates a 16KB buffer aligned on a 64KB boundary.
Example 22-Allocate and free aligned pages using MemoryAllocationLib
#include <Uefi.h>
#include <Library/MemoryAllocationLib.h>
VOID *Buffer;
UINTN Pages;
//
// Allocate a buffer for a data structure
//
Pages = EFI_SIZE_TO_PAGES (SIZE_16KB);
Buffer = (EXAMPLE_DEVICE *)AllocateAlignedPages (Pages, SIZE_64KB);
if (Buffer == NULL) {
return EFI_OUT_OF_RESOURCES;
}
//
// Free the allocated buffer
//
FreePages (Buffer, Pages);