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    elseif(NOT (CONFIG_BUILD_OUTPUT_BIN OR CONFIG_BUILD_OUTPUT_HEX))
35      message(FATAL_ERROR "Can't sign images for MCUboot: Neither "
36                          "CONFIG_BUILD_OUTPUT_BIN nor CONFIG_BUILD_OUTPUT_HEX "
37                          "is enabled, so there's nothing to sign.")
38    endif()
39
40    foreach(file keyfile keyfile_enc)
41      if("${${file}}" STREQUAL "")
42        continue()
43      endif()
44
45      # Find the key files in the order of preference for a simple search
46      # modeled by the if checks across the various locations
47      #
48      #  1. absolute
49      #  2. application config
50      #  3. west topdir (optional when the workspace is not west managed)
51      #
52      if(NOT IS_ABSOLUTE "${${file}}")
53        if(EXISTS "${APPLICATION_CONFIG_DIR}/${${file}}")
54          set(${file} "${APPLICATION_CONFIG_DIR}/${${file}}")
55        else()
56          # Relative paths are relative to 'west topdir'.
57          #
58          # This is the only file that has a relative check to topdir likely
59          # from the historical callouts to "west" itself before using
60          # imgtool. So, this is maintained here for backward compatibility
61          #
62          if(NOT WEST OR NOT WEST_TOPDIR)
63            message(FATAL_ERROR "Can't sign images for MCUboot: west workspace undefined. "
64                                "To fix, ensure `west topdir` is a valid workspace directory.")
65          endif()
66          set(${file} "${WEST_TOPDIR}/${${file}}")
67        endif()
68      endif()
69
70      if(NOT EXISTS "${${file}}")
71        message(FATAL_ERROR "Can't sign images for MCUboot: can't find file ${${file}} "
72                            "(Note: Relative paths are searched through "
73                            "APPLICATION_CONFIG_DIR=\"${APPLICATION_CONFIG_DIR}\" "
74                            "and WEST_TOPDIR=\"${WEST_TOPDIR}\")")
75      endif()
76    endforeach()
77  endif()
78
79  # No imgtool, no signed binaries.
80  if(NOT DEFINED IMGTOOL)
81    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.")
82    return()
83  endif()
84
85  # Fetch devicetree details for flash and slot information
86  dt_chosen(flash_node PROPERTY "zephyr,flash")
87  dt_nodelabel(slot0_flash NODELABEL "slot0_partition" REQUIRED)
88  dt_prop(slot_size PATH "${slot0_flash}" PROPERTY "reg" INDEX 1 REQUIRED)
89
90  # If single slot mode, or if in firmware updater mode and this is the firmware updater image,
91  # use slot 0 information
92  if(NOT CONFIG_MCUBOOT_BOOTLOADER_MODE_SINGLE_APP AND (NOT CONFIG_MCUBOOT_BOOTLOADER_MODE_FIRMWARE_UPDATER OR CONFIG_MCUBOOT_APPLICATION_FIRMWARE_UPDATER)
93      AND NOT CONFIG_MCUBOOT_BOOTLOADER_MODE_SINGLE_APP_RAM_LOAD)
94    # Slot 1 size is used instead of slot 0 size
95    set(slot_size)
96    dt_nodelabel(slot1_flash NODELABEL "slot1_partition" REQUIRED)
97    dt_prop(slot_size PATH "${slot1_flash}" PROPERTY "reg" INDEX 1 REQUIRED)
98  endif()
99
100  # Basic 'imgtool sign' command with known image information.
101  set(imgtool_sign ${PYTHON_EXECUTABLE} ${IMGTOOL} sign
102      --version ${CONFIG_MCUBOOT_IMGTOOL_SIGN_VERSION} --header-size ${CONFIG_ROM_START_OFFSET}
103      --slot-size ${slot_size})
104
105  # Arguments to imgtool.
106  if(NOT CONFIG_MCUBOOT_EXTRA_IMGTOOL_ARGS STREQUAL "")
107    # Separate extra arguments into the proper format for adding to
108    # extra_post_build_commands.
109    #
110    # Use UNIX_COMMAND syntax for uniform results across host
111    # platforms.
112    separate_arguments(imgtool_args UNIX_COMMAND ${CONFIG_MCUBOOT_EXTRA_IMGTOOL_ARGS})
113  else()
114    set(imgtool_args)
115  endif()
116
117  if(NOT "${keyfile}" STREQUAL "")
118    set(imgtool_args --key "${keyfile}" ${imgtool_args})
119  endif()
120
121  if(CONFIG_MCUBOOT_IMGTOOL_OVERWRITE_ONLY)
122    # Use overwrite-only instead of swap upgrades.
123    set(imgtool_args --overwrite-only --align 1 ${imgtool_args})
124  elseif(CONFIG_MCUBOOT_BOOTLOADER_MODE_RAM_LOAD OR CONFIG_MCUBOOT_BOOTLOADER_MODE_RAM_LOAD_WITH_REVERT)
125    # RAM load requires setting the location of where to load the image to
126    dt_chosen(chosen_ram PROPERTY "zephyr,sram")
127    dt_reg_addr(chosen_ram_address PATH ${chosen_ram})
128    dt_nodelabel(slot0_partition NODELABEL "slot0_partition" REQUIRED)
129    dt_reg_addr(slot0_partition_address PATH ${slot0_partition})
130    dt_nodelabel(slot1_partition NODELABEL "slot1_partition" REQUIRED)
131    dt_reg_addr(slot1_partition_address PATH ${slot1_partition})
132
133    dt_prop(write_block_size PATH "${flash_node}" PROPERTY "write-block-size")
134    set(imgtool_args --align ${write_block_size} --load-addr ${chosen_ram_address} ${imgtool_args})
135    set(imgtool_args_alt_slot ${imgtool_args} --hex-addr ${slot1_partition_address})
136    set(imgtool_args ${imgtool_args} --hex-addr ${slot0_partition_address})
137  elseif(CONFIG_MCUBOOT_BOOTLOADER_MODE_SINGLE_APP_RAM_LOAD)
138    dt_chosen(ram_load_dev PROPERTY "mcuboot,ram-load-dev")
139    if(DEFINED ram_load_dev)
140      dt_reg_addr(load_address PATH ${ram_load_dev})
141    else()
142      dt_chosen(chosen_ram PROPERTY "zephyr,sram")
143      dt_reg_addr(load_address PATH ${chosen_ram})
144    endif()
145    set(imgtool_args --align 1 --load-addr ${load_address} ${imgtool_args})
146  else()
147    dt_prop(write_block_size PATH "${flash_node}" PROPERTY "write-block-size")
148
149    if(NOT write_block_size)
150      set(write_block_size 4)
151      message(WARNING "slot0_partition write block size devicetree parameter is missing, assuming write block size is 4")
152    endif()
153
154    set(imgtool_args --align ${write_block_size} ${imgtool_args})
155  endif()
156
157  # Set proper hash calculation algorithm for signing
158  if(CONFIG_MCUBOOT_BOOTLOADER_SIGNATURE_TYPE_PURE)
159    set(imgtool_args --pure ${imgtool_args})
160  elseif(CONFIG_MCUBOOT_BOOTLOADER_USES_SHA512)
161    set(imgtool_args --sha 512 ${imgtool_args})
162  endif()
163
164  if(NOT "${keyfile_enc}" STREQUAL "")
165    if(CONFIG_MCUBOOT_ENCRYPTION_ALG_AES_256)
166      # Note: this overrides the default behavior of using AES-128
167      set(imgtool_args ${imgtool_args} --encrypt-keylen 256)
168    endif()
169  endif()
170
171  # Extensionless prefix of any output file.
172  set(output ${ZEPHYR_BINARY_DIR}/${KERNEL_NAME})
173
174  # List of additional build byproducts.
175  set(byproducts)
176
177  # Set up .bin outputs.
178  if(CONFIG_BUILD_OUTPUT_BIN)
179    list(APPEND byproducts ${output}.signed.bin)
180    zephyr_runner_file(bin ${output}.signed.bin)
181    set(BYPRODUCT_KERNEL_SIGNED_BIN_NAME "${output}.signed.bin"
182        CACHE FILEPATH "Signed kernel bin file" FORCE
183    )
184    set_property(GLOBAL APPEND PROPERTY extra_post_build_commands COMMAND
185                 ${imgtool_sign} ${imgtool_args} ${output}.bin ${output}.signed.bin)
186
187    if(CONFIG_MCUBOOT_GENERATE_CONFIRMED_IMAGE)
188      list(APPEND byproducts ${output}.signed.confirmed.bin)
189      zephyr_runner_file(bin ${output}.signed.confirmed.bin)
190      set(BYPRODUCT_KERNEL_SIGNED_CONFIRMED_BIN_NAME "${output}.signed.confirmed.bin"
191          CACHE FILEPATH "Signed and confirmed kernel bin file" FORCE
192      )
193      set_property(GLOBAL APPEND PROPERTY extra_post_build_commands COMMAND
194                   ${imgtool_sign} ${imgtool_args} --pad --confirm ${output}.bin
195                   ${output}.signed.confirmed.bin)
196      zephyr_runner_file(bin ${output}.signed.confirmed.bin)
197    endif()
198
199    if(NOT "${keyfile_enc}" STREQUAL "")
200      list(APPEND byproducts ${output}.signed.encrypted.bin)
201      set(BYPRODUCT_KERNEL_SIGNED_ENCRYPTED_BIN_NAME "${output}.signed.encrypted.bin"
202          CACHE FILEPATH "Signed and encrypted kernel bin file" FORCE
203      )
204      set_property(GLOBAL APPEND PROPERTY extra_post_build_commands COMMAND
205                   ${imgtool_sign} ${imgtool_args} --encrypt "${keyfile_enc}" ${output}.bin
206                   ${output}.signed.encrypted.bin)
207    endif()
208
209    if(CONFIG_MCUBOOT_BOOTLOADER_MODE_RAM_LOAD OR CONFIG_MCUBOOT_BOOTLOADER_MODE_RAM_LOAD_WITH_REVERT)
210      list(APPEND byproducts ${output}.slot1.signed.encrypted.bin)
211      set_property(GLOBAL APPEND PROPERTY extra_post_build_commands COMMAND
212                   ${imgtool_sign} ${imgtool_args_alt_slot} ${output}.bin
213                   ${output}.slot1.signed.bin)
214
215      if(CONFIG_MCUBOOT_GENERATE_CONFIRMED_IMAGE)
216        list(APPEND byproducts ${output}.slot1.signed.confirmed.bin)
217        set_property(GLOBAL APPEND PROPERTY extra_post_build_commands COMMAND
218                     ${imgtool_sign} ${imgtool_args_alt_slot} --pad --confirm ${output}.bin
219                     ${output}.slot1.signed.confirmed.bin)
220      endif()
221
222      if(NOT "${keyfile_enc}" STREQUAL "")
223        list(APPEND byproducts ${output}.slot1.signed.encrypted.bin)
224        set_property(GLOBAL APPEND PROPERTY extra_post_build_commands COMMAND
225                     ${imgtool_sign} ${imgtool_args_alt_slot} --encrypt "${keyfile_enc}"
226                     ${output}.bin ${output}.slot1.signed.encrypted.bin)
227      endif()
228    endif()
229  endif()
230
231  # Set up .hex outputs.
232  if(CONFIG_BUILD_OUTPUT_HEX)
233    list(APPEND byproducts ${output}.signed.hex)
234    zephyr_runner_file(hex ${output}.signed.hex)
235    set(BYPRODUCT_KERNEL_SIGNED_HEX_NAME "${output}.signed.hex"
236        CACHE FILEPATH "Signed kernel hex file" FORCE
237    )
238
239    if(NOT "${keyfile_enc}" STREQUAL "")
240      # When encryption is enabled, set the encrypted bit when signing the image but do not
241      # encrypt the data, this means that when the image is moved out of the primary into the
242      # secondary, it will be encrypted rather than being in unencrypted
243      set_property(GLOBAL APPEND PROPERTY extra_post_build_commands COMMAND
244                   ${imgtool_sign} ${imgtool_args} --encrypt "${keyfile_enc}" --clear
245                   ${output}.hex ${output}.signed.hex)
246    else()
247      set_property(GLOBAL APPEND PROPERTY extra_post_build_commands COMMAND
248                   ${imgtool_sign} ${imgtool_args} ${output}.hex ${output}.signed.hex)
249    endif()
250
251    if(CONFIG_MCUBOOT_GENERATE_CONFIRMED_IMAGE)
252      list(APPEND byproducts ${output}.signed.confirmed.hex)
253      zephyr_runner_file(hex ${output}.signed.confirmed.hex)
254      set(BYPRODUCT_KERNEL_SIGNED_CONFIRMED_HEX_NAME "${output}.signed.confirmed.hex"
255          CACHE FILEPATH "Signed and confirmed kernel hex file" FORCE
256      )
257      set_property(GLOBAL APPEND PROPERTY extra_post_build_commands COMMAND
258                   ${imgtool_sign} ${imgtool_args} --pad --confirm ${output}.hex
259                   ${output}.signed.confirmed.hex)
260      zephyr_runner_file(hex ${output}.signed.confirmed.hex)
261    endif()
262
263    if(NOT "${keyfile_enc}" STREQUAL "")
264      list(APPEND byproducts ${output}.signed.encrypted.hex)
265      set(BYPRODUCT_KERNEL_SIGNED_ENCRYPTED_HEX_NAME "${output}.signed.encrypted.hex"
266          CACHE FILEPATH "Signed and encrypted kernel hex file" FORCE
267      )
268      set_property(GLOBAL APPEND PROPERTY extra_post_build_commands COMMAND
269                   ${imgtool_sign} ${imgtool_args} --encrypt "${keyfile_enc}" ${output}.hex
270                   ${output}.signed.encrypted.hex)
271    endif()
272
273    if(CONFIG_MCUBOOT_BOOTLOADER_MODE_RAM_LOAD OR CONFIG_MCUBOOT_BOOTLOADER_MODE_RAM_LOAD_WITH_REVERT)
274      list(APPEND byproducts ${output}.slot1.signed.hex)
275      set_property(GLOBAL APPEND PROPERTY extra_post_build_commands COMMAND
276                   ${imgtool_sign} ${imgtool_args_alt_slot} ${output}.hex
277                   ${output}.slot1.signed.hex)
278
279      if(CONFIG_MCUBOOT_GENERATE_CONFIRMED_IMAGE)
280        list(APPEND byproducts ${output}.slot1.signed.confirmed.hex)
281        set_property(GLOBAL APPEND PROPERTY extra_post_build_commands COMMAND
282                     ${imgtool_sign} ${imgtool_args_alt_slot} --pad --confirm ${output}.hex
283                     ${output}.slot1.signed.confirmed.hex)
284      endif()
285
286      if(NOT "${keyfile_enc}" STREQUAL "")
287        list(APPEND byproducts ${output}.slot1.signed.encrypted.hex)
288        set_property(GLOBAL APPEND PROPERTY extra_post_build_commands COMMAND
289                     ${imgtool_sign} ${imgtool_args_alt_slot} --encrypt "${keyfile_enc}"
290                     ${output}.hex ${output}.slot1.signed.encrypted.hex)
291      endif()
292    endif()
293  endif()
294  set_property(GLOBAL APPEND PROPERTY extra_post_build_byproducts ${byproducts})
295endfunction()
296
297zephyr_mcuboot_tasks()
298