diff options
author | Vadim Bendebury <vbendeb@chromium.org> | 2018-02-22 17:07:27 -0800 |
---|---|---|
committer | chrome-bot <chrome-bot@chromium.org> | 2018-02-23 23:12:58 -0800 |
commit | d57e5eb3128774732e3b3fe40c0f939d9aaeff1a (patch) | |
tree | 8d0ef7e582227d66faa958de61aa967b05eefaa4 /extra | |
parent | 52b93ce19d00c9a6507c630501904b845a1b6486 (diff) | |
download | chrome-ec-d57e5eb3128774732e3b3fe40c0f939d9aaeff1a.tar.gz |
gsctool: add open box RMA option
This patch enhances the gsctool utility to allow to verify RO sections
of the target AP and EC flash memory.
The only command line parameter required for the new option ('O') is
the file name of the target descriptors database, containing memory
description sections for one or more Chrome OS devices.
Memory description sections are of two types (both types could be
referring AP or EC memory):
- hash descriptor, this section includes the address range of the
memory and one or more hash values for the contents of that address
range. Multiple hashes are needed in case when the same device has
mnore than one RO firmware releases in circulation.
- dump descriptor, this is a request for this utility to display on
the console the contents of the certain area of flash memory on the
target.
When this utility starts the process, the target might request that
the operator confirms physical presence, in this case the utility
keeps prompting the operator to press the physical presence button
until DUT is satisfied,
BRANCH=none
BUG=b:73668125
TEST=created a descriptor database for a Robo device feeding it with
values retrieved on the device by locally running spihash command
on the device.
Then ran this utility to verify successful hash and dump
retrievals, comparing dump values with values obtained through
Cr50 console directly.
Created additional dummy hash variants and verified that the
utility succeeds only if all matches happen at the same variant
index in different hash sections.
Change-Id: Ib43cf4eb642d141b7cd7f129ef412e14bd59f30b
Signed-off-by: Vadim Bendebury <vbendeb@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/933545
Reviewed-by: Mary Ruthven <mruthven@chromium.org>
Diffstat (limited to 'extra')
-rw-r--r-- | extra/usb_updater/Makefile | 2 | ||||
-rw-r--r-- | extra/usb_updater/gsctool.c | 25 | ||||
-rw-r--r-- | extra/usb_updater/verify_ro.c | 340 | ||||
-rw-r--r-- | extra/usb_updater/verify_ro.h | 15 |
4 files changed, 376 insertions, 6 deletions
diff --git a/extra/usb_updater/Makefile b/extra/usb_updater/Makefile index 86632903c8..2c6baf84f9 100644 --- a/extra/usb_updater/Makefile +++ b/extra/usb_updater/Makefile @@ -41,7 +41,7 @@ LIBS_common = -lfmap all: $(PROGRAMS) -GSCTOOL_SOURCES := gsctool.c desc_parser.c +GSCTOOL_SOURCES := gsctool.c desc_parser.c verify_ro.c GSCTOOL_OBJS := $(patsubst %.c,%.o,$(GSCTOOL_SOURCES)) DEPS := $(patsubst %.c,%.d,$(GSCTOOL_SOURCES)) diff --git a/extra/usb_updater/gsctool.c b/extra/usb_updater/gsctool.c index abbfe5433d..aa973c7b76 100644 --- a/extra/usb_updater/gsctool.c +++ b/extra/usb_updater/gsctool.c @@ -30,6 +30,7 @@ #include "tpm_vendor_cmds.h" #include "upgrade_fw.h" #include "usb_descriptor.h" +#include "verify_ro.h" #ifdef DEBUG #define debug printf @@ -193,7 +194,7 @@ struct upgrade_pkt { static uint32_t protocol_version; static char *progname; -static char *short_opts = "abcd:fhikoPprstUu"; +static char *short_opts = "abcd:fhikO:oPprstUu"; static const struct option long_opts[] = { /* name hasarg *flag val */ {"any", 0, NULL, 'a'}, @@ -206,6 +207,7 @@ static const struct option long_opts[] = { {"device", 1, NULL, 'd'}, {"fwver", 0, NULL, 'f'}, {"help", 0, NULL, 'h'}, + {"openbox_rma", 1, NULL, 'O'}, {"password", 0, NULL, 'P'}, {"post_reset", 0, NULL, 'p'}, {"rma_auth", 2, NULL, 'r'}, @@ -511,10 +513,14 @@ static void usage(int errs) " ID could be 32 bit hex or 4 " "character string.\n" " -k,--ccd_lock Lock CCD\n" + " -O,--openbox_rma <desc_file>\n" + " Verify other device's RO integrity\n" + " using information provided in " + "<desc file>\n" " -o,--ccd_open Start CCD open sequence\n" " -P,--password <password>\n" " Set or clear CCD password. Use\n" - " 'clear:<cur password>' to clear it.\n" + " 'clear:<cur password>' to clear it\n" " -p,--post_reset Request post reset after transfer\n" " -r,--rma_auth [[auth_code|\"disable\"]\n" " Request RMA challenge, process " @@ -1815,6 +1821,7 @@ int main(int argc, char *argv[]) int try_all_transfer = 0; const char *exclusive_opt_error = "Options -a, -s and -t are mutually exclusive\n"; + const char *openbox_desc_file = NULL; progname = strrchr(argv[0], '/'); if (progname) @@ -1876,6 +1883,9 @@ int main(int argc, char *argv[]) case 'k': ccd_lock = 1; break; + case 'O': + openbox_desc_file = optarg; + break; case 'o': ccd_open = 1; break; @@ -1949,7 +1959,8 @@ int main(int argc, char *argv[]) !corrupt_inactive_rw && !password && !rma && - !show_fw_ver) { + !show_fw_ver && + !openbox_desc_file) { if (optind >= argc) { fprintf(stderr, "\nERROR: Missing required <binary image>\n\n"); @@ -1975,8 +1986,9 @@ int main(int argc, char *argv[]) } if (((bid_action != bid_none) + !!rma + !!password + - !!ccd_open + !!ccd_unlock + !!ccd_lock) > 2) { - fprintf(stderr, "ERROR: options -i, -k, -o, -P, -r, and -u " + !!ccd_open + !!ccd_unlock + !!ccd_lock + + !!openbox_desc_file) > 2) { + fprintf(stderr, "ERROR: options -i, -k, -O, -o, -P, -r, and -u " "are mutually exclusive\n"); exit(update_error); } @@ -1994,6 +2006,9 @@ int main(int argc, char *argv[]) } } + if (openbox_desc_file) + return verify_ro(&td, openbox_desc_file); + if (ccd_unlock || ccd_open || ccd_lock) process_ccd_state(&td, ccd_unlock, ccd_open, ccd_lock); diff --git a/extra/usb_updater/verify_ro.c b/extra/usb_updater/verify_ro.c new file mode 100644 index 0000000000..6f6de59867 --- /dev/null +++ b/extra/usb_updater/verify_ro.c @@ -0,0 +1,340 @@ +/* + * Copyright 2018 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. + */ + +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "config.h" +#include "desc_parser.h" +#include "gsctool.h" +#include "tpm_vendor_cmds.h" +#include "verify_ro.h" + + +/* + * Print out passed in buffer contents in hex, 16 bytes per line, each line + * starting with the base address value. + * + * If the passed in base address is not aligned at 16 byte boundary, skip + * positions in the dump line so that the address is displayed rounded down to + * the closest lower 16 byte boundary. + * + * For instance passing base of 0x4007 and size of 20 will result in a + * printout like: + * + * 004000 e0 00 00 00 00 66 c7 05 04 + * 004010 80 06 e0 06 00 66 c7 05 20 90 06 + * + * If title is nonzero - print out the string it points to before printing + * out buffer contents. + */ +static void print_buffer_aligned(const char *title, uint32_t base, + size_t size, const void *data) +{ + const uint8_t *bytes = data; + size_t i; + uint8_t alignment; + + /* + * Calculate how many characters we need to skip in the first dump + * line. + */ + alignment = base % 16; + if (alignment) { + size += alignment; + base &= ~0xf; + } + + if (title) + printf("%s\n", title); + + /* Let's print data space separated 16 bytes per line. */ + for (i = 0; i < size; i++) { + if (!(i % 16)) + printf("\n%06zx", base + i); + + if (i < alignment) + printf(" "); + else + printf(" %02x", bytes[i - alignment]); + } +} + +/* Change the DUT spihash range to the new_type value. */ +static int set_new_range(struct transfer_descriptor *td, + enum range_type_t new_type) +{ + uint32_t rv; + struct vendor_cc_spi_hash_request req; + + memset(&req, 0, sizeof(req)); + + /* Need to send command to change spihash mode. */ + switch (new_type) { + case AP_RANGE: + req.subcmd = SPI_HASH_SUBCMD_AP; + break; + case EC_RANGE: + req.subcmd = SPI_HASH_SUBCMD_EC; + break; + case EC_GANG_RANGE: + req.subcmd = SPI_HASH_SUBCMD_EC; + req.flags = SPI_HASH_FLAG_EC_GANG; + break; + default: /* Should never happen. */ + return -EINVAL; + } + + rv = send_vendor_command(td, VENDOR_CC_SPI_HASH, &req, sizeof(req), + 0, NULL); + + if (!rv) + return 0; + + if (rv == VENDOR_RC_IN_PROGRESS) { + /* This will exit() on error. */ + poll_for_pp(td, VENDOR_CC_SPI_HASH, SPI_HASH_PP_POLL); + } else { + fprintf(stderr, + "%s: failed setting range type %d, error %d\n", + __func__, new_type, rv); + return -EINVAL; + } + + return 0; +} + +/* + * Verify a dump descriptor hash section defined by 'range'. The passed in by + * pointer structure req has the range offset and size already initialized. + * + * Make sure that matching hashes are at the same index in the hash variants + * array in all hash sections. + */ +static int verify_hash_section(struct transfer_descriptor *td, + struct vendor_cc_spi_hash_request *req, + struct addr_range *range) +{ + static ssize_t matching_range = -1; + size_t i; + uint8_t response[sizeof(range->variants->expected_result)]; + size_t response_size; + int rv; + + /* First retrieve hash from the DUT. */ + response_size = sizeof(response); + req->subcmd = SPI_HASH_SUBCMD_SHA256; + rv = send_vendor_command(td, VENDOR_CC_SPI_HASH, + req, sizeof(*req), response, &response_size); + + if (rv) { + fprintf(stderr, + "%s: failed retrieving hash at %x, tpm error %d\n", + __func__, req->offset, rv); + return -EINVAL; + } + + if (response_size != sizeof(response)) { + fprintf(stderr, "got %zd bytes in response for range %x:%x\n", + response_size, req->offset, req->size); + return -EINVAL; + } + + if (matching_range < 0) { + /* This is the first hash range to be processed. */ + struct result_node *variant = range->variants; + + for (i = 0; i < range->variant_count; i++) { + if (!memcmp(variant->expected_result, + response, response_size)) { + matching_range = i; + return 0; + } + variant++; + } + + fprintf(stderr, "no matching hash found for range %x:%x\n", + req->offset, req->size); + return -EINVAL; + } + + if (!memcmp(range->variants[matching_range].expected_result, + response, response_size)) + return 0; + + fprintf(stderr, "hash mismatch for range %x:%x\n", + req->offset, req->size); + + return -EINVAL; +} + +/* + * Dump DUT's memory in the range defined by contents of the passed in req + * structure. + * + * The Cr50 SPI hash dump vendor command implementation limits size of the + * dump to 32, so in case the caller requests more than 32 bytes retrieve them + * in 32 byte blocks. + * + * If base address of the range is not aligned at 16, retrieve smaller + * quantity such that the following transactions retrieve block starting at + * aligned addresses, this makes for a better looking hex dump. + */ +static int dump_range(struct transfer_descriptor *td, + struct vendor_cc_spi_hash_request *req) +{ + size_t remaining_size = req->size; + size_t response_size; + /* Max size of a single shot is 32 bytes. */ + const size_t max_transfer = 32; + uint8_t response[max_transfer]; + + req->subcmd = SPI_HASH_SUBCMD_DUMP; + while (remaining_size) { + size_t shot_size = max_transfer; + uint8_t alignment; + uint32_t rv; + + alignment = req->offset % 16; + + if (alignment && ((alignment + remaining_size) > max_transfer)) + /* first line should be truncated. */ + shot_size = max_transfer - alignment; + else if (shot_size > remaining_size) + shot_size = remaining_size; + + req->size = shot_size; + response_size = shot_size; + rv = send_vendor_command(td, VENDOR_CC_SPI_HASH, + req, sizeof(*req), response, + &response_size); + if (rv) { + fprintf(stderr, + "%s: failed getting dump contents at %x\n", + __func__, req->offset); + return -EINVAL; + } + + if (response_size != shot_size) { + fprintf(stderr, + "%s: dump error: got %zd bytes, expected %zd\n", + __func__, response_size, shot_size); + return -EINVAL; + } + + print_buffer_aligned(NULL, req->offset, shot_size, response); + remaining_size -= shot_size; + req->offset += shot_size; + } + printf("\n"); + + return 0; +} + +/* + * Iterate through sections of a board descriptor database, retrieving hashes + * or straight memory blocks as defined by description sections. + */ +static int process_descriptor_sections(struct transfer_descriptor *td) +{ + struct vendor_cc_spi_hash_request req; + int rv; + struct addr_range *range; + enum range_type_t current_range = NOT_A_RANGE; + + do { + /* + * Retrieve next range descriptor section from the descriptor + * database. The function below is guaranteed to set range to + * NULL on any error. + */ + rv = parser_get_next_range(&range); + if (rv) { + /* + * ENODATA means all board's sections have been + * processed. + */ + if (rv == -ENODATA) + rv = 0; + break; + } + + if (current_range != range->range_type) { + rv = set_new_range(td, range->range_type); + if (rv) + break; + } + + memset(&req, 0, sizeof(req)); + req.offset = range->base_addr; + req.size = range->range_size; + + if (range->variant_count) + rv = verify_hash_section(td, &req, range); + else + rv = dump_range(td, &req); + + free(range); + range = NULL; + } while (!rv); + + if (range) + free(range); + + parser_done(); + + if (rv) + return rv; + + memset(&req, 0, sizeof(req)); + req.subcmd = SPI_HASH_SUBCMD_DISABLE; + rv = send_vendor_command(td, VENDOR_CC_SPI_HASH, &req, + sizeof(req), 0, NULL); + if (rv) { + fprintf(stderr, + "%s: spi hash disable TPM error %d\n", __func__, rv); + return -EINVAL; + } + return 0; +} + +int verify_ro(struct transfer_descriptor *td, + const char *desc_file_name) +{ + /* First find out board ID of the target. */ + struct board_id bid; + char rlz_code[sizeof(bid.type) + 1]; + + /* + * Find out what Board ID is the device we are talking to. This + * function calls exit() on any error. + */ + process_bid(td, bid_get, &bid); + + if (bid.type != ~bid.type_inv) { + fprintf(stderr, "Inconsistent board ID: %08x != ~%08x\n", + bid.type, bid.type_inv); + return -EINVAL; + } + + /* + * Convert bid from int to asciiz so that it could be used for + * strcmp() on the descriptor file section headers. + */ + memcpy(rlz_code, &bid.type, sizeof(rlz_code) - 1); + rlz_code[sizeof(rlz_code) - 1] = '\0'; + + if (!parser_find_board(desc_file_name, rlz_code)) { + /* Opened the file and found descriptors for DUT. */ + printf("Processing sections for board ID %s\n", rlz_code); + return process_descriptor_sections(td); + } + + printf("No description for board ID %s found\n", rlz_code); + return -1; +} diff --git a/extra/usb_updater/verify_ro.h b/extra/usb_updater/verify_ro.h new file mode 100644 index 0000000000..c37ba3a337 --- /dev/null +++ b/extra/usb_updater/verify_ro.h @@ -0,0 +1,15 @@ +/* + * Copyright 2018 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. + */ + +#ifndef __EXTRA_USB_UPDATER_VERIFY_RO_H +#define __EXTRA_USB_UPDATER_VERIFY_RO_H + +#include "gsctool.h" + +int verify_ro(struct transfer_descriptor *td, + const char *desc_file_name); + +#endif // __EXTRA_USB_UPDATER_VERIFY_RO_H |