282 lines
8.3 KiB
Python
Executable File
282 lines
8.3 KiB
Python
Executable File
#!/usr/bin/python
|
|
# -*- coding: utf-8 -*-
|
|
import argparse
|
|
import logging
|
|
from os import path, getcwd
|
|
from XmlParser import XmlParser
|
|
from tempfile import mkdtemp
|
|
import sys
|
|
import subprocess
|
|
|
|
FORMAT = "%(levelname)s: %(message)s"
|
|
logging.basicConfig(level=logging.INFO, format=FORMAT)
|
|
|
|
|
|
def resource_path(relative_path):
|
|
""" Get absolute path to resource, works for dev and for PyInstaller """
|
|
try:
|
|
# PyInstaller creates a temp folder and stores path in _MEIPASS
|
|
base_path = sys._MEIPASS
|
|
except Exception:
|
|
base_path = path.dirname(path.realpath(__file__))
|
|
return path.join(base_path, relative_path)
|
|
|
|
|
|
def parse_Args():
|
|
parser = argparse.ArgumentParser(description="Create UBI image")
|
|
parser.add_argument("xml", help="path to partition xml")
|
|
parser.add_argument("label", help="label of the partition")
|
|
parser.add_argument(
|
|
"input_path", metavar="input", type=str, help="input file or folder for packing"
|
|
)
|
|
parser.add_argument(
|
|
"output_path",
|
|
metavar="output_file_path",
|
|
type=str,
|
|
help="the folder path to install dir inclued fip,rootfs and kernel",
|
|
)
|
|
|
|
parser.add_argument(
|
|
"-v", "--verbose", help="increase output verbosity", action="store_true"
|
|
)
|
|
parser.add_argument(
|
|
"-p",
|
|
"--pagesize",
|
|
help="page size of nand, default is 2Kib",
|
|
type=str,
|
|
default="2K",
|
|
)
|
|
parser.add_argument(
|
|
"-b",
|
|
"--blocksize",
|
|
help="block size of nand, default is 128Kib",
|
|
type=str,
|
|
default="128K",
|
|
)
|
|
|
|
parser.add_argument("--ubinize", help="path to ubinize", type=str)
|
|
parser.add_argument("--mkfs", help="path to mkfs.ubifs", type=str)
|
|
parser.add_argument(
|
|
"--ubionly",
|
|
help="create ubi image only",
|
|
action="store_true",
|
|
)
|
|
parser.add_argument(
|
|
"--maxsize",
|
|
help="Set max size for the partition"
|
|
"(For the partition without size assigned) ",
|
|
type=int,
|
|
)
|
|
|
|
parser.add_argument(
|
|
"--reserved",
|
|
help="Set reserved blocks percentage for the partition,"
|
|
"For example: --reserved 10 means 10%% reserved blocks."
|
|
"(For the partition without size assigned) ",
|
|
type=int,
|
|
)
|
|
args = parser.parse_args()
|
|
if args.verbose:
|
|
logging.debug("Enable more verbose output")
|
|
logging.getLogger().setLevel(level=logging.DEBUG)
|
|
|
|
if args.ubinize is None:
|
|
args.ubinize = resource_path("ubinize")
|
|
|
|
if args.mkfs is None:
|
|
args.mkfs = resource_path("mkfs.ubifs")
|
|
return args
|
|
|
|
|
|
def log_subprocess_output(pipe):
|
|
for line in iter(pipe.readline, b""): # b'\n'-separated lines
|
|
logging.debug("got line from subprocess: %r", line)
|
|
|
|
|
|
def create_ubicfg(img_path, part_size, label, output, ubionly=False):
|
|
with open(output, "w") as f:
|
|
f.write("[ubifs]\n")
|
|
f.write("mode=ubi\n")
|
|
f.write("image=%s\n" % img_path)
|
|
f.write("vol_id=0\n")
|
|
if not ubionly:
|
|
f.write("vol_size=%d\n" % part_size)
|
|
f.write("vol_type=dynamic\n")
|
|
f.write("vol_name=%s\n" % label)
|
|
f.write("vol_flags=autoresize\n")
|
|
|
|
# Debug message
|
|
logging.debug("[ubifs]")
|
|
logging.debug("[mode]=ubi")
|
|
logging.debug("image=%s" % img_path)
|
|
logging.debug("vol_id=0")
|
|
logging.debug("vol_size=%d" % part_size)
|
|
logging.debug("vol_type=dynamic")
|
|
logging.debug("vol_name=%s" % label)
|
|
logging.debug("vol_flags=autoresize")
|
|
|
|
|
|
def create_ubifs(
|
|
input_dir, output_dir, internal_part_size, pagesize, pebsize, mkfs, verbose=False
|
|
):
|
|
|
|
lebsize = pebsize - 2 * pagesize
|
|
part_lebcnt = internal_part_size / lebsize
|
|
ubifs_args = "-r %s -m %d -e %d -c %d -F" % (
|
|
input_dir,
|
|
pagesize,
|
|
lebsize,
|
|
part_lebcnt,
|
|
)
|
|
logging.debug("ubifs args %s", ubifs_args)
|
|
tmp_ubifs = path.join(output_dir, "tmp.ubifs")
|
|
mkfs_cmd = "%s %s -o %s" % (mkfs, ubifs_args, tmp_ubifs)
|
|
logging.debug("mkfs_cmd:%s", mkfs_cmd)
|
|
try:
|
|
process = subprocess.Popen(
|
|
mkfs_cmd,
|
|
stdout=subprocess.PIPE,
|
|
stderr=subprocess.STDOUT,
|
|
cwd=getcwd(),
|
|
shell=True,
|
|
)
|
|
except Exception:
|
|
return -1
|
|
if verbose:
|
|
with process.stdout:
|
|
log_subprocess_output(process.stdout)
|
|
ret = process.wait()
|
|
return [ret, tmp_ubifs]
|
|
|
|
|
|
def create_ubi(img_path, cfg_path, output, pagesize, pebsize, ubinize, verbose=False):
|
|
ubi_args = "-p %d -m %d" % (pebsize, pagesize)
|
|
logging.debug("ubi args %s", ubi_args)
|
|
ubinize_cmd = "%s -o %s %s %s" % (ubinize, output, ubi_args, cfg_path)
|
|
logging.debug("ubinize_cmd:%s", ubinize_cmd)
|
|
try:
|
|
process = subprocess.Popen(
|
|
ubinize_cmd,
|
|
stdout=subprocess.PIPE,
|
|
stderr=subprocess.STDOUT,
|
|
cwd=getcwd(),
|
|
shell=True,
|
|
)
|
|
except Exception:
|
|
return -1
|
|
if verbose:
|
|
with process.stdout:
|
|
log_subprocess_output(process.stdout)
|
|
ret = process.wait()
|
|
return ret
|
|
|
|
|
|
def main():
|
|
args = parse_Args()
|
|
parser = XmlParser(args.xml)
|
|
parts = parser.parse()
|
|
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 args.label == p["label"]:
|
|
part = p
|
|
|
|
break
|
|
try:
|
|
part_size = part["part_size"]
|
|
label = part["label"]
|
|
except Exception:
|
|
logging.error("label is not found in partition.xml, please check!")
|
|
return -1
|
|
logging.debug("get partition as below:")
|
|
logging.debug(p)
|
|
pagesize = XmlParser.parse_size(args.pagesize)
|
|
pebsize = XmlParser.parse_size(args.blocksize)
|
|
lebsize = pebsize - 2 * pagesize
|
|
|
|
if part_size == sys.maxsize:
|
|
if args.maxsize:
|
|
part_size = args.maxsize
|
|
else:
|
|
logging.error("please use --maxsize size to assign size")
|
|
return 1
|
|
# 1.
|
|
# 2 PEBs are used to store the volume table;
|
|
# 1 PEB is reserved for wear-leveling purposes;
|
|
# 1 PEB is reserved for the atomic LEB change operation;
|
|
# 2.
|
|
# some amount of PEBs are reserved for bad PEB handling;
|
|
# this is applicable for NAND flash but not for NOR flash
|
|
# the amount of reserved PEBs is configurable and is equal to
|
|
# 20 blocks per 1024 blocks by default.
|
|
# 3.
|
|
# UBI stores the EC and VID headers at the beginning of each PEB;
|
|
# the number of bytes used for these purposes depends on
|
|
# the flash type and is explained below.
|
|
# 4.
|
|
# According to above design, set at lease 5 blocks reserved
|
|
# Set extra reserved for bad blocks, at least 1 block or 1% of partition
|
|
# size for r/w partition
|
|
# 5.
|
|
# SP: PEB
|
|
# SL: LEB
|
|
# P: Total number of PEBs on the MTD device
|
|
# O: The overhead related to storing EC and VID headers in bytes, i.e. O = SP - SL
|
|
# B: Number of bad block handling
|
|
# UBI Overhead = (B + 4) * SP + O * (P - B - 4)
|
|
SP = pebsize
|
|
SL = lebsize
|
|
P = int(part_size / pebsize)
|
|
Ox = SP - SL
|
|
try:
|
|
B = max(1, int((args.reserved / 100) * P), int(0.01 * P))
|
|
except Exception:
|
|
B = max(int(0.01 * P), 1)
|
|
ubi_overhead = (B + 4) * SP + Ox * (P - B - 4)
|
|
lebcnt = int((part_size - ubi_overhead) / pebsize)
|
|
|
|
internal_part_size = lebcnt * lebsize
|
|
tmpdir = mkdtemp()
|
|
if not args.ubionly:
|
|
logging.info("Creating ubifs")
|
|
ret, fs_path = create_ubifs(
|
|
args.input_path,
|
|
tmpdir,
|
|
internal_part_size,
|
|
pagesize,
|
|
pebsize,
|
|
args.mkfs,
|
|
args.verbose,
|
|
)
|
|
if ret:
|
|
logging.error("create ubifs error, please enable verbose!")
|
|
return -1
|
|
else:
|
|
fs_path = args.input_path
|
|
tmp_cfg_path = path.join(tmpdir, "tmp.cfg")
|
|
create_ubicfg(
|
|
fs_path,
|
|
internal_part_size,
|
|
label,
|
|
tmp_cfg_path,
|
|
(args.ubionly or part_size == sys.maxsize),
|
|
)
|
|
|
|
ret = create_ubi(
|
|
fs_path,
|
|
tmp_cfg_path,
|
|
args.output_path,
|
|
pagesize,
|
|
pebsize,
|
|
args.ubinize,
|
|
args.verbose,
|
|
)
|
|
if ret:
|
|
logging.error("create ubi image error, please enable verbose!")
|
|
return -1
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|