summaryrefslogtreecommitdiff
path: root/extra/usb_updater
diff options
context:
space:
mode:
authorNamyoon Woo <namyoon@chromium.org>2020-02-28 09:44:46 -0800
committerCommit Bot <commit-bot@chromium.org>2020-03-09 21:48:18 +0000
commit5ccb5c7cc9c448b66c6f8e0abbb2dd785da0efb7 (patch)
tree84f6021a658d5b8b11c7612dafac86ede442b27a /extra/usb_updater
parent97c73303cf9d5f6c8b6e871ddebd01a8e4ce4206 (diff)
downloadchrome-ec-5ccb5c7cc9c448b66c6f8e0abbb2dd785da0efb7.tar.gz
remove cr50 related files
BUG=b:149350081 BRANCH=none TEST=build all, and emerged ec related packages for host and octopus. $ make buildall -j $ cros_workon --host list chromeos-base/chromeos-cr50-dev chromeos-base/chromeos-ec chromeos-base/chromeos-ec-headers chromeos-base/ec-devutils chromeos-base/ec-utils chromeos-base/ec-utils-test dev-util/hdctools $ sudo emerge chromeos-cr50-dev -j $ sudo emerge chromeos-ec -j $ sudo emerge chromeos-ec-headers -j $ sudo emerge ec-devutils -j $ sudo emerge ec-utils -j $ sudo emerge ec-utils-test -j $ sudo emerge hdctools -j $ cros_workon-octopus list chromeos-base/chromeos-ec chromeos-base/chromeos-ec-headers chromeos-base/ec-devutils chromeos-base/ec-utils chromeos-base/ec-utils-test dev-util/hdctools $ sudo emerge-octopus chromeos-ec -j $ sudo emerge-octopus chromeos-ec-headers -j $ sudo emerge-octopus ec-devutils -j $ sudo emerge-octopus ec-utils -j $ sudo emerge-octopus ec-utils-test -j $ sudo emerge-octopus hdctools -j Signed-off-by: Namyoon Woo <namyoon@chromium.org> Change-Id: If751b26b0635b0021c077338e96eaa8e8dcf17a5 Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/2080631 Reviewed-by: Edward Hill <ecgh@chromium.org> Reviewed-by: Vadim Bendebury <vbendeb@chromium.org>
Diffstat (limited to 'extra/usb_updater')
-rw-r--r--extra/usb_updater/Makefile28
-rw-r--r--extra/usb_updater/gsctool.c3027
-rw-r--r--extra/usb_updater/gsctool.h122
-rw-r--r--extra/usb_updater/verify_ro.c362
-rw-r--r--extra/usb_updater/verify_ro.h24
5 files changed, 3 insertions, 3560 deletions
diff --git a/extra/usb_updater/Makefile b/extra/usb_updater/Makefile
index 2f8f70a6d3..1dfbc55645 100644
--- a/extra/usb_updater/Makefile
+++ b/extra/usb_updater/Makefile
@@ -4,7 +4,7 @@
CC ?= gcc
PKG_CONFIG ?= pkg-config
-PROGRAMS := gsctool usb_updater2
+PROGRAMS := usb_updater2
LIBS :=
LFLAGS :=
CFLAGS := -std=gnu99 \
@@ -34,27 +34,12 @@ CFLAGS += -I../../include -I../../util -I../../fuzz -I../../test
VPATH = ../../util
-BOARD := cr50
-LIBS_g = $(shell $(PKG_CONFIG) --libs libcrypto)
-CFLAGS_g = $(shell $(PKG_CONFIG) --cflags libcrypto)
-CFLAGS_g += -I../../board/cr50 -I ../../chip/g
-
LIBS_common = -lfmap
all: $(PROGRAMS)
-GSCTOOL_SOURCES := gsctool.c desc_parser.c usb_if.c verify_ro.c
-GSCTOOL_OBJS := $(patsubst %.c,%.o,$(GSCTOOL_SOURCES))
-DEPS := $(patsubst %.c,%.d,$(GSCTOOL_SOURCES))
-
-# chip/g updater
-gsctool: $(GSCTOOL_OBJS) Makefile
- $(CC) $(GSCTOOL_OBJS) $(LFLAGS) $(LIBS) $(LIBS_g) -o $@
-
%.o: %.c
- $(CC) $(CFLAGS) $(CFLAGS_g) -c -MMD -MF $(basename $@).d -o $@ $<
-
-gsctool.o: generated_version.h
+ $(CC) $(CFLAGS) -c -MMD -MF $(basename $@).d -o $@ $<
# common EC code USB updater
usb_updater2: usb_updater2.c Makefile
@@ -62,16 +47,9 @@ usb_updater2: usb_updater2.c Makefile
.PHONY: clean
-generated_version.h: $(GSCTOOL_SOURCES)
- # Make sure ${BOARD} not set to anything when invoking getversion.sh,
- # as even when building with Cr50 enabled, other directories do not
- # matter for gsctool.
- @(cd ../../; BOARD= util/getversion.sh) > $@
-
clean:
- rm -rf $(PROGRAMS) *~ *.o *.d dp generated_version.h
+ rm -rf $(PROGRAMS) *~ *.o *.d dp
parser_debug: desc_parser.c
gcc -g -O0 -DTEST_PARSER desc_parser.c -o dp
--include $(DEPS)
diff --git a/extra/usb_updater/gsctool.c b/extra/usb_updater/gsctool.c
deleted file mode 100644
index 4540814e56..0000000000
--- a/extra/usb_updater/gsctool.c
+++ /dev/null
@@ -1,3027 +0,0 @@
-/*
- * Copyright 2015 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 <asm/byteorder.h>
-#include <ctype.h>
-#include <endian.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <getopt.h>
-#include <libusb.h>
-#include <openssl/sha.h>
-#include <stdarg.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <termios.h>
-#include <unistd.h>
-
-#include "config.h"
-
-#include "ccd_config.h"
-#include "compile_time_macros.h"
-#include "flash_log.h"
-#include "generated_version.h"
-#include "gsctool.h"
-#include "misc_util.h"
-#include "signed_header.h"
-#include "tpm_registers.h"
-#include "tpm_vendor_cmds.h"
-#include "upgrade_fw.h"
-#include "usb_descriptor.h"
-#include "verify_ro.h"
-
-/*
- * This file contains the source code of a Linux application used to update
- * CR50 device firmware.
- *
- * The CR50 firmware image consists of multiple sections, of interest to this
- * app are the RO and RW code sections, two of each. When firmware update
- * session is established, the CR50 device reports locations of backup RW and RO
- * sections (those not used by the device at the time of transfer).
- *
- * Based on this information this app carves out the appropriate sections form
- * the full CR50 firmware binary image and sends them to the device for
- * programming into flash. Once the new sections are programmed and the device
- * is restarted, the new RO and RW are used if they pass verification and are
- * logically newer than the existing sections.
- *
- * There are two ways to communicate with the CR50 device: USB and /dev/tpm0
- * (when this app is running on a chromebook with the CR50 device). Originally
- * different protocols were used to communicate over different channels,
- * starting with version 3 the same protocol is used.
- *
- * This app provides backwards compatibility to ensure that earlier CR50
- * devices still can be updated.
- *
- *
- * The host (either a local AP or a workstation) is the master of the firmware
- * update protocol, it sends data to the cr50 device, which proceeses it and
- * responds.
- *
- * The encapsultation format is different between the /dev/tpm0 and USB cases:
- *
- * 4 bytes 4 bytes 4 bytes variable size
- * +-----------+--------------+---------------+----------~~--------------+
- * + total size| block digest | dest address | data |
- * +-----------+--------------+---------------+----------~~--------------+
- * \ \ /
- * \ \ /
- * \ +----- FW update PDU sent over /dev/tpm0 -----------+
- * \ /
- * +--------- USB frame, requires total size field ------------+
- *
- * The update protocol data unints (PDUs) are passed over /dev/tpm0, the
- * encapsulation includes integritiy verification and destination address of
- * the data (more of this later). /dev/tpm0 transactions pretty much do not
- * have size limits, whereas the USB data is sent in chunks of the size
- * determined when the USB connestion is set up. This is why USB requires an
- * additional encapsulation into frames to communicate the PDU size to the
- * client side so that the PDU can be reassembled before passing to the
- * programming function.
- *
- * In general, the protocol consists of two phases: connection establishment
- * and actual image transfer.
- *
- * The very first PDU of the transfer session is used to establish the
- * connection. The first PDU does not have any data, and the dest. address
- * field is set to zero. Receiving such a PDU signals the programming function
- * that the host intends to transfer a new image.
- *
- * The response to the first PDU varies depending on the protocol version.
- *
- * Note that protocol versions before 5 are described here for completeness,
- * but are not supported any more by this utility.
- *
- * Version 1 is used over /dev/tpm0. The response is either 4 or 1 bytes in
- * size. The 4 byte response is the *base address* of the backup RW section,
- * no support for RO updates. The one byte response is an error indication,
- * possibly reporting flash erase failure, command format error, etc.
- *
- * Version 2 is used over USB. The response is 8 bytes in size. The first four
- * bytes are either the *base address* of the backup RW section (still no RO
- * updates), or an error code, the same as in Version 1. The second 4 bytes
- * are the protocol version number (set to 2).
- *
- * All versions above 2 behave the same over /dev/tpm0 and USB.
- *
- * Version 3 response is 16 bytes in size. The first 4 bytes are the error code
- * the second 4 bytes are the protocol version (set to 3) and then 4 byte
- * *offset* of the RO section followed by the 4 byte *offset* of the RW section.
- *
- * Version 4 response in addition to version 3 provides header revision fields
- * for active RO and RW images running on the target.
- *
- * Once the connection is established, the image to be programmed into flash
- * is transferred to the CR50 in 1K PDUs. In versions 1 and 2 the address in
- * the header is the absolute address to place the block to, in version 3 and
- * later it is the offset into the flash.
- *
- * Protocol version 5 includes RO and RW key ID information into the first PDU
- * response. The key ID could be used to tell between prod and dev signing
- * modes, among other things.
- *
- * Protocol version 6 does not change the format of the first PDU response,
- * but it indicates the target's ablitiy to channel TPM vendor commands
- * through USB connection.
- *
- * When channeling TPM vendor commands the USB frame looks as follows:
- *
- * 4 bytes 4 bytes 4 bytes 2 bytes variable size
- * +-----------+--------------+---------------+-----------+------~~~-------+
- * + total size| block digest | EXT_CMD | Vend. sub.| data |
- * +-----------+--------------+---------------+-----------+------~~~-------+
- *
- * Where 'Vend. sub' is the vendor subcommand, and data field is subcommand
- * dependent. The target tells between update PDUs and encapsulated vendor
- * subcommands by looking at the EXT_CMD value - it is set to 0xbaccd00a and
- * as such is guaranteed not to be a valid update PDU destination address.
- *
- * The vendor command response size is not fixed, it is subcommand dependent.
- *
- * The CR50 device responds to each update PDU with a confirmation which is 4
- * bytes in size in protocol version 2, and 1 byte in size in all other
- * versions. Zero value means success, non zero value is the error code
- * reported by CR50.
- *
- * Again, vendor command responses are subcommand specific.
- */
-
-/* Look for Cr50 FW update interface */
-#define VID USB_VID_GOOGLE
-#define PID CONFIG_USB_PID
-#define SUBCLASS USB_SUBCLASS_GOOGLE_CR50
-#define PROTOCOL USB_PROTOCOL_GOOGLE_CR50_NON_HC_FW_UPDATE
-
-/*
- * Need to create an entire TPM PDU when upgrading over /dev/tpm0 and need to
- * have space to prepare the entire PDU.
- */
-struct upgrade_pkt {
- __be16 tag;
- __be32 length;
- __be32 ordinal;
- __be16 subcmd;
- union {
- /*
- * Upgrade PDUs as opposed to all other vendor and extension
- * commands include two additional fields in the header.
- */
- struct {
- __be32 digest;
- __be32 address;
- char data[0];
- } upgrade;
- struct {
- char data[0];
- } command;
- };
-} __packed;
-
-/*
- * Structure used to simplify mapping command line options into Boolean
- * variables. If an option is present, the corresponding integer value is set
- * to 1.
- */
-struct options_map {
- char opt;
- int *flag;
-};
-
-/*
- * Structure used to combine option description used by getopt_long() and help
- * text for the option.
- */
-struct option_container {
- struct option opt;
- const char *help_text;
-};
-
-/*
- * This by far exceeds the largest vendor command response size we ever
- * expect.
- */
-#define MAX_BUF_SIZE 500
-
-/*
- * Max. length of the board ID string representation.
- *
- * Board ID is either a 4-character ASCII alphanumeric string or an 8-digit
- * hex.
- */
-#define MAX_BOARD_ID_LENGTH 9
-
-/*
- * Length, in bytes, of the SN Bits serial number bits.
- */
-#define SN_BITS_SIZE (96 >> 3)
-
-/*
- * Max. length of FW version in the format of <epoch>.<major>.<minor>
- * (3 uint32_t string representation + 2 separators + NULL terminator).
- */
-#define MAX_FW_VER_LENGTH 33
-
-static int verbose_mode;
-static uint32_t protocol_version;
-static char *progname;
-
-/*
- * List of command line options, ***sorted by the short form***.
- *
- * The help_text field does not include the short and long option strings,
- * they are retrieved from the opt structure. In case the help text needs to
- * have something printed immediately after the option strings (for example,
- * an optional parameter), it should be included in the beginning of help_text
- * string separated by the % character.
- *
- * usage() function which prints out the help message will concatenate the
- * short and long options and the optional parameter, if present, and then
- * print the rest of the text message at a fixed indentation.
- */
-static const struct option_container cmd_line_options[] = {
- /* name has_arg *flag val */
- {{"any", no_argument, NULL, 'a'},
- "Try any interfaces to find Cr50"
- " (-d, -s, -t are all ignored)"},
- {{"background_update_supported", no_argument, NULL, 'B'},
- "Force background update mode (relevant"
- " only when interacting"
- " with Cr50 versions before 0.0.19)"
- },
- {{"binvers", no_argument, NULL, 'b'},
- "Report versions of Cr50 image's "
- "RW and RO headers, do not update"},
- {{"corrupt", no_argument, NULL, 'c'},
- "Corrupt the inactive rw"},
- {{"device", required_argument, NULL, 'd'},
- " VID:PID%USB device (default 18d1:5014)"},
- {{"endorsement_seed", optional_argument, NULL, 'e'},
- "[state]%get/set the endorsement key seed"},
- {{"fwver", no_argument, NULL, 'f'},
- "Report running Cr50 firmware versions"},
- {{"factory", required_argument, NULL, 'F'},
- "[enable|disable]%Control factory mode"},
- {{"help", no_argument, NULL, 'h'},
- "Show this message"},
- {{"ccd_info", no_argument, NULL, 'I'},
- "Get information about CCD state"},
- {{"board_id", optional_argument, NULL, 'i'},
- "[ID[:FLAGS]]%Get or set Info1 board ID fields. ID could be 32 bit "
- "hex or 4 character string."},
- {{"ccd_lock", no_argument, NULL, 'k'},
- "Lock CCD"},
- {{"flog", optional_argument, NULL, 'L'},
- "[prev entry]%Retrieve contents of the flash log"
- " (newer than <prev entry> if specified)"},
- {{"machine", no_argument, NULL, 'M'},
- "Output in a machine-friendly way. "
- "Effective with -b, -f, -i, and -O."},
- {{"tpm_mode", optional_argument, NULL, 'm'},
- "[enable|disable]%Change or query tpm_mode"},
- {{"serial", required_argument, NULL, 'n'},
- "Cr50 CCD serial number"},
- {{"openbox_rma", required_argument, NULL, 'O'},
- "<desc_file>%Verify other device's RO integrity using information "
- "provided in <desc file>"},
- {{"ccd_open", no_argument, NULL, 'o'},
- "Start CCD open sequence"},
- {{"password", no_argument, NULL, 'P'},
- "Set or clear CCD password. Use 'clear:<cur password>' to clear it"},
- {{"post_reset", no_argument, NULL, 'p'},
- "Request post reset after transfer"},
- {{"force_ro", no_argument, NULL, 'q'},
- "Force inactive RO update"},
- {{"sn_rma_inc", required_argument, NULL, 'R'},
- "RMA_INC%Increment SN RMA count by RMA_INC. RMA_INC should be 0-7."},
- {{"rma_auth", optional_argument, NULL, 'r'},
- "[auth_code]%Request RMA challenge, process "
- "RMA authentication code"},
- {{"sn_bits", required_argument, NULL, 'S'},
- "SN_BITS%Set Info1 SN bits fields. SN_BITS should be 96 bit hex."},
- {{"systemdev", no_argument, NULL, 's'},
- "Use /dev/tpm0 (-d is ignored)"},
- {{"tstamp", optional_argument, NULL, 'T'},
- "[<tstamp>]%Get or set flash log timestamp base"},
- {{"trunks_send", no_argument, NULL, 't'},
- "Use `trunks_send --raw' (-d is ignored)"},
- {{"ccd_unlock", no_argument, NULL, 'U'},
- "Start CCD unlock sequence"},
- {{"upstart", no_argument, NULL, 'u'},
- "Upstart mode (strict header checks)"},
- {{"verbose", no_argument, NULL, 'V'},
- "Enable debug messages"},
- {{"version", no_argument, NULL, 'v'},
- "Report this utility version"},
- {{"wp", no_argument, NULL, 'w'},
- "Get the current wp setting"}
-};
-
-/* Helper to print debug messages when verbose flag is specified. */
-static void debug(const char *fmt, ...)
-{
- va_list args;
-
- if (verbose_mode) {
- va_start(args, fmt);
- vprintf(fmt, args);
- va_end(args);
- }
-}
-
-/* Helpers to convert between binary and hex ascii and back. */
-static char to_hexascii(uint8_t c)
-{
- if (c <= 9)
- return '0' + c;
- return 'a' + c - 10;
-}
-
-static int from_hexascii(char c)
-{
- /* convert to lower case. */
- c = tolower(c);
-
- if (c < '0' || c > 'f' || ((c > '9') && (c < 'a')))
- return -1; /* Not an ascii character. */
-
- if (c <= '9')
- return c - '0';
-
- return c - 'a' + 10;
-}
-
-/* Functions to communicate with the TPM over the trunks_send --raw channel. */
-
-/* File handle to share between write and read sides. */
-static FILE *tpm_output;
-static int ts_write(const void *out, size_t len)
-{
- const char *cmd_head = "PATH=\"${PATH}:/usr/sbin\" trunks_send --raw ";
- size_t head_size = strlen(cmd_head);
- char full_command[head_size + 2 * len + 1];
- size_t i;
-
- strcpy(full_command, cmd_head);
- /*
- * Need to convert binary input into hex ascii output to pass to the
- * trunks_send command.
- */
- for (i = 0; i < len; i++) {
- uint8_t c = ((const uint8_t *)out)[i];
-
- full_command[head_size + 2 * i] = to_hexascii(c >> 4);
- full_command[head_size + 2 * i + 1] = to_hexascii(c & 0xf);
- }
-
- /* Make it a proper zero terminated string. */
- full_command[sizeof(full_command) - 1] = 0;
- debug("cmd: %s\n", full_command);
- tpm_output = popen(full_command, "r");
- if (tpm_output)
- return len;
-
- fprintf(stderr, "Error: failed to launch trunks_send --raw\n");
- return -1;
-}
-
-static int ts_read(void *buf, size_t max_rx_size)
-{
- int i;
- int pclose_rv;
- int rv;
- char response[max_rx_size * 2];
-
- if (!tpm_output) {
- fprintf(stderr, "Error: attempt to read empty output\n");
- return -1;
- }
-
- rv = fread(response, 1, sizeof(response), tpm_output);
- if (rv > 0)
- rv -= 1; /* Discard the \n character added by trunks_send. */
-
- debug("response of size %d, max rx size %zd: %s\n",
- rv, max_rx_size, response);
-
- pclose_rv = pclose(tpm_output);
- if (pclose_rv < 0) {
- fprintf(stderr,
- "Error: pclose failed: error %d (%s)\n",
- errno, strerror(errno));
- return -1;
- }
-
- tpm_output = NULL;
-
- if (rv & 1) {
- fprintf(stderr,
- "Error: trunks_send returned odd number of bytes: %s\n",
- response);
- return -1;
- }
-
- for (i = 0; i < rv/2; i++) {
- uint8_t byte;
- char c;
- int nibble;
-
- c = response[2 * i];
- nibble = from_hexascii(c);
- if (nibble < 0) {
- fprintf(stderr, "Error: "
- "trunks_send returned non hex character %c\n",
- c);
- return -1;
- }
- byte = nibble << 4;
-
- c = response[2 * i + 1];
- nibble = from_hexascii(c);
- if (nibble < 0) {
- fprintf(stderr, "Error: "
- "trunks_send returned non hex character %c\n",
- c);
- return -1;
- }
- byte |= nibble;
-
- ((uint8_t *)buf)[i] = byte;
- }
-
- return rv/2;
-}
-
-/*
- * Prepare and transfer a block to either to /dev/tpm0 or through trunks_send
- * --raw, get a reply.
- */
-static int tpm_send_pkt(struct transfer_descriptor *td, unsigned int digest,
- unsigned int addr, const void *data, int size,
- void *response, size_t *response_size,
- uint16_t subcmd)
-{
- /* Used by transfer to /dev/tpm0 */
- static uint8_t outbuf[MAX_BUF_SIZE];
- struct upgrade_pkt *out = (struct upgrade_pkt *)outbuf;
- int len, done;
- int response_offset = offsetof(struct upgrade_pkt, command.data);
- void *payload;
- size_t header_size;
- uint32_t rv;
- const size_t rx_size = sizeof(outbuf);
-
- debug("%s: sending to %#x %d bytes\n", __func__, addr, size);
-
- out->tag = htobe16(0x8001);
- out->subcmd = htobe16(subcmd);
-
- if (subcmd <= LAST_EXTENSION_COMMAND)
- out->ordinal = htobe32(CONFIG_EXTENSION_COMMAND);
- else
- out->ordinal = htobe32(TPM_CC_VENDOR_BIT_MASK);
-
- if (subcmd == EXTENSION_FW_UPGRADE) {
- /* FW Upgrade PDU header includes a couple of extra fields. */
- out->upgrade.digest = digest;
- out->upgrade.address = htobe32(addr);
- header_size = offsetof(struct upgrade_pkt, upgrade.data);
- } else {
- header_size = offsetof(struct upgrade_pkt, command.data);
- }
-
- payload = outbuf + header_size;
- len = size + header_size;
-
- out->length = htobe32(len);
- memcpy(payload, data, size);
-
- if (verbose_mode) {
- int i;
-
- debug("Writing %d bytes to TPM at %x\n", len, addr);
- for (i = 0; i < MIN(len, 20); i++)
- debug("%2.2x ", outbuf[i]);
- debug("\n");
- }
-
- switch (td->ep_type) {
- case dev_xfer:
- done = write(td->tpm_fd, out, len);
- break;
- case ts_xfer:
- done = ts_write(out, len);
- break;
- default:
- fprintf(stderr, "Error: %s:%d: unknown transfer type %d\n",
- __func__, __LINE__, td->ep_type);
- return -1;
- }
-
- if (done < 0) {
- perror("Could not write to TPM");
- return -1;
- } else if (done != len) {
- fprintf(stderr, "Error: Wrote %d bytes, expected to write %d\n",
- done, len);
- return -1;
- }
-
- switch (td->ep_type) {
- case dev_xfer: {
- int read_count;
-
- len = 0;
- do {
- uint8_t *rx_buf = outbuf + len;
- size_t rx_to_go = rx_size - len;
-
- read_count = read(td->tpm_fd, rx_buf, rx_to_go);
-
- len += read_count;
- } while (read_count);
- break;
- }
- case ts_xfer:
- len = ts_read(outbuf, rx_size);
- break;
- default:
- /*
- * This sure will never happen, type is verifed in the
- * previous switch statement.
- */
- len = -1;
- break;
- }
-
- debug("Read %d bytes from TPM\n", len);
- if (len > 0) {
- int i;
-
- for (i = 0; i < len; i++)
- debug("%2.2x ", outbuf[i]);
- debug("\n");
- }
- len = len - response_offset;
- if (len < 0) {
- fprintf(stderr, "Problems reading from TPM, got %d bytes\n",
- len + response_offset);
- return -1;
- }
-
- if (response && response_size) {
- len = MIN(len, *response_size);
- memcpy(response, outbuf + response_offset, len);
- *response_size = len;
- }
-
- /* Return the actual return code from the TPM response header. */
- memcpy(&rv, &((struct upgrade_pkt *)outbuf)->ordinal, sizeof(rv));
- rv = be32toh(rv);
-
- /* Clear out vendor command return value offset.*/
- if ((rv & VENDOR_RC_ERR) == VENDOR_RC_ERR)
- rv &= ~VENDOR_RC_ERR;
-
- return rv;
-}
-
-/* Release USB device and return error to the OS. */
-static void shut_down(struct usb_endpoint *uep)
-{
- usb_shut_down(uep);
- exit(update_error);
-}
-
-static void usage(int errs)
-{
- size_t i;
- const int indent = 27; /* This is the size used by gsctool all along. */
-
- printf("\nUsage: %s [options] [<binary image>]\n"
- "\n"
- "This utility allows to update Cr50 RW firmware, configure\n"
- "various aspects of Cr50 operation, analyze Cr50 binary\n"
- "images, etc.\n\n"
- "<binary image> is the file name of a full RO+RW binary image.\n"
- "\n"
- "Options:\n\n",
- progname);
-
- for (i = 0; i < ARRAY_SIZE(cmd_line_options); i++) {
- const char *help_text = cmd_line_options[i].help_text;
- int printed_length;
- const char *separator;
-
- /*
- * First print the short and long forms of the command line
- * option.
- */
- printed_length = printf(" -%c,--%s",
- cmd_line_options[i].opt.val,
- cmd_line_options[i].opt.name);
-
- /*
- * If there is something to print immediately after the
- * options, print it.
- */
- separator = strchr(help_text, '%');
- if (separator) {
- char buffer[80];
- size_t extra_size;
-
- extra_size = separator - help_text;
- if (extra_size >= sizeof(buffer)) {
- fprintf(stderr, "misformatted help text: %s\n",
- help_text);
- exit(-1);
- }
- memcpy(buffer, help_text, extra_size);
- buffer[extra_size] = '\0';
- printed_length += printf(" %s", buffer);
- help_text = separator + 1;
- }
-
- /*
- * If printed length exceeds or is too close to indent, print
- * help text on the next line.
- */
- if (printed_length >= (indent - 1)) {
- printf("\n");
- printed_length = 0;
- }
-
- while (printed_length++ < indent)
- printf(" ");
- printf("%s\n", help_text);
- }
- printf("\n");
- exit(errs ? update_error : noop);
-}
-
-/* Read file into buffer */
-static uint8_t *get_file_or_die(const char *filename, size_t *len_ptr)
-{
- FILE *fp;
- struct stat st;
- uint8_t *data;
- size_t len;
-
- fp = fopen(filename, "rb");
- if (!fp) {
- perror(filename);
- exit(update_error);
- }
- if (fstat(fileno(fp), &st)) {
- perror("stat");
- exit(update_error);
- }
-
- len = st.st_size;
-
- data = malloc(len);
- if (!data) {
- perror("malloc");
- exit(update_error);
- }
-
- if (1 != fread(data, st.st_size, 1, fp)) {
- perror("fread");
- exit(update_error);
- }
-
- fclose(fp);
-
- *len_ptr = len;
- return data;
-}
-
-/* Returns true if parsed. */
-static int parse_vidpid(const char *input, uint16_t *vid_ptr, uint16_t *pid_ptr)
-{
- char *copy, *s, *e = 0;
-
- copy = strdup(input);
-
- s = strchr(copy, ':');
- if (!s)
- return 0;
- *s++ = '\0';
-
- *vid_ptr = (uint16_t) strtoul(copy, &e, 16);
- if (!*optarg || (e && *e))
- return 0;
-
- *pid_ptr = (uint16_t) strtoul(s, &e, 16);
- if (!*optarg || (e && *e))
- return 0;
-
- return 1;
-}
-
-struct update_pdu {
- uint32_t block_size; /* Total block size, include this field's size. */
- struct upgrade_command cmd;
- /* The actual payload goes here. */
-};
-
-static void do_xfer(struct usb_endpoint *uep, void *outbuf, int outlen,
- void *inbuf, int inlen, int allow_less, size_t *rxed_count)
-{
- if (usb_trx(uep, outbuf, outlen, inbuf, inlen, allow_less, rxed_count))
- shut_down(uep);
-}
-
-static int transfer_block(struct usb_endpoint *uep, struct update_pdu *updu,
- uint8_t *transfer_data_ptr, size_t payload_size)
-{
- size_t transfer_size;
- uint32_t reply;
- int actual;
- int r;
-
- /* First send the header. */
- do_xfer(uep, updu, sizeof(*updu), NULL, 0, 0, NULL);
-
- /* Now send the block, chunk by chunk. */
- for (transfer_size = 0; transfer_size < payload_size;) {
- int chunk_size;
-
- chunk_size = MIN(uep->chunk_len, payload_size - transfer_size);
- do_xfer(uep, transfer_data_ptr, chunk_size, NULL, 0, 0, NULL);
- transfer_data_ptr += chunk_size;
- transfer_size += chunk_size;
- }
-
- /* Now get the reply. */
- r = libusb_bulk_transfer(uep->devh, uep->ep_num | 0x80,
- (void *) &reply, sizeof(reply),
- &actual, 1000);
- if (r) {
- if (r == -7) {
- fprintf(stderr, "Timeout!\n");
- return r;
- }
- USB_ERROR("libusb_bulk_transfer", r);
- shut_down(uep);
- }
-
- reply = *((uint8_t *)&reply);
- if (reply) {
- fprintf(stderr, "Error: status %#x\n", reply);
- exit(update_error);
- }
-
- return 0;
-}
-
-/**
- * Transfer an image section (typically RW or RO).
- *
- * td - transfer descriptor to use to communicate with the target
- * data_ptr - pointer at the section base in the image
- * section_addr - address of the section in the target memory space
- * data_len - section size
- */
-static void transfer_section(struct transfer_descriptor *td,
- uint8_t *data_ptr,
- uint32_t section_addr,
- size_t data_len)
-{
- /*
- * Actually, we can skip trailing chunks of 0xff, as the entire
- * section space must be erased before the update is attempted.
- */
- while (data_len && (data_ptr[data_len - 1] == 0xff))
- data_len--;
-
- /*
- * Make sure total size is 4 bytes aligned, this is required for
- * successful flashing.
- */
- data_len = (data_len + 3) & ~3;
-
- printf("sending 0x%zx bytes to %#x\n", data_len, section_addr);
- while (data_len) {
- size_t payload_size;
- SHA_CTX ctx;
- uint8_t digest[SHA_DIGEST_LENGTH];
- int max_retries;
- struct update_pdu updu;
-
- /* prepare the header to prepend to the block. */
- payload_size = MIN(data_len, SIGNED_TRANSFER_SIZE);
- updu.block_size = htobe32(payload_size +
- sizeof(struct update_pdu));
-
- updu.cmd.block_base = htobe32(section_addr);
-
- /* Calculate the digest. */
- SHA1_Init(&ctx);
- SHA1_Update(&ctx, &updu.cmd.block_base,
- sizeof(updu.cmd.block_base));
- SHA1_Update(&ctx, data_ptr, payload_size);
- SHA1_Final(digest, &ctx);
-
- /* Copy the first few bytes. */
- memcpy(&updu.cmd.block_digest, digest,
- sizeof(updu.cmd.block_digest));
- if (td->ep_type == usb_xfer) {
- for (max_retries = 10; max_retries; max_retries--)
- if (!transfer_block(&td->uep, &updu,
- data_ptr, payload_size))
- break;
-
- if (!max_retries) {
- fprintf(stderr,
- "Failed to transfer block, %zd to go\n",
- data_len);
- exit(update_error);
- }
- } else {
- uint8_t error_code[4];
- size_t rxed_size = sizeof(error_code);
- uint32_t block_addr;
-
- block_addr = section_addr;
-
- /*
- * A single byte response is expected, but let's give
- * the driver a few extra bytes to catch cases when a
- * different amount of data is transferred (which
- * would indicate a synchronization problem).
- */
- if (tpm_send_pkt(td,
- updu.cmd.block_digest,
- block_addr,
- data_ptr,
- payload_size, error_code,
- &rxed_size,
- EXTENSION_FW_UPGRADE) < 0) {
- fprintf(stderr,
- "Failed to trasfer block, %zd to go\n",
- data_len);
- exit(update_error);
- }
- if (rxed_size != 1) {
- fprintf(stderr, "Unexpected return size %zd\n",
- rxed_size);
- exit(update_error);
- }
-
- if (error_code[0]) {
- fprintf(stderr, "Error %d\n", error_code[0]);
- exit(update_error);
- }
- }
- data_len -= payload_size;
- data_ptr += payload_size;
- section_addr += payload_size;
- }
-}
-
-/* Information about the target */
-static struct first_response_pdu targ;
-
-/*
- * Each RO or RW section of the new image can be in one of the following
- * states.
- */
-enum upgrade_status {
- not_needed = 0, /* Version below or equal that on the target. */
- not_possible, /*
- * RO is newer, but can't be transferred due to
- * target RW shortcomings.
- */
- needed /*
- * This section needs to be transferred to the
- * target.
- */
-};
-
-/* This array describes all four sections of the new image. */
-static struct {
- const char *name;
- uint32_t offset;
- uint32_t size;
- enum upgrade_status ustatus;
- struct signed_header_version shv;
- uint32_t keyid;
-} sections[] = {
- {"RO_A", CONFIG_RO_MEM_OFF, CONFIG_RO_SIZE},
- {"RW_A", CONFIG_RW_MEM_OFF, CONFIG_RW_SIZE},
- {"RO_B", CHIP_RO_B_MEM_OFF, CONFIG_RO_SIZE},
- {"RW_B", CONFIG_RW_B_MEM_OFF, CONFIG_RW_SIZE}
-};
-
-/*
- * Scan the new image and retrieve versions of all four sections, two RO and
- * two RW.
- */
-static void fetch_header_versions(const void *image)
-{
- size_t i;
-
- for (i = 0; i < ARRAY_SIZE(sections); i++) {
- const struct SignedHeader *h;
-
- h = (const struct SignedHeader *)((uintptr_t)image +
- sections[i].offset);
- sections[i].shv.epoch = h->epoch_;
- sections[i].shv.major = h->major_;
- sections[i].shv.minor = h->minor_;
- sections[i].keyid = h->keyid;
- }
-}
-
-
-/* Compare to signer headers and determine which one is newer. */
-static int a_newer_than_b(struct signed_header_version *a,
- struct signed_header_version *b)
-{
- uint32_t fields[][3] = {
- {a->epoch, a->major, a->minor},
- {b->epoch, b->major, b->minor},
- };
- size_t i;
-
- for (i = 0; i < ARRAY_SIZE(fields[0]); i++) {
- uint32_t a_value;
- uint32_t b_value;
-
- a_value = fields[0][i];
- b_value = fields[1][i];
-
- /*
- * Let's filter out images where the section is not
- * initialized and the version field value is set to all ones.
- */
- if (a_value == 0xffffffff)
- a_value = 0;
-
- if (b_value == 0xffffffff)
- b_value = 0;
-
- if (a_value != b_value)
- return a_value > b_value;
- }
-
- return 0; /* All else being equal A is no newer than B. */
-}
-/*
- * Pick sections to transfer based on information retrieved from the target,
- * the new image, and the protocol version the target is running.
- */
-static void pick_sections(struct transfer_descriptor *td)
-{
- size_t i;
-
- for (i = 0; i < ARRAY_SIZE(sections); i++) {
- uint32_t offset = sections[i].offset;
-
- if ((offset == CONFIG_RW_MEM_OFF) ||
- (offset == CONFIG_RW_B_MEM_OFF)) {
-
- /* Skip currently active section. */
- if (offset != td->rw_offset)
- continue;
- /*
- * Ok, this would be the RW section to transfer to the
- * device. Is it newer in the new image than the
- * running RW section on the device?
- *
- * If not in 'upstart' mode - transfer even if
- * versions are the same, timestamps could be
- * different.
- */
-
- if (a_newer_than_b(&sections[i].shv, &targ.shv[1]) ||
- !td->upstart_mode)
- sections[i].ustatus = needed;
- continue;
- }
-
- /* Skip currently active section. */
- if (offset != td->ro_offset)
- continue;
- /*
- * Ok, this would be the RO section to transfer to the device.
- * Is it newer in the new image than the running RO section on
- * the device?
- */
- if (a_newer_than_b(&sections[i].shv, &targ.shv[0]) ||
- td->force_ro)
- sections[i].ustatus = needed;
- }
-}
-
-static void setup_connection(struct transfer_descriptor *td)
-{
- size_t rxed_size;
- size_t i;
- uint32_t error_code;
-
- /*
- * Need to be backwards compatible, communicate with targets running
- * different protocol versions.
- */
- union {
- struct first_response_pdu rpdu;
- uint32_t legacy_resp;
- } start_resp;
-
- /* Send start request. */
- printf("start\n");
-
- if (td->ep_type == usb_xfer) {
- struct update_pdu updu;
-
- memset(&updu, 0, sizeof(updu));
- updu.block_size = htobe32(sizeof(updu));
- do_xfer(&td->uep, &updu, sizeof(updu), &start_resp,
- sizeof(start_resp), 1, &rxed_size);
- } else {
- rxed_size = sizeof(start_resp);
- if (tpm_send_pkt(td, 0, 0, NULL, 0,
- &start_resp, &rxed_size,
- EXTENSION_FW_UPGRADE) < 0) {
- fprintf(stderr, "Failed to start transfer\n");
- exit(update_error);
- }
- }
-
- /* We got something. Check for errors in response */
- if (rxed_size < 8) {
- fprintf(stderr, "Unexpected response size %zd: ", rxed_size);
- for (i = 0; i < rxed_size; i++)
- fprintf(stderr, " %02x", ((uint8_t *)&start_resp)[i]);
- fprintf(stderr, "\n");
- exit(update_error);
- }
-
- protocol_version = be32toh(start_resp.rpdu.protocol_version);
- if (protocol_version < 5) {
- fprintf(stderr, "Unsupported protocol version %d\n",
- protocol_version);
- exit(update_error);
- }
-
- printf("target running protocol version %d\n", protocol_version);
-
- error_code = be32toh(start_resp.rpdu.return_value);
-
- if (error_code) {
- fprintf(stderr, "Target reporting error %d\n", error_code);
- if (td->ep_type == usb_xfer)
- shut_down(&td->uep);
- exit(update_error);
- }
-
- td->rw_offset = be32toh(start_resp.rpdu.backup_rw_offset);
- td->ro_offset = be32toh(start_resp.rpdu.backup_ro_offset);
-
- /* Running header versions. */
- for (i = 0; i < ARRAY_SIZE(targ.shv); i++) {
- targ.shv[i].minor = be32toh(start_resp.rpdu.shv[i].minor);
- targ.shv[i].major = be32toh(start_resp.rpdu.shv[i].major);
- targ.shv[i].epoch = be32toh(start_resp.rpdu.shv[i].epoch);
- }
-
- for (i = 0; i < ARRAY_SIZE(targ.keyid); i++)
- targ.keyid[i] = be32toh(start_resp.rpdu.keyid[i]);
-
- printf("keyids: RO 0x%08x, RW 0x%08x\n", targ.keyid[0], targ.keyid[1]);
- printf("offsets: backup RO at %#x, backup RW at %#x\n",
- td->ro_offset, td->rw_offset);
-
- pick_sections(td);
-}
-
-/*
- * Channel TPM extension/vendor command over USB. The payload of the USB frame
- * in this case consists of the 2 byte subcommand code concatenated with the
- * command body. The caller needs to indicate if a response is expected, and
- * if it is - of what maximum size.
- */
-static int ext_cmd_over_usb(struct usb_endpoint *uep, uint16_t subcommand,
- const void *cmd_body, size_t body_size,
- void *resp, size_t *resp_size)
-{
- struct update_frame_header *ufh;
- uint16_t *frame_ptr;
- size_t usb_msg_size;
- SHA_CTX ctx;
- uint8_t digest[SHA_DIGEST_LENGTH];
-
- usb_msg_size = sizeof(struct update_frame_header) +
- sizeof(subcommand) + body_size;
-
- ufh = malloc(usb_msg_size);
- if (!ufh) {
- fprintf(stderr, "%s: failed to allocate %zd bytes\n",
- __func__, usb_msg_size);
- return -1;
- }
-
- ufh->block_size = htobe32(usb_msg_size);
- ufh->cmd.block_base = htobe32(CONFIG_EXTENSION_COMMAND);
- frame_ptr = (uint16_t *)(ufh + 1);
- *frame_ptr = htobe16(subcommand);
-
- if (body_size)
- memcpy(frame_ptr + 1, cmd_body, body_size);
-
- /* Calculate the digest. */
- SHA1_Init(&ctx);
- SHA1_Update(&ctx, &ufh->cmd.block_base,
- usb_msg_size -
- offsetof(struct update_frame_header, cmd.block_base));
- SHA1_Final(digest, &ctx);
- memcpy(&ufh->cmd.block_digest, digest, sizeof(ufh->cmd.block_digest));
-
- do_xfer(uep, ufh, usb_msg_size, resp,
- resp_size ? *resp_size : 0, 1, resp_size);
-
- free(ufh);
- return 0;
-}
-
-/*
- * Indicate to the target that update image transfer has been completed. Upon
- * receiveing of this message the target state machine transitions into the
- * 'rx_idle' state. The host may send an extension command to reset the target
- * after this.
- */
-static void send_done(struct usb_endpoint *uep)
-{
- uint32_t out;
-
- /* Send stop request, ignoring reply. */
- out = htobe32(UPGRADE_DONE);
- do_xfer(uep, &out, sizeof(out), &out, 1, 0, NULL);
-}
-
-/*
- * Old cr50 images fail the update if sections are sent out of order. They
- * require each block to have an offset greater than the block that was sent
- * before. RO has a lower offset than RW, so old cr50 images reject RO if it's
- * sent right after RW.
- * This offset restriction expires after 60 seconds. Delay the RO update long
- * enough for cr50 to not care that it has a lower offset than RW.
- *
- * Make the delay 65 seconds instead of 60 to cover differences in the speed of
- * H1's clock and the host clock.
- */
-#define NEXT_SECTION_DELAY 65
-
-/* Support for flashing RO immediately after RW was added in 0.3.20/0.4.20. */
-static int supports_reordered_section_updates(struct signed_header_version *rw)
-{
- return (rw->epoch || rw->major > 4 ||
- (rw->major >= 3 && rw->minor >= 20));
-}
-
-/* Returns number of successfully transmitted image sections. */
-static int transfer_image(struct transfer_descriptor *td,
- uint8_t *data, size_t data_len)
-{
- size_t j;
- int num_txed_sections = 0;
- int needs_delay = !supports_reordered_section_updates(&targ.shv[1]);
-
- /*
- * In case both RO and RW updates are required, make sure the RW
- * section is updated before the RO. The array below keeps sections
- * offsets in the required order.
- */
- const size_t update_order[] = {CONFIG_RW_MEM_OFF,
- CONFIG_RW_B_MEM_OFF,
- CONFIG_RO_MEM_OFF,
- CHIP_RO_B_MEM_OFF};
-
- for (j = 0; j < ARRAY_SIZE(update_order); j++) {
- size_t i;
-
- for (i = 0; i < ARRAY_SIZE(sections); i++) {
- if (sections[i].offset != update_order[j])
- continue;
-
- if (sections[i].ustatus != needed)
- break;
- if (num_txed_sections && needs_delay) {
- /*
- * Delays more than 5 seconds cause the update
- * to timeout. End the update before the delay
- * and set it up after to recover from the
- * timeout.
- */
- if (td->ep_type == usb_xfer)
- send_done(&td->uep);
- printf("Waiting %ds for %s update.\n",
- NEXT_SECTION_DELAY, sections[i].name);
- sleep(NEXT_SECTION_DELAY);
- setup_connection(td);
- }
-
- transfer_section(td,
- data + sections[i].offset,
- sections[i].offset,
- sections[i].size);
- num_txed_sections++;
- }
- }
-
- if (!num_txed_sections)
- printf("nothing to do\n");
- else
- printf("-------\nupdate complete\n");
- return num_txed_sections;
-}
-
-uint32_t send_vendor_command(struct transfer_descriptor *td,
- uint16_t subcommand,
- const void *command_body,
- size_t command_body_size,
- void *response,
- size_t *response_size)
-{
- int32_t rv;
-
- if (td->ep_type == usb_xfer) {
- /*
- * When communicating over USB the response is always supposed
- * to have the result code in the first byte of the response,
- * to be stripped from the actual response body by this
- * function.
- */
- uint8_t temp_response[MAX_BUF_SIZE];
- size_t max_response_size;
-
- if (!response_size) {
- max_response_size = 1;
- } else if (*response_size < (sizeof(temp_response))) {
- max_response_size = *response_size + 1;
- } else {
- fprintf(stderr,
- "Error: Expected response too large (%zd)\n",
- *response_size);
- /* Should happen only when debugging. */
- exit(update_error);
- }
-
- ext_cmd_over_usb(&td->uep, subcommand,
- command_body, command_body_size,
- temp_response, &max_response_size);
- if (!max_response_size) {
- /*
- * we must be talking to an older Cr50 firmware, which
- * does not return the result code in the first byte
- * on success, nothing to do.
- */
- if (response_size)
- *response_size = 0;
- rv = 0;
- } else {
- rv = temp_response[0];
- if (response_size) {
- *response_size = max_response_size - 1;
- memcpy(response,
- temp_response + 1, *response_size);
- }
- }
- } else {
-
- rv = tpm_send_pkt(td, 0, 0,
- command_body, command_body_size,
- response, response_size, subcommand);
-
- if (rv == -1) {
- fprintf(stderr,
- "Error: Failed to send vendor command %d\n",
- subcommand);
- exit(update_error);
- }
- }
-
- return rv; /* This will be converted into uint32_t */
-}
-
-/*
- * Corrupt the header of the inactive rw image to make sure the system can't
- * rollback
- */
-static void invalidate_inactive_rw(struct transfer_descriptor *td)
-{
- /* Corrupt the rw image that is not running. */
- uint32_t rv;
-
- rv = send_vendor_command(td, VENDOR_CC_INVALIDATE_INACTIVE_RW,
- NULL, 0, NULL, NULL);
- if (!rv) {
- printf("Inactive header invalidated\n");
- return;
- }
-
- fprintf(stderr, "*%s: Error %#x\n", __func__, rv);
- exit(update_error);
-}
-
-static struct signed_header_version ver19 = {
- .epoch = 0,
- .major = 0,
- .minor = 19,
-};
-
-static void generate_reset_request(struct transfer_descriptor *td)
-{
- size_t response_size;
- uint8_t response;
- uint16_t subcommand;
- uint8_t command_body[2]; /* Max command body size. */
- size_t command_body_size;
- uint32_t background_update_supported;
- const char *reset_type;
- int rv;
-
- if (protocol_version < 6) {
- if (td->ep_type == usb_xfer) {
- /*
- * Send a second stop request, which should reboot
- * without replying.
- */
- send_done(&td->uep);
- }
- /* Nothing we can do over /dev/tpm0 running versions below 6. */
- return;
- }
-
- /* RW version 0.0.19 and above has support for background updates. */
- background_update_supported = td->background_update_supported ||
- !a_newer_than_b(&ver19, &targ.shv[1]);
-
- /*
- * If this is an upstart request and there is support for background
- * updates, don't post a request now. The target should handle it on
- * the next reboot.
- */
- if (td->upstart_mode && background_update_supported)
- return;
-
- /*
- * If the user explicitly wants it or a reset is needed because h1
- * does not support background updates, request post reset instead of
- * immediate reset. In this case next time the target reboots, the h1
- * will reboot as well, and will consider running the uploaded code.
- *
- * In case target RW version is 19 or above, to reset the target the
- * host is supposed to send the command to enable the uploaded image
- * disabled by default.
- *
- * Otherwise the immediate reset command would suffice.
- */
- /* Most common case. */
- command_body_size = 0;
- response_size = 1;
- if (td->post_reset || td->upstart_mode) {
- subcommand = EXTENSION_POST_RESET;
- reset_type = "posted";
- } else if (background_update_supported) {
- subcommand = VENDOR_CC_TURN_UPDATE_ON;
- command_body_size = sizeof(command_body);
- command_body[0] = 0;
- command_body[1] = 100; /* Reset in 100 ms. */
- reset_type = "requested";
- } else {
- response_size = 0;
- subcommand = VENDOR_CC_IMMEDIATE_RESET;
- reset_type = "triggered";
- }
-
- rv = send_vendor_command(td, subcommand, command_body,
- command_body_size, &response, &response_size);
-
- if (rv) {
- fprintf(stderr, "*%s: Error %#x\n", __func__, rv);
- exit(update_error);
- }
- printf("reboot %s\n", reset_type);
-}
-
-/*
- * Machine output is formatted as "key=value", one key-value pair per line, and
- * parsed by other programs (e.g., debugd). The value part should be specified
- * in the printf-like way. For example:
- *
- * print_machine_output("date", "%d/%d/%d", 2018, 1, 1),
- *
- * which outputs this line in console:
- *
- * date=2018/1/1
- *
- * The key part should not contain '=' or newline. The value part may contain
- * special characters like spaces, quotes, brackets, but not newlines. The
- * newline character means end of value.
- *
- * Any output format change in this function may require similar changes on the
- * programs that are using this gsctool.
- */
-__attribute__((__format__(__printf__, 2, 3)))
-static void print_machine_output(const char *key, const char *format, ...)
-{
- va_list args;
-
- if (strchr(key, '=') != NULL || strchr(key, '\n') != NULL) {
- fprintf(stderr,
- "Error: key %s contains '=' or a newline character.\n",
- key);
- return;
- }
-
- if (strchr(format, '\n') != NULL) {
- fprintf(stderr,
- "Error: value format %s contains a newline character. "
- "\n",
- format);
- return;
- }
-
- va_start(args, format);
-
- printf("%s=", key);
- vprintf(format, args);
- printf("\n");
-
- va_end(args);
-}
-
-/*
- * Prints out the header, including FW versions and board IDs, of the given
- * image. Output in a machine-friendly format if show_machine_output is true.
- */
-static int show_headers_versions(const void *image, bool show_machine_output)
-{
- /*
- * There are 2 FW slots in an image, and each slot has 2 sections, RO
- * and RW. The 2 slots should have identical FW versions and board
- * IDs.
- */
- const struct {
- const char *name;
- uint32_t offset;
- } sections[] = {
- /* Slot A. */
- {"RO", CONFIG_RO_MEM_OFF},
- {"RW", CONFIG_RW_MEM_OFF},
- /* Slot B. */
- {"RO", CHIP_RO_B_MEM_OFF},
- {"RW", CONFIG_RW_B_MEM_OFF}
- };
- const size_t kNumSlots = 2;
- const size_t kNumSectionsPerSlot = 2;
-
- /*
- * String representation of FW version (<epoch>:<major>:<minor>), one
- * string for each FW section.
- */
- char ro_fw_ver[kNumSlots][MAX_FW_VER_LENGTH];
- char rw_fw_ver[kNumSlots][MAX_FW_VER_LENGTH];
-
- uint32_t dev_id0_[kNumSlots];
- uint32_t dev_id1_[kNumSlots];
- uint32_t print_devid = 0;
-
- struct board_id {
- uint32_t id;
- uint32_t mask;
- uint32_t flags;
- } bid[kNumSlots];
-
- char bid_string[kNumSlots][MAX_BOARD_ID_LENGTH];
-
- size_t i;
-
- for (i = 0; i < ARRAY_SIZE(sections); i++) {
- const struct SignedHeader *h =
- (const struct SignedHeader *)
- ((uintptr_t)image + sections[i].offset);
- const size_t slot_idx = i / kNumSectionsPerSlot;
-
- uint32_t cur_bid;
- size_t j;
-
- if (sections[i].name[1] == 'O') {
- /* RO. */
- snprintf(ro_fw_ver[slot_idx], MAX_FW_VER_LENGTH,
- "%d.%d.%d", h->epoch_, h->major_, h->minor_);
- /* No need to read board ID in an RO section. */
- continue;
- } else {
- /* RW. */
- snprintf(rw_fw_ver[slot_idx], MAX_FW_VER_LENGTH,
- "%d.%d.%d", h->epoch_, h->major_, h->minor_);
- }
-
- /*
- * For RW sections, retrieves the board ID fields' contents,
- * which are stored XORed with a padding value.
- */
- bid[slot_idx].id = h->board_id_type ^ SIGNED_HEADER_PADDING;
- bid[slot_idx].mask =
- h->board_id_type_mask ^ SIGNED_HEADER_PADDING;
- bid[slot_idx].flags = h->board_id_flags ^ SIGNED_HEADER_PADDING;
-
- dev_id0_[slot_idx] = h->dev_id0_;
- dev_id1_[slot_idx] = h->dev_id1_;
- /* Print the devid if any slot has a non-zero devid. */
- print_devid |= h->dev_id0_ | h->dev_id1_;
-
- /*
- * If board ID is a 4-uppercase-letter string (as it ought to
- * be), print it as 4 letters, otherwise print it as an 8-digit
- * hex.
- */
- cur_bid = bid[slot_idx].id;
- for (j = 0; j < sizeof(cur_bid); ++j)
- if (!isupper(((const char *)&cur_bid)[j]))
- break;
-
- if (j == sizeof(cur_bid)) {
- cur_bid = be32toh(cur_bid);
- snprintf(bid_string[slot_idx], MAX_BOARD_ID_LENGTH,
- "%.4s", (const char *)&cur_bid);
- } else {
- snprintf(bid_string[slot_idx], MAX_BOARD_ID_LENGTH,
- "%08x", cur_bid);
- }
- }
-
- if (show_machine_output) {
- print_machine_output("IMAGE_RO_FW_VER", "%s", ro_fw_ver[0]);
- print_machine_output("IMAGE_RW_FW_VER", "%s", rw_fw_ver[0]);
- print_machine_output("IMAGE_BID_STRING", "%s", bid_string[0]);
- print_machine_output("IMAGE_BID_MASK", "%08x", bid[0].mask);
- print_machine_output("IMAGE_BID_FLAGS", "%08x", bid[0].flags);
- } else {
- printf("RO_A:%s RW_A:%s[%s:%08x:%08x] ",
- ro_fw_ver[0], rw_fw_ver[0],
- bid_string[0], bid[0].mask, bid[0].flags);
- printf("RO_B:%s RW_B:%s[%s:%08x:%08x]\n",
- ro_fw_ver[1], rw_fw_ver[1],
- bid_string[1], bid[1].mask, bid[1].flags);
-
- if (print_devid) {
- printf("DEVID: 0x%08x 0x%08x", dev_id0_[0],
- dev_id1_[0]);
- /*
- * Only print the second devid if it's different.
- * Separate the devids with tabs, so it's easier to
- * read.
- */
- if (dev_id0_[0] != dev_id0_[1] ||
- dev_id1_[0] != dev_id1_[1])
- printf("\t\t\t\tDEVID_B: 0x%08x 0x%08x",
- dev_id0_[1], dev_id1_[1]);
- printf("\n");
- }
- }
-
- return 0;
-}
-
-/*
- * The default flag value will allow to run images built for any hardware
- * generation of a particular board ID.
- */
-#define DEFAULT_BOARD_ID_FLAG 0xff00
-static int parse_bid(const char *opt,
- struct board_id *bid,
- enum board_id_action *bid_action)
-{
- char *e;
- const char *param2;
- size_t param1_length;
-
- if (!opt) {
- *bid_action = bid_get;
- return 1;
- }
-
- /* Set it here to make bailing out easier later. */
- bid->flags = DEFAULT_BOARD_ID_FLAG;
-
- *bid_action = bid_set; /* Ignored by caller on errors. */
-
- /*
- * Pointer to the optional second component of the command line
- * parameter, when present - separated by a colon.
- */
- param2 = strchr(opt, ':');
- if (param2) {
- param1_length = param2 - opt;
- param2++;
- if (!*param2)
- return 0; /* Empty second parameter. */
- } else {
- param1_length = strlen(opt);
- }
-
- if (!param1_length)
- return 0; /* Colon is the first character of the string? */
-
- if (param1_length <= 4) {
- unsigned i;
-
- /* Input must be a symbolic board name. */
- bid->type = 0;
- for (i = 0; i < param1_length; i++)
- bid->type = (bid->type << 8) | opt[i];
- } else {
- bid->type = (uint32_t)strtoul(opt, &e, 0);
- if ((param2 && (*e != ':')) || (!param2 && *e))
- return 0;
- }
-
- if (param2) {
- bid->flags = (uint32_t)strtoul(param2, &e, 0);
- if (*e)
- return 0;
- }
-
- return 1;
-}
-
-/*
- * Reads a two-character hexadecimal byte from a string. If the string is
- * ill-formed, returns 0. Otherwise, |byte| contains the byte value and the
- * return value is non-zero.
- */
-static int read_hex_byte(const char* s, uint8_t* byte) {
- uint8_t b = 0;
- for (const char* end = s + 2; s < end; ++s) {
- if (*s >= '0' && *s <= '9')
- b = b * 16 + *s - '0';
- else if (*s >= 'A' && *s <= 'F')
- b = b * 16 + 10 + *s - 'A';
- else if (*s >= 'a' && *s <= 'f')
- b = b * 16 + 10 + *s - 'a';
- else
- return 0;
- }
- *byte = b;
- return 1;
-}
-
-static int parse_sn_bits(const char *opt, uint8_t *sn_bits)
-{
- size_t len = strlen(opt);
-
- if (!strncmp(opt, "0x", 2)) {
- opt += 2;
- len -= 2;
- }
- if (len != SN_BITS_SIZE * 2) return 0;
-
- for (int i = 0; i < SN_BITS_SIZE; ++i, opt +=2)
- if (!read_hex_byte(opt, sn_bits++)) return 0;
-
- return 1;
-}
-
-static int parse_sn_inc_rma(const char *opt, uint8_t *arg)
-{
- uint32_t inc;
- char *e;
-
- inc = (uint32_t)strtoul(opt, &e, 0);
-
- if (opt == e || *e != '\0' || inc > 7)
- return 0;
-
- *arg = inc;
- return 1;
-}
-
-static uint32_t common_process_password(struct transfer_descriptor *td,
- enum ccd_vendor_subcommands subcmd)
-{
- size_t response_size;
- uint8_t response;
- uint32_t rv;
- char *password = NULL;
- char *password_copy = NULL;
- size_t copy_len = 0;
- size_t len = 0;
- struct termios oldattr, newattr;
-
- /* Suppress command line echo while password is being entered. */
- tcgetattr(STDIN_FILENO, &oldattr);
- newattr = oldattr;
- newattr.c_lflag &= ~ECHO;
- newattr.c_lflag |= (ICANON | ECHONL);
- tcsetattr(STDIN_FILENO, TCSANOW, &newattr);
-
- /* With command line echo suppressed request password entry twice. */
- printf("Enter password:");
- len = getline(&password, &len, stdin);
- printf("Re-enter password:");
- getline(&password_copy, &copy_len, stdin);
-
- /* Restore command line echo. */
- tcsetattr(STDIN_FILENO, TCSANOW, &oldattr);
-
- /* Empty password will still have the newline. */
- if ((len <= 1) || !password_copy) {
- if (password)
- free(password);
- if (password_copy)
- free(password_copy);
- fprintf(stderr, "Error reading password\n");
- exit(update_error);
- }
-
- /* Compare the two inputs. */
- if (strcmp(password, password_copy)) {
- fprintf(stderr, "Entered passwords don't match\n");
- free(password);
- free(password_copy);
- exit(update_error);
- }
-
- /*
- * Ok, we have a password, let's move it in the buffer to overwrite
- * the newline and free a byte to prepend the subcommand code.
- */
- memmove(password + 1, password, len - 1);
- password[0] = subcmd;
- response_size = sizeof(response);
- rv = send_vendor_command(td, VENDOR_CC_CCD,
- password, len,
- &response, &response_size);
- free(password);
- free(password_copy);
-
- if ((rv != VENDOR_RC_SUCCESS) && (rv != VENDOR_RC_IN_PROGRESS))
- fprintf(stderr, "Error sending password: rv %d, response %d\n",
- rv, response_size ? response : 0);
-
- return rv;
-}
-
-static void process_password(struct transfer_descriptor *td)
-{
- if (common_process_password(td, CCDV_PASSWORD) == VENDOR_RC_SUCCESS)
- return;
-
- exit(update_error);
-}
-
-void poll_for_pp(struct transfer_descriptor *td,
- uint16_t command,
- uint8_t poll_type)
-{
- uint8_t response;
- uint8_t prev_response;
- size_t response_size;
- int rv;
-
- prev_response = ~0; /* Guaranteed invalid value. */
-
- while (1) {
- response_size = sizeof(response);
- rv = send_vendor_command(td, command,
- &poll_type, sizeof(poll_type),
- &response, &response_size);
-
- if (((rv != VENDOR_RC_SUCCESS) && (rv != VENDOR_RC_IN_PROGRESS))
- || (response_size != 1)) {
- fprintf(stderr, "Error: rv %d, response %d\n",
- rv, response_size ? response : 0);
- exit(update_error);
- }
-
- if (response == CCD_PP_DONE) {
- printf("PP Done!\n");
- return;
- }
-
- if (response == CCD_PP_CLOSED) {
- fprintf(stderr,
- "Error: Physical presence check timeout!\n");
- exit(update_error);
- }
-
-
- if (response == CCD_PP_AWAITING_PRESS) {
- printf("Press PP button now!\n");
- } else if (response == CCD_PP_BETWEEN_PRESSES) {
- if (prev_response != response)
- printf("Another press will be required!\n");
- } else {
- fprintf(stderr, "Error: unknown poll result %d\n",
- response);
- exit(update_error);
- }
- prev_response = response;
-
- usleep(500 * 1000); /* Poll every half a second. */
- }
-
-}
-
-static void print_ccd_info(void *response, size_t response_size)
-{
- struct ccd_info_response ccd_info;
- size_t i;
- const struct ccd_capability_info cap_info[] = CAP_INFO_DATA;
- const char *state_names[] = CCD_STATE_NAMES;
- const char *cap_state_names[] = CCD_CAP_STATE_NAMES;
- uint32_t caps_bitmap = 0;
-
- if (response_size != sizeof(ccd_info)) {
- fprintf(stderr, "Unexpected CCD info response size %zd\n",
- response_size);
- exit(update_error);
- }
-
- memcpy(&ccd_info, response, sizeof(ccd_info));
-
- /* Convert it back to host endian format. */
- ccd_info.ccd_flags = be32toh(ccd_info.ccd_flags);
- for (i = 0; i < ARRAY_SIZE(ccd_info.ccd_caps_current); i++) {
- ccd_info.ccd_caps_current[i] =
- be32toh(ccd_info.ccd_caps_current[i]);
- ccd_info.ccd_caps_defaults[i] =
- be32toh(ccd_info.ccd_caps_defaults[i]);
- }
-
- /* Now report CCD state on the console. */
- printf("State: %s\n", ccd_info.ccd_state > ARRAY_SIZE(state_names) ?
- "Error" : state_names[ccd_info.ccd_state]);
- printf("Password: %s\n", (ccd_info.ccd_indicator_bitmap &
- CCD_INDICATOR_BIT_HAS_PASSWORD) ? "Set" : "None");
- printf("Flags: %#06x\n", ccd_info.ccd_flags);
- printf("Capabilities, current and default:\n");
- for (i = 0; i < CCD_CAP_COUNT; i++) {
- int is_enabled;
- int index;
- int shift;
- int cap_current;
- int cap_default;
-
- index = i / (32 / CCD_CAP_BITS);
- shift = (i % (32 / CCD_CAP_BITS)) * CCD_CAP_BITS;
-
- cap_current = (ccd_info.ccd_caps_current[index] >> shift)
- & CCD_CAP_BITMASK;
- cap_default = (ccd_info.ccd_caps_defaults[index] >> shift)
- & CCD_CAP_BITMASK;
-
- if (ccd_info.ccd_force_disabled) {
- is_enabled = 0;
- } else {
- switch (cap_current) {
- case CCD_CAP_STATE_ALWAYS:
- is_enabled = 1;
- break;
- case CCD_CAP_STATE_UNLESS_LOCKED:
- is_enabled = (ccd_info.ccd_state !=
- CCD_STATE_LOCKED);
- break;
- default:
- is_enabled = (ccd_info.ccd_state ==
- CCD_STATE_OPENED);
- break;
- }
- }
-
- printf(" %-15s %c %s",
- cap_info[i].name,
- is_enabled ? 'Y' : '-',
- cap_state_names[cap_current]);
-
- if (cap_current != cap_default)
- printf(" (%s)", cap_state_names[cap_default]);
-
- printf("\n");
-
- if (is_enabled)
- caps_bitmap |= (1 << i);
- }
- printf("CCD caps bitmap: %#x\n", caps_bitmap);
- printf("Capabilities are %s.\n", (ccd_info.ccd_indicator_bitmap &
- CCD_INDICATOR_BIT_ALL_CAPS_DEFAULT) ? "default" : "modified");
-}
-
-static void process_ccd_state(struct transfer_descriptor *td, int ccd_unlock,
- int ccd_open, int ccd_lock, int ccd_info)
-{
- uint8_t payload;
- /* Max possible response size is when ccd_info is requested. */
- uint8_t response[sizeof(struct ccd_info_response)];
- size_t response_size;
- int rv;
-
- if (ccd_unlock)
- payload = CCDV_UNLOCK;
- else if (ccd_open)
- payload = CCDV_OPEN;
- else if (ccd_lock)
- payload = CCDV_LOCK;
- else
- payload = CCDV_GET_INFO;
-
- response_size = sizeof(response);
- rv = send_vendor_command(td, VENDOR_CC_CCD,
- &payload, sizeof(payload),
- &response, &response_size);
-
- /*
- * If password is required - try sending the same subcommand
- * accompanied by user password.
- */
- if (rv == VENDOR_RC_PASSWORD_REQUIRED)
- rv = common_process_password(td, payload);
-
- if (rv == VENDOR_RC_SUCCESS) {
- if (ccd_info)
- print_ccd_info(response, response_size);
- return;
- }
-
- if (rv != VENDOR_RC_IN_PROGRESS) {
- fprintf(stderr, "Error: rv %d, response %d\n",
- rv, response_size ? response[0] : 0);
- exit(update_error);
- }
-
- /*
- * Physical presence process started, poll for the state the user
- * asked for. Only two subcommands would return 'IN_PROGRESS'.
- */
- if (ccd_unlock)
- poll_for_pp(td, VENDOR_CC_CCD, CCDV_PP_POLL_UNLOCK);
- else
- poll_for_pp(td, VENDOR_CC_CCD, CCDV_PP_POLL_OPEN);
-}
-
-static void process_wp(struct transfer_descriptor *td)
-{
- size_t response_size;
- uint8_t response;
- int rv = 0;
-
- response_size = sizeof(response);
-
- printf("Getting WP\n");
-
- rv = send_vendor_command(td, VENDOR_CC_WP, NULL, 0,
- &response, &response_size);
- if (rv != VENDOR_RC_SUCCESS) {
- fprintf(stderr, "Error %d getting write protect\n", rv);
- exit(update_error);
- }
- if (response_size != sizeof(response)) {
- fprintf(stderr, "Unexpected response size %zd while getting "
- "write protect\n",
- response_size);
- exit(update_error);
- }
-
- printf("WP: %08x\n", response);
- printf("Flash WP: %s%s\n",
- response & WPV_FORCE ? "forced " : "",
- response & WPV_ENABLE ? "enabled" : "disabled");
- printf(" at boot: %s\n",
- !(response & WPV_ATBOOT_SET) ? "follow_batt_pres" :
- response & WPV_ATBOOT_ENABLE ? "forced enabled" :
- "forced disabled");
-}
-
-
-void process_bid(struct transfer_descriptor *td,
- enum board_id_action bid_action,
- struct board_id *bid,
- bool show_machine_output)
-{
- size_t response_size;
-
- if (bid_action == bid_get) {
-
- response_size = sizeof(*bid);
- send_vendor_command(td, VENDOR_CC_GET_BOARD_ID,
- bid, sizeof(*bid),
- bid, &response_size);
-
- if (response_size != sizeof(*bid)) {
- fprintf(stderr,
- "Error reading board ID: response size %zd, "
- "first byte %#02x\n",
- response_size,
- response_size ? *(uint8_t *)&bid : -1);
- exit(update_error);
- }
-
- if (show_machine_output) {
- print_machine_output(
- "BID_TYPE", "%08x", be32toh(bid->type));
- print_machine_output(
- "BID_TYPE_INV", "%08x", be32toh(bid->type_inv));
- print_machine_output(
- "BID_FLAGS", "%08x", be32toh(bid->flags));
-
- for (int i = 0; i < 4; i++) {
- if (!isupper(((const char *)bid)[i])) {
- print_machine_output(
- "BID_RLZ", "%s", "????");
- return;
- }
- }
-
- print_machine_output(
- "BID_RLZ", "%c%c%c%c",
- ((const char *)bid)[0],
- ((const char *)bid)[1],
- ((const char *)bid)[2],
- ((const char *)bid)[3]);
- } else {
- printf("Board ID space: %08x:%08x:%08x\n",
- be32toh(bid->type),
- be32toh(bid->type_inv),
- be32toh(bid->flags));
-
- }
-
- return;
- }
-
- if (bid_action == bid_set) {
- /* Sending just two fields: type and flags. */
- uint32_t command_body[2];
- uint8_t response;
-
- command_body[0] = htobe32(bid->type);
- command_body[1] = htobe32(bid->flags);
-
- response_size = sizeof(command_body);
- send_vendor_command(td, VENDOR_CC_SET_BOARD_ID,
- command_body, sizeof(command_body),
- command_body, &response_size);
-
- /*
- * Speculative assignment: the response is expected to be one
- * byte in size and be placed in the first byte of the buffer.
- */
- response = *((uint8_t *)command_body);
-
- if (response_size == 1) {
- if (!response)
- return; /* Success! */
-
- fprintf(stderr, "Error %d while setting board id\n",
- response);
- } else {
- fprintf(stderr, "Unexpected response size %zd"
- " while setting board id\n",
- response_size);
- }
- exit(update_error);
- }
-}
-
-static void process_sn_bits(struct transfer_descriptor *td,
- uint8_t *sn_bits)
-{
- int rv;
- uint8_t response_code;
- size_t response_size = sizeof(response_code);
-
- rv = send_vendor_command(td, VENDOR_CC_SN_SET_HASH,
- sn_bits, SN_BITS_SIZE,
- &response_code, &response_size);
-
- if (rv) {
- fprintf(stderr, "Error %d while sending vendor command\n", rv);
- exit(update_error);
- }
-
- if (response_size != 1) {
- fprintf(stderr,
- "Unexpected response size while setting sn bits\n");
- exit(update_error);
- }
-
- if (response_code != 0) {
- fprintf(stderr, "Error %d while setting sn bits\n",
- response_code);
- exit(update_error);
- }
-}
-
-static void process_sn_inc_rma(struct transfer_descriptor *td,
- uint8_t arg)
-{
- int rv;
- uint8_t response_code;
- size_t response_size = sizeof(response_code);
-
- rv = send_vendor_command(td, VENDOR_CC_SN_INC_RMA,
- &arg, sizeof(arg),
- &response_code, &response_size);
- if (rv) {
- fprintf(stderr, "Error %d while sending vendor command\n", rv);
- exit(update_error);
- }
-
- if (response_size != 1) {
- fprintf(stderr,
- "Unexpected response size while "
- "incrementing sn rma count\n");
- exit(update_error);
- }
-
- if (response_code != 0) {
- fprintf(stderr, "Error %d while incrementing rma count\n",
- response_code);
- exit(update_error);
- }
-}
-
-/* Get/Set the primary seed of the info1 manufacture state. */
-static int process_endorsement_seed(struct transfer_descriptor *td,
- const char *endorsement_seed_str)
-{
- uint8_t endorsement_seed[32];
- uint8_t response_seed[32];
- size_t seed_size = sizeof(endorsement_seed);
- size_t response_size = sizeof(response_seed);
- size_t i;
- int rv;
-
- if (!endorsement_seed_str) {
- rv = send_vendor_command(td, VENDOR_CC_ENDORSEMENT_SEED, NULL,
- 0, response_seed, &response_size);
- if (rv) {
- fprintf(stderr, "Error sending vendor command %d\n",
- rv);
- return update_error;
- }
- printf("Endorsement key seed: ");
- for (i = 0; i < response_size; i++)
- printf("%02x", response_seed[i]);
- printf("\n");
- return 0;
- }
- if (seed_size * 2 != strlen(endorsement_seed_str)) {
- printf("Invalid seed %s\n", endorsement_seed_str);
- return update_error;
- }
-
- for (i = 0; i < seed_size; i++) {
- int nibble;
- char c;
-
- c = endorsement_seed_str[2 * i];
- nibble = from_hexascii(c);
- if (nibble < 0) {
- fprintf(stderr, "Error: Non hex character in seed %c\n",
- c);
- return update_error;
- }
- endorsement_seed[i] = nibble << 4;
-
- c = endorsement_seed_str[2 * i + 1];
- nibble = from_hexascii(c);
- if (nibble < 0) {
- fprintf(stderr, "Error: Non hex character in seed %c\n",
- c);
- return update_error;
- }
- endorsement_seed[i] |= nibble;
- }
-
- printf("Setting seed: %s\n", endorsement_seed_str);
- rv = send_vendor_command(td, VENDOR_CC_ENDORSEMENT_SEED,
- endorsement_seed, seed_size,
- response_seed, &response_size);
- if (rv == VENDOR_RC_NOT_ALLOWED) {
- fprintf(stderr, "Seed already set\n");
- return update_error;
- }
- if (rv) {
- fprintf(stderr, "Error sending vendor command %d\n", rv);
- return update_error;
- }
- printf("Updated endorsement key seed.\n");
- return 0;
-}
-
-/*
- * Retrieve the RMA authentication challenge from the Cr50, print out the
- * challenge on the console, then prompt the user for the authentication code,
- * and send the code back to Cr50. The Cr50 would report if the code matched
- * its expectations or not.
- */
-static void process_rma(struct transfer_descriptor *td, const char *authcode)
-{
- char rma_response[81];
- size_t response_size = sizeof(rma_response);
- size_t i;
- size_t auth_size = 0;
-
- if (!authcode) {
- send_vendor_command(td, VENDOR_CC_RMA_CHALLENGE_RESPONSE,
- NULL, 0, rma_response, &response_size);
-
- if (response_size == 1) {
- fprintf(stderr, "error %d\n", rma_response[0]);
- if (td->ep_type == usb_xfer)
- shut_down(&td->uep);
- exit(update_error);
- }
-
- printf("Challenge:");
- for (i = 0; i < response_size; i++) {
- if (!(i % 5)) {
- if (!(i % 40))
- printf("\n");
- printf(" ");
- }
- printf("%c", rma_response[i]);
- }
- printf("\n");
- return;
- }
-
- if (!*authcode) {
- printf("Empty response.\n");
- exit(update_error);
- return;
- }
-
- if (!strcmp(authcode, "disable")) {
- printf("Invalid arg. Try using 'gsctool -F disable'\n");
- exit(update_error);
- return;
- }
-
- printf("Processing response...\n");
- auth_size = strlen(authcode);
- response_size = sizeof(rma_response);
-
- send_vendor_command(td, VENDOR_CC_RMA_CHALLENGE_RESPONSE,
- authcode, auth_size,
- rma_response, &response_size);
-
- if (response_size == 1) {
- fprintf(stderr, "\nrma unlock failed, code %d ",
- *rma_response);
- switch (*rma_response) {
- case VENDOR_RC_BOGUS_ARGS:
- fprintf(stderr, "(wrong authcode size)\n");
- break;
- case VENDOR_RC_INTERNAL_ERROR:
- fprintf(stderr, "(authcode mismatch)\n");
- break;
- default:
- fprintf(stderr, "(unknown error)\n");
- break;
- }
- if (td->ep_type == usb_xfer)
- shut_down(&td->uep);
- exit(update_error);
- }
- printf("RMA unlock succeeded.\n");
-}
-
-/*
- * Enable or disable factory mode. Factory mode will only be enabled if HW
- * write protect is removed.
- */
-static void process_factory_mode(struct transfer_descriptor *td,
- const char *arg)
-{
- uint8_t rma_response;
- size_t response_size = sizeof(rma_response);
- char *cmd_str;
- int rv;
- uint16_t subcommand;
-
- if (!strcasecmp(arg, "disable")) {
- subcommand = VENDOR_CC_DISABLE_FACTORY;
- cmd_str = "dis";
- } else if (!strcasecmp(arg, "enable")) {
- subcommand = VENDOR_CC_RESET_FACTORY;
- cmd_str = "en";
-
- } else {
- fprintf(stderr, "Invalid factory mode arg %s", arg);
- exit(update_error);
- }
-
- printf("%sabling factory mode\n", cmd_str);
- rv = send_vendor_command(td, subcommand, NULL, 0, &rma_response,
- &response_size);
- if (rv) {
- fprintf(stderr, "Failed %sabling factory mode\nvc error "
- "%d\n", cmd_str, rv);
- if (response_size == 1)
- fprintf(stderr, "ec error %d\n", rma_response);
- exit(update_error);
- }
- printf("Factory %sable succeeded.\n", cmd_str);
-}
-
-static void report_version(void)
-{
- /* Get version from the generated file, ignore the underscore prefix. */
- const char *v = strchr(VERSION, '_');
-
- printf("Version: %s, built on %s by %s\n", v ? v + 1 : "?",
- DATE, BUILDER);
- exit(0);
-}
-
-/*
- * Either change or query TPM mode value.
- */
-static int process_tpm_mode(struct transfer_descriptor *td,
- const char *arg)
-{
- int rv;
- size_t command_size;
- size_t response_size;
- uint8_t response;
- uint8_t command_body;
-
- response_size = sizeof(response);
- if (!arg) {
- command_size = 0;
- } else if (!strcasecmp(arg, "disable")) {
- command_size = sizeof(command_body);
- command_body = (uint8_t) TPM_MODE_DISABLED;
- } else if (!strcasecmp(arg, "enable")) {
- command_size = sizeof(command_body);
- command_body = (uint8_t) TPM_MODE_ENABLED;
- } else {
- fprintf(stderr, "Invalid tpm mode arg: %s.\n", arg);
- return update_error;
- }
-
- rv = send_vendor_command(td, VENDOR_CC_TPM_MODE,
- &command_body, command_size,
- &response, &response_size);
- if (rv) {
- fprintf(stderr, "Error %d in setting TPM mode.\n", rv);
- return update_error;
- }
- if (response_size != sizeof(response)) {
- fprintf(stderr, "Error in the size of response,"
- " %zu.\n", response_size);
- return update_error;
- }
- if (response >= TPM_MODE_MAX) {
- fprintf(stderr, "Error in the value of response,"
- " %d.\n", response);
- return update_error;
- }
-
- printf("TPM Mode: %s (%d)\n", (response == TPM_MODE_DISABLED) ?
- "disabled" : "enabled", response);
-
- return rv;
-}
-
-/*
- * Retrieve from H1 flash log entries which are newer than the passed in
- * timestamp.
- *
- * On error retry a few times just in case flash log is locked by a concurrent
- * access.
- */
-static int process_get_flog(struct transfer_descriptor *td, uint32_t prev_stamp)
-{
- int rv;
- const int max_retries = 3;
- int retries = max_retries;
-
- while (retries--) {
- union entry_u entry;
- size_t resp_size;
- size_t i;
-
- resp_size = sizeof(entry);
- rv = send_vendor_command(td, VENDOR_CC_POP_LOG_ENTRY,
- &prev_stamp, sizeof(prev_stamp),
- &entry, &resp_size);
-
- if (rv) {
- /*
- * Flash log could be momentarily locked by a
- * concurrent access, let it settle and try again, 10
- * ms should be enough.
- */
- usleep(10 * 1000);
- continue;
- }
-
- if (resp_size == 0)
- /* No more entries. */
- return 0;
-
- memcpy(&prev_stamp, &entry.r.timestamp, sizeof(prev_stamp));
- printf("%10u:%02x", prev_stamp, entry.r.type);
- for (i = 0; i < FLASH_LOG_PAYLOAD_SIZE(entry.r.size); i++)
- printf(" %02x", entry.r.payload[i]);
- printf("\n");
- retries = max_retries;
- }
-
- fprintf(stderr, "%s: error %d\n", __func__, rv);
-
- return rv;
-}
-
-static int process_tstamp(struct transfer_descriptor *td,
- const char *tstamp_ascii)
-{
- char *e;
- size_t expected_response_size;
- size_t message_size;
- size_t response_size;
- uint32_t rv;
- uint32_t tstamp = 0;
- uint8_t max_response[sizeof(uint32_t)];
-
- if (tstamp_ascii) {
- tstamp = strtoul(tstamp_ascii, &e, 10);
- if (*e) {
- fprintf(stderr, "invalid base timestamp value \"%s\"\n",
- tstamp_ascii);
- return -1;
- }
- tstamp = htobe32(tstamp);
- expected_response_size = 0;
- message_size = sizeof(tstamp);
- } else {
- expected_response_size = 4;
- message_size = 0;
- }
-
- response_size = sizeof(max_response);
- rv = send_vendor_command(td, VENDOR_CC_FLOG_TIMESTAMP, &tstamp,
- message_size, max_response, &response_size);
-
- if (rv) {
- fprintf(stderr, "error: return value %d\n", rv);
- return rv;
- }
- if (response_size != expected_response_size) {
- fprintf(stderr, "error: got %zd bytes, expected %zd\n",
- response_size, expected_response_size);
- return -1; /* Should never happen. */
- }
-
- if (response_size) {
- memcpy(&tstamp, max_response, sizeof(tstamp));
- printf("Current H1 time is %d\n", be32toh(tstamp));
- }
- return 0;
-}
-
-/*
- * Search the passed in zero terminated array of options_map structures for
- * option 'option'.
- *
- * If found - set the corresponding integer to 1 and return 1. If not found -
- * return 0.
- */
-static int check_boolean(const struct options_map *omap, char option)
-{
- do {
- if (omap->opt != option)
- continue;
-
- *omap->flag = 1;
- return 1;
- } while ((++omap)->opt);
-
- return 0;
-}
-
-/*
- * Set the long_opts table and short_opts string.
- *
- * This function allows to avoid maintaining two command line option
- * descriptions, for short and long forms.
- *
- * The long_opts table is built based on the cmd_line_options table contents,
- * and the short form is built based on the long_opts table contents.
- *
- * The 'required_argument' short options are followed by ':'.
- *
- * The passed in long_opts array and short_opts string are guaranteed to
- * accommodate all necessary objects/characters.
- */
-static void set_opt_descriptors(struct option *long_opts, char *short_opts)
-{
- size_t i;
- int j;
-
- for (i = j = 0; i < ARRAY_SIZE(cmd_line_options); i++) {
- long_opts[i] = cmd_line_options[i].opt;
- short_opts[j++] = long_opts[i].val;
- if (long_opts[i].has_arg == required_argument)
- short_opts[j++] = ':';
- }
-}
-
-/*
- * Find the long_opts table index where .val field is set to the passed in
- * short option value.
- */
-static int get_longindex(int short_opt, const struct option *long_opts)
-{
- int i;
-
- for (i = 0; long_opts[i].name; i++)
- if (long_opts[i].val == short_opt)
- return i;
-
- /*
- * We could never come here as the short options list is compiled
- * based on long options table.
- */
- fprintf(stderr, "could not find long opt table index for %d\n",
- short_opt);
- exit(1);
-
- return -1; /* Not reached. */
-}
-
-/*
- * Combine searching for command line parameters and optional arguments.
- *
- * The canonical short options description string does not allow to specify
- * that a command line argument expects an optional parameter. but gsctool
- * users expect to be able to use the following styles for optional
- * parameters:
- *
- * a) -x <param value>
- * b) --x_long <param_value>
- * c) --x_long=<param_value>
- *
- * Styles a) and b) are not supported standard getopt_long(), this function
- * adds ability to handle cases a) and b).
- */
-static int getopt_all(int argc, char *argv[])
-{
- int longindex = -1;
- static char short_opts[2 * ARRAY_SIZE(cmd_line_options)] = {};
- static struct option long_opts[ARRAY_SIZE(cmd_line_options) + 1] = {};
- int i;
-
- if (!short_opts[0])
- set_opt_descriptors(long_opts, short_opts);
-
- i = getopt_long(argc, argv, short_opts, long_opts, &longindex);
- if (i != -1) {
-
- if (longindex < 0) {
- /*
- * longindex is not set, this must have been the short
- * option case, Find the long_opts table index based
- * on the short option value.
- */
- longindex = get_longindex(i, long_opts);
- }
-
- if (long_opts[longindex].has_arg == optional_argument) {
- /*
- * This command line option may include an argument,
- * let's check if it is there as the next token in the
- * command line.
- */
- if (!optarg && argv[optind] && argv[optind][0] != '-')
- /* Yes, it is. */
- optarg = argv[optind++];
- }
- }
-
- return i;
-}
-
-int main(int argc, char *argv[])
-{
- struct transfer_descriptor td;
- int errorcnt;
- uint8_t *data = 0;
- size_t data_len = 0;
- uint16_t vid = 0;
- uint16_t pid = 0;
- int i;
- size_t j;
- int transferred_sections = 0;
- int binary_vers = 0;
- int show_fw_ver = 0;
- int rma = 0;
- const char *rma_auth_code;
- int get_endorsement_seed = 0;
- const char *endorsement_seed_str;
- int corrupt_inactive_rw = 0;
- struct board_id bid;
- enum board_id_action bid_action;
- int password = 0;
- int ccd_open = 0;
- int ccd_unlock = 0;
- int ccd_lock = 0;
- int ccd_info = 0;
- int get_flog = 0;
- uint32_t prev_log_entry = 0;
- int wp = 0;
- int try_all_transfer = 0;
- int tpm_mode = 0;
- bool show_machine_output = false;
- int tstamp = 0;
- const char *tstamp_arg = NULL;
-
- const char *exclusive_opt_error =
- "Options -a, -s and -t are mutually exclusive\n";
- const char *openbox_desc_file = NULL;
- int factory_mode = 0;
- char *factory_mode_arg;
- char *tpm_mode_arg = NULL;
- char *serial = NULL;
- int sn_bits = 0;
- uint8_t sn_bits_arg[SN_BITS_SIZE];
- int sn_inc_rma = 0;
- uint8_t sn_inc_rma_arg;
-
- /*
- * All options which result in setting a Boolean flag to True, along
- * with addresses of the flags. Terminated by a zeroed entry.
- */
- const struct options_map omap[] = {
- { 'B', &td.background_update_supported},
- { 'b', &binary_vers },
- { 'c', &corrupt_inactive_rw },
- { 'f', &show_fw_ver },
- { 'I', &ccd_info },
- { 'k', &ccd_lock },
- { 'o', &ccd_open },
- { 'P', &password },
- { 'p', &td.post_reset },
- { 'U', &ccd_unlock },
- { 'u', &td.upstart_mode },
- { 'V', &verbose_mode },
- { 'w', &wp },
- {},
- };
-
- /*
- * Explicitly sets buffering type to line buffered so that output
- * lines can be written to pipe instantly. This is needed when the
- * cr50-verify-ro.sh execution in verify_ro is moved from crosh to
- * debugd.
- */
- setlinebuf(stdout);
-
- progname = strrchr(argv[0], '/');
- if (progname)
- progname++;
- else
- progname = argv[0];
-
- /* Usb transfer - default mode. */
- memset(&td, 0, sizeof(td));
- td.ep_type = usb_xfer;
-
- bid_action = bid_none;
- errorcnt = 0;
- opterr = 0; /* quiet, you */
-
- while ((i = getopt_all(argc, argv)) != -1) {
- if (check_boolean(omap, i))
- continue;
- switch (i) {
- case 'a':
- if (td.ep_type) {
- errorcnt++;
- fprintf(stderr, "%s", exclusive_opt_error);
- break;
- }
- try_all_transfer = 1;
- /* Try dev_xfer first. */
- td.ep_type = dev_xfer;
- break;
- case 'd':
- if (!parse_vidpid(optarg, &vid, &pid)) {
- fprintf(stderr,
- "Invalid device argument: \"%s\"\n",
- optarg);
- errorcnt++;
- }
- break;
- case 'e':
- get_endorsement_seed = 1;
- endorsement_seed_str = optarg;
- break;
- case 'F':
- factory_mode = 1;
- factory_mode_arg = optarg;
- break;
- case 'h':
- usage(errorcnt);
- break;
- case 'i':
- if (!parse_bid(optarg, &bid, &bid_action)) {
- fprintf(stderr,
- "Invalid board id argument: \"%s\"\n",
- optarg);
- errorcnt++;
- }
- break;
- case 'L':
- get_flog = 1;
- if (optarg)
- prev_log_entry = strtoul(optarg, NULL, 0);
- break;
- case 'M':
- show_machine_output = true;
- break;
- case 'm':
- tpm_mode = 1;
- tpm_mode_arg = optarg;
- break;
- case 'n':
- serial = optarg;
- break;
- case 'O':
- openbox_desc_file = optarg;
- break;
- case 'q':
- td.force_ro = 1;
- break;
- case 'r':
- rma = 1;
- rma_auth_code = optarg;
- break;
- case 'R':
- sn_inc_rma = 1;
- if (!parse_sn_inc_rma(optarg, &sn_inc_rma_arg)) {
- fprintf(stderr,
- "Invalid sn_rma_inc argument: \"%s\"\n",
- optarg);
- errorcnt++;
- }
-
- break;
- case 's':
- if (td.ep_type || try_all_transfer) {
- errorcnt++;
- fprintf(stderr, "%s", exclusive_opt_error);
- break;
- }
- td.ep_type = dev_xfer;
- break;
- case 'S':
- sn_bits = 1;
- if (!parse_sn_bits(optarg, sn_bits_arg)) {
- fprintf(stderr,
- "Invalid sn_bits argument: \"%s\"\n",
- optarg);
- errorcnt++;
- }
-
- break;
- case 't':
- if (td.ep_type || try_all_transfer) {
- errorcnt++;
- fprintf(stderr, "%s", exclusive_opt_error);
- break;
- }
- td.ep_type = ts_xfer;
- break;
- case 'T':
- tstamp = 1;
- tstamp_arg = optarg;
- break;
- case 'v':
- report_version(); /* This will call exit(). */
- break;
- case 0: /* auto-handled option */
- break;
- case '?':
- if (optopt)
- fprintf(stderr, "Unrecognized option: -%c\n",
- optopt);
- else
- fprintf(stderr, "Unrecognized option: %s\n",
- argv[optind - 1]);
- errorcnt++;
- break;
- case ':':
- fprintf(stderr, "Missing argument to %s\n",
- argv[optind - 1]);
- errorcnt++;
- break;
- default:
- fprintf(stderr, "Internal error at %s:%d\n",
- __FILE__, __LINE__);
- exit(update_error);
- }
- }
-
- if (errorcnt)
- usage(errorcnt);
-
- /*
- * If no usb device information was given, default to the using cr50
- * vendor and product id to find the usb device.
- */
- if (!serial && !vid && !pid) {
- vid = VID;
- pid = PID;
- }
-
- if ((bid_action == bid_none) &&
- !ccd_info &&
- !ccd_lock &&
- !ccd_open &&
- !ccd_unlock &&
- !corrupt_inactive_rw &&
- !get_flog &&
- !get_endorsement_seed &&
- !factory_mode &&
- !password &&
- !rma &&
- !show_fw_ver &&
- !sn_bits &&
- !sn_inc_rma &&
- !openbox_desc_file &&
- !tstamp &&
- !tpm_mode &&
- !wp) {
- if (optind >= argc) {
- fprintf(stderr,
- "\nERROR: Missing required <binary image>\n\n");
- usage(1);
- }
-
- data = get_file_or_die(argv[optind], &data_len);
- printf("read %zd(%#zx) bytes from %s\n",
- data_len, data_len, argv[optind]);
- if (data_len != CONFIG_FLASH_SIZE) {
- fprintf(stderr, "Image file is not %d bytes\n",
- CONFIG_FLASH_SIZE);
- exit(update_error);
- }
-
- fetch_header_versions(data);
-
- if (binary_vers)
- exit(show_headers_versions(data, show_machine_output));
- } else {
- if (optind < argc)
- printf("Ignoring binary image %s\n", argv[optind]);
- }
-
-
- if (((bid_action != bid_none) + !!rma + !!password + !!ccd_open +
- !!ccd_unlock + !!ccd_lock + !!ccd_info + !!get_flog +
- !!openbox_desc_file + !!factory_mode + !!wp +
- !!get_endorsement_seed) > 1) {
- fprintf(stderr,
- "ERROR: options"
- "-e, -F, -I, -i, -k, -L, -O, -o, -P, -r, -U and -w "
- "are mutually exclusive\n");
- exit(update_error);
- }
-
- if (td.ep_type == usb_xfer) {
- if (usb_findit(serial, vid, pid, USB_SUBCLASS_GOOGLE_CR50,
- USB_PROTOCOL_GOOGLE_CR50_NON_HC_FW_UPDATE,
- &td.uep))
- exit(update_error);
- } else if (td.ep_type == dev_xfer) {
- td.tpm_fd = open("/dev/tpm0", O_RDWR);
- if (td.tpm_fd < 0) {
- if (!try_all_transfer) {
- perror("Could not open TPM");
- exit(update_error);
- }
- td.ep_type = ts_xfer;
- }
- }
-
- if (openbox_desc_file)
- return verify_ro(&td, openbox_desc_file, show_machine_output);
-
- if (ccd_unlock || ccd_open || ccd_lock || ccd_info)
- process_ccd_state(&td, ccd_unlock, ccd_open,
- ccd_lock, ccd_info);
-
- if (password)
- process_password(&td);
-
- if (bid_action != bid_none)
- process_bid(&td, bid_action, &bid, show_machine_output);
-
- if (get_endorsement_seed)
- exit(process_endorsement_seed(&td, endorsement_seed_str));
-
- if (rma)
- process_rma(&td, rma_auth_code);
-
- if (factory_mode)
- process_factory_mode(&td, factory_mode_arg);
- if (wp)
- process_wp(&td);
-
- if (corrupt_inactive_rw)
- invalidate_inactive_rw(&td);
-
- if (tpm_mode) {
- int rv = process_tpm_mode(&td, tpm_mode_arg);
-
- exit(rv);
- }
-
- if (tstamp)
- return process_tstamp(&td, tstamp_arg);
-
- if (sn_bits)
- process_sn_bits(&td, sn_bits_arg);
-
- if (sn_inc_rma)
- process_sn_inc_rma(&td, sn_inc_rma_arg);
-
- if (get_flog)
- process_get_flog(&td, prev_log_entry);
-
- if (data || show_fw_ver) {
-
- setup_connection(&td);
-
- if (data) {
- transferred_sections = transfer_image(&td,
- data, data_len);
- free(data);
- }
-
- /*
- * Move USB updater sate machine to idle state so that vendor
- * commands can be processed later, if any.
- */
- if (td.ep_type == usb_xfer)
- send_done(&td.uep);
-
- if (transferred_sections)
- generate_reset_request(&td);
-
- if (show_fw_ver) {
- if (show_machine_output) {
- print_machine_output("RO_FW_VER", "%d.%d.%d",
- targ.shv[0].epoch,
- targ.shv[0].major,
- targ.shv[0].minor);
- print_machine_output("RW_FW_VER", "%d.%d.%d",
- targ.shv[1].epoch,
- targ.shv[1].major,
- targ.shv[1].minor);
- } else {
- printf("Current versions:\n");
- printf("RO %d.%d.%d\n", targ.shv[0].epoch,
- targ.shv[0].major, targ.shv[0].minor);
- printf("RW %d.%d.%d\n", targ.shv[1].epoch,
- targ.shv[1].major, targ.shv[1].minor);
- }
- }
- }
-
- if (td.ep_type == usb_xfer) {
- libusb_close(td.uep.devh);
- libusb_exit(NULL);
- }
-
- if (!transferred_sections)
- return noop;
- /*
- * We should indicate if RO update was not done because of the
- * insufficient RW version.
- */
- for (j = 0; j < ARRAY_SIZE(sections); j++)
- if (sections[j].ustatus == not_possible) {
- /* This will allow scripting repeat attempts. */
- printf("Failed to update RO, run the command again\n");
- return rw_updated;
- }
-
- printf("image updated\n");
- return all_updated;
-}
diff --git a/extra/usb_updater/gsctool.h b/extra/usb_updater/gsctool.h
deleted file mode 100644
index 0be6faeb1c..0000000000
--- a/extra/usb_updater/gsctool.h
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * 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_GSCTOOL_H
-#define __EXTRA_USB_UPDATER_GSCTOOL_H
-
-#include <stdbool.h>
-#include <stdint.h>
-#include <sys/types.h>
-
-#include "usb_if.h"
-
-/*
- * gsctool uses this structure to keep information about the communications
- * channel used to talk to the Cr50, and about the state of the Cr50 image.
- */
-struct transfer_descriptor {
- /*
- * Set to true for use in an upstart script. Do not reboot after
- * transfer, and do not transfer RW if versions are the same.
- *
- * When using in development environment it is beneficial to transfer
- * RW images with the same version, as they get started based on the
- * header timestamp.
- */
- int upstart_mode;
- /*
- * Override in case updater is used w/ boards that do not follow
- * the cr50 versioning scheme.
- */
- int background_update_supported;
- /*
- * Unconditionally update the inactive RO, helps to make sure both RO
- * sections are at the same level.
- */
- int force_ro;
- /*
- * offsets of RO and WR sections available for update (not currently
- * active).
- */
- uint32_t ro_offset;
- uint32_t rw_offset;
-
- /* Do not reset the H1 immediately after update, wait for TPM reset. */
- int post_reset;
-
- /* Type of channel used to communicate with Cr50. */
- enum transfer_type {
- usb_xfer = 0, /* usb interface. */
- dev_xfer = 1, /* /dev/tpm0 */
- ts_xfer = 2 /* trunks_send */
- } ep_type;
- union {
- struct usb_endpoint uep;
- int tpm_fd;
- };
-};
-
-/*
- * These are values returned by the gsctool utility, they are interpreted by
- * the startup files to decide how to proceed (try to update to a new Cr50
- * image or not).
- */
-enum exit_values {
- noop = 0, /* All up to date, no update needed. */
- all_updated = 1, /* Update completed, reboot required. */
- rw_updated = 2, /* RO was not updated, reboot required. */
- update_error = 3 /* Something went wrong. */
-};
-
-
-struct board_id {
- uint32_t type; /* Board type */
- uint32_t type_inv; /* Board type (inverted) */
- uint32_t flags; /* Flags */
-};
-
-enum board_id_action {
- bid_none,
- bid_get,
- bid_set
-};
-
-/*
- * This function allows to retrieve or set (if not initialized) board ID of
- * the H1 chip. If bid_action is bid_get and show_machine_output is set,
- * prints out board ID in a machine-friendly format.
- */
-void process_bid(struct transfer_descriptor *td,
- enum board_id_action bid_action,
- struct board_id *bid,
- bool show_machine_output);
-
-/*
- * This function can be used to retrieve the current PP status from Cr50 and
- * prompt the user when a PP press is required.
- *
- * Physical presence can be required by different gsctool options, for which
- * Cr50 behavior also differs. The 'command' and 'poll_type' parameters are
- * used by Cr50 to tell what the host is polling for.
- */
-void poll_for_pp(struct transfer_descriptor *td,
- uint16_t command,
- uint8_t poll_type);
-
-/*
- * Function used to send vendor command to the Cr50 and receive a response.
- * Returns the error code from TPM response header, which is set to zero on
- * success.
- */
-uint32_t send_vendor_command(struct transfer_descriptor *td,
- uint16_t subcommand,
- const void *command_body,
- size_t command_body_size,
- void *response,
- size_t *response_size);
-
-
-#endif // __EXTRA_USB_UPDATER_GSCTOOL_H
diff --git a/extra/usb_updater/verify_ro.c b/extra/usb_updater/verify_ro.c
deleted file mode 100644
index 4a4aea792a..0000000000
--- a/extra/usb_updater/verify_ro.c
+++ /dev/null
@@ -1,362 +0,0 @@
-/*
- * 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 <stdbool.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"
-
-/* Index of the matching hash variant. */
-static ssize_t matching_variant;
-
-/*
- * 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
- * arrays within the same board section.
- */
-static int verify_hash_section(struct transfer_descriptor *td,
- struct vendor_cc_spi_hash_request *req,
- struct addr_range *range)
-{
- 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_variant < 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_variant = 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_variant].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);
-
- return rv;
-}
-
-int verify_ro(struct transfer_descriptor *td,
- const char *desc_file_name,
- bool show_machine_output)
-{
- /* First find out board ID of the target. */
- struct board_id bid;
- char rlz_code[sizeof(bid.type) + 1];
- int section_count = 0;
- int rv = 0;
-
- /*
- * 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, show_machine_output);
-
- 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';
-
- while (!parser_find_board(desc_file_name, rlz_code)) {
-
- /*
- * Each board section might have different index of the
- * matching hash variant.
- */
- matching_variant = -1;
-
- section_count++;
- rv = process_descriptor_sections(td);
- if (rv)
- break;
- }
-
- if (section_count != 2) {
- printf("Found wrong number of sections (%d) for board ID %s\n",
- section_count, rlz_code);
- rv = -EINVAL;
- } else if (!rv) {
- /*
- * Check was successful, send command to exit verification
- * mode.
- */
- struct vendor_cc_spi_hash_request req;
-
- 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);
- rv = -EINVAL;
- }
- }
-
- parser_done();
- return rv;
-}
diff --git a/extra/usb_updater/verify_ro.h b/extra/usb_updater/verify_ro.h
deleted file mode 100644
index de2443b8b4..0000000000
--- a/extra/usb_updater/verify_ro.h
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * 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 <stdbool.h>
-
-#include "gsctool.h"
-
-/*
- * Runs RO verification on the target specified in td using the description file
- * desc_file_name. If show_machine_output is set, target's board ID will be
- * outputted in a machine-friendly format. Returns 0 on success or a negative
- * value if there is an error.
- */
-int verify_ro(struct transfer_descriptor *td,
- const char *desc_file_name,
- bool show_machine_output);
-
-#endif // __EXTRA_USB_UPDATER_VERIFY_RO_H