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

custom DFU in App

Hello,

I am developing DFU over a custom IEEE802.15.4 Protocol in our own embedded OS. I managed to write the new Firmware starting at bank1_start address. Now I need to tell the Bootloader (the BTL from SDK16) to do its magic (verifying and copying the Firmware to bank0). For now I fill the init_command in the nrf_dfu_settings_t at BOOTLOADER_SETTINGS_ADDRESS with the values I get from nrfutil and write it back to flash, but that doesn't impress the bootloader. What else do I need to do so the bootloader knows that there is a new Firmware it should take care of?

This is what I am doing right now:

> nrfutil settings generate --application ./Firmware.hex --application-version 0x0100 --family NRF52840 --bootloader-version 1 --bl-settings-version 2 Firmware_btl_settings.hex

Note: Generating a DFU settings page with backup page included.
This is only required for bootloaders from nRF5 SDK 15.1 and newer.
If you want to skip backup page generation, use --no-backup option.

Generated Bootloader DFU settings .hex file and stored it in: Firmware_btl_settings.hex

Bootloader DFU Settings:
* File:                     MPS_Master_btl_settings.hex
* Family:                   NRF52840
* Start Address:            0x000FF000
* CRC:                      0x8800843F
* Settings Version:         0x00000002 (2)
* App Version:              0x00000100 (256)
* Bootloader Version:       0x00000001 (1)
* Bank Layout:              0x00000000
* Current Bank:             0x00000000
* Application Size:         0x00024C3C (150588 bytes)
* Application CRC:          0xF899D96C
* Bank0 Bank Code:          0x00000001
* Softdevice Size:          0x00000000 (0 bytes)
* Boot Validation CRC:      0x5FF8E2DA
* SD Boot Validation Type:  0x00000000 (0)
* App Boot Validation Type: 0x00000001 (1)

----------------------------------------------------------------------

nrf_dfu_settings_t * p_dfu_settings = (nrf_dfu_settings_t *)BOOTLOADER_SETTINGS_ADDRESS;
nrf_dfu_settings_t dfu_settings;
dfu_init_command_t init_cmd;

memcpy(&dfu_settings, p_dfu_settings, sizeof(nrf_dfu_settings_t));

init_cmd.has_fw_version = true;
init_cmd.fw_version     = 0x0100;
init_cmd.has_hw_version = false;
init_cmd.hw_version     = 0xFFF00000;
init_cmd.sd_req_count   = 0;
// init_cmd.sd_req[16]  = ;
init_cmd.has_type       = true;
init_cmd.type           = DFU_FW_TYPE_APPLICATION;
init_cmd.has_sd_size    = false;
// init_cmd.sd_size     = ;
init_cmd.has_bl_size    = false;
// init_cmd.bl_size     = ;
init_cmd.has_app_size   = true;
init_cmd.app_size       = 0x00024C3C;
init_cmd.has_hash       = false;
// init_cmd.hash        = ;
init_cmd.has_is_debug   = false;
init_cmd.is_debug       = false;
init_cmd.boot_validation_count             = 1;
init_cmd.boot_validation[0].type           = DFU_VALIDATION_TYPE_VALIDATE_GENERATED_CRC;
init_cmd.boot_validation[0].bytes.size     = 4;
init_cmd.boot_validation[0].bytes.bytes[0] = 0xF8;
init_cmd.boot_validation[0].bytes.bytes[1] = 0x99;
init_cmd.boot_validation[0].bytes.bytes[2] = 0xD9;
init_cmd.boot_validation[0].bytes.bytes[3] = 0x6C;
memcpy(dfu_settings.init_command, &init_cmd, sizeof(init_cmd));

nrf_dfu_settings_write(&dfu_settings);

enter_bootloader(); // set NRF_POWER->GPREGRET and reboot

----------------------------------------------------------------------

