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)) |
Martí Bolívar | 250b213 | 2021-04-28 16:20:38 -0700 | [diff] [blame] | 153 | build_conf = 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 = [] |
Martí Bolívar | 250b213 | 2021-04-28 16:20:38 -0700 | [diff] [blame] | 157 | bin_exists = build_conf.getboolean('CONFIG_BUILD_OUTPUT_BIN') |
Marti Bolivar | d371c54 | 2019-06-12 11:39:09 -0600 | [diff] [blame] | 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 | |
Martí Bolívar | 250b213 | 2021-04-28 16:20:38 -0700 | [diff] [blame] | 167 | hex_exists = build_conf.getboolean('CONFIG_BUILD_OUTPUT_HEX') |
Marti Bolivar | d371c54 | 2019-06-12 11:39:09 -0600 | [diff] [blame] | 168 | if args.gen_hex: |
| 169 | self.check_force(hex_exists, |
Marti Bolivar | d371c54 | 2019-06-12 11:39:09 -0600 | [diff] [blame] | 170 | '--hex given but CONFIG_BUILD_OUTPUT_HEX not set ' |
| 171 | "in build directory's ({}) .config". |
| 172 | format(build_dir)) |
| 173 | formats.append('hex') |
| 174 | elif args.gen_hex is None and hex_exists: |
| 175 | formats.append('hex') |
| 176 | |
Marti Bolivar | d371c54 | 2019-06-12 11:39:09 -0600 | [diff] [blame] | 177 | # Delegate to the signer. |
Marti Bolivar | d1780aa | 2019-01-30 20:30:42 -0700 | [diff] [blame] | 178 | if args.tool == 'imgtool': |
| 179 | signer = ImgtoolSigner() |
Andrei Emeltchenko | d44d986 | 2019-11-19 12:35:49 +0200 | [diff] [blame] | 180 | elif args.tool == 'rimage': |
| 181 | signer = RimageSigner() |
Marti Bolivar | d1780aa | 2019-01-30 20:30:42 -0700 | [diff] [blame] | 182 | # (Add support for other signers here in elif blocks) |
| 183 | else: |
| 184 | raise RuntimeError("can't happen") |
| 185 | |
Martí Bolívar | 250b213 | 2021-04-28 16:20:38 -0700 | [diff] [blame] | 186 | signer.sign(self, build_dir, build_conf, formats) |
Marti Bolivar | d1780aa | 2019-01-30 20:30:42 -0700 | [diff] [blame] | 187 | |
| 188 | |
| 189 | class Signer(abc.ABC): |
| 190 | '''Common abstract superclass for signers. |
| 191 | |
| 192 | To add support for a new tool, subclass this and add support for |
| 193 | it in the Sign.do_run() method.''' |
| 194 | |
| 195 | @abc.abstractmethod |
Martí Bolívar | 250b213 | 2021-04-28 16:20:38 -0700 | [diff] [blame] | 196 | def sign(self, command, build_dir, build_conf, formats): |
Marti Bolivar | d1780aa | 2019-01-30 20:30:42 -0700 | [diff] [blame] | 197 | '''Abstract method to perform a signature; subclasses must implement. |
| 198 | |
Marti Bolivar | 4a0f1f2 | 2019-02-14 14:49:03 -0700 | [diff] [blame] | 199 | :param command: the Sign instance |
Marti Bolivar | 06c9f8e | 2019-06-12 11:49:02 -0600 | [diff] [blame] | 200 | :param build_dir: the build directory |
Martí Bolívar | 250b213 | 2021-04-28 16:20:38 -0700 | [diff] [blame] | 201 | :param build_conf: BuildConfiguration for build directory |
Marti Bolivar | d371c54 | 2019-06-12 11:39:09 -0600 | [diff] [blame] | 202 | :param formats: list of formats to generate ('bin', 'hex') |
Marti Bolivar | d1780aa | 2019-01-30 20:30:42 -0700 | [diff] [blame] | 203 | ''' |
| 204 | |
| 205 | |
| 206 | class ImgtoolSigner(Signer): |
| 207 | |
Martí Bolívar | 250b213 | 2021-04-28 16:20:38 -0700 | [diff] [blame] | 208 | def sign(self, command, build_dir, build_conf, formats): |
Martí Bolívar | d8f459a | 2019-11-12 14:55:03 -0800 | [diff] [blame] | 209 | if not formats: |
| 210 | return |
Marti Bolivar | 4a0f1f2 | 2019-02-14 14:49:03 -0700 | [diff] [blame] | 211 | |
Martí Bolívar | d8f459a | 2019-11-12 14:55:03 -0800 | [diff] [blame] | 212 | args = command.args |
| 213 | b = pathlib.Path(build_dir) |
Martí Bolívar | d8f459a | 2019-11-12 14:55:03 -0800 | [diff] [blame] | 214 | |
Martí Bolívar | b4903d4 | 2021-02-02 12:05:58 -0800 | [diff] [blame] | 215 | imgtool = self.find_imgtool(command, args) |
Martí Bolívar | d8f459a | 2019-11-12 14:55:03 -0800 | [diff] [blame] | 216 | # The vector table offset is set in Kconfig: |
Martí Bolívar | 250b213 | 2021-04-28 16:20:38 -0700 | [diff] [blame] | 217 | vtoff = self.get_cfg(command, build_conf, 'CONFIG_ROM_START_OFFSET') |
Martí Bolívar | d8f459a | 2019-11-12 14:55:03 -0800 | [diff] [blame] | 218 | # Flash device write alignment and the partition's slot size |
| 219 | # come from devicetree: |
Martí Bolívar | 9b67367 | 2020-08-19 15:29:37 -0700 | [diff] [blame] | 220 | flash = self.edt_flash_node(b, args.quiet) |
Martí Bolívar | d8f459a | 2019-11-12 14:55:03 -0800 | [diff] [blame] | 221 | align, addr, size = self.edt_flash_params(flash) |
| 222 | |
Arvin Farahmand | 2de6bf9 | 2021-05-06 14:49:07 -0400 | [diff] [blame] | 223 | if not build_conf.getboolean('CONFIG_BOOTLOADER_MCUBOOT'): |
Martí Bolívar | 7492997 | 2020-08-07 15:40:05 -0700 | [diff] [blame] | 224 | log.wrn("CONFIG_BOOTLOADER_MCUBOOT is not set to y in " |
Martí Bolívar | 250b213 | 2021-04-28 16:20:38 -0700 | [diff] [blame] | 225 | f"{build_conf.path}; this probably won't work") |
Martí Bolívar | 7492997 | 2020-08-07 15:40:05 -0700 | [diff] [blame] | 226 | |
Martí Bolívar | 250b213 | 2021-04-28 16:20:38 -0700 | [diff] [blame] | 227 | kernel = build_conf.get('CONFIG_KERNEL_BIN_NAME', 'zephyr') |
Martí Bolívar | 7492997 | 2020-08-07 15:40:05 -0700 | [diff] [blame] | 228 | |
Martí Bolívar | d8f459a | 2019-11-12 14:55:03 -0800 | [diff] [blame] | 229 | if 'bin' in formats: |
Martí Bolívar | 7492997 | 2020-08-07 15:40:05 -0700 | [diff] [blame] | 230 | in_bin = b / 'zephyr' / f'{kernel}.bin' |
| 231 | if not in_bin.is_file(): |
| 232 | log.die(f"no unsigned .bin found at {in_bin}") |
| 233 | in_bin = os.fspath(in_bin) |
Martí Bolívar | d8f459a | 2019-11-12 14:55:03 -0800 | [diff] [blame] | 234 | else: |
| 235 | in_bin = None |
| 236 | if 'hex' in formats: |
Martí Bolívar | 7492997 | 2020-08-07 15:40:05 -0700 | [diff] [blame] | 237 | in_hex = b / 'zephyr' / f'{kernel}.hex' |
| 238 | if not in_hex.is_file(): |
| 239 | log.die(f"no unsigned .hex found at {in_hex}") |
| 240 | in_hex = os.fspath(in_hex) |
Martí Bolívar | d8f459a | 2019-11-12 14:55:03 -0800 | [diff] [blame] | 241 | else: |
| 242 | in_hex = None |
| 243 | |
Martí Bolívar | 9b67367 | 2020-08-19 15:29:37 -0700 | [diff] [blame] | 244 | if not args.quiet: |
| 245 | log.banner('image configuration:') |
| 246 | log.inf('partition offset: {0} (0x{0:x})'.format(addr)) |
| 247 | log.inf('partition size: {0} (0x{0:x})'.format(size)) |
| 248 | log.inf('rom start offset: {0} (0x{0:x})'.format(vtoff)) |
Martí Bolívar | d8f459a | 2019-11-12 14:55:03 -0800 | [diff] [blame] | 249 | |
| 250 | # Base sign command. |
| 251 | # |
| 252 | # We provide a default --version in case the user is just |
| 253 | # messing around and doesn't want to set one. It will be |
| 254 | # overridden if there is a --version in args.tool_args. |
Martí Bolívar | b4903d4 | 2021-02-02 12:05:58 -0800 | [diff] [blame] | 255 | sign_base = imgtool + ['sign', |
| 256 | '--version', '0.0.0+0', |
| 257 | '--align', str(align), |
| 258 | '--header-size', str(vtoff), |
| 259 | '--slot-size', str(size)] |
Martí Bolívar | d8f459a | 2019-11-12 14:55:03 -0800 | [diff] [blame] | 260 | sign_base.extend(args.tool_args) |
| 261 | |
Martí Bolívar | 9b67367 | 2020-08-19 15:29:37 -0700 | [diff] [blame] | 262 | if not args.quiet: |
| 263 | log.banner('signing binaries') |
Martí Bolívar | d8f459a | 2019-11-12 14:55:03 -0800 | [diff] [blame] | 264 | if in_bin: |
| 265 | out_bin = args.sbin or str(b / 'zephyr' / 'zephyr.signed.bin') |
| 266 | sign_bin = sign_base + [in_bin, out_bin] |
Martí Bolívar | 9b67367 | 2020-08-19 15:29:37 -0700 | [diff] [blame] | 267 | if not args.quiet: |
| 268 | log.inf(f'unsigned bin: {in_bin}') |
| 269 | log.inf(f'signed bin: {out_bin}') |
| 270 | log.dbg(quote_sh_list(sign_bin)) |
Martí Bolívar | d8f459a | 2019-11-12 14:55:03 -0800 | [diff] [blame] | 271 | subprocess.check_call(sign_bin) |
| 272 | if in_hex: |
| 273 | out_hex = args.shex or str(b / 'zephyr' / 'zephyr.signed.hex') |
| 274 | sign_hex = sign_base + [in_hex, out_hex] |
Martí Bolívar | 9b67367 | 2020-08-19 15:29:37 -0700 | [diff] [blame] | 275 | if not args.quiet: |
| 276 | log.inf(f'unsigned hex: {in_hex}') |
| 277 | log.inf(f'signed hex: {out_hex}') |
| 278 | log.dbg(quote_sh_list(sign_hex)) |
Martí Bolívar | d8f459a | 2019-11-12 14:55:03 -0800 | [diff] [blame] | 279 | subprocess.check_call(sign_hex) |
| 280 | |
| 281 | @staticmethod |
| 282 | def find_imgtool(command, args): |
Marti Bolivar | 4a0f1f2 | 2019-02-14 14:49:03 -0700 | [diff] [blame] | 283 | if args.tool_path: |
Martí Bolívar | 698db69 | 2021-02-02 12:20:47 -0800 | [diff] [blame] | 284 | imgtool = args.tool_path |
| 285 | if not os.path.isfile(imgtool): |
| 286 | log.die(f'--tool-path {imgtool}: no such file') |
| 287 | else: |
| 288 | imgtool = shutil.which('imgtool') or shutil.which('imgtool.py') |
| 289 | if not imgtool: |
| 290 | log.die('imgtool not found; either install it', |
| 291 | '(e.g. "pip3 install imgtool") or provide --tool-path') |
Martí Bolívar | b4903d4 | 2021-02-02 12:05:58 -0800 | [diff] [blame] | 292 | |
Martí Bolívar | 698db69 | 2021-02-02 12:20:47 -0800 | [diff] [blame] | 293 | if platform.system() == 'Windows' and imgtool.endswith('.py'): |
| 294 | # Windows users may not be able to run .py files |
| 295 | # as executables in subprocesses, regardless of |
| 296 | # what the mode says. Always run imgtool as |
| 297 | # 'python path/to/imgtool.py' instead of |
| 298 | # 'path/to/imgtool.py' in these cases. |
| 299 | # https://github.com/zephyrproject-rtos/zephyr/issues/31876 |
| 300 | return [sys.executable, imgtool] |
| 301 | |
Martí Bolívar | b4903d4 | 2021-02-02 12:05:58 -0800 | [diff] [blame] | 302 | return [imgtool] |
Marti Bolivar | 2f839da | 2019-06-12 11:25:23 -0600 | [diff] [blame] | 303 | |
Ulf Magnusson | bb63416 | 2019-09-04 16:28:50 +0200 | [diff] [blame] | 304 | @staticmethod |
Martí Bolívar | 250b213 | 2021-04-28 16:20:38 -0700 | [diff] [blame] | 305 | def get_cfg(command, build_conf, item): |
Marti Bolivar | 4a0f1f2 | 2019-02-14 14:49:03 -0700 | [diff] [blame] | 306 | try: |
Martí Bolívar | 250b213 | 2021-04-28 16:20:38 -0700 | [diff] [blame] | 307 | return build_conf[item] |
Marti Bolivar | 4a0f1f2 | 2019-02-14 14:49:03 -0700 | [diff] [blame] | 308 | except KeyError: |
| 309 | command.check_force( |
Martí Bolívar | d8f459a | 2019-11-12 14:55:03 -0800 | [diff] [blame] | 310 | False, "build .config is missing a {} value".format(item)) |
Marti Bolivar | 4a0f1f2 | 2019-02-14 14:49:03 -0700 | [diff] [blame] | 311 | return None |
Martí Bolívar | d8f459a | 2019-11-12 14:55:03 -0800 | [diff] [blame] | 312 | |
| 313 | @staticmethod |
Martí Bolívar | 9b67367 | 2020-08-19 15:29:37 -0700 | [diff] [blame] | 314 | def edt_flash_node(b, quiet=False): |
Martí Bolívar | d8f459a | 2019-11-12 14:55:03 -0800 | [diff] [blame] | 315 | # Get the EDT Node corresponding to the zephyr,flash chosen DT |
Martí Bolívar | 9c92baa | 2020-07-08 14:43:07 -0700 | [diff] [blame] | 316 | # node; 'b' is the build directory as a pathlib object. |
Martí Bolívar | d8f459a | 2019-11-12 14:55:03 -0800 | [diff] [blame] | 317 | |
| 318 | # Ensure the build directory has a compiled DTS file |
| 319 | # where we expect it to be. |
Martí Bolívar | 9c92baa | 2020-07-08 14:43:07 -0700 | [diff] [blame] | 320 | dts = b / 'zephyr' / 'zephyr.dts' |
Martí Bolívar | 9b67367 | 2020-08-19 15:29:37 -0700 | [diff] [blame] | 321 | if not quiet: |
| 322 | log.dbg('DTS file:', dts, level=log.VERBOSE_VERY) |
Martí Bolívar | 9c92baa | 2020-07-08 14:43:07 -0700 | [diff] [blame] | 323 | edt_pickle = b / 'zephyr' / 'edt.pickle' |
| 324 | if not edt_pickle.is_file(): |
| 325 | log.die("can't load devicetree; expected to find:", edt_pickle) |
Martí Bolívar | d8f459a | 2019-11-12 14:55:03 -0800 | [diff] [blame] | 326 | |
Martí Bolívar | 9c92baa | 2020-07-08 14:43:07 -0700 | [diff] [blame] | 327 | # Load the devicetree. |
| 328 | with open(edt_pickle, 'rb') as f: |
| 329 | edt = pickle.load(f) |
Martí Bolívar | d8f459a | 2019-11-12 14:55:03 -0800 | [diff] [blame] | 330 | |
| 331 | # By convention, the zephyr,flash chosen node contains the |
| 332 | # partition information about the zephyr image to sign. |
| 333 | flash = edt.chosen_node('zephyr,flash') |
| 334 | if not flash: |
| 335 | log.die('devicetree has no chosen zephyr,flash node;', |
| 336 | "can't infer flash write block or image-0 slot sizes") |
| 337 | |
| 338 | return flash |
| 339 | |
| 340 | @staticmethod |
| 341 | def edt_flash_params(flash): |
Fabio Utzig | 716ab47 | 2020-08-17 08:59:36 -0300 | [diff] [blame] | 342 | # Get the flash device's write alignment and offset from the |
| 343 | # image-0 partition and the size from image-1 partition, out of the |
| 344 | # build directory's devicetree. image-1 partition size is used, |
| 345 | # when available, because in swap-move mode it can be one sector |
| 346 | # smaller. When not available, fallback to image-0 (single image dfu). |
Martí Bolívar | d8f459a | 2019-11-12 14:55:03 -0800 | [diff] [blame] | 347 | |
| 348 | # The node must have a "partitions" child node, which in turn |
Fabio Utzig | 716ab47 | 2020-08-17 08:59:36 -0300 | [diff] [blame] | 349 | # must have child node labeled "image-0" and may have a child node |
| 350 | # named "image-1". By convention, the slots for consumption by |
| 351 | # imgtool are linked into these partitions. |
Martí Bolívar | d8f459a | 2019-11-12 14:55:03 -0800 | [diff] [blame] | 352 | if 'partitions' not in flash.children: |
| 353 | log.die("DT zephyr,flash chosen node has no partitions,", |
Fabio Utzig | 716ab47 | 2020-08-17 08:59:36 -0300 | [diff] [blame] | 354 | "can't find partitions for MCUboot slots") |
Martí Bolívar | d8f459a | 2019-11-12 14:55:03 -0800 | [diff] [blame] | 355 | |
Fabio Utzig | 716ab47 | 2020-08-17 08:59:36 -0300 | [diff] [blame] | 356 | partitions = flash.children['partitions'] |
| 357 | images = { |
| 358 | node.label: node for node in partitions.children.values() |
| 359 | if node.label in set(['image-0', 'image-1']) |
| 360 | } |
| 361 | |
| 362 | if 'image-0' not in images: |
| 363 | log.die("DT zephyr,flash chosen node has no image-0 partition,", |
| 364 | "can't determine its address") |
Martí Bolívar | d8f459a | 2019-11-12 14:55:03 -0800 | [diff] [blame] | 365 | |
| 366 | # Die on missing or zero alignment or slot_size. |
| 367 | if "write-block-size" not in flash.props: |
| 368 | log.die('DT zephyr,flash node has no write-block-size;', |
| 369 | "can't determine imgtool write alignment") |
| 370 | align = flash.props['write-block-size'].val |
| 371 | if align == 0: |
| 372 | log.die('expected nonzero flash alignment, but got ' |
| 373 | 'DT flash device write-block-size {}'.format(align)) |
Martí Bolívar | d8f459a | 2019-11-12 14:55:03 -0800 | [diff] [blame] | 374 | |
Fabio Utzig | 716ab47 | 2020-08-17 08:59:36 -0300 | [diff] [blame] | 375 | # The partitions node, and its subnode, must provide |
| 376 | # the size of image-1 or image-0 partition via the regs property. |
| 377 | image_key = 'image-1' if 'image-1' in images else 'image-0' |
| 378 | if not images[image_key].regs: |
| 379 | log.die(f'{image_key} flash partition has no regs property;', |
| 380 | "can't determine size of image") |
| 381 | |
| 382 | # always use addr of image-0, which is where images are run |
| 383 | addr = images['image-0'].regs[0].addr |
| 384 | |
| 385 | size = images[image_key].regs[0].size |
| 386 | if size == 0: |
| 387 | log.die('expected nonzero slot size for {}'.format(image_key)) |
| 388 | |
| 389 | return (align, addr, size) |
Andrei Emeltchenko | d44d986 | 2019-11-19 12:35:49 +0200 | [diff] [blame] | 390 | |
| 391 | class RimageSigner(Signer): |
| 392 | |
Martí Bolívar | 250b213 | 2021-04-28 16:20:38 -0700 | [diff] [blame] | 393 | def sign(self, command, build_dir, build_conf, formats): |
Andrei Emeltchenko | d44d986 | 2019-11-19 12:35:49 +0200 | [diff] [blame] | 394 | args = command.args |
| 395 | |
| 396 | if args.tool_path: |
| 397 | command.check_force(shutil.which(args.tool_path), |
| 398 | '--tool-path {}: not an executable'. |
| 399 | format(args.tool_path)) |
| 400 | tool_path = args.tool_path |
| 401 | else: |
| 402 | tool_path = shutil.which('rimage') |
| 403 | if not tool_path: |
| 404 | log.die('rimage not found; either install it', |
| 405 | 'or provide --tool-path') |
| 406 | |
Marc Herbert | 2fdc551 | 2022-02-19 07:29:03 +0000 | [diff] [blame] | 407 | #### -c sof/rimage/config/signing_schema.toml #### |
| 408 | |
Andrei Emeltchenko | d44d986 | 2019-11-19 12:35:49 +0200 | [diff] [blame] | 409 | b = pathlib.Path(build_dir) |
| 410 | cache = CMakeCache.from_build_dir(build_dir) |
| 411 | |
Marc Herbert | 2fdc551 | 2022-02-19 07:29:03 +0000 | [diff] [blame] | 412 | # warning: RIMAGE_TARGET is a duplicate of CONFIG_RIMAGE_SIGNING_SCHEMA |
Maureen Helm | 525fa76 | 2021-11-17 08:05:25 -0600 | [diff] [blame] | 413 | target = cache.get('RIMAGE_TARGET') |
| 414 | if not target: |
| 415 | log.die('rimage target not defined') |
| 416 | |
Marc Herbert | 2fdc551 | 2022-02-19 07:29:03 +0000 | [diff] [blame] | 417 | cmake_toml = target + '.toml' |
Andrei Emeltchenko | d44d986 | 2019-11-19 12:35:49 +0200 | [diff] [blame] | 418 | |
Martí Bolívar | 9b67367 | 2020-08-19 15:29:37 -0700 | [diff] [blame] | 419 | if not args.quiet: |
| 420 | log.inf('Signing with tool {}'.format(tool_path)) |
Andrei Emeltchenko | d44d986 | 2019-11-19 12:35:49 +0200 | [diff] [blame] | 421 | |
Iuliana Prodan | 602e675 | 2021-10-01 19:10:57 +0300 | [diff] [blame] | 422 | if target in ('imx8', 'imx8m'): |
Iuliana Prodan | 8230421 | 2021-06-30 03:15:21 +0300 | [diff] [blame] | 423 | kernel = str(b / 'zephyr' / 'zephyr.elf') |
| 424 | out_bin = str(b / 'zephyr' / 'zephyr.ri') |
| 425 | out_xman = str(b / 'zephyr' / 'zephyr.ri.xman') |
| 426 | out_tmp = str(b / 'zephyr' / 'zephyr.rix') |
| 427 | else: |
Andy Ross | 2906d1a | 2021-12-10 15:37:17 -0800 | [diff] [blame] | 428 | bootloader = str(b / 'zephyr' / 'boot.mod') |
| 429 | kernel = str(b / 'zephyr' / 'main.mod') |
Iuliana Prodan | 8230421 | 2021-06-30 03:15:21 +0300 | [diff] [blame] | 430 | out_bin = str(b / 'zephyr' / 'zephyr.ri') |
| 431 | out_xman = str(b / 'zephyr' / 'zephyr.ri.xman') |
| 432 | out_tmp = str(b / 'zephyr' / 'zephyr.rix') |
Marc Herbert | 2fdc551 | 2022-02-19 07:29:03 +0000 | [diff] [blame] | 433 | |
Anas Nashif | fee9af2 | 2021-01-25 13:59:57 -0500 | [diff] [blame] | 434 | conf_path_cmd = [] |
Marc Herbert | 2fdc551 | 2022-02-19 07:29:03 +0000 | [diff] [blame] | 435 | |
| 436 | if '-c' in args.tool_args: |
| 437 | # Precedence to the -- rimage command line |
| 438 | conf_path_cmd = [] |
| 439 | if args.tool_data: |
| 440 | log.wrn('--tool-data ' + args.tool_data + ' ignored, overridden by -c') |
| 441 | # For logging only |
| 442 | conf_path = args.tool_args[args.tool_args.index('-c') + 1] |
Anas Nashif | fee9af2 | 2021-01-25 13:59:57 -0500 | [diff] [blame] | 443 | elif args.tool_data: |
| 444 | conf_dir = pathlib.Path(args.tool_data) |
Marc Herbert | 2fdc551 | 2022-02-19 07:29:03 +0000 | [diff] [blame] | 445 | conf_path = str(conf_dir / cmake_toml) |
| 446 | conf_path_cmd = ['-c', conf_path] |
| 447 | elif cache.get('RIMAGE_CONFIG_PATH'): |
| 448 | rimage_conf = pathlib.Path(cache['RIMAGE_CONFIG_PATH']) |
| 449 | conf_path = str(rimage_conf / cmake_toml) |
Anas Nashif | fee9af2 | 2021-01-25 13:59:57 -0500 | [diff] [blame] | 450 | conf_path_cmd = ['-c', conf_path] |
| 451 | else: |
Marc Herbert | 2fdc551 | 2022-02-19 07:29:03 +0000 | [diff] [blame] | 452 | log.die('-c configuration not found') |
| 453 | |
| 454 | log.inf('Signing for SOC target ' + target + ' using ' + conf_path) |
| 455 | |
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 | |
Daniel Leung | 10eab23 | 2021-09-21 13:37:33 -0700 | [diff] [blame] | 462 | if no_manifest: |
| 463 | extra_ri_args = ['-i', '3'] |
| 464 | else: |
| 465 | extra_ri_args = ['-i', '3', '-e'] |
| 466 | |
Iuliana Prodan | 602e675 | 2021-10-01 19:10:57 +0300 | [diff] [blame] | 467 | components = [ ] if (target in ('imx8', 'imx8m')) else [ bootloader ] |
| 468 | components += [ kernel ] |
| 469 | sign_base = ([tool_path] + args.tool_args + |
| 470 | ['-o', out_bin] + conf_path_cmd + extra_ri_args + |
| 471 | components) |
Andrei Emeltchenko | d44d986 | 2019-11-19 12:35:49 +0200 | [diff] [blame] | 472 | |
Martí Bolívar | 9b67367 | 2020-08-19 15:29:37 -0700 | [diff] [blame] | 473 | if not args.quiet: |
| 474 | log.inf(quote_sh_list(sign_base)) |
Andrei Emeltchenko | d44d986 | 2019-11-19 12:35:49 +0200 | [diff] [blame] | 475 | subprocess.check_call(sign_base) |
Guennadi Liakhovetski | 3de40b4 | 2020-10-27 12:48:36 +0100 | [diff] [blame] | 476 | |
Jian Kang | 8c9b06a | 2021-02-03 17:05:24 +0800 | [diff] [blame] | 477 | if no_manifest: |
| 478 | filenames = [out_bin] |
| 479 | else: |
| 480 | filenames = [out_xman, out_bin] |
Marc Herbert | 2fdc551 | 2022-02-19 07:29:03 +0000 | [diff] [blame] | 481 | if not args.quiet: |
| 482 | log.inf('Prefixing ' + out_bin + ' with manifest ' + out_xman) |
Guennadi Liakhovetski | 3de40b4 | 2020-10-27 12:48:36 +0100 | [diff] [blame] | 483 | with open(out_tmp, 'wb') as outfile: |
| 484 | for fname in filenames: |
| 485 | with open(fname, 'rb') as infile: |
| 486 | outfile.write(infile.read()) |
| 487 | |
| 488 | os.remove(out_bin) |
| 489 | os.rename(out_tmp, out_bin) |