3 # Copyright 2015, The Android Open Source Project
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
9 # http://www.apache.org/licenses/LICENSE-2.0
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.
17 from __future__
import print_function
18 from sys
import argv
, exit
, stderr
19 from argparse
import ArgumentParser
, FileType
, Action
21 from struct
import pack
22 from hashlib
import sha1
30 return fstat(f
.fileno()).st_size
34 def update_sha(sha
, f
):
38 sha
.update(pack('I', filesize(f
)))
40 sha
.update(pack('I', 0))
42 def pad_file(f
, padding
):
43 pad
= (padding
- (f
.tell() & (padding
- 1))) & (padding
- 1)
44 f
.write(pack(str(pad
) + 'x'))
46 def write_header(args
):
47 BOOT_MAGIC
= 'ANDROID!'.encode()
48 dt_addr
= args
.tags_addr
51 dt_addr
= args
.base
+ args
.tags_offset
53 args
.output
.write(pack('8s', BOOT_MAGIC
))
54 args
.output
.write(pack('10I',
55 filesize(args
.kernel
), # size in bytes
56 args
.base
+ args
.kernel_offset
, # physical load addr
57 filesize(args
.ramdisk
), # size in bytes
58 args
.base
+ args
.ramdisk_offset
, # physical load addr
59 filesize(args
.second
), # size in bytes
60 args
.base
+ args
.second_offset
, # physical load addr
61 dt_addr
, # physical addr for kernel tags
62 args
.pagesize
, # flash page size we assume
63 0, # future expansion: MUST be 0
64 (args
.os_version
<< 11) | args
.os_patch_level
)) # os version and patch level
65 args
.output
.write(pack('16s', args
.board
.encode())) # asciiz product name
66 args
.output
.write(pack('512s', args
.cmdline
[:512].encode()))
69 update_sha(sha
, args
.kernel
)
70 update_sha(sha
, args
.ramdisk
)
71 update_sha(sha
, args
.second
)
72 img_id
= pack('32s', sha
.digest())
74 args
.output
.write(img_id
)
75 args
.output
.write(pack('1024s', args
.cmdline
[512:].encode()))
76 pad_file(args
.output
, args
.pagesize
)
80 class ValidateStrLenAction(Action
):
81 def __init__(self
, option_strings
, dest
, nargs
=None, **kwargs
):
82 if 'maxlen' not in kwargs
:
83 raise ValueError('maxlen must be set')
85 self
.maxlen
= int(kwargs
['maxlen'])
87 super(ValidateStrLenAction
, self
).__init
__(option_strings
, dest
, **kwargs
)
89 def __call__(self
, parser
, namespace
, values
, option_string
=None):
90 if len(values
) > self
.maxlen
:
91 raise ValueError('String argument too long: max {0:d}, got {1:d}'.
92 format(self
.maxlen
, len(values
)))
93 setattr(namespace
, self
.dest
, values
)
95 def write_padded_file(f_out
, f_in
, padding
):
99 f_out
.write(f_in
.read())
100 pad_file(f_out
, padding
)
105 def parse_os_version(x
):
106 match
= re
.search(r
'^(\d{1,3})(?:\.(\d{1,3})(?:\.(\d{1,3}))?)?', x
)
108 a
= int(match
.group(1))
112 if match
.lastindex
>= 2:
113 b
= int(match
.group(2))
115 if match
.lastindex
== 3:
116 c
= int(match
.group(3))
118 # 7 bits allocated for each field
122 return (a
<< 14) |
(b
<< 7) | c
126 def parse_os_patch_level(x
):
127 match
= re
.search(r
'^(\d{4})-(\d{2})-(\d{2})', x
)
129 y
= int(match
.group(1)) - 2000
130 m
= int(match
.group(2))
132 # 7 bits allocated for the year, 4 bits for the month
133 assert y
>= 0 and y
< 128
134 assert m
> 0 and m
<= 12
140 parser
= ArgumentParser()
141 parser
.add_argument('--kernel', help='path to the kernel', type=FileType('rb'),
143 parser
.add_argument('--ramdisk', help='path to the ramdisk', type=FileType('rb'))
144 parser
.add_argument('--second', help='path to the 2nd bootloader', type=FileType('rb'))
145 parser
.add_argument('--cmdline', help='extra arguments to be passed on the '
146 'kernel command line', default
='', action
=ValidateStrLenAction
, maxlen
=1536)
147 parser
.add_argument('--base', help='base address', type=parse_int
, default
=0x10000000)
148 parser
.add_argument('--tags-addr', help='tags addr', type=parse_int
, default
=0x0)
149 parser
.add_argument('--kernel_offset', help='kernel offset', type=parse_int
, default
=0x00008000)
150 parser
.add_argument('--ramdisk_offset', help='ramdisk offset', type=parse_int
, default
=0x01000000)
151 parser
.add_argument('--second_offset', help='2nd bootloader offset', type=parse_int
,
153 parser
.add_argument('--os_version', help='operating system version', type=parse_os_version
,
155 parser
.add_argument('--os_patch_level', help='operating system patch level',
156 type=parse_os_patch_level
, default
=0)
157 parser
.add_argument('--tags_offset', help='tags offset', type=parse_int
, default
=0x00000100)
158 parser
.add_argument('--board', help='board name', default
='', action
=ValidateStrLenAction
,
160 parser
.add_argument('--pagesize', help='page size', type=parse_int
,
161 choices
=[2**i
for i
in range(11,15)], default
=2048)
162 parser
.add_argument('--id', help='print the image ID on standard output',
164 parser
.add_argument('-o', '--output', help='output file name', type=FileType('wb'),
166 return parser
.parse_args()
168 def write_data(args
):
169 write_padded_file(args
.output
, args
.kernel
, args
.pagesize
)
170 write_padded_file(args
.output
, args
.ramdisk
, args
.pagesize
)
171 write_padded_file(args
.output
, args
.second
, args
.pagesize
)
174 args
= parse_cmdline()
175 img_id
= write_header(args
)
178 if isinstance(img_id
, str):
179 # Python 2's struct.pack returns a string, but py3 returns bytes.
180 img_id
= [ord(x
) for x
in img_id
]
182 print('0x' + ''.join('{:02x}'.format(c
) for c
in img_id
))
184 if __name__
== '__main__':