Embedded Linux Essentials
intermediate
Weight: 4/10

Linux boot process

Understand the embedded Linux boot sequence from power-on through U-Boot, kernel initialization, initramfs, and rootfs mounting to the first userspace process.

embedded-linux
boot
u-boot
kernel
initramfs
rootfs
bootloader

Quick Cap

"Walk me through the boot process" is one of THE most common embedded Linux interview questions. The answer is a four-stage chain: ROM bootloader loads U-Boot SPL, which loads full U-Boot, which loads the kernel + device tree blob (DTB), and the kernel mounts the root filesystem and starts PID 1. Each stage has specific responsibilities and failure modes that interviewers probe.

Key Facts:

  • Boot chain: ROM code, then U-Boot SPL, then U-Boot, then kernel, then init (PID 1)
  • U-Boot loads three things: kernel image (zImage/Image), device tree blob (DTB), and optionally initramfs
  • Kernel command line (bootargs) controls root device, console, init, and debug options
  • initramfs is an in-memory rootfs for early setup before the real rootfs is available
  • "Kernel panic: VFS: Unable to mount root fs" is the most common boot failure — wrong root= or missing filesystem driver
  • Boot time optimization can reduce cold boot from 30s to under 2s

Deep Dive

At a Glance

StageWho Provides ItWhat It DoesTypical Time
ROM bootloaderSoC vendor (burned into chip)Loads SPL from Flash/SD/eMMC0.1-0.5s
U-Boot SPLOpen-source (U-Boot project)Initializes DRAM, loads full U-Boot0.2-1s
U-BootOpen-source (U-Boot project)Loads kernel + DTB + initramfs1-3s
Linux kernelOpen-source (kernel.org)Decompresses, initializes subsystems, mounts rootfs1-10s
init (PID 1)Distro/build systemStarts services, reaches application ready1-15s

Boot Sequence Overview

px-2 py-1 rounded text-sm font-mono border
Power On
|
v
ROM Bootloader (SoC internal, loads SPL from Flash/SD/eMMC)
|
v
U-Boot SPL (initializes DRAM, loads full U-Boot)
|
v
U-Boot (loads kernel + DTB + optional initramfs from storage/network)
|
v
Linux Kernel (decompresses, initializes subsystems, mounts rootfs)
|
v
init (PID 1) -- systemd, BusyBox init, or SysVinit
|
v
Application ready

Each stage trusts the previous one to have set up the environment correctly. The ROM bootloader has no user-configurable code — it is mask-programmed into the SoC and defines which boot media are checked (SD, eMMC, SPI NOR, USB) and in what order. The boot media order is typically set by hardware pin strapping or eFuses.

U-Boot: More Than Just a Bootloader

U-Boot (Das U-Boot) is the standard bootloader for embedded Linux. It is a full development environment with a command shell, scripting, network stack, filesystem drivers, and environment variable persistence.

What U-Boot does:

  1. Initializes DRAM (SPL stage) and peripheral clocks
  2. Provides a command-line interface over serial console
  3. Reads kernel, DTB, and initramfs from storage (eMMC, SD, NOR, NAND) or network (TFTP)
  4. Passes the kernel command line and DTB address to the kernel
  5. Transfers control to the kernel entry point

Key environment variables:

VariablePurposeExample
bootcmdAuto-boot command sequenceload mmc 0:1 ${loadaddr} zImage; bootz ${loadaddr} - ${fdt_addr}
bootargsKernel command lineconsole=ttyS0,115200 root=/dev/mmcblk0p2 rootwait
bootdelaySeconds to wait before auto-boot3 (set to 0 for production)
loadaddrAddress to load kernel image0x42000000
fdt_addrAddress to load device tree blob0x43000000

Boot commands:

CommandImage FormatArchitecture
bootzzImage (compressed)32-bit ARM
bootiImage (uncompressed or gzip)64-bit ARM (AArch64)
bootmuImage (U-Boot legacy wrapper)Any (legacy format)

Network boot for development: During development, TFTP boot avoids reflashing for every kernel change. U-Boot fetches the kernel and DTB over Ethernet, and the kernel mounts the rootfs via NFS. This gives a desktop-like edit-compile-test cycle on embedded hardware.

