Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 31 additions & 20 deletions firmware/config/boards/common_make.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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

Expand All @@ -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
Expand Down
98 changes: 94 additions & 4 deletions firmware/controllers/system/bootloader_updater.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,107 @@
#include "bootloader_updater.h"
#include "crc_accelerator.h"

void checkBootloaderIntegrity() {
#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<const uint32_t*>(image + checksumOffset);
}

static void flashBootloader() {
// Verify the embedded image has a valid integrity check before we trust it
if (!checkFirmwareImageIntegrity(reinterpret_cast<uintptr_t>(bootloader_image))) {
efiPrintf("Bootloader update: embedded image integrity check FAILED, aborting");
return;
}

uint32_t embeddedCrc = getStoredCrc(bootloader_image);
uint32_t flashCrc = getStoredCrc(reinterpret_cast<const uint8_t*>(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<const char*>(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<const char*>(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

// 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
}
2 changes: 1 addition & 1 deletion firmware/controllers/system/bootloader_updater.h
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
#pragma once

void checkBootloaderIntegrity();
void updateBootloader();
6 changes: 6 additions & 0 deletions firmware/hw_layer/ports/mpu_util.h
Original file line number Diff line number Diff line change
Expand Up @@ -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" {
Expand Down
14 changes: 14 additions & 0 deletions firmware/hw_layer/ports/stm32/stm32_common.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -486,6 +486,20 @@ bool setBootAddress(uintptr_t address) {

return status == HAL_OK;
}

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", (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", (uintptr_t)FLASH_BASE);
}

#endif // CORTEX_MODEL == 7

void baseMCUInit(void) {
Expand Down
2 changes: 1 addition & 1 deletion firmware/rusefi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ void runRusEfi() {
initMainLoop();

#if EFI_USE_OPENBLT
checkBootloaderIntegrity();
updateBootloader();
#endif

runMainLoop();
Expand Down
Loading