#!/usr/bin/python3 # -*- coding: utf-8 -*- import logging import argparse import sys from os import path, stat from array import array from XmlParser import XmlParser FORMAT = "%(levelname)s: %(message)s" logging.basicConfig(level=logging.INFO, format=FORMAT) LBA_SIZE = 512 MAX_WRITE_SIZE = 50 * 1024 * 1024 DEFAULT_BLOCK_SIZE = 128 * 1024 FIP_BACKUP_BLOCK_POS = 10 SV_BLOCK_NUM = 4 BLOCK_SIZE_FOR_4K_NAND = 262144 global chip_list chip_list = ["cv183x", "cv182x", "cv181x"] def parse_Args(): parser = argparse.ArgumentParser(description="Create CVITEK device image for burning") parser.add_argument("chip", help="the current chip for using") parser.add_argument("xml", help="path to partition xml") parser.add_argument("images_path", help="path to images") parser.add_argument( "output", metavar="output", type=str, help="the output folder for saving the data.bin and boot.bin", ) parser.add_argument( "-m", "--max_write_size", type=int, help="max write buffer size when generating file. " "Increasing the size when speedup the procedue " "but it will use more system memory. " "Default is 50MB.", default=MAX_WRITE_SIZE, ) parser.add_argument( "-b", "--block_size", type=int, help="block size only for nand, defaule is 128K", default=DEFAULT_BLOCK_SIZE, ) parser.add_argument( "-v", "--verbose", help="increase output verbosity", action="store_true" ) args = parser.parse_args() if args.verbose: logging.debug("Enable more verbose output") logging.getLogger().setLevel(level=logging.DEBUG) return args def handle_fip_for_4k_page(images_path, blocksize: int): logging.info("handle fip for 4K page....") fill_array = array("B", [0xFF for _ in range(2048)]) fip_path = path.join(images_path, "fip.bin") new_fip_path = path.join(images_path, "fip_4k.bin") if path.exists(fip_path): with open(fip_path, "rb") as fip: with open(new_fip_path, "wb") as new_fip: off = 0 while True: data = fip.read(2048) if data == b"": break new_fip.seek(off, 0) new_fip.write(data) fill_array.tofile(new_fip) new_fip.flush() off = off + 4096 new_fip_size = stat(new_fip_path).st_size print("new fip size is %d " % new_fip_size) append_array = array("B", [0xFF for _ in range(blocksize * 5 - new_fip_size)]) with open(new_fip_path, "ab") as f: append_array.tofile(f) def raw_image_check(f, filename): if filename == "fip.bin": return # CIMG head = f.read(4) if head == b"CIMG": logging.info("%s is not raw image, please use raw image" % filename) sys.exit(-1) def genDataBin( out, parts, images_path, storage_type, max_write_size=50 * 1024 * 1024, block_size=128 * 1024, chip="cv182x", ): if chip not in chip_list: logging.info("do not support %s" % chip) logging.info("only support ") logging.info(chip) sys.exit(-1) sv_array = array("B") if storage_type == "spinand" and chip != "cv181x": sv_path = path.join(images_path, "sv.bin") if path.exists(sv_path): logging.info("sv.bin is exist!") sv_size = stat(sv_path).st_size with open(sv_path, "rb") as f: sv_array.fromfile(f, sv_size) out.seek(0, 0) sv_array.tofile(out) else: logging.info("there is no sv.bin, please add it") for i, p in enumerate(parts): file_array = array("B") skip_size = 0 # we change offset of fip for nand if p["file_name"] == "fip.bin" and storage_type == "spinand" and chip != "cv181x": p["offset"] = block_size * SV_BLOCK_NUM skip_size = block_size * SV_BLOCK_NUM # handle fip for 4K page if block_size == BLOCK_SIZE_FOR_4K_NAND: handle_fip_for_4k_page(images_path, block_size) # use fip_4k.bin to pack p["file_name"] = "fip_4k.bin" if p["file_name"] == "": continue logging.debug("file name is %s" % p["file_name"]) file_path = path.join(images_path, p["file_name"]) if not path.exists(file_path): continue logging.debug("%s is exits" % file_path) file_size = stat(file_path).st_size logging.debug("%s size is %d" % (p["file_name"], file_size)) logging.debug("Packing %s" % p["label"]) if path.exists(file_path): with open(file_path, "rb") as f: raw_image_check(f, p["file_name"]) f.seek(0) file_array.fromfile(f, file_size) out.seek(p["offset"], 0) logging.info("Writing %s to pos %d" % (p["label"], p["offset"])) file_array.tofile(out) # for fip.bin of spi nand, we do a backup at 9th block if i == 0 and storage_type == "spinand": out.seek(block_size * FIP_BACKUP_BLOCK_POS, 0) file_array.tofile(out) logging.info( "do a backup for fip.bin at %d" % (block_size * FIP_BACKUP_BLOCK_POS) ) # Only append 0xff when the partition is not the last partition. if i != len(parts) - 1 and p["file_name"] != "fip.bin": append_size = p["part_size"] - file_size - skip_size # This part may seems stupid, but it works when image is too large # to keep content in memory. for j in range(0, append_size, max_write_size): append_byte = array( "B", [0xFF for _ in range(min(max_write_size, append_size - j))] ) append_byte.tofile(out) logging.info("generating Data.bin done!") def genBootBin(out, images_path): file_path = path.join(images_path, "fip.bin") try: file_array = array("B") fip_size = stat(file_path).st_size with open(file_path, "rb") as f: file_array.fromfile(f, fip_size) for _ in range(0x800 * LBA_SIZE - stat(file_path).st_size): file_array.append(0xFF) file_array.tofile(out) # write twice to backup fip file_array.tofile(out) logging.info("generating Boot.bin done!") out.close() except FileNotFoundError as e: logging.error("fip.bin is not exist") raise e def main(): args = parse_Args() xmlParser = XmlParser(args.xml) parts = xmlParser.parse(args.images_path) out_path = path.join(args.output, "Data.bin") storage_type = xmlParser.getStorage() logging.info("storage type is %s " % storage_type) with open(out_path, "wb") as out: genDataBin( out, parts, args.images_path, storage_type, args.max_write_size, args.block_size, args.chip, ) # append fip to 1M for emmc if storage_type == "emmc": fip_out_path = path.join(args.output, "Boot.bin") try: fd = open(fip_out_path, "wb") except Exception: logging.error("Create %s failed!", out_path) raise genBootBin(fd, args.images_path) if __name__ == "__main__": main()