hc
2024-12-19 9370bb92b2d16684ee45cf24e879c93c509162da
kernel/scripts/unpack_bootimg
....@@ -1,4 +1,5 @@
1
-#!/usr/bin/env python
1
+#!/usr/bin/env python3
2
+#
23 # Copyright 2018, The Android Open Source Project
34 #
45 # Licensed under the Apache License, Version 2.0 (the "License");
....@@ -13,18 +14,20 @@
1314 # See the License for the specific language governing permissions and
1415 # limitations under the License.
1516
16
-"""unpacks the bootimage.
17
+"""Unpacks the boot image.
1718
1819 Extracts the kernel, ramdisk, second bootloader, dtb and recovery dtbo images.
1920 """
2021
21
-from __future__ import print_function
22
-from argparse import ArgumentParser, FileType
22
+from argparse import ArgumentParser, RawDescriptionHelpFormatter
2323 from struct import unpack
2424 import os
25
+import shlex
2526
2627 BOOT_IMAGE_HEADER_V3_PAGESIZE = 4096
27
-VENDOR_BOOT_IMAGE_HEADER_V3_SIZE = 2112
28
+VENDOR_RAMDISK_NAME_SIZE = 32
29
+VENDOR_RAMDISK_TABLE_ENTRY_BOARD_ID_SIZE = 16
30
+
2831
2932 def create_out_dir(dir_path):
3033 """creates a directory 'dir_path' if it does not exist"""
....@@ -50,196 +53,510 @@
5053
5154
5255 def format_os_version(os_version):
56
+ if os_version == 0:
57
+ return None
5358 a = os_version >> 14
5459 b = os_version >> 7 & ((1<<7) - 1)
5560 c = os_version & ((1<<7) - 1)
56
- return '{}.{}.{}'.format(a, b, c)
61
+ return f'{a}.{b}.{c}'
5762
5863
5964 def format_os_patch_level(os_patch_level):
65
+ if os_patch_level == 0:
66
+ return None
6067 y = os_patch_level >> 4
6168 y += 2000
6269 m = os_patch_level & ((1<<4) - 1)
63
- return '{:04d}-{:02d}'.format(y, m)
70
+ return f'{y:04d}-{m:02d}'
6471
6572
66
-def print_os_version_patch_level(value):
67
- os_version = value >> 11
68
- os_patch_level = value & ((1<<11) - 1)
69
- print('os version: %s' % format_os_version(os_version))
70
- print('os patch level: %s' % format_os_patch_level(os_patch_level))
73
+def 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))
7179
7280
73
-def unpack_bootimage(args):
81
+class BootImageInfoFormatter:
82
+ """Formats the boot image info."""
83
+
84
+ def format_pretty_text(self):
85
+ lines = []
86
+ lines.append(f'boot magic: {self.boot_magic}')
87
+
88
+ 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}')
105
+
106
+ 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}')
109
+
110
+ if self.header_version < 3:
111
+ lines.append(f'product name: {self.product_name}')
112
+
113
+ lines.append(f'command line args: {self.cmdline}')
114
+
115
+ if self.header_version < 3:
116
+ lines.append(f'additional command line args: {self.extra_cmdline}')
117
+
118
+ 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}')
123
+
124
+ 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
+
134
+ def format_mkbootimg_argument(self):
135
+ args = []
136
+ args.extend(['--header_version', str(self.header_version)])
137
+ 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])
141
+
142
+ args.extend(['--kernel', os.path.join(self.image_dir, 'kernel')])
143
+ args.extend(['--ramdisk', os.path.join(self.image_dir, 'ramdisk')])
144
+
145
+ if self.header_version <= 2:
146
+ 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])
178
+ else:
179
+ args.extend(['--cmdline', self.cmdline])
180
+
181
+ return args
182
+
183
+
184
+def unpack_boot_image(boot_img, output_dir):
74185 """extracts kernel, ramdisk, second bootloader and recovery dtbo"""
75
- kernel_ramdisk_second_info = unpack('9I', args.boot_img.read(9 * 4))
76
- version = kernel_ramdisk_second_info[8]
77
- if version < 3:
78
- print('kernel_size: %s' % kernel_ramdisk_second_info[0])
79
- print('kernel load address: %#x' % kernel_ramdisk_second_info[1])
80
- print('ramdisk size: %s' % kernel_ramdisk_second_info[2])
81
- print('ramdisk load address: %#x' % kernel_ramdisk_second_info[3])
82
- print('second bootloader size: %s' % kernel_ramdisk_second_info[4])
83
- print('second bootloader load address: %#x' % kernel_ramdisk_second_info[5])
84
- print('kernel tags load address: %#x' % kernel_ramdisk_second_info[6])
85
- print('page size: %s' % kernel_ramdisk_second_info[7])
86
- print_os_version_patch_level(unpack('I', args.boot_img.read(1 * 4))[0])
186
+ info = BootImageInfoFormatter()
187
+ info.boot_magic = unpack('8s', boot_img.read(8))[0].decode()
188
+
189
+ kernel_ramdisk_second_info = unpack('9I', boot_img.read(9 * 4))
190
+ # header_version is always at [8] regardless of the value of header_version.
191
+ info.header_version = kernel_ramdisk_second_info[8]
192
+
193
+ 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]
202
+ os_version_patch_level = unpack('I', boot_img.read(1 * 4))[0]
87203 else:
88
- print('kernel_size: %s' % kernel_ramdisk_second_info[0])
89
- print('ramdisk size: %s' % kernel_ramdisk_second_info[1])
90
- print_os_version_patch_level(kernel_ramdisk_second_info[2])
204
+ info.kernel_size = kernel_ramdisk_second_info[0]
205
+ info.ramdisk_size = kernel_ramdisk_second_info[1]
206
+ os_version_patch_level = kernel_ramdisk_second_info[2]
207
+ info.second_size = 0
208
+ info.page_size = BOOT_IMAGE_HEADER_V3_PAGESIZE
91209
92
- print('boot image header version: %s' % version)
210
+ info.os_version, info.os_patch_level = decode_os_version_patch_level(
211
+ os_version_patch_level)
93212
94
- if version < 3:
95
- product_name = cstr(unpack('16s', args.boot_img.read(16))[0].decode())
96
- print('product name: %s' % product_name)
97
- cmdline = cstr(unpack('512s', args.boot_img.read(512))[0].decode())
98
- print('command line args: %s' % cmdline)
213
+ if info.header_version < 3:
214
+ info.product_name = cstr(unpack('16s',
215
+ 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
218
+ info.extra_cmdline = cstr(unpack('1024s',
219
+ boot_img.read(1024))[0].decode())
99220 else:
100
- cmdline = cstr(unpack('1536s', args.boot_img.read(1536))[0].decode())
101
- print('command line args: %s' % cmdline)
221
+ info.cmdline = cstr(unpack('1536s',
222
+ boot_img.read(1536))[0].decode())
102223
103
- if version < 3:
104
- args.boot_img.read(32) # ignore SHA
105
-
106
- if version < 3:
107
- extra_cmdline = cstr(unpack('1024s',
108
- args.boot_img.read(1024))[0].decode())
109
- print('additional command line args: %s' % extra_cmdline)
110
-
111
- if version < 3:
112
- kernel_size = kernel_ramdisk_second_info[0]
113
- ramdisk_size = kernel_ramdisk_second_info[2]
114
- second_size = kernel_ramdisk_second_info[4]
115
- page_size = kernel_ramdisk_second_info[7]
224
+ if info.header_version in {1, 2}:
225
+ 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]
116228 else:
117
- kernel_size = kernel_ramdisk_second_info[0]
118
- ramdisk_size = kernel_ramdisk_second_info[1]
119
- second_size = 0
120
- page_size = BOOT_IMAGE_HEADER_V3_PAGESIZE
229
+ info.recovery_dtbo_size = 0
121230
122
- if 0 < version < 3:
123
- recovery_dtbo_size = unpack('I', args.boot_img.read(1 * 4))[0]
124
- print('recovery dtbo size: %s' % recovery_dtbo_size)
125
- recovery_dtbo_offset = unpack('Q', args.boot_img.read(8))[0]
126
- print('recovery dtbo offset: %#x' % recovery_dtbo_offset)
127
- boot_header_size = unpack('I', args.boot_img.read(4))[0]
128
- print('boot header size: %s' % boot_header_size)
231
+ if info.header_version == 2:
232
+ info.dtb_size = unpack('I', boot_img.read(4))[0]
233
+ info.dtb_load_address = unpack('Q', boot_img.read(8))[0]
129234 else:
130
- recovery_dtbo_size = 0
131
- if 1 < version < 3:
132
- dtb_size = unpack('I', args.boot_img.read(4))[0]
133
- print('dtb size: %s' % dtb_size)
134
- dtb_load_address = unpack('Q', args.boot_img.read(8))[0]
135
- print('dtb address: %#x' % dtb_load_address)
136
- else:
137
- dtb_size = 0
235
+ info.dtb_size = 0
236
+ info.dtb_load_address = 0
138237
238
+ if info.header_version >= 4:
239
+ info.boot_signature_size = unpack('I', boot_img.read(4))[0]
240
+ else:
241
+ info.boot_signature_size = 0
139242
140243 # The first page contains the boot header
141244 num_header_pages = 1
142245
143
- num_kernel_pages = get_number_of_pages(kernel_size, page_size)
144
- kernel_offset = page_size * num_header_pages # header occupies a page
145
- image_info_list = [(kernel_offset, kernel_size, 'kernel')]
246
+ # Convenient shorthand.
247
+ page_size = info.page_size
146248
147
- num_ramdisk_pages = get_number_of_pages(ramdisk_size, page_size)
249
+ 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)
148254 ramdisk_offset = page_size * (num_header_pages + num_kernel_pages
149255 ) # header + kernel
150
- image_info_list.append((ramdisk_offset, ramdisk_size, 'ramdisk'))
256
+ image_info_list.append((ramdisk_offset, info.ramdisk_size, 'ramdisk'))
151257
152
- if second_size > 0:
258
+ if info.second_size > 0:
153259 second_offset = page_size * (
154260 num_header_pages + num_kernel_pages + num_ramdisk_pages
155261 ) # header + kernel + ramdisk
156
- image_info_list.append((second_offset, second_size, 'second'))
262
+ image_info_list.append((second_offset, info.second_size, 'second'))
157263
158
- if recovery_dtbo_size > 0:
159
- image_info_list.append((recovery_dtbo_offset, recovery_dtbo_size,
264
+ if info.recovery_dtbo_size > 0:
265
+ image_info_list.append((info.recovery_dtbo_offset,
266
+ info.recovery_dtbo_size,
160267 'recovery_dtbo'))
161
- if dtb_size > 0:
162
- num_second_pages = get_number_of_pages(second_size, page_size)
163
- num_recovery_dtbo_pages = get_number_of_pages(recovery_dtbo_size, page_size)
268
+ if info.dtb_size > 0:
269
+ num_second_pages = get_number_of_pages(info.second_size, page_size)
270
+ num_recovery_dtbo_pages = get_number_of_pages(
271
+ info.recovery_dtbo_size, page_size)
164272 dtb_offset = page_size * (
165
- num_header_pages + num_kernel_pages + num_ramdisk_pages + num_second_pages +
166
- num_recovery_dtbo_pages
167
- )
273
+ num_header_pages + num_kernel_pages + num_ramdisk_pages +
274
+ num_second_pages + num_recovery_dtbo_pages)
168275
169
- image_info_list.append((dtb_offset, dtb_size, 'dtb'))
276
+ image_info_list.append((dtb_offset, info.dtb_size, 'dtb'))
170277
171
- for image_info in image_info_list:
172
- extract_image(image_info[0], image_info[1], args.boot_img,
173
- os.path.join(args.out, image_info[2]))
278
+ if info.boot_signature_size > 0:
279
+ # 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
+
284
+ image_info_list.append((boot_signature_offset, info.boot_signature_size,
285
+ 'boot_signature'))
286
+
287
+ create_out_dir(output_dir)
288
+ for offset, size, name in image_info_list:
289
+ extract_image(offset, size, boot_img, os.path.join(output_dir, name))
290
+ info.image_dir = output_dir
291
+
292
+ return info
174293
175294
176
-def unpack_vendor_bootimage(args):
177
- kernel_ramdisk_info = unpack('5I', args.boot_img.read(5 * 4))
178
- print('vendor boot image header version: %s' % kernel_ramdisk_info[0])
179
- print('kernel load address: %#x' % kernel_ramdisk_info[2])
180
- print('ramdisk load address: %#x' % kernel_ramdisk_info[3])
181
- print('vendor ramdisk size: %s' % kernel_ramdisk_info[4])
295
+class VendorBootImageInfoFormatter:
296
+ """Formats the vendor_boot image info."""
182297
183
- cmdline = cstr(unpack('2048s', args.boot_img.read(2048))[0].decode())
184
- print('vendor command line args: %s' % cmdline)
298
+ 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}')
185341
186
- tags_load_address = unpack('I', args.boot_img.read(1 * 4))[0]
187
- print('kernel tags load address: %#x' % tags_load_address)
342
+ return '\n'.join(lines)
188343
189
- product_name = cstr(unpack('16s', args.boot_img.read(16))[0].decode())
190
- print('product name: %s' % product_name)
344
+ def format_mkbootimg_argument(self):
345
+ 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])
191355
192
- dtb_size = unpack('2I', args.boot_img.read(2 * 4))[1]
193
- print('dtb size: %s' % dtb_size)
194
- dtb_load_address = unpack('Q', args.boot_img.read(8))[0]
195
- print('dtb address: %#x' % dtb_load_address)
356
+ if self.dtb_size > 0:
357
+ args.extend(['--dtb', os.path.join(self.image_dir, 'dtb')])
196358
197
- ramdisk_size = kernel_ramdisk_info[4]
198
- page_size = kernel_ramdisk_info[1]
359
+ if self.header_version > 3:
360
+ args.extend(['--vendor_bootconfig',
361
+ os.path.join(self.image_dir, 'bootconfig')])
199362
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:
375
+ args.extend(['--vendor_ramdisk',
376
+ os.path.join(self.image_dir, 'vendor_ramdisk')])
377
+
378
+ return args
379
+
380
+
381
+def unpack_vendor_boot_image(boot_img, output_dir):
382
+ info = VendorBootImageInfoFormatter()
383
+ 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]
395
+
396
+ # Convenient shorthand.
397
+ page_size = info.page_size
200398 # The first pages contain the boot header
201
- num_boot_header_pages = get_number_of_pages(VENDOR_BOOT_IMAGE_HEADER_V3_SIZE, page_size)
202
- num_boot_ramdisk_pages = get_number_of_pages(ramdisk_size, page_size)
203
- ramdisk_offset = page_size * num_boot_header_pages
204
- image_info_list = [(ramdisk_offset, ramdisk_size, 'vendor_ramdisk')]
399
+ 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)
403
+
404
+ ramdisk_offset_base = page_size * num_boot_header_pages
405
+ image_info_list = []
406
+
407
+ if info.header_version > 3:
408
+ 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]
412
+ num_vendor_ramdisk_table_pages = get_number_of_pages(
413
+ info.vendor_ramdisk_table_size, page_size)
414
+ vendor_ramdisk_table_offset = page_size * (
415
+ num_boot_header_pages + num_boot_ramdisk_pages + num_boot_dtb_pages)
416
+
417
+ vendor_ramdisk_table = []
418
+ vendor_ramdisk_symlinks = []
419
+ for idx in range(vendor_ramdisk_table_entry_num):
420
+ entry_offset = vendor_ramdisk_table_offset + (
421
+ vendor_ramdisk_table_entry_size * idx)
422
+ 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]
426
+ ramdisk_name = cstr(unpack(
427
+ f'{VENDOR_RAMDISK_NAME_SIZE}s',
428
+ boot_img.read(VENDOR_RAMDISK_NAME_SIZE))[0].decode())
429
+ board_id = unpack(
430
+ f'{VENDOR_RAMDISK_TABLE_ENTRY_BOARD_ID_SIZE}I',
431
+ boot_img.read(
432
+ 4 * VENDOR_RAMDISK_TABLE_ENTRY_BOARD_ID_SIZE))
433
+ output_ramdisk_name = f'vendor_ramdisk{idx:02}'
434
+
435
+ image_info_list.append((ramdisk_offset_base + ramdisk_offset,
436
+ ramdisk_size, output_ramdisk_name))
437
+ vendor_ramdisk_symlinks.append((output_ramdisk_name, ramdisk_name))
438
+ 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
+
444
+ bootconfig_offset = page_size * (num_boot_header_pages
445
+ + num_boot_ramdisk_pages + num_boot_dtb_pages
446
+ + num_vendor_ramdisk_table_pages)
447
+ image_info_list.append((bootconfig_offset, info.vendor_bootconfig_size,
448
+ 'bootconfig'))
449
+ else:
450
+ image_info_list.append(
451
+ (ramdisk_offset_base, info.vendor_ramdisk_size, 'vendor_ramdisk'))
205452
206453 dtb_offset = page_size * (num_boot_header_pages + num_boot_ramdisk_pages
207454 ) # header + vendor_ramdisk
208
- image_info_list.append((dtb_offset, dtb_size, 'dtb'))
455
+ if info.dtb_size > 0:
456
+ image_info_list.append((dtb_offset, info.dtb_size, 'dtb'))
209457
210
- for image_info in image_info_list:
211
- extract_image(image_info[0], image_info[1], args.boot_img,
212
- os.path.join(args.out, image_info[2]))
458
+ create_out_dir(output_dir)
459
+ for offset, size, name in image_info_list:
460
+ extract_image(offset, size, boot_img, os.path.join(output_dir, name))
461
+ info.image_dir = output_dir
462
+
463
+ if info.header_version > 3:
464
+ vendor_ramdisk_by_name_dir = os.path.join(
465
+ output_dir, 'vendor-ramdisk-by-name')
466
+ 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
+
475
+ return info
213476
214477
215
-def unpack_image(args):
216
- boot_magic = unpack('8s', args.boot_img.read(8))[0].decode()
217
- print('boot_magic: %s' % boot_magic)
218
- if boot_magic == "ANDROID!":
219
- unpack_bootimage(args)
220
- elif boot_magic == "VNDRBOOT":
221
- unpack_vendor_bootimage(args)
478
+def 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}')
489
+
490
+ return info
491
+
492
+
493
+def print_bootimg_info(info, output_format, null_separator):
494
+ """Format and print boot image info."""
495
+ if output_format == 'mkbootimg':
496
+ mkbootimg_args = info.format_mkbootimg_argument()
497
+ if null_separator:
498
+ print('\0'.join(mkbootimg_args) + '\0', end='')
499
+ else:
500
+ print(shlex.join(mkbootimg_args))
501
+ else:
502
+ print(info.format_pretty_text())
503
+
504
+
505
+def 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
+"""
222534
223535
224536 def parse_cmdline():
225537 """parse command line arguments"""
226538 parser = ArgumentParser(
227
- description='Unpacks boot.img/recovery.img, extracts the kernel,'
228
- 'ramdisk, second bootloader, recovery dtbo and dtb')
229
- parser.add_argument(
230
- '--boot_img',
231
- help='path to boot image',
232
- type=FileType('rb'),
233
- required=True)
234
- parser.add_argument('--out', help='path to out binaries', default='out')
539
+ formatter_class=RawDescriptionHelpFormatter,
540
+ description='Unpacks boot, recovery or vendor_boot image.',
541
+ epilog=get_unpack_usage(),
542
+ )
543
+ parser.add_argument('--boot_img', required=True,
544
+ 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')
547
+ 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')
235552 return parser.parse_args()
236553
237554
238555 def main():
239556 """parse arguments and unpack boot image"""
240557 args = parse_cmdline()
241
- create_out_dir(args.out)
242
- unpack_image(args)
558
+ info = unpack_bootimg(args.boot_img, args.out)
559
+ print_bootimg_info(info, args.format, args.null)
243560
244561
245562 if __name__ == '__main__':