When an Arm v8-based system powers on, it goes through a complex boot process to load and initialize the operating system. This boot process involves multiple stages, from low-level firmware executing to load the kernel, to starting init processes that fully initialize userspace. Understanding the full Arm v8 boot flow provides insight into the boot architecture and timing.
The Arm v8 boot process can be divided into four high-level stages:
- System Boot Stage – Power on, perform basic SoC initialization
- Firmware Boot Stage – Initialize hardware, load bootloader
- Bootloader Stage – Locate, load, and transfer control to the kernel
- Kernel Initialization Stage – Mount filesystems, start init process, begin scheduling applications
Within these stages are more detailed steps that progressively initialize more complex hardware and software components.
1. System Boot Stage
When power is first applied to an Arm v8 system, the system boot stage begins. This involves the initial SoC bring up and initialization:
- Power on reset sequence
- Execute boot ROM code
- Initialize CPU and memory
- Configure clock circuitry and I/O pins
- Initialize on-chip RAM and any boot devices
These low-level actions are governed by proprietary code in the SoC boot ROM. This establishes a basic hardware environment sufficient to load and run more complex firmware.
2. Firmware Boot Stage
After the system boot stage, firmware boot routines take over:
- Load system firmware (e.g. UEFI)
- Perform hardware initialization – buses, peripherals
- Initialize DRAM and memory controllers
- Verify/initialize persistent storage
- Load bootloader to RAM
Arm development boards like the Juno board utilize UEFI as firmware. UEFI initializes hardware, drives device detection, and contains services for loading images from storage. The end goal is to load a bootloader like U-Boot into memory and transfer control to it.
3. Bootloader Stage
With the bootloader loaded into RAM, it takes over to handle OS booting duties:
- Parse boot configuration and parameters
- Initialize networking and drivers
- Locate kernel image on boot device
- Load kernel image into RAM
- Parse kernel image format to find entry point
- Decompress kernel if needed
- Transfer control to kernel entry point
The bootloader stage handles key tasks like locating the kernel image, loading it into the right memory region, and setting up any kernel arguments before jumping into it.
4. Kernel Initialization
With the uncompressed kernel image loaded into RAM and control passed to its entry point, the lengthy kernel initialization can begin:
- Set up kernel stack and heap memory regions
- Parse kernel image format
- Initialize kernel subsystems
- Mount root filesystem
- Start init process (systemd)
- Begin scheduling other userspace processes
The kernel handles critical mounting of disks and starting of the userspace init daemon. After init starts, normal system services and eventually applications can begin.
A typical Arm v8 system goes through the four boot stages in a matter of seconds. Measuring boot timing provides insight into performance:
- System Boot Stage – 10s of milliseconds
- Firmware Boot Stage – 100s of milliseconds
- Bootloader Stage – 100s of milliseconds
- Kernel Initialization Stage – 1-5 seconds
The system boot stage is quite fast since it involves only basic SoC initialization. The firmware and bootloader stages take longer since they initialize hardware and load images from storage. Finally, kernel initialization takes several seconds depending on hardware detected and services started.
Optimization opportunities exist in the firmware, bootloader, and kernel stages to improve boot speed. For example, transitioning to a faster bootloader like LK can dramatically speed up the bootloader phase.
While the four boot stages are generally standard across Arm v8 systems, there are many implementation details that can vary:
- Boot ROM – Proprietary per SoC
- Firmware – UEFI, U-Boot SPL, or custom
- Bootloader – U-Boot, UEFI, LK, or custom
- Kernel – Linux, FreeBSD, Android, or other
- Filesystems – ext4, F2FS, initramfs, etc
Boot firmware, bootloader, kernel, and other boot components can be customized. This allows tuning boot timing, capabilities, security policies, and other attributes.
For example, Android systems combine a bootloader and kernel into a single Android boot image. This streamlines boot but provides less flexibility.
As the Arm v8 boot process advances through progressive stages, security capabilities also increase:
- Boot ROM – Permanently immutable code
- Firmware – Read-only updatable code
- Bootloader – Read-only or mutable code with verification
- Kernel – Enforces process isolation and permissions
Secure boot capabilities in firmware and bootloaders verify that the boot chain loads only authentic software. ARM TrustZone also supports trusted execution environments isolated from normal software.
These capabilities prevent attackers from modifying boot components or injecting malicious code during boot. Securely booting to an immutable root of trust is critical for security.
Debugging the Boot Sequence
When bringing up new Arm v8 hardware, debugging boot issues is often needed. Common techniques include:
- Print debug output – Many boot components print logs if enabled
- Use a debug UART – Low level console that works before OS
- Debug registers and exceptions in bootloader
- Kernel panics on failures
- Analyze boot timing – Find slow components
Debug UART consoles allow interactive debugging and inspection. The OS provides many tools for analyzing crashes and issues during later boot stages.
The Arm v8 boot process stages progressively initialize hardware and software components vital to starting an OS. Understanding this boot flow is key to successfully bringing up and debugging Arm v8 platforms. While the four high-level stages are standardized, many implementation details can vary and customize the boot process.