void nrf_dfu_settings_write(nrf_dfu_settings_t * pData) {
    /* g_btl_info need to be the start of a page */
    assert(((uint32_t)p_dfu_settings % NRF_FICR->CODEPAGESIZE) == 0);
    assert(pData);

    if (0 == memcmp(pData, &p_dfu_settings, sizeof(nrf_dfu_settings_t))) {
        return;
    }
    nrf_nvmc_page_erase((uint32_t)p_dfu_settings);
    nrf_nvmc_bytes_write((uint32_t)p_dfu_settings, pData, sizeof(nrf_dfu_settings_t));
}

Parents
  • Hello,

    So you have basically already transferred the new image, and you need the bootloader to move it into the correct location and verify the signature, right?

    I suggest you look into how the bootloader does this when it receive the "object execute" command. I am using SDK17.0.2 and the serial(uart) bootloader as reference, as you may not be using the softdevice.

    nrf_dfu_req_handler.c -> nrf_dfu_command_req() -> case NRF_DFU_OP_OBJECT_EXECUTE -> on_cmd_obj_execute_request() -> 

    static void on_cmd_obj_execute_request(nrf_dfu_request_t const * p_req, nrf_dfu_response_t * p_res)
    {
        ASSERT(p_req);
        ASSERT(p_res);
    
        NRF_LOG_DEBUG("Handle NRF_DFU_OP_OBJECT_EXECUTE (command)");
    
        nrf_dfu_result_t ret_val;
        ret_val = nrf_dfu_validation_init_cmd_execute(&m_firmware_start_addr, &m_firmware_size_req);
        p_res->result = ext_err_code_handle(ret_val);
    
        if (p_res->result == NRF_DFU_RES_CODE_SUCCESS)
        {
            if (nrf_dfu_settings_write_and_backup(NULL) == NRF_SUCCESS)
            {
                /* Setting DFU to initialized */
                NRF_LOG_DEBUG("Writing valid init command to flash.");
            }
            else
            {
                p_res->result = NRF_DFU_RES_CODE_OPERATION_FAILED;
            }
        }
    }

    particularly the line containing nrf_dfu_validation_init_cmd_execute().

    This will validate the firmware. If it is successful, it will continue to write new settings:

    nrf_dfu_settings_write_and_backup()

    I don't remember the complete application flow, but this should eventually reset the bootloader, and the next time it runs, it will run into main() -> nrf_bootloader_init() -> nrf_bootloader_fw_activate() case ACTIVATION_SUCCESS, which will reset it again, and then your application will start.

    Best regards,

    Edvin

  • Hello Edvin,

    I set up a DFU_IN_APP project with SDK17.0.2. The verification of the new Firmware fails with error 0x8512 (NRF_ERROR_CRYPTO_INPUT_LOCATION). I tracked it down to cc310_backend_hash_sha256_update(). There it verifies if p_data is in RAM

    VERIFY_TRUE(nrfx_is_in_ram(p_data), NRF_ERROR_CRYPTO_INPUT_LOCATION);

    But obviously the updated firmware is located in flash. What am I doing wrong here?

    dfu_in_app.zip

  • So that is the cc310_bl_backend_hash_sha256_update() (with "_bl_", right?)

    Remember that at this point, even if the DFU was performed from the bootloader (and not from the DFU_IN_APP), the new application would have been stored in the Flash of the nRF52840 as well.

    Line 168 and 172in cc310_bl_backend_hash.c in sdk17.0.2 says:

    168: // Copy a block from FLASH to RAM for use in CC310
    172: // Copy from FLASH to ram

    Is there a particular reason why you have set the define in sdk_config.h:

    NRF_CRYPTO_BACKEND_CC310_BL_HASH_AUTOMATIC_RAM_BUFFER_ENABLED

    to 0? It is 1 by default. When this is set to 1, it will copy the data from flash to RAM, and in fact, the lines 164 -> 182 will be replacing line 183 -> 199 in cc310_bl_backend_hash.c.

    Try setting it (NRF_CRYPTO_BACKEND_CC310_BL_HASH_AUTOMATIC_RAM_BUFFER_ENABLED) back to 1. If that is not an option, I guess you need to manually pull each buffer with size "NRF_CRYPTO_BACKEND_CC310_BL_HASH_AUTOMATIC_RAM_BUFFER_SIZE" from flash before you run the VERIFY_TRUE() macro.

    Best regards,

    Edvin

  • as I said, it's cc310_backend_hash_sha256_update() without _bl_. Here is the call stack:

    NRF_CRYPTO_BACKEND_CC310_BL_HASH_AUTOMATIC_RAM_BUFFER_ENABLED is set to 1 as you can see in the project files I attached to my earlier post, but that wasn't on purpose. If my NRF_CRYPTO_* setup is wrong, could you please help me setting it up right.

    The new application is stored in bank1, but fw_hash_ok() fails in postvalidate(), so s_dfu_settings.bank_current isn't set to NRF_DFU_CURRENT_BANK_1.

