Busfault when MCUboot reads Network core secondary image header in external flash

Hello there,

I'm stuck on an issue when trying to perform a multi-image firmware upgrade on nrf5340dk when having both secondary images on external flash. I'm building the project with MCUboot as the immutable bootloader, setting it for multi-image DFU following the official documentation here :

developer.nordicsemi.com/.../ug_nrf5340.html

The application builds and flash and can boot with no issue as long as it doesn't try to perform DFU. I used nrfjprog to program app_moved_test_update.hex and net_core_app_moved_test_update.hex  to the QSPI flash, which succeeded when trying to inspect QSPI content with nrfjprog --qspiread. Images are placed at the location I specified in my partition manager static configuration.

Then I reboot the MCU to try performing a test DFU. After the slots are validated, when boot_validated_swap_type is called for the network core secondary image, I encounter an error.

This is the code snippet that triggers the error :

static int boot_validated_swap_type(struct boot_loader_state *state,
                         struct boot_status *bs)
{
    int swap_type;
    fih_int fih_rc = FIH_FAILURE;
    bool upgrade_valid = false;

#if defined(PM_S1_ADDRESS) || defined(CONFIG_SOC_NRF5340_CPUAPP)
    const struct flash_area *secondary_fa =
        BOOT_IMG_AREA(state, BOOT_SECONDARY_SLOT);
    struct image_header *hdr = (struct image_header *)secondary_fa->fa_off;
    uint32_t vtable_addr = 0;
    uint32_t *vtable = 0;
    uint32_t reset_addr = 0;
    /* Patch needed for NCS. Since image 0 (the app) and image 1 (the other
     * B1 slot S0 or S1) share the same secondary slot, we need to check
     * whether the update candidate in the secondary slot is intended for
     * image 0 or image 1 primary by looking at the address of the reset
     * vector. Note that there are good reasons for not using img_num from
     * the swap info.
     */

