Clarification on Partition Manager (pm_static.yml) for OTA

Hello there,

I am seeing some issues during OTA where the full image is being downloaded from the Golioth cloud but is not booted up by the MCUBoot.

So is pm_static.yml that is provided with the golioth reference design is necessary for the OTA to work?

These are the system settings:

  1. I am using NCS 2.9.0 and Golioth SDK v0.17.0
  2. The image is generated without sysbuild so just app_update.bin which is then uploaded to the console.
  3. CONFIG_BOOTLOADER_MCUBOOT=y is enabled in the prj.conf
  4. Has all the settings defined in the template: thingy91-golioth/socs/nrf9151_ns.conf at main · golioth/thingy91-golioth · GitHub

Here is the snipped of the OTA logs from the nRF9151 device where the device does see that there is new image available, downloads it, but fails to boot the new image.

[00:08:10.913,299] <inf> golioth_fw_update: Received OTA manifest
[00:08:10.913,391] <inf> golioth_fw_update: Current version = 1.0.0, Target version = 2.0.0
[00:08:10.913,421] <inf> golioth_fw_update: State = Downloading
[00:08:11.767,761] <inf> golioth_fw_update: Received block 0/433
[00:08:11.767,822] <inf> mcuboot_util: Image index: 0, Swap type: none
[00:08:11.767,852] <inf> golioth_fw_zephyr: swap type: none
[00:08:12.321,655] <inf> golioth_fw_update: Received block 1/433
[00:08:12.616,271] <inf> vibe_monitor: Elapsed ms since last status report: 470001
[00:08:12.721,679] <inf> golioth_fw_update: Received block 2/433
[00:08:13.127,777] <inf> golioth_fw_update: Received block 3/433
[00:08:13.521,728] <inf> golioth_fw_update: Received block 4/433
[00:08:14.001,739] <inf> golioth_fw_update: Received block 5/433
[00:08:14.407,836] <inf> golioth_fw_update: Received block 6/433
[00:08:14.801,757] <inf> golioth_fw_update: Received block 7/433

.....

[00:11:14.814,270] <inf> golioth_fw_update: Received block 432/433
[00:11:15.267,059] <inf> golioth_fw_update: Received block 433/433
[00:11:15.274,200] <inf> golioth_fw_update: Successfully downloaded 443991 bytes in 184063 ms
[00:11:15.274,230] <inf> golioth_fw_update: State = Downloaded
[00:11:15.528,869] <inf> golioth_fw_update: State = Updating
[00:11:15.847,137] <inf> golioth_fw_update: Rebooting into new image in 5 seconds
[00:11:16.847,198] <inf> golioth_fw_update: Rebooting into new image in 4 seconds
[00:11:17.847,351] <inf> golioth_fw_update: Rebooting into new image in 3 seconds
[00:11:18.847,503] <inf> golioth_fw_update: Rebooting into new image in 2 seconds
uart:~$ All pins have been configured as non-secure
Booting TF-M v2.1.1-ncs2-snapshot1
[Sec Thread] Secure image initializing!

*** Booting My Application v1.0.0-8612a1f058ed ***
*** Using nRF Connect SDK v2.9.0-7787b2649840 ***
*** Using Zephyr OS v3.7.99-1f8f3dc29142 ***
*** Golioth Firmware SDK v0.17.0 ***
[00:00:00.607,055] <inf> golioth_settings_autoload: Initializing settings subsystem
[00:00:00.613,372] <inf> fs_nvs: 2 Sectors of 4096 bytes
[00:00:00.613,403] <inf> fs_nvs: alloc wra: 0, fa8
[00:00:00.613,403] <inf> fs_nvs: data wra: 0, b8
[00:00:00.613,464] <inf> golioth_settings_autoload: Loading settings
[00:00:00.614,288] <dbg> test: main: Starting application on nRF9151
[00:00:00.614,318] <inf> test: Firmware version: 1.0.0
[00:00:00.695,831] <inf> modem: Modem firmware version: mfw_nrf91x1_2.0.2

Has anyone seen this or am I missing something crucial that might be causing the OTA to fail.

Any directions or suggestions will be super helpful. Thank you.

Hi @rajeev,

The log you shared shows that MCUboot downloads the firmware but refuses to boot it. That almost always means it can’t find the image header at the flash address it expects, so the header-verification step fails.

Nordic uses Zephyr’s Sysbuild to compile every image that runs on a single core. In the Thingy91 reference design, the pm_static.yml file locks the partition layout that Partition Manager generates, guaranteeing that every image in the build, bootloader, TF-M, and application, uses identical addresses and sizes.

An OTA update will succeed only when the running bootloader and the newly built application agree on each slot’s start address and size, and both images are signed with the same key.

In practice that means your application must be compiled for the correct address space, and the bootloader must be built with the very same partition map.

When doing OTA with the thingy91 project, you should use sysbuild and the provided pm_static.yml as shown in the repository README.

If you start a new project or change the memory layout, generate a fresh static table by doing a pristine build, then copy build/partitions.yml to your source tree as pm_static.yml. That freezes the layout for future builds and prevents surprises in the field.

Nordic has a Lesson on Sysbuild as part of their DevAcademy that is well worth reading.

@marko, thanks for the insights on the OTA process and partition manager. I will retry with the steps mentioned and then get back to you if any issues.

This is super helpful.
Cheers