Skip to content

sysbuild: image_signing: set ih_load_addr from slot0 partition#29021

Open
kapi-no wants to merge 1 commit into
nrfconnect:mainfrom
kapi-no:image-signing-inject-slot0-load-addr
Open

sysbuild: image_signing: set ih_load_addr from slot0 partition#29021
kapi-no wants to merge 1 commit into
nrfconnect:mainfrom
kapi-no:image-signing-inject-slot0-load-addr

Conversation

@kapi-no
Copy link
Copy Markdown
Contributor

@kapi-no kapi-no commented May 26, 2026

Summary

Fixes the NCS sysbuild signing pipeline so that the application image header carries a non-zero ih_load_addr, making CONFIG_MCUBOOT_CHECK_HEADER_LOAD_ADDRESS=y work for legitimate DFU updates instead of rejecting every package.

Background

MCUboot's CONFIG_MCUBOOT_CHECK_HEADER_LOAD_ADDRESS feature verifies that an image in the secondary slot is intended for the primary slot by comparing the ih_load_addr field in the image header against the primary slot boundaries (bootloader/mcuboot/boot/bootutil/src/loader.c, boot_validate_slot(), lines ~810-890):

if (BOOT_CURR_IMG(state) == CONFIG_MCUBOOT_APPLICATION_IMAGE_NUMBER) {
    min_addr = flash_area_get_off(BOOT_IMG_AREA(state, BOOT_SLOT_PRIMARY));
    max_addr = flash_area_get_size(BOOT_IMG_AREA(state, BOOT_SLOT_PRIMARY)) + min_addr;
    check_addresses = true;
}
#ifdef MCUBOOT_CHECK_HEADER_LOAD_ADDRESS
    internal_img_addr = secondary_hdr->ih_load_addr;
#endif
if (check_addresses == true && (internal_img_addr < min_addr || internal_img_addr >= max_addr)) {
    /* reject */
}

The NCS sysbuild signing script (nrf/cmake/sysbuild/image_signing.cmake) did not pass --load-addr or --rom-fixed to imgtool for the CONFIG_MCUBOOT_IMGTOOL_OVERWRITE_ONLY=y branch and for the default swap branch. The signed application image therefore had ih_load_addr = 0x0, which is outside the primary slot range, so MCUboot would reject every legitimate update.

The same gap exists in upstream zephyr/cmake/mcuboot.cmake; a follow-up fix is needed there for users that do not route through the NCS sysbuild signing script.

