Create repack_gki, a standalone tool for repacking GKI images.
Bug: 191162695
Test: repack_gki --kernel_version 5.10 \
--ramdisk_build_id <build id> --kernel_build_id <build id>
Test: unpack_bootimg for each individual boot.img.
Ensure kernel images are replaced, and adhere to the following:
- allsyms boot images use the debug (allsyms) kernel Image
- gz boot images use the gz kernel Image
- lz4 boot images use the lz4 kernel Image
Test: Repeat for boot images inside the img.zip.
Test: Ensure BOOT/kernel-* inside target_files.zip match kernel build.
Change-Id: I0a3287ca83cb6e7b9739bfece4cffa1081272748
diff --git a/gki/Android.bp b/gki/Android.bp
new file mode 100644
index 0000000..d5b886d
--- /dev/null
+++ b/gki/Android.bp
@@ -0,0 +1,50 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "tools_treble_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["tools_treble_license"],
+}
+
+python_defaults {
+ name: "repack_gki_defaults",
+ version: {
+ py2: {
+ enabled: false,
+ embedded_launcher: false,
+ },
+ py3: {
+ enabled: true,
+ embedded_launcher: false,
+ },
+ },
+}
+
+python_library_host {
+ name: "repack_gki_lib",
+ defaults: ["repack_gki_defaults"],
+ srcs: [
+ "repack_gki_lib.py",
+ ],
+ libs: [
+ "fetcher-lib",
+ ],
+ pkg_path: "treble/gki",
+}
+
+python_binary_host {
+ name: "repack_gki",
+ main: "repack_gki.py",
+ defaults: ["repack_gki_defaults"],
+ srcs: [
+ "repack_gki.py",
+ ],
+ libs: [
+ "repack_gki_lib",
+ ],
+ required: [
+ "mkbootimg",
+ "unpack_bootimg",
+ ],
+}
diff --git a/gki/repack_gki.py b/gki/repack_gki.py
new file mode 100644
index 0000000..38e50fd
--- /dev/null
+++ b/gki/repack_gki.py
@@ -0,0 +1,137 @@
+"""Repacks GKI boot images with the given kernel images."""
+import argparse
+import json
+import os
+import shutil
+import tempfile
+
+from treble.fetcher import fetcher_lib
+from treble.gki import repack_gki_lib
+
+
+def main():
+ parser = argparse.ArgumentParser(
+ description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
+ parser.add_argument(
+ '--json_keyfile',
+ help='JSON keyfile containing credentials. '
+ '(Default: Use default credential file)')
+ parser.add_argument(
+ '--ramdisk_build_id',
+ required=True,
+ help='Download from the specified build.')
+ parser.add_argument(
+ '--ramdisk_target',
+ required=True,
+ help='Name of the ramdisk target from the ramdisk branch.')
+ parser.add_argument(
+ '--kernel_build_id',
+ required=True,
+ help='Download from the specified build.')
+ parser.add_argument(
+ '--kernel_target',
+ required=True,
+ help='Name of the kernel target from the kernel branch.')
+ parser.add_argument(
+ '--kernel_debug_target',
+ required=True,
+ help='Name of the kernel debug target from the kernel branch.')
+ parser.add_argument(
+ '--kernel_version',
+ required=True,
+ help='The Kernel version to use when repacking.')
+ parser.add_argument(
+ '--out_dir', required=True, help='Save output to this directory.')
+
+ args = parser.parse_args()
+ client = fetcher_lib.create_client_from_json_keyfile(
+ json_keyfile_name=args.json_keyfile)
+
+ if not os.path.exists(args.out_dir):
+ os.makedirs(args.out_dir)
+
+ with tempfile.TemporaryDirectory() as tmp_bootimg_dir, \
+ tempfile.TemporaryDirectory() as tmp_kernel_dir:
+ # Fetch boot images.
+ repack_gki_lib.fetch_bootimg(
+ client=client,
+ out_dir=tmp_bootimg_dir,
+ build_id=args.ramdisk_build_id,
+ kernel_version=args.kernel_version,
+ target=args.ramdisk_target,
+ )
+
+ # Fetch kernel artifacts.
+ kernel_dir, kernel_debug_dir = repack_gki_lib.fetch_kernel(
+ client=client,
+ out_dir=tmp_kernel_dir,
+ build_id=args.kernel_build_id,
+ kernel_target=args.kernel_target,
+ kernel_debug_target=args.kernel_debug_target,
+ )
+
+ # Save kernel artifacts to the out dir.
+ kernel_out_dir = os.path.join(args.out_dir, 'kernel', args.kernel_version)
+ if not os.path.exists(kernel_out_dir):
+ os.makedirs(kernel_out_dir)
+
+ def copy_kernel_file(in_dir, filename, outname=None):
+ if not outname:
+ outname = filename
+ shutil.copy(
+ os.path.join(in_dir, filename), os.path.join(kernel_out_dir, outname))
+
+ copy_kernel_file(kernel_dir, 'System.map')
+ copy_kernel_file(kernel_dir, 'abi.xml')
+ copy_kernel_file(kernel_dir, 'abi_symbollist')
+ copy_kernel_file(kernel_dir, 'Image',
+ 'kernel-{}'.format(args.kernel_version))
+ copy_kernel_file(kernel_dir, 'Image.lz4',
+ 'kernel-{}-lz4'.format(args.kernel_version))
+ copy_kernel_file(kernel_dir, 'Image.gz',
+ 'kernel-{}-gz'.format(args.kernel_version))
+ copy_kernel_file(kernel_debug_dir, 'System.map', 'System.map-allsyms')
+ copy_kernel_file(kernel_debug_dir, 'Image',
+ 'kernel-{}-allsyms'.format(args.kernel_version))
+ copy_kernel_file(kernel_debug_dir, 'Image.lz4',
+ 'kernel-{}-lz4-allsyms'.format(args.kernel_version))
+ copy_kernel_file(kernel_debug_dir, 'Image.gz',
+ 'kernel-{}-gz-allsyms'.format(args.kernel_version))
+
+ # Repack individual boot images using the fetched kernel artifacts,
+ # then save to the out dir.
+ repack_gki_lib.repack_bootimgs(tmp_bootimg_dir, kernel_dir,
+ kernel_debug_dir)
+ shutil.copytree(tmp_bootimg_dir, args.out_dir, dirs_exist_ok=True)
+
+ # Repack boot images inside the img.zip and save to the out dir.
+ img_zip_name = [f for f in os.listdir(tmp_bootimg_dir) if '-img-' in f][0]
+ img_zip_path = os.path.join(tmp_bootimg_dir, img_zip_name)
+ repack_gki_lib.repack_img_zip(img_zip_path, kernel_dir, kernel_debug_dir,
+ args.kernel_version)
+ shutil.copy(img_zip_path, args.out_dir)
+
+ # Replace kernels within the target_files.zip and save to the out dir.
+ target_files_zip_name = [
+ f for f in os.listdir(tmp_bootimg_dir) if '-target_files-' in f
+ ][0]
+ target_files_zip_path = os.path.join(tmp_bootimg_dir, target_files_zip_name)
+ repack_gki_lib.replace_target_files_zip_kernels(target_files_zip_path,
+ kernel_out_dir,
+ args.kernel_version)
+ shutil.copy(target_files_zip_path, args.out_dir)
+
+ # Copy otatools.zip from the ramdisk build, used for GKI signing.
+ shutil.copy(os.path.join(tmp_bootimg_dir, 'otatools.zip'), args.out_dir)
+
+ # Write prebuilt-info.txt using the prebuilt artifact build IDs.
+ data = {
+ 'ramdisk-build-id': int(args.ramdisk_build_id),
+ 'kernel-build-id': int(args.kernel_build_id),
+ }
+ with open(os.path.join(kernel_out_dir, 'prebuilt-info.txt'), 'w') as f:
+ json.dump(data, f, indent=4)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/gki/repack_gki_lib.py b/gki/repack_gki_lib.py
new file mode 100644
index 0000000..6df90d1
--- /dev/null
+++ b/gki/repack_gki_lib.py
@@ -0,0 +1,160 @@
+"""Helper library for repacking GKI boot images."""
+import os
+import shutil
+import subprocess
+import tempfile
+
+from treble.fetcher import fetcher_lib
+
+
+def fetch_bootimg(client, out_dir, build_id, kernel_version, target):
+ """Fetches boot.img artifacts from a given build ID."""
+ fetcher_lib.fetch_artifacts(
+ client=client,
+ build_id=build_id,
+ target=target,
+ pattern=r'(.*-img-.*\.zip|.*-target_files-.*\.zip|boot-debug-{version}.*\.img|boot-test-harness-{version}.*\.img|otatools.zip)'
+ .format(version=kernel_version),
+ out_dir=out_dir)
+
+
+def fetch_kernel(client, out_dir, build_id, kernel_target, kernel_debug_target):
+ """Fetches kernel artifacts from a given build ID."""
+ kernel_dir = os.path.join(out_dir, 'kernel')
+ kernel_debug_dir = os.path.join(out_dir, 'kernel_debug')
+ os.makedirs(kernel_dir)
+ os.makedirs(kernel_debug_dir)
+
+ fetcher_lib.fetch_artifacts(
+ client=client,
+ build_id=build_id,
+ target=kernel_target,
+ pattern=r'(Image|Image.lz4|System\.map|abi.xml|abi_symbollist)',
+ out_dir=kernel_dir)
+ fetcher_lib.fetch_artifacts(
+ client=client,
+ build_id=build_id,
+ target=kernel_debug_target,
+ pattern=r'(Image|Image.lz4|System\.map)',
+ out_dir=kernel_debug_dir)
+
+ print('Compressing kernels')
+
+ def compress_kernel(kernel_path):
+ zipped_kernel_path = os.path.join(os.path.dirname(kernel_path), 'Image.gz')
+ with open(zipped_kernel_path, 'wb') as zipped_kernel:
+ cmd = [
+ 'gzip',
+ '-nc',
+ kernel_path,
+ ]
+ print(' '.join(cmd))
+ subprocess.check_call(cmd, stdout=zipped_kernel)
+
+ compress_kernel(os.path.join(kernel_dir, 'Image'))
+ compress_kernel(os.path.join(kernel_debug_dir, 'Image'))
+
+ return kernel_dir, kernel_debug_dir
+
+
+def _replace_kernel(bootimg_path, kernel_path):
+ """Unpacks a boot.img, replaces the kernel, then repacks."""
+ with tempfile.TemporaryDirectory() as unpack_dir:
+ print('Unpacking bootimg %s' % bootimg_path)
+ cmd = [
+ 'out/host/linux-x86/bin/unpack_bootimg',
+ '--boot_img',
+ bootimg_path,
+ '--out',
+ unpack_dir,
+ '--format',
+ 'mkbootimg',
+ ]
+ print(' '.join(cmd))
+ mkbootimg_args = subprocess.check_output(cmd).decode('utf-8').split(' ')
+ print('Copying kernel %s' % kernel_path)
+ shutil.copy(kernel_path, os.path.join(unpack_dir, 'kernel'))
+ print('Repacking with mkbootimg')
+ cmd = [
+ 'out/host/linux-x86/bin/mkbootimg',
+ '--output',
+ bootimg_path,
+ ] + mkbootimg_args
+ print(' '.join(cmd))
+ subprocess.check_call(cmd)
+
+
+def repack_bootimgs(bootimg_dir, kernel_dir, kernel_debug_dir):
+ """Repacks all boot images in a given dir using the provided kernels."""
+ for bootimg_path in os.listdir(bootimg_dir):
+ bootimg_path = os.path.join(bootimg_dir, bootimg_path)
+ if not bootimg_path.endswith('.img'):
+ continue
+
+ kernel_name = 'Image'
+ if '-gz' in bootimg_path:
+ kernel_name = 'Image.gz'
+ elif '-lz4' in bootimg_path:
+ kernel_name = 'Image.lz4'
+
+ kernel_path = os.path.join(kernel_dir, kernel_name)
+ if bootimg_path.endswith('-allsyms.img'):
+ kernel_path = os.path.join(kernel_debug_dir, kernel_name)
+
+ _replace_kernel(bootimg_path, kernel_path)
+
+
+def repack_img_zip(img_zip_path, kernel_dir, kernel_debug_dir, kernel_version):
+ """Repacks boot images within an img.zip archive."""
+ with tempfile.TemporaryDirectory() as unzip_dir:
+ pattern = 'boot-{}*'.format(kernel_version)
+ print('Unzipping %s to repack bootimgs' % img_zip_path)
+ cmd = [
+ 'unzip',
+ '-d',
+ unzip_dir,
+ img_zip_path,
+ pattern,
+ ]
+ print(' '.join(cmd))
+ subprocess.check_call(cmd)
+ repack_bootimgs(unzip_dir, kernel_dir, kernel_debug_dir)
+ cmd = [
+ 'zip',
+ img_zip_path,
+ pattern,
+ ]
+ print(' '.join(cmd))
+ subprocess.check_call(cmd, cwd=unzip_dir)
+
+
+def replace_target_files_zip_kernels(target_files_zip_path, kernel_out_dir,
+ kernel_version):
+ """Replaces the BOOT/kernel-* kernels within a target_files.zip archive."""
+ with tempfile.TemporaryDirectory() as unzip_dir:
+ pattern = 'BOOT/kernel-{}*'.format(kernel_version)
+ print(
+ 'Unzipping %s to replace kernels in preparation for signing' %
+ target_files_zip_path,)
+ cmd = [
+ 'unzip',
+ '-d',
+ unzip_dir,
+ target_files_zip_path,
+ pattern,
+ ]
+ print(' '.join(cmd))
+ subprocess.check_call(cmd)
+ for kernel in os.listdir(kernel_out_dir):
+ if kernel.startswith('kernel-{}'.format(kernel_version)):
+ print('Copying %s' % kernel)
+ shutil.copy(
+ os.path.join(kernel_out_dir, kernel),
+ os.path.join(unzip_dir, 'BOOT'))
+ cmd = [
+ 'zip',
+ target_files_zip_path,
+ pattern,
+ ]
+ print(' '.join(cmd))
+ subprocess.check_call(cmd, cwd=unzip_dir)