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.

  • 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

Related