summaryrefslogtreecommitdiff
path: root/chip/mchp
diff options
context:
space:
mode:
authorScott Worley <scott.worley@microchip.corp-partner.google.com>2021-03-10 14:18:25 -0500
committerCommit Bot <commit-bot@chromium.org>2021-03-30 06:01:10 +0000
commit6954cd5df6c11d0d6b2c92d2c071fd78e4bbc573 (patch)
tree55fb2357f79f7841e69080eb304896e3bef3fc07 /chip/mchp
parentba480aa4d17833c5dc88ffa3eafe650db705088a (diff)
downloadchrome-ec-6954cd5df6c11d0d6b2c92d2c071fd78e4bbc573.tar.gz
mchp: Add MEC172x build files
Update chip build rules for MEC172x. Add a new little-firmware (LFW) linker file supporting the larger SRAM size of MEC172x. Add a new python SPI image generator for MEC172x new SPI layout (no flash-map). BRANCH=none BUG=none TEST=Build MEC170x and MEC152x boards Signed-off-by: Scott Worley <scott.worley@microchip.corp-partner.google.com> Change-Id: I9ff2302ca99d4e7296acdb8352849f5cd355adf0 Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/2749674 Commit-Queue: Aseda Aboagye <aaboagye@chromium.org> Reviewed-by: Martin Yan <martin.yan@microchip.corp-partner.google.com> Reviewed-by: Aseda Aboagye <aaboagye@chromium.org> Reviewed-by: Ravin Kumar <ravin.kumar@microchip.com> Reviewed-by: Vijay P Hiremath <vijay.p.hiremath@intel.com> Tested-by: Martin Yan <martin.yan@microchip.corp-partner.google.com>
Diffstat (limited to 'chip/mchp')
-rw-r--r--chip/mchp/build.mk13
-rw-r--r--chip/mchp/lfw/ec_lfw_416kb.ld89
-rwxr-xr-xchip/mchp/util/pack_ec_mec172x.py847
3 files changed, 949 insertions, 0 deletions
diff --git a/chip/mchp/build.mk b/chip/mchp/build.mk
index 2d8edede13..506d0d3470 100644
--- a/chip/mchp/build.mk
+++ b/chip/mchp/build.mk
@@ -63,6 +63,9 @@ PACK_EC=pack_ec.py
ifeq ($(CHIP_FAMILY),mec152x)
PACK_EC=pack_ec_mec152x.py
endif
+ifeq ($(CHIP_FAMILY),mec172x)
+ PACK_EC=pack_ec_mec172x.py
+endif
# pack_ec.py creates SPI flash image for MEC
# _rw_size is CONFIG_RW_SIZE
@@ -97,6 +100,15 @@ $(out)/RW/%-lfw.o: %.c
$(call quiet,c_to_o,CC )
# let lfw's elf link only with selected objects
+ifeq ($(CHIP_FAMILY),mec172x)
+$(out)/RW/%-lfw.elf: private objs = $(objs_lfw)
+$(out)/RW/%-lfw.elf: override shlib :=
+$(out)/RW/%-lfw.elf: %_416kb.ld $(objs_lfw)
+ $(call quiet,elf,LD )
+
+# final image needs lfw loader
+$(out)/$(PROJECT).bin: $(chip-lfw-flat)
+else
$(out)/RW/%-lfw.elf: private objs = $(objs_lfw)
$(out)/RW/%-lfw.elf: override shlib :=
$(out)/RW/%-lfw.elf: %.ld $(objs_lfw)
@@ -104,3 +116,4 @@ $(out)/RW/%-lfw.elf: %.ld $(objs_lfw)
# final image needs lfw loader
$(out)/$(PROJECT).bin: $(chip-lfw-flat)
+endif
diff --git a/chip/mchp/lfw/ec_lfw_416kb.ld b/chip/mchp/lfw/ec_lfw_416kb.ld
new file mode 100644
index 0000000000..97be2fe06a
--- /dev/null
+++ b/chip/mchp/lfw/ec_lfw_416kb.ld
@@ -0,0 +1,89 @@
+/* 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.
+ *
+ * MCHP MEC parts with 416KB SRAM SoC little FW
+ *
+ */
+
+/*
+ * Memory Spaces Definitions
+ * LFW occupies first 4KB of CODE SRAM.
+ * First 24 bytes contain a minimal Cortex-M4
+ * vector table.
+ */
+MEMORY
+{
+ VECTOR(r ) : ORIGIN = 0x0C0000, LENGTH = 0x18
+ SRAM (xrw) : ORIGIN = 0x0C0018, LENGTH = 0xFE8
+}
+
+/*
+ * ld does not allow mathematical expressions in ORIGIN/LENGTH, so check the
+ * values here.
+ */
+ASSERT(ORIGIN(VECTOR) + LENGTH(VECTOR) == ORIGIN(SRAM), "Invalid SRAM origin.")
+ASSERT(LENGTH(VECTOR) + LENGTH(SRAM) == 0x1000, "Invalid VECTOR+SRAM length.")
+
+/*
+ * The entry point is informative, for debuggers and simulators,
+ * since the Cortex-M vector points to it anyway.
+ */
+ENTRY(lfw_main)
+
+/*
+ * MEC172xN has 416KB total SRAM: 352KB CODE 64KB DATA
+ * CODE: 0x0C0000 - 0x117FFF
+ * DATA: 0x118000 - 0x127FFF
+ * Boot-ROM log is 0x11FF00 - 0x11FFFF
+ * MEC172x Top 1KB is not cleared if OTP customer flag enabled.
+ * !!! TODO !!! Does presence of PUF feature move customer area?
+ * Boot-ROM spec states 3.5KB from top is lost.
+ * 0x12_7800 - 0x12_7fff 2KB used by PUF option
+ * 0x12_7400 - 0x12_77ff 1KB Customer use. Not cleared by Boot-ROM
+ * 0x12_7200 - 0x12_73ff 512 byte Boot-ROM log
+ * CrOS EC puts panic data at Top of RAM.
+ * We must set Top of RAM to be customer region far enough to
+ * hold panic data.
+ * Set Top of SRAM to 0x12_7800.
+ * This requires size of SRAM = 0x127800 - 0x118000 = 0xF800 (62 KB)
+ */
+PROVIDE( lfw_stack_top = 0x127800 );
+
+/* Sections Definitions */
+
+SECTIONS
+{
+
+ /*
+ * The vector table goes first
+ */
+ .intvector :
+ {
+ . = ALIGN(4);
+ KEEP(*(.intvector))
+ } > VECTOR
+
+ /*
+ * The program code is stored in the .text section,
+ * which goes to FLASH.
+ */
+
+ .text :
+ {
+ *(.text .text.*) /* all remaining code */
+ *(.rodata .rodata.*) /* read-only data (constants) */
+ } >SRAM
+
+ . = ALIGN(4);
+
+ /* Padding */
+
+ .fill : {
+ FILL(0xFF);
+ . = ORIGIN(SRAM) + LENGTH(SRAM) - 1;
+ BYTE(0xFF); /* emit at least a byte to make linker happy */
+ }
+
+ __image_size = LOADADDR(.text) + SIZEOF(.text) - ORIGIN(VECTOR);
+}
diff --git a/chip/mchp/util/pack_ec_mec172x.py b/chip/mchp/util/pack_ec_mec172x.py
new file mode 100755
index 0000000000..2f4727c483
--- /dev/null
+++ b/chip/mchp/util/pack_ec_mec172x.py
@@ -0,0 +1,847 @@
+#!/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.
+
+# 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()