1414# limitations under the License.
1515
1616from __future__ import print_function
17- from sys import argv , exit , stderr
17+
1818from argparse import ArgumentParser , FileType , Action
19- from os import fstat
20- from struct import pack
2119from hashlib import sha1
22- import sys
20+ from os import fstat
2321import re
22+ from struct import pack
23+
24+
25+ BOOT_IMAGE_HEADER_V3_PAGESIZE = 4096
2426
2527def filesize (f ):
2628 if f is None :
@@ -61,18 +63,61 @@ def get_recovery_dtbo_offset(args):
6163 return dtbo_offset
6264
6365
66+ def write_header_v3 (args ):
67+ BOOT_IMAGE_HEADER_V3_SIZE = 1580
68+ BOOT_MAGIC = 'ANDROID!' .encode ()
69+
70+ args .output .write (pack ('8s' , BOOT_MAGIC ))
71+ args .output .write (pack (
72+ '4I' ,
73+ filesize (args .kernel ), # kernel size in bytes
74+ filesize (args .ramdisk ), # ramdisk size in bytes
75+ (args .os_version << 11 ) | args .os_patch_level , # os version and patch level
76+ BOOT_IMAGE_HEADER_V3_SIZE ))
77+
78+ args .output .write (pack ('4I' , 0 , 0 , 0 , 0 )) # reserved
79+
80+ args .output .write (pack ('I' , args .header_version )) # version of bootimage header
81+ args .output .write (pack ('1536s' , args .cmdline .encode ()))
82+ pad_file (args .output , BOOT_IMAGE_HEADER_V3_PAGESIZE )
83+
84+ def write_vendor_boot_header (args ):
85+ VENDOR_BOOT_IMAGE_HEADER_V3_SIZE = 2112
86+ BOOT_MAGIC = 'VNDRBOOT' .encode ()
87+
88+ args .vendor_boot .write (pack ('8s' , BOOT_MAGIC ))
89+ args .vendor_boot .write (pack (
90+ '5I' ,
91+ args .header_version , # version of header
92+ args .pagesize , # flash page size we assume
93+ args .base + args .kernel_offset , # kernel physical load addr
94+ args .base + args .ramdisk_offset , # ramdisk physical load addr
95+ filesize (args .vendor_ramdisk ))) # vendor ramdisk size in bytes
96+ args .vendor_boot .write (pack ('2048s' , args .vendor_cmdline .encode ()))
97+ args .vendor_boot .write (pack ('I' , args .base + args .tags_offset )) # physical addr for kernel tags
98+ args .vendor_boot .write (pack ('16s' , args .board .encode ())) # asciiz product name
99+ args .vendor_boot .write (pack ('I' , VENDOR_BOOT_IMAGE_HEADER_V3_SIZE )) # header size in bytes
100+ if filesize (args .dtb ) == 0 :
101+ raise ValueError ("DTB image must not be empty." )
102+ args .vendor_boot .write (pack ('I' , filesize (args .dtb ))) # size in bytes
103+ args .vendor_boot .write (pack ('Q' , args .base + args .dtb_offset )) # dtb physical load address
104+ pad_file (args .vendor_boot , args .pagesize )
105+
64106def write_header (args ):
65107 BOOT_IMAGE_HEADER_V1_SIZE = 1648
66108 BOOT_IMAGE_HEADER_V2_SIZE = 1660
67109 BOOT_MAGIC = 'ANDROID!' .encode ()
68110
69- if ( args .header_version > 2 ) :
111+ if args .header_version > 3 :
70112 raise ValueError ('Boot header version %d not supported' % args .header_version )
113+ elif args .header_version == 3 :
114+ return write_header_v3 (args )
71115
72116 args .output .write (pack ('8s' , BOOT_MAGIC ))
73117 final_ramdisk_offset = (args .base + args .ramdisk_offset ) if filesize (args .ramdisk ) > 0 else 0
74118 final_second_offset = (args .base + args .second_offset ) if filesize (args .second ) > 0 else 0
75- args .output .write (pack ('10I' ,
119+ args .output .write (pack (
120+ '10I' ,
76121 filesize (args .kernel ), # size in bytes
77122 args .base + args .kernel_offset , # physical load addr
78123 filesize (args .ramdisk ), # size in bytes
@@ -115,6 +160,10 @@ def write_header(args):
115160 args .output .write (pack ('I' , BOOT_IMAGE_HEADER_V2_SIZE ))
116161
117162 if args .header_version > 1 :
163+
164+ # if filesize(args.dtb) == 0:
165+ # raise ValueError("DTB image must not be empty.")
166+
118167 args .output .write (pack ('I' , filesize (args .dtb ))) # size in bytes
119168 args .output .write (pack ('Q' , args .base + args .dtb_offset )) # dtb physical load address
120169 pad_file (args .output , args .pagesize )
@@ -131,8 +180,8 @@ class ValidateStrLenAction(Action):
131180
132181 def __call__ (self , parser , namespace , values , option_string = None ):
133182 if len (values ) > self .maxlen :
134- raise ValueError ('String argument too long: max {0:d}, got {1:d}' .
135- format (self .maxlen , len (values )))
183+ raise ValueError (
184+ 'String argument too long: max {0:d}, got {1:d}' . format (self .maxlen , len (values )))
136185 setattr (namespace , self .dest , values )
137186
138187
@@ -146,6 +195,7 @@ def write_padded_file(f_out, f_in, padding):
146195def parse_int (x ):
147196 return int (x , 0 )
148197
198+
149199def parse_os_version (x ):
150200 match = re .search (r'^(\d{1,3})(?:\.(\d{1,3})(?:\.(\d{1,3}))?)?' , x )
151201 if match :
@@ -162,33 +212,40 @@ def parse_os_version(x):
162212 return (a << 14 ) | (b << 7 ) | c
163213 return 0
164214
215+
165216def parse_os_patch_level (x ):
166- match = re .search (r'^(\d{4})-(\d{2})-(\d{2})' , x )
217+ match = re .search (r'^(\d{4})-(\d{2})(?: -(\d{2}))? ' , x )
167218 if match :
168219 y = int (match .group (1 )) - 2000
169220 m = int (match .group (2 ))
170221 # 7 bits allocated for the year, 4 bits for the month
171- assert y >= 0 and y < 128
172- assert m > 0 and m <= 12
222+ assert 0 <= y < 128
223+ assert 0 < m <= 12
173224 return (y << 4 ) | m
174225 return 0
175226
227+
176228def parse_cmdline ():
177229 parser = ArgumentParser ()
178- parser .add_argument ('--kernel' , help = 'path to the kernel' , type = FileType ('rb' ),
179- required = True )
230+ parser .add_argument ('--kernel' , help = 'path to the kernel' , type = FileType ('rb' ))
180231 parser .add_argument ('--ramdisk' , help = 'path to the ramdisk' , type = FileType ('rb' ))
181232 parser .add_argument ('--second' , help = 'path to the 2nd bootloader' , type = FileType ('rb' ))
182233 parser .add_argument ('--dtb' , help = 'path to dtb' , type = FileType ('rb' ))
183234 recovery_dtbo_group = parser .add_mutually_exclusive_group ()
184- recovery_dtbo_group .add_argument ('--recovery_dtbo' , help = 'path to the recovery DTBO' , type = FileType ('rb' ))
235+ recovery_dtbo_group .add_argument ('--recovery_dtbo' , help = 'path to the recovery DTBO' ,
236+ type = FileType ('rb' ))
185237 recovery_dtbo_group .add_argument ('--recovery_acpio' , help = 'path to the recovery ACPIO' ,
186- type = FileType ('rb' ), metavar = 'RECOVERY_ACPIO' , dest = 'recovery_dtbo' )
238+ type = FileType ('rb' ), metavar = 'RECOVERY_ACPIO' ,
239+ dest = 'recovery_dtbo' )
187240 parser .add_argument ('--cmdline' , help = 'extra arguments to be passed on the '
188241 'kernel command line' , default = '' , action = ValidateStrLenAction , maxlen = 1536 )
242+ parser .add_argument ('--vendor_cmdline' ,
243+ help = 'kernel command line arguments contained in vendor boot' ,
244+ default = '' , action = ValidateStrLenAction , maxlen = 2048 )
189245 parser .add_argument ('--base' , help = 'base address' , type = parse_int , default = 0x10000000 )
190246 parser .add_argument ('--kernel_offset' , help = 'kernel offset' , type = parse_int , default = 0x00008000 )
191- parser .add_argument ('--ramdisk_offset' , help = 'ramdisk offset' , type = parse_int , default = 0x01000000 )
247+ parser .add_argument ('--ramdisk_offset' , help = 'ramdisk offset' , type = parse_int ,
248+ default = 0x01000000 )
192249 parser .add_argument ('--second_offset' , help = '2nd bootloader offset' , type = parse_int ,
193250 default = 0x00f00000 )
194251 parser .add_argument ('--dtb_offset' , help = 'dtb offset' , type = parse_int , default = 0x01f00000 )
@@ -201,34 +258,59 @@ def parse_cmdline():
201258 parser .add_argument ('--board' , help = 'board name' , default = '' , action = ValidateStrLenAction ,
202259 maxlen = 16 )
203260 parser .add_argument ('--pagesize' , help = 'page size' , type = parse_int ,
204- choices = [2 ** i for i in range (11 ,15 )], default = 2048 )
261+ choices = [2 ** i for i in range (11 , 15 )], default = 2048 )
205262 parser .add_argument ('--id' , help = 'print the image ID on standard output' ,
206263 action = 'store_true' )
207- parser .add_argument ('--header_version' , help = 'boot image header version' , type = parse_int , default = 0 )
208- parser .add_argument ('-o' , '--output' , help = 'output file name' , type = FileType ('wb' ),
209- required = True )
264+ parser .add_argument ('--header_version' , help = 'boot image header version' , type = parse_int ,
265+ default = 0 )
266+ parser .add_argument ('-o' , '--output' , help = 'output file name' , type = FileType ('wb' ))
267+ parser .add_argument ('--vendor_boot' , help = 'vendor boot output file name' , type = FileType ('wb' ))
268+ parser .add_argument ('--vendor_ramdisk' , help = 'path to the vendor ramdisk' , type = FileType ('rb' ))
269+
210270 return parser .parse_args ()
211271
212272
213- def write_data (args ):
214- write_padded_file (args .output , args .kernel , args .pagesize )
215- write_padded_file (args .output , args .ramdisk , args .pagesize )
216- write_padded_file (args .output , args .second , args .pagesize )
273+ def write_data (args , pagesize ):
274+ write_padded_file (args .output , args .kernel , pagesize )
275+ write_padded_file (args .output , args .ramdisk , pagesize )
276+ write_padded_file (args .output , args .second , pagesize )
277+
278+ if args .header_version > 0 and args .header_version < 3 :
279+ write_padded_file (args .output , args .recovery_dtbo , pagesize )
280+ if args .header_version == 2 :
281+ write_padded_file (args .output , args .dtb , pagesize )
282+
283+
284+ def write_vendor_boot_data (args ):
285+ write_padded_file (args .vendor_boot , args .vendor_ramdisk , args .pagesize )
286+ write_padded_file (args .vendor_boot , args .dtb , args .pagesize )
217287
218- if args .header_version > 0 :
219- write_padded_file (args .output , args .recovery_dtbo , args .pagesize )
220- if args .header_version > 1 :
221- write_padded_file (args .output , args .dtb , args .pagesize )
222288
223289def main ():
224290 args = parse_cmdline ()
225- img_id = write_header (args )
226- write_data (args )
227- if args .id :
228- if isinstance (img_id , str ):
291+ if args .vendor_boot is not None :
292+ if args .header_version < 3 :
293+ raise ValueError ('--vendor_boot not compatible with given header version' )
294+ if args .vendor_ramdisk is None :
295+ raise ValueError ('--vendor_ramdisk missing or invalid' )
296+ write_vendor_boot_header (args )
297+ write_vendor_boot_data (args )
298+ if args .output is not None :
299+ if args .kernel is None :
300+ raise ValueError ('kernel must be supplied when creating a boot image' )
301+ if args .second is not None and args .header_version > 2 :
302+ raise ValueError ('--second not compatible with given header version' )
303+ img_id = write_header (args )
304+ if args .header_version > 2 :
305+ write_data (args , BOOT_IMAGE_HEADER_V3_PAGESIZE )
306+ else :
307+ write_data (args , args .pagesize )
308+ if args .id and img_id is not None :
229309 # Python 2's struct.pack returns a string, but py3 returns bytes.
230- img_id = [ord (x ) for x in img_id ]
231- print ('0x' + '' .join ('{:02x}' .format (c ) for c in img_id ))
310+ if isinstance (img_id , str ):
311+ img_id = [ord (x ) for x in img_id ]
312+ print ('0x' + '' .join ('{:02x}' .format (c ) for c in img_id ))
313+
232314
233315if __name__ == '__main__' :
234316 main ()
0 commit comments