diff options
author | Iru Cai <mytbk920423@gmail.com> | 2020-05-13 10:00:28 +0800 |
---|---|---|
committer | Iru Cai <mytbk920423@gmail.com> | 2020-05-14 13:29:21 +0800 |
commit | a7683e92c5bf3ed62f473b903d8fdb4a4c22037d (patch) | |
tree | f0966318990dc2fe1ff3e0249dfdf2a59a5da928 | |
parent | 6b95507ec5b087658178a325bdc68570bc48bb20 (diff) | |
download | coreboot-mec1322.tar.xz |
util: add a tool to dump and insert MEC1322 firmwaremec1322
Refer to chip/mec1322/util/pack_ec.py in chromeec project for MEC1322
firmware format.
Tested on the backup files of the system and private flashes of HP
EliteBook 820 G1. This tool can dump the EC firmware from both flashes
and insert the EC firmware to the original ROM file and make the ROM
unchanged.
It needs to be tested on real hardware to see if a modified ROM with
a re-inserted EC firmware works.
Change-Id: I999bf0289216bf72c4f8f19182c7670d7008b5f9
Signed-off-by: Iru Cai <mytbk920423@gmail.com>
-rw-r--r-- | util/README.md | 1 | ||||
-rw-r--r-- | util/mec1322/Makefile | 21 | ||||
-rw-r--r-- | util/mec1322/mec1322.c | 302 |
3 files changed, 324 insertions, 0 deletions
diff --git a/util/README.md b/util/README.md index 8b05f6f729..672d1da342 100644 --- a/util/README.md +++ b/util/README.md @@ -63,6 +63,7 @@ embedded controller and insert them to the firmware image. `C` * __marvell__ - Add U-Boot boot loader for Marvell ARMADA38X `C` * __[me_cleaner](https://github.com/corna/me_cleaner)__ - Tool for partial deblobbing of Intel ME/TXE firmware images `Python` +* __mec1322__ - Dumps and inserts MEC1322 EC firmware `C` * __mma__ - Memory Margin Analysis automation tests `Bash` * __msrtool__ - Dumps chipset-specific MSR registers. `C` * __mtkheader__ - Generate MediaTek bootload header. `Python2` diff --git a/util/mec1322/Makefile b/util/mec1322/Makefile new file mode 100644 index 0000000000..4ccbe92b9e --- /dev/null +++ b/util/mec1322/Makefile @@ -0,0 +1,21 @@ +## This file is part of the coreboot project. +## +## SPDX-License-Identifier: GPL-2.0-or-later + +obj = mec1322 +HOSTCC := $(if $(shell type gcc 2>/dev/null),gcc,cc) + +ifeq ($(VERIFY_SIG),1) + CFLAGS += -DVERIFY_SIG + LIBS := -lcrypto +endif + +all: $(obj) + +%: %.c + $(HOSTCC) $(CFLAGS) -Wall -o $@ $< $(LIBS) + +clean: + rm -f $(obj) + +.PHONY: all clean diff --git a/util/mec1322/mec1322.c b/util/mec1322/mec1322.c new file mode 100644 index 0000000000..7ee83bb2b1 --- /dev/null +++ b/util/mec1322/mec1322.c @@ -0,0 +1,302 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include <stdio.h> +#include <string.h> +#include <stdint.h> +#include <stdlib.h> + +#ifdef VERIFY_SIG +#include <openssl/obj_mac.h> +#include <openssl/rsa.h> +#include <openssl/sha.h> +#endif + +void usage(void) +{ + fprintf(stderr, + "Usage:\n" + "\tmec1322 dump [-s <flash size>] [-o <outfile>] <infile>\n" + "\tmec1322 insert [-s <flash size>] [-f <image>] <infile> <header_loc>\n"); + + exit(1); +} + +const int SPI_CLOCK_LIST[] = {48, 24, 12, 8}; +const uint8_t SPI_READ_CMD_LIST[] = {0x3, 0xb, 0x3b}; + +// FIXME: uint32_t cannot be used directly on big endian systems +struct mec1322_header { + uint8_t sig[6]; // "CSMS\0\0" + uint8_t spi_clk; + uint8_t spi_read_cmd; + uint32_t load_addr; + uint32_t entry_point; + uint16_t payload_blks; // payload_len >> 6 + uint8_t padding0[2]; + uint32_t payload_offset; + uint8_t padding1[8]; + uint8_t payload_key_exp[16]; // @0x20 + uint8_t payload_key_modulus[256]; // @0x30 +}; + +uint8_t crc8(const uint8_t *data, size_t len) +{ + const uint8_t CRC_TABLE[] = {0x00, 0x07, 0x0e, 0x09, 0x1c, 0x1b, 0x12, 0x15, + 0x38, 0x3f, 0x36, 0x31, 0x24, 0x23, 0x2a, 0x2d}; + uint8_t crc = 0; + for (size_t i = 0; i < len; i++) { + uint8_t v = data[i]; + crc = (crc << 4) ^ (CRC_TABLE[(crc >> 4) ^ (v >> 4)]); + crc = (crc << 4) ^ (CRC_TABLE[(crc >> 4) ^ (v & 0xf)]); + } + return crc ^ 0x55; +} + +// return the total length of the firmware, including header, +// header_signature, and payload +// return 0 if there's error +size_t parse_header(const struct mec1322_header *hdr) +{ + if (memcmp(hdr->sig, "CSMS", 4) != 0) { + fprintf(stderr, "MEC1322 header signature error!\n"); + return 0; + } + + printf("SPI clock = %hhd MHz\n", SPI_CLOCK_LIST[hdr->spi_clk]); + printf("SPI read cmd = 0x%hhx\n", SPI_READ_CMD_LIST[hdr->spi_read_cmd]); + printf("load address = 0x%x\n", hdr->load_addr); + printf("entry point = 0x%x\n", hdr->entry_point); + size_t payload_len = (size_t)hdr->payload_blks * 64; + printf("payload length = 0x%lx\n", payload_len); + printf("payload offset = 0x%x\n", hdr->payload_offset); + return payload_len + hdr->payload_offset; +} + +int verify_payload_signature(const struct mec1322_header *hdr, void *payload, + const uint8_t *sig) +{ +#ifdef VERIFY_SIG + uint8_t hash[32]; + uint8_t sigrev[256]; + + for (size_t i = 0; i < 256; i++) + sigrev[i] = sig[255 - i]; + + RSA *rsa = RSA_new(); + BIGNUM *modulus = BN_lebin2bn(hdr->payload_key_modulus, 256, NULL); + BIGNUM *exp = BN_lebin2bn(hdr->payload_key_exp, 8, NULL); + RSA_set0_key(rsa, modulus, exp, NULL); + + SHA256(payload, (size_t)(hdr->payload_blks) * 64, hash); + + int v = RSA_verify(NID_sha256, hash, 32, sigrev, 256, rsa); + if (!v) + fprintf(stderr, "MEC1322 payload RSA signature error!\n"); + else + printf("MEC1322 payload RSA signature matches the payload.\n"); + + return v; +#else + fprintf(stderr, + "** MEC1322 payload signature verification is not enabled.\n" + "** You can run `make VERIFY_SIG=1` to enable it.\n"); + return 1; +#endif +} + +int mec1322_dump(int argc, char *argv[]) +{ + FILE *infile, *outfile = NULL; + long spisize = 0; + + while (argc > 2) { + if (strcmp(argv[0], "-s") == 0) { + spisize = strtol(argv[1], NULL, 0); + argc -= 2; + argv += 2; + continue; + } + if (strcmp(argv[0], "-o") == 0) { + outfile = fopen(argv[1], "w"); + if (outfile == NULL) { + fprintf(stderr, "Fail to open file %s!\n", argv[1]); + exit(1); + } + argc -= 2; + argv += 2; + continue; + } + // otherwise, print usage and exit + usage(); + } + + if (argc != 1) + usage(); + + // without the above options, it's the input file + infile = fopen(argv[0], "r"); + if (infile == NULL) { + fprintf(stderr, "Fail to open file %s!\n", argv[0]); + exit(1); + } + + if (spisize == 0) { + fseek(infile, -1, SEEK_END); + spisize = ftell(infile) + 1; + + printf("Input file size is 0x%lx.\n", spisize); + } + + // read MEC1322 firmware tag + fseek(infile, -256, SEEK_END); + uint8_t fwTag[4]; + uint32_t header_loc; + fread(fwTag, 1, 4, infile); + + if (crc8(fwTag, 3) == fwTag[3]) { + header_loc = (((uint32_t)fwTag[0]) << 8) | (((uint32_t)fwTag[1]) << 16) + | (((uint32_t)fwTag[2]) << 24); + printf("MEC1322 firmware header location is 0x%x.\n", header_loc); + } else { + fprintf(stderr, "MEC1322 tag CRC8 error!\n"); + return 1; + } + + // we use the distance of header_loc to end when seeking + struct mec1322_header hdr; + fseek(infile, header_loc - spisize, SEEK_END); + fread(&hdr, 1, sizeof(hdr), infile); + size_t fwlen = parse_header(&hdr); + if (fwlen == 0) { + fprintf(stderr, "Error parsing MEC1322 firmware header!\n"); + return 1; + } + + if (outfile) { + fseek(infile, header_loc - spisize, SEEK_END); + void *image = malloc(fwlen); + fread(image, 1, fwlen, infile); + fwrite(image, 1, fwlen, outfile); + + uint8_t rsasig[256]; + fread(rsasig, 1, 256, infile); + fwrite(rsasig, 1, 256, outfile); + + if (!verify_payload_signature(&hdr, image + hdr.payload_offset, rsasig)) + return 1; + + free(image); + fclose(outfile); + } + + fclose(infile); + return 0; +} + +int mec1322_insert(int argc, char *argv[]) +{ + FILE *infile, *ecfile = NULL; + void *image = NULL; + size_t fwlen; + long spisize = 0; + long header_loc; + + if (argc < 2) + usage(); + + while (argc > 2) { + if (strcmp(argv[0], "-s") == 0) { + spisize = strtol(argv[1], NULL, 0); + argc -= 2; + argv += 2; + continue; + } + if (strcmp(argv[0], "-f") == 0) { + ecfile = fopen(argv[1], "r"); + if (ecfile == NULL) { + fprintf(stderr, "Fail to open EC file %s!\n", argv[1]); + return 1; + } + argc -= 2; + argv += 2; + continue; + } + usage(); + } + + if (argc != 2) + usage(); + + infile = fopen(argv[0], "r+"); + header_loc = strtol(argv[1], NULL, 0); + + if (infile == NULL) { + fprintf(stderr, "Fail to open firmware image %s!\n", argv[1]); + return 1; + } + + if (spisize == 0) { + fseek(infile, -1, SEEK_END); + spisize = ftell(infile) + 1; + + printf("Input file size is 0x%lx.\n", spisize); + } + + if (ecfile != NULL) { + struct mec1322_header hdr; + fread(&hdr, 1, sizeof(hdr), ecfile); + fwlen = parse_header(&hdr); + + if (fwlen == 0) { + fprintf(stderr, "Error parsing MEC1322 firmware header!\n"); + return 1; + } + fseek(ecfile, 0, SEEK_SET); + size_t totallen = fwlen + 256; // firmware length with signature + image = malloc(totallen); + fread(image, 1, totallen, ecfile); + fclose(ecfile); + + if (!verify_payload_signature(&hdr, image + hdr.payload_offset, image + fwlen)) + return 1; + + fwlen = totallen; + } + + if (header_loc & 0xff) { + fprintf(stderr, "Header location should be aligned to 256 bytes!\n"); + return 1; + } + uint8_t fwTag[4] = {header_loc >> 8, header_loc >> 16, header_loc >> 24}; + fwTag[3] = crc8(fwTag, 3); + + // write tag + fseek(infile, -256, SEEK_END); + fwrite(fwTag, 1, 4, infile); + + // if EC image is given, write EC image + if (image) { + fseek(infile, header_loc - spisize, SEEK_END); + fwrite(image, 1, fwlen, infile); + free(image); + } + + fclose(infile); + + return 0; +} + +int main(int argc, char *argv[]) +{ + if (argc < 3) + usage(); + + if (strcmp(argv[1], "dump") == 0) + return mec1322_dump(argc - 2, argv + 2); + + if (strcmp(argv[1], "insert") == 0) + return mec1322_insert(argc - 2, argv + 2); + + // no action provided + usage(); +} |