blob: a3f1a508796b5af54216397b05637f1f5e692a3d [file] [log] [blame]
Yi-Yo Chiangcc1cecc2021-02-20 14:25:06 +08001#!/usr/bin/env python3
2#
Hridya Valsarajufcf89632018-03-21 22:18:19 -07003# Copyright 2018, The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
Yi-Yo Chiangcc1cecc2021-02-20 14:25:06 +080017"""Unpacks the boot image.
Hridya Valsarajufcf89632018-03-21 22:18:19 -070018
Hridya Valsarajue55998a2019-01-22 08:58:27 -080019Extracts the kernel, ramdisk, second bootloader, dtb and recovery dtbo images.
Hridya Valsarajufcf89632018-03-21 22:18:19 -070020"""
21
Bowgo Tsaiac2f0c22022-03-23 17:38:43 +080022from argparse import ArgumentParser, RawDescriptionHelpFormatter
Hridya Valsarajufcf89632018-03-21 22:18:19 -070023from struct import unpack
24import os
Yi-Yo Chiang64b181c2021-04-02 22:10:08 +080025import shlex
Hridya Valsarajufcf89632018-03-21 22:18:19 -070026
Luca Stefani9c75ef82019-10-03 11:11:31 +020027BOOT_IMAGE_HEADER_V3_PAGESIZE = 4096
Yo Chiange38be232021-01-19 18:15:24 +080028VENDOR_RAMDISK_NAME_SIZE = 32
Yo Chiang71f7dd62020-11-12 18:58:53 +080029VENDOR_RAMDISK_TABLE_ENTRY_BOARD_ID_SIZE = 16
Hridya Valsarajufcf89632018-03-21 22:18:19 -070030
Yi-Yo Chiangcc1cecc2021-02-20 14:25:06 +080031
Hridya Valsarajufcf89632018-03-21 22:18:19 -070032def create_out_dir(dir_path):
33 """creates a directory 'dir_path' if it does not exist"""
34 if not os.path.exists(dir_path):
35 os.makedirs(dir_path)
36
37
38def extract_image(offset, size, bootimage, extracted_image_name):
39 """extracts an image from the bootimage"""
40 bootimage.seek(offset)
41 with open(extracted_image_name, 'wb') as file_out:
42 file_out.write(bootimage.read(size))
43
44
45def get_number_of_pages(image_size, page_size):
46 """calculates the number of pages required for the image"""
LuK1337cfef94a2019-02-25 04:01:31 +010047 return (image_size + page_size - 1) // page_size
Hridya Valsarajufcf89632018-03-21 22:18:19 -070048
49
Daniel Mentzf91a1562020-01-07 17:23:21 -080050def cstr(s):
51 """Remove first NULL character and any character beyond."""
52 return s.split('\0', 1)[0]
53
54
Daniel Mentz88f7b0f2020-01-03 20:15:15 -080055def format_os_version(os_version):
Yi-Yo Chiangf1d50082021-12-29 01:05:27 +080056 if os_version == 0:
57 return None
Daniel Mentz88f7b0f2020-01-03 20:15:15 -080058 a = os_version >> 14
59 b = os_version >> 7 & ((1<<7) - 1)
60 c = os_version & ((1<<7) - 1)
Bowgo Tsaie71c4402022-03-25 13:49:32 +080061 return f'{a}.{b}.{c}'
Daniel Mentz88f7b0f2020-01-03 20:15:15 -080062
63
64def format_os_patch_level(os_patch_level):
Yi-Yo Chiangf1d50082021-12-29 01:05:27 +080065 if os_patch_level == 0:
66 return None
Daniel Mentz88f7b0f2020-01-03 20:15:15 -080067 y = os_patch_level >> 4
68 y += 2000
69 m = os_patch_level & ((1<<4) - 1)
Bowgo Tsaie71c4402022-03-25 13:49:32 +080070 return f'{y:04d}-{m:02d}'
Daniel Mentz88f7b0f2020-01-03 20:15:15 -080071
72
Bowgo Tsai4903faa2021-03-23 19:02:57 +080073def decode_os_version_patch_level(os_version_patch_level):
74 """Returns a tuple of (os_version, os_patch_level)."""
75 os_version = os_version_patch_level >> 11
76 os_patch_level = os_version_patch_level & ((1<<11) - 1)
77 return (format_os_version(os_version),
78 format_os_patch_level(os_patch_level))
79
80
Bowgo Tsai39b2d4f2021-04-27 17:40:05 +080081class BootImageInfoFormatter:
82 """Formats the boot image info."""
Bowgo Tsai4903faa2021-03-23 19:02:57 +080083
Bowgo Tsai39b2d4f2021-04-27 17:40:05 +080084 def format_pretty_text(self):
85 lines = []
86 lines.append(f'boot magic: {self.boot_magic}')
Bowgo Tsai4903faa2021-03-23 19:02:57 +080087
Bowgo Tsai39b2d4f2021-04-27 17:40:05 +080088 if self.header_version < 3:
89 lines.append(f'kernel_size: {self.kernel_size}')
90 lines.append(
91 f'kernel load address: {self.kernel_load_address:#010x}')
92 lines.append(f'ramdisk size: {self.ramdisk_size}')
93 lines.append(
94 f'ramdisk load address: {self.ramdisk_load_address:#010x}')
95 lines.append(f'second bootloader size: {self.second_size}')
96 lines.append(
97 f'second bootloader load address: '
98 f'{self.second_load_address:#010x}')
99 lines.append(
100 f'kernel tags load address: {self.tags_load_address:#010x}')
101 lines.append(f'page size: {self.page_size}')
102 else:
103 lines.append(f'kernel_size: {self.kernel_size}')
104 lines.append(f'ramdisk size: {self.ramdisk_size}')
Bowgo Tsai4903faa2021-03-23 19:02:57 +0800105
Bowgo Tsai39b2d4f2021-04-27 17:40:05 +0800106 lines.append(f'os version: {self.os_version}')
107 lines.append(f'os patch level: {self.os_patch_level}')
108 lines.append(f'boot image header version: {self.header_version}')
Bowgo Tsai4903faa2021-03-23 19:02:57 +0800109
Bowgo Tsai39b2d4f2021-04-27 17:40:05 +0800110 if self.header_version < 3:
111 lines.append(f'product name: {self.product_name}')
Bowgo Tsai4903faa2021-03-23 19:02:57 +0800112
Bowgo Tsai39b2d4f2021-04-27 17:40:05 +0800113 lines.append(f'command line args: {self.cmdline}')
Bowgo Tsai4903faa2021-03-23 19:02:57 +0800114
Bowgo Tsai39b2d4f2021-04-27 17:40:05 +0800115 if self.header_version < 3:
116 lines.append(f'additional command line args: {self.extra_cmdline}')
Bowgo Tsai4903faa2021-03-23 19:02:57 +0800117
Bowgo Tsai39b2d4f2021-04-27 17:40:05 +0800118 if self.header_version in {1, 2}:
119 lines.append(f'recovery dtbo size: {self.recovery_dtbo_size}')
120 lines.append(
121 f'recovery dtbo offset: {self.recovery_dtbo_offset:#018x}')
122 lines.append(f'boot header size: {self.boot_header_size}')
Bowgo Tsai9612cd42021-02-02 12:11:31 +0800123
Bowgo Tsai39b2d4f2021-04-27 17:40:05 +0800124 if self.header_version == 2:
125 lines.append(f'dtb size: {self.dtb_size}')
126 lines.append(f'dtb address: {self.dtb_load_address:#018x}')
127
128 if self.header_version >= 4:
129 lines.append(
130 f'boot.img signature size: {self.boot_signature_size}')
131
132 return '\n'.join(lines)
133
Yi-Yo Chiang07c60632021-05-03 20:25:11 +0800134 def format_mkbootimg_argument(self):
135 args = []
136 args.extend(['--header_version', str(self.header_version)])
Yi-Yo Chiangf1d50082021-12-29 01:05:27 +0800137 if self.os_version:
138 args.extend(['--os_version', self.os_version])
139 if self.os_patch_level:
140 args.extend(['--os_patch_level', self.os_patch_level])
Bowgo Tsai39b2d4f2021-04-27 17:40:05 +0800141
Yi-Yo Chiang07c60632021-05-03 20:25:11 +0800142 args.extend(['--kernel', os.path.join(self.image_dir, 'kernel')])
143 args.extend(['--ramdisk', os.path.join(self.image_dir, 'ramdisk')])
Bowgo Tsai39b2d4f2021-04-27 17:40:05 +0800144
Bowgo Tsai39b2d4f2021-04-27 17:40:05 +0800145 if self.header_version <= 2:
Yi-Yo Chiang07c60632021-05-03 20:25:11 +0800146 if self.second_size > 0:
147 args.extend(['--second',
148 os.path.join(self.image_dir, 'second')])
149 if self.recovery_dtbo_size > 0:
150 args.extend(['--recovery_dtbo',
151 os.path.join(self.image_dir, 'recovery_dtbo')])
152 if self.dtb_size > 0:
153 args.extend(['--dtb', os.path.join(self.image_dir, 'dtb')])
154
155 args.extend(['--pagesize', f'{self.page_size:#010x}'])
156
157 # Kernel load address is base + kernel_offset in mkbootimg.py.
158 # However we don't know the value of 'base' when unpacking a boot
159 # image in this script, so we set 'base' to zero and 'kernel_offset'
160 # to the kernel load address, 'ramdisk_offset' to the ramdisk load
161 # address, ... etc.
162 args.extend(['--base', f'{0:#010x}'])
163 args.extend(['--kernel_offset',
164 f'{self.kernel_load_address:#010x}'])
165 args.extend(['--ramdisk_offset',
166 f'{self.ramdisk_load_address:#010x}'])
167 args.extend(['--second_offset',
168 f'{self.second_load_address:#010x}'])
169 args.extend(['--tags_offset', f'{self.tags_load_address:#010x}'])
170
171 # dtb is added in boot image v2, and is absent in v1 or v0.
172 if self.header_version == 2:
173 # dtb_offset is uint64_t.
174 args.extend(['--dtb_offset', f'{self.dtb_load_address:#018x}'])
175
176 args.extend(['--board', self.product_name])
177 args.extend(['--cmdline', self.cmdline + self.extra_cmdline])
Bowgo Tsai39b2d4f2021-04-27 17:40:05 +0800178 else:
Yi-Yo Chiang07c60632021-05-03 20:25:11 +0800179 args.extend(['--cmdline', self.cmdline])
180
181 return args
Bowgo Tsai9612cd42021-02-02 12:11:31 +0800182
183
Bowgo Tsaiac2f0c22022-03-23 17:38:43 +0800184def unpack_boot_image(boot_img, output_dir):
Hridya Valsarajufcf89632018-03-21 22:18:19 -0700185 """extracts kernel, ramdisk, second bootloader and recovery dtbo"""
Bowgo Tsai39b2d4f2021-04-27 17:40:05 +0800186 info = BootImageInfoFormatter()
Bowgo Tsaiac2f0c22022-03-23 17:38:43 +0800187 info.boot_magic = unpack('8s', boot_img.read(8))[0].decode()
Yi-Yo Chiangafb003d2021-04-04 13:54:04 +0800188
Bowgo Tsaiac2f0c22022-03-23 17:38:43 +0800189 kernel_ramdisk_second_info = unpack('9I', boot_img.read(9 * 4))
Bowgo Tsai39b2d4f2021-04-27 17:40:05 +0800190 # header_version is always at [8] regardless of the value of header_version.
191 info.header_version = kernel_ramdisk_second_info[8]
Bowgo Tsai4903faa2021-03-23 19:02:57 +0800192
Bowgo Tsai39b2d4f2021-04-27 17:40:05 +0800193 if info.header_version < 3:
194 info.kernel_size = kernel_ramdisk_second_info[0]
195 info.kernel_load_address = kernel_ramdisk_second_info[1]
196 info.ramdisk_size = kernel_ramdisk_second_info[2]
197 info.ramdisk_load_address = kernel_ramdisk_second_info[3]
198 info.second_size = kernel_ramdisk_second_info[4]
199 info.second_load_address = kernel_ramdisk_second_info[5]
200 info.tags_load_address = kernel_ramdisk_second_info[6]
201 info.page_size = kernel_ramdisk_second_info[7]
Bowgo Tsaiac2f0c22022-03-23 17:38:43 +0800202 os_version_patch_level = unpack('I', boot_img.read(1 * 4))[0]
Luca Stefani9c75ef82019-10-03 11:11:31 +0200203 else:
Bowgo Tsai39b2d4f2021-04-27 17:40:05 +0800204 info.kernel_size = kernel_ramdisk_second_info[0]
205 info.ramdisk_size = kernel_ramdisk_second_info[1]
Bowgo Tsai4903faa2021-03-23 19:02:57 +0800206 os_version_patch_level = kernel_ramdisk_second_info[2]
Bowgo Tsai39b2d4f2021-04-27 17:40:05 +0800207 info.second_size = 0
208 info.page_size = BOOT_IMAGE_HEADER_V3_PAGESIZE
Bowgo Tsai4903faa2021-03-23 19:02:57 +0800209
Bowgo Tsai39b2d4f2021-04-27 17:40:05 +0800210 info.os_version, info.os_patch_level = decode_os_version_patch_level(
211 os_version_patch_level)
Luca Stefani9c75ef82019-10-03 11:11:31 +0200212
Bowgo Tsai39b2d4f2021-04-27 17:40:05 +0800213 if info.header_version < 3:
214 info.product_name = cstr(unpack('16s',
Bowgo Tsaiac2f0c22022-03-23 17:38:43 +0800215 boot_img.read(16))[0].decode())
216 info.cmdline = cstr(unpack('512s', boot_img.read(512))[0].decode())
217 boot_img.read(32) # ignore SHA
Bowgo Tsai39b2d4f2021-04-27 17:40:05 +0800218 info.extra_cmdline = cstr(unpack('1024s',
Bowgo Tsaiac2f0c22022-03-23 17:38:43 +0800219 boot_img.read(1024))[0].decode())
Hridya Valsarajufcf89632018-03-21 22:18:19 -0700220 else:
Bowgo Tsai39b2d4f2021-04-27 17:40:05 +0800221 info.cmdline = cstr(unpack('1536s',
Bowgo Tsaiac2f0c22022-03-23 17:38:43 +0800222 boot_img.read(1536))[0].decode())
Bowgo Tsai4903faa2021-03-23 19:02:57 +0800223
Bowgo Tsai39b2d4f2021-04-27 17:40:05 +0800224 if info.header_version in {1, 2}:
Bowgo Tsaiac2f0c22022-03-23 17:38:43 +0800225 info.recovery_dtbo_size = unpack('I', boot_img.read(1 * 4))[0]
226 info.recovery_dtbo_offset = unpack('Q', boot_img.read(8))[0]
227 info.boot_header_size = unpack('I', boot_img.read(4))[0]
Hridya Valsarajue55998a2019-01-22 08:58:27 -0800228 else:
Bowgo Tsai39b2d4f2021-04-27 17:40:05 +0800229 info.recovery_dtbo_size = 0
Bowgo Tsai4903faa2021-03-23 19:02:57 +0800230
Bowgo Tsai39b2d4f2021-04-27 17:40:05 +0800231 if info.header_version == 2:
Bowgo Tsaiac2f0c22022-03-23 17:38:43 +0800232 info.dtb_size = unpack('I', boot_img.read(4))[0]
233 info.dtb_load_address = unpack('Q', boot_img.read(8))[0]
Bowgo Tsai4903faa2021-03-23 19:02:57 +0800234 else:
Bowgo Tsai39b2d4f2021-04-27 17:40:05 +0800235 info.dtb_size = 0
236 info.dtb_load_address = 0
Hridya Valsarajue55998a2019-01-22 08:58:27 -0800237
Bowgo Tsai39b2d4f2021-04-27 17:40:05 +0800238 if info.header_version >= 4:
Bowgo Tsaiac2f0c22022-03-23 17:38:43 +0800239 info.boot_signature_size = unpack('I', boot_img.read(4))[0]
Bowgo Tsai8d0922b2021-02-22 18:19:29 +0800240 else:
Bowgo Tsai39b2d4f2021-04-27 17:40:05 +0800241 info.boot_signature_size = 0
Hridya Valsarajufcf89632018-03-21 22:18:19 -0700242
243 # The first page contains the boot header
244 num_header_pages = 1
245
Bowgo Tsai39b2d4f2021-04-27 17:40:05 +0800246 # Convenient shorthand.
247 page_size = info.page_size
Hridya Valsarajufcf89632018-03-21 22:18:19 -0700248
Bowgo Tsai39b2d4f2021-04-27 17:40:05 +0800249 num_kernel_pages = get_number_of_pages(info.kernel_size, page_size)
250 kernel_offset = page_size * num_header_pages # header occupies a page
251 image_info_list = [(kernel_offset, info.kernel_size, 'kernel')]
252
253 num_ramdisk_pages = get_number_of_pages(info.ramdisk_size, page_size)
Hridya Valsarajufcf89632018-03-21 22:18:19 -0700254 ramdisk_offset = page_size * (num_header_pages + num_kernel_pages
255 ) # header + kernel
Bowgo Tsai39b2d4f2021-04-27 17:40:05 +0800256 image_info_list.append((ramdisk_offset, info.ramdisk_size, 'ramdisk'))
Hridya Valsarajufcf89632018-03-21 22:18:19 -0700257
Bowgo Tsai39b2d4f2021-04-27 17:40:05 +0800258 if info.second_size > 0:
Yuvraj Saxena6302f152019-03-06 14:23:02 +0000259 second_offset = page_size * (
Luca Stefanife614a02019-10-03 10:54:09 +0200260 num_header_pages + num_kernel_pages + num_ramdisk_pages
261 ) # header + kernel + ramdisk
Bowgo Tsai39b2d4f2021-04-27 17:40:05 +0800262 image_info_list.append((second_offset, info.second_size, 'second'))
Hridya Valsarajufcf89632018-03-21 22:18:19 -0700263
Bowgo Tsai39b2d4f2021-04-27 17:40:05 +0800264 if info.recovery_dtbo_size > 0:
265 image_info_list.append((info.recovery_dtbo_offset,
266 info.recovery_dtbo_size,
Hridya Valsarajufcf89632018-03-21 22:18:19 -0700267 'recovery_dtbo'))
Bowgo Tsai39b2d4f2021-04-27 17:40:05 +0800268 if info.dtb_size > 0:
269 num_second_pages = get_number_of_pages(info.second_size, page_size)
Yi-Yo Chiangcc1cecc2021-02-20 14:25:06 +0800270 num_recovery_dtbo_pages = get_number_of_pages(
Bowgo Tsai39b2d4f2021-04-27 17:40:05 +0800271 info.recovery_dtbo_size, page_size)
Hridya Valsarajue55998a2019-01-22 08:58:27 -0800272 dtb_offset = page_size * (
Yi-Yo Chiangcc1cecc2021-02-20 14:25:06 +0800273 num_header_pages + num_kernel_pages + num_ramdisk_pages +
274 num_second_pages + num_recovery_dtbo_pages)
Hridya Valsarajue55998a2019-01-22 08:58:27 -0800275
Bowgo Tsai39b2d4f2021-04-27 17:40:05 +0800276 image_info_list.append((dtb_offset, info.dtb_size, 'dtb'))
Hridya Valsarajufcf89632018-03-21 22:18:19 -0700277
Bowgo Tsai39b2d4f2021-04-27 17:40:05 +0800278 if info.boot_signature_size > 0:
Bowgo Tsai8d0922b2021-02-22 18:19:29 +0800279 # boot signature only exists in boot.img version >= v4.
280 # There are only kernel and ramdisk pages before the signature.
281 boot_signature_offset = page_size * (
282 num_header_pages + num_kernel_pages + num_ramdisk_pages)
283
Bowgo Tsai39b2d4f2021-04-27 17:40:05 +0800284 image_info_list.append((boot_signature_offset, info.boot_signature_size,
Bowgo Tsai8d0922b2021-02-22 18:19:29 +0800285 'boot_signature'))
286
Bowgo Tsaiac2f0c22022-03-23 17:38:43 +0800287 create_out_dir(output_dir)
Yi-Yo Chiang07c60632021-05-03 20:25:11 +0800288 for offset, size, name in image_info_list:
Bowgo Tsaiac2f0c22022-03-23 17:38:43 +0800289 extract_image(offset, size, boot_img, os.path.join(output_dir, name))
290 info.image_dir = output_dir
Hridya Valsarajufcf89632018-03-21 22:18:19 -0700291
Yi-Yo Chiang07c60632021-05-03 20:25:11 +0800292 return info
Bowgo Tsai39b2d4f2021-04-27 17:40:05 +0800293
Hridya Valsarajufcf89632018-03-21 22:18:19 -0700294
Yi-Yo Chiangafb003d2021-04-04 13:54:04 +0800295class VendorBootImageInfoFormatter:
296 """Formats the vendor_boot image info."""
Bowgo Tsai9612cd42021-02-02 12:11:31 +0800297
Yi-Yo Chiangafb003d2021-04-04 13:54:04 +0800298 def format_pretty_text(self):
299 lines = []
300 lines.append(f'boot magic: {self.boot_magic}')
301 lines.append(f'vendor boot image header version: {self.header_version}')
302 lines.append(f'page size: {self.page_size:#010x}')
303 lines.append(f'kernel load address: {self.kernel_load_address:#010x}')
304 lines.append(f'ramdisk load address: {self.ramdisk_load_address:#010x}')
305 if self.header_version > 3:
306 lines.append(
307 f'vendor ramdisk total size: {self.vendor_ramdisk_size}')
308 else:
309 lines.append(f'vendor ramdisk size: {self.vendor_ramdisk_size}')
310 lines.append(f'vendor command line args: {self.cmdline}')
311 lines.append(
312 f'kernel tags load address: {self.tags_load_address:#010x}')
313 lines.append(f'product name: {self.product_name}')
314 lines.append(f'vendor boot image header size: {self.header_size}')
315 lines.append(f'dtb size: {self.dtb_size}')
316 lines.append(f'dtb address: {self.dtb_load_address:#018x}')
317 if self.header_version > 3:
318 lines.append(
319 f'vendor ramdisk table size: {self.vendor_ramdisk_table_size}')
320 lines.append('vendor ramdisk table: [')
321 indent = lambda level: ' ' * 4 * level
322 for entry in self.vendor_ramdisk_table:
323 (output_ramdisk_name, ramdisk_size, ramdisk_offset,
324 ramdisk_type, ramdisk_name, board_id) = entry
325 lines.append(indent(1) + f'{output_ramdisk_name}: ''{')
326 lines.append(indent(2) + f'size: {ramdisk_size}')
327 lines.append(indent(2) + f'offset: {ramdisk_offset}')
328 lines.append(indent(2) + f'type: {ramdisk_type:#x}')
329 lines.append(indent(2) + f'name: {ramdisk_name}')
330 lines.append(indent(2) + 'board_id: [')
331 stride = 4
332 for row_idx in range(0, len(board_id), stride):
333 row = board_id[row_idx:row_idx + stride]
334 lines.append(
335 indent(3) + ' '.join(f'{e:#010x},' for e in row))
336 lines.append(indent(2) + ']')
337 lines.append(indent(1) + '}')
338 lines.append(']')
339 lines.append(
340 f'vendor bootconfig size: {self.vendor_bootconfig_size}')
Bowgo Tsai9612cd42021-02-02 12:11:31 +0800341
Yi-Yo Chiangafb003d2021-04-04 13:54:04 +0800342 return '\n'.join(lines)
Bowgo Tsai9612cd42021-02-02 12:11:31 +0800343
Yi-Yo Chiang07c60632021-05-03 20:25:11 +0800344 def format_mkbootimg_argument(self):
Yi-Yo Chiang64b181c2021-04-02 22:10:08 +0800345 args = []
346 args.extend(['--header_version', str(self.header_version)])
347 args.extend(['--pagesize', f'{self.page_size:#010x}'])
348 args.extend(['--base', f'{0:#010x}'])
349 args.extend(['--kernel_offset', f'{self.kernel_load_address:#010x}'])
350 args.extend(['--ramdisk_offset', f'{self.ramdisk_load_address:#010x}'])
351 args.extend(['--tags_offset', f'{self.tags_load_address:#010x}'])
352 args.extend(['--dtb_offset', f'{self.dtb_load_address:#018x}'])
353 args.extend(['--vendor_cmdline', self.cmdline])
354 args.extend(['--board', self.product_name])
355
Lucas Wei053c3892022-03-08 16:50:57 +0800356 if self.dtb_size > 0:
Bowgo Tsaie71c4402022-03-25 13:49:32 +0800357 args.extend(['--dtb', os.path.join(self.image_dir, 'dtb')])
Yi-Yo Chiang64b181c2021-04-02 22:10:08 +0800358
359 if self.header_version > 3:
Yi-Yo Chiang07c60632021-05-03 20:25:11 +0800360 args.extend(['--vendor_bootconfig',
361 os.path.join(self.image_dir, 'bootconfig')])
Yi-Yo Chiang64b181c2021-04-02 22:10:08 +0800362
363 for entry in self.vendor_ramdisk_table:
364 (output_ramdisk_name, _, _, ramdisk_type,
365 ramdisk_name, board_id) = entry
366 args.extend(['--ramdisk_type', str(ramdisk_type)])
367 args.extend(['--ramdisk_name', ramdisk_name])
368 for idx, e in enumerate(board_id):
369 if e:
370 args.extend([f'--board_id{idx}', f'{e:#010x}'])
371 vendor_ramdisk_path = os.path.join(
372 self.image_dir, output_ramdisk_name)
373 args.extend(['--vendor_ramdisk_fragment', vendor_ramdisk_path])
374 else:
Yi-Yo Chiang07c60632021-05-03 20:25:11 +0800375 args.extend(['--vendor_ramdisk',
376 os.path.join(self.image_dir, 'vendor_ramdisk')])
Yi-Yo Chiang64b181c2021-04-02 22:10:08 +0800377
Yi-Yo Chiang07c60632021-05-03 20:25:11 +0800378 return args
Bowgo Tsai9612cd42021-02-02 12:11:31 +0800379
380
Bowgo Tsaiac2f0c22022-03-23 17:38:43 +0800381def unpack_vendor_boot_image(boot_img, output_dir):
Yi-Yo Chiangafb003d2021-04-04 13:54:04 +0800382 info = VendorBootImageInfoFormatter()
Bowgo Tsaiac2f0c22022-03-23 17:38:43 +0800383 info.boot_magic = unpack('8s', boot_img.read(8))[0].decode()
384 info.header_version = unpack('I', boot_img.read(4))[0]
385 info.page_size = unpack('I', boot_img.read(4))[0]
386 info.kernel_load_address = unpack('I', boot_img.read(4))[0]
387 info.ramdisk_load_address = unpack('I', boot_img.read(4))[0]
388 info.vendor_ramdisk_size = unpack('I', boot_img.read(4))[0]
389 info.cmdline = cstr(unpack('2048s', boot_img.read(2048))[0].decode())
390 info.tags_load_address = unpack('I', boot_img.read(4))[0]
391 info.product_name = cstr(unpack('16s', boot_img.read(16))[0].decode())
392 info.header_size = unpack('I', boot_img.read(4))[0]
393 info.dtb_size = unpack('I', boot_img.read(4))[0]
394 info.dtb_load_address = unpack('Q', boot_img.read(8))[0]
Luca Stefani01c0e302019-10-03 10:52:24 +0200395
Yi-Yo Chiangafb003d2021-04-04 13:54:04 +0800396 # Convenient shorthand.
397 page_size = info.page_size
Steve Muckle3d4b0082020-02-18 14:14:28 -0800398 # The first pages contain the boot header
Yi-Yo Chiangafb003d2021-04-04 13:54:04 +0800399 num_boot_header_pages = get_number_of_pages(info.header_size, page_size)
400 num_boot_ramdisk_pages = get_number_of_pages(
401 info.vendor_ramdisk_size, page_size)
402 num_boot_dtb_pages = get_number_of_pages(info.dtb_size, page_size)
Yo Chiang71f7dd62020-11-12 18:58:53 +0800403
404 ramdisk_offset_base = page_size * num_boot_header_pages
405 image_info_list = []
406
Yi-Yo Chiangafb003d2021-04-04 13:54:04 +0800407 if info.header_version > 3:
Bowgo Tsaiac2f0c22022-03-23 17:38:43 +0800408 info.vendor_ramdisk_table_size = unpack('I', boot_img.read(4))[0]
409 vendor_ramdisk_table_entry_num = unpack('I', boot_img.read(4))[0]
410 vendor_ramdisk_table_entry_size = unpack('I', boot_img.read(4))[0]
411 info.vendor_bootconfig_size = unpack('I', boot_img.read(4))[0]
Devin Moore808d42e2021-01-13 10:20:01 -0800412 num_vendor_ramdisk_table_pages = get_number_of_pages(
Yi-Yo Chiangafb003d2021-04-04 13:54:04 +0800413 info.vendor_ramdisk_table_size, page_size)
Yo Chiang71f7dd62020-11-12 18:58:53 +0800414 vendor_ramdisk_table_offset = page_size * (
415 num_boot_header_pages + num_boot_ramdisk_pages + num_boot_dtb_pages)
Yi-Yo Chiangafb003d2021-04-04 13:54:04 +0800416
417 vendor_ramdisk_table = []
Yo Chianga08985e2021-04-08 11:08:49 +0000418 vendor_ramdisk_symlinks = []
Yo Chiang71f7dd62020-11-12 18:58:53 +0800419 for idx in range(vendor_ramdisk_table_entry_num):
420 entry_offset = vendor_ramdisk_table_offset + (
421 vendor_ramdisk_table_entry_size * idx)
Bowgo Tsaiac2f0c22022-03-23 17:38:43 +0800422 boot_img.seek(entry_offset)
423 ramdisk_size = unpack('I', boot_img.read(4))[0]
424 ramdisk_offset = unpack('I', boot_img.read(4))[0]
425 ramdisk_type = unpack('I', boot_img.read(4))[0]
Yi-Yo Chiangcc1cecc2021-02-20 14:25:06 +0800426 ramdisk_name = cstr(unpack(
Yi-Yo Chiangafb003d2021-04-04 13:54:04 +0800427 f'{VENDOR_RAMDISK_NAME_SIZE}s',
Bowgo Tsaiac2f0c22022-03-23 17:38:43 +0800428 boot_img.read(VENDOR_RAMDISK_NAME_SIZE))[0].decode())
Yi-Yo Chiangafb003d2021-04-04 13:54:04 +0800429 board_id = unpack(
430 f'{VENDOR_RAMDISK_TABLE_ENTRY_BOARD_ID_SIZE}I',
Bowgo Tsaiac2f0c22022-03-23 17:38:43 +0800431 boot_img.read(
Yi-Yo Chiangafb003d2021-04-04 13:54:04 +0800432 4 * VENDOR_RAMDISK_TABLE_ENTRY_BOARD_ID_SIZE))
Yi-Yo Chiang8852a3a2021-04-06 19:15:36 +0800433 output_ramdisk_name = f'vendor_ramdisk{idx:02}'
Yo Chiang71f7dd62020-11-12 18:58:53 +0800434
Yo Chiang71f7dd62020-11-12 18:58:53 +0800435 image_info_list.append((ramdisk_offset_base + ramdisk_offset,
Yo Chiange38be232021-01-19 18:15:24 +0800436 ramdisk_size, output_ramdisk_name))
Yo Chianga08985e2021-04-08 11:08:49 +0000437 vendor_ramdisk_symlinks.append((output_ramdisk_name, ramdisk_name))
Yi-Yo Chiangafb003d2021-04-04 13:54:04 +0800438 vendor_ramdisk_table.append(
439 (output_ramdisk_name, ramdisk_size, ramdisk_offset,
440 ramdisk_type, ramdisk_name, board_id))
441
442 info.vendor_ramdisk_table = vendor_ramdisk_table
443
Devin Moore808d42e2021-01-13 10:20:01 -0800444 bootconfig_offset = page_size * (num_boot_header_pages
445 + num_boot_ramdisk_pages + num_boot_dtb_pages
446 + num_vendor_ramdisk_table_pages)
Yi-Yo Chiangafb003d2021-04-04 13:54:04 +0800447 image_info_list.append((bootconfig_offset, info.vendor_bootconfig_size,
Devin Moore808d42e2021-01-13 10:20:01 -0800448 'bootconfig'))
Yi-yo Chiangf85a2fd2022-08-10 09:12:10 +0000449 else:
450 image_info_list.append(
451 (ramdisk_offset_base, info.vendor_ramdisk_size, 'vendor_ramdisk'))
Luca Stefani01c0e302019-10-03 10:52:24 +0200452
453 dtb_offset = page_size * (num_boot_header_pages + num_boot_ramdisk_pages
454 ) # header + vendor_ramdisk
Lucas Wei053c3892022-03-08 16:50:57 +0800455 if info.dtb_size > 0:
Bowgo Tsaie71c4402022-03-25 13:49:32 +0800456 image_info_list.append((dtb_offset, info.dtb_size, 'dtb'))
Luca Stefani01c0e302019-10-03 10:52:24 +0200457
Bowgo Tsaiac2f0c22022-03-23 17:38:43 +0800458 create_out_dir(output_dir)
Yi-Yo Chiang07c60632021-05-03 20:25:11 +0800459 for offset, size, name in image_info_list:
Bowgo Tsaiac2f0c22022-03-23 17:38:43 +0800460 extract_image(offset, size, boot_img, os.path.join(output_dir, name))
461 info.image_dir = output_dir
Yo Chianga08985e2021-04-08 11:08:49 +0000462
463 if info.header_version > 3:
464 vendor_ramdisk_by_name_dir = os.path.join(
Bowgo Tsaiac2f0c22022-03-23 17:38:43 +0800465 output_dir, 'vendor-ramdisk-by-name')
Yo Chianga08985e2021-04-08 11:08:49 +0000466 create_out_dir(vendor_ramdisk_by_name_dir)
467 for src, dst in vendor_ramdisk_symlinks:
468 src_pathname = os.path.join('..', src)
469 dst_pathname = os.path.join(
470 vendor_ramdisk_by_name_dir, f'ramdisk_{dst}')
471 if os.path.lexists(dst_pathname):
472 os.remove(dst_pathname)
473 os.symlink(src_pathname, dst_pathname)
474
Yi-Yo Chiang07c60632021-05-03 20:25:11 +0800475 return info
Luca Stefani01c0e302019-10-03 10:52:24 +0200476
477
Bowgo Tsaiac2f0c22022-03-23 17:38:43 +0800478def unpack_bootimg(boot_img, output_dir):
479 """Unpacks the |boot_img| to |output_dir|, and returns the 'info' object."""
480 with open(boot_img, 'rb') as image_file:
481 boot_magic = unpack('8s', image_file.read(8))[0].decode()
482 image_file.seek(0)
483 if boot_magic == 'ANDROID!':
484 info = unpack_boot_image(image_file, output_dir)
485 elif boot_magic == 'VNDRBOOT':
486 info = unpack_vendor_boot_image(image_file, output_dir)
487 else:
488 raise ValueError(f'Not an Android boot image, magic: {boot_magic}')
Yi-Yo Chiang07c60632021-05-03 20:25:11 +0800489
Bowgo Tsaiac2f0c22022-03-23 17:38:43 +0800490 return info
491
492
493def print_bootimg_info(info, output_format, null_separator):
494 """Format and print boot image info."""
495 if output_format == 'mkbootimg':
Yi-Yo Chiang07c60632021-05-03 20:25:11 +0800496 mkbootimg_args = info.format_mkbootimg_argument()
Bowgo Tsaiac2f0c22022-03-23 17:38:43 +0800497 if null_separator:
Yi-Yo Chiang07c60632021-05-03 20:25:11 +0800498 print('\0'.join(mkbootimg_args) + '\0', end='')
499 else:
500 print(shlex.join(mkbootimg_args))
501 else:
502 print(info.format_pretty_text())
Luca Stefani01c0e302019-10-03 10:52:24 +0200503
504
Yi-Yo Chiang64b181c2021-04-02 22:10:08 +0800505def get_unpack_usage():
506 return """Output format:
507
508 * info
509
510 Pretty-printed info-rich text format suitable for human inspection.
511
512 * mkbootimg
513
514 Output shell-escaped (quoted) argument strings that can be used to
515 reconstruct the boot image. For example:
516
517 $ unpack_bootimg --boot_img vendor_boot.img --out out --format=mkbootimg |
518 tee mkbootimg_args
519 $ sh -c "mkbootimg $(cat mkbootimg_args) --vendor_boot repacked.img"
520
521 vendor_boot.img and repacked.img would be equivalent.
522
523 If the -0 option is specified, output unescaped null-terminated argument
524 strings that are suitable to be parsed by a shell script (xargs -0 format):
525
526 $ unpack_bootimg --boot_img vendor_boot.img --out out --format=mkbootimg \\
527 -0 | tee mkbootimg_args
528 $ declare -a MKBOOTIMG_ARGS=()
529 $ while IFS= read -r -d '' ARG; do
530 MKBOOTIMG_ARGS+=("${ARG}")
531 done <mkbootimg_args
532 $ mkbootimg "${MKBOOTIMG_ARGS[@]}" --vendor_boot repacked.img
533"""
534
535
Hridya Valsarajufcf89632018-03-21 22:18:19 -0700536def parse_cmdline():
537 """parse command line arguments"""
538 parser = ArgumentParser(
Yi-Yo Chiangafb003d2021-04-04 13:54:04 +0800539 formatter_class=RawDescriptionHelpFormatter,
540 description='Unpacks boot, recovery or vendor_boot image.',
Yi-Yo Chiang64b181c2021-04-02 22:10:08 +0800541 epilog=get_unpack_usage(),
Yi-Yo Chiangafb003d2021-04-04 13:54:04 +0800542 )
Bowgo Tsaiac2f0c22022-03-23 17:38:43 +0800543 parser.add_argument('--boot_img', required=True,
Yi-Yo Chiangafb003d2021-04-04 13:54:04 +0800544 help='path to the boot, recovery or vendor_boot image')
545 parser.add_argument('--out', default='out',
546 help='output directory of the unpacked images')
Yi-Yo Chiang64b181c2021-04-02 22:10:08 +0800547 parser.add_argument('--format', choices=['info', 'mkbootimg'],
548 default='info',
549 help='text output format (default: info)')
550 parser.add_argument('-0', '--null', action='store_true',
551 help='output null-terminated argument strings')
Hridya Valsarajufcf89632018-03-21 22:18:19 -0700552 return parser.parse_args()
553
554
555def main():
556 """parse arguments and unpack boot image"""
557 args = parse_cmdline()
Bowgo Tsaiac2f0c22022-03-23 17:38:43 +0800558 info = unpack_bootimg(args.boot_img, args.out)
559 print_bootimg_info(info, args.format, args.null)
Hridya Valsarajufcf89632018-03-21 22:18:19 -0700560
561
562if __name__ == '__main__':
563 main()