diff options
-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 |