I'm getting my hands around developing a custom bootloader on STM32 (something like IAP AN4657). Assuming the flash is divided into 3 regions scratch,user area, IAP code (bootloader) and firmware to be upgraded is in scratch area. I want to have a header with firmware version and checksum, I need some understanding on how to implement header to firmware and check the for validity of firmware from IAP (bootloader) code. Any reference to resources are appreciated.
3 Answers
You can create a structure that holds the header information and put this header at a known location in flash. There are two general approaches to this that you see being used:
- Padding firmware binary up to the size of flash, minus size of the header and putting it at the end,
- Putting firmware header somewhere at the beginning, before .text, .data and .bss that vary in size.
The approach depends on what your requirements are, how complex you want your bootloader to be and how much effort are you willing to put in to make it more optimized or flexible. It is often desired for the bootloader to be as simple as possible and often not self-programmable (there's one bootloader section that is never modified after leaving the factory). Either way, the header should reside at a constant offset within flash. To elaborate further on the two approaches mentioned above:
1. Firmware header at the end of (padded) binary.
This is an easier approach, especially as some tools/IDEs (like IAR for example) have ready-to-use mechanisms for this. Let's say you have the firmware header that has the following format:
typedef struct
{
uint32_t firmware_verson;
uint32_t crc32;
} sFirmwareHeader;
In such case you pad the firmware binary (with 0xFF
for example) up to FLASH_SIZE - sizeof(sFirmwareHeader)
and place the struct there. Those same tools often also have the capability of calculating CRC of the firmware binary and placing it at the end, which is exactly what fits this approach. The big downside is that every time you want to do firmware upgrade this way, you need to transfer the whole application binary from start to finish, including the padding bytes. If your application is small, this is quite a few unnecessary bytes being transferred. Of course I'm not mentioning any compression methods that would make this smaller, as those would make your bootloader more complex and therefore more error-prone.
2. Firmware header at the start of the application binary
Another approach is placing the firmware header somewhere at the start. Good place for this may be after ISR vectors but before .text, .data and .bss which will vary in size as you change your application code. A good idea is to extend the mentioned structure with the firmware_size
field:
typedef struct
{
uint32_t firmware_verson;
uint32_t firmware_size;
uint32_t crc32;
} sFirmwareHeader;
That way the bootloader can still not only verify the CRC of the firmware file to be loaded, but also verify the integrity of the application that has already been loaded into the flash, as the firmware header still resides under constant offset in flash memory, it's just not at the end like in the 1st approach. The advantage of this method is that you only need to transfer as many bytes as necessary. The downside is that there likely won't be any ready-to-use tools for you. You'll need to write some kind of a simple program/script to calculate the CRC value for you after building the application firmware binary (firmware_size value can be supplied by the linker).
To make the answer more complete, here's a snippet of code that should give you a good starting point regardless which approach you plan to use. These are for gcc.
Part of the linker script defining a section to hold firmware header at a constant 0x200
offset counting from the start of the FLASH
memory section:
__fw_header_offset = 0x200;
SECTIONS
{
/* ISR vectors */
.fw_header : ALIGN(4)
{
FILL(0xFF)
. = ORIGIN(FLASH) + __fw_header_offset;
KEEP(*(.fw_header))
} >FLASH
/* Other sections in Flash */
}
Then in your application code you can do this:
const sFirmwareHeader __attribute__ ((section(".fw_header"))) FirmwareHeader = {
1, // firmware_verson
0 // crc32, this can be filled with an external application after building firmmwre binary
};
which creates a global FirmwareHeader
structure and places is under `.fw_header' section defined eariler.
I also use in my projects something similar.
The header with firmware version and CRC checksum was placed in linker script at the end of FLASH, so header structure with version of firmware was defined on one place in source code and checksum was last bytes in binary.
Checksum was added after building and converting elf to binary file with simple python scrip. but some compilers (IAR) has feature to calculate and place checksum of firmware.
In my case after boot I look to header in update area and if there was something and CRC was OK and whole header was different than header in application I start copying this update area to application area.
I am starting to implement option #2 (fw header at the start of tha app binary), but there is something I am not able to figure out how to solve:
- Once the app binary is produced, I manually add binary size in header (at sFirmwareHeader's firmware_size position) and I use an external tool to generate its CRC.
- The resulting CRC is stored again in binary's header (at sFirmwareHeader's crc position)
- Image is downloaded in device and device bootloader tries to validate new image before using it
- The issue is that calculated CRC does not match header's. Reason? The calculated CRC is parsing the image containing header with crc value while the header's crc value was obtained with header crc parameter 0.
How should I proceed? Under this approach image header is inside the binary image (between vector table and code), so when calculating the crc the header data is also computed...
Thks!