diff --git a/CMakeLists.txt b/CMakeLists.txt index 5dc666f6e57..efcd20732d6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1284,6 +1284,11 @@ if(CONFIG_BUILD_OUTPUT_EXE) ) endif() +# Generate and use MCUboot related artifacts as needed. +if(CONFIG_BOOTLOADER_MCUBOOT) + include(${CMAKE_CURRENT_LIST_DIR}/cmake/mcuboot.cmake) +endif() + get_property(extra_post_build_commands GLOBAL PROPERTY extra_post_build_commands diff --git a/Kconfig.zephyr b/Kconfig.zephyr index a76ca8c29dd..26f9a23ed5f 100644 --- a/Kconfig.zephyr +++ b/Kconfig.zephyr @@ -398,6 +398,55 @@ config BOOTLOADER_MCUBOOT (or Armv8-M baseline) targets with no built-in vector relocation mechanisms +if BOOTLOADER_MCUBOOT + +config MCUBOOT_SIGNATURE_KEY_FILE + string "Path to the mcuboot signing key file" + default "" + help + The file contains a key pair whose public half is verified + by your target's MCUboot image. The file is in PEM format. + + If set to a non-empty value, the build system tries to + sign the final binaries using a 'west sign -t imgtool' command. + The signed binaries are placed in the build directory + at zephyr/zephyr.signed.bin and zephyr/zephyr.signed.hex. + + The file names can be customized with CONFIG_KERNEL_BIN_NAME. + The existence of bin and hex files depends on CONFIG_BUILD_OUTPUT_BIN + and CONFIG_BUILD_OUTPUT_HEX. + + This option should contain an absolute path to the same file + as the BOOT_SIGNATURE_KEY_FILE option in your MCUboot + .config. (The MCUboot config option is used for the MCUboot + bootloader image; this option is for your application which + is to be loaded by MCUboot. The MCUboot config option can be + a relative path from the MCUboot repository root; this option's + behavior is undefined for relative paths.) + + If left empty, you must sign the Zephyr binaries manually. + +config MCUBOOT_EXTRA_IMGTOOL_ARGS + string "Extra arguments to pass to imgtool" + default "" + help + If CONFIG_MCUBOOT_SIGNATURE_KEY_FILE is a non-empty string, + you can use this option to pass extra options to imgtool. + For example, you could set this to "--version 1.2". + +config MCUBOOT_GENERATE_CONFIRMED_IMAGE + bool "Also generate a confirmed image" + help + The signed and confirmed binaries are placed in the build directory + at zephyr/zephyr.signed.confirmed.bin and + zephyr/zephyr.signed.confirmed.hex. + + The file names can be customized with CONFIG_KERNEL_BIN_NAME. + The existence of bin and hex files depends on CONFIG_BUILD_OUTPUT_BIN + and CONFIG_BUILD_OUTPUT_HEX. + +endif # BOOTLOADER_MCUBOOT + config BOOTLOADER_ESP_IDF bool "ESP-IDF bootloader support" depends on SOC_ESP32 diff --git a/cmake/mcuboot.cmake b/cmake/mcuboot.cmake new file mode 100644 index 00000000000..318091c13b6 --- /dev/null +++ b/cmake/mcuboot.cmake @@ -0,0 +1,134 @@ +# Copyright (c) 2020 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +# This file includes extra build system logic that is enabled when +# CONFIG_BOOTLOADER_MCUBOOT=y. +# +# It builds signed binaries using imgtool as a post-processing step +# after zephyr/zephyr.elf is created in the build directory. +# +# Since this file is brought in via include(), we do the work in a +# function to avoid polluting the top-level scope. + +function(zephyr_mcuboot_tasks) + set(keyfile "${CONFIG_MCUBOOT_SIGNATURE_KEY_FILE}") + + # Check for misconfiguration. + if("${keyfile}" STREQUAL "") + # No signature key file, no signed binaries. No error, though: + # this is the documented behavior. + return() + endif() + + if(NOT WEST) + # This feature requires west. + message(FATAL_ERROR "Can't sign images for MCUboot: west not found. To fix, install west and ensure it's on PATH.") + endif() + + if(NOT IS_ABSOLUTE "${keyfile}") + # Relative paths are relative to 'west topdir'. + set(keyfile "${WEST_TOPDIR}/${keyfile}") + set(keyfile_relative TRUE) + else() + set(keyfile_relative FALSE) + endif() + + if(NOT EXISTS "${keyfile}") + if(keyfile_relative) + set(relative_msg " Note: relative paths are relative to the west workspace topdir \"${WEST_TOPDIR}\".") + else() + set(relative_msg "") + endif() + message(FATAL_ERROR "Can't sign images for MCUboot: CONFIG_MCUBOOT_SIGNATURE_KEY_FILE=\"${CONFIG_MCUBOOT_SIGNATURE_KEY_FILE}\" not found.${relative_msg}") + elseif(NOT (CONFIG_BUILD_OUTPUT_BIN OR CONFIG_BUILD_OUTPUT_HEX)) + message(FATAL_ERROR "Can't sign images for MCUboot: Neither CONFIG_BUILD_OUTPUT_BIN nor CONFIG_BUILD_OUTPUT_HEX is enabled, so there's nothing to sign.") + endif() + + # Find imgtool. Even though west is installed, imgtool might not be. + # The user may also have a custom manifest which doesn't include + # MCUboot. + # + # Therefore, go with an explicitly installed imgtool first, falling + # back on mcuboot/scripts/imgtool.py. + if(IMGTOOL) + set(imgtool_path "${IMGTOOL}") + elseif(DEFINED ZEPHYR_MCUBOOT_MODULE_DIR) + set(IMGTOOL_PY "${ZEPHYR_MCUBOOT_MODULE_DIR}/scripts/imgtool.py") + if(EXISTS "${IMGTOOL_PY}") + set(imgtool_path "${IMGTOOL_PY}") + endif() + endif() + + # No imgtool, no signed binaries. + if(NOT DEFINED imgtool_path) + message(FATAL_ERROR "Can't sign images for MCUboot: can't find imgtool. To fix, install imgtool with pip3, or add the mcuboot repository to the west manifest and ensure it has a scripts/imgtool.py file.") + return() + endif() + + # Basic 'west sign' command and output format independent arguments. + set(west_sign ${WEST} sign --quiet --tool imgtool + --tool-path "${imgtool_path}" + --build-dir "${APPLICATION_BINARY_DIR}") + + # Arguments to imgtool. + if(NOT CONFIG_MCUBOOT_EXTRA_IMGTOOL_ARGS STREQUAL "") + # Separate extra arguments into the proper format for adding to + # extra_post_build_commands. + # + # Use UNIX_COMMAND syntax for uniform results across host + # platforms. + separate_arguments(imgtool_extra UNIX_COMMAND ${CONFIG_MCUBOOT_EXTRA_IMGTOOL_ARGS}) + else() + set(imgtool_extra) + endif() + set(imgtool_args -- --key "${keyfile}" ${imgtool_extra}) + + # Extensionless prefix of any output file. + set(output ${ZEPHYR_BINARY_DIR}/${KERNEL_NAME}) + + # List of additional build byproducts. + set(byproducts) + + # 'west sign' arguments for confirmed and unconfirmed images. + set(unconfirmed_args) + set(confirmed_args) + + # Set up .bin outputs. + if(CONFIG_BUILD_OUTPUT_BIN) + list(APPEND unconfirmed_args --bin --sbin ${output}.signed.bin) + list(APPEND byproducts ${output}.signed.bin) + + if(CONFIG_MCUBOOT_GENERATE_CONFIRMED_IMAGE) + list(APPEND confirmed_args --bin --sbin ${output}.signed.confirmed.bin) + list(APPEND byproducts ${output}.signed.confirmed.bin) + endif() + endif() + + # Set up .hex outputs. + if(CONFIG_BUILD_OUTPUT_HEX) + list(APPEND unconfirmed_args --hex --shex ${output}.signed.hex) + list(APPEND byproducts ${output}.signed.hex) + + if(CONFIG_MCUBOOT_GENERATE_CONFIRMED_IMAGE) + list(APPEND confirmed_args --hex --shex ${output}.signed.confirmed.hex) + list(APPEND byproducts ${output}.signed.confirmed.hex) + endif() + endif() + + # Add the west sign calls and their byproducts to the post-processing + # steps for zephyr.elf. + # + # CMake guarantees that multiple COMMANDs given to + # add_custom_command() are run in order, so adding the 'west sign' + # calls to the "extra_post_build_commands" property ensures they run + # after the commands which generate the unsigned versions. + set_property(GLOBAL APPEND PROPERTY extra_post_build_commands COMMAND + ${west_sign} ${unconfirmed_args} ${imgtool_args}) + if(confirmed_args) + set_property(GLOBAL APPEND PROPERTY extra_post_build_commands COMMAND + ${west_sign} ${confirmed_args} ${imgtool_args} --confirm) + endif() + set_property(GLOBAL APPEND PROPERTY extra_post_build_byproducts ${byproducts}) +endfunction() + +zephyr_mcuboot_tasks()