1# Copyright (c) 2020-2023 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
23  if(NOT "${CONFIG_MCUBOOT_GENERATE_UNSIGNED_IMAGE}")
24    # Check for misconfiguration.
25    if("${keyfile}" STREQUAL "")
26      # No signature key file, no signed binaries. No error, though:
27      # this is the documented behavior.
28      message(WARNING "Neither CONFIG_MCUBOOT_GENERATE_UNSIGNED_IMAGE or "
29                      "CONFIG_MCUBOOT_SIGNATURE_KEY_FILE are set, the generated build will not be "
30                      "bootable by MCUboot unless it is signed manually/externally.")
31      return()
32    endif()
33  endif()
34
35  if(NOT WEST)
36    # This feature requires west.
37    message(FATAL_ERROR "Can't sign images for MCUboot: west not found. To fix, install west and ensure it's on PATH.")
38  endif()
39
40  foreach(file keyfile keyfile_enc)
41    if(NOT "${${file}}" STREQUAL "")
42      if(NOT IS_ABSOLUTE "${${file}}")
43        # Relative paths are relative to 'west topdir'.
44        set(${file} "${WEST_TOPDIR}/${${file}}")
45      endif()
46
47      if(NOT EXISTS "${${file}}" AND NOT "${CONFIG_MCUBOOT_GENERATE_UNSIGNED_IMAGE}")
48        message(FATAL_ERROR "west sign can't find file ${${file}} (Note: Relative paths are relative to the west workspace topdir \"${WEST_TOPDIR}\")")
49      elseif(NOT (CONFIG_BUILD_OUTPUT_BIN OR CONFIG_BUILD_OUTPUT_HEX))
50        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.")
51      endif()
52    endif()
53  endforeach()
54
55  # Find imgtool. Even though west is installed, imgtool might not be.
56  # The user may also have a custom manifest which doesn't include
57  # MCUboot.
58  #
59  # Therefore, go with an explicitly installed imgtool first, falling
60  # back on mcuboot/scripts/imgtool.py.
61  if(IMGTOOL)
62    set(imgtool_path "${IMGTOOL}")
63  elseif(DEFINED ZEPHYR_MCUBOOT_MODULE_DIR)
64    set(IMGTOOL_PY "${ZEPHYR_MCUBOOT_MODULE_DIR}/scripts/imgtool.py")
65    if(EXISTS "${IMGTOOL_PY}")
66      set(imgtool_path "${IMGTOOL_PY}")
67    endif()
68  endif()
69
70  # No imgtool, no signed binaries.
71  if(NOT DEFINED imgtool_path)
72    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.")
73    return()
74  endif()
75
76  # Basic 'west sign' command and output format independent arguments.
77  separate_arguments(west_sign_extra UNIX_COMMAND ${CONFIG_MCUBOOT_CMAKE_WEST_SIGN_PARAMS})
78  set(west_sign ${WEST} sign ${west_sign_extra}
79    --tool imgtool
80    --tool-path "${imgtool_path}"
81    --build-dir "${APPLICATION_BINARY_DIR}")
82
83  # Arguments to imgtool.
84  if(NOT CONFIG_MCUBOOT_EXTRA_IMGTOOL_ARGS STREQUAL "")
85    # Separate extra arguments into the proper format for adding to
86    # extra_post_build_commands.
87    #
88    # Use UNIX_COMMAND syntax for uniform results across host
89    # platforms.
90    separate_arguments(imgtool_extra UNIX_COMMAND ${CONFIG_MCUBOOT_EXTRA_IMGTOOL_ARGS})
91  else()
92    set(imgtool_extra)
93  endif()
94
95  if(NOT "${keyfile}" STREQUAL "")
96    set(imgtool_extra --key "${keyfile}" ${imgtool_extra})
97  endif()
98
99  # Use overwrite-only instead of swap upgrades.
100  if(CONFIG_MCUBOOT_IMGTOOL_OVERWRITE_ONLY)
101    set(imgtool_extra --overwrite-only --align 1 ${imgtool_extra})
102  endif()
103
104  set(imgtool_args -- ${imgtool_extra})
105
106  # Extensionless prefix of any output file.
107  set(output ${ZEPHYR_BINARY_DIR}/${KERNEL_NAME})
108
109  # List of additional build byproducts.
110  set(byproducts)
111
112  # 'west sign' arguments for confirmed, unconfirmed and encrypted images.
113  set(unconfirmed_args)
114  set(confirmed_args)
115  set(encrypted_args)
116
117  # Set up .bin outputs.
118  if(CONFIG_BUILD_OUTPUT_BIN)
119    list(APPEND unconfirmed_args --bin --sbin ${output}.signed.bin)
120    list(APPEND byproducts ${output}.signed.bin)
121    zephyr_runner_file(bin ${output}.signed.bin)
122    set(BYPRODUCT_KERNEL_SIGNED_BIN_NAME "${output}.signed.bin"
123        CACHE FILEPATH "Signed kernel bin file" FORCE
124    )
125
126    if(CONFIG_MCUBOOT_GENERATE_CONFIRMED_IMAGE)
127      list(APPEND confirmed_args --bin --sbin ${output}.signed.confirmed.bin)
128      list(APPEND byproducts ${output}.signed.confirmed.bin)
129      set(BYPRODUCT_KERNEL_SIGNED_CONFIRMED_BIN_NAME "${output}.signed.confirmed.bin"
130          CACHE FILEPATH "Signed and confirmed kernel bin file" FORCE
131      )
132    endif()
133
134    if(NOT "${keyfile_enc}" STREQUAL "")
135      list(APPEND encrypted_args --bin --sbin ${output}.signed.encrypted.bin)
136      list(APPEND byproducts ${output}.signed.encrypted.bin)
137      set(BYPRODUCT_KERNEL_SIGNED_ENCRYPTED_BIN_NAME "${output}.signed.encrypted.bin"
138          CACHE FILEPATH "Signed and encrypted kernel bin file" FORCE
139      )
140    endif()
141  endif()
142
143  # Set up .hex outputs.
144  if(CONFIG_BUILD_OUTPUT_HEX)
145    list(APPEND unconfirmed_args --hex --shex ${output}.signed.hex)
146    list(APPEND byproducts ${output}.signed.hex)
147    zephyr_runner_file(hex ${output}.signed.hex)
148    set(BYPRODUCT_KERNEL_SIGNED_HEX_NAME "${output}.signed.hex"
149        CACHE FILEPATH "Signed kernel hex file" FORCE
150    )
151
152    if(CONFIG_MCUBOOT_GENERATE_CONFIRMED_IMAGE)
153      list(APPEND confirmed_args --hex --shex ${output}.signed.confirmed.hex)
154      list(APPEND byproducts ${output}.signed.confirmed.hex)
155      set(BYPRODUCT_KERNEL_SIGNED_CONFIRMED_HEX_NAME "${output}.signed.confirmed.hex"
156          CACHE FILEPATH "Signed and confirmed kernel hex file" FORCE
157      )
158    endif()
159
160    if(NOT "${keyfile_enc}" STREQUAL "")
161      list(APPEND encrypted_args --hex --shex ${output}.signed.encrypted.hex)
162      list(APPEND byproducts ${output}.signed.encrypted.hex)
163      set(BYPRODUCT_KERNEL_SIGNED_ENCRYPTED_HEX_NAME "${output}.signed.encrypted.hex"
164          CACHE FILEPATH "Signed and encrypted kernel hex file" FORCE
165      )
166    endif()
167  endif()
168
169  # Add the west sign calls and their byproducts to the post-processing
170  # steps for zephyr.elf.
171  #
172  # CMake guarantees that multiple COMMANDs given to
173  # add_custom_command() are run in order, so adding the 'west sign'
174  # calls to the "extra_post_build_commands" property ensures they run
175  # after the commands which generate the unsigned versions.
176  set_property(GLOBAL APPEND PROPERTY extra_post_build_commands COMMAND
177    ${west_sign} ${unconfirmed_args} ${imgtool_args})
178  if(confirmed_args)
179    set_property(GLOBAL APPEND PROPERTY extra_post_build_commands COMMAND
180      ${west_sign} ${confirmed_args} ${imgtool_args} --pad --confirm)
181  endif()
182  if(encrypted_args)
183    set_property(GLOBAL APPEND PROPERTY extra_post_build_commands COMMAND
184      ${west_sign} ${encrypted_args} ${imgtool_args} --encrypt "${keyfile_enc}")
185  endif()
186  set_property(GLOBAL APPEND PROPERTY extra_post_build_byproducts ${byproducts})
187endfunction()
188
189zephyr_mcuboot_tasks()
190