Guideline for nRF5340 network-core FOTA over MCUmgr (with app-core rollback)

Environment

  • nRF Connect SDK: v3.2.1
  • Zephyr: 4.2.99 (NCS fork)
  • CMake (bundled): 3.21.0
  • Board: nrf5340dk_nrf5340_cpuapp
  • OS: Linux

What already works: application-core FOTA (image 0)

This has worked reliably for a long time:

mcumgr ... image upload -e -n 0 app.signed.bin
mcumgr ... image list          # note new hash in slot 1
mcumgr ... image test <hash>
mcumgr ... reset
mcumgr ... image confirm <hash>

Swap + revert behave exactly as expected.

Goal

Add network-core (hci_ipc) FOTA through MCUmgr in the same way (image 1), without losing the application core's swap/revert.

Config we added

# sysbuild
SB_CONFIG_SECURE_BOOT_NETCORE=y
SB_CONFIG_NETCORE_APP_UPDATE=y
# application prj.conf
CONFIG_MCUMGR_GRP_IMG_UPDATABLE_IMAGE_NUMBER=2

Network-core FOTA steps we follow

mcumgr ... image upload -e -n 1 signed_by_mcuboot_and_b0_hci_ipc.bin
mcumgr ... image list          # image=1 slot=1 appears, secondary magic=good
mcumgr ... image confirm <hash>
mcumgr ... reset               # -> device bricked, only recoverable with: nrfjprog --qspieraseall

What we have tried

In every combination below, staging works fine (image upload -n 1image=1 slot=1, magic=good), but the result is always the same: after image test/confirm + reset, the device bricks (reboot loop, only recoverable with nrfjprog --qspieraseall).

  • A) SECURE_BOOT_NETCORE + NETCORE_APP_UPDATE only (app core keeps BOOT_SWAP_USING_MOVE). → Brick on reset. RTT shows MCUboot validating image 1 OK, then abort() right after boot_verify_slot_dependencies. The net image seems to go through the app-core swap path. We noticed the network-core swap-skip in boot_slots_compatible() (swap_move.c) is guarded by #ifdef PM_S1_ADDRESS, which is undefined in this config.

  • B) Added SECURE_BOOT_APPCORE too (to define PM_S1_ADDRESS). → Build assert PM_S0_SIZE == PM_S1_SIZE; fixed by removing our explicit CONFIG_PM_PARTITION_SIZE_MCUBOOT so PM sizes S0/S1 equally. The device still bricks on reset — now apparently later, possibly during the PCD copy to the network core.

  • We have not switched MCUboot to OVERWRITE_ONLY (used by the NCS ref_smp_svr_ext_flash sample for nRF53), because it removes the application core's revert capability, which we need to keep.

So we seem to be missing a config combination: every variant we tried either bricks immediately or bricks "later", always around the network-core update on reset.

Question

Is there a documented minimal configuration for nRF5340 network-core FOTA over MCUmgr (image 1) that coexists with application-core image swap + revert — i.e. without forcing MCUboot into OVERWRITE_ONLY? Does enabling network-core update with BOOT_SWAP_USING_MOVE on the app core require application-core secure boot (B0), or is there a supported combination we are missing?

A reference set of Kconfig options for this exact case would be ideal.

