Add simple script to unpack boot.img
Change-Id: Iff3dfbdaf96f989343bfaa074a0c90dc8ea363a1
(cherry picked from commit 47615d1dc15328f8fdd94aed43999db92942a884)
diff --git a/deploy/unpack_boot_image.py b/deploy/unpack_boot_image.py
new file mode 100755
index 0000000..530f92b
--- /dev/null
+++ b/deploy/unpack_boot_image.py
@@ -0,0 +1,111 @@
+#!/usr/bin/env python
+#
+#Copyright(C) 2017 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 boot.img files.
+
+Most Android devices have boot roms that interpret the boot.img file. The
+VMs that Cuttlefish uses don't handle Android's format. This breaks the
+kernel image, ram disk, and kernel command line into indivdual files.
+"""
+
+import argparse
+import os
+import re
+import struct
+import subprocess
+
+
+def pad(size, value):
+ """Adds up to size bytes to value to make value mod size == 0"""
+ return (value + size - 1)/size*size
+
+def extract_file(f, seekpt, size, dest_path):
+ """Creates a new file and then copies bytes to fill it.
+
+ Args:
+ f: The source file
+ seekpt: The offset in the source file to copy from
+ size: the number of bytes to copy
+ dest_path: the pathname of the file that should be created
+ """
+ f.seek(seekpt)
+ with open(dest_path, 'wb') as out:
+ while size:
+ bytes=min(size, 8192)
+ out.write(f.read(bytes))
+ size -= bytes
+
+
+def unpack_boot_img(path, out_dir):
+ """Unpacks the boot image, writing the parts to files in out_dir.
+
+ Args:
+ path: The path to the boot.img
+ out_dir: The path to an existing direction that should hold the parts.
+ """
+
+#This header is defined in host / linux - x86 / bin / mkbootimg
+ header = (
+ ('signature', '8s', 8),
+ ('kernel_size', 'I', 4),
+ ('kernel_addr', 'I', 4),
+ ('ramdisk_size', 'I', 4),
+ ('ramdisk_addr', 'I', 4),
+ ('second_loader_size', 'I', 4),
+ ('second_loader_addr', 'I', 4),
+ ('tags_addr', 'I', 4),
+ ('page_size', 'I', 4),
+ ('reserved', 'I', 4),
+ ('version', 'I', 4),
+ ('device', '16s', 16),
+ ('cmdline_1', '512s', 512),
+ ('sha', '32s', 32),
+ ('cmdline_2', '1024s', 1024)
+ )
+
+ pack_string = ''
+ pack_size = 0
+ for field in header:
+ pack_string += field[1]
+ pack_size += field[2]
+ with open(path, 'rb') as infile:
+ values = struct.unpack(pack_string, infile.read(pack_size))
+ fields = dict(zip([f[0] for f in header], values))
+ page_size = int(fields['page_size'])
+ cmdline = (fields['cmdline_1'] + fields['cmdline_2']).split('\0')[0]
+ with open(os.path.join(out_dir, 'cmdline'), 'w') as out:
+ out.write(cmdline)
+ offset = pad(page_size, pack_size)
+ kernel_size = int(fields['kernel_size'])
+ extract_file(infile, offset, kernel_size, os.path.join(out_dir, 'kernel'))
+ offset += pad(page_size, kernel_size)
+ ramdisk_size = int(fields['ramdisk_size'])
+ extract_file(infile, offset, ramdisk_size, os.path.join(out_dir, 'ramdisk.img'))
+
+
+def main():
+ parser = argparse.ArgumentParser(
+ description='Unpacks a boot.img')
+ parser.add_argument('-dest', type=str, default='.',
+ help='Destination directory')
+ parser.add_argument('-boot_img',type=str,
+ help='Path to the boot.img')
+ args = parser.parse_args()
+ unpack_boot_img(args.boot_img, args.dest)
+
+
+if __name__ == '__main__':
+ main()