    if (hdr->ih_magic == IMAGE_MAGIC) {
        vtable_addr = (uint32_t)hdr + hdr->ih_hdr_size;
        vtable = (uint32_t *)(vtable_addr);
        reset_addr = vtable[1];
        
        ....

When trying to read hdr->ih_magic (line 23) for the network core secondary image, a Busfault occurs (Precise data bus error). It seems that the hdr pointer doesn't point to anything. I don't understand this behavior since it works fine the first time this code is run for the application core update, and it's secondary image is also in external flash.

Here is my pm_static.yml content :

mcuboot:
  address: 0x0
  size: 0x14000
  region: flash_primary
mcuboot_pad:
  address: 0x14000
  size: 0x200
  region: flash_primary
app:
  address: 0x14200
  size: 0xebe00
mcuboot_primary:
  address: 0x14000
  size: 0xec000
  orig_span: &id001
    - mcuboot_pad
    - app
  region: flash_primary
  span: *id001
mcuboot_primary_app:
  address: 0x14200
  size: 0xebe00
  orig_span: &id002
    - app
  region: flash_primary
  span: *id002
mcuboot_primary_1:
  address: 0x0
  size: 0x40000
  device: flash_ctrl
  region: ram_flash
production_data:
  address: 0x0
  size: 0x80000
  device: MX25R64
  region: external_flash
mcuboot_secondary:
  address: 0x80000
  size: 0xec000
  device: MX25R64
  region: external_flash  
mcuboot_secondary_1:
  address: 0x16c000
  size: 0x40000
  device: MX25R64
  region: external_flash  
littlefs_storage:
  address: 0x1ac000
  size: 0x654000
  device: MX25R64
  region: external_flash
external_flash:
  address: 0x800000
  size: 0x0
  device: MX25R64
  region: external_flash
  

At this point I don't really understand what might cause the issue, my MCUboot related configuration for the root application and MCUboot child image are identical to what is used in samples that do multi-image DFU samples. I haven't tried to perform DFU with those samples though.

UPDATE :

Found this : https://github.com/nrfconnect/sdk-mcuboot/pull/197

This patch prevents the Busfault from occurring, but the network core firmware update still doesn't seem to work because then the vtable pointer isn't defined in pcd_network_core_update.

child_image/mcuboot/prj.conf content :

CONFIG_SIZE_OPTIMIZATIONS=y

CONFIG_SYSTEM_CLOCK_NO_WAIT=y
CONFIG_PM=n

CONFIG_MAIN_STACK_SIZE=10240
CONFIG_MBEDTLS_CFG_FILE="mcuboot-mbedtls-cfg.h"
CONFIG_BOOT_MAX_IMG_SECTORS=2048

CONFIG_DEBUG=y

CONFIG_BOOT_SIGNATURE_TYPE_RSA=y
CONFIG_BOOT_SIGNATURE_KEY_FILE="test_private_key.pem"


CONFIG_FLASH=y
CONFIG_BOOT_ERASE_PROGRESSIVELY=y
CONFIG_SOC_FLASH_NRF_EMULATE_ONE_BYTE_WRITE_ACCESS=y
CONFIG_FPROTECT=y

CONFIG_SERIAL=y
CONFIG_UART_LINE_CTRL=y

CONFIG_USB_DEVICE_STACK=y
CONFIG_USB_DEVICE_PRODUCT="MCUBOOT"
CONFIG_USB_CDC_ACM=y
CONFIG_USB_COMPOSITE_DEVICE=n
CONFIG_USB_MASS_STORAGE=n

CONFIG_GPIO=y
CONFIG_MCUBOOT_SERIAL=y
CONFIG_MCUBOOT_SERIAL_DIRECT_IMAGE_UPLOAD=y
CONFIG_BOOT_SERIAL_CDC_ACM=y

# When this pin is set to PIN_VAL, it triggers serial recovery mode
CONFIG_BOOT_SERIAL_DETECT_PORT="GPIO_0"
CONFIG_BOOT_SERIAL_DETECT_PIN=23 
CONFIG_BOOT_SERIAL_DETECT_PIN_VAL=0

CONFIG_NORDIC_QSPI_NOR=y
CONFIG_NORDIC_QSPI_NOR_FLASH_LAYOUT_PAGE_SIZE=4096
CONFIG_NORDIC_QSPI_NOR_STACK_WRITE_BUFFER_SIZE=16

CONFIG_MULTITHREADING=y

CONFIG_CBPRINTF_NANO=y
CONFIG_TIMESLICING=n
CONFIG_BOOT_BANNER=n
CONFIG_USE_SEGGER_RTT=n
CONFIG_ERRNO=n
CONFIG_RESET_ON_FATAL_ERROR=n

CONFIG_UART_CONSOLE=y
CONFIG_CONSOLE=y
CONFIG_PRINTK=y
CONFIG_LOG=y
CONFIG_LOG_MODE_MINIMAL=y

CONFIG_PCD_APP=y
CONFIG_UPDATEABLE_IMAGE_NUMBER=2
CONFIG_BOOT_UPGRADE_ONLY=y
CONFIG_FLASH_SIMULATOR=y
CONFIG_FLASH_SIMULATOR_DOUBLE_WRITES=y
CONFIG_FLASH_SIMULATOR_STATS=n

CONFIG_BOOT_IMAGE_ACCESS_HOOKS=y
CONFIG_BOOT_IMAGE_ACCESS_HOOKS_FILE="../../../../myproject/child_image/mcuboot/boot_hook.c"

CONFIG_PM_PARTITION_SIZE_MCUBOOT=0x14000

CONFIG_MCUBOOT_DOWNGRADE_PREVENTION=y
CONFIG_BOOT_VALIDATE_SLOT0=y

root application prj.conf :

CONFIG_HEAP_MEM_POOL_SIZE=4096
CONFIG_MAIN_STACK_SIZE=8192

CONFIG_GPIO=y

CONFIG_LOG=y
CONFIG_LOG_MODE_MINIMAL=y
CONFIG_PRINTK=y
CONFIG_STDOUT_CONSOLE=y

CONFIG_NORDIC_QSPI_NOR=y

CONFIG_FLASH=y
CONFIG_FLASH_MAP=y
CONFIG_FLASH_PAGE_LAYOUT=y

CONFIG_FILE_SYSTEM=y
CONFIG_FILE_SYSTEM_LITTLEFS=y

CONFIG_NANOPB=y

CONFIG_USB_DEVICE_STACK=y
CONFIG_USB_DEVICE_PRODUCT="Test device"

CONFIG_USB_DRIVER_LOG_LEVEL_ERR=y
CONFIG_USB_CDC_ACM=y

CONFIG_USB_DEVICE_LOG_LEVEL_ERR=y

CONFIG_SERIAL=y
CONFIG_UART_INTERRUPT_DRIVEN=y
CONFIG_UART_LINE_CTRL=y

CONFIG_DEBUG=y
CONFIG_ASSERT=y
CONFIG_ASSERT_VERBOSE=y

CONFIG_DEBUG_INFO=y

CONFIG_BOOT_BANNER=y
CONFIG_THREAD_MONITOR=y
CONFIG_THREAD_NAME=y

CONFIG_EXCEPTION_STACK_TRACE=y
CONFIG_DEBUG_THREAD_INFO=y

CONFIG_DEBUG_COREDUMP=y
CONFIG_DEBUG_COREDUMP_BACKEND_LOGGING=y
CONFIG_DEBUG_COREDUMP_MEMORY_DUMP_MIN=y

CONFIG_DEBUG_OPTIMIZATIONS=y

CONFIG_BOOTLOADER_MCUBOOT=y
CONFIG_UPDATEABLE_IMAGE_NUMBER=2
CONFIG_IMG_MANAGER=y
CONFIG_MCUBOOT_IMG_MANAGER=y
CONFIG_IMG_ERASE_PROGRESSIVELY=y

  • This issue seems to be related, however it has not received any updates from developers yet.

  • Hi Jimmy, 
    Thanks for giving us update on this. I have reported the issue internally.

    One small issue is that Andrzej (nvlsianpu) is on leave and will be back in a month so there will be some delay. 

  • I found a fix to the problem :

    In the loader.c file in mcuboot/boot/bootutil/src, I just completely removed these lines from the file  :

    #if defined(CONFIG_SOC_NRF5340_CPUAPP) && defined(PM_CPUNET_B0N_ADDRESS)
            /* If the update is valid, and it targets the network core: perform the
             * update and indicate to the caller of this function that no update is
             * available
             */
            if (upgrade_valid && reset_addr > PM_CPUNET_B0N_ADDRESS) {
                uint32_t fw_size = hdr->ih_img_size;
                BOOT_LOG_INF("Starting network core update");
                int rc = pcd_network_core_update(vtable, fw_size);
                if (rc != 0) {
                    swap_type = BOOT_SWAP_TYPE_FAIL;
                } else {
                    BOOT_LOG_INF("Done updating network core");
    #if defined(MCUBOOT_SWAP_USING_SCRATCH) || defined(MCUBOOT_SWAP_USING_MOVE)
                    /* swap_erase_trailer_sectors is undefined if upgrade only
                     * method is used. There is no need to erase sectors, because
                     * the image cannot be reverted.
                     */
                    rc = swap_erase_trailer_sectors(state,
                            secondary_fa);
    #endif
                    swap_type = BOOT_SWAP_TYPE_NONE;
                }
            }
    #endif /* CONFIG_SOC_NRF5340_CPUAPP */

    In addition of this patch : https://github.com/nrfconnect/sdk-mcuboot/pull/197

    I can now update app core and net core both at the same time or individually.

    I don't really understand why this is still left here since now the upgrade of the network core is done after the network core primary image is copied in RAM using MCUboot hooks and defined nrf53_hooks.c .To me it seems to be out of date code that should be removed.

  • Thanks Jimmy for the update. I have reported to the team and we confirmed it's a bug. The github issue is also update :) 

  • Hi Hung,

    I have experienced the same issue with same configuration excpet i am not using the multi-image feature.

    I saw that a patch has been developed.

    Would it be possible to know when it will be part of the new nordic Connect SDK ? Will it be only available with the version 2.x.x or there will be also a 1.9.2 ?

    Thanks in advance !

    Kind regads

    Riccardo Gaiati

Related