summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRandall Spangler <rspangler@chromium.org>2012-06-22 12:56:56 -0700
committerGerrit <chrome-bot@google.com>2012-06-25 15:37:42 -0700
commit900c0215b4f47551c850259ef7c716285c46e8b5 (patch)
treed467eb3c50b9a3def174db18b7dc6dcfb110ab43
parentaab92d552d7274f00e624bc8bc74ec41d3101b6d (diff)
downloadchrome-ec-900c0215b4f47551c850259ef7c716285c46e8b5.tar.gz
Add hash support
EC computes a SHA-256 hash of its RW code on boot. Also adds host and console commands to tell the EC to recompute the hash, or hash a different section of flash memory. BUG=chrome-os-partner:10777 TEST=manual 1) ectool echash -> should match what the EC precomputed 2a) ectool echash recalc 0 0x10000 5 2b) on EC console, 'hash 0 0x10000 5' 2c) results should agree 3a) on ec console, 'hash 0 0x3e000' then quickly 'hash abort' 3b) ectool echash -> status should be unavailable 4) ectool echash start 0 0x3e000 6 && ectool echash && ectool echash abort && sleep 2 && ectool echash status should be busy, then unavailable Signed-off-by: Randall Spangler <rspangler@chromium.org> Change-Id: I6806d7b4d4dca3a74f476092551b4dba875d558e Reviewed-on: https://gerrit.chromium.org/gerrit/26023
-rw-r--r--board/bds/ec.tasklist1
-rw-r--r--board/link/ec.tasklist1
-rw-r--r--common/build.mk2
-rw-r--r--common/system_common.c5
-rw-r--r--common/vboot_hash.c305
-rw-r--r--include/ec_commands.h45
-rw-r--r--util/ectool.c123
7 files changed, 478 insertions, 4 deletions
diff --git a/board/bds/ec.tasklist b/board/bds/ec.tasklist
index beaaec5552..c8cf1637e0 100644
--- a/board/bds/ec.tasklist
+++ b/board/bds/ec.tasklist
@@ -15,5 +15,6 @@
*/
#define CONFIG_TASK_LIST \
TASK(WATCHDOG, watchdog_task, NULL) \
+ TASK(VBOOTHASH, vboot_hash_task, NULL) \
TASK(LIGHTBAR, lightbar_task, NULL) \
TASK(CONSOLE, console_task, NULL)
diff --git a/board/link/ec.tasklist b/board/link/ec.tasklist
index 2aee1d9be2..b31a575bf9 100644
--- a/board/link/ec.tasklist
+++ b/board/link/ec.tasklist
@@ -15,6 +15,7 @@
*/
#define CONFIG_TASK_LIST \
TASK(WATCHDOG, watchdog_task, NULL) \
+ TASK(VBOOTHASH, vboot_hash_task, NULL) \
TASK(LIGHTBAR, lightbar_task, NULL) \
TASK(POWERSTATE, charge_state_machine_task, NULL) \
TASK(TEMPSENSOR, temp_sensor_task, NULL) \
diff --git a/common/build.mk b/common/build.mk
index 8c282b9d84..ab44f4dddd 100644
--- a/common/build.mk
+++ b/common/build.mk
@@ -39,7 +39,7 @@ VBOOT_DEVKEYS?=/usr/share/vboot/devkeys
CFLAGS_$(CONFIG_VBOOT)+= -DCHROMEOS_ENVIRONMENT -DCHROMEOS_EC
# CFLAGS_$(CONFIG_VBOOT)+= -DVBOOT_DEBUG
-common-$(CONFIG_VBOOT)+= vboot.o vboot_stub.o
+common-$(CONFIG_VBOOT)+= vboot.o vboot_stub.o vboot_hash.o
includes-$(CONFIG_VBOOT)+= \
$(VBOOT_SOURCE)/include \
diff --git a/common/system_common.c b/common/system_common.c
index 573995f1f8..4fb05422e7 100644
--- a/common/system_common.c
+++ b/common/system_common.c
@@ -521,7 +521,8 @@ DECLARE_CONSOLE_COMMAND(sysinfo, command_sysinfo,
NULL);
-#ifdef CONSOLE_COMMAND_SCRATCHPAD
+#define CONFIG_CONSOLE_CMD_SCRATCHPAD
+#ifdef CONFIG_CONSOLE_CMD_SCRATCHPAD
static int command_scratchpad(int argc, char **argv)
{
int rv = EC_SUCCESS;
@@ -541,7 +542,7 @@ DECLARE_CONSOLE_COMMAND(scratchpad, command_scratchpad,
"[val]",
"Get or set scratchpad value",
NULL);
-#endif
+#endif /* CONFIG_CONSOLE_CMD_SCRATCHPAD */
static int command_hibernate(int argc, char **argv)
diff --git a/common/vboot_hash.c b/common/vboot_hash.c
new file mode 100644
index 0000000000..fc801a2e98
--- /dev/null
+++ b/common/vboot_hash.c
@@ -0,0 +1,305 @@
+/* Copyright (c) 2012 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.
+ */
+
+/* Verified boot hash computing module for Chrome EC */
+
+#include "config.h"
+#include "console.h"
+#include "cryptolib.h"
+#include "hooks.h"
+#include "host_command.h"
+#include "system.h"
+#include "task.h"
+#include "timer.h"
+#include "util.h"
+#include "watchdog.h"
+
+/* Console output macros */
+#define CPUTS(outstr) cputs(CC_VBOOT, outstr)
+#define CPRINTF(format, args...) cprintf(CC_VBOOT, format, ## args)
+
+struct vboot_hash_tag {
+ uint8_t hash[SHA256_DIGEST_SIZE];
+ uint32_t offset;
+ uint32_t size;
+};
+
+#define VBOOT_HASH_SYSJUMP_TAG 0x5648 /* "VH" */
+#define VBOOT_HASH_SYSJUMP_VERSION 1
+#define CHUNK_SIZE 1024
+
+static int data_offset;
+static int data_size;
+static int curr_pos;
+static const uint8_t *hash; /* Hash, or NULL if not valid */
+static int want_abort;
+
+static SHA256_CTX ctx;
+
+
+/* Return non-zero if a hash operation is in progress */
+static int vboot_hash_in_progress(void)
+{
+ if (hash)
+ return 0; /* Already done */
+ return data_size ? 1 : 0; /* Nothing to hash */
+}
+
+
+/*
+ * Start computing a hash of <size> bytes of data at flash offset <offset>.
+ * If nonce_size is non-zero, prefixes the <nonce> onto the data to be
+ * hashed. Returns non-zero if error.
+ */
+static int vboot_hash_start(int offset, int size, const uint8_t *nonce,
+ int nonce_size)
+{
+ /* Fail if hash computation is already in progress */
+ if (vboot_hash_in_progress())
+ return EC_ERROR_BUSY;
+
+ /*
+ * Make sure request fits inside flash. That is, you can't use this
+ * command to peek at other memory.
+ */
+ if (offset > CONFIG_FLASH_SIZE || size > CONFIG_FLASH_SIZE ||
+ offset + size > CONFIG_FLASH_SIZE) {
+ return EC_ERROR_INVAL;
+ }
+
+ /* Save new hash request */
+ data_offset = offset;
+ data_size = size;
+ curr_pos = 0;
+ hash = NULL;
+ want_abort = 0;
+
+ /* Restart the hash computation */
+ CPRINTF("[%T hash start 0x%08x 0x%08x]\n", offset, size);
+ SHA256_init(&ctx);
+ if (nonce_size)
+ SHA256_update(&ctx, nonce, nonce_size);
+
+ /* Wake the hash task */
+ task_wake(TASK_ID_VBOOTHASH);
+
+ return EC_SUCCESS;
+}
+
+
+/* Abort hash currently in progress, if any. */
+static void vboot_hash_abort(void)
+{
+ if (vboot_hash_in_progress())
+ want_abort = 1;
+}
+
+
+static void vboot_hash_init(void)
+{
+ const struct vboot_hash_tag *tag;
+ int version, size;
+
+ tag = (const struct vboot_hash_tag *)system_get_jump_tag(
+ VBOOT_HASH_SYSJUMP_TAG, &version, &size);
+ if (tag && version == VBOOT_HASH_SYSJUMP_VERSION &&
+ size == sizeof(*tag)) {
+ /* Already computed a hash, so don't recompute */
+ CPRINTF("[%T hash precomputed]\n");
+ hash = tag->hash;
+ data_offset = tag->offset;
+ data_size = tag->size;
+ } else {
+ /* Start computing the hash of firmware A */
+ vboot_hash_start(CONFIG_FW_A_OFF - CONFIG_FLASH_BASE,
+ CONFIG_FW_A_SIZE, NULL, 0);
+ }
+}
+
+
+void vboot_hash_task(void)
+{
+ vboot_hash_init();
+
+ while (1) {
+ if (!vboot_hash_in_progress()) {
+ /* Nothing to do, so go back to sleep */
+ task_wait_event(-1);
+ } else if (want_abort) {
+ /* Abort hash computation currently in progress */
+ CPRINTF("[%T hash abort]\n");
+ data_size = 0;
+ want_abort = 0;
+ } else {
+ /* Compute the next chunk of hash */
+ int size = MIN(CHUNK_SIZE, data_size - curr_pos);
+
+ SHA256_update(&ctx,
+ (const uint8_t *)(CONFIG_FLASH_BASE +
+ data_offset + curr_pos),
+ size);
+ curr_pos += size;
+ if (curr_pos >= data_size) {
+ int i;
+
+ hash = SHA256_final(&ctx);
+
+ CPRINTF("[%T hash done ");
+ for (i = 0; i < SHA256_DIGEST_SIZE; i++)
+ CPRINTF("%02x", hash[i]);
+ CPUTS("]\n");
+ }
+
+ /*
+ * Let other tasks (really, just the watchdog task)
+ * run for a bit.
+ */
+ usleep(100);
+ }
+ }
+}
+
+/*****************************************************************************/
+/* Hooks */
+
+static int vboot_hash_preserve_state(void)
+{
+ struct vboot_hash_tag tag;
+
+ /* If we haven't finished our hash, nothing to save */
+ if (!hash)
+ return EC_SUCCESS;
+
+ memcpy(tag.hash, hash, sizeof(tag.hash));
+ tag.offset = data_offset;
+ tag.size = data_size;
+ system_add_jump_tag(VBOOT_HASH_SYSJUMP_TAG,
+ VBOOT_HASH_SYSJUMP_VERSION,
+ sizeof(tag), &tag);
+ return EC_SUCCESS;
+}
+DECLARE_HOOK(HOOK_SYSJUMP, vboot_hash_preserve_state, HOOK_PRIO_DEFAULT);
+
+/****************************************************************************/
+/* Console commands */
+
+static int command_hash(int argc, char **argv)
+{
+ int offset = CONFIG_FW_A_OFF - CONFIG_FLASH_BASE;
+ int size = CONFIG_FW_A_SIZE;
+ char *e;
+
+ if (argc == 2 && !strcasecmp(argv[1], "abort")) {
+ vboot_hash_abort();
+ return EC_SUCCESS;
+ }
+
+ if (argc >= 3) {
+ offset = strtoi(argv[1], &e, 0);
+ if (*e)
+ return EC_ERROR_PARAM1;
+
+ size = strtoi(argv[2], &e, 0);
+ if (*e)
+ return EC_ERROR_PARAM2;
+ }
+
+ if (argc == 4) {
+ int nonce = strtoi(argv[3], &e, 0);
+ if (*e)
+ return EC_ERROR_PARAM3;
+
+ return vboot_hash_start(offset, size,
+ (const uint8_t *)&nonce,
+ sizeof(nonce));
+ } else
+ return vboot_hash_start(offset, size, NULL, 0);
+}
+DECLARE_CONSOLE_COMMAND(hash, command_hash,
+ "[abort] | [<offset> <size> [<nonce>]]",
+ "Request hash recomputation",
+ NULL);
+
+/****************************************************************************/
+/* Host commands */
+
+/* Fill in the response with the current hash status */
+static void fill_response(struct ec_response_vboot_hash *r)
+{
+ if (vboot_hash_in_progress())
+ r->status = EC_VBOOT_HASH_STATUS_BUSY;
+ else if (hash) {
+ r->status = EC_VBOOT_HASH_STATUS_DONE;
+ r->hash_type = EC_VBOOT_HASH_TYPE_SHA256;
+ r->digest_size = SHA256_DIGEST_SIZE;
+ r->reserved0 = 0;
+ r->offset = data_offset;
+ r->size = data_size;
+ ASSERT(SHA256_DIGEST_SIZE < sizeof(r->hash_digest));
+ memcpy(r->hash_digest, hash, SHA256_DIGEST_SIZE);
+ } else
+ r->status = EC_VBOOT_HASH_STATUS_NONE;
+}
+
+
+static int host_command_vboot_hash(uint8_t *data, int *resp_size)
+{
+ struct ec_params_vboot_hash *p = (struct ec_params_vboot_hash *)data;
+ struct ec_response_vboot_hash *r =
+ (struct ec_response_vboot_hash *)data;
+ int rv;
+
+ switch (p->cmd) {
+ case EC_VBOOT_HASH_GET:
+ fill_response(r);
+ *resp_size = sizeof(struct ec_response_vboot_hash);
+ return EC_RES_SUCCESS;
+
+ case EC_VBOOT_HASH_ABORT:
+ vboot_hash_abort();
+ return EC_RES_SUCCESS;
+
+ case EC_VBOOT_HASH_START:
+ if (p->hash_type != EC_VBOOT_HASH_TYPE_SHA256)
+ return EC_RES_INVALID_PARAM;
+ if (p->nonce_size > sizeof(p->nonce_data))
+ return EC_RES_INVALID_PARAM;
+
+ rv = vboot_hash_start(p->offset, p->size, p->nonce_data,
+ p->nonce_size);
+
+ if (rv == EC_SUCCESS)
+ return EC_RES_SUCCESS;
+ else if (rv == EC_ERROR_INVAL)
+ return EC_RES_INVALID_PARAM;
+ else
+ return EC_RES_ERROR;
+
+ case EC_VBOOT_HASH_RECALC:
+ if (p->hash_type != EC_VBOOT_HASH_TYPE_SHA256)
+ return EC_RES_INVALID_PARAM;
+ if (p->nonce_size > sizeof(p->nonce_data))
+ return EC_RES_INVALID_PARAM;
+
+ rv = vboot_hash_start(p->offset, p->size, p->nonce_data,
+ p->nonce_size);
+ if (rv == EC_ERROR_INVAL)
+ return EC_RES_INVALID_PARAM;
+ else if (rv != EC_SUCCESS)
+ return EC_RES_ERROR;
+
+ /* Wait for hash to finish */
+ while (vboot_hash_in_progress())
+ usleep(1000);
+
+ fill_response(r);
+ *resp_size = sizeof(struct ec_response_vboot_hash);
+ return EC_RES_SUCCESS;
+
+ default:
+ return EC_RES_INVALID_PARAM;
+ }
+}
+DECLARE_HOST_COMMAND(EC_CMD_VBOOT_HASH, host_command_vboot_hash);
diff --git a/include/ec_commands.h b/include/ec_commands.h
index 3c8b8cb11c..570fc9fa5f 100644
--- a/include/ec_commands.h
+++ b/include/ec_commands.h
@@ -433,7 +433,9 @@ struct ec_params_lightbar_cmd {
} __attribute__ ((packed));
/*****************************************************************************/
-/* Verified boot commands. Details still evolving. */
+/* Verified boot commands */
+
+/* Verified boot command. Details still evolving. */
#define EC_CMD_VBOOT_CMD 0x29
struct ec_params_vboot_cmd {
union {
@@ -459,6 +461,47 @@ struct ec_params_vboot_cmd {
};
} __attribute__ ((packed));
+/* Verified boot hash command */
+#define EC_CMD_VBOOT_HASH 0x2A
+
+struct ec_params_vboot_hash {
+ uint8_t cmd; /* enum ec_vboot_hash_cmd */
+ uint8_t hash_type; /* enum ec_vboot_hash_type */
+ uint8_t nonce_size; /* Nonce size; may be 0 */
+ uint8_t reserved0; /* Reserved; set 0 */
+ uint32_t offset; /* Offset in flash to hash */
+ uint32_t size; /* Number of bytes to hash */
+ uint8_t nonce_data[64]; /* Nonce data; ignored if nonce_size=0 */
+} __attribute__ ((packed));
+
+struct ec_response_vboot_hash {
+ uint8_t status; /* enum ec_vboot_hash_status */
+ uint8_t hash_type; /* enum ec_vboot_hash_type */
+ uint8_t digest_size; /* Size of hash digest in bytes */
+ uint8_t reserved0; /* Ignore; will be 0 */
+ uint32_t offset; /* Offset in flash which was hashed */
+ uint32_t size; /* Number of bytes hashed */
+ uint8_t hash_digest[64]; /* Hash digest data */
+} __attribute__ ((packed));
+
+enum ec_vboot_hash_cmd {
+ EC_VBOOT_HASH_GET, /* Get current hash status */
+ EC_VBOOT_HASH_ABORT, /* Abort calculating current hash */
+ EC_VBOOT_HASH_START, /* Start computing a new hash */
+ EC_VBOOT_HASH_RECALC, /* Synchronously compute a new hash */
+};
+
+enum ec_vboot_hash_type {
+ EC_VBOOT_HASH_TYPE_SHA256, /* SHA-256 */
+};
+
+enum ec_vboot_hash_status {
+ EC_VBOOT_HASH_STATUS_NONE, /* No hash (not started, or aborted) */
+ EC_VBOOT_HASH_STATUS_DONE, /* Finished computing a hash */
+ EC_VBOOT_HASH_STATUS_BUSY, /* Busy computing a hash */
+};
+
+
/*****************************************************************************/
/* USB charging control commands */
diff --git a/util/ectool.c b/util/ectool.c
index f608d952cb..4fb953b1fe 100644
--- a/util/ectool.c
+++ b/util/ectool.c
@@ -36,6 +36,8 @@ const char help_str[] =
" Prints battery info\n"
" chipinfo\n"
" Prints chip info\n"
+ " echash [CMDS]\n"
+ " Various EC hash commands\n"
" eventclear <mask>\n"
" Clears EC host events flags where mask has bits set\n"
" eventget\n"
@@ -1565,6 +1567,126 @@ int cmd_chipinfo(int argc, char *argv[])
return 0;
}
+
+static int ec_hash_help(const char *cmd)
+{
+ printf("Usage:\n");
+ printf(" %s - get last hash\n", cmd);
+ printf(" %s abort - abort hashing\n", cmd);
+ printf(" %s start [<offset> <size> [<nonce>]] - start hashing\n", cmd);
+ printf(" %s recalc [<offset> <size> [<nonce>]] - sync rehash\n", cmd);
+ return 0;
+}
+
+
+static int ec_hash_print(const struct ec_response_vboot_hash *r)
+{
+ int i;
+
+ if (r->status == EC_VBOOT_HASH_STATUS_BUSY) {
+ printf("status: busy\n");
+ return 0;
+ } else if (r->status == EC_VBOOT_HASH_STATUS_NONE) {
+ printf("status: unavailable\n");
+ return 0;
+ } else if (r->status != EC_VBOOT_HASH_STATUS_DONE) {
+ printf("status: %d\n", r->status);
+ return 0;
+ }
+
+ printf("status: done\n");
+ if (r->hash_type == EC_VBOOT_HASH_TYPE_SHA256)
+ printf("type: SHA-256\n");
+ else
+ printf("type: %d\n", r->hash_type);
+
+ printf("offset: 0x%08x\n", r->offset);
+ printf("size: 0x%08x\n", r->size);
+
+ printf("hash: ");
+ for (i = 0; i < r->digest_size; i++)
+ printf("%02x", r->hash_digest[i]);
+ printf("\n");
+ return 0;
+}
+
+
+int cmd_ec_hash(int argc, char *argv[])
+{
+ struct ec_params_vboot_hash p;
+ struct ec_response_vboot_hash r;
+ char *e;
+
+ if (argc < 2) {
+ /* Get hash status */
+ p.cmd = EC_VBOOT_HASH_GET;
+ if (ec_command(EC_CMD_VBOOT_HASH, &p, sizeof(p), &r, sizeof(r)))
+ return -1;
+
+ return ec_hash_print(&r);
+ }
+
+ if (argc == 2 && !strcasecmp(argv[1], "abort")) {
+ /* Abort hash calculation */
+ p.cmd = EC_VBOOT_HASH_ABORT;
+ if (ec_command(EC_CMD_VBOOT_HASH, &p, sizeof(p), &r, sizeof(r)))
+ return -1;
+ return 0;
+ }
+
+ /* The only other commands are start and recalc */
+ if (!strcasecmp(argv[1], "start"))
+ p.cmd = EC_VBOOT_HASH_START;
+ else if (!strcasecmp(argv[1], "recalc"))
+ p.cmd = EC_VBOOT_HASH_RECALC;
+ else
+ return ec_hash_help(argv[0]);
+
+ if (argc < 4) {
+ fprintf(stderr, "Must specify offset and size\n");
+ return -1;
+ }
+
+ p.hash_type = EC_VBOOT_HASH_TYPE_SHA256;
+ p.offset = strtol(argv[2], &e, 0);
+ if (e && *e) {
+ fprintf(stderr, "Bad offset.\n");
+ return -1;
+ }
+ p.size = strtol(argv[3], &e, 0);
+ if (e && *e) {
+ fprintf(stderr, "Bad size.\n");
+ return -1;
+ }
+
+ if (argc == 5) {
+ /*
+ * Technically nonce can be any binary data up to 64 bytes,
+ * but this command only supports a 32-bit value.
+ */
+ uint32_t nonce = strtol(argv[4], &e, 0);
+ if (e && *e) {
+ fprintf(stderr, "Bad nonce integer.\n");
+ return -1;
+ }
+ memcpy(p.nonce_data, &nonce, sizeof(nonce));
+ p.nonce_size = sizeof(nonce);
+ } else
+ p.nonce_size = 0;
+
+ printf("Hashing %d bytes at offset %d...\n", p.size, p.offset);
+ if (ec_command(EC_CMD_VBOOT_HASH, &p, sizeof(p), &r, sizeof(r)))
+ return -1;
+
+ /* Start command doesn't wait for hashing to finish */
+ if (p.cmd == EC_VBOOT_HASH_START)
+ return 0;
+
+ /* Recalc command does wait around, so a result is ready now */
+ return ec_hash_print(&r);
+}
+
+
struct command {
const char *name;
int (*handler)(int argc, char *argv[]);
@@ -1576,6 +1698,7 @@ const struct command commands[] = {
{"backlight", cmd_lcd_backlight},
{"battery", cmd_battery},
{"chipinfo", cmd_chipinfo},
+ {"echash", cmd_ec_hash},
{"eventclear", cmd_host_event_clear},
{"eventget", cmd_host_event_get_raw},
{"eventgetscimask", cmd_host_event_get_sci_mask},