Snap for 8426163 from 41bec4c46b0045ba68939eb0689fddd416a2171a to mainline-tzdata2-release
Change-Id: I40b6fc364b72ed23a0ca541baa3893922d72ba76
diff --git a/.gitignore b/.gitignore
deleted file mode 100644
index bee8a64..0000000
--- a/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-__pycache__
diff --git a/Android.bp b/Android.bp
index 23c55b8..c3cf746 100644
--- a/Android.bp
+++ b/Android.bp
@@ -1,9 +1,5 @@
// Copyright 2012 The Android Open Source Project
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
cc_library_headers {
name: "libmkbootimg_abi_headers",
vendor_available: true,
@@ -21,10 +17,6 @@
enabled: true,
},
},
- apex_available: [
- "//apex_available:platform",
- "com.android.virt",
- ],
}
cc_library {
@@ -42,14 +34,16 @@
python_defaults {
name: "mkbootimg_defaults",
+
version: {
py2: {
- enabled: false,
- },
- py3: {
enabled: true,
embedded_launcher: true,
},
+ py3: {
+ enabled: false,
+ embedded_launcher: false,
+ },
},
}
@@ -59,9 +53,6 @@
srcs: [
"mkbootimg.py",
],
- required: [
- "avbtool",
- ],
}
python_binary_host {
@@ -71,38 +62,3 @@
"unpack_bootimg.py",
],
}
-
-
-python_binary_host {
- name: "repack_bootimg",
- defaults: ["mkbootimg_defaults"],
- srcs: [
- "repack_bootimg.py",
- ],
- required: [
- "lz4",
- "minigzip",
- "mkbootfs",
- "mkbootimg",
- "toybox",
- "unpack_bootimg",
- ],
-}
-
-python_test_host {
- name: "mkbootimg_test",
- defaults: ["mkbootimg_defaults"],
- main: "tests/mkbootimg_test.py",
- srcs: [
- "tests/mkbootimg_test.py",
- ],
- data: [
- ":avbtool",
- ":mkbootimg",
- ":unpack_bootimg",
- "tests/data/*",
- ],
- test_options: {
- unit_test: true,
- },
-}
diff --git a/OWNERS b/OWNERS
index 51e09a2..de2d568 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1,3 +1,2 @@
hridya@google.com
smuckle@google.com
-yochiang@google.com
\ No newline at end of file
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 734a94d..d937da1 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -1,2 +1,3 @@
[Builtin Hooks]
-pylint = true
+pylint2 = true
+pylint3 = true
diff --git a/include/bootimg/bootimg.h b/include/bootimg/bootimg.h
index 8ad95a8..8c9f6ee 100644
--- a/include/bootimg/bootimg.h
+++ b/include/bootimg/bootimg.h
@@ -29,41 +29,8 @@
#define VENDOR_BOOT_ARGS_SIZE 2048
#define VENDOR_BOOT_NAME_SIZE 16
-#define VENDOR_RAMDISK_TYPE_NONE 0
-#define VENDOR_RAMDISK_TYPE_PLATFORM 1
-#define VENDOR_RAMDISK_TYPE_RECOVERY 2
-#define VENDOR_RAMDISK_TYPE_DLKM 3
-#define VENDOR_RAMDISK_NAME_SIZE 32
-#define VENDOR_RAMDISK_TABLE_ENTRY_BOARD_ID_SIZE 16
-
-/* When a boot header is of version 0, the structure of boot image is as
- * follows:
- *
- * +-----------------+
- * | boot header | 1 page
- * +-----------------+
- * | kernel | n pages
- * +-----------------+
- * | ramdisk | m pages
- * +-----------------+
- * | second stage | o pages
- * +-----------------+
- *
- * n = (kernel_size + page_size - 1) / page_size
- * m = (ramdisk_size + page_size - 1) / page_size
- * o = (second_size + page_size - 1) / page_size
- *
- * 0. all entities are page_size aligned in flash
- * 1. kernel and ramdisk are required (size != 0)
- * 2. second is optional (second_size == 0 -> no second)
- * 3. load each element (kernel, ramdisk, second) at
- * the specified physical address (kernel_addr, etc)
- * 4. prepare tags at tag_addr. kernel_args[] is
- * appended to the kernel commandline in the tags.
- * 5. r0 = 0, r1 = MACHINE_TYPE, r2 = tags_addr
- * 6. if second_size != 0: jump to second_addr
- * else: jump to kernel_addr
- */
+// The bootloader expects the structure of boot_img_hdr with header
+// version 0 to be as follows:
struct boot_img_hdr_v0 {
// Must be BOOT_MAGIC.
uint8_t magic[BOOT_MAGIC_SIZE];
@@ -103,13 +70,12 @@
uint8_t name[BOOT_NAME_SIZE]; /* asciiz product name */
- uint8_t cmdline[BOOT_ARGS_SIZE]; /* asciiz kernel commandline */
+ uint8_t cmdline[BOOT_ARGS_SIZE];
uint32_t id[8]; /* timestamp / checksum / sha1 / etc */
// Supplemental command line data; kept here to maintain
// binary compatibility with older versions of mkbootimg.
- // Asciiz.
uint8_t extra_cmdline[BOOT_EXTRA_ARGS_SIZE];
} __attribute__((packed));
@@ -119,40 +85,35 @@
*/
typedef struct boot_img_hdr_v0 boot_img_hdr;
-/* When a boot header is of version 1, the structure of boot image is as
+/* When a boot header is of version 0, the structure of boot image is as
* follows:
*
- * +---------------------+
- * | boot header | 1 page
- * +---------------------+
- * | kernel | n pages
- * +---------------------+
- * | ramdisk | m pages
- * +---------------------+
- * | second stage | o pages
- * +---------------------+
- * | recovery dtbo/acpio | p pages
- * +---------------------+
+ * +-----------------+
+ * | boot header | 1 page
+ * +-----------------+
+ * | kernel | n pages
+ * +-----------------+
+ * | ramdisk | m pages
+ * +-----------------+
+ * | second stage | o pages
+ * +-----------------+
*
* n = (kernel_size + page_size - 1) / page_size
* m = (ramdisk_size + page_size - 1) / page_size
* o = (second_size + page_size - 1) / page_size
- * p = (recovery_dtbo_size + page_size - 1) / page_size
*
* 0. all entities are page_size aligned in flash
* 1. kernel and ramdisk are required (size != 0)
- * 2. recovery_dtbo/recovery_acpio is required for recovery.img in non-A/B
- * devices(recovery_dtbo_size != 0)
- * 3. second is optional (second_size == 0 -> no second)
- * 4. load each element (kernel, ramdisk, second) at
+ * 2. second is optional (second_size == 0 -> no second)
+ * 3. load each element (kernel, ramdisk, second) at
* the specified physical address (kernel_addr, etc)
- * 5. If booting to recovery mode in a non-A/B device, extract recovery
- * dtbo/acpio and apply the correct set of overlays on the base device tree
- * depending on the hardware/product revision.
- * 6. set up registers for kernel entry as required by your architecture
- * 7. if second_size != 0: jump to second_addr
+ * 4. prepare tags at tag_addr. kernel_args[] is
+ * appended to the kernel commandline in the tags.
+ * 5. r0 = 0, r1 = MACHINE_TYPE, r2 = tags_addr
+ * 6. if second_size != 0: jump to second_addr
* else: jump to kernel_addr
*/
+
struct boot_img_hdr_v1 : public boot_img_hdr_v0 {
uint32_t recovery_dtbo_size; /* size in bytes for recovery DTBO/ACPIO image */
uint64_t recovery_dtbo_offset; /* offset to recovery dtbo/acpio in boot image */
@@ -279,7 +240,6 @@
// Version of the boot image header.
uint32_t header_version;
- // Asciiz kernel commandline.
uint8_t cmdline[BOOT_ARGS_SIZE + BOOT_EXTRA_ARGS_SIZE];
} __attribute__((packed));
@@ -297,7 +257,7 @@
uint32_t vendor_ramdisk_size; /* size in bytes */
- uint8_t cmdline[VENDOR_BOOT_ARGS_SIZE]; /* asciiz kernel commandline */
+ uint8_t cmdline[VENDOR_BOOT_ARGS_SIZE];
uint32_t tags_addr; /* physical addr for kernel tags (if required) */
uint8_t name[VENDOR_BOOT_NAME_SIZE]; /* asciiz product name */
@@ -307,114 +267,3 @@
uint32_t dtb_size; /* size in bytes for DTB image */
uint64_t dtb_addr; /* physical load address for DTB image */
} __attribute__((packed));
-
-/* When the boot image header has a version of 4, the structure of the boot
- * image is as follows:
- *
- * +---------------------+
- * | boot header | 4096 bytes
- * +---------------------+
- * | kernel | m pages
- * +---------------------+
- * | ramdisk | n pages
- * +---------------------+
- * | boot signature | g pages
- * +---------------------+
- *
- * m = (kernel_size + 4096 - 1) / 4096
- * n = (ramdisk_size + 4096 - 1) / 4096
- * g = (signature_size + 4096 - 1) / 4096
- *
- * Note that in version 4 of the boot image header, page size is fixed at 4096
- * bytes.
- *
- * The structure of the vendor boot image version 4, which is required to be
- * present when a version 4 boot image is used, is as follows:
- *
- * +------------------------+
- * | vendor boot header | o pages
- * +------------------------+
- * | vendor ramdisk section | p pages
- * +------------------------+
- * | dtb | q pages
- * +------------------------+
- * | vendor ramdisk table | r pages
- * +------------------------+
- * | bootconfig | s pages
- * +------------------------+
- *
- * o = (2128 + page_size - 1) / page_size
- * p = (vendor_ramdisk_size + page_size - 1) / page_size
- * q = (dtb_size + page_size - 1) / page_size
- * r = (vendor_ramdisk_table_size + page_size - 1) / page_size
- * s = (vendor_bootconfig_size + page_size - 1) / page_size
- *
- * Note that in version 4 of the vendor boot image, multiple vendor ramdisks can
- * be included in the vendor boot image. The bootloader can select a subset of
- * ramdisks to load at runtime. To help the bootloader select the ramdisks, each
- * ramdisk is tagged with a type tag and a set of hardware identifiers
- * describing the board, soc or platform that this ramdisk is intended for.
- *
- * The vendor ramdisk section is consist of multiple ramdisk images concatenated
- * one after another, and vendor_ramdisk_size is the size of the section, which
- * is the total size of all the ramdisks included in the vendor boot image.
- *
- * The vendor ramdisk table holds the size, offset, type, name and hardware
- * identifiers of each ramdisk. The type field denotes the type of its content.
- * The vendor ramdisk names are unique. The hardware identifiers are specified
- * in the board_id field in each table entry. The board_id field is consist of a
- * vector of unsigned integer words, and the encoding scheme is defined by the
- * hardware vendor.
- *
- * For the different type of ramdisks, there are:
- * - VENDOR_RAMDISK_TYPE_NONE indicates the value is unspecified.
- * - VENDOR_RAMDISK_TYPE_PLATFORM ramdisks contain platform specific bits, so
- * the bootloader should always load these into memory.
- * - VENDOR_RAMDISK_TYPE_RECOVERY ramdisks contain recovery resources, so
- * the bootloader should load these when booting into recovery.
- * - VENDOR_RAMDISK_TYPE_DLKM ramdisks contain dynamic loadable kernel
- * modules.
- *
- * Version 4 of the vendor boot image also adds a bootconfig section to the end
- * of the image. This section contains Boot Configuration parameters known at
- * build time. The bootloader is responsible for placing this section directly
- * after the generic ramdisk, followed by the bootconfig trailer, before
- * entering the kernel.
- *
- * 0. all entities in the boot image are 4096-byte aligned in flash, all
- * entities in the vendor boot image are page_size (determined by the vendor
- * and specified in the vendor boot image header) aligned in flash
- * 1. kernel, ramdisk, and DTB are required (size != 0)
- * 2. load the kernel and DTB at the specified physical address (kernel_addr,
- * dtb_addr)
- * 3. load the vendor ramdisks at ramdisk_addr
- * 4. load the generic ramdisk immediately following the vendor ramdisk in
- * memory
- * 5. load the bootconfig immediately following the generic ramdisk. Add
- * additional bootconfig parameters followed by the bootconfig trailer.
- * 6. set up registers for kernel entry as required by your architecture
- * 7. if the platform has a second stage bootloader jump to it (must be
- * contained outside boot and vendor boot partitions), otherwise
- * jump to kernel_addr
- */
-struct boot_img_hdr_v4 : public boot_img_hdr_v3 {
- uint32_t signature_size; /* size in bytes */
-} __attribute__((packed));
-
-struct vendor_boot_img_hdr_v4 : public vendor_boot_img_hdr_v3 {
- uint32_t vendor_ramdisk_table_size; /* size in bytes for the vendor ramdisk table */
- uint32_t vendor_ramdisk_table_entry_num; /* number of entries in the vendor ramdisk table */
- uint32_t vendor_ramdisk_table_entry_size; /* size in bytes for a vendor ramdisk table entry */
- uint32_t bootconfig_size; /* size in bytes for the bootconfig section */
-} __attribute__((packed));
-
-struct vendor_ramdisk_table_entry_v4 {
- uint32_t ramdisk_size; /* size in bytes for the ramdisk image */
- uint32_t ramdisk_offset; /* offset to the ramdisk image in vendor ramdisk section */
- uint32_t ramdisk_type; /* type of the ramdisk */
- uint8_t ramdisk_name[VENDOR_RAMDISK_NAME_SIZE]; /* asciiz ramdisk name */
-
- // Hardware identifiers describing the board, soc or platform which this
- // ramdisk is intended to be loaded on.
- uint32_t board_id[VENDOR_RAMDISK_TABLE_ENTRY_BOARD_ID_SIZE];
-} __attribute__((packed));
diff --git a/mkbootimg.py b/mkbootimg.py
old mode 100755
new mode 100644
index e0b0839..00a4623
--- a/mkbootimg.py
+++ b/mkbootimg.py
@@ -1,5 +1,4 @@
-#!/usr/bin/env python3
-#
+#!/usr/bin/env python
# Copyright 2015, The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,55 +13,16 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-"""Creates the boot image."""
+from __future__ import print_function
-from argparse import (ArgumentParser, ArgumentTypeError,
- FileType, RawDescriptionHelpFormatter)
+from argparse import ArgumentParser, FileType, Action
from hashlib import sha1
from os import fstat
+import re
from struct import pack
-import array
-import collections
-import os
-import re
-import subprocess
-import tempfile
-# 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:
@@ -89,135 +49,87 @@
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_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
+def write_header_v3(args):
+ BOOT_IMAGE_HEADER_V3_SIZE = 1580
+ BOOT_MAGIC = 'ANDROID!'.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.
- args.output.write(pack('I', BOOT_IMAGE_V4_SIGNATURE_SIZE))
+ 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
+
+ args.output.write(pack('I', args.header_version)) # version of bootimage header
+ args.output.write(pack('1536s', args.cmdline.encode()))
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()
+
+ 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.')
-
- 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(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))
-
- # 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:
- 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)))
+ 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)
-
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)
+ BOOT_IMAGE_HEADER_V1_SIZE = 1648
+ BOOT_IMAGE_HEADER_V2_SIZE = 1660
+ BOOT_MAGIC = 'ANDROID!'.encode()
- 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)
+ 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)
- 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))
+ 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()))
sha = sha1()
update_sha(sha, args.kernel)
@@ -232,18 +144,14 @@
img_id = pack('32s', sha.digest())
args.output.write(img_id)
- args.output.write(pack(f'{BOOT_EXTRA_ARGS_SIZE}s', args.extra_cmdline))
+ args.output.write(pack('1024s', args.cmdline[512:].encode()))
if args.header_version > 0:
+ args.output.write(pack('I', filesize(args.recovery_dtbo))) # size in bytes
if args.recovery_dtbo:
- # 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)))
+ args.output.write(pack('Q', get_recovery_dtbo_offset(args))) # recovery dtbo offset
else:
- # Set to zero if no recovery dtbo
- args.output.write(pack('I', 0))
- args.output.write(pack('Q', 0))
+ args.output.write(pack('Q', 0)) # Will be set to 0 for devices without a recovery dtbo
# Populate boot image header size for header versions 1 and 2.
if args.header_version == 1:
@@ -252,101 +160,29 @@
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 AsciizBytes:
- """Parses a string and encodes it as an asciiz bytes object.
+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)
- >>> 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:
+ def __call__(self, parser, namespace, values, option_string=None):
+ if len(values) > self.maxlen:
raise ValueError(
- 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)
+ 'String argument too long: max {0:d}, got {1:d}'.format(self.maxlen, len(values)))
+ setattr(namespace, self.dest, values)
def write_padded_file(f_out, f_in, padding):
@@ -389,236 +225,49 @@
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():
- 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 = 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)
parser.add_argument('--vendor_cmdline',
- 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')
+ 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)
- 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('--gki_signing_algorithm',
- help='GKI signing algorithm to use')
- parser.add_argument('--gki_signing_key',
- help='path to RSA private key file')
- parser.add_argument('--gki_signing_signature_args',
- help='other hash arguments passed to avbtool')
- parser.add_argument('--gki_signing_avbtool_path',
- help='path to avbtool for boot signature generation')
- 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')
+ 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'))
- 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.
- """
- args.output.flush() # Flush the buffer for signature calculation.
-
- # Appends zeros if the signing key is not specified.
- if not args.gki_signing_key or not args.gki_signing_algorithm:
- zeros = b'\x00' * BOOT_IMAGE_V4_SIGNATURE_SIZE
- args.output.write(zeros)
- pad_file(args.output, pagesize)
- return
-
- avbtool = 'avbtool' # Used from otatools.zip or Android build env.
-
- # We need to specify the path of avbtool in build/core/Makefile.
- # Because avbtool is not guaranteed to be in $PATH there.
- if args.gki_signing_avbtool_path:
- avbtool = args.gki_signing_avbtool_path
-
- # 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', 'boot',
- '--partition_size', str(64 * 1024 * 1024),
- '--image', args.output.name,
- '--algorithm', args.gki_signing_algorithm,
- '--key', args.gki_signing_key,
- '--salt', 'd00df00d'] # TODO: use a hash of kernel/ramdisk as the salt.
-
- # Additional arguments passed to avbtool.
- if args.gki_signing_signature_args:
- avbtool_cmd += args.gki_signing_signature_args.split()
-
- # 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')
- avbtool_cmd += ['--do_not_append_vbmeta_image',
- '--output_vbmeta_image', boot_signature_output]
- subprocess.check_call(avbtool_cmd)
- with open(boot_signature_output, 'rb') as boot_signature:
- if filesize(boot_signature) > BOOT_IMAGE_V4_SIGNATURE_SIZE:
- raise ValueError(
- f'boot sigature size is > {BOOT_IMAGE_V4_SIGNATURE_SIZE}')
- write_padded_file(args.output, boot_signature, pagesize)
+ return parser.parse_args()
def write_data(args, pagesize):
@@ -630,44 +279,37 @@
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:
- add_boot_image_signature(args, pagesize)
def write_vendor_boot_data(args):
- 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)
+ 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 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:
+ if args.header_version < 3:
+ raise ValueError('--vendor_boot not compatible with given header version')
+ if 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:
- print('0x' + ''.join(f'{octet:02x}' for octet in img_id))
+ # 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))
if __name__ == '__main__':
diff --git a/pylintrc b/pylintrc
index b65d218..1990be7 100644
--- a/pylintrc
+++ b/pylintrc
@@ -1,33 +1,46 @@
-# This Pylint rcfile contains a best-effort configuration to uphold the
-# best-practices and style described in the Google Python style guide:
-# https://google.github.io/styleguide/pyguide.html
-#
-# Its canonical open-source location is:
-# https://google.github.io/styleguide/pylintrc
-
[MASTER]
-# Files or directories to be skipped. They should be base names, not paths.
-ignore=third_party
+# Specify a configuration file.
+#rcfile=
-# Files or directories matching the regex patterns are skipped. The regex
-# matches against base names, not paths.
-ignore-patterns=
+# Python code to execute, usually for sys.path manipulation such as
+# pygtk.require().
+#init-hook=
+
+# Profiled execution.
+profile=no
+
+# Add files or directories to the blacklist. They should be base names, not
+# paths.
+ignore=CVS
# Pickle collected data for later comparisons.
-persistent=no
+persistent=yes
# List of plugins (as comma separated values of python modules names) to load,
# usually to register additional checkers.
load-plugins=
# Use multiple processes to speed up Pylint.
-jobs=4
+jobs=1
# Allow loading of arbitrary C extensions. Extensions are imported into the
# active Python interpreter and may run arbitrary code.
unsafe-load-any-extension=no
+# A comma-separated list of package or module names from where C extensions may
+# be loaded. Extensions are loading into the active Python interpreter and may
+# run arbitrary code
+extension-pkg-whitelist=
+
+# Allow optimization of some AST trees. This will activate a peephole AST
+# optimizer, which will apply various small optimizations. For instance, it can
+# be used to obtain the result of joining multiple strings with the addition
+# operator. Joining a lot of strings can lead to a maximum recursion error in
+# Pylint and this flag can prevent that. It has one side effect, the resulting
+# AST will be different than the one from reality.
+optimize-ast=no
+
[MESSAGES CONTROL]
@@ -37,8 +50,7 @@
# Enable the message, report, category or checker with the given id(s). You can
# either give multiple identifier separated by comma (,) or put this option
-# multiple time (only on the command line, not in the configuration file where
-# it should appear only once). See also the "--disable" option for examples.
+# multiple time. See also the "--disable" option for examples.
#enable=
# Disable the message, report, category or checker with the given id(s). You
@@ -50,102 +62,7 @@
# --enable=similarities". If you want to run only the classes checker, but have
# no Warning level messages displayed, use"--disable=all --enable=classes
# --disable=W"
-disable=abstract-method,
- apply-builtin,
- arguments-differ,
- attribute-defined-outside-init,
- backtick,
- bad-option-value,
- basestring-builtin,
- buffer-builtin,
- c-extension-no-member,
- consider-using-enumerate,
- cmp-builtin,
- cmp-method,
- coerce-builtin,
- coerce-method,
- delslice-method,
- div-method,
- duplicate-code,
- eq-without-hash,
- execfile-builtin,
- file-builtin,
- filter-builtin-not-iterating,
- fixme,
- getslice-method,
- global-statement,
- hex-method,
- idiv-method,
- implicit-str-concat-in-sequence,
- import-error,
- import-self,
- import-star-module-level,
- inconsistent-return-statements,
- input-builtin,
- intern-builtin,
- invalid-str-codec,
- locally-disabled,
- long-builtin,
- long-suffix,
- map-builtin-not-iterating,
- misplaced-comparison-constant,
- missing-function-docstring,
- metaclass-assignment,
- next-method-called,
- next-method-defined,
- no-absolute-import,
- no-else-break,
- no-else-continue,
- no-else-raise,
- no-else-return,
- no-init, # added
- no-member,
- no-name-in-module,
- no-self-use,
- nonzero-method,
- oct-method,
- old-division,
- old-ne-operator,
- old-octal-literal,
- old-raise-syntax,
- parameter-unpacking,
- print-statement,
- raising-string,
- range-builtin-not-iterating,
- raw_input-builtin,
- rdiv-method,
- reduce-builtin,
- relative-import,
- reload-builtin,
- round-builtin,
- setslice-method,
- signature-differs,
- standarderror-builtin,
- suppressed-message,
- sys-max-int,
- too-few-public-methods,
- too-many-ancestors,
- too-many-arguments,
- too-many-boolean-expressions,
- too-many-branches,
- too-many-instance-attributes,
- too-many-locals,
- too-many-nested-blocks,
- too-many-public-methods,
- too-many-return-statements,
- too-many-statements,
- trailing-newlines,
- unichr-builtin,
- unicode-builtin,
- unnecessary-pass,
- unpacking-in-except,
- useless-else-on-loop,
- useless-object-inheritance,
- useless-suppression,
- using-cmp-argument,
- wrong-import-order,
- xrange-builtin,
- zip-builtin-not-iterating,
+disable=invalid-name,missing-docstring,too-many-branches,too-many-locals,too-many-arguments,too-many-statements,duplicate-code,too-few-public-methods,too-many-instance-attributes,too-many-lines,too-many-public-methods,locally-disabled,fixme,not-callable
[REPORTS]
@@ -157,12 +74,11 @@
# Put messages in a separate file for each module / package specified on the
# command line instead of printing them on stdout. Reports (if any) will be
-# written in a file name "pylint_global.[txt|html]". This option is deprecated
-# and it will be removed in Pylint 2.0.
+# written in a file name "pylint_global.[txt|html]".
files-output=no
# Tells whether to display a full report or only the messages
-reports=no
+reports=yes
# Python expression which should return a note less than 10 (10 is the highest
# note). You have access to the variables errors warning, statement which
@@ -171,177 +87,15 @@
# (RP0004).
evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)
+# Add a comment according to your evaluation note. This is used by the global
+# evaluation report (RP0004).
+comment=no
+
# Template used to display messages. This is a python new-style format string
# used to format the message information. See doc for all details
#msg-template=
-[BASIC]
-
-# Good variable names which should always be accepted, separated by a comma
-good-names=main,_
-
-# Bad variable names which should always be refused, separated by a comma
-bad-names=
-
-# Colon-delimited sets of names that determine each other's naming style when
-# the name regexes allow several styles.
-name-group=
-
-# Include a hint for the correct naming format with invalid-name
-include-naming-hint=no
-
-# List of decorators that produce properties, such as abc.abstractproperty. Add
-# to this list to register other decorators that produce valid properties.
-property-classes=abc.abstractproperty,cached_property.cached_property,cached_property.threaded_cached_property,cached_property.cached_property_with_ttl,cached_property.threaded_cached_property_with_ttl
-
-# Regular expression matching correct function names
-function-rgx=^(?:(?P<exempt>setUp|tearDown|setUpModule|tearDownModule)|(?P<camel_case>_?[A-Z][a-zA-Z0-9]*)|(?P<snake_case>_?[a-z][a-z0-9_]*))$
-
-# Regular expression matching correct variable names
-variable-rgx=^[a-z][a-z0-9_]*$
-
-# Regular expression matching correct constant names
-const-rgx=^(_?[A-Z][A-Z0-9_]*|__[a-z0-9_]+__|_?[a-z][a-z0-9_]*)$
-
-# Regular expression matching correct attribute names
-attr-rgx=^_{0,2}[a-z][a-z0-9_]*$
-
-# Regular expression matching correct argument names
-argument-rgx=^[a-z][a-z0-9_]*$
-
-# Regular expression matching correct class attribute names
-class-attribute-rgx=^(_?[A-Z][A-Z0-9_]*|__[a-z0-9_]+__|_?[a-z][a-z0-9_]*)$
-
-# Regular expression matching correct inline iteration names
-inlinevar-rgx=^[a-z][a-z0-9_]*$
-
-# Regular expression matching correct class names
-class-rgx=^_?[A-Z][a-zA-Z0-9]*$
-
-# Regular expression matching correct module names
-module-rgx=^(_?[a-z][a-z0-9_]*|__init__)$
-
-# Regular expression matching correct method names
-method-rgx=(?x)^(?:(?P<exempt>_[a-z0-9_]+__|runTest|setUp|tearDown|setUpTestCase|tearDownTestCase|setupSelf|tearDownClass|setUpClass|(test|assert)_*[A-Z0-9][a-zA-Z0-9_]*|next)|(?P<camel_case>_{0,2}[A-Z][a-zA-Z0-9_]*)|(?P<snake_case>_{0,2}[a-z][a-z0-9_]*))$
-
-# Regular expression which should only match function or class names that do
-# not require a docstring.
-no-docstring-rgx=(__.*__|main|test.*|.*test|.*Test)$
-
-# Minimum line length for functions/classes that require docstrings, shorter
-# ones are exempt.
-docstring-min-length=10
-
-
-[TYPECHECK]
-
-# List of decorators that produce context managers, such as
-# contextlib.contextmanager. Add to this list to register other decorators that
-# produce valid context managers.
-contextmanager-decorators=contextlib.contextmanager,contextlib2.contextmanager
-
-# Tells whether missing members accessed in mixin class should be ignored. A
-# mixin class is detected if its name ends with "mixin" (case insensitive).
-ignore-mixin-members=yes
-
-# List of module names for which member attributes should not be checked
-# (useful for modules/projects where namespaces are manipulated during runtime
-# and thus existing member attributes cannot be deduced by static analysis. It
-# supports qualified module names, as well as Unix pattern matching.
-ignored-modules=
-
-# List of class names for which member attributes should not be checked (useful
-# for classes with dynamically set attributes). This supports the use of
-# qualified names.
-ignored-classes=optparse.Values,thread._local,_thread._local
-
-# List of members which are set dynamically and missed by pylint inference
-# system, and so shouldn't trigger E1101 when accessed. Python regular
-# expressions are accepted.
-generated-members=
-
-
-[FORMAT]
-
-# Maximum number of characters on a single line.
-max-line-length=80
-
-# TODO(https://github.com/PyCQA/pylint/issues/3352): Direct pylint to exempt
-# lines made too long by directives to pytype.
-
-# Regexp for a line that is allowed to be longer than the limit.
-ignore-long-lines=(?x)(
- ^\s*(\#\ )?<?https?://\S+>?$|
- ^\s*(from\s+\S+\s+)?import\s+.+$)
-
-# Allow the body of an if to be on the same line as the test if there is no
-# else.
-single-line-if-stmt=yes
-
-# List of optional constructs for which whitespace checking is disabled. `dict-
-# separator` is used to allow tabulation in dicts, etc.: {1 : 1,\n222: 2}.
-# `trailing-comma` allows a space between comma and closing bracket: (a, ).
-# `empty-line` allows space-only lines.
-no-space-check=
-
-# Maximum number of lines in a module
-max-module-lines=99999
-
-# String used as indentation unit. The internal Google style guide mandates 2
-# spaces. Google's externaly-published style guide says 4, consistent with
-# PEP 8.
-indent-string=' '
-
-# Number of spaces of indent required inside a hanging or continued line.
-indent-after-paren=4
-
-# Expected format of line ending, e.g. empty (any line ending), LF or CRLF.
-expected-line-ending-format=LF
-
-
-[MISCELLANEOUS]
-
-# List of note tags to take in consideration, separated by a comma.
-notes=TODO
-
-
-[STRING]
-
-# This flag controls whether inconsistent-quotes generates a warning when the
-# character used as a quote delimiter is used inconsistently within a module.
-check-quote-consistency=yes
-
-
-[VARIABLES]
-
-# Tells whether we should check for unused import in __init__ files.
-init-import=no
-
-# A regular expression matching the name of dummy variables (i.e. expectedly
-# not used).
-dummy-variables-rgx=^\*{0,2}(_$|unused_|dummy_)
-
-# List of additional names supposed to be defined in builtins. Remember that
-# you should avoid to define new builtins when possible.
-additional-builtins=
-
-# List of strings which can identify a callback function by name. A callback
-# name must start or end with one of those strings.
-callbacks=cb_,_cb
-
-# List of qualified module names which can have objects that can redefine
-# builtins.
-redefining-builtins-modules=six,six.moves,past.builtins,future.builtins,functools
-
-
-[LOGGING]
-
-# Logging modules to check that the string format arguments are in logging
-# function parameter format
-logging-modules=logging,absl.logging,tensorflow.io.logging
-
-
[SIMILARITIES]
# Minimum lines number of a similarity.
@@ -357,6 +111,124 @@
ignore-imports=no
+[TYPECHECK]
+
+# Tells whether missing members accessed in mixin class should be ignored. A
+# mixin class is detected if its name ends with "mixin" (case insensitive).
+ignore-mixin-members=yes
+
+# List of module names for which member attributes should not be checked
+# (useful for modules/projects where namespaces are manipulated during runtime
+# and thus existing member attributes cannot be deduced by static analysis
+ignored-modules=
+
+# List of classes names for which member attributes should not be checked
+# (useful for classes with attributes dynamically set).
+ignored-classes=SQLObject
+
+# When zope mode is activated, add a predefined set of Zope acquired attributes
+# to generated-members.
+zope=no
+
+# List of members which are set dynamically and missed by pylint inference
+# system, and so shouldn't trigger E0201 when accessed. Python regular
+# expressions are accepted.
+generated-members=REQUEST,acl_users,aq_parent
+
+
+[MISCELLANEOUS]
+
+# List of note tags to take in consideration, separated by a comma.
+notes=FIXME,XXX,TODO
+
+
+[BASIC]
+
+# List of builtins function names that should not be used, separated by a comma
+bad-functions=map,filter,input
+
+# Good variable names which should always be accepted, separated by a comma
+good-names=i,j,k,ex,Run,_
+
+# Bad variable names which should always be refused, separated by a comma
+bad-names=foo,bar,baz,toto,tutu,tata
+
+# Colon-delimited sets of names that determine each other's naming style when
+# the name regexes allow several styles.
+name-group=
+
+# Include a hint for the correct naming format with invalid-name
+include-naming-hint=no
+
+# Regular expression matching correct function names
+function-rgx=[a-z_][a-z0-9_]{2,30}$
+
+# Naming hint for function names
+function-name-hint=[a-z_][a-z0-9_]{2,30}$
+
+# Regular expression matching correct variable names
+variable-rgx=[a-z_][a-z0-9_]{2,30}$
+
+# Naming hint for variable names
+variable-name-hint=[a-z_][a-z0-9_]{2,30}$
+
+# Regular expression matching correct constant names
+const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$
+
+# Naming hint for constant names
+const-name-hint=(([A-Z_][A-Z0-9_]*)|(__.*__))$
+
+# Regular expression matching correct attribute names
+attr-rgx=[a-z_][a-z0-9_]{2,30}$
+
+# Naming hint for attribute names
+attr-name-hint=[a-z_][a-z0-9_]{2,30}$
+
+# Regular expression matching correct argument names
+argument-rgx=[a-z_][a-z0-9_]{2,30}$
+
+# Naming hint for argument names
+argument-name-hint=[a-z_][a-z0-9_]{2,30}$
+
+# Regular expression matching correct class attribute names
+class-attribute-rgx=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$
+
+# Naming hint for class attribute names
+class-attribute-name-hint=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$
+
+# Regular expression matching correct inline iteration names
+inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$
+
+# Naming hint for inline iteration names
+inlinevar-name-hint=[A-Za-z_][A-Za-z0-9_]*$
+
+# Regular expression matching correct class names
+class-rgx=[A-Z_][a-zA-Z0-9]+$
+
+# Naming hint for class names
+class-name-hint=[A-Z_][a-zA-Z0-9]+$
+
+# Regular expression matching correct module names
+module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$
+
+# Naming hint for module names
+module-name-hint=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$
+
+# Regular expression matching correct method names
+method-rgx=[a-z_][a-z0-9_]{2,30}$
+
+# Naming hint for method names
+method-name-hint=[a-z_][a-z0-9_]{2,30}$
+
+# Regular expression which should only match function or class names that do
+# not require a docstring.
+no-docstring-rgx=__.*__
+
+# Minimum line length for functions/classes that require docstrings, shorter
+# ones are exempt.
+docstring-min-length=-1
+
+
[SPELLING]
# Spelling dictionary name. Available dictionaries: none. To make it working
@@ -374,14 +246,98 @@
spelling-store-unknown-words=no
+[FORMAT]
+
+# Maximum number of characters on a single line.
+max-line-length=100
+
+# Regexp for a line that is allowed to be longer than the limit.
+ignore-long-lines=^\s*(# )?<?https?://\S+>?$
+
+# Allow the body of an if to be on the same line as the test if there is no
+# else.
+single-line-if-stmt=no
+
+# List of optional constructs for which whitespace checking is disabled
+no-space-check=trailing-comma,dict-separator
+
+# Maximum number of lines in a module
+max-module-lines=1000
+
+# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1
+# tab).
+indent-string=' '
+
+# Number of spaces of indent required inside a hanging or continued line.
+indent-after-paren=4
+
+# Expected format of line ending, e.g. empty (any line ending), LF or CRLF.
+expected-line-ending-format=LF
+
+
+[LOGGING]
+
+# Logging modules to check that the string format arguments are in logging
+# function parameter format
+logging-modules=logging
+
+
+[VARIABLES]
+
+# Tells whether we should check for unused import in __init__ files.
+init-import=no
+
+# A regular expression matching the name of dummy variables (i.e. expectedly
+# not used).
+dummy-variables-rgx=_$|dummy
+
+# List of additional names supposed to be defined in builtins. Remember that
+# you should avoid to define new builtins when possible.
+additional-builtins=
+
+# List of strings which can identify a callback function by name. A callback
+# name must start or end with one of those strings.
+callbacks=cb_,_cb
+
+
+[DESIGN]
+
+# Maximum number of arguments for function / method
+max-args=5
+
+# Argument names that match this expression will be ignored. Default to name
+# with leading underscore
+ignored-argument-names=_.*
+
+# Maximum number of locals for function / method body
+max-locals=15
+
+# Maximum number of return / yield for function / method body
+max-returns=6
+
+# Maximum number of branch for function / method body
+max-branches=12
+
+# Maximum number of statements in function / method body
+max-statements=50
+
+# Maximum number of parents for a class (see R0901).
+max-parents=7
+
+# Maximum number of attributes for a class (see R0902).
+max-attributes=7
+
+# Minimum number of public methods for a class (see R0903).
+min-public-methods=2
+
+# Maximum number of public methods for a class (see R0904).
+max-public-methods=20
+
+
[IMPORTS]
# Deprecated modules which should not be used, separated by a comma
-deprecated-modules=regsub,
- TERMIOS,
- Bastion,
- rexec,
- sets
+deprecated-modules=regsub,TERMIOS,Bastion,rexec
# Create a graph of every (i.e. internal and external) dependencies in the
# given file (report RP0402 must not be disabled)
@@ -395,46 +351,25 @@
# not be disabled)
int-import-graph=
-# Force import order to recognize a module as part of the standard
-# compatibility libraries.
-known-standard-library=
-
-# Force import order to recognize a module as part of a third party library.
-known-third-party=enchant, absl
-
-# Analyse import fallback blocks. This can be used to support both Python 2 and
-# 3 compatible code, which means that the block might have code that exists
-# only in one or another interpreter, leading to false positives when analysed.
-analyse-fallback-blocks=no
-
[CLASSES]
# List of method names used to declare (i.e. assign) instance attributes.
-defining-attr-methods=__init__,
- __new__,
- setUp
-
-# List of member names, which should be excluded from the protected access
-# warning.
-exclude-protected=_asdict,
- _fields,
- _replace,
- _source,
- _make
+defining-attr-methods=__init__,__new__,setUp
# List of valid names for the first argument in a class method.
-valid-classmethod-first-arg=cls,
- class_
+valid-classmethod-first-arg=cls
# List of valid names for the first argument in a metaclass class method.
valid-metaclass-classmethod-first-arg=mcs
+# List of member names, which should be excluded from the protected access
+# warning.
+exclude-protected=_asdict,_fields,_replace,_source,_make
+
[EXCEPTIONS]
# Exceptions that will emit a warning when being caught. Defaults to
# "Exception"
-overgeneral-exceptions=StandardError,
- Exception,
- BaseException
+overgeneral-exceptions=Exception
diff --git a/repack_bootimg.py b/repack_bootimg.py
deleted file mode 100755
index c320018..0000000
--- a/repack_bootimg.py
+++ /dev/null
@@ -1,362 +0,0 @@
-#!/usr/bin/env python3
-#
-# Copyright 2021, The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-"""Repacks the boot image.
-
-Unpacks the boot image and the ramdisk inside, then add files into
-the ramdisk to repack the boot image.
-"""
-
-import argparse
-import datetime
-import enum
-import glob
-import os
-import shlex
-import shutil
-import subprocess
-import tempfile
-
-
-class TempFileManager:
- """Manages temporary files and dirs."""
-
- def __init__(self):
- self._temp_files = []
-
- def __del__(self):
- """Removes temp dirs and files."""
- for f in self._temp_files:
- if os.path.isdir(f):
- shutil.rmtree(f, ignore_errors=True)
- else:
- os.remove(f)
-
- def make_temp_dir(self, prefix='tmp', suffix=''):
- """Makes a temporary dir that will be cleaned up in the destructor.
-
- Returns:
- The absolute pathname of the new directory.
- """
- dir_name = tempfile.mkdtemp(prefix=prefix, suffix=suffix)
- self._temp_files.append(dir_name)
- return dir_name
-
- def make_temp_file(self, prefix='tmp', suffix=''):
- """Make a temp file that will be deleted in the destructor.
-
- Returns:
- The absolute pathname of the new file.
- """
- fd, file_name = tempfile.mkstemp(prefix=prefix, suffix=suffix)
- os.close(fd)
- self._temp_files.append(file_name)
- return file_name
-
-
-class RamdiskFormat(enum.Enum):
- """Enum class for different ramdisk compression formats."""
- LZ4 = 1
- GZIP = 2
-
-
-class BootImageType(enum.Enum):
- """Enum class for different boot image types."""
- BOOT_IMAGE = 1
- VENDOR_BOOT_IMAGE = 2
- SINGLE_RAMDISK_FRAGMENT = 3
- MULTIPLE_RAMDISK_FRAGMENTS = 4
-
-
-class RamdiskImage:
- """A class that supports packing/unpacking a ramdisk."""
- def __init__(self, ramdisk_img, unpack=True):
- self._ramdisk_img = ramdisk_img
- self._ramdisk_format = None
- self._ramdisk_dir = None
- self._temp_file_manager = TempFileManager()
-
- if unpack:
- self._unpack_ramdisk()
- else:
- self._ramdisk_dir = self._temp_file_manager.make_temp_dir(
- suffix='_new_ramdisk')
-
- def _unpack_ramdisk(self):
- """Unpacks the ramdisk."""
- self._ramdisk_dir = self._temp_file_manager.make_temp_dir(
- suffix='_' + os.path.basename(self._ramdisk_img))
-
- # The compression format might be in 'lz4' or 'gzip' format,
- # trying lz4 first.
- for compression_type, compression_util in [
- (RamdiskFormat.LZ4, 'lz4'),
- (RamdiskFormat.GZIP, 'minigzip')]:
-
- # Command arguments:
- # -d: decompression
- # -c: write to stdout
- decompression_cmd = [
- compression_util, '-d', '-c', self._ramdisk_img]
-
- decompressed_result = subprocess.run(
- decompression_cmd, check=False, capture_output=True)
-
- if decompressed_result.returncode == 0:
- self._ramdisk_format = compression_type
- break
-
- if self._ramdisk_format is not None:
- # toybox cpio arguments:
- # -i: extract files from stdin
- # -d: create directories if needed
- # -u: override existing files
- subprocess.run(
- ['toybox', 'cpio', '-idu'], check=True,
- input=decompressed_result.stdout, cwd=self._ramdisk_dir)
-
- print("=== Unpacked ramdisk: '{}' ===".format(
- self._ramdisk_img))
- else:
- raise RuntimeError('Failed to decompress ramdisk.')
-
- def repack_ramdisk(self, out_ramdisk_file):
- """Repacks a ramdisk from self._ramdisk_dir.
-
- Args:
- out_ramdisk_file: the output ramdisk file to save.
- """
- compression_cmd = ['lz4', '-l', '-12', '--favor-decSpeed']
- if self._ramdisk_format == RamdiskFormat.GZIP:
- compression_cmd = ['minigzip']
-
- print('Repacking ramdisk, which might take a few seconds ...')
-
- mkbootfs_result = subprocess.run(
- ['mkbootfs', self._ramdisk_dir], check=True, capture_output=True)
-
- with open(out_ramdisk_file, 'w') as output_fd:
- subprocess.run(compression_cmd, check=True,
- input=mkbootfs_result.stdout, stdout=output_fd)
-
- print("=== Repacked ramdisk: '{}' ===".format(out_ramdisk_file))
-
- @property
- def ramdisk_dir(self):
- """Returns the internal ramdisk dir."""
- return self._ramdisk_dir
-
-
-class BootImage:
- """A class that supports packing/unpacking a boot.img and ramdisk."""
-
- def __init__(self, bootimg):
- self._bootimg = bootimg
- self._bootimg_dir = None
- self._bootimg_type = None
- self._ramdisk = None
- self._previous_mkbootimg_args = []
- self._temp_file_manager = TempFileManager()
-
- self._unpack_bootimg()
-
- def _get_vendor_ramdisks(self):
- """Returns a list of vendor ramdisks after unpack."""
- return sorted(glob.glob(
- os.path.join(self._bootimg_dir, 'vendor_ramdisk*')))
-
- def _unpack_bootimg(self):
- """Unpacks the boot.img and the ramdisk inside."""
- self._bootimg_dir = self._temp_file_manager.make_temp_dir(
- suffix='_' + os.path.basename(self._bootimg))
-
- # Unpacks the boot.img first.
- unpack_bootimg_cmds = [
- 'unpack_bootimg',
- '--boot_img', self._bootimg,
- '--out', self._bootimg_dir,
- '--format=mkbootimg',
- ]
- result = subprocess.run(unpack_bootimg_cmds, check=True,
- capture_output=True, encoding='utf-8')
- self._previous_mkbootimg_args = shlex.split(result.stdout)
- print("=== Unpacked boot image: '{}' ===".format(self._bootimg))
-
- # From the output dir, checks there is 'ramdisk' or 'vendor_ramdisk'.
- ramdisk = os.path.join(self._bootimg_dir, 'ramdisk')
- vendor_ramdisk = os.path.join(self._bootimg_dir, 'vendor_ramdisk')
- vendor_ramdisks = self._get_vendor_ramdisks()
- if os.path.exists(ramdisk):
- self._ramdisk = RamdiskImage(ramdisk)
- self._bootimg_type = BootImageType.BOOT_IMAGE
- elif os.path.exists(vendor_ramdisk):
- self._ramdisk = RamdiskImage(vendor_ramdisk)
- self._bootimg_type = BootImageType.VENDOR_BOOT_IMAGE
- elif len(vendor_ramdisks) == 1:
- self._ramdisk = RamdiskImage(vendor_ramdisks[0])
- self._bootimg_type = BootImageType.SINGLE_RAMDISK_FRAGMENT
- elif len(vendor_ramdisks) > 1:
- # Creates an empty RamdiskImage() below, without unpack.
- # We'll then add files into this newly created ramdisk, then pack
- # it with other vendor ramdisks together.
- self._ramdisk = RamdiskImage(ramdisk_img=None, unpack=False)
- self._bootimg_type = BootImageType.MULTIPLE_RAMDISK_FRAGMENTS
- else:
- raise RuntimeError('Both ramdisk and vendor_ramdisk do not exist.')
-
- def repack_bootimg(self):
- """Repacks the ramdisk and rebuild the boot.img"""
-
- new_ramdisk = self._temp_file_manager.make_temp_file(
- prefix='ramdisk-patched')
- self._ramdisk.repack_ramdisk(new_ramdisk)
-
- mkbootimg_cmd = ['mkbootimg']
-
- # Uses previous mkbootimg args, e.g., --vendor_cmdline, --dtb_offset.
- mkbootimg_cmd.extend(self._previous_mkbootimg_args)
-
- ramdisk_option = ''
- if self._bootimg_type == BootImageType.BOOT_IMAGE:
- ramdisk_option = '--ramdisk'
- mkbootimg_cmd.extend(['--output', self._bootimg])
- elif self._bootimg_type == BootImageType.VENDOR_BOOT_IMAGE:
- ramdisk_option = '--vendor_ramdisk'
- mkbootimg_cmd.extend(['--vendor_boot', self._bootimg])
- elif self._bootimg_type == BootImageType.SINGLE_RAMDISK_FRAGMENT:
- ramdisk_option = '--vendor_ramdisk_fragment'
- mkbootimg_cmd.extend(['--vendor_boot', self._bootimg])
- elif self._bootimg_type == BootImageType.MULTIPLE_RAMDISK_FRAGMENTS:
- mkbootimg_cmd.extend(['--ramdisk_type', 'PLATFORM'])
- ramdisk_name = (
- 'RAMDISK_' +
- datetime.datetime.now().strftime('%Y-%m-%d_%H:%M:%S'))
- mkbootimg_cmd.extend(['--ramdisk_name', ramdisk_name])
- mkbootimg_cmd.extend(['--vendor_ramdisk_fragment', new_ramdisk])
- mkbootimg_cmd.extend(['--vendor_boot', self._bootimg])
-
- if ramdisk_option and ramdisk_option not in mkbootimg_cmd:
- raise RuntimeError("Failed to find '{}' from:\n {}".format(
- ramdisk_option, shlex.join(mkbootimg_cmd)))
- # Replaces the original ramdisk with the newly packed ramdisk.
- if ramdisk_option:
- ramdisk_index = mkbootimg_cmd.index(ramdisk_option) + 1
- mkbootimg_cmd[ramdisk_index] = new_ramdisk
-
- subprocess.check_call(mkbootimg_cmd)
- print("=== Repacked boot image: '{}' ===".format(self._bootimg))
-
- def add_files(self, src_dir, files):
- """Copy files from the src_dir into current ramdisk.
-
- Args:
- src_dir: a source dir containing the files to copy from.
- files: a list of files or src_file:dst_file pairs to copy from
- src_dir to the current ramdisk.
- """
- # Creates missing parent dirs with 0o755.
- original_mask = os.umask(0o022)
- for f in files:
- if ':' in f:
- src_file = os.path.join(src_dir, f.split(':')[0])
- dst_file = os.path.join(self.ramdisk_dir, f.split(':')[1])
- else:
- src_file = os.path.join(src_dir, f)
- dst_file = os.path.join(self.ramdisk_dir, f)
-
- dst_dir = os.path.dirname(dst_file)
- if not os.path.exists(dst_dir):
- print("Creating dir '{}'".format(dst_dir))
- os.makedirs(dst_dir, 0o755)
- print("Copying file '{}' into '{}'".format(src_file, dst_file))
- shutil.copy2(src_file, dst_file)
- os.umask(original_mask)
-
- @property
- def ramdisk_dir(self):
- """Returns the internal ramdisk dir."""
- return self._ramdisk.ramdisk_dir
-
-
-def _get_repack_usage():
- return """Usage examples:
-
- * --ramdisk_add
-
- Specifies a list of files or src_file:dst_file pairs to copy from
- --src_bootimg's ramdisk into --dst_bootimg's ramdisk.
-
- $ repack_bootimg \\
- --src_bootimg boot-debug-5.4.img --dst_bootimg vendor_boot-debug.img \\
- --ramdisk_add first_stage_ramdisk/userdebug_plat_sepolicy.cil:userdebug_plat_sepolicy.cil
-
- The above command copies '/first_stage_ramdisk/userdebug_plat_sepolicy.cil'
- from --src_bootimg's ramdisk to '/userdebug_plat_sepolicy.cil' of
- --dst_bootimg's ramdisk, then repacks the --dst_bootimg.
-
- $ repack_bootimg \\
- --src_bootimg boot-debug-5.4.img --dst_bootimg vendor_boot-debug.img \\
- --ramdisk_add first_stage_ramdisk/userdebug_plat_sepolicy.cil
-
- This is similar to the previous example, but the source file path and
- destination file path are the same:
- '/first_stage_ramdisk/userdebug_plat_sepolicy.cil'.
-
- We can also combine both usage together with a list of copy instructions.
- For example:
-
- $ repack_bootimg \\
- --src_bootimg boot-debug-5.4.img --dst_bootimg vendor_boot-debug.img \\
- --ramdisk_add file1 file2:/subdir/file2 file3
-"""
-
-
-def _parse_args():
- """Parse command-line options."""
- parser = argparse.ArgumentParser(
- formatter_class=argparse.RawDescriptionHelpFormatter,
- description='Repacks boot, recovery or vendor_boot image by importing'
- 'ramdisk files from --src_bootimg to --dst_bootimg.',
- epilog=_get_repack_usage(),
- )
-
- parser.add_argument(
- '--src_bootimg', help='filename to source boot image',
- type=str, required=True)
- parser.add_argument(
- '--dst_bootimg', help='filename to destination boot image',
- type=str, required=True)
- parser.add_argument(
- '--ramdisk_add', nargs='+',
- help='a list of files or src_file:dst_file pairs to add into '
- 'the ramdisk',
- default=['userdebug_plat_sepolicy.cil']
- )
-
- return parser.parse_args()
-
-
-def main():
- """Parse arguments and repack boot image."""
- args = _parse_args()
- src_bootimg = BootImage(args.src_bootimg)
- dst_bootimg = BootImage(args.dst_bootimg)
- dst_bootimg.add_files(src_bootimg.ramdisk_dir, args.ramdisk_add)
- dst_bootimg.repack_bootimg()
-
-
-if __name__ == '__main__':
- main()
diff --git a/tests/data/testkey_rsa2048.pem b/tests/data/testkey_rsa2048.pem
deleted file mode 100644
index 867dcff..0000000
--- a/tests/data/testkey_rsa2048.pem
+++ /dev/null
@@ -1,27 +0,0 @@
------BEGIN RSA PRIVATE KEY-----
-MIIEowIBAAKCAQEAxlVR3TIkouAOvH79vaJTgFhpfvVKQIeVkFRZPVXK/zY0Gvrh
-4JAqGjJoW/PfrQv5sdD36qtHH3a+G5hLZ6Ni+t/mtfjucxZfuLGC3kmJ1T3XqEKZ
-gXXI2IR7vVSoImREvDQGEDyJwtHzLANlkbGg0cghVhWZSCAndO8BenalC2v94/rt
-DfkPekH6dgU3Sf40T0sBSeSY94mOzTaqOR2pfV1rWlLRdWmo33zeHBv52Rlbt0dM
-uXAureXWiHztkm5GCBC1dgM+CaxNtizNEgC91KcD0xuRCCM2WxH+r1lpszyIJDct
-YbrFmVEYl/kjQpafhy7Nsk1fqSTyRdriZSYmTQIDAQABAoIBAQC+kJgaCuX8wYAn
-SXWQ0fmdZlXnMNRpcF0a0pD0SAzGb1RdYBXMaXiqtyhiwc53PPxsCDdNecjayIMd
-jJVXPTwLhTruOgMS/bp3gcgWwV34UHV4LJXGOGAE+jbS0hbDBMiudOYmj6RmVshp
-z9G1zZCSQNMXHaWsEYkX59XpzzoB384nRul2QgEtwzUNR9XlpzgtJBLk3SACkvsN
-mQ/DW8IWHXLg8vLn1LzVJ2e3B16H4MoE2TCHxqfMgr03IDRRJogkenQuQsFhevYT
-o/mJyHSWavVgzMHG9I5m+eepF4Wyhj1Y4WyKAuMI+9dHAX/h7Lt8XFCQCh5DbkVG
-zGr34sWBAoGBAOs7n7YZqNaaguovfIdRRsxxZr1yJAyDsr6w3yGImDZYju4c4WY9
-5esO2kP3FA4p0c7FhQF5oOb1rBuHEPp36cpL4aGeK87caqTfq63WZAujoTZpr9Lp
-BRbkL7w/xG7jpQ/clpA8sHzHGQs/nelxoOtC7E118FiRgvD/jdhlMyL9AoGBANfX
-vyoN1pplfT2xR8QOjSZ+Q35S/+SAtMuBnHx3l0qH2bbBjcvM1MNDWjnRDyaYhiRu
-i+KA7tqfib09+XpB3g5D6Ov7ls/Ldx0S/VcmVWtia2HK8y8iLGtokoBZKQ5AaFX2
-iQU8+tC4h69GnJYQKqNwgCUzh8+gHX5Y46oDiTmRAoGAYpOx8lX+czB8/Da6MNrW
-mIZNT8atZLEsDs2ANEVRxDSIcTCZJId7+m1W+nRoaycLTWNowZ1+2ErLvR10+AGY
-b7Ys79Wg9idYaY9yGn9lnZsMzAiuLeyIvXcSqgjvAKlVWrhOQFOughvNWvFl85Yy
-oWSCMlPiTLtt7CCsCKsgKuECgYBgdIp6GZsIfkgclKe0hqgvRoeU4TR3gcjJlM9A
-lBTo+pKhaBectplx9RxR8AnsPobbqwcaHnIfAuKDzjk5mEvKZjClnFXF4HAHbyAF
-nRzZEy9XkWFhc80T5rRpZO7C7qdxmu2aiKixM3V3L3/0U58qULEDbubHMw9bEhAT
-PudI8QKBgHEEiMm/hr9T41hbQi/LYanWnlFw1ue+osKuF8bXQuxnnHNuFT/c+9/A
-vWhgqG6bOEHu+p/IPrYm4tBMYlwsyh4nXCyGgDJLbLIfzKwKAWCtH9LwnyDVhOow
-GH9shdR+sW3Ew97xef02KAH4VlNANEmBV4sQNqWWvsYrcFm2rOdL
------END RSA PRIVATE KEY-----
diff --git a/tests/mkbootimg_test.py b/tests/mkbootimg_test.py
deleted file mode 100644
index ae5cf6b..0000000
--- a/tests/mkbootimg_test.py
+++ /dev/null
@@ -1,735 +0,0 @@
-#!/usr/bin/env python3
-#
-# Copyright 2020, The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-"""Tests mkbootimg and unpack_bootimg."""
-
-import filecmp
-import logging
-import os
-import random
-import shlex
-import subprocess
-import sys
-import tempfile
-import unittest
-
-BOOT_ARGS_OFFSET = 64
-BOOT_ARGS_SIZE = 512
-BOOT_EXTRA_ARGS_OFFSET = 608
-BOOT_EXTRA_ARGS_SIZE = 1024
-BOOT_V3_ARGS_OFFSET = 44
-VENDOR_BOOT_ARGS_OFFSET = 28
-VENDOR_BOOT_ARGS_SIZE = 2048
-
-BOOT_IMAGE_V4_SIGNATURE_SIZE = 4096
-
-TEST_KERNEL_CMDLINE = (
- 'printk.devkmsg=on firmware_class.path=/vendor/etc/ init=/init '
- 'kfence.sample_interval=500 loop.max_part=7 bootconfig'
-)
-
-
-def generate_test_file(pathname, size, seed=None):
- """Generates a gibberish-filled test file and returns its pathname."""
- random.seed(os.path.basename(pathname) if seed is None else seed)
- with open(pathname, 'wb') as f:
- f.write(random.randbytes(size))
- return pathname
-
-
-def subsequence_of(list1, list2):
- """Returns True if list1 is a subsequence of list2.
-
- >>> subsequence_of([], [1])
- True
- >>> subsequence_of([2, 4], [1, 2, 3, 4])
- True
- >>> subsequence_of([1, 2, 2], [1, 2, 3])
- False
- """
- if len(list1) == 0:
- return True
- if len(list2) == 0:
- return False
- if list1[0] == list2[0]:
- return subsequence_of(list1[1:], list2[1:])
- return subsequence_of(list1, list2[1:])
-
-
-class MkbootimgTest(unittest.TestCase):
- """Tests the functionalities of mkbootimg and unpack_bootimg."""
-
- def setUp(self):
- # Saves the test executable directory so that relative path references
- # to test dependencies don't rely on being manually run from the
- # executable directory.
- # With this, we can just open "./tests/data/testkey_rsa2048.pem" in the
- # following tests with subprocess.run(..., cwd=self._exec_dir, ...).
- self._exec_dir = os.path.abspath(os.path.dirname(sys.argv[0]))
-
- self._avbtool_path = os.path.join(self._exec_dir, 'avbtool')
-
- # Set self.maxDiff to None to see full diff in assertion.
- # C0103: invalid-name for maxDiff.
- self.maxDiff = None # pylint: disable=C0103
-
- def _test_boot_image_v4_signature(self, avbtool_path):
- """Tests the boot_signature in boot.img v4."""
- with tempfile.TemporaryDirectory() as temp_out_dir:
- boot_img = os.path.join(temp_out_dir, 'boot.img')
- kernel = generate_test_file(os.path.join(temp_out_dir, 'kernel'),
- 0x1000)
- ramdisk = generate_test_file(os.path.join(temp_out_dir, 'ramdisk'),
- 0x1000)
- mkbootimg_cmds = [
- 'mkbootimg',
- '--header_version', '4',
- '--kernel', kernel,
- '--ramdisk', ramdisk,
- '--cmdline', TEST_KERNEL_CMDLINE,
- '--os_version', '11.0.0',
- '--os_patch_level', '2021-01',
- '--gki_signing_algorithm', 'SHA256_RSA2048',
- '--gki_signing_key', './tests/data/testkey_rsa2048.pem',
- '--gki_signing_signature_args',
- '--prop foo:bar --prop gki:nice',
- '--output', boot_img,
- ]
-
- if avbtool_path:
- mkbootimg_cmds.extend(
- ['--gki_signing_avbtool_path', avbtool_path])
-
- unpack_bootimg_cmds = [
- 'unpack_bootimg',
- '--boot_img', boot_img,
- '--out', os.path.join(temp_out_dir, 'out'),
- ]
-
- # cwd=self._exec_dir is required to read
- # ./tests/data/testkey_rsa2048.pem for --gki_signing_key.
- subprocess.run(mkbootimg_cmds, check=True, cwd=self._exec_dir)
- subprocess.run(unpack_bootimg_cmds, check=True)
-
- # Checks the content of the boot signature.
- expected_boot_signature_info = (
- 'Minimum libavb version: 1.0\n'
- 'Header Block: 256 bytes\n'
- 'Authentication Block: 320 bytes\n'
- 'Auxiliary Block: 832 bytes\n'
- 'Public key (sha1): '
- 'cdbb77177f731920bbe0a0f94f84d9038ae0617d\n'
- 'Algorithm: SHA256_RSA2048\n'
- 'Rollback Index: 0\n'
- 'Flags: 0\n'
- 'Rollback Index Location: 0\n'
- "Release String: 'avbtool 1.2.0'\n"
- 'Descriptors:\n'
- ' Hash descriptor:\n'
- ' Image Size: 12288 bytes\n'
- ' Hash Algorithm: sha256\n'
- ' Partition Name: boot\n'
- ' Salt: d00df00d\n'
- ' Digest: '
- 'cf3755630856f23ab70e501900050fee'
- 'f30b633b3e82a9085a578617e344f9c7\n'
- ' Flags: 0\n'
- " Prop: foo -> 'bar'\n"
- " Prop: gki -> 'nice'\n"
- )
-
- avbtool_info_cmds = [
- # use avbtool_path if it is not None.
- avbtool_path or 'avbtool',
- 'info_image', '--image',
- os.path.join(temp_out_dir, 'out', 'boot_signature')
- ]
- result = subprocess.run(avbtool_info_cmds, check=True,
- capture_output=True, encoding='utf-8')
-
- self.assertEqual(result.stdout, expected_boot_signature_info)
-
- def test_boot_image_v4_signature_without_avbtool_path(self):
- """Boot signature generation without --gki_signing_avbtool_path."""
- self._test_boot_image_v4_signature(avbtool_path=None)
-
- def test_boot_image_v4_signature_with_avbtool_path(self):
- """Boot signature generation with --gki_signing_avbtool_path."""
- self._test_boot_image_v4_signature(avbtool_path=self._avbtool_path)
-
- def test_boot_image_v4_signature_exceed_size(self):
- """Tests the boot signature size exceeded in a boot image version 4."""
- with tempfile.TemporaryDirectory() as temp_out_dir:
- boot_img = os.path.join(temp_out_dir, 'boot.img')
- kernel = generate_test_file(os.path.join(temp_out_dir, 'kernel'),
- 0x1000)
- ramdisk = generate_test_file(os.path.join(temp_out_dir, 'ramdisk'),
- 0x1000)
- mkbootimg_cmds = [
- 'mkbootimg',
- '--header_version', '4',
- '--kernel', kernel,
- '--ramdisk', ramdisk,
- '--cmdline', TEST_KERNEL_CMDLINE,
- '--os_version', '11.0.0',
- '--os_patch_level', '2021-01',
- '--gki_signing_avbtool_path', self._avbtool_path,
- '--gki_signing_algorithm', 'SHA256_RSA2048',
- '--gki_signing_key', './tests/data/testkey_rsa2048.pem',
- '--gki_signing_signature_args',
- # Makes it exceed the signature max size.
- '--prop foo:bar --prop gki:nice ' * 64,
- '--output', boot_img,
- ]
-
- # cwd=self._exec_dir is required to read
- # ./tests/data/testkey_rsa2048.pem for --gki_signing_key.
- try:
- subprocess.run(mkbootimg_cmds, check=True, capture_output=True,
- cwd=self._exec_dir, encoding='utf-8')
- self.fail('Exceeding signature size assertion is not raised')
- except subprocess.CalledProcessError as e:
- self.assertIn('ValueError: boot sigature size is > 4096',
- e.stderr)
-
- def test_boot_image_v4_signature_zeros(self):
- """Tests no boot signature in a boot image version 4."""
- with tempfile.TemporaryDirectory() as temp_out_dir:
- boot_img = os.path.join(temp_out_dir, 'boot.img')
- kernel = generate_test_file(os.path.join(temp_out_dir, 'kernel'),
- 0x1000)
- ramdisk = generate_test_file(os.path.join(temp_out_dir, 'ramdisk'),
- 0x1000)
-
- # The boot signature will be zeros if no
- # --gki_signing_[algorithm|key] is provided.
- mkbootimg_cmds = [
- 'mkbootimg',
- '--header_version', '4',
- '--kernel', kernel,
- '--ramdisk', ramdisk,
- '--cmdline', TEST_KERNEL_CMDLINE,
- '--os_version', '11.0.0',
- '--os_patch_level', '2021-01',
- '--output', boot_img,
- ]
- unpack_bootimg_cmds = [
- 'unpack_bootimg',
- '--boot_img', boot_img,
- '--out', os.path.join(temp_out_dir, 'out'),
- ]
-
- subprocess.run(mkbootimg_cmds, check=True)
- subprocess.run(unpack_bootimg_cmds, check=True)
-
- boot_signature = os.path.join(
- temp_out_dir, 'out', 'boot_signature')
- with open(boot_signature) as f:
- zeros = '\x00' * BOOT_IMAGE_V4_SIGNATURE_SIZE
- self.assertEqual(f.read(), zeros)
-
- def test_vendor_boot_v4(self):
- """Tests vendor_boot version 4."""
- with tempfile.TemporaryDirectory() as temp_out_dir:
- vendor_boot_img = os.path.join(temp_out_dir, 'vendor_boot.img')
- dtb = generate_test_file(os.path.join(temp_out_dir, 'dtb'), 0x1000)
- ramdisk1 = generate_test_file(
- os.path.join(temp_out_dir, 'ramdisk1'), 0x1000)
- ramdisk2 = generate_test_file(
- os.path.join(temp_out_dir, 'ramdisk2'), 0x2000)
- bootconfig = generate_test_file(
- os.path.join(temp_out_dir, 'bootconfig'), 0x1000)
- mkbootimg_cmds = [
- 'mkbootimg',
- '--header_version', '4',
- '--vendor_boot', vendor_boot_img,
- '--dtb', dtb,
- '--vendor_ramdisk', ramdisk1,
- '--ramdisk_type', 'PLATFORM',
- '--ramdisk_name', 'RAMDISK1',
- '--vendor_ramdisk_fragment', ramdisk1,
- '--ramdisk_type', 'DLKM',
- '--ramdisk_name', 'RAMDISK2',
- '--board_id0', '0xC0FFEE',
- '--board_id15', '0x15151515',
- '--vendor_ramdisk_fragment', ramdisk2,
- '--vendor_cmdline', TEST_KERNEL_CMDLINE,
- '--vendor_bootconfig', bootconfig,
- ]
- unpack_bootimg_cmds = [
- 'unpack_bootimg',
- '--boot_img', vendor_boot_img,
- '--out', os.path.join(temp_out_dir, 'out'),
- ]
- expected_output = [
- 'boot magic: VNDRBOOT',
- 'vendor boot image header version: 4',
- 'vendor ramdisk total size: 16384',
- f'vendor command line args: {TEST_KERNEL_CMDLINE}',
- 'dtb size: 4096',
- 'vendor ramdisk table size: 324',
- 'size: 4096', 'offset: 0', 'type: 0x1', 'name:',
- '0x00000000, 0x00000000, 0x00000000, 0x00000000,',
- '0x00000000, 0x00000000, 0x00000000, 0x00000000,',
- '0x00000000, 0x00000000, 0x00000000, 0x00000000,',
- '0x00000000, 0x00000000, 0x00000000, 0x00000000,',
- 'size: 4096', 'offset: 4096', 'type: 0x1', 'name: RAMDISK1',
- '0x00000000, 0x00000000, 0x00000000, 0x00000000,',
- '0x00000000, 0x00000000, 0x00000000, 0x00000000,',
- '0x00000000, 0x00000000, 0x00000000, 0x00000000,',
- '0x00000000, 0x00000000, 0x00000000, 0x00000000,',
- 'size: 8192', 'offset: 8192', 'type: 0x3', 'name: RAMDISK2',
- '0x00c0ffee, 0x00000000, 0x00000000, 0x00000000,',
- '0x00000000, 0x00000000, 0x00000000, 0x00000000,',
- '0x00000000, 0x00000000, 0x00000000, 0x00000000,',
- '0x00000000, 0x00000000, 0x00000000, 0x15151515,',
- 'vendor bootconfig size: 4096',
- ]
-
- subprocess.run(mkbootimg_cmds, check=True)
- result = subprocess.run(unpack_bootimg_cmds, check=True,
- capture_output=True, encoding='utf-8')
- output = [line.strip() for line in result.stdout.splitlines()]
- if not subsequence_of(expected_output, output):
- msg = '\n'.join([
- 'Unexpected unpack_bootimg output:',
- 'Expected:',
- ' ' + '\n '.join(expected_output),
- '',
- 'Actual:',
- ' ' + '\n '.join(output),
- ])
- self.fail(msg)
-
- def test_unpack_vendor_boot_image_v4(self):
- """Tests that mkbootimg(unpack_bootimg(image)) is an identity."""
- with tempfile.TemporaryDirectory() as temp_out_dir:
- vendor_boot_img = os.path.join(temp_out_dir, 'vendor_boot.img')
- vendor_boot_img_reconstructed = os.path.join(
- temp_out_dir, 'vendor_boot.img.reconstructed')
- dtb = generate_test_file(os.path.join(temp_out_dir, 'dtb'), 0x1000)
- ramdisk1 = generate_test_file(
- os.path.join(temp_out_dir, 'ramdisk1'), 0x121212)
- ramdisk2 = generate_test_file(
- os.path.join(temp_out_dir, 'ramdisk2'), 0x212121)
- bootconfig = generate_test_file(
- os.path.join(temp_out_dir, 'bootconfig'), 0x1000)
-
- mkbootimg_cmds = [
- 'mkbootimg',
- '--header_version', '4',
- '--vendor_boot', vendor_boot_img,
- '--dtb', dtb,
- '--vendor_ramdisk', ramdisk1,
- '--ramdisk_type', 'PLATFORM',
- '--ramdisk_name', 'RAMDISK1',
- '--vendor_ramdisk_fragment', ramdisk1,
- '--ramdisk_type', 'DLKM',
- '--ramdisk_name', 'RAMDISK2',
- '--board_id0', '0xC0FFEE',
- '--board_id15', '0x15151515',
- '--vendor_ramdisk_fragment', ramdisk2,
- '--vendor_cmdline', TEST_KERNEL_CMDLINE,
- '--vendor_bootconfig', bootconfig,
- ]
- unpack_bootimg_cmds = [
- 'unpack_bootimg',
- '--boot_img', vendor_boot_img,
- '--out', os.path.join(temp_out_dir, 'out'),
- '--format=mkbootimg',
- ]
- subprocess.run(mkbootimg_cmds, check=True)
- result = subprocess.run(unpack_bootimg_cmds, check=True,
- capture_output=True, encoding='utf-8')
- mkbootimg_cmds = [
- 'mkbootimg',
- '--vendor_boot', vendor_boot_img_reconstructed,
- ]
- unpack_format_args = shlex.split(result.stdout)
- mkbootimg_cmds.extend(unpack_format_args)
-
- subprocess.run(mkbootimg_cmds, check=True)
- self.assertTrue(
- filecmp.cmp(vendor_boot_img, vendor_boot_img_reconstructed),
- 'reconstructed vendor_boot image differ from the original')
-
- # Also check that -0, --null are as expected.
- unpack_bootimg_cmds.append('--null')
- result = subprocess.run(unpack_bootimg_cmds, check=True,
- capture_output=True, encoding='utf-8')
- unpack_format_null_args = result.stdout
- self.assertEqual('\0'.join(unpack_format_args) + '\0',
- unpack_format_null_args)
-
- def test_unpack_vendor_boot_image_v3(self):
- """Tests that mkbootimg(unpack_bootimg(image)) is an identity."""
- with tempfile.TemporaryDirectory() as temp_out_dir:
- vendor_boot_img = os.path.join(temp_out_dir, 'vendor_boot.img')
- vendor_boot_img_reconstructed = os.path.join(
- temp_out_dir, 'vendor_boot.img.reconstructed')
- dtb = generate_test_file(os.path.join(temp_out_dir, 'dtb'), 0x1000)
- ramdisk = generate_test_file(os.path.join(temp_out_dir, 'ramdisk'),
- 0x121212)
- mkbootimg_cmds = [
- 'mkbootimg',
- '--header_version', '3',
- '--vendor_boot', vendor_boot_img,
- '--vendor_ramdisk', ramdisk,
- '--dtb', dtb,
- '--vendor_cmdline', TEST_KERNEL_CMDLINE,
- '--board', 'product_name',
- '--base', '0x00000000',
- '--dtb_offset', '0x01f00000',
- '--kernel_offset', '0x00008000',
- '--pagesize', '0x00001000',
- '--ramdisk_offset', '0x01000000',
- '--tags_offset', '0x00000100',
- ]
- unpack_bootimg_cmds = [
- 'unpack_bootimg',
- '--boot_img', vendor_boot_img,
- '--out', os.path.join(temp_out_dir, 'out'),
- '--format=mkbootimg',
- ]
- subprocess.run(mkbootimg_cmds, check=True)
- result = subprocess.run(unpack_bootimg_cmds, check=True,
- capture_output=True, encoding='utf-8')
- mkbootimg_cmds = [
- 'mkbootimg',
- '--vendor_boot', vendor_boot_img_reconstructed,
- ]
- mkbootimg_cmds.extend(shlex.split(result.stdout))
-
- subprocess.run(mkbootimg_cmds, check=True)
- self.assertTrue(
- filecmp.cmp(vendor_boot_img, vendor_boot_img_reconstructed),
- 'reconstructed vendor_boot image differ from the original')
-
- def test_unpack_boot_image_v3(self):
- """Tests that mkbootimg(unpack_bootimg(image)) is an identity."""
- with tempfile.TemporaryDirectory() as temp_out_dir:
- boot_img = os.path.join(temp_out_dir, 'boot.img')
- boot_img_reconstructed = os.path.join(
- temp_out_dir, 'boot.img.reconstructed')
- kernel = generate_test_file(os.path.join(temp_out_dir, 'kernel'),
- 0x1000)
- ramdisk = generate_test_file(os.path.join(temp_out_dir, 'ramdisk'),
- 0x1000)
- mkbootimg_cmds = [
- 'mkbootimg',
- '--header_version', '3',
- '--kernel', kernel,
- '--ramdisk', ramdisk,
- '--cmdline', TEST_KERNEL_CMDLINE,
- '--os_version', '11.0.0',
- '--os_patch_level', '2021-01',
- '--output', boot_img,
- ]
- unpack_bootimg_cmds = [
- 'unpack_bootimg',
- '--boot_img', boot_img,
- '--out', os.path.join(temp_out_dir, 'out'),
- '--format=mkbootimg',
- ]
-
- subprocess.run(mkbootimg_cmds, check=True)
- result = subprocess.run(unpack_bootimg_cmds, check=True,
- capture_output=True, encoding='utf-8')
- mkbootimg_cmds = [
- 'mkbootimg',
- '--out', boot_img_reconstructed,
- ]
- mkbootimg_cmds.extend(shlex.split(result.stdout))
-
- subprocess.run(mkbootimg_cmds, check=True)
- self.assertTrue(
- filecmp.cmp(boot_img, boot_img_reconstructed),
- 'reconstructed boot image differ from the original')
-
- def test_unpack_boot_image_v2(self):
- """Tests that mkbootimg(unpack_bootimg(image)) is an identity."""
- with tempfile.TemporaryDirectory() as temp_out_dir:
- # Output image path.
- boot_img = os.path.join(temp_out_dir, 'boot.img')
- boot_img_reconstructed = os.path.join(
- temp_out_dir, 'boot.img.reconstructed')
- # Creates blank images first.
- kernel = generate_test_file(
- os.path.join(temp_out_dir, 'kernel'), 0x1000)
- ramdisk = generate_test_file(
- os.path.join(temp_out_dir, 'ramdisk'), 0x1000)
- second = generate_test_file(
- os.path.join(temp_out_dir, 'second'), 0x1000)
- recovery_dtbo = generate_test_file(
- os.path.join(temp_out_dir, 'recovery_dtbo'), 0x1000)
- dtb = generate_test_file(
- os.path.join(temp_out_dir, 'dtb'), 0x1000)
-
- cmdline = (BOOT_ARGS_SIZE - 1) * 'x'
- extra_cmdline = (BOOT_EXTRA_ARGS_SIZE - 1) * 'y'
-
- mkbootimg_cmds = [
- 'mkbootimg',
- '--header_version', '2',
- '--base', '0x00000000',
- '--kernel', kernel,
- '--kernel_offset', '0x00008000',
- '--ramdisk', ramdisk,
- '--ramdisk_offset', '0x01000000',
- '--second', second,
- '--second_offset', '0x40000000',
- '--recovery_dtbo', recovery_dtbo,
- '--dtb', dtb,
- '--dtb_offset', '0x01f00000',
- '--tags_offset', '0x00000100',
- '--pagesize', '0x00001000',
- '--os_version', '11.0.0',
- '--os_patch_level', '2021-03',
- '--board', 'boot_v2',
- '--cmdline', cmdline + extra_cmdline,
- '--output', boot_img,
- ]
- unpack_bootimg_cmds = [
- 'unpack_bootimg',
- '--boot_img', boot_img,
- '--out', os.path.join(temp_out_dir, 'out'),
- '--format=mkbootimg',
- ]
-
- subprocess.run(mkbootimg_cmds, check=True)
- result = subprocess.run(unpack_bootimg_cmds, check=True,
- capture_output=True, encoding='utf-8')
- mkbootimg_cmds = [
- 'mkbootimg',
- '--out', boot_img_reconstructed,
- ]
- mkbootimg_cmds.extend(shlex.split(result.stdout))
-
- subprocess.run(mkbootimg_cmds, check=True)
- self.assertTrue(
- filecmp.cmp(boot_img, boot_img_reconstructed),
- 'reconstructed boot image differ from the original')
-
- def test_unpack_boot_image_v1(self):
- """Tests that mkbootimg(unpack_bootimg(image)) is an identity."""
- with tempfile.TemporaryDirectory() as temp_out_dir:
- # Output image path.
- boot_img = os.path.join(temp_out_dir, 'boot.img')
- boot_img_reconstructed = os.path.join(
- temp_out_dir, 'boot.img.reconstructed')
- # Creates blank images first.
- kernel = generate_test_file(
- os.path.join(temp_out_dir, 'kernel'), 0x1000)
- ramdisk = generate_test_file(
- os.path.join(temp_out_dir, 'ramdisk'), 0x1000)
- recovery_dtbo = generate_test_file(
- os.path.join(temp_out_dir, 'recovery_dtbo'), 0x1000)
-
- cmdline = (BOOT_ARGS_SIZE - 1) * 'x'
- extra_cmdline = (BOOT_EXTRA_ARGS_SIZE - 1) * 'y'
-
- mkbootimg_cmds = [
- 'mkbootimg',
- '--header_version', '1',
- '--base', '0x00000000',
- '--kernel', kernel,
- '--kernel_offset', '0x00008000',
- '--ramdisk', ramdisk,
- '--ramdisk_offset', '0x01000000',
- '--recovery_dtbo', recovery_dtbo,
- '--tags_offset', '0x00000100',
- '--pagesize', '0x00001000',
- '--os_version', '11.0.0',
- '--os_patch_level', '2021-03',
- '--board', 'boot_v1',
- '--cmdline', cmdline + extra_cmdline,
- '--output', boot_img,
- ]
- unpack_bootimg_cmds = [
- 'unpack_bootimg',
- '--boot_img', boot_img,
- '--out', os.path.join(temp_out_dir, 'out'),
- '--format=mkbootimg',
- ]
-
- subprocess.run(mkbootimg_cmds, check=True)
- result = subprocess.run(unpack_bootimg_cmds, check=True,
- capture_output=True, encoding='utf-8')
- mkbootimg_cmds = [
- 'mkbootimg',
- '--out', boot_img_reconstructed,
- ]
- mkbootimg_cmds.extend(shlex.split(result.stdout))
-
- subprocess.run(mkbootimg_cmds, check=True)
- self.assertTrue(
- filecmp.cmp(boot_img, boot_img_reconstructed),
- 'reconstructed boot image differ from the original')
-
- def test_unpack_boot_image_v0(self):
- """Tests that mkbootimg(unpack_bootimg(image)) is an identity."""
- with tempfile.TemporaryDirectory() as temp_out_dir:
- # Output image path.
- boot_img = os.path.join(temp_out_dir, 'boot.img')
- boot_img_reconstructed = os.path.join(
- temp_out_dir, 'boot.img.reconstructed')
- # Creates blank images first.
- kernel = generate_test_file(
- os.path.join(temp_out_dir, 'kernel'), 0x1000)
- ramdisk = generate_test_file(
- os.path.join(temp_out_dir, 'ramdisk'), 0x1000)
- second = generate_test_file(
- os.path.join(temp_out_dir, 'second'), 0x1000)
-
- cmdline = (BOOT_ARGS_SIZE - 1) * 'x'
- extra_cmdline = (BOOT_EXTRA_ARGS_SIZE - 1) * 'y'
-
- mkbootimg_cmds = [
- 'mkbootimg',
- '--header_version', '0',
- '--base', '0x00000000',
- '--kernel', kernel,
- '--kernel_offset', '0x00008000',
- '--ramdisk', ramdisk,
- '--ramdisk_offset', '0x01000000',
- '--second', second,
- '--second_offset', '0x40000000',
- '--tags_offset', '0x00000100',
- '--pagesize', '0x00001000',
- '--os_version', '11.0.0',
- '--os_patch_level', '2021-03',
- '--board', 'boot_v0',
- '--cmdline', cmdline + extra_cmdline,
- '--output', boot_img,
- ]
- unpack_bootimg_cmds = [
- 'unpack_bootimg',
- '--boot_img', boot_img,
- '--out', os.path.join(temp_out_dir, 'out'),
- ]
- unpack_bootimg_cmds = [
- 'unpack_bootimg',
- '--boot_img', boot_img,
- '--out', os.path.join(temp_out_dir, 'out'),
- '--format=mkbootimg',
- ]
-
- subprocess.run(mkbootimg_cmds, check=True)
- result = subprocess.run(unpack_bootimg_cmds, check=True,
- capture_output=True, encoding='utf-8')
- mkbootimg_cmds = [
- 'mkbootimg',
- '--out', boot_img_reconstructed,
- ]
- mkbootimg_cmds.extend(shlex.split(result.stdout))
-
- subprocess.run(mkbootimg_cmds, check=True)
- self.assertTrue(
- filecmp.cmp(boot_img, boot_img_reconstructed),
- 'reconstructed boot image differ from the original')
-
- def test_boot_image_v2_cmdline_null_terminator(self):
- """Tests that kernel commandline is null-terminated."""
- with tempfile.TemporaryDirectory() as temp_out_dir:
- dtb = generate_test_file(os.path.join(temp_out_dir, 'dtb'), 0x1000)
- kernel = generate_test_file(os.path.join(temp_out_dir, 'kernel'),
- 0x1000)
- ramdisk = generate_test_file(os.path.join(temp_out_dir, 'ramdisk'),
- 0x1000)
- cmdline = (BOOT_ARGS_SIZE - 1) * 'x'
- extra_cmdline = (BOOT_EXTRA_ARGS_SIZE - 1) * 'y'
- boot_img = os.path.join(temp_out_dir, 'boot.img')
- mkbootimg_cmds = [
- 'mkbootimg',
- '--header_version', '2',
- '--dtb', dtb,
- '--kernel', kernel,
- '--ramdisk', ramdisk,
- '--cmdline', cmdline + extra_cmdline,
- '--output', boot_img,
- ]
-
- subprocess.run(mkbootimg_cmds, check=True)
-
- with open(boot_img, 'rb') as f:
- raw_boot_img = f.read()
- raw_cmdline = raw_boot_img[BOOT_ARGS_OFFSET:][:BOOT_ARGS_SIZE]
- raw_extra_cmdline = (raw_boot_img[BOOT_EXTRA_ARGS_OFFSET:]
- [:BOOT_EXTRA_ARGS_SIZE])
- self.assertEqual(raw_cmdline, cmdline.encode() + b'\x00')
- self.assertEqual(raw_extra_cmdline,
- extra_cmdline.encode() + b'\x00')
-
- def test_boot_image_v3_cmdline_null_terminator(self):
- """Tests that kernel commandline is null-terminated."""
- with tempfile.TemporaryDirectory() as temp_out_dir:
- kernel = generate_test_file(os.path.join(temp_out_dir, 'kernel'),
- 0x1000)
- ramdisk = generate_test_file(os.path.join(temp_out_dir, 'ramdisk'),
- 0x1000)
- cmdline = BOOT_ARGS_SIZE * 'x' + (BOOT_EXTRA_ARGS_SIZE - 1) * 'y'
- boot_img = os.path.join(temp_out_dir, 'boot.img')
- mkbootimg_cmds = [
- 'mkbootimg',
- '--header_version', '3',
- '--kernel', kernel,
- '--ramdisk', ramdisk,
- '--cmdline', cmdline,
- '--output', boot_img,
- ]
-
- subprocess.run(mkbootimg_cmds, check=True)
-
- with open(boot_img, 'rb') as f:
- raw_boot_img = f.read()
- raw_cmdline = (raw_boot_img[BOOT_V3_ARGS_OFFSET:]
- [:BOOT_ARGS_SIZE + BOOT_EXTRA_ARGS_SIZE])
- self.assertEqual(raw_cmdline, cmdline.encode() + b'\x00')
-
- def test_vendor_boot_image_v3_cmdline_null_terminator(self):
- """Tests that kernel commandline is null-terminated."""
- with tempfile.TemporaryDirectory() as temp_out_dir:
- dtb = generate_test_file(os.path.join(temp_out_dir, 'dtb'), 0x1000)
- ramdisk = generate_test_file(os.path.join(temp_out_dir, 'ramdisk'),
- 0x1000)
- vendor_cmdline = (VENDOR_BOOT_ARGS_SIZE - 1) * 'x'
- vendor_boot_img = os.path.join(temp_out_dir, 'vendor_boot.img')
- mkbootimg_cmds = [
- 'mkbootimg',
- '--header_version', '3',
- '--dtb', dtb,
- '--vendor_ramdisk', ramdisk,
- '--vendor_cmdline', vendor_cmdline,
- '--vendor_boot', vendor_boot_img,
- ]
-
- subprocess.run(mkbootimg_cmds, check=True)
-
- with open(vendor_boot_img, 'rb') as f:
- raw_vendor_boot_img = f.read()
- raw_vendor_cmdline = (raw_vendor_boot_img[VENDOR_BOOT_ARGS_OFFSET:]
- [:VENDOR_BOOT_ARGS_SIZE])
- self.assertEqual(raw_vendor_cmdline,
- vendor_cmdline.encode() + b'\x00')
-
-
-# I don't know how, but we need both the logger configuration and verbosity
-# level > 2 to make atest work. And yes this line needs to be at the very top
-# level, not even in the "__main__" indentation block.
-logging.basicConfig(stream=sys.stdout)
-
-if __name__ == '__main__':
- unittest.main(verbosity=2)
diff --git a/unpack_bootimg.py b/unpack_bootimg.py
index 2b176e5..83c2bbe 100755
--- a/unpack_bootimg.py
+++ b/unpack_bootimg.py
@@ -1,5 +1,4 @@
-#!/usr/bin/env python3
-#
+#!/usr/bin/env python
# Copyright 2018, The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,20 +13,18 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-"""Unpacks the boot image.
+"""unpacks the bootimage.
Extracts the kernel, ramdisk, second bootloader, dtb and recovery dtbo images.
"""
-from argparse import ArgumentParser, FileType, RawDescriptionHelpFormatter
+from __future__ import print_function
+from argparse import ArgumentParser, FileType
from struct import unpack
import os
-import shlex
BOOT_IMAGE_HEADER_V3_PAGESIZE = 4096
-VENDOR_RAMDISK_NAME_SIZE = 32
-VENDOR_RAMDISK_TABLE_ENTRY_BOARD_ID_SIZE = 16
-
+VENDOR_BOOT_IMAGE_HEADER_V3_SIZE = 2112
def create_out_dir(dir_path):
"""creates a directory 'dir_path' if it does not exist"""
@@ -66,480 +63,182 @@
return '{:04d}-{:02d}'.format(y, m)
-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 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))
-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)])
- args.extend(['--os_version', self.os_version])
- 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(args):
+def unpack_bootimage(args):
"""extracts kernel, ramdisk, second bootloader and recovery dtbo"""
- info = BootImageInfoFormatter()
- info.boot_magic = unpack('8s', args.boot_img.read(8))[0].decode()
-
kernel_ramdisk_second_info = unpack('9I', args.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', args.boot_img.read(1 * 4))[0]
+ 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])
else:
- 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('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.os_version, info.os_patch_level = decode_os_version_patch_level(
- os_version_patch_level)
+ print('boot image header version: %s' % version)
- if info.header_version < 3:
- info.product_name = cstr(unpack('16s',
- args.boot_img.read(16))[0].decode())
- info.cmdline = cstr(unpack('512s', args.boot_img.read(512))[0].decode())
+ 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)
+ else:
+ cmdline = cstr(unpack('1536s', args.boot_img.read(1536))[0].decode())
+ print('command line args: %s' % cmdline)
+
+ if version < 3:
args.boot_img.read(32) # ignore SHA
- info.extra_cmdline = cstr(unpack('1024s',
- args.boot_img.read(1024))[0].decode())
- else:
- info.cmdline = cstr(unpack('1536s',
- args.boot_img.read(1536))[0].decode())
- if info.header_version in {1, 2}:
- info.recovery_dtbo_size = unpack('I', args.boot_img.read(1 * 4))[0]
- info.recovery_dtbo_offset = unpack('Q', args.boot_img.read(8))[0]
- info.boot_header_size = unpack('I', args.boot_img.read(4))[0]
- else:
- info.recovery_dtbo_size = 0
+ if version < 3:
+ extra_cmdline = cstr(unpack('1024s',
+ args.boot_img.read(1024))[0].decode())
+ print('additional command line args: %s' % extra_cmdline)
- if info.header_version == 2:
- info.dtb_size = unpack('I', args.boot_img.read(4))[0]
- info.dtb_load_address = unpack('Q', args.boot_img.read(8))[0]
+ 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]
else:
- info.dtb_size = 0
- info.dtb_load_address = 0
+ kernel_size = kernel_ramdisk_second_info[0]
+ ramdisk_size = kernel_ramdisk_second_info[1]
+ second_size = 0
+ page_size = BOOT_IMAGE_HEADER_V3_PAGESIZE
- if info.header_version >= 4:
- info.boot_signature_size = unpack('I', args.boot_img.read(4))[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)
else:
- info.boot_signature_size = 0
+ 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
+
# The first page contains the boot header
num_header_pages = 1
- # Convenient shorthand.
- page_size = info.page_size
-
- num_kernel_pages = get_number_of_pages(info.kernel_size, page_size)
+ 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, info.kernel_size, 'kernel')]
+ image_info_list = [(kernel_offset, kernel_size, 'kernel')]
- num_ramdisk_pages = get_number_of_pages(info.ramdisk_size, page_size)
+ num_ramdisk_pages = get_number_of_pages(ramdisk_size, page_size)
ramdisk_offset = page_size * (num_header_pages + num_kernel_pages
) # header + kernel
- image_info_list.append((ramdisk_offset, info.ramdisk_size, 'ramdisk'))
+ image_info_list.append((ramdisk_offset, ramdisk_size, 'ramdisk'))
- if info.second_size > 0:
+ if 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, info.second_size, 'second'))
+ image_info_list.append((second_offset, second_size, 'second'))
- if info.recovery_dtbo_size > 0:
- image_info_list.append((info.recovery_dtbo_offset,
- info.recovery_dtbo_size,
+ if recovery_dtbo_size > 0:
+ image_info_list.append((recovery_dtbo_offset, recovery_dtbo_size,
'recovery_dtbo'))
- 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)
+ 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)
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, info.dtb_size, 'dtb'))
+ image_info_list.append((dtb_offset, dtb_size, 'dtb'))
- 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(args.out)
- for offset, size, name in image_info_list:
- extract_image(offset, size, args.boot_img, os.path.join(args.out, name))
- info.image_dir = args.out
-
- return info
+ 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]))
-class VendorBootImageInfoFormatter:
- """Formats the vendor_boot image 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])
- 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}')
+ cmdline = cstr(unpack('2048s', args.boot_img.read(2048))[0].decode())
+ print('vendor command line args: %s' % cmdline)
- return '\n'.join(lines)
+ tags_load_address = unpack('I', args.boot_img.read(1 * 4))[0]
+ print('kernel tags load address: %#x' % tags_load_address)
- 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])
+ product_name = cstr(unpack('16s', args.boot_img.read(16))[0].decode())
+ print('product name: %s' % product_name)
- args.extend(['--dtb', os.path.join(self.image_dir, 'dtb')])
+ 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.header_version > 3:
- args.extend(['--vendor_bootconfig',
- os.path.join(self.image_dir, 'bootconfig')])
+ ramdisk_size = kernel_ramdisk_info[4]
+ page_size = kernel_ramdisk_info[1]
- 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(args):
- info = VendorBootImageInfoFormatter()
- info.boot_magic = unpack('8s', args.boot_img.read(8))[0].decode()
- info.header_version = unpack('I', args.boot_img.read(4))[0]
- info.page_size = unpack('I', args.boot_img.read(4))[0]
- info.kernel_load_address = unpack('I', args.boot_img.read(4))[0]
- info.ramdisk_load_address = unpack('I', args.boot_img.read(4))[0]
- info.vendor_ramdisk_size = unpack('I', args.boot_img.read(4))[0]
- info.cmdline = cstr(unpack('2048s', args.boot_img.read(2048))[0].decode())
- info.tags_load_address = unpack('I', args.boot_img.read(4))[0]
- info.product_name = cstr(unpack('16s', args.boot_img.read(16))[0].decode())
- info.header_size = unpack('I', args.boot_img.read(4))[0]
- info.dtb_size = unpack('I', args.boot_img.read(4))[0]
- info.dtb_load_address = unpack('Q', args.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(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', args.boot_img.read(4))[0]
- vendor_ramdisk_table_entry_num = unpack('I', args.boot_img.read(4))[0]
- vendor_ramdisk_table_entry_size = unpack('I', args.boot_img.read(4))[0]
- info.vendor_bootconfig_size = unpack('I', args.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)
- args.boot_img.seek(entry_offset)
- ramdisk_size = unpack('I', args.boot_img.read(4))[0]
- ramdisk_offset = unpack('I', args.boot_img.read(4))[0]
- ramdisk_type = unpack('I', args.boot_img.read(4))[0]
- ramdisk_name = cstr(unpack(
- f'{VENDOR_RAMDISK_NAME_SIZE}s',
- args.boot_img.read(VENDOR_RAMDISK_NAME_SIZE))[0].decode())
- board_id = unpack(
- f'{VENDOR_RAMDISK_TABLE_ENTRY_BOARD_ID_SIZE}I',
- args.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'))
+ 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')]
dtb_offset = page_size * (num_boot_header_pages + num_boot_ramdisk_pages
) # header + vendor_ramdisk
- image_info_list.append((dtb_offset, info.dtb_size, 'dtb'))
+ image_info_list.append((dtb_offset, dtb_size, 'dtb'))
- create_out_dir(args.out)
- for offset, size, name in image_info_list:
- extract_image(offset, size, args.boot_img, os.path.join(args.out, name))
- info.image_dir = args.out
-
- if info.header_version > 3:
- vendor_ramdisk_by_name_dir = os.path.join(
- args.out, '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
+ 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]))
def unpack_image(args):
boot_magic = unpack('8s', args.boot_img.read(8))[0].decode()
- args.boot_img.seek(0)
- if boot_magic == 'ANDROID!':
- info = unpack_boot_image(args)
- elif boot_magic == 'VNDRBOOT':
- info = unpack_vendor_boot_image(args)
- else:
- raise ValueError(f'Not an Android boot image, magic: {boot_magic}')
-
- if args.format == 'mkbootimg':
- mkbootimg_args = info.format_mkbootimg_argument()
- if args.null:
- 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
-"""
+ print('boot_magic: %s' % boot_magic)
+ if boot_magic == "ANDROID!":
+ unpack_bootimage(args)
+ elif boot_magic == "VNDRBOOT":
+ unpack_vendor_bootimage(args)
def parse_cmdline():
"""parse command line arguments"""
parser = ArgumentParser(
- formatter_class=RawDescriptionHelpFormatter,
- description='Unpacks boot, recovery or vendor_boot image.',
- epilog=get_unpack_usage(),
- )
- parser.add_argument('--boot_img', type=FileType('rb'), 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')
+ 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')
return parser.parse_args()
def main():
"""parse arguments and unpack boot image"""
args = parse_cmdline()
+ create_out_dir(args.out)
unpack_image(args)