Reply
  • as I said, it's cc310_backend_hash_sha256_update() without _bl_. Here is the call stack:

    NRF_CRYPTO_BACKEND_CC310_BL_HASH_AUTOMATIC_RAM_BUFFER_ENABLED is set to 1 as you can see in the project files I attached to my earlier post, but that wasn't on purpose. If my NRF_CRYPTO_* setup is wrong, could you please help me setting it up right.

    The new application is stored in bank1, but fw_hash_ok() fails in postvalidate(), so s_dfu_settings.bank_current isn't set to NRF_DFU_CURRENT_BANK_1.

Children
  • MeisterBob said:
    NRF_CRYPTO_BACKEND_CC310_BL_HASH_AUTOMATIC_RAM_BUFFER_ENABLED is set to 1 as you can see in the project files I attached to my earlier post,

     I can't see that it is.

    Try starting with an unmodified version of the bootloader and go from there. Please try to keep NRF_CRYPTO_BACKEND_CC310_BL_HASH_AUTOMATIC_RAM_BUFFER_ENABLED  set to 1.

    BR,

    Edvin

  • It's in sdk_config.h. Actualy NRF_CRYPTO_BACKEND_CC310_BL wasn't enabled. So I disabled the CC310 backend and enabled the CC310_BL backend, added some src files and include pathes to the Makefile and was finaly able to update the firmware from the app.

    I pushed the code to https://github.com/MeisterBob/nrf52_blinky_dfu_in_app for anyone who needs a minimal example for DFU_IN_APP, it's based on SDK 17.0.2 and uses UART transport.

    I was able to find the answer to my initial question. My major missunderstanding was, that the init command needs to be stored protobuf encoded, I was trying to safe it decoded. The steps to success are:

    nrf_dfu_settings_t *p_dfu_settings = /* Pointer to bootloader settings page (0xFF000 on nRF52840) */
    nrf_dfu_settings_t mku_dfu_settings;
    
    // copy old dfu_settings
    memcpy(&dfu_settings, p_dfu_settings, sizeof(nrf_dfu_settings_t));
    
    // update init command (protobuf encoded !)
    memcpy(dfu_settings.init_command, /* init command */, /* init command size */);
    
    // update .progress
    dfu_settings.progress.command_size               = /* size of the init command */;
    dfu_settings.progress.command_offset             = dfu_settings.progress.command_size;
    dfu_settings.progress.data_object_size           = 0;
    dfu_settings.progress.firmware_image_crc         = /* application crc returned by nrfutil settings generate */;
    dfu_settings.progress.firmware_image_crc_last    = dfu_settings.progress.firmware_image_crc;
    dfu_settings.progress.firmware_image_offset      = /* application size */
    dfu_settings.progress.firmware_image_offset_last = dfu_settings.progress.firmware_image_offset;
    dfu_settings.progress.command_crc                = crc32_compute((uint8_t *)(dfu_settings.init_command), dfu_settings.progress.command_size, NULL);
    
    // set bank current to 1
    dfu_settings.bank_current = 1;  // NRF_DFU_CURRENT_BANK_1
    
    // update settings crc
    dfu_settings.crc = crc32_compute((uint8_t *)(&dfu_settings) + 4, offsetof(nrf_dfu_settings_t, init_command) - 4, NULL);
    
    // write dfu_settings back to p_dfu_settings
    // write new app to MBR_SIZE + dfu_settings->bank_0.image_size (if you use a softdevice MBR_SIZE is a bad choice here)
    // reboot the system

Related