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

    I explain a bunch of context. However, it might not be super-relevant for you until the red text further down.

    I agree with you here, I were unspecific in my previous answer.
    b0n is the bootloader for the network core.
    For the application core, you can use b0 and/or MCUBoot as a bootloader.

    For a typical example for use of b0n, with context:
    Our Machine Learning sample has configurations for bootloader support on the nRF5340.
    In this sample, MCUBoot is running on the Application Core, while b0n is running at the Network Core.
    To update the firmware, the nRF5340 has to enter Serial Recovery mode or using the Simple Management Protocol over Bluetooth.
    This will use the mcumgr tool to upload an image to the application core, which is then swapped to the network core using Peripheral CPU DFU (PCD).
    When you now reboot, b0n will boot into the new image.

    So when i sat "alongside", I really mean that it is generally not the only bootloader running on the nRF5340.

    I should point out that use of the b0n is a bit weird in the sense of multi-images.
    Normally, you can use overlays in your project to change the configurations of your child images.
    Or you could add a custom child image to your project, to avoid making changes to the nRF Connect SDK directly.
    When using the nRF5340, the code running on the network core will be built as a child image of the application.
    For example, if CONFIG_BT=y when you build for your nrf5340dk_nrf5340, the child image hci_rpmsg is added to your project.

    However, if you want to use the b0n, it is added as a child image of the child image running on the network core. (for example a child_image of hci_rpmsg).
    There is one  way to add a configuration overlay to a "recursive" child image, by nesting their names in CMake variables for child image configurations:

    west build -b nrf5340dk_nrf5340_cpuapp -- -Dhci_rpmsg_b0n_CONFIG_DEBUG=y

    So I think your approach is the best way to add a custom b0n: By first making an custom application for the network core. Then you can add this as a child image when you later make firmware for the network core.
    To try to answer your question:

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

    I have not been able to find where in the nRF Connect SDK this is set,
    but I am able to build with a custom b0n if I build hello_world for nrf52840dk_nrf52840_cpuapp with the following configurations:

    CONFIG_BT=y
    CONFIG_SECURE_BOOT=y
    CONFIG_BOOTLOADER_MCUBOOT=y
    

    (Hello World since it is a minimal sample)

    I will continue looking into child images next week, to try to find more for you. If you are able to figure out something in the meantime, let me know.

    Regards,
    Sigurd Hellesvik

Reply
  • Hi

    I explain a bunch of context. However, it might not be super-relevant for you until the red text further down.

    I agree with you here, I were unspecific in my previous answer.
    b0n is the bootloader for the network core.
    For the application core, you can use b0 and/or MCUBoot as a bootloader.

    For a typical example for use of b0n, with context:
    Our Machine Learning sample has configurations for bootloader support on the nRF5340.
    In this sample, MCUBoot is running on the Application Core, while b0n is running at the Network Core.
    To update the firmware, the nRF5340 has to enter Serial Recovery mode or using the Simple Management Protocol over Bluetooth.
    This will use the mcumgr tool to upload an image to the application core, which is then swapped to the network core using Peripheral CPU DFU (PCD).
    When you now reboot, b0n will boot into the new image.

    So when i sat "alongside", I really mean that it is generally not the only bootloader running on the nRF5340.

    I should point out that use of the b0n is a bit weird in the sense of multi-images.
    Normally, you can use overlays in your project to change the configurations of your child images.
    Or you could add a custom child image to your project, to avoid making changes to the nRF Connect SDK directly.
    When using the nRF5340, the code running on the network core will be built as a child image of the application.
    For example, if CONFIG_BT=y when you build for your nrf5340dk_nrf5340, the child image hci_rpmsg is added to your project.

    However, if you want to use the b0n, it is added as a child image of the child image running on the network core. (for example a child_image of hci_rpmsg).
    There is one  way to add a configuration overlay to a "recursive" child image, by nesting their names in CMake variables for child image configurations:

    west build -b nrf5340dk_nrf5340_cpuapp -- -Dhci_rpmsg_b0n_CONFIG_DEBUG=y

    So I think your approach is the best way to add a custom b0n: By first making an custom application for the network core. Then you can add this as a child image when you later make firmware for the network core.
    To try to answer your question:

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

    I have not been able to find where in the nRF Connect SDK this is set,
    but I am able to build with a custom b0n if I build hello_world for nrf52840dk_nrf52840_cpuapp with the following configurations:

    CONFIG_BT=y
    CONFIG_SECURE_BOOT=y
    CONFIG_BOOTLOADER_MCUBOOT=y
    

    (Hello World since it is a minimal sample)

    I will continue looking into child images next week, to try to find more for you. If you are able to figure out something in the meantime, let me know.

    Regards,
    Sigurd Hellesvik

Children
  • 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.

    So, I can get it to build the b0n image now. I just had to remove the "if (CONFIG_SECURE_BOOT)" and set that config to "n" in the prj.conf for the net application. It then tells me that it cannot add the child image unless "CONFIG_PARTITION_MANAGER_ENABLED" is set to "y". Fine. I do that. Then I get a build error where it cannot find "pm_config.h".

    Now I have chased that down to the following link: https://developer.nordicsemi.com/nRF_Connect_SDK/doc/latest/nrf/scripts/partition_manager/partition_manager.html

    Now that link says that:

    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 have tracked it down to the file "partition_manager.cmake", which should make the "pm_config.h" file. This section of code is where it exits before actually creating the header file.

    # This file is executed once per domain.
    #
    # It will be executed if one of the following criteria is true for the
    # current image:
    # - It's a child image, and is the dynamic partition in the domain
    # - It's the root image, and a static configuration has been provided
    # - It's the root image, and PM_IMAGES is populated.
    # - It's the root image, and other domains exist.
    # - A subsys has defined a partition and CONFIG_PM_SINGLE_IMAGE is set.
    # Otherwise, return here
    if (NOT (
      (IMAGE_NAME AND is_dynamic_partition_in_domain) OR
      (NOT IMAGE_NAME AND static_configuration) OR
      (NOT IMAGE_NAME AND PM_IMAGES) OR
      (NOT IMAGE_NAME AND PM_DOMAINS) OR
      (PM_SUBSYS_PREPROCESSED AND CONFIG_PM_SINGLE_IMAGE)
      ))
      return()
    endif()
     
    None of the above are true and so it doesn't create the header file. By adding CMake messages, I can see that:
    • For b0n the IMAGE_NAME is set to b0n and the other parameters are empty. I assume this means that b0n shouldn't build the pm_config.h (which is what I would expect.
    • None of the other parameters are set for the application, which seems wrong.
    • This seems like PM_IMAGES or PM_DOMAINS should not be blank ...

    What should these parameters be? Where should they be set?

  • 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

Related