nRF53 Custom b0n Build

I am trying to compile a custom bootloader for nRF5340 net core (b0n) that is part of a multi-image build.

I have created two separate applications, one based on b0n (nrf/samples/nrf5340/netboot) and one based on nrf/samples/bluetooth/peripheral_lbs. These both build without issue when used as single image applications and I can debug them (using hello_world on the app core) and they appear to work.

I have tried adding the b0n as a child image of the application by adding the following code to CMakeLists.txt

if (CONFIG_SECURE_BOOT)
  add_child_image(
    NAME b0n
    SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/../boot_stage1
  )
endif()

If I then build the net app, the application builds, but b0n is neither built nor linked.

I have tried adding CONFIG_SECURE_BOOT=y to the prj.conf file. If I have the above CMakeLists.txt change, I get the following compilation error:

=== child image b0n -  begin ===
loading initial cache file C:/Users/anton/src/bootloader-test/netapp/build/b0n/child_image_preload.cmake
CMake Error: The source "C:/Users/anton/src/bootloader-test/boot_stage1/CMakeLists.txt" does not match the source "C:/Users/anton/ncs/v1.9.1/nrf/samples/nrf5340/netboot/CMakeLists.txt" used to generate cache.  Re-run cmake with a different source directory.
CMake Error at C:\Users\anton\ncs\v1.9.1\nrf\cmake\multi_image.cmake:409 (message):
  CMake generation for b0n failed, aborting.  Command: 1
Call Stack (most recent call first):
  C:\Users\anton\ncs\v1.9.1\nrf\cmake\multi_image.cmake:150 (add_child_image_from_source)
  c:\Users\anton\src\bootloader-test\netapp\build\CMakeLists.txt:21 (add_child_image)
This confuses me in the following ways
  • I don't know why the netboot sample should be used to generate the cache.
  • The CMakeLists.txt are identical in both the sample and in my local build.
  • I don't know why they need to match anyway.

What is going wrong here?

p.s. Most of the documnetation about multi-image bootloader builds suggest using CONFIG_B0_BUILD_STRATEGY_FROM_SOURCE=y. There doesn't appear to be an equivalent CONFIG_B0N_BUILD_STRATEGY_FROM_SOURCE.

