* fix(bootloader): scrub uncorrectable flash ECC errors on STM32H7 before boot A torn write to a flash row (power lost mid program/erase) can leave it with inconsistent ECC. On H7 a CPU read of such a row raises an uncorrectable double-bit ECC error -> bus fault, so the application hard faults on every boot before printing anything, and only a mass erase recovers it: reflashing does not, because neither the app nor the bootloader image touches the parameter sector. Scan the application and parameter flash with DMA right after clock_init (a DMA read latches FLASH_SR DBECCERR instead of bus-faulting the CPU) and erase any sector that holds an uncorrectable error. A corrupt parameter sector then re-seeds to defaults on the next app boot; corrupt app flash fails the image check and stays in the bootloader for reflash. Mirrors ArduPilot's bootloader ECC scrub. H7-gated; the scan runs before the interface DMA is brought up, so DMA1 stream 0 is uncontended. * test(tools): add STM32H7 flash ECC fault injection tool A bench helper that deterministically plants an uncorrectable (double-bit) flash ECC error on an STM32H7, reproducing the torn-write parameter-store brick. Used to validate the bootloader ECC scrub and the parameter re-seed recovery on hardware: plant the fault, then confirm the board boots clean instead of hard-faulting every boot. brick_ecc.c is a freestanding stub that double-programs one flash word with two different single-bit clears (the recipe that yields an inconsistent ECC); brick_ecc.sh builds it, loads it into AXI SRAM over ST-Link, runs it, and resets the board. README documents usage and how to retarget another board. * fix(bootloader): require DMA transfer-complete for a clean ECC scan chunk The stream self-disables on both transfer-complete and transfer-error. A non-ECC bus error (unreachable scan buffer, rejected FIFO config) previously read as a clean chunk, silently turning the scan into a no-op. Require TCIF so an unread chunk takes the skip-the-scan fallback instead of passing. Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com> --------- Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>
Flash ECC fault injection (STM32H7)
A bench tool that deterministically plants an uncorrectable (double-bit) flash ECC error on an STM32H7 board, to test the bootloader's ECC scrub and the parameter-store recovery path.
Why this exists
On STM32H7, a torn write to a flash row (power lost mid program/erase) can leave a 256-bit flash word with inconsistent ECC. A CPU read of such a word raises an uncorrectable ECC error, which becomes a precise bus fault. On a board that stores parameters in flash, this hits find_entry() during parameter_flashfs_init() very early in boot, so the board hard-faults on every boot. Reflashing the app or bootloader does not help (neither image touches the parameter sector); only a mass erase recovers it.
The bootloader ECC scrub (platforms/nuttx/src/bootloader/stm/stm32h7/ecc_scrub.c) DMA-scans the application and parameter flash before booting the app and erases any sector with an uncorrectable error. This tool plants the exact fault that scrub is meant to recover from, so the scrub and the param re-seed can be validated on hardware.
How it works
A complete double-program of a flash word does not always yield an uncorrectable codeword. The recipe that does (verified on H743): program one 256-bit word twice with no erase between, clearing a different single bit each time — 0xFFFFFFFE then 0xFFFFFFFD. The stored data becomes 0xFFFFFFFC but the stored ECC is ecc(P1) & ecc(P2), inconsistent with the data → uncorrectable. (A complementary pair such as 0x55…/0xAA… collapses to data 0 with a valid ECC and does not fault.)
brick_ecc.c is a tiny freestanding stub: it masks interrupts, unlocks flash bank 2, erases the target sector, double-programs the target word, then halts on a breakpoint. It runs from AXI SRAM because st-link cannot write the Cortex-M7 TCMs over SWD; the board is reset afterwards so clobbering RAM is harmless.
Requirements
- Target board connected over SWD (ST-Link).
stlink-tools(providesst-util).gcc-arm-none-eabi(provides the compiler,objcopy,nm).- An ARM-capable gdb.
gdb-multiarchis preferred — the 2020-q4arm-none-eabi-gdblinks againstlibncurses.so.5, which modern distros do not ship.
sudo apt-get install stlink-tools gcc-arm-none-eabi gdb-multiarch
Usage
The defaults target the ARK_FPV parameter sector (STM32H743, bank 2, sector 7 @ 0x081E0000).
./brick_ecc.sh
The script builds the stub, loads it into RAM, runs it, reads back the planted word (a bad-ECC word reports Cannot access memory — that is the expected success indicator), and resets the board. Then power-cycle and watch the NSH console:
- Non-hardened bootloader: hard-faults on every boot (
BFAR=0x081e0000, fault infind_entry). - Bootloader with the ECC scrub: boots clean — the scrub erased the parameter sector, and the app re-seeds parameters to defaults.
Adapting to another board
The defaults are ARK_FPV-specific. For a different H7 target, edit brick_ecc.c:
ADDR— a word inside the sector you want to corrupt (e.g. the parameter sector base for that board, from itssector_mapinboards/<vendor>/<board>/src/init.c).SNB— the sector number within the bank for that address.- The
FLASH_*2register addresses target bank 2; for a word in bank 1 use the bank-1 registers (FLASH_KEYR10x52002004,FLASH_CR10x5200200C,FLASH_SR10x52002010,FLASH_CCR10x52002014).
LOAD_ADDR/STACK in brick_ecc.sh assume free AXI SRAM at 0x24040000; adjust if your target's RAM map differs.
⚠️ This tool intentionally corrupts flash. Use it only on a development board you are willing to mass-erase. It is a bench/test helper and is not built or shipped as part of any firmware image.