From 8d746fa40b8d22678da71aab179a050763447862 Mon Sep 17 00:00:00 2001 From: Matthew Kennedy Date: Tue, 24 Feb 2026 14:15:10 -0800 Subject: [PATCH 1/4] build the bootloader first so we can embed it --- firmware/config/boards/common_make.sh | 51 ++++++++++++++++----------- 1 file changed, 31 insertions(+), 20 deletions(-) diff --git a/firmware/config/boards/common_make.sh b/firmware/config/boards/common_make.sh index 54c4b5a7a1..d9ec43253a 100755 --- a/firmware/config/boards/common_make.sh +++ b/firmware/config/boards/common_make.sh @@ -18,21 +18,6 @@ FW_DIR=$(readlink -f $FW_DIR) echo "FW dir is $FW_DIR" cd $FW_DIR -mkdir -p .dep # ChibiOS build's DEPDIR -mkdir -p build # ChibiOS build's BUILDDIR -echo "Calling make for the main firmware..." -make -j6 -r PROJECT_BOARD=$PROJECT_BOARD PROJECT_CPU=$PROJECT_CPU BOARD_DIR=$BOARD_DIR -[ -e build/fome.hex ] || { echo "FAILED to compile by $SCRIPT_NAME with $PROJECT_BOARD $DEBUG_LEVEL_OPT and $EXTRA_PARAMS"; exit 1; } -if [ "${USE_OPENBLT-no}" = "yes" ]; then - # TODO: why is this rm necessary? - rm -f pch/pch.h.gch/* - rm engine_modules_generated* - rm modules_list_generated* - echo "Calling make for the bootloader..." - cd bootloader; make -j6 PROJECT_BOARD=$PROJECT_BOARD PROJECT_CPU=$PROJECT_CPU BOARD_DIR=$BOARD_DIR; cd .. - [ -e bootloader/blbuild/fome_bl.hex ] || { echo "FAILED to compile OpenBLT by $SCRIPT_NAME with $PROJECT_BOARD"; exit 1; } -fi - if uname | grep "NT"; then export HEX2DFU=../misc/encedo_hex2dfu/hex2dfu.exe else @@ -44,6 +29,36 @@ mkdir -p deliver rm -rf deliver/* mkdir -p deliver/temp +# Build bootloader first (if OpenBLT) so we can embed its image in the main firmware +if [ "${USE_OPENBLT-no}" = "yes" ]; then + mkdir -p .dep + mkdir -p build + echo "Calling make for the bootloader..." + cd bootloader; make -j6 PROJECT_BOARD=$PROJECT_BOARD PROJECT_CPU=$PROJECT_CPU BOARD_DIR=$BOARD_DIR; cd .. + [ -e bootloader/blbuild/fome_bl.hex ] || { echo "FAILED to compile OpenBLT by $SCRIPT_NAME with $PROJECT_BOARD"; exit 1; } + + # Add a checksum to the bootloader image + $FW_DIR/config/boards/write_firmware_checksum.sh bootloader/blbuild/fome_bl.elf deliver/temp/bootloader + + # Generate bootloader image header for embedding in main firmware + # static const ensures the image is stored in flash, not copied to RAM + echo "Generating bootloader_image.h..." + xxd -i deliver/temp/bootloader.bin \ + | sed 's/deliver_temp_bootloader_bin/bootloader_image/g; s/^unsigned/static const unsigned/' \ + > generated/bootloader_image.h + + # Clean up shared build artifacts before firmware build + rm -f pch/pch.h.gch/* + rm -f engine_modules_generated* + rm -f modules_list_generated* +fi + +mkdir -p .dep # ChibiOS build's DEPDIR +mkdir -p build # ChibiOS build's BUILDDIR +echo "Calling make for the main firmware..." +make -j6 -r PROJECT_BOARD=$PROJECT_BOARD PROJECT_CPU=$PROJECT_CPU BOARD_DIR=$BOARD_DIR +[ -e build/fome.hex ] || { echo "FAILED to compile by $SCRIPT_NAME with $PROJECT_BOARD $DEBUG_LEVEL_OPT and $EXTRA_PARAMS"; exit 1; } + # Add a checksum to the main firmware image $FW_DIR/config/boards/write_firmware_checksum.sh build/fome.elf deliver/temp/main_firmware @@ -58,13 +73,9 @@ fi # bootloader and combined image if [ "$USE_OPENBLT" = "yes" ]; then - # Add a checksum to the bootloader image - $FW_DIR/config/boards/write_firmware_checksum.sh bootloader/blbuild/fome_bl.elf deliver/temp/bootloader - + # Bootloader checksum was already added before firmware build (above) cp deliver/temp/bootloader.srec deliver/fome_bl.srec - echo "$SCRIPT_NAME: invoking hex2dfu for OpenBLT" - echo "$SCRIPT_NAME: invoking hex2dfu for combined OpenBLT+FOME image" $HEX2DFU -i deliver/temp/bootloader.hex -i deliver/temp/main_firmware.hex -b deliver/fome.bin fi From ee0cae9fc2176310ce45ff782c95d61d5f69c82d Mon Sep 17 00:00:00 2001 From: Matthew Kennedy Date: Tue, 24 Feb 2026 14:18:00 -0800 Subject: [PATCH 2/4] bootloader update from firmware --- .../controllers/system/bootloader_updater.cpp | 93 ++++++++++++++++++- .../controllers/system/bootloader_updater.h | 2 +- firmware/hw_layer/ports/mpu_util.h | 6 ++ .../hw_layer/ports/stm32/stm32_common.cpp | 19 ++++ firmware/rusefi.cpp | 2 +- 5 files changed, 119 insertions(+), 3 deletions(-) diff --git a/firmware/controllers/system/bootloader_updater.cpp b/firmware/controllers/system/bootloader_updater.cpp index 2d164daea1..e124706096 100644 --- a/firmware/controllers/system/bootloader_updater.cpp +++ b/firmware/controllers/system/bootloader_updater.cpp @@ -3,7 +3,92 @@ #include "bootloader_updater.h" #include "crc_accelerator.h" -void checkBootloaderIntegrity() { +// uint32_t is unsigned long on ARM, but this printf handles %x for 32-bit values +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wformat" + +#if EFI_USE_OPENBLT && CORTEX_MODEL == 7 +#include "flash_int.h" + +// This header is generated during the build from the checksummed bootloader binary +#include "bootloader_image.h" + +static const size_t checksumOffset = 0x1C; + +static uint32_t getStoredCrc(const uint8_t* image) { + return *reinterpret_cast(image + checksumOffset); +} + +static void flashBootloader() { + // Verify the embedded image has a valid integrity check before we trust it + if (!checkFirmwareImageIntegrity(reinterpret_cast(bootloader_image))) { + efiPrintf("Bootloader update: embedded image integrity check FAILED, aborting"); + return; + } + + uint32_t embeddedCrc = getStoredCrc(bootloader_image); + uint32_t flashCrc = getStoredCrc(reinterpret_cast(FLASH_BASE)); + bool currentBootloaderValid = checkFirmwareImageIntegrity(FLASH_BASE); + + if (currentBootloaderValid && embeddedCrc == flashCrc) { + efiPrintf("Bootloader is up to date (CRC 0x%08x)", flashCrc); + + // Bootloader is valid, but make sure boot address points to it + if (getBootAddress() != FLASH_BASE) { + efiPrintf("Boot address was 0x%08x, correcting to bootloader", getBootAddress()); + postBootloaderUpdate(); + } + + return; + } + + if (!currentBootloaderValid) { + efiPrintf("Bootloader update: current bootloader is CORRUPT, updating..."); + } else { + efiPrintf("Bootloader update: CRC mismatch (flash 0x%08x, embedded 0x%08x), updating...", + flashCrc, embeddedCrc); + } + + // Set boot address to firmware so MCU stays bootable during the update + preBootloaderUpdate(); + + // Erase the bootloader flash sector(s) + int eraseResult = intFlashErase(FLASH_BASE, bootloader_image_len); + if (eraseResult != FLASH_RETURN_SUCCESS) { + efiPrintf("Bootloader update: erase FAILED (%d)", eraseResult); + // Boot address stays at firmware - MCU is still bootable + return; + } + + // Write the embedded bootloader image + int writeResult = intFlashWrite(FLASH_BASE, reinterpret_cast(bootloader_image), bootloader_image_len); + if (writeResult != FLASH_RETURN_SUCCESS) { + efiPrintf("Bootloader update: write FAILED (%d)", writeResult); + // Boot address stays at firmware - MCU is still bootable + return; + } + + // Verify the written data matches + if (!intFlashCompare(FLASH_BASE, reinterpret_cast(bootloader_image), bootloader_image_len)) { + efiPrintf("Bootloader update: verify FAILED - data mismatch after write"); + // Boot address stays at firmware - MCU is still bootable + return; + } + + // Verify the written image passes integrity check + if (!checkFirmwareImageIntegrity(FLASH_BASE)) { + efiPrintf("Bootloader update: verify FAILED - integrity check failed after write"); + // Boot address stays at firmware - MCU is still bootable + return; + } + + // Success - restore boot address to bootloader + postBootloaderUpdate(); + efiPrintf("Bootloader update: SUCCESS (new CRC 0x%08x)", embeddedCrc); +} +#endif // EFI_USE_OPENBLT && CORTEX_MODEL == 7 + +void updateBootloader() { #if CORTEX_MODEL == 7 efiPrintf("Current boot address: 0x%08x", getBootAddress()); #endif @@ -16,4 +101,10 @@ void checkBootloaderIntegrity() { } else { efiPrintf("Bootloader integrity check failed - here be dragons!"); } + +#if EFI_USE_OPENBLT && CORTEX_MODEL == 7 + flashBootloader(); +#endif } + +#pragma GCC diagnostic pop diff --git a/firmware/controllers/system/bootloader_updater.h b/firmware/controllers/system/bootloader_updater.h index 4813632029..3f18e8fee4 100644 --- a/firmware/controllers/system/bootloader_updater.h +++ b/firmware/controllers/system/bootloader_updater.h @@ -1,3 +1,3 @@ #pragma once -void checkBootloaderIntegrity(); +void updateBootloader(); diff --git a/firmware/hw_layer/ports/mpu_util.h b/firmware/hw_layer/ports/mpu_util.h index 72dcee935c..f3e3b00f44 100644 --- a/firmware/hw_layer/ports/mpu_util.h +++ b/firmware/hw_layer/ports/mpu_util.h @@ -78,6 +78,12 @@ BOR_Result_t BOR_Set(BOR_Level_t BORValue); uintptr_t getBootAddress(); bool setBootAddress(uintptr_t address); +// Bootloader update safety functions (F7/H7 only) +// Sets boot address to firmware so MCU remains bootable if power is lost during bootloader erase/write +void preBootloaderUpdate(); +// Restores boot address to bootloader after successful update +void postBootloaderUpdate(); + void assertInterruptPriority(const char* func, uint8_t expectedPrio); extern "C" { diff --git a/firmware/hw_layer/ports/stm32/stm32_common.cpp b/firmware/hw_layer/ports/stm32/stm32_common.cpp index d9495b7601..413655b546 100644 --- a/firmware/hw_layer/ports/stm32/stm32_common.cpp +++ b/firmware/hw_layer/ports/stm32/stm32_common.cpp @@ -486,6 +486,25 @@ bool setBootAddress(uintptr_t address) { return status == HAL_OK; } + +// uint32_t is unsigned long on ARM, but this printf handles %x for 32-bit values +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wformat" + +void preBootloaderUpdate() { + // Set boot address to the main firmware so that if power is lost during + // bootloader erase/write, the MCU still boots into working firmware + setBootAddress(SCB->VTOR); + efiPrintf("Boot address set to firmware: 0x%08x", SCB->VTOR); +} + +void postBootloaderUpdate() { + // Restore boot address to the bootloader at the start of flash + setBootAddress(FLASH_BASE); + efiPrintf("Boot address restored to bootloader: 0x%08x", FLASH_BASE); +} + +#pragma GCC diagnostic pop #endif // CORTEX_MODEL == 7 void baseMCUInit(void) { diff --git a/firmware/rusefi.cpp b/firmware/rusefi.cpp index 5ce776e3d7..adf2f538c1 100644 --- a/firmware/rusefi.cpp +++ b/firmware/rusefi.cpp @@ -216,7 +216,7 @@ void runRusEfi() { initMainLoop(); #if EFI_USE_OPENBLT - checkBootloaderIntegrity(); + updateBootloader(); #endif runMainLoop(); From f1f7335611a19cf6f6a53fc55b472e0d3c047818 Mon Sep 17 00:00:00 2001 From: Matthew Kennedy Date: Tue, 24 Feb 2026 14:24:01 -0800 Subject: [PATCH 3/4] check main firmware integrity for fun too --- .../controllers/system/bootloader_updater.cpp | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/firmware/controllers/system/bootloader_updater.cpp b/firmware/controllers/system/bootloader_updater.cpp index e124706096..c2e52757cd 100644 --- a/firmware/controllers/system/bootloader_updater.cpp +++ b/firmware/controllers/system/bootloader_updater.cpp @@ -45,8 +45,8 @@ static void flashBootloader() { if (!currentBootloaderValid) { efiPrintf("Bootloader update: current bootloader is CORRUPT, updating..."); } else { - efiPrintf("Bootloader update: CRC mismatch (flash 0x%08x, embedded 0x%08x), updating...", - flashCrc, embeddedCrc); + efiPrintf( + "Bootloader update: CRC mismatch (flash 0x%08x, embedded 0x%08x), updating...", flashCrc, embeddedCrc); } // Set boot address to firmware so MCU stays bootable during the update @@ -94,14 +94,19 @@ void updateBootloader() { #endif // Bootloader always lives in the first page of flash - FLASH_BASE - bool bootloaderIntact = checkFirmwareImageIntegrity(FLASH_BASE); - - if (bootloaderIntact) { + if (checkFirmwareImageIntegrity(FLASH_BASE)) { efiPrintf("Bootloader integrity check OK!"); } else { efiPrintf("Bootloader integrity check failed - here be dragons!"); } + // Check our own firmware image too - not much we can do, but good to know + if (checkFirmwareImageIntegrity(SCB->VTOR)) { + efiPrintf("Firmware integrity check OK!"); + } else { + efiPrintf("Firmware integrity check failed!"); + } + #if EFI_USE_OPENBLT && CORTEX_MODEL == 7 flashBootloader(); #endif From 5cf4d5edacfaf3998ab6dc3ff3e2869998592af4 Mon Sep 17 00:00:00 2001 From: Matthew Kennedy Date: Tue, 24 Feb 2026 14:30:43 -0800 Subject: [PATCH 4/4] unnecessary pragmas --- firmware/controllers/system/bootloader_updater.cpp | 6 ------ firmware/hw_layer/ports/stm32/stm32_common.cpp | 9 ++------- 2 files changed, 2 insertions(+), 13 deletions(-) diff --git a/firmware/controllers/system/bootloader_updater.cpp b/firmware/controllers/system/bootloader_updater.cpp index c2e52757cd..156992bb06 100644 --- a/firmware/controllers/system/bootloader_updater.cpp +++ b/firmware/controllers/system/bootloader_updater.cpp @@ -3,10 +3,6 @@ #include "bootloader_updater.h" #include "crc_accelerator.h" -// uint32_t is unsigned long on ARM, but this printf handles %x for 32-bit values -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wformat" - #if EFI_USE_OPENBLT && CORTEX_MODEL == 7 #include "flash_int.h" @@ -111,5 +107,3 @@ void updateBootloader() { flashBootloader(); #endif } - -#pragma GCC diagnostic pop diff --git a/firmware/hw_layer/ports/stm32/stm32_common.cpp b/firmware/hw_layer/ports/stm32/stm32_common.cpp index 413655b546..3eeb578993 100644 --- a/firmware/hw_layer/ports/stm32/stm32_common.cpp +++ b/firmware/hw_layer/ports/stm32/stm32_common.cpp @@ -487,24 +487,19 @@ bool setBootAddress(uintptr_t address) { return status == HAL_OK; } -// uint32_t is unsigned long on ARM, but this printf handles %x for 32-bit values -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wformat" - void preBootloaderUpdate() { // Set boot address to the main firmware so that if power is lost during // bootloader erase/write, the MCU still boots into working firmware setBootAddress(SCB->VTOR); - efiPrintf("Boot address set to firmware: 0x%08x", SCB->VTOR); + efiPrintf("Boot address set to firmware: 0x%08x", (uintptr_t)SCB->VTOR); } void postBootloaderUpdate() { // Restore boot address to the bootloader at the start of flash setBootAddress(FLASH_BASE); - efiPrintf("Boot address restored to bootloader: 0x%08x", FLASH_BASE); + efiPrintf("Boot address restored to bootloader: 0x%08x", (uintptr_t)FLASH_BASE); } -#pragma GCC diagnostic pop #endif // CORTEX_MODEL == 7 void baseMCUInit(void) {