diff options
Diffstat (limited to 'chip/mchp/util')
-rwxr-xr-x | chip/mchp/util/pack_ec.py | 536 | ||||
-rwxr-xr-x | chip/mchp/util/pack_ec_mec152x.py | 803 | ||||
-rwxr-xr-x | chip/mchp/util/pack_ec_mec172x.py | 851 |
3 files changed, 0 insertions, 2190 deletions
diff --git a/chip/mchp/util/pack_ec.py b/chip/mchp/util/pack_ec.py deleted file mode 100755 index 7908b0bf37..0000000000 --- a/chip/mchp/util/pack_ec.py +++ /dev/null @@ -1,536 +0,0 @@ -#!/usr/bin/env python3 - -# Copyright 2013 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 MEC17xx -# Based on MEC170x_ROM_Description.pdf DS00002225C (07-28-17). -import argparse -import hashlib -import os -import struct -import subprocess -import tempfile -import zlib # CRC32 - -# MEC1701 has 256KB SRAM from 0xE0000 - 0x120000 -# SRAM is divided into contiguous CODE & DATA -# CODE at [0xE0000, 0x117FFF] DATA at [0x118000, 0x11FFFF] -# SPI flash size for board is 512KB -# Boot-ROM TAG is located at SPI offset 0 (two 4-byte tags) -# - -LFW_SIZE = 0x1000 -LOAD_ADDR = 0x0E0000 -LOAD_ADDR_RW = 0xE1000 -HEADER_SIZE = 0x40 -SPI_CLOCK_LIST = [48, 24, 16, 12] -SPI_READ_CMD_LIST = [0x3, 0xb, 0x3b, 0x6b] - -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 - -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 struct.unpack('<I', s)[0] - -def GetPayloadFromOffset(payload_file, offset): - """Read payload and pad it to 64-byte aligned.""" - with open(payload_file, 'rb') as f: - f.seek(offset) - payload = bytearray(f.read()) - rem_len = len(payload) % 64 - if rem_len: - payload += b'\0' * (64 - rem_len) - return payload - -def GetPayload(payload_file): - """Read payload and pad it to 64-byte aligned.""" - return GetPayloadFromOffset(payload_file, 0) - -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('<Q', exp), modulus - -def GetSpiClockParameter(args): - assert args.spi_clock in SPI_CLOCK_LIST, \ - "Unsupported SPI clock speed %d MHz" % args.spi_clock - return SPI_CLOCK_LIST.index(args.spi_clock) - -def GetSpiReadCmdParameter(args): - assert args.spi_read_cmd in SPI_READ_CMD_LIST, \ - "Unsupported SPI read command 0x%x" % args.spi_read_cmd - return SPI_READ_CMD_LIST.index(args.spi_read_cmd) - -def PadZeroTo(data, size): - data.extend(b'\0' * (size - len(data))) - -def BuildHeader(args, payload_len, load_addr, rorofile): - # Identifier and header version - header = bytearray(b'PHCM\0') - - # byte[5] - b = GetSpiClockParameter(args) - b |= (1 << 2) - header.append(b) - - # byte[6] - b = 0 - header.append(b) - - # byte[7] - header.append(GetSpiReadCmdParameter(args)) - - # bytes 0x08 - 0x0b - header.extend(struct.pack('<I', load_addr)) - # bytes 0x0c - 0x0f - header.extend(struct.pack('<I', GetEntryPoint(rorofile))) - # bytes 0x10 - 0x13 - header.append((payload_len >> 6) & 0xff) - header.append((payload_len >> 14) & 0xff) - PadZeroTo(header, 0x14) - # bytes 0x14 - 0x17 - header.extend(struct.pack('<I', args.payload_offset)) - - # bytes 0x14 - 0x3F all 0 - PadZeroTo(header, 0x40) - - # header signature is appended by the caller - - return header - - -def BuildHeader2(args, payload_len, load_addr, payload_entry): - # Identifier and header version - header = bytearray(b'PHCM\0') - - # byte[5] - b = GetSpiClockParameter(args) - b |= (1 << 2) - header.append(b) - - # byte[6] - b = 0 - header.append(b) - - # byte[7] - header.append(GetSpiReadCmdParameter(args)) - - # bytes 0x08 - 0x0b - header.extend(struct.pack('<I', load_addr)) - # bytes 0x0c - 0x0f - header.extend(struct.pack('<I', payload_entry)) - # bytes 0x10 - 0x13 - header.append((payload_len >> 6) & 0xff) - header.append((payload_len >> 14) & 0xff) - PadZeroTo(header, 0x14) - # bytes 0x14 - 0x17 - header.extend(struct.pack('<I', args.payload_offset)) - - # bytes 0x14 - 0x3F all 0 - PadZeroTo(header, 0x40) - - # header signature is appended by the caller - - return header - -# -# Compute SHA-256 of data and return digest -# as a bytearray -# -def HashByteArray(data): - hasher = hashlib.sha256() - hasher.update(data) - h = hasher.digest() - bah = bytearray(h) - return bah - -# -# Return 64-byte signature of byte array data. -# Signature is SHA256 of data with 32 0 bytes appended -# -def SignByteArray(data): - debug_print("Signature is SHA-256 of data") - sigb = HashByteArray(data) - sigb.extend(b'\0' * 32) - return sigb - - -# MEC1701H 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 - - -# -# 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("<L", pldrw[cookie1_pos+0x24:cookie1_pos+0x28]) - size = t[0] - debug_print("EC_RW size =", size, " = ", hex(size)) - - debug_print("Found cookie1 at ", hex(cookie1_pos)) - debug_print("Found cookie2 at ", hex(cookie2_pos)) - - if cookie1_pos > 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("-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", - default=0x1000) - parser.add_argument("-p", "--payload_offset", type=int, - help="The offset of payload from the start of header", - default=0x80) - 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, or 0x3B.", - default=0xb) - parser.add_argument("--image_size", type=int, - help="Size of a single image. Default 220KB", - default=(220 * 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) - - return parser.parse_args() - -# 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,"= ") - 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("\n") - -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(".header_loc = ", hex(args.header_loc)) - debug_print(".payload_offset = ", hex(args.payload_offset)) - 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 = ", args.spi_clock) - debug_print(".spi_read_cmd = ", args.spi_read_cmd) - debug_print(".test_spi = ", args.test_spi) - debug_print(".verbose = ", args.verbose) - -# -# Handle quiet mode build from Makefile -# Quiet mode when V is unset or V=0 -# Verbose mode when V=1 -# -def main(): - global debug_print - - args = parseargs() - - if args.verbose: - debug_print = print - - debug_print("Begin MEC17xx pack_ec.py script") - - - # MEC17xx maximum 192KB each for RO & RW - # mec1701 chip Makefile sets args.spi_size = 512 - # Tags at offset 0 - # - print_args(args) - - spi_size = args.spi_size * 1024 - debug_print("SPI Flash image size in bytes =", hex(spi_size)) - - # !!! IMPORTANT !!! - # These values MUST match chip/mec1701/config_flash_layout.h - # defines. - # MEC17xx Boot-ROM TAGs are at offset 0 and 4. - # lfw + EC_RO starts at beginning of second 4KB sector - # EC_RW starts at offset 0x40000 (256KB) - - spi_list = [] - - debug_print("args.input = ",args.input) - debug_print("args.loader_file = ",args.loader_file) - debug_print("args.image_size = ",hex(args.image_size)) - - rorofile=PacklfwRoImage(args.input, args.loader_file, args.image_size) - - payload = GetPayload(rorofile) - payload_len = len(payload) - # debug - debug_print("EC_LFW + EC_RO length = ",hex(payload_len)) - - # SPI image integrity test - # compute CRC32 of EC_RO except for last 4 bytes - # skip over 4KB LFW - # Store CRC32 in last 4 bytes - if args.test_spi == True: - crc = zlib.crc32(bytes(payload[LFW_SIZE:(payload_len - 4)])) - crc_ofs = payload_len - 4 - debug_print("EC_RO CRC32 = 0x{0:08x} @ 0x{1:08x}".format(crc, crc_ofs)) - for i in range(4): - payload[crc_ofs + i] = crc & 0xff - crc = crc >> 8 - - # Chromebooks are not using MEC BootROM ECDSA. - # We implemented the ECDSA disabled case where - # the 64-byte signature contains a SHA-256 of the binary plus - # 32 zeros bytes. - payload_signature = SignByteArray(payload) - # debug - printByteArrayAsHex(payload_signature, "LFW + EC_RO payload_signature") - - # MEC17xx Header is 0x80 bytes with an 64 byte signature - # (32 byte SHA256 + 32 zero bytes) - header = BuildHeader(args, payload_len, LOAD_ADDR, rorofile) - # debug - printByteArrayAsHex(header, "Header LFW + EC_RO") - - # MEC17xx payload ECDSA not used, 64 byte signature is - # SHA256 + 32 zero bytes - header_signature = SignByteArray(header) - # debug - printByteArrayAsHex(header_signature, "header_signature") - - tag = BuildTag(args) - # MEC17xx truncate RW length to args.image_size to not overwrite LFW - # offset may be different due to Header size and other changes - # MCHP we want to append a SHA-256 to the end of the actual payload - # to test SPI read routines. - debug_print("Call to GetPayloadFromOffset") - debug_print("args.input = ", args.input) - debug_print("args.image_size = ", hex(args.image_size)) - - payload_rw = GetPayloadFromOffset(args.input, args.image_size) - debug_print("type(payload_rw) is ", type(payload_rw)) - debug_print("len(payload_rw) is ", hex(len(payload_rw))) - - # truncate to args.image_size - rw_len = args.image_size - payload_rw = payload_rw[:rw_len] - payload_rw_len = len(payload_rw) - debug_print("Truncated size of EC_RW = ", hex(payload_rw_len)) - - payload_entry_tuple = struct.unpack_from('<I', payload_rw, 4) - debug_print("payload_entry_tuple = ", payload_entry_tuple) - - payload_entry = payload_entry_tuple[0] - debug_print("payload_entry = ", hex(payload_entry)) - - # Note: payload_rw is a bytearray therefore is mutable - if args.test_ecrw: - gen_test_ecrw(payload_rw) - - # SPI image integrity test - # compute CRC32 of EC_RW except for last 4 bytes - # Store CRC32 in last 4 bytes - if args.test_spi == True: - crc = zlib.crc32(bytes(payload_rw[:(payload_rw_len - 32)])) - crc_ofs = payload_rw_len - 4 - debug_print("EC_RW CRC32 = 0x{0:08x} at offset 0x{1:08x}".format(crc, crc_ofs)) - for i in range(4): - payload_rw[crc_ofs + i] = crc & 0xff - crc = crc >> 8 - - payload_rw_sig = SignByteArray(payload_rw) - # debug - printByteArrayAsHex(payload_rw_sig, "payload_rw_sig") - - os.remove(rorofile) # clean up the temp file - - # MEC170x Boot-ROM Tags are located at SPI offset 0 - spi_list.append((0, tag, "tag")) - - spi_list.append((args.header_loc, header, "header(lwf + ro)")) - spi_list.append((args.header_loc + HEADER_SIZE, header_signature, - "header(lwf + ro) signature")) - spi_list.append((args.header_loc + args.payload_offset, payload, - "payload(lfw + ro)")) - - offset = args.header_loc + args.payload_offset + payload_len - - # No SPI Header for EC_RW as its not loaded by BootROM - spi_list.append((offset, payload_signature, - "payload(lfw_ro) signature")) - - # EC_RW location - rw_offset = int(spi_size // 2) - if args.rw_loc >= 0: - rw_offset = args.rw_loc - - debug_print("rw_offset = 0x{0:08x}".format(rw_offset)) - - if rw_offset < offset + len(payload_signature): - print("ERROR: EC_RW overlaps EC_RO") - - spi_list.append((rw_offset, payload_rw, "payload(rw)")) - - # don't add to EC_RW. We don't know if Google will process - # EC SPI flash binary with other tools during build of - # coreboot and OS. - #offset = rw_offset + payload_rw_len - #spi_list.append((offset, payload_rw_sig, "payload(rw) signature")) - - spi_list = sorted(spi_list) - - dumpsects(spi_list) - - # - # MEC17xx Boot-ROM locates TAG 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() diff --git a/chip/mchp/util/pack_ec_mec152x.py b/chip/mchp/util/pack_ec_mec152x.py deleted file mode 100755 index 34846cd6ba..0000000000 --- a/chip/mchp/util/pack_ec_mec152x.py +++ /dev/null @@ -1,803 +0,0 @@ -#!/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 MEC152x -# Based on MEC1521/MEC1523_ROM_Description.pdf -import argparse -import hashlib -import os -import struct -import subprocess -import tempfile -import zlib # CRC32 - -# MEC152xH has 256KB SRAM from 0xE0000 - 0x120000 -# SRAM is divided into contiguous CODE & DATA -# CODE at [0xE0000, 0x117FFF] DATA at [0x118000, 0x11FFFF] -# SPI flash size for board is 512KB -# Boot-ROM TAG is located at SPI offset 0 (two 4-byte tags) - -LFW_SIZE = 0x1000 -LOAD_ADDR = 0x0E0000 -LOAD_ADDR_RW = 0xE1000 -MEC152X_HEADER_SIZE = 0x140 -MEC152X_HEADER_VERSION = 0x02 -PAYLOAD_PAD_BYTE = b'\xff' -SPI_ERASE_BLOCK_SIZE = 0x1000 -SPI_CLOCK_LIST = [48, 24, 16, 12] -SPI_READ_CMD_LIST = [0x3, 0xb, 0x3b, 0x6b] -SPI_DRIVE_STR_DICT = {2:0, 4:1, 8:2, 12:3} -CHIP_MAX_CODE_SRAM_KB = 224 - -MEC152X_DICT = { - "HEADER_SIZE":0x140, - "HEADER_VER":0x02, - "PAYLOAD_OFFSET":0x140, - "PAYLOAD_GRANULARITY":128, - "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('<Q', exp), modulus - -def GetSpiClockParameter(args): - assert args.spi_clock in SPI_CLOCK_LIST, \ - "Unsupported SPI clock speed %d MHz" % args.spi_clock - return SPI_CLOCK_LIST.index(args.spi_clock) - -def GetSpiReadCmdParameter(args): - assert args.spi_read_cmd in SPI_READ_CMD_LIST, \ - "Unsupported SPI read command 0x%x" % args.spi_read_cmd - return SPI_READ_CMD_LIST.index(args.spi_read_cmd) - -def GetEncodedSpiDriveStrength(args): - assert args.spi_drive_str in SPI_DRIVE_STR_DICT, \ - "Unsupported SPI drive strength %d mA" % args.spi_drive_str - return SPI_DRIVE_STR_DICT.get(args.spi_drive_str) - -# Return 0=Slow slew rate or 1=Fast slew rate -def GetSpiSlewRate(args): - if args.spi_slew_fast == True: - return 1 - return 0 - -# Return SPI CPOL = 0 or 1 -def GetSpiCpol(args): - if args.spi_cpol == 0: - return 0 - return 1 - -# Return SPI CPHA_MOSI -# 0 = SPI Master drives data is stable on inactive to clock edge -# 1 = SPI Master drives data is stable on active to inactive clock edge -def GetSpiCphaMosi(args): - if args.spi_cpha_mosi == 0: - return 0 - return 1 - -# Return SPI CPHA_MISO 0 or 1 -# 0 = SPI Master samples data on inactive to active clock edge -# 1 = SPI Master samples data on active to inactive clock edge -def GetSpiCphaMiso(args): - if args.spi_cpha_miso == 0: - return 0 - return 1 - -def PadZeroTo(data, size): - data.extend(b'\0' * (size - len(data))) - -# -# Boot-ROM SPI image encryption not used with Chromebooks -# -def EncryptPayload(args, chip_dict, payload): - return None - -# -# Build SPI image header for MEC152x -# MEC152x image header size = 320(0x140) bytes -# -# Description using Python slice notation [start:start+len] -# -# header[0:4] = 'PHCM' -# header[4] = header version = 0x02(MEC152x) -# header[5] = SPI clock speed, drive strength, sampling mode -# bits[1:0] = SPI clock speed: 0=48, 1=24, 2=16, 3=12 -# bits[3:2] = SPI controller pins drive strength -# 00b=2mA, 01b=4mA, 10b=8mA, 11b=12mA -# bit[4] = SPI controller pins slew rate: 0=slow, 1=fast -# bit[5] = SPI CPOL: 0=SPI clock idle is low, 1=idle is high -# bit[6] = CHPHA_MOSI -# 1:data change on first inactive to active clock edge -# 0:data change on first active to inactive clock edge -# bit[7] = CHPHA_MISO: -# 1: Data captured on first inactive to active clock edge -# 0: Data captured on first active to inactive clock edge -# header[6] Boot-ROM loader flags -# bits[2:0] = VTR0,1,2 rails. 0=3.3V, 1=1.8V. NOTE VTR1=0 always -# bits[5:3] = 111b -# bit[6]: For MEC152x controls authentication -# 0=Authentication disabled. Signature is SHA-384 of FW payload -# 1=Authentication enabled. Signature is ECDSA P-384 -# bit[7]: 0=FW pyload not encrypted, 1=FW payload is encrypted -# header[7]: SPI Flash read command -# 0x03 1-1-1 read freq < 33MHz -# 0x0B 1-1-1 + 8 clocks(data tri-stated) -# 0x3B 1-1-2 + 8 clocks(data tri-stated). Data phase is dual I/O -# 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(MEC152x) -# header[0x12:0x14] = 0 reserved -# header[0x14:0x18] = Little-ending format: Unsigned offset from start of -# header to FW payload. -# MEC152x: Offset must be a multiple of 128 -# Offset must be > header size. -# NOTE: If Authentication is enabled size includes -# the appended signature. -# MEC152x: -# header[0x18] = Authentication key select. Set to 0 for no Authentication. -# header[0x19:0x50] = 0 reserved. -# 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 = MEC152X_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] = MEC152X_HEADER_VERSION - - # SPI frequency, drive strength, CPOL/CPHA encoding same for both chips - spiFreqMHz = GetSpiClockParameter(args) - header[5] = (int(spiFreqMHz // 48) - 1) & 0x03 - 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) - - # b[0]=0 VTR1 must be 3.3V - # b[1]=0(VTR2 3.3V), 1(VTR2 1.8V) - # b[2]=0(VTR3 3.3V), 1(VTR3 1.8V) - # b[5:3]=111b - # b[6]=0 No ECDSA - # b[7]=0 No encrypted FW image - header[6] = 0x7 << 3 - if args.vtr2_V18 == True: - header[6] |= 0x02 - if args.vtr3_V18 == True: - header[6] |= 0x04 - - # 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 - - 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 - header[0x14:0x18] = chip_dict["PAYLOAD_OFFSET"].to_bytes(4, 'little') - - # MEC152x: Disable ECDSA and encryption - header[0x18] = 0 - - # 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 - -# -# MEC152x 128-byte EC Info Block appended to -# end of padded FW binary -# bytes 0 through 103 are undefined, we set to 0xFF -# bytes 104 through 119 are rollback permissions -# bytes 120 through 123 are key revocation permissions -# byte 124 = customer platform ID[7:0] -# byte 125 = customer platform ID[15:8] -# byte 126 = customer auto rollback flags -# byte 127 = customer current image revision -# -def GenEcInfoBlock(args, chip_dict): - ecinfo = bytearray(chip_dict["EC_INFO_BLK_SZ"]) - return ecinfo - -# -# Generate SPI FW image co-signature. -# MEC152X 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. -# MEC152X: 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): - trailer = bytearray(chip_dict["TAILER_PAD_BYTE"] * chip_dict["TRAILER_SZ"]) - hasher = hashlib.sha384() - hasher.update(payload) - if ec_info_block != None: - hasher.update(ec_info_block) - if encryption_key_header != None: - hasher.update(encryption_key_header) - if cosignature != None: - hasher.update(cosignature) - trailer[0:48] = hasher.digest() - trailer[-16:] = 16 * b'\xff' - - return trailer - -# MEC152xH 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 MEC152x -# 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("<L", pldrw[cookie1_pos+0x24:cookie1_pos+0x28]) - size = t[0] - debug_print("EC_RW size =", size, " = ", hex(size)) - - debug_print("Found cookie1 at ", hex(cookie1_pos)) - debug_print("Found cookie2 at ", hex(cookie2_pos)) - - if cookie1_pos > 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(): - #TODO I commented this out. Why? - 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("-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", - 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, or 0x3B.", - default=0xb) - parser.add_argument("--image_size", type=int, - help="Size of a single image. Default 220KB", - default=(220 * 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="MEC152X TAG0 SPI offset", - default=0) - parser.add_argument("--tag1_loc", type=int, - help="MEC152X 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 master 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 master samples data. - 0=Data sampled on inactive to active clock edge, - 1=Data sampled on active to inactive clock edge""", - default=0) - - parser.add_argument("--vtr2_V18", action='store_true', - help="Chip VTR2 rail is 1.8V. Default is False(3.3V)", - default=False) - - parser.add_argument("--vtr3_V18", action='store_true', - help="Chip VTR3 rail is 1.8V. Default is False(3.3V)", - default=False) - - 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(".tag0_loc = ", hex(args.tag0_loc)) - debug_print(".tag1_loc = ", hex(args.tag1_loc)) - debug_print(".header_loc = ", hex(args.header_loc)) - 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) - debug_print(".vtr2_V18 = ", args.vtr2_V18) - debug_print(".vtr3_V18 = ", args.vtr3_V18) - -# -# Handle quiet mode build from Makefile -# Quiet mode when V is unset or V=0 -# Verbose mode when V=1 -# -# MEC152x 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]. -# -# MEC152X 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_mec152x.py script") - - print_args(args) - - chip_dict = MEC152X_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. - # - assert (args.header_loc % SPI_ERASE_BLOCK_SIZE) == 0, \ - "Header location %d is not on a flash erase block boundary boundary" % args.header_loc - - max_image_size = CHIP_MAX_CODE_SRAM_KB - LFW_SIZE - if args.test_spi: - max_image_size -= 32 # SHA256 digest - - assert args.image_size > max_image_size, \ - "Image size exceeds maximum" % args.image_size - - spi_size = args.spi_size * 1024 - debug_print("SPI Flash image size in bytes =", hex(spi_size)) - - # !!! IMPORTANT !!! - # These values MUST match chip/mchp/config_flash_layout.h - # defines. - # MEC152x Boot-ROM TAGs are at offset 0 and 4. - # lfw + EC_RO starts at beginning of second 4KB sector - # EC_RW starts at (flash size / 2) i.e. 0x40000 for a 512KB flash. - - spi_list = [] - - debug_print("args.input = ",args.input) - debug_print("args.loader_file = ",args.loader_file) - debug_print("args.image_size = ",hex(args.image_size)) - - 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) - - # 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, - 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('<I', ecrw, 4) - debug_print("ecrw_entry_tuple[0] = ", hex(ecrw_entry_tuple[0])) - - ecrw_entry = ecrw_entry_tuple[0] - debug_print("ecrw_entry = ", hex(ecrw_entry)) - - # Note: payload_rw is a bytearray therefore is mutable - if args.test_ecrw: - gen_test_ecrw(ecrw) - - os.remove(rorofile) # clean up the temp file - - # MEC152X Add TAG's - spi_list.append((args.tag0_loc, tag0, "tag0")) - spi_list.append((args.tag1_loc, tag1, "tag1")) - - # flashmap is non-zero only for systems with two external - # SPI flash chips. - flashmap = BuildFlashMap(0) - spi_list.append((8, flashmap, "flashmap")) - - # Boot-ROM SPI image header for LFW+EC-RO - spi_list.append((args.header_loc, header, "header(lfw + ro)")) - spi_list.append((args.header_loc + chip_dict["PAYLOAD_OFFSET"], lfw_ecro, - "lfw_ecro")) - - offset = args.header_loc + chip_dict["PAYLOAD_OFFSET"] + lfw_ecro_len - - if ec_info_block != None: - spi_list.append((offset, ec_info_block, "EC Info Block")) - offset += len(ec_info_block) - - if cosignature != None: - spi_list.append((offset, cosignature, "ECRO Cosignature")) - offset += len(cosignature) - - if trailer != None: - spi_list.append((offset, trailer, "ECRO Trailer")) - offset += len(trailer) - - # EC_RW location - rw_offset = int(spi_size // 2) - if args.rw_loc >= 0: - rw_offset = args.rw_loc - - debug_print("rw_offset = 0x{0:08x}".format(rw_offset)) - - assert rw_offset >= offset, \ - print("""Offset of EC_RW at {0:08x} overlaps end - of EC_RO at {0:08x}""".format(rw_offset, offset)) - - spi_list.append((rw_offset, ecrw, "ecrw")) - offset = rw_offset + len(ecrw) - - spi_list = sorted(spi_list) - - dumpsects(spi_list) - - # - # MEC152X 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() diff --git a/chip/mchp/util/pack_ec_mec172x.py b/chip/mchp/util/pack_ec_mec172x.py deleted file mode 100755 index 32747d3d9a..0000000000 --- a/chip/mchp/util/pack_ec_mec172x.py +++ /dev/null @@ -1,851 +0,0 @@ -#!/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('<Q', exp), modulus - -def GetSpiClockParameter(args): - assert args.spi_clock in SPI_CLOCK_LIST, \ - "Unsupported SPI clock speed %d MHz" % args.spi_clock - return SPI_CLOCK_LIST.index(args.spi_clock) - -def GetSpiReadCmdParameter(args): - assert args.spi_read_cmd in SPI_READ_CMD_LIST, \ - "Unsupported SPI read command 0x%x" % args.spi_read_cmd - return SPI_READ_CMD_LIST.index(args.spi_read_cmd) - -def GetEncodedSpiDriveStrength(args): - assert args.spi_drive_str in SPI_DRIVE_STR_DICT, \ - "Unsupported SPI drive strength %d mA" % args.spi_drive_str - return SPI_DRIVE_STR_DICT.get(args.spi_drive_str) - -# Return 0=Slow slew rate or 1=Fast slew rate -def GetSpiSlewRate(args): - if args.spi_slew_fast == True: - return 1 - return 0 - -# Return SPI CPOL = 0 or 1 -def GetSpiCpol(args): - if args.spi_cpol == 0: - return 0 - return 1 - -# Return SPI CPHA_MOSI -# 0 = SPI Master drives data is stable on inactive to clock edge -# 1 = SPI Master drives data is stable on active to inactive clock edge -def GetSpiCphaMosi(args): - if args.spi_cpha_mosi == 0: - return 0 - return 1 - -# Return SPI CPHA_MISO 0 or 1 -# 0 = SPI Master samples data on inactive to active clock edge -# 1 = SPI Master samples data on active to inactive clock edge -def GetSpiCphaMiso(args): - if args.spi_cpha_miso == 0: - return 0 - return 1 - -def PadZeroTo(data, size): - data.extend(b'\0' * (size - len(data))) - -# -# Boot-ROM SPI image encryption not used with Chromebooks -# -def EncryptPayload(args, chip_dict, payload): - return None - -# -# Build SPI image header for MEC172x -# MEC172x image header size = 320(0x140) bytes -# -# Description using Python slice notation [start:start+len] -# -# header[0:4] = 'PHCM' -# header[4] = header version = 0x03(MEC172x) -# header[5] = SPI clock speed, drive strength, sampling mode -# bits[1:0] = SPI clock speed: 0=48, 1=24, 2=16, 3=12 -# bits[3:2] = SPI controller pins drive strength -# 00b=2mA, 01b=4mA, 10b=8mA, 11b=12mA -# bit[4] = SPI controller pins slew rate: 0=slow, 1=fast -# bit[5] = SPI CPOL: 0=SPI clock idle is low, 1=idle is high -# bit[6] = CHPHA_MOSI -# 1:data change on first inactive to active clock edge -# 0:data change on first active to inactive clock edge -# bit[7] = CHPHA_MISO: -# 1: Data captured on first inactive to active clock edge -# 0: Data captured on first active to inactive clock edge -# header[6] Boot-ROM loader flags -# bits[0] = 0(use header[5] b[1:0]), 1(96 MHz) -# bits[2:1] = 0 reserved -# bits[5:3] = 111b -# bit[6]: For MEC172x controls authentication -# 0=Authentication disabled. Signature is SHA-384 of FW payload -# 1=Authentication enabled. Signature is ECDSA P-384 -# bit[7]: 0=FW pyload not encrypted, 1=FW payload is encrypted -# header[7]: SPI Flash read command -# 0 -> 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("<L", pldrw[cookie1_pos+0x24:cookie1_pos+0x28]) - size = t[0] - debug_print("EC_RW size =", size, " = ", hex(size)) - - debug_print("Found cookie1 at ", hex(cookie1_pos)) - debug_print("Found cookie2 at ", hex(cookie2_pos)) - - if cookie1_pos > 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('<I', ecrw, 4) - debug_print("ecrw_entry_tuple[0] = ", hex(ecrw_entry_tuple[0])) - - ecrw_entry = ecrw_entry_tuple[0] - debug_print("ecrw_entry = ", hex(ecrw_entry)) - - # Note: payload_rw is a bytearray therefore is mutable - if args.test_ecrw: - gen_test_ecrw(ecrw) - - os.remove(rorofile) # clean up the temp file - - spi_list = [] - - # MEC172x Add TAG's - #spi_list.append((args.tag0_loc, tag0, "tag0")) - #spi_list.append((args.tag1_loc, tag1, "tag1")) - spi_list_append(spi_list, args.tag0_loc, tag0, "TAG0") - spi_list_append(spi_list, args.tag1_loc, tag1, "TAG1") - - # Boot-ROM SPI image header for LFW+EC-RO - #spi_list.append((args.header_loc, header, "header(lfw + ro)")) - spi_list_append(spi_list, args.header_loc, header, "LFW-EC_RO Header") - - spi_list_append(spi_list, args.lfw_loc, lfw_ecro, "LFW-EC_RO FW") - - offset = args.lfw_loc + len(lfw_ecro) - debug_print("SPI offset after LFW_ECRO = 0x{0:08x}".format(offset)) - - if ec_info_block != None: - spi_list_append(spi_list, offset, ec_info_block, "LFW-EC_RO Info Block") - offset += len(ec_info_block) - - debug_print("SPI offset after ec_info_block = 0x{0:08x}".format(offset)) - - if cosignature != None: - #spi_list.append((offset, co-signature, "ECRO Co-signature")) - spi_list_append(spi_list, offset, cosignature, "LFW-EC_RO Co-signature") - offset += len(cosignature) - - debug_print("SPI offset after co-signature = 0x{0:08x}".format(offset)) - - if trailer != None: - #spi_list.append((offset, trailer, "ECRO Trailer")) - spi_list_append(spi_list, offset, trailer, "LFW-EC_RO trailer") - offset += len(trailer) - - debug_print("SPI offset after trailer = 0x{0:08x}".format(offset)) - - # EC_RW location - rw_offset = int(spi_size // 2) - if args.rw_loc >= 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() |