Changes

  1. nrf/cmake/sysbuild/image_signing.cmake — read the absolute slot0_partition address with dt_partition_addr(... REQUIRED ABSOLUTE) and pass it to imgtool as --rom-fixed ${slot0_partition_address} for both the OVERWRITE_ONLY and default swap branches. --rom-fixed matches the pattern already used by the cpunet image signing in nrf/cmake/sysbuild/b0_mcuboot_signing.cmake and avoids the spurious IMAGE_F_RAM_LOAD flag that --load-addr would set.
  2. nrf/sysbuild/CMakeLists.txt — add SB_CONFIG_SOC_SERIES_NRF53 to the list of platforms that route through the NCS sysbuild signing script. This makes non-PM nRF53 builds (such as the Fast Pair Locator Tag sample being migrated to DTS in PR samples: fast_pair: locator_tag: migrate nRF53 targets to DTS partitioning #28462) pick up the fix. Mirrors the existing treatment of SB_CONFIG_SOC_SERIES_NRF54L and SB_CONFIG_SOC_SERIES_NRF54H.

Verification

Built the Fast Pair Locator Tag sample for nrf5340dk/nrf5340/cpuapp against this branch + the DTS-migrated sample (PR #28462). Used imgtool dumpinfo to inspect the signed images and the resulting dfu_application.zip:

Image Before this PR After this PR
locator_tag.signed.bin (app) load_addr = 0x0, flags = 0x0 load_addr = 0xc000, flags = ROM_FIXED
signed_by_mcuboot_and_b0_ipc_radio.bin (cpunet) load_addr = 0x01002800, flags = ROM_FIXED unchanged

0xc000 matches the slot0_partition@c000 reg in samples/bluetooth/fast_pair/locator_tag/configuration/boards/nrf5340dk_nrf5340_cpuapp.overlay, derived from DT (no sample-level Kconfig workaround needed).

Notes

  • The IMAGE_F_ROM_FIXED flag is only consulted by MCUboot in MCUBOOT_DIRECT_XIP mode (boot_rom_address_check() in loader.c), so it has no runtime side effect in OVERWRITE_ONLY/swap configurations beyond providing the ih_load_addr value that the new CHECK_HEADER_LOAD_ADDRESS check needs.
  • An equivalent fix should land in upstream Zephyr zephyr/cmake/mcuboot.cmake for users that build outside the NCS sysbuild signing path. I am happy to follow up with a PR to the Zephyr fork once this lands.
  • Related: the cpunet check is currently a range membership check (min_addr <= ih_load_addr < max_addr), not equality to the slot start, so a deliberately mis-targeted cpunet image inside the slot still passes verification. Tightening that to require ih_load_addr == NETCPU_APP_SLOT_OFFSET is a separate MCUboot improvement.

Ref: NCSDK-38010

The MCUboot CHECK_HEADER_LOAD_ADDRESS feature
(CONFIG_MCUBOOT_CHECK_HEADER_LOAD_ADDRESS) verifies that an
image in the secondary slot is intended for the primary slot
by comparing the ih_load_addr field in the image header
against the primary slot boundaries. The NCS sysbuild signing
script did not pass --load-addr or --rom-fixed to imgtool for
the OVERWRITE_ONLY and default swap branches, leaving
ih_load_addr at zero in the signed application image. This
caused MCUboot to reject every legitimate application update
because zero falls outside the primary slot's address range.

Updated nrf/cmake/sysbuild/image_signing.cmake to read the
absolute slot0_partition address with dt_partition_addr
ABSOLUTE and pass it as --rom-fixed to imgtool for both the
OVERWRITE_ONLY and default swap branches. The ROM_FIXED flag
follows the pattern already used by the cpunet image signing
in nrf/cmake/sysbuild/b0_mcuboot_signing.cmake.

Added SB_CONFIG_SOC_SERIES_NRF53 to the list of platforms in
nrf/sysbuild/CMakeLists.txt that route through the NCS
sysbuild signing script instead of the upstream
zephyr/cmake/mcuboot.cmake. This makes non-PM nRF53 builds
(such as the Fast Pair Locator Tag sample migrated to DTS)
pick up the ih_load_addr fix. The change mirrors the existing
treatment of SB_CONFIG_SOC_SERIES_NRF54L and
SB_CONFIG_SOC_SERIES_NRF54H.

Verified on the nRF5340 DK Fast Pair Locator Tag sample
(nrf5340dk/nrf5340/cpuapp): the signed application image now
reports ih_load_addr = 0xc000 and flags = ROM_FIXED, matching
the slot0_partition definition in DTS. The cpunet image
signing remains unchanged (ih_load_addr = 0x01002800,
flags = ROM_FIXED).

The equivalent fix is needed in upstream Zephyr
(zephyr/cmake/mcuboot.cmake) for users that do not route
through the NCS sysbuild signing script.

Ref: NCSDK-38010

Signed-off-by: Kamil Piszczek <Kamil.Piszczek@nordicsemi.no>
@NordicBuilder
Copy link
Copy Markdown
Contributor

NordicBuilder commented May 26, 2026

CI Information

To view the history of this post, click the 'edited' button above
Build number: 3

Inputs:

Sources:

sdk-nrf: PR head: 7b4d2ba191123cf2490db0bfa630fcb3c7749299

more details

sdk-nrf:

PR head: 7b4d2ba191123cf2490db0bfa630fcb3c7749299
merge base: e795138a6e5a0743cc27bb1c47e378b8882476f6
target head (main): 5334ca64d8fdd179f0edbe6fc5e6f29967d01cb6
Diff

Github labels

Enabled Name Description
ci-disabled Disable the ci execution
ci-all-test Run all of ci, no test spec filtering will be done
ci-force-downstream Force execution of downstream even if twister fails
ci-run-twister Force run twister
ci-run-zephyr-twister Force run zephyr twister
List of changed files detected by CI (2)
cmake
│  ├── sysbuild
│  │  │ image_signing.cmake
sysbuild
│  │ CMakeLists.txt

Outputs:

Toolchain

Version: f0aa129f09
Build docker image: docker-dtr.nordicsemi.no/sw-production/ncs-build:f0aa129f09_5ea73affbf

Test Spec & Results: ✅ Success; ❌ Failure; 🟠 Queued; 🟡 Progress; ◻️ Skipped; ⚠️ Quarantine

  • ◻️ Toolchain - Skipped: existing toolchain is used
  • ✅ Build twister - Skipped: Skipping Build & Test as it succeeded in a previous run: 2
  • ❌ Integration tests
    • ✅ test-sdk-audio - Skipped: Job was skipped as it succeeded in a previous run
    • ✅ desktop52_verification - Skipped: Job was skipped as it succeeded in a previous run
    • ✅ test-fw-nrfconnect-apps_nrfdesktop - Skipped: Job was skipped as it succeeded in a previous run
    • ✅ test-fw-nrfconnect-apps - Skipped: Job was skipped as it succeeded in a previous run
    • ✅ test_ble_nrf_config - Skipped: Job was skipped as it succeeded in a previous run
    • ✅ test-fw-nrfconnect-ble_mesh - Skipped: Job was skipped as it succeeded in a previous run
    • ✅ test-fw-nrfconnect-ble_samples - Skipped: Job was skipped as it succeeded in a previous run
    • ✅ test-fw-nrfconnect-chip - Skipped: Job was skipped as it succeeded in a previous run
    • ✅ test-fw-nrfconnect-nfc - Skipped: Job was skipped as it succeeded in a previous run
    • ✅ test-fw-nrfconnect-nrf-iot_libmodem-nrf - Skipped: Job was skipped as it succeeded in a previous run
    • ✅ test-fw-nrfconnect-nrf-iot_zephyr_lwm2m - Skipped: Job was skipped as it succeeded in a previous run
    • ✅ test-fw-nrfconnect-nrf-iot_samples - Skipped: Job was skipped as it succeeded in a previous run
    • ✅ test-fw-nrfconnect-nrf-iot_lwm2m - Skipped: Job was skipped as it succeeded in a previous run
    • ✅ test-fw-nrfconnect-nrf-iot_thingy91 - Skipped: Job was skipped as it succeeded in a previous run
    • ✅ test-fw-nrfconnect-nrf_crypto - Skipped: Job was skipped as it succeeded in a previous run
    • ✅ test-fw-nrfconnect-rpc - Skipped: Job was skipped as it succeeded in a previous run
    • ✅ test-fw-nrfconnect-rs - Skipped: Job was skipped as it succeeded in a previous run
    • ✅ test-fw-nrfconnect-fem - Skipped: Job was skipped as it succeeded in a previous run
    • ✅ test-fw-nrfconnect-tfm - Skipped: Job was skipped as it succeeded in a previous run
    • ✅ test-fw-nrfconnect-thread-main - Skipped: Job was skipped as it succeeded in a previous run
    • ✅ test-sdk-find-my - Skipped: Job was skipped as it succeeded in a previous run
    • ✅ test-fw-nrfconnect-nrf_lrcs_mosh - Skipped: Job was skipped as it succeeded in a previous run
    • ✅ test-fw-nrfconnect-nrf_lrcs_positioning - Skipped: Job was skipped as it succeeded in a previous run
    • ✅ test-sdk-wifi - Skipped: Job was skipped as it succeeded in a previous run
    • ✅ test-low-level - Skipped: Job was skipped as it succeeded in a previous run
    • ✅ test-sdk-mcuboot - Skipped: Job was skipped as it succeeded in a previous run
    • ✅ test-sdk-dfu - Skipped: Job was skipped as it succeeded in a previous run
    • ❌ test-fw-nrfconnect-ps-main
    • ✅ test-secdom-samples-public - Skipped: Job was skipped as it succeeded in a previous run

Note: This message is automatically posted and updated by the CI

@kapi-no kapi-no marked this pull request as ready for review May 26, 2026 13:47
@kapi-no kapi-no requested a review from a team as a code owner May 26, 2026 13:47
@kapi-no kapi-no added this to the 3.4.0 milestone May 26, 2026
@NordicBuilder
Copy link
Copy Markdown
Contributor

Memory footprint analysis revealed the following potential issues

applications.hpf.gpio.mbox[nrf54l15dk/nrf54l15/cpuflpr]: High RAM usage: 7202[B] - link (cc: @nrfconnect/ncs-ll-ursus)
applications.hpf.gpio.mbox[nrf54l15dk/nrf54l15/cpuflpr]: High ROM usage: 3958[B] - link (cc: @nrfconnect/ncs-ll-ursus)
applications.hpf.gpio.icbmsg[nrf54l15dk/nrf54l15/cpuflpr]: High RAM usage: 13024[B] - link (cc: @nrfconnect/ncs-ll-ursus)
applications.hpf.gpio.icbmsg[nrf54l15dk/nrf54l15/cpuflpr]: High ROM usage: 9760[B] - link (cc: @nrfconnect/ncs-ll-ursus)
applications.hpf.gpio.icmsg[nrf54l15dk/nrf54l15/cpuflpr]: High RAM usage: 9352[B] - link (cc: @nrfconnect/ncs-ll-ursus)
applications.hpf.gpio.icmsg[nrf54l15dk/nrf54l15/cpuflpr]: High ROM usage: 6096[B] - link (cc: @nrfconnect/ncs-ll-ursus)

Note: This message is automatically posted and updated by the CI (latest/sdk-nrf/PR-29021/2)

Copy link
Copy Markdown
Contributor

@nvlsianpu nvlsianpu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please write commit message by your-self, not generating it by LLM.
I'm fine with vibe coding, but not with reading LLM elaborates.

if(CONFIG_MCUBOOT_IMGTOOL_OVERWRITE_ONLY)
# Use overwrite-only instead of swap upgrades.
set(imgtool_rom_command --overwrite-only --align 1)
set(imgtool_rom_command --overwrite-only --align 1 --rom-fixed ${slot0_partition_address})
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be done with zephyr,code-partition as few deozen line bellow.

set(imgtool_rom_command --rom-fixed ${code_partition_offset} --align ${write_block_size})
else()
set(imgtool_rom_command --align ${write_block_size})
set(imgtool_rom_command --align ${write_block_size} --rom-fixed ${slot0_partition_address})
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
set(imgtool_rom_command --align ${write_block_size} --rom-fixed ${slot0_partition_address})
dt_chosen(code_partition PROPERTY "zephyr,code-partition")
dt_partition_addr(code_partition_offset PATH "${code_partition}" REQUIRED)
set(imgtool_rom_command --align ${write_block_size} --rom-fixed ${code_partition_offset)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants