Customizing the UEFI boot process
Smart Startup
The traditional BIOS dates back decades and has not been able to keep up with the rapid development of PCs and laptops. Its powerful successor UEFI takes over its tasks and provides more features, more convenience, and better security.
Since 1981, PCs have booted with the help of the Basic Input/Output System (BIOS). Over the years, different manufacturers have continued to expand the BIOS firmware system, but even after all this time, the BIOS system is difficult to adjust and still does not support 64-bit operation.
Back in the late 1992, Intel launched a new initiative to replace the venerable BIOS system with a 64-bit alternative. This Extensible Firmware Interface (EFI) initiative became the Unified EFI (UEFI) Forum in 2005. In addition to Intel, AMD, Microsoft, HP, and other prominent vendors contributed to the UEFI project.
UEFI describes the interface between a computer's firmware and operating system. Like BIOS, the UEFI implementation initializes the hardware components to prepare the launch of the operating system. However, UEFI natively supports 64-bit architectures, enables graphical user interfaces, and can protect against malicious software by only allowing signed operating systems (a feature that has come to be known as Secure Boot).
If you are using a Linux system that is preconfigured with support for UEFI and Secure Boot, you don't have to think about it very much unless you need to customize or troubleshoot the boot process. But if you want to dig deeper, you will find that UEFI is quite versatile and powerful. This article takes a closer look at the UEFI boot process and shows how you can use UEFI to add a custom application that runs independently of the operating system.
Boot Phases
UEFI firmware is modular and extensible. When it wakes up, the firmware runs through various stages in a strict chronological order (shown in Figure 1). The first phase is known as the Security (SEC) phase. However, the name is unfortunate because very few security-relevant actions occur in this first phase – for instance, the UEFI firmware does not use this phase to check signatures. Essentially, the SEC phase initializes the CPU with hardware-specific code. The CPU loads the code directly from flash memory. RAM is not yet available at this time. Instead, the UEFI firmware uses the temporary memory of the CPU cache as RAM. The SEC phase transfers phase size and location of this temporary storage, as well as optional information about the CPU, to the Pre-EFI Initialization (PEI) phase.
The PEI phase initializes the main memory, as well as hardware components that are required for the next phases. PEI Modules (PEIMs) provide APIs (PEIM-to-PEIM interface, PPI). The PEIM dispatcher is responsible for loading and running the PEIMs. It investigates the PEIMs' dependencies, loads them into the now available main memory, and runs them in a specific order.
In addition to initializing basic hardware components, the UEFI firmware also reads a structured collection of codes and data, known as the firmware volume location. This data is typically stored in flash memory and includes device drivers for the next phase. After the dispatcher has run most of the necessary PEIMs, it finally looks for the Driver Execution Environment (DXE) initial program load (IPL) PEIM. This last PEIM then transfers the execution to the following DXE phase.
In the DXE phase, most of the necessary system initialization then follows. Like the PEI phase, the DXE phase also has a dispatcher, which loads DXE drivers from the firmware volume location and runs them in the desired order. The DXE drivers initialize all required hardware components and register or use protocols.
Protocols provide the text output to the console or access to the PCI devices. If a protocol is only available before the operating system launches, it is known as a boot service. A protocol that you can access them while the operating system is running is called a run-time service.
After the dispatcher has loaded all drivers, it continues with the Boot Device Selection (BDS) phase. The BDS phase runs the UEFI Boot Manager. NVRAM variables tell the Boot Manager which UEFI applications must be launched. The operating system can influence these variables through a run-time service and determine the UEFI applications that must run. The hardware manufacturers frequently provide some UEFI applications, such as diagnostics or recovery applications. You can add additional applications yourself and integrate them in the boot process.
The OS bootloader is a special application. The system typically loads it and launches it from the EFI system partition (/boot/efi
). By launching the OS bootloader, the boot process transitions to the Transient System Load (TSL) phase. If the system uses Secure Boot, its signature would be tested in this phase. Unsigned code will not run. The TSL phase ends on calling the ExitBootServices()
function. This call thoroughly cleans up the memory and only leaves the specially marked interfaces in the memory that need to be available at operating system run time.
After calling ExitBootServices()
, the system is in the Run Time (RT) phase; the operating system is running and can in turn access the UEFI run-time services. After the RT phase, the system enters the After Life (AL) phase with the termination of the operating system. This is intended for an orderly shutdown. However, it is hardly used.
UEFI Run-Time Services and Variables
UEFI run-time services are UEFI functions that can be used at operating system run time. For this purpose, a UEFI driver registers as a run-time service and creates an entry in the run-time services table. During the boot process, the operating system is given this table. The table gives the operating system access to the real-time clock; the OS can trigger a platform reset, access variables in the NVRAM, or initiate a firmware update.
While most run-time services are only accessible to kernel drives on a Linux system, access to the NVRAM variables and the firmware update function are exported to userspace by the Linux kernel. The Linux kernel makes UEFI variables accessible in userspace via the UEFI variable filesystem (efivarfs
). Linux distributions launched by UEFI integrate the filesystem under /sys/firmware/efi/efivars
.
You can list all available variables via:
ls /sys/firmware/efi/efivars
You can read UEFI variables like normal files. The following command outputs the contents of a variable:
cat /sys/firmware/efi/efivars/variable_name
on the console. However, the stored information is available in a machine-readable format; the output of the cat
command is often fairly cryptic.
You can write or delete these variables as you would a normal file. But deleting a variable can be fraught with danger. Previously, a rm-rf /
would result in not just all the files on the hard drive being deleted but all UEFI variables too. However, some buggy UEFI implementations expected certain variables. If these variables were deleted, the computer no longer launched and you had to send the motherboard to be repaired.
Current Linux kernels therefore protect most of the variables with the immutable flag:
lsattr PKDefault-8be4df61-93ca-11d2-aa0d-00e098032b8c ----i-------------- PKDefault-8be4df61-93ca-11d2-aa0d-00e098032b8
Thus, accidental deletion or modification is ruled out. In practice, you cannot directly access the variable via commands such as cat
. Instead, you need to use special programs such as efibootmgr
that let you edit the group of boot variables.
The BDS phase is controlled by the boot variables. A variable starting with Boot000
represents an entry in the UEFI Boot Manager. The value of the variable specifies the location of a UEFI application that is bootable during the BDS phase. The BootOrder-GUID
variable determines the order in which the applications must be launched. The efibootmgr
program manages these variables at the command line. The command in Listing 1 displays the current configuration of the BDS phase.
Listing 1
BDS Phase Configuration
Listing 1 shows two entries. The entry known as debian
is right at the front in the boot order and is therefore run first. The entry points to the EFI system partition (/boot/efi
), on which GRUB is stored. The system first runs the UEFI implementation of GRUB in the BDS phase. GRUB, in turn, launches the Linux kernel. You can add new boot variables or change the boot order with the efibootmgr
command.
While the boot variables are freely adjustable, write access to other variables is restricted. They include, among others, the PK
, KEK
, DB,
and DBX
variables. These variables store the public keys for signature verification within the scope of Secure Boot.
If an attacker can change these variables, they can also bypass or switch off Secure Boot. Therefore, only signed data can be stored in these variables. The UEFI implementation verifies the signature of the transferred public key and only allows the write operation if the signature is trustworthy.
Update Capsule
Another interesting UEFI run-time service bears the name Update Capsule. The operating system can use this service to transfer data blocks to the UEFI firmware. The operating system stores the data in memory and shares the location with firmware via the Update Capsule service. A system reset is then triggered. The computer restarts. UEFI now accesses the provisioned data block.
The service is used primarily to run updates of the UEFI firmware. The advantage of this procedure is that the firmware can handle the actual update process. You don't need any proprietary tools, which are usually only available on Windows.
The standardized UEFI interface makes firmware updates independent of the operating system. For example, Microsoft uses this process to provision surface tablets with new firmware via Windows Update. Apple also uses this process for certain MacBooks. At present, Linux users can only use the update function in conjunction with a few systems. In addition to Microsoft and Apple, Dell is the only big manufacturer that offers firmware updates in the Update Capsule format.
Like the UEFI variables, the Linux kernel exports the update interface to the filesystem. The /sys/firmware/efi/esrt
directory contains the EFI System Resource Table (ESRT). The kernel generates an input for each device that can be updated via the Update Capsule interface. The fwupdate
tool lists these devices. The tool is installed using:
sudo apt-get install fwupdate
The command generates a list of all Update Capsule-capable devices in the system:
fwupdate --list
If there is an Update Capsule-compatible firmware for the devices listed, it can be installed as follows:
fwupdate --apply=guid-of-hardware firmware.cap
Unfortunately, as previously mentioned, only a few manufacturers support this functionality to date. However, it shows what advantages the standardized UEFI interface has over proprietary BIOS implementations.
Buy this article as PDF
(incl. VAT)
Buy Linux Magazine
Subscribe to our Linux Newsletters
Find Linux and Open Source Jobs
Subscribe to our ADMIN Newsletters
Support Our Work
Linux Magazine content is made possible with support from readers like you. Please consider contributing when you’ve found an article to be beneficial.
News
-
Armbian 24.11 Released with Expanded Hardware Support
If you've been waiting for Armbian to support OrangePi 5 Max and Radxa ROCK 5B+, the wait is over.
-
SUSE Renames Several Products for Better Name Recognition
SUSE has been a very powerful player in the European market, but it knows it must branch out to gain serious traction. Will a name change do the trick?
-
ESET Discovers New Linux Malware
WolfsBane is an all-in-one malware that has hit the Linux operating system and includes a dropper, a launcher, and a backdoor.
-
New Linux Kernel Patch Allows Forcing a CPU Mitigation
Even when CPU mitigations can consume precious CPU cycles, it might not be a bad idea to allow users to enable them, even if your machine isn't vulnerable.
-
Red Hat Enterprise Linux 9.5 Released
Notify your friends, loved ones, and colleagues that the latest version of RHEL is available with plenty of enhancements.
-
Linux Sees Massive Performance Increase from a Single Line of Code
With one line of code, Intel was able to increase the performance of the Linux kernel by 4,000 percent.
-
Fedora KDE Approved as an Official Spin
If you prefer the Plasma desktop environment and the Fedora distribution, you're in luck because there's now an official spin that is listed on the same level as the Fedora Workstation edition.
-
New Steam Client Ups the Ante for Linux
The latest release from Steam has some pretty cool tricks up its sleeve.
-
Gnome OS Transitioning Toward a General-Purpose Distro
If you're looking for the perfectly vanilla take on the Gnome desktop, Gnome OS might be for you.
-
Fedora 41 Released with New Features
If you're a Fedora fan or just looking for a Linux distribution to help you migrate from Windows, Fedora 41 might be just the ticket.