• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python3
2#
3# Copyright 2015, The Android Open Source Project
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.
16"""Creates the boot image."""
17from argparse import (ArgumentParser, ArgumentTypeError,
18                      FileType, RawDescriptionHelpFormatter)
19from hashlib import sha1
20from os import fstat
21from struct import pack
22import array
23import collections
24import os
25import re
26import subprocess
27import tempfile
28# Constant and structure definition is in
29# system/tools/mkbootimg/include/bootimg/bootimg.h
30BOOT_MAGIC = 'ANDROID!'
31BOOT_MAGIC_SIZE = 8
32BOOT_NAME_SIZE = 16
33BOOT_ARGS_SIZE = 512
34BOOT_EXTRA_ARGS_SIZE = 1024
35BOOT_IMAGE_HEADER_V1_SIZE = 1648
36BOOT_IMAGE_HEADER_V2_SIZE = 1660
37BOOT_IMAGE_HEADER_V3_SIZE = 1580
38BOOT_IMAGE_HEADER_V3_PAGESIZE = 4096
39BOOT_IMAGE_HEADER_V4_SIZE = 1584
40BOOT_IMAGE_V4_SIGNATURE_SIZE = 4096
41VENDOR_BOOT_MAGIC = 'VNDRBOOT'
42VENDOR_BOOT_MAGIC_SIZE = 8
43VENDOR_BOOT_NAME_SIZE = BOOT_NAME_SIZE
44VENDOR_BOOT_ARGS_SIZE = 2048
45VENDOR_BOOT_IMAGE_HEADER_V3_SIZE = 2112
46VENDOR_BOOT_IMAGE_HEADER_V4_SIZE = 2128
47VENDOR_RAMDISK_TYPE_NONE = 0
48VENDOR_RAMDISK_TYPE_PLATFORM = 1
49VENDOR_RAMDISK_TYPE_RECOVERY = 2
50VENDOR_RAMDISK_TYPE_DLKM = 3
51VENDOR_RAMDISK_NAME_SIZE = 32
52VENDOR_RAMDISK_TABLE_ENTRY_BOARD_ID_SIZE = 16
53VENDOR_RAMDISK_TABLE_ENTRY_V4_SIZE = 108
54# Names with special meaning, mustn't be specified in --ramdisk_name.
55VENDOR_RAMDISK_NAME_BLOCKLIST = {b'default'}
56PARSER_ARGUMENT_VENDOR_RAMDISK_FRAGMENT = '--vendor_ramdisk_fragment'
57def filesize(f):
58    if f is None:
59        return 0
60    try:
61        return fstat(f.fileno()).st_size
62    except OSError:
63        return 0
64def update_sha(sha, f):
65    if f:
66        sha.update(f.read())
67        f.seek(0)
68        sha.update(pack('I', filesize(f)))
69    else:
70        sha.update(pack('I', 0))
71def pad_file(f, padding):
72    pad = (padding - (f.tell() & (padding - 1))) & (padding - 1)
73    f.write(pack(str(pad) + 'x'))
74def get_number_of_pages(image_size, page_size):
75    """calculates the number of pages required for the image"""
76    return (image_size + page_size - 1) // page_size
77def get_recovery_dtbo_offset(args):
78    """calculates the offset of recovery_dtbo image in the boot image"""
79    num_header_pages = 1 # header occupies a page
80    num_kernel_pages = get_number_of_pages(filesize(args.kernel), args.pagesize)
81    num_ramdisk_pages = get_number_of_pages(filesize(args.ramdisk),
82                                            args.pagesize)
83    num_second_pages = get_number_of_pages(filesize(args.second), args.pagesize)
84    dtbo_offset = args.pagesize * (num_header_pages + num_kernel_pages +
85                                   num_ramdisk_pages + num_second_pages)
86    return dtbo_offset
87def write_header_v3_and_above(args):
88    if args.header_version > 3:
89        boot_header_size = BOOT_IMAGE_HEADER_V4_SIZE
90    else:
91        boot_header_size = BOOT_IMAGE_HEADER_V3_SIZE
92    args.output.write(pack(f'{BOOT_MAGIC_SIZE}s', BOOT_MAGIC.encode()))
93    # kernel size in bytes
94    args.output.write(pack('I', filesize(args.kernel)))
95    # ramdisk size in bytes
96    args.output.write(pack('I', filesize(args.ramdisk)))
97    # os version and patch level
98    args.output.write(pack('I', (args.os_version << 11) | args.os_patch_level))
99    args.output.write(pack('I', boot_header_size))
100    # reserved
101    args.output.write(pack('4I', 0, 0, 0, 0))
102    # version of boot image header
103    args.output.write(pack('I', args.header_version))
104    args.output.write(pack(f'{BOOT_ARGS_SIZE + BOOT_EXTRA_ARGS_SIZE}s',
105                           args.cmdline))
106    if args.header_version >= 4:
107        # The signature used to verify boot image v4.
108        args.output.write(pack('I', BOOT_IMAGE_V4_SIGNATURE_SIZE))
109    pad_file(args.output, BOOT_IMAGE_HEADER_V3_PAGESIZE)
110def write_vendor_boot_header(args):
111    if filesize(args.dtb) == 0:
112        raise ValueError('DTB image must not be empty.')
113    if args.header_version > 3:
114        vendor_ramdisk_size = args.vendor_ramdisk_total_size
115        vendor_boot_header_size = VENDOR_BOOT_IMAGE_HEADER_V4_SIZE
116    else:
117        vendor_ramdisk_size = filesize(args.vendor_ramdisk)
118        vendor_boot_header_size = VENDOR_BOOT_IMAGE_HEADER_V3_SIZE
119    args.vendor_boot.write(pack(f'{VENDOR_BOOT_MAGIC_SIZE}s',
120                                VENDOR_BOOT_MAGIC.encode()))
121    # version of boot image header
122    args.vendor_boot.write(pack('I', args.header_version))
123    # flash page size
124    args.vendor_boot.write(pack('I', args.pagesize))
125    # kernel physical load address
126    args.vendor_boot.write(pack('I', args.base + args.kernel_offset))
127    # ramdisk physical load address
128    args.vendor_boot.write(pack('I', args.base + args.ramdisk_offset))
129    # ramdisk size in bytes
130    args.vendor_boot.write(pack('I', vendor_ramdisk_size))
131    args.vendor_boot.write(pack(f'{VENDOR_BOOT_ARGS_SIZE}s',
132                                args.vendor_cmdline))
133    # kernel tags physical load address
134    args.vendor_boot.write(pack('I', args.base + args.tags_offset))
135    # asciiz product name
136    args.vendor_boot.write(pack(f'{VENDOR_BOOT_NAME_SIZE}s', args.board))
137    # header size in bytes
138    args.vendor_boot.write(pack('I', vendor_boot_header_size))
139    # dtb size in bytes
140    args.vendor_boot.write(pack('I', filesize(args.dtb)))
141    # dtb physical load address
142    args.vendor_boot.write(pack('Q', args.base + args.dtb_offset))
143    if args.header_version > 3:
144        vendor_ramdisk_table_size = (args.vendor_ramdisk_table_entry_num *
145                                     VENDOR_RAMDISK_TABLE_ENTRY_V4_SIZE)
146        # vendor ramdisk table size in bytes
147        args.vendor_boot.write(pack('I', vendor_ramdisk_table_size))
148        # number of vendor ramdisk table entries
149        args.vendor_boot.write(pack('I', args.vendor_ramdisk_table_entry_num))
150        # vendor ramdisk table entry size in bytes
151        args.vendor_boot.write(pack('I', VENDOR_RAMDISK_TABLE_ENTRY_V4_SIZE))
152        # bootconfig section size in bytes
153        args.vendor_boot.write(pack('I', filesize(args.vendor_bootconfig)))
154    pad_file(args.vendor_boot, args.pagesize)
155def write_header(args):
156    if args.header_version > 4:
157        raise ValueError(
158            f'Boot header version {args.header_version} not supported')
159    if args.header_version in {3, 4}:
160        return write_header_v3_and_above(args)
161    ramdisk_load_address = ((args.base + args.ramdisk_offset)
162                            if filesize(args.ramdisk) > 0 else 0)
163    second_load_address = ((args.base + args.second_offset)
164                           if filesize(args.second) > 0 else 0)
165    args.output.write(pack(f'{BOOT_MAGIC_SIZE}s', BOOT_MAGIC.encode()))
166    # kernel size in bytes
167    args.output.write(pack('I', filesize(args.kernel)))
168    # kernel physical load address
169    args.output.write(pack('I', args.base + args.kernel_offset))
170    # ramdisk size in bytes
171    args.output.write(pack('I', filesize(args.ramdisk)))
172    # ramdisk physical load address
173    args.output.write(pack('I', ramdisk_load_address))
174    # second bootloader size in bytes
175    args.output.write(pack('I', filesize(args.second)))
176    # second bootloader physical load address
177    args.output.write(pack('I', second_load_address))
178    # kernel tags physical load address
179    args.output.write(pack('I', args.base + args.tags_offset))
180    # flash page size
181    args.output.write(pack('I', args.pagesize))
182    # version of boot image header
183    args.output.write(pack('I', args.header_version))
184    # os version and patch level
185    args.output.write(pack('I', (args.os_version << 11) | args.os_patch_level))
186    # asciiz product name
187    args.output.write(pack(f'{BOOT_NAME_SIZE}s', args.board))
188    args.output.write(pack(f'{BOOT_ARGS_SIZE}s', args.cmdline))
189    sha = sha1()
190    update_sha(sha, args.kernel)
191    update_sha(sha, args.ramdisk)
192    update_sha(sha, args.second)
193    if args.header_version > 0:
194        update_sha(sha, args.recovery_dtbo)
195    if args.header_version > 1:
196        update_sha(sha, args.dtb)
197    img_id = pack('32s', sha.digest())
198    args.output.write(img_id)
199    args.output.write(pack(f'{BOOT_EXTRA_ARGS_SIZE}s', args.extra_cmdline))
200    if args.header_version > 0:
201        if args.recovery_dtbo:
202            # recovery dtbo size in bytes
203            args.output.write(pack('I', filesize(args.recovery_dtbo)))
204            # recovert dtbo offset in the boot image
205            args.output.write(pack('Q', get_recovery_dtbo_offset(args)))
206        else:
207            # Set to zero if no recovery dtbo
208            args.output.write(pack('I', 0))
209            args.output.write(pack('Q', 0))
210    # Populate boot image header size for header versions 1 and 2.
211    if args.header_version == 1:
212        args.output.write(pack('I', BOOT_IMAGE_HEADER_V1_SIZE))
213    elif args.header_version == 2:
214        args.output.write(pack('I', BOOT_IMAGE_HEADER_V2_SIZE))
215    if args.header_version > 1:
216        if filesize(args.dtb) == 0:
217            raise ValueError('DTB image must not be empty.')
218        # dtb size in bytes
219        args.output.write(pack('I', filesize(args.dtb)))
220        # dtb physical load address
221        args.output.write(pack('Q', args.base + args.dtb_offset))
222    pad_file(args.output, args.pagesize)
223    return img_id
224class AsciizBytes:
225    """Parses a string and encodes it as an asciiz bytes object.
226    >>> AsciizBytes(bufsize=4)('foo')
227    b'foo\\x00'
228    >>> AsciizBytes(bufsize=4)('foob')
229    Traceback (most recent call last):
230        ...
231    argparse.ArgumentTypeError: Encoded asciiz length exceeded: max 4, got 5
232    """
233    def __init__(self, bufsize):
234        self.bufsize = bufsize
235    def __call__(self, arg):
236        arg_bytes = arg.encode() + b'\x00'
237        if len(arg_bytes) > self.bufsize:
238            raise ArgumentTypeError(
239                'Encoded asciiz length exceeded: '
240                f'max {self.bufsize}, got {len(arg_bytes)}')
241        return arg_bytes
242class VendorRamdiskTableBuilder:
243    """Vendor ramdisk table builder.
244    Attributes:
245        entries: A list of VendorRamdiskTableEntry namedtuple.
246        ramdisk_total_size: Total size in bytes of all ramdisks in the table.
247    """
248    VendorRamdiskTableEntry = collections.namedtuple(  # pylint: disable=invalid-name
249        'VendorRamdiskTableEntry',
250        ['ramdisk_path', 'ramdisk_size', 'ramdisk_offset', 'ramdisk_type',
251         'ramdisk_name', 'board_id'])
252    def __init__(self):
253        self.entries = []
254        self.ramdisk_total_size = 0
255        self.ramdisk_names = set()
256    def add_entry(self, ramdisk_path, ramdisk_type, ramdisk_name, board_id):
257        # Strip any trailing null for simple comparison.
258        stripped_ramdisk_name = ramdisk_name.rstrip(b'\x00')
259        if stripped_ramdisk_name in VENDOR_RAMDISK_NAME_BLOCKLIST:
260            raise ValueError(
261                f'Banned vendor ramdisk name: {stripped_ramdisk_name}')
262        if stripped_ramdisk_name in self.ramdisk_names:
263            raise ValueError(
264                f'Duplicated vendor ramdisk name: {stripped_ramdisk_name}')
265        self.ramdisk_names.add(stripped_ramdisk_name)
266        if board_id is None:
267            board_id = array.array(
268                'I', [0] * VENDOR_RAMDISK_TABLE_ENTRY_BOARD_ID_SIZE)
269        else:
270            board_id = array.array('I', board_id)
271        if len(board_id) != VENDOR_RAMDISK_TABLE_ENTRY_BOARD_ID_SIZE:
272            raise ValueError('board_id size must be '
273                             f'{VENDOR_RAMDISK_TABLE_ENTRY_BOARD_ID_SIZE}')
274        with open(ramdisk_path, 'rb') as f:
275            ramdisk_size = filesize(f)
276        self.entries.append(self.VendorRamdiskTableEntry(
277            ramdisk_path, ramdisk_size, self.ramdisk_total_size, ramdisk_type,
278            ramdisk_name, board_id))
279        self.ramdisk_total_size += ramdisk_size
280    def write_ramdisks_padded(self, fout, alignment):
281        for entry in self.entries:
282            with open(entry.ramdisk_path, 'rb') as f:
283                fout.write(f.read())
284        pad_file(fout, alignment)
285    def write_entries_padded(self, fout, alignment):
286        for entry in self.entries:
287            fout.write(pack('I', entry.ramdisk_size))
288            fout.write(pack('I', entry.ramdisk_offset))
289            fout.write(pack('I', entry.ramdisk_type))
290            fout.write(pack(f'{VENDOR_RAMDISK_NAME_SIZE}s',
291                            entry.ramdisk_name))
292            fout.write(entry.board_id)
293        pad_file(fout, alignment)
294def write_padded_file(f_out, f_in, padding):
295    if f_in is None:
296        return
297    f_out.write(f_in.read())
298    pad_file(f_out, padding)
299def parse_int(x):
300    return int(x, 0)
301def parse_os_version(x):
302    match = re.search(r'^(\d{1,3})(?:\.(\d{1,3})(?:\.(\d{1,3}))?)?', x)
303    if match:
304        a = int(match.group(1))
305        b = c = 0
306        if match.lastindex >= 2:
307            b = int(match.group(2))
308        if match.lastindex == 3:
309            c = int(match.group(3))
310        # 7 bits allocated for each field
311        assert a < 128
312        assert b < 128
313        assert c < 128
314        return (a << 14) | (b << 7) | c
315    return 0
316def parse_os_patch_level(x):
317    match = re.search(r'^(\d{4})-(\d{2})(?:-(\d{2}))?', x)
318    if match:
319        y = int(match.group(1)) - 2000
320        m = int(match.group(2))
321        # 7 bits allocated for the year, 4 bits for the month
322        assert 0 <= y < 128
323        assert 0 < m <= 12
324        return (y << 4) | m
325    return 0
326def parse_vendor_ramdisk_type(x):
327    type_dict = {
328        'none': VENDOR_RAMDISK_TYPE_NONE,
329        'platform': VENDOR_RAMDISK_TYPE_PLATFORM,
330        'recovery': VENDOR_RAMDISK_TYPE_RECOVERY,
331        'dlkm': VENDOR_RAMDISK_TYPE_DLKM,
332    }
333    if x.lower() in type_dict:
334        return type_dict[x.lower()]
335    return parse_int(x)
336def get_vendor_boot_v4_usage():
337    return """vendor boot version 4 arguments:
338  --ramdisk_type {none,platform,recovery,dlkm}
339                        specify the type of the ramdisk
340  --ramdisk_name NAME
341                        specify the name of the ramdisk
342  --board_id{0..15} NUMBER
343                        specify the value of the board_id vector, defaults to 0
344  --vendor_ramdisk_fragment VENDOR_RAMDISK_FILE
345                        path to the vendor ramdisk file
346  These options can be specified multiple times, where each vendor ramdisk
347  option group ends with a --vendor_ramdisk_fragment option.
348  Each option group appends an additional ramdisk to the vendor boot image.
349"""
350def parse_vendor_ramdisk_args(args, args_list):
351    """Parses vendor ramdisk specific arguments.
352    Args:
353        args: An argparse.Namespace object. Parsed results are stored into this
354            object.
355        args_list: A list of argument strings to be parsed.
356    Returns:
357        A list argument strings that are not parsed by this method.
358    """
359    parser = ArgumentParser(add_help=False)
360    parser.add_argument('--ramdisk_type', type=parse_vendor_ramdisk_type,
361                        default=VENDOR_RAMDISK_TYPE_NONE)
362    parser.add_argument('--ramdisk_name',
363                        type=AsciizBytes(bufsize=VENDOR_RAMDISK_NAME_SIZE),
364                        required=True)
365    for i in range(VENDOR_RAMDISK_TABLE_ENTRY_BOARD_ID_SIZE):
366        parser.add_argument(f'--board_id{i}', type=parse_int, default=0)
367    parser.add_argument(PARSER_ARGUMENT_VENDOR_RAMDISK_FRAGMENT, required=True)
368    unknown_args = []
369    vendor_ramdisk_table_builder = VendorRamdiskTableBuilder()
370    if args.vendor_ramdisk is not None:
371        vendor_ramdisk_table_builder.add_entry(
372            args.vendor_ramdisk.name, VENDOR_RAMDISK_TYPE_PLATFORM, b'', None)
373    while PARSER_ARGUMENT_VENDOR_RAMDISK_FRAGMENT in args_list:
374        idx = args_list.index(PARSER_ARGUMENT_VENDOR_RAMDISK_FRAGMENT) + 2
375        vendor_ramdisk_args = args_list[:idx]
376        args_list = args_list[idx:]
377        ramdisk_args, extra_args = parser.parse_known_args(vendor_ramdisk_args)
378        ramdisk_args_dict = vars(ramdisk_args)
379        unknown_args.extend(extra_args)
380        ramdisk_path = ramdisk_args.vendor_ramdisk_fragment
381        ramdisk_type = ramdisk_args.ramdisk_type
382        ramdisk_name = ramdisk_args.ramdisk_name
383        board_id = [ramdisk_args_dict[f'board_id{i}']
384                    for i in range(VENDOR_RAMDISK_TABLE_ENTRY_BOARD_ID_SIZE)]
385        vendor_ramdisk_table_builder.add_entry(ramdisk_path, ramdisk_type,
386                                               ramdisk_name, board_id)
387    if len(args_list) > 0:
388        unknown_args.extend(args_list)
389    args.vendor_ramdisk_total_size = (vendor_ramdisk_table_builder
390                                      .ramdisk_total_size)
391    args.vendor_ramdisk_table_entry_num = len(vendor_ramdisk_table_builder
392                                              .entries)
393    args.vendor_ramdisk_table_builder = vendor_ramdisk_table_builder
394    return unknown_args
395def parse_cmdline():
396    version_parser = ArgumentParser(add_help=False)
397    version_parser.add_argument('--header_version', type=parse_int, default=0)
398    if version_parser.parse_known_args()[0].header_version < 3:
399        # For boot header v0 to v2, the kernel commandline field is split into
400        # two fields, cmdline and extra_cmdline. Both fields are asciiz strings,
401        # so we minus one here to ensure the encoded string plus the
402        # null-terminator can fit in the buffer size.
403        cmdline_size = BOOT_ARGS_SIZE + BOOT_EXTRA_ARGS_SIZE - 1
404    else:
405        cmdline_size = BOOT_ARGS_SIZE + BOOT_EXTRA_ARGS_SIZE
406    parser = ArgumentParser(formatter_class=RawDescriptionHelpFormatter,
407                            epilog=get_vendor_boot_v4_usage())
408    parser.add_argument('--kernel', type=FileType('rb'),
409                        help='path to the kernel')
410    parser.add_argument('--ramdisk', type=FileType('rb'),
411                        help='path to the ramdisk')
412    parser.add_argument('--second', type=FileType('rb'),
413                        help='path to the second bootloader')
414    parser.add_argument('--dtb', type=FileType('rb'), help='path to the dtb')
415    dtbo_group = parser.add_mutually_exclusive_group()
416    dtbo_group.add_argument('--recovery_dtbo', type=FileType('rb'),
417                            help='path to the recovery DTBO')
418    dtbo_group.add_argument('--recovery_acpio', type=FileType('rb'),
419                            metavar='RECOVERY_ACPIO', dest='recovery_dtbo',
420                            help='path to the recovery ACPIO')
421    parser.add_argument('--cmdline', type=AsciizBytes(bufsize=cmdline_size),
422                        default='', help='kernel command line arguments')
423    parser.add_argument('--vendor_cmdline',
424                        type=AsciizBytes(bufsize=VENDOR_BOOT_ARGS_SIZE),
425                        default='',
426                        help='vendor boot kernel command line arguments')
427    parser.add_argument('--base', type=parse_int, default=0x10000000,
428                        help='base address')
429    parser.add_argument('--kernel_offset', type=parse_int, default=0x00008000,
430                        help='kernel offset')
431    parser.add_argument('--ramdisk_offset', type=parse_int, default=0x01000000,
432                        help='ramdisk offset')
433    parser.add_argument('--second_offset', type=parse_int, default=0x00f00000,
434                        help='second bootloader offset')
435    parser.add_argument('--dtb_offset', type=parse_int, default=0x01f00000,
436                        help='dtb offset')
437    parser.add_argument('--os_version', type=parse_os_version, default=0,
438                        help='operating system version')
439    parser.add_argument('--os_patch_level', type=parse_os_patch_level,
440                        default=0, help='operating system patch level')
441    parser.add_argument('--tags_offset', type=parse_int, default=0x00000100,
442                        help='tags offset')
443    parser.add_argument('--board', type=AsciizBytes(bufsize=BOOT_NAME_SIZE),
444                        default='', help='board name')
445    parser.add_argument('--pagesize', type=parse_int,
446                        choices=[2**i for i in range(11, 15)], default=2048,
447                        help='page size')
448    parser.add_argument('--id', action='store_true',
449                        help='print the image ID on standard output')
450    parser.add_argument('--header_version', type=parse_int, default=0,
451                        help='boot image header version')
452    parser.add_argument('-o', '--output', type=FileType('wb'),
453                        help='output file name')
454    parser.add_argument('--gki_signing_algorithm',
455                        help='GKI signing algorithm to use')
456    parser.add_argument('--gki_signing_key',
457                        help='path to RSA private key file')
458    parser.add_argument('--gki_signing_signature_args',
459                        help='other hash arguments passed to avbtool')
460    parser.add_argument('--gki_signing_avbtool_path',
461                        help='path to avbtool for boot signature generation')
462    parser.add_argument('--vendor_boot', type=FileType('wb'),
463                        help='vendor boot output file name')
464    parser.add_argument('--vendor_ramdisk', type=FileType('rb'),
465                        help='path to the vendor ramdisk')
466    parser.add_argument('--vendor_bootconfig', type=FileType('rb'),
467                        help='path to the vendor bootconfig file')
468    args, extra_args = parser.parse_known_args()
469    if args.vendor_boot is not None and args.header_version > 3:
470        extra_args = parse_vendor_ramdisk_args(args, extra_args)
471    if len(extra_args) > 0:
472        raise ValueError(f'Unrecognized arguments: {extra_args}')
473    if args.header_version < 3:
474        args.extra_cmdline = args.cmdline[BOOT_ARGS_SIZE-1:]
475        args.cmdline = args.cmdline[:BOOT_ARGS_SIZE-1] + b'\x00'
476        assert len(args.cmdline) <= BOOT_ARGS_SIZE
477        assert len(args.extra_cmdline) <= BOOT_EXTRA_ARGS_SIZE
478    return args
479def add_boot_image_signature(args, pagesize):
480    """Adds the boot image signature.
481    Note that the signature will only be verified in VTS to ensure a
482    generic boot.img is used. It will not be used by the device
483    bootloader at boot time. The bootloader should only verify
484    the boot vbmeta at the end of the boot partition (or in the top-level
485    vbmeta partition) via the Android Verified Boot process, when the
486    device boots.
487    """
488    args.output.flush()  # Flush the buffer for signature calculation.
489    # Appends zeros if the signing key is not specified.
490    if not args.gki_signing_key or not args.gki_signing_algorithm:
491        zeros = b'\x00' * BOOT_IMAGE_V4_SIGNATURE_SIZE
492        args.output.write(zeros)
493        pad_file(args.output, pagesize)
494        return
495    avbtool = 'avbtool'  # Used from otatools.zip or Android build env.
496    # We need to specify the path of avbtool in build/core/Makefile.
497    # Because avbtool is not guaranteed to be in $PATH there.
498    if args.gki_signing_avbtool_path:
499        avbtool = args.gki_signing_avbtool_path
500    # Need to specify a value of --partition_size for avbtool to work.
501    # We use 64 MB below, but avbtool will not resize the boot image to
502    # this size because --do_not_append_vbmeta_image is also specified.
503    avbtool_cmd = [
504        avbtool, 'add_hash_footer',
505        '--partition_name', 'boot',
506        '--partition_size', str(64 * 1024 * 1024),
507        '--image', args.output.name,
508        '--algorithm', args.gki_signing_algorithm,
509        '--key', args.gki_signing_key,
510        '--salt', 'd00df00d']  # TODO: use a hash of kernel/ramdisk as the salt.
511    # Additional arguments passed to avbtool.
512    if args.gki_signing_signature_args:
513        avbtool_cmd += args.gki_signing_signature_args.split()
514    # Outputs the signed vbmeta to a separate file, then append to boot.img
515    # as the boot signature.
516    with tempfile.TemporaryDirectory() as temp_out_dir:
517        boot_signature_output = os.path.join(temp_out_dir, 'boot_signature')
518        avbtool_cmd += ['--do_not_append_vbmeta_image',
519                        '--output_vbmeta_image', boot_signature_output]
520        subprocess.check_call(avbtool_cmd)
521        with open(boot_signature_output, 'rb') as boot_signature:
522            if filesize(boot_signature) > BOOT_IMAGE_V4_SIGNATURE_SIZE:
523                raise ValueError(
524                    f'boot sigature size is > {BOOT_IMAGE_V4_SIGNATURE_SIZE}')
525            write_padded_file(args.output, boot_signature, pagesize)
526def write_data(args, pagesize):
527    write_padded_file(args.output, args.kernel, pagesize)
528    write_padded_file(args.output, args.ramdisk, pagesize)
529    write_padded_file(args.output, args.second, pagesize)
530    if args.header_version > 0 and args.header_version < 3:
531        write_padded_file(args.output, args.recovery_dtbo, pagesize)
532    if args.header_version == 2:
533        write_padded_file(args.output, args.dtb, pagesize)
534    if args.header_version >= 4:
535        add_boot_image_signature(args, pagesize)
536def write_vendor_boot_data(args):
537    if args.header_version > 3:
538        builder = args.vendor_ramdisk_table_builder
539        builder.write_ramdisks_padded(args.vendor_boot, args.pagesize)
540        write_padded_file(args.vendor_boot, args.dtb, args.pagesize)
541        builder.write_entries_padded(args.vendor_boot, args.pagesize)
542        write_padded_file(args.vendor_boot, args.vendor_bootconfig,
543            args.pagesize)
544    else:
545        write_padded_file(args.vendor_boot, args.vendor_ramdisk, args.pagesize)
546        write_padded_file(args.vendor_boot, args.dtb, args.pagesize)
547def main():
548    args = parse_cmdline()
549    if args.vendor_boot is not None:
550        if args.header_version not in {3, 4}:
551            raise ValueError(
552                '--vendor_boot not compatible with given header version')
553        if args.header_version == 3 and args.vendor_ramdisk is None:
554            raise ValueError('--vendor_ramdisk missing or invalid')
555        write_vendor_boot_header(args)
556        write_vendor_boot_data(args)
557    if args.output is not None:
558        if args.second is not None and args.header_version > 2:
559            raise ValueError(
560                '--second not compatible with given header version')
561        img_id = write_header(args)
562        if args.header_version > 2:
563            write_data(args, BOOT_IMAGE_HEADER_V3_PAGESIZE)
564        else:
565            write_data(args, args.pagesize)
566        if args.id and img_id is not None:
567            print('0x' + ''.join(f'{octet:02x}' for octet in img_id))
568if __name__ == '__main__':
569    main()
570