💡Interview Insight

When asked about U-Boot, do not say "it is just a bootloader." Explain that it is a full development environment with network boot, scripting, environment variables, and storage drivers. This shows you have actually worked with it, not just read about it.

Kernel Command Line (bootargs)

The kernel command line is a string of key-value parameters that control how the kernel behaves. It is passed via the bootargs environment variable in U-Boot or embedded in the device tree blob's /chosen node.

Critical parameters:

ParameterPurposeExample
console=Serial console device and baud rateconsole=ttyS0,115200
root=Root filesystem deviceroot=/dev/mmcblk0p2
rootfstype=Filesystem type of root devicerootfstype=ext4
rootwaitWait for root device to appear (essential for slow-probing storage)rootwait (no value)
init=Path to init programinit=/sbin/init
quietSuppress kernel messages (faster boot)quiet (no value)
loglevel=Kernel log verbosity (0-7)loglevel=3

A typical production bootargs string looks like:

px-2 py-1 rounded text-sm font-mono border
console=ttyS0,115200 root=/dev/mmcblk0p2 rootfstype=ext4 rootwait quiet loglevel=3

The kernel parses this string during early boot. Unknown parameters are passed to init as arguments or set as environment variables. You can inspect the active command line at runtime via /proc/cmdline.

⚠️Common Trap: Missing rootwait

Without rootwait, the kernel may attempt to mount the root filesystem before the storage controller has finished probing. This causes the dreaded "VFS: Unable to mount root fs" panic even though the device and filesystem are perfectly valid. Always include rootwait when booting from SD, eMMC, or USB.

initramfs vs initrd

Both provide an early root filesystem that the kernel can use before the real rootfs is available, but they work differently:

Characteristicinitramfsinitrd
Formatcpio archive (optionally compressed)Block device image (ext2/ext4)
Kernel handlingExtracted into tmpfs (ramfs)Mounted as a ramdisk block device
Memory modelPages freed as files are deletedFixed-size ramdisk allocated at boot
Preferred?Yes — simpler, more flexibleLegacy — rarely used in new designs
Built into kernel?Can be linked directly into the kernel imageAlways a separate file

When you need initramfs:

  • Storage drivers are compiled as modules (not built-in) and must be loaded before the rootfs device is accessible
  • The rootfs is on an encrypted partition that must be unlocked before mounting
  • A/B partition selection logic must run before mounting the real root
  • Complex network-based rootfs setup (iSCSI, NBD)

When you can skip it:

  • All storage drivers are built into the kernel (not modules)
  • The rootfs is on a simple block device (eMMC, SD) that the kernel can mount directly
  • No pre-mount setup is required

In practice, many embedded systems skip initramfs entirely — the kernel is configured with built-in eMMC/SD drivers and mounts the rootfs directly. This saves boot time and reduces complexity.

Rootfs Mounting Strategies

Strategybootargs ConfigurationUse Case
Block device rootroot=/dev/mmcblk0p2 rootfstype=ext4 rootwaitStandard production setup
NFS root (development)root=/dev/nfs nfsroot=192.168.1.1:/path ip=dhcpDevelopment — edit files on host, test on target instantly
Read-only root with overlayroot=/dev/mmcblk0p2 ro + overlayfs in init scriptsProduction — squashfs base + writable overlay for config
A/B partitioningBootloader selects partition based on boot count or last-known-good flagOTA updates — boot from A, update B, swap on next boot

The read-only root with overlayfs pattern is the most robust for production: the base image is immutable (verified, compressed), modifications go to a separate writable partition, and factory reset is just wiping the overlay. A/B partitioning adds another layer of safety — if an OTA update fails, the bootloader falls back to the known-good partition.

Boot Time Optimization

Boot time is critical in automotive (functional safety requires fast startup), industrial (minimize downtime), and consumer (user experience). A naive embedded Linux boot can take 30+ seconds; an optimized one can reach application-ready in under 2 seconds.

