diff --git a/boot/bootutil/src/loader.c b/boot/bootutil/src/loader.c index 9769428b4d..7cd771fd84 100644 --- a/boot/bootutil/src/loader.c +++ b/boot/bootutil/src/loader.c @@ -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: * @@ -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. @@ -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); } diff --git a/scripts/combine_images.py b/scripts/combine_images.py new file mode 100644 index 0000000000..45c1f8aefe --- /dev/null +++ b/scripts/combine_images.py @@ -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() diff --git a/scripts/combine_images.yaml b/scripts/combine_images.yaml new file mode 100644 index 0000000000..98bec2d3e6 --- /dev/null +++ b/scripts/combine_images.yaml @@ -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