From 6bacea2bec4fe09e33fb3f51bb887a862ddf2350 Mon Sep 17 00:00:00 2001 From: Hridya Valsaraju Date: Tue, 20 Mar 2018 15:26:00 -0700 Subject: [PATCH 1/2] Add a recovery DTBO section to boot image For non-A/B devices, the recovery partition should be self-sufficient and not depend on any other partition. Since recovery may need access to devices added to the DT by overlays(for eg.display), we need to make sure that the the recovery image also contains the information from the DTBO image. The following fields are added to the boot image header for this purpose: -boot header version -header size -recovery dtbo size -recovery dtbo address Bug: 74763691 Test: unpack_bootimage --boot_img $OUT/recovery.img diff out/extracted_dtbo $TOP/device/google/wahoo-kernel/dtbo.img Change-Id: I1969859038b84fac1e4a71f79f2f546b9cacffc8 Merged-In: I1969859038b84fac1e4a71f79f2f546b9cacffc8 (cherry picked from commit 147b355bb34cbd457a260c585be93193cef43b8e) --- mkbootimg/bootimg.h | 113 ++++++++++++++++++++++++++++++++------------ mkbootimg/mkbootimg | 18 ++++++- 2 files changed, 100 insertions(+), 31 deletions(-) diff --git a/mkbootimg/bootimg.h b/mkbootimg/bootimg.h index 60834fed9..1be8c229c 100644 --- a/mkbootimg/bootimg.h +++ b/mkbootimg/bootimg.h @@ -20,16 +20,18 @@ #ifndef _BOOT_IMAGE_H_ #define _BOOT_IMAGE_H_ -typedef struct boot_img_hdr boot_img_hdr; - #define BOOT_MAGIC "ANDROID!" #define BOOT_MAGIC_SIZE 8 #define BOOT_NAME_SIZE 16 #define BOOT_ARGS_SIZE 512 #define BOOT_EXTRA_ARGS_SIZE 1024 -struct boot_img_hdr -{ +#define BOOT_HEADER_VERSION_ZERO 0 +/* + * Bootloader expects the structure of boot_img_hdr with header version + * BOOT_HEADER_VERSION_ZERO to be as follows: + */ +struct boot_img_hdr_v0 { uint8_t magic[BOOT_MAGIC_SIZE]; uint32_t kernel_size; /* size in bytes */ @@ -43,7 +45,10 @@ struct boot_img_hdr uint32_t tags_addr; /* physical addr for kernel tags */ uint32_t page_size; /* flash page size we assume */ - uint32_t unused; /* reserved for future expansion: MUST be 0 */ + /* + * version for the boot image header. + */ + uint32_t header_version; /* operating system version and security patch level; for * version "A.B.C" and patch level "Y-M-D": @@ -64,31 +69,79 @@ struct boot_img_hdr } __attribute__((packed)); /* -** +-----------------+ -** | 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 -*/ + * It is expected that callers would explicitly specify which version of the + * boot image header they need to use. + */ +typedef struct boot_img_hdr_v0 boot_img_hdr; + +/* When a boot header is of version BOOT_HEADER_VERSION_ZERO, 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 + */ + +#define BOOT_HEADER_VERSION_ONE 1 + +struct boot_img_hdr_v1 : public boot_img_hdr_v0 { + uint32_t recovery_dtbo_size; /* size in bytes for recovery DTBO image */ + uint64_t recovery_dtbo_offset; /* physical load addr */ + uint32_t header_size; +} __attribute__((packed)); + +/* When the boot image header has a version of BOOT_HEADER_VERSION_ONE, the structure of the boot + * image is as follows: + * + * +-----------------+ + * | boot header | 1 page + * +-----------------+ + * | kernel | n pages + * +-----------------+ + * | ramdisk | m pages + * +-----------------+ + * | second stage | o pages + * +-----------------+ + * | recovery dtbo | p 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 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, recovery_dtbo) at + * the specified physical address (kernel_addr, etc) + * 5. prepare tags at tag_addr. kernel_args[] is + * appended to the kernel commandline in the tags. + * 6. r0 = 0, r1 = MACHINE_TYPE, r2 = tags_addr + * 7. if second_size != 0: jump to second_addr + * else: jump to kernel_addr + */ #if 0 typedef struct ptentry ptentry; diff --git a/mkbootimg/mkbootimg b/mkbootimg/mkbootimg index 5a13da26b..ac20d0503 100755 --- a/mkbootimg/mkbootimg +++ b/mkbootimg/mkbootimg @@ -57,7 +57,7 @@ def write_header(args): args.base + args.second_offset, # physical load addr args.base + args.tags_offset, # physical addr for kernel tags args.pagesize, # flash page size we assume - 0, # future expansion: MUST be 0 + 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())) @@ -66,10 +66,20 @@ def write_header(args): update_sha(sha, args.kernel) update_sha(sha, args.ramdisk) update_sha(sha, args.second) + + if args.header_version > 0: + update_sha(sha, args.recovery_dtbo) + img_id = pack('32s', sha.digest()) args.output.write(img_id) 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 + args.output.write(pack('Q', args.base + args.recovery_dtbo_offset)) # physical load addr + args.output.write(pack('I', args.output.tell() + 4)) # size of boot header + pad_file(args.output, args.pagesize) return img_id @@ -132,6 +142,7 @@ def parse_cmdline(): required=True) 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('--recovery_dtbo', help='path to the recovery DTBO', type=FileType('rb')) parser.add_argument('--cmdline', help='extra arguments to be passed on the ' 'kernel command line', default='', action=ValidateStrLenAction, maxlen=1536) parser.add_argument('--base', help='base address', type=parse_int, default=0x10000000) @@ -139,6 +150,8 @@ def parse_cmdline(): 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('--recovery_dtbo_offset', help='recovery dtbo offset', type=parse_int, + default=0x0f000000) 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', @@ -150,6 +163,7 @@ def parse_cmdline(): 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'), required=True) return parser.parse_args() @@ -160,6 +174,8 @@ def write_data(args): write_padded_file(args.output, args.ramdisk, args.pagesize) write_padded_file(args.output, args.second, args.pagesize) + if args.header_version > 0: + write_padded_file(args.output, args.recovery_dtbo, args.pagesize) def main(): args = parse_cmdline() From d71e39ae5756ed20cd2c196465cf96cbd4ebd21e Mon Sep 17 00:00:00 2001 From: Hridya Valsaraju Date: Wed, 21 Mar 2018 22:18:19 -0700 Subject: [PATCH 2/2] Add a script to unpack boot image The script prints out the boot image header contents and extracts the kernel, ramdisk, second bootloader and recovery DTBO images. Bug: 74763691 Test: unpack_bootimg --boot_img $OUT/recovery.img Change-Id: Iadbca81c157d9e4607f808a14468ab5542347507 Merged-In: Iadbca81c157d9e4607f808a14468ab5542347507 (cherry picked from commit 02c08cc3471f11db10853c4f668d3348ce6f403f) --- mkbootimg/Android.mk | 9 +++ mkbootimg/unpack_bootimg | 137 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 146 insertions(+) create mode 100755 mkbootimg/unpack_bootimg diff --git a/mkbootimg/Android.mk b/mkbootimg/Android.mk index 8661d7d6b..92e1e27d2 100644 --- a/mkbootimg/Android.mk +++ b/mkbootimg/Android.mk @@ -9,3 +9,12 @@ LOCAL_IS_HOST_MODULE := true LOCAL_MODULE := mkbootimg include $(BUILD_PREBUILT) + +include $(CLEAR_VARS) +LOCAL_SRC_FILES := unpack_bootimg +LOCAL_MODULE_CLASS := EXECUTABLES +LOCAL_IS_HOST_MODULE := true + +LOCAL_MODULE := unpack_bootimg + +include $(BUILD_PREBUILT) diff --git a/mkbootimg/unpack_bootimg b/mkbootimg/unpack_bootimg new file mode 100755 index 000000000..8e42ec029 --- /dev/null +++ b/mkbootimg/unpack_bootimg @@ -0,0 +1,137 @@ +#!/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 and recovery dtbo images. +""" + +from __future__ import print_function +from argparse import ArgumentParser, FileType +from struct import unpack +import os + + +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 unpack_bootimage(args): + """extracts kernel, ramdisk, second bootloader and recovery dtbo""" + boot_magic = unpack('8s', args.boot_img.read(8)) + print('boot_magic: %s' % boot_magic) + kernel_ramdisk_second_info = unpack('10I', args.boot_img.read(10 * 4)) + print('kernel_size: %s' % kernel_ramdisk_second_info[0]) + print('kernel load address: %s' % kernel_ramdisk_second_info[1]) + print('ramdisk size: %s' % kernel_ramdisk_second_info[2]) + print('ramdisk load address: %s' % kernel_ramdisk_second_info[3]) + print('second bootloader size: %s' % kernel_ramdisk_second_info[4]) + print('second bootloader load address: %s' % kernel_ramdisk_second_info[5]) + print('kernel tags load address: %s' % kernel_ramdisk_second_info[6]) + print('page size: %s' % kernel_ramdisk_second_info[7]) + print('boot image header version: %s' % kernel_ramdisk_second_info[8]) + print('os version and patch level: %s' % kernel_ramdisk_second_info[9]) + + product_name = unpack('16s', args.boot_img.read(16)) + print('product name: %s' % product_name) + cmdline = unpack('512s', args.boot_img.read(512)) + print('command line args: %s' % cmdline) + + args.boot_img.read(32) # ignore SHA + + extra_cmdline = unpack('1024s', args.boot_img.read(1024)) + print('additional command line args: %s' % extra_cmdline) + + 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] + version = kernel_ramdisk_second_info[8] + if version > 0: + recovery_dtbo_size = unpack('I', args.boot_img.read(1 * 4))[0] + print('recovery dtbo size: %s' % recovery_dtbo_size) + recovery_dtbo_address = unpack('Q', args.boot_img.read(8))[0] + print('recovery dtbo load address: %s' % recovery_dtbo_address) + boot_header_size = unpack('I', args.boot_img.read(4))[0] + print('boot header size: %s' % boot_header_size) + else: + recovery_dtbo_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')) + + num_second_pages = get_number_of_pages(second_size, page_size) + 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: + dtbo_offset = page_size * (num_header_pages + num_kernel_pages + + num_ramdisk_pages + num_second_pages) + image_info_list.append((dtbo_offset, recovery_dtbo_size, + 'recovery_dtbo')) + + 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 parse_cmdline(): + """parse command line arguments""" + parser = ArgumentParser( + description='Unpacks boot.img/recovery.img, extracts the kernel,' + 'ramdisk, second bootloader and recovery dtbo') + 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_bootimage(args) + + +if __name__ == '__main__': + main()