Beware that this post is related to an SDK in maintenance mode
More Info: Consider nRF Connect SDK for new designs

Is it possible to update the bootloader from the main application using nRF5 SDK? Program crashes from flash erase calls.

I need to update a custom bootloader from the main application running on an nRF52840. The bootloader is contained in 0xF0000 to 0xF90D7 in internal flash, and the S140 SoftDevice is present from 0x1000 to 0x253C7. I attempted to run the code below but my program crashes once it attempts to erase a block.

NRF_FSTORAGE_DEF(nrf_fstorage_t bl_flash) =
{
    .evt_handler = fs_event_handler,
    .start_addr = 0x0000F0000,
    .end_addr 	= 0x000100000,
};

static void fs_event_handler(nrf_fstorage_evt_t * evt){
	if (evt->result != NRF_SUCCESS)
		NRF_LOG_ERROR("Flash error");
}

int main (void) {
	ret_code_t err_code = nrf_fstorage_init(&bl_flash, &nrf_fstorage_sd, NULL);
	err_code = nrf_fstorage_erase(&bl_flash, 0xF0000, 1, NULL);
}

I also tried nrf_dfu_flash_erase(). In both cases, the init functions work and return an error code of 0, but the app faults when I try to erase or modify the target memory region. Am I going about this the wrong way, or is it not possible to overwrite bootloader code from the main application?

  • Hi!

    In the default bootloader, we have support for DFU of the Bootloader. See this page: https://infocenter.nordicsemi.com/topic/sdk_nrf5_v17.1.0/lib_bootloader_dfu_banks.html

    But it need's to be dual-bank. You should not try to overwrite/erase the current active bootloader from main, then if something goes wrong with the DFU, you could end up with a "bricked device".

    See also  Getting started with Nordic's Secure DFU bootloader, a step by step guide 

    PS: If this is a new product you are creating, I would strongly recommend that you use nRF Connect SDK instead. 

     nRF Connect SDK and nRF5 SDK statement 

    https://www.nordicsemi.com/Products/Development-software/nRF-Connect-SDK

  • Hi Sigurd,

    Thank you for your response.

    Is a dual-bank update possible from the main application?

    I understand that updating the bootloader from main is risky, but currently I don’t see a better option available assuming this is possible. The units are enclosed in a waterproof chassis. The bootloader uses a custom protocol over the 2.4GHz radio and was purpose-built to update only the main application using this protocol. I want to overwrite it with an updated bootloader that can receive binaries over BLE following a more standard process. If the bootloader upgrade fails I can open the bricked units to access the debug pins and flash over-wire, but without the bootloader upgrade I would need to open all the units.

    This is an otherwise completed program, aside from the bootloader lacking BLE support or secure OTA DFU, so I don’t have the ability to shift to NCS + Zephyr.

  • rbmarcus said:
    Is a dual-bank update possible from the main application?

    Yes, it should be possible, assuming you have a mbr params page in flash. You need to have the new bootloader image to be present in internal flash. Then use sd_mbr_command() with the command SD_MBR_COMMAND_COPY_BL

  • I just attempted this method, but unfortunately it did not successfully modify the bootloader region. I tested the code below and got a return code of 4, which I believe corresponds to NRF_ERR_NO_MEM. I'm using a very small 6-byte array and testing this in a sandbox environment, so I suspect I'm either going about this method incorrectly or I may need to initialize something else before calling the sd_mbr_command?

    /* Enable SoftDevice */
    
    ret_code_t err_code;
    err_code = nrf_sdh_enable_request();
    APP_ERROR_CHECK(err_code);
    
    uint32_t ram_start = 0;
    err_code = nrf_sdh_ble_app_ram_start_get(&ram_start);
    APP_ERROR_CHECK(err_code);
    
    err_code = nrf_sdh_ble_default_cfg_set(APP_BLE_CONN_CFG_TAG, &ram_start);
    APP_ERROR_CHECK(err_code);
    
    err_code = nrf_sdh_ble_enable(&ram_start);
    APP_ERROR_CHECK(err_code);
    
    /* MBR->SD method */
    
    uint8_t bootloader_bin[] = {0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC};
    uint32_t bootloader_bin_len = 6;
    uint32_t len_words = bootloader_bin_len / sizeof(uint32_t);
    
    sd_mbr_command_t command =
    {
        .command = SD_MBR_COMMAND_COPY_BL,
        .params.copy_bl.bl_src = bootloader_bin,
        .params.copy_bl.bl_len = len_words
    };
    uint32_t ret_val = sd_mbr_command(&command);
    NRF_LOG_INFO("ret: %d", ret_val);

    I also tried the methods below, making one build for each and commenting out the other methods. They all return NRF_SUCCESS, but the bootloader is still present and functional after POR and the memory region is unchanged based on the device memory layouts from the nRF Connect Programmer.

    /* SoftDevice library method */
    err_code = sd_flash_page_erase(240);
    APP_ERROR_CHECK(err_code);
    
    /* DFU library method */
    err_code = nrf_dfu_flash_init(true);
    APP_ERROR_CHECK(err_code);
    err_code = nrf_dfu_flash_erase(0xF0000, 1, NULL);
    APP_ERROR_CHECK(err_code);
    	
    	
    /* Flash storage library method */
    nrf_fstorage_api_t * p_fs_api = &nrf_fstorage_sd;
    err_code = nrf_fstorage_init(&bl_flash, &nrf_fstorage_sd, NULL);
    APP_ERROR_CHECK(err_code);
    err_code = nrf_fstorage_erase(&bl_flash, 0xF0000, 1, NULL);
    APP_ERROR_CHECK(err_code);

  • Hi!

    rbmarcus said:
    I tested the code below and got a return code of 4, which I believe corresponds to NRF_ERR_NO_MEM. I'm using a very small 6-byte array and testing this in a sandbox environment, so I suspect I'm either going about this method incorrectly or I may need to initialize something else before calling the sd_mbr_command?

    Sounds like you might not have the mbr params page in flash present.

Related