David Brown | dbc5727 | 2017-07-17 15:38:54 -0600 | [diff] [blame] | 1 | #! /usr/bin/env python3 |
David Brown | b730e24 | 2017-12-20 11:10:55 -0700 | [diff] [blame] | 2 | # |
| 3 | # Copyright 2017 Linaro Limited |
| 4 | # |
| 5 | # Licensed under the Apache License, Version 2.0 (the "License"); |
| 6 | # you may not use this file except in compliance with the License. |
| 7 | # You may obtain a copy of the License at |
| 8 | # |
| 9 | # http://www.apache.org/licenses/LICENSE-2.0 |
| 10 | # |
| 11 | # Unless required by applicable law or agreed to in writing, software |
| 12 | # distributed under the License is distributed on an "AS IS" BASIS, |
| 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 14 | # See the License for the specific language governing permissions and |
| 15 | # limitations under the License. |
David Brown | dbc5727 | 2017-07-17 15:38:54 -0600 | [diff] [blame] | 16 | |
| 17 | """ |
| 18 | Assemble multiple images into a single image that can be flashed on the device. |
| 19 | """ |
| 20 | |
| 21 | import argparse |
David Brown | 2cf522c | 2017-07-20 12:15:31 -0600 | [diff] [blame] | 22 | import errno |
David Brown | dbc5727 | 2017-07-17 15:38:54 -0600 | [diff] [blame] | 23 | import io |
| 24 | import re |
| 25 | import os.path |
| 26 | |
| 27 | def same_keys(a, b): |
| 28 | """Determine if the dicts a and b have the same keys in them""" |
| 29 | for ak in a.keys(): |
| 30 | if ak not in b: |
| 31 | return False |
| 32 | for bk in b.keys(): |
| 33 | if bk not in a: |
| 34 | return False |
| 35 | return True |
| 36 | |
Fabio Utzig | 539d766 | 2019-09-05 10:57:00 -0300 | [diff] [blame^] | 37 | offset_re = re.compile(r"^#define DT_FLASH_AREA_([0-9A-Z_]+)_OFFSET(_0)?\s+(0x[0-9a-fA-F]+|[0-9]+)$") |
| 38 | size_re = re.compile(r"^#define DT_FLASH_AREA_([0-9A-Z_]+)_SIZE(_0)?\s+(0x[0-9a-fA-F]+|[0-9]+)$") |
David Brown | dbc5727 | 2017-07-17 15:38:54 -0600 | [diff] [blame] | 39 | |
| 40 | class Assembly(): |
| 41 | def __init__(self, output, bootdir): |
| 42 | self.find_slots(bootdir) |
David Brown | 2cf522c | 2017-07-20 12:15:31 -0600 | [diff] [blame] | 43 | try: |
| 44 | os.unlink(output) |
| 45 | except OSError as e: |
| 46 | if e.errno != errno.ENOENT: |
| 47 | raise |
David Brown | dbc5727 | 2017-07-17 15:38:54 -0600 | [diff] [blame] | 48 | self.output = output |
| 49 | |
| 50 | def find_slots(self, bootdir): |
| 51 | offsets = {} |
| 52 | sizes = {} |
Fabio Utzig | 539d766 | 2019-09-05 10:57:00 -0300 | [diff] [blame^] | 53 | with open(os.path.join(bootdir, 'zephyr', 'include', 'generated', 'generated_dts_board_unfixed.h'), 'r') as fd: |
David Brown | dbc5727 | 2017-07-17 15:38:54 -0600 | [diff] [blame] | 54 | for line in fd: |
| 55 | m = offset_re.match(line) |
| 56 | if m is not None: |
Kiril Zyapkov | 5d5c446 | 2018-05-28 15:40:16 +0300 | [diff] [blame] | 57 | offsets[m.group(1)] = int(m.group(3), 0) |
David Brown | dbc5727 | 2017-07-17 15:38:54 -0600 | [diff] [blame] | 58 | m = size_re.match(line) |
| 59 | if m is not None: |
Kiril Zyapkov | 5d5c446 | 2018-05-28 15:40:16 +0300 | [diff] [blame] | 60 | sizes[m.group(1)] = int(m.group(3), 0) |
David Brown | dbc5727 | 2017-07-17 15:38:54 -0600 | [diff] [blame] | 61 | |
| 62 | if not same_keys(offsets, sizes): |
| 63 | raise Exception("Inconsistent data in generated_dts_board.h") |
| 64 | |
| 65 | # We care about the MCUBOOT, IMAGE_0, and IMAGE_1 partitions. |
| 66 | if 'MCUBOOT' not in offsets: |
| 67 | raise Exception("Board partition table does not have mcuboot partition") |
| 68 | |
| 69 | if 'IMAGE_0' not in offsets: |
| 70 | raise Exception("Board partition table does not have image-0 partition") |
| 71 | |
| 72 | if 'IMAGE_1' not in offsets: |
| 73 | raise Exception("Board partition table does not have image-1 partition") |
| 74 | |
| 75 | self.offsets = offsets |
| 76 | self.sizes = sizes |
| 77 | |
| 78 | def add_image(self, source, partition): |
| 79 | with open(self.output, 'ab') as ofd: |
| 80 | pos = ofd.tell() |
| 81 | print("partition {}, pos={}, offset={}".format(partition, pos, self.offsets[partition])) |
| 82 | if pos > self.offsets[partition]: |
| 83 | raise Exception("Partitions not in order, unsupported") |
| 84 | if pos < self.offsets[partition]: |
| 85 | buf = b'\xFF' * (self.offsets[partition] - pos) |
| 86 | ofd.write(buf) |
| 87 | with open(source, 'rb') as rfd: |
| 88 | ibuf = rfd.read() |
| 89 | if len(ibuf) > self.sizes[partition]: |
| 90 | raise Exception("Image {} is too large for partition".format(source)) |
| 91 | ofd.write(ibuf) |
| 92 | |
| 93 | def main(): |
| 94 | parser = argparse.ArgumentParser() |
| 95 | |
| 96 | parser.add_argument('-b', '--bootdir', required=True, |
| 97 | help='Directory of built bootloader') |
| 98 | parser.add_argument('-p', '--primary', required=True, |
| 99 | help='Signed image file for primary image') |
| 100 | parser.add_argument('-s', '--secondary', |
| 101 | help='Signed image file for secondary image') |
| 102 | parser.add_argument('-o', '--output', required=True, |
| 103 | help='Filename to write full image to') |
| 104 | |
| 105 | args = parser.parse_args() |
| 106 | output = Assembly(args.output, args.bootdir) |
| 107 | |
Fabio Utzig | 539d766 | 2019-09-05 10:57:00 -0300 | [diff] [blame^] | 108 | output.add_image(os.path.join(args.bootdir, 'zephyr', 'zephyr.bin'), 'MCUBOOT') |
David Brown | dbc5727 | 2017-07-17 15:38:54 -0600 | [diff] [blame] | 109 | output.add_image(args.primary, "IMAGE_0") |
| 110 | if args.secondary is not None: |
| 111 | output.add_image(args.secondary, "IMAGE_1") |
| 112 | |
| 113 | if __name__ == '__main__': |
| 114 | main() |