Android Verified Boot makes sure a device only starts software its maker has signed. In U-Boot, the bootloader used on many embedded boards, that check only worked when Android was booting from an SD card or eMMC. Our Android team changed that: U-Boot's verified boot layer now works on any block storage the board can start, such as a USB drive or an NVMe SSD. The U-Boot maintainers already reviewed the change and merged it into mainline U-Boot, and it is part of the v2027.01 release candidates. This article explains what we did and why it was important.
Verified boot keeps a device on the software you shipped
When an Android device starts, the bootloader loads the kernel and the first parts of the system from storage. With Android Verified Boot (AVB), the bootloader first checks these images against signatures from the device maker. If someone has replaced or modified them, the check fails, and the device refuses to boot or, if its bootloader is unlocked, reports the failed check to Android. Android then continues the same chain of checks for the rest of the system.
The storage the device boots from matters here. Many prototypes start on an SD card, while products in the field often boot from eMMC, a USB drive or an NVMe SSD, for capacity, speed or robustness. If verified boot only works on one kind of storage, the choice of storage decides whether a product can use it at all.
U-Boot's verified boot logic assumed MMC storage
SD cards and eMMC are both MMC devices. U-Boot's AVB support assumed MMC storage throughout: it identified the boot device by a bare number without saying which bus it belonged to, looked up partitions through the MMC layer, only accepted an MMC device in its avb init command, and even named its generic read and write helpers after MMC. Each of these alone ruled out verified boot from any other storage.
After the change, a storage interface travels with the device number, and U-Boot resolves the device through its generic block layer. The command accepts both forms:
avb init 0 # still means mmc 0, as before
avb init nvme 0 # any block interface the board has started
The new code is also stricter. A device number that does not exist is rejected before anything is set up, instead of failing later during a read. And if verified boot was never initialized, U-Boot reports an error instead of quietly reading from the SD card.
The change is part of mainline U-Boot
Our work builds on open source. Android, the Linux kernel and U-Boot are community projects, and we benefit from them every day. That is why we contribute back: when we improve an open source project for our customers, we bring the change upstream where we can, so that every user of the project benefits and the code is reviewed and maintained by the wider community, not only by us. This work went upstream as a series of patches, reviewed by U-Boot maintainers and merged into the main branch:
- avb: Take a storage interface in the AVB operations
- blk: Add a name-to-interface-type lookup
- avb: Drop the mmc naming from the block IO helpers
Jan Kehren, who leads our Android OS development, wrote and submitted the patches, with help and reviews from the rest of our Android team. Any board that runs U-Boot can use verified boot on its other storage interfaces once it is built on a U-Boot release that contains these changes.
On our Raspberry Pi builds, one new boot command now replaces a legacy shell script
The upstream change was the foundation for a second piece of work in our own bootloader for the Raspberry Pi 4, Compute Module 4, Raspberry Pi 5 and Compute Module 5. Our previous Android boot flow on these boards was a U-Boot shell script of about seventy lines on the boot partition. It selected the A/B slot, loaded the boot images, ran verified boot and built the kernel command line. Every storage device in it was fixed to the SD card, and a single typo in the file dropped the board to a U-Boot prompt.
We replaced the script with one compiled bootloader command, bootandroid. It verifies both the boot and the vendor boot image on every boot. This also includes recovery boots, which the script did not verify in the past. The command takes the boot medium from the Raspberry Pi firmware, which reports in the device tree where it booted from, instead of assuming the SD card. Each image is sized from its own header and checked against the memory the board reports before it is loaded.
The failure behavior matters most for devices in the field. If a slot cannot boot, the command switches to the other slot. If nothing is left to boot, it offers fastboot where a connection for it exists, and otherwise resets. No input to the boot path leads to a U-Boot prompt on a customer's screen any more. Our upcoming Android releases for these boards use the new boot flow.
| Behavior | Before | After |
| Verified boot storage | MMC only (SD card, eMMC) | Any block interface the board has started |
| Images verified | Normal boot only | Boot and vendor boot, on every boot including recovery |
| Boot medium | Fixed to the SD card | Taken from what the firmware reports |
| Boot flow | Shell script on the boot partition | Compiled into the bootloader |
| Image loading | Whole partition read to a fixed address | Sized from the image header, checked against reported RAM |
| Slot cannot boot | Reset, or a U-Boot prompt on a script error | Other slot, then fastboot or reset |
USB and NVMe boot are available as beta support
Our test boards boot the new flow from SD cards and eMMC. Booting from USB drives and NVMe SSDs is built into the bootloader as beta support: the bootloader detects the drive, verified boot can check the images on it, and the boot command understands when the firmware reports a boot from it.
- Raspberry Pi 4 and Compute Module 4. NVMe support is enabled in our bootloader for Compute Module 4 carrier boards with an M.2 slot.
- Raspberry Pi 5 and Compute Module 5. USB and the M.2 slot are connected through a PCIe controller that needs a newer U-Boot version. The boot flow is prepared for it, because the storage options live in one table in the board code.
Next, we are building a unit with NVMe storage for our hardware test lab, so that the new storage options get the same regular testing as SD cards and eMMC.
If your product boots from a USB drive or an NVMe SSD and you want verified boot on it, talk to us. We are looking for pilot projects and welcome your feedback, and we can build and maintain the Android OS for your device.
Raspberry Pi is a trademark of Raspberry Pi Ltd.
Build unique products, boost device performance
See why Emteria is the chosen Android™ customization & management platform for OEM solution builders, and what it can do for your team and customers.
