Using BLE OTA DFU to update the Bootloader itself

Hi,

We develop in an OpenSuse VM so that we all have the benefit of only having to setup the tools once.  Segger Embedded Studio was building our variant of the DFU Bootloader example.  We use the debug version of the project since we have security key checking turned off for the time being.  When we use a Jlink and SES to program one of our target boards containing the nrf52833 the target programs correctly and we get an MBR, SoftDevice S113, a bootloader, and and MBR parameters page and Bootloader Settings page that all seem to work together.  However, when we try to package up the bootloader app and generated bootloader settings page, with a small update, just the advertising name to show that the update worked, the OTA DFU (using nrf connect on an android phone to send the .zip file) the update appears to work, but the new advertising name does not show up, until I erase the chip and use SES to update the target hardware. 

I have attached a link to our zipped repo of the modified DFU example.
The modified DFU example code is here:  https://drive.google.com/file/d/1AynGuAxH_khmbu_RpCUoxpiVGjGaK0PB/view?usp=sharing  

We had to change the BOOTLOADER_START_ADDR to be 0x71000 instead of the 0x78000 used in the original SDK example because we compiled in uECC for the security library.  I changed this in the project options section placement macros in SES to FLASH_START=0x71000.  Do I need to change that somewhere else as well?  The reason I ask is that location 0x0FF8 and NRF_UICR->NRFFW[0] the shows different addresses when I look at the flash I read out with the Jlink after programming through SES and the generated hex file in the output/exe folder.

In our repo we have a script IsaacPackageDfuForDFU to package the bootloader hex and settings hex into a .zip file for OTA DFU using an android phone.  The relevant lines are:
nrfutil settings generate --family NRF52 --application $DFU_DBG_EXE --application-version 0 --bootloader-version 2 --bl-settings-version 2 $DFU_DBG_SETTINGS_FILE

In_The_Field Production_Unit_Readback.hexActall_HDT_NG_Bootloader_S113_Debug.hexmergehex -m $DFU_DBG_SETTINGS_FILE $DFU_DBG_EXE -o merged.hex
nrfutil pkg generate --debug-mode --hw-version 52 --application-version 0 --application merged.hex $DFU_DBG_APP_FILE

After I package the bootloader and attempt to update it OTA, it seems like the DFU update took, but rebooting the hardware does not cause advertising, not with the advertising name NRF_DFU_BLE_ADV_NAME "DfuTarg" of the original image that was in the hardware or with the updated NRF_DFU_BLE_ADV_NAME of "Actall_Temporary".

Do I need to be packaging up a MBR settings page as well?  If I did that would it even help since the bootloader start address has been modified and there is a hard coded address inside the MBR code that is packaged with the softdevice?

Our issue is that we have a thousand units in the field with just the bootloader and no app in them.  We can add the app once, but then we can never update it again because the DFU will always jump to the app after that.  So we want to update the custom bootloader we put in there to our new custom bootloader that will jump to DFU if a button is held down at boot.  We can make this work when programmed with a J-link, but we can't do it over the air which means we would need to open the cases of the 1000 units in the field that contain just the bootloader and use a Jlink and a cable to update the bootloader.

I have attached a hex file of the image we have in units in the field which only allow us to update the application once as well as the hex being generated by SES for the improved hold-the-button-on-boot-to-DFU version of the bootloader Actall_HDT_NG_Bootloader_S113_Debug.hex.  

If it helps, I can give you access to the whole linux dev environment virtual machine, although it is 14 GB to download.

Any hints as to what I may be doing wrong?


