Skip to content

Commit 0659a29

Browse files
committed
sysbuild: Add a MCUboot manifest generator
Add a possibility to automatically generate a MCUboot manifest with all expected image's digests. Signed-off-by: Tomasz Chyrowicz <[email protected]>
1 parent 8c57fe5 commit 0659a29

File tree

6 files changed

+188
-2
lines changed

6 files changed

+188
-2
lines changed

cmake/sysbuild/image_signing.cmake

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,10 @@ function(zephyr_mcuboot_tasks)
143143
set(imgtool_extra ${imgtool_extra} --cid "${CONFIG_MCUBOOT_IMGTOOL_UUID_CID_NAME}")
144144
endif()
145145

146+
if(CONFIG_NCS_MCUBOOT_IMGTOOL_APPEND_MANIFEST)
147+
set(imgtool_extra ${imgtool_extra} --manifest "manifest.yaml")
148+
endif()
149+
146150
set(imgtool_args ${imgtool_extra})
147151

148152
# Extensionless prefix of any output file.
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
#
2+
# Copyright (c) 2025 Nordic Semiconductor
3+
#
4+
# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
5+
6+
include(${ZEPHYR_NRF_MODULE_DIR}/cmake/sysbuild/bootloader_dts_utils.cmake)
7+
8+
yaml_create(NAME mcuboot_manifest)
9+
yaml_set(NAME mcuboot_manifest KEY format VALUE "1")
10+
yaml_set(NAME mcuboot_manifest KEY images LIST)
11+
set(manifest_path "manifest.yaml")
12+
set(manifest_img_slot_0 "${DEFAULT_IMAGE}")
13+
14+
yaml_create(NAME mcuboot_secondary_manifest)
15+
yaml_set(NAME mcuboot_secondary_manifest KEY format VALUE "1")
16+
yaml_set(NAME mcuboot_secondary_manifest KEY images LIST)
17+
set(manifest_secondary_path "manifest_secondary.yaml")
18+
set(manifest_img_slot_1 "mcuboot_secondary_app")
19+
20+
# There is no need to generate a manifest if there is only a single (merged) image.
21+
if(NOT SB_CONFIG_MCUBOOT_SIGN_MERGED_BINARY)
22+
sysbuild_get(manifest_img IMAGE mcuboot VAR CONFIG_MCUBOOT_MANIFEST_IMAGE_INDEX KCONFIG)
23+
math(EXPR manifest_slot_0 "${manifest_img} * 2")
24+
math(EXPR manifest_slot_1 "${manifest_img} * 2 + 1")
25+
dt_partition_addr(slot0_addr LABEL "slot${manifest_slot_0}_partition" TARGET mcuboot ABSOLUTE REQUIRED)
26+
dt_partition_addr(slot1_addr LABEL "slot${manifest_slot_1}_partition" TARGET mcuboot ABSOLUTE REQUIRED)
27+
28+
UpdateableImage_Get(images GROUP "DEFAULT")
29+
foreach(image ${images})
30+
sysbuild_get(BINARY_DIR IMAGE ${image} VAR APPLICATION_BINARY_DIR CACHE)
31+
sysbuild_get(BINARY_BIN_FILE IMAGE ${image} VAR CONFIG_KERNEL_BIN_NAME KCONFIG)
32+
dt_chosen(code_flash TARGET ${image} PROPERTY "zephyr,code-partition")
33+
dt_partition_addr(code_addr PATH "${code_flash}" TARGET ${image} ABSOLUTE REQUIRED)
34+
35+
if("${code_addr}" STREQUAL "${slot0_addr}")
36+
cmake_path(APPEND BINARY_DIR "zephyr" "manifest.yaml" OUTPUT_VARIABLE manifest_path)
37+
set(manifest_img_slot_0 "${image}")
38+
continue()
39+
endif()
40+
41+
if(NOT "${SB_CONFIG_SIGNATURE_TYPE}" STREQUAL "NONE")
42+
cmake_path(APPEND BINARY_DIR "zephyr" "${BINARY_BIN_FILE}.signed.bin" OUTPUT_VARIABLE image_path)
43+
else()
44+
cmake_path(APPEND BINARY_DIR "zephyr" "${BINARY_BIN_FILE}.bin" OUTPUT_VARIABLE image_path)
45+
endif()
46+
47+
yaml_set(NAME mcuboot_manifest KEY images APPEND LIST MAP "path: ${image_path}, name: ${image}")
48+
endforeach()
49+
50+
foreach(image ${images})
51+
if("${image}" STREQUAL "${manifest_img_slot_0}")
52+
continue()
53+
endif()
54+
add_dependencies("${manifest_img_slot_0}" "${image}")
55+
endforeach()
56+
57+
UpdateableImage_Get(variants GROUP "VARIANT")
58+
foreach(image ${variants})
59+
sysbuild_get(BINARY_DIR IMAGE ${image} VAR APPLICATION_BINARY_DIR CACHE)
60+
sysbuild_get(BINARY_BIN_FILE IMAGE ${image} VAR CONFIG_KERNEL_BIN_NAME KCONFIG)
61+
dt_chosen(code_flash TARGET ${image} PROPERTY "zephyr,code-partition")
62+
dt_partition_addr(code_addr PATH "${code_flash}" TARGET ${image} ABSOLUTE REQUIRED)
63+
64+
if("${code_addr}" STREQUAL "${slot1_addr}")
65+
cmake_path(APPEND BINARY_DIR "zephyr" "manifest.yaml" OUTPUT_VARIABLE manifest_secondary_path)
66+
set(manifest_img_slot_1 "${image}")
67+
continue()
68+
endif()
69+
70+
if(NOT "${SB_CONFIG_SIGNATURE_TYPE}" STREQUAL "NONE")
71+
cmake_path(APPEND BINARY_DIR "zephyr" "${BINARY_BIN_FILE}.signed.bin" OUTPUT_VARIABLE image_path)
72+
else()
73+
cmake_path(APPEND BINARY_DIR "zephyr" "${BINARY_BIN_FILE}.bin" OUTPUT_VARIABLE image_path)
74+
endif()
75+
76+
yaml_set(NAME mcuboot_secondary_manifest KEY images APPEND LIST MAP "path: ${image_path}, name: ${image}")
77+
endforeach()
78+
79+
foreach(image ${variants})
80+
if("${image}" STREQUAL "${manifest_img_slot_1}")
81+
continue()
82+
endif()
83+
add_dependencies("${manifest_img_slot_1}" "${image}")
84+
endforeach()
85+
endif()
86+
87+
yaml_save(NAME mcuboot_manifest FILE "${manifest_path}")
88+
yaml_save(NAME mcuboot_secondary_manifest FILE "${manifest_secondary_path}")