TechniqueTypical SavingEffort
Kernel pruning (remove unused drivers)2-5sLow
quiet boot (suppress kernel messages)0.5-1sTrivial
Deferred module loading1-3sMedium
systemd-analyze blame (find slow services)1-5sLow
Application preloading (start before all services)2-5sMedium
Kernel XIP (execute-in-place from Flash)1-2sHigh
U-Boot Falcon mode (skip full U-Boot, SPL loads kernel directly)1-2sHigh

The low-hanging fruit is always kernel pruning and quiet boot. Run systemd-analyze blame to find the slowest services, then decide which can be deferred or removed. Falcon mode eliminates the full U-Boot stage entirely — the SPL loads the kernel directly, shaving 1-2 seconds but sacrificing the interactive U-Boot environment (no more TFTP boot or environment editing without reflashing).

Debugging Story: The Missing Block Device

A team building a custom SBC received "Kernel panic: VFS: Unable to mount root fs on unknown-block(0,0)" on every boot. The rootfs was correctly written to eMMC partition 2, bootargs had root=/dev/mmcblk0p2 rootfstype=ext4, and the same rootfs image worked fine on a reference board.

The clue was in unknown-block(0,0) — major=0, minor=0 means the kernel could not find ANY block device matching the root= parameter. The team checked the kernel config and found that the eMMC/SDHCI host controller driver was set to =m (module) instead of =y (built-in). The kernel could not probe the eMMC controller, so /dev/mmcblk0p2 never appeared.

The fix: change CONFIG_MMC_SDHCI=y and CONFIG_MMC_SDHCI_OF_ARASAN=y (for their specific SoC) in the kernel config and rebuild. Alternatively, they could have used an initramfs to load the module before mounting root.

The lesson: Always ensure the kernel has built-in (not module) support for the storage controller that holds the rootfs — unless you use an initramfs to load the module first. When you see unknown-block(0,0), it means the kernel has zero visibility of the storage device.

What Interviewers Want to Hear

  • You can walk through the full boot chain without hesitation: ROM, SPL, U-Boot, kernel, init
  • You understand that U-Boot is more than a bootloader — it is a development environment with network boot, scripting, and persistent environment variables
  • You know the critical kernel command line parameters and what happens when each is missing
  • You can explain when initramfs is necessary vs when it adds unnecessary complexity
  • You can debug the most common boot failure (VFS mount panic) by checking kernel config and bootargs
  • You have practical experience with boot time optimization and can name specific techniques with their tradeoffs

Interview Focus

Classic Interview Questions

Q1: "Walk me through the embedded Linux boot process from power-on to application ready."

Model Answer Starter: "The boot process is a chain of trust with four main stages. First, the ROM bootloader — code burned into the SoC at the factory — reads boot media configuration from pin strapping or eFuses, finds the first-stage bootloader (U-Boot SPL) on Flash, SD, or eMMC, and loads it into internal SRAM. Second, U-Boot SPL initializes external DRAM (which was not available until now) and loads the full U-Boot into DRAM. Third, full U-Boot provides a rich environment — it reads its environment variables, loads the kernel image, device tree blob, and optionally an initramfs from storage or network, sets up the kernel command line via bootargs, and jumps to the kernel entry point. Fourth, the kernel decompresses itself, initializes CPU, memory management, and device subsystems using the DTB, mounts the root filesystem as specified by the root= parameter, and executes /sbin/init as PID 1. From there, the init system (systemd, BusyBox init, or SysVinit) starts services until the application is ready."

Q2: "What is U-Boot and what does it do?"

Model Answer Starter: "U-Boot is the de facto standard bootloader for embedded Linux. It is much more than a simple loader — it is a full development environment. In the SPL stage, it initializes DRAM. In the full stage, it provides a command shell over serial console, supports loading files from eMMC, SD, NAND, NOR Flash, USB, and network (TFTP). Its persistent environment variables (bootcmd, bootargs, bootdelay, loadaddr) control the boot sequence and kernel parameters. During development, U-Boot enables TFTP kernel loading and NFS root for rapid iteration. For production, bootdelay is set to 0, and Falcon mode can skip the full U-Boot stage entirely to save 1-2 seconds of boot time."

Q3: "What are the most important kernel command line parameters?"

