Zephyr MCUboot example for PlatformIO

Hi all, do anyone has a working example, or had experience of compiling MCUboot for Zephyr on PlatformIO?

There’s was a user who tried to do the same thing but gave up.

Currently I’m trying to do the same but unable to compile, likely due to wrong lib dependency configuration in platformio.ini

Dependency Graph
|-- <flash_map_backend>
|-- <hal>
|-- <mcuboot_config>
|-- <os>
|-- <serial_adapter>
|-- <sysflash>
|-- <boot_serial>
|   |-- <bootutil>
|-- <bootutil>
Building in release mode
...
Compiling .pio\build\ivm22_f103re\lib4ab\bootutil\image_ed25519.o
C:\Users\yongc\.platformio\packages\framework-zephyr-mcuboot\boot\bootutil\src\boot_record.c:25:10: fatal error: mcuboot_config/mcuboot_config.h: No such file or directory
 #include "mcuboot_config/mcuboot_config.h"
          ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
compilation terminated.
*** [.pio\build\ivm22_f103re\lib4ab\bootutil\boot_record.o] Error 1
C:\Users\yongc\.platformio\packages\framework-zephyr-mcuboot\boot\bootutil\src\encrypted.c:8:10: fatal error: mcuboot_config/mcuboot_config.h: No such file or directory
 #include "mcuboot_config/mcuboot_config.h"
          ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
compilation terminated.
*** [.pio\build\ivm22_f103re\lib4ab\bootutil\encrypted.o] Error 1
C:\Users\yongc\.platformio\packages\framework-zephyr-mcuboot\boot\bootutil\src\caps.c:20:10: fatal error: mcuboot_config/mcuboot_config.h: No such file or directory
 #include "mcuboot_config/mcuboot_config.h"
          ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
compilation terminated.
C:\Users\yongc\.platformio\packages\framework-zephyr-mcuboot\boot\bootutil\src\bootutil_misc.c:33:10: fatal error: sysflash/sysflash.h: No such file or directory
 #include "sysflash/sysflash.h"
          ^~~~~~~~~~~~~~~~~~~~~
compilation terminated.
C:\Users\yongc\.platformio\packages\framework-zephyr-mcuboot\boot\bootutil\src\image_ec.c:28:10: fatal error: mcuboot_config/mcuboot_config.h: No such file or directory
 #include "mcuboot_config/mcuboot_config.h"
          ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
compilation terminated.
*** [.pio\build\ivm22_f103re\lib4ab\bootutil\bootutil_misc.o] Error 1
*** [.pio\build\ivm22_f103re\lib4ab\bootutil\caps.o] Error 1
*** [.pio\build\ivm22_f103re\lib4ab\bootutil\image_ec.o] Error 1
C:\Users\yongc\.platformio\packages\framework-zephyr-mcuboot\boot\bootutil\src\image_ec256.c:29:10: fatal error: mcuboot_config/mcuboot_config.h: No such file or directory
 #include "mcuboot_config/mcuboot_config.h"
          ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
compilation terminated.
C:\Users\yongc\.platformio\packages\framework-zephyr-mcuboot\boot\bootutil\src\image_ed25519.c:9:10: fatal error: mcuboot_config/mcuboot_config.h: No such file or directory
 #include "mcuboot_config/mcuboot_config.h"
          ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
compilation terminated.
*** [.pio\build\ivm22_f103re\lib4ab\bootutil\image_ec256.o] Error 1
*** [.pio\build\ivm22_f103re\lib4ab\bootutil\image_ed25519.o] Error 1

My platformio.ini file:

...
lib_extra_dirs =
  C:\Users\yongc\.platformio\packages\framework-zephyr-mcuboot\boot\zephyr\include
  C:\Users\yongc\.platformio\packages\framework-zephyr-mcuboot\boot

lib_deps =
  flash_map_backend
  hal
  mcuboot_config
  os
  serial_adapter
  sysflash
  boot_serial
  bootutil

lib extra dirs are for adding libraries from a specific folder. You probably want to

build_flags = 
  -I C:\Users\yongc\.platformio\packages\framework-zephyr-mcuboot\boot\zephyr\include

