1#! /usr/bin/env python3 2# 3# Copyright 2017 Linaro Limited 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""" 20Assemble multiple images into a single image that can be flashed on the device. 21""" 22 23import argparse 24import errno 25import io 26import re 27import os 28import os.path 29import sys 30 31def same_keys(a, b): 32 """Determine if the dicts a and b have the same keys in them""" 33 for ak in a.keys(): 34 if ak not in b: 35 return False 36 for bk in b.keys(): 37 if bk not in a: 38 return False 39 return True 40 41offset_re = re.compile(r"^#define DT_FLASH_AREA_([0-9A-Z_]+)_OFFSET(_0)?\s+(0x[0-9a-fA-F]+|[0-9]+)$") 42size_re = re.compile(r"^#define DT_FLASH_AREA_([0-9A-Z_]+)_SIZE(_0)?\s+(0x[0-9a-fA-F]+|[0-9]+)$") 43 44class Assembly(): 45 def __init__(self, output, bootdir, edt): 46 self.find_slots(edt) 47 try: 48 os.unlink(output) 49 except OSError as e: 50 if e.errno != errno.ENOENT: 51 raise 52 self.output = output 53 54 def find_slots(self, edt): 55 offsets = {} 56 sizes = {} 57 58 part_nodes = edt.compat2nodes["fixed-partitions"] 59 for node in part_nodes: 60 for child in node.children.values(): 61 if "label" in child.props: 62 label = child.props["label"].val 63 offsets[label] = child.regs[0].addr 64 sizes[label] = child.regs[0].size 65 66 if not same_keys(offsets, sizes): 67 raise Exception("Inconsistent data in devicetree.h") 68 69 # We care about the mcuboot, image-0, and image-1 partitions. 70 if 'mcuboot' not in offsets: 71 raise Exception("Board partition table does not have mcuboot partition") 72 73 if 'image-0' not in offsets: 74 raise Exception("Board partition table does not have image-0 partition") 75 76 if 'image-1' not in offsets: 77 raise Exception("Board partition table does not have image-1 partition") 78 79 self.offsets = offsets 80 self.sizes = sizes 81 82 def add_image(self, source, partition): 83 with open(self.output, 'ab') as ofd: 84 pos = ofd.tell() 85 print("partition {}, pos={}, offset={}".format(partition, pos, self.offsets[partition])) 86 if pos > self.offsets[partition]: 87 raise Exception("Partitions not in order, unsupported") 88 if pos < self.offsets[partition]: 89 buf = b'\xFF' * (self.offsets[partition] - pos) 90 ofd.write(buf) 91 with open(source, 'rb') as rfd: 92 ibuf = rfd.read() 93 if len(ibuf) > self.sizes[partition]: 94 raise Exception("Image {} is too large for partition".format(source)) 95 ofd.write(ibuf) 96 97def find_board_name(bootdir): 98 suffix = ".dts.pre.tmp" 99 100 for _, _, files in os.walk(os.path.join(bootdir, "zephyr")): 101 for filename in files: 102 if filename.endswith(suffix): 103 return filename[:-len(suffix)] 104 105 106def main(): 107 parser = argparse.ArgumentParser() 108 109 parser.add_argument('-b', '--bootdir', required=True, 110 help='Directory of built bootloader') 111 parser.add_argument('-p', '--primary', required=True, 112 help='Signed image file for primary image') 113 parser.add_argument('-s', '--secondary', 114 help='Signed image file for secondary image') 115 parser.add_argument('-o', '--output', required=True, 116 help='Filename to write full image to') 117 parser.add_argument('-z', '--zephyr-base', 118 help='Zephyr base containing the Zephyr repository') 119 120 args = parser.parse_args() 121 122 zephyr_base = args.zephyr_base 123 if zephyr_base is None: 124 try: 125 zephyr_base = os.environ['ZEPHYR_BASE'] 126 except KeyError: 127 print('Need to either have ZEPHYR_BASE in environment or pass in -z') 128 sys.exit(1) 129 130 sys.path.insert(0, os.path.join(zephyr_base, "scripts", "dts", "python-devicetree", "src")) 131 import devicetree.edtlib 132 133 board = find_board_name(args.bootdir) 134 135 dts_path = os.path.join(args.bootdir, "zephyr", board + ".dts.pre.tmp") 136 137 edt = devicetree.edtlib.EDT(dts_path, [os.path.join(zephyr_base, "dts", "bindings")], 138 warn_reg_unit_address_mismatch=False) 139 140 output = Assembly(args.output, args.bootdir, edt) 141 142 output.add_image(os.path.join(args.bootdir, 'zephyr', 'zephyr.bin'), 'mcuboot') 143 output.add_image(args.primary, "image-0") 144 if args.secondary is not None: 145 output.add_image(args.secondary, "image-1") 146 147if __name__ == '__main__': 148 main() 149