Model Answer Starter: "The critical parameters are: console= which sets the serial console device and baud rate for kernel messages and login, root= which tells the kernel where the root filesystem is located, rootfstype= which specifies the filesystem type so the kernel does not need to auto-detect, rootwait which tells the kernel to wait for the root device to appear rather than panicking immediately (essential for eMMC/SD which need time to probe), and init= which overrides the default init path. For production, quiet and loglevel=3 suppress kernel messages and can save 0.5-1 second of boot time. For debugging, removing quiet and setting loglevel=7 reveals all kernel output."

Q4: "When would you use an initramfs?"

Model Answer Starter: "An initramfs is needed when the kernel cannot directly access the root filesystem at boot time. The three main scenarios are: first, when the storage controller driver is compiled as a module rather than built-in — the initramfs loads the module so the kernel can then see the block device. Second, when the rootfs is encrypted — the initramfs runs the decryption setup before mounting. Third, for A/B partition selection — the initramfs contains logic to choose the correct root partition based on boot counters or flags. For simple embedded systems where all storage drivers are built-in and the rootfs is on a plain block device, you can skip initramfs entirely, which saves boot time and reduces complexity."

Q5: "How would you optimize boot time from 30 seconds to under 5 seconds?"

Model Answer Starter: "I would attack it in layers. First, the low-hanging fruit: add quiet to bootargs to suppress kernel messages (saves 0.5-1s), and run systemd-analyze blame to identify the slowest services — often network-wait and logging services can be deferred. Second, kernel pruning: run lsmod on the target to find which modules are actually needed, then rebuild the kernel with only those drivers built-in — this can save 2-5 seconds of module probing. Third, optimize the init sequence: start the main application early (before all services are up) and defer non-critical initialization. Fourth, if more speed is needed, consider U-Boot Falcon mode (SPL loads the kernel directly, skipping the full U-Boot stage) for another 1-2 seconds. The exact approach depends on where the time is spent — always measure first with grabserial or bootchart before optimizing."

Trap Alerts

  • Don't say "U-Boot is just a bootloader" — it is a full development environment (network boot, scripting, environment variables, filesystem drivers)
  • Don't forget rootwait — without it, fast-booting kernels may panic because the storage device has not finished probing
  • Don't ignore the DTB — the kernel needs the device tree blob to know what hardware exists; without it, no platform devices probe and the system hangs silently

Follow-up Questions

  • "What is U-Boot Falcon mode and what are the tradeoffs?"
  • "How does the kernel know which device tree blob to use on a board with multiple hardware revisions?"
  • "What happens if you set init=/bin/sh in bootargs? When is that useful?"
  • "How would you implement A/B partition switching in U-Boot?"
  • "What is the difference between zImage, Image, and uImage?"

Practice

What does 'unknown-block(0,0)' mean in a kernel panic about root filesystem mounting?

In the embedded Linux boot chain, what is the primary job of U-Boot SPL?

Why is the 'rootwait' kernel parameter critical for eMMC and SD card boot?

When can you safely skip using an initramfs in an embedded Linux system?

Which U-Boot boot command is used for 64-bit ARM (AArch64) kernel images?

Real-World Tie-In

Industrial Gateway Boot Optimization — A factory automation gateway running on a Cortex-A7 with 256 MB RAM needed to reach its MQTT broker connection within 4 seconds of power-on. The team started at 28 seconds. They pruned the kernel from 2800 to 400 config options (saving 5s), switched from systemd to BusyBox init with a minimal rcS script (saving 8s), built all storage drivers as built-in to eliminate initramfs (saving 1s), added quiet and loglevel=0 (saving 1s), and started the MQTT application directly from inittab before network services finished initializing. Final boot: 3.2 seconds to first MQTT publish.

Automotive Fast-Boot with Rearview Camera — An automotive rearview camera system required video display within 2 seconds of ignition (regulatory requirement in some markets). The team used U-Boot Falcon mode (SPL loads kernel directly, skipping full U-Boot), a kernel stripped to only video, display, and camera drivers, and a minimal initramfs that started the camera application as PID 1 — no init system at all. The camera feed appeared in 1.6 seconds. Full Linux with connectivity services continued booting in the background via a switch_root to the real rootfs after the camera was active.