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