All about bootloaders

 BOOTLOADERS
***

Some interesting general boot loader facts:

The Boot Loader is basically a section at the top of flash memory, how big it is depends on the BOOTSZ1 and BOOTSZ0 bits in the Low Fuse byte.
It has the special property that ONLY while the core is running instructions in the bootloader, a special instruction called SPM will function. This function allows writing data to the flash memory. As normal with all flash, you need to erase the page you are writing first, otherwise your write operation will often not function correctly. I think you can change 1 bits to 0 bits but not the other way around, which is true for all flash writing. So the flash erase process always sets a flash memory page to all 0xFF (all 1 bits)
The usual way the boot loader runs is if the fuse bit BOOTRST is active (i.e. it reads as a 0!) then when the MCU is reset, it will start execution at the beginning of the bootloader (as defined above). If the bit is not set, it will start execution at 0x0000.
But this is not the only way the bootloader can be run! You can just JMP to the start of it and it will run. This might actually be an interesting use case. For example your program has a feature where you ask it to go to "firmware update mode" and it jumps to the bootloader. You can write a bootloader that doesn't do any resets at all, but updates the program then starts from the beginning, without actually resetting the CPU. People don't usually do it but there's no technical reason why you need to have a reset in the process.

OPTIBOOT
***

Some interesting optiboot facts:

Optiboot is a very carefully written bootloader that implements a "skeleton" portion of the STK500 protocol, which is the firmware update over UART protocol. It's been around for a long time (nearly 20 years?) and the original protocol was written by Atmel for use with their tools and bootloaders, etc. Arduino made their own bootloader that was I think 1k in size, when they launched the first Arduinos (Duemillanova etc.) Even now, many older Arduino Nano boards will use this older bootloader.

Optiboot was a cleverly designed update to this. It removed features people don't use much, such as fuse read/write, signature read, EEPROM read/write and just concentrated on an extremely tightly written simple bootloader. This is it in hex...

:107E0000112484B714BE81FFF0D085E080938100F7
:107E100082E08093C00088E18093C10086E0809377
:107E2000C20080E18093C4008EE0C9D0259A86E02C
:107E300020E33CEF91E0309385002093840096BBD3
:107E4000B09BFECF1D9AA8958150A9F7CC24DD24C4
:107E500088248394B5E0AB2EA1E19A2EF3E0BF2EE7
:107E6000A2D0813461F49FD0082FAFD0023811F036
:107E7000013811F484E001C083E08DD089C08234E0
:107E800011F484E103C0853419F485E0A6D080C0E4
:107E9000853579F488D0E82EFF2485D0082F10E0AE
:107EA000102F00270E291F29000F111F8ED06801E7
:107EB0006FC0863521F484E090D080E0DECF843638
:107EC00009F040C070D06FD0082F6DD080E0C81688
:107ED00080E7D80618F4F601B7BEE895C0E0D1E017
:107EE00062D089930C17E1F7F0E0CF16F0E7DF06D8
:107EF00018F0F601B7BEE89568D007B600FCFDCFD4
:107F0000A601A0E0B1E02C9130E011968C91119780
:107F100090E0982F8827822B932B1296FA010C0160
:107F200087BEE89511244E5F5F4FF1E0A038BF0790
:107F300051F7F601A7BEE89507B600FCFDCF97BE46
:107F4000E89526C08437B1F42ED02DD0F82E2BD052
:107F50003CD0F601EF2C8F010F5F1F4F84911BD097
:107F6000EA94F801C1F70894C11CD11CFA94CF0C13
:107F7000D11C0EC0853739F428D08EE10CD085E9AC
:107F80000AD08FE07ACF813511F488E018D01DD067
:107F900080E101D065CF982F8091C00085FFFCCF94
:107FA0009093C60008958091C00087FFFCCF809118
:107FB000C00084FD01C0A8958091C6000895E0E648
:107FC000F0E098E1908380830895EDDF803219F02E
:107FD00088E0F5DFFFCF84E1DECF1F93182FE3DFCA
:107FE0001150E9F7F2DF1F91089580E0E8DFEE27F6
:047FF000FF270994CA
:027FFE00040479
:0400000300007E007B
:00000001FF

