UEFI (Unified Extensible Firmware Interface) is a standard set of software (or firmware, if you prefer) designed, primarily by Intel and Microsoft, to replace the older more ad hoc BIOS as the first software to be run upon power on or system reset. (The term, "firmware", is frequently used to designate computer software that is stored in ROM or other non-volatile storage.)
There is a UEFI standard specification that describes the structure and interfaces to be used by a UEFI implementation.
There is also the book, Beyond BIOS by Zimmer, Marisetti, and Rothman. I relied heavily on that book in writing this article.
Generally UEFI begins execution immediately following Power On Reset or other method of restarting the system. The Power On Reset causes CPU control to transfer to a specific address which is assumed to contain a ROM or other byte-addressable non-volatile storage, such as NOR flash memory.
It should be mentioned that UEFI was designed to support a wide range of computing devices, whereas the original BIOS was designed specifically to boot the IBM PC, and its successors.
In reality the boot firmware on most PC-class machines (laptops, desktops, and servers) provide both a BIOS and UEFI interface; which one is chosen is usually a setup option. This combined software in normally called "BIOS" even though it also contains UEFI functionality. Most Linux distributions can support both BIOS and UEFI booting, whereas Microsoft Windows heavily favors UEFI. Windows 11 requires UEFI.
The UEFI code that is executed following the Power On Reset or other reset is divided into the following major steps:
The primary goal of the first code to execute is to verify that the next phase (PEI) has not been corrupted or maliciously altered. One of the prime goals of UEFI is to support "secure boot". Although the details of secure boot are beyond the scope of this article, I can say that in order for a boot to be secure it must be secure from beginning to end. For that reason, secure boot is one of the goals of and motivations for the development of UEFI.
In order to accomplish the security checking, it might be necessary to initialize some additional hardware, such as memory or additional storage devices. (The main memory of virtually all computers today is DRAM (dynamic random-access memory). DRAM requires initializing a memory controller. Initializing DRAM is somewhat complex and is usually deferred to a later stage of the boot process. Instead, when possible, some form of SRAM (static random-access memory) is used at this stage. In many cases this SRAM is in the form of cache memory. It is often possible to pre-populate the cache with the code and data to be accessed, thus avoiding accessing main memory. (The computer I am writing this on has more than 256 MB of cache memory, which, presumably, is enough to hold the contents of these early phases of UEFI.)
It will be seen that most of the components of UEFI are loaded from one or more file systems (thus the word, "Extensible" in the name UEFI). These file systems might be in the same storage with the SEC phase of UEFI, but more likely they are on other pieces of non-volatile storage, or even on the main disk drive, SSD, etc. The type of file system is generally one supported by Microsoft. For that reason, on Linux distributions that support UEFI, there is normally a separate EFI partition that is formatted as a FAT file system.
Once the necessary hardware initialization has occurred and the next phase is validated, control is transferred to that next phase, which is...
The purpose of the PEI phase is to do additional initialization, as needed to support the Driver Execution Environment (DXE).
BIOS was designed strictly for Intel 8086 and subsequent processors and was originally written in assembly language; I don't know what language modern BIOS versions are written in since they are closed-source, but they surely contain significant direct assembler code due to the the nature of BIOS. UEFI on the other hand is designed to be used on multiple architectures, and so, like modern kernels, much of it is written in C.
There are multiple components of the PEI phase. These components can be divided into two categories:
The PEI Foundation contains the logic to locate (in a specified file system) the PEIMs to be loaded. There is a mechanism whereby each PEIM can specify under what conditions, and in what order the PEIM is to be run. The PEI Foundation loads the selected PEIMs into memory and executes a main entry point for each of them.
The PEI Foundation also contains a set of utility functions that can be used by the PEIMs when needed. Further, PEIMs can export functionality that can be used by other PEIMs directly.
Once all necessary PEIMs have been located and executed, the PEI Foundation transfers control to the next phase, which is...
DXE essentially loads remaining drivers. Just as the PEI phase did enough initialization to allow the DXE phase to run, so the DXE phase initializes enough hardware to load the operating system. Moreover, just as the PEI phase is larger than the SEC phase, so the DXE phase is larger than the PEI phase.
There are three types of DXE components:
It is the DXE core that creates a data structure called the EFI System Table. This acts as a kind of master control block for locating other EFI components, during the EFI stage, boot stage, and while the loaded OS is running.
The EFI System Table in turn points to two tables of services:
The Boot Services Table contains references to functions that are only available during boot. The Runtime Services table, on the other hand, contains references to functions that continue to be available while the operating system is running. An example of the latter are ACPI (Advanced Computer Power Interface) for managing power and power states while the OS is running.
The DXE Dispatcher looks for, loads, and executes DXE Drivers which do additional system initialization and provide services to other components as neeed. As with PEIMs in the PEI phase, DXE drivers can specify the conditions and order under which they are to be run.
Whereas the DXE core and DXE dispatcher are common across UEFI-supported systems, the DXE drivers are system specific and correspond to the specific hardware on a given system.
There will generally be drivers loaded and initialized for all of the following components (unless they were loaded by the PEI stage):
Once all drivers have been loaded and run, the next phase is...
The boot device selection code is located by the DXE via the Boot Services Table described above. The specific mechanism of discovering which device and file to run next is system specific.
It is the overall goal of UEFI to load and start an operating system, specifically a kernel. It is the job of the BDS to locate which operating system to run. The BDS could theoretically directly run a kernel, provided that the kernel is in a PE/COFF object file. I am not aware of any operating system that does that. Instead the common next element is referred to in the UEFI documentation as a "boot manager", described below.
However, prior to loading a boot manager the BDS can load other transient programs that run and then return to the BDS. The most common example is the EFI shell, which allows the user to interact to select an operating system, a specific kernel, or take some other action.
The boot manager will automatically, or through user interaction, determines which OS and kernel to load. The two most common examples of boot managers are:
In the case of Windows, bootmgr is in the root directory of the boot device and is hidden.
In the case of Linux, the boot manager is in the file /boot/efi/EFI/OS/grub64.efi, where /efi is a mount point for a FAT file system, and OS is the name of the distro. (/efi almost certainly has to be a mount point, because it needs to be a FAT file system in order to be accessed by EFI code; /boot might also be a mount point, or it may be an ordinary directory in the root file system--the choice is up to the person who installs the OS.)
Note that "Boot Manager" (capitalized) is a specific component of Microsoft Windows, whereas "boot manager" (all lower case) is a generic term used by UEFI.
Normally the boot manager will continue the boot process by loading and starting the operating-system kernel. (In the case of GRUB, running as the boot manager, there is a GRUB command to start the UEFI shell. This can be attached to a GRUB menu entry to allow the user to start the UEFI shell. In that case GRUB returns to the BDS.)
At some point the memory for the Boot Services Table, as well as the code to which it points are freed. This is done by one of the BDS, boot loader, or even possibly the OS kernel. The Runtime Services Table, and the code to which it points, remain in memory and are accessible to the kernel for such things as power management.
Next: Boot Manager or Grub