#!/usr/bin/env python3 # Copyright 2021 The Chromium OS Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. # # Ignore indention messages, since legacy scripts use 2 spaces instead of 4. # pylint: disable=bad-indentation,docstring-section-indent # pylint: disable=docstring-trailing-quotes # A script to pack EC binary into SPI flash image for MEC172x # Based on MEC172x_ROM_Description.pdf revision 6/8/2020 import argparse import hashlib import os import struct import subprocess import tempfile import zlib # CRC32 # MEC172x has 416KB SRAM from 0xC0000 - 0x127FFF # SRAM is divided into contiguous CODE & DATA # CODE at [0xC0000, 0x117FFF] DATA at [0x118000, 0x127FFF] # Google EC SPI flash size for board is currently 512KB # split into 1/2. # EC_RO: 0 - 0x3FFFF # EC_RW: 0x40000 - 0x7FFFF # SPI_ERASE_BLOCK_SIZE = 0x1000 SPI_CLOCK_LIST = [48, 24, 16, 12, 96] SPI_READ_CMD_LIST = [0x3, 0xb, 0x3b, 0x6b] SPI_DRIVE_STR_DICT = {2:0, 4:1, 8:2, 12:3} # Maximum EC_RO/EC_RW code size is based upon SPI flash erase # sector size, MEC172x Boot-ROM TAG, Header, Footer. # SPI Offset Description # 0x00 - 0x07 TAG0 and TAG1 # 0x1000 - 0x113F Boot-ROM SPI Header # 0x1140 - 0x213F 4KB LFW # 0x2040 - 0x3EFFF # 0x3F000 - 0x3FFFF BootROM EC_INFO_BLK || COSIG || ENCR_KEY_HDR(optional) || TRAILER CHIP_MAX_CODE_SRAM_KB = (256 - 12) MEC172X_DICT = { "LFW_SIZE": 0x1000, "LOAD_ADDR": 0xC0000, "TAG_SIZE": 4, "KEY_BLOB_SIZE": 1584, "HEADER_SIZE":0x140, "HEADER_VER":0x03, "PAYLOAD_GRANULARITY":128, "PAYLOAD_PAD_BYTE":b'\xff', "EC_INFO_BLK_SZ":128, "ENCR_KEY_HDR_SZ":128, "COSIG_SZ":96, "TRAILER_SZ":160, "TAILER_PAD_BYTE":b'\xff', "PAD_SIZE":128 } CRC_TABLE = [0x00, 0x07, 0x0e, 0x09, 0x1c, 0x1b, 0x12, 0x15, 0x38, 0x3f, 0x36, 0x31, 0x24, 0x23, 0x2a, 0x2d] def mock_print(*args, **kwargs): pass debug_print = mock_print # Debug helper routine def dumpsects(spi_list): debug_print("spi_list has {0} entries".format(len(spi_list))) for s in spi_list: debug_print("0x{0:x} 0x{1:x} {2:s}".format(s[0],len(s[1]),s[2])) def printByteArrayAsHex(ba, title): debug_print(title,"= ") if ba == None: debug_print("None") return count = 0 for b in ba: count = count + 1 debug_print("0x{0:02x}, ".format(b),end="") if (count % 8) == 0: debug_print("") debug_print("") def Crc8(crc, data): """Update CRC8 value.""" for v in data: crc = ((crc << 4) & 0xff) ^ (CRC_TABLE[(crc >> 4) ^ (v >> 4)]); crc = ((crc << 4) & 0xff) ^ (CRC_TABLE[(crc >> 4) ^ (v & 0xf)]); return crc ^ 0x55 def GetEntryPoint(payload_file): """Read entry point from payload EC image.""" with open(payload_file, 'rb') as f: f.seek(4) s = f.read(4) return int.from_bytes(s, byteorder='little') def GetPayloadFromOffset(payload_file, offset, padsize): """Read payload and pad it to padsize.""" with open(payload_file, 'rb') as f: f.seek(offset) payload = bytearray(f.read()) rem_len = len(payload) % padsize debug_print("GetPayload: padsize={0:0x} len(payload)={1:0x} rem={2:0x}".format(padsize,len(payload),rem_len)) if rem_len: payload += PAYLOAD_PAD_BYTE * (padsize - rem_len) debug_print("GetPayload: Added {0} padding bytes".format(padsize - rem_len)) return payload def GetPayload(payload_file, padsize): """Read payload and pad it to padsize""" return GetPayloadFromOffset(payload_file, 0, padsize) def GetPublicKey(pem_file): """Extract public exponent and modulus from PEM file.""" result = subprocess.run(['openssl', 'rsa', '-in', pem_file, '-text', '-noout'], stdout=subprocess.PIPE, encoding='utf-8') modulus_raw = [] in_modulus = False for line in result.stdout.splitlines(): if line.startswith('modulus'): in_modulus = True elif not line.startswith(' '): in_modulus = False elif in_modulus: modulus_raw.extend(line.strip().strip(':').split(':')) if line.startswith('publicExponent'): exp = int(line.split(' ')[1], 10) modulus_raw.reverse() modulus = bytearray((int(x, 16) for x in modulus_raw[:256])) return struct.pack(' 0x03 1-1-1 read freq < 33MHz # 1 -> 0x0B 1-1-1 + 8 clocks(data tri-stated) # 2 -> 0x3B 1-1-2 + 8 clocks(data tri-stated). Data phase is dual I/O # 3 -> 0x6B 1-1-4 + 8 clocks(data tri-stated). Data phase is Quad I/O # NOTE: Quad requires SPI flash device QE(quad enable) bit # to be factory set. Enabling QE disables HOLD# and WP# # functionality of the SPI flash device. # header[0x8:0xC] SRAM Load address little-endian format # header[0xC:0x10] SRAM FW entry point. Boot-ROM jumps to # this address on successful load. (little-endian) # header[0x10:0x12] little-endian format: FW binary size in units of # 128 bytes(MEC172x) # header[0x12:0x14] = 0 reserved # header[0x14:0x18] = Little-ending format: Unsigned offset from start of # header to FW payload. # MEC172x: Offset must be a multiple of 128 # Offset must be >= header size. # NOTE: If Authentication is enabled size includes # the appended signature. # MEC172x: # header[0x18] = Authentication key select. Set to 0 for no Authentication. # header[0x19] = SPI flash device(s) drive strength feature flags # b[0]=1 SPI flash device 0 has drive strength feature # b[1]=SPI flash 0: DrvStr Write format(0=2 bytes, 1=1 byte) # b[2]=1 SPI flash device 1 has drive strength feature # b[3]=SPI flash 1: DrvStr Write format(0=2 bytes, 1=1 byte) # b[7:4] = 0 reserved # header[0x1A:0x20] = 0 reserved # header[0x20] = SPI opcode to read drive strength from SPI flash 0 # header[0x21] = SPI opcode to write drive strength to SPI flash 0 # header[0x22] = SPI flash 0: drvStr value # header[0x23] = SPI flash 0: drvStr mask # header[0x24] = SPI opcode to read drive strength from SPI flash 1 # header[0x25] = SPI opcode to write drive strength to SPI flash 1 # header[0x26] = SPI flash 1: drvStr value # header[0x27] = SPI flash 1: drvStr mask # header[0x28:0x48] = reserved, may be any value # header[0x48:0x50] = reserved for customer use # header[0x50:0x80] = ECDSA-384 public key x-coord. = 0 Auth. disabled # header[0x80:0xB0] = ECDSA-384 public key y-coord. = 0 Auth. disabled # header[0xB0:0xE0] = SHA-384 digest of header[0:0xB0] # header[0xE0:0x110] = Header ECDSA-384 signature x-coord. = 0 Auth. disabled # header[0x110:0x140] = Header ECDSA-384 signature y-coor. = 0 Auth. disabled # def BuildHeader2(args, chip_dict, payload_len, load_addr, payload_entry): header_size = chip_dict["HEADER_SIZE"] # allocate zero filled header header = bytearray(b'\x00' * header_size) debug_print("len(header) = ", len(header)) # Identifier and header version header[0:4] = b'PHCM' header[4] = chip_dict["HEADER_VER"] # SPI frequency, drive strength, CPOL/CPHA encoding same for both chips spiFreqIndex = GetSpiClockParameter(args) if spiFreqIndex > 3: header[6] |= 0x01 else: header[5] = spiFreqIndex header[5] |= ((GetEncodedSpiDriveStrength(args) & 0x03) << 2) header[5] |= ((GetSpiSlewRate(args) & 0x01) << 4) header[5] |= ((GetSpiCpol(args) & 0x01) << 5) header[5] |= ((GetSpiCphaMosi(args) & 0x01) << 6) header[5] |= ((GetSpiCphaMiso(args) & 0x01) << 7) # header[6] # b[0] value set above # b[2:1] = 00b, b[5:3]=111b # b[7]=0 No encryption of FW payload header[6] |= 0x7 << 3 # SPI read command set same for both chips header[7] = GetSpiReadCmdParameter(args) & 0xFF # bytes 0x08 - 0x0b header[0x08:0x0C] = load_addr.to_bytes(4, byteorder='little') # bytes 0x0c - 0x0f header[0x0C:0x10] = payload_entry.to_bytes(4, byteorder='little') # bytes 0x10 - 0x11 payload length in units of 128 bytes assert payload_len % chip_dict["PAYLOAD_GRANULARITY"] == 0, \ print("Payload size not a multiple of {0}".format(chip_dict["PAYLOAD_GRANULARITY"])) payload_units = int(payload_len // chip_dict["PAYLOAD_GRANULARITY"]) assert payload_units < 0x10000, \ print("Payload too large: len={0} units={1}".format(payload_len, payload_units)) header[0x10:0x12] = payload_units.to_bytes(2, 'little') # bytes 0x14 - 0x17 TODO offset from start of payload to FW payload to be # loaded by Boot-ROM. We ask Boot-ROM to load (LFW || EC_RO). # LFW location provided on the command line. assert (args.lfw_loc % 4096 == 0), \ print("LFW location not on a 4KB boundary! 0x{0:0x}".format(args.lfw_loc)) assert args.lfw_loc >= (args.header_loc + chip_dict["HEADER_SIZE"]), \ print("LFW location not greater than header location + header size") lfw_ofs = args.lfw_loc - args.header_loc header[0x14:0x18] = lfw_ofs.to_bytes(4, 'little') # MEC172x: authentication key select. Authentication not used, set to 0. header[0x18] = 0 # header[0x19], header[0x20:0x28] # header[0x1A:0x20] reserved 0 # MEC172x: supports SPI flash devices with drive strength settings # TODO leave these fields at 0 for now. We must add 6 command line # arguments. # header[0x28:0x48] reserve can be any value # header[0x48:0x50] Customer use. TODO # authentication disabled, leave these 0. # header[0x50:0x80] ECDSA P384 Authentication Public key Rx # header[0x80:0xB0] ECDSA P384 Authentication Public key Ry # header[0xB0:0xE0] = SHA384(header[0:0xB0]) header[0xB0:0xE0] = hashlib.sha384(header[0:0xB0]).digest() # When ECDSA authentication is disabled MCHP SPI image generator # is filling the last 48 bytes of the Header with 0xff header[-48:] = b'\xff' * 48 debug_print("After hash: len(header) = ", len(header)) return header # # MEC172x 128-byte EC Info Block appended to end of padded FW binary. # Python slice notation # byte[0:0x64] are undefined, we set to 0xFF # byte[0x64] = FW Build LSB # byte[0x65] = FW Build MSB # byte[0x66:0x68] = undefined, set to 0xFF # byte[0x68:0x78] = Roll back permissions bit maps # byte[0x78:0x7c] = key revocation bit maps # byte[0x7c] = platform ID LSB # byte[0x7d] = platform ID MSB # byte[0x7e] = auto-rollback protection enable # byte[0x7f] = current imeage revision # def GenEcInfoBlock(args, chip_dict): # ecinfo = bytearray([0xff] * chip_dict["EC_INFO_BLK_SZ"]) ecinfo = bytearray(chip_dict["EC_INFO_BLK_SZ"]) return ecinfo # # Generate SPI FW image co-signature. # MEC172x cosignature is 96 bytes used by OEM FW # developer to sign their binary with ECDSA-P384-SHA384 or # some other signature algorithm that fits in 96 bytes. # At this time Cros-EC is not using this field, fill with 0xFF. # If this feature is implemented we need to read the OEM's # generated signature from a file and extract the binary # signature. # def GenCoSignature(args, chip_dict, payload): return bytearray(b'\xff' * chip_dict["COSIG_SZ"]) # # Generate SPI FW Image trailer. # MEC172x: Size = 160 bytes # binary = payload || encryption_key_header || ec_info_block || cosignature # trailer[0:48] = SHA384(binary) # trailer[48:144] = 0xFF # trailer[144:160] = 0xFF. Boot-ROM spec. says these bytes should be random. # Authentication & encryption are not used therefore random data # is not necessary. def GenTrailer(args, chip_dict, payload, encryption_key_header, ec_info_block, cosignature): debug_print("GenTrailer SHA384 computation") trailer = bytearray(chip_dict["TAILER_PAD_BYTE"] * chip_dict["TRAILER_SZ"]) hasher = hashlib.sha384() hasher.update(payload) debug_print(" Update: payload len=0x{0:0x}".format(len(payload))) if ec_info_block != None: hasher.update(ec_info_block) debug_print(" Update: ec_info_block len=0x{0:0x}".format(len(ec_info_block))) if encryption_key_header != None: hasher.update(encryption_key_header) debug_print(" Update: encryption_key_header len=0x{0:0x}".format(len(encryption_key_header))) if cosignature != None: hasher.update(cosignature) debug_print(" Update: cosignature len=0x{0:0x}".format(len(cosignature))) trailer[0:48] = hasher.digest() trailer[-16:] = 16 * b'\xff' return trailer # MEC172x supports two 32-bit Tags located at offsets 0x0 and 0x4 # in the SPI flash. # Tag format: # bits[23:0] correspond to bits[31:8] of the Header SPI address # Header is always on a 256-byte boundary. # bits[31:24] = CRC8-ITU of bits[23:0]. # Notice there is no chip-select field in the Tag both Tag's point # to the same flash part. # def BuildTag(args): tag = bytearray([(args.header_loc >> 8) & 0xff, (args.header_loc >> 16) & 0xff, (args.header_loc >> 24) & 0xff]) tag.append(Crc8(0, tag)) return tag def BuildTagFromHdrAddr(header_loc): tag = bytearray([(header_loc >> 8) & 0xff, (header_loc >> 16) & 0xff, (header_loc >> 24) & 0xff]) tag.append(Crc8(0, tag)) return tag # FlashMap is an option for MEC172x # It is a 32 bit structure # bits[18:0] = bits[30:12] of second SPI flash base address # bits[23:19] = 0 reserved # bits[31:24] = CRC8 of bits[23:0] # Input: # integer containing base address of second SPI flash # This value is usually equal to the size of the first # SPI flash and should be a multiple of 4KB # Output: # bytearray of length 4 def BuildFlashMap(secondSpiFlashBaseAddr): flashmap = bytearray(4) flashmap[0] = (secondSpiFlashBaseAddr >> 12) & 0xff flashmap[1] = (secondSpiFlashBaseAddr >> 20) & 0xff flashmap[2] = (secondSpiFlashBaseAddr >> 28) & 0xff flashmap[3] = Crc8(0, flashmap) return flashmap # # Creates temporary file for read/write # Reads binary file containing LFW image_size (loader_file) # Writes LFW image to temporary file # Reads RO image at beginning of rorw_file up to image_size # (assumes RO/RW images have been padded with 0xFF # Returns temporary file name # def PacklfwRoImage(rorw_file, loader_file, image_size): """Create a temp file with the first image_size bytes from the loader file and append bytes from the rorw file. return the filename""" fo=tempfile.NamedTemporaryFile(delete=False) # Need to keep file around with open(loader_file,'rb') as fin1: # read 4KB loader file pro = fin1.read() fo.write(pro) # write 4KB loader data to temp file with open(rorw_file, 'rb') as fin: ro = fin.read(image_size) fo.write(ro) fo.close() return fo.name # # Generate a test EC_RW image of same size # as original. # Preserve image_data structure and fill all # other bytes with 0xA5. # useful for testing SPI read and EC build # process hash generation. # def gen_test_ecrw(pldrw): debug_print("gen_test_ecrw: pldrw type =", type(pldrw)) debug_print("len pldrw =", len(pldrw), " = ", hex(len(pldrw))) cookie1_pos = pldrw.find(b'\x99\x88\x77\xce') cookie2_pos = pldrw.find(b'\xdd\xbb\xaa\xce', cookie1_pos+4) t = struct.unpack(" 0 and cookie2_pos > cookie1_pos: for i in range(0, cookie1_pos): pldrw[i] = 0xA5 for i in range(cookie2_pos+4, len(pldrw)): pldrw[i] = 0xA5 with open("ec_RW_test.bin", "wb") as fecrw: fecrw.write(pldrw[:size]) def parseargs(): rpath = os.path.dirname(os.path.relpath(__file__)) parser = argparse.ArgumentParser() parser.add_argument("-i", "--input", help="EC binary to pack, usually ec.bin or ec.RO.flat.", metavar="EC_BIN", default="ec.bin") parser.add_argument("-o", "--output", help="Output flash binary file", metavar="EC_SPI_FLASH", default="ec.packed.bin") parser.add_argument("--loader_file", help="EC loader binary", default="ecloader.bin") parser.add_argument("--load_addr", type=int, help="EC SRAM load address", default=0xC0000) parser.add_argument("-s", "--spi_size", type=int, help="Size of the SPI flash in KB", default=512) parser.add_argument("-l", "--header_loc", type=int, help="Location of header in SPI flash. Must be on a 256 byte boundary", default=0x0100) parser.add_argument("--lfw_loc", type=int, help="Location of LFW in SPI flash. Must be on a 4KB boundary", default=0x1000) parser.add_argument("--lfw_size", type=int, help="LFW size in bytes", default=0x1000) parser.add_argument("-r", "--rw_loc", type=int, help="Start offset of EC_RW. Default is -1 meaning 1/2 flash size", default=-1) parser.add_argument("--spi_clock", type=int, help="SPI clock speed. 8, 12, 24, or 48 MHz.", default=24) parser.add_argument("--spi_read_cmd", type=int, help="SPI read command. 0x3, 0xB, 0x3B, or 0x6B.", default=0xb) parser.add_argument("--image_size", type=int, help="Size of a single image. Default 244KB", default=(244 * 1024)) parser.add_argument("--test_spi", action='store_true', help="Test SPI data integrity by adding CRC32 in last 4-bytes of RO/RW binaries", default=False) parser.add_argument("--test_ecrw", action='store_true', help="Use fixed pattern for EC_RW but preserve image_data", default=False) parser.add_argument("--verbose", action='store_true', help="Enable verbose output", default=False) parser.add_argument("--tag0_loc", type=int, help="MEC172x TAG0 SPI offset", default=0) parser.add_argument("--tag1_loc", type=int, help="MEC172x TAG1 SPI offset", default=4) parser.add_argument("--spi_drive_str", type=int, help="Chip SPI drive strength in mA: 2, 4, 8, or 12", default=4) parser.add_argument("--spi_slew_fast", action='store_true', help="SPI use fast slew rate. Default is False", default=False) parser.add_argument("--spi_cpol", type=int, help="SPI clock polarity when idle. Defealt is 0(low)", default=0) parser.add_argument("--spi_cpha_mosi", type=int, help="""SPI clock phase controller drives data. 0=Data driven on active to inactive clock edge, 1=Data driven on inactive to active clock edge""", default=0) parser.add_argument("--spi_cpha_miso", type=int, help="""SPI clock phase controller samples data. 0=Data sampled on inactive to active clock edge, 1=Data sampled on active to inactive clock edge""", default=0) return parser.parse_args() def print_args(args): debug_print("parsed arguments:") debug_print(".input = ", args.input) debug_print(".output = ", args.output) debug_print(".loader_file = ", args.loader_file) debug_print(".spi_size (KB) = ", hex(args.spi_size)) debug_print(".image_size = ", hex(args.image_size)) debug_print(".load_addr", hex(args.load_addr)) debug_print(".tag0_loc = ", hex(args.tag0_loc)) debug_print(".tag1_loc = ", hex(args.tag1_loc)) debug_print(".header_loc = ", hex(args.header_loc)) debug_print(".lfw_loc = ", hex(args.lfw_loc)) debug_print(".lfw_size = ", hex(args.lfw_size)) if args.rw_loc < 0: debug_print(".rw_loc = ", args.rw_loc) else: debug_print(".rw_loc = ", hex(args.rw_loc)) debug_print(".spi_clock (MHz) = ", args.spi_clock) debug_print(".spi_read_cmd = ", hex(args.spi_read_cmd)) debug_print(".test_spi = ", args.test_spi) debug_print(".test_ecrw = ", args.test_ecrw) debug_print(".verbose = ", args.verbose) debug_print(".spi_drive_str = ", args.spi_drive_str) debug_print(".spi_slew_fast = ", args.spi_slew_fast) debug_print(".spi_cpol = ", args.spi_cpol) debug_print(".spi_cpha_mosi = ", args.spi_cpha_mosi) debug_print(".spi_cpha_miso = ", args.spi_cpha_miso) def spi_list_append(mylist, loc, data, description): """Append SPI data block tuple to list""" t = (loc, data, description) mylist.append(t) debug_print("Add SPI entry: offset=0x{0:08x} len=0x{1:0x} descr={2}".format(loc, len(data), description)) # # Handle quiet mode build from Makefile # Quiet mode when V is unset or V=0 # Verbose mode when V=1 # # MEC172x SPI Image Generator # No authentication # No payload encryption # # SPI Offset 0x0 = TAG0 points to Header for EC-RO FW # SPI Offset 0x4 = TAG1 points to Header for EC-RO FW # TAG Size = 4 bytes # bits[23:0] = bits[31:8] of Header SPI offset # bits[31:24] = CRC8-ITU checksum of bits[23:0]. # # MEC172x SPI header and payload layout for minimum size # header offset aligned on 256 byte boundary # header_spi_address: # header[0:0x4F] = Header data # header[0x50:0x80] = ECDSA-P384 public key x for Header authentication # header[0x80:0xB0] = ECDSA-P384 public key y for Header authentication # header[0xB0:0xE0] = SHA384 digest of header[0:0xB0] # header[0xE0:0x110] = ECDSA-P384-SHA384 Signature.R of header[0:0xB0] # header[0x110:0x140] = ECDSA-P384-SHA384 Signature.S of header[0:0xB0] # payload_spi_address = header_spi_address + len(Header) # Payload had been padded such that len(padded_payload) % 128 == 0 # padded_payload[padded_payload_len] # payload_signature_address = payload_spi_address + len(padded_payload) # payload_encryption_key_header[128] Not present if encryption disabled # payload_cosignature[96] = 0 if Authentication is disabled # payload_trailer[160] = SHA384(padded_payload || # optional payload_encryption_key_header) # || 48 * [0] # || 48 * [0] # def main(): global debug_print args = parseargs() if args.verbose: debug_print = print debug_print("Begin pack_ec_mec172x.py script") print_args(args) chip_dict = MEC172X_DICT # Boot-ROM requires header location aligned >= 256 bytes. # CrOS EC flash image update code requires EC_RO/RW location to be aligned # on a flash erase size boundary and EC_RO/RW size to be a multiple of # the smallest flash erase block size. spi_size = args.spi_size * 1024 spi_image_size = spi_size // 2 rorofile=PacklfwRoImage(args.input, args.loader_file, args.image_size) debug_print("Temporary file containing LFW + EC_RO is ", rorofile) lfw_ecro = GetPayload(rorofile, chip_dict["PAD_SIZE"]) lfw_ecro_len = len(lfw_ecro) debug_print("Padded LFW + EC_RO length = ", hex(lfw_ecro_len)) # SPI test mode compute CRC32 of EC_RO and store in last 4 bytes if args.test_spi: crc32_ecro = zlib.crc32(bytes(lfw_ecro[LFW_SIZE:-4])) crc32_ecro_bytes = crc32_ecro.to_bytes(4, byteorder='little') lfw_ecro[-4:] = crc32_ecro_bytes debug_print("ecro len = ", hex(len(lfw_ecro) - LFW_SIZE)) debug_print("CRC32(ecro-4) = ", hex(crc32_ecro)) # Reads entry point from offset 4 of file. # This assumes binary has Cortex-M4 vector table at offset 0. # 32-bit word at offset 0x0 initial stack pointer value # 32-bit word at offset 0x4 address of reset handler # NOTE: reset address will have bit[0]=1 to ensure thumb mode. lfw_ecro_entry = GetEntryPoint(rorofile) debug_print("LFW Entry point from GetEntryPoint = 0x{0:08x}".format(lfw_ecro_entry)) # Chromebooks are not using MEC BootROM SPI header/payload authentication # or payload encryption. In this case the header authentication signature # is filled with the hash digest of the respective entity. # BuildHeader2 computes the hash digest and stores it in the correct # header location. header = BuildHeader2(args, chip_dict, lfw_ecro_len, args.load_addr, lfw_ecro_entry) printByteArrayAsHex(header, "Header(lfw_ecro)") # If payload encryption used then encrypt payload and # generate Payload Key Header. If encryption not used # payload is not modified and the method returns None encryption_key_header = EncryptPayload(args, chip_dict, lfw_ecro) printByteArrayAsHex(encryption_key_header, "LFW + EC_RO encryption_key_header") ec_info_block = GenEcInfoBlock(args, chip_dict) printByteArrayAsHex(ec_info_block, "EC Info Block") cosignature = GenCoSignature(args, chip_dict, lfw_ecro) printByteArrayAsHex(cosignature, "LFW + EC_RO cosignature") trailer = GenTrailer(args, chip_dict, lfw_ecro, encryption_key_header, ec_info_block, cosignature) printByteArrayAsHex(trailer, "LFW + EC_RO trailer") # Build TAG0. Set TAG1=TAG0 Boot-ROM is allowed to load EC-RO only. tag0 = BuildTag(args) tag1 = tag0 debug_print("Call to GetPayloadFromOffset") debug_print("args.input = ", args.input) debug_print("args.image_size = ", hex(args.image_size)) ecrw = GetPayloadFromOffset(args.input, args.image_size, chip_dict["PAD_SIZE"]) debug_print("type(ecrw) is ", type(ecrw)) debug_print("len(ecrw) is ", hex(len(ecrw))) # truncate to args.image_size ecrw_len = len(ecrw) if ecrw_len > args.image_size: debug_print("Truncate EC_RW len={0:0x} to image_size={1:0x}".format(ecrw_len,args.image_size)) ecrw = ecrw[:args.image_size] ecrw_len = len(ecrw) debug_print("len(EC_RW) = ", hex(ecrw_len)) # SPI test mode compute CRC32 of EC_RW and store in last 4 bytes if args.test_spi: crc32_ecrw = zlib.crc32(bytes(ecrw[0:-4])) crc32_ecrw_bytes = crc32_ecrw.to_bytes(4, byteorder='little') ecrw[-4:] = crc32_ecrw_bytes debug_print("ecrw len = ", hex(len(ecrw))) debug_print("CRC32(ecrw) = ", hex(crc32_ecrw)) # Assume FW layout is standard Cortex-M style with vector # table at start of binary. # 32-bit word at offset 0x0 = Initial stack pointer # 32-bit word at offset 0x4 = Address of reset handler ecrw_entry_tuple = struct.unpack_from('= 0: rw_offset = args.rw_loc debug_print("rw_offset = 0x{0:08x}".format(rw_offset)) #spi_list.append((rw_offset, ecrw, "ecrw")) spi_list_append(spi_list, rw_offset, ecrw, "EC_RW") offset = rw_offset + len(ecrw) spi_list = sorted(spi_list) debug_print("Display spi_list:") dumpsects(spi_list) # # MEC172x Boot-ROM locates TAG0/1 at SPI offset 0 # instead of end of SPI. # with open(args.output, 'wb') as f: debug_print("Write spi list to file", args.output) addr = 0 for s in spi_list: if addr < s[0]: debug_print("Offset ",hex(addr)," Length", hex(s[0]-addr), "fill with 0xff") f.write(b'\xff' * (s[0] - addr)) addr = s[0] debug_print("Offset ",hex(addr), " Length", hex(len(s[1])), "write data") f.write(s[1]) addr += len(s[1]) if addr < spi_size: debug_print("Offset ",hex(addr), " Length", hex(spi_size - addr), "fill with 0xff") f.write(b'\xff' * (spi_size - addr)) f.flush() if __name__ == '__main__': main()