UEFI — Unified Extensible Firmware Interface
Technical Overview
UEFI (Unified Extensible Firmware Interface) is the modern replacement for BIOS firmware. It provides a standardized environment for platform initialization, hardware abstraction, and bootloader execution. Unlike BIOS, UEFI runs in 32-bit or 64-bit mode, supports drives larger than 2TB, enforces a structured boot process with defined phases, and provides a rich runtime API that survives into the operating system.
UEFI is not just a boot protocol — it is a complete firmware specification encompassing hardware abstraction, a module driver model, network stack, filesystem drivers, and cryptographic services. A UEFI firmware implementation is a small operating system in its own right, running EFI applications (including bootloaders and the UEFI Shell) before handing off to the main OS.
Prerequisites
- BIOS and MBR concepts (see 01-bios-and-post.md)
- Basic understanding of disk partitioning
- FAT32 filesystem concepts
- x86-64 architecture (long mode)
- PKI basics (certificates, signatures) for Secure Boot context
Historical Context
Intel developed EFI (Extensible Firmware Interface) starting in 1998 for the Itanium (IA-64) architecture. Itanium had no legacy x86 real mode and could not use BIOS at all — EFI was a clean-slate firmware design from the start. EFI 1.0 shipped with the first Itanium systems in 2001.
Recognizing that x86 PC firmware needed modernization, Intel published EFI as an industry specification and founded the Unified EFI Forum in 2005 with AMD, Apple, IBM, Microsoft, and major OEMs as members. UEFI 2.0 was published in 2006, incorporating EFI's core design with additions from multiple vendors.
Apple was among the first to deploy UEFI broadly: all Intel Macs (2006–2020) use EFI firmware. Microsoft required UEFI for Windows 8 logo certification in 2012, driving rapid OEM adoption. By 2014, BIOS firmware on new consumer hardware had effectively ceased. Windows 11 (2021) mandates UEFI with Secure Boot, completing the transition.
UEFI Boot Process Phases
UEFI defines seven boot phases in a pipeline called the Platform Initialization (PI) architecture:
UEFI Boot Phase Pipeline
Power On
|
v
+----------+
| SEC | Security Phase
| | - Cache-as-RAM (CAR): use CPU L1 cache as
| | temporary RAM before DRAM init
| | - Minimal CPU validation
| | - Locate and validate PEI Core
+----+-----+
|
v
+----------+
| PEI | Pre-EFI Initialization
| | - PEIM (PEI Module) dispatcher
| | - Memory discovery and initialization
| | - DRAM training, SPD read
| | - Chipset/CPU basic init
| | - Build Hand-Off Blocks (HOBs) for DXE
+----+-----+
|
v
+----------+
| DXE | Driver Execution Environment
| | - DXE Core loads, creates memory map
| | - DXE drivers executed (PCI, storage, video)
| | - UEFI Boot Services + Runtime Services populated
| | - Protocol database built
| | - Full hardware support available
+----+-----+
|
v
+----------+
| BDS | Boot Device Selection
| | - Read BootOrder NVRAM variable
| | - Attempt each Boot#### entry in order
| | - Load EFI application from ESP
| | - UEFI Boot Manager logic lives here
+----+-----+
|
v
+----------+
| TSL | Transient System Load
| | - EFI application (bootloader) executing
| | - Boot Services still available
| | - Bootloader loads OS kernel
| | - Calls ExitBootServices() when ready
+----+-----+
|
v
+----------+
| RT | Runtime
| | - OS is running
| | - Boot Services gone
| | - Runtime Services still available
| | (GetVariable, SetVariable, GetTime,
| | ResetSystem, UpdateCapsule)
+----+-----+
|
v
+----------+
| AL | After Life (error recovery)
| | - S4/S5 resume, watchdog timeout
| | - Platform-specific recovery
+----------+
The SEC and PEI phases execute from CPU cache (Cache-as-RAM / CAR) because DRAM is not initialized yet. The DXE phase is where UEFI becomes a full execution environment with a driver model resembling a simplified OS kernel.
EFI System Partition (ESP)
The EFI System Partition is a FAT32-formatted partition on the boot disk containing UEFI applications (bootloaders, diagnostics, recovery tools). Its existence and location are discoverable via GPT partition type GUID C12A7328-F81F-11D2-BA4B-00A0C93EC93B.
ESP Directory Structure
/EFI/
BOOT/
BOOTX64.EFI ← fallback bootloader (removable media default)
BOOTIA32.EFI ← 32-bit UEFI fallback
Microsoft/
Boot/
bootmgfw.efi ← Windows Boot Manager
ubuntu/
grubx64.efi ← GRUB2 for Ubuntu
shimx64.efi ← shim for Secure Boot
grub.cfg ← GRUB config (sometimes here)
fedora/
shim.efi
grubx64.efi
APPLE/
...
The fallback path \EFI\BOOT\BOOTX64.EFI is used when no NVRAM boot entry matches — this is how live USB drives and removable media work without pre-registered boot entries.
Mount the ESP on Linux at /boot/efi. Typical size is 100MB (Microsoft minimum) to 512MB (recommended for multiple OSes).
UEFI Variables (NVRAM)
UEFI stores boot configuration in non-volatile RAM variables, accessible via GetVariable/SetVariable services. Variables are identified by a GUID + name tuple.
Key boot-related variables (GUID: 8BE4DF61-93CA-11D2-AA0D-00E098032B8C):
| Variable | Type | Description |
|---|---|---|
BootOrder |
UINT16[] |
Ordered list of Boot#### entry IDs |
Boot0000–BootFFFF |
EFI_LOAD_OPTION |
Boot entry: description, device path, optional data |
BootCurrent |
UINT16 |
Entry used for current boot |
BootNext |
UINT16 |
One-shot override for next boot |
Timeout |
UINT16 |
Boot menu timeout in seconds |
The efibootmgr tool on Linux reads and writes these variables:
# List all boot entries
efibootmgr -v
# Create a new boot entry
efibootmgr --create --disk /dev/sda --part 1 \
--loader /EFI/ubuntu/shimx64.efi \
--label "Ubuntu 24.04"
# Change boot order
efibootmgr --bootorder 0003,0001,0002
# Set one-shot next boot
efibootmgr --bootnext 0003
# Delete a boot entry
efibootmgr --bootnum 0005 --delete-bootnum
UEFI variables are stored in a dedicated flash region (typically SPI flash separate from the main UEFI firmware image). Variable storage is authenticated on Secure Boot systems — the OS can only set Secure Boot variables with proper authentication headers.
UEFI Services
UEFI provides two categories of services:
Boot Services (available until ExitBootServices() is called):
- Memory allocation (AllocatePool, AllocatePages)
- Protocol discovery (LocateProtocol, HandleProtocol)
- Event management (CreateEvent, SignalEvent)
- Driver loading (LoadImage, StartImage)
- File system access via EFI_FILE_PROTOCOL
- Console I/O
- PCI enumeration via EFI_PCI_IO_PROTOCOL
Runtime Services (available throughout OS lifetime):
- Variable access (GetVariable, SetVariable)
- Time (GetTime, SetTime)
- System reset (ResetSystem)
- Memory mapping (for OS to maintain correct virtual mappings)
- Firmware updates (UpdateCapsule — for UEFI capsule-based updates)
After ExitBootServices(), the OS owns all memory except what is marked as EfiRuntimeServicesCode and EfiRuntimeServicesData. Runtime service functions are called at elevated privilege from the OS (e.g., Linux calls efi_call_virt() with CPU in kernel mode).
GPT Disk Layout
GUID Partition Table replaces MBR partitioning. GPT supports up to 128 partition entries by default (extensible), uses 64-bit LBA addressing (supporting 9.4 ZB at 512-byte sectors), and stores redundant header copies at the start and end of the disk.
GPT Disk Layout
LBA 0 +---------------------------+
| Protective MBR |
| (type 0xEE covering full |
| disk — prevents old MBR |
| tools from destroying |
| GPT partition table) |
LBA 1 +---------------------------+
| Primary GPT Header |
| - Signature "EFI PART" |
| - MyLBA = 1 |
| - AlternateLBA = last LBA |
| - FirstUsableLBA |
| - LastUsableLBA |
| - DiskGUID (128-bit UUID) |
| - PartitionEntryLBA = 2 |
| - NumberOfPartitions |
| - SizeOfPartitionEntry |
| - Header CRC32 |
LBA 2 +---------------------------+
| Partition Entry Array |
| 128 entries × 128 bytes |
| Each entry: |
| PartitionTypeGUID |
| UniquePartitionGUID |
| StartingLBA |
| EndingLBA |
| Attributes (bootable, |
| read-only, hidden) |
| PartitionName (UTF-16) |
LBA 34 +---------------------------+
| First Partition |
| (typically ESP) |
| |
| ... data partitions ... |
| |
Last-33 +---------------------------+
| Backup Partition Array |
| (mirror of LBA 2–33) |
Last-1 +---------------------------+
| Backup GPT Header |
| (mirror of LBA 1) |
Last LBA +---------------------------+
The protective MBR in LBA 0 contains a single partition entry with type 0xEE spanning the full disk. This prevents legacy tools (including old fdisk) from treating the disk as unpartitioned. BIOS firmware reads the protective MBR but cannot boot from GPT without GPT-aware bootloaders.
Common GPT Partition Type GUIDs:
| GUID | Type |
|---|---|
C12A7328-F81F-11D2-BA4B-00A0C93EC93B |
EFI System Partition |
0FC63DAF-8483-4772-8E79-3D69D8477DE4 |
Linux filesystem data |
E6D6D379-F507-44C2-A23C-238F2A3DF928 |
Linux LVM |
A19D880F-05FC-4D3B-A006-743F0F84911E |
Linux RAID |
933AC7E1-2EB4-4F13-B844-0E14E2AEF915 |
Linux /home |
DE94BBA4-06D1-4D40-A16A-BFD50179D6AC |
Windows Recovery |
EBD0A0A2-B9E5-4433-87C0-68B6B72699C7 |
Windows Basic Data |
UEFI vs BIOS Comparison
| Feature | BIOS | UEFI |
|---|---|---|
| CPU mode | 16-bit real mode | 32 or 64-bit protected/long mode |
| Max addressable RAM | 1MB (real mode) | 16 exabytes (64-bit) |
| Disk addressing | MBR (32-bit LBA) | GPT (64-bit LBA) |
| Max disk size | 2TB (MBR) | 9.4 ZB (GPT) |
| Max partitions | 4 primary | 128 (default) |
| Boot partition | MBR (sector 0) | ESP (FAT32 partition) |
| Bootloader size | 446 bytes (stage 1) | Unlimited EFI application |
| Secure Boot | No | Yes (PKI-based) |
| Network boot | INT 18h/PXE option ROM | EFI network stack built-in |
| Scripting | None | UEFI Shell scripts |
| Update mechanism | Vendor DOS tool | Capsule update (in-band) |
| Driver model | Option ROMs | UEFI DXE drivers |
| Accessibility | POST beeps | GUI firmware setup |
| Initialization time | ~30s typical | ~5–15s typical |
UEFI Shell
The UEFI Shell is an interactive shell environment provided by the Shell.efi application. It provides:
- Filesystem navigation:
fs0:,fs1:map to ESP and other FAT volumes - EFI application execution:
fs0:\EFI\ubuntu\grubx64.efi - Memory and device inspection:
memmap,pci,dh(device handles) - Variable management:
setvar,dmpstore - Network operations:
ifconfig,tftp - Scripting:
.nshscript files (analogous to batch files)
The shell is invaluable for recovering broken boot configurations. Booting into the shell from a USB drive allows manual inspection and repair of NVRAM boot entries without any OS.
# UEFI Shell example: manually boot Linux
Shell> fs0:
fs0:\> ls EFI\ubuntu\
fs0:\> EFI\ubuntu\grubx64.efi
UEFI Debugging with OVMF
OVMF (Open Virtual Machine Firmware) is an open-source UEFI firmware implementation for QEMU/KVM, developed by Intel and maintained in the EDK2 (EFI Development Kit 2) repository.
# Install OVMF
apt install ovmf # Debian/Ubuntu
dnf install edk2-ovmf # Fedora/RHEL
# Boot QEMU with UEFI
qemu-system-x86_64 \
-bios /usr/share/OVMF/OVMF_CODE.fd \
-drive if=none,id=disk0,file=disk.img \
-device nvme,drive=disk0,serial=boot0 \
-m 4G \
-enable-kvm
# For persistent NVRAM (saves boot entries between runs)
cp /usr/share/OVMF/OVMF_VARS.fd /tmp/my_vars.fd
qemu-system-x86_64 \
-drive if=pflash,format=raw,readonly=on,file=/usr/share/OVMF/OVMF_CODE.fd \
-drive if=pflash,format=raw,file=/tmp/my_vars.fd \
...
OVMF debug builds (from EDK2 source) print full DXE dispatch logs, protocol installation events, and ExitBootServices timing to a serial port. This is the standard environment for UEFI driver development and testing.
efibootmgr and efivars
On Linux, UEFI variables are exposed via the kernel's efivarfs filesystem, mounted at /sys/firmware/efi/efivars/. Each variable appears as a file named <name>-<GUID>.
# Check UEFI variable filesystem
ls /sys/firmware/efi/efivars/ | grep -i boot
# Read BootOrder raw bytes (skip 4-byte attribute header)
xxd /sys/firmware/efi/efivars/BootOrder-8be4df61-93ca-11d2-aa0d-00e098032b8c
# Check if running in UEFI mode
[ -d /sys/firmware/efi ] && echo "UEFI" || echo "BIOS"
# Read UEFI memory map
cat /sys/firmware/efi/systab
Production Examples
Multi-OS UEFI Boot: On a developer workstation with Linux + Windows on separate partitions, both OSes register entries in NVRAM. efibootmgr shows both. The UEFI firmware presents a boot menu with a timeout. Each OS has its own entry in /EFI/<vendor>/ on the shared ESP.
PXE Boot via UEFI: UEFI has a built-in network stack (EFI_PXE_BASE_CODE_PROTOCOL). DHCP option 93 (Client System Architecture) identifies UEFI vs BIOS clients, allowing DHCP servers to serve different PXE images. UEFI PXE fetches bootx64.efi via TFTP and executes it directly.
Capsule Firmware Updates: UEFI capsule updates (fwupd on Linux using UpdateCapsule) allow firmware updates to be delivered by the OS and applied during the next boot's pre-OS phase. This is how fwupd/LVFS (Linux Vendor Firmware Service) distributes UEFI, NVMe, and peripheral firmware updates on Linux.
# Check for firmware updates
fwupdmgr get-updates
# Apply pending capsule updates
fwupdmgr update
Debugging Notes
No boot entries in NVRAM: If efibootmgr shows no entries, or the system falls back to \EFI\BOOT\BOOTX64.EFI, the NVRAM was cleared (battery replacement, firmware update, or variable corruption). Re-run grub-install to re-register the entry.
Boot entry present but fails: Verify the EFI file exists at the path in the Boot#### entry. Use UEFI Shell to test manually. Check that the ESP is FAT32 with correct partition type GUID.
UEFI variable write protected: Some firmware implementations make UEFI variables read-only after ExitBootServices or in certain Secure Boot states. Check efivar --list for attribute flags (0x07 = BS+RT+NV, 0x0F = authenticated).
DXE driver failure: On server hardware, UEFI may hang during DXE dispatch if a driver for an absent device is loaded. Serial console (UART redirected from firmware setup) shows DXE dispatch progress. EDK2-based firmware logs the handle database state.
Security Implications
Authenticated Variables: Secure Boot-related variables (PK, KEK, db, dbx) require authenticated writes — they can only be modified by an entity holding the signing key. This prevents the OS from disabling Secure Boot programmatically.
BootKit Persistence via UEFI Variables: The BlackLotus bootkit (2023) demonstrated that UEFI variables can be abused for malware persistence. UEFI-level persistence survives OS reinstallation.
ESP Tampering: The ESP is not encrypted or integrity-protected by default. Any OS with write access to the ESP can replace bootloaders. Secure Boot mitigates this by verifying EFI application signatures before execution.
Runtime Service Vulnerabilities: UEFI runtime services execute in kernel context. Vulnerabilities in runtime service code (e.g., SMM call handlers, SetVariable implementations) represent ring-0 to ring-(-1) privilege escalation paths. Multiple CVEs have targeted UEFI runtime service parsing.
SMM (System Management Mode): UEFI firmware initializes SMM, which is a CPU mode invisible to the OS, executing from SMRAM. SMM has full memory access and cannot be inspected. SMM-based implants are the highest-privilege rootkits known.
Performance Implications
Boot time improvement over BIOS: UEFI's modular driver model allows parallel initialization where BIOS is strictly sequential. UEFI systems routinely achieve firmware-to-bootloader handoff in 3–8 seconds vs 30–60 seconds for legacy BIOS.
Fast Boot / Ultra-Fast Boot: UEFI can skip most initialization on warm boot by caching hardware state. Windows Fast Startup uses a UEFI-level mechanism. However, Fast Boot skips full POST and may miss hardware failures.
ExitBootServices latency: The handoff from UEFI to OS involves ExitBootServices(), which reclaims boot-services memory. This call can take 10–100ms on systems with large memory maps (many ACPI regions, many UEFI drivers). Visible in Linux boot logs as efi: EFI runtime service ranges processing time.
Failure Modes
Brick from bad UEFI update: Interrupted UEFI flash can render a system unbootable. Enterprise systems mitigate with dual-SPI flash. Consumer boards may require hardware SPI flashing to recover.
Variable store exhaustion: UEFI variable storage is limited (often 64KB). Systems with many boot entries, Secure Boot databases, or verbose firmware logs can exhaust the store. efibootmgr will fail with EFI_OUT_OF_RESOURCES. Mitigated by clearing old entries.
ESP corruption: FAT32 corruption on ESP (unsafe shutdown, filesystem bug) causes no bootable device errors. The ESP is typically tiny and not part of OS filesystem mount management, making corruption detection rare.
ACPI table bugs: UEFI provides ACPI tables to the OS. Bugs in OEM ACPI tables cause kernel panics, wrong power management behavior, or device enumeration failures. Linux's acpi_override kernel parameter allows loading corrected ACPI tables from initramfs.
Modern Usage
UEFI is the universal firmware standard for all x86-64 hardware since 2012. Its use extends beyond traditional PCs:
- ARM UEFI: ARM server platforms (Ampere, AWS Graviton) use UEFI. The ARM Server Base System Architecture (SBSA) mandates UEFI.
- RISC-V UEFI: UEFI spec 2.9+ supports RISC-V. SiFive and Alibaba platforms use UEFI.
- Cloud VMs: AWS, Azure, GCP all provide UEFI firmware for VMs. Azure requires UEFI + Secure Boot for trusted launch VMs.
- EDK2 ecosystem: TianoCore EDK2 is the reference UEFI implementation. Production OEM firmware is typically derived from EDK2 plus vendor customizations.
Future Directions
- Project MU: Microsoft's EDK2-based UEFI platform for modern Windows devices, with improved security defaults and rapid update cadence
- Slim Bootloader: Intel's minimal UEFI alternative for embedded/IoT, reducing attack surface
- Rust-based UEFI: rust-osdev/uefi-rs provides safe Rust bindings for UEFI development, with growing adoption in embedded UEFI modules
- Confidential Computing: UEFI integration with AMD SEV-SNP and Intel TDX requires attestation of firmware measurements at VM launch
- UEFI 3.0 roadmap: Ongoing work in UEFI Forum on memory safety, signed firmware volumes, and improved runtime services
Exercises
-
ESP Exploration: Mount the ESP on a running Linux system (
mount /dev/sda1 /mnt/efi). Enumerate all EFI applications and directories. Identify the fallback bootloader path. Usefileto determine the architecture of each.efibinary. -
NVRAM Boot Entry Inspection: Run
efibootmgr -vand decode the device path for each boot entry. Identify the GPT partition GUID and file path for the main OS bootloader. Add a one-shot boot entry pointing to the UEFI Shell if present on your ESP. -
OVMF Lab: Create a QEMU VM with OVMF firmware and a disk image. Install a minimal Linux system. Verify boot entry creation with efibootmgr inside the VM. Delete the NVRAM entry and recover by booting to UEFI Shell and manually executing the bootloader.
-
GPT Inspection: Use
gdisk -l /dev/sdaorsgdisk --print /dev/sdato inspect a GPT disk. Identify the protective MBR, GPT header location, backup header location, and each partition's type GUID. Compare the primary and backup GPT headers withdd+xxd. -
fwupd Firmware Update: On a supported system, run
fwupdmgr get-devicesandfwupdmgr get-updates. If updates are available, examine the capsule update mechanism: check/sys/firmware/efi/capsule/and understand how the kernel passes the capsule to firmware viaUpdateCapsuleruntime service.
References
- UEFI Specification 2.10 (2022): https://uefi.org/specifications
- TianoCore EDK2: https://github.com/tianocore/edk2
- OVMF documentation: https://github.com/tianocore/tianocore.github.io/wiki/OVMF
- efibootmgr source: https://github.com/rhboot/efibootmgr
- "Beyond BIOS" — Vincent Zimmer, Michael Rothman, Suresh Marisetty (Intel Press)
- OSDev Wiki, "UEFI": https://wiki.osdev.org/UEFI
- Linux kernel
drivers/firmware/efi/— EFI runtime and variable handling - UEFI Forum Blog: https://uefi.org/blog
- Roderick W. Smith, "Rod's Books" on GPT and UEFI: http://www.rodsbooks.com/