1# Copyright (c) 2020-2025 Nordic Semiconductor ASA
2# SPDX-License-Identifier: Apache-2.0
3
4# This file includes extra build system logic that is enabled when
5# CONFIG_BOOTLOADER_MCUBOOT=y.
6#
7# It builds signed binaries using imgtool as a post-processing step
8# after zephyr/zephyr.elf is created in the build directory.
9#
10# Since this file is brought in via include(), we do the work in a
11# function to avoid polluting the top-level scope.
12
13function(zephyr_runner_file type path)
14  # Property magic which makes west flash choose the signed build
15  # output of a given type.
16  set_target_properties(runners_yaml_props_target PROPERTIES "${type}_file" "${path}")
17endfunction()
18
19function(zephyr_mcuboot_tasks)
20  set(keyfile "${CONFIG_MCUBOOT_SIGNATURE_KEY_FILE}")
21  set(keyfile_enc "${CONFIG_MCUBOOT_ENCRYPTION_KEY_FILE}")
22  string(CONFIGURE "${keyfile}" keyfile)
23  string(CONFIGURE "${keyfile_enc}" keyfile_enc)
24
25  if(NOT "${CONFIG_MCUBOOT_GENERATE_UNSIGNED_IMAGE}")
26    # Check for misconfiguration.
27    if("${keyfile}" STREQUAL "")
28      # No signature key file, no signed binaries. No error, though:
29      # this is the documented behavior.
30      message(WARNING "Neither CONFIG_MCUBOOT_GENERATE_UNSIGNED_IMAGE or "
31                      "CONFIG_MCUBOOT_SIGNATURE_KEY_FILE are set, the generated build will not be "
32                      "bootable by MCUboot unless it is signed manually/externally.")
33      return()
34    endif()
35  endif()
36
37  if(NOT WEST)
38    # This feature requires west.
39    message(FATAL_ERROR "Can't sign images for MCUboot: west not found. To fix, install west and ensure it's on PATH.")
40  endif()
41
42  foreach(file keyfile keyfile_enc)
43    if(NOT "${${file}}" STREQUAL "")
44      if(NOT IS_ABSOLUTE "${${file}}")
45        # Relative paths are relative to 'west topdir'.
46        set(${file} "${WEST_TOPDIR}/${${file}}")
47      endif()
48
49      if(NOT EXISTS "${${file}}" AND NOT "${CONFIG_MCUBOOT_GENERATE_UNSIGNED_IMAGE}")
50        message(FATAL_ERROR "west sign can't find file ${${file}} (Note: Relative paths are relative to the west workspace topdir \"${WEST_TOPDIR}\")")
51      elseif(NOT (CONFIG_BUILD_OUTPUT_BIN OR CONFIG_BUILD_OUTPUT_HEX))
52        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.")
53      endif()
54    endif()
55  endforeach()
56
57  # No imgtool, no signed binaries.
58  if(NOT DEFINED IMGTOOL)
59    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.")
60    return()
61  endif()
62
63  # Fetch devicetree details for flash and slot information
64  dt_chosen(flash_node PROPERTY "zephyr,flash")
65  dt_nodelabel(slot0_flash NODELABEL "slot0_partition" REQUIRED)
66  dt_prop(slot_size PATH "${slot0_flash}" PROPERTY "reg" INDEX 1 REQUIRED)
67  dt_prop(write_block_size PATH "${flash_node}" PROPERTY "write-block-size")
68
69  if(NOT write_block_size)
70    set(write_block_size 4)
71    message(WARNING "slot0_partition write block size devicetree parameter is missing, assuming write block size is 4")
72  endif()
73
74  # If single slot mode, or if in firmware updater mode and this is the firmware updater image,
75  # use slot 0 information
76  if(NOT CONFIG_MCUBOOT_BOOTLOADER_MODE_SINGLE_APP AND (NOT CONFIG_MCUBOOT_BOOTLOADER_MODE_FIRMWARE_UPDATER OR CONFIG_MCUBOOT_APPLICATION_FIRMWARE_UPDATER))
77    # Slot 1 size is used instead of slot 0 size
78    set(slot_size)
79    dt_nodelabel(slot1_flash NODELABEL "slot1_partition" REQUIRED)
80    dt_prop(slot_size PATH "${slot1_flash}" PROPERTY "reg" INDEX 1 REQUIRED)
81  endif()
82
83  # Basic 'imgtool sign' command with known image information.
84  set(imgtool_sign ${PYTHON_EXECUTABLE} ${IMGTOOL} sign
85      --version ${CONFIG_MCUBOOT_IMGTOOL_SIGN_VERSION} --header-size ${CONFIG_ROM_START_OFFSET}
86      --slot-size ${slot_size})
87
88  # Arguments to imgtool.
89  if(NOT CONFIG_MCUBOOT_EXTRA_IMGTOOL_ARGS STREQUAL "")
90    # Separate extra arguments into the proper format for adding to
91    # extra_post_build_commands.
92    #
93    # Use UNIX_COMMAND syntax for uniform results across host
94    # platforms.
95    separate_arguments(imgtool_args UNIX_COMMAND ${CONFIG_MCUBOOT_EXTRA_IMGTOOL_ARGS})
96  else()
97    set(imgtool_args)
98  endif()
99
100  if(NOT "${keyfile}" STREQUAL "")
101    set(imgtool_args --key "${keyfile}" ${imgtool_args})
102  endif()
103
104  if(CONFIG_MCUBOOT_IMGTOOL_OVERWRITE_ONLY)
105    # Use overwrite-only instead of swap upgrades.
106    set(imgtool_args --overwrite-only --align 1 ${imgtool_args})
107  elseif(CONFIG_MCUBOOT_BOOTLOADER_MODE_RAM_LOAD)
108    # RAM load requires setting the location of where to load the image to
109    dt_chosen(chosen_ram PROPERTY "zephyr,sram")
110    dt_reg_addr(chosen_ram_address PATH ${chosen_ram})
111    dt_nodelabel(slot0_partition NODELABEL "slot0_partition" REQUIRED)
112    dt_reg_addr(slot0_partition_address PATH ${slot0_partition})
113    dt_nodelabel(slot1_partition NODELABEL "slot1_partition" REQUIRED)
114    dt_reg_addr(slot1_partition_address PATH ${slot1_partition})
115
116    set(imgtool_args --align 1 --load-addr ${chosen_ram_address} ${imgtool_args})
117    set(imgtool_args_alt_slot ${imgtool_args} --hex-addr ${slot1_partition_address})
118    set(imgtool_args ${imgtool_args} --hex-addr ${slot0_partition_address})
119  else()
120    set(imgtool_args --align ${write_block_size} ${imgtool_args})
121  endif()
122
123  # Extensionless prefix of any output file.
124  set(output ${ZEPHYR_BINARY_DIR}/${KERNEL_NAME})
125
126  # List of additional build byproducts.
127  set(byproducts)
128
129  # Set up .bin outputs.
130  if(CONFIG_BUILD_OUTPUT_BIN)
131    list(APPEND byproducts ${output}.signed.bin)
132    zephyr_runner_file(bin ${output}.signed.bin)
133    set(BYPRODUCT_KERNEL_SIGNED_BIN_NAME "${output}.signed.bin"
134        CACHE FILEPATH "Signed kernel bin file" FORCE
135    )
136    set_property(GLOBAL APPEND PROPERTY extra_post_build_commands COMMAND
137                 ${imgtool_sign} ${imgtool_args} ${output}.bin ${output}.signed.bin)
138
139    if(CONFIG_MCUBOOT_GENERATE_CONFIRMED_IMAGE)
140      list(APPEND byproducts ${output}.signed.confirmed.bin)
141      set(BYPRODUCT_KERNEL_SIGNED_CONFIRMED_BIN_NAME "${output}.signed.confirmed.bin"
142          CACHE FILEPATH "Signed and confirmed kernel bin file" FORCE
143      )
144      set_property(GLOBAL APPEND PROPERTY extra_post_build_commands COMMAND
145                   ${imgtool_sign} ${imgtool_args} --pad --confirm ${output}.bin
146                   ${output}.signed.confirmed.bin)
147    endif()
148
149    if(NOT "${keyfile_enc}" STREQUAL "")
150      list(APPEND byproducts ${output}.signed.encrypted.bin)
151      set(BYPRODUCT_KERNEL_SIGNED_ENCRYPTED_BIN_NAME "${output}.signed.encrypted.bin"
152          CACHE FILEPATH "Signed and encrypted kernel bin file" FORCE
153      )
154      set_property(GLOBAL APPEND PROPERTY extra_post_build_commands COMMAND
155                   ${imgtool_sign} ${imgtool_args} --encrypt "${keyfile_enc}" ${output}.bin
156                   ${output}.signed.encrypted.bin)
157    endif()
158
159    if(CONFIG_MCUBOOT_BOOTLOADER_MODE_RAM_LOAD)
160      list(APPEND byproducts ${output}.slot1.signed.encrypted.bin)
161      set_property(GLOBAL APPEND PROPERTY extra_post_build_commands COMMAND
162                   ${imgtool_sign} ${imgtool_args_alt_slot} ${output}.bin
163                   ${output}.slot1.signed.bin)
164
165      if(CONFIG_MCUBOOT_GENERATE_CONFIRMED_IMAGE)
166        list(APPEND byproducts ${output}.slot1.signed.confirmed.bin)
167        set_property(GLOBAL APPEND PROPERTY extra_post_build_commands COMMAND
168                     ${imgtool_sign} ${imgtool_args_alt_slot} --pad --confirm ${output}.bin
169                     ${output}.slot1.signed.confirmed.bin)
170      endif()
171
172      if(NOT "${keyfile_enc}" STREQUAL "")
173        list(APPEND byproducts ${output}.slot1.signed.encrypted.bin)
174        set_property(GLOBAL APPEND PROPERTY extra_post_build_commands COMMAND
175                     ${imgtool_sign} ${imgtool_args_alt_slot} --encrypt "${keyfile_enc}"
176                     ${output}.bin ${output}.slot1.signed.encrypted.bin)
177      endif()
178    endif()
179  endif()
180
181  # Set up .hex outputs.
182  if(CONFIG_BUILD_OUTPUT_HEX)
183    list(APPEND byproducts ${output}.signed.hex)
184    zephyr_runner_file(hex ${output}.signed.hex)
185    set(BYPRODUCT_KERNEL_SIGNED_HEX_NAME "${output}.signed.hex"
186        CACHE FILEPATH "Signed kernel hex file" FORCE
187    )
188
189    if(NOT "${keyfile_enc}" STREQUAL "")
190      # When encryption is enabled, set the encrypted bit when signing the image but do not
191      # encrypt the data, this means that when the image is moved out of the primary into the
192      # secondary, it will be encrypted rather than being in unencrypted
193      set_property(GLOBAL APPEND PROPERTY extra_post_build_commands COMMAND
194                   ${imgtool_sign} ${imgtool_args} --encrypt "${keyfile_enc}" --clear
195                   ${output}.hex ${output}.signed.hex)
196    else()
197      set_property(GLOBAL APPEND PROPERTY extra_post_build_commands COMMAND
198                   ${imgtool_sign} ${imgtool_args} ${output}.hex ${output}.signed.hex)
199    endif()
200
201    if(CONFIG_MCUBOOT_GENERATE_CONFIRMED_IMAGE)
202      list(APPEND byproducts ${output}.signed.confirmed.hex)
203      set(BYPRODUCT_KERNEL_SIGNED_CONFIRMED_HEX_NAME "${output}.signed.confirmed.hex"
204          CACHE FILEPATH "Signed and confirmed kernel hex file" FORCE
205      )
206      set_property(GLOBAL APPEND PROPERTY extra_post_build_commands COMMAND
207                   ${imgtool_sign} ${imgtool_args} --pad --confirm ${output}.hex
208                   ${output}.signed.confirmed.hex)
209    endif()
210
211    if(NOT "${keyfile_enc}" STREQUAL "")
212      list(APPEND byproducts ${output}.signed.encrypted.hex)
213      set(BYPRODUCT_KERNEL_SIGNED_ENCRYPTED_HEX_NAME "${output}.signed.encrypted.hex"
214          CACHE FILEPATH "Signed and encrypted kernel hex file" FORCE
215      )
216      set_property(GLOBAL APPEND PROPERTY extra_post_build_commands COMMAND
217                   ${imgtool_sign} ${imgtool_args} --encrypt "${keyfile_enc}" ${output}.hex
218                   ${output}.signed.encrypted.hex)
219    endif()
220
221    if(CONFIG_MCUBOOT_BOOTLOADER_MODE_RAM_LOAD)
222      list(APPEND byproducts ${output}.slot1.signed.hex)
223      set_property(GLOBAL APPEND PROPERTY extra_post_build_commands COMMAND
224                   ${imgtool_sign} ${imgtool_args_alt_slot} ${output}.hex
225                   ${output}.slot1.signed.hex)
226
227      if(CONFIG_MCUBOOT_GENERATE_CONFIRMED_IMAGE)
228        list(APPEND byproducts ${output}.slot1.signed.confirmed.hex)
229        set_property(GLOBAL APPEND PROPERTY extra_post_build_commands COMMAND
230                     ${imgtool_sign} ${imgtool_args_alt_slot} --pad --confirm ${output}.hex
231                     ${output}.slot1.signed.confirmed.hex)
232      endif()
233
234      if(NOT "${keyfile_enc}" STREQUAL "")
235        list(APPEND byproducts ${output}.slot1.signed.encrypted.hex)
236        set_property(GLOBAL APPEND PROPERTY extra_post_build_commands COMMAND
237                     ${imgtool_sign} ${imgtool_args_alt_slot} --encrypt "${keyfile_enc}"
238                     ${output}.hex ${output}.slot1.signed.encrypted.hex)
239      endif()
240    endif()
241  endif()
242  set_property(GLOBAL APPEND PROPERTY extra_post_build_byproducts ${byproducts})
243endfunction()
244
245zephyr_mcuboot_tasks()
246