Loading learning content...
Press the power button on any computer, and you initiate a remarkable transformation. One moment, you have a collection of inert components—transistors holding no charge, capacitors discharged, memory cells in random states. A fraction of a second later, electrical signals begin propagating through billions of pathways, and sophisticated software begins executing instructions that have been waiting, frozen in non-volatile storage, for exactly this moment.
This transformation doesn't happen by magic. It's orchestrated by firmware—the foundational software layer that bridges raw hardware and the operating system. Without firmware, an operating system would have no way to discover what hardware exists, no way to initialize it into a usable state, and no way to load itself into memory.
The firmware that performs this critical role has evolved dramatically over four decades, from the simple BIOS (Basic Input/Output System) designed for the first IBM PC to the sophisticated UEFI (Unified Extensible Firmware Interface) that powers modern systems. Understanding both is essential for any systems engineer.
By the end of this page, you will understand: the fundamental role of firmware in system initialization; how legacy BIOS operates and its inherent limitations; the architecture and capabilities of modern UEFI; the critical differences between MBR and GPT partitioning; Secure Boot and its security implications; and why this firmware layer is the foundation upon which all operating systems depend.
When a computer powers on, it faces a profound bootstrapping problem: the CPU needs instructions to execute, but all normal storage (RAM) is empty or contains garbage. The solution is firmware—software permanently stored in non-volatile memory that provides the initial instructions the CPU needs.
The Cold Boot Challenge:
At power-on, the system is in a primitive state:
The firmware's job is to transform this chaos into order—to perform Power-On Self-Test (POST), initialize hardware, discover boot devices, and transfer control to the operating system's bootloader.
When an x86 CPU powers on or resets, it begins executing instructions at a fixed memory address called the reset vector (0xFFFFFFF0 in 32-bit mode, near the top of the 4GB address space). The chipset maps this address to the firmware ROM, ensuring the first instruction executed is always from firmware. This hardware-enforced behavior is the root of the boot chain.
Firmware Storage:
Firmware resides in flash memory chips on the motherboard (historically, ROM or EEPROM). This non-volatile storage retains content without power, ensuring firmware is always available at boot. Modern systems also store configuration data (boot order, hardware settings) in battery-backed NVRAM or flash storage.
The firmware executes directly from this flash storage initially (Execute-In-Place or XIP), then typically copies itself to RAM for faster execution once memory is initialized. This copy-to-RAM operation is called shadowing.
The BIOS (Basic Input/Output System) originated with the IBM PC in 1981 and remained the dominant firmware architecture for over three decades. Despite its age, understanding BIOS is essential—many concepts carry forward to UEFI, legacy systems remain in production, and BIOS remains a common interview topic.
Historical Context:
The original IBM PC BIOS was designed when:
These constraints shaped BIOS into a simple, 16-bit system with severe limitations that would haunt PC architecture for decades.
| Characteristic | BIOS Implementation | Implication |
|---|---|---|
| Execution Mode | 16-bit Real Mode | Limited to 1MB addressable memory; no memory protection |
| Addressable Memory | 1MB (20-bit addresses) | Cannot directly access memory above 1MB during boot |
| Partition Table | Master Boot Record (MBR) | Maximum 4 primary partitions; 2TB disk size limit |
| Boot Code Size | 446 bytes in MBR | Extremely constrained; requires chain loading |
| Driver Model | Interrupt-based (INT 10h, INT 13h, etc.) | Simple but slow; no driver extensibility |
| User Interface | Text-mode only (typically blue screens) | No graphical capabilities; keyboard-only navigation |
| Security | None | No signature verification; easily compromised |
The BIOS Boot Sequence:
The 512-Byte Constraint:
The MBR's 512-byte limit is one of BIOS's most notorious constraints:
No meaningful bootloader fits in 446 bytes, so MBR boot code typically just loads a larger second-stage bootloader from disk—a technique called chain loading.
123456789101112131415161718192021222324252627282930313233343536
Master Boot Record (MBR) Layout - 512 Bytes============================================ Offset Size Description------ ---- -----------0x000 446 Bootstrap code (first-stage bootloader) - Must fit complex logic in 446 bytes - Typically just loads second-stage loader - Executes in 16-bit real mode 0x1BE 16 Partition Entry 10x1CE 16 Partition Entry 2 0x1DE 16 Partition Entry 30x1EE 16 Partition Entry 4 - Only 4 primary partitions possible - Extended partitions work around limit 0x1FE 2 Boot signature (0x55, 0xAA) - BIOS checks this to verify valid MBR - Missing signature = not bootable Partition Entry Structure (16 bytes each):------------------------------------------Offset Size Description0x00 1 Boot indicator (0x80 = active/bootable)0x01 3 Starting CHS address (legacy addressing)0x04 1 Partition type (0x83=Linux, 0x07=NTFS, etc.)0x05 3 Ending CHS address0x08 4 Starting LBA (sector number)0x0C 4 Total sectors in partition 2TB Limit Explanation:- LBA stored as 32-bit unsigned integer- Maximum value: 2^32 - 1 = 4,294,967,295 sectors- Sector size: 512 bytes- Maximum addressable: 4,294,967,295 × 512 = 2,199,023,255,040 bytes ≈ 2TBMBR's use of 32-bit Logical Block Addressing (LBA) creates a hard 2TB limit for disk addressing. When disk manufacturers began shipping drives larger than 2TB around 2010, this forced accelerated adoption of GPT partitioning and UEFI firmware. Systems still using MBR cannot access data beyond the 2TB boundary.
BIOS provides services to bootloaders and early operating system code through software interrupts. These interrupt handlers, installed by BIOS during initialization, offer standardized interfaces for hardware access without requiring knowledge of specific hardware details.
The Interrupt Vector Table (IVT):
In real mode, the first 1024 bytes of memory (addresses 0x0000-0x03FF) contain the Interrupt Vector Table—256 entries of 4 bytes each, where each entry holds a segment:offset pointer to an interrupt handler. BIOS populates this table with pointers to its service routines.
| Interrupt | Service Category | Common Functions |
|---|---|---|
| INT 10h | Video Services | Set video mode, write character, set cursor position, scroll window, graphics operations |
| INT 13h | Disk Services | Read/write sectors, get drive parameters, reset disk system, check drive status |
| INT 14h | Serial Port | Initialize port, send/receive characters, get port status |
| INT 15h | System Services | Get memory map, APM power management, keyboard intercept, wait functions |
| INT 16h | Keyboard Services | Read keystroke, check keyboard buffer, get shift key status |
| INT 17h | Printer Services | Print character, initialize printer, get printer status |
| INT 19h | Bootstrap Loader | Reboot system, load boot sector from boot device |
| INT 1Ah | Time Services | Get/set system time, read real-time clock |
12345678910111213141516171819202122232425262728293031323334353637383940414243
; Example: Reading sectors using BIOS INT 13h; This is how bootloaders load additional code from disk read_sectors: mov ah, 0x02 ; BIOS function: Read Sectors mov al, 5 ; Number of sectors to read mov ch, 0 ; Cylinder number (low 8 bits) mov cl, 2 ; Sector number (1-based, bits 0-5) ; Cylinder high bits in bits 6-7 mov dh, 0 ; Head number mov dl, 0x80 ; Drive number (0x80 = first hard disk) mov bx, 0x7E00 ; ES:BX = destination buffer mov es, bx xor bx, bx int 0x13 ; Call BIOS disk interrupt jc disk_error ; Carry flag set on error cmp al, 5 ; Verify sectors read count jne disk_error ret ; Modern bootloaders use INT 13h Extensions (LBA mode); Required for drives > 8GB or > 1024 cylinders read_sectors_lba: mov ah, 0x42 ; Extended Read Sectors mov dl, 0x80 ; Drive number mov si, dap ; Disk Address Packet pointer int 0x13 jc disk_error ret ; Disk Address Packet (DAP) structuredap: db 0x10 ; Size of DAP (16 bytes) db 0 ; Reserved dw 5 ; Number of sectors to read dw 0x7E00 ; Offset of buffer dw 0 ; Segment of buffer dq 1 ; Starting LBA (64-bit)Limitations of BIOS Interrupts:
While BIOS interrupts provided essential abstraction, they suffer from fundamental limitations:
16-bit Real Mode Only — BIOS services are only available in real mode. Once an OS switches to protected mode (32-bit) or long mode (64-bit), BIOS interrupts become unusable. The OS must provide its own drivers.
No Parallelism — BIOS operations are synchronous and blocking. Disk reads halt the CPU until complete—no background operations possible.
Limited Functionality — BIOS was designed for 1981-era hardware. No native support for USB, no network stack, limited graphics capabilities.
CHS vs LBA — Original BIOS used Cylinder-Head-Sector addressing, limiting access to ~8GB. Extended INT 13h added LBA support, but older BIOSes lack it.
No Extensibility — Adding new BIOS functions requires modifying firmware ROM—effectively impossible for end users.
When an operating system transitions from real mode to protected mode, it loses access to BIOS services. This is why OS kernels must include their own device drivers—they cannot rely on BIOS once running in protected or long mode. UEFI solves this by providing runtime services that remain available after OS boot.
UEFI (Unified Extensible Firmware Interface) represents a complete redesign of the firmware layer. Developed initially by Intel as EFI (Extensible Firmware Interface) for Itanium processors, it was later adopted as an industry standard through the UEFI Forum, with contributions from AMD, Apple, Dell, HP, IBM, Lenovo, Microsoft, and others.
UEFI addresses virtually every limitation of legacy BIOS while adding capabilities never possible before:
Core UEFI Improvements:
| Feature | Legacy BIOS | UEFI |
|---|---|---|
| Execution Mode | 16-bit Real Mode | 32-bit or 64-bit Protected/Long Mode |
| Addressable Memory | 1 MB | Full system memory (up to 16 EB theoretically) |
| Partition Scheme | MBR (4 partitions, 2TB limit) | GPT (128+ partitions, 9.4 ZB limit) |
| Boot Code Size | 446 bytes | Megabytes (limited only by partition size) |
| Driver Model | ROM-based, interrupt-driven | Modular EFI drivers, protocol-based |
| User Interface | Text-mode only | Full graphical UI support, mouse input |
| Network | Limited PXE only | Full network stack, HTTP boot |
| Security | None | Secure Boot, TPM integration, signed binaries |
| File System | Raw sector access | Native FAT32, extensible to others |
| Boot Method | Execute MBR code | Load EFI applications from ESP |
UEFI Boot Flow:
SEC (Security Phase) — Minimal code executes from ROM to initialize the CPU and transition out of the reset state. Establishes initial trusted environment.
PEI (Pre-EFI Initialization) — Initialize minimum hardware needed for memory. Discovers and initializes system RAM, which is critical for the next phases.
DXE (Driver Execution Environment) — The main UEFI phase. Loads and executes drivers for all system devices. Builds comprehensive system tables. Provides boot services and runtime services.
BDS (Boot Device Selection) — Evaluates boot options, presents boot menu if configured, loads the selected OS bootloader.
TSL (Transient System Load) — Brief phase where the OS bootloader runs using UEFI boot services.
RT (Runtime) — After OS calls ExitBootServices(), boot services terminate but runtime services remain available to the running OS.
UEFI boots from a special FAT32 partition called the ESP (EFI System Partition), typically mounted at /boot/efi on Linux or hidden on Windows. This partition contains EFI applications (.efi files)—including bootloaders, drivers, and utilities. Unlike MBR's raw sector approach, UEFI understands file systems natively, vastly simplifying boot management.
GPT (GUID Partition Table) is the modern partitioning scheme designed alongside UEFI to overcome MBR's severe limitations. While GPT is closely associated with UEFI, they are technically independent—GPT disks can boot via legacy BIOS (with limitations), and UEFI can theoretically boot MBR disks.
GPT Structure:
GPT uses a more robust design with redundancy:
Protective MBR (LBA 0) — A fake MBR that marks the entire disk as a single partition of type 0xEE (GPT Protective). This prevents legacy tools from misinterpreting GPT disks as empty.
Primary GPT Header (LBA 1) — Contains disk GUID, partition entry location, CRC32 checksums for integrity verification.
Partition Entry Array (LBA 2-33) — 128 partition entries of 128 bytes each. Each entry contains partition type GUID, unique partition GUID, starting/ending LBA, attributes, and name.
Partition Data — The actual partition contents.
Backup Partition Entries — Mirror of primary entries at end of disk.
Backup GPT Header — Mirror of primary header at last LBA.
12345678910111213141516171819202122232425262728293031323334353637383940414243444546
GUID Partition Table (GPT) Layout=================================== LBA 0: Protective MBR - Contains single partition entry covering entire disk - Partition type 0xEE (GPT Protective) - Prevents legacy tools from corrupting GPT disk LBA 1: GPT Header (92 bytes + padding to sector size) Offset Size Description 0x00 8 Signature ("EFI PART") 0x08 4 Revision (typically 0x00010000) 0x0C 4 Header size (usually 92) 0x10 4 CRC32 of header (with this field zeroed) 0x14 4 Reserved (zero) 0x18 8 Current LBA (location of this header) 0x20 8 Backup LBA (location of backup header) 0x28 8 First usable LBA for partitions 0x30 8 Last usable LBA for partitions 0x38 16 Disk GUID 0x48 8 Partition entry array starting LBA 0x50 4 Number of partition entries 0x54 4 Size of each partition entry (128) 0x58 4 CRC32 of partition entry array LBA 2-33: Partition Entry Array (128 entries × 128 bytes) Offset Size Description 0x00 16 Partition Type GUID 0x10 16 Unique Partition GUID 0x20 8 Starting LBA 0x28 8 Ending LBA 0x30 8 Attribute flags 0x38 72 Partition name (UTF-16LE) Common Partition Type GUIDs: EFI System: C12A7328-F81F-11D2-BA4B-00A0C93EC93B Microsoft Data: EBD0A0A2-B9E5-4433-87C0-68B6B72699C7 Linux Filesystem: 0FC63DAF-8483-4772-8E79-3D69D8477DE4 Linux Swap: 0657FD6D-A4AB-43C4-84E5-0933C84B4F4F Linux LVM: E6D6D379-F507-44C2-A23C-238F2A3DF928 Disk Capacity: - LBA addresses are 64-bit - Maximum addressable: 2^64 sectors - With 512-byte sectors: 9.4 ZB (zettabytes) - With 4096-byte sectors: 75.6 ZBWhile GPT disks can boot on legacy BIOS systems, it requires a BIOS Boot Partition—a small (1MB) unformatted partition with a special type GUID. GRUB stores its core image here. This hybrid approach works, but UEFI native boot provides a cleaner, more reliable solution.
UEFI includes a built-in boot manager that provides sophisticated boot option management without requiring a separate bootloader. This represents a fundamental shift from the BIOS model.
Boot Variables:
UEFI stores boot configuration in NVRAM variables—persistent storage that survives reboots. Key variables include:
| Variable Name | Purpose | Example Value |
|---|---|---|
| BootOrder | Ordered list of boot option numbers to try | 0001,0002,0003,0000 |
| Boot0000, Boot0001, ... | Individual boot option definitions | Ubuntu: HD(1,GPT,...)\EFI\ubuntu\shimx64.efi |
| BootCurrent | Boot option used for current boot | 0001 |
| BootNext | One-time override for next boot only | 0003 |
| Timeout | Seconds to wait before auto-booting | 5 |
123456789101112131415161718192021222324252627282930313233
# Linux efibootmgr - Manage UEFI boot entries # List all boot entries with details$ efibootmgr -vBootCurrent: 0001Timeout: 5 secondsBootOrder: 0001,0002,0003,0000Boot0000* Windows Boot Manager HD(1,GPT,a1b2c3d4-...)/File(\EFI\Microsoft\Boot\bootmgfw.efi)Boot0001* Ubuntu HD(1,GPT,a1b2c3d4-...)/File(\EFI\ubuntu\shimx64.efi)Boot0002* UEFI: USB Drive PciRoot(0x0)/Pci(0x14,0x0)/USB(2,0)Boot0003* UEFI: Network PciRoot(0x0)/Pci(0x1c,0x0)/Pci(0x0,0x0)/MAC(...) # Create new boot entry$ efibootmgr -c -d /dev/sda -p 1 -L "Custom Linux" -l "\EFI\custom\vmlinuz.efi" # Change boot order (try Ubuntu first, then Windows)$ efibootmgr -o 0001,0000 # Set one-time boot to USB drive$ efibootmgr -n 0002 # Delete a boot entry$ efibootmgr -b 0003 -B # Set timeout to 10 seconds$ efibootmgr -t 10 # Boot entry path components explained:# HD(1,GPT,uuid...) - Hard disk partition 1, GPT, with specific GUID# File(\EFI\...\.efi) - Path to EFI application (backslashes, Windows-style)# PciRoot(0x0) - PCI root bus# Pci(0x14,0x0) - PCI device at slot 0x14, function 0# USB(2,0) - USB port 2EFI System Partition Structure:
The ESP contains a standardized directory structure:
/boot/efi/ (ESP mount point on Linux)
├── EFI/
│ ├── BOOT/
│ │ └── BOOTX64.EFI (Fallback bootloader path)
│ ├── Microsoft/
│ │ └── Boot/
│ │ └── bootmgfw.efi (Windows Boot Manager)
│ ├── ubuntu/
│ │ ├── shimx64.efi (Signed shim for Secure Boot)
│ │ ├── grubx64.efi (GRUB bootloader)
│ │ └── grub.cfg (GRUB configuration)
│ └── systemd/
│ └── systemd-bootx64.efi (systemd-boot loader)
The fallback path \EFI\BOOT\BOOTX64.EFI (or BOOTIA32.EFI for 32-bit, BOOTAA64.EFI for ARM64) is used when no explicit boot entries exist—such as when booting from USB drives or newly installed systems.
Linux kernels can be compiled as EFI applications (CONFIG_EFI_STUB), allowing UEFI to boot them directly without an intermediate bootloader like GRUB. This EFI Stub feature simplifies boot chains and is increasingly used with systemd-boot's approach of directly loading kernel images from the ESP.
Secure Boot is a UEFI feature that prevents execution of unauthorized code during boot. By cryptographically verifying every component in the boot chain, Secure Boot defends against bootkits and rootkits—malware that loads before the operating system and can hide from OS-level security tools.
The Trust Problem:
Legacy BIOS had no mechanism to verify that boot code was legitimate. Malware could:
Secure Boot addresses this by establishing a chain of trust rooted in firmware.
The Microsoft Key Problem:
Most consumer hardware ships with Microsoft's keys pre-installed because Windows requires Secure Boot certification. This creates a practical reality:
Most major distributions use the shim approach. The shim is signed by Microsoft and contains the distribution's own certificate, allowing it to verify and load the distribution's GRUB.
Secure Boot has been controversial in Linux communities. Critics argue it gives Microsoft control over what operating systems can boot on PC hardware. Defenders note that Secure Boot can be disabled on non-ARM Windows hardware, and that the shim mechanism provides a practical path for Linux distributions. ARM devices with Windows (like Surface Pro X) cannot disable Secure Boot, making Linux installation impossible on factory firmware.
1234567891011121314151617181920212223242526
# Check Secure Boot status on Linux$ mokutil --sb-stateSecureBoot enabled # List enrolled keys$ mokutil --list-enrolled # Import Machine Owner Key (MOK) for custom modules$ mokutil --import my_signing_key.der# System will prompt for password, then require reboot# During boot, MOK manager will ask to enroll the key # Sign a kernel module for Secure Boot$ /usr/src/linux-headers-$(uname -r)/scripts/sign-file sha256 \ ./my_signing_key.priv \ ./my_signing_key.der \ my_module.ko # Check if running in Secure Boot mode (alternative method)$ cat /sys/firmware/efi/efivars/SecureBoot-*# Non-zero value = Secure Boot enabled # Verify an EFI binary's signature$ sbverify --list /boot/efi/EFI/ubuntu/shimx64.efisignature 1image signature isance: CN=Microsoft Windows UEFI Driver PublisherUnlike BIOS, which becomes completely inaccessible after the OS takes over, UEFI provides runtime services that remain available throughout system operation. This enables the OS to interact with firmware for specific tasks without requiring direct hardware access.
Boot Services vs Runtime Services:
Boot Services — Available only during boot, before the OS calls ExitBootServices(). Include memory allocation, driver loading, protocol management. Terminated when OS takes control.
Runtime Services — Survive ExitBootServices() and remain available to the running OS. Provide controlled firmware access for specific functions.
| Service Category | Functions | Use Case |
|---|---|---|
| Time Services | GetTime, SetTime, GetWakeupTime, SetWakeupTime | Read/write hardware RTC, schedule wake events |
| Variable Services | GetVariable, SetVariable, GetNextVariableName | Access/modify NVRAM variables (boot entries, etc.) |
| Virtual Memory | SetVirtualAddressMap, ConvertPointer | Remap runtime service addresses for OS virtual memory |
| Reset Services | ResetSystem | Reboot, shutdown, or enter S4 sleep state |
| Capsule Services | UpdateCapsule, QueryCapsuleCapabilities | Firmware updates via OS-delivered capsules |
| Miscellaneous | GetNextHighMonotonicCount | Monotonic counter for cryptographic applications |
Variable Services in Practice:
The variable services are particularly important—they provide the mechanism for the OS to modify boot options, query firmware settings, and receive hardware information.
On Linux, UEFI variables are exposed via the efivarfs filesystem mounted at /sys/firmware/efi/efivars/. Each variable appears as a file whose name encodes the variable name and GUID.
123456789101112131415161718192021222324252627282930
# UEFI variables on Linux (efivarfs) # List all variables$ ls /sys/firmware/efi/efivars/Boot0000-8be4df61-93ca-11d2-aa0d-00e098032b8cBoot0001-8be4df61-93ca-11d2-aa0d-00e098032b8cBootCurrent-8be4df61-93ca-11d2-aa0d-00e098032b8cBootOrder-8be4df61-93ca-11d2-aa0d-00e098032b8cSecureBoot-8be4df61-93ca-11d2-aa0d-00e098032b8c... # Variable naming format: <VariableName>-<VendorGUID># Common GUID: 8be4df61-93ca-11d2-aa0d-00e098032b8c (EFI Global Variable) # Read SecureBoot state (first 4 bytes are attributes)$ xxd /sys/firmware/efi/efivars/SecureBoot-8be4df61-93ca-11d2-aa0d-00e098032b8c00000000: 0600 0000 01 ..... # Attributes (bytes 0-3): 0x00000006 = BOOTSERVICE_ACCESS | RUNTIME_ACCESS# Data (byte 4): 0x01 = Secure Boot enabled # WARNING: Writing to efivars can brick your system!# Files are immutable by default to prevent accidents$ chattr -i /sys/firmware/efi/efivars/SomeVar-... # Remove immutable flag# ... make changes ... # Safer approach: use efivar tool$ efivar -l # List variables$ efivar -p -n <guid>-<name> # Print variable contents$ efivar -w -n <guid>-<name> -f file # Write from fileIn 2013, some Samsung laptops became permanently bricked when running Linux if too many UEFI variables were written. The firmware stored variables in a fixed-size flash region, and overflow corrupted critical firmware code. This infamous bug highlights why kernel developers treat efivarfs with extreme caution—adding rate limiting and sanity checks to prevent runaway variable creation.
The transition from BIOS to UEFI didn't happen overnight. For many years, systems included the Compatibility Support Module (CSM)—a UEFI component that emulates legacy BIOS, allowing old operating systems and bootloaders to function on UEFI hardware.
CSM Operation:
The End of CSM:
Intel announced in 2017 that CSM support would be removed from UEFI by 2020. While this deadline slipped, the industry is firmly moving away from CSM:
Dual-Boot Considerations:
Systems dual-booting multiple OSes must ensure consistency:
On Linux, check if booted in UEFI or BIOS mode: if /sys/firmware/efi exists, you booted via UEFI. On Windows, run msinfo32 and check 'BIOS Mode'—it will show 'UEFI' or 'Legacy'. In the installer, press Shift+F10 for cmd and run echo %firmware_type%.
We've examined the firmware layer that transforms powered-off hardware into a system ready to boot an operating system. This foundation is invisible to most users but essential for every computation that follows.
What's Next:
The firmware hands control to a bootloader—an intermediate program that understands operating system requirements, allows boot option selection, and properly loads the kernel. In the next page, we'll examine GRUB, the most common Linux bootloader, and explore how bootloaders bridge the gap between firmware and operating system.
You now understand the firmware layer: BIOS's historical constraints, UEFI's modern architecture, partition schemes, Secure Boot, and the runtime services that persist into the running OS. This firmware foundation makes everything that follows possible—the bootloader, the kernel, and ultimately the user-space applications.