aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNathan Chancellor <natechancellor@gmail.com>2020-10-30 13:41:35 -0700
committerNathan Chancellor <natechancellor@gmail.com>2020-10-30 13:41:35 -0700
commitc55cdece5cf9006d1dda7c4995171e5a577226f3 (patch)
tree2068cff3f615aff13b557dcdb84aff86a486ea45
parentkernels: add sunfish (diff)
downloadandroid-wireguard-module-builder-c55cdece5cf9006d1dda7c4995171e5a577226f3.tar.xz
android-wireguard-module-builder-c55cdece5cf9006d1dda7c4995171e5a577226f3.zip
util: Add support for extracting Pixel 4a (5G) and Pixel 5 boot.img
Signed-off-by: Nathan Chancellor <natechancellor@gmail.com>
-rwxr-xr-xutil/extract-version-hash-from-factory.bash15
-rwxr-xr-xutil/unpack_bootimg.py246
2 files changed, 258 insertions, 3 deletions
diff --git a/util/extract-version-hash-from-factory.bash b/util/extract-version-hash-from-factory.bash
index a9e0778..8e95a5b 100755
--- a/util/extract-version-hash-from-factory.bash
+++ b/util/extract-version-hash-from-factory.bash
@@ -5,6 +5,7 @@ URL="$1"
# Expecting URL like https://dl.google.com/dl/android/aosp/crosshatch-qp1a.191005.007-factory-2989a08d.zip
[[ -n $URL ]] || { echo "Usage: $0 URL" >&2; exit 1; }
+UTIL="$(dirname "$(readlink -f "$0")")"
D="$(mktemp -d)"
trap 'rm -rf "$D"' INT TERM EXIT
cd "$D"
@@ -12,8 +13,16 @@ cd "$D"
curl -#o out.zip "$URL"
bsdtar --strip-components 1 -xvf out.zip
bsdtar -xvf image-*.zip boot.img
-abootimg -x boot.img
-unlz4 zImage Image
-version="$(strings Image | grep '^Linux version [^%]' | head -n 1)"
+if [[ $URL =~ bramble || $URL =~ redfin ]]; then
+ "$UTIL"/unpack_bootimg.py --boot_img boot.img
+ COMP_IMG=out/kernel
+ DECOMP_IMG=out/kernel-decomp
+else
+ abootimg -x boot.img
+ COMP_IMG=zImage
+ DECOMP_IMG=Image
+fi
+unlz4 "$COMP_IMG" "$DECOMP_IMG"
+version="$(strings "$DECOMP_IMG" | grep '^Linux version [^%]' | head -n 1)"
[[ -n $version ]] || { echo "ERROR: no proper version in image" >&2; exit 1; }
printf '\n==========================================\n\n%s|%s\n' "$(echo "$version" | sha256sum | cut -d ' ' -f 1)" "$version"
diff --git a/util/unpack_bootimg.py b/util/unpack_bootimg.py
new file mode 100755
index 0000000..83c2bbe
--- /dev/null
+++ b/util/unpack_bootimg.py
@@ -0,0 +1,246 @@
+#!/usr/bin/env python
+# Copyright 2018, 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.
+
+"""unpacks the bootimage.
+
+Extracts the kernel, ramdisk, second bootloader, dtb and recovery dtbo images.
+"""
+
+from __future__ import print_function
+from argparse import ArgumentParser, FileType
+from struct import unpack
+import os
+
+BOOT_IMAGE_HEADER_V3_PAGESIZE = 4096
+VENDOR_BOOT_IMAGE_HEADER_V3_SIZE = 2112
+
+def create_out_dir(dir_path):
+ """creates a directory 'dir_path' if it does not exist"""
+ if not os.path.exists(dir_path):
+ os.makedirs(dir_path)
+
+
+def extract_image(offset, size, bootimage, extracted_image_name):
+ """extracts an image from the bootimage"""
+ bootimage.seek(offset)
+ with open(extracted_image_name, 'wb') as file_out:
+ file_out.write(bootimage.read(size))
+
+
+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
+
+
+def cstr(s):
+ """Remove first NULL character and any character beyond."""
+ return s.split('\0', 1)[0]
+
+
+def format_os_version(os_version):
+ a = os_version >> 14
+ b = os_version >> 7 & ((1<<7) - 1)
+ c = os_version & ((1<<7) - 1)
+ return '{}.{}.{}'.format(a, b, c)
+
+
+def format_os_patch_level(os_patch_level):
+ y = os_patch_level >> 4
+ y += 2000
+ m = os_patch_level & ((1<<4) - 1)
+ return '{:04d}-{:02d}'.format(y, m)
+
+
+def print_os_version_patch_level(value):
+ os_version = value >> 11
+ os_patch_level = value & ((1<<11) - 1)
+ print('os version: %s' % format_os_version(os_version))
+ print('os patch level: %s' % format_os_patch_level(os_patch_level))
+
+
+def unpack_bootimage(args):
+ """extracts kernel, ramdisk, second bootloader and recovery dtbo"""
+ kernel_ramdisk_second_info = unpack('9I', args.boot_img.read(9 * 4))
+ version = kernel_ramdisk_second_info[8]
+ if version < 3:
+ print('kernel_size: %s' % kernel_ramdisk_second_info[0])
+ print('kernel load address: %#x' % kernel_ramdisk_second_info[1])
+ print('ramdisk size: %s' % kernel_ramdisk_second_info[2])
+ print('ramdisk load address: %#x' % kernel_ramdisk_second_info[3])
+ print('second bootloader size: %s' % kernel_ramdisk_second_info[4])
+ print('second bootloader load address: %#x' % kernel_ramdisk_second_info[5])
+ print('kernel tags load address: %#x' % kernel_ramdisk_second_info[6])
+ print('page size: %s' % kernel_ramdisk_second_info[7])
+ print_os_version_patch_level(unpack('I', args.boot_img.read(1 * 4))[0])
+ else:
+ print('kernel_size: %s' % kernel_ramdisk_second_info[0])
+ print('ramdisk size: %s' % kernel_ramdisk_second_info[1])
+ print_os_version_patch_level(kernel_ramdisk_second_info[2])
+
+ print('boot image header version: %s' % version)
+
+ 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
+
+ if version < 3:
+ extra_cmdline = cstr(unpack('1024s',
+ args.boot_img.read(1024))[0].decode())
+ print('additional command line args: %s' % extra_cmdline)
+
+ if version < 3:
+ kernel_size = kernel_ramdisk_second_info[0]
+ ramdisk_size = kernel_ramdisk_second_info[2]
+ second_size = kernel_ramdisk_second_info[4]
+ page_size = kernel_ramdisk_second_info[7]
+ else:
+ kernel_size = kernel_ramdisk_second_info[0]
+ ramdisk_size = kernel_ramdisk_second_info[1]
+ second_size = 0
+ page_size = BOOT_IMAGE_HEADER_V3_PAGESIZE
+
+ 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:
+ 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
+
+ num_kernel_pages = get_number_of_pages(kernel_size, page_size)
+ kernel_offset = page_size * num_header_pages # header occupies a page
+ image_info_list = [(kernel_offset, kernel_size, 'kernel')]
+
+ 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, ramdisk_size, 'ramdisk'))
+
+ 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, second_size, 'second'))
+
+ if recovery_dtbo_size > 0:
+ image_info_list.append((recovery_dtbo_offset, recovery_dtbo_size,
+ 'recovery_dtbo'))
+ if dtb_size > 0:
+ num_second_pages = get_number_of_pages(second_size, page_size)
+ num_recovery_dtbo_pages = get_number_of_pages(recovery_dtbo_size, page_size)
+ dtb_offset = page_size * (
+ num_header_pages + num_kernel_pages + num_ramdisk_pages + num_second_pages +
+ num_recovery_dtbo_pages
+ )
+
+ image_info_list.append((dtb_offset, dtb_size, 'dtb'))
+
+ 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_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])
+
+ cmdline = cstr(unpack('2048s', args.boot_img.read(2048))[0].decode())
+ print('vendor command line args: %s' % cmdline)
+
+ tags_load_address = unpack('I', args.boot_img.read(1 * 4))[0]
+ print('kernel tags load address: %#x' % tags_load_address)
+
+ product_name = cstr(unpack('16s', args.boot_img.read(16))[0].decode())
+ print('product name: %s' % product_name)
+
+ dtb_size = unpack('2I', args.boot_img.read(2 * 4))[1]
+ print('dtb size: %s' % dtb_size)
+ dtb_load_address = unpack('Q', args.boot_img.read(8))[0]
+ print('dtb address: %#x' % dtb_load_address)
+
+ ramdisk_size = kernel_ramdisk_info[4]
+ page_size = kernel_ramdisk_info[1]
+
+ # The first pages contain the boot header
+ num_boot_header_pages = get_number_of_pages(VENDOR_BOOT_IMAGE_HEADER_V3_SIZE, page_size)
+ num_boot_ramdisk_pages = get_number_of_pages(ramdisk_size, page_size)
+ ramdisk_offset = page_size * num_boot_header_pages
+ image_info_list = [(ramdisk_offset, ramdisk_size, 'vendor_ramdisk')]
+
+ dtb_offset = page_size * (num_boot_header_pages + num_boot_ramdisk_pages
+ ) # header + vendor_ramdisk
+ image_info_list.append((dtb_offset, dtb_size, 'dtb'))
+
+ for image_info in image_info_list:
+ extract_image(image_info[0], image_info[1], args.boot_img,
+ os.path.join(args.out, image_info[2]))
+
+
+def unpack_image(args):
+ boot_magic = unpack('8s', args.boot_img.read(8))[0].decode()
+ print('boot_magic: %s' % boot_magic)
+ if boot_magic == "ANDROID!":
+ unpack_bootimage(args)
+ elif boot_magic == "VNDRBOOT":
+ unpack_vendor_bootimage(args)
+
+
+def parse_cmdline():
+ """parse command line arguments"""
+ parser = ArgumentParser(
+ description='Unpacks boot.img/recovery.img, extracts the kernel,'
+ 'ramdisk, second bootloader, recovery dtbo and dtb')
+ parser.add_argument(
+ '--boot_img',
+ help='path to boot image',
+ type=FileType('rb'),
+ required=True)
+ parser.add_argument('--out', help='path to out binaries', default='out')
+ return parser.parse_args()
+
+
+def main():
+ """parse arguments and unpack boot image"""
+ args = parse_cmdline()
+ create_out_dir(args.out)
+ unpack_image(args)
+
+
+if __name__ == '__main__':
+ main()