Introduction to UEFI Boot Process and Its Implementation in Embedded Systems
The article explains UEFI’s seven‑phase boot process, its open‑source EDK2 implementation, and how its modular drivers, event‑driven multitasking, and extensible services differ from traditional embedded bootloaders such as u‑boot or LK, making UEFI better suited for complex devices like smartphones.
Most people encounter UEFI in PC scenarios, such as configuring boot order or CPU virtualization. Originally designed as a BIOS replacement for PCs, UEFI is now also used in mobile devices; Qualcomm started using UEFI from the MSM8998 platform to replace the Little Kernel (LK) bootloader. Embedded developers naturally wonder how UEFI differs from common embedded bootloaders like u‑boot or LK.
UEFI (Unified Extensible Firmware Interface) defines the interface between the operating system and platform firmware. It is a standard published by the UEFI Forum and does not provide an implementation itself. The following sections explore the concrete implementation of UEFI through the open‑source EDK2 codebase provided by the Tianocore community, highlighting its advantages over other bootloaders.
1. UEFI Startup Process
The UEFI system can be divided into seven phases from power‑on to shutdown: SEC → PEI → DXE → BDS → TSL → RT → AL .
Typical open‑source embedded bootloaders consist of two stages. Stage 1 performs minimal hardware initialization; Stage 2 completes hardware setup and loads the operating system. In UEFI terminology, SEC and PEI correspond to Stage 1, while DXE , BDS and TSL correspond to Stage 2.
SEC / PEI
SEC (Security Phase) and PEI (Pre‑EFI Initialization) mainly perform security checks and early hardware initialization, most importantly RAM initialization, so that later phases can run from external RAM.
DXE / BDS
DXE (Driver Execution Environment) loads driver modules; each driver registers services via Protocol structures that remain resident in memory. BDS (Boot Device Select) sets the boot order for operating systems.
TSL
TSL (Transient System Load) executes the OS loader, typically as a UEFI application. For Android, TSL performs AVB verification, loads the Linux kernel into memory, parses the device tree blob (DTB) and kernel command line, etc.
Qualcomm’s UEFI implementation streamlines the process by merging SEC and PEI into a single module.
2. EDK2 Directory Structure
The EDK2 root contains many *Pkg directories, each representing a package—a collection of related functionality. For example, MdePkg provides basic static libraries, while MdeModulePkg contains PEI/DXE startup code and common driver frameworks. Inside each package, individual modules can be compiled into .efi files that are dynamically loaded or unloaded.
The .dsc and .inf files act like Makefiles, defining which modules are built and which .efi files are produced. The .fdf file packages multiple .efi binaries into a flashable image.
3. Characteristics of UEFI
Unlike lightweight bootloaders such as LK or u‑boot, UEFI is more complex but offers modular extensibility, multitasking, and OS‑like features, making it suitable for sophisticated devices like smartphones.
Extensibility
UEFI can dynamically load additional compiled .efi images, whereas u‑boot cannot load new drivers or commands after compilation. The core DXE phase initializes the Boot Services (BS), which provide almost all fundamental service interfaces for later boot stages and applications.
BS manages Image and Protocol interfaces. The Image interface handles loading and launching EFI images, while the Protocol interface enables discovery of services provided by those images. The following diagram shows how an image’s handle links to its protocols via GUID lookup.
When the BDS phase jumps to TSL, BS calls CoreLoadImage() to load the TSL EFI image. The call chain is:
CoreLoadImage → CoreLoadImageCommon → CoreInstallProtocolInterfaceNotify
This creates an IHANDLE structure and adds it to the global handle list.
After loading, CoreStartImage() invokes the image’s entry point defined in its .inf file.
Device Driver Model
UEFI implements a Linux‑like driver model with a clear separation between Host Bus, Driver, and Device. For example, the SCSI driver architecture is illustrated below.
Drivers consist of two parts: the Driver Binding Protocol, which matches a driver to a device handle, and the hardware‑specific I/O Protocol, which is installed on the device handle after a successful match.
The SCSI bus driver’s Start() function scans the bus, creates a controller handle for each LUN, and installs the SCSI I/O protocol.
SCSIBusDriverBindingStart() → ScsiScanCreateDevice()
The ScsiDisk driver then binds to the LUN device created by the bus driver, installs the BlockIo protocol, and enables read/write access to the device.
Multitasking Mechanism
Unlike many embedded bootloaders that are single‑threaded, UEFI provides a single‑threaded, event‑driven multitasking model similar to Linux’s select/epoll . Applications and event notifiers act as tasks, while the Boot Services (BS) expose event‑related interfaces for asynchronous operations.
UEFI events are analogous to Linux condition variables, offering create , wait , and signal functions. The TPL (Task Priority Level) interface raises task priority; when the level exceeds 31, interrupts are disabled, which is used to implement UEFI locks because task scheduling relies on timer interrupts.
The scheduler is initialized by CoreInitializeEventServices , which creates an event queue and a linked list of tasks ordered by priority and time.
When a lock is released, the scheduler traverses pending events from highest to lowest priority, executing each task and then clearing the pending flag.
Timer interrupts trigger the scheduler: each timer tick calls CoreTimerTick , which adds timer‑based tasks to the dispatch list and finally calls CoreReleaseLock (also known as RestoreTpl ) to run higher‑priority tasks.
During timer initialization, CoreNotifyOnProtocolInstallation registers the CPU‑related timer protocol, and the generic protocol notifier links the timer interrupt handler TimerInterruptHandler to CoreTimerTick .
Finally, CoreTimerTick checks the timer, queues timer tasks, and invokes CoreReleaseLock to trigger task dispatch.
Conclusion
u‑boot, LK, and UEFI each have their strengths. u‑boot remains the most widely used bootloader in embedded fields due to its simplicity and low integration cost. UEFI, with its modularity and rich services, is better suited for complex embedded hardware such as smartphones and tablets, where diverse device requirements demand a more capable firmware layer.
References:
1. EDK2 source code: https://github.com/tianocore/edk2
2. "UEFI Principles and Programming" – Dai Zhenghua
OPPO Kernel Craftsman
Sharing Linux kernel-related cutting-edge technology, technical articles, technical news, and curated tutorials
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.