subsys/bootloader/Kconfig

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,4 +167,26 @@ config NCS_MCUBOOT_BOOTLOADER_SIGN_MERGED_BINARY
167167
help
168168
This is a Kconfig which is informative only, the value should not be changed.
169169

170+
config NCS_MCUBOOT_MANIFEST_UPDATES
171+
bool "Manifest-based updates (informative only, do not change)"
172+
help
173+
When enabled, supports transactional updates using manifests.
174+
This is a Kconfig which is informative only, the value should not be changed.
175+
176+
if NCS_MCUBOOT_MANIFEST_UPDATES
177+
178+
config NCS_MCUBOOT_IMGTOOL_APPEND_MANIFEST
179+
bool "MCUboot append manifest (informative only, do not change)"
180+
help
181+
Append common manifest while signing the image.
182+
This is a Kconfig which is informative only, the value should not be changed.
183+
184+
config NCS_MCUBOOT_MANIFEST_IMAGE_INDEX
185+
int "Index of the image that must include manifest (informative only, do not change)"
186+
help
187+
Specifies the index of the image that must include the manifest.
188+
This is a Kconfig which is informative only, the value should not be changed.
189+
190+
endif # NCS_MCUBOOT_MANIFEST_UPDATES
191+
170192
endmenu

sysbuild/CMakeLists.txt

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,7 @@ function(${SYSBUILD_CURRENT_MODULE_NAME}_pre_cmake)
172172
set(dfu_slots_output_names Application;Network;Wifi\ patches;QSPI\ XIP;MCUboot\ b0\ update)
173173
set(dfu_slots_output_text "-- Sysbuild assigned MCUboot image IDs:\n")
174174
set(dfu_image_index -1)
175+
set(manifest_image_target "none")
175176

