Custom Update Module on bare flash: where to store "which bank is live"?

I have a custom Update Module working on mender-mcu, and I want to check if my approach is right.

Setup

NXP MIMXRT1170-EVK, Zephyr 4.2.1, mender-mcu. Two modules:

  • zephyr-image for firmware (not modified)
  • signage-content (mine) for the images on the display

The device is a small SPI display. Firmware update by MCUboot is fine. But content is different. Rewriting 1.5 MB of firmware just to change a price tag is too much. And reboot makes the screen blank while MCUboot checks the RSA signature.

So content has its own artifact type, requires_reboot = false, and its own two flash partitions (5.5 MB each). The update writes the bank that is not displayed. Then it switches. This works well.

The problem on MCU

I read Edge AI Models: Custom Update Modules in Practice. In that article, the module does the switch by itself — cp to backup, then promote staging/ to current/. Mender does not store any state for this.

This works on Linux because:

  • current/ is a fixed name. Nothing needs to remember which copy is live.
  • mv is atomic. So overwriting the live location is safe.

On bare flash, both are missing. There is no rename. To overwrite the live location, I must erase the partition the display is reading. If power fails there, the screen is blank and there is no fallback.

So I need A/B banks. And then I need to remember which bank is live. Linux never has this problem.

What I did

I write a footer in the last sector of each bank: magic, generation counter, frame count. At boot, the bank with the higher generation wins. If the magic does not match, the bank is empty.

Before writing, the download erases the target bank’s footer. So if the download fails, the old bank still has the higher generation and stays on the screen. Writing the footer is the switch. Rollback is free, because the live bank was never touched.

Questions

  1. Is this the expected way on MCU? Or is there a pattern I missed?
  2. For fleet visibility, should I use mender_inventory_add_callback() to report signage_bank=1? Composable Updates explains provides/depends well, but the bank number is decided at runtime on the device. So I cannot put it in the artifact like --software-version. Inventory looks correct, but the server can only see the value. It cannot gate a deployment on it like --depends can.
  3. Is there a plan to expose the provides store to Update Modules on MCU? mender_storage_get_provides() exists in src/include/storage.h, but the public headers are only the seven in include/mender/. And include/mender/update-module.h is the same in main and in the release I use. I am not sure if this is intentional.

If the footer approach is correct, I am happy to write a short note about it. It would have saved me a day.

Thanks.

Hi @hkenken,

Thanks for reaching out! Yes indeed that’s a perfectly valid and very nice use case of Mender MCU.:+1:

On the question of A/B marking: it depends a bit on the actual hardware and peripherals, especially NVS which you have at your disposal. I’m not super familiar with the IMXRT1170, and of course not with your board at all, so I can just provide a few thoughts.

  1. a header/footer in each block as you mentioned, which is erased first and written last should work. Might require careful handling of write syncing, but it’s a good approach.
  2. If you have something like NVRAM or an EEPROM, you could use that as A/B marker. It is not required for the Update Module to use only one piece of storage, so you can put the payload in flash and markers somewhere else.

On visibility, I would add it to inventory, yes. What would you need provides/depends for? It could be used to abstract the API or data format (depending on the direction), but I don’t exactly see how it would relate to the specific current payload bank.

On mender_storage_get_provides(), I’ll ping the developers, don’t know right off the cuff.

Greetz,
Josef