rk: scripts: update mkbootimg/unpack_bootimg
AOSP f85a2fd5640d ("Revert "unpack_bootimg: Export 'vendor_ramdisk' for vendor_boot v4"")
Revert 7261bb083a97 ("Check DTB image size for boot image header version 2 and above")
which failed to repack image without dtb.
Include gki.generate_gki_certificate.
Signed-off-by: Tao Huang <huangtao@rock-chips.com>
Change-Id: Ibf9543fd4a10547f9d956b6ffc09834389fbb20f
This commit is contained in:
+507
-132
@@ -1,4 +1,5 @@
|
||||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
# Copyright 2015, The Android Open Source Project
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@@ -13,16 +14,81 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from __future__ import print_function
|
||||
"""Creates the boot image."""
|
||||
|
||||
from argparse import ArgumentParser, FileType, Action
|
||||
from argparse import (ArgumentParser, ArgumentTypeError,
|
||||
FileType, RawDescriptionHelpFormatter)
|
||||
from hashlib import sha1
|
||||
from os import fstat
|
||||
import re
|
||||
from struct import pack
|
||||
|
||||
import array
|
||||
import collections
|
||||
import os
|
||||
import re
|
||||
import tempfile
|
||||
|
||||
# from gki.generate_gki_certificate import generate_gki_certificate
|
||||
def generate_gki_certificate(image, avbtool, name, algorithm, key, salt,
|
||||
additional_avb_args, output):
|
||||
"""Shell out to avbtool to generate a GKI certificate."""
|
||||
|
||||
# Need to specify a value of --partition_size for avbtool to work.
|
||||
# We use 64 MB below, but avbtool will not resize the boot image to
|
||||
# this size because --do_not_append_vbmeta_image is also specified.
|
||||
avbtool_cmd = [
|
||||
avbtool, 'add_hash_footer',
|
||||
'--partition_name', name,
|
||||
'--partition_size', str(64 * 1024 * 1024),
|
||||
'--image', image,
|
||||
'--algorithm', algorithm,
|
||||
'--key', key,
|
||||
'--do_not_append_vbmeta_image',
|
||||
'--output_vbmeta_image', output,
|
||||
]
|
||||
|
||||
if salt is not None:
|
||||
avbtool_cmd += ['--salt', salt]
|
||||
|
||||
avbtool_cmd += additional_avb_args
|
||||
|
||||
subprocess.check_call(avbtool_cmd)
|
||||
|
||||
|
||||
# Constant and structure definition is in
|
||||
# system/tools/mkbootimg/include/bootimg/bootimg.h
|
||||
BOOT_MAGIC = 'ANDROID!'
|
||||
BOOT_MAGIC_SIZE = 8
|
||||
BOOT_NAME_SIZE = 16
|
||||
BOOT_ARGS_SIZE = 512
|
||||
BOOT_EXTRA_ARGS_SIZE = 1024
|
||||
BOOT_IMAGE_HEADER_V1_SIZE = 1648
|
||||
BOOT_IMAGE_HEADER_V2_SIZE = 1660
|
||||
BOOT_IMAGE_HEADER_V3_SIZE = 1580
|
||||
BOOT_IMAGE_HEADER_V3_PAGESIZE = 4096
|
||||
BOOT_IMAGE_HEADER_V4_SIZE = 1584
|
||||
BOOT_IMAGE_V4_SIGNATURE_SIZE = 4096
|
||||
|
||||
VENDOR_BOOT_MAGIC = 'VNDRBOOT'
|
||||
VENDOR_BOOT_MAGIC_SIZE = 8
|
||||
VENDOR_BOOT_NAME_SIZE = BOOT_NAME_SIZE
|
||||
VENDOR_BOOT_ARGS_SIZE = 2048
|
||||
VENDOR_BOOT_IMAGE_HEADER_V3_SIZE = 2112
|
||||
VENDOR_BOOT_IMAGE_HEADER_V4_SIZE = 2128
|
||||
|
||||
VENDOR_RAMDISK_TYPE_NONE = 0
|
||||
VENDOR_RAMDISK_TYPE_PLATFORM = 1
|
||||
VENDOR_RAMDISK_TYPE_RECOVERY = 2
|
||||
VENDOR_RAMDISK_TYPE_DLKM = 3
|
||||
VENDOR_RAMDISK_NAME_SIZE = 32
|
||||
VENDOR_RAMDISK_TABLE_ENTRY_BOARD_ID_SIZE = 16
|
||||
VENDOR_RAMDISK_TABLE_ENTRY_V4_SIZE = 108
|
||||
|
||||
# Names with special meaning, mustn't be specified in --ramdisk_name.
|
||||
VENDOR_RAMDISK_NAME_BLOCKLIST = {b'default'}
|
||||
|
||||
PARSER_ARGUMENT_VENDOR_RAMDISK_FRAGMENT = '--vendor_ramdisk_fragment'
|
||||
|
||||
|
||||
def filesize(f):
|
||||
if f is None:
|
||||
@@ -49,87 +115,141 @@ def pad_file(f, padding):
|
||||
|
||||
def get_number_of_pages(image_size, page_size):
|
||||
"""calculates the number of pages required for the image"""
|
||||
return (image_size + page_size - 1) / page_size
|
||||
return (image_size + page_size - 1) // page_size
|
||||
|
||||
|
||||
def get_recovery_dtbo_offset(args):
|
||||
"""calculates the offset of recovery_dtbo image in the boot image"""
|
||||
num_header_pages = 1 # header occupies a page
|
||||
num_kernel_pages = get_number_of_pages(filesize(args.kernel), args.pagesize)
|
||||
num_ramdisk_pages = get_number_of_pages(filesize(args.ramdisk), args.pagesize)
|
||||
num_ramdisk_pages = get_number_of_pages(filesize(args.ramdisk),
|
||||
args.pagesize)
|
||||
num_second_pages = get_number_of_pages(filesize(args.second), args.pagesize)
|
||||
dtbo_offset = args.pagesize * (num_header_pages + num_kernel_pages +
|
||||
num_ramdisk_pages + num_second_pages)
|
||||
return dtbo_offset
|
||||
|
||||
|
||||
def write_header_v3(args):
|
||||
BOOT_IMAGE_HEADER_V3_SIZE = 1580
|
||||
BOOT_MAGIC = 'ANDROID!'.encode()
|
||||
def should_add_legacy_gki_boot_signature(args):
|
||||
if args.gki_signing_key and args.gki_signing_algorithm:
|
||||
return True
|
||||
return False
|
||||
|
||||
args.output.write(pack('8s', BOOT_MAGIC))
|
||||
args.output.write(pack(
|
||||
'4I',
|
||||
filesize(args.kernel), # kernel size in bytes
|
||||
filesize(args.ramdisk), # ramdisk size in bytes
|
||||
(args.os_version << 11) | args.os_patch_level, # os version and patch level
|
||||
BOOT_IMAGE_HEADER_V3_SIZE))
|
||||
|
||||
args.output.write(pack('4I', 0, 0, 0, 0)) # reserved
|
||||
def write_header_v3_and_above(args):
|
||||
if args.header_version > 3:
|
||||
boot_header_size = BOOT_IMAGE_HEADER_V4_SIZE
|
||||
else:
|
||||
boot_header_size = BOOT_IMAGE_HEADER_V3_SIZE
|
||||
|
||||
args.output.write(pack('I', args.header_version)) # version of bootimage header
|
||||
args.output.write(pack('1536s', args.cmdline.encode()))
|
||||
args.output.write(pack(f'{BOOT_MAGIC_SIZE}s', BOOT_MAGIC.encode()))
|
||||
# kernel size in bytes
|
||||
args.output.write(pack('I', filesize(args.kernel)))
|
||||
# ramdisk size in bytes
|
||||
args.output.write(pack('I', filesize(args.ramdisk)))
|
||||
# os version and patch level
|
||||
args.output.write(pack('I', (args.os_version << 11) | args.os_patch_level))
|
||||
args.output.write(pack('I', boot_header_size))
|
||||
# reserved
|
||||
args.output.write(pack('4I', 0, 0, 0, 0))
|
||||
# version of boot image header
|
||||
args.output.write(pack('I', args.header_version))
|
||||
args.output.write(pack(f'{BOOT_ARGS_SIZE + BOOT_EXTRA_ARGS_SIZE}s',
|
||||
args.cmdline))
|
||||
if args.header_version >= 4:
|
||||
# The signature used to verify boot image v4.
|
||||
boot_signature_size = 0
|
||||
if should_add_legacy_gki_boot_signature(args):
|
||||
boot_signature_size = BOOT_IMAGE_V4_SIGNATURE_SIZE
|
||||
args.output.write(pack('I', boot_signature_size))
|
||||
pad_file(args.output, BOOT_IMAGE_HEADER_V3_PAGESIZE)
|
||||
|
||||
|
||||
def write_vendor_boot_header(args):
|
||||
VENDOR_BOOT_IMAGE_HEADER_V3_SIZE = 2112
|
||||
BOOT_MAGIC = 'VNDRBOOT'.encode()
|
||||
if args.header_version > 3:
|
||||
vendor_ramdisk_size = args.vendor_ramdisk_total_size
|
||||
vendor_boot_header_size = VENDOR_BOOT_IMAGE_HEADER_V4_SIZE
|
||||
else:
|
||||
vendor_ramdisk_size = filesize(args.vendor_ramdisk)
|
||||
vendor_boot_header_size = VENDOR_BOOT_IMAGE_HEADER_V3_SIZE
|
||||
|
||||
args.vendor_boot.write(pack('8s', BOOT_MAGIC))
|
||||
args.vendor_boot.write(pack(
|
||||
'5I',
|
||||
args.header_version, # version of header
|
||||
args.pagesize, # flash page size we assume
|
||||
args.base + args.kernel_offset, # kernel physical load addr
|
||||
args.base + args.ramdisk_offset, # ramdisk physical load addr
|
||||
filesize(args.vendor_ramdisk))) # vendor ramdisk size in bytes
|
||||
args.vendor_boot.write(pack('2048s', args.vendor_cmdline.encode()))
|
||||
args.vendor_boot.write(pack('I', args.base + args.tags_offset)) # physical addr for kernel tags
|
||||
args.vendor_boot.write(pack('16s', args.board.encode())) # asciiz product name
|
||||
args.vendor_boot.write(pack('I', VENDOR_BOOT_IMAGE_HEADER_V3_SIZE)) # header size in bytes
|
||||
if filesize(args.dtb) == 0:
|
||||
raise ValueError("DTB image must not be empty.")
|
||||
args.vendor_boot.write(pack('I', filesize(args.dtb))) # size in bytes
|
||||
args.vendor_boot.write(pack('Q', args.base + args.dtb_offset)) # dtb physical load address
|
||||
pad_file(args.vendor_boot, args.pagesize)
|
||||
args.vendor_boot.write(pack(f'{VENDOR_BOOT_MAGIC_SIZE}s',
|
||||
VENDOR_BOOT_MAGIC.encode()))
|
||||
# version of boot image header
|
||||
args.vendor_boot.write(pack('I', args.header_version))
|
||||
# flash page size
|
||||
args.vendor_boot.write(pack('I', args.pagesize))
|
||||
# kernel physical load address
|
||||
args.vendor_boot.write(pack('I', args.base + args.kernel_offset))
|
||||
# ramdisk physical load address
|
||||
args.vendor_boot.write(pack('I', args.base + args.ramdisk_offset))
|
||||
# ramdisk size in bytes
|
||||
args.vendor_boot.write(pack('I', vendor_ramdisk_size))
|
||||
args.vendor_boot.write(pack(f'{VENDOR_BOOT_ARGS_SIZE}s',
|
||||
args.vendor_cmdline))
|
||||
# kernel tags physical load address
|
||||
args.vendor_boot.write(pack('I', args.base + args.tags_offset))
|
||||
# asciiz product name
|
||||
args.vendor_boot.write(pack(f'{VENDOR_BOOT_NAME_SIZE}s', args.board))
|
||||
|
||||
def write_header(args):
|
||||
BOOT_IMAGE_HEADER_V1_SIZE = 1648
|
||||
BOOT_IMAGE_HEADER_V2_SIZE = 1660
|
||||
BOOT_MAGIC = 'ANDROID!'.encode()
|
||||
# header size in bytes
|
||||
args.vendor_boot.write(pack('I', vendor_boot_header_size))
|
||||
|
||||
# dtb size in bytes
|
||||
args.vendor_boot.write(pack('I', filesize(args.dtb)))
|
||||
# dtb physical load address
|
||||
args.vendor_boot.write(pack('Q', args.base + args.dtb_offset))
|
||||
|
||||
if args.header_version > 3:
|
||||
raise ValueError('Boot header version %d not supported' % args.header_version)
|
||||
elif args.header_version == 3:
|
||||
return write_header_v3(args)
|
||||
vendor_ramdisk_table_size = (args.vendor_ramdisk_table_entry_num *
|
||||
VENDOR_RAMDISK_TABLE_ENTRY_V4_SIZE)
|
||||
# vendor ramdisk table size in bytes
|
||||
args.vendor_boot.write(pack('I', vendor_ramdisk_table_size))
|
||||
# number of vendor ramdisk table entries
|
||||
args.vendor_boot.write(pack('I', args.vendor_ramdisk_table_entry_num))
|
||||
# vendor ramdisk table entry size in bytes
|
||||
args.vendor_boot.write(pack('I', VENDOR_RAMDISK_TABLE_ENTRY_V4_SIZE))
|
||||
# bootconfig section size in bytes
|
||||
args.vendor_boot.write(pack('I', filesize(args.vendor_bootconfig)))
|
||||
pad_file(args.vendor_boot, args.pagesize)
|
||||
|
||||
args.output.write(pack('8s', BOOT_MAGIC))
|
||||
final_ramdisk_offset = (args.base + args.ramdisk_offset) if filesize(args.ramdisk) > 0 else 0
|
||||
final_second_offset = (args.base + args.second_offset) if filesize(args.second) > 0 else 0
|
||||
args.output.write(pack(
|
||||
'10I',
|
||||
filesize(args.kernel), # size in bytes
|
||||
args.base + args.kernel_offset, # physical load addr
|
||||
filesize(args.ramdisk), # size in bytes
|
||||
final_ramdisk_offset, # physical load addr
|
||||
filesize(args.second), # size in bytes
|
||||
final_second_offset, # physical load addr
|
||||
args.base + args.tags_offset, # physical addr for kernel tags
|
||||
args.pagesize, # flash page size we assume
|
||||
args.header_version, # version of bootimage header
|
||||
(args.os_version << 11) | args.os_patch_level)) # os version and patch level
|
||||
args.output.write(pack('16s', args.board.encode())) # asciiz product name
|
||||
args.output.write(pack('512s', args.cmdline[:512].encode()))
|
||||
|
||||
def write_header(args):
|
||||
if args.header_version > 4:
|
||||
raise ValueError(
|
||||
f'Boot header version {args.header_version} not supported')
|
||||
if args.header_version in {3, 4}:
|
||||
return write_header_v3_and_above(args)
|
||||
|
||||
ramdisk_load_address = ((args.base + args.ramdisk_offset)
|
||||
if filesize(args.ramdisk) > 0 else 0)
|
||||
second_load_address = ((args.base + args.second_offset)
|
||||
if filesize(args.second) > 0 else 0)
|
||||
|
||||
args.output.write(pack(f'{BOOT_MAGIC_SIZE}s', BOOT_MAGIC.encode()))
|
||||
# kernel size in bytes
|
||||
args.output.write(pack('I', filesize(args.kernel)))
|
||||
# kernel physical load address
|
||||
args.output.write(pack('I', args.base + args.kernel_offset))
|
||||
# ramdisk size in bytes
|
||||
args.output.write(pack('I', filesize(args.ramdisk)))
|
||||
# ramdisk physical load address
|
||||
args.output.write(pack('I', ramdisk_load_address))
|
||||
# second bootloader size in bytes
|
||||
args.output.write(pack('I', filesize(args.second)))
|
||||
# second bootloader physical load address
|
||||
args.output.write(pack('I', second_load_address))
|
||||
# kernel tags physical load address
|
||||
args.output.write(pack('I', args.base + args.tags_offset))
|
||||
# flash page size
|
||||
args.output.write(pack('I', args.pagesize))
|
||||
# version of boot image header
|
||||
args.output.write(pack('I', args.header_version))
|
||||
# os version and patch level
|
||||
args.output.write(pack('I', (args.os_version << 11) | args.os_patch_level))
|
||||
# asciiz product name
|
||||
args.output.write(pack(f'{BOOT_NAME_SIZE}s', args.board))
|
||||
args.output.write(pack(f'{BOOT_ARGS_SIZE}s', args.cmdline))
|
||||
|
||||
sha = sha1()
|
||||
update_sha(sha, args.kernel)
|
||||
@@ -144,14 +264,18 @@ def write_header(args):
|
||||
img_id = pack('32s', sha.digest())
|
||||
|
||||
args.output.write(img_id)
|
||||
args.output.write(pack('1024s', args.cmdline[512:].encode()))
|
||||
args.output.write(pack(f'{BOOT_EXTRA_ARGS_SIZE}s', args.extra_cmdline))
|
||||
|
||||
if args.header_version > 0:
|
||||
args.output.write(pack('I', filesize(args.recovery_dtbo))) # size in bytes
|
||||
if args.recovery_dtbo:
|
||||
args.output.write(pack('Q', get_recovery_dtbo_offset(args))) # recovery dtbo offset
|
||||
# recovery dtbo size in bytes
|
||||
args.output.write(pack('I', filesize(args.recovery_dtbo)))
|
||||
# recovert dtbo offset in the boot image
|
||||
args.output.write(pack('Q', get_recovery_dtbo_offset(args)))
|
||||
else:
|
||||
args.output.write(pack('Q', 0)) # Will be set to 0 for devices without a recovery dtbo
|
||||
# Set to zero if no recovery dtbo
|
||||
args.output.write(pack('I', 0))
|
||||
args.output.write(pack('Q', 0))
|
||||
|
||||
# Populate boot image header size for header versions 1 and 2.
|
||||
if args.header_version == 1:
|
||||
@@ -160,29 +284,101 @@ def write_header(args):
|
||||
args.output.write(pack('I', BOOT_IMAGE_HEADER_V2_SIZE))
|
||||
|
||||
if args.header_version > 1:
|
||||
|
||||
# if filesize(args.dtb) == 0:
|
||||
# raise ValueError("DTB image must not be empty.")
|
||||
# raise ValueError('DTB image must not be empty.')
|
||||
|
||||
# dtb size in bytes
|
||||
args.output.write(pack('I', filesize(args.dtb)))
|
||||
# dtb physical load address
|
||||
args.output.write(pack('Q', args.base + args.dtb_offset))
|
||||
|
||||
args.output.write(pack('I', filesize(args.dtb))) # size in bytes
|
||||
args.output.write(pack('Q', args.base + args.dtb_offset)) # dtb physical load address
|
||||
pad_file(args.output, args.pagesize)
|
||||
return img_id
|
||||
|
||||
|
||||
class ValidateStrLenAction(Action):
|
||||
def __init__(self, option_strings, dest, nargs=None, **kwargs):
|
||||
if 'maxlen' not in kwargs:
|
||||
raise ValueError('maxlen must be set')
|
||||
self.maxlen = int(kwargs['maxlen'])
|
||||
del kwargs['maxlen']
|
||||
super(ValidateStrLenAction, self).__init__(option_strings, dest, **kwargs)
|
||||
class AsciizBytes:
|
||||
"""Parses a string and encodes it as an asciiz bytes object.
|
||||
|
||||
def __call__(self, parser, namespace, values, option_string=None):
|
||||
if len(values) > self.maxlen:
|
||||
>>> AsciizBytes(bufsize=4)('foo')
|
||||
b'foo\\x00'
|
||||
>>> AsciizBytes(bufsize=4)('foob')
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
argparse.ArgumentTypeError: Encoded asciiz length exceeded: max 4, got 5
|
||||
"""
|
||||
|
||||
def __init__(self, bufsize):
|
||||
self.bufsize = bufsize
|
||||
|
||||
def __call__(self, arg):
|
||||
arg_bytes = arg.encode() + b'\x00'
|
||||
if len(arg_bytes) > self.bufsize:
|
||||
raise ArgumentTypeError(
|
||||
'Encoded asciiz length exceeded: '
|
||||
f'max {self.bufsize}, got {len(arg_bytes)}')
|
||||
return arg_bytes
|
||||
|
||||
|
||||
class VendorRamdiskTableBuilder:
|
||||
"""Vendor ramdisk table builder.
|
||||
|
||||
Attributes:
|
||||
entries: A list of VendorRamdiskTableEntry namedtuple.
|
||||
ramdisk_total_size: Total size in bytes of all ramdisks in the table.
|
||||
"""
|
||||
|
||||
VendorRamdiskTableEntry = collections.namedtuple( # pylint: disable=invalid-name
|
||||
'VendorRamdiskTableEntry',
|
||||
['ramdisk_path', 'ramdisk_size', 'ramdisk_offset', 'ramdisk_type',
|
||||
'ramdisk_name', 'board_id'])
|
||||
|
||||
def __init__(self):
|
||||
self.entries = []
|
||||
self.ramdisk_total_size = 0
|
||||
self.ramdisk_names = set()
|
||||
|
||||
def add_entry(self, ramdisk_path, ramdisk_type, ramdisk_name, board_id):
|
||||
# Strip any trailing null for simple comparison.
|
||||
stripped_ramdisk_name = ramdisk_name.rstrip(b'\x00')
|
||||
if stripped_ramdisk_name in VENDOR_RAMDISK_NAME_BLOCKLIST:
|
||||
raise ValueError(
|
||||
'String argument too long: max {0:d}, got {1:d}'.format(self.maxlen, len(values)))
|
||||
setattr(namespace, self.dest, values)
|
||||
f'Banned vendor ramdisk name: {stripped_ramdisk_name}')
|
||||
if stripped_ramdisk_name in self.ramdisk_names:
|
||||
raise ValueError(
|
||||
f'Duplicated vendor ramdisk name: {stripped_ramdisk_name}')
|
||||
self.ramdisk_names.add(stripped_ramdisk_name)
|
||||
|
||||
if board_id is None:
|
||||
board_id = array.array(
|
||||
'I', [0] * VENDOR_RAMDISK_TABLE_ENTRY_BOARD_ID_SIZE)
|
||||
else:
|
||||
board_id = array.array('I', board_id)
|
||||
if len(board_id) != VENDOR_RAMDISK_TABLE_ENTRY_BOARD_ID_SIZE:
|
||||
raise ValueError('board_id size must be '
|
||||
f'{VENDOR_RAMDISK_TABLE_ENTRY_BOARD_ID_SIZE}')
|
||||
|
||||
with open(ramdisk_path, 'rb') as f:
|
||||
ramdisk_size = filesize(f)
|
||||
self.entries.append(self.VendorRamdiskTableEntry(
|
||||
ramdisk_path, ramdisk_size, self.ramdisk_total_size, ramdisk_type,
|
||||
ramdisk_name, board_id))
|
||||
self.ramdisk_total_size += ramdisk_size
|
||||
|
||||
def write_ramdisks_padded(self, fout, alignment):
|
||||
for entry in self.entries:
|
||||
with open(entry.ramdisk_path, 'rb') as f:
|
||||
fout.write(f.read())
|
||||
pad_file(fout, alignment)
|
||||
|
||||
def write_entries_padded(self, fout, alignment):
|
||||
for entry in self.entries:
|
||||
fout.write(pack('I', entry.ramdisk_size))
|
||||
fout.write(pack('I', entry.ramdisk_offset))
|
||||
fout.write(pack('I', entry.ramdisk_type))
|
||||
fout.write(pack(f'{VENDOR_RAMDISK_NAME_SIZE}s',
|
||||
entry.ramdisk_name))
|
||||
fout.write(entry.board_id)
|
||||
pad_file(fout, alignment)
|
||||
|
||||
|
||||
def write_padded_file(f_out, f_in, padding):
|
||||
@@ -225,49 +421,221 @@ def parse_os_patch_level(x):
|
||||
return 0
|
||||
|
||||
|
||||
def parse_vendor_ramdisk_type(x):
|
||||
type_dict = {
|
||||
'none': VENDOR_RAMDISK_TYPE_NONE,
|
||||
'platform': VENDOR_RAMDISK_TYPE_PLATFORM,
|
||||
'recovery': VENDOR_RAMDISK_TYPE_RECOVERY,
|
||||
'dlkm': VENDOR_RAMDISK_TYPE_DLKM,
|
||||
}
|
||||
if x.lower() in type_dict:
|
||||
return type_dict[x.lower()]
|
||||
return parse_int(x)
|
||||
|
||||
|
||||
def get_vendor_boot_v4_usage():
|
||||
return """vendor boot version 4 arguments:
|
||||
--ramdisk_type {none,platform,recovery,dlkm}
|
||||
specify the type of the ramdisk
|
||||
--ramdisk_name NAME
|
||||
specify the name of the ramdisk
|
||||
--board_id{0..15} NUMBER
|
||||
specify the value of the board_id vector, defaults to 0
|
||||
--vendor_ramdisk_fragment VENDOR_RAMDISK_FILE
|
||||
path to the vendor ramdisk file
|
||||
|
||||
These options can be specified multiple times, where each vendor ramdisk
|
||||
option group ends with a --vendor_ramdisk_fragment option.
|
||||
Each option group appends an additional ramdisk to the vendor boot image.
|
||||
"""
|
||||
|
||||
|
||||
def parse_vendor_ramdisk_args(args, args_list):
|
||||
"""Parses vendor ramdisk specific arguments.
|
||||
|
||||
Args:
|
||||
args: An argparse.Namespace object. Parsed results are stored into this
|
||||
object.
|
||||
args_list: A list of argument strings to be parsed.
|
||||
|
||||
Returns:
|
||||
A list argument strings that are not parsed by this method.
|
||||
"""
|
||||
parser = ArgumentParser(add_help=False)
|
||||
parser.add_argument('--ramdisk_type', type=parse_vendor_ramdisk_type,
|
||||
default=VENDOR_RAMDISK_TYPE_NONE)
|
||||
parser.add_argument('--ramdisk_name',
|
||||
type=AsciizBytes(bufsize=VENDOR_RAMDISK_NAME_SIZE),
|
||||
required=True)
|
||||
for i in range(VENDOR_RAMDISK_TABLE_ENTRY_BOARD_ID_SIZE):
|
||||
parser.add_argument(f'--board_id{i}', type=parse_int, default=0)
|
||||
parser.add_argument(PARSER_ARGUMENT_VENDOR_RAMDISK_FRAGMENT, required=True)
|
||||
|
||||
unknown_args = []
|
||||
|
||||
vendor_ramdisk_table_builder = VendorRamdiskTableBuilder()
|
||||
if args.vendor_ramdisk is not None:
|
||||
vendor_ramdisk_table_builder.add_entry(
|
||||
args.vendor_ramdisk.name, VENDOR_RAMDISK_TYPE_PLATFORM, b'', None)
|
||||
|
||||
while PARSER_ARGUMENT_VENDOR_RAMDISK_FRAGMENT in args_list:
|
||||
idx = args_list.index(PARSER_ARGUMENT_VENDOR_RAMDISK_FRAGMENT) + 2
|
||||
vendor_ramdisk_args = args_list[:idx]
|
||||
args_list = args_list[idx:]
|
||||
|
||||
ramdisk_args, extra_args = parser.parse_known_args(vendor_ramdisk_args)
|
||||
ramdisk_args_dict = vars(ramdisk_args)
|
||||
unknown_args.extend(extra_args)
|
||||
|
||||
ramdisk_path = ramdisk_args.vendor_ramdisk_fragment
|
||||
ramdisk_type = ramdisk_args.ramdisk_type
|
||||
ramdisk_name = ramdisk_args.ramdisk_name
|
||||
board_id = [ramdisk_args_dict[f'board_id{i}']
|
||||
for i in range(VENDOR_RAMDISK_TABLE_ENTRY_BOARD_ID_SIZE)]
|
||||
vendor_ramdisk_table_builder.add_entry(ramdisk_path, ramdisk_type,
|
||||
ramdisk_name, board_id)
|
||||
|
||||
if len(args_list) > 0:
|
||||
unknown_args.extend(args_list)
|
||||
|
||||
args.vendor_ramdisk_total_size = (vendor_ramdisk_table_builder
|
||||
.ramdisk_total_size)
|
||||
args.vendor_ramdisk_table_entry_num = len(vendor_ramdisk_table_builder
|
||||
.entries)
|
||||
args.vendor_ramdisk_table_builder = vendor_ramdisk_table_builder
|
||||
return unknown_args
|
||||
|
||||
|
||||
def parse_cmdline():
|
||||
parser = ArgumentParser()
|
||||
parser.add_argument('--kernel', help='path to the kernel', type=FileType('rb'))
|
||||
parser.add_argument('--ramdisk', help='path to the ramdisk', type=FileType('rb'))
|
||||
parser.add_argument('--second', help='path to the 2nd bootloader', type=FileType('rb'))
|
||||
parser.add_argument('--dtb', help='path to dtb', type=FileType('rb'))
|
||||
recovery_dtbo_group = parser.add_mutually_exclusive_group()
|
||||
recovery_dtbo_group.add_argument('--recovery_dtbo', help='path to the recovery DTBO',
|
||||
type=FileType('rb'))
|
||||
recovery_dtbo_group.add_argument('--recovery_acpio', help='path to the recovery ACPIO',
|
||||
type=FileType('rb'), metavar='RECOVERY_ACPIO',
|
||||
dest='recovery_dtbo')
|
||||
parser.add_argument('--cmdline', help='extra arguments to be passed on the '
|
||||
'kernel command line', default='', action=ValidateStrLenAction, maxlen=1536)
|
||||
version_parser = ArgumentParser(add_help=False)
|
||||
version_parser.add_argument('--header_version', type=parse_int, default=0)
|
||||
if version_parser.parse_known_args()[0].header_version < 3:
|
||||
# For boot header v0 to v2, the kernel commandline field is split into
|
||||
# two fields, cmdline and extra_cmdline. Both fields are asciiz strings,
|
||||
# so we minus one here to ensure the encoded string plus the
|
||||
# null-terminator can fit in the buffer size.
|
||||
cmdline_size = BOOT_ARGS_SIZE + BOOT_EXTRA_ARGS_SIZE - 1
|
||||
else:
|
||||
cmdline_size = BOOT_ARGS_SIZE + BOOT_EXTRA_ARGS_SIZE
|
||||
|
||||
parser = ArgumentParser(formatter_class=RawDescriptionHelpFormatter,
|
||||
epilog=get_vendor_boot_v4_usage())
|
||||
parser.add_argument('--kernel', type=FileType('rb'),
|
||||
help='path to the kernel')
|
||||
parser.add_argument('--ramdisk', type=FileType('rb'),
|
||||
help='path to the ramdisk')
|
||||
parser.add_argument('--second', type=FileType('rb'),
|
||||
help='path to the second bootloader')
|
||||
parser.add_argument('--dtb', type=FileType('rb'), help='path to the dtb')
|
||||
dtbo_group = parser.add_mutually_exclusive_group()
|
||||
dtbo_group.add_argument('--recovery_dtbo', type=FileType('rb'),
|
||||
help='path to the recovery DTBO')
|
||||
dtbo_group.add_argument('--recovery_acpio', type=FileType('rb'),
|
||||
metavar='RECOVERY_ACPIO', dest='recovery_dtbo',
|
||||
help='path to the recovery ACPIO')
|
||||
parser.add_argument('--cmdline', type=AsciizBytes(bufsize=cmdline_size),
|
||||
default='', help='kernel command line arguments')
|
||||
parser.add_argument('--vendor_cmdline',
|
||||
help='kernel command line arguments contained in vendor boot',
|
||||
default='', action=ValidateStrLenAction, maxlen=2048)
|
||||
parser.add_argument('--base', help='base address', type=parse_int, default=0x10000000)
|
||||
parser.add_argument('--kernel_offset', help='kernel offset', type=parse_int, default=0x00008000)
|
||||
parser.add_argument('--ramdisk_offset', help='ramdisk offset', type=parse_int,
|
||||
default=0x01000000)
|
||||
parser.add_argument('--second_offset', help='2nd bootloader offset', type=parse_int,
|
||||
default=0x00f00000)
|
||||
parser.add_argument('--dtb_offset', help='dtb offset', type=parse_int, default=0x01f00000)
|
||||
type=AsciizBytes(bufsize=VENDOR_BOOT_ARGS_SIZE),
|
||||
default='',
|
||||
help='vendor boot kernel command line arguments')
|
||||
parser.add_argument('--base', type=parse_int, default=0x10000000,
|
||||
help='base address')
|
||||
parser.add_argument('--kernel_offset', type=parse_int, default=0x00008000,
|
||||
help='kernel offset')
|
||||
parser.add_argument('--ramdisk_offset', type=parse_int, default=0x01000000,
|
||||
help='ramdisk offset')
|
||||
parser.add_argument('--second_offset', type=parse_int, default=0x00f00000,
|
||||
help='second bootloader offset')
|
||||
parser.add_argument('--dtb_offset', type=parse_int, default=0x01f00000,
|
||||
help='dtb offset')
|
||||
|
||||
parser.add_argument('--os_version', help='operating system version', type=parse_os_version,
|
||||
default=0)
|
||||
parser.add_argument('--os_patch_level', help='operating system patch level',
|
||||
type=parse_os_patch_level, default=0)
|
||||
parser.add_argument('--tags_offset', help='tags offset', type=parse_int, default=0x00000100)
|
||||
parser.add_argument('--board', help='board name', default='', action=ValidateStrLenAction,
|
||||
maxlen=16)
|
||||
parser.add_argument('--pagesize', help='page size', type=parse_int,
|
||||
choices=[2**i for i in range(11, 15)], default=2048)
|
||||
parser.add_argument('--id', help='print the image ID on standard output',
|
||||
action='store_true')
|
||||
parser.add_argument('--header_version', help='boot image header version', type=parse_int,
|
||||
default=0)
|
||||
parser.add_argument('-o', '--output', help='output file name', type=FileType('wb'))
|
||||
parser.add_argument('--vendor_boot', help='vendor boot output file name', type=FileType('wb'))
|
||||
parser.add_argument('--vendor_ramdisk', help='path to the vendor ramdisk', type=FileType('rb'))
|
||||
parser.add_argument('--os_version', type=parse_os_version, default=0,
|
||||
help='operating system version')
|
||||
parser.add_argument('--os_patch_level', type=parse_os_patch_level,
|
||||
default=0, help='operating system patch level')
|
||||
parser.add_argument('--tags_offset', type=parse_int, default=0x00000100,
|
||||
help='tags offset')
|
||||
parser.add_argument('--board', type=AsciizBytes(bufsize=BOOT_NAME_SIZE),
|
||||
default='', help='board name')
|
||||
parser.add_argument('--pagesize', type=parse_int,
|
||||
choices=[2**i for i in range(11, 15)], default=2048,
|
||||
help='page size')
|
||||
parser.add_argument('--id', action='store_true',
|
||||
help='print the image ID on standard output')
|
||||
parser.add_argument('--header_version', type=parse_int, default=0,
|
||||
help='boot image header version')
|
||||
parser.add_argument('-o', '--output', type=FileType('wb'),
|
||||
help='output file name')
|
||||
parser.add_argument('--vendor_boot', type=FileType('wb'),
|
||||
help='vendor boot output file name')
|
||||
parser.add_argument('--vendor_ramdisk', type=FileType('rb'),
|
||||
help='path to the vendor ramdisk')
|
||||
parser.add_argument('--vendor_bootconfig', type=FileType('rb'),
|
||||
help='path to the vendor bootconfig file')
|
||||
|
||||
return parser.parse_args()
|
||||
gki_2_0_signing_args = parser.add_argument_group(
|
||||
'[DEPRECATED] GKI 2.0 signing arguments')
|
||||
gki_2_0_signing_args.add_argument(
|
||||
'--gki_signing_algorithm', help='GKI signing algorithm to use')
|
||||
gki_2_0_signing_args.add_argument(
|
||||
'--gki_signing_key', help='path to RSA private key file')
|
||||
gki_2_0_signing_args.add_argument(
|
||||
'--gki_signing_signature_args', default='',
|
||||
help='other hash arguments passed to avbtool')
|
||||
gki_2_0_signing_args.add_argument(
|
||||
'--gki_signing_avbtool_path', default='avbtool',
|
||||
help='path to avbtool for boot signature generation')
|
||||
|
||||
args, extra_args = parser.parse_known_args()
|
||||
if args.vendor_boot is not None and args.header_version > 3:
|
||||
extra_args = parse_vendor_ramdisk_args(args, extra_args)
|
||||
if len(extra_args) > 0:
|
||||
raise ValueError(f'Unrecognized arguments: {extra_args}')
|
||||
|
||||
if args.header_version < 3:
|
||||
args.extra_cmdline = args.cmdline[BOOT_ARGS_SIZE-1:]
|
||||
args.cmdline = args.cmdline[:BOOT_ARGS_SIZE-1] + b'\x00'
|
||||
assert len(args.cmdline) <= BOOT_ARGS_SIZE
|
||||
assert len(args.extra_cmdline) <= BOOT_EXTRA_ARGS_SIZE
|
||||
|
||||
return args
|
||||
|
||||
|
||||
def add_boot_image_signature(args, pagesize):
|
||||
"""Adds the boot image signature.
|
||||
|
||||
Note that the signature will only be verified in VTS to ensure a
|
||||
generic boot.img is used. It will not be used by the device
|
||||
bootloader at boot time. The bootloader should only verify
|
||||
the boot vbmeta at the end of the boot partition (or in the top-level
|
||||
vbmeta partition) via the Android Verified Boot process, when the
|
||||
device boots.
|
||||
"""
|
||||
# Flush the buffer for signature calculation.
|
||||
args.output.flush()
|
||||
|
||||
# Outputs the signed vbmeta to a separate file, then append to boot.img
|
||||
# as the boot signature.
|
||||
with tempfile.TemporaryDirectory() as temp_out_dir:
|
||||
boot_signature_output = os.path.join(temp_out_dir, 'boot_signature')
|
||||
generate_gki_certificate(
|
||||
image=args.output.name, avbtool=args.gki_signing_avbtool_path,
|
||||
name='boot', algorithm=args.gki_signing_algorithm,
|
||||
key=args.gki_signing_key, salt='d00df00d',
|
||||
additional_avb_args=args.gki_signing_signature_args.split(),
|
||||
output=boot_signature_output,
|
||||
)
|
||||
with open(boot_signature_output, 'rb') as boot_signature:
|
||||
boot_signature_bytes = boot_signature.read()
|
||||
if len(boot_signature_bytes) > BOOT_IMAGE_V4_SIGNATURE_SIZE:
|
||||
raise ValueError(
|
||||
f'boot sigature size is > {BOOT_IMAGE_V4_SIGNATURE_SIZE}')
|
||||
boot_signature_bytes += b'\x00' * (
|
||||
BOOT_IMAGE_V4_SIGNATURE_SIZE - len(boot_signature_bytes))
|
||||
assert len(boot_signature_bytes) == BOOT_IMAGE_V4_SIGNATURE_SIZE
|
||||
args.output.write(boot_signature_bytes)
|
||||
pad_file(args.output, pagesize)
|
||||
|
||||
|
||||
def write_data(args, pagesize):
|
||||
@@ -279,37 +647,44 @@ def write_data(args, pagesize):
|
||||
write_padded_file(args.output, args.recovery_dtbo, pagesize)
|
||||
if args.header_version == 2:
|
||||
write_padded_file(args.output, args.dtb, pagesize)
|
||||
if args.header_version >= 4 and should_add_legacy_gki_boot_signature(args):
|
||||
add_boot_image_signature(args, pagesize)
|
||||
|
||||
|
||||
def write_vendor_boot_data(args):
|
||||
write_padded_file(args.vendor_boot, args.vendor_ramdisk, args.pagesize)
|
||||
write_padded_file(args.vendor_boot, args.dtb, args.pagesize)
|
||||
if args.header_version > 3:
|
||||
builder = args.vendor_ramdisk_table_builder
|
||||
builder.write_ramdisks_padded(args.vendor_boot, args.pagesize)
|
||||
write_padded_file(args.vendor_boot, args.dtb, args.pagesize)
|
||||
builder.write_entries_padded(args.vendor_boot, args.pagesize)
|
||||
write_padded_file(args.vendor_boot, args.vendor_bootconfig,
|
||||
args.pagesize)
|
||||
else:
|
||||
write_padded_file(args.vendor_boot, args.vendor_ramdisk, args.pagesize)
|
||||
write_padded_file(args.vendor_boot, args.dtb, args.pagesize)
|
||||
|
||||
|
||||
def main():
|
||||
args = parse_cmdline()
|
||||
if args.vendor_boot is not None:
|
||||
if args.header_version < 3:
|
||||
raise ValueError('--vendor_boot not compatible with given header version')
|
||||
if args.vendor_ramdisk is None:
|
||||
if args.header_version not in {3, 4}:
|
||||
raise ValueError(
|
||||
'--vendor_boot not compatible with given header version')
|
||||
if args.header_version == 3 and args.vendor_ramdisk is None:
|
||||
raise ValueError('--vendor_ramdisk missing or invalid')
|
||||
write_vendor_boot_header(args)
|
||||
write_vendor_boot_data(args)
|
||||
if args.output is not None:
|
||||
if args.kernel is None:
|
||||
raise ValueError('kernel must be supplied when creating a boot image')
|
||||
if args.second is not None and args.header_version > 2:
|
||||
raise ValueError('--second not compatible with given header version')
|
||||
raise ValueError(
|
||||
'--second not compatible with given header version')
|
||||
img_id = write_header(args)
|
||||
if args.header_version > 2:
|
||||
write_data(args, BOOT_IMAGE_HEADER_V3_PAGESIZE)
|
||||
else:
|
||||
write_data(args, args.pagesize)
|
||||
if args.id and img_id is not None:
|
||||
# Python 2's struct.pack returns a string, but py3 returns bytes.
|
||||
if isinstance(img_id, str):
|
||||
img_id = [ord(x) for x in img_id]
|
||||
print('0x' + ''.join('{:02x}'.format(c) for c in img_id))
|
||||
print('0x' + ''.join(f'{octet:02x}' for octet in img_id))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
+447
-130
@@ -1,4 +1,5 @@
|
||||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
# Copyright 2018, The Android Open Source Project
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@@ -13,18 +14,20 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""unpacks the bootimage.
|
||||
"""Unpacks the boot image.
|
||||
|
||||
Extracts the kernel, ramdisk, second bootloader, dtb and recovery dtbo images.
|
||||
"""
|
||||
|
||||
from __future__ import print_function
|
||||
from argparse import ArgumentParser, FileType
|
||||
from argparse import ArgumentParser, RawDescriptionHelpFormatter
|
||||
from struct import unpack
|
||||
import os
|
||||
import shlex
|
||||
|
||||
BOOT_IMAGE_HEADER_V3_PAGESIZE = 4096
|
||||
VENDOR_BOOT_IMAGE_HEADER_V3_SIZE = 2112
|
||||
VENDOR_RAMDISK_NAME_SIZE = 32
|
||||
VENDOR_RAMDISK_TABLE_ENTRY_BOARD_ID_SIZE = 16
|
||||
|
||||
|
||||
def create_out_dir(dir_path):
|
||||
"""creates a directory 'dir_path' if it does not exist"""
|
||||
@@ -50,196 +53,510 @@ def cstr(s):
|
||||
|
||||
|
||||
def format_os_version(os_version):
|
||||
if os_version == 0:
|
||||
return None
|
||||
a = os_version >> 14
|
||||
b = os_version >> 7 & ((1<<7) - 1)
|
||||
c = os_version & ((1<<7) - 1)
|
||||
return '{}.{}.{}'.format(a, b, c)
|
||||
return f'{a}.{b}.{c}'
|
||||
|
||||
|
||||
def format_os_patch_level(os_patch_level):
|
||||
if os_patch_level == 0:
|
||||
return None
|
||||
y = os_patch_level >> 4
|
||||
y += 2000
|
||||
m = os_patch_level & ((1<<4) - 1)
|
||||
return '{:04d}-{:02d}'.format(y, m)
|
||||
return f'{y:04d}-{m:02d}'
|
||||
|
||||
|
||||
def print_os_version_patch_level(value):
|
||||
os_version = value >> 11
|
||||
os_patch_level = value & ((1<<11) - 1)
|
||||
print('os version: %s' % format_os_version(os_version))
|
||||
print('os patch level: %s' % format_os_patch_level(os_patch_level))
|
||||
def decode_os_version_patch_level(os_version_patch_level):
|
||||
"""Returns a tuple of (os_version, os_patch_level)."""
|
||||
os_version = os_version_patch_level >> 11
|
||||
os_patch_level = os_version_patch_level & ((1<<11) - 1)
|
||||
return (format_os_version(os_version),
|
||||
format_os_patch_level(os_patch_level))
|
||||
|
||||
|
||||
def unpack_bootimage(args):
|
||||
class BootImageInfoFormatter:
|
||||
"""Formats the boot image info."""
|
||||
|
||||
def format_pretty_text(self):
|
||||
lines = []
|
||||
lines.append(f'boot magic: {self.boot_magic}')
|
||||
|
||||
if self.header_version < 3:
|
||||
lines.append(f'kernel_size: {self.kernel_size}')
|
||||
lines.append(
|
||||
f'kernel load address: {self.kernel_load_address:#010x}')
|
||||
lines.append(f'ramdisk size: {self.ramdisk_size}')
|
||||
lines.append(
|
||||
f'ramdisk load address: {self.ramdisk_load_address:#010x}')
|
||||
lines.append(f'second bootloader size: {self.second_size}')
|
||||
lines.append(
|
||||
f'second bootloader load address: '
|
||||
f'{self.second_load_address:#010x}')
|
||||
lines.append(
|
||||
f'kernel tags load address: {self.tags_load_address:#010x}')
|
||||
lines.append(f'page size: {self.page_size}')
|
||||
else:
|
||||
lines.append(f'kernel_size: {self.kernel_size}')
|
||||
lines.append(f'ramdisk size: {self.ramdisk_size}')
|
||||
|
||||
lines.append(f'os version: {self.os_version}')
|
||||
lines.append(f'os patch level: {self.os_patch_level}')
|
||||
lines.append(f'boot image header version: {self.header_version}')
|
||||
|
||||
if self.header_version < 3:
|
||||
lines.append(f'product name: {self.product_name}')
|
||||
|
||||
lines.append(f'command line args: {self.cmdline}')
|
||||
|
||||
if self.header_version < 3:
|
||||
lines.append(f'additional command line args: {self.extra_cmdline}')
|
||||
|
||||
if self.header_version in {1, 2}:
|
||||
lines.append(f'recovery dtbo size: {self.recovery_dtbo_size}')
|
||||
lines.append(
|
||||
f'recovery dtbo offset: {self.recovery_dtbo_offset:#018x}')
|
||||
lines.append(f'boot header size: {self.boot_header_size}')
|
||||
|
||||
if self.header_version == 2:
|
||||
lines.append(f'dtb size: {self.dtb_size}')
|
||||
lines.append(f'dtb address: {self.dtb_load_address:#018x}')
|
||||
|
||||
if self.header_version >= 4:
|
||||
lines.append(
|
||||
f'boot.img signature size: {self.boot_signature_size}')
|
||||
|
||||
return '\n'.join(lines)
|
||||
|
||||
def format_mkbootimg_argument(self):
|
||||
args = []
|
||||
args.extend(['--header_version', str(self.header_version)])
|
||||
if self.os_version:
|
||||
args.extend(['--os_version', self.os_version])
|
||||
if self.os_patch_level:
|
||||
args.extend(['--os_patch_level', self.os_patch_level])
|
||||
|
||||
args.extend(['--kernel', os.path.join(self.image_dir, 'kernel')])
|
||||
args.extend(['--ramdisk', os.path.join(self.image_dir, 'ramdisk')])
|
||||
|
||||
if self.header_version <= 2:
|
||||
if self.second_size > 0:
|
||||
args.extend(['--second',
|
||||
os.path.join(self.image_dir, 'second')])
|
||||
if self.recovery_dtbo_size > 0:
|
||||
args.extend(['--recovery_dtbo',
|
||||
os.path.join(self.image_dir, 'recovery_dtbo')])
|
||||
if self.dtb_size > 0:
|
||||
args.extend(['--dtb', os.path.join(self.image_dir, 'dtb')])
|
||||
|
||||
args.extend(['--pagesize', f'{self.page_size:#010x}'])
|
||||
|
||||
# Kernel load address is base + kernel_offset in mkbootimg.py.
|
||||
# However we don't know the value of 'base' when unpacking a boot
|
||||
# image in this script, so we set 'base' to zero and 'kernel_offset'
|
||||
# to the kernel load address, 'ramdisk_offset' to the ramdisk load
|
||||
# address, ... etc.
|
||||
args.extend(['--base', f'{0:#010x}'])
|
||||
args.extend(['--kernel_offset',
|
||||
f'{self.kernel_load_address:#010x}'])
|
||||
args.extend(['--ramdisk_offset',
|
||||
f'{self.ramdisk_load_address:#010x}'])
|
||||
args.extend(['--second_offset',
|
||||
f'{self.second_load_address:#010x}'])
|
||||
args.extend(['--tags_offset', f'{self.tags_load_address:#010x}'])
|
||||
|
||||
# dtb is added in boot image v2, and is absent in v1 or v0.
|
||||
if self.header_version == 2:
|
||||
# dtb_offset is uint64_t.
|
||||
args.extend(['--dtb_offset', f'{self.dtb_load_address:#018x}'])
|
||||
|
||||
args.extend(['--board', self.product_name])
|
||||
args.extend(['--cmdline', self.cmdline + self.extra_cmdline])
|
||||
else:
|
||||
args.extend(['--cmdline', self.cmdline])
|
||||
|
||||
return args
|
||||
|
||||
|
||||
def unpack_boot_image(boot_img, output_dir):
|
||||
"""extracts kernel, ramdisk, second bootloader and recovery dtbo"""
|
||||
kernel_ramdisk_second_info = unpack('9I', args.boot_img.read(9 * 4))
|
||||
version = kernel_ramdisk_second_info[8]
|
||||
if version < 3:
|
||||
print('kernel_size: %s' % kernel_ramdisk_second_info[0])
|
||||
print('kernel load address: %#x' % kernel_ramdisk_second_info[1])
|
||||
print('ramdisk size: %s' % kernel_ramdisk_second_info[2])
|
||||
print('ramdisk load address: %#x' % kernel_ramdisk_second_info[3])
|
||||
print('second bootloader size: %s' % kernel_ramdisk_second_info[4])
|
||||
print('second bootloader load address: %#x' % kernel_ramdisk_second_info[5])
|
||||
print('kernel tags load address: %#x' % kernel_ramdisk_second_info[6])
|
||||
print('page size: %s' % kernel_ramdisk_second_info[7])
|
||||
print_os_version_patch_level(unpack('I', args.boot_img.read(1 * 4))[0])
|
||||
info = BootImageInfoFormatter()
|
||||
info.boot_magic = unpack('8s', boot_img.read(8))[0].decode()
|
||||
|
||||
kernel_ramdisk_second_info = unpack('9I', boot_img.read(9 * 4))
|
||||
# header_version is always at [8] regardless of the value of header_version.
|
||||
info.header_version = kernel_ramdisk_second_info[8]
|
||||
|
||||
if info.header_version < 3:
|
||||
info.kernel_size = kernel_ramdisk_second_info[0]
|
||||
info.kernel_load_address = kernel_ramdisk_second_info[1]
|
||||
info.ramdisk_size = kernel_ramdisk_second_info[2]
|
||||
info.ramdisk_load_address = kernel_ramdisk_second_info[3]
|
||||
info.second_size = kernel_ramdisk_second_info[4]
|
||||
info.second_load_address = kernel_ramdisk_second_info[5]
|
||||
info.tags_load_address = kernel_ramdisk_second_info[6]
|
||||
info.page_size = kernel_ramdisk_second_info[7]
|
||||
os_version_patch_level = unpack('I', boot_img.read(1 * 4))[0]
|
||||
else:
|
||||
print('kernel_size: %s' % kernel_ramdisk_second_info[0])
|
||||
print('ramdisk size: %s' % kernel_ramdisk_second_info[1])
|
||||
print_os_version_patch_level(kernel_ramdisk_second_info[2])
|
||||
info.kernel_size = kernel_ramdisk_second_info[0]
|
||||
info.ramdisk_size = kernel_ramdisk_second_info[1]
|
||||
os_version_patch_level = kernel_ramdisk_second_info[2]
|
||||
info.second_size = 0
|
||||
info.page_size = BOOT_IMAGE_HEADER_V3_PAGESIZE
|
||||
|
||||
print('boot image header version: %s' % version)
|
||||
info.os_version, info.os_patch_level = decode_os_version_patch_level(
|
||||
os_version_patch_level)
|
||||
|
||||
if version < 3:
|
||||
product_name = cstr(unpack('16s', args.boot_img.read(16))[0].decode())
|
||||
print('product name: %s' % product_name)
|
||||
cmdline = cstr(unpack('512s', args.boot_img.read(512))[0].decode())
|
||||
print('command line args: %s' % cmdline)
|
||||
if info.header_version < 3:
|
||||
info.product_name = cstr(unpack('16s',
|
||||
boot_img.read(16))[0].decode())
|
||||
info.cmdline = cstr(unpack('512s', boot_img.read(512))[0].decode())
|
||||
boot_img.read(32) # ignore SHA
|
||||
info.extra_cmdline = cstr(unpack('1024s',
|
||||
boot_img.read(1024))[0].decode())
|
||||
else:
|
||||
cmdline = cstr(unpack('1536s', args.boot_img.read(1536))[0].decode())
|
||||
print('command line args: %s' % cmdline)
|
||||
info.cmdline = cstr(unpack('1536s',
|
||||
boot_img.read(1536))[0].decode())
|
||||
|
||||
if version < 3:
|
||||
args.boot_img.read(32) # ignore SHA
|
||||
|
||||
if version < 3:
|
||||
extra_cmdline = cstr(unpack('1024s',
|
||||
args.boot_img.read(1024))[0].decode())
|
||||
print('additional command line args: %s' % extra_cmdline)
|
||||
|
||||
if version < 3:
|
||||
kernel_size = kernel_ramdisk_second_info[0]
|
||||
ramdisk_size = kernel_ramdisk_second_info[2]
|
||||
second_size = kernel_ramdisk_second_info[4]
|
||||
page_size = kernel_ramdisk_second_info[7]
|
||||
if info.header_version in {1, 2}:
|
||||
info.recovery_dtbo_size = unpack('I', boot_img.read(1 * 4))[0]
|
||||
info.recovery_dtbo_offset = unpack('Q', boot_img.read(8))[0]
|
||||
info.boot_header_size = unpack('I', boot_img.read(4))[0]
|
||||
else:
|
||||
kernel_size = kernel_ramdisk_second_info[0]
|
||||
ramdisk_size = kernel_ramdisk_second_info[1]
|
||||
second_size = 0
|
||||
page_size = BOOT_IMAGE_HEADER_V3_PAGESIZE
|
||||
info.recovery_dtbo_size = 0
|
||||
|
||||
if 0 < version < 3:
|
||||
recovery_dtbo_size = unpack('I', args.boot_img.read(1 * 4))[0]
|
||||
print('recovery dtbo size: %s' % recovery_dtbo_size)
|
||||
recovery_dtbo_offset = unpack('Q', args.boot_img.read(8))[0]
|
||||
print('recovery dtbo offset: %#x' % recovery_dtbo_offset)
|
||||
boot_header_size = unpack('I', args.boot_img.read(4))[0]
|
||||
print('boot header size: %s' % boot_header_size)
|
||||
if info.header_version == 2:
|
||||
info.dtb_size = unpack('I', boot_img.read(4))[0]
|
||||
info.dtb_load_address = unpack('Q', boot_img.read(8))[0]
|
||||
else:
|
||||
recovery_dtbo_size = 0
|
||||
if 1 < version < 3:
|
||||
dtb_size = unpack('I', args.boot_img.read(4))[0]
|
||||
print('dtb size: %s' % dtb_size)
|
||||
dtb_load_address = unpack('Q', args.boot_img.read(8))[0]
|
||||
print('dtb address: %#x' % dtb_load_address)
|
||||
else:
|
||||
dtb_size = 0
|
||||
info.dtb_size = 0
|
||||
info.dtb_load_address = 0
|
||||
|
||||
if info.header_version >= 4:
|
||||
info.boot_signature_size = unpack('I', boot_img.read(4))[0]
|
||||
else:
|
||||
info.boot_signature_size = 0
|
||||
|
||||
# The first page contains the boot header
|
||||
num_header_pages = 1
|
||||
|
||||
num_kernel_pages = get_number_of_pages(kernel_size, page_size)
|
||||
kernel_offset = page_size * num_header_pages # header occupies a page
|
||||
image_info_list = [(kernel_offset, kernel_size, 'kernel')]
|
||||
# Convenient shorthand.
|
||||
page_size = info.page_size
|
||||
|
||||
num_ramdisk_pages = get_number_of_pages(ramdisk_size, page_size)
|
||||
num_kernel_pages = get_number_of_pages(info.kernel_size, page_size)
|
||||
kernel_offset = page_size * num_header_pages # header occupies a page
|
||||
image_info_list = [(kernel_offset, info.kernel_size, 'kernel')]
|
||||
|
||||
num_ramdisk_pages = get_number_of_pages(info.ramdisk_size, page_size)
|
||||
ramdisk_offset = page_size * (num_header_pages + num_kernel_pages
|
||||
) # header + kernel
|
||||
image_info_list.append((ramdisk_offset, ramdisk_size, 'ramdisk'))
|
||||
image_info_list.append((ramdisk_offset, info.ramdisk_size, 'ramdisk'))
|
||||
|
||||
if second_size > 0:
|
||||
if info.second_size > 0:
|
||||
second_offset = page_size * (
|
||||
num_header_pages + num_kernel_pages + num_ramdisk_pages
|
||||
) # header + kernel + ramdisk
|
||||
image_info_list.append((second_offset, second_size, 'second'))
|
||||
image_info_list.append((second_offset, info.second_size, 'second'))
|
||||
|
||||
if recovery_dtbo_size > 0:
|
||||
image_info_list.append((recovery_dtbo_offset, recovery_dtbo_size,
|
||||
if info.recovery_dtbo_size > 0:
|
||||
image_info_list.append((info.recovery_dtbo_offset,
|
||||
info.recovery_dtbo_size,
|
||||
'recovery_dtbo'))
|
||||
if dtb_size > 0:
|
||||
num_second_pages = get_number_of_pages(second_size, page_size)
|
||||
num_recovery_dtbo_pages = get_number_of_pages(recovery_dtbo_size, page_size)
|
||||
if info.dtb_size > 0:
|
||||
num_second_pages = get_number_of_pages(info.second_size, page_size)
|
||||
num_recovery_dtbo_pages = get_number_of_pages(
|
||||
info.recovery_dtbo_size, page_size)
|
||||
dtb_offset = page_size * (
|
||||
num_header_pages + num_kernel_pages + num_ramdisk_pages + num_second_pages +
|
||||
num_recovery_dtbo_pages
|
||||
)
|
||||
num_header_pages + num_kernel_pages + num_ramdisk_pages +
|
||||
num_second_pages + num_recovery_dtbo_pages)
|
||||
|
||||
image_info_list.append((dtb_offset, dtb_size, 'dtb'))
|
||||
image_info_list.append((dtb_offset, info.dtb_size, 'dtb'))
|
||||
|
||||
for image_info in image_info_list:
|
||||
extract_image(image_info[0], image_info[1], args.boot_img,
|
||||
os.path.join(args.out, image_info[2]))
|
||||
if info.boot_signature_size > 0:
|
||||
# boot signature only exists in boot.img version >= v4.
|
||||
# There are only kernel and ramdisk pages before the signature.
|
||||
boot_signature_offset = page_size * (
|
||||
num_header_pages + num_kernel_pages + num_ramdisk_pages)
|
||||
|
||||
image_info_list.append((boot_signature_offset, info.boot_signature_size,
|
||||
'boot_signature'))
|
||||
|
||||
create_out_dir(output_dir)
|
||||
for offset, size, name in image_info_list:
|
||||
extract_image(offset, size, boot_img, os.path.join(output_dir, name))
|
||||
info.image_dir = output_dir
|
||||
|
||||
return info
|
||||
|
||||
|
||||
def unpack_vendor_bootimage(args):
|
||||
kernel_ramdisk_info = unpack('5I', args.boot_img.read(5 * 4))
|
||||
print('vendor boot image header version: %s' % kernel_ramdisk_info[0])
|
||||
print('kernel load address: %#x' % kernel_ramdisk_info[2])
|
||||
print('ramdisk load address: %#x' % kernel_ramdisk_info[3])
|
||||
print('vendor ramdisk size: %s' % kernel_ramdisk_info[4])
|
||||
class VendorBootImageInfoFormatter:
|
||||
"""Formats the vendor_boot image info."""
|
||||
|
||||
cmdline = cstr(unpack('2048s', args.boot_img.read(2048))[0].decode())
|
||||
print('vendor command line args: %s' % cmdline)
|
||||
def format_pretty_text(self):
|
||||
lines = []
|
||||
lines.append(f'boot magic: {self.boot_magic}')
|
||||
lines.append(f'vendor boot image header version: {self.header_version}')
|
||||
lines.append(f'page size: {self.page_size:#010x}')
|
||||
lines.append(f'kernel load address: {self.kernel_load_address:#010x}')
|
||||
lines.append(f'ramdisk load address: {self.ramdisk_load_address:#010x}')
|
||||
if self.header_version > 3:
|
||||
lines.append(
|
||||
f'vendor ramdisk total size: {self.vendor_ramdisk_size}')
|
||||
else:
|
||||
lines.append(f'vendor ramdisk size: {self.vendor_ramdisk_size}')
|
||||
lines.append(f'vendor command line args: {self.cmdline}')
|
||||
lines.append(
|
||||
f'kernel tags load address: {self.tags_load_address:#010x}')
|
||||
lines.append(f'product name: {self.product_name}')
|
||||
lines.append(f'vendor boot image header size: {self.header_size}')
|
||||
lines.append(f'dtb size: {self.dtb_size}')
|
||||
lines.append(f'dtb address: {self.dtb_load_address:#018x}')
|
||||
if self.header_version > 3:
|
||||
lines.append(
|
||||
f'vendor ramdisk table size: {self.vendor_ramdisk_table_size}')
|
||||
lines.append('vendor ramdisk table: [')
|
||||
indent = lambda level: ' ' * 4 * level
|
||||
for entry in self.vendor_ramdisk_table:
|
||||
(output_ramdisk_name, ramdisk_size, ramdisk_offset,
|
||||
ramdisk_type, ramdisk_name, board_id) = entry
|
||||
lines.append(indent(1) + f'{output_ramdisk_name}: ''{')
|
||||
lines.append(indent(2) + f'size: {ramdisk_size}')
|
||||
lines.append(indent(2) + f'offset: {ramdisk_offset}')
|
||||
lines.append(indent(2) + f'type: {ramdisk_type:#x}')
|
||||
lines.append(indent(2) + f'name: {ramdisk_name}')
|
||||
lines.append(indent(2) + 'board_id: [')
|
||||
stride = 4
|
||||
for row_idx in range(0, len(board_id), stride):
|
||||
row = board_id[row_idx:row_idx + stride]
|
||||
lines.append(
|
||||
indent(3) + ' '.join(f'{e:#010x},' for e in row))
|
||||
lines.append(indent(2) + ']')
|
||||
lines.append(indent(1) + '}')
|
||||
lines.append(']')
|
||||
lines.append(
|
||||
f'vendor bootconfig size: {self.vendor_bootconfig_size}')
|
||||
|
||||
tags_load_address = unpack('I', args.boot_img.read(1 * 4))[0]
|
||||
print('kernel tags load address: %#x' % tags_load_address)
|
||||
return '\n'.join(lines)
|
||||
|
||||
product_name = cstr(unpack('16s', args.boot_img.read(16))[0].decode())
|
||||
print('product name: %s' % product_name)
|
||||
def format_mkbootimg_argument(self):
|
||||
args = []
|
||||
args.extend(['--header_version', str(self.header_version)])
|
||||
args.extend(['--pagesize', f'{self.page_size:#010x}'])
|
||||
args.extend(['--base', f'{0:#010x}'])
|
||||
args.extend(['--kernel_offset', f'{self.kernel_load_address:#010x}'])
|
||||
args.extend(['--ramdisk_offset', f'{self.ramdisk_load_address:#010x}'])
|
||||
args.extend(['--tags_offset', f'{self.tags_load_address:#010x}'])
|
||||
args.extend(['--dtb_offset', f'{self.dtb_load_address:#018x}'])
|
||||
args.extend(['--vendor_cmdline', self.cmdline])
|
||||
args.extend(['--board', self.product_name])
|
||||
|
||||
dtb_size = unpack('2I', args.boot_img.read(2 * 4))[1]
|
||||
print('dtb size: %s' % dtb_size)
|
||||
dtb_load_address = unpack('Q', args.boot_img.read(8))[0]
|
||||
print('dtb address: %#x' % dtb_load_address)
|
||||
if self.dtb_size > 0:
|
||||
args.extend(['--dtb', os.path.join(self.image_dir, 'dtb')])
|
||||
|
||||
ramdisk_size = kernel_ramdisk_info[4]
|
||||
page_size = kernel_ramdisk_info[1]
|
||||
if self.header_version > 3:
|
||||
args.extend(['--vendor_bootconfig',
|
||||
os.path.join(self.image_dir, 'bootconfig')])
|
||||
|
||||
for entry in self.vendor_ramdisk_table:
|
||||
(output_ramdisk_name, _, _, ramdisk_type,
|
||||
ramdisk_name, board_id) = entry
|
||||
args.extend(['--ramdisk_type', str(ramdisk_type)])
|
||||
args.extend(['--ramdisk_name', ramdisk_name])
|
||||
for idx, e in enumerate(board_id):
|
||||
if e:
|
||||
args.extend([f'--board_id{idx}', f'{e:#010x}'])
|
||||
vendor_ramdisk_path = os.path.join(
|
||||
self.image_dir, output_ramdisk_name)
|
||||
args.extend(['--vendor_ramdisk_fragment', vendor_ramdisk_path])
|
||||
else:
|
||||
args.extend(['--vendor_ramdisk',
|
||||
os.path.join(self.image_dir, 'vendor_ramdisk')])
|
||||
|
||||
return args
|
||||
|
||||
|
||||
def unpack_vendor_boot_image(boot_img, output_dir):
|
||||
info = VendorBootImageInfoFormatter()
|
||||
info.boot_magic = unpack('8s', boot_img.read(8))[0].decode()
|
||||
info.header_version = unpack('I', boot_img.read(4))[0]
|
||||
info.page_size = unpack('I', boot_img.read(4))[0]
|
||||
info.kernel_load_address = unpack('I', boot_img.read(4))[0]
|
||||
info.ramdisk_load_address = unpack('I', boot_img.read(4))[0]
|
||||
info.vendor_ramdisk_size = unpack('I', boot_img.read(4))[0]
|
||||
info.cmdline = cstr(unpack('2048s', boot_img.read(2048))[0].decode())
|
||||
info.tags_load_address = unpack('I', boot_img.read(4))[0]
|
||||
info.product_name = cstr(unpack('16s', boot_img.read(16))[0].decode())
|
||||
info.header_size = unpack('I', boot_img.read(4))[0]
|
||||
info.dtb_size = unpack('I', boot_img.read(4))[0]
|
||||
info.dtb_load_address = unpack('Q', boot_img.read(8))[0]
|
||||
|
||||
# Convenient shorthand.
|
||||
page_size = info.page_size
|
||||
# The first pages contain the boot header
|
||||
num_boot_header_pages = get_number_of_pages(VENDOR_BOOT_IMAGE_HEADER_V3_SIZE, page_size)
|
||||
num_boot_ramdisk_pages = get_number_of_pages(ramdisk_size, page_size)
|
||||
ramdisk_offset = page_size * num_boot_header_pages
|
||||
image_info_list = [(ramdisk_offset, ramdisk_size, 'vendor_ramdisk')]
|
||||
num_boot_header_pages = get_number_of_pages(info.header_size, page_size)
|
||||
num_boot_ramdisk_pages = get_number_of_pages(
|
||||
info.vendor_ramdisk_size, page_size)
|
||||
num_boot_dtb_pages = get_number_of_pages(info.dtb_size, page_size)
|
||||
|
||||
ramdisk_offset_base = page_size * num_boot_header_pages
|
||||
image_info_list = []
|
||||
|
||||
if info.header_version > 3:
|
||||
info.vendor_ramdisk_table_size = unpack('I', boot_img.read(4))[0]
|
||||
vendor_ramdisk_table_entry_num = unpack('I', boot_img.read(4))[0]
|
||||
vendor_ramdisk_table_entry_size = unpack('I', boot_img.read(4))[0]
|
||||
info.vendor_bootconfig_size = unpack('I', boot_img.read(4))[0]
|
||||
num_vendor_ramdisk_table_pages = get_number_of_pages(
|
||||
info.vendor_ramdisk_table_size, page_size)
|
||||
vendor_ramdisk_table_offset = page_size * (
|
||||
num_boot_header_pages + num_boot_ramdisk_pages + num_boot_dtb_pages)
|
||||
|
||||
vendor_ramdisk_table = []
|
||||
vendor_ramdisk_symlinks = []
|
||||
for idx in range(vendor_ramdisk_table_entry_num):
|
||||
entry_offset = vendor_ramdisk_table_offset + (
|
||||
vendor_ramdisk_table_entry_size * idx)
|
||||
boot_img.seek(entry_offset)
|
||||
ramdisk_size = unpack('I', boot_img.read(4))[0]
|
||||
ramdisk_offset = unpack('I', boot_img.read(4))[0]
|
||||
ramdisk_type = unpack('I', boot_img.read(4))[0]
|
||||
ramdisk_name = cstr(unpack(
|
||||
f'{VENDOR_RAMDISK_NAME_SIZE}s',
|
||||
boot_img.read(VENDOR_RAMDISK_NAME_SIZE))[0].decode())
|
||||
board_id = unpack(
|
||||
f'{VENDOR_RAMDISK_TABLE_ENTRY_BOARD_ID_SIZE}I',
|
||||
boot_img.read(
|
||||
4 * VENDOR_RAMDISK_TABLE_ENTRY_BOARD_ID_SIZE))
|
||||
output_ramdisk_name = f'vendor_ramdisk{idx:02}'
|
||||
|
||||
image_info_list.append((ramdisk_offset_base + ramdisk_offset,
|
||||
ramdisk_size, output_ramdisk_name))
|
||||
vendor_ramdisk_symlinks.append((output_ramdisk_name, ramdisk_name))
|
||||
vendor_ramdisk_table.append(
|
||||
(output_ramdisk_name, ramdisk_size, ramdisk_offset,
|
||||
ramdisk_type, ramdisk_name, board_id))
|
||||
|
||||
info.vendor_ramdisk_table = vendor_ramdisk_table
|
||||
|
||||
bootconfig_offset = page_size * (num_boot_header_pages
|
||||
+ num_boot_ramdisk_pages + num_boot_dtb_pages
|
||||
+ num_vendor_ramdisk_table_pages)
|
||||
image_info_list.append((bootconfig_offset, info.vendor_bootconfig_size,
|
||||
'bootconfig'))
|
||||
else:
|
||||
image_info_list.append(
|
||||
(ramdisk_offset_base, info.vendor_ramdisk_size, 'vendor_ramdisk'))
|
||||
|
||||
dtb_offset = page_size * (num_boot_header_pages + num_boot_ramdisk_pages
|
||||
) # header + vendor_ramdisk
|
||||
image_info_list.append((dtb_offset, dtb_size, 'dtb'))
|
||||
if info.dtb_size > 0:
|
||||
image_info_list.append((dtb_offset, info.dtb_size, 'dtb'))
|
||||
|
||||
for image_info in image_info_list:
|
||||
extract_image(image_info[0], image_info[1], args.boot_img,
|
||||
os.path.join(args.out, image_info[2]))
|
||||
create_out_dir(output_dir)
|
||||
for offset, size, name in image_info_list:
|
||||
extract_image(offset, size, boot_img, os.path.join(output_dir, name))
|
||||
info.image_dir = output_dir
|
||||
|
||||
if info.header_version > 3:
|
||||
vendor_ramdisk_by_name_dir = os.path.join(
|
||||
output_dir, 'vendor-ramdisk-by-name')
|
||||
create_out_dir(vendor_ramdisk_by_name_dir)
|
||||
for src, dst in vendor_ramdisk_symlinks:
|
||||
src_pathname = os.path.join('..', src)
|
||||
dst_pathname = os.path.join(
|
||||
vendor_ramdisk_by_name_dir, f'ramdisk_{dst}')
|
||||
if os.path.lexists(dst_pathname):
|
||||
os.remove(dst_pathname)
|
||||
os.symlink(src_pathname, dst_pathname)
|
||||
|
||||
return info
|
||||
|
||||
|
||||
def unpack_image(args):
|
||||
boot_magic = unpack('8s', args.boot_img.read(8))[0].decode()
|
||||
print('boot_magic: %s' % boot_magic)
|
||||
if boot_magic == "ANDROID!":
|
||||
unpack_bootimage(args)
|
||||
elif boot_magic == "VNDRBOOT":
|
||||
unpack_vendor_bootimage(args)
|
||||
def unpack_bootimg(boot_img, output_dir):
|
||||
"""Unpacks the |boot_img| to |output_dir|, and returns the 'info' object."""
|
||||
with open(boot_img, 'rb') as image_file:
|
||||
boot_magic = unpack('8s', image_file.read(8))[0].decode()
|
||||
image_file.seek(0)
|
||||
if boot_magic == 'ANDROID!':
|
||||
info = unpack_boot_image(image_file, output_dir)
|
||||
elif boot_magic == 'VNDRBOOT':
|
||||
info = unpack_vendor_boot_image(image_file, output_dir)
|
||||
else:
|
||||
raise ValueError(f'Not an Android boot image, magic: {boot_magic}')
|
||||
|
||||
return info
|
||||
|
||||
|
||||
def print_bootimg_info(info, output_format, null_separator):
|
||||
"""Format and print boot image info."""
|
||||
if output_format == 'mkbootimg':
|
||||
mkbootimg_args = info.format_mkbootimg_argument()
|
||||
if null_separator:
|
||||
print('\0'.join(mkbootimg_args) + '\0', end='')
|
||||
else:
|
||||
print(shlex.join(mkbootimg_args))
|
||||
else:
|
||||
print(info.format_pretty_text())
|
||||
|
||||
|
||||
def get_unpack_usage():
|
||||
return """Output format:
|
||||
|
||||
* info
|
||||
|
||||
Pretty-printed info-rich text format suitable for human inspection.
|
||||
|
||||
* mkbootimg
|
||||
|
||||
Output shell-escaped (quoted) argument strings that can be used to
|
||||
reconstruct the boot image. For example:
|
||||
|
||||
$ unpack_bootimg --boot_img vendor_boot.img --out out --format=mkbootimg |
|
||||
tee mkbootimg_args
|
||||
$ sh -c "mkbootimg $(cat mkbootimg_args) --vendor_boot repacked.img"
|
||||
|
||||
vendor_boot.img and repacked.img would be equivalent.
|
||||
|
||||
If the -0 option is specified, output unescaped null-terminated argument
|
||||
strings that are suitable to be parsed by a shell script (xargs -0 format):
|
||||
|
||||
$ unpack_bootimg --boot_img vendor_boot.img --out out --format=mkbootimg \\
|
||||
-0 | tee mkbootimg_args
|
||||
$ declare -a MKBOOTIMG_ARGS=()
|
||||
$ while IFS= read -r -d '' ARG; do
|
||||
MKBOOTIMG_ARGS+=("${ARG}")
|
||||
done <mkbootimg_args
|
||||
$ mkbootimg "${MKBOOTIMG_ARGS[@]}" --vendor_boot repacked.img
|
||||
"""
|
||||
|
||||
|
||||
def parse_cmdline():
|
||||
"""parse command line arguments"""
|
||||
parser = ArgumentParser(
|
||||
description='Unpacks boot.img/recovery.img, extracts the kernel,'
|
||||
'ramdisk, second bootloader, recovery dtbo and dtb')
|
||||
parser.add_argument(
|
||||
'--boot_img',
|
||||
help='path to boot image',
|
||||
type=FileType('rb'),
|
||||
required=True)
|
||||
parser.add_argument('--out', help='path to out binaries', default='out')
|
||||
formatter_class=RawDescriptionHelpFormatter,
|
||||
description='Unpacks boot, recovery or vendor_boot image.',
|
||||
epilog=get_unpack_usage(),
|
||||
)
|
||||
parser.add_argument('--boot_img', required=True,
|
||||
help='path to the boot, recovery or vendor_boot image')
|
||||
parser.add_argument('--out', default='out',
|
||||
help='output directory of the unpacked images')
|
||||
parser.add_argument('--format', choices=['info', 'mkbootimg'],
|
||||
default='info',
|
||||
help='text output format (default: info)')
|
||||
parser.add_argument('-0', '--null', action='store_true',
|
||||
help='output null-terminated argument strings')
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def main():
|
||||
"""parse arguments and unpack boot image"""
|
||||
args = parse_cmdline()
|
||||
create_out_dir(args.out)
|
||||
unpack_image(args)
|
||||
info = unpack_bootimg(args.boot_img, args.out)
|
||||
print_bootimg_info(info, args.format, args.null)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
Reference in New Issue
Block a user