Parents Reply Children
  • Hi  ,

    Thanks for your answer.

    My goal is to support both scenarios: simultaneous FOTA updates for both cores, while also retaining the ability to perform FOTA updates of the target core independently. But if just supporting individual updates (non-simultaneous mode) is easier, it is fine for me.


    The secondary app must be placed in external RAM, as the application itself is too big to allocate both in internal flash. 
    I am using the same external QSPI flash available on the nRF5340 DK.

    I have already gone through the guides you mentioned, and I have now successfully performed a network core FOTA update as pointed in my second comment, however, this was only possible after applying the following configuration (as the guide is pointing):

    # STEP 7.6 - Simultaneous FOTA does not support rollback
    SB_CONFIG_MCUBOOT_MODE_OVERWRITE_ONLY=y

    As soon as I enabled this option, I lost the rollback capability for application core updates.

    So, as far as you know, is sacrificing application core rollback functionality a requirement in order to support network core FOTA updates? Or is there a way to achieve both network core FOTA and application core rollback at the same time?

  • Hi Selrac, 

    Could you try CONFIG_USE_NRF53_MULTI_IMAGE_WITHOUT_UPGRADE_ONLY=y and don't set SB_CONFIG_MCUBOOT_MODE_OVERWRITE_ONLY ? 

    I haven't tried myself but it seems that the configuration is what you need. You can read a little bit more about it here: 
    https://nrfconnectdocs.nordicsemi.com/ncs/3.3.1/kconfig/index.html#CONFIG_USE_NRF53_MULTI_IMAGE_WITHOUT_UPGRADE_ONLY

    https://nrfconnectdocs.nordicsemi.com/ncs/2.4.2/nrf/working_with_nrf/nrf53/nrf5340.html#simultaneous-multi-image-dfu

     Multi-image or non-simultaneous network core update example with BOOT_SWAP_USING_SCRATCH? 

    My understanding is that when you do only application core update (only one bin file) you can select to do Test and Confirm. 


    But if you send 2 images at the same time you should stick to Confirm only. 

  • I have tried this configuration.
    Then the APP CORE is updated properly and the SWAP/ROLLBACK works fine.

    BUT, when I try to perform the NET CORE FOTA, the device gets totally bricked until I perform a QSPI Flash erase:

    nrfjprog --qspieraseall --family NRF53

  • After iterating on Claude, the report and explanation for my problem suing the SDK 3.2.1:

    Why the default behavior bricks the device in swap mode (NCS v3.2.1)

    With MCUboot in BOOT_SWAP_USING_MOVE (the NCS default, and the mode that
    provides application-core rollback), a staged image-1 update takes the swap
    path instead. Trace (all references NCS v3.2.1):

    1. context_boot_go() determines an upgrade swap type (PERM/TEST) for
      image 1 and calls boot_perform_update()  boot_swap_image()
      (bootloader/mcuboot/boot/bootutil/src/loader.c). The stock
      boot_perform_update_hook is an empty stub, so nothing keeps the net core
      out of this generic swap path — this stub is exactly what the fix overrides
      (see The fix below).
    2. boot_swap_image() reads the primary header to size the swap. The
      hook hands it the fake header: ih_magic = IMAGE_MAGIC,
      ih_img_size = PM_CPUNET_APP_SIZE.
    3. Seeing a "valid" image, it calls
      boot_read_image_size(state, BOOT_SLOT_PRIMARY, ...), which reads the TLV
      info trailer at ih_hdr_size + ih_img_size  from the RAM flash
      simulator, which contains garbage
       (uninitialized RAM / zeros).
    4. The TLV magic check fails → boot_read_image_size() returns
      BOOT_EBADIMAGE  assert(rc == 0) in boot_swap_image() fires →
      abort().
    5. The device reboots. The staged image in external flash is still
      pending
      , so every boot repeats steps 1–4: a reboot loop only
      recoverable by erasing the QSPI (nrfjprog --qspieraseall).

    No Kconfig combination avoids this in v3.2.1 — the failing code path is the
    generic swap engine itself. In particular:

    • SB_CONFIG_SECURE_BOOT_APPCORE (defines PM_S1_ADDRESS, which gates the
      netcore exemption in boot_slots_compatible()) does not help: the
      slots happen to be "compatible" anyway (both 256 KB / 4 KB sectors), so the
      swap proceeds and crashes in step 3 regardless.
    • CONFIG_USE_NRF53_MULTI_IMAGE_WITHOUT_UPGRADE_ONLY (auto-set by sysbuild
      when multi-image update is enabled without overwrite-only) merely allows
      the build — it is flagged [dangerous] and does not make the update work
      (see Two separate failure modes below).
    • The official documentation ("Simultaneous multi-image DFU with nRF5340")
      requires CONFIG_BOOT_UPGRADE_ONLY for this feature, and every Nordic
      reference (including the Matter samples) forces
      MCUBOOT_MODE_OVERWRITE_ONLY when network-core update is enabled.

    So the supported configuration removes application-core rollback, and the
    permitted configuration bricks the device. Hence this module.

    Status in newer NCS releases

    Verified locally against ~/ncs/v3.3.1 (sdk-mcuboot + sdk-nrf):

    • The crash itself is fixed: boot_swap_image() now special-cases the
      network-core image (if (image_index == CONFIG_MCUBOOT_NETWORK_CORE_IMAGE_NUMBER))
      and reads the size from the secondary slot instead of the faked primary
      header, so the assert(rc == 0) no longer fires.
    • Everything else is unchanged: nrf53_hooks.c still leaves
      boot_perform_update_hook unimplemented (return BOOT_HOOK_REGULAR), the
      Kconfig still labels the combination [dangerous], and the official
      documentation still requires overwrite-only.
    • v3.3.1 does add a clean inline PCD path in boot_validated_swap_type(),
      but it is gated to !CONFIG_NRF53_MULTI_IMAGE_UPDATE (the single-image-pair
      / recovery mode). Our build uses CONFIG_NRF53_MULTI_IMAGE_UPDATE, so that
      path does not apply to us — Nordic still has no clean net-core path for
      the multi-image configuration.
    • On v3.3.1 without this module (multi-image mode), an image-1 update would
      run a full, pointless swap-move between QSPI and RAM (slow, wears the QSPI,
      writes RAM garbage into the secondary slot as the "old image") and the
      revert-bricks-the-netcore hazard remains live.
Related