Skip to content

Commit 3011760

Browse files
Michael Eskowitzozersa
authored andcommitted
scripts: imgtool: Add script to combine multiple image as single
MCUBoot multi image support mode requires each image be procedded individually that requires multiple signature check during boot. If there be 4 imageis it requires 4 times signature validatation. This feature increase boot time. Depend on the project requirement long boot time may not be accepted. To provide a solution for this case we propose to - Generate each image as regular mcuboot format - Concatanate them and re generate a main mcuboot image. As below ------------------------- | Header | ------------------------- | Image-1 | | (Header+Data+Footer) | ------------------------- | Image-2 | | (Header+Data+Footer) | ------------------------- | ..... | ------------------------- | Footer | ------------------------- During boot time if top level image be validated sub image can be just copied to the target location without to recheck them. There will be two commit to implement this feature 1- Add a script that combine images 2- Update mcuboot source code to process subimages This commit is for #1, it adds a script which called as combine_images.py. Any imgtool parameters can be passed to the script It will directly pass them to the imgtool. Usage: python combine_images.py --config combine_images.yaml --imgtool imgtool --output <outfolder> combine_images.yaml file is added as reference file it need to be updated as per of the project need. Signed-off-by: Sadik Ozer <sadik.ozer@analog.com> Signed-off-by: Michael Eskowitz <Michael.Eskowitz@analog.com>
1 parent 3c8e571 commit 3011760

File tree

5 files changed

+232
-0
lines changed

5 files changed

+232
-0
lines changed

docs/combine_images.md

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# [combine_images Extensions to MCUboot for Multi-Core SoC Image Distribution](#combine_images_for_multi-core_SoC_image_distribution)
2+
3+
In multi-core system-on-chip based platforms, each processor core typically runs separate firmware
4+
as every core may have distinct responsibilities. Packaging and distributing firmware images for
5+
these systems presents a challenge as multiple independent images must be bundled, versioned, and
6+
delivered as a single update while preserving the ability to secure and authenticate each image. The
7+
combine_images extensions to MCUboot allow for the bundling and distribution of multiple images
8+
together as an MCUboot AppPack.
9+
10+
# [AppPack Structure](#apppack_structure)
11+
12+
The purpose of this method to handle multiple images in a single image pack is to decrease the number
13+
of signature checks required during bootup. An example AppPack layout is shown in the image below.
14+
This example contains the standard MCUboot header and trailer surrounding a number of images. Each
15+
of the enclosed images may itself be an AppPack.
16+
17+
![AppPack Structure](./images/AppPack.png)
18+
19+
# [AppPack Generation](#apppack_generation)
20+
21+
## [combine_images.py](#combine_images.py)
22+
23+
In order to generate an AppPack you will need to call the combine_images.py script.
24+
25+
Usage: python combine_images.py --config [yaml file] --imgtool [path to imgtool] --output [output directory]
26+
27+
Options:
28+
--config [yaml file] yaml file specifying content of the AppPack to
29+
be generated. The yaml file will specify input
30+
binaries, output file names and the path to any
31+
signing keys.
32+
--imgtool [path to imgtool] Path to ImgTool which can be either a python file
33+
or binary. The provided ImgTool is called to
34+
package binaries and create an AppPack structure
35+
defined according to the specified yaml file.
36+
--output [output directory] Path to an directory which will be used to store
37+
any intermediary file products and the final
38+
AppPack itself.
39+
40+
## [combine_images.yaml](#combine_images.yaml)
41+
42+
The combine_images.py script requires a yaml config file to define the layout of the desired AppPack.
43+
The structure of the yaml file is shown below. In this example there is a top level AppPack which
44+
may or may not contain an inputfile. The top level AppPack may also contain some number of nested
45+
AppPacks which can contain additional AppPacks itself.
46+
47+
[AppPack Name]_pack:
48+
# Optional: top-level infline can be omitted if not needed
49+
infile: [filename]
50+
51+
# Required
52+
outfile: [filename]
53+
params: [imgtool parameter string]
54+
55+
[Nested AppPack Name]_pack:
56+
infile: [filename]
57+
outfile: [filename]
58+
params: [imgtool parameter string]

docs/images/AppPack.png

78.4 KB
Loading

docs/index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ The MCUboot documentation is composed of the following pages:
4040
- [Bootloader design](design.md)
4141
- [Encrypted images](encrypted_images.md)
4242
- [imgtool](imgtool.md) - image signing and key management
43+
- [Combine Images](combine_images.md) - Combine images feature
4344
- [ECDSA](ecdsa.md) - information about ECDSA signature formats
4445
- [Serial Recovery](serial_recovery.md) - MCUmgr as the serial recovery protocol
4546
- Usage instructions:

scripts/combine_images.py

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
#! /usr/bin/env python3
2+
#
3+
# Copyright (C) 2025 Analog Devices, Inc.
4+
#
5+
# SPDX-License-Identifier: Apache-2.0
6+
#
7+
# Licensed under the Apache License, Version 2.0 (the "License");
8+
# you may not use this file except in compliance with the License.
9+
# You may obtain a copy of the License at
10+
#
11+
# http://www.apache.org/licenses/LICENSE-2.0
12+
#
13+
# Unless required by applicable law or agreed to in writing, software
14+
# distributed under the License is distributed on an "AS IS" BASIS,
15+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
# See the License for the specific language governing permissions and
17+
# limitations under the License.
18+
19+
import argparse
20+
import os
21+
import pathlib
22+
import subprocess
23+
import sys
24+
25+
import yaml
26+
import shutil
27+
28+
29+
def main():
30+
global img_tool
31+
global output_dir
32+
global config_path
33+
34+
parser = argparse.ArgumentParser(description="Create application package", allow_abbrev=False)
35+
parser.add_argument('--config', help="The path to config yaml file", required=True)
36+
parser.add_argument('--imgtool', help="The path to ImgTool", required=True)
37+
parser.add_argument('--output', help="Output directory", required=True)
38+
39+
args = parser.parse_args()
40+
41+
if not os.path.isfile(args.config):
42+
print(f"Error: The config file '{args.config}' does not exist")
43+
return
44+
45+
config_path = os.path.dirname(os.path.abspath(args.config))
46+
print(f"config_path: {config_path}")
47+
48+
with open(args.config) as config_file:
49+
config = yaml.safe_load(config_file)
50+
51+
img_tool = args.imgtool
52+
if img_tool.endswith('.py'):
53+
if not os.path.exists(img_tool): # Is python file exist?
54+
print(f"Error: The '{img_tool}' not found")
55+
return
56+
else:
57+
if not shutil.which(img_tool): # Is binary file exist?
58+
print(f"Error: The '{img_tool}' not found in the path")
59+
return
60+
61+
62+
output_dir = args.output
63+
os.makedirs(output_dir, exist_ok=True)
64+
65+
parse_app_pack(config, None)
66+
67+
print(f"\nCreated {package_name}")
68+
69+
70+
def verify_file_exists(filename):
71+
filepath = pathlib.Path(filename)
72+
73+
if not filepath.exists():
74+
print("ERROR: File " + filename + " not found")
75+
sys.exit(1)
76+
77+
78+
def parse_app_pack(app_pack, name):
79+
params = None
80+
image = []
81+
global package_name
82+
83+
for key in app_pack:
84+
if key.endswith("_pack"):
85+
imagename = parse_app_pack(app_pack[key], name)
86+
image += [imagename]
87+
if key.lower() == "params":
88+
params = app_pack[key]
89+
if key.lower() == "infile":
90+
if app_pack[key] != "":
91+
image_path = os.path.join(config_path, app_pack[key])
92+
verify_file_exists(image_path)
93+
image += [image_path]
94+
if key.lower() == "outfile":
95+
name = app_pack[key]
96+
97+
# Exit if params or image are not specified
98+
if (params is None) or (image is None):
99+
return None
100+
101+
cmd = []
102+
if img_tool.endswith('.py'):
103+
cmd += [sys.executable]
104+
105+
cmd += [img_tool]
106+
cmd += ["sign"]
107+
cmd += params.split()
108+
109+
# Combine images for parent package (top level package)
110+
if len(image) > 1:
111+
combined_images = os.path.join(output_dir, "combined.bin")
112+
113+
with open(combined_images, 'wb') as outfile:
114+
for fname in image:
115+
with open(fname, 'rb') as infile:
116+
outfile.write(infile.read())
117+
infile.close()
118+
outfile.close()
119+
120+
image_input = combined_images
121+
else:
122+
image_input = image[0]
123+
124+
image_output = os.path.join(output_dir, name)
125+
126+
cmd += [image_input]
127+
cmd += [image_output]
128+
129+
print(f'Calling imgtool to generate file {image_output}')
130+
package_name = image_output
131+
subprocess.run(cmd)
132+
133+
return image_output
134+
135+
136+
if __name__ == "__main__":
137+
main()

scripts/combine_images.yaml

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
#
2+
# Copyright (C) 2025 Analog Devices, Inc.
3+
#
4+
# SPDX-License-Identifier: Apache-2.0
5+
#
6+
# Licensed under the Apache License, Version 2.0 (the "License");
7+
# you may not use this file except in compliance with the License.
8+
# You may obtain a copy of the License at
9+
#
10+
# http://www.apache.org/licenses/LICENSE-2.0
11+
#
12+
# Unless required by applicable law or agreed to in writing, software
13+
# distributed under the License is distributed on an "AS IS" BASIS,
14+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
# See the License for the specific language governing permissions and
16+
# limitations under the License.
17+
18+
19+
img0_pack:
20+
outfile: combined_signed.bin
21+
params: "--key ../root-rsa-2048.pem --header-size 0x20 --align 4 --load-addr 0x20080000 --pad-header --version 1.0.0 --slot-size 0x100000"
22+
23+
img1_pack:
24+
infile: img1.bin
25+
outfile: img1_signed.bin
26+
params: "--key ../root-rsa-2048.pem --header-size 0x20 --align 4 --load-addr 0x20010000 --pad-header --version 1.0.0 --slot-size 0x10000"
27+
28+
img2_pack:
29+
infile: img2.bin
30+
outfile: img2_signed.bin
31+
params: "--key ../root-rsa-2048.pem --header-size 0x20 --align 4 --load-addr 0x20020000 --pad-header --version 1.0.0 --slot-size 0x10000"
32+
33+
img3_pack:
34+
infile: img3.bin
35+
outfile: img3_signed.bin
36+
params: "--key ../root-rsa-2048.pem --header-size 0x20 --align 4 --load-addr 0x20030000 --pad-header --version 1.0.0 --slot-size 0x10000"

0 commit comments

Comments
 (0)