to add the include path.

But this still doesn’t feel right. If we already have framework-zephyr-mcuboot, there should be a nice way to include it – no hacking with adding it as a library dependency or with include flags.

Maybe @valeros has some insights?

Please be noted that I’m very new to Platform.IO (~1 week), and had negligible experience in CMakeList.txt.

I’ve tried to copy platformio.ini, /board & /zephyr folder folder to the framework-zephyr-mcuboot folder and tried to build there straightaway, after fixing some compilation error with the CMakeLists.txt and possibly some other issues, I managed to build and flash it on my custom board, but it only output the Zephyr bootup string:

*** Booting Zephyr 20400 ***

and that’s it. I debugged and it turns out that the Zephyr OS was unable to locate an app (main.c) file. I thought that it was because the CMakeLists.txt in the framework-zephyr-mcuboot package doesn’t have these lines:

FILE(GLOB app_sources ../src/*.c*)
target_sources(app PRIVATE ${app_sources})

And the fact that I was able to compile this thing gave me enough courage to scramble everything and start fresh from a Zephyr blinky example.

This time I copied and/or replaced:

  • /framework-zephyr-mcuboot/boot/zephyr/main.c with /blinky/src/main.c
  • /framework-zephyr-mcuboot/boot/zephyr/prj.conf with /blinky/zephyr/prj.conf
  • /framework-zephyr-mcuboot/boot/zephyr/dts.overlay to /blinky/zephyr folder
  • /framework-zephyr-mcuboot/boot/zephyr/Kconfig to /blinky/zephyr folder

as well as my custom board json definitions in blinky/board folder and dts files in /blinky/zephyr/boards folder.

I then edit the blinky/zephyr/CMakeLists.txt by copying lines from the framework-zephyr-mcuboot’s CMakeLists.txt, and edited the MCUBOOT_FW so that it points to the framework-mcuboot-zephyr package like so:

get_filename_component(PIO_FW_DIR ${ZEPHYR_BASE} DIRECTORY)

# Path to the "framework-zephyr-mcuboot" platformio package
set(MCUBOOT_FW ${PIO_FW_DIR}/framework-zephyr-mcuboot)

And now I ended up with this error if full rebuild is performed:

<command-line>: fatal error: mcuboot-mbedtls-cfg.h: No such file or directory

*****************************************************************************
* Looking for mcuboot-mbedtls-cfg.h dependency? Check our library registry!
*
* CLI  > platformio lib search "header:mcuboot-mbedtls-cfg.h"
* Web  > Compiling .pio\build\ivm22_f103re\arch__arm__core__aarch32__cortex_m\zephyr\arch\arch\arm\core\aarch32\cortex_m\irq_init.c.o
https://platformio.org/lib/search?query=header:mcuboot-mbedtls-cfg.hCompiling .pio\build\ivm22_f103re\..__-mbedtls\modules\framework-zephyr-mbedtls\library\x509_csr.c.o

*
*****************************************************************************

Compiling .pio\build\ivm22_f103re\..__-mbedtls\modules\framework-zephyr-mbedtls\library\pkcs12.c.o
compilation terminated.
*** [.pio\build\ivm22_f103re\..__-mbedtls\modules\framework-zephyr-mbedtls\library\md4.c.o] Error 1
<command-line>: fatal error: mcuboot-mbedtls-cfg.h: No such file or directory

*****************************************************************************
* Looking for mcuboot-mbedtls-cfg.h dependency? Check our library registry!
*
* CLI  > platformio lib search "header:mcuboot-mbedtls-cfg.h"
* Web  > https://platformio.org/lib/search?query=header:mcuboot-mbedtls-cfg.h
*
*****************************************************************************

compilation terminated.
*** [.pio\build\ivm22_f103re\..__-mbedtls\modules\framework-zephyr-mbedtls\library\x509_csr.c.o] Error 1
<command-line>: fatal error: mcuboot-mbedtls-cfg.h: No such file or directory

*****************************************************************************
* Looking for mcuboot-mbedtls-cfg.h dependency? Check our library registry!
*
* CLI  > platformio lib search "header:mcuboot-mbedtls-cfg.h"
* Web  > https://platformio.org/lib/search?query=header:mcuboot-mbedtls-cfg.h
*
*****************************************************************************

compilation terminated.
*** [.pio\build\ivm22_f103re\..__-mbedtls\modules\framework-zephyr-mbedtls\library\pkcs12.c.o] Error 1

Build again without clean ends up with this:

In file included from src\main.c:27:
C:\Users\yongc\.platformio\packages\framework-zephyr-mcuboot\boot\zephyr\include/target.h:42:2: error: #error "Target support is incomplete; cannot build mcuboot."
 #error "Target support is incomplete; cannot build mcuboot."
  ^~~~~
Compiling .pio\build\ivm22_f103re\kernel\zephyr\kernel\thread_abort.c.o
*** [.pio\build\ivm22_f103re\src\main.o] Error 1

The framework-zephyr-mcuboot/boot/zephyr/include/target.h part that is giving me issue:

/*
 * Sanity check the target support.
 */
#if (!defined(CONFIG_XTENSA) && !defined(DT_CHOSEN_ZEPHYR_FLASH_CONTROLLER_LABEL)) || \
    (defined(CONFIG_XTENSA) && !defined(JEDEC_SPI_NOR_0_LABEL)) || \
    !defined(FLASH_ALIGN) ||                  \
    !(FLASH_AREA_LABEL_EXISTS(image_0)) || \
    !(FLASH_AREA_LABEL_EXISTS(image_1) || CONFIG_SINGLE_IMAGE_DFU) || \
    (!defined(CONFIG_BOOT_SWAP_USING_MOVE) && !FLASH_AREA_LABEL_EXISTS(image_scratch) && !defined(CONFIG_SINGLE_IMAGE_DFU))
#error "Target support is incomplete; cannot build mcuboot."
#endif

And I’m not sure what to do next.
This is my blinky’s CMakeLists.txt file for anyone who is interested:

# Path to out-of-tree boards definitions
set (BOARD_ROOT "${CMAKE_CURRENT_SOURCE_DIR}")

cmake_minimum_required(VERSION 3.13.1)
#include($ENV{ZEPHYR_BASE}/cmake/app/boilerplate.cmake NO_POLICY_SCOPE)

# Add a common dts overlay necessary to ensure mcuboot is linked into,
# and fits inside, the boot partition. (If the user specified a
# DTC_OVERLAY_FILE on the CMake command line, we need to append onto
# the list).
if(DTC_OVERLAY_FILE)
  set(DTC_OVERLAY_FILE
    "${DTC_OVERLAY_FILE} ${CMAKE_CURRENT_LIST_DIR}/dts.overlay"
    CACHE STRING "" FORCE
    )
else()
  set(DTC_OVERLAY_FILE ${CMAKE_CURRENT_LIST_DIR}/dts.overlay)
endif()

# Enable Zephyr runner options which request mass erase if so
# configured.
#
# Note that this also disables the default "leave" option when
# targeting STM32 DfuSe devices with dfu-util, making the chip stay in
# the bootloader after flashing.
#
# That's the right thing, because mcuboot has nothing to do since the
# chip was just erased. The next thing the user is going to want to do
# is flash the application. (Developers can reset DfuSE devices
# manually to test mcuboot behavior on an otherwise erased flash
# device.)
macro(app_set_runner_args)
  if(CONFIG_ZEPHYR_TRY_MASS_ERASE)
    board_runner_args(dfu-util "--dfuse-modifiers=force:mass-erase")
    board_runner_args(pyocd "--flash-opt=-e=chip")
    board_runner_args(nrfjprog "--erase")
  endif()
endmacro()

# find_package(Zephyr) in order to load application boilerplate:
# http://docs.zephyrproject.org/application/application.html
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(zephyr_mcuboot)

get_filename_component(PIO_FW_DIR ${ZEPHYR_BASE} DIRECTORY)

# Path to the "framework-zephyr-mcuboot" platformio package
set(MCUBOOT_FW ${PIO_FW_DIR}/framework-zephyr-mcuboot)
MESSAGE("MCUBOOT_FW -> ${MCUBOOT_FW}")

# Path to "boot" subdirectory of repository root.
set(BOOT_DIR "${MCUBOOT_FW}/boot")
MESSAGE("BOOT_DIR -> ${BOOT_DIR}")
# Path to top-level repository root directory.
get_filename_component(MCUBOOT_DIR ${BOOT_DIR} DIRECTORY)
MESSAGE("MCUBOOT_DIR -> ${MCUBOOT_DIR}")
# Path to tinycrypt library source subdirectory of MCUBOOT_DIR.
set(TINYCRYPT_DIR "${MCUBOOT_DIR}/ext/tinycrypt/lib")
assert_exists(TINYCRYPT_DIR)
set(TINYCRYPT_SHA512_DIR "${MCUBOOT_DIR}/ext/tinycrypt-sha512/lib")
assert_exists(TINYCRYPT_SHA512_DIR)
# Path to crypto-fiat
set(FIAT_DIR "${MCUBOOT_DIR}/ext/fiat")
assert_exists(FIAT_DIR)
# Path to mbed-tls' asn1 parser library.
set(MBEDTLS_ASN1_DIR "${MCUBOOT_DIR}/ext/mbedtls-asn1")
assert_exists(MBEDTLS_ASN1_DIR)
set(NRF_DIR "${MCUBOOT_DIR}/ext/nrf")

if(CONFIG_BOOT_USE_NRF_CC310_BL)
set(NRFXLIB_DIR ${ZEPHYR_BASE}/../nrfxlib)
assert_exists(NRFXLIB_DIR)
# Don't include this if we are using west
 add_subdirectory(${NRFXLIB_DIR} ${PROJECT_BINARY_DIR}/nrfxlib)
endif()

zephyr_library_include_directories(
  ${ZEPHYR_BASE}/drivers/flash
  ${BOOT_DIR}/zephyr/include
  ${BOOT_DIR}/zephyr/targets
  ${MCUBOOT_DIR}/ext/mbedtls-asn1/include
  )
if(EXISTS targets/${BOARD}.h)
  zephyr_library_compile_definitions(MCUBOOT_TARGET_CONFIG="${BOARD}.h")
endif()

# Zephyr port-specific sources.
zephyr_library_sources(
#  main.c
  ${BOOT_DIR}/zephyr/flash_map_extended.c
  ${BOOT_DIR}/zephyr/os.c
  ${BOOT_DIR}/zephyr/keys.c
  )

if(NOT DEFINED CONFIG_FLASH_PAGE_LAYOUT)
  zephyr_library_sources(
    ${BOOT_DIR}/zephyr/flash_map_legacy.c
    )
endif()

# Generic bootutil sources and includes.
zephyr_library_include_directories(${BOOT_DIR}/bootutil/include)
zephyr_library_sources(
  ${BOOT_DIR}/bootutil/src/image_validate.c
  ${BOOT_DIR}/bootutil/src/tlv.c
  ${BOOT_DIR}/bootutil/src/encrypted.c
  ${BOOT_DIR}/bootutil/src/image_rsa.c
  ${BOOT_DIR}/bootutil/src/image_ec256.c
  ${BOOT_DIR}/bootutil/src/image_ed25519.c
  ${BOOT_DIR}/bootutil/src/bootutil_misc.c
  )

if(CONFIG_SINGLE_IMAGE_DFU)
  zephyr_library_sources(
    ${BOOT_DIR}/zephyr/single_loader.c
    )
  zephyr_library_include_directories(${BOOT_DIR}/bootutil/src)
  else()
  zephyr_library_sources(
    ${BOOT_DIR}/bootutil/src/loader.c
    ${BOOT_DIR}/bootutil/src/swap_misc.c
    ${BOOT_DIR}/bootutil/src/swap_scratch.c
    ${BOOT_DIR}/bootutil/src/swap_move.c
    ${BOOT_DIR}/bootutil/src/caps.c
    )
  endif()

if(CONFIG_BOOT_SIGNATURE_TYPE_ECDSA_P256 OR CONFIG_BOOT_ENCRYPT_EC256)
  zephyr_library_include_directories(
    ${MBEDTLS_ASN1_DIR}/include
    )
  zephyr_library_sources(
    # Additionally pull in just the ASN.1 parser from mbedTLS.
    ${MBEDTLS_ASN1_DIR}/src/asn1parse.c
    ${MBEDTLS_ASN1_DIR}/src/platform_util.c
    )
  if(CONFIG_BOOT_USE_TINYCRYPT)
  # When using ECDSA signatures, pull in our copy of the tinycrypt library.
  zephyr_library_include_directories(
    ${BOOT_DIR}/zephyr/include
    ${TINYCRYPT_DIR}/include
    )

  zephyr_library_sources(
    ${TINYCRYPT_DIR}/source/ecc.c
    ${TINYCRYPT_DIR}/source/ecc_dsa.c
    ${TINYCRYPT_DIR}/source/sha256.c
    ${TINYCRYPT_DIR}/source/utils.c
    )
  elseif(CONFIG_BOOT_USE_NRF_CC310_BL)
    zephyr_library_sources(${NRF_DIR}/cc310_glue.c)
    zephyr_library_include_directories(${NRF_DIR})
    zephyr_link_libraries(nrfxlib_crypto)
  endif()

  # Since here we are not using Zephyr's mbedTLS but rather our own, we need
  # to set MBEDTLS_CONFIG_FILE ourselves. When using Zephyr's copy, this
  # variable is set by its Kconfig in the Zephyr codebase.
  zephyr_library_compile_definitions(
    MBEDTLS_CONFIG_FILE="${CMAKE_CURRENT_LIST_DIR}/include/mcuboot-mbedtls-cfg.h"
    )
elseif(CONFIG_BOOT_SIGNATURE_TYPE_NONE)
  zephyr_library_include_directories(
    ${BOOT_DIR}/zephyr/include
    ${TINYCRYPT_DIR}/include
    )

  zephyr_library_sources(
    ${TINYCRYPT_DIR}/source/sha256.c
    ${TINYCRYPT_DIR}/source/utils.c
    )
elseif(CONFIG_BOOT_SIGNATURE_TYPE_RSA)
  # Use mbedTLS provided by Zephyr for RSA signatures. (Its config file
  # is set using Kconfig.)
  zephyr_include_directories(include)
elseif(CONFIG_BOOT_SIGNATURE_TYPE_ED25519 OR CONFIG_BOOT_ENCRYPT_X25519)
  if(CONFIG_BOOT_USE_TINYCRYPT)
    zephyr_library_include_directories(
      ${MBEDTLS_ASN1_DIR}/include
      ${BOOT_DIR}/zephyr/include
      ${TINYCRYPT_DIR}/include
      ${TINYCRYPT_SHA512_DIR}/include
      )
    zephyr_library_sources(
      ${TINYCRYPT_DIR}/source/sha256.c
      ${TINYCRYPT_DIR}/source/utils.c
      ${TINYCRYPT_SHA512_DIR}/source/sha512.c
      # Additionally pull in just the ASN.1 parser from mbedTLS.
      ${MBEDTLS_ASN1_DIR}/src/asn1parse.c
      ${MBEDTLS_ASN1_DIR}/src/platform_util.c
      )
    zephyr_library_compile_definitions(
      MBEDTLS_CONFIG_FILE="${BOOT_DIR}/zephyr/include/mcuboot-mbedtls-cfg.h"
      )
  else()
    zephyr_include_directories(include)
  endif()

  zephyr_library_include_directories(
    ${BOOT_DIR}/zephyr/include
    ${FIAT_DIR}/include/
  )

  zephyr_library_sources(
    ${FIAT_DIR}/src/curve25519.c
  )
endif()

if(CONFIG_BOOT_ENCRYPT_EC256 OR CONFIG_BOOT_ENCRYPT_X25519)
  zephyr_library_sources(
    ${TINYCRYPT_DIR}/source/aes_encrypt.c
    ${TINYCRYPT_DIR}/source/aes_decrypt.c
    ${TINYCRYPT_DIR}/source/ctr_mode.c
    ${TINYCRYPT_DIR}/source/hmac.c
    ${TINYCRYPT_DIR}/source/ecc_dh.c
    )
endif()

if(CONFIG_BOOT_ENCRYPT_EC256)
  zephyr_library_sources(
    ${TINYCRYPT_DIR}/source/ecc_dh.c
    )
endif()

if(CONFIG_MCUBOOT_SERIAL)
  zephyr_sources(${BOOT_DIR}/zephyr/serial_adapter.c)
  zephyr_sources(${BOOT_DIR}/boot_serial/src/boot_serial.c)
  zephyr_sources(${BOOT_DIR}/boot_serial/src/serial_recovery_cbor.c)
  zephyr_sources(${BOOT_DIR}/boot_serial/src/cbor_decode.c)

  zephyr_include_directories(${BOOT_DIR}/bootutil/include)
  zephyr_include_directories(${BOOT_DIR}/boot_serial/include)
  zephyr_include_directories(include)

  zephyr_include_directories_ifdef(
    CONFIG_BOOT_ERASE_PROGRESSIVELY
    ${BOOT_DIR}/bootutil/src
    )
endif()

# CONF_FILE points to the KConfig configuration files of the bootloader.
foreach (filepath ${CONF_FILE})
  file(READ ${filepath} temp_text)
  string(FIND "${temp_text}" ${CONFIG_BOOT_SIGNATURE_KEY_FILE} match)
  if (${match} GREATER_EQUAL 0)
    if (NOT DEFINED CONF_DIR)
      get_filename_component(CONF_DIR ${filepath} DIRECTORY)
    else()
      message(FATAL_ERROR "Signature key file defined in multiple conf files")
    endif()
  endif()
endforeach()

if(NOT CONFIG_BOOT_SIGNATURE_KEY_FILE STREQUAL "")
  if(IS_ABSOLUTE ${CONFIG_BOOT_SIGNATURE_KEY_FILE})
    set(KEY_FILE ${CONFIG_BOOT_SIGNATURE_KEY_FILE})
  elseif((DEFINED CONF_DIR) AND
	 (EXISTS ${CONF_DIR}/${CONFIG_BOOT_SIGNATURE_KEY_FILE}))
    set(KEY_FILE ${CONF_DIR}/${CONFIG_BOOT_SIGNATURE_KEY_FILE})
  else()
    set(KEY_FILE ${MCUBOOT_DIR}/${CONFIG_BOOT_SIGNATURE_KEY_FILE})
  endif()
  message("MCUBoot bootloader key file: ${KEY_FILE}")

  set(GENERATED_PUBKEY ${ZEPHYR_BINARY_DIR}/autogen-pubkey.c)
  add_custom_command(
    OUTPUT ${GENERATED_PUBKEY}
    COMMAND
    ${PYTHON_EXECUTABLE}
    ${MCUBOOT_DIR}/scripts/imgtool.py
    getpub
    -k
    ${KEY_FILE}
    > ${GENERATED_PUBKEY}
    DEPENDS ${KEY_FILE}
    )
  zephyr_library_sources(${GENERATED_PUBKEY})
endif()

if(CONFIG_MCUBOOT_CLEANUP_ARM_CORE)
zephyr_library_sources(
  ${BOOT_DIR}/zephyr/arm_cleanup.c
)
endif()


FILE(GLOB app_sources ../src/*.c*)
target_sources(app PRIVATE ${app_sources})

After all these now I feel like my first try is closer to the solution, but need to find a way to let the compiler ‘know’ that the app (main.c) is now in /zephyr folder instead of the /src folder. But my second approach seems like a ‘cleaner’ approach to me, without having to meddle with the platformio’s framework-zephyr-mcuboot package, and the created mcuboot project (aka blinky) only requires a few files from the framework-zephyr-mcuboot package

I’ve tried the first approach again, but is unable to solve the no main.c issue:

This is the link to the zip to recreate the issue, note that I’m using a custom board.

Hi,
I’m new to PIO & wanted to know if, we can build & successfully run the MCUboot repo in PlatformIO? or has it to be run using west commands, etc.(I have imported MCUboot into PIO, but, has some compilation issues on - file not found - to solve)
Thanks for any suggestions on this!