From Bootloader to OTA: Unlocking the Right Approach for Embedded Firmware Updates

In the world of embedded development, firmware updates are undoubtedly a headache but an unavoidable challenge. To allow the development board to program firmware, we need to reserve a set of dedicated interface pins. These pins are used in the factory to program the software, but once the product is shipped, we need to find alternative methods for firmware updates. Today, let’s discuss the intricacies of firmware programming.

Firmware Updates

Have you heard of that classic interview question? A person needs to transport a goat, cabbage, and wolf across a river, but the small boat can only carry him and one item at a time, and he must make multiple trips to prevent the wolf from eating the goat and the goat from eating the cabbage. Firmware updates have a similar brain-teasing logic: we need to move new firmware from one place to the device while ensuring the device doesn’t become a brick and preventing the firmware from being compromised.

From Bootloader to OTA: Unlocking the Right Approach for Embedded Firmware Updates

If you are just starting with embedded systems, you might think that updating firmware via USB is the easiest method, after all, who doesn’t have a USB port? However, upon trying it, you will find that this approach is prohibitively expensive. On the hardware side, USB ports and related circuits are not cheap; on the software side, adapting to the various operating systems of customers is a nightmare. Using Over-The-Air (OTA) updates is a more natural choice.

For example, Nordic’s SDK provides sample code for Device Firmware Update (DFU), which has decent security and a relatively stable process. However, even with ready-made code, firmware updates remain a high-risk operation. Especially for resource-constrained small chips, we must consider real-world scenarios like power outages and disconnections, as a moment’s inattention could turn the device into a brick.

Three Common Approaches to Firmware Updates

Before we dive into implementation, let’s review several mainstream methods for firmware updates. Each method has its own advantages and disadvantages, and the choice depends on your device and scenario.

From Bootloader to OTA: Unlocking the Right Approach for Embedded Firmware Updates

1. Using the Chip’s Built-in Bootloader

Many processors come with a built-in bootloader that runs a piece of code at startup to check specific conditions (such as whether a certain button is pressed or if a specific value is stored at a certain address in Flash). If the conditions are met, the device enters bootloader mode, receives new firmware, and writes it to Flash; otherwise, it runs the normal program. This method is common on ATTiny, for example, programming via SPI. If the power goes out during the download, the device may not run the new program, but the bootloader remains, giving you a chance to retry.

However, there are exceptions. If you lose power while updating the bootloader itself, the device is completely bricked, and congratulations, you have a brick.

2. Writing Your Own Bootloader

Another approach is to add a loading code segment in your main program. The new firmware is first transferred to a temporary storage area (usually RAM, sometimes external storage). Once the new firmware is fully transferred, decrypted, and verified, the system writes the new code to the execution area. The advantage of this method is relative safety: if the update fails or verification fails, the system does not modify the original code, making it less likely for the device to become a brick.

However, the drawbacks are also evident: you need to allocate enough space to store a complete new firmware image. This means your program size cannot exceed half of the Flash capacity. In resource-constrained embedded systems, wasting storage space like this is simply extravagant.

3. Updating from RAM

There is an even more space-efficient method: the main program only needs to load a small piece of code into RAM, which is a dedicated bootloader responsible for receiving and programming the remaining firmware. This method has low storage space requirements, and the loading program is independent, allowing the main program to occupy as much Flash as possible. However, the risks are higher: if the loading program loses power or disconnects after erasing the old code but before writing the new code, the device will become a brick.

To reduce risk, you can use a small temporary storage area to hold the new firmware, and only erase the old code after verification. This approach takes up a bit more space but is much more efficient than the second method.

Firmware Updates for IoT Lights

Now that we’ve discussed the theory, let’s look at how to implement it in practice. Taking an IoT light project as an example, the device uses a Nordic chip along with an auxiliary MCU. The older version of Nordic’s SDK uses the second method (self-built loading program), requiring careful planning of storage space to accommodate the new firmware image.

From Bootloader to OTA: Unlocking the Right Approach for Embedded Firmware Updates

Here comes the problem: Nordic’s firmware update solution does not know how to handle the firmware of the other MCU. However, we certainly want to update its code on-site as well. What to do? A simple and straightforward solution is to write a small Python script that appends the firmware of the small MCU to the end of the Nordic firmware image and uploads them together to the device. This means that the temporary storage area must be large enough to hold both firmware images simultaneously.

From Bootloader to OTA: Unlocking the Right Approach for Embedded Firmware Updates

Don’t Forget About Security Issues

When it comes to firmware updates, security issues cannot be overlooked. Your boss may only care about preventing code theft, as firmware updates are a potential vulnerability that could impact company revenue. As a developer, your responsibility is even greater: you must not only protect the code but also ensure user data security and prevent devices from being exploited by hackers to attack other systems.

Security does not have a one-size-fits-all solution and is an ever-evolving challenge. Nordic’s BLE firmware update solution is reasonably secure, but the encryption strength of BLE itself is limited, and even moderately experienced hackers can find a way in. Suppose a hacker gains access to your device; they can connect to test points, use debugging interfaces to study it slowly, or even disassemble the chip to analyze memory layout. The cost is high, but those determined to breach your security can certainly do so.From Bootloader to OTA: Unlocking the Right Approach for Embedded Firmware Updates Therefore, threat modeling must be done from the beginning of development. Consider what hackers can do: if they crack one device, can they trace back to disrupt your server, steal user data, or affect other devices? If your security measures are robust enough, hackers may need physical access to the device to proceed. However, even so, you must assume they have succeeded and then assess the consequences.

Here’s a piece of advice: never try to create your own security algorithms. Use standardized encryption methods, seek help from professionals, and preferably have a third party audit your implementation. Security is too complex, and a slight oversight can leave you with vulnerabilities that you may never discover.

From Bootloader to OTA: Unlocking the Right Approach for Embedded Firmware Updates

Leave a Comment