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 write_header(args): 49 BOOT_MAGIC = 'ANDROID!'.encode() 50 args.output.write(pack('8s', BOOT_MAGIC)) 51 args.output.write(pack('10I', 52 filesize(args.kernel), # size in bytes 53 args.base + args.kernel_offset, # physical load addr 54 filesize(args.ramdisk), # size in bytes 55 args.base + args.ramdisk_offset, # physical load addr 56 filesize(args.second), # size in bytes 57 args.base + args.second_offset, # physical load addr 58 args.base + args.tags_offset, # physical addr for kernel tags 59 args.pagesize, # flash page size we assume 60 0, # future expansion: MUST be 0 61 (args.os_version << 11) | args.os_patch_level)) # os version and patch level 62 args.output.write(pack('16s', args.board.encode())) # asciiz product name 63 args.output.write(pack('512s', args.cmdline[:512].encode())) 64 65 sha = sha1() 66 update_sha(sha, args.kernel) 67 update_sha(sha, args.ramdisk) 68 update_sha(sha, args.second) 69 img_id = pack('32s', sha.digest()) 70 71 args.output.write(img_id) 72 args.output.write(pack('1024s', args.cmdline[512:].encode())) 73 pad_file(args.output, args.pagesize) 74 return img_id 75 76 77class ValidateStrLenAction(Action): 78 def __init__(self, option_strings, dest, nargs=None, **kwargs): 79 if 'maxlen' not in kwargs: 80 raise ValueError('maxlen must be set') 81 self.maxlen = int(kwargs['maxlen']) 82 del kwargs['maxlen'] 83 super(ValidateStrLenAction, self).__init__(option_strings, dest, **kwargs) 84 85 def __call__(self, parser, namespace, values, option_string=None): 86 if len(values) > self.maxlen: 87 raise ValueError('String argument too long: max {0:d}, got {1:d}'. 88 format(self.maxlen, len(values))) 89 setattr(namespace, self.dest, values) 90 91 92def write_padded_file(f_out, f_in, padding): 93 if f_in is None: 94 return 95 f_out.write(f_in.read()) 96 pad_file(f_out, padding) 97 98 99def parse_int(x): 100 return int(x, 0) 101 102def parse_os_version(x): 103 match = re.search(r'^(\d{1,3})(?:\.(\d{1,3})(?:\.(\d{1,3}))?)?', x) 104 if match: 105 a = int(match.group(1)) 106 b = c = 0 107 if match.lastindex >= 2: 108 b = int(match.group(2)) 109 if match.lastindex == 3: 110 c = int(match.group(3)) 111 # 7 bits allocated for each field 112 assert a < 128 113 assert b < 128 114 assert c < 128 115 return (a << 14) | (b << 7) | c 116 return 0 117 118def parse_os_patch_level(x): 119 match = re.search(r'^(\d{4})-(\d{2})-(\d{2})', x) 120 if match: 121 y = int(match.group(1)) - 2000 122 m = int(match.group(2)) 123 # 7 bits allocated for the year, 4 bits for the month 124 assert y >= 0 and y < 128 125 assert m > 0 and m <= 12 126 return (y << 4) | m 127 return 0 128 129def parse_cmdline(): 130 parser = ArgumentParser() 131 parser.add_argument('--kernel', help='path to the kernel', type=FileType('rb'), 132 required=True) 133 parser.add_argument('--ramdisk', help='path to the ramdisk', type=FileType('rb')) 134 parser.add_argument('--second', help='path to the 2nd bootloader', type=FileType('rb')) 135 parser.add_argument('--cmdline', help='extra arguments to be passed on the ' 136 'kernel command line', default='', action=ValidateStrLenAction, maxlen=1536) 137 parser.add_argument('--base', help='base address', type=parse_int, default=0x10000000) 138 parser.add_argument('--kernel_offset', help='kernel offset', type=parse_int, default=0x00008000) 139 parser.add_argument('--ramdisk_offset', help='ramdisk offset', type=parse_int, default=0x01000000) 140 parser.add_argument('--second_offset', help='2nd bootloader offset', type=parse_int, 141 default=0x00f00000) 142 parser.add_argument('--os_version', help='operating system version', type=parse_os_version, 143 default=0) 144 parser.add_argument('--os_patch_level', help='operating system patch level', 145 type=parse_os_patch_level, default=0) 146 parser.add_argument('--tags_offset', help='tags offset', type=parse_int, default=0x00000100) 147 parser.add_argument('--board', help='board name', default='', action=ValidateStrLenAction, 148 maxlen=16) 149 parser.add_argument('--pagesize', help='page size', type=parse_int, 150 choices=[2**i for i in range(11,15)], default=2048) 151 parser.add_argument('--id', help='print the image ID on standard output', 152 action='store_true') 153 parser.add_argument('-o', '--output', help='output file name', type=FileType('wb'), 154 required=True) 155 return parser.parse_args() 156 157 158def write_data(args): 159 write_padded_file(args.output, args.kernel, args.pagesize) 160 write_padded_file(args.output, args.ramdisk, args.pagesize) 161 write_padded_file(args.output, args.second, args.pagesize) 162 163 164def main(): 165 args = parse_cmdline() 166 img_id = write_header(args) 167 write_data(args) 168 if args.id: 169 if isinstance(img_id, str): 170 # Python 2's struct.pack returns a string, but py3 returns bytes. 171 img_id = [ord(x) for x in img_id] 172 print('0x' + ''.join('{:02x}'.format(c) for c in img_id)) 173 174if __name__ == '__main__': 175 main() 176