That's everything!

The C code is pretty strange in many points. That's because the author(s) wanted to make sure it compiled to exactly the correct assembly/MC to keep it in the tight size constraints, so they did a lot of AVR-GCC related tricks. But they wanted to keep the code written almost entirely in C with only little bits of inline ASM, I think partly to help support the wide range of chips that optiboot can run on.
It has some interesting features I'd not appreciated before:

The characteristic three LED flashes on a reboot are from the optiboot bootloader. It's configurable... you can make it nine flashes or none, and change the delay.

It can also do the UART comms over software UART, so you can use any pins for uploading firmware, not just RX/TX!

Many of the other configurations are for unusual architectures, such as chips in the attiny range that don't have the full bootloader support (no BOOTRST) so they have to use a workaround.

It can still report it's own version number to avrdude or a similar program as well as flash programming.
EEPROM is left unaffected by a firmware update. In fact you have no way to access or control EEPROM with avrdude if you're using avrdude -c arduino and optiboot.


However some compromises were made:

When you read fuses using optiboot, it gives dummy values.

When you read the chip signature it gives dummy values. This can be dangerous if you're not careful. In most cases it should not be too bad, because you should compile the optiboot bootloader for a specific chip and upload that specific bootloader. As part of the code you return that chip's signature, hard coded in optiboot... rather than actually reading it from the chip.

This means if you upload the wrong bootloader to a chip (e.g. upload an atmega328 optiboot bootloader to an atmega328p chip), it will start to report the signature incorrectly if someone uses avrdude over the "Arduino" / UART / STK500 protocol. So someone might think they have an atmega328p when they don't.

Other compromises from optiboot...

EEPROM reading/writing is not available by default. Although I think you can build a version that can do it. I don't know if it will fit in 512 bytes or not.

Chip erase doesn't work. So the -e flag on avrdude DOES NOTHING if you're programming using avrdude -c arduino (for example).

Likewise, when programming using avrdude -c arduino... the -D flag is pointless. No chip erase occurs before programming flash memory (unlike programmers... see below). The bootloader itself erases the flash page by page as it needs to. This means that if your program is smaller than the program it was replacing, some of the old program will possibly be left in flash memory after the end of your program. Most programs go into an infinite loop at end anyway and nothing should jump out into random address space... but if it did you could get some unexpected effects. Arguably, this is a downside of a bootloaded program.  If the chip is erased first and some faulty code jumps outside of the program, the MCU will probably run through the 0xFF 0xFF instructions until it hits the bootloader, which is a little more predictable. But it's really only a small gain.

Signature verification or fuse reading with the bootloader is often pointless and misleading, because you are in fact just reading whatever was hard coded into the optiboot bootloader. This can throw avrdude off sometimes. As a result the -F flag is probably reasonable when using the -c arduino protocol in avrdude.



ICSP PROGRAMMERS

***

In reality I think for most of us building commercial products who are likely to go outside of the normal chip range, we will be using an ICSP programmer so a lot of the above issues won't be a problem.

Using a programmer, the bootloader restrictions in optiboot will not apply.

You can do a chip erase with -e.

In fact, if you upload a flash program using -U:w:XXXXXX:flash the chip erase will happen automatically just before the program is written.

You have to use -D to disable the behaviour... I believe in that case the programmer will erase only the pages necessary to write a program... so it behaves like optiboot in terms of erasing only what's needed and in the same way, might sometimes leave fragments of older programs in the higher memory of flash... something that's probably usually harmless.

You can read and write fuses correctly.
You can read signatures correctly.
You can read and write EEPROM using avrdude, meaning you can set some config bytes for a program, like set up parameters.

EEPROM is usually erased when the chip is erased as part of a standard upload to flash. But you can disable that behaviour with a fuse setting so that your "firmware settings" are kept across firmware updates. (Another approach is using the -D setting to disable chip erase during flash upload, so only the necessary pages are erased.)


That's what I found so far! Comments and thoughts welcome!

Comments

Popular posts from this blog

Halloween LED lights on a plastic trick or treat cauldron

code signing, entitlements, bundles, sandboxes, hardened runtime, notarisation, app store security