Marti Bolivar | d1780aa | 2019-01-30 20:30:42 -0700 | [diff] [blame] | 1 | # Copyright (c) 2018 Foundries.io |
| 2 | # |
| 3 | # SPDX-License-Identifier: Apache-2.0 |
| 4 | |
| 5 | import abc |
| 6 | import argparse |
| 7 | import os |
Marti Bolivar | 2f839da | 2019-06-12 11:25:23 -0600 | [diff] [blame] | 8 | import pathlib |
Martí Bolívar | 9c92baa | 2020-07-08 14:43:07 -0700 | [diff] [blame] | 9 | import pickle |
Martí Bolívar | 698db69 | 2021-02-02 12:20:47 -0800 | [diff] [blame] | 10 | import platform |
Marti Bolivar | 4a0f1f2 | 2019-02-14 14:49:03 -0700 | [diff] [blame] | 11 | import shutil |
Marti Bolivar | d1780aa | 2019-01-30 20:30:42 -0700 | [diff] [blame] | 12 | import subprocess |
Martí Bolívar | d8f459a | 2019-11-12 14:55:03 -0800 | [diff] [blame] | 13 | import sys |
Marti Bolivar | d1780aa | 2019-01-30 20:30:42 -0700 | [diff] [blame] | 14 | |
Marti Bolivar | d1780aa | 2019-01-30 20:30:42 -0700 | [diff] [blame] | 15 | from west import log |
Marti Bolivar | d1780aa | 2019-01-30 20:30:42 -0700 | [diff] [blame] | 16 | from west.util import quote_sh_list |
| 17 | |
Martí Bolívar | d8f459a | 2019-11-12 14:55:03 -0800 | [diff] [blame] | 18 | from build_helpers import find_build_dir, is_zephyr_build, \ |
Marti Bolivar | 3bd07a2 | 2019-06-12 11:48:50 -0600 | [diff] [blame] | 19 | FIND_BUILD_DIR_DESCRIPTION |
Martí Bolívar | d8f459a | 2019-11-12 14:55:03 -0800 | [diff] [blame] | 20 | from runners.core import BuildConfiguration |
| 21 | from zcmake import CMakeCache |
Dominik Ermel | 09980d6 | 2021-01-19 18:19:36 +0000 | [diff] [blame^] | 22 | from zephyr_ext_common import Forceable, ZEPHYR_SCRIPTS |
Jun Li | 2d5fb6d | 2019-05-07 02:00:20 -0700 | [diff] [blame] | 23 | |
Martí Bolívar | 9c92baa | 2020-07-08 14:43:07 -0700 | [diff] [blame] | 24 | # This is needed to load edt.pickle files. |
Mikkel Jakobsen | 3fe785d | 2021-04-06 15:05:23 +0200 | [diff] [blame] | 25 | sys.path.append(str(ZEPHYR_SCRIPTS / 'dts' / 'python-devicetree' / 'src')) |
Martí Bolívar | 6dab163 | 2020-02-10 16:53:06 -0800 | [diff] [blame] | 26 | |
Marti Bolivar | d1780aa | 2019-01-30 20:30:42 -0700 | [diff] [blame] | 27 | SIGN_DESCRIPTION = '''\ |
| 28 | This command automates some of the drudgery of creating signed Zephyr |
| 29 | binaries for chain-loading by a bootloader. |
| 30 | |
| 31 | In the simplest usage, run this from your build directory: |
| 32 | |
| 33 | west sign -t your_tool -- ARGS_FOR_YOUR_TOOL |
| 34 | |
Andrei Emeltchenko | fb44188 | 2020-07-07 11:57:51 +0300 | [diff] [blame] | 35 | The "ARGS_FOR_YOUR_TOOL" value can be any additional |
Marti Bolivar | d1780aa | 2019-01-30 20:30:42 -0700 | [diff] [blame] | 36 | arguments you want to pass to the tool, such as the location of a |
| 37 | signing key, a version identifier, etc. |
| 38 | |
| 39 | See tool-specific help below for details.''' |
| 40 | |
| 41 | SIGN_EPILOG = '''\ |
| 42 | imgtool |
| 43 | ------- |
| 44 | |
Andrei Emeltchenko | 51182ab | 2020-07-06 15:37:00 +0300 | [diff] [blame] | 45 | To build a signed binary you can load with MCUboot using imgtool, |
| 46 | run this from your build directory: |
Marti Bolivar | d1780aa | 2019-01-30 20:30:42 -0700 | [diff] [blame] | 47 | |
| 48 | west sign -t imgtool -- --key YOUR_SIGNING_KEY.pem |
| 49 | |
Marti Bolivar | 4a0f1f2 | 2019-02-14 14:49:03 -0700 | [diff] [blame] | 50 | For this to work, either imgtool must be installed (e.g. using pip3), |
| 51 | or you must pass the path to imgtool.py using the -p option. |
| 52 | |
Andrei Emeltchenko | fb44188 | 2020-07-07 11:57:51 +0300 | [diff] [blame] | 53 | Assuming your binary was properly built for processing and handling by |
| 54 | imgtool, this creates zephyr.signed.bin and zephyr.signed.hex |
| 55 | files which are ready for use by your bootloader. |
| 56 | |
Marti Bolivar | d1780aa | 2019-01-30 20:30:42 -0700 | [diff] [blame] | 57 | The image header size, alignment, and slot sizes are determined from |
Marti Bolivar | 4a0f1f2 | 2019-02-14 14:49:03 -0700 | [diff] [blame] | 58 | the build directory using .config and the device tree. A default |
| 59 | version number of 0.0.0+0 is used (which can be overridden by passing |
| 60 | "--version x.y.z+w" after "--key"). As shown above, extra arguments |
Andrei Emeltchenko | 51182ab | 2020-07-06 15:37:00 +0300 | [diff] [blame] | 61 | after a '--' are passed to imgtool directly. |
| 62 | |
| 63 | rimage |
| 64 | ------ |
| 65 | |
| 66 | To create a signed binary with the rimage tool, run this from your build |
| 67 | directory: |
| 68 | |
| 69 | west sign -t rimage -- -k YOUR_SIGNING_KEY.pem |
| 70 | |
| 71 | For this to work, either rimage must be installed or you must pass |
| 72 | the path to rimage using the -p option.''' |
Marti Bolivar | d1780aa | 2019-01-30 20:30:42 -0700 | [diff] [blame] | 73 | |
| 74 | |
| 75 | class ToggleAction(argparse.Action): |
| 76 | |
| 77 | def __call__(self, parser, args, ignored, option): |
| 78 | setattr(args, self.dest, not option.startswith('--no-')) |
| 79 | |
| 80 | |
| 81 | class Sign(Forceable): |
| 82 | def __init__(self): |
| 83 | super(Sign, self).__init__( |
| 84 | 'sign', |
| 85 | # Keep this in sync with the string in west-commands.yml. |
| 86 | 'sign a Zephyr binary for bootloader chain-loading', |
| 87 | SIGN_DESCRIPTION, |
| 88 | accepts_unknown_args=False) |
| 89 | |
| 90 | def do_add_parser(self, parser_adder): |
| 91 | parser = parser_adder.add_parser( |
| 92 | self.name, |
| 93 | epilog=SIGN_EPILOG, |
| 94 | help=self.help, |
| 95 | formatter_class=argparse.RawDescriptionHelpFormatter, |
| 96 | description=self.description) |
| 97 | |
Marti Bolivar | 3bd07a2 | 2019-06-12 11:48:50 -0600 | [diff] [blame] | 98 | parser.add_argument('-d', '--build-dir', |
| 99 | help=FIND_BUILD_DIR_DESCRIPTION) |
Martí Bolívar | 9b67367 | 2020-08-19 15:29:37 -0700 | [diff] [blame] | 100 | parser.add_argument('-q', '--quiet', action='store_true', |
| 101 | help='suppress non-error output') |
Marti Bolivar | d1780aa | 2019-01-30 20:30:42 -0700 | [diff] [blame] | 102 | self.add_force_arg(parser) |
| 103 | |
| 104 | # general options |
| 105 | group = parser.add_argument_group('tool control options') |
Andrei Emeltchenko | d44d986 | 2019-11-19 12:35:49 +0200 | [diff] [blame] | 106 | group.add_argument('-t', '--tool', choices=['imgtool', 'rimage'], |
| 107 | required=True, |
| 108 | help='''image signing tool name; imgtool and rimage |
| 109 | are currently supported''') |
Marti Bolivar | 4a0f1f2 | 2019-02-14 14:49:03 -0700 | [diff] [blame] | 110 | group.add_argument('-p', '--tool-path', default=None, |
Marti Bolivar | d1780aa | 2019-01-30 20:30:42 -0700 | [diff] [blame] | 111 | help='''path to the tool itself, if needed''') |
Anas Nashif | b553166 | 2021-01-25 13:57:20 -0500 | [diff] [blame] | 112 | group.add_argument('-D', '--tool-data', default=None, |
| 113 | help='''path to tool data/configuration directory, if needed''') |
Marti Bolivar | d1780aa | 2019-01-30 20:30:42 -0700 | [diff] [blame] | 114 | group.add_argument('tool_args', nargs='*', metavar='tool_opt', |
| 115 | help='extra option(s) to pass to the signing tool') |
| 116 | |
| 117 | # bin file options |
| 118 | group = parser.add_argument_group('binary (.bin) file options') |
| 119 | group.add_argument('--bin', '--no-bin', dest='gen_bin', nargs=0, |
| 120 | action=ToggleAction, |
| 121 | help='''produce a signed .bin file? |
Marti Bolivar | d371c54 | 2019-06-12 11:39:09 -0600 | [diff] [blame] | 122 | (default: yes, if supported and unsigned bin |
| 123 | exists)''') |
Marti Bolivar | d1780aa | 2019-01-30 20:30:42 -0700 | [diff] [blame] | 124 | group.add_argument('-B', '--sbin', metavar='BIN', |
Marti Bolivar | d1780aa | 2019-01-30 20:30:42 -0700 | [diff] [blame] | 125 | help='''signed .bin file name |
Marti Bolivar | 33cbba3 | 2019-06-12 10:29:01 -0600 | [diff] [blame] | 126 | (default: zephyr.signed.bin in the build |
| 127 | directory, next to zephyr.bin)''') |
Marti Bolivar | d1780aa | 2019-01-30 20:30:42 -0700 | [diff] [blame] | 128 | |
| 129 | # hex file options |
| 130 | group = parser.add_argument_group('Intel HEX (.hex) file options') |
| 131 | group.add_argument('--hex', '--no-hex', dest='gen_hex', nargs=0, |
| 132 | action=ToggleAction, |
| 133 | help='''produce a signed .hex file? |
Marti Bolivar | d371c54 | 2019-06-12 11:39:09 -0600 | [diff] [blame] | 134 | (default: yes, if supported and unsigned hex |
| 135 | exists)''') |
Marti Bolivar | d1780aa | 2019-01-30 20:30:42 -0700 | [diff] [blame] | 136 | group.add_argument('-H', '--shex', metavar='HEX', |
Marti Bolivar | d1780aa | 2019-01-30 20:30:42 -0700 | [diff] [blame] | 137 | help='''signed .hex file name |
Marti Bolivar | 33cbba3 | 2019-06-12 10:29:01 -0600 | [diff] [blame] | 138 | (default: zephyr.signed.hex in the build |
| 139 | directory, next to zephyr.hex)''') |
Marti Bolivar | d1780aa | 2019-01-30 20:30:42 -0700 | [diff] [blame] | 140 | |
Marti Bolivar | d1780aa | 2019-01-30 20:30:42 -0700 | [diff] [blame] | 141 | return parser |
| 142 | |
| 143 | def do_run(self, args, ignored): |
Marti Bolivar | f13fa53 | 2019-02-14 10:01:00 -0700 | [diff] [blame] | 144 | self.args = args # for check_force |
Marti Bolivar | d371c54 | 2019-06-12 11:39:09 -0600 | [diff] [blame] | 145 | |
| 146 | # Find the build directory and parse .config and DT. |
| 147 | build_dir = find_build_dir(args.build_dir) |
Marti Bolivar | 06c9f8e | 2019-06-12 11:49:02 -0600 | [diff] [blame] | 148 | self.check_force(os.path.isdir(build_dir), |
| 149 | 'no such build directory {}'.format(build_dir)) |
| 150 | self.check_force(is_zephyr_build(build_dir), |
Marti Bolivar | d1780aa | 2019-01-30 20:30:42 -0700 | [diff] [blame] | 151 | "build directory {} doesn't look like a Zephyr build " |
Marti Bolivar | 06c9f8e | 2019-06-12 11:49:02 -0600 | [diff] [blame] | 152 | 'directory'.format(build_dir)) |
Marti Bolivar | d371c54 | 2019-06-12 11:39:09 -0600 | [diff] [blame] | 153 | bcfg = BuildConfiguration(build_dir) |
Marti Bolivar | d1780aa | 2019-01-30 20:30:42 -0700 | [diff] [blame] | 154 | |
Marti Bolivar | d371c54 | 2019-06-12 11:39:09 -0600 | [diff] [blame] | 155 | # Decide on output formats. |
| 156 | formats = [] |
| 157 | bin_exists = 'CONFIG_BUILD_OUTPUT_BIN' in bcfg |
| 158 | if args.gen_bin: |
| 159 | self.check_force(bin_exists, |
| 160 | '--bin given but CONFIG_BUILD_OUTPUT_BIN not set ' |
| 161 | "in build directory's ({}) .config". |
| 162 | format(build_dir)) |
| 163 | formats.append('bin') |
| 164 | elif args.gen_bin is None and bin_exists: |
| 165 | formats.append('bin') |
| 166 | |
| 167 | hex_exists = 'CONFIG_BUILD_OUTPUT_HEX' in bcfg |
| 168 | if args.gen_hex: |
| 169 | self.check_force(hex_exists, |
| 170 | |
| 171 | '--hex given but CONFIG_BUILD_OUTPUT_HEX not set ' |
| 172 | "in build directory's ({}) .config". |
| 173 | format(build_dir)) |
| 174 | formats.append('hex') |
| 175 | elif args.gen_hex is None and hex_exists: |
| 176 | formats.append('hex') |
| 177 | |
Marti Bolivar | d371c54 | 2019-06-12 11:39:09 -0600 | [diff] [blame] | 178 | # Delegate to the signer. |
Marti Bolivar | d1780aa | 2019-01-30 20:30:42 -0700 | [diff] [blame] | 179 | if args.tool == 'imgtool': |
| 180 | signer = ImgtoolSigner() |
Andrei Emeltchenko | d44d986 | 2019-11-19 12:35:49 +0200 | [diff] [blame] | 181 | elif args.tool == 'rimage': |
| 182 | signer = RimageSigner() |
Marti Bolivar | d1780aa | 2019-01-30 20:30:42 -0700 | [diff] [blame] | 183 | # (Add support for other signers here in elif blocks) |
| 184 | else: |
| 185 | raise RuntimeError("can't happen") |
| 186 | |
Marti Bolivar | d371c54 | 2019-06-12 11:39:09 -0600 | [diff] [blame] | 187 | signer.sign(self, build_dir, bcfg, formats) |
Marti Bolivar | d1780aa | 2019-01-30 20:30:42 -0700 | [diff] [blame] | 188 | |
| 189 | |
| 190 | class Signer(abc.ABC): |
| 191 | '''Common abstract superclass for signers. |
| 192 | |
| 193 | To add support for a new tool, subclass this and add support for |
| 194 | it in the Sign.do_run() method.''' |
| 195 | |
| 196 | @abc.abstractmethod |
Marti Bolivar | d371c54 | 2019-06-12 11:39:09 -0600 | [diff] [blame] | 197 | def sign(self, command, build_dir, bcfg, formats): |
Marti Bolivar | d1780aa | 2019-01-30 20:30:42 -0700 | [diff] [blame] | 198 | '''Abstract method to perform a signature; subclasses must implement. |
| 199 | |
Marti Bolivar | 4a0f1f2 | 2019-02-14 14:49:03 -0700 | [diff] [blame] | 200 | :param command: the Sign instance |
Marti Bolivar | 06c9f8e | 2019-06-12 11:49:02 -0600 | [diff] [blame] | 201 | :param build_dir: the build directory |
Marti Bolivar | d371c54 | 2019-06-12 11:39:09 -0600 | [diff] [blame] | 202 | :param bcfg: BuildConfiguration for build directory |
| 203 | :param formats: list of formats to generate ('bin', 'hex') |
Marti Bolivar | d1780aa | 2019-01-30 20:30:42 -0700 | [diff] [blame] | 204 | ''' |
| 205 | |
| 206 | |
| 207 | class ImgtoolSigner(Signer): |
| 208 | |
Marti Bolivar | d371c54 | 2019-06-12 11:39:09 -0600 | [diff] [blame] | 209 | def sign(self, command, build_dir, bcfg, formats): |
Martí Bolívar | d8f459a | 2019-11-12 14:55:03 -0800 | [diff] [blame] | 210 | if not formats: |
| 211 | return |
Marti Bolivar | 4a0f1f2 | 2019-02-14 14:49:03 -0700 | [diff] [blame] | 212 | |
Martí Bolívar | d8f459a | 2019-11-12 14:55:03 -0800 | [diff] [blame] | 213 | args = command.args |
| 214 | b = pathlib.Path(build_dir) |
Martí Bolívar | d8f459a | 2019-11-12 14:55:03 -0800 | [diff] [blame] | 215 | |
Martí Bolívar | b4903d4 | 2021-02-02 12:05:58 -0800 | [diff] [blame] | 216 | imgtool = self.find_imgtool(command, args) |
Martí Bolívar | d8f459a | 2019-11-12 14:55:03 -0800 | [diff] [blame] | 217 | # The vector table offset is set in Kconfig: |
Stephanos Ioannidis | 3322489 | 2020-02-10 16:37:24 +0900 | [diff] [blame] | 218 | vtoff = self.get_cfg(command, bcfg, 'CONFIG_ROM_START_OFFSET') |
Martí Bolívar | d8f459a | 2019-11-12 14:55:03 -0800 | [diff] [blame] | 219 | # Flash device write alignment and the partition's slot size |
| 220 | # come from devicetree: |
Martí Bolívar | 9b67367 | 2020-08-19 15:29:37 -0700 | [diff] [blame] | 221 | flash = self.edt_flash_node(b, args.quiet) |
Martí Bolívar | d8f459a | 2019-11-12 14:55:03 -0800 | [diff] [blame] | 222 | align, addr, size = self.edt_flash_params(flash) |
| 223 | |
Dominik Ermel | 09980d6 | 2021-01-19 18:19:36 +0000 | [diff] [blame^] | 224 | if bcfg.get('CONFIG_BOOTLOADER_MCUBOOT', 'n') != 'y': |
Martí Bolívar | 7492997 | 2020-08-07 15:40:05 -0700 | [diff] [blame] | 225 | log.wrn("CONFIG_BOOTLOADER_MCUBOOT is not set to y in " |
Dominik Ermel | 09980d6 | 2021-01-19 18:19:36 +0000 | [diff] [blame^] | 226 | f"{bcfg.path}; this probably won't work") |
Martí Bolívar | 7492997 | 2020-08-07 15:40:05 -0700 | [diff] [blame] | 227 | |
Dominik Ermel | 09980d6 | 2021-01-19 18:19:36 +0000 | [diff] [blame^] | 228 | kernel = bcfg.get('CONFIG_KERNEL_BIN_NAME', 'zephyr') |
Martí Bolívar | 7492997 | 2020-08-07 15:40:05 -0700 | [diff] [blame] | 229 | |
Martí Bolívar | d8f459a | 2019-11-12 14:55:03 -0800 | [diff] [blame] | 230 | if 'bin' in formats: |
Martí Bolívar | 7492997 | 2020-08-07 15:40:05 -0700 | [diff] [blame] | 231 | in_bin = b / 'zephyr' / f'{kernel}.bin' |
| 232 | if not in_bin.is_file(): |
| 233 | log.die(f"no unsigned .bin found at {in_bin}") |
| 234 | in_bin = os.fspath(in_bin) |
Martí Bolívar | d8f459a | 2019-11-12 14:55:03 -0800 | [diff] [blame] | 235 | else: |
| 236 | in_bin = None |
| 237 | if 'hex' in formats: |
Martí Bolívar | 7492997 | 2020-08-07 15:40:05 -0700 | [diff] [blame] | 238 | in_hex = b / 'zephyr' / f'{kernel}.hex' |
| 239 | if not in_hex.is_file(): |
| 240 | log.die(f"no unsigned .hex found at {in_hex}") |
| 241 | in_hex = os.fspath(in_hex) |
Martí Bolívar | d8f459a | 2019-11-12 14:55:03 -0800 | [diff] [blame] | 242 | else: |
| 243 | in_hex = None |
| 244 | |
Martí Bolívar | 9b67367 | 2020-08-19 15:29:37 -0700 | [diff] [blame] | 245 | if not args.quiet: |
| 246 | log.banner('image configuration:') |
| 247 | log.inf('partition offset: {0} (0x{0:x})'.format(addr)) |
| 248 | log.inf('partition size: {0} (0x{0:x})'.format(size)) |
| 249 | log.inf('rom start offset: {0} (0x{0:x})'.format(vtoff)) |
Martí Bolívar | d8f459a | 2019-11-12 14:55:03 -0800 | [diff] [blame] | 250 | |
| 251 | # Base sign command. |
| 252 | # |
| 253 | # We provide a default --version in case the user is just |
| 254 | # messing around and doesn't want to set one. It will be |
| 255 | # overridden if there is a --version in args.tool_args. |
Martí Bolívar | b4903d4 | 2021-02-02 12:05:58 -0800 | [diff] [blame] | 256 | sign_base = imgtool + ['sign', |
| 257 | '--version', '0.0.0+0', |
| 258 | '--align', str(align), |
| 259 | '--header-size', str(vtoff), |
| 260 | '--slot-size', str(size)] |
Martí Bolívar | d8f459a | 2019-11-12 14:55:03 -0800 | [diff] [blame] | 261 | sign_base.extend(args.tool_args) |
| 262 | |
Martí Bolívar | 9b67367 | 2020-08-19 15:29:37 -0700 | [diff] [blame] | 263 | if not args.quiet: |
| 264 | log.banner('signing binaries') |
Martí Bolívar | d8f459a | 2019-11-12 14:55:03 -0800 | [diff] [blame] | 265 | if in_bin: |
| 266 | out_bin = args.sbin or str(b / 'zephyr' / 'zephyr.signed.bin') |
| 267 | sign_bin = sign_base + [in_bin, out_bin] |
Martí Bolívar | 9b67367 | 2020-08-19 15:29:37 -0700 | [diff] [blame] | 268 | if not args.quiet: |
| 269 | log.inf(f'unsigned bin: {in_bin}') |
| 270 | log.inf(f'signed bin: {out_bin}') |
| 271 | log.dbg(quote_sh_list(sign_bin)) |
Martí Bolívar | d8f459a | 2019-11-12 14:55:03 -0800 | [diff] [blame] | 272 | subprocess.check_call(sign_bin) |
| 273 | if in_hex: |
| 274 | out_hex = args.shex or str(b / 'zephyr' / 'zephyr.signed.hex') |
| 275 | sign_hex = sign_base + [in_hex, out_hex] |
Martí Bolívar | 9b67367 | 2020-08-19 15:29:37 -0700 | [diff] [blame] | 276 | if not args.quiet: |
| 277 | log.inf(f'unsigned hex: {in_hex}') |
| 278 | log.inf(f'signed hex: {out_hex}') |
| 279 | log.dbg(quote_sh_list(sign_hex)) |
Martí Bolívar | d8f459a | 2019-11-12 14:55:03 -0800 | [diff] [blame] | 280 | subprocess.check_call(sign_hex) |
| 281 | |
| 282 | @staticmethod |
| 283 | def find_imgtool(command, args): |
Marti Bolivar | 4a0f1f2 | 2019-02-14 14:49:03 -0700 | [diff] [blame] | 284 | if args.tool_path: |
Martí Bolívar | 698db69 | 2021-02-02 12:20:47 -0800 | [diff] [blame] | 285 | imgtool = args.tool_path |
| 286 | if not os.path.isfile(imgtool): |
| 287 | log.die(f'--tool-path {imgtool}: no such file') |
| 288 | else: |
| 289 | imgtool = shutil.which('imgtool') or shutil.which('imgtool.py') |
| 290 | if not imgtool: |
| 291 | log.die('imgtool not found; either install it', |
| 292 | '(e.g. "pip3 install imgtool") or provide --tool-path') |
Martí Bolívar | b4903d4 | 2021-02-02 12:05:58 -0800 | [diff] [blame] | 293 | |
Martí Bolívar | 698db69 | 2021-02-02 12:20:47 -0800 | [diff] [blame] | 294 | if platform.system() == 'Windows' and imgtool.endswith('.py'): |
| 295 | # Windows users may not be able to run .py files |
| 296 | # as executables in subprocesses, regardless of |
| 297 | # what the mode says. Always run imgtool as |
| 298 | # 'python path/to/imgtool.py' instead of |
| 299 | # 'path/to/imgtool.py' in these cases. |
| 300 | # https://github.com/zephyrproject-rtos/zephyr/issues/31876 |
| 301 | return [sys.executable, imgtool] |
| 302 | |
Martí Bolívar | b4903d4 | 2021-02-02 12:05:58 -0800 | [diff] [blame] | 303 | return [imgtool] |
Marti Bolivar | 2f839da | 2019-06-12 11:25:23 -0600 | [diff] [blame] | 304 | |
Ulf Magnusson | bb63416 | 2019-09-04 16:28:50 +0200 | [diff] [blame] | 305 | @staticmethod |
| 306 | def get_cfg(command, bcfg, item): |
Marti Bolivar | 4a0f1f2 | 2019-02-14 14:49:03 -0700 | [diff] [blame] | 307 | try: |
Marti Bolivar | 2f839da | 2019-06-12 11:25:23 -0600 | [diff] [blame] | 308 | return bcfg[item] |
Marti Bolivar | 4a0f1f2 | 2019-02-14 14:49:03 -0700 | [diff] [blame] | 309 | except KeyError: |
| 310 | command.check_force( |
Martí Bolívar | d8f459a | 2019-11-12 14:55:03 -0800 | [diff] [blame] | 311 | False, "build .config is missing a {} value".format(item)) |
Marti Bolivar | 4a0f1f2 | 2019-02-14 14:49:03 -0700 | [diff] [blame] | 312 | return None |
Martí Bolívar | d8f459a | 2019-11-12 14:55:03 -0800 | [diff] [blame] | 313 | |
| 314 | @staticmethod |
Martí Bolívar | 9b67367 | 2020-08-19 15:29:37 -0700 | [diff] [blame] | 315 | def edt_flash_node(b, quiet=False): |
Martí Bolívar | d8f459a | 2019-11-12 14:55:03 -0800 | [diff] [blame] | 316 | # Get the EDT Node corresponding to the zephyr,flash chosen DT |
Martí Bolívar | 9c92baa | 2020-07-08 14:43:07 -0700 | [diff] [blame] | 317 | # node; 'b' is the build directory as a pathlib object. |
Martí Bolívar | d8f459a | 2019-11-12 14:55:03 -0800 | [diff] [blame] | 318 | |
| 319 | # Ensure the build directory has a compiled DTS file |
| 320 | # where we expect it to be. |
Martí Bolívar | 9c92baa | 2020-07-08 14:43:07 -0700 | [diff] [blame] | 321 | dts = b / 'zephyr' / 'zephyr.dts' |
Martí Bolívar | 9b67367 | 2020-08-19 15:29:37 -0700 | [diff] [blame] | 322 | if not quiet: |
| 323 | log.dbg('DTS file:', dts, level=log.VERBOSE_VERY) |
Martí Bolívar | 9c92baa | 2020-07-08 14:43:07 -0700 | [diff] [blame] | 324 | edt_pickle = b / 'zephyr' / 'edt.pickle' |
| 325 | if not edt_pickle.is_file(): |
| 326 | log.die("can't load devicetree; expected to find:", edt_pickle) |
Martí Bolívar | d8f459a | 2019-11-12 14:55:03 -0800 | [diff] [blame] | 327 | |
Martí Bolívar | 9c92baa | 2020-07-08 14:43:07 -0700 | [diff] [blame] | 328 | # Load the devicetree. |
| 329 | with open(edt_pickle, 'rb') as f: |
| 330 | edt = pickle.load(f) |
Martí Bolívar | d8f459a | 2019-11-12 14:55:03 -0800 | [diff] [blame] | 331 | |
| 332 | # By convention, the zephyr,flash chosen node contains the |
| 333 | # partition information about the zephyr image to sign. |
| 334 | flash = edt.chosen_node('zephyr,flash') |
| 335 | if not flash: |
| 336 | log.die('devicetree has no chosen zephyr,flash node;', |
| 337 | "can't infer flash write block or image-0 slot sizes") |
| 338 | |
| 339 | return flash |
| 340 | |
| 341 | @staticmethod |
| 342 | def edt_flash_params(flash): |
Fabio Utzig | 716ab47 | 2020-08-17 08:59:36 -0300 | [diff] [blame] | 343 | # Get the flash device's write alignment and offset from the |
| 344 | # image-0 partition and the size from image-1 partition, out of the |
| 345 | # build directory's devicetree. image-1 partition size is used, |
| 346 | # when available, because in swap-move mode it can be one sector |
| 347 | # smaller. When not available, fallback to image-0 (single image dfu). |
Martí Bolívar | d8f459a | 2019-11-12 14:55:03 -0800 | [diff] [blame] | 348 | |
| 349 | # The node must have a "partitions" child node, which in turn |
Fabio Utzig | 716ab47 | 2020-08-17 08:59:36 -0300 | [diff] [blame] | 350 | # must have child node labeled "image-0" and may have a child node |
| 351 | # named "image-1". By convention, the slots for consumption by |
| 352 | # imgtool are linked into these partitions. |
Martí Bolívar | d8f459a | 2019-11-12 14:55:03 -0800 | [diff] [blame] | 353 | if 'partitions' not in flash.children: |
| 354 | log.die("DT zephyr,flash chosen node has no partitions,", |
Fabio Utzig | 716ab47 | 2020-08-17 08:59:36 -0300 | [diff] [blame] | 355 | "can't find partitions for MCUboot slots") |
Martí Bolívar | d8f459a | 2019-11-12 14:55:03 -0800 | [diff] [blame] | 356 | |
Fabio Utzig | 716ab47 | 2020-08-17 08:59:36 -0300 | [diff] [blame] | 357 | partitions = flash.children['partitions'] |
| 358 | images = { |
| 359 | node.label: node for node in partitions.children.values() |
| 360 | if node.label in set(['image-0', 'image-1']) |
| 361 | } |
| 362 | |
| 363 | if 'image-0' not in images: |
| 364 | log.die("DT zephyr,flash chosen node has no image-0 partition,", |
| 365 | "can't determine its address") |
Martí Bolívar | d8f459a | 2019-11-12 14:55:03 -0800 | [diff] [blame] | 366 | |
| 367 | # Die on missing or zero alignment or slot_size. |
| 368 | if "write-block-size" not in flash.props: |
| 369 | log.die('DT zephyr,flash node has no write-block-size;', |
| 370 | "can't determine imgtool write alignment") |
| 371 | align = flash.props['write-block-size'].val |
| 372 | if align == 0: |
| 373 | log.die('expected nonzero flash alignment, but got ' |
| 374 | 'DT flash device write-block-size {}'.format(align)) |
Martí Bolívar | d8f459a | 2019-11-12 14:55:03 -0800 | [diff] [blame] | 375 | |
Fabio Utzig | 716ab47 | 2020-08-17 08:59:36 -0300 | [diff] [blame] | 376 | # The partitions node, and its subnode, must provide |
| 377 | # the size of image-1 or image-0 partition via the regs property. |
| 378 | image_key = 'image-1' if 'image-1' in images else 'image-0' |
| 379 | if not images[image_key].regs: |
| 380 | log.die(f'{image_key} flash partition has no regs property;', |
| 381 | "can't determine size of image") |
| 382 | |
| 383 | # always use addr of image-0, which is where images are run |
| 384 | addr = images['image-0'].regs[0].addr |
| 385 | |
| 386 | size = images[image_key].regs[0].size |
| 387 | if size == 0: |
| 388 | log.die('expected nonzero slot size for {}'.format(image_key)) |
| 389 | |
| 390 | return (align, addr, size) |
Andrei Emeltchenko | d44d986 | 2019-11-19 12:35:49 +0200 | [diff] [blame] | 391 | |
| 392 | class RimageSigner(Signer): |
| 393 | |
Anas Nashif | f751dd4 | 2020-10-14 14:44:53 -0400 | [diff] [blame] | 394 | @staticmethod |
| 395 | def edt_get_rimage_target(board): |
Anas Nashif | 3ac163e | 2020-10-21 09:40:02 -0400 | [diff] [blame] | 396 | if 'intel_adsp_cavs15' in board: |
Anas Nashif | f751dd4 | 2020-10-14 14:44:53 -0400 | [diff] [blame] | 397 | return 'apl' |
| 398 | if 'intel_adsp_cavs18' in board: |
| 399 | return 'cnl' |
| 400 | if 'intel_adsp_cavs20' in board: |
| 401 | return 'icl' |
| 402 | if 'intel_adsp_cavs25' in board: |
| 403 | return 'tgl' |
| 404 | |
| 405 | log.die('Signing not supported for board ' + board) |
| 406 | |
Andrei Emeltchenko | d44d986 | 2019-11-19 12:35:49 +0200 | [diff] [blame] | 407 | def sign(self, command, build_dir, bcfg, formats): |
| 408 | args = command.args |
| 409 | |
| 410 | if args.tool_path: |
| 411 | command.check_force(shutil.which(args.tool_path), |
| 412 | '--tool-path {}: not an executable'. |
| 413 | format(args.tool_path)) |
| 414 | tool_path = args.tool_path |
| 415 | else: |
| 416 | tool_path = shutil.which('rimage') |
| 417 | if not tool_path: |
| 418 | log.die('rimage not found; either install it', |
| 419 | 'or provide --tool-path') |
| 420 | |
| 421 | b = pathlib.Path(build_dir) |
| 422 | cache = CMakeCache.from_build_dir(build_dir) |
| 423 | |
| 424 | board = cache['CACHED_BOARD'] |
Anas Nashif | f751dd4 | 2020-10-14 14:44:53 -0400 | [diff] [blame] | 425 | log.inf('Signing for board ' + board) |
| 426 | target = self.edt_get_rimage_target(board) |
Guennadi Liakhovetski | 3de40b4 | 2020-10-27 12:48:36 +0100 | [diff] [blame] | 427 | conf = target + '.toml' |
| 428 | log.inf('Signing for SOC target ' + target + ' using ' + conf) |
Andrei Emeltchenko | d44d986 | 2019-11-19 12:35:49 +0200 | [diff] [blame] | 429 | |
Martí Bolívar | 9b67367 | 2020-08-19 15:29:37 -0700 | [diff] [blame] | 430 | if not args.quiet: |
| 431 | log.inf('Signing with tool {}'.format(tool_path)) |
Andrei Emeltchenko | d44d986 | 2019-11-19 12:35:49 +0200 | [diff] [blame] | 432 | |
| 433 | bootloader = str(b / 'zephyr' / 'bootloader.elf.mod') |
| 434 | kernel = str(b / 'zephyr' / 'zephyr.elf.mod') |
| 435 | out_bin = str(b / 'zephyr' / 'zephyr.ri') |
Guennadi Liakhovetski | 3de40b4 | 2020-10-27 12:48:36 +0100 | [diff] [blame] | 436 | out_xman = str(b / 'zephyr' / 'zephyr.ri.xman') |
| 437 | out_tmp = str(b / 'zephyr' / 'zephyr.rix') |
Anas Nashif | fee9af2 | 2021-01-25 13:59:57 -0500 | [diff] [blame] | 438 | conf_path_cmd = [] |
| 439 | if cache.get('RIMAGE_CONFIG_PATH') and not args.tool_data: |
| 440 | rimage_conf = pathlib.Path(cache['RIMAGE_CONFIG_PATH']) |
| 441 | conf_path = str(rimage_conf / conf) |
| 442 | conf_path_cmd = ['-c', conf_path] |
| 443 | elif args.tool_data: |
| 444 | conf_dir = pathlib.Path(args.tool_data) |
| 445 | conf_path = str(conf_dir / conf) |
| 446 | conf_path_cmd = ['-c', conf_path] |
| 447 | else: |
| 448 | log.die('Configuration not found') |
Jian Kang | 8c9b06a | 2021-02-03 17:05:24 +0800 | [diff] [blame] | 449 | if '--no-manifest' in args.tool_args: |
| 450 | no_manifest = True |
| 451 | args.tool_args.remove('--no-manifest') |
| 452 | else: |
| 453 | no_manifest = False |
Andrei Emeltchenko | d44d986 | 2019-11-19 12:35:49 +0200 | [diff] [blame] | 454 | |
| 455 | sign_base = ([tool_path] + args.tool_args + |
Anas Nashif | fee9af2 | 2021-01-25 13:59:57 -0500 | [diff] [blame] | 456 | ['-o', out_bin] + conf_path_cmd + ['-i', '3', '-e'] + |
Andrei Emeltchenko | d44d986 | 2019-11-19 12:35:49 +0200 | [diff] [blame] | 457 | [bootloader, kernel]) |
| 458 | |
Martí Bolívar | 9b67367 | 2020-08-19 15:29:37 -0700 | [diff] [blame] | 459 | if not args.quiet: |
| 460 | log.inf(quote_sh_list(sign_base)) |
Andrei Emeltchenko | d44d986 | 2019-11-19 12:35:49 +0200 | [diff] [blame] | 461 | subprocess.check_call(sign_base) |
Guennadi Liakhovetski | 3de40b4 | 2020-10-27 12:48:36 +0100 | [diff] [blame] | 462 | |
Jian Kang | 8c9b06a | 2021-02-03 17:05:24 +0800 | [diff] [blame] | 463 | if no_manifest: |
| 464 | filenames = [out_bin] |
| 465 | else: |
| 466 | filenames = [out_xman, out_bin] |
Guennadi Liakhovetski | 3de40b4 | 2020-10-27 12:48:36 +0100 | [diff] [blame] | 467 | with open(out_tmp, 'wb') as outfile: |
| 468 | for fname in filenames: |
| 469 | with open(fname, 'rb') as infile: |
| 470 | outfile.write(infile.read()) |
| 471 | |
| 472 | os.remove(out_bin) |
| 473 | os.rename(out_tmp, out_bin) |