#!/usr/bin/env python3 # SPDX-License-Identifier: BSD-3-Clause # PYTHON_ARGCOMPLETE_OK import sys import logging import os import os.path import argparse from collections import OrderedDict import binascii from struct import pack, unpack import lzma import pprint PYTHON_MIN_VERSION = (3, 5, 2) # Ubuntu 16.04 LTS contains Python v3.5.2 by default if sys.version_info < PYTHON_MIN_VERSION: print("Python >= %r is required" % (PYTHON_MIN_VERSION,)) sys.exit(-1) try: import coloredlogs except ImportError: coloredlogs = None try: import argcomplete except ImportError: argcomplete = None LOADER_2ND_MAGIC_ORIG = b"BL33" LOADER_2ND_MAGIC_LZMA = b"B3MA" LOADER_2ND_MAGIC_LZ4 = b"B3Z4" LOADER_2ND_MAGIC_LIST = [ LOADER_2ND_MAGIC_ORIG, LOADER_2ND_MAGIC_LZMA, LOADER_2ND_MAGIC_LZ4, ] IMAGE_ALIGN = 512 PARAM1_SIZE = 0x1000 PARAM1_SIZE_WO_SIG = 0x800 PARAM2_SIZE = 0x1000 def round_up(divident, divisor): return ((divident + divisor - 1) // divisor) * divisor def lzma_compress(body): z = lzma.LZMACompressor(lzma.FORMAT_ALONE, preset=lzma.PRESET_EXTREME) compressed = z.compress(body) compressed += z.flush() return compressed def lz4_compress(body): try: import lz4.frame except ImportError: logging.error("lz4 is not installed. Run 'pip install lz4'.") raise compressed = lz4.frame.compress(body) return compressed class Entry: __slots__ = "name", "type", "addr", "_content", "entry_size" def __init__(self): self.addr = None self._content = None @property def end(self): return self.addr + self.entry_size @property def content(self): return self._content @content.setter def content(self, value): if type(value) == int: value = value.to_bytes(self.entry_size, "little") if self.entry_size is not None: if len(value) > self.entry_size: raise ValueError("%s (%d bytes) must <= %#r" % (self.name, len(value), self.entry_size)) value = value + b"\0" * (self.entry_size - len(value)) self._content = value @classmethod def make(cls, name, entry_size, _type, init=None): entry = Entry() entry.name = name entry.type = _type entry.entry_size = entry_size if type(init) in (bytes, bytearray): entry.content = bytes(init) elif entry_size is not None: entry.content = b"\0" * entry.entry_size else: entry.content = b"" return (name, entry) def toint(self): if self.type != int: raise TypeError("%s is not int type" % self.name) return int.from_bytes(self.content, "little") def tostr(self): v = self.content if self.type == int: v = "%#08x" % self.toint() elif type(self.content) in [bytes, bytearray]: v = v.hex() if len(v) > 32: v = v[:32] + "..." return v def __str__(self): v = self.tostr() return "<%s=%s (%dbytes)>" % (self.name, v, self.entry_size) def __repr__(self): v = self.tostr() return "<%s: a=%#x s=%#x c=%s %r>" % (self.name, self.addr, self.entry_size, v, self.type) class FIP: param1 = OrderedDict( [ Entry.make("MAGIC1", 8, int, b"CVBL01\n\0"), Entry.make("MAGIC2", 4, int), Entry.make("PARAM_CKSUM", 4, int), Entry.make("NAND_INFO", 128, int), Entry.make("NOR_INFO", 36, int), Entry.make("FIP_FLAGS", 8, int), Entry.make("CHIP_CONF_SIZE", 4, int), Entry.make("BLCP_IMG_CKSUM", 4, int), Entry.make("BLCP_IMG_SIZE", 4, int), Entry.make("BLCP_IMG_RUNADDR", 4, int), Entry.make("BLCP_PARAM_LOADADDR", 4, int), Entry.make("BLCP_PARAM_SIZE", 4, int), Entry.make("BL2_IMG_CKSUM", 4, int), Entry.make("BL2_IMG_SIZE", 4, int), Entry.make("BLD_IMG_SIZE", 4, int), Entry.make("PARAM2_LOADADDR", 4, int), Entry.make("RESERVED1", 4, int), Entry.make("CHIP_CONF", 760, bytes), Entry.make("BL_EK", 32, bytes), Entry.make("ROOT_PK", 512, bytes), Entry.make("BL_PK", 512, bytes), Entry.make("BL_PK_SIG", 512, bytes), Entry.make("CHIP_CONF_SIG", 512, bytes), Entry.make("BL2_IMG_SIG", 512, bytes), Entry.make("BLCP_IMG_SIG", 512, bytes), ] ) body1 = OrderedDict( [ Entry.make("BLCP", None, bytes), Entry.make("BL2", None, bytes), ] ) param2 = OrderedDict( [ Entry.make("MAGIC1", 8, int, b"CVLD02\n\0"), Entry.make("PARAM2_CKSUM", 4, int), Entry.make("RESERVED1", 4, bytes), # DDR param Entry.make("DDR_PARAM_CKSUM", 4, int), Entry.make("DDR_PARAM_LOADADDR", 4, int), Entry.make("DDR_PARAM_SIZE", 4, int), Entry.make("DDR_PARAM_RESERVED", 4, int), # BLCP_2ND Entry.make("BLCP_2ND_CKSUM", 4, int), Entry.make("BLCP_2ND_LOADADDR", 4, int), Entry.make("BLCP_2ND_SIZE", 4, int), Entry.make("BLCP_2ND_RUNADDR", 4, int), # ATF-BL31 or OpenSBI Entry.make("MONITOR_CKSUM", 4, int), Entry.make("MONITOR_LOADADDR", 4, int), Entry.make("MONITOR_SIZE", 4, int), Entry.make("MONITOR_RUNADDR", 4, int), # u-boot Entry.make("LOADER_2ND_RESERVED0", 4, int), Entry.make("LOADER_2ND_LOADADDR", 4, int), Entry.make("LOADER_2ND_RESERVED1", 4, int), Entry.make("LOADER_2ND_RESERVED2", 4, int), # Reserved Entry.make("RESERVED_LAST", 4096 - 16 * 5, bytes), ] ) body2 = OrderedDict( [ Entry.make("DDR_PARAM", None, bytes), Entry.make("BLCP_2ND", None, bytes), Entry.make("MONITOR", None, bytes), Entry.make("LOADER_2ND", None, bytes), ] ) ldr_2nd_hdr = OrderedDict( [ Entry.make("JUMP0", 4, int), Entry.make("MAGIC", 4, int), Entry.make("CKSUM", 4, int), Entry.make("SIZE", 4, int), Entry.make("RUNADDR", 8, int), Entry.make("RESERVED1", 4, int), Entry.make("RESERVED2", 4, int), ] ) FIP_FLAGS_SCS_MASK = 0x000c FIP_FLAGS_ENCRYPTED_MASK = 0x0030 def _param_size(self, param): return max((e.end for e in param.values())) def _gen_param(self): addr = 0 for entry in self.param1.values(): entry.addr = addr addr += entry.entry_size assert PARAM1_SIZE_WO_SIG == self.param1["BL_PK_SIG"].addr addr = 0 for entry in self.param2.values(): entry.addr = addr addr += entry.entry_size assert PARAM2_SIZE == self.param2["RESERVED_LAST"].addr + self.param2["RESERVED_LAST"].entry_size addr = 0 for entry in self.ldr_2nd_hdr.values(): entry.addr = addr addr += entry.entry_size def __init__(self): self.compress_algo = None self._gen_param() def image_crc(self, image): crc = binascii.crc_hqx(image, 0) crc = pack("