MCUboot Integration Notes: Slot Layout and Key Management
MCUboot is well-documented in theory. The devil is in image slot alignment, key provisioning, and what happens during a failed upgrade on constrained flash.

MCUboot is well-documented in theory. The devil is in image slot alignment, key provisioning, and what happens during a failed upgrade on constrained flash — we integrated MCUboot 1.10.0 on three boards before patterns stabilized.
Answer first
Read imgtool.py output and your linker script together. Slot size = max image + trailer + swap scratch requirements. Keys live in CI HSM, not in repo. Test swap-with-power-cut before fleet push — MCUboot handles it if scratch partition sized correctly; your board config probably undersized scratch first time.
Slot layout
Typical nRF52840 (1 MB internal)
From our shipped pm.yml equivalent:
0x00000 MCUboot 48 KB
0x0C000 Slot 0 472 KB (primary)
0x82000 Slot 1 472 KB (secondary)
0xF8000 NVS 32 KB (outside swap)
Image max must include TLV trailer (~4 KB headroom for Ed25519 + dependencies). Build fails at signing if .bin + trailer exceeds slot — good. Silent failure mode: link script allows 480 KB app, imgtool rejects, CI green on unsigned target — enforce signed artifact gate.
STM32H743 + external QSPI
Primary on internal, secondary on W25Q128 — MCUboot swap-using-offset with scratch in QSPI sector aligned to 4 KB erase.
Errata lesson: QSPI memory-mapped read during execute-from-internal OK; don't XIP from QSPI on slot you're erasing mid-swap. Bootloader runs from internal only during swap.
Cross-reference OTA design patterns for staging partition policy — MCUboot doesn't replace download integrity; it protects boot path.
Alignment requirements
MCUboot image header 32-byte aligned start. ARM vector table alignment 0x200 on v8-M with TrustZone — check your core.
imgtool create \
--align 8 \
--header-size 0x200 \
--pad-header \
--slot-size 0x76000 \
--version 1.2.3+0 \
--key @/secure/path/ed25519.pem \
app.bin signed.bin
--align 8 minimum; flash write granularity on STM32 is 8 bytes; nRF52 4 bytes — match worst device if unified CI.
Trailer magic at end of slot — imgtool computes; manual hex edit breaks swap.
Key management
What we do
| Key | Storage | Use |
|---|---|---|
| Dev Ed25519 | 1Password vault, CI secret | PR builds, dev devices |
| Prod Ed25519 | AWS CloudHSM / YubiHSM | Release tag only |
| Encryption key (optional) | Separate HSM key | Confidential firmware on RT1064 |
Public key embedded as bootutil_key array in keys.c — prod and dev different C files selected by Kconfig CONFIG_BOOT_SIGNATURE_KEY_FILE.
Never ship dev key on production board revision >= P1.
Rotation story
MCUboot supports multiple key slots in image TLV for rotation — we haven't rotated prod key yet. Runbook drafted: sign new releases with key B, include key B hash in image signed by key A for transition window — read MCUboot docs section "key revocation" before you need it.
Failed upgrade on constrained flash
Scenario A: swap interrupted
MCUboot swap status in scratch/metadata sector — resume or revert on next boot. Requires scratch ≥ largest sector touched in swap algorithm. nRF52 default scratch 4 KB — sufficient. Custom external flash layout: we needed 16 KB scratch — undetected until power-cut test.
Scenario B: invalid signature after download
App marks pending without verify — bricked if bootloader tries boot and fails signature repeatedly. Policy: only bootloader sets pending after boot_go() validates staging copy; app calls boot_set_pending only after imgtool-compatible self-check or MCUboot img_mgmt state.
Scenario C: NVS corruption during swap
App NVS in same flash bank as slot — erase during swap wiped config. Moved NVS to protected partition outside slot regions; see USB-PD board where config partition shared PMIC cal data — same lesson.
Integration with Zephyr vs NuttX
- Zephyr:
CONFIG_BOOTLOADER_MCUBOOT,imgtoolinwest sign, sysbuild dual image — smoothest path. - NuttX: manual
Makefilehook +boardctlreset — workable; 2 days wiring on RT1064. - Bare metal: copy
bootutilsources — don't; use MCUboot as submodule with minimal port layer.
Scheduling interactions minimal; Zephyr migration notes mention OTA last in branch order — still true.
Debugging toolbox
# Verify signed image locally
imgtool verify --key /dev/stdin <<<"$PUBKEY_PEM" signed.bin
# Serial boot log
CONFIG_MCUBOOT_SERIAL=y # recovery UART — disable in prod + eFuse lock
GPIO bit-bang boot stage codes on bring-up — remove in prod or leak state to attackers with logic analyzer (acceptable on consumer puck with enclosure).
Production checklist
- Slot sizes in linker == imgtool
--slot-size - Signed build mandatory in release CI
- Power-cut test 100 cycles on EVT
- Security counter monotonic in hardware-backed storage
- Rollback confirm path tested (postmortem when not)
What I'd do next
Adopt MCUboot 2.x serial recovery only on development units with physical tamper switch — field UART recovery is attack surface.
Automate diff between imgtool getpub output and embedded keys.c on every release — human copy-paste caused one near-miss dev key in staging build.
MCUboot isn't magic — it's a contract between flash geometry, signing CI, and bootloader port quality. Nail the contract once per silicon family, then OTA stops being scary.
imgtool version pinning
CI pins imgtool from MCUboot submodule tag v1.10.0 — not pip install imgtool floating. TLV format drift between imgtool versions caused verify pass in CI, fail in bootloader once.
pip install ./bootloader/mcuboot/scripts/imgtool
imgtool version # must match release notes
Serial recovery attack surface
CONFIG_MCUBOOT_SERIAL=y saved bring-up; prod builds disable and blow OTP disallow debug if your threat model includes physical access. Consumer puck kept UART pads inside enclosure — policy doc states warranty void if opened.
Swap algorithm cheat sheet
| Mode | When | Scratch need |
|---|---|---|
| swap using move | internal flash both slots | metadata only |
| swap using offset | external QSPI secondary | offset + scratch sector |
| overwrite-only | A/B no rollback | none |
Pick once in mcuboot.conf; changing mid-product requires flash layout rev.
What I'd do next
Manish Bookreader
Electronics enthusiast, Embedded Systems Expert, Linux/Networking programmer, and Software Engineer passionate about AI, electronics, books, and cooking.

