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