Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
132 changes: 132 additions & 0 deletions boot/bootutil/src/loader.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
* Copyright (c) 2016-2019 JUUL Labs
* Copyright (c) 2019-2023 Arm Limited
* Copyright (c) 2024-2025 Nordic Semiconductor ASA
* Portions Copyright (c) 2025 Analog Devices Inc.
*
* Original license:
*
Expand Down Expand Up @@ -3067,6 +3068,130 @@ context_boot_go(struct boot_loader_state *state, struct boot_rsp *rsp)
}
#endif /* MCUBOOT_DIRECT_XIP || MCUBOOT_RAM_LOAD */


#if (BOOT_IMAGE_NUMBER == 1) && defined(MCUBOOT_RAM_LOAD)

static int
read_image_info(uint32_t addr, struct image_header *hdr,
uint32_t *total_size, uint32_t *footer_size)
{
struct image_tlv_info info;
uint32_t off;
uint32_t protect_tlv_size;

memcpy(hdr, (unsigned char *)addr, sizeof(struct image_header));
if (hdr->ih_magic != IMAGE_MAGIC) {
BOOT_LOG_INF("IMAGE_MAGIC not correct.");
return BOOT_EBADIMAGE;
}

off = BOOT_TLV_OFF(hdr);

memcpy(&info, (unsigned char *)(addr + off), sizeof(info));

protect_tlv_size = hdr->ih_protect_tlv_size;
if (info.it_magic == IMAGE_TLV_PROT_INFO_MAGIC) {
if (protect_tlv_size != info.it_tlv_tot) {
return BOOT_EBADIMAGE;
}

memcpy(&info, (unsigned char *)(addr + off + info.it_tlv_tot), sizeof(info));
} else if (protect_tlv_size != 0) {
return BOOT_EBADIMAGE;
}

if (info.it_magic != IMAGE_TLV_INFO_MAGIC) {
return BOOT_EBADIMAGE;
}

*footer_size = protect_tlv_size + info.it_tlv_tot;
*total_size = off + *footer_size;

return 0;
}

/**
* Check the main image and find sub images, then copy them to the target addr.
* Set boot image address with the first image that found in the list.
*
* -------------------------
* | Header |
* -------------------------
* | SubImage (optional) |
* | (Header+Data+Footer) |
* -------------------------
* | SubImage (optional) |
* | (Header+Data+Footer) |
* -------------------------
* | ..... |
* -------------------------
* | Footer |
* -------------------------
*
* @param addr Image start address
* @param rsp On success, indicates how booting should occur.
*
* @return 0 on success; nonzero on failure.
*/
static int
process_sub_images(uint32_t addr, struct boot_rsp *rsp)
{
int rc = 0;
bool first_subimage = true;
uint32_t main_image_size;
struct image_header hdr;
uint32_t img_total_size;
uint32_t img_footer_size;

/* read main image info */
rc = read_image_info(addr, &hdr, &img_total_size, &img_footer_size);
if (rc != 0) {
/* No valid image header, main image format not correct. */
return rc;
}

/* Set main image size */
main_image_size = img_total_size;
/* Decrease image header size and footer size */
main_image_size -= (hdr.ih_hdr_size + img_footer_size);

/* Pass image header */
addr += hdr.ih_hdr_size;

while (main_image_size) {
/* read sub image info */
rc = read_image_info(addr, &hdr, &img_total_size, &img_footer_size);
if (rc != 0) {
/* No valid sub-image header, so it migth be single image return 0 */
rc = 0;
break;
}

/* copy image to target addr */
if (hdr.ih_flags & IMAGE_F_RAM_LOAD) {
/*
* For heterogenous system that have multi core on same IC.
* Assuming main core that execute MCUBoot able to access other cores ITCM/DTCM
*/
memcpy((unsigned char *)(hdr.ih_load_addr), (unsigned char *)addr, img_total_size);
BOOT_LOG_INF("Copying image from 0x%x to 0x%x is succeeded.", addr, hdr.ih_load_addr);
}

/* Execute first sub image */
if ((first_subimage) && !(hdr.ih_flags & IMAGE_F_NON_BOOTABLE)) {
first_subimage = false;
rsp->br_hdr = (struct image_header *)hdr.ih_load_addr;
}

/* go next image */
main_image_size -= img_total_size;
addr += img_total_size;
}

return rc;
}
#endif // #if (BOOT_IMAGE_NUMBER == 1) && defined(MCUBOOT_RAM_LOAD)

