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