1# Copyright (c) 2024 Jamie Smith
2# SPDX-License-Identifier: Apache-2.0
3
4check_python_package(imgtool.main MCUBOOT_IMGTOOL_FOUND)
5
6get_filename_component(IMGTOOL_SCRIPTS_DIR ${CMAKE_CURRENT_LIST_DIR}/../../scripts REALPATH)
7
8# Find or install imgtool
9
10if(NOT MCUBOOT_IMGTOOL_FOUND)
11  # If we are using the Mbed venv, we can install asn1tools automatically
12  if(MBED_CREATE_PYTHON_VENV)
13    message(STATUS "mcuboot: Installing imgtool into Mbed's Python virtualenv")
14    execute_process(
15      COMMAND ${Python3_EXECUTABLE} -m pip install ${IMGTOOL_SCRIPTS_DIR}
16      COMMAND_ERROR_IS_FATAL ANY
17    )
18  else()
19    message(FATAL_ERROR "The mcuboot imgtool python package needs to be installed (from mcuboot/scripts/) into Mbed's python interpreter (${Python3_EXECUTABLE})")
20  endif()
21endif()
22
23# Signing key
24set(MCUBOOT_SIGNING_KEY "" CACHE STRING "Path to key file (.pem) used to sign firmware updates for your device. The public key will be stored in the bootloader. This file must be kept safe!")
25
26# Make sure the signing key path is absolute for EXISTS, relative to the top level build dir
27get_filename_component(MCUBOOT_SIGNING_KEY_ABSPATH "${MCUBOOT_SIGNING_KEY}" ABSOLUTE BASE_DIR ${CMAKE_SOURCE_DIR})
28set(MCUBOOT_SIGNING_KEY_ABSPATH ${MCUBOOT_SIGNING_KEY_ABSPATH} CACHE INTERNAL "Absolute version of MCUBOOT_SIGNING_KEY" FORCE)
29
30if("${MCUBOOT_SIGNING_KEY}" STREQUAL "" OR NOT EXISTS "${MCUBOOT_SIGNING_KEY_ABSPATH}")
31  message(FATAL_ERROR "Must specify path to valid image signing key via MCUBOOT_SIGNING_KEY CMake option in order to build this project.")
32endif()
33
34# Encryption key
35if("MCUBOOT_ENCRYPT_RSA=1" IN_LIST MBED_CONFIG_DEFINITIONS)
36  set(MCUBOOT_ENCRYPTION_KEY "" CACHE STRING "Path to key file (.pem) used to encrypt firmware updates for your device. The private key will be stored in the bootloader. This file must be kept safe!")
37
38  # Make sure the signing key path is absolute for EXISTS, relative to the top level build dir
39  get_filename_component(MCUBOOT_ENCRYPTION_KEY_ABSPATH "${MCUBOOT_ENCRYPTION_KEY}" ABSOLUTE BASE_DIR ${CMAKE_SOURCE_DIR})
40  set(MCUBOOT_ENCRYPTION_KEY_ABSPATH ${MCUBOOT_ENCRYPTION_KEY_ABSPATH} CACHE INTERNAL "Absolute version of MCUBOOT_ENCRYPTION_KEY" FORCE)
41
42  if("${MCUBOOT_ENCRYPTION_KEY}" STREQUAL "" OR NOT EXISTS "${MCUBOOT_ENCRYPTION_KEY_ABSPATH}")
43    message(FATAL_ERROR "Since mcuboot.encrypt-rsa is enabled, you must specify the path to a valid image encryption key via the MCUBOOT_ENCRYPTION_KEY CMake option in order to build this project.")
44  endif()
45endif()
46
47# Imgtool usage functions
48
49#
50# Generate a signed image hex file for the given executable target.
51#
52function(_mcuboot_generate_image TARGET IMAGE_TYPE IMAGE_BASE_PATH)
53  if("${MBED_OUTPUT_EXT}" STREQUAL "bin")
54    message(FATAL_ERROR "Hex file output must be enabled to use mcuboot.  Set MBED_OUTPUT_EXT to empty string after including app.cmake in your top level CMakeLists.txt!")
55  endif()
56
57  if("${PROJECT_VERSION}" STREQUAL "")
58    message(FATAL_ERROR "You must set the project version to sign images by passing a version number into your app's project() command!")
59  endif()
60
61  # mbed_generate_bin_hex() puts the hex file at the following path
62  set(TARGET_HEX_FILE ${CMAKE_CURRENT_BINARY_DIR}/$<TARGET_FILE_BASE_NAME:${TARGET}>.hex)
63
64  # Grab header size
65  if(NOT "${MBED_CONFIG_DEFINITIONS}" MATCHES "MCUBOOT_HEADER_SIZE=(0x[0-9A-Fa-f]+)")
66    message(FATAL_ERROR "Couldn't find MCUBOOT_HEADER_SIZE in Mbed configuration!")
67  endif()
68  set(HEADER_SIZE_HEX ${CMAKE_MATCH_1})
69
70  # Grab slot size
71  if(NOT "${MBED_CONFIG_DEFINITIONS}" MATCHES "MCUBOOT_SLOT_SIZE=(0x[0-9A-Fa-f]+)")
72    message(FATAL_ERROR "Couldn't find MCUBOOT_SLOT_SIZE in Mbed configuration!")
73  endif()
74  set(SLOT_SIZE_HEX ${CMAKE_MATCH_1})
75
76  get_property(objcopy GLOBAL PROPERTY ELF2BIN)
77
78  if(${IMAGE_TYPE} STREQUAL "update" AND "MCUBOOT_ENCRYPT_RSA=1" IN_LIST MBED_CONFIG_DEFINITIONS)
79    set(IMGTOOL_EXTRA_ARGS --encrypt ${MCUBOOT_ENCRYPTION_KEY_ABSPATH})
80  elseif(${IMAGE_TYPE} STREQUAL "initial" AND "MCUBOOT_ENCRYPT_RSA=1" IN_LIST MBED_CONFIG_DEFINITIONS)
81    # If encryption is enabled, generate unencrypted initial image which supports encryption.
82    # See https://github.com/mbed-ce/mbed-os/issues/401#issuecomment-2567099213
83    set(IMGTOOL_EXTRA_ARGS --clear)
84  else()
85    set(IMGTOOL_EXTRA_ARGS "")
86  endif()
87
88  add_custom_command(
89    TARGET ${TARGET}
90    POST_BUILD
91    DEPENDS ${MCUBOOT_SIGNING_KEY_ABSPATH}
92    COMMAND
93      ${Python3_EXECUTABLE} -m imgtool.main
94      sign
95      --key ${MCUBOOT_SIGNING_KEY_ABSPATH} # this specifies the file containing the keys used to sign/verify the application
96      --align 4 # this lets mcuboot know the intrinsic alignment of the flash (32-bits = 4 byte alignment)
97      --version ${PROJECT_VERSION} # this sets the version number of the application
98      --header-size ${HEADER_SIZE_HEX} # this must be the same as the value specified in mcuboot.header-size configuration
99      --pad-header # this tells imgtool to insert the entire header, including any necessary padding bytes.
100      --slot-size ${SLOT_SIZE_HEX} # this specifies the maximum size of the application ("slot size"). It must be the same as the configured mcuboot.slot-size!
101      ${IMGTOOL_EXTRA_ARGS}
102      ${TARGET_HEX_FILE} ${IMAGE_BASE_PATH}.hex
103
104    COMMAND
105      ${CMAKE_COMMAND} -E echo "-- built: ${IMAGE_BASE_PATH}.hex"
106
107    # Also generate bin file
108    COMMAND
109      ${objcopy} -I ihex -O binary ${IMAGE_BASE_PATH}.hex ${IMAGE_BASE_PATH}.bin
110
111    COMMAND
112      ${CMAKE_COMMAND} -E echo "-- built: ${IMAGE_BASE_PATH}.bin"
113
114    COMMENT "Generating mcuboot ${IMAGE_TYPE} image for ${TARGET}..."
115    WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
116    VERBATIM
117  )
118endfunction(_mcuboot_generate_image)
119
120#
121# Generate an initial image hex file for the given executable target.
122# This initial image is what should be flashed to a blank device (along with the bootloader).
123# A flash target (ninja flash-${TARGET}-initial-image) will also be created.
124#
125# NOTE: This function must be called *after* mbed_set_post_build() for the target!
126#
127# If you wish to specify the base name of the initial image, pass that as the second argument to
128# this function.  Otherwise, it will default to $<target name>-initial-image
129#
130function(mcuboot_generate_initial_image TARGET) # optional 2nd arg: initial image base filename
131  # Figure out file path
132  if("${ARGN}" STREQUAL "")
133    set(INITIAL_IMAGE_BASE_PATH ${CMAKE_CURRENT_BINARY_DIR}/$<TARGET_FILE_BASE_NAME:${TARGET}>-initial-image)
134  else()
135    set(INITIAL_IMAGE_BASE_PATH ${CMAKE_CURRENT_BINARY_DIR}/${ARGN})
136  endif()
137
138  _mcuboot_generate_image(${TARGET} initial ${INITIAL_IMAGE_BASE_PATH})
139
140  # Create a flash target.
141  # We need to be slightly creative here -- Mbed thinks that the application start address
142  # is <primary slot address> + <header size>, but we actually want to upload to <primary slot address>.
143  # So we need to temporarily override MBED_UPLOAD_BASE_ADDR with an offset value
144  if(NOT "${MBED_CONFIG_DEFINITIONS}" MATCHES "MCUBOOT_HEADER_SIZE=(0x[0-9A-Fa-f]+)")
145    message(FATAL_ERROR "Couldn't find MCUBOOT_HEADER_SIZE in Mbed configuration!")
146  endif()
147  set(HEADER_SIZE_HEX ${CMAKE_MATCH_1})
148  math(EXPR MBED_UPLOAD_BASE_ADDR "${MBED_UPLOAD_BASE_ADDR} - ${HEADER_SIZE_HEX}" OUTPUT_FORMAT HEXADECIMAL)
149
150  gen_upload_target(${TARGET}-initial-image ${INITIAL_IMAGE_BASE_PATH}.bin)
151  if(TARGET flash-${TARGET}-initial-image)
152    add_dependencies(flash-${TARGET}-initial-image ${TARGET})
153  endif()
154endfunction(mcuboot_generate_initial_image)
155
156#
157# Generate an update image hex file for the given executable target.
158# This image is what should be flashed to the secondary block device and passed to
159# mcuboot as an update file.
160#
161# NOTE: This function must be called *after* mbed_set_post_build() for the target!
162#
163# NOTE 2: The hex file produced by this function will still "declare" its address as the primary slot
164# address. This can cause issues if you pass it to a tool that uses this offset to decide where to load it.
165# If this is a problem, we recommend the "arm-none-eabi-objcopy --change-addresses" command to change this address.
166#
167# If you wish to specify the base name of the update image, pass that as the second argument to
168# this function.  Otherwise, it will default to $<target name>-update-image
169#
170function(mcuboot_generate_update_image TARGET) # optional 2nd arg: update image base filename
171  # Figure out file path
172  if("${ARGN}" STREQUAL "")
173    set(UPDATE_IMAGE_BASE_PATH ${CMAKE_CURRENT_BINARY_DIR}/$<TARGET_FILE_BASE_NAME:${TARGET}>-update-image)
174  else()
175    set(UPDATE_IMAGE_BASE_PATH${CMAKE_CURRENT_BINARY_DIR}/${ARGN})
176  endif()
177
178  _mcuboot_generate_image(${TARGET} update ${UPDATE_IMAGE_BASE_PATH})
179endfunction(mcuboot_generate_update_image)
180
181#
182# Generate a C source file with signing keys in it at the given location.
183# The file should be added as a source file to a library built in the same directory.
184#
185function(mcuboot_generate_signing_keys_file SIGNING_KEYS_C_PATH)
186  add_custom_command(
187    OUTPUT ${SIGNING_KEYS_C_PATH}
188    COMMAND
189      ${Python3_EXECUTABLE} -m imgtool.main
190      getpub
191      --key ${MCUBOOT_SIGNING_KEY_ABSPATH}
192      --output ${SIGNING_KEYS_C_PATH}
193
194    DEPENDS ${MCUBOOT_SIGNING_KEY_ABSPATH}
195    COMMENT "Converting signing key to C source..."
196    WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
197    VERBATIM
198  )
199endfunction(mcuboot_generate_signing_keys_file)
200
201#
202# Generate a C source file with the encryption private key in it at the given location.
203# The file should be added as a source file to a library built in the same directory.
204#
205function(mcuboot_generate_encryption_key_file ENC_KEY_C_PATH)
206  add_custom_command(
207    OUTPUT ${ENC_KEY_C_PATH}
208    COMMAND
209      ${Python3_EXECUTABLE} -m imgtool.main
210      getpriv
211      --key ${MCUBOOT_SIGNING_KEY_ABSPATH}
212      > ${ENC_KEY_C_PATH}
213
214    DEPENDS ${MCUBOOT_SIGNING_KEY_ABSPATH}
215    COMMENT "Converting encryption key to C source..."
216    WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
217    VERBATIM
218  )
219endfunction(mcuboot_generate_encryption_key_file)