/**
* Prepares the booting process. This function moves images around in flash as
* appropriate, and tells you what address to boot from.
Expand All @@ -3083,6 +3208,13 @@ boot_go(struct boot_rsp *rsp)
boot_state_clear(NULL);

FIH_CALL(context_boot_go, fih_rc, &boot_data, rsp);

#if (BOOT_IMAGE_NUMBER == 1) && defined(MCUBOOT_RAM_LOAD)
if (FIH_EQ(fih_rc, FIH_SUCCESS)) {
(void) process_sub_images(rsp->br_hdr->ih_load_addr, rsp);
}
#endif

FIH_RET(fih_rc);
}

Expand Down
178 changes: 178 additions & 0 deletions scripts/combine_images.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
#! /usr/bin/env python3
#
# Copyright (C) 2025 Analog Devices, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import argparse
import os
import pathlib
import subprocess
import sys

import yaml
import shutil


# Function definitions


def main():
global img_tool
global output_dir
global config_path

parser = argparse.ArgumentParser(description="Create application package", allow_abbrev=False)
parser.add_argument('--config', help="The path to config yaml file", required=True)
parser.add_argument('--imgtool', help="The path to ImgTool", required=True)
parser.add_argument('--output', help="Output directory", required=True)

args = parser.parse_args()

if not os.path.isfile(args.config):
print(f"Error: The config file '{args.config}' does not exist")
return

config_path = os.path.dirname(os.path.abspath(args.config))
print(f"config_path: {config_path}")

with open(args.config) as config_file:
config = yaml.safe_load(config_file)

img_tool = args.imgtool
if img_tool.endswith('.py'):
if not os.path.exists(img_tool): # Is python file exist?
print(f"Error: The '{img_tool}' not found")
return
else:
if not shutil.which(img_tool): # Is binary file exist?
print(f"Error: The '{img_tool}' not found in the path")
return


output_dir = args.output
os.makedirs(output_dir, exist_ok=True)

parse_app_pack(config, None)

print(f"\nCreated {package_name}")


def verify_file_exists(filename):
filepath = pathlib.Path(filename)

if not filepath.exists():
print("ERROR: File " + filename + " not found")
sys.exit(1)


def parse_app_pack(app_pack, name):
header = None
image = []
imgtype = 0
global package_name

for key in app_pack:
if key.endswith("_pack"):
imagename, imagetype = parse_app_pack(app_pack[key], name)
image += [imagename]
if key.lower() == "header":
header = app_pack[key]
if key.lower() == "image":
image_path = os.path.join(config_path, app_pack[key])
verify_file_exists(image_path)
image += [image_path]
if key.lower() == "outputfile":
name = app_pack[key]

# Exit if header or image are not specified
if (header is None) or (image is None):
return (None, 0)

operation = 0

cmd = []
if img_tool.endswith('.py'):
cmd += [sys.executable]

cmd += [img_tool]
cmd += ["sign"]

for key in header:
if key == "align":
cmd += ["--align", str(header[key])]
elif key == "aes-gcm-key":
operation += 2
gcm_key_path = os.path.join(config_path, header[key])
verify_file_exists(gcm_key_path)
cmd += ["--aes-gcm-key", gcm_key_path]
elif key == "aes-kw-key":
operation += 2
kw_key_path = os.path.join(config_path, header[key])
verify_file_exists(kw_key_path)
cmd += ["--aes-kw-key", kw_key_path]
elif key == "header-size":
cmd += ["--header-size", hex(header[key])]
elif key == "load-addr":
cmd += ["--load-addr", hex(header[key])]
elif key == "pad-header":
if header[key] is True:
cmd += ["--pad-header"]
elif key == "private_signing_key":
operation += 1
private_key_path = os.path.join(config_path, header[key])
verify_file_exists(private_key_path)
cmd += ["--key", private_key_path]
elif key == "public-key-format":
cmd += ["--public-key-format", header[key]]
elif key == "slot-size":
cmd += ["--slot-size", hex(header[key])]
elif key == "version":
cmd += ["--version", header[key]]
else:
print("Unknown argument: " + key)

# If there are multiple input files they must be combined for signing or encryption
if len(image) > 1:
combined_images = os.path.join(output_dir, name + ".bin")

with open(combined_images, 'wb') as outfile:
for fname in image:
with open(fname, 'rb') as infile:
outfile.write(infile.read())
infile.close()
outfile.close()

image_input = combined_images
else:
image_input = image[0]

if operation > 1:
image_output = os.path.join(output_dir, name + "_signed_encrypted.bin")
else:
image_output = os.path.join(output_dir, name + "_signed.bin")

cmd += [image_input]
cmd += [image_output]

print(f'Calling imgtool to generate file {image_output}')
package_name = image_output
subprocess.run(cmd)

return (image_output, imgtype)


if __name__ == "__main__":
main()
53 changes: 53 additions & 0 deletions scripts/combine_images.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
#! /usr/bin/env python3
#
# Copyright (C) 2025 Analog Devices, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

combined_app_pack:
outputfile: combined

header:
private_signing_key: ../root-rsa-2048.pem
header-size: 0x400
align: 4
load-addr: 0x20080000
pad-header: yes
version: 1.0.0
slot-size: 0x40000

image1_pack:
outputfile: image1
header:
private_signing_key: ../root-rsa-2048.pem
header-size: 0x400
align: 4
load-addr: 0x20010000
pad-header: yes
version: 1.0.0
slot-size: 0x10000
image: image1.bin

image2_pack:
outputfile: image2
header:
private_signing_key: ../root-rsa-2048.pem
header-size: 0x400
align: 4
load-addr: 0x20020000
pad-header: yes
version: 1.0.0
slot-size: 0x10000
image: image2.bin