176 lines
5.6 KiB
Python
Executable File
176 lines
5.6 KiB
Python
Executable File
#!/usr/bin/python3
|
|
# -*- coding: utf-8 -*-
|
|
import logging
|
|
import argparse
|
|
import os
|
|
from array import array
|
|
import binascii
|
|
from XmlParser import XmlParser
|
|
from tempfile import TemporaryDirectory
|
|
import shutil
|
|
|
|
MAX_LOAD_SIZE = 16 * 1024 * 1024
|
|
CHUNK_TYPE_DONT_CARE = 0
|
|
CHUNK_TYPE_CRC_CHECK = 1
|
|
FORMAT = "%(levelname)s: %(message)s"
|
|
logging.basicConfig(level=logging.INFO, format=FORMAT)
|
|
|
|
|
|
def parse_Args():
|
|
parser = argparse.ArgumentParser(description="Create CVITEK device image")
|
|
|
|
parser.add_argument(
|
|
"file_path",
|
|
metavar="file_path",
|
|
type=str,
|
|
help="the file you want to pack with cvitek image header",
|
|
)
|
|
parser.add_argument(
|
|
"output_dir",
|
|
metavar="output_folder_path",
|
|
type=str,
|
|
help="the folder path to install dir inclued fip,rootfs and kernel",
|
|
)
|
|
parser.add_argument("xml", help="path to partition xml")
|
|
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
|
|
|
|
|
|
class ImagerBuilder(object):
|
|
def __init__(self, storage: int, output_path):
|
|
self.storage = storage
|
|
self.output_path = output_path
|
|
|
|
def packHeader(self, part):
|
|
"""
|
|
Header format total 64 bytes
|
|
4 Bytes: Magic
|
|
4 Bytes: Version
|
|
4 Bytes: Chunk header size
|
|
4 Bytes: Total chunks
|
|
4 Bytes: File size
|
|
32 Bytes: Extra Flags
|
|
12 Bytes: Reserved
|
|
"""
|
|
with open(part["file_path"], "rb") as fd:
|
|
magic = fd.read(4)
|
|
if magic == b"CIMG":
|
|
logging.debug("%s has been packed, skip it!" % part["file_name"])
|
|
return
|
|
fd.seek(0)
|
|
Magic = array("b", [ord(c) for c in "CIMG"])
|
|
Version = array("I", [1])
|
|
chunk_header_sz = 64
|
|
Chunk_sz = array("I", [chunk_header_sz])
|
|
chunk_counts = part["file_size"] // MAX_LOAD_SIZE
|
|
remain = part["file_size"] - MAX_LOAD_SIZE * chunk_counts
|
|
if (remain != 0):
|
|
chunk_counts = chunk_counts + 1
|
|
Totak_chunk = array("I", [chunk_counts])
|
|
File_sz = array("I", [part["file_size"] + (chunk_counts * chunk_header_sz)])
|
|
try:
|
|
label = part["label"]
|
|
except KeyError:
|
|
label = "gpt"
|
|
Extra_flags = array("B", [ord(c) for c in label])
|
|
for _ in range(len(label), 32):
|
|
Extra_flags.append(ord("\0"))
|
|
|
|
img = open(os.path.join(self.output_path, part["file_name"]), "wb")
|
|
# Write Header
|
|
for h in [Magic, Version, Chunk_sz, Totak_chunk, File_sz, Extra_flags]:
|
|
h.tofile(img)
|
|
img.seek(64)
|
|
total_size = part["file_size"]
|
|
offset = part["offset"]
|
|
part_sz = part["part_size"]
|
|
op_len = 0
|
|
while total_size:
|
|
chunk_sz = min(MAX_LOAD_SIZE, total_size)
|
|
chunk = fd.read(chunk_sz)
|
|
crc = binascii.crc32(chunk) & 0xFFFFFFFF
|
|
if chunk_sz == MAX_LOAD_SIZE:
|
|
op_len += chunk_sz
|
|
else:
|
|
op_len = part_sz - op_len
|
|
chunk_header = self._getChunkHeader(chunk_sz, offset, op_len, crc)
|
|
img.write(chunk_header)
|
|
img.write(chunk)
|
|
total_size -= chunk_sz
|
|
offset += chunk_sz
|
|
img.close()
|
|
|
|
def _getChunkHeader(self, size: int, offset: int, part_sz: int, crc32: int):
|
|
"""
|
|
Header format total 64 bytes
|
|
4 Bytes: Chunk Type
|
|
4 Bytes: Chunk data size
|
|
4 Bytes: Program part offset
|
|
4 Bytes: Program part size
|
|
4 Bytes: Crc32 checksum
|
|
"""
|
|
logging.info("size:%x, offset:%x, part_sz:%x, crc:%x" % (size, offset, part_sz, crc32))
|
|
Chunk = array(
|
|
"I",
|
|
[
|
|
CHUNK_TYPE_CRC_CHECK,
|
|
size,
|
|
offset,
|
|
part_sz,
|
|
crc32,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
],
|
|
)
|
|
return Chunk
|
|
|
|
|
|
def main():
|
|
args = parse_Args()
|
|
xmlParser = XmlParser(args.xml)
|
|
install_dir = os.path.dirname(args.file_path)
|
|
parts = xmlParser.parse(install_dir)
|
|
storage = xmlParser.getStorage()
|
|
tmp = TemporaryDirectory()
|
|
imgBuilder = ImagerBuilder(storage, tmp.name)
|
|
for p in parts:
|
|
# Since xml parser will parse with abspath and the user input path can
|
|
# be relative path, use file name to check.
|
|
if os.path.basename(args.file_path) == p["file_name"]:
|
|
if (
|
|
storage != "emmc" and storage != "spinor"
|
|
and p["file_size"] > p["part_size"] - 128 * 1024
|
|
and p["mountpoint"]
|
|
and p["mountpoint"] != ""
|
|
):
|
|
logging.error(
|
|
"Imaege is too big, it will cause mount partition failed!!"
|
|
)
|
|
raise ValueError
|
|
imgBuilder.packHeader(p)
|
|
tmp_path = os.path.join(tmp.name, p["file_name"])
|
|
out_path = os.path.join(args.output_dir, p["file_name"])
|
|
logging.debug("Moving %s -> %s" % (tmp_path, out_path))
|
|
shutil.move(tmp_path, out_path)
|
|
logging.info("Packing %s done!" % (p["file_name"]))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|