summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRandall Spangler <rspangler@chromium.org>2018-02-08 11:09:54 -0800
committerChromeOS Commit Bot <chromeos-commit-bot@chromium.org>2018-02-20 23:54:42 +0000
commit50fb91d83be03def29485eb32babb9cedb93441a (patch)
tree41d24668406a87dbe81d1804780968b50edf5bd4
parentc7c449cd096d6edcfce1f1f26174cae64b8d1bdb (diff)
downloadchrome-ec-50fb91d83be03def29485eb32babb9cedb93441a.tar.gz
cr50: Convert spihash to TPM vendor command
The console command now calls the vendor command to do the work. Otherwise, the same as before. Conflicts: include/tpm_vendor_cmds.h set to the upstream version. BUG=chromium:804507 BRANCH=cr50 release (after testing) TEST=manual: # Sample sequence spihash ap -> requires physical presence; tap power button spihash 0 1024 -> gives a hash; compare with first 1KB of image.bin spihash dump 0 128 -> dumps first 128 bytes; compare with image.bin spihash 128 128 -> offset works spihash 0 0x100000 -> gives a hash; doesn't watchdog reset spihdev ec spihash 0 1024 -> compare with ec.bin spihash disable # Test timeout spihash ap # Wait 30 seconds spihash 0 1024 -> still works # Wait 60 seconds; goes back disabled automatically spihash 0 1024 -> fails because spihash is disabled # Presence not required when CCD opened ccd open spihash ap -> no PP required spihash 0 1024 -> works spihash disable # Possible for owner to disable via CCD config ccd -> HashFlash is "Always" ccd set HashFlash IfOpened ccd lock spihash ap -> access denied # Cleanup ccd open ccd reset ccd lock Change-Id: Ife9335a1e402a7596d99bf515ec89ff94e8a0044 Signed-off-by: Randall Spangler <rspangler@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/910083 Reviewed-by: Aseda Aboagye <aaboagye@chromium.org> Reviewed-by: Vadim Bendebury <vbendeb@chromium.org> (cherry picked from commit f49e1c3b42026eeaf57df0fd86b43660ddb1c184) Reviewed-on: https://chromium-review.googlesource.com/927720 Commit-Queue: Vadim Bendebury <vbendeb@chromium.org> Tested-by: Vadim Bendebury <vbendeb@chromium.org>
-rw-r--r--board/cr50/usb_spi.c353
-rw-r--r--include/tpm_vendor_cmds.h33
2 files changed, 258 insertions, 128 deletions
diff --git a/board/cr50/usb_spi.c b/board/cr50/usb_spi.c
index 5681535ab6..26c3e0c7fc 100644
--- a/board/cr50/usb_spi.c
+++ b/board/cr50/usb_spi.c
@@ -3,10 +3,12 @@
* found in the LICENSE file.
*/
+#include "byteorder.h"
#include "ccd_config.h"
#include "cryptoc/sha256.h"
#include "console.h"
#include "dcrypto.h"
+#include "extension.h"
#include "gpio.h"
#include "hooks.h"
#include "physical_presence.h"
@@ -16,6 +18,8 @@
#include "system.h"
#include "task.h"
#include "timer.h"
+#include "tpm_registers.h"
+#include "tpm_vendor_cmds.h"
#include "usb_spi.h"
#define CPRINTS(format, args...) cprints(CC_USB, format, ## args)
@@ -192,7 +196,7 @@ static void enable_spi_pinmux(void)
/* Set SPI_CS to be an internal pull up */
GWRITE_FIELD(PINMUX, DIOA14_CTL, PU, 1);
- CPRINTS("usb_spi enable %s",
+ CPRINTS("%s: %s", __func__,
gpio_get_level(GPIO_AP_FLASH_SELECT) ? "AP" : "EC");
spi_enable(CONFIG_SPI_FLASH_PORT, 1);
@@ -232,21 +236,21 @@ int usb_spi_board_enable(struct usb_spi_config const *config)
/* Make sure we're allowed to enable the requested device */
if (host == USB_SPI_EC) {
if (!ccd_is_cap_enabled(CCD_CAP_EC_FLASH)) {
- CPRINTS("EC SPI access denied");
+ CPRINTS("%s: EC access denied", __func__);
return EC_ERROR_ACCESS_DENIED;
}
} else if (host == USB_SPI_AP) {
if (!ccd_is_cap_enabled(CCD_CAP_AP_FLASH)) {
- CPRINTS("AP SPI access denied");
+ CPRINTS("%s: AP access denied", __func__);
return EC_ERROR_ACCESS_DENIED;
}
} else {
- CPRINTS("SPI device not supported");
+ CPRINTS("%s: device %d not supported", __func__, host);
return EC_ERROR_INVAL;
}
if (set_spi_bus_user(SPI_BUS_USER_USB, 1) != EC_SUCCESS) {
- CPRINTS("SPI bus in use");
+ CPRINTS("%s: bus in use", __func__);
return EC_ERROR_BUSY;
}
@@ -267,7 +271,7 @@ int usb_spi_board_enable(struct usb_spi_config const *config)
void usb_spi_board_disable(struct usb_spi_config const *config)
{
- CPRINTS("usb_spi disable");
+ CPRINTS("%s", __func__);
/* Only disable the SPI bus if we own it */
if (get_spi_bus_user() != SPI_BUS_USER_USB)
@@ -302,7 +306,7 @@ int usb_spi_interface(struct usb_spi_config const *config,
config->state->enabled_host = USB_SPI_EC;
break;
case USB_SPI_REQ_ENABLE:
- CPRINTS("ERROR: Must specify target");
+ CPRINTS("%s: Must specify target", __func__);
/* Fall through... */
case USB_SPI_REQ_DISABLE:
config->state->enabled_host = USB_SPI_DISABLE;
@@ -377,13 +381,16 @@ static void spi_hash_stop_ec_device(void)
/**
* Disable SPI hashing mode.
*
- * @return EC_SUCCESS or non-zero error code.
+ * @return Vendor command return code.
*/
-static int spi_hash_disable(void)
+static enum vendor_cmd_rc spi_hash_disable(void)
{
+ if (spi_hash_device == USB_SPI_DISABLE)
+ return VENDOR_RC_SUCCESS;
+
/* Can't disable SPI if we don't own it */
if (get_spi_bus_user() != SPI_BUS_USER_HASH)
- return EC_ERROR_ACCESS_DENIED;
+ return VENDOR_RC_NOT_ALLOWED;
/* Disable the SPI bus and chip select */
disable_spi_pinmux();
@@ -401,8 +408,8 @@ static int spi_hash_disable(void)
/* Disable inactivity timer to turn hashing mode off */
hook_call_deferred(&spi_hash_inactive_timeout_data, -1);
- CPRINTS("SPI hash device: disable\n");
- return EC_SUCCESS;
+ CPRINTS("%s", __func__);
+ return VENDOR_RC_SUCCESS;
}
/**
@@ -420,7 +427,7 @@ static void spi_hash_pp_done(void)
{
/* Acquire the bus */
if (set_spi_bus_user(SPI_BUS_USER_HASH, 1)) {
- CPRINTS("spihdev: bus busy");
+ CPRINTS("%s: bus busy", __func__);
return;
}
@@ -459,167 +466,257 @@ static void spi_hash_pp_done(void)
hook_call_deferred(&spi_hash_inactive_timeout_data,
SPI_HASH_TIMEOUT_US);
- CPRINTS("SPI hash device: %s",
+ CPRINTS("%s: %s", __func__,
(spi_hash_device == USB_SPI_AP ? "AP" : "EC"));
}
-static int command_spi_hash_set_device(int argc, char **argv)
+/**
+ * Set the SPI hashing device.
+ *
+ * @param dev Device (enum usb_spi)
+ * @param gang_mode If non-zero, EC uses gang mode
+ *
+ * @return Vendor command return code
+ */
+static enum vendor_cmd_rc spi_hash_set_device(int dev, int gang_mode)
{
- new_device = spi_hash_device;
- new_gang_mode = 0;
+ if (dev == spi_hash_device)
+ return VENDOR_RC_SUCCESS;
- /* See if user wants to change the hash device */
- if (argc >= 2) {
- if (!strcasecmp(argv[1], "AP"))
- new_device = USB_SPI_AP;
- else if (!strcasecmp(argv[1], "EC"))
- new_device = USB_SPI_EC;
- else if (!strcasecmp(argv[1], "disable"))
- new_device = USB_SPI_DISABLE;
- else
- return EC_ERROR_PARAM1;
- }
+ /* Enabling requires permission */
+ if (!(ccd_is_cap_enabled(CCD_CAP_FLASH_READ)))
+ return VENDOR_RC_NOT_ALLOWED;
- /* Check for whether to use NPCX gang programmer mode */
- if (argc >= 3) {
- if (new_device == USB_SPI_EC && !strcasecmp(argv[2], "gang"))
- new_gang_mode = 1;
- else
- return EC_ERROR_PARAM2;
+ new_device = dev;
+ new_gang_mode = gang_mode;
+
+ /* Handle enabling */
+ if (spi_hash_device == USB_SPI_DISABLE &&
+ !(ccd_is_cap_enabled(CCD_CAP_AP_FLASH) &&
+ ccd_is_cap_enabled(CCD_CAP_EC_FLASH))) {
+ /*
+ * We were disabled, and CCD does not grant permission
+ * to both flash chips. So we need physical presence
+ * to take the SPI bus. That prevents a malicious
+ * peripheral from using this to reset the device.
+ *
+ * Technically, we could track the chips separately,
+ * and only require physical presence the first time we
+ * check a chip which CCD doesn't grant access to. But
+ * that's more bookkeeping, so for now the only way to
+ * skip physical presence is to have access to both.
+ */
+ return physical_detect_start(0, spi_hash_pp_done);
}
- if (new_device != spi_hash_device) {
- /* If we don't have permission, only allow disabling */
- if (new_device != USB_SPI_DISABLE &&
- !(ccd_is_cap_enabled(CCD_CAP_FLASH_READ)))
- return EC_ERROR_ACCESS_DENIED;
+ /*
+ * If we're still here, we already own the SPI bus, and are
+ * changing which chip we're looking at. Update hash device
+ * directly; no new physical presence required.
+ */
+ spi_hash_pp_done();
+ return VENDOR_RC_SUCCESS;
+}
- if (new_device == USB_SPI_DISABLE) {
- /* Disable SPI hashing */
- return spi_hash_disable();
- }
+static enum vendor_cmd_rc spi_hash_dump(uint8_t *dest, uint32_t offset,
+ uint32_t size)
+{
+ /* Fail if we don't own the bus */
+ if (get_spi_bus_user() != SPI_BUS_USER_HASH) {
+ CPRINTS("%s: not enabled", __func__);
+ return VENDOR_RC_NOT_ALLOWED;
+ }
- if (spi_hash_device == USB_SPI_DISABLE &&
- !(ccd_is_cap_enabled(CCD_CAP_AP_FLASH) &&
- ccd_is_cap_enabled(CCD_CAP_EC_FLASH))) {
- /*
- * We were disabled, and CCD does not grant permission
- * to both flash chips. So we need physical presence
- * to take the SPI bus. That prevents a malicious
- * peripheral from using this to reset the device.
- *
- * Technically, we could track the chips separately,
- * and only require physical presence the first time we
- * check a chip which CCD doesn't grant access to. But
- * that's more bookkeeping, so for now the only way to
- * skip physical presence is to have access to both.
- */
- return physical_detect_start(0, spi_hash_pp_done);
- }
+ /* Bump inactivity timer to turn hashing mode off */
+ hook_call_deferred(&spi_hash_inactive_timeout_data,
+ SPI_HASH_TIMEOUT_US);
- /*
- * If we're still here, we already own the SPI bus, and are
- * changing which chip we're looking at. Update hash device
- * directly; no new physical presence required.
- */
- spi_hash_pp_done();
- return EC_SUCCESS;
+ if (size > SPI_HASH_MAX_RESPONSE_BYTES)
+ return VENDOR_RC_BOGUS_ARGS;
+
+ if (spi_read_chunk(dest, offset, size) != EC_SUCCESS) {
+ CPRINTS("%s: read error at 0x%x", __func__, offset);
+ return VENDOR_RC_READ_FLASH_FAIL;
}
- ccprintf("SPI hash device: %s\n",
- (spi_hash_device ?
- (spi_hash_device == USB_SPI_AP ? "AP" : "EC") : "disable"));
- return EC_SUCCESS;
+ return VENDOR_RC_SUCCESS;
}
-static int command_spi_hash(int argc, char **argv)
+static enum vendor_cmd_rc spi_hash_sha256(uint8_t *dest, uint32_t offset,
+ uint32_t size)
{
HASH_CTX sha;
- int offset = -1;
- int chunk_size = SPI_HASH_CHUNK_SIZE;
- int size = 256;
- int rv = EC_SUCCESS;
uint8_t data[SPI_HASH_CHUNK_SIZE];
- int dump = 0;
+ int chunk_size = SPI_HASH_CHUNK_SIZE;
int chunks = 0;
- int i;
-
- /* Handle setting/printing the active device */
- if (argc == 1 ||
- !strcasecmp(argv[1], "AP") ||
- !strcasecmp(argv[1], "EC") ||
- !strcasecmp(argv[1], "disable"))
- return command_spi_hash_set_device(argc, argv);
/* Fail if we don't own the bus */
if (get_spi_bus_user() != SPI_BUS_USER_HASH) {
- ccprintf("SPI hash not enabled\n");
- return EC_ERROR_ACCESS_DENIED;
+ CPRINTS("%s: not enabled", __func__);
+ return VENDOR_RC_NOT_ALLOWED;
}
/* Bump inactivity timer to turn hashing mode off */
hook_call_deferred(&spi_hash_inactive_timeout_data,
SPI_HASH_TIMEOUT_US);
- /* Parse args */
- // TODO: parse offset and size directly, since we want them both
- rv = parse_offset_size(argc, argv, 1, &offset, &size);
- if (rv)
- return rv;
- if (argc > 3 && !strcasecmp(argv[3], "dump"))
- dump = 1;
+ if (size > MAX_SPI_HASH_SIZE)
+ return VENDOR_RC_BOGUS_ARGS;
- if (size < 0 || size > MAX_SPI_HASH_SIZE)
- return EC_ERROR_INVAL;
+ CPRINTS("%s: 0x%x 0x%x", __func__, offset, size);
DCRYPTO_SHA256_init(&sha, 0);
for (chunks = 0; size > 0; chunks++) {
int this_chunk = MIN(size, chunk_size);
-
/* Read the data */
- rv = spi_read_chunk(data, offset, this_chunk);
- if (rv) {
- ccprintf("Read error at 0x%x\n", offset);
- return rv;
+ if (spi_read_chunk(data, offset, this_chunk) != EC_SUCCESS) {
+ CPRINTS("%s: read error at 0x%x", __func__, offset);
+ return VENDOR_RC_READ_FLASH_FAIL;
}
/* Update hash */
HASH_update(&sha, data, this_chunk);
- if (dump) {
- /* Also dump it */
- for (i = 0; i < this_chunk; i++) {
- if ((offset + i) % 16) {
- ccprintf(" %02x", data[i]);
- } else {
- ccprintf("\n%08x: %02x",
- offset + i, data[i]);
- cflush();
- }
- }
- ccputs("\n");
+ /* Give other things a chance to happen */
+ if (!(chunks % 128))
msleep(1);
- } else {
- /* Print often at first then slow down */
- if (chunks < 16 || !(chunks % 64)) {
- ccputs(".");
- msleep(1);
- }
- }
size -= this_chunk;
offset += this_chunk;
}
- if (!dump) {
- cflush(); /* Make sure there's space for the hash to print */
- ccputs("\n");
+ memcpy(dest, HASH_final(&sha), SHA256_DIGEST_SIZE);
+
+ CPRINTS("%s: done", __func__);
+ return VENDOR_RC_SUCCESS;
+}
+
+/*
+ * TPM Vendor command handler for SPI hash commands which need to be available
+ * both through CLI and over /dev/tpm0.
+ */
+static enum vendor_cmd_rc spi_hash_vendor(enum vendor_cmd_cc code,
+ void *buf,
+ size_t input_size,
+ size_t *response_size)
+{
+ const struct vendor_cc_spi_hash_request *req = buf;
+ enum vendor_cmd_rc rc;
+
+ /* Default to no response data */
+ *response_size = 0;
+
+ /* Pick what to do based on subcommand. */
+ switch (req->subcmd) {
+ case SPI_HASH_SUBCMD_DISABLE:
+ /* Handle disabling */
+ return spi_hash_disable();
+ case SPI_HASH_SUBCMD_AP:
+ return spi_hash_set_device(USB_SPI_AP, 0);
+ case SPI_HASH_SUBCMD_EC:
+ return spi_hash_set_device(USB_SPI_EC,
+ !!(req->flags &
+ SPI_HASH_FLAG_EC_GANG));
+ case SPI_HASH_SUBCMD_SHA256:
+ *response_size = SHA256_DIGEST_SIZE;
+ rc = spi_hash_sha256(buf, req->offset, req->size);
+ if (rc != VENDOR_RC_SUCCESS)
+ *response_size = 0;
+ return rc;
+ case SPI_HASH_SUBCMD_DUMP:
+ /* Save size before we overwrite it with data */
+ *response_size = req->size;
+ rc = spi_hash_dump(buf, req->offset, req->size);
+ if (rc != VENDOR_RC_SUCCESS)
+ *response_size = 0;
+ return rc;
+ default:
+ CPRINTS("%s:%d - unknown subcommand", __func__, __LINE__);
+ *response_size = 0;
+ return VENDOR_RC_NO_SUCH_SUBCOMMAND;
}
+}
+DECLARE_VENDOR_COMMAND(VENDOR_CC_SPI_HASH, spi_hash_vendor);
- ccprintf("Hash = %.32h\n", HASH_final(&sha));
- return EC_SUCCESS;
+/**
+ * Wrapper for hash commands which are passed through the TPM task context.
+ */
+static int hash_command_wrapper(int argc, char *argv[])
+{
+ int rv;
+ struct vendor_cc_spi_hash_request req;
+ struct tpm_cmd_header *tpm_header;
+ const size_t command_size = sizeof(*tpm_header) +
+ MAX(sizeof(req), SPI_HASH_MAX_RESPONSE_BYTES);
+ uint8_t buf[command_size];
+ uint8_t *p;
+ uint32_t return_code;
+
+ /* If no args, just return */
+ if (argc < 2) {
+ ccprintf("SPI hash device: %s\n",
+ (spi_hash_device ?
+ (spi_hash_device == USB_SPI_AP ? "AP" : "EC") :
+ "disable"));
+ return EC_SUCCESS;
+ }
+
+ /* Parse args into stack-based struct */
+ memset(&req, 0, sizeof(req));
+ if (!strcasecmp(argv[1], "AP")) {
+ req.subcmd = SPI_HASH_SUBCMD_AP;
+ } else if (!strcasecmp(argv[1], "EC")) {
+ req.subcmd = SPI_HASH_SUBCMD_EC;
+ if (argc > 2 && !strcasecmp(argv[2], "gang"))
+ req.flags |= SPI_HASH_FLAG_EC_GANG;
+ } else if (!strcasecmp(argv[1], "disable")) {
+ req.subcmd = SPI_HASH_SUBCMD_DISABLE;
+ } else if (argc == 3) {
+ req.subcmd = SPI_HASH_SUBCMD_SHA256;
+ rv = parse_offset_size(argc, argv, 1, &req.offset, &req.size);
+ if (rv)
+ return rv;
+ } else if (argc == 4 && !strcasecmp(argv[1], "dump")) {
+ req.subcmd = SPI_HASH_SUBCMD_DUMP;
+ rv = parse_offset_size(argc, argv, 2, &req.offset, &req.size);
+ if (rv)
+ return rv;
+ } else {
+ return EC_ERROR_PARAM1;
+ }
+
+ /* Build the extension command */
+ tpm_header = (struct tpm_cmd_header *)buf;
+ tpm_header->tag = htobe16(0x8001); /* TPM_ST_NO_SESSIONS */
+ tpm_header->size = htobe32(command_size);
+ tpm_header->command_code = htobe32(TPM_CC_VENDOR_BIT_MASK);
+ tpm_header->subcommand_code = htobe16(VENDOR_CC_SPI_HASH);
+ /* Copy request data */
+ p = (uint8_t *)(tpm_header + 1);
+ memcpy(p, &req, sizeof(req));
+
+ tpm_alt_extension(tpm_header, command_size);
+
+ /*
+ * Return status in the command code field now, in case of error,
+ * error code is the first byte after the header.
+ */
+ return_code = be32toh(tpm_header->command_code);
+ if ((return_code != EC_SUCCESS) &&
+ (return_code != VENDOR_RC_IN_PROGRESS)) {
+ rv = p[0];
+ } else {
+ rv = EC_SUCCESS;
+
+ if (req.subcmd == SPI_HASH_SUBCMD_DUMP)
+ ccprintf("data: %.*h\n", req.size, p);
+ else if (req.subcmd == SPI_HASH_SUBCMD_SHA256)
+ ccprintf("hash: %.32h\n", p);
+ }
+
+ return rv;
}
-DECLARE_SAFE_CONSOLE_COMMAND(spihash, command_spi_hash,
- "ap | ec [gang] | disable | <offset> <size> [dump]",
- "Hash SPI flash");
+DECLARE_SAFE_CONSOLE_COMMAND(spihash, hash_command_wrapper,
+ "ap | ec [gang] | disable | [dump] <offset> <size>",
+ "Hash SPI flash via TPM vendor command");
diff --git a/include/tpm_vendor_cmds.h b/include/tpm_vendor_cmds.h
index b61ca2623c..927a39817b 100644
--- a/include/tpm_vendor_cmds.h
+++ b/include/tpm_vendor_cmds.h
@@ -50,6 +50,8 @@ enum vendor_cmd_cc {
VENDOR_CC_DISABLE_RMA = 32,
VENDOR_CC_MANAGE_CCD_PWD = 33,
VENDOR_CC_CCD = 34,
+ VENDOR_CC_GET_ALERTS_DATA = 35,
+ VENDOR_CC_SPI_HASH = 36,
LAST_VENDOR_COMMAND = 65535,
};
@@ -107,5 +109,36 @@ enum vendor_cmd_rc {
*/
#define VENDOR_RC_ERR 0x00000500
+/*** Structures and constants for VENDOR_CC_SPI_HASH ***/
+
+enum vendor_cc_spi_hash_request_subcmd {
+ /* Relinquish the bus */
+ SPI_HASH_SUBCMD_DISABLE = 0,
+ /* Acquire the bus for AP SPI */
+ SPI_HASH_SUBCMD_AP = 1,
+ /* Acquire the bus for EC SPI */
+ SPI_HASH_SUBCMD_EC = 2,
+ /* Hash SPI data */
+ SPI_HASH_SUBCMD_SHA256 = 4,
+ /* Read SPI data */
+ SPI_HASH_SUBCMD_DUMP = 5,
+};
+
+enum vendor_cc_spi_hash_request_flags {
+ /* EC uses gang programmer mode */
+ SPI_HASH_FLAG_EC_GANG = (1 << 0),
+};
+
+/* Structure for VENDOR_CC_SPI_HASH request which follows tpm_header */
+struct vendor_cc_spi_hash_request {
+ uint8_t subcmd; /* See vendor_cc_spi_hash_request_subcmd */
+ uint8_t flags; /* See vendor_cc_spi_hash_request_flags */
+ /* Offset and size used by SHA256 and DUMP; ignored by other subcmds */
+ uint32_t offset; /* Offset in flash to hash/read */
+ uint32_t size; /* Size in bytes to hash/read */
+} __packed;
+
+/* Maximum size of a response = SHA-256 hash or 1-32 bytes of data */
+#define SPI_HASH_MAX_RESPONSE_BYTES 32
#endif /* __INCLUDE_TPM_VENDOR_CMDS_H */