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)