xref: /OK3568_Linux_fs/u-boot/scripts/mkbootimg (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1#!/usr/bin/env python
2# Copyright 2015, The Android Open Source Project
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8#     http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15
16from __future__ import print_function
17from sys import argv, exit, stderr
18from argparse import ArgumentParser, FileType, Action
19from os import fstat
20from struct import pack
21from hashlib import sha1
22import sys
23import re
24
25def filesize(f):
26    if f is None:
27        return 0
28    try:
29        return fstat(f.fileno()).st_size
30    except OSError:
31        return 0
32
33
34def update_sha(sha, f):
35    if f:
36        sha.update(f.read())
37        f.seek(0)
38        sha.update(pack('I', filesize(f)))
39    else:
40        sha.update(pack('I', 0))
41
42
43def pad_file(f, padding):
44    pad = (padding - (f.tell() & (padding - 1))) & (padding - 1)
45    f.write(pack(str(pad) + 'x'))
46
47
48def get_number_of_pages(image_size, page_size):
49    """calculates the number of pages required for the image"""
50    return (image_size + page_size - 1) / page_size
51
52
53def get_recovery_dtbo_offset(args):
54    """calculates the offset of recovery_dtbo image in the boot image"""
55    num_header_pages = 1 # header occupies a page
56    num_kernel_pages = get_number_of_pages(filesize(args.kernel), args.pagesize)
57    num_ramdisk_pages = get_number_of_pages(filesize(args.ramdisk), args.pagesize)
58    num_second_pages = get_number_of_pages(filesize(args.second), args.pagesize)
59    dtbo_offset = args.pagesize * (num_header_pages + num_kernel_pages +
60                                   num_ramdisk_pages + num_second_pages)
61    return dtbo_offset
62
63
64def write_header(args):
65    BOOT_IMAGE_HEADER_V1_SIZE = 1648
66    BOOT_IMAGE_HEADER_V2_SIZE = 1660
67    BOOT_MAGIC = 'ANDROID!'.encode()
68
69    if (args.header_version > 2):
70        raise ValueError('Boot header version %d not supported' % args.header_version)
71
72    args.output.write(pack('8s', BOOT_MAGIC))
73    final_ramdisk_offset = (args.base + args.ramdisk_offset) if filesize(args.ramdisk) > 0 else 0
74    final_second_offset = (args.base + args.second_offset) if filesize(args.second) > 0 else 0
75    args.output.write(pack('10I',
76        filesize(args.kernel),                          # size in bytes
77        args.base + args.kernel_offset,                 # physical load addr
78        filesize(args.ramdisk),                         # size in bytes
79        final_ramdisk_offset,                           # physical load addr
80        filesize(args.second),                          # size in bytes
81        final_second_offset,                            # physical load addr
82        args.base + args.tags_offset,                   # physical addr for kernel tags
83        args.pagesize,                                  # flash page size we assume
84        args.header_version,                            # version of bootimage header
85        (args.os_version << 11) | args.os_patch_level)) # os version and patch level
86    args.output.write(pack('16s', args.board.encode())) # asciiz product name
87    args.output.write(pack('512s', args.cmdline[:512].encode()))
88
89    sha = sha1()
90    update_sha(sha, args.kernel)
91    update_sha(sha, args.ramdisk)
92    update_sha(sha, args.second)
93
94    if args.header_version > 0:
95        update_sha(sha, args.recovery_dtbo)
96    if args.header_version > 1:
97        update_sha(sha, args.dtb)
98
99    img_id = pack('32s', sha.digest())
100
101    args.output.write(img_id)
102    args.output.write(pack('1024s', args.cmdline[512:].encode()))
103
104    if args.header_version > 0:
105        args.output.write(pack('I', filesize(args.recovery_dtbo)))   # size in bytes
106        if args.recovery_dtbo:
107            args.output.write(pack('Q', get_recovery_dtbo_offset(args))) # recovery dtbo offset
108        else:
109            args.output.write(pack('Q', 0)) # Will be set to 0 for devices without a recovery dtbo
110
111    # Populate boot image header size for header versions 1 and 2.
112    if args.header_version == 1:
113        args.output.write(pack('I', BOOT_IMAGE_HEADER_V1_SIZE))
114    elif args.header_version == 2:
115        args.output.write(pack('I', BOOT_IMAGE_HEADER_V2_SIZE))
116
117    if args.header_version > 1:
118        args.output.write(pack('I', filesize(args.dtb)))   # size in bytes
119        args.output.write(pack('Q', args.base + args.dtb_offset)) # dtb physical load address
120    pad_file(args.output, args.pagesize)
121    return img_id
122
123
124class ValidateStrLenAction(Action):
125    def __init__(self, option_strings, dest, nargs=None, **kwargs):
126        if 'maxlen' not in kwargs:
127            raise ValueError('maxlen must be set')
128        self.maxlen = int(kwargs['maxlen'])
129        del kwargs['maxlen']
130        super(ValidateStrLenAction, self).__init__(option_strings, dest, **kwargs)
131
132    def __call__(self, parser, namespace, values, option_string=None):
133        if len(values) > self.maxlen:
134            raise ValueError('String argument too long: max {0:d}, got {1:d}'.
135                format(self.maxlen, len(values)))
136        setattr(namespace, self.dest, values)
137
138
139def write_padded_file(f_out, f_in, padding):
140    if f_in is None:
141        return
142    f_out.write(f_in.read())
143    pad_file(f_out, padding)
144
145
146def parse_int(x):
147    return int(x, 0)
148
149def parse_os_version(x):
150    match = re.search(r'^(\d{1,3})(?:\.(\d{1,3})(?:\.(\d{1,3}))?)?', x)
151    if match:
152        a = int(match.group(1))
153        b = c = 0
154        if match.lastindex >= 2:
155            b = int(match.group(2))
156        if match.lastindex == 3:
157            c = int(match.group(3))
158        # 7 bits allocated for each field
159        assert a < 128
160        assert b < 128
161        assert c < 128
162        return (a << 14) | (b << 7) | c
163    return 0
164
165def parse_os_patch_level(x):
166    match = re.search(r'^(\d{4})-(\d{2})-(\d{2})', x)
167    if match:
168        y = int(match.group(1)) - 2000
169        m = int(match.group(2))
170        # 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
173        return (y << 4) | m
174    return 0
175
176def parse_cmdline():
177    parser = ArgumentParser()
178    parser.add_argument('--kernel', help='path to the kernel', type=FileType('rb'),
179                        required=True)
180    parser.add_argument('--ramdisk', help='path to the ramdisk', type=FileType('rb'))
181    parser.add_argument('--second', help='path to the 2nd bootloader', type=FileType('rb'))
182    parser.add_argument('--dtb', help='path to dtb', type=FileType('rb'))
183    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'))
185    recovery_dtbo_group.add_argument('--recovery_acpio', help='path to the recovery ACPIO',
186                                     type=FileType('rb'), metavar='RECOVERY_ACPIO', dest='recovery_dtbo')
187    parser.add_argument('--cmdline', help='extra arguments to be passed on the '
188                        'kernel command line', default='', action=ValidateStrLenAction, maxlen=1536)
189    parser.add_argument('--base', help='base address', type=parse_int, default=0x10000000)
190    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)
192    parser.add_argument('--second_offset', help='2nd bootloader offset', type=parse_int,
193                        default=0x00f00000)
194    parser.add_argument('--dtb_offset', help='dtb offset', type=parse_int, default=0x01f00000)
195
196    parser.add_argument('--os_version', help='operating system version', type=parse_os_version,
197                        default=0)
198    parser.add_argument('--os_patch_level', help='operating system patch level',
199                        type=parse_os_patch_level, default=0)
200    parser.add_argument('--tags_offset', help='tags offset', type=parse_int, default=0x00000100)
201    parser.add_argument('--board', help='board name', default='', action=ValidateStrLenAction,
202                        maxlen=16)
203    parser.add_argument('--pagesize', help='page size', type=parse_int,
204                        choices=[2**i for i in range(11,15)], default=2048)
205    parser.add_argument('--id', help='print the image ID on standard output',
206                        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)
210    return parser.parse_args()
211
212
213def 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)
217
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)
222
223def main():
224    args = parse_cmdline()
225    img_id = write_header(args)
226    write_data(args)
227    if args.id:
228        if isinstance(img_id, str):
229            # 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))
232
233if __name__ == '__main__':
234    main()
235