Teste semf
Kontakt

Bootloader

General

The purpose of this module is to update the currently running firmware with a new one and reboot the embedded device. In order to successfully accomplish this difficult task, the new firmware must be decrypted and verified, before the existing firmware can then be overwritten.

In this tutorial one must distinguish between two terms: the firmware file and the firmware, which is part of the firmware file. The firmware file includes two parts:

1. The header, where the general information about the update are listed: version information, size, encryption algorithms, etc. Moreover, it includes the verification digits (checksum, hash, signature) of the header and the firmware.
2. The firmware, which is the new executable binary (program) and is encrypted.

This task is accomplished in two main classes:

  1. The FirmwareUpdater class, where the firmware file is read part by part, decrypted and verified.
  2. The Bootloader class, where the existing firmware will be overwritten with the new one.

FirmwareUpdater Class

This class reads the firmware file from an internal or external flash memory, executes the decryption (if necessary) and then verifies it with CRC or SHA256 algorithms. If these two steps execute faultlessly, it triggers the ready signal, otherwise the error signal.

The ready signal must be connected with the bootloader class object, to start the bootloading process.

The FirmwareUpdater class is comprised of four subclasses, which are executed in the sequence in which they are listed:

  1. FirmwareHeader, where the first part of the firmware file is read, stored and verified. Read signal is fired after successful execution.
  2. FirmwareDecrypter, where the encrypted firmware/application is decrypted using Advanced Encryption Standard (AES) in Cipher Block Chaining (CBC) Mode. Successful signal is fired after successful execution.
  3. FirmwareVerifier, where the decrypted firmware/application is verified using CRC or SHA256 algorithms, with the help of the verification digits that are read with FirmwareHeader class object. The successful signal is fired after successful execution.

After successful execution of these steps, the FirmwareUpdater class object overwrites the shared memory at the address it was given via the constructor. The data of the Shared-Memory includes the following information:

  • Source address, where the new firmware is stored (located in an internal or external flash memory)
  • Destination address, where the new firmware will be written to (located in an internal flash memory)
  • Firmware size in bytes
  • Firmware entry address, where the application starts (located in an internal flash memory)
  • State of the Shared-Memory, which signals whether
    • a new firmware exists and it should be written,
    • the existing firmware should be run, or
    • no valid data is in the Shared-Memory.
The signals of the three subclasses are connected together in the FirmwareUpdater class object.
The firmware file could be stored in an internal or external flash memory. A reference to the flash memory must be conveyed to the FirmwareUpdater class object using registerInterface function.

Bootloader

Its main responsibility is to read the Shared-Memory, and to decide (based on the state of the Shared-Memory), whether to overwrite the exisiting firmware with the new and decrypted one, or to run the exisiting application.

isReady() function must be called continuously, if it returns true, then the startApplication() function can be called safely, otherwise the bootloader class object is busy with erase, read or write operations, then call isReady() again.

Initialization of FirmwareUpdater Class

There is a software implementation in the library for the hashing, verification and decryption of the firmware, which uses a third party library called Mbed TLS. Should the microcontroller being used in the project offer hardware capabilities in this regard, a hardware implementation can be supplemented.

The main application must be stored somewhere in the internal flash memory, but remember to leave a memory space for the bootloader application and the shared data. For example, the bootloader application starts at 0x08000000 for 17 KBytes, the shared data starts at 0x8004800 for 2 KBytes, and the main firmware is stored at 0x08005400 until the rest of the memory; all this can be done in the linker file. Note that the shared data is usually less than 1 Kbyte, but because of the internal flash memory organization, you can only erase by page/sector that is 2 Kbytes in size, so you must block 2kBytes for the shared data.

In this example we consider STM32F1 microcontroller, and SST26VF016B external flash memory.

In this example, the firmware file is expected to be stored in an external flash memory at sector 12 and it is assumed that the new firmware (which is inside the firmware file) is encrypted using AES-CBC algorithm, and the verfication is done using PKCS1 standards.
// stm32f1 power modes.
#include "HardwareAbstraction/Stm32F1/stm32f1power.h"
// internal flash memory.
#include "HardwareAbstraction/Stm32F1/stm32f1flash.h"
// external spi-flash memory  
#include "Devices/Storage/NorFlash/microchipsst26vf016b.h"

#include "Components/Bootloader/firmwareupdater.h"
#include "Components/Bootloader/bootloadersharedmemoryflash.h"
#include "ThirdParty/mbedtls/aescbcmbed.h"
#include "ThirdParty/mbedtls/hashsha256mbed.h"
#include "ThirdParty/mbedtls/signaturepkcs1mbed.h"

// Address where the shared data is stored in the internal flash memory.
static constexpr uint32_t kShardedMemoryAddress 0x08004400;
// Address where the firmware file is stored in the external flash memory.
static constexpr uint32_t kExtFlashFirmwareFirstSector 12;

semf::Stm32F1Power powerModes;  // for hardware reset.
semf::Stm32F1Flash internalFlash;  // where the new firmware will be written to it.
semf::MicrochipSst26vf016b externalFlash;  // where the new firmware file is stored.

// Decryption 
uint8_t aesBuffer[16];
semf::AesCbcMbed aesCbc(aesBuffer, sizeof(aesBuffer));  
// Hash
uint8_t sha256Buffer[32];
semf::HashSha256Mbed sha256(sha256Buffer, sizeof(sha256Buffer));
// Signature verification
uint8_t pkcs1Buffer[128];
semf::SignaturePkcs1Mbed pkcs1(pkcs1Buffer, sizeof(pkcs1Buffer)); 
 
semf::BootloaderSharedMemoryFlash bootloaderSharedMemory(internalFlash,
	kSharedMemoryAddress);
uint8_t firmwareupdaterBuffer[1024];
semf::FirmwareUpdater firmwareupdater(externalFlash, aesCbc, sha256, pkcs1, 
	bootloaderSharedMemory, firmwareupdaterBuffer, sizeof(firmwareupdaterBuffer));

Usage of FirmwareUpdater

void onFirmwareReady()
{
	// do system reset
	powerModes.reset();
}

// connect the ready-signal with its corresponding slot.
firmwareupdater.ready.connect(&onFirmwareReady);
// binary received, start verification process.
firmwareupdater.start(extFlash.address(kExtFlashFirmwareFirstSector));

Usage of Bootloader

// Inclusion of the microcontroller specific part.
#include "HardwareAbstraction/Stm32/stm32f1bootloader.h"  
#include "HardwareAbstraction/Stm32/stm32f1flash.h"
#include "Devices/Storage/NorFlash/microchipsst26vf016b.h"

static constexpr uint32_t kSharedMemoryAddress 0x08004800;
static constexpr uint32_t kApplicationAddress 0x08005400;

semf::Stm32F1Flash internalFlash;
semf::MicrochipSst26vf016b externalFlash;
uint8_t bootloaderBuffer[128];
semf::BootloaderSharedMemoryFlash bootloaderSharedMemory(externalFlash, 
	kSharedMemoryAddress);
semf::Stm32F1Bootloader bootloader(externalFlash, internalFlash, 
	bootloaderSharedMemory, kApplicationAddress, 
	bootloaderBuffer, sizeof(bootloaderBuffer));

bootloader.start();
// block till the bootloader finishes its job.
while(!bootloader.isready()) {;}
bootloader.startApplication();

zurück zur Dokumentation