176177
list(LENGTH dfu_slots_sysbuild_kconfigs test_things_size)
177178
math(EXPR test_things_size "${test_things_size} - 1")
@@ -193,6 +194,22 @@ function(${SYSBUILD_CURRENT_MODULE_NAME}_pre_cmake)
193194
set_config_int(${image} ${current_application_kconfig} ${value})
194195
endforeach()
195196

197+
if(SB_CONFIG_MCUBOOT_MANIFEST_UPDATES)
198+
if(${value} EQUAL ${SB_CONFIG_MCUBOOT_MANIFEST_IMAGE_INDEX})
199+
if("${current_application_kconfig}" STREQUAL "CONFIG_MCUBOOT_APPLICATION_IMAGE_NUMBER")
200+
set(manifest_image_target ${DEFAULT_IMAGE})
201+
elseif("${current_application_kconfig}" STREQUAL "CONFIG_MCUBOOT_NETWORK_CORE_IMAGE_NUMBER")
202+
set(manifest_image_target ${SB_CONFIG_NETCORE_IMAGE_NAME})
203+
else()
204+
# Unsupported indexes:
205+
# - CONFIG_MCUBOOT_WIFI_PATCHES_IMAGE_NUMBER
206+
# - CONFIG_MCUBOOT_QSPI_XIP_IMAGE_NUMBER
207+
# - CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER
208+
message(FATAL_ERROR "MCUboot manifest image can only be assigned to application or network core images")
209+
endif()
210+
endif()
211+
endif()
212+
196213
set(${current_cache_name} ${value} CACHE INTERNAL "" FORCE)
197214
endforeach()
198215

@@ -256,6 +273,25 @@ function(${SYSBUILD_CURRENT_MODULE_NAME}_pre_cmake)
256273
set_config_bool(mcuboot CONFIG_BOOT_IMG_HASH_ALG_SHA512 y)
257274
endif()
258275

276+
if(SB_CONFIG_MCUBOOT_MANIFEST_UPDATES)
277+
set_config_bool(mcuboot CONFIG_MCUBOOT_MANIFEST_UPDATES y)
278+
set_config_int(mcuboot CONFIG_MCUBOOT_MANIFEST_IMAGE_INDEX
279+
${SB_CONFIG_MCUBOOT_MANIFEST_IMAGE_INDEX})
280+
281+
if("${manifest_image_target}" STREQUAL "none")
282+
message(FATAL_ERROR "No manifest image target, cannot append manifest to image")
283+
endif()
284+
set_config_bool(${manifest_image_target} CONFIG_NCS_MCUBOOT_IMGTOOL_APPEND_MANIFEST y)
285+
if(SB_CONFIG_MCUBOOT_BUILD_DIRECT_XIP_VARIANT)
286+
if("${manifest_image_target}" STREQUAL "${DEFAULT_IMAGE}")
287+
set_config_bool(mcuboot_secondary_app CONFIG_NCS_MCUBOOT_IMGTOOL_APPEND_MANIFEST y)
288+
else()
289+
set_config_bool(${manifest_image_target}_secondary_app
290+
CONFIG_NCS_MCUBOOT_IMGTOOL_APPEND_MANIFEST y)
291+
endif()
292+
endif()
293+
endif()
294+
259295
# Apply configuration to application
260296
foreach(image ${updateable_images})
261297
foreach(mode ${application_mcuboot_modes})
@@ -266,6 +302,11 @@ function(${SYSBUILD_CURRENT_MODULE_NAME}_pre_cmake)
266302
endif()
267303
endforeach()
268304

