diff --git a/cmake/modules/dts.cmake b/cmake/modules/dts.cmake index 376245fbdc1..f72bf4b7289 100644 --- a/cmake/modules/dts.cmake +++ b/cmake/modules/dts.cmake @@ -127,6 +127,9 @@ set(DTS_KCONFIG ${KCONFIG_BINARY_DIR}/Kconfig.dts) # modules. set(VENDOR_PREFIXES dts/bindings/vendor-prefixes.txt) +# Fetch variable from sysbuild which might be forcing a configuration (for variant build images) +zephyr_get(DTS_SOURCE SYSBUILD LOCAL) + if(NOT DEFINED DTS_SOURCE) zephyr_build_string(board_string SHORT shortened_board_string BOARD ${BOARD} BOARD_QUALIFIERS ${BOARD_QUALIFIERS} @@ -258,6 +261,9 @@ zephyr_dt_preprocess( WORKING_DIRECTORY ${APPLICATION_SOURCE_DIR} ) +# Fetch variable from sysbuild which might be forcing a configuration (for variant build images) +zephyr_get(DTS_DEPS SYSBUILD LOCAL) + # # Make sure we re-run CMake if any devicetree sources or transitive # includes change. diff --git a/cmake/modules/kconfig.cmake b/cmake/modules/kconfig.cmake index 1a6332298ba..fe56fb3ffb1 100644 --- a/cmake/modules/kconfig.cmake +++ b/cmake/modules/kconfig.cmake @@ -51,6 +51,8 @@ else() set(KCONFIG_ROOT ${ZEPHYR_BASE}/Kconfig) endif() +zephyr_get(KCONFIG_VARIANT_SOURCE SYSBUILD LOCAL) + if(NOT DEFINED BOARD_DEFCONFIG) zephyr_file(CONF_FILES ${BOARD_DIRECTORIES} DEFCONFIG BOARD_DEFCONFIG) endif() @@ -197,7 +199,11 @@ set(EXTRA_KCONFIG_TARGET_COMMAND_FOR_traceconfig ${PROJECT_BINARY_DIR}/kconfig-trace.md ) -set_ifndef(KCONFIG_TARGETS menuconfig guiconfig hardenconfig traceconfig) +zephyr_get(KCONFIG_TARGETS SYSBUILD LOCAL) + +if(NOT DEFINED KCONFIG_TARGETS) + set(KCONFIG_TARGETS menuconfig guiconfig hardenconfig traceconfig) +endif() foreach(kconfig_target ${KCONFIG_TARGETS} @@ -313,6 +319,13 @@ foreach(f ${merge_config_files}) endif() endforeach() +if(KCONFIG_VARIANT_SOURCE) + set( + merge_config_files + ${KCONFIG_VARIANT_SOURCE} + ) +endif() + # Calculate a checksum of merge_config_files to determine if we need # to re-generate .config set(merge_config_files_checksum "") @@ -355,7 +368,10 @@ if(EXISTS ${DOTCONFIG} AND EXISTS ${merge_config_files_checksum_file}) endif() if(CREATE_NEW_DOTCONFIG) - set(input_configs_flags --handwritten-input-configs) + if(NOT KCONFIG_VARIANT_SOURCE) + set(input_configs_flags --handwritten-input-configs) + endif() + set(input_configs ${merge_config_files} ${FORCED_CONF_FILE}) build_info(kconfig files PATH ${input_configs}) else() diff --git a/share/sysbuild/cmake/modules/sysbuild_extensions.cmake b/share/sysbuild/cmake/modules/sysbuild_extensions.cmake index f5a1137712a..2efbd2f126e 100644 --- a/share/sysbuild/cmake/modules/sysbuild_extensions.cmake +++ b/share/sysbuild/cmake/modules/sysbuild_extensions.cmake @@ -1,4 +1,4 @@ -# Copyright (c) 2021-2023 Nordic Semiconductor +# Copyright (c) 2021-2026 Nordic Semiconductor # # SPDX-License-Identifier: Apache-2.0 @@ -192,7 +192,8 @@ endfunction() # ExternalZephyrProject_Add(APPLICATION # SOURCE_DIR # [BOARD [BOARD_REVISION ]] -# [APP_TYPE ] +# [APP_TYPE ] +# [BUILD_ONLY ] # ) # # This function includes a Zephyr based build system into the multiimage @@ -204,13 +205,14 @@ endfunction() # BOARD : Use for application build instead user defined BOARD. # BOARD_REVISION : Use of for application (only valid if # is also supplied). -# APP_TYPE : Application type. -# MAIN indicates this application is the main application +# APP_TYPE MAIN indicates this application is the main application # and where user defined settings should be passed on as-is # except for multi image build flags. # For example, -DCONF_FILES= will be passed on to the # MAIN_APP unmodified. # BOOTLOADER indicates this app is a bootloader +# FIRMWARE_LOADER indicates this app is a firmware loader image for MCUboot # BUILD_ONLY : Mark the application as build-only. If evaluates to # true, then this application will be excluded from flashing # and debugging. @@ -329,12 +331,12 @@ function(ExternalZephyrProject_Add) set(shared_cmake_vars_argument) foreach(shared_var ${shared_cmake_variables_list}) if(DEFINED CACHE{${ZBUILD_APPLICATION}_${shared_var}}) - get_property(var_type CACHE ${ZBUILD_APPLICATION}_${shared_var} PROPERTY TYPE) + get_property(var_type CACHE ${ZBUILD_APPLICATION}_${shared_var} PROPERTY TYPE) list(APPEND shared_cmake_vars_argument "-D${shared_var}:${var_type}=$CACHE{${ZBUILD_APPLICATION}_${shared_var}}" ) elseif(DEFINED CACHE{${shared_var}}) - get_property(var_type CACHE ${shared_var} PROPERTY TYPE) + get_property(var_type CACHE ${shared_var} PROPERTY TYPE) list(APPEND shared_cmake_vars_argument "-D${shared_var}:${var_type}=$CACHE{${shared_var}}" ) @@ -385,6 +387,7 @@ function(ExternalZephyrProject_Add) BUILD_ALWAYS True USES_TERMINAL_BUILD True ) + set_property(TARGET ${ZBUILD_APPLICATION} PROPERTY APP_SOURCE_DIR ${ZBUILD_SOURCE_DIR}) set_property(TARGET ${ZBUILD_APPLICATION} PROPERTY APP_TYPE ${ZBUILD_APP_TYPE}) set_property(TARGET ${ZBUILD_APPLICATION} PROPERTY CONFIG "# sysbuild controlled configuration settings\n" @@ -447,6 +450,197 @@ function(ExternalZephyrProject_Add) endif() endfunction() +# Usage: +# ExternalZephyrVariantProject_Add(APPLICATION +# SOURCE_APP +# [SNIPPET ] +# [EXTRA_DTC_OVERLAY_FILE ] +# [EXTRA_CONF_FILE ] +# [BUILD_ONLY ] +# ) +# +# This function duplicates an existing Zephyr based build system into the multi-image +# build system with a specified modification. This will not creates the extra build targets that +# ExternalZephyrProject_Add() adds e.g. ``_menuconfig``. Note that the variant image must +# either have a ``CONF_FILE``, ``EXTRA_CONF_FILE``, ``EXTRA_DTC_OVERLAY_FILE`` or ``SNIPPET`` +# added to it or it will be invalid and image configuration will result in a fatal error. +# +# APPLICATION: : Name of the application, name will also be used for build folder +# of the application. +# SOURCE_APP : Name of the existing image to use for duplication. +# SNIPPET : List of default snippets to apply for variant image. +# EXTRA_DTC_OVERLAY_FILE : List of default extra DTC files to apply for variant image. +# EXTRA_CONF_FILE : List of default extra Kconfig fragments to apply for variant +# image. +# BUILD_ONLY : Mark the application as build-only. If evaluates to true, +# then this application will be excluded from flashing and +# debugging. +# +function(ExternalZephyrVariantProject_Add) + cmake_parse_arguments(ZBUILD "" "SOURCE_APP;APPLICATION;SNIPPET;EXTRA_DTC_OVERLAY_FILE;EXTRA_CONF_FILE;BUILD_ONLY" "" ${ARGN}) + + if(ZBUILD_UNPARSED_ARGUMENTS) + message(FATAL_ERROR + "ExternalZephyrVariantProject_Add(${ARGV0} ...) given unknown arguments:" + " ${ZBUILD_UNPARSED_ARGUMENTS}" + ) + endif() + + if(TARGET ${ZBUILD_APPLICATION}) + message(FATAL_ERROR + "ExternalZephyrVariantProject_Add(APPLICATION ${ZBUILD_APPLICATION} ...) " + "already exists. Application names must be unique." + ) + endif() + + if(NOT DEFINED ZBUILD_SOURCE_APP OR NOT TARGET ${ZBUILD_SOURCE_APP}) + message(FATAL_ERROR + "ExternalZephyrVariantProject_Add(SOURCE_APP ${ZBUILD_SOURCE_APP} ...) " + "does not exist. Existing image must already exist." + ) + endif() + + if(NOT DEFINED SYSBUILD_CURRENT_SOURCE_DIR) + message(FATAL_ERROR + "ExternalZephyrVariantProject_Add(${ARGV0} ...) must not be called outside of" + " sysbuild_add_subdirectory(). SYSBUILD_CURRENT_SOURCE_DIR is undefined." + ) + endif() + + get_target_property(ZBUILD_BOARD ${DEFAULT_IMAGE} BOARD) + get_target_property(ZBUILD_SOURCE_DIR ${DEFAULT_IMAGE} APP_SOURCE_DIR) + get_property(var_type CACHE ${ZBUILD_SOURCE_APP}_${shared_var} PROPERTY TYPE) + + set_property( + DIRECTORY "${SYSBUILD_CURRENT_SOURCE_DIR}" + APPEND PROPERTY sysbuild_images ${ZBUILD_APPLICATION} + ) + set_property( + GLOBAL + APPEND PROPERTY sysbuild_images ${ZBUILD_APPLICATION} + ) + + # Update ROOT variables with relative paths to use absolute paths based on + # the source application directory. + foreach(type MODULE_EXT BOARD SOC ARCH SCA) + if(DEFINED CACHE{${ZBUILD_APPLICATION}_${type}_ROOT} AND NOT IS_ABSOLUTE $CACHE{${ZBUILD_APPLICATION}_${type}_ROOT}) + set(rel_path $CACHE{${ZBUILD_APPLICATION}_${type}_ROOT}) + cmake_path(ABSOLUTE_PATH rel_path BASE_DIRECTORY "${ZBUILD_SOURCE_DIR}" NORMALIZE OUTPUT_VARIABLE abs_path) + set(${ZBUILD_APPLICATION}_${type}_ROOT ${abs_path} CACHE PATH "Sysbuild adjusted absolute path" FORCE) + endif() + endforeach() + + # CMake variables which must be known by all Zephyr CMake build systems + # Those are settings which controls the build and must be known to CMake at + # invocation time, and thus cannot be passed through the sysbuild cache file. + set( + shared_cmake_variables_list + CMAKE_BUILD_TYPE + CMAKE_VERBOSE_MAKEFILE + ) + + set(sysbuild_cache_file ${CMAKE_BINARY_DIR}/${ZBUILD_APPLICATION}_sysbuild_cache.txt) + set(shared_cmake_vars_argument) + + foreach(shared_var ${shared_cmake_variables_list}) + if(DEFINED CACHE{${ZBUILD_SOURCE_APP}_${shared_var}}) + get_property(var_type CACHE ${ZBUILD_SOURCE_APP}_${shared_var} PROPERTY TYPE) + list(APPEND shared_cmake_vars_argument + "-D${shared_var}:${var_type}=$CACHE{${ZBUILD_SOURCE_APP}_${shared_var}}" + ) + elseif(DEFINED CACHE{${ZBUILD_APPLICATION}_${shared_var}}) + get_property(var_type CACHE ${ZBUILD_APPLICATION}_${shared_var} PROPERTY TYPE) + list(APPEND shared_cmake_vars_argument + "-D${shared_var}:${var_type}=$CACHE{${ZBUILD_APPLICATION}_${shared_var}}" + ) + elseif(DEFINED CACHE{${shared_var}}) + get_property(var_type CACHE ${shared_var} PROPERTY TYPE) + list(APPEND shared_cmake_vars_argument + "-D${shared_var}:${var_type}=$CACHE{${shared_var}}" + ) + endif() + endforeach() + + set(list_separator ",") + + include(ExternalProject) + set(application_binary_dir ${CMAKE_BINARY_DIR}/${ZBUILD_APPLICATION}) + ExternalProject_Add( + ${ZBUILD_APPLICATION} + SOURCE_DIR ${ZBUILD_SOURCE_DIR} + BINARY_DIR ${application_binary_dir} + CONFIGURE_COMMAND "" + LIST_SEPARATOR "${list_separator}" + CMAKE_ARGS -DSYSBUILD:BOOL=True + -DSYSBUILD_CACHE:FILEPATH=${sysbuild_cache_file} + ${shared_cmake_vars_argument} + BUILD_COMMAND ${CMAKE_COMMAND} --build . + INSTALL_COMMAND "" + BUILD_ALWAYS True + USES_TERMINAL_BUILD True + ) + + get_property(${ZBUILD_SOURCE_APP}_APP_TYPE TARGET ${ZBUILD_SOURCE_APP} PROPERTY APP_TYPE) + + set_property(TARGET ${ZBUILD_APPLICATION} PROPERTY APP_TYPE ${${ZBUILD_SOURCE_APP}_APP_TYPE}) + set_property(TARGET ${ZBUILD_APPLICATION} PROPERTY CONFIG + "# sysbuild controlled configuration settings\n" + ) + set_target_properties(${ZBUILD_APPLICATION} PROPERTIES CACHE_FILE ${sysbuild_cache_file}) + set_target_properties(${ZBUILD_APPLICATION} PROPERTIES KCONFIG_BINARY_DIR + ${application_binary_dir}/Kconfig + ) + + if("${${ZBUILD_SOURCE_APP}_APP_TYPE}" STREQUAL "MAIN") + set_target_properties(${ZBUILD_APPLICATION} PROPERTIES MAIN_APP True) + endif() + + set(${ZBUILD_APPLICATION}_DTS_SOURCE ${CMAKE_BINARY_DIR}/${ZBUILD_SOURCE_APP}/zephyr/zephyr.dts + CACHE INTERNAL "Application DTC file" FORCE + ) + + set(${ZBUILD_APPLICATION}_DTS_DEPS ${CMAKE_BINARY_DIR}/${ZBUILD_SOURCE_APP}/zephyr/zephyr.dts.d + CACHE INTERNAL "Application DTC dependency file" FORCE + ) + + set(${ZBUILD_APPLICATION}_KCONFIG_VARIANT_SOURCE + ${CMAKE_BINARY_DIR}/${ZBUILD_SOURCE_APP}/zephyr/.config + CACHE INTERNAL "Application config file" FORCE + ) + + set(${ZBUILD_APPLICATION}_KCONFIG_TARGETS "KCONFIG_TARGETS-NOTFOUND" + CACHE INTERNAL "Disable Kconfig targets" FORCE + ) + + set(${ZBUILD_APPLICATION}_SNIPPET ${ZBUILD_SNIPPET} + CACHE INTERNAL "Application snippet" FORCE + ) + + set(${ZBUILD_APPLICATION}_EXTRA_DTC_OVERLAY_FILE ${ZBUILD_EXTRA_DTC_OVERLAY_FILE} + CACHE INTERNAL "Application extra DTC overlay file" FORCE + ) + + set(${ZBUILD_APPLICATION}_EXTRA_CONF_FILE ${ZBUILD_EXTRA_CONF_FILE} + CACHE INTERNAL "Application extra config file" FORCE + ) + + set_target_properties(${ZBUILD_APPLICATION} PROPERTIES IMAGE_CONF_SCRIPT "") + set_target_properties(${ZBUILD_APPLICATION} PROPERTIES APP_CLONE ${ZBUILD_SOURCE_APP}) + + if(DEFINED ZBUILD_BOARD) + # Only set image specific board if provided. + # The sysbuild BOARD is exported through sysbuild cache, and will be used + # unless _BOARD is defined. + set_target_properties(${ZBUILD_APPLICATION} PROPERTIES BOARD ${ZBUILD_BOARD}) + endif() + + if(DEFINED ZBUILD_BUILD_ONLY) + set_target_properties(${ZBUILD_APPLICATION} PROPERTIES BUILD_ONLY ${ZBUILD_BUILD_ONLY}) + endif() + + sysbuild_add_dependencies(CONFIGURE ${ZBUILD_APPLICATION} ${ZBUILD_SOURCE_APP}) +endfunction() + # Usage: # ExternalZephyrProject_Cmake(APPLICATION ) # @@ -500,18 +694,32 @@ function(ExternalZephyrProject_Cmake) ExternalProject_Get_Property(${ZCMAKE_APPLICATION} SOURCE_DIR BINARY_DIR CMAKE_ARGS LIST_SEPARATOR) get_target_property(${ZCMAKE_APPLICATION}_BOARD ${ZCMAKE_APPLICATION} BOARD) + get_target_property(${ZCMAKE_APPLICATION}_APP_CLONE ${ZCMAKE_APPLICATION} APP_CLONE) + set(dotconfigsysbuild ${BINARY_DIR}/zephyr/.config.sysbuild) + sysbuild_cache(CREATE APPLICATION ${ZCMAKE_APPLICATION}) get_property(${ZCMAKE_APPLICATION}_CONF_SCRIPT TARGET ${ZCMAKE_APPLICATION} PROPERTY IMAGE_CONF_SCRIPT ) - sysbuild_cache(CREATE APPLICATION ${ZCMAKE_APPLICATION}) + if(${ZCMAKE_APPLICATION}_APP_CLONE) + if(NOT ${ZCMAKE_APPLICATION}_CONF_SCRIPT AND NOT ${ZCMAKE_APPLICATION}_EXTRA_CONF_FILE AND NOT + ${ZCMAKE_APPLICATION}_EXTRA_DTC_OVERLAY_FILE AND NOT ${ZCMAKE_APPLICATION}_SNIPPET + ) + message(FATAL_ERROR + "${ZCMAKE_APPLICATION} is a variant application but has no CONF_SCRIPT, EXTRA_CONF_FILE, " + "EXTRA_DTC_OVERLAY_FILE or SNIPPET variables defined, which is not valid." + ) + endif() + + get_target_property(config_content ${${ZCMAKE_APPLICATION}_APP_CLONE} CONFIG) + set_property(TARGET ${ZCMAKE_APPLICATION} PROPERTY CONFIG ${config_content}) + endif() foreach(script ${${ZCMAKE_APPLICATION}_CONF_SCRIPT}) include(${script}) endforeach() - set(dotconfigsysbuild ${BINARY_DIR}/zephyr/.config.sysbuild) get_target_property(config_content ${ZCMAKE_APPLICATION} CONFIG) string(CONFIGURE "${config_content}" config_content) file(WRITE ${dotconfigsysbuild} ${config_content}) diff --git a/share/sysbuild/images/bootloader/CMakeLists.txt b/share/sysbuild/images/bootloader/CMakeLists.txt index 501a1f366f2..243e794d7d3 100644 --- a/share/sysbuild/images/bootloader/CMakeLists.txt +++ b/share/sysbuild/images/bootloader/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2022 Nordic Semiconductor +# Copyright (c) 2022-2026 Nordic Semiconductor # # SPDX-License-Identifier: Apache-2.0 @@ -15,4 +15,12 @@ if(SB_CONFIG_BOOTLOADER_MCUBOOT) sysbuild_add_dependencies(FLASH ${DEFAULT_IMAGE} ${image}) set_config_string(${image} CONFIG_BOOT_SIGNATURE_KEY_FILE "${SB_CONFIG_BOOT_SIGNATURE_KEY_FILE}") + + if(SB_CONFIG_MCUBOOT_DIRECT_XIP_GENERATE_VARIANT) + ExternalZephyrVariantProject_Add( + APPLICATION ${DEFAULT_IMAGE}_slot1_variant + SOURCE_APP ${DEFAULT_IMAGE} + SNIPPET slot1-partition + ) + endif() endif() diff --git a/share/sysbuild/images/bootloader/Kconfig b/share/sysbuild/images/bootloader/Kconfig index a3c9bbef83a..40790490cda 100644 --- a/share/sysbuild/images/bootloader/Kconfig +++ b/share/sysbuild/images/bootloader/Kconfig @@ -150,6 +150,17 @@ config MCUBOOT_MODE_SINGLE_APP_RAM_LOAD endchoice +config MCUBOOT_DIRECT_XIP_GENERATE_VARIANT + bool "Generate slot 1 variant image [EXPERIMENTAL]" + depends on MCUBOOT_MODE_DIRECT_XIP || MCUBOOT_MODE_DIRECT_XIP_WITH_REVERT + select EXPERIMENTAL + default y + help + Will generate an image for the alternate partition (``slot1_partition``) which can be + used for firmware updates. This image will have the same configuration options applied + as the main image, with an extra dts overlay that changes the chosen flash partition to + slot1_partition, and will be named ``_slot_1_variant``. + config MCUBOOT_MODE_FIRMWARE_UPDATER_BOOT_MODE_ENTRANCE bool "Firmware updater retention boot mode entrance" depends on MCUBOOT_MODE_FIRMWARE_UPDATER