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

DFU by app using MTP

Hi
my application will be based on nRF52820 and has only USB connection (no BLE).
I'm developping with nRF5_SDK_17.0.2_d674dde and a nRF52840 devkit.

The end user should be able to update the firmware without any tools.
Thus I implemented Media Transfer Protocol to copy the new image to the device.

First I used
nrfutil pkg generate ...
to create and sign the new application and I wrote a script to unzip the result and combine bootloader data and firmware image to single file which can be copied to the device using MTP.
On the device I receive the combined file and I first check the bootloader data
After I've written the firmware to bank 1 the check with   nrf_dfu_validation_prevalidate() returns  NRF_DFU_RES_CODE_SUCCESS

The I took a copy from
nRF5_SDK_17.0.2_d674dde/examples/dfu/secure_bootloader/pca10056_uart/
and removed DFU completely (I just want the activation part, but no transport at all)

Now I think I have to write valid bootloader settings and reboot.
After many days of reading examples, bootloader code and trial/error I think I miserably fail because of some conceptual misunderstanding or wrong expectation.

I try to prepare bootloader settings following the procedures I find in the examples.

settings_forbidden_parts_copy_from_backup()
detects unacceptable changes compared to the backup settings in MBR parameter page

OK - I did not do any DFU before, the backup can not contain reasonable data yet - this makes sense.

Lets "invent" reasonable bootloader settings reflecting the current app (very nasty hack) at startup and write them to the backup too.

nrfutil says about my fake...
Bootloader DFU Settings:
* File:                 fakeBootloaderSettings.hex
* Family:               NRF52840
* CRC:                  0xF36F5B3B
* Settings Version:     0x00000001 (1)
* App Version:          0x00000001 (1)
* Bootloader Version:   0x00000002 (2)
* Bank Layout:          0x00000000
* Current Bank:         0x00000000
* Application Size:     0x0007AFFC (503804 bytes)
* Application CRC:      0xEC72A580
* Bank0 Bank Code:      0x00000001

Looking at the flash memory I see my dummy settings, but the newly created settings are never written to flash.

I tried
nrf_dfu_validation_activation_prepare()
nrf_dfu_validation_post_data_execute()
and other variants

when I follow the execution in Segger Studio and it comes to reboot I have the strong feeling to get started in the app again.
I think I never hit the bootloader. So I checked the UICR 0x10001014 and 0x10001018 and I think they point to the correct location
I use
                nrf_pwr_mgmt_shutdown(NRF_PWR_MGMT_SHUTDOWN_RESET);

I have 3 problems:
a) how to correctly prepare bootloader settings in the app?
b) how to shutdown/reboot the system correctly?
c) how can I know if Segger reboots into the bootloader or the app?

I'd appreciate a comment how solve problem a) because here I'm definitely stuck

