UEFI DXE Phase: Driver Discovery and Execution
The Driver Execution Environment (DXE) phase is the central stage of the Unified Extensible Firmware Interface (UEFI) boot flow, responsible for initializing the vast majority of system hardware and setting up runtime services. This article explains how the DXE Foundation initializes, processes the Hand-Off Block (HOB) list from the preceding Pre-EFI Initialization (PEI) phase, and uses the DXE Dispatcher to discover, evaluate, and execute firmware drivers stored in Firmware Volumes (FVs) through an event-driven, dependency-based model.
The DXE Architecture and Entry
The DXE phase begins immediately after the PEI phase completes. The PEI Foundation produces the Hand-Off Block (HOB) list, which contains critical system state information such as physical memory maps, firmware volume locations, and hardware-specific configurations. Control is handed over to the DXE Foundation—a non-architectural core engine compiled as a single binary.
Upon execution, the DXE Foundation establishes core services, including:
- Boot Services: Memory allocation, event timers, and protocol database management.
- Runtime Services: NVRAM variable access, time services, and system reset functionality.
- Protocol Database: A central registry mapping Unique Identifiers (GUIDs) to data structures and function pointers.
Driver Storage and Firmware Volumes
UEFI drivers reside within flash memory encapsulated inside one or
more Firmware Volumes (FVs). These FVs are formatted using the Firmware
File System (FFS). Individual drivers are packaged as FFS files with
specific attributes, typically marked with the file type
EFI_FV_FILETYPE_DRIVER or
EFI_FV_FILETYPE_DXE_CORE.
Within each driver file, the binary contains:
- The Executable Image: Usually a PE32/PE32+ formatted binary containing the driver's logic.
- The Dependency Expression (Depex) Section: A compiled binary stream defining the prerequisites that must exist before the driver can safely run.
Driver Discovery: The Role of the DXE Dispatcher
The DXE Dispatcher is the component inside the DXE Foundation responsible for scheduling and executing drivers. It discovers drivers through a systematic scanning process:
- Locating Firmware Volumes: The Dispatcher examines
the HOB list to identify all published Firmware Volumes. It also listens
for
EFI_FIRMWARE_VOLUME2_PROTOCOLinstances produced during the boot process to discover additional volumes, such as option ROMs on expansion cards. - Parsing the FFS Files: The Dispatcher iterates through each volume, inspecting the file headers to locate unexecuted drivers.
- Building the Execution Queue: The Dispatcher adds discovered drivers to an internal list of candidate images, categorizing their dependencies and execution states.
Prioritization and Dependency Evaluation
To avoid race conditions and ensure correct hardware initialization
order, the Dispatcher relies on two mechanisms: the aPRIORI
file and Dependency Expressions (DEPEX).
The aPRIORI File
Firmware engineers can include an optional aPRIORI file
within a Firmware Volume. This file lists GUIDs of critical drivers that
must be executed first, in an explicit, linear order, bypassing
dependency evaluation. This is commonly used to load fundamental
services like memory managers or architectural protocols required by the
Dispatcher itself.
Dependency Expressions (DEPEX)
For all drivers not governed by an aPRIORI list, the
Dispatcher evaluates their DEPEX section. A DEPEX consists of byte-code
instructions representing Boolean logic (such as AND,
OR, NOT, TRUE, END)
evaluated against the GUIDs registered in the central Protocol
Database.
For example, a PCI host controller driver might have a DEPEX
requiring the EFI_CPU_IO2_PROTOCOL. The Dispatcher reads
this expression and queries the Protocol Database. If the protocol is
not present, the expression evaluates to FALSE, and the
driver remains deferred.
Execution and the Protocol Loop
Once the Dispatcher evaluates a driver's DEPEX to TRUE,
execution proceeds through the following steps:
- Loading the Image: The Dispatcher uses the
LoadImage()Boot Service to copy the PE32+ driver into system memory, fix up relocations, and assign it anEFI_HANDLE. - Calling the Entry Point: The Dispatcher invokes
StartImage(), calling the driver's standard entry function (EFI_IMAGE_ENTRY_POINT). - Protocol Registration: During its execution, the
driver initializes its hardware target and registers one or more
protocols in the Protocol Database using
InstallProtocolInterface()orInstallMultipleProtocolInterfaces(). - Re-evaluating the Queue: The addition of new
protocols triggers the Dispatcher to re-evaluate the DEPEX expressions
of all deferred drivers. A driver that previously evaluated to
FALSEmay now evaluate toTRUE.
This process runs as an iterative loop. The Dispatcher continuously scans the firmware volumes, executes drivers whose dependencies are satisfied, publishes their new protocols, and evaluates deferred drivers until no more drivers can be executed.
Transition to Boot Device Selection (BDS)
The DXE phase ends when the Dispatcher can execute no further drivers
and all core Architectural Protocols (such as Timer, Real Time Clock,
Watchdog, and Variable Services) are installed. At this point, the
Dispatcher passes execution control to the BDS Architectural Protocol
(EFI_BDS_ARCH_PROTOCOL), transferring the platform into the
Boot Device Selection phase to detect boot media and load the operating
system.