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