Parents
  • We actually have the larger bootloader expanded and using uECC in the units in the field.  That bootloader works.  Well it will work once and then forever jump to the installed app.  So we are not trying to change the size of the bootloader from what is already in the target hardware.  I would assume we could leave the MBR and Softdevice in there, and just update the bootloader itself.  Is that not possible?

  • Hi,

    1) What values do you read from 0x0FF8 and NRF_UICR->NRFFW[0] (0x10001014). These should hold the start address of the bootloader and never change.

    2) Since you are using the debug version of the bootloader, which has RTT logging enabled by default. Could you get the RTT log and upload it here?

    3) Which SDK version are you using?

    I would assume we could leave the MBR and Softdevice in there, and just update the bootloader itself.  Is that not possible?

    That is possible.

  • Is there a separate setting for removing the encryption requirement from --application updates vs. --bootloader updates?

    I had to check this. The function signature_required() looks like this:

    // Function determines if init command signature is obligatory.
    static bool signature_required(dfu_fw_type_t fw_type_to_be_updated)
    {
        bool result = true;
    
        // DFU_FW_TYPE_EXTERNAL_APPLICATION and bootloader updates always require
        // signature check
        if ((!DFU_REQUIRES_SOFTDEVICE && (fw_type_to_be_updated == DFU_FW_TYPE_SOFTDEVICE)) ||
                (fw_type_to_be_updated == DFU_FW_TYPE_APPLICATION))
        {
            result = NRF_DFU_REQUIRE_SIGNED_APP_UPDATE;
        }
        return result;
    }
    

    So it looks like bootloader updates always require signing.

  • Where can I find the documentation on the difference between using --application vs. --bootloader in nrfutil?  The user's guide for nrfutil https://infocenter.nordicsemi.com/pdf/nrfutil_v6.1.0.pdf only mentions the switch in the context of generating bootloader settings pages.  I do see a sentence here:

    https://infocenter.nordicsemi.com/index.jsp?topic=%2Fsdk_nrf5_v17.0.2%2Flib_bootloader.html 

    "Note that bootloaders are always checked when they are updated."


    That suggests even if I have signature check turned off for the application the bootloader will still be checked.

    Or perhaps you just have to dive into the code for nrfutil in https://github.com/NordicSemiconductor/pc-nrfutil to know how -bootloader and --application are different?

    The only documentation I can find on --bootloader for nrfutil is the paragraph that says it exists in:

    infocenter.nordicsemi.com/index.jsp

    You can add the following firmware images in binary or hexadecimal format to the zip file:

    • --application image: an image of an application
    • --bootloader image: an image of a bootloader
    • --softdevice image: an image of a SoftDevice


    Is it correct to assume that in sdk_config.h #define NRF_BL_APP_SIGNATURE_CHECK_REQUIRED 0 does not apply when updating the bootloader itself?

    We used the default public key in our bootloader because we thought we had signing turned off.  It sounds like we do for the application, which is why --application allows us to upload (albeit to the wrong memory location) and --bootloader does not.  In order for us to try --key-file key.pem signing, it sounds like we would need the key.pem that Nordic used to generate the dfu_public_key.c that is compiled into our bootloader that we thought had signing turned off for applications and bootloader images. 

    The public key in the example dfu_public+_key.c we used starts with:   0x35, 0x7f, 0x66, 0xa3, 0x96, 0xf2,

    I don't see that "throwaway key" as it is called in the sdk. 

    /* This file was generated with a throwaway private key, that is only inteded for a debug version of the DFU project.
    Please see github.com/.../README.md to generate a valid public key. */


    Is the private key available for experimentation with the default example?  

  • Hi,

    I have attached a link to our zipped repo of the modified DFU example.
    The modified DFU example code is here: 

    Looking at your code, you seem to have changed NRF_DFU_REQUIRE_SIGNED_APP_UPDATES from default 1, to 0, this means that Application update may come from an unsigned package. Bootloader update must come from signed packages.

    idavenport said:
    Is the private key available for experimentation with the default example?  

    As far as I can see, this key is not provided.

    Our issue is that we have a thousand units in the field with just the bootloader and no app in them.  We can add the app once, but then we can never update it again because the DFU will always jump to the app after that. 

    How does the bootloader's sdk_config.h file look like for the devices you have in field? You might not need to update the bootloader to do this. You seem to have NRF_BL_DFU_ENTER_METHOD_BUTTON set to 1, with button 8 being used to enter DFU mode. Snippet:

    #ifndef NRF_BL_DFU_ENTER_METHOD_BUTTON_PIN
    // Original pin for the dev kit:
    // #define NRF_BL_DFU_ENTER_METHOD_BUTTON_PIN 25
    //
    // This is the 'PANIC' switch on HDT_NG, P0.08. It is active low when button
    // pressed.
    //
    // We have not implemented the full-on multi-platform support here as we did
    // in the HDT_NG code.
    #define NRF_BL_DFU_ENTER_METHOD_BUTTON_PIN 8
    #else
    #warning "NRF_BL_DFU_ENTER_METHOD_BUTTON_PIN already defined!"
    #endif

    So that is one method.

    You also have:

    // <q> NRF_BL_DFU_ENTER_METHOD_GPREGRET  - Enter DFU mode when bit 0 is set in the NRF_POWER_GPREGRET register.
    #ifndef NRF_BL_DFU_ENTER_METHOD_GPREGRET
    #define NRF_BL_DFU_ENTER_METHOD_GPREGRET 1
    #endif

    So you can call this in the application code:
     

        uint32_t err_code;
    
        err_code = sd_power_gpregret_clr(0, 0xffffffff);
        APP_ERROR_CHECK(err_code);
    
        err_code = sd_power_gpregret_set(0, BOOTLOADER_DFU_START);
        APP_ERROR_CHECK(err_code);

    And call sd_nvic_SystemReset(), and on reset it will then enter DFU mode. This is the same mechanism that the Butonless DFU service uses.

  • We updated that in the bootloader thinking we could update the bootloader.  The sdk_config.h used to create the bootloader that is in the field is here since I can't seem to upload more files after opening a ticket:

    drive.google.com/.../view

    That sdk_config.h had:

    //==========================================================
    // <e> NRF_BL_DFU_ENTER_METHOD_BUTTON - Enter DFU mode on button press.
    //==========================================================
    #ifndef NRF_BL_DFU_ENTER_METHOD_BUTTON
    #define NRF_BL_DFU_ENTER_METHOD_BUTTON 1
    #endif

    ...and...
    #ifndef NRF_BL_DFU_ENTER_METHOD_BUTTON_PIN
    #define NRF_BL_DFU_ENTER_METHOD_BUTTON_PIN 25
    #endif

    We did have:

    // <q> NRF_BL_DFU_ENTER_METHOD_GPREGRET - Enter DFU mode when bit 0 is set in the NRF_POWER_GPREGRET register.


    #ifndef NRF_BL_DFU_ENTER_METHOD_GPREGRET
    #define NRF_BL_DFU_ENTER_METHOD_GPREGRET 1
    #endif

    // <q> NRF_BL_DFU_ENTER_METHOD_BUTTONLESS - Enter DFU mode when the Boolean enter_buttonless_dfu in DFU settings is true.


    #ifndef NRF_BL_DFU_ENTER_METHOD_BUTTONLESS
    #define NRF_BL_DFU_ENTER_METHOD_BUTTONLESS 0
    #endif

    I tried adding your suggested code.  It build and runs, I have to look up how to debug into a bootloader plus application again without the settings flash sectors getting out of synch.  It seems to go right back into the application and I fear that the old bootloader is being vectored to from inside the app and then that the bootloader may just be jumping right back into the app.

    void
    TestForDfuButtonReboot(void)
    {
      uint32_t err_code;
      if ((nrf_gpio_pin_read(8) == 0)) {

        err_code = sd_power_gpregret_clr(0, 0xffffffff);    
        APP_ERROR_CHECK(err_code);

        err_code = sd_power_gpregret_set(0, BOOTLOADER_DFU_START);
        APP_ERROR_CHECK(err_code);
        //now reboot to send the CPU into the bootloader which is a
        //separately compiled application 
        sd_nvic_SystemReset();
      }
    }

  • I get a hard fault in nrf_soc.h at line 621:
    SVCALL(SD_POWER_GPREGRET_CLR, uint32_t, sd_power_gpregret_clr(uint32_t gpregret_id, uint32_t gpregret_msk));

    which is an expansion from my new TestForDfuButtonReboot(void)
    line:
    err_code = sd_power_gpregret_clr(0, 0xffffffff);
     
    Although in the hex readout of the device I am debugging, the address in the MBR at 0FF8 appears to be 9B01601A which doesn't seem to be a valid address.

    :200FE00004FB00B583B00190019B002B0ED0019B00225A60019B00221A60019B0022DA602C

