DFU using USB file system (not serial emulation)?

My project has a nrf5340+external flash, and an external USB-C connector. The device exposes a USB file system (FAT) which is hosted on the external flash partition, and appears as an external disk when connected to a host. This all works well and is used to update config, image files etc for the device to use.

I would like to be able to do application / network firmware updates by simply copying the new image (hex or bin) to this file system and rebooting. Has anyone done this? I'm guessing it involves using MCUBoot bootloader and copying the image into the secondary image slot somehow? I also want to put the secondard image slot on my external flash as won't have space on the internal flash - even better would be if MCUBoot could take the image directly from the file in the FS....

by the way, to be clear, I do NOT want to use the 'DFU over USB serial emulation' method, as this requires the user to install the specific tool to the host machine, which may not be available for their host or may not be allowed by IT policy (or by their level of expertise...). 

thanks for any pointers!

Parents
  • Hi Brian, 
    You should set 

    CONFIG_MCUBOOT_SERIAL=n in the mcuboot.conf overlay to apply the config to the MCUBoot child image, not the application. Usually you put that file into child_image folder.  
    Anyway , by default MCUBOOT_SERIAL is not enable so you don't really have to worry about it. (double check with .config file in the MCUBoot build folder)
  • Ok... I removed all the wifi stuff from my app, added a mcuboot.cong in the child_image folder with:

    CONFIG_MCUBOOT_SERIAL=n
    CONFIG_BOOT_USE_MIN_PARTITION_SIZE=y

    This didn't fix it, although it has changed things.

    Latest build error is:

    [373/415] Performing build step for 'mcuboot_subimage'

    ...

    `text' will not fit in region `FLASH'
    c:/ncs/toolchains/cf2149caf2/opt/zephyr-sdk/arm-zephyr-eabi/bin/../lib/gcc/arm-zephyr-eabi/12.2.0/../../../../arm-zephyr-eabi/bin/ld.bfd.exe: region `FLASH' overflowed by 37320 bytes
    collect2.exe: error: ld returned 1 exit status
    ninja: build stopped: subcommand failed.

    ...
    [396/415] Linking C executable zephyr\zephyr_pre0.elf
    c:/ncs/toolchains/cf2149caf2/opt/zephyr-sdk/arm-zephyr-eabi/bin/../lib/gcc/arm-zephyr-eabi/12.2.0/../../../../arm-zephyr-eabi/bin/ld.bfd.exe: warning: orphan section `.fonts' from `app/libapp.a(u8g2_fonts.c.obj)' being placed in section `.fonts'
    [397/415] Generating ../../zephyr/net_core_app_test_update.hex
    image.py: sign the payload
    [402/415] Linking C executable zephyr\zephyr.elf
    FAILED: modules/mcuboot/mcuboot_subimage-prefix/src/mcuboot_subimage-stamp/mcuboot_subimage-build mcuboot/zephyr/zephyr.hex mcuboot/zephyr/zephyr.elf C:/work/dev/if-device-nrf53/cc1-med/build/modules/mcuboot/mcuboot_subimage-prefix/src/mcuboot_subimage-stamp/mcuboot_subimage-build C:/work/dev/if-device-nrf53/cc1-med/build/mcuboot/zephyr/zephyr.hex C:/work/dev/if-device-nrf53/cc1-med/build/mcuboot/zephyr/zephyr.elf
    ...
    Memory region Used Size Region Size %age Used
    FLASH: 446762 B 1031680 B 43.30%
    RAM: 152572 B 440 KB 33.86%
    IDT_LIST: 0 GB 32 KB 0.00%
    ninja: build stopped: subcommand failed.

    I note that the application built correctly, and is around 446Kb (with the wifi enabled I'm thinking this was showing 901kB...). But this didnt stop the mcuboot subimage failing, this time with its 'text' segment overflowing the FLASH...

    Maybe I should try to reconfigure to stop using the partition manager if its creating a partition for mcuboot which is too small?

    or just build the mcuboot child image to check if that is the issue?

  • Hi Brian, 

    I don't know if you are familiar with MCUboot and the DFU process. If you are not, my suggestion is to get familiar with the samples first before you implement into your application. 
    Please start with the \zephyr\samples\subsys\mgmt\mcumgr\smp_svr sample. 

    BrianW said:
    Maybe I should try to reconfigure to stop using the partition manager if its creating a partition for mcuboot which is too small?

    Please try to let the partition manager to dynamically locate the partitions so you can base on that to define your pm_static. 
    I would suggest to take a look at this repo from one of my coworkers: 
    https://github.com/hellesvik-nordic/samples_for_nrf_connect_sdk/tree/main/bootloader_samples/nrf5340

  • or just build the mcuboot child image to check if that is the issue?

    which I thought would be using --domain mcuboot (as per documentation about west and the example for debugging...), but no, the only domain it knows about is "app".

    Q: How to build just the mcuboot subimage?

    and the app subimage builds fine, without wifi is:

    Memory region Used Size Region Size %age Used
    FLASH: 446762 B 1031680 B 43.30%
    RAM: 152572 B 440 KB 33.86%
    IDT_LIST: 0 GB 32 KB 0.00%

    and with is:
    Memory region Used Size Region Size %age Used
    FLASH: 910118 B 1031680 B 88.22%
    RAM: 205912 B 440 KB 45.70%
    IDT_LIST: 0 GB 32 KB 0.00%

    Even if the bootloader is 48K, this would fit in the 1Mb FLASH before the 'app'...

  • I don't know if you are familiar with MCUboot and the DFU process. If you are not, my suggestion is to get familiar with the samples first before you implement into your application. 

    Fairly familar in general yes, its mostly the zephyr build system that is new to me and frankly not simple (and also the documentation manages to be both over detailed and lacking in critical info, as well as often out of date between the zephyr/NCS version changes)

    The samples are helpful, but are the 'simple' cases - once you start wanting something beyond that its there you get the strange errors and unexplainable 'does not fit in flash' issues (which btw I had hoped the partition manager would avoid - isn't that its main job?)

    BrianW said:
    Maybe I should try to reconfigure to stop using the partition manager if its creating a partition for mcuboot which is too small?

    Please try to let the partition manager to dynamically locate the partitions so you can base on that to define your pm_static. 

    This is indeed what I am now trying, especially as the build already warns:

    ---------------------------------------------------------------------
    --- WARNING: Using a bootloader without pm_static.yml. ---
    --- There are cases where a deployed product can consist of ---
    --- multiple images, and only a subset of these images can be ---
    --- upgraded through a firmware update mechanism. In such cases, ---
    --- the upgradable images must have partitions that are static ---
    --- and are matching the partition map used by the bootloader ---
    --- programmed onto the device. ---
    ---------------------------------------------------------------------

    and this has already shown me that the mcuboot was built as around 53K, so too large for the PM allocated.

    So, with a pm_static.yml giving nearly 64k (minus the pad) to mcuboot, it now builds...

    My pm_static.yml for reference:

    app:
      address: 0x10000
      end_address: 0x100000
      region: flash_primary
      size: 0xf0000
    mcuboot:
      address: 0x0
      end_address: 0xfe00
      placement:
        before:
        - mcuboot_primary
      region: flash_primary
      size: 0xfe00
    mcuboot_pad:
      address: 0xfe00
      end_address: 0x10000
      placement:
        align:
          start: 0xfe00
        before:
        - mcuboot_primary
      region: flash_primary
      size: 0x200
    mcuboot_primary:
      address: 0xfe00
      end_address: 0x100000
      orig_span: &id001
      - mcuboot_pad
      - app
      region: flash_primary
      size: 0xf0200
      span: *id001
    
    # define partition for fatfs disk, on the external flash storage (probably the device defined by nordic,pm-ext-flash in the DTS)
    fatfs_storage:
        region: external_flash
        affiliation:
            - disk
        extra_params: {
            disk_name: "NAND",
            disk_cache_size: 4096,
            disk_sector_size: 512,
            disk_read_only: 0
        }
        placement:
            after: [start]
            align: {start: 4096}
        # 6Mb size, external flash is 8Mb total
        address: 0x0
        size: 0x600000
    
    nvs_storage:
        region: external_flash
        affiliation:
            - nvs
        # 64kB size, its just for small stuff
        address: 0x600000
        size: 0x10000
    
    mcuboot_secondary_1:
        region: external_flash
        affiliation:
            - mcuboot
        # 256kb size, its for netcore flash image
        address: 0x610000
        size: 0x40000
    
    mcuboot_secondary:
        region: external_flash
        affiliation:
            - mcuboot
        # 1Mb size, its for an application flash image
        address: 0x650000
        size: 0xfc000
    
    # internal cuisine
    otp:
      address: 0xff8100
      end_address: 0xff83fc
      region: otp
      size: 0x2fc
    pcd_sram:
      address: 0x20000000
      end_address: 0x20002000
      placement:
        after:
        - start
      region: sram_primary
      size: 0x2000
    rpmsg_nrf53_sram:
      address: 0x20070000
      end_address: 0x20080000
      placement:
        before:
        - end
      region: sram_primary
      size: 0x10000
    sram_primary:
      address: 0x20002000
      end_address: 0x20070000
      region: sram_primary
      size: 0x6e000
    
    # TODO need fake flash ram flash partition to do cpunet DFU...
    #mcuboot_primary_1:
    #  address: 0x0
    #  device: nordic_ram_flash_controller
    #  end_address: 0x40000
    #  region: ram_flash
    #  size: 0x40000
    #
    #ram_flash:
    #  address: 0x40000
    #  end_address: 0x40000
    #  region: ram_flash
    #  size: 0x0

    so many output files as hexes, bins, merged, domain merged, etc...

    But merged.hex still flashes to the CPUAPP ok as before, and persumably contains the full 1Mb for the nrf53 internal flash...

    But then refuses to boot:

    *** Booting nRF Connect SDK 3758bcbfa5cd ***
    I: Starting bootloader
    W: Failed reading sectors; BOOT_MAX_IMG_SECTORS=128 - too small?
    E: Image in the primary slot is not valid!
    E: Unable to find bootable image

    So I added 

    CONFIG_BOOT_MAX_IMG_SECTORS=512

    to child_image/mcuboot.conf and did a pristine rebuild.

    This gave:

    *** Booting nRF Connect SDK 3758bcbfa5cd ***
    I: Starting bootloader
    W: Cannot upgrade: not a compatible amount of sectors
    I: Bootloader chainload address offset: 0xfe00
    I: Jumping to the first image slot
    E: Protect mcuboot flash failed, cancel startup.

    So....

    1/ what makes it an 'incompatible' number of sectors?

    512*4K -> max image size 2Mb, sure its bigger than the image slot - does this mean that the value of BOOT_MAX_IMG_SECTORS must be equal to the size in sectors of the mcuboot_primary slot?

    256 gives same error. But the mcuboot_secondary image slot in the externam flash is probably not nicely initialised, so perhaps not surprisng mcuboot doesn't like it...

    2/ it jumps to the mcuboot_pad partition...

    what is the use of this partition? what is in it? Is the app at the start of 'app' partition or 'mcuboot_primary' or 'mcuboot_primary_app' (added by PM to the partitions.yml during the build...)? What do I now need to configure to find the right start point?

    I have read (again) the mcuboot documentation on the nordic docs site, and the partiion/flash docs without success. Google gave me a question here, which explains its for the image header (see https://docs.zephyrproject.org/1.12.0/reference/kconfig/CONFIG_TEXT_SECTION_OFFSET.html)

    But why does mcuboot jump to the header, not to the code? And in my build, does this header get filled in and by who? 

  • I find the error:

    E: Protect mcuboot flash failed, cancel startup.

    is linked to the mcuboot flash protect mechanism.

    Adding 

    CONFIG_FPROTECT=n
    to child_image/mcuboot.conf means my application now runs!
    But... does this mean I have an attempted write to mcuboot's partition, or is it just that this mechanism is not supported?
  • Hi Brian, 
    FPROTECT is the library that use hardware protection peripheral to protect the MCUBoot from being written by the application or external. 
    Here is the code where the error is thrown: 

    /* There is only one instance of MCUBoot */
    #define PROTECT_SIZE (PM_MCUBOOT_PRIMARY_ADDRESS - PM_MCUBOOT_ADDRESS)
    #define PROTECT_ADDR PM_MCUBOOT_ADDRESS
    #endif
    
        rc = fprotect_area(PROTECT_ADDR, PROTECT_SIZE);
    
        if (rc != 0) {
            BOOT_LOG_ERR("Protect mcuboot flash failed, cancel startup.");
            while (1)
                ;
        }

    Could you check what's the MCUBoot address and the PM_MCUBOOT_PRIMARY_ADDRESS. Please also send us the partitions.yml file in your success build. 

Reply
  • Hi Brian, 
    FPROTECT is the library that use hardware protection peripheral to protect the MCUBoot from being written by the application or external. 
    Here is the code where the error is thrown: 

    /* There is only one instance of MCUBoot */
    #define PROTECT_SIZE (PM_MCUBOOT_PRIMARY_ADDRESS - PM_MCUBOOT_ADDRESS)
    #define PROTECT_ADDR PM_MCUBOOT_ADDRESS
    #endif
    
        rc = fprotect_area(PROTECT_ADDR, PROTECT_SIZE);
    
        if (rc != 0) {
            BOOT_LOG_ERR("Protect mcuboot flash failed, cancel startup.");
            while (1)
                ;
        }

    Could you check what's the MCUBoot address and the PM_MCUBOOT_PRIMARY_ADDRESS. Please also send us the partitions.yml file in your success build. 

Children
  • From pm.config in the build directory:

    PM_MCUBOOT_OFFSET=0x0

    PM_MCUBOOT_PRIMARY_OFFSET=0xfe00

    This is exactly what I expect given my pm_static.yml which starts like:

    mcuboot:
        address: 0x0
        end_address: 0xfe00
        #placement:
        #  before:
        #  - mcuboot_primary
        region: flash_primary
        size: 0xfe00
    
    mcuboot_pad:
        address: 0xfe00
        end_address: 0x10000
        #placement:
        #  align:
        #    start: 0xfe00
        #  before:
        #  - mcuboot_primary
        region: flash_primary
        size: 0x200
    
    app:
        address: 0x10000
        end_address: 0x100000
        region: flash_primary
        size: 0xf0000
    
    mcuboot_primary_app:
        address: 0x10000
        end_address: 0x100000
        region: flash_primary
        size: 0xf0000
        #  span: *id002
        span: [app]
    
    mcuboot_primary:
        address: 0xfe00
        end_address: 0x100000
        region: flash_primary
        size: 0xf0200
        #  span: *id001
        span: [mcuboot_pad, mcuboot_primary_app]
     

    I don't see how to attach files to a ticket? Simple drag-n-drop tells me the file type yml is not allowed...

    partition_manager_report target gives me:

    external_flash (0x800000 - 8192kB):
    +-------------------------------------------------+
    | 0x0: fatfs_storage (0x600000 - 6144kB) |
    | 0x600000: nvs_storage (0x10000 - 64kB) |
    | 0x610000: mcuboot_secondary_1 (0x40000 - 256kB) |
    | 0x650000: mcuboot_secondary (0xfc000 - 1008kB) |
    | 0x74c000: external_flash (0xb4000 - 720kB) |
    +-------------------------------------------------+
    
    flash_primary (0x100000 - 1024kB):
    +--------------------------------------------------+
    | 0x0: mcuboot (0xfe00 - 63kB) |
    +---0xfe00: mcuboot_primary (0xf0200 - 960kB)------+
    | 0xfe00: mcuboot_pad (0x200 - 512B) |
    +---0x10000: mcuboot_primary_app (0xf0000 - 960kB)-+
    | 0x10000: app (0xf0000 - 960kB) |
    +--------------------------------------------------+
    
    otp (0x2fc - 764B):
    +------------------------------+
    | 0xff8100: otp (0x2fc - 764B) |
    +------------------------------+
    
    sram_primary (0x80000 - 512kB):
    +-----------------------------------------------+
    | 0x20000000: pcd_sram (0x2000 - 8kB) |
    | 0x20002000: sram_primary (0x6e000 - 440kB) |
    | 0x20070000: rpmsg_nrf53_sram (0x10000 - 64kB) |
    +-----------------------------------------------+
    
    CPUNET flash_primary (0x40000 - 256kB):
    +--------------------------------------------+
    +---0x1000000: b0n_container (0x8800 - 34kB)-+
    | 0x1000000: b0n (0x8580 - 33kB) |
    | 0x1008580: provision (0x280 - 640B) |
    +---0x1008800: app (0x37800 - 222kB)---------+
    | 0x1008800: hci_ipc (0x37800 - 222kB) |
    +--------------------------------------------+
    
    CPUNET sram_primary (0x10000 - 64kB):
    +-------------------------------------------+
    | 0x21000000: sram_primary (0x10000 - 64kB) |
    +-------------------------------------------+

    which is prettier anyway...

  • So, now that mcuboot is actually bootingmy code (FPROTECT disabled) I have added 

    CONFIG_STREAM_FLASH=y
    CONFIG_STREAM_FLASH_ERASE=y
    CONFIG_DFU_TARGET_STREAM=y
    CONFIG_IMG_MANAGER=y
    CONFIG_DFU_TARGET_MCUBOOT=y
    and using the dfu_target library to copy the contents of app_update.bin to the mcuboot_secondary partition.
    However, it failed with this log
    "Missing stream_buf, call '..set_buf' before '..init"
    as found in dfu_target_mcuboot.c
    I use the dfu_target api as described in the doc
    but it doesn't mention having to set a buffer for mcuboot before the call to dfu_target_init()
    And nowhere in dfu_target.c does it do this?!
    What is the point of having a 'standard' api dfu_target to mask the different 'targets', if app has to explicitly call a target specific function to get it to work? And not described in the doc either?
     
  • Hi again, 

    You can attach file by clicking Insert -> Image/Video/file 

    Or you can add code by Insert -> Code 

    Regarding the mcuboot partition, can you tell why you choose 0xfe00 ( mcuboot end address) and 0x10000 (mcuboot pad end address) ? 
    The problem of this address is that it doesn't allign with FPROTECT_BLOCK_SIZE ( =NRF_SPU_FLASH_REGION_SIZE = 0x4000). 


    Could you try again with 0xc000 and 0xc200 respectively or try 0x10000 and 0x10200 respectively. 

  • I chose 0xfe00 because the original problem was that mcuboot was too big for the flash size selected by PM (0xc000 = 48kB, mcuboot image is around 53kB...), so I aligned the app start partition nicely on 0x10000 -> which when you take off the 0x200 pad gives 0xfe00.

    Sadly the build process and the PM didn't flag up this alignment requirement for FPROTECT...

    I will change it to 0xe000 size for mcuboot and see if that lets FPROTECT be happy... with the primary partition therefore at 0xe200.

  • Still complains:

    *** Booting nRF Connect SDK 3758bcbfa5cd ***
    I: Starting bootloader
    W: Cannot upgrade: not a compatible amount of sectors
    I: Bootloader chainload address offset: 0xe000
    I: Jumping to the first image slot
    E: Protect mcuboot flash failed, cancel startup.

    partitions.yml : insert->file "type yml still not allowed."

    app:
      address: 0xe200
      end_address: 0x100000
      region: flash_primary
      size: 0xf1e00
    external_flash:
      address: 0x750000
      end_address: 0x800000
      region: external_flash
      size: 0xb0000
    fatfs_storage:
      address: 0x0
      affiliation:
      - disk
      end_address: 0x600000
      extra_params:
        disk_cache_size: 0x1000
        disk_name: NAND
        disk_read_only: 0x0
        disk_sector_size: 0x200
      region: external_flash
      size: 0x600000
    mcuboot:
      address: 0x0
      end_address: 0xe000
      region: flash_primary
      size: 0xe000
    mcuboot_pad:
      address: 0xe000
      end_address: 0xe200
      region: flash_primary
      size: 0x200
    mcuboot_primary:
      address: 0xe000
      end_address: 0x100000
      region: flash_primary
      size: 0xf2000
      span:
      - mcuboot_pad
      - mcuboot_primary_app
    mcuboot_primary_app:
      address: 0xe200
      end_address: 0x100000
      region: flash_primary
      size: 0xf1e00
      span:
      - app
    mcuboot_secondary:
      address: 0x650000
      affiliation:
      - mcuboot
      end_address: 0x750000
      region: external_flash
      size: 0x100000
    mcuboot_secondary_1:
      address: 0x610000
      affiliation:
      - mcuboot
      end_address: 0x650000
      region: external_flash
      size: 0x40000
    nvs_storage:
      address: 0x600000
      affiliation:
      - nvs
      end_address: 0x610000
      region: external_flash
      size: 0x10000
    otp:
      address: 0xff8100
      end_address: 0xff83fc
      region: otp
      size: 0x2fc
    pcd_sram:
      address: 0x20000000
      end_address: 0x20002000
      region: sram_primary
      size: 0x2000
    rpmsg_nrf53_sram:
      address: 0x20070000
      end_address: 0x20080000
      region: sram_primary
      size: 0x10000
    sram_primary:
      address: 0x20002000
      end_address: 0x20070000
      region: sram_primary
      size: 0x6e000
    

    pm_static.yml

    mcuboot:
        address: 0x0
        end_address: 0xe000
        region: flash_primary
        size: 0xe000
    
    mcuboot_pad:
        address: 0xe000
        end_address: 0xe200
        region: flash_primary
        size: 0x200
    
    app:
        address: 0xe200
        end_address: 0x100000
        region: flash_primary
        size: 0xf1e00
    
    mcuboot_primary_app:
        address: 0xe200
        end_address: 0x100000
        region: flash_primary
        size: 0xf1e00
        span: [app]
    
    mcuboot_primary:
        address: 0xe000
        end_address: 0x100000
        region: flash_primary
        size: 0xf2000
        span: [mcuboot_pad, mcuboot_primary_app]
    
    # define partition for fatfs disk, on the external flash storage (probably the device defined by nordic,pm-ext-flash in the DTS)
    fatfs_storage:
        region: external_flash
        affiliation: 
            - disk
        extra_params: {
            disk_name: "NAND",
            disk_cache_size: 4096,
            disk_sector_size: 512,
            disk_read_only: 0
        }
        # 6Mb size, external flash is 8Mb total
        address: 0x0
        size: 0x600000
    
    nvs_storage:
        region: external_flash
        affiliation: 
            - nvs
        # 64kB size, its just for small stuff
        address: 0x600000
        size: 0x10000
    
    mcuboot_secondary_1:
        region: external_flash
        affiliation: 
            - mcuboot
        # 256kb size, its for netcore flash image
        address: 0x610000
        size: 0x40000
    
    mcuboot_secondary:
        region: external_flash
        affiliation: 
            - mcuboot
        # 1Mb size, its for an application flash image
        address: 0x650000
        size: 0x100000
        #share_size: [mcuboot_primary_app]
    
    # internal cuisine
    otp:
        address: 0xff8100
        end_address: 0xff83fc
        region: otp
        size: 0x2fc
    pcd_sram:
        address: 0x20000000
        end_address: 0x20002000
        #placement:
        #    after:
        #    - start
        region: sram_primary
        size: 0x2000
    rpmsg_nrf53_sram:
        address: 0x20070000
        end_address: 0x20080000
        #placement:
        #    before:
        #    - end
        region: sram_primary
        size: 0x10000
    sram_primary:
        address: 0x20002000
        end_address: 0x20070000
        region: sram_primary
        size: 0x6e000
    
    # TODO need fake flash ram flash partition to do cpunet DFU...
    #mcuboot_primary_1:
    #  address: 0x0
    #  device: nordic_ram_flash_controller
    #  end_address: 0x40000
    #  region: ram_flash
    #  size: 0x40000
    #
    #ram_flash:
    #  address: 0x40000
    #  end_address: 0x40000
    #  region: ram_flash
    #  size: 0x0
    

    ie partition table for internal flash:

    flash_primary (0x100000 - 1024kB):
    +-------------------------------------------------+
    | 0x0: mcuboot (0xe000 - 56kB) |
    +---0xe000: mcuboot_primary (0xf2000 - 968kB)-----+
    | 0xe000: mcuboot_pad (0x200 - 512B) |
    +---0xe200: mcuboot_primary_app (0xf1e00 - 967kB)-+
    | 0xe200: app (0xf1e00 - 967kB) |
    +-------------------------------------------------+

Related