best regards
Peter

  • Hi
    after further investigation I think the missing piece in my picture is MBR. Its source code is not available so I have to guess.
    The RESET vector points to the code in MBR. If the MBR code detects a bootloader it passes control to this bootloader. Otherwise it calls an application at 0x1000 (aka softdevice).

    Helpful: the blinky example comes in 2 flavours - blank and MBR. The difference between the linker files is

    $ diff blank/armgcc/blinky_gcc_nrf52.ld mbr/armgcc/blinky_gcc_nrf52.ld
    <   FLASH (rx) : ORIGIN = 0x0, LENGTH = 0x100000
    <   RAM (rwx) :  ORIGIN = 0x20000000, LENGTH = 0x40000
    ---
    >   FLASH (rx) : ORIGIN = 0x1000, LENGTH = 0xff000
    >   RAM (rwx) :  ORIGIN = 0x20000008, LENGTH = 0x3fff8
    

    The MBR version puts blinky in the position of the softdevice (interesting and not yet understood: 8 bytes of RAM are skipped - why?)

    Now I guess my path to succes should be:
    a) move my application code to 0x1000 (reserving 8 bytes of RAM) to put it in place of the softdevice
    b) add MBR

    best regards, Peter

  • Hi,

    peterz said:
    Now I guess my path to succes should be:

     Correct.

    peterz said:
    interesting and not yet understood: 8 bytes of RAM are skipped - why?

     The first 8 bytes are used by the MBR for interrupt forwarding.

  • Hi again
    as I can bear 3-4 hours of frustration per day I make very little progress
    I boiled it all down to a simple question (see below):

    nrfjprog -f nrf52 --halt
    nrfjprog -f nrf52 --eraseall
    nrfjprog -f nrf52 --program <myOwnBootloader.hex>
    nrfjprog -f nrf52 --program nRF5_SDK_17.0.2_d674dde/components/softdevice/mbr/hex/mbr_nrf52_2.4.1_mbr.hex
    nrfjprog -f nrf52 --reset
    


    My bootloader gets started and produces empty bootloader settings at 0x000FF000 and an identical backup at 0x000FE000
    This is essentially unmodified code and expected behaviour (taken from an example, just removed the transport).

    Then I add the blinky MBR example image
    nrfjprog -f nrf52 --program nRF5_SDK_17.0.2_d674dde/examples/dfu/secure_dfu_test_images/uart/nrf52840/blinky_mbr.hex
    nrfjprog -f nrf52 --reset
    

    result: MBR does NOT call bootloader anymore but starts directly blinky
    But if the bootloader is not called anymore it has no chance to ever install the new image I prepare within my app.
    To find out why MBR prefers starting the app instead of the bootloader I compared bootloader settings produced by examples with settings I produced myself. I found only one candidate which could explain what I see, the structure nrf_dfu_settings_t member
      boot_validation_t  boot_validation_bootloader

    That would make sense: MBR could check validity of the bootloader.
    So I tried to reproduce the values I found in the examples - I failed.
    Then I tried to guess (and failed again):
    mySettings.boot_validation_bootloader.type = VALIDATE_CRC;
    crc = crc32_compute((uint8_t*)0x000F8000, 0x000FE000 - 0x000F8000, NULL);
    *(uint32_t *)&(mySettings.boot_validation_bootloader.bytes[0]) = crc;
    


    Now I'm out of ideas and there's no MBR source to check:
    MBR calls the bootloader as long as there's no application.
    But why does MBR not call the bootloader as soon as there's the blinky app?

    Maybe you could copy/paste the few lines of MBR code that show how/hwy the bootloader is ruled out?

    Thank you

  • Hi,

    We have some MBR documentation here: https://infocenter.nordicsemi.com/topic/sds_s140/SDS/s1xx/mbr_bootloader/bootloader.html

    peterz said:
    To find out why MBR prefers starting the app instead of the bootloader

     From the docs:

    The BOOTLOADERADDR address can be provided either at the MBR_BOOTLOADER_ADDR flash memory location, which is defined in nrf_mbr.h, or in the UICR.NRFFW[0] register.

    So if this is not done, MBR will not start the bootloader.

  • Hi Sigurd

    the HEX file of the bootloader ends with
    :020000041000EA
    :0410140000800F0049
    :0410180000E00F00E5
    :04000003F00082B5D2
    which writes UICR registers

    double checking:
    nrfjprog  --memrd 0x10001014 0x04 0x01
    0x10001014: 000F8000                              |....|

    nrfjprog  --memrd 0x10001018 0x04 0x01
    0x10001018: 000FE000                              |....|

    and most important: the bootloader gets started as long as there is no blinky app.
    IMHO this proves the MBR knows the starting address of the bootloader.

    Adding the blinky app with nrfjprog (without touching anything else! UICR is NOT modified, just checked again) alters the situation: MBR ignores the bootloader and starts blinky.

    IMHO this is at least unexpected. You never encounter this situation for the whole concept of the bootloader is based on the idea, that the first application is loaded by the bootloader (not the app). So setting the UICR vector is not enough. The bootloader does something more - something that I do not do. But what???

Related