UEFI Memory Map Descriptors for Operating Systems

This article provides a technical overview of how the Unified Extensible Firmware Interface (UEFI) defines and conveys physical memory layouts to an operating system. It examines the EFI_MEMORY_DESCRIPTOR structure, breaks down each of its essential fields, explains how the operating system retrieves this map using boot services, and describes how the kernel interprets different memory types to safely manage system resources and reclaim firmware memory.


The EFI_MEMORY_DESCRIPTOR Structure

In the UEFI specification, the physical memory layout of the platform is represented as an array of descriptor structures. When an operating system bootloader queries the firmware for the system memory layout, it receives an array of EFI_MEMORY_DESCRIPTOR entries.

In C-style syntax, the structure is defined as follows:

typedef struct {
    UINT32                Type;
    EFI_PHYSICAL_ADDRESS  PhysicalStart;
    EFI_VIRTUAL_ADDRESS   VirtualStart;
    UINT64                NumberOfPages;
    UINT64                Attribute;
} EFI_MEMORY_DESCRIPTOR;

Each field inside this structure serves a specific role:

  • Type (UINT32): Specifies the classification and intended usage of the memory region.
  • PhysicalStart (EFI_PHYSICAL_ADDRESS / UINT64): The starting physical address of the memory region. It is always aligned to a 4 KiB boundary.
  • VirtualStart (EFI_VIRTUAL_ADDRESS / UINT64): The starting virtual address of the memory region. During boot services, this is typically unassigned or identical to PhysicalStart. It is populated if the operating system configures virtual addresses and invokes the SetVirtualAddressMap() runtime service.
  • NumberOfPages (UINT64): The size of the memory region expressed in the number of contiguous 4 KiB pages. The total byte size of the region is calculated as NumberOfPages * 4096.
  • Attribute (UINT64): A bitmask defining the architectural capabilities, caching modes (such as UC, WC, WT, WB), and protection levels (such as WP, RP, XP) of the region, as well as whether the region needs to be preserved for runtime firmware calls (EFI_MEMORY_RUNTIME).

Key UEFI Memory Types

The Type field dictates whether the operating system can immediately take control of the region, must preserve it, or can reclaim it later. The standard types defined by the specification include:

  • EfiConventionalMemory: Free, unallocated RAM. The operating system can immediately use this memory for its own allocators and page tables.
  • EfiLoaderCode and EfiLoaderData: Memory allocated by the UEFI OS loader. The operating system may reclaim this space once the bootloader has finished execution.
  • EfiBootServicesCode and EfiBootServicesData: Memory holding UEFI boot services code and data. The operating system can safely reclaim these regions after successfully calling ExitBootServices().
  • EfiRuntimeServicesCode and EfiRuntimeServicesData: Memory holding UEFI runtime services code and operational data. The OS must reserve these ranges across reboots and map them to allow the kernel to execute runtime services (such as updating NVRAM variables or setting the RTC).
  • EfiACPIReclaimMemory: Contains ACPI tables. The OS can reclaim this memory only after it has fully read and parsed the ACPI tables into its own structures.
  • EfiACPIMemoryNVS: Non-Volatile Sleep memory used by firmware ACPI code. The operating system must never overwrite this memory.
  • EfiMemoryMappedIO and EfiMemoryMappedIOPortSpace: Physical address ranges designated for memory-mapped hardware registers.
  • EfiReservedMemoryType and EfiUnusableMemory: Memory regions that are firmware-reserved or have sustained hardware errors; the OS must not use them.

Retrieving the Memory Map via GetMemoryMap()

The operating system bootloader retrieves the memory map array by calling the UEFI Boot Service function GetMemoryMap():

EFI_STATUS GetMemoryMap (
    IN OUT UINTN                  *MemoryMapSize,
    OUT EFI_MEMORY_DESCRIPTOR     *MemoryMap,
    OUT UINTN                     *MapKey,
    OUT UINTN                     *DescriptorSize,
    OUT UINT32                    *DescriptorVersion
);

Iterating the Array Correctly

Compilers and firmware implementations may extend the descriptor or add padding. Therefore, operating systems must not iterate through the returned buffer using sizeof(EFI_MEMORY_DESCRIPTOR). Instead, the OS must advance to the next entry by adding the returned DescriptorSize parameter in bytes:

\[\text{Next Descriptor} = (\text{EFI\_MEMORY\_DESCRIPTOR}*) ((\text{UINT8}*)\text{CurrentDescriptor} + \text{DescriptorSize})\]

The Role of MapKey

The MapKey parameter acts as an integrity token. When the operating system is ready to assume full hardware control, it calls ExitBootServices(ImageHandle, MapKey). If memory allocations changed between the last call to GetMemoryMap() and ExitBootServices(), the MapKey becomes invalid, the exit call fails, and the OS must fetch the memory map again before retrying.


Operating System Handoff

Once ExitBootServices() succeeds:

  1. The OS kernel marks all EfiConventionalMemory, EfiBootServicesCode, and EfiBootServicesData regions as usable system RAM.
  2. The OS maps regions flagged with the EFI_MEMORY_RUNTIME attribute into its own address space if it intends to utilize UEFI runtime services.
  3. Hardware registers identified by EfiMemoryMappedIO are tracked to prevent overlapping physical memory allocations.