Parents
  • Hi Anton

    I have tried adding the b0n as a child image of the application by adding the following code to CMakeLists.txt

    Can you specify which CmakeLists.txt you are referring to here?

    If I then build the net app

    The b0n is usually used alongside the bootloader in the application core. What happens if you build for nrf5340dk_nrf5340_cpuapp instead?

    Regards,
    Sigurd Hellesvik

  • The CMakeLists.txt that I am referring to is the root level CMakeLists.txt for the application on the net core (based on nrf/samples/bluetooth/peripheral_lbs on the net core).

    I think you are mistaken: b0 is the app core bootloader, b0n is the net core bootloader (https://developer.nordicsemi.com/nRF_Connect_SDK/doc/latest/nrf/samples/nrf5340/netboot/README.html). Also, as I have said, I can compile and run b0n on the net core without issue. Also, b0n isn't used alongside the bootloader, it is a bootloader. Did you mean it is often run alongside mcuboot as a set of first and second stage bootloaders?

    Edited: I attempted to build the b0n/app using app core but that failed as it was unable to find "pm_config.h" ... but I don't think that is the correct path to follow anyway.

  • Hi Anton

    Since b0n and b0 is easily mixed, I will from now on refer to them as follows:

    Network core bootloader: b0n

    Application core nRF Secure Immutable Bootloader: NSIB

    AntonD said:
    I assume when you say that you are building a custom b0n for the cpuapp you mean that you are building a custom b0 for the cpuapp.

    Your assumption is wrong. I am talking about custom b0n.

    AntonD said:
    So for some reason the partition manager script must not be running and must not be generating the "pm_config.h" header, or it is not in the correct include path. How do I rectify that?

    I think that the reason it does not work for you is that the "application design"  we intent to be used for child images is different from the one you use.

    I suggest that you take a step back and look at the overall design.
    Am you making two different projects: One for the APP core and one for the NET core?

    Or are you making one project for the whole nRF5340, like this?

    The second solution is the one I described in my previous post, and the one recommended for nRF5340.

    Regards,
    Sigurd Hellesvik

  • I have no problem creating the minimal example you talk about based on hello_world with unmodified b0 and b0n. The real problem is that I need to have custom bootloaders on both cores. I am not fussed about which of the two above architectures make more sense. However, they will need to be custom bootloaders, and that is where the problems come in.

    The issues I have in the original email occur whether it is b0n on the cpunet or b0 on the cpuapp. I have tracked the problem to the block starting at "v1.9.1/nrf/samples/CMakeLists.txt:57" that is included in both the net core app and the app core app. If you enable "CONFIG_SECURE_BOOT" then this file will automatically include b0 or b0n into the application. If I add a custom b0 or b0n using the instructions here https://developer.nordicsemi.com/nRF_Connect_SDK/doc/latest/nrf/ug_multi_image.html then it includes b0/b0n twice. This is why I get the error message above (the CMakeCache.txt is created once by the nrf samples b0/b0n code and once by my b0/b0n code ... and it gets quite confused). I have attempted to fix this by (temporarily) modifying the above CMakeLists.txt in the samples folder to not include b0 or b0n when CONFIG_B0_BUILD_STRATEGY_FROM_SOURCE is enabled. This returns me to the infamous "pm_config.h" not found error.

    Following the above link, I have tried to add a custom netcore app too. I have created one using the hello_world example and as advised I have added the following line to the CMakeLists.txt of the appcore app:

    add_child_image(
      NAME netapp
      SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/netapp
      DOMAIN CPUNET
      )

    where "netapp" was the name and subdirectory of the hello_world application code for the cpunet core.

    No board specified for domain 'CPUNET'. This configuration is typically defined in C:/Users/anton/ncs/v1.9.1/zephyr/boards/arm/nrf5340dk_nrf5340/Kconfig

    So, to get back to the original question, how do I set up a build that has a custom b0 and a custom b0n?

    I will need both. Minimal examples are fine.

  • Hi

    nRF Connect SDK v1.9.1. nRF5340DK.

    Here is a sample showcasing how to use custom NSIB and b0n with a hello world sample. The files I have added or changed will be listed below:

    ncs/nrf/samples/CmakeLists.txt:

    #
    # Copyright (c) 2019 Nordic Semiconductor ASA
    #
    # SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
    #
    
    if (CONFIG_SPM)
      get_cmake_property(VARIABLES              VARIABLES)
      get_cmake_property(VARIABLES_CACHED CACHE_VARIABLES)
    
      set(regex "^CONFIG_SPM_SERVICE.+")
    
      list(FILTER VARIABLES        INCLUDE REGEX ${regex})
      list(FILTER VARIABLES_CACHED INCLUDE REGEX ${regex})
    
      foreach(var_name ${VARIABLES} ${VARIABLES_CACHED})
        set(spm_${var_name} ${${var_name}})
      endforeach()
    
      set(spm_CONFIG_SPM_SECURE_SERVICES ${CONFIG_SPM_SECURE_SERVICES})
    
      if (CONFIG_SPM_SERVICE_NS_HANDLER_FROM_SPM_FAULT)
          add_overlay_config(
            spm
            ${NRF_DIR}/samples/spm/spm_ns_handler_from_spm_fault.conf
            )
    
          add_overlay_dts(
            mcuboot
            ${NRF_DIR}/samples/spm/spm_uart.overlay
          )
      endif()
    
      if (CONFIG_BOARD_ENABLE_CPUNET)
          add_overlay_config(
            spm
            ${NRF_DIR}/samples/spm/spm_enable_cpunet.conf
            )
      endif()
    
      add_child_image(
        NAME spm
        SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/spm
        )
    
      if (CONFIG_ARM_FIRMWARE_USES_SECURE_ENTRY_FUNCS AND
          NOT CONFIG_SPM_BUILD_STRATEGY_FROM_SOURCE AND
          NOT EXISTS ${CONFIG_ARM_ENTRY_VENEERS_LIB_NAME})
        message(WARNING "NOTE: SPM is not built from source, and the firmware use secure "
          "entry functions. However, the configured library file is not found.
          Ensure that the configuration 'ARM_ENTRY_VENEERS_LIB_NAME'
          points to the .a file (default 'spm/libspmsecureentries.a')
          generated alongside the flashed or merged SPM hex file")
      endif()
    endif()
    
    if (CONFIG_SECURE_BOOT)
      if (CONFIG_SOC_NRF5340_CPUNET)
        # Share some information which is used when generating the zip file
        # with the update binaries.
        set_shared(IMAGE net_core PROPERTY SOC ${CONFIG_SOC})
        set_shared(IMAGE net_core PROPERTY VERSION ${CONFIG_FW_INFO_FIRMWARE_VERSION})
        add_child_image(
          NAME b0n
          SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/custom_b0n
          )
      else()
        add_child_image(
          NAME b0
          SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/custom_nsib
          )
      endif()
      if (CONFIG_SOC_NRF5340_CPUAPP AND CONFIG_BOOTLOADER_MCUBOOT)
          add_overlay_config(
            mcuboot
            ${NRF_DIR}/subsys/bootloader/image/log_minimal.conf
            )
      endif()
      if (CONFIG_BUILD_S1_VARIANT AND "${CONFIG_S1_VARIANT_IMAGE_NAME}" STREQUAL "app")
        add_child_image(
          NAME s1_image
          PRELOAD_IMAGE app
          )
      endif()
    endif()
    
    if (CONFIG_NCS_SAMPLE_EMPTY_APP_CORE_CHILD_IMAGE)
      add_child_image(
        NAME "empty_app_core"
        SOURCE_DIR "${NRF_DIR}/samples/nrf5340/empty_app_core"
        DOMAIN "CPUAPP"
        BOARD ${CONFIG_DOMAIN_CPUAPP_BOARD}
        )
    endif()
    
    if (CONFIG_NCS_INCLUDE_RPMSG_CHILD_IMAGE)
    
      if (CONFIG_NCS_SAMPLE_MULTIPROTOCOL_RPMSG_CHILD_IMAGE)
        set(CHILD_IMAGE_NAME "multiprotocol_rpmsg")
        set(CHILD_IMAGE_PATH "${NRF_DIR}/samples/nrf5340/multiprotocol_rpmsg")
    
      elseif (CONFIG_NCS_SAMPLE_HCI_RPMSG_CHILD_IMAGE)
        set(CHILD_IMAGE_NAME "hci_rpmsg")
        set(CHILD_IMAGE_PATH "${ZEPHYR_BASE}/samples/bluetooth/hci_rpmsg")
    
      elseif (CONFIG_NCS_SAMPLE_802154_RPMSG_CHILD_IMAGE)
        set(CHILD_IMAGE_NAME "802154_rpmsg")
        set(CHILD_IMAGE_PATH "${ZEPHYR_BASE}/samples/boards/nrf/ieee802154/802154_rpmsg")
    
      elseif (CONFIG_NCS_SAMPLE_RPC_HOST_CHILD_IMAGE)
        set(CHILD_IMAGE_NAME "rpc_host")
        set(CHILD_IMAGE_PATH "${NRF_DIR}/samples/bluetooth/rpc_host")
    
      else()
        # If we get here, something is wrong with the Kconfig resolution
        message(WARNING "CONFIG_NCS_INCLUDE_RPMSG_CHILD_IMAGE is set but "
          "couldn't resolve which RPMsg sample to add.")
      endif()
    
      # Only include a child image if both image name and path have been defined.
      # Prevents e.g. menuconfig from becoming inaccessible, among others.
      if(CHILD_IMAGE_NAME AND CHILD_IMAGE_PATH)
    
        if (CONFIG_NRF53_UPGRADE_NETWORK_CORE)
          # Inject this configuration from parent image to enable
          # secure bootloader on the network core. This enables firmware update
          # of the network core application.
          add_overlay_config(
            ${CHILD_IMAGE_NAME}
            "${NRF_DIR}/subsys/bootloader/image/secure_boot.conf"
          )
    
          # Inject this configuration from parent image mcuboot to enable
          # the PCD subsystem which is used to communicate the firmware update
          # to the network core bootloader.
          add_overlay_config(
            mcuboot
            "${NRF_DIR}/subsys/pcd/pcd.conf"
          )
        endif()
    
        add_child_image(
          NAME ${CHILD_IMAGE_NAME}
          SOURCE_DIR ${CHILD_IMAGE_PATH}
          DOMAIN "CPUNET"
          BOARD ${CONFIG_DOMAIN_CPUNET_BOARD}
          )
      endif()
    
    endif()
    

    Any location: hello_world:
    2275.hello_world.zip

    ncs/nrf/samples/custom_nsib:

    custom_nsib.zip

    ncs/nrf/samples/custom_b0n:

    custom_b0n.zip

    custom_nsib is a slightly changed copy of nrf/samples/bootloader and custom_b0n is a slightly changed sample of nrf/samples/nrf5340/netboot.
    They both have a print function added to the top of main to verify the change.

    Build this hello_world as such:

    west build -b nrf5340dk_nrf5340_cpuapp

    Is this what you needed?

    Regards,
    Sigurd Hellesvik

  • No. This is not what I am looking for. Modifying the SDK in this way is a bad idea for several reasons but most notably:

    • This will break any other project I am working on using the SDK supplanting the standard bootloader with a custom one.
    • If I need to change SDK version it will revert any changes and if this file changes significantly may mean that I cannot replicate this behaviour.
    • The source code for the bootloaders ends up in the SDK instead of a logical place for development/version control etc.

    Also, this doesn't really answer the network core app problem.

  • Hi

    Yea, I agree that it is far from ideal to make changes to the SDK.
    I asked our developers about this, and here is my question and what they had to say:

    "I want to make changes to the b0n child image for my application. So I copy nrf/samples/nrf5340/netboot to somewhere else(ex ~/custom_netboot).
    How to I tell my application(ex. hello_world) to include custom_netboot instead of netboot? How to do this without making changes to the SDK?"

    " You need to add new kconfig for selecting custom_netboot and then add some cmake code (typically in a ZEPHYR_EXTRA_MODULE which performs the conditional add_child_image. See the example: https://github.com/oivoii/rpmsg-simple-nrf5340/pull/2/files "

    I will try this myself as well. Then I will have a look at your network core app problem after.

    Regards,
    Sigurd Hellesvik

Reply
  • Hi

    Yea, I agree that it is far from ideal to make changes to the SDK.
    I asked our developers about this, and here is my question and what they had to say:

    "I want to make changes to the b0n child image for my application. So I copy nrf/samples/nrf5340/netboot to somewhere else(ex ~/custom_netboot).
    How to I tell my application(ex. hello_world) to include custom_netboot instead of netboot? How to do this without making changes to the SDK?"

    " You need to add new kconfig for selecting custom_netboot and then add some cmake code (typically in a ZEPHYR_EXTRA_MODULE which performs the conditional add_child_image. See the example: https://github.com/oivoii/rpmsg-simple-nrf5340/pull/2/files "

    I will try this myself as well. Then I will have a look at your network core app problem after.

    Regards,
    Sigurd Hellesvik

Children
  • Hi

    I have tried to do custom NSIB and b0n in the samples I suggested you, and I have talked with my colleagues about it.

    And I have finally found what I suspect you have tried to tell me all this time: It does not work.

    As far as I can find, CONFIG_SECURE_BOOT is needed for both NSIB and b0n, and if this is set the child images are imported directly from our samples folder.
    And CONFIG_SECURE_BOOT is too interconnected with the nRF Connect SDK for us to disable it without consequences.

    In conclusion, the only way to have custom NSIB/b0n is to fork the nRF Connect SDK, and make changes to our SDK.
    See my earlier comment for an example, and also Adding your own code.

    Thank you for being thorough in getting your point across.
    While this is not the solution you want right now, I hope it will suffice.
    I will ask our developers about this feature, and maybe we can get it in a future version of the nRF Connect SDK.

    Regards,
    Sigurd Hellesvik

  • Thanks for your honesty.

    This project is for a product that needs to be able to recover even under extreme circumstances so custom bootloaders are absolutely necessary. I have worked around the worst of the issues modifying the SDK by adding the following lines to the top of the application root CMakeLists.txt :

    set(CONFIG_B0_CUSTOM_FOLDER "${CMAKE_SOURCE_DIR}/cpuboot")
    set(CONFIG_B0N_CUSTOM_FOLDER "${CMAKE_SOURCE_DIR}/netboot")

    I have then added the custom bootloaders at those subdirectories. I then copied the SDK into a new directory next to the application (so as not to polute the common SDK) and made the following changes to the samples CMakeLists.txt :

    if (CONFIG_SECURE_BOOT)
      if (CONFIG_SOC_NRF5340_CPUNET)
        # Share some information which is used when generating the zip file
        # with the update binaries.
        set_shared(IMAGE net_core PROPERTY SOC ${CONFIG_SOC})
        set_shared(IMAGE net_core PROPERTY VERSION ${CONFIG_FW_INFO_FIRMWARE_VERSION})
        if (DEFINED CONFIG_B0N_CUSTOM_FOLDER)
          add_child_image(
            NAME b0n
            SOURCE_DIR ${CONFIG_B0N_CUSTOM_FOLDER}
            )
        else()
          add_child_image(
            NAME b0n
            SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/nrf5340/netboot
            )
        endif()
      else()
        if (DEFINED CONFIG_B0_CUSTOM_FOLDER)
          add_child_image(
            NAME b0
            SOURCE_DIR ${CONFIG_B0_CUSTOM_FOLDER}
            )
        else()
          add_child_image(
            NAME b0
            SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/bootloader
            )
        endif()
      endif()

    This does get the job done but is still a bit of a hack; so, I am not marking this as an answer until custom bootloaders can be built using the unmodified SDK.

  • Sigurd,

    I found this discussion  due to a similar need. I also appreciate the honesty and candor in your response.

    Can you comment if there has been any progress on this or any plans by Nordic to support custom(ized) bootloader builds in nrf connect (without needed to modify the sdk)?

    Anton's use case is very common in the real world :

    "This project is for a product that needs to be able to recover even under extreme circumstances so custom bootloaders are absolutely necessary."

    The use of well structured, well designed, well tested, and open (in case of mcuboot) bootloaders is a good thing, but there will always be real world low level (and often proprietary/legacy methods) recovery needed  directly from the lowest levels of code. It is most often the more complex and higher levels of boot which fail (later stage bootloaders, application etc). And there may also be some other low level/sensitive/proprietary customization required in the first and/or second stages of boot.

    On this same topic and related, Nordic should consider the case of being able to build the bootloaders as standalone projects directly in the toolchain (the documentation states that this is somewhat doable via command line).

    There are  numerous real world (especially in production and repair environments) where a standalone bootloader project/build (not as a child project) is required for the various workflows.

    Building as child application embedded in main effort can also create design isolation challenges: some extreme security projects have separate bootloader/security development environments and don't want to share source/build with the application teams.

    Having the bootloader projects  as  separate standalone examples would allow that customization and should be accommodated by your toolchain, in addition to composite projects that have bootloader and app.

    There are some cases where products never even see an application until some post fielding OTA/download/low level upgrade that is done by bootloader.

    Thank you for considering these inputs.

  • Thank you for the complements!

    Joe Kane said:
    Can you comment if there has been any progress on this or any plans by Nordic to support custom(ized) bootloader builds in nrf connect (without needed to modify the sdk)?

    There have not been any changes to the multi-image builds yet.

    However, there are work being done on sysbuild. From these release notes:
    "Sysbuild was introduced in Zephyr's v3.2.0 release, and has been adopted in the nRF Connect SDK v2.4.0. Sysbuild will gradually become the new workflow for handling multi-image build configurations and it will replace the child image mechanism, specific to the nRF Connect SDK
    "

    I have still  not tried sysbuild myself, but from what I have heard I suspect it could make it easier to add customized bootloaders to projects.

    Joe Kane said:

    On this same topic and related, Nordic should consider the case of being able to build the bootloaders as standalone projects directly in the toolchain (the documentation states that this is somewhat doable via command line).

    There are  numerous real world (especially in production and repair environments) where a standalone bootloader project/build (not as a child project) is required for the various workflows.

    Building as child application embedded in main effort can also create design isolation challenges: some extreme security projects have separate bootloader/security development environments and don't want to share source/build with the application teams.

    Having the bootloader projects  as  separate standalone examples would allow that customization and should be accommodated by your toolchain, in addition to composite projects that have bootloader and app.

    There are some cases where products never even see an application until some post fielding OTA/download/low level upgrade that is done by bootloader.

    In the end, a bootloader does not really care about how which binary it boots to (as long as said binary is signed).

    Therefore, you can in theory add your own bootloader and boot any nRF Connect SDK application after.
    As long as the bootloader is good enough at resetting states of peripherals and such, the application should not be affected by this either.

    In fact, our nRF52840 Dongles run the (old) nRF5 Bootloader, which does not know anything about the nRF Connect SDK.
    And this works.
    You can use the Kconfig option CONFIG_FLASH_LOAD_OFFSET to make the application start at the correct offset for your bootloader.
    And then if you want to do DFU, you either must have an update mechanism in the bootloader (ex. serial recovery) or one in your application which supports the custom bootloader.

    Here you can see a case where I discuss using the nRF Bootloader  One bootloader to rule them all .

    Did this make sense to you?

    Regards,
    Sigurd Hellesvik

Related