| Episodes | 53 |
| Mentions | 83 |
| Cited here | 35 |
| First — last | #113 — #724 |
| Top guests | Colin O'Flynn, Elecia White, Trey German |
| Related | firmware update · firmware · bluetooth · flash memory · sd card |
A bootloader is a small program resident in a device’s program storage that runs first at power-up, checks the application image for validity, and transfers execution to it when the image is good.[212] Its primary purpose is to allow the application firmware to be replaced in the field: updating requires a route for the running application to re-enter the bootloader, after which a replacement image arrives over whatever communication channel the product already has — a vehicle bus, USB, or a plain serial link.[212] The mechanism exists because the alternative is physical access: a microcontroller buried in a product cannot be removed, opened, and reprogrammed through its debug header when a defect is found.[212]
Purpose and operation
Without a bootloader, loading code requires access to the debug pins and a hardware programmer, which is precisely the alternative the bootloader exists to avoid.[589] The bootloader therefore occupies a privileged position in a product’s architecture: it is the component that must survive every update, and everything needed for recovery must live inside its protected region — placing the radio routines there, for example, is what allows a device with a completely broken application to be recovered wirelessly.[516]
A bootloader can decide what to do from its environment. One board’s bootloader stays resident and waits for a command when it detects a connected host, and automatically boots the previously loaded user configuration when it finds only a power supply.[395] Keeping the bootloader in a separate serial flash preserves a recovery path, so a hard reset can always bring the device back up even after a user configuration has replaced it in the running device.[395] Booting the entire firmware from a removable card is a stronger variant of the same idea: a bad image is replaced by swapping the card rather than by recovering the device, eliminating the bricking risk.[325]
Larger systems
On a system with memory management, the bootloader’s task is to read the kernel image from storage into memory at some address and set the program counter to it, after which the kernel takes over.[515] Porting such a system to a new board requires a bootloader that knows how to load the kernel for that architecture before any of the kernel’s hardware abstraction becomes useful.[378] Because the bootloader is part of the storage image rather than the kernel, supporting a variant board that needs a different bootloader means patching the disc image rather than booting the same card unmodified.[378]
Design principles
The governing principle of bootloader design is simplicity: the bootloader should be kept as simple as it can possibly be, because every additional capability is another way for the one irrecoverable component to fail.[364] The standard to aim at is that nothing can go wrong with the bootloader once the product has shipped and that it remains compatible with every future release.[364]
On one instrument, that principle produced a bootloader containing only four things: code to read the memory card, the display routines, simple button reading for the key combination that triggers an update, and nothing else.[364] Even a well-scoped bootloader can need revision once — on that product, a display contrast fault caused by incorrectly set registers had to be fixed in the bootloader because the display code lived there.[364]
The reason simplicity matters is that changing the bootloader on a shipped product is the dangerous operation: it requires the vendor’s development kit and an internal fine-pitch header, and a failed write bricks the unit outright.[364] For the same reason, on Mike Harrison’s production practice the bootloader is written first and then used as the means of loading code throughout development, so that by the time the product ships it has been exercised thousands of times and is known to be sound.[294] The reason to sequence it that way is that the bootloader is the one component that has to be beyond doubt, and writing it last leaves it the least tested part of the product.[294]
Memory layout
Microcontrollers commonly provide a separate, segregated, and protected region of program memory for the bootloader, which is limited in size — on the order of a couple of kilobytes — and cannot simply be extended when more is wanted.[516] On parts with a vendor radio stack, that stack occupies a reserved block at the bottom of the address space while the bootloader sits at the very top, with both required to be protected memory.[516] The limited space shapes product features directly: one intended design in which stored data would be transferred wirelessly was abandoned because the storage access code would have had to live in the bootloader and there was no room for it.[690] At the extreme of cost, a ten-cent processor provides roughly nineteen hundred bytes of dedicated boot area alongside sixteen kilobytes of flash, and fitting a USB stack into that area makes the part programmable over USB with no external programmer.[637]
Update mechanisms
A robust update scheme makes the transfer atomic. Placing the received image in an external flash before committing it achieves this: the bootloader looks for a complete image and does nothing if the transfer broke off partway, which matters because a wireless transfer can be interrupted at any point.[398] The sequence in that arrangement is that the running application receives the image into external storage and reboots, after which the bootloader detects a new image, erases the old application, writes the new one, and jumps to it.[398] A comparable design derived its bootloader from an existing open-source one, expanded the reserved bootloader region on a part chosen for having space, and taught the bootloader to boot from an external serial flash into which the application downloads a hex file that the bootloader parses directly.[250]
Compactness can dictate the protocol itself. One minimal loader avoids acknowledgements entirely by determining the baud rate from a preamble and relying on the sender to insert padding bytes calculated so that the flash write has completed before the next frame arrives, making the transfer a strictly one-way stream.[697] Because such a loader needs only a single wire, pairing it with an infrared receiver turns it into a wireless update path on a part with no radio at all.[697] Firmware update over USB likewise requires at least a bootloader resident on the microcontroller, which is part of why a plain serial interface is simpler for a product that only needs to move console data.[551] On contemporary modules, the standard circuit for entering bootloader mode uses a pair of cross-wired transistors driven from the serial adapter to assert the boot pin and then reset the part automatically.[702]
Implementing the loading interface in the device itself, rather than adding a dedicated programming chip, removed cost from one board design, making the finished product substantially cheaper for the buyer.[395]
Manufacturing and provisioning
Microcontroller parts can be bought pre-programmed from the manufacturer against a minimum order of about a hundred and fifty pieces, adding roughly two days of setup and two days to the order lead time — worthwhile at volumes where programming thousands of leadless packages on the board would be impractical.[224] Loading only a bootloader at that stage is sufficient, since the application can then be written while the parts are in transit and blown down the serial bus in production.[224] Production programming can also be delegated cheaply by handing a subcontractor an inexpensive programmer with the code preloaded, reducing the operation to connecting it, pressing a button, and waiting for a green light.[224]
High-volume consumer production tends toward pre-programmed parts because there may be no room on the board for a programming header, which forces the firmware — or at least the bootloader — to be ready earlier than a low-volume product would require.[363] Even where every subsequent release arrives over the air, the device has to be programmed once physically, and that first programming step is the critical one.[544] Shipping a first-generation product before the software is finished means the only thing that can be loaded at the factory is the bootloader, with everything else arriving later.[256] The pattern of a factory-programmed bootloader loadable through an ordinary serial port, with no dedicated programmer, is what made early accessible microcontroller boards possible and remains the pattern used today.[570]
A proprietary bootloader can also be the commercial mechanism by which a module vendor protects its work, so designing that module into a product means buying the board or a separately sold pre-programmed part rather than the bare processor.[713] In one such case the restriction followed from a non-disclosure agreement covering the chip’s security features, which is also what made a lockable variant offering encrypted firmware and a secure update process possible at all.[713]
Security
A bootloader can be the mechanism by which software intellectual property is protected: an encrypted bootloader is programmed into the part at a secure facility with no network access, after which the programmed part is consigned to the assembly factory.[113] That arrangement also controls the supply chain, because a factory issued with a counted number of programmed parts cannot run an unauthorised extra production shift, and it allows subsequent images to be sent as encrypted files rather than delivered physically.[113]
Roots of trust
A root of trust boots an otherwise untrusted system by verifying each stage in turn: an unchangeable first-stage bootloader in read-only memory validates and starts the next, which validates the one after, and the same component subsequently governs signing and firmware updates so a malicious image is rejected.[693] Such a root of trust is isolated by construction, with its only connection being to a service processor responsible for delivering update data to it securely.[590] Key material can be held in one-time-programmable antifuse memory, with hardware able to permanently disable reads or writes to part of the array or to disable access until the next reset; the intended pattern is that an early bootloader reads the key, uses it, wipes its own memory, and then locks the key away for the rest of the session.[687]
Attacks and weaknesses
Encrypting the bootloader is not a checkbox: an implementation on real embedded hardware leaks through power consumption, because driving more lines high costs measurably more current than driving them low, and those differences can be measured and used to recover the key.[239] The relevant standard was defined without an attacker holding the device in mind, and the countermeasures are largely at the hardware level, such as balanced registers that set one line high whenever another goes low so no net switching is visible.[239]
A common structural weakness is that a failed check falls into an infinite loop rather than halting the processor, so an attacker who can skip past that loop finds the remaining code sitting there ready to run.[552] The three established ways of disrupting logic to defeat a secure boot process are voltage glitching, which perturbs the whole supply rail; laser fault injection, which reaches very small regions of logic because silicon is relatively transparent in the infrared; and electromagnetic injection through a small coil, which is localised but less precise than a laser.[687] In a public challenge against one such implementation, the practical breaks included faulting the signature check with a laser, injecting a fault into the USB bootloader by disturbing the supply, and extracting the one-time-programmable secret directly by imaging the die.[687] The recurring design failure in secure boot is disproportionate attention: enormous effort goes into resisting fault injection while a crafted flash image or a crafted packet arriving over the update interface opens the device without needing physical access at all.[687] Boot code kept in an ordinary serial flash alongside a server’s processor is straightforward to modify, making it a far cheaper route for an attacker than fabricating custom hardware.[418]
Implementation in practice
Writing a bootloader in house means deciding how to divide the program storage into regions that support robust image updates, and settling the versioning scheme and whether rollback to an earlier image is permitted at all.[590] Bootloader code is largely reusable across products, so a practitioner accumulates bootloader libraries in which the high-level update protocol is shared and only a platform-specific portion has to be adapted.[518] Silicon vendors increasingly supply the pieces, with storage-card and firmware-loading code already written, which removes the need to implement the bootloader from scratch.[364] The task also has a reputation that exceeds its difficulty: one bootloader that a team had been trying to hire out was written over a weekend once someone simply attempted it.[438]
Failure modes
Omitting the bootloader from a shipped firmware image is unrecoverable: once such an image was loaded, the devices could not be reloaded at all.[215] Multiple processors in one product multiply the problem, since one must pass code to another and a power loss during that transfer is unrecoverable; good bootloaders can be designed for it, but it remains an argument for keeping the number of separately updatable firmware images down.[187]
A bootloader also interferes with debugging: a debugger that has no symbols for it loses track when execution enters it, and one project found its debug session only worked once the bootloader was removed.[383] The same effect appeared with a widely used open bootloader, where debugging failed until it was taken out, so anyone debugging on a system with one initialised should expect to do extra work.[509]