summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVadim Bendebury <vbendeb@chromium.org>2016-02-11 16:42:01 -0800
committerchrome-bot <chrome-bot@chromium.org>2016-02-17 12:01:32 -0800
commit93f2848eb9e73b1460029a39f520e5a14286b5ba (patch)
tree622d7cbab43aeab3ac84b6812919538613a746b6
parentffd5819d326e66c49f1df517b26581bff4fd68be (diff)
downloadchrome-ec-93f2848eb9e73b1460029a39f520e5a14286b5ba.tar.gz
cr50: upgrade command extension
This patch suggests a firmware upgrade mechanism implemented through an extended TPM command. The firmware is transmitted in chunks, each chunk accompanied by its checksum (first 32 bits of SHA1) and the base address. The first chunk is of size zero and has the base address set to zero. When the first chunk is received, the command handler determines the destination flash space (A or B), erases it, and returns its base address to the caller, such that the firmware update agent can tell in which of the two spaces it should write the update. The ultimate verification happens after the device is reset - the integrity and authentity of the firmware upgrade is verified at that point, the new firmware will not be started unless it is properly signed. BRANCH=none BUG=chrome-os-partner:37754 TEST=with all patches applied it is possible to upgrade firmware in both spaces A and B. Change-Id: I6aedc587ec630d65ba81000496f372c9044959a0 Signed-off-by: Vadim Bendebury <vbendeb@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/327415 Reviewed-by: Bill Richardson <wfrichar@chromium.org>
-rw-r--r--board/cr50/build.mk1
-rw-r--r--board/cr50/tpm2/upgrade.c186
-rw-r--r--include/extension.h1
3 files changed, 188 insertions, 0 deletions
diff --git a/board/cr50/build.mk b/board/cr50/build.mk
index 18683292ac..2fe51066f9 100644
--- a/board/cr50/build.mk
+++ b/board/cr50/build.mk
@@ -38,6 +38,7 @@ board-y += tpm2/platform.o
board-y += tpm2/rsa.o
board-y += tpm2/stubs.o
board-y += tpm2/trng.o
+board-y += tpm2/upgrade.o
# Build and link with an external library
EXTLIB := $(realpath ../../third_party/tpm2)
diff --git a/board/cr50/tpm2/upgrade.c b/board/cr50/tpm2/upgrade.c
new file mode 100644
index 0000000000..634c7fa9e4
--- /dev/null
+++ b/board/cr50/tpm2/upgrade.c
@@ -0,0 +1,186 @@
+/* Copyright 2016 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 "byteorder.h"
+#include "console.h"
+#include "dcrypto/dcrypto.h"
+#include "extension.h"
+#include "flash.h"
+#include "hooks.h"
+#include "include/compile_time_macros.h"
+#include "sha1.h"
+#include "uart.h"
+
+#define CPRINTF(format, args...) cprintf(CC_EXTENSION, format, ## args)
+
+/* Various upgrade extension command return values. */
+enum return_value {
+ UPGRADE_SUCCESS = 0,
+ UPGRADE_BAD_ADDR = 1,
+ UPGRADE_ERASE_FAILURE = 2,
+ UPGRADE_DATA_ERROR = 3,
+ UPGRADE_WRITE_FAILURE = 4,
+ UPGRADE_VERIFY_ERROR = 5,
+ UPGRADE_GEN_ERROR = 6,
+};
+
+/*
+ * The payload of the upgrade command. (Integer values in network byte order).
+ *
+ * block digest: the first four bytes of the sha1 digest of the rest of the
+ * structure.
+ * block_base: address where this block needs to be written to.
+ * block_body: variable size data to written at address 'block_base'.
+ */
+struct upgrade_command {
+ uint32_t block_digest;
+ uint32_t block_base;
+ uint8_t block_body[0];
+} __packed;
+
+/*
+ * This array defines two possibe sections available for the firmare update.
+ * The section whcih does not map the current execting code is picked as the
+ * valid update area. The values are offsets into the flash space.
+ */
+const struct section_descriptor {
+ uint32_t sect_base_offset;
+ uint32_t sect_top_offset;
+} rw_sections[] = {
+ {CONFIG_RW_MEM_OFF,
+ CONFIG_RW_MEM_OFF + CONFIG_RW_SIZE},
+ {CONFIG_RW_B_MEM_OFF,
+ CONFIG_RW_B_MEM_OFF + CONFIG_RW_SIZE}
+};
+
+const struct section_descriptor *valid_section;
+
+/* Pick the section where updates can go to based on current code address. */
+static void set_valid_section(void)
+{
+ int i;
+ uint32_t run_time_offs = (uint32_t) set_valid_section -
+ CONFIG_PROGRAM_MEMORY_BASE;
+
+ for (i = 0; i < ARRAY_SIZE(rw_sections); i++) {
+ if ((run_time_offs > rw_sections[i].sect_base_offset) &&
+ (run_time_offs < rw_sections[i].sect_top_offset))
+ continue;
+ valid_section = rw_sections + i;
+ break;
+ }
+}
+
+/* Verify that the passed in block fits into the valid area. */
+static int valid_upgrade_chunk(uint32_t block_offset, size_t body_size)
+{
+ if (valid_section &&
+ (block_offset >= valid_section->sect_base_offset) &&
+ ((block_offset + body_size) < valid_section->sect_top_offset))
+ return 1;
+
+ return 0;
+}
+
+static void fw_upgrade_command_handler(void *body,
+ size_t cmd_size,
+ size_t *response_size)
+{
+ struct upgrade_command *cmd_body = body;
+ uint8_t *rv = body;
+ uint8_t sha1_digest[SHA1_DIGEST_SIZE];
+ size_t body_size;
+ uint32_t block_offset;
+
+ /*
+ * A single byte response, unless this is the first message in the
+ * programming sequence.
+ */
+ *response_size = sizeof(*rv);
+
+ body_size = cmd_size - offsetof(struct upgrade_command, block_body);
+ if (body_size < 0) {
+ CPRINTF("%s:%d\n", __func__, __LINE__);
+ *rv = UPGRADE_GEN_ERROR;
+ return;
+ }
+
+ if (!cmd_body->block_base && !body_size) {
+ /*
+ * This is the first message of the upgrade process, let's
+ * determine the valid upgrade section and erase its contents.
+ */
+ set_valid_section();
+
+ if (flash_physical_erase(valid_section->sect_base_offset,
+ valid_section->sect_top_offset -
+ valid_section->sect_base_offset)) {
+ CPRINTF("%s:%d erase failure of 0x%x..+0x%x\n",
+ __func__, __LINE__,
+ valid_section->sect_base_offset,
+ valid_section->sect_top_offset -
+ valid_section->sect_base_offset);
+ *rv = UPGRADE_ERASE_FAILURE;
+ return;
+ }
+ /*
+ * Successful erase means that we need to return the base
+ * address of the section to be programmed with the upgrade.
+ */
+ *(uint32_t *)body = htobe32(valid_section->sect_base_offset +
+ CONFIG_PROGRAM_MEMORY_BASE);
+ *response_size = sizeof(uint32_t);
+ return;
+ }
+
+ /* Check if the block will fit into the valid area. */
+ block_offset = be32toh(cmd_body->block_base) -
+ CONFIG_PROGRAM_MEMORY_BASE;
+ if (!valid_upgrade_chunk(block_offset, body_size)) {
+ *rv = UPGRADE_BAD_ADDR;
+ CPRINTF("%s:%d %x, %d base %x top %x\n", __func__, __LINE__,
+ block_offset, body_size,
+ valid_section->sect_base_offset,
+ valid_section->sect_top_offset);
+ return;
+ }
+
+ /* Check if the block was received properly. */
+ DCRYPTO_SHA1_hash((uint8_t *)&cmd_body->block_base,
+ body_size + sizeof(cmd_body->block_base),
+ sha1_digest);
+ if (memcmp(sha1_digest, &cmd_body->block_digest,
+ sizeof(cmd_body->block_digest))) {
+ *rv = UPGRADE_DATA_ERROR;
+ CPRINTF("%s:%d sha1 %x not equal received %x at offs. 0x%x\n",
+ __func__, __LINE__,
+ *(uint32_t *)sha1_digest, cmd_body->block_digest,
+ block_offset);
+ return;
+ }
+
+ CPRINTF("%s: programming at offset 0x%x\n", __func__, block_offset);
+ if (flash_physical_write(block_offset, body_size,
+ cmd_body->block_body) != EC_SUCCESS) {
+ *rv = UPGRADE_WRITE_FAILURE;
+ CPRINTF("%s:%d upgrade write error\n",
+ __func__, __LINE__);
+ return;
+ }
+
+ /* Werify that data was written properly. */
+ if (memcmp(cmd_body->block_body, (void *)
+ (block_offset + CONFIG_PROGRAM_MEMORY_BASE),
+ body_size)) {
+ *rv = UPGRADE_VERIFY_ERROR;
+ CPRINTF("%s:%d upgrade verification error\n",
+ __func__, __LINE__);
+ return;
+ }
+
+ *rv = UPGRADE_SUCCESS;
+}
+
+DECLARE_EXTENSION_COMMAND(EXTENSION_FW_UPGRADE, fw_upgrade_command_handler);
diff --git a/include/extension.h b/include/extension.h
index 78b9d63f37..4e7b66558c 100644
--- a/include/extension.h
+++ b/include/extension.h
@@ -50,6 +50,7 @@ enum {
EXTENSION_HASH = 1,
EXTENSION_RSA = 2,
EXTENSION_EC = 3,
+ EXTENSION_FW_UPGRADE = 4,
};