13d0407baSopenharmony_ci#!/usr/bin/env python 23d0407baSopenharmony_ci# Copyright (C) 2021 HiHope Open Source Organization . 33d0407baSopenharmony_ci# 43d0407baSopenharmony_ci# Licensed under the Apache License, Version 2.0 (the "License"); 53d0407baSopenharmony_ci# you may not use this file except in compliance with the License. 63d0407baSopenharmony_ci# You may obtain a copy of the License at 73d0407baSopenharmony_ci# 83d0407baSopenharmony_ci# http://www.apache.org/licenses/LICENSE-2.0 93d0407baSopenharmony_ci# 103d0407baSopenharmony_ci# Unless required by applicable law or agreed to in writing, software 113d0407baSopenharmony_ci# distributed under the License is distributed on an "AS IS" BASIS, 123d0407baSopenharmony_ci# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 133d0407baSopenharmony_ci# See the License for the specific language governing permissions and 143d0407baSopenharmony_ci# limitations under the License. 153d0407baSopenharmony_ci 163d0407baSopenharmony_cifrom __future__ import print_function 173d0407baSopenharmony_ci 183d0407baSopenharmony_cifrom argparse import ArgumentParser, FileType, Action 193d0407baSopenharmony_cifrom hashlib import sha1 203d0407baSopenharmony_cifrom os import fstat 213d0407baSopenharmony_ciimport re 223d0407baSopenharmony_cifrom struct import pack 233d0407baSopenharmony_ci 243d0407baSopenharmony_ci 253d0407baSopenharmony_ciBOOT_IMAGE_HEADER_V3_PAGESIZE = 4096 263d0407baSopenharmony_ci 273d0407baSopenharmony_cidef filesize(f): 283d0407baSopenharmony_ci if f is None: 293d0407baSopenharmony_ci return 0 303d0407baSopenharmony_ci try: 313d0407baSopenharmony_ci return fstat(f.fileno()).st_size 323d0407baSopenharmony_ci except OSError: 333d0407baSopenharmony_ci return 0 343d0407baSopenharmony_ci 353d0407baSopenharmony_ci 363d0407baSopenharmony_cidef update_sha(sha, f): 373d0407baSopenharmony_ci if f: 383d0407baSopenharmony_ci sha.update(f.read()) 393d0407baSopenharmony_ci f.seek(0) 403d0407baSopenharmony_ci sha.update(pack('I', filesize(f))) 413d0407baSopenharmony_ci else: 423d0407baSopenharmony_ci sha.update(pack('I', 0)) 433d0407baSopenharmony_ci 443d0407baSopenharmony_ci 453d0407baSopenharmony_cidef pad_file(f, padding): 463d0407baSopenharmony_ci pad = (padding - (f.tell() & (padding - 1))) & (padding - 1) 473d0407baSopenharmony_ci f.write(pack(str(pad) + 'x')) 483d0407baSopenharmony_ci 493d0407baSopenharmony_ci 503d0407baSopenharmony_cidef get_number_of_pages(image_size, page_size): 513d0407baSopenharmony_ci """calculates the number of pages required for the image""" 523d0407baSopenharmony_ci return (image_size + page_size - 1) / page_size 533d0407baSopenharmony_ci 543d0407baSopenharmony_ci 553d0407baSopenharmony_cidef get_recovery_dtbo_offset(args): 563d0407baSopenharmony_ci """calculates the offset of recovery_dtbo image in the boot image""" 573d0407baSopenharmony_ci num_header_pages = 1 # header occupies a page 583d0407baSopenharmony_ci num_kernel_pages = get_number_of_pages(filesize(args.kernel), args.pagesize) 593d0407baSopenharmony_ci num_ramdisk_pages = get_number_of_pages(filesize(args.ramdisk), args.pagesize) 603d0407baSopenharmony_ci num_second_pages = get_number_of_pages(filesize(args.second), args.pagesize) 613d0407baSopenharmony_ci dtbo_offset = args.pagesize * (num_header_pages + num_kernel_pages + 623d0407baSopenharmony_ci num_ramdisk_pages + num_second_pages) 633d0407baSopenharmony_ci return dtbo_offset 643d0407baSopenharmony_ci 653d0407baSopenharmony_ci 663d0407baSopenharmony_cidef write_header_v3(args): 673d0407baSopenharmony_ci BOOT_IMAGE_HEADER_V3_SIZE = 1580 683d0407baSopenharmony_ci BOOT_MAGIC = 'ANDROID!'.encode() 693d0407baSopenharmony_ci 703d0407baSopenharmony_ci args.output.write(pack('8s', BOOT_MAGIC)) 713d0407baSopenharmony_ci args.output.write(pack( 723d0407baSopenharmony_ci '4I', 733d0407baSopenharmony_ci filesize(args.kernel), # kernel size in bytes 743d0407baSopenharmony_ci filesize(args.ramdisk), # ramdisk size in bytes 753d0407baSopenharmony_ci (args.os_version << 11) | args.os_patch_level, # os version and patch level 763d0407baSopenharmony_ci BOOT_IMAGE_HEADER_V3_SIZE)) 773d0407baSopenharmony_ci 783d0407baSopenharmony_ci args.output.write(pack('4I', 0, 0, 0, 0)) # reserved 793d0407baSopenharmony_ci 803d0407baSopenharmony_ci args.output.write(pack('I', args.header_version)) # version of bootimage header 813d0407baSopenharmony_ci args.output.write(pack('1536s', args.cmdline.encode())) 823d0407baSopenharmony_ci pad_file(args.output, BOOT_IMAGE_HEADER_V3_PAGESIZE) 833d0407baSopenharmony_ci 843d0407baSopenharmony_cidef write_vendor_boot_header(args): 853d0407baSopenharmony_ci VENDOR_BOOT_IMAGE_HEADER_V3_SIZE = 2112 863d0407baSopenharmony_ci BOOT_MAGIC = 'VNDRBOOT'.encode() 873d0407baSopenharmony_ci 883d0407baSopenharmony_ci args.vendor_boot.write(pack('8s', BOOT_MAGIC)) 893d0407baSopenharmony_ci args.vendor_boot.write(pack( 903d0407baSopenharmony_ci '5I', 913d0407baSopenharmony_ci args.header_version, # version of header 923d0407baSopenharmony_ci args.pagesize, # flash page size we assume 933d0407baSopenharmony_ci args.base + args.kernel_offset, # kernel physical load addr 943d0407baSopenharmony_ci args.base + args.ramdisk_offset, # ramdisk physical load addr 953d0407baSopenharmony_ci filesize(args.vendor_ramdisk))) # vendor ramdisk size in bytes 963d0407baSopenharmony_ci args.vendor_boot.write(pack('2048s', args.vendor_cmdline.encode())) 973d0407baSopenharmony_ci args.vendor_boot.write(pack('I', args.base + args.tags_offset)) # physical addr for kernel tags 983d0407baSopenharmony_ci args.vendor_boot.write(pack('16s', args.board.encode())) # asciiz product name 993d0407baSopenharmony_ci args.vendor_boot.write(pack('I', VENDOR_BOOT_IMAGE_HEADER_V3_SIZE)) # header size in bytes 1003d0407baSopenharmony_ci if filesize(args.dtb) == 0: 1013d0407baSopenharmony_ci raise ValueError("DTB image must not be empty.") 1023d0407baSopenharmony_ci args.vendor_boot.write(pack('I', filesize(args.dtb))) # size in bytes 1033d0407baSopenharmony_ci args.vendor_boot.write(pack('Q', args.base + args.dtb_offset)) # dtb physical load address 1043d0407baSopenharmony_ci pad_file(args.vendor_boot, args.pagesize) 1053d0407baSopenharmony_ci 1063d0407baSopenharmony_cidef write_header(args): 1073d0407baSopenharmony_ci BOOT_IMAGE_HEADER_V1_SIZE = 1648 1083d0407baSopenharmony_ci BOOT_IMAGE_HEADER_V2_SIZE = 1660 1093d0407baSopenharmony_ci BOOT_MAGIC = 'ANDROID!'.encode() 1103d0407baSopenharmony_ci 1113d0407baSopenharmony_ci if args.header_version > 3: 1123d0407baSopenharmony_ci raise ValueError('Boot header version %d not supported' % args.header_version) 1133d0407baSopenharmony_ci elif args.header_version == 3: 1143d0407baSopenharmony_ci return write_header_v3(args) 1153d0407baSopenharmony_ci 1163d0407baSopenharmony_ci args.output.write(pack('8s', BOOT_MAGIC)) 1173d0407baSopenharmony_ci final_ramdisk_offset = (args.base + args.ramdisk_offset) if filesize(args.ramdisk) > 0 else 0 1183d0407baSopenharmony_ci final_second_offset = (args.base + args.second_offset) if filesize(args.second) > 0 else 0 1193d0407baSopenharmony_ci args.output.write(pack( 1203d0407baSopenharmony_ci '10I', 1213d0407baSopenharmony_ci filesize(args.kernel), # size in bytes 1223d0407baSopenharmony_ci args.base + args.kernel_offset, # physical load addr 1233d0407baSopenharmony_ci filesize(args.ramdisk), # size in bytes 1243d0407baSopenharmony_ci final_ramdisk_offset, # physical load addr 1253d0407baSopenharmony_ci filesize(args.second), # size in bytes 1263d0407baSopenharmony_ci final_second_offset, # physical load addr 1273d0407baSopenharmony_ci args.base + args.tags_offset, # physical addr for kernel tags 1283d0407baSopenharmony_ci args.pagesize, # flash page size we assume 1293d0407baSopenharmony_ci args.header_version, # version of bootimage header 1303d0407baSopenharmony_ci (args.os_version << 11) | args.os_patch_level)) # os version and patch level 1313d0407baSopenharmony_ci args.output.write(pack('16s', args.board.encode())) # asciiz product name 1323d0407baSopenharmony_ci args.output.write(pack('512s', args.cmdline[:512].encode())) 1333d0407baSopenharmony_ci 1343d0407baSopenharmony_ci sha = sha1() 1353d0407baSopenharmony_ci update_sha(sha, args.kernel) 1363d0407baSopenharmony_ci update_sha(sha, args.ramdisk) 1373d0407baSopenharmony_ci update_sha(sha, args.second) 1383d0407baSopenharmony_ci 1393d0407baSopenharmony_ci if args.header_version > 0: 1403d0407baSopenharmony_ci update_sha(sha, args.recovery_dtbo) 1413d0407baSopenharmony_ci if args.header_version > 1: 1423d0407baSopenharmony_ci update_sha(sha, args.dtb) 1433d0407baSopenharmony_ci 1443d0407baSopenharmony_ci img_id = pack('32s', sha.digest()) 1453d0407baSopenharmony_ci 1463d0407baSopenharmony_ci args.output.write(img_id) 1473d0407baSopenharmony_ci args.output.write(pack('1024s', args.cmdline[512:].encode())) 1483d0407baSopenharmony_ci 1493d0407baSopenharmony_ci if args.header_version > 0: 1503d0407baSopenharmony_ci args.output.write(pack('I', filesize(args.recovery_dtbo))) # size in bytes 1513d0407baSopenharmony_ci if args.recovery_dtbo: 1523d0407baSopenharmony_ci args.output.write(pack('Q', get_recovery_dtbo_offset(args))) # recovery dtbo offset 1533d0407baSopenharmony_ci else: 1543d0407baSopenharmony_ci args.output.write(pack('Q', 0)) # Will be set to 0 for devices without a recovery dtbo 1553d0407baSopenharmony_ci 1563d0407baSopenharmony_ci # Populate boot image header size for header versions 1 and 2. 1573d0407baSopenharmony_ci if args.header_version == 1: 1583d0407baSopenharmony_ci args.output.write(pack('I', BOOT_IMAGE_HEADER_V1_SIZE)) 1593d0407baSopenharmony_ci elif args.header_version == 2: 1603d0407baSopenharmony_ci args.output.write(pack('I', BOOT_IMAGE_HEADER_V2_SIZE)) 1613d0407baSopenharmony_ci 1623d0407baSopenharmony_ci if args.header_version > 1: 1633d0407baSopenharmony_ci 1643d0407baSopenharmony_ci # if filesize(args.dtb) == 0: 1653d0407baSopenharmony_ci # raise ValueError("DTB image must not be empty.") 1663d0407baSopenharmony_ci 1673d0407baSopenharmony_ci args.output.write(pack('I', filesize(args.dtb))) # size in bytes 1683d0407baSopenharmony_ci args.output.write(pack('Q', args.base + args.dtb_offset)) # dtb physical load address 1693d0407baSopenharmony_ci pad_file(args.output, args.pagesize) 1703d0407baSopenharmony_ci return img_id 1713d0407baSopenharmony_ci 1723d0407baSopenharmony_ci 1733d0407baSopenharmony_ciclass ValidateStrLenAction(Action): 1743d0407baSopenharmony_ci def __init__(self, option_strings, dest, nargs=None, **kwargs): 1753d0407baSopenharmony_ci if 'maxlen' not in kwargs: 1763d0407baSopenharmony_ci raise ValueError('maxlen must be set') 1773d0407baSopenharmony_ci self.maxlen = int(kwargs['maxlen']) 1783d0407baSopenharmony_ci del kwargs['maxlen'] 1793d0407baSopenharmony_ci super(ValidateStrLenAction, self).__init__(option_strings, dest, **kwargs) 1803d0407baSopenharmony_ci 1813d0407baSopenharmony_ci def __call__(self, parser, namespace, values, option_string=None): 1823d0407baSopenharmony_ci if len(values) > self.maxlen: 1833d0407baSopenharmony_ci raise ValueError( 1843d0407baSopenharmony_ci 'String argument too long: max {0:d}, got {1:d}'.format(self.maxlen, len(values))) 1853d0407baSopenharmony_ci setattr(namespace, self.dest, values) 1863d0407baSopenharmony_ci 1873d0407baSopenharmony_ci 1883d0407baSopenharmony_cidef write_padded_file(f_out, f_in, padding): 1893d0407baSopenharmony_ci if f_in is None: 1903d0407baSopenharmony_ci return 1913d0407baSopenharmony_ci f_out.write(f_in.read()) 1923d0407baSopenharmony_ci pad_file(f_out, padding) 1933d0407baSopenharmony_ci 1943d0407baSopenharmony_ci 1953d0407baSopenharmony_cidef parse_int(x): 1963d0407baSopenharmony_ci return int(x, 0) 1973d0407baSopenharmony_ci 1983d0407baSopenharmony_ci 1993d0407baSopenharmony_cidef parse_os_version(x): 2003d0407baSopenharmony_ci match = re.search(r'^(\d{1,3})(?:\.(\d{1,3})(?:\.(\d{1,3}))?)?', x) 2013d0407baSopenharmony_ci if match: 2023d0407baSopenharmony_ci a = int(match.group(1)) 2033d0407baSopenharmony_ci b = c = 0 2043d0407baSopenharmony_ci if match.lastindex >= 2: 2053d0407baSopenharmony_ci b = int(match.group(2)) 2063d0407baSopenharmony_ci if match.lastindex == 3: 2073d0407baSopenharmony_ci c = int(match.group(3)) 2083d0407baSopenharmony_ci # 7 bits allocated for each field 2093d0407baSopenharmony_ci assert a < 128 2103d0407baSopenharmony_ci assert b < 128 2113d0407baSopenharmony_ci assert c < 128 2123d0407baSopenharmony_ci return (a << 14) | (b << 7) | c 2133d0407baSopenharmony_ci return 0 2143d0407baSopenharmony_ci 2153d0407baSopenharmony_ci 2163d0407baSopenharmony_cidef parse_os_patch_level(x): 2173d0407baSopenharmony_ci match = re.search(r'^(\d{4})-(\d{2})(?:-(\d{2}))?', x) 2183d0407baSopenharmony_ci if match: 2193d0407baSopenharmony_ci y = int(match.group(1)) - 2000 2203d0407baSopenharmony_ci m = int(match.group(2)) 2213d0407baSopenharmony_ci # 7 bits allocated for the year, 4 bits for the month 2223d0407baSopenharmony_ci assert 0 <= y < 128 2233d0407baSopenharmony_ci assert 0 < m <= 12 2243d0407baSopenharmony_ci return (y << 4) | m 2253d0407baSopenharmony_ci return 0 2263d0407baSopenharmony_ci 2273d0407baSopenharmony_ci 2283d0407baSopenharmony_cidef parse_cmdline(): 2293d0407baSopenharmony_ci parser = ArgumentParser() 2303d0407baSopenharmony_ci parser.add_argument('--kernel', help='path to the kernel', type=FileType('rb')) 2313d0407baSopenharmony_ci parser.add_argument('--ramdisk', help='path to the ramdisk', type=FileType('rb')) 2323d0407baSopenharmony_ci parser.add_argument('--second', help='path to the 2nd bootloader', type=FileType('rb')) 2333d0407baSopenharmony_ci parser.add_argument('--dtb', help='path to dtb', type=FileType('rb')) 2343d0407baSopenharmony_ci recovery_dtbo_group = parser.add_mutually_exclusive_group() 2353d0407baSopenharmony_ci recovery_dtbo_group.add_argument('--recovery_dtbo', help='path to the recovery DTBO', 2363d0407baSopenharmony_ci type=FileType('rb')) 2373d0407baSopenharmony_ci recovery_dtbo_group.add_argument('--recovery_acpio', help='path to the recovery ACPIO', 2383d0407baSopenharmony_ci type=FileType('rb'), metavar='RECOVERY_ACPIO', 2393d0407baSopenharmony_ci dest='recovery_dtbo') 2403d0407baSopenharmony_ci parser.add_argument('--cmdline', help='extra arguments to be passed on the ' 2413d0407baSopenharmony_ci 'kernel command line', default='', action=ValidateStrLenAction, maxlen=1536) 2423d0407baSopenharmony_ci parser.add_argument('--vendor_cmdline', 2433d0407baSopenharmony_ci help='kernel command line arguments contained in vendor boot', 2443d0407baSopenharmony_ci default='', action=ValidateStrLenAction, maxlen=2048) 2453d0407baSopenharmony_ci parser.add_argument('--base', help='base address', type=parse_int, default=0x10000000) 2463d0407baSopenharmony_ci parser.add_argument('--kernel_offset', help='kernel offset', type=parse_int, default=0x00008000) 2473d0407baSopenharmony_ci parser.add_argument('--ramdisk_offset', help='ramdisk offset', type=parse_int, 2483d0407baSopenharmony_ci default=0x01000000) 2493d0407baSopenharmony_ci parser.add_argument('--second_offset', help='2nd bootloader offset', type=parse_int, 2503d0407baSopenharmony_ci default=0x00f00000) 2513d0407baSopenharmony_ci parser.add_argument('--dtb_offset', help='dtb offset', type=parse_int, default=0x01f00000) 2523d0407baSopenharmony_ci 2533d0407baSopenharmony_ci parser.add_argument('--os_version', help='operating system version', type=parse_os_version, 2543d0407baSopenharmony_ci default=0) 2553d0407baSopenharmony_ci parser.add_argument('--os_patch_level', help='operating system patch level', 2563d0407baSopenharmony_ci type=parse_os_patch_level, default=0) 2573d0407baSopenharmony_ci parser.add_argument('--tags_offset', help='tags offset', type=parse_int, default=0x00000100) 2583d0407baSopenharmony_ci parser.add_argument('--board', help='board name', default='', action=ValidateStrLenAction, 2593d0407baSopenharmony_ci maxlen=16) 2603d0407baSopenharmony_ci parser.add_argument('--pagesize', help='page size', type=parse_int, 2613d0407baSopenharmony_ci choices=[2**i for i in range(11, 15)], default=2048) 2623d0407baSopenharmony_ci parser.add_argument('--id', help='print the image ID on standard output', 2633d0407baSopenharmony_ci action='store_true') 2643d0407baSopenharmony_ci parser.add_argument('--header_version', help='boot image header version', type=parse_int, 2653d0407baSopenharmony_ci default=0) 2663d0407baSopenharmony_ci parser.add_argument('-o', '--output', help='output file name', type=FileType('wb')) 2673d0407baSopenharmony_ci parser.add_argument('--vendor_boot', help='vendor boot output file name', type=FileType('wb')) 2683d0407baSopenharmony_ci parser.add_argument('--vendor_ramdisk', help='path to the vendor ramdisk', type=FileType('rb')) 2693d0407baSopenharmony_ci 2703d0407baSopenharmony_ci return parser.parse_args() 2713d0407baSopenharmony_ci 2723d0407baSopenharmony_ci 2733d0407baSopenharmony_cidef write_data(args, pagesize): 2743d0407baSopenharmony_ci write_padded_file(args.output, args.kernel, pagesize) 2753d0407baSopenharmony_ci write_padded_file(args.output, args.ramdisk, pagesize) 2763d0407baSopenharmony_ci write_padded_file(args.output, args.second, pagesize) 2773d0407baSopenharmony_ci 2783d0407baSopenharmony_ci if args.header_version > 0 and args.header_version < 3: 2793d0407baSopenharmony_ci write_padded_file(args.output, args.recovery_dtbo, pagesize) 2803d0407baSopenharmony_ci if args.header_version == 2: 2813d0407baSopenharmony_ci write_padded_file(args.output, args.dtb, pagesize) 2823d0407baSopenharmony_ci 2833d0407baSopenharmony_ci 2843d0407baSopenharmony_cidef write_vendor_boot_data(args): 2853d0407baSopenharmony_ci write_padded_file(args.vendor_boot, args.vendor_ramdisk, args.pagesize) 2863d0407baSopenharmony_ci write_padded_file(args.vendor_boot, args.dtb, args.pagesize) 2873d0407baSopenharmony_ci 2883d0407baSopenharmony_ci 2893d0407baSopenharmony_cidef main(): 2903d0407baSopenharmony_ci args = parse_cmdline() 2913d0407baSopenharmony_ci if args.vendor_boot is not None: 2923d0407baSopenharmony_ci if args.header_version < 3: 2933d0407baSopenharmony_ci raise ValueError('--vendor_boot not compatible with given header version') 2943d0407baSopenharmony_ci if args.vendor_ramdisk is None: 2953d0407baSopenharmony_ci raise ValueError('--vendor_ramdisk missing or invalid') 2963d0407baSopenharmony_ci write_vendor_boot_header(args) 2973d0407baSopenharmony_ci write_vendor_boot_data(args) 2983d0407baSopenharmony_ci if args.output is not None: 2993d0407baSopenharmony_ci if args.kernel is None: 3003d0407baSopenharmony_ci raise ValueError('kernel must be supplied when creating a boot image') 3013d0407baSopenharmony_ci if args.second is not None and args.header_version > 2: 3023d0407baSopenharmony_ci raise ValueError('--second not compatible with given header version') 3033d0407baSopenharmony_ci img_id = write_header(args) 3043d0407baSopenharmony_ci if args.header_version > 2: 3053d0407baSopenharmony_ci write_data(args, BOOT_IMAGE_HEADER_V3_PAGESIZE) 3063d0407baSopenharmony_ci else: 3073d0407baSopenharmony_ci write_data(args, args.pagesize) 3083d0407baSopenharmony_ci if args.id and img_id is not None: 3093d0407baSopenharmony_ci # Python 2's struct.pack returns a string, but py3 returns bytes. 3103d0407baSopenharmony_ci if isinstance(img_id, str): 3113d0407baSopenharmony_ci img_id = [ord(x) for x in img_id] 3123d0407baSopenharmony_ci print('0x' + ''.join('{:02x}'.format(c) for c in img_id)) 3133d0407baSopenharmony_ci 3143d0407baSopenharmony_ci 3153d0407baSopenharmony_ciif __name__ == '__main__': 3163d0407baSopenharmony_ci main() 317