MCUBoot’s secondary image on external Flash : Down into the rabbit hole and back

I struggled to host MCUBoot's secondary image on the external Flash of the nRF5340DK. I hit so many obstacles along the way that I'm pretty sure I went off track early on and didn't do this properly. First I followed these guidelines and added a device tree overlay:

/ {
        chosen {
                nordic,pm-ext-flash = &mx25r64;
        };

};

and made sure it also applied to MCUBoot (cmake from application):

set(mcuboot_DTC_OVERLAY_FILE "${CMAKE_CURRENT_SOURCE_DIR}/${BOARD}.overlay")

But at runtime, MCUBoot complained about BOOT_MAX_IMG_SECTORS:

W: Failed reading sectors; BOOT_MAX_IMG_SECTORS=128 - too small?

where I assume that 128 matches the number of sectors of the primary partition when the external Flash isn't use. So I doubled it :

set(mcuboot_CONFIG_BOOT_MAX_IMG_SECTORS 256)

even if I would have expected that CONFIG_PM_EXTERNAL_FLASH_MCUBOOT_SECONDARY (defined in prj.conf) would take care of that. Next, MCUBoot complained about an incompatible amount of sectors. As a matter of fact, the generated static partitions (build/partitions.yml) defined the size of mcuboot_secondary as 0. So I also took care of that by redefining the static partitions and matching the size of the mcuboot_secondary to mcuboot_primary:

set(PM_STATIC_YML_FILE
  ${CMAKE_CURRENT_SOURCE_DIR}/partitions.yml
  )
  

app:
  address: 0xc200
  end_address: 0xfe000
  region: flash_primary
  size: 0xf1e00
external_flash:
  address: 0x0
  end_address: 0x800000
  region: external_flash
  size: 0x800000
mcuboot:
  address: 0x0
  end_address: 0xc000
  placement:
    before:
    - mcuboot_primary
  region: flash_primary
  size: 0xc000
mcuboot_pad:
  address: 0xc000
  end_address: 0xc200
  placement:
    align:
      start: 0x4000
    before:
    - mcuboot_primary_app
  region: flash_primary
  size: 0x200
mcuboot_primary:
  address: 0xc000
  end_address: 0xfe000
  orig_span: &id001
  - app
  - mcuboot_pad
  region: flash_primary
  size: 0xf2000
  span: *id001
mcuboot_primary_app:
  address: 0xc200
  end_address: 0xfe000
  orig_span: &id002
  - app
  region: flash_primary
  size: 0xf1e00
  span: *id002
mcuboot_secondary:
  address: 0x0
  device: MX25R64
  end_address: 0xf2000
  placement:
    align:
      start: 0x4
  region: external_flash
  size: 0xf2000
otp:
  address: 0xff8100
  end_address: 0xff83fc
  region: otp
  size: 0x2fc
pcd_sram:
  address: 0x2007e000
  end_address: 0x20080000
  placement:
    before:
    - end
  region: sram_primary
  size: 0x2000
settings_storage:
  address: 0xfe000
  end_address: 0x100000
  placement:
    before:
    - end
  region: flash_primary
  size: 0x2000
sram_primary:
  address: 0x20000000
  end_address: 0x2007e000
  region: sram_primary
  size: 0x7e000


For there, things went smoothly up until the point where boot_request_upgrade is called (either directly or from DFU API). I dug a little deeper and found that it fails attempting to write the boot magic number. Unlike the chunks of firmware updates, the magic number is not hosted in RAM, but in the internal Flash program memory (variable defined as const) and write_from_nvmc instead of nrfx_qspi_write is called. Quite frankly I didn't bother to check why, but write_from_nvmc returns an error that translates to EINVAL (Invalid argument). I just went ahead and changed MCUBoot to :

int
boot_write_magic(const struct flash_area *fap)
{
    uint32_t off;
    int rc;
    static uint32_t lBootMagic[] = {
    0xf395c277,
    0x7fefd260,
    0x0f505235,
    0x8079b62c,
    };

    off = boot_magic_off(fap);

    printk("writing magic; fa_id=%d off=0x%lx (0x%lx), size=%d, total size=%d vs %d\n\n",
                 fap->fa_id, (unsigned long)off,
                 (unsigned long)(fap->fa_off + off), BOOT_MAGIC_SZ, fap->fa_size,off+BOOT_MAGIC_SZ);
    rc = flash_area_write(fap, off, lBootMagic, BOOT_MAGIC_SZ);
    if (rc != 0) {
        printk("flash_area_write failed with error code %d\n\n", rc);
        return BOOT_EFLASH;
    }

    return 0;
}

