Firmware Development8 min read

Bootloaders Explained: Secure Firmware Updates

Lafiz Maruf Rahman
Lafiz Maruf Rahman
March 8, 2025
Bootloaders Explained: Secure Firmware Updates

The first time I needed to update firmware on a deployed device, I realized I had a problem. The device was installed in a remote location. I could not physically connect a programmer to it. I needed a way to update it over the air.

That is when I learned about bootloaders properly.

What a bootloader actually does

When a microcontroller powers on, it starts executing code from a fixed memory address. The code at that address is the bootloader.

The bootloader's job is simple: check if there is a new firmware update waiting. If yes, write it to flash memory and then run it. If no, just run the existing firmware.

Without a bootloader, updating firmware requires a hardware programmer physically connected to the device. With a bootloader, you can update over USB, UART, Bluetooth, WiFi, or cellular — whatever communication interface your device has.

How flash memory is organized

A typical flash memory layout with a bootloader looks like this:

text
Flash Memory (512 KB total):
┌─────────────────────┐ 0x08000000  (start)
│     Bootloader      │ 32 KB
├─────────────────────┤ 0x08008000
│   Application       │ 224 KB
│   Firmware          │
├─────────────────────┤ 0x08040000
│   Update Buffer     │ 224 KB (new firmware downloaded here)
├─────────────────────┤ 0x08078000
│   Configuration     │ 32 KB
└─────────────────────┘ 0x08080000  (end)

The bootloader lives at the very start of flash and is never overwritten. New firmware is downloaded to the update buffer. The bootloader then copies it to the application area.

The bootloader code

c
int main(void) {
    SystemInit();
    
    // Is there a valid update waiting?
    if (IsUpdatePending() && IsUpdateValid()) {
        ApplyUpdate(); // copy from buffer to application area
    }
    
    // Is the application valid?
    if (!IsApplicationValid()) {
        EnterRecoveryMode(); // wait for a new firmware over USB/UART
    }
    
    // Jump to the application
    JumpToApplication(APP_START_ADDRESS);
}

void JumpToApplication(uint32_t address) {
    // Set the stack pointer to the application's stack
    __set_MSP(*(uint32_t*)address);
    
    // Get the application's reset handler address
    void (*app_start)(void) = (void(*)(void))(*(uint32_t*)(address + 4));
    
    // Jump!
    app_start();
}

Validating firmware before applying it

Never apply firmware without checking it first. At minimum, verify a CRC checksum:

c
bool IsUpdateValid(void) {
    // The last 4 bytes of the firmware are the CRC
    uint32_t stored_crc = *(uint32_t*)(UPDATE_BUFFER + UPDATE_SIZE - 4);
    uint32_t calculated_crc = CRC32_Calculate(UPDATE_BUFFER, UPDATE_SIZE - 4);
    return stored_crc == calculated_crc;
}

For security-critical devices, use cryptographic signatures. The firmware is signed with a private key during the build process. The bootloader verifies the signature using a public key stored in read-only memory. If the signature is invalid, the update is rejected — even if an attacker tries to flash malicious firmware.

Rollback protection

Always keep the previous firmware so you can roll back if the new version has a bug. The dual-bank approach stores two complete firmware images. If the new one fails to boot after a few attempts, the bootloader automatically switches back to the old one.

c
// Track boot attempts in non-volatile memory
if (GetBootAttempts() >= 3) {
    // New firmware is failing, roll back
    ActivatePreviousFirmware();
    ResetBootAttempts();
}
IncrementBootAttempts();

// Application calls this after successful startup
void ConfirmSuccessfulBoot(void) {
    ResetBootAttempts();
}

A device that cannot be bricked is a device your customers can trust.

FirmwareBootloaderSecurityOTA Updates

Ready to build something great?

Let's talk about your project. We will give you honest advice, a clear plan, and a fair price. No pressure, no sales pitch.

Free consultation
No commitment required
Response within 24 hours