Reply
  • I get a hard fault in nrf_soc.h at line 621:
    SVCALL(SD_POWER_GPREGRET_CLR, uint32_t, sd_power_gpregret_clr(uint32_t gpregret_id, uint32_t gpregret_msk));

    which is an expansion from my new TestForDfuButtonReboot(void)
    line:
    err_code = sd_power_gpregret_clr(0, 0xffffffff);
     
    Although in the hex readout of the device I am debugging, the address in the MBR at 0FF8 appears to be 9B01601A which doesn't seem to be a valid address.

    :200FE00004FB00B583B00190019B002B0ED0019B00225A60019B00221A60019B0022DA602C

Children
  • Hi,

    idavenport said:
    I get a hard fault in nrf_soc.h at line 621:
    SVCALL(SD_POWER_GPREGRET_CLR, uint32_t, sd_power_gpregret_clr(uint32_t gpregret_id, uint32_t gpregret_msk));

    which is an expansion from my new TestForDfuButtonReboot(void)
    line:
    err_code = sd_power_gpregret_clr(0, 0xffffffff);

    Are you calling it from a interrupt handler? Note that you can only call the SoftDevice API from application interrupt priority 5 and lower: https://infocenter.nordicsemi.com/topic/sds_s113/SDS/s1xx/processor_avail_interrupt_latency/exception_mgmt_sd.html

    idavenport said:
    the address in the MBR at 0FF8 appears to be 9B01601A which doesn't seem to be a valid address.

    Do you seem the same if you do read it out using nrfjprog ?

    nrfjprog --memrd 0xFF8

  • When I read with nrfjprog instead of J-flash I still get 9B01601A at 0xFF8.  I didn't think we were calling err_code = sd_power_gpregret_clr(0, 0xffffffff); from an elevated interrupt priority.  It appears to be from a normal thread within FreeRTOS using FreeRTOS task priority 1, which in FreeRTOS context is the lowest priority task.  I don't know how to look up what hardware interrupt priority this corresponds to.

    We will need to update the bootloader in our units in the field so we can use secure firmware updates going forward, so jumping to the bootloader from the application won't solve for our long term need of security, just the short term desire to update the end application firmware more than once.  Since the private key to do this is not available from Nordic, I think we will just have to call this issue as "closed, won't fix" and we will need to send someone out to open up the cases on our thousand units in the field so we can erase the chip and start over.

Related