and I finally managed successfully upgrade the system using a secondary image hosted on the external Flash. But I had to change MCUBoot to do so. Is this a bug, or did I simply went off track somewhere? Thanks.

Parents
  • Hi,

     

    I'm sorry to hear that the process was this demanding! I will surely report this internally.

    For there, things went smoothly up until the point where boot_request_upgrade is called (either directly or from DFU API). I dug a little deeper and found that it fails attempting to write the boot magic number. Unlike the chunks of firmware updates, the magic number is not hosted in RAM, but in the internal Flash program memory (variable defined as const) and write_from_nvmc instead of nrfx_qspi_write is called. Quite frankly I didn't bother to check why, but write_from_nvmc returns an error that translates to EINVAL (Invalid argument). I just went ahead and changed MCUBoot to :

    Looking at the sequence, and your description of the problem, it seems that it is unable to write the external flash (secondary slot) magic tags.

    As a alternative test, could you try setting this in mcuboot.conf and see if it works w/o changing boot_write_magic() then?

    Here's the setting for child_image/mcuboot.conf:

    CONFIG_SPI_NRFX_RAM_BUFFER_SIZE=16

     

    Kind regards,

    Håkon

  • Hi  ,

    I am having a similar use case (but over BLE) and similar issues. Whenever I start a transfer from nRF Connect Device Manager the target microcontroller immediately restarts. I can confirm that the restart is getting triggered from os_mgmt by SMP. I can also confirm that OTA works just with internal flash.

    Unlike the original poster, I am not getting to the point of writing the boot magic number. I am working with nRF52840 SoC and AT25QL641 QSPI Flash chip. I am on v2.1.0 of nRF Connect SDK.

    I have already fixed the "partition_manager.cmake" per your comment here:

    https://devzone.nordicsemi.com/f/nordic-q-a/91180/nrf9160dk-fota-using-external-flash-ncs-2-0-2/394765

    In the device tree I have:

    / {
        chosen {
                nordic,pm-ext-flash = &at25ql641;
        };
    };

    In child_image/mcuboot.conf I have:

    CONFIG_PM_EXTERNAL_FLASH_MCUBOOT_SECONDARY=y
    CONFIG_BOOT_MAX_IMG_SECTORS=256
    CONFIG_PM_OVERRIDE_EXTERNAL_DRIVER_CHECK=n
    CONFIG_FLASH=y
    CONFIG_NRFX_QSPI=y
    CONFIG_NORDIC_QSPI_NOR=y
    CONFIG_NORDIC_QSPI_NOR_STACK_WRITE_BUFFER_SIZE=16

    In my partitions.yml I made sure that primary and secondary partitions have the same size:

    mcuboot_primary:
      address: 0xc000
      end_address: 0x100000
      orig_span: &id001
      - app
      - mcuboot_pad
      region: flash_primary
      size: 0xf4000
      span: *id001
    mcuboot_primary_app:
      address: 0xc200
      end_address: 0x100000
      orig_span: &id002
      - app
      region: flash_primary
      size: 0xf3e00
      span: *id002
    mcuboot_secondary:
      address: 0x0
      device: /soc/qspi@40029000/at25ql641@0
      end_address: 0xf4000
      placement:
        align:
          start: 0x4
      region: external_flash
      share_size:
      - mcuboot_primary
      size: 0xf4000

    I also noticed that providing "size-in-bytes" parameter to the QSPI driver causes Partition Manager to fail at compilation, but providing "size" parameter works. 

    I will try to provide more information as I continue to test. Thank you.

  • After merging my device tree (custom board) and the overlay file. I am no longer getting the abovementioned restarts. So it seems I was not passing the device tree overlay to the mcuboot correctly. However, now I am getting "Remote error: No Entry (5)" when I try to upload an image in the nRF Connect Device Manager. This cooresponds to "MGMT_ERR_ENOENT" of mcumgr. This code is returned from img_mgmt_read_info(). 

    I have opened a thread on this issue:

    devzone.nordicsemi.com/.../dfu-over-ble-mcuboot-external-flash-qspi-mgmt_err_enoent

Reply Children
Related