Bootloaders
A bootloader is a program in a computers memory that runs before the main application. In an embedded context, it is typically responsible for:
- Configuring up basic hardware (most of the hardware configuration is left to the main application, the bootloader would typically just configure the clocks it needs and perhaps the watchdog)
- Loading the main app into memory
- Executing the main app (typically a “jump” instruction which goes to a predefined memory address)
- Updating the main app (this is optional, and if this functionality is required, it may be included in the bootloader or in the application depending on the chosen topology).
They are commonly added to microcontrollers to update the main firmware application. Bootloaders will use a communication protocol/transport to “download” new firmware from a host computer (e.g. laptop via cable, or server on the internet). Common transports include UART, RS-232, I2C, CAN, Ethernet, WiFi or Bluetooth.
The bootloader will typically verify the downloaded image using a checksum or hash before allowing the new version of firmware to run. The bootloader can either then force a reset and boot into the new application, or allow it to happen at a more convenient time in the future. The bootloader can also enforce that the application “checks in” and states it’s ok (typically once initialization is complete) otherwise it will fall back to the previous version.
Bootloaders are typically the first thing that is run whenever the microcontroller is reset. This gives the bootloader control over what application to then boot.
Child Pages
Memory Layout
The image below shows what the memory layout might look like for a microcontroller using a basic bootloader. The Reset Vector is the first instruction executed by the microcontroller on start-up. This is typically located at memory address 0x0000 0000
(the Intel 8086 processor is a notable exception). The reset vector usually contains a “goto” (jump) instruction will tells the CPU to jump to the bootloaders entry point. The bootloader will then execute. The bootloader could then initialise a transport to download new firmware, e.g. over UART. It then might wait for a message for a fixed amount of time. After 10s the chance to download firmware passes, and then the bootloader jumps to the start of the application executable.
Erase Cycles
Flash memory has a limited number of erase cycles, and you have to be careful you do not exceed this number during the lifetime of the device.
Multiple Application Images
Relocation can be problematic for memory. When your firmware application undergoes the linking step during compilation, then linker assigns memory addresses for all the functions and static variables in your code. It needs to know where the executable is being placed in memory to do this. But what if you have two different images, which require different offsets?
One solution is a swap algorithm.
Swap-Scratch Algorithm
The Swap-Scratch algorithm is a way of swapping two firmware images in flash memory.
The scratch area must be large enough to hold the largest sector that is going to be swapped.
Swap-Move Algorithm
- a): All of the segments in slot 0 are moved one segment up, starting by copying segment 3 into the empty sector, then segment 2 into where segment 3 used to be, e.t.c. This moves the empty segment to the start of slot 0.
- b): The first segment of slot 1 is moved down to the empty segment.
- c): The first segment of slot 0 is moved up to where the first segment of slot 1 used to be.
- d): The process repeats (b) and c)) now with the second segments of each slot.
- e): This continues until…
- f): All segments have been swapped over and the empty segment is back at the end of slot 0. Job done! Note that the contents of each slot have been swapped. I’ve get the old names for each slot to highlight the fact they have moved. Technically speaking though, slot 0 is still in the same place, it just now has the contents of what used to be in slot 1.
Each segment in slot 0 is erased/written to twice and each segment in slot 1 is erased/written to once. This is a significant improvement over the Swap-Scratch algorithm which performs as many erase/write cycles as there are segments.
Security
TODO: Add info here.
Golden Image
A golden image is a firmware application with minimal support for firmware updating. If updating over-the-air (OTA), than this image must include basic wireless comms support. It is usually stored either in external flash or in a protected area of the microcontrollers internal memory. It is loaded by the bootloader if the main application is faulty, and in that sense, it is a backup application which is guaranteed to work.
Zynq 7000 Boot Sequence
BootROM is the first thing that executes. BootROM runs on the first A9 processor core on the Zynq. The second A9 executes a “wait for event” (WFE) instruction1.
The main job of BootROM is to configure the system, copy the First Stage Bootloader (FSBL) from the boot device (e.g. QSPI flash) to the on-chip memory (OCM) and then execute the FSBL.
The BootROM expects the boot images to be in a specific format as defined by AMD. Each boot image contains:
- A boot header. This contains info such as whether the boot image in encrypted, the size, offset and execution address of the FSBL, and offsets to the image and partition header tables.
- A register initialization table: This contains 256 address-value pairs used to initialize PS registers for MIO multiplexing, and flash clocks1.
- A image header table. This contains info about the number of
- Image and partition headers.
- Partition 1 containing the FSBL.
- Optional additional partitions containing the main application.
Confusingly, AMD uses an “image” to refer to two different things. A “boot image” is the entire structure defined above. This “boot image” can contain multiple images, which are defined as pieces of data that have been extracted by Bootgen from files (e.g. the ELF files, bitstream, data files) when creating the boot image. For example, the FSBL is an image, as is the main application1. Each image inside the overall boot image can contain one or more partitions. One use case for this is because an ELF file can contain multiple loadable sections — each one of these sections becomes a partition.
There is a method for the FSBL (or other applications running post-BootROM) to make the BootROM boot a different boot image. This is done by the FSBL writing to devcfg.MULTIBOOT_ADDR
and then performing a software system reset2. The is useful for example if the FSBL detects that the main application is corrupt and there is a secondary/backup boot image available (or rollback to a previously stable version).
Bootloader Libraries
MCUboot
MCUboot is a popular, open-source, secure bootloader for 32-bit microcontrollers. As of Jan 2024 it has 1.1k star on GitHub, 2,201 commits and active development month over month. It is designed to be run from flash memory3.
It currently supports the following frameworks/OSes:
- Zephyr
- Apache Mynewt
- Apache NuttX
- RIOT
- Mbed OS
- Espressif
- Cypress/Infineon
- Simulator (primarily for testing the upgrade code and badly timed resets - written in Rust4)
The MCUboot implementation for Zephyr supports Bluetooth bootloading. The Nordic nRF app can be used to bootload during development.
Footnotes
-
AMD. UG1283 - Bootgen User Guide. Retrieved 2024-08-16, from https://docs.amd.com/r/2023.1-English/ug1283-bootgen-user-guide/Introduction. ↩ ↩2 ↩3 ↩4
-
AMD (2023, Jun 30). UG585 - Zynq 7000 SoC Technical Reference Manual. Retrieved 2024-08-16, from https://docs.amd.com/r/en-US/ug585-zynq-7000-SoC-TRM/Zynq-7000-SoC-Technical-Reference-Manual. ↩
-
MCUboot. MCUboot - Secure boot for 32-bit Microcontrollers! [project homepage]. Retrieved 2024-01-27, from https://docs.mcuboot.com/. ↩
-
MCUboot. MCUboot simulator - README. Retrieved 2024-01-27, from https://github.com/mcu-tools/mcuboot/blob/main/sim/README.rst. ↩