diff --git a/CMakeLists.txt b/CMakeLists.txt index 57fcf315edcf..08ac1ba1a166 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,6 +7,8 @@ # Point to NCS root directory. set(NRF_DIR ${CMAKE_CURRENT_LIST_DIR} CACHE PATH "NCS root directory") +include(cmake/multi_image.cmake) + zephyr_include_directories(include) add_subdirectory(ext) diff --git a/cmake/multi_image.cmake b/cmake/multi_image.cmake new file mode 100644 index 000000000000..a5e8fb6840dd --- /dev/null +++ b/cmake/multi_image.cmake @@ -0,0 +1,180 @@ +# +# Copyright (c) 2019 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-BSD-5-Clause-Nordic +# + +if(IMAGE_NAME) + set_property( + TARGET zephyr_property_target + APPEND_STRING + PROPERTY shared_vars + "set(${IMAGE_NAME}KERNEL_HEX_NAME ${KERNEL_HEX_NAME})\n" + ) + + set_property( + TARGET zephyr_property_target + APPEND_STRING + PROPERTY shared_vars + "list(APPEND ${IMAGE_NAME}BUILD_BYPRODUCTS ${PROJECT_BINARY_DIR}/${KERNEL_HEX_NAME})\n" + ) + + file(GENERATE OUTPUT ${CMAKE_BINARY_DIR}/shared_vars.cmake + CONTENT $ + ) +endif(IMAGE_NAME) + +function(image_board_selection board_in board_out) + # It is assumed that only the root app will be built as non-secure. + # This is not a valid assumption as there might be multiple non-secure + # images defined. + # TODO: Allow multiple non-secure images by using Kconfig to set the + # secure/non-secure property rather than using a separate board definition. + if(${board_in} STREQUAL nrf9160_pca10090ns) + set(${board_out} nrf9160_pca10090 PARENT_SCOPE) + message("Changed board to secure nrf9160_pca10090 (NOT NS)") + + elseif(${board_in} STREQUAL nrf9160_pca20035ns) + set(${board_out} nrf9160_pca20035 PARENT_SCOPE) + message("Changed board to secure nrf9160_pca20035 (NOT NS)") + + elseif(${board_in} STREQUAL actinius_icarus_ns) + set(${board_out} actinius_icarus PARENT_SCOPE) + message("Changed board to secure actinius_icarus (NOT NS)") + + else() + set(${board_out} ${board_in} PARENT_SCOPE) + endif() +endfunction() + +function(add_child_image name sourcedir) + string(TOUPPER ${name} UPNAME) + + if (CONFIG_${UPNAME}_BUILD_STRATEGY_USE_HEX_FILE) + assert_exists(CONFIG_${UPNAME}_HEX_FILE) + message("Using ${CONFIG_${UPNAME}_HEX_FILE} instead of building ${name}") + elseif (CONFIG_${UPNAME}_BUILD_STRATEGY_SKIP_BUILD) + message("Skipping building of ${name}") + else() + # Build normally + add_child_image_from_source(${name} ${sourcedir}) + endif() +endfunction() + +function(add_child_image_from_source name sourcedir) + message("\n=== child image ${name} begin ===") + + # Construct a list of variables that, when present in the root + # image, should be passed on to all child images as well. + list(APPEND + SHARED_MULTI_IMAGE_VARIABLES + BOARD_DIR + ZEPHYR_MODULES + ZEPHYR_EXTRA_MODULES + ZEPHYR_TOOLCHAIN_VARIANT + GNUARMEMB_TOOLCHAIN_PATH + EXTRA_KCONFIG_TARGETS + ) + + foreach(kconfig_target ${EXTRA_KCONFIG_TARGETS}) + list(APPEND + SHARED_MULTI_IMAGE_VARIABLES + EXTRA_KCONFIG_TARGET_COMMAND_FOR_${kconfig_target} + ) + endforeach() + + unset(image_cmake_args) + list(REMOVE_DUPLICATES SHARED_MULTI_IMAGE_VARIABLES) + foreach(shared_var ${SHARED_MULTI_IMAGE_VARIABLES}) + if(DEFINED ${shared_var}) + list(APPEND image_cmake_args + -D${shared_var}=${${shared_var}} + ) + endif() + endforeach() + + # Set ${name}_BOARD based on what BOARD is set to. + image_board_selection(${BOARD} ${name}_BOARD) + + get_cmake_property(VARIABLES VARIABLES) + get_cmake_property(VARIABLES_CACHED CACHE_VARIABLES) + + set(regex "^${name}_.+") + + list(FILTER VARIABLES INCLUDE REGEX ${regex}) + list(FILTER VARIABLES_CACHED INCLUDE REGEX ${regex}) + + foreach(var_name + ${VARIABLES} + ${VARIABLES_CACHED} + ) + # This regex is guaranteed to match due to the filtering done + # above, we only re-run the regex to extract the part after + # '_'. We run the regex twice because it is believed that + # list(FILTER is faster than doing a string(REGEX on each item. + string(REGEX MATCH "^${name}_(.+)" unused_out_var ${var_name}) + list(APPEND image_cmake_args + -D${CMAKE_MATCH_1}=${${var_name}} + ) + endforeach() + + file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/${name}) + execute_process( + COMMAND ${CMAKE_COMMAND} + -G${CMAKE_GENERATOR} + ${EXTRA_MULTI_IMAGE_CMAKE_ARGS} # E.g. --trace-expand + -DIMAGE_NAME=${name}_ + ${image_cmake_args} + ${sourcedir} + WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/${name} + RESULT_VARIABLE ret + ) + + set_property(DIRECTORY APPEND PROPERTY + CMAKE_CONFIGURE_DEPENDS + ${CMAKE_BINARY_DIR}/${name}/zephyr/.config + ) + + if(NOT ${ret} EQUAL "0") + message(FATAL_ERROR "CMake generation for ${name} failed, aborting. Command: ${ret}") + endif() + + message("=== child image ${name} end ===\n") + + # Include some variables from the child image into the parent image + # namespace + include(${CMAKE_BINARY_DIR}/${name}/shared_vars.cmake) + + # Increase the scope of this variable to make it more available + set(${name}_KERNEL_HEX_NAME ${${name}_KERNEL_HEX_NAME} CACHE STRING "" FORCE) + + include(ExternalProject) + ExternalProject_Add(${name}_subimage + SOURCE_DIR ${sourcedir} + BINARY_DIR ${CMAKE_BINARY_DIR}/${name} + BUILD_BYPRODUCTS ${${name}_BUILD_BYPRODUCTS} # Set by shared_vars.cmake + CONFIGURE_COMMAND "" + BUILD_COMMAND ${CMAKE_MAKE_PROGRAM} + ${EXTRA_MULTI_IMAGE_BUILD_OPT} # E.g. -v + INSTALL_COMMAND "" + BUILD_ALWAYS True + ) + + foreach(kconfig_target + menuconfig + guiconfig + ${EXTRA_KCONFIG_TARGETS} + ) + add_custom_target(${name}_${kconfig_target} + ${CMAKE_MAKE_PROGRAM} ${kconfig_target} + WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/${name} + USES_TERMINAL + ) + endforeach() + + set_property( + GLOBAL APPEND PROPERTY + PM_IMAGES + "${name}" + ) +endfunction() diff --git a/cmake/partition_manager.cmake b/cmake/partition_manager.cmake index b143dcdf8399..2a7d2fe8c5ab 100644 --- a/cmake/partition_manager.cmake +++ b/cmake/partition_manager.cmake @@ -5,200 +5,196 @@ # define_property(GLOBAL PROPERTY PM_IMAGES - BRIEF_DOCS "A list of all images (${IMAGE} instances) that should be processed by the Partition Manager." - FULL_DOCS "A list of all images (${IMAGE} instances) that should be processed by the Partition Manager. + BRIEF_DOCS "A list of all images that should be processed by the Partition Manager." + FULL_DOCS "A list of all images that should be processed by the Partition Manager. Each image's directory will be searched for a pm.yml, and will receive a pm_config.h header file with the result. Also, the each image's hex file will be automatically associated with its partition.") -# Create a variable which exposes the correct logical target for the current image. -set(${IMAGE}logical_target ${logical_target_for_zephyr_elf} CACHE STRING "" FORCE) +get_property(PM_IMAGES GLOBAL PROPERTY PM_IMAGES) +get_property(PM_SUBSYS_PREPROCESSED GLOBAL PROPERTY PM_SUBSYS_PREPROCESSED) -if(FIRST_BOILERPLATE_EXECUTION) - get_property(PM_IMAGES GLOBAL PROPERTY PM_IMAGES) - get_property(PM_SUBSYS_PREPROCESSED GLOBAL PROPERTY PM_SUBSYS_PREPROCESSED) +set(static_configuration_file ${APPLICATION_SOURCE_DIR}/pm_static.yml) - set(static_configuration_file ${APPLICATION_SOURCE_DIR}/pm_static.yml) +if(PM_IMAGES OR (EXISTS ${static_configuration_file})) + # Partition manager is enabled because we have populated PM_IMAGES, + # or because the application has specified a static configuration. + if (EXISTS ${static_configuration_file}) + set(static_configuration --static-config ${static_configuration_file}) + endif() - if(PM_IMAGES OR (EXISTS ${static_configuration_file})) - # Partition manager is enabled because we have populated PM_IMAGES, - # or because the application has specified a static configuration. - if (EXISTS ${static_configuration_file}) - set(static_configuration --static-config ${static_configuration_file}) - endif() + set(generated_path zephyr/include/generated) + + # Add the "app" as an image partition. + set_property(GLOBAL PROPERTY app_PM_HEX_FILE ${PROJECT_BINARY_DIR}/${KERNEL_HEX_NAME}) + set_property(GLOBAL PROPERTY app_PM_TARGET ${logical_target_for_zephyr_elf}) + + # Prepare the input_files, header_files, and images lists + foreach (image_name ${PM_IMAGES}) + list(APPEND images ${image_name}) + list(APPEND input_files ${CMAKE_BINARY_DIR}/${image_name}/${generated_path}/pm.yml) + list(APPEND header_files ${CMAKE_BINARY_DIR}/${image_name}/${generated_path}/pm_config.h) + endforeach() + + # Special treatmen of the app image. + list(APPEND images "app") + list(APPEND input_files ${CMAKE_BINARY_DIR}/${generated_path}/pm.yml) + list(APPEND header_files ${CMAKE_BINARY_DIR}/${generated_path}/pm_config.h) + + # Add subsys defined pm.yml to the input_files + list(APPEND input_files ${PM_SUBSYS_PREPROCESSED}) + + math(EXPR flash_size "${CONFIG_FLASH_SIZE} * 1024") + + set(pm_cmd + ${PYTHON_EXECUTABLE} + ${NRF_DIR}/scripts/partition_manager.py + --input-files ${input_files} + --flash-size ${flash_size} + --output ${CMAKE_BINARY_DIR}/partitions.yml + ${static_configuration} + ) + + set(pm_output_cmd + ${PYTHON_EXECUTABLE} + ${NRF_DIR}/scripts/partition_manager_output.py + --input ${CMAKE_BINARY_DIR}/partitions.yml + --config-file ${CMAKE_BINARY_DIR}/pm.config + --input-names ${images} + --header-files + ${header_files} + ) + + # Run the partition manager algorithm. + execute_process( + COMMAND + ${pm_cmd} + RESULT_VARIABLE ret + ) + + if(NOT ${ret} EQUAL "0") + message(FATAL_ERROR "Partition Manager failed, aborting. Command: ${pm_cmd}") + endif() - set(generated_path include/generated) - - # Add the "app" as an image partition. - set_property(GLOBAL PROPERTY app_PROJECT_BINARY_DIR ${PROJECT_BINARY_DIR}) - set_property(GLOBAL PROPERTY app_PM_HEX_FILE ${PROJECT_BINARY_DIR}/${KERNEL_HEX_NAME}) - set_property(GLOBAL PROPERTY app_PM_TARGET ${logical_target_for_zephyr_elf}) - list(APPEND PM_IMAGES app_) - - # Prepare the input_files, header_files, and images lists - foreach (IMAGE ${PM_IMAGES}) - get_image_name(${IMAGE} image_name) # Removes the trailing '_' - list(APPEND images ${image_name}) - get_property(${IMAGE}PROJECT_BINARY_DIR GLOBAL PROPERTY ${IMAGE}PROJECT_BINARY_DIR) - list(APPEND input_files ${${IMAGE}PROJECT_BINARY_DIR}/${generated_path}/pm.yml) - list(APPEND header_files ${${IMAGE}PROJECT_BINARY_DIR}/${generated_path}/pm_config.h) - endforeach() + # Produce header files and config file. + execute_process( + COMMAND + ${pm_output_cmd} + RESULT_VARIABLE ret + ) - # Add subsys defined pm.yml to the input_files - list(APPEND input_files ${PM_SUBSYS_PREPROCESSED}) + if(NOT ${ret} EQUAL "0") + message(FATAL_ERROR "Partition Manager output generation failed, aborting. Command: ${pm_output_cmd}") + endif() - math(EXPR flash_size "${CONFIG_FLASH_SIZE} * 1024") + # Create a dummy target that we can add properties to for + # extraction in generator expressions. + add_custom_target(partition_manager) - set(pm_cmd - ${PYTHON_EXECUTABLE} - ${NRF_DIR}/scripts/partition_manager.py - --input-files ${input_files} - --flash-size ${flash_size} - --output ${CMAKE_BINARY_DIR}/partitions.yml - ${static_configuration} - ) + # Make Partition Manager configuration available in CMake + import_kconfig(PM_ ${CMAKE_BINARY_DIR}/pm.config pm_var_names) - set(pm_output_cmd - ${PYTHON_EXECUTABLE} - ${NRF_DIR}/scripts/partition_manager_output.py - --input ${CMAKE_BINARY_DIR}/partitions.yml - --config-file ${CMAKE_BINARY_DIR}/pm.config - --input-names ${images} - --header-files - ${header_files} - ) - - # Run the partition manager algorithm. - execute_process( - COMMAND - ${pm_cmd} - RESULT_VARIABLE ret + foreach(name ${pm_var_names}) + set_property( + TARGET partition_manager + PROPERTY ${name} + ${${name}} ) + endforeach() - if(NOT ${ret} EQUAL "0") - message(FATAL_ERROR "Partition Manager failed, aborting. Command: ${pm_cmd}") - endif() + # Turn the space-separated list into a Cmake list. + string(REPLACE " " ";" PM_ALL_BY_SIZE ${PM_ALL_BY_SIZE}) - # Produce header files and config file. - execute_process( - COMMAND - ${pm_output_cmd} - RESULT_VARIABLE ret - ) + # Iterate over every partition, from smallest to largest. + foreach(part ${PM_ALL_BY_SIZE}) + string(TOUPPER ${part} PART) + get_property(${part}_PM_HEX_FILE GLOBAL PROPERTY ${part}_PM_HEX_FILE) - if(NOT ${ret} EQUAL "0") - message(FATAL_ERROR "Partition Manager output generation failed, aborting. Command: ${pm_output_cmd}") + # Process container partitions (if it has a SPAN list it is a container partition). + if(DEFINED PM_${PART}_SPAN) + string(REPLACE " " ";" PM_${PART}_SPAN ${PM_${PART}_SPAN}) + list(APPEND containers ${part}) endif() - # Create a dummy target that we can add properties to for - # extraction in generator expressions. - add_custom_target(partition_manager) + # Include the partition in the merge operation if it has a hex file. + if(DEFINED ${part}_PM_HEX_FILE) + get_property(${part}_PM_TARGET GLOBAL PROPERTY ${part}_PM_TARGET) + list(APPEND explicitly_assigned ${part}) + else() + if(${part} IN_LIST images) + set(${part}_PM_HEX_FILE ${CMAKE_BINARY_DIR}/${part}/zephyr/${${part}_KERNEL_HEX_NAME}) + set(${part}_PM_TARGET ${part}_subimage) + elseif(${part} IN_LIST containers) + set(${part}_PM_HEX_FILE ${PROJECT_BINARY_DIR}/${part}.hex) + set(${part}_PM_TARGET ${part}_hex) + endif() + list(APPEND implicitly_assigned ${part}) + endif() + endforeach() - # Make Partition Manager configuration available in CMake - import_kconfig(PM_ ${CMAKE_BINARY_DIR}/pm.config pm_var_names) + set(PM_MERGED_SPAN ${implicitly_assigned} ${explicitly_assigned}) + set(merged_overlap TRUE) # Enable overlapping for the merged hex file. - foreach(name ${pm_var_names}) - set_property( - TARGET partition_manager - PROPERTY ${name} - ${${name}} - ) - endforeach() + # Iterate over all container partitions, plus the "fake" merged paritition. + # The loop will create a hex file for each iteration. + foreach(container ${containers} merged) + string(TOUPPER ${container} CONTAINER) - # Turn the space-separated list into a Cmake list. - string(REPLACE " " ";" PM_ALL_BY_SIZE ${PM_ALL_BY_SIZE}) - - # Iterate over every partition, from smallest to largest. - foreach(part ${PM_ALL_BY_SIZE}) + # Prepare the list of hex files and list of dependencies for the merge command. + foreach(part ${PM_${CONTAINER}_SPAN}) string(TOUPPER ${part} PART) - get_property(${part}_PM_HEX_FILE GLOBAL PROPERTY ${part}_PM_HEX_FILE) - - # Process container partitions (if it has a SPAN list it is a container partition). - if(DEFINED PM_${PART}_SPAN) - string(REPLACE " " ";" PM_${PART}_SPAN ${PM_${PART}_SPAN}) - list(APPEND containers ${part}) - endif() - - # Include the partition in the merge operation if it has a hex file. - if(DEFINED ${part}_PM_HEX_FILE) - get_property(${part}_PM_TARGET GLOBAL PROPERTY ${part}_PM_TARGET) - list(APPEND explicitly_assigned ${part}) - else() - if(${part} IN_LIST images) - get_property(${part}_KERNEL_NAME GLOBAL PROPERTY ${part}_KERNEL_NAME) - set(${part}_PM_HEX_FILE ${${part}_PROJECT_BINARY_DIR}/${${part}_KERNEL_NAME}.hex) - set(${part}_PM_TARGET ${${part}_logical_target}) - elseif(${part} IN_LIST containers) - set(${part}_PM_HEX_FILE ${PROJECT_BINARY_DIR}/${part}.hex) - set(${part}_PM_TARGET ${part}_hex) - endif() - list(APPEND implicitly_assigned ${part}) - endif() + list(APPEND ${container}hex_files ${${part}_PM_HEX_FILE}) + list(APPEND ${container}targets ${${part}_PM_TARGET}) endforeach() - set(PM_MERGED_SPAN ${implicitly_assigned} ${explicitly_assigned}) - set(merged_overlap TRUE) # Enable overlapping for the merged hex file. - - # Iterate over all container partitions, plus the "fake" merged paritition. - # The loop will create a hex file for each iteration. - foreach(container ${containers} merged) - string(TOUPPER ${container} CONTAINER) - - # Prepare the list of hex files and list of dependencies for the merge command. - foreach(part ${PM_${CONTAINER}_SPAN}) - string(TOUPPER ${part} PART) - list(APPEND ${container}hex_files ${${part}_PM_HEX_FILE}) - list(APPEND ${container}targets ${${part}_PM_TARGET}) - endforeach() + # If overlapping is enabled, add the appropriate argument. + if(${${container}_overlap}) + set(${container}overlap_arg --overlap=replace) + endif() - # If overlapping is enabled, add the appropriate argument. - if(${${container}_overlap}) - set(${container}overlap_arg --overlap=replace) - endif() + # Add command to merge files. + add_custom_command( + OUTPUT ${PROJECT_BINARY_DIR}/${container}.hex + COMMAND + ${PYTHON_EXECUTABLE} + ${ZEPHYR_BASE}/scripts/mergehex.py + -o ${PROJECT_BINARY_DIR}/${container}.hex + ${${container}overlap_arg} + ${${container}hex_files} + DEPENDS + ${${container}targets} + ${${container}hex_files} + ) - # Add command to merge files. - add_custom_command( - OUTPUT ${PROJECT_BINARY_DIR}/${container}.hex - COMMAND - ${PYTHON_EXECUTABLE} - ${ZEPHYR_BASE}/scripts/mergehex.py - -o ${PROJECT_BINARY_DIR}/${container}.hex - ${${container}overlap_arg} - ${${container}hex_files} - DEPENDS - ${${container}targets} - ) - - # Wrapper target for the merge command. - add_custom_target(${container}_hex ALL DEPENDS ${PROJECT_BINARY_DIR}/${container}.hex) - endforeach() + # Wrapper target for the merge command. + add_custom_target(${container}_hex ALL DEPENDS ${PROJECT_BINARY_DIR}/${container}.hex) + endforeach() - # Add merged.hex as the representative hex file for flashing this app. - if(TARGET flash) - add_dependencies(flash merged_hex) - endif() - set(ZEPHYR_RUNNER_CONFIG_KERNEL_HEX "${PROJECT_BINARY_DIR}/merged.hex" - CACHE STRING "Path to merged image in Intel Hex format" FORCE) + # Add merged.hex as the representative hex file for flashing this app. + if(TARGET flash) + add_dependencies(flash merged_hex) endif() + set(ZEPHYR_RUNNER_CONFIG_KERNEL_HEX "${PROJECT_BINARY_DIR}/merged.hex" + CACHE STRING "Path to merged image in Intel Hex format" FORCE) +endif() - if (CONFIG_SECURE_BOOT AND CONFIG_BOOTLOADER_MCUBOOT) - # Create symbols for the offsets required for moving test update hex files - # to MCUBoots secondary slot. This is needed because objcopy does not - # support arithmetic expressions as argument (e.g. '0x100+0x200'), and all - # of the symbols used to generate the offset is only available as a - # generator expression when MCUBoots cmake code exectues. This because - # partition manager is performed as the last step in the configuration stage. - math(EXPR s0_offset "${PM_MCUBOOT_SECONDARY_ADDRESS} - ${PM_S0_ADDRESS}") - math(EXPR s1_offset "${PM_MCUBOOT_SECONDARY_ADDRESS} - ${PM_S1_ADDRESS}") - - set_property( - TARGET partition_manager - PROPERTY s0_TO_SECONDARY - ${s0_offset} - ) - set_property( - TARGET partition_manager - PROPERTY s1_TO_SECONDARY - ${s1_offset} - ) - endif() +if (CONFIG_SECURE_BOOT AND CONFIG_BOOTLOADER_MCUBOOT) + # Create symbols for the offsets required for moving test update hex files + # to MCUBoots secondary slot. This is needed because objcopy does not + # support arithmetic expressions as argument (e.g. '0x100+0x200'), and all + # of the symbols used to generate the offset is only available as a + # generator expression when MCUBoots cmake code exectues. This because + # partition manager is performed as the last step in the configuration stage. + math(EXPR s0_offset "${PM_MCUBOOT_SECONDARY_ADDRESS} - ${PM_S0_ADDRESS}") + math(EXPR s1_offset "${PM_MCUBOOT_SECONDARY_ADDRESS} - ${PM_S1_ADDRESS}") + + set_property( + TARGET partition_manager + PROPERTY s0_TO_SECONDARY + ${s0_offset} + ) + set_property( + TARGET partition_manager + PROPERTY s1_TO_SECONDARY + ${s1_offset} + ) endif() diff --git a/doc/nrf/ug_multi_image.rst b/doc/nrf/ug_multi_image.rst index efbb1bec4f6a..2974d82c8440 100644 --- a/doc/nrf/ug_multi_image.rst +++ b/doc/nrf/ug_multi_image.rst @@ -4,9 +4,27 @@ Multi-image builds ################## In many cases, the firmware that is programmed to a device consists of not only one application, but several separate images, where one of the images (the *parent image*) requires one or more other images (the *child images*) to be present. +The child image then *chain-loads* (or *boots*) the parent image, which in turn might be a child image to another parent image and boot that one. The most common use case for builds consisting of multiple images is an application that requires a bootloader to be present. -In the context of |NCS|, multiple images are required in the following scenarios: + +When to use multiple images +*************************** + +An *image* (also referred to as executable, program, or elf file) consists of pieces of code and data that are identified by image-unique names recorded in a single symbol table. +The symbol table exists as metadata in a ``.elf`` or ``.exe`` file and is not included when the image is converted to a HEX file for programming. +Instead, the code and data are placed at addresses by a linker. +This linking process is what distinguishes images from object files (which do not require linking). +Therefore, to determine if you have zero, one, or more images, count the number of times the linker runs. + +Using multiple images has the following advantages: + +* You can run the linker multiple times and partition the final firmware into several regions. + This partitioning is often useful for bootloaders. +* Since there is a symbol table for each image, the same symbol names can exist multiple times in the final firmware. + This is useful for bootloader images, which might required their own copy of a library that the application uses, but in a different version or configuration. + +In the |NCS|, multiple images are required in the following scenarios: nRF9160 SPU configuration The nRF9160 SiP application MCU is divided into a secure and a non-secure domain. @@ -24,12 +42,148 @@ MCUboot bootloader See :ref:`about_mcuboot` for more information. The MCUboot bootloader is used in the :ref:`http_application_update_sample` sample. -In such cases where the firmware consists of several images, you can build each of the images separately and program it to the correct place in flash. +nRF5340 support + nRF5340 contains two separate processors: a network core and an application core. + When programming applications to the nRF5340 PDK, they must be divided into at least two images, one for each core. + See :ref:`ug_nrf5340` for more information. + + .. important:: + Currently, the two images must be built and programmed separately. + Multi-image builds are not yet supported for nRF5340. + + +Default configuration +********************* + +The |NCS| samples are set up to build all related images as one solution, starting from the parent image. +This is referred to as *multi-image build*. + +When building the parent image, you can configure how the child image should be handled: + +* Build the child image from source and include it with the parent image. + This is the default setting. +* Use a prebuilt HEX file of the child image and include it with the parent image. +* Ignore the child image. + +When building the child image from source or using a prebuilt HEX file, the build system merges the HEX files of the parent and child image together so that they can easily be programmed in one single step. +This means that you might enable and integrate an additional image without even realizing it, just by using the default configuration. + +To change the default configuration and configure how a child image is handled, locate the BUILD_STRATEGY configuration options for the respective child image in the parent image configuration. +For example, to use a prebuilt HEX file of the :ref:`secure_partition_manager` instead of building it, select :option:`CONFIG_SPM_BUILD_STRATEGY_USE_HEX_FILE` instead of the default :option:`CONFIG_SPM_BUILD_STRATEGY_FROM_SOURCE`, and specify the HEX file in :option:`CONFIG_SPM_HEX_FILE`. +To ignore an MCUboot child image, select :option:`CONFIG_MCUBOOT_BUILD_STRATEGY_SKIP_BUILD` instead of :option:`CONFIG_MCUBOOT_BUILD_STRATEGY_FROM_SOURCE`. + + +Defining and enabling a child image +*********************************** + +You can enable existing child images in the |NCS| by enabling the respective modules in the parent image and selecting the desired build strategy. +To turn an application that you have implemented into a child image that can be included in a parent image, you must update the build scripts to make it possible to enable the child image and add the required configuration options. +You should also know how image-specific variables are disambiguated and what targets of the child images are available. + +Updating the build scripts +========================== + +To make it possible to enable a child image from a parent image, you must include the child image in the build script. + +This code should be put in place in the cmake tree that is conditional on a configuration option for having the parent image use the child image. +In the |NCS|, the code is included in the :file:`CMakeLists.txt` file for the samples, and in the MCUboot repository. + +See the following example code: + +.. code-block:: cmake + + if (CONFIG_SPM) + add_child_image(spm ${CMAKE_CURRENT_LIST_DIR}/nrf9160/spm) + endif() + + if (CONFIG_SECURE_BOOT) + add_child_image(b0 ${CMAKE_CURRENT_LIST_DIR}/bootloader) + endif() + + if (CONFIG_BOOTLOADER_MCUBOOT) + add_child_image(mcuboot ${MCUBOOT_BASE}/boot/zephyr) + endif() + +In this code, ``add_child_image`` registers the child image with the given name and file path and executes the build scripts of the child image. +Note that both the child image's application build scripts and the core build scripts are executed. +The core build scripts might use a different configuration and possibly different DeviceTree settings. + +Adding configuration options +============================ + +When enabling a child image, you select the build strategy, thus how the image is included. +The three options are: + +* Build the child image from source along with the parent image - *IMAGENAME*\_BUILD_STRATEGY_FROM_SOURCE +* Merge the specified HEX file of the child image with the parent image - *IMAGENAME*\_BUILD_STRATEGY_USE_HEX_FILE, and *IMAGENAME*\_HEX_FILE to specify the HEX file +* Ignore the child image when building and build only the parent image - *IMAGENAME*\_BUILD_STRATEGY_SKIP_BUILD + +You must add these four configuration options to the Kconfig file for your child image, replacing *IMAGENAME* with the (uppercase) name of your child image (as specified in ``add_child_image``). + +The following example shows the configuration options for MCUboot: + +.. code-block:: Kconfig + + choice + prompt "MCUboot build strategy" + default MCUBOOT_BUILD_STRATEGY_FROM_SOURCE + + config MCUBOOT_BUILD_STRATEGY_USE_HEX_FILE + # Mandatory option when being built through 'add_child_image' + bool "Use HEX file instead of building MCUboot" + + if MCUBOOT_BUILD_STRATEGY_USE_HEX_FILE + + config MCUBOOT_HEX_FILE + # Mandatory option when being built through 'add_child_image' + string "MCUboot HEX file" + + endif # MCUBOOT_USE_HEX_FILE + + config MCUBOOT_BUILD_STRATEGY_SKIP_BUILD + # Mandatory option when being built through 'add_child_image' + bool "Skip building MCUboot" + + config MCUBOOT_BUILD_STRATEGY_FROM_SOURCE + # Mandatory option when being built through 'add_child_image' + bool "Build from source" + + endchoice + + +Image-specific variables +======================== + +The child image and parent image are executed in different CMake processes and thus have different namespaces. +Variables in the parent image are not propagated to the child image, with the following exceptions: + +* Any variable named *IMAGENAME*\_FOO in a parent image is propagated to the child image named *IMAGENAME* as FOO. +* Variables that are in the list ``SHARED_MULTI_IMAGE_VARIABLES`` are propagated to all child images. + +With these two mechanisms, it is possible to set variables in child images from either parent images or the command line, and it is possible to set variables globally across all images. +For example, to change the ``CONF_FILE`` variable for the MCUboot image and the parent image, specify the CMake command as follows:: + + cmake -Dmcuboot_CONF_FILE=prj_a.conf -DCONF_FILE=app_prj.conf + +The command line that is used to create the child images can be extended by adding flags to the CMake variable ``EXTRA_MULTI_IMAGE_CMAKE_ARGS``. +This could for instance be used to get more debug information with the flag ``---trace-expand``. + +Similarly the CMake variable ``EXTRA_MULTI_IMAGE_BUILD_OPT`` can be used to modify the options used when ninja is invoked on the child images. + +Child image targets +=================== + +You can indirectly invoke a selection of child image targets from the parent image. +Currently, the child targets that can be invoked from the parent targets are ``menuconfig``, ``guiconfig``, and any targets listed in ``EXTRA_KCONFIG_TARGETS``. + +To disambiguate targets, the same prefix convention is used as for variables. +This means that to run menuconfig, for example, you invoke the ``menuconfig`` target to configure the parent image and ``mcuboot_menuconfig`` to configure the MCUboot child image. + +You can also invoke any child target directly from its build directory. +Child build directories are located at the top of the parent's build directory. -Alternatively, you can build the related images as one solution, starting from the parent image. -This is referred to as *multi-image build*, and it is the default way how the |NCS| samples are set up. -When building the parent image, you can configure if a child image should automatically be built and included in the parent image (the default), if a prebuilt HEX file should be included instead of the child image, or if the child image should be ignored. -If you are creating your own application, see *Building and Configuring multiple images* in :ref:`zephyr:application` for more information about how to turn your application into a parent image that requires specific child images. +Memory placement +**************** In a multi-image build, all images must be placed in memory so that they do not overlap. The flash start address for each image must be specified by, for example, :option:`CONFIG_FLASH_LOAD_OFFSET`. diff --git a/drivers/entropy/CMakeLists.txt b/drivers/entropy/CMakeLists.txt index 85e8599ad828..aeecd9d867c2 100644 --- a/drivers/entropy/CMakeLists.txt +++ b/drivers/entropy/CMakeLists.txt @@ -12,5 +12,5 @@ zephyr_library_sources_if_kconfig(entropy_cc310.c) # -nRF9150 device that is using SPM and in a secure image # (CONFIG_SPM is not defined in a secure image) if (CONFIG_SOC_NRF52840 OR (CONFIG_SOC_NRF9160 AND (NOT CONFIG_SPM))) - zephyr_link_libraries_ifdef(CONFIG_ENTROPY_CC310 ${IMAGE}platform_cc310) + zephyr_link_libraries_ifdef(CONFIG_ENTROPY_CC310 platform_cc310) endif () diff --git a/drivers/hw_cc310/CMakeLists.txt b/drivers/hw_cc310/CMakeLists.txt index a6cd66eead94..753077450b39 100644 --- a/drivers/hw_cc310/CMakeLists.txt +++ b/drivers/hw_cc310/CMakeLists.txt @@ -7,4 +7,4 @@ zephyr_library() zephyr_library_sources(hw_cc310.c) # Link with the nrf_cc310 platform library -zephyr_library_link_libraries(${IMAGE}platform_cc310) +zephyr_library_link_libraries(platform_cc310) diff --git a/lib/bin/lwm2m_carrier/CMakeLists.txt b/lib/bin/lwm2m_carrier/CMakeLists.txt index 4a9313fbcc06..54d6940e03fe 100644 --- a/lib/bin/lwm2m_carrier/CMakeLists.txt +++ b/lib/bin/lwm2m_carrier/CMakeLists.txt @@ -27,9 +27,9 @@ if(NOT EXISTS ${LWM2M_CARRIER_LIB_PATH}) "(${LWM2M_CARRIER_LIB_PATH} doesn't exist.)") endif() -set( LWM2M_CARRIER_TARGET ${IMAGE}liblwm2m_carrier) +set( LWM2M_CARRIER_TARGET liblwm2m_carrier) zephyr_library_import(${LWM2M_CARRIER_TARGET} ${LWM2M_CARRIER_LIB_PATH}/liblwm2m_carrier.a) -target_link_libraries(${LWM2M_CARRIER_TARGET} INTERFACE ${IMAGE}bsd_nrf9160_xxaa -lc) +target_link_libraries(${LWM2M_CARRIER_TARGET} INTERFACE bsd_nrf9160_xxaa -lc) zephyr_include_directories(include) zephyr_library() diff --git a/samples/CMakeLists.txt b/samples/CMakeLists.txt index af705c5df0b9..4ccb6fa6e742 100644 --- a/samples/CMakeLists.txt +++ b/samples/CMakeLists.txt @@ -5,15 +5,9 @@ # if (CONFIG_SPM) - zephyr_add_executable(spm require_build) - if (${require_build}) - add_subdirectory(nrf9160/spm ${CMAKE_BINARY_DIR}/spm) - endif () + add_child_image(spm ${CMAKE_CURRENT_LIST_DIR}/nrf9160/spm) endif() if (CONFIG_SECURE_BOOT) - zephyr_add_executable(b0 require_build) - if(${require_build}) - add_subdirectory(bootloader ${CMAKE_BINARY_DIR}/b0) - endif () + add_child_image(b0 ${CMAKE_CURRENT_LIST_DIR}/bootloader) endif () diff --git a/scripts/partition_manager/partition_manager.rst b/scripts/partition_manager/partition_manager.rst index d7c32132d055..99a72233875c 100644 --- a/scripts/partition_manager/partition_manager.rst +++ b/scripts/partition_manager/partition_manager.rst @@ -6,7 +6,7 @@ Partition Manager Partition Manager is a Python script that sets the start address and size of all image partitions in a multi-image build context. When creating an application that requires child images (for example, a bootloader), you can configure Partition Manager to control where in memory each image should be placed. -See :ref:`ug_multi_image` and *Building and Configuring multiple images* in :ref:`zephyr:application` for more information about multi-image builds. +See :ref:`ug_multi_image` for more information about multi-image builds. The Partition Manager is activated for all multi-image builds, no matter what build strategy is used for the child image. @@ -259,8 +259,7 @@ It includes :file:`autoconf.h` (which is generated by Kconfig) and uses a Kconfi Build system ************ -The Partition Manager uses Zephyr's multi-image build system to collect configurations for all child images. -If one or more child images are included in a build, their names are appended to a global list. +The build system finds the child images that have been enabled and their configurations. For each image, Partition Manager's CMake code infers the paths to the following files and folders from the name and from other global properties: diff --git a/subsys/bootloader/bl_crypto/CMakeLists.txt b/subsys/bootloader/bl_crypto/CMakeLists.txt index 3609d0c9f7ac..0fbde4fe5416 100644 --- a/subsys/bootloader/bl_crypto/CMakeLists.txt +++ b/subsys/bootloader/bl_crypto/CMakeLists.txt @@ -6,7 +6,7 @@ zephyr_library() zephyr_library_sources(bl_crypto.c) -zephyr_library_link_libraries(${IMAGE}nrfxlib_crypto) +zephyr_library_link_libraries(nrfxlib_crypto) if (CONFIG_SB_CRYPTO_OBERON_ECDSA_SECP256R1) zephyr_library_sources(bl_crypto_oberon_ecdsa.c) diff --git a/subsys/bootloader/cmake/sign.cmake b/subsys/bootloader/cmake/sign.cmake index 6107be445caf..4c5f68f78b88 100644 --- a/subsys/bootloader/cmake/sign.cmake +++ b/subsys/bootloader/cmake/sign.cmake @@ -61,11 +61,19 @@ set(slots s0_image) if (CONFIG_MCUBOOT_BUILD_S1_VARIANT) list(APPEND slots s1_image) + set(s1_image_is_from_child_image mcuboot) endif () foreach (slot ${slots}) set(signed_hex ${PROJECT_BINARY_DIR}/signed_by_b0_${slot}.hex) - set(sign_depends ${PROJECT_BINARY_DIR}/${slot}.hex;${slot}_hex) + + set(sign_depends ${PROJECT_BINARY_DIR}/${slot}.hex) + if(DEFINED ${slot}_is_from_child_image) + list(APPEND sign_depends ${${slot}_is_from_child_image}_subimage) + else() + list(APPEND sign_depends ${slot}_hex) + endif() + set(to_sign ${PROJECT_BINARY_DIR}/${slot}.hex) set(hash_file ${GENERATED_PATH}/${slot}_firmware.sha256) set(signature_file ${GENERATED_PATH}/${slot}_firmware.signature) diff --git a/subsys/nonsecure/CMakeLists.txt b/subsys/nonsecure/CMakeLists.txt index d77c273e2d5f..363a19dfc643 100644 --- a/subsys/nonsecure/CMakeLists.txt +++ b/subsys/nonsecure/CMakeLists.txt @@ -4,4 +4,8 @@ # SPDX-License-Identifier: LicenseRef-BSD-5-Clause-Nordic # -zephyr_sources_ifdef(CONFIG_ARM_FIRMWARE_USES_SECURE_ENTRY_FUNCS secure_services_ns.c) +if(CONFIG_ARM_FIRMWARE_USES_SECURE_ENTRY_FUNCS) + zephyr_sources(secure_services_ns.c) + + add_dependencies(zephyr_interface spm_subimage) +endif() diff --git a/subsys/partition_manager/CMakeLists.txt b/subsys/partition_manager/CMakeLists.txt index 4326bd5501cf..4b1010475d8b 100644 --- a/subsys/partition_manager/CMakeLists.txt +++ b/subsys/partition_manager/CMakeLists.txt @@ -23,30 +23,31 @@ function(preprocess_pm_yml in_file out_file) endfunction() +# We are using partition manager if we are a child image or if we are +# the root image and the 'partition_manager' target exists. +set(using_partition_manager + $,$> + ) zephyr_compile_definitions( - USE_PARTITION_MANAGER=$ + USE_PARTITION_MANAGER=${using_partition_manager} ) + +# TODO: check how this patch got lost and if more are missing set_property(GLOBAL APPEND PROPERTY - ${IMAGE}PROPERTY_LINKER_SCRIPT_DEFINES - -DUSE_PARTITION_MANAGER=$ + PROPERTY_LINKER_SCRIPT_DEFINES + -DUSE_PARTITION_MANAGER=${using_partition_manager} ) -if((EXISTS ${APPLICATION_SOURCE_DIR}/pm.yml) AND IMAGE) +if((EXISTS ${CMAKE_SOURCE_DIR}/pm.yml) AND IMAGE_NAME) # Only preprocess pm.yml when being built as sub image. preprocess_pm_yml( - ${APPLICATION_SOURCE_DIR}/pm.yml + ${CMAKE_SOURCE_DIR}/pm.yml ${PROJECT_BINARY_DIR}/include/generated/pm.yml ) - - set_property( - GLOBAL APPEND PROPERTY - PM_IMAGES - ${IMAGE} - ) endif() -get_property(img_pm_subsys GLOBAL PROPERTY ${IMAGE}PM_SUBSYS) +get_property(img_pm_subsys GLOBAL PROPERTY PM_SUBSYS) if (img_pm_subsys) foreach (subsys_pm_yml ${img_pm_subsys}) file(RELATIVE_PATH rel_path_to_yml ${ZEPHYR_BASE} ${subsys_pm_yml}) diff --git a/subsys/partition_manager/Kconfig.template.build_strategy b/subsys/partition_manager/Kconfig.template.build_strategy index 8113284d06ef..465ea971a1cb 100644 --- a/subsys/partition_manager/Kconfig.template.build_strategy +++ b/subsys/partition_manager/Kconfig.template.build_strategy @@ -3,23 +3,23 @@ choice default $(module)_BUILD_STRATEGY_FROM_SOURCE config $(module)_BUILD_STRATEGY_USE_HEX_FILE - # Mandatory option when being built through 'zephyr_add_executable' + # Mandatory option when being built through add_child_image' bool "Use hex file instead of building $(module)" if $(module)_BUILD_STRATEGY_USE_HEX_FILE config $(module)_HEX_FILE - # Mandatory option when being built through 'zephyr_add_executable' + # Mandatory option when being built through add_child_image' string "$(module) hex file" endif # $(module)_BUILD_STRATEGY_USE_HEX_FILE config $(module)_BUILD_STRATEGY_SKIP_BUILD - # Mandatory option when being built through 'zephyr_add_executable' + # Mandatory option when being built through add_child_image' bool "Skip building $(module)" config $(module)_BUILD_STRATEGY_FROM_SOURCE - # Mandatory option when being built through 'zephyr_add_executable' + # Mandatory option when being built through add_child_image' bool "Build from source" endchoice diff --git a/subsys/spm/CMakeLists.txt b/subsys/spm/CMakeLists.txt index 43ac12ac49f0..24a051041f18 100644 --- a/subsys/spm/CMakeLists.txt +++ b/subsys/spm/CMakeLists.txt @@ -7,3 +7,13 @@ zephyr_sources(spm.c) zephyr_sources_ifdef(CONFIG_SPM_SECURE_SERVICES secure_services.c) zephyr_linker_sources(SECTIONS secure_services.ld) + + +if(CONFIG_ARM_FIRMWARE_HAS_SECURE_ENTRY_FUNCS) + set_property( + TARGET zephyr_property_target + APPEND_STRING + PROPERTY shared_vars + "list(APPEND ${IMAGE_NAME}BUILD_BYPRODUCTS ${CMAKE_BINARY_DIR}/${CONFIG_ARM_ENTRY_VENEERS_LIB_NAME})\n" + ) +endif() diff --git a/subsys/spm/Kconfig b/subsys/spm/Kconfig index 1ff6cf16dbd3..856793f61513 100644 --- a/subsys/spm/Kconfig +++ b/subsys/spm/Kconfig @@ -211,9 +211,15 @@ endif # IS_SPM endmenu -if SPM || IS_SPM -# Set new default name for the veneers file. To avoid file name collisions if -# someone has multiple secure firmwares with secure entry functions. +# Set new default name for the veneers file. + +if ARM_FIRMWARE_USES_SECURE_ENTRY_FUNCS +config ARM_ENTRY_VENEERS_LIB_NAME + string + default "spm/libspmsecureentries.a" +endif + +if ARM_FIRMWARE_HAS_SECURE_ENTRY_FUNCS config ARM_ENTRY_VENEERS_LIB_NAME string default "libspmsecureentries.a" diff --git a/west.yml b/west.yml index fecd7514872c..cedcb974e24a 100644 --- a/west.yml +++ b/west.yml @@ -38,7 +38,7 @@ manifest: - name: zephyr repo-path: fw-nrfconnect-zephyr west-commands: scripts/west-commands.yml - revision: b687d5d02214db82e5bd224e73b8050742a234e0 + revision: 1907c07852a6fc94882d5c8f31b32dcf60203bf1 - name: nffs revision: bc62a2fa9d98ddb5d633c932ea199bc68e10f194 path: modules/fs/nffs @@ -48,20 +48,20 @@ manifest: path: modules/debug/segger remote: zephyrproject - name: mbedtls - repo-path: fw-nrfconnect-mbedtls path: modules/crypto/mbedtls - revision: 9d56815aa4abf99a6e96af6ed035968add4b8cab + revision: 4f1e8f5a78dc08aa42a47cc1ad059cce558c26c3 + remote: zephyrproject - name: mcuboot repo-path: fw-nrfconnect-mcuboot - revision: f1beb319b074115883ef1f7f1e5dbe9281e5e2d0 + revision: 0b59f48e4dce86d0c8d4e09688feb362c7223ad3 - name: mcumgr - repo-path: fw-nrfconnect-mcumgr - revision: f663988d35da559a37f263d369842dbce309d1fa path: modules/lib/mcumgr + revision: 84934959d2d1722a23b7e7e200191ae4a6f96168 + remote: zephyrproject - name: tinycbor - repo-path: fw-nrfconnect-tinycbor path: modules/lib/tinycbor - revision: 86d5ed5dd544b107d2d0882961a19c2c6cb06572 + revision: 0fc68fceacd1efc1ce809c5880c380f3d98b7b6e + remote: zephyrproject - name: ci-tools path: tools/ci-tools remote: zephyrproject @@ -72,7 +72,7 @@ manifest: revision: 30b7efa827b04d2e47840716b0372737fe7d6c92 - name: nrfxlib path: nrfxlib - revision: 562c6785b3ac863cc3d0a994d6f53c780a858891 + revision: fec2a3c3b4d79b7eb6d1ebdf24e6813476116769 - name: cmock path: test/cmock revision: c243b9a7a7b3c471023193992b46cf1bd1910450