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

Updatable bootloader for nrf9160

Hi, I'm confused how to do fota of mcuboot on the nrf9160.

The source code is public on github in this branch: https://github.com/ExploratoryEngineering/nrf9160-telenor/tree/immutable-bootloader

fota sample is here: https://github.com/ExploratoryEngineering/nrf9160-telenor/tree/immutable-bootloader/samples/fota

The sample use lwm2m to download a new firmware image from our (Telenor's) IoT gateway. It's just standard LwM2M, and uses dfu_target to flash the new firmware. I have enabled CONFIG_SECURE_BOOT to use the immutable bootloader and use CONFIG_FW_INFO_FIRMWARE_VERSION to bump mcuboot's version.

I've tried to read the source code to understand what's going on, but I'm struggling to understand a few pieces. According to the mcuboot design docs, I get the impression that image swaps only happen between the image slots. However the dfu_target doesn't look at the image header before flashing the image to the secondary slot for the application instead of s1. I might be missing some magic configuration or call to change this behaviour, but I can't find it. By commenting out the reboot I can verify that the image is actually written to the secondary slot:

$ nrfjprog -f nrf91 --memrd 0x94400 --n 28
0x00094400: 281EE6DE 8FCEBB4C 00005B02 0000003C   |...(L....[..<...|
0x00094410: 00009080 00000002 00015200            |.........R..|

The secondary slot starts at 0x94000 and by looking at the fw_info data we can see that this is version 2 and the start of the image is 0x15200, which is where s1 is located.

Double check fw_info for s0 and s1:

$ nrfjprog -f nrf91 --memrd 0x8400 --n 28
0x00008400: 281EE6DE 8FCEBB4C 00005B02 0000003C   |...(L....[..<...|
0x00008410: 00009080 00000001 00008200            |............|

$ nrfjprog -f nrf91 --memrd 0x15400 --n 28
0x00015400: 281EE6DE 8FCEBB4C 00005B02 0000003C   |...(L....[..<...|
0x00015410: 00009080 00000001 00015200            |.........R..|

When calling dfu_target_done, it writes BOOT_SWAP_TYPE_TEST to the FLASH_AREA_IMAGE_SECONDARY swap field.

On the next reboot the immutable bootloader choose s0, since s1 is not changed in flash yet. Then (old) mcuboot does it's magic and boots the application. Now if I look at the flash, s1 has been updated with the image flashed to the secondary slot:

$ nrfjprog -f nrf91 --memrd 0x8400 --n 28
0x00008400: 281EE6DE 8FCEBB4C 00005B02 0000003C   |...(L....[..<...|
0x00008410: 00009080 00000001 00008200            |............|

$ nrfjprog -f nrf91 --memrd 0x15400 --n 28
0x00015400: 281EE6DE 8FCEBB4C 00005B02 0000003C   |...(L....[..<...|
0x00015410: 00009080 00000002 00015200            |.........R..|

In the fota code in our application we would like to see what version and slot the bootloader is using. We used fw_info to check this, but even though s0 was used during boot fw_info will report what's on flash after booting, so it will look like s1 was used since it's valid and has a higher version.

By manually rebooting, the immutable bootloader now sees version 2 in s1 and boots from 0x15200. However I don't know how we'll be able to detect this state from code and trigger the second reboot.

Sorry for the long explanation. Here are my questions:

  • Are we doing something wrong since the dfu_target stores the image in the wrong slot? (or is it not wrong)
  • mcuboot only has one public key embedded to verify the signature of the application. If we want to change this key, I assume we have to update both mcuboot and the application in flash before rebooting. How can we do this if both are stored in the secondary slot?
  • Is it possible to do a test run of mcuboot before persisting it?
  • The immutable bootloader have a list of public keys stored in the OTP area for verifying the mcuboot signature. Will we brick the device if we upload a new version of mcuboot signed with a new key and mcuboot or the application fails the test? Looks like all previous public keys are invalidated before the new images are marked OK. Is this intentional?
  • I've read that images can have dependencies between them, so when one fails mcuboot will roll back the others. Is this used automatically for mcuboot and the application, or does it not make sense to use for the bootloader?
  • I have a few suggestions for improving the documentation. What is your preferred method of giving feedback on the documentation? Do you have a public github for the docs hosted on https://developer.nordicsemi.com?
Parents
  • "I have enabled CONFIG_SECURE_BOOT to use the immutable bootloader and use CONFIG_FW_INFO_FIRMWARE_VERSION to bump mcuboot's version."

    (looks like you did this correct, but I'm explaining here for any future readers)
    Note that this must be done for the mcuboot image.
    E.g. by running `ninja mcuboot_menuconfig` by adding it to `ncs/bootloader/mcuboot/boot/zephyr/prj.conf`


    "However the dfu_target doesn't look at the image header before flashing the image to the secondary slot for the application instead of s1"

    This is a bit tricky to explain, but I will do my best.
    MCUBoot supports multiple images, where each image has a secondary and primary slot.
    When an update is approved, it is swapped from its secondary slot to its primary slot.
    We made a modification to MCUBoot where two images can _share_ the secondary slot.
    When MCUBoot iterates over all of its images (two in your case) it will look at the vector table of the image in the secondary slot to ensure that the correct primary slot is used.
    Hence, both application and mcuboot updates will be stored in the same secondary slot.
    For this reason (single secondary slot) there is no support for simultaneous updates of the application and mcuboot, hence there is no dependency management in this architecture.

    The overall flow is now:
    - Download mcubootv2 into secondary slot
    - mcubootv1 checks if image0 (application) has an update in the secondary slot
    - there is an update, but is it for image0 (check address of vector table against address of image0 primary slot)
    - NO
    - mcubootv1 check if image1 (mcuboot) has an update in the secondary slot (same slot as above)
    - there is an update, AND it is for image1 (vector table is within range of image1 primary slot)
    - YES
    - Swap image from secondary slot to image1 (mcuboot) primary slot (S0 or S1).

    Note that the mcuboot candidate generated for running from S0 has S1 as image1 primary while the mcuboot candidate running from S1 has S0 as image1 primary slot.


    "In the fota code in our application we would like to see what version and slot the bootloader is using. We used fw_info to check this, but even though s0 was used during boot fw_info will report what's on flash after booting, so it will look like s1 was used since it's valid and has a higher version"

    I see your point, this would be fixed if mcuboot did a reset after performing the update of mcuboot, do you agree?

    Now to your actual questions:

    "Are we doing something wrong since the dfu_target stores the image in the wrong slot? (or is it not wrong)"

    Its not wrong. As explained above the two images seen by mcuboot (mcuboot and application) share the secondary slot.

    "mcuboot only has one public key embedded to verify the signature of the application. If we want to change this key, I assume we have to update both mcuboot and the application in flash before rebooting. How can we do this if both are stored in the secondary slot?"

    You can not do this with the current solution (single secondary bank again).
    I would propose that you post a issue in the upstream MCUBoot project to support the same key revocation scheme that we use in NCS, or check why its not supported).
    As a hack, you can update to a version of mcuboot which does not verify the application on startup, then the new key will only be used in DFU scenarios.

    "Is it possible to do a test run of mcuboot before persisting it?"

    This is not trivial, since the mcuboot instance being reverted is the one currently running (if that makes sense) if we want to use the mcuboot test run mechanism.
    Instead this is left for B0, and if a signature check of s0 or s1 fails it will be marked invalid, and the alternate slot will be used.

    "The immutable bootloader have a list of public keys stored in the OTP area for verifying the mcuboot signature. Will we brick the device if we upload a new version of mcuboot signed with a new key and mcuboot or the application fails the test? Looks like all previous public keys are invalidated before the new images are marked OK. Is this intentional?"

    Yes, this is the key revocation scheme. If you have 5 keys [0-4] and you loose key 0 you can sign with key 1 to invalidate key 0 etc.

    "I've read that images can have dependencies between them, so when one fails mcuboot will roll back the others. Is this used automatically for mcuboot and the application, or does it not make sense to use for the bootloader?"

    Again, because of the single secondary slot this is not supported.

    "I have a few suggestions for improving the documentation. What is your preferred method of giving feedback on the documentation? Do you have a public github for the docs hosted on ">developer.nordicsemi.com

    You are very welcome, I would propose PRs directly against github.com/.../fw-nrfconnect-nrf

Reply
  • "I have enabled CONFIG_SECURE_BOOT to use the immutable bootloader and use CONFIG_FW_INFO_FIRMWARE_VERSION to bump mcuboot's version."

    (looks like you did this correct, but I'm explaining here for any future readers)
    Note that this must be done for the mcuboot image.
    E.g. by running `ninja mcuboot_menuconfig` by adding it to `ncs/bootloader/mcuboot/boot/zephyr/prj.conf`


    "However the dfu_target doesn't look at the image header before flashing the image to the secondary slot for the application instead of s1"

    This is a bit tricky to explain, but I will do my best.
    MCUBoot supports multiple images, where each image has a secondary and primary slot.
    When an update is approved, it is swapped from its secondary slot to its primary slot.
    We made a modification to MCUBoot where two images can _share_ the secondary slot.
    When MCUBoot iterates over all of its images (two in your case) it will look at the vector table of the image in the secondary slot to ensure that the correct primary slot is used.
    Hence, both application and mcuboot updates will be stored in the same secondary slot.
    For this reason (single secondary slot) there is no support for simultaneous updates of the application and mcuboot, hence there is no dependency management in this architecture.

    The overall flow is now:
    - Download mcubootv2 into secondary slot
    - mcubootv1 checks if image0 (application) has an update in the secondary slot
    - there is an update, but is it for image0 (check address of vector table against address of image0 primary slot)
    - NO
    - mcubootv1 check if image1 (mcuboot) has an update in the secondary slot (same slot as above)
    - there is an update, AND it is for image1 (vector table is within range of image1 primary slot)
    - YES
    - Swap image from secondary slot to image1 (mcuboot) primary slot (S0 or S1).

    Note that the mcuboot candidate generated for running from S0 has S1 as image1 primary while the mcuboot candidate running from S1 has S0 as image1 primary slot.


    "In the fota code in our application we would like to see what version and slot the bootloader is using. We used fw_info to check this, but even though s0 was used during boot fw_info will report what's on flash after booting, so it will look like s1 was used since it's valid and has a higher version"

    I see your point, this would be fixed if mcuboot did a reset after performing the update of mcuboot, do you agree?

    Now to your actual questions:

    "Are we doing something wrong since the dfu_target stores the image in the wrong slot? (or is it not wrong)"

    Its not wrong. As explained above the two images seen by mcuboot (mcuboot and application) share the secondary slot.

    "mcuboot only has one public key embedded to verify the signature of the application. If we want to change this key, I assume we have to update both mcuboot and the application in flash before rebooting. How can we do this if both are stored in the secondary slot?"

    You can not do this with the current solution (single secondary bank again).
    I would propose that you post a issue in the upstream MCUBoot project to support the same key revocation scheme that we use in NCS, or check why its not supported).
    As a hack, you can update to a version of mcuboot which does not verify the application on startup, then the new key will only be used in DFU scenarios.

    "Is it possible to do a test run of mcuboot before persisting it?"

    This is not trivial, since the mcuboot instance being reverted is the one currently running (if that makes sense) if we want to use the mcuboot test run mechanism.
    Instead this is left for B0, and if a signature check of s0 or s1 fails it will be marked invalid, and the alternate slot will be used.

    "The immutable bootloader have a list of public keys stored in the OTP area for verifying the mcuboot signature. Will we brick the device if we upload a new version of mcuboot signed with a new key and mcuboot or the application fails the test? Looks like all previous public keys are invalidated before the new images are marked OK. Is this intentional?"

    Yes, this is the key revocation scheme. If you have 5 keys [0-4] and you loose key 0 you can sign with key 1 to invalidate key 0 etc.

    "I've read that images can have dependencies between them, so when one fails mcuboot will roll back the others. Is this used automatically for mcuboot and the application, or does it not make sense to use for the bootloader?"

    Again, because of the single secondary slot this is not supported.

    "I have a few suggestions for improving the documentation. What is your preferred method of giving feedback on the documentation? Do you have a public github for the docs hosted on ">developer.nordicsemi.com

    You are very welcome, I would propose PRs directly against github.com/.../fw-nrfconnect-nrf

Children
No Data
Related