305+
if(SB_CONFIG_MCUBOOT_MANIFEST_UPDATES)
306+
set_config_bool(${image} CONFIG_NCS_MCUBOOT_MANIFEST_UPDATES y)
307+
set_config_int(${image} CONFIG_NCS_MCUBOOT_MANIFEST_IMAGE_INDEX ${SB_CONFIG_MCUBOOT_MANIFEST_IMAGE_INDEX})
308+
endif()
309+
269310
if(SB_CONFIG_BOOT_SIGNATURE_TYPE_ED25519)
270311
set_config_bool(${image} CONFIG_MCUBOOT_BOOTLOADER_SIGNATURE_TYPE_ED25519 y)
271312
endif()
@@ -385,7 +426,8 @@ function(${SYSBUILD_CURRENT_MODULE_NAME}_pre_cmake)
385426
OR SB_CONFIG_MCUBOOT_MODE_DIRECT_XIP_WITH_REVERT
386427
OR SB_CONFIG_MCUBOOT_COMPRESSED_IMAGE_SUPPORT
387428
OR (SB_CONFIG_SOC_SERIES_NRF54LX AND SB_CONFIG_BOOT_ENCRYPTION)
388-
OR SB_CONFIG_MCUBOOT_HARDWARE_DOWNGRADE_PREVENTION)
429+
OR SB_CONFIG_MCUBOOT_HARDWARE_DOWNGRADE_PREVENTION
430+
OR SB_CONFIG_MCUBOOT_MANIFEST_UPDATES)
389431
# Use NCS signing script with support for PM or direct XIP (NCS specific features)
390432
if(SB_CONFIG_QSPI_XIP_SPLIT_IMAGE)
391433
set(${DEFAULT_IMAGE}_SIGNING_SCRIPT "${ZEPHYR_NRF_MODULE_DIR}/cmake/sysbuild/image_signing_split.cmake" CACHE INTERNAL "MCUboot signing script" FORCE)
@@ -888,6 +930,10 @@ function(${SYSBUILD_CURRENT_MODULE_NAME}_post_cmake)
888930
include(${ZEPHYR_NRF_MODULE_DIR}/cmake/sysbuild/mcuboot_nrf54h20.cmake)
889931
endif()
890932

933+
if(SB_CONFIG_BOOTLOADER_MCUBOOT AND SB_CONFIG_MCUBOOT_MANIFEST_UPDATES)
934+
include(${ZEPHYR_NRF_MODULE_DIR}/cmake/sysbuild/mcuboot_manifest.cmake)
935+
endif()
936+
891937
if(SB_CONFIG_DFU_ZIP)
892938
if(SB_CONFIG_BOOTLOADER_MCUBOOT)
893939
include(${ZEPHYR_NRF_MODULE_DIR}/cmake/sysbuild/zip.cmake)

sysbuild/Kconfig.mcuboot

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,32 @@ config MCUBOOT_IMAGES_ROM_END_OFFSET_AUTO
2323
(e.g. "smp_svr;ipc_radio", "mcuboot_secondary_app").
2424
If empty the default set of updateable images will be affected.
2525

26+
config MCUBOOT_MANIFEST_UPDATES
27+
bool "Manifest-based updates"
28+
depends on SOC_NRF54H20
29+
depends on (MCUBOOT_UPDATEABLE_IMAGES > 1) && (MCUBOOT_MODE_DIRECT_XIP || MCUBOOT_MODE_DIRECT_XIP_WITH_REVERT)
30+
help
31+
When enabled, supports transactional updates using manifests.
32+
This allows multiple images to be updated atomically. The manifest
33+
is a separate TLV which contains a list of images to update and
34+
their expected hash values. The manifest TLV is a part of an image
35+
that is signed to prevent tampering.
36+
The manifest must be transferred as part of the image with index 0.
37+
It can be a dedicated image, or part of an existing image.
38+
If the second option is selected, all updates must contain an update
39+
for image 0.
40+
41+
if MCUBOOT_MANIFEST_UPDATES
42+
43+
config MCUBOOT_MANIFEST_IMAGE_INDEX
44+
int "Index of the image that must include manifest"
45+
default 0
46+
range 0 MCUBOOT_UPDATEABLE_IMAGES
47+
help
48+
Specifies the index of the image that must include the manifest.
49+
50+
endif # MCUBOOT_MANIFEST_UPDATES
51+
2652
config MCUBOOT_BUILD_DIRECT_XIP_VARIANT
2753
bool "Build DirectXIP variant image"
2854
depends on MCUBOOT_MODE_DIRECT_XIP || MCUBOOT_MODE_DIRECT_XIP_WITH_REVERT

west.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ manifest:
126126
compare-by-default: true
127127
- name: mcuboot
128128
repo-path: sdk-mcuboot
129-
revision: 3839107e52c7228eba123129a3806fb3391781d6
129+
revision: 9ba25b87567fceaea2e290fbda6be30ebc00625c
130130
path: bootloader/mcuboot
131131
- name: qcbor
132132
url: https://github.com/laurencelundblade/QCBOR

0 commit comments

Comments
 (0)