This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

DFU change bootloader to single-bank

I am using nRF51 and latest s310 softdevice (3.0.0) and latest SDK (v10), which is the latest compatible with the softdevice.

My device is programmed with the softdevice and the dual-bank DFU BLE bootloader.

I want to update to single-bank bootloader.

I create the single-bank bootloader by substituting "dfu_single_bank.c" for "dfu_dual_bank.c" in my Makefile.

The resulting DFU works when I load it directly, but bricks the device when I update it with the NRF toolbox.

I create the zip image with nrfutil:

nrfutil dfu genpkg ~/Dropbox/firmware/bootloader_only.zip \
	--bootloader bootloader.hex

Then I upload it from my phone with nrf toobox.

What I expect to happen: The new bootloader is installed and working

What actually happens: The device is bricked and must be recovered over JTAG.

Looking at the memory map, it looks like this happened:

  1. The dual bank bootloader received the new image and copied it into bank 1.

  2. After verification, it copied the wrong bank into the bootloader space. Now 0x3c000 has a copy of the first blocks of the application code in bank 0, not the bootloader image in bank 1!

Parents
  • The problem with updating the bootloader from a dual to single bank bootloader is that the new bootloader expects the firmware image to be at the start of BANK 0, while it's actually loaded to BANK1 by the old bootloader. After the update the new single bank bootloader verifies that the image was correctly written to flash by comparing itself with the firmware image that it expects to be located in BANK0. Since the actual firmware image is in BANK 1 this check will fail and the new bootloader updates itself with whatever data located in BANK0 using the MBR.

    The fix is to modify the dfu_bl_image_validate() function in dfu_single_bank.c run verification on both addresses (BANK0 and BANK1), i.e.

    uint32_t dfu_bl_image_validate(void) {
        bootloader_settings_t bootloader_settings;
        sd_mbr_command_t      sd_mbr_cmd_1;
        sd_mbr_command_t      sd_mbr_cmd_2;
    
        bootloader_settings_get(&bootloader_settings);
    
        if (bootloader_settings.bl_image_size != 0)
        {
            uint32_t bl_image_start_1 = (bootloader_settings.sd_image_size == 0) ?
                                      DFU_BANK_0_REGION_START :
                                      bootloader_settings.sd_image_start +
                                      bootloader_settings.sd_image_size;
    
            sd_mbr_cmd_1.command             = SD_MBR_COMMAND_COMPARE;
            sd_mbr_cmd_1.params.compare.ptr1 = (uint32_t *)BOOTLOADER_REGION_START;
            sd_mbr_cmd_1.params.compare.ptr2 = (uint32_t *)(bl_image_start_1);
            sd_mbr_cmd_1.params.compare.len  = bootloader_settings.bl_image_size / sizeof(uint32_t);
    
    
    
        uint32_t bl_image_start_2 = (bootloader_settings.sd_image_size == 0) ?
                                      DFU_BANK_1_REGION_START :
                                      bootloader_settings.sd_image_start +
                                      bootloader_settings.sd_image_size;
    
            sd_mbr_cmd_2.command             = SD_MBR_COMMAND_COMPARE;
            sd_mbr_cmd_2.params.compare.ptr1 = (uint32_t *)BOOTLOADER_REGION_START;
            sd_mbr_cmd_2.params.compare.ptr2 = (uint32_t *)(bl_image_start_2);
            sd_mbr_cmd_2.params.compare.len  = bootloader_settings.bl_image_size / sizeof(uint32_t);
    
            return sd_mbr_command(&sd_mbr_cmd_1) && sd_mbr_command(&sd_mbr_cmd_2);
        }
        return NRF_SUCCESS;
    }
    
Reply
  • The problem with updating the bootloader from a dual to single bank bootloader is that the new bootloader expects the firmware image to be at the start of BANK 0, while it's actually loaded to BANK1 by the old bootloader. After the update the new single bank bootloader verifies that the image was correctly written to flash by comparing itself with the firmware image that it expects to be located in BANK0. Since the actual firmware image is in BANK 1 this check will fail and the new bootloader updates itself with whatever data located in BANK0 using the MBR.

    The fix is to modify the dfu_bl_image_validate() function in dfu_single_bank.c run verification on both addresses (BANK0 and BANK1), i.e.

    uint32_t dfu_bl_image_validate(void) {
        bootloader_settings_t bootloader_settings;
        sd_mbr_command_t      sd_mbr_cmd_1;
        sd_mbr_command_t      sd_mbr_cmd_2;
    
        bootloader_settings_get(&bootloader_settings);
    
        if (bootloader_settings.bl_image_size != 0)
        {
            uint32_t bl_image_start_1 = (bootloader_settings.sd_image_size == 0) ?
                                      DFU_BANK_0_REGION_START :
                                      bootloader_settings.sd_image_start +
                                      bootloader_settings.sd_image_size;
    
            sd_mbr_cmd_1.command             = SD_MBR_COMMAND_COMPARE;
            sd_mbr_cmd_1.params.compare.ptr1 = (uint32_t *)BOOTLOADER_REGION_START;
            sd_mbr_cmd_1.params.compare.ptr2 = (uint32_t *)(bl_image_start_1);
            sd_mbr_cmd_1.params.compare.len  = bootloader_settings.bl_image_size / sizeof(uint32_t);
    
    
    
        uint32_t bl_image_start_2 = (bootloader_settings.sd_image_size == 0) ?
                                      DFU_BANK_1_REGION_START :
                                      bootloader_settings.sd_image_start +
                                      bootloader_settings.sd_image_size;
    
            sd_mbr_cmd_2.command             = SD_MBR_COMMAND_COMPARE;
            sd_mbr_cmd_2.params.compare.ptr1 = (uint32_t *)BOOTLOADER_REGION_START;
            sd_mbr_cmd_2.params.compare.ptr2 = (uint32_t *)(bl_image_start_2);
            sd_mbr_cmd_2.params.compare.len  = bootloader_settings.bl_image_size / sizeof(uint32_t);
    
            return sd_mbr_command(&sd_mbr_cmd_1) && sd_mbr_command(&sd_mbr_cmd_2);
        }
        return NRF_SUCCESS;
    }
    
Children
No Data
Related