summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVadim Bendebury <vbendeb@chromium.org>2016-08-17 11:39:06 -0700
committerchrome-bot <chrome-bot@chromium.org>2016-08-22 23:27:23 -0700
commitbccd2f94f1a53bf604b184db266045dc657752c7 (patch)
treef55aafbd9b3404fd0e7d785f8b2f431438efded3
parentc439b33cdd1c427a7a48c7721225e318d9e40d6a (diff)
downloadchrome-ec-bccd2f94f1a53bf604b184db266045dc657752c7.tar.gz
g: implement firmware upgrade protocol version 3
This patch modifies CR50 code to switch to firmware update protocol version 3. In this version both SPI and USB updates use exactly the same messages (apart from the size field added by USB to help reassembly of the fragmented frames). Bot unused RO and RW sections can be now programmed. BRANCH=none BUG=chrome-os-partner:55789 TEST=on a Kevin running the existing cr50 image programmed the new cr50 image, restarted the device, programmed the cr50 image again (this time using version 3), restarted the device, then programmed both RO and RW images and restarted the device. Each time verified that the proper image is running. Change-Id: I0cf196ec6de1786510433f8252164a33ccdc6dec Signed-off-by: Vadim Bendebury <vbendeb@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/371941 Reviewed-by: Bill Richardson <wfrichar@chromium.org>
-rw-r--r--chip/g/upgrade_fw.c210
-rw-r--r--chip/g/upgrade_fw.h49
-rw-r--r--chip/g/usb_upgrade.c83
3 files changed, 216 insertions, 126 deletions
diff --git a/chip/g/upgrade_fw.c b/chip/g/upgrade_fw.c
index ccb69dfb57..299c355e79 100644
--- a/chip/g/upgrade_fw.c
+++ b/chip/g/upgrade_fw.c
@@ -11,6 +11,8 @@
#include "hooks.h"
#include "include/compile_time_macros.h"
#include "memory.h"
+#include "system.h"
+#include "registers.h"
#include "uart.h"
#include "upgrade_fw.h"
@@ -18,59 +20,77 @@
#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,
-};
-
/*
- * 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.
+ * This structure defines flash offset ranges of the RO and RW images which
+ * are not currently active and as such could be overwritten with an update.
*/
-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)
+struct {
+ uint32_t ro_base_offset;
+ uint32_t ro_top_offset;
+ uint32_t rw_base_offset;
+ uint32_t rw_top_offset;
+} valid_sections;
+
+/* Pick sections where updates can go to based on current code addresses. */
+static void set_valid_sections(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;
+ switch (system_get_ro_image_copy()) {
+ case SYSTEM_IMAGE_RO:
+ valid_sections.ro_base_offset = CHIP_RO_B_MEM_OFF;
+ break;
+ case SYSTEM_IMAGE_RO_B:
+ valid_sections.ro_base_offset = CONFIG_RO_MEM_OFF;
+ break;
+ default:
+ CPRINTF("Failed to set RO image offsets\n");
+ return;
+ }
+
+ switch (system_get_image_copy()) {
+ case SYSTEM_IMAGE_RW:
+ valid_sections.rw_base_offset = CONFIG_RW_B_MEM_OFF;
+ break;
+ case SYSTEM_IMAGE_RW_B:
+ valid_sections.rw_base_offset = CONFIG_RW_MEM_OFF;
break;
+ default:
+ CPRINTF("Failed to set RW image offsets\n");
+ return;
}
+
+ valid_sections.ro_top_offset = valid_sections.ro_base_offset +
+ CONFIG_RO_SIZE - 0x800; /* 2K for certs! */
+
+ valid_sections.rw_top_offset = valid_sections.rw_base_offset +
+ CONFIG_RW_SIZE;
}
/* 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))
+ /* Is this an RW chunk? */
+ if (valid_sections.rw_top_offset &&
+ (block_offset >= valid_sections.rw_base_offset) &&
+ ((block_offset + body_size) <= valid_sections.rw_top_offset))
return 1;
- return 0;
+ /* Is this an RO chunk? */
+ if (valid_sections.ro_top_offset &&
+ (block_offset >= valid_sections.ro_base_offset) &&
+ ((block_offset + body_size) <= valid_sections.ro_top_offset))
+ return 1;
+ return 0;
+}
+
+/* Enable write access to the backup RO section. */
+static void open_ro_window(uint32_t offset, size_t size_b)
+{
+ GREG32(GLOBALSEC, FLASH_REGION6_BASE_ADDR) =
+ offset + CONFIG_PROGRAM_MEMORY_BASE;
+ GREG32(GLOBALSEC, FLASH_REGION6_SIZE) = size_b - 1;
+ GWRITE_FIELD(GLOBALSEC, FLASH_REGION6_CTRL, EN, 1);
+ GWRITE_FIELD(GLOBALSEC, FLASH_REGION6_CTRL, RD_EN, 1);
+ GWRITE_FIELD(GLOBALSEC, FLASH_REGION6_CTRL, WR_EN, 1);
}
void fw_upgrade_command_handler(void *body,
@@ -79,67 +99,94 @@ void fw_upgrade_command_handler(void *body,
{
struct upgrade_command *cmd_body = body;
void *upgrade_data;
- uint8_t *rv = body;
+ uint8_t *error_code = body; /* Cache the address for code clarity. */
uint8_t sha1_digest[SHA_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);
+ *response_size = 1; /* One byte response unless this is a start PDU. */
if (cmd_size < sizeof(struct upgrade_command)) {
CPRINTF("%s:%d\n", __func__, __LINE__);
- *rv = UPGRADE_GEN_ERROR;
+ *error_code = UPGRADE_GEN_ERROR;
return;
}
body_size = cmd_size - sizeof(struct upgrade_command);
if (!cmd_body->block_base && !body_size) {
+ struct first_response_pdu *rpdu = body;
+ uint32_t base;
+ uint32_t size;
+
/*
- * This is the first message of the upgrade process, let's
- * determine the valid upgrade section and erase its contents.
+ * This is the connection establishment request, the response
+ * allows the server to decide what sections of the image to
+ * send to program into the flash.
*/
- 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;
- }
+ /* First, prepare the response structure. */
+ memset(rpdu, 0, sizeof(*rpdu));
+ *response_size = sizeof(*rpdu);
+ rpdu->protocol_version = htobe32(UPGRADE_PROTOCOL_VERSION);
+
+ /*
+ * Determine the valid upgrade sections.
+ */
+ set_valid_sections();
/*
- crosbug.com/p/54916
- wipe_nvram(); Do not keep any state around.
- */
+ * If there have been any problems when determining the valid
+ * secitons offsets/sizes - return an error code.
+ */
+ if (!valid_sections.ro_top_offset ||
+ !valid_sections.rw_top_offset) {
+ rpdu->return_value = htobe32(UPGRADE_GEN_ERROR);
+ return;
+ }
/*
- * Successful erase means that we need to return the base
- * address of the section to be programmed with the upgrade.
+ * No problems - let's erase the backup sections and return
+ * their descriptions to the server.
*/
- *(uint32_t *)body = htobe32(valid_section->sect_base_offset +
- CONFIG_PROGRAM_MEMORY_BASE);
- *response_size = sizeof(uint32_t);
+ base = valid_sections.ro_base_offset;
+ size = valid_sections.ro_top_offset - base;
+
+ /* backup RO write access needs to be enabled. */
+ open_ro_window(base, size);
+ if (flash_erase(base, size) != EC_SUCCESS) {
+ CPRINTF("%s:%d erase failure of 0x%x..+0x%x\n",
+ __func__, __LINE__, base, size);
+ rpdu->return_value = htobe32(UPGRADE_ERASE_FAILURE);
+ return;
+ }
+
+ /* Now the RW backup section. */
+ base = valid_sections.rw_base_offset;
+ size = valid_sections.rw_top_offset - base;
+ if (flash_erase(base, size) != EC_SUCCESS) {
+ CPRINTF("%s:%d erase failure of 0x%x..+0x%x\n",
+ __func__, __LINE__, base, size);
+ rpdu->return_value = htobe32(UPGRADE_ERASE_FAILURE);
+ return;
+ }
+
+ rpdu->vers3.backup_ro_offset =
+ htobe32(valid_sections.ro_base_offset);
+
+ rpdu->vers3.backup_rw_offset =
+ htobe32(valid_sections.rw_base_offset);
+
return;
}
/* Check if the block will fit into the valid area. */
- block_offset = be32toh(cmd_body->block_base) -
- CONFIG_PROGRAM_MEMORY_BASE;
+ block_offset = be32toh(cmd_body->block_base);
if (!valid_upgrade_chunk(block_offset, body_size)) {
- *rv = UPGRADE_BAD_ADDR;
+ *error_code = 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);
+ valid_sections.rw_base_offset,
+ valid_sections.rw_top_offset);
return;
}
@@ -149,7 +196,7 @@ void fw_upgrade_command_handler(void *body,
sha1_digest);
if (memcmp(sha1_digest, &cmd_body->block_digest,
sizeof(cmd_body->block_digest))) {
- *rv = UPGRADE_DATA_ERROR;
+ *error_code = 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,
@@ -162,21 +209,20 @@ void fw_upgrade_command_handler(void *body,
upgrade_data = cmd_body + 1;
if (flash_physical_write(block_offset, body_size, upgrade_data)
!= EC_SUCCESS) {
- *rv = UPGRADE_WRITE_FAILURE;
- CPRINTF("%s:%d upgrade write error\n",
- __func__, __LINE__);
+ *error_code = UPGRADE_WRITE_FAILURE;
+ CPRINTF("%s:%d upgrade write error\n", __func__, __LINE__);
return;
}
- /* Werify that data was written properly. */
+ /* Verify that data was written properly. */
if (memcmp(upgrade_data, (void *)
(block_offset + CONFIG_PROGRAM_MEMORY_BASE),
body_size)) {
- *rv = UPGRADE_VERIFY_ERROR;
+ *error_code = UPGRADE_VERIFY_ERROR;
CPRINTF("%s:%d upgrade verification error\n",
__func__, __LINE__);
return;
}
- *rv = UPGRADE_SUCCESS;
+ *error_code = UPGRADE_SUCCESS;
}
diff --git a/chip/g/upgrade_fw.h b/chip/g/upgrade_fw.h
index 8eee3dbdb3..19aa5d9e5c 100644
--- a/chip/g/upgrade_fw.h
+++ b/chip/g/upgrade_fw.h
@@ -8,14 +8,40 @@
#include <stddef.h>
-#define UPGRADE_PROTOCOL_VERSION 2
-/* This is the format of the header the flash update function expects. */
+/*
+ * This file contains structures used to facilitate cr50 firmware updates,
+ * which can be used on any g chip.
+ *
+ * The firmware update protocol consists of two phases: connection
+ * establishment and actual image transfer.
+ *
+ * Image transfer is done in 1K blocks. The host supplying the image
+ * encapsulates blocks in frames by prepending a header including the flash
+ * offset where the block is destined and its digest.
+ *
+ * The CR50 device responds to each frame with a confirmation which is 1 byte
+ * response. Zero value means success, non zero value is the error code
+ * reported by CR50.
+ *
+ * To establish the connection, the host sends a different frame, which
+ * contains no data and is destined to offset 0. Receiving such a frame
+ * signals the CR50 that the host intends to transfer a new image.
+ *
+ * Version 3 connection establishment response is 16 bytes in size, all values
+ * in network byte order. The first 4 bytes are the error code (if any), 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.
+ */
+
+#define UPGRADE_PROTOCOL_VERSION 3
+
+/* This is the format of the update frame header. */
struct upgrade_command {
uint32_t block_digest; /* first 4 bytes of sha1 of the rest of the
- * block.
+ * frame.
*/
- uint32_t block_base; /* Offset of this block into the flash SPI. */
+ uint32_t block_base; /* Offset of this frame into the flash SPI. */
/* The actual payload goes here. */
} __packed;
@@ -37,7 +63,7 @@ struct update_frame_header {
};
/*
- * Response to the message initiating the update sequence.
+ * Response to the connection establishment request.
*
* When responding to the very first packet of the upgrade sequence, the
* original USB update implementation was responding with a four byte value,
@@ -74,4 +100,17 @@ void fw_upgrade_command_handler(void *body,
size_t cmd_size,
size_t *response_size);
+
+/* Various upgrade 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,
+ UPGRADE_MALLOC_ERROR = 7,
+};
+
#endif /* ! __EC_CHIP_G_UPGRADE_FW_H */
diff --git a/chip/g/usb_upgrade.c b/chip/g/usb_upgrade.c
index f7d548ecc2..5c213668d1 100644
--- a/chip/g/usb_upgrade.c
+++ b/chip/g/usb_upgrade.c
@@ -26,12 +26,13 @@
* programming blocks from the USB chunks, and invokes the programmer passing
* it the full block.
*
- * The programmer reports results by putting the return value of one or four
- * bytes into the same buffer where the block was passed in. This wrapper
- * retrieves the programmer's return value, normalizes it to 4 bytes and sends
- * it back to the host.
+ * The programmer reports results by putting the return value into the same
+ * buffer where the block was passed in. This wrapper retrieves the
+ * programmer's return value, and sends it back to the host. The return value
+ * is usually one byte in size, the only exception is the connection
+ * establishment phase where the return value is 16 bytes in size.
*
- * In the end of the successful image transfer and programming, the host send
+ * In the end of the successful image transfer and programming, the host sends
* the reset command, and the device reboots itself.
*/
@@ -117,7 +118,7 @@ static void upgrade_out_handler(struct consumer const *consumer, size_t count)
{
struct update_frame_header upfr;
size_t resp_size;
- uint32_t resp_value;
+ uint8_t resp_value;
uint64_t delta_time;
/* How much time since the previous USB callback? */
@@ -139,44 +140,43 @@ static void upgrade_out_handler(struct consumer const *consumer, size_t count)
}
if (rx_state_ == rx_idle) {
- struct first_response_pdu *startup_resp;
-
- if (!valid_transfer_start(consumer, count, &upfr))
+ /*
+ * The payload must be an update initiating PDU.
+ *
+ * The size of the response returned in the same buffer will
+ * exceed the received frame size; Let's make sure there is
+ * enough room for the response in the buffer.
+ */
+ union {
+ struct update_frame_header upfr;
+ struct {
+ uint32_t unused;
+ struct first_response_pdu startup_resp;
+ };
+ } u;
+
+ if (!valid_transfer_start(consumer, count, &u.upfr)) {
+ /*
+ * Someting is wrong, this payload is not a valid
+ * update start PDU. Let'w indicate this by returning
+ * a single byte error code.
+ */
+ resp_value = UPGRADE_GEN_ERROR;
+ QUEUE_ADD_UNITS(&upgrade_to_usb, &resp_value, 1);
return;
+ }
CPRINTS("FW update: starting...");
-
- fw_upgrade_command_handler(&upfr.cmd, count -
+ fw_upgrade_command_handler(&u.upfr.cmd, count -
offsetof(struct update_frame_header,
cmd),
&resp_size);
- /*
- * The handler reuses receive buffer to return the result
- * value.
- */
- startup_resp = (struct first_response_pdu *)&upfr.cmd;
- if (resp_size == 4) {
- /*
- * The handler is happy, returned a 4 byte base
- * offset, it is in startup_resp->return_value now in
- * the proper byte order.
- */
- rx_state_ = rx_outside_block;
- } else {
- /*
- * This must be a single byte error code, convert it
- * into a 4 byte network order representation.
- */
- startup_resp->return_value = htobe32
- (*((uint8_t *)&startup_resp->return_value));
- }
- startup_resp->protocol_version =
- htobe32(UPGRADE_PROTOCOL_VERSION);
+ if (!u.startup_resp.return_value)
+ rx_state_ = rx_outside_block; /* We're in business. */
/* Let the host know what upgrader had to say. */
- QUEUE_ADD_UNITS(&upgrade_to_usb, startup_resp,
- sizeof(*startup_resp));
+ QUEUE_ADD_UNITS(&upgrade_to_usb, &u.startup_resp, resp_size);
return;
}
@@ -205,9 +205,10 @@ static void upgrade_out_handler(struct consumer const *consumer, size_t count)
command = be32toh(command);
if (command == UPGRADE_DONE) {
CPRINTS("FW update: done");
+
resp_value = 0;
- QUEUE_ADD_UNITS(&upgrade_to_usb, &resp_value,
- sizeof(resp_value));
+ QUEUE_ADD_UNITS(&upgrade_to_usb,
+ &resp_value, 1);
rx_state_ = rx_awaiting_reset;
return;
}
@@ -220,22 +221,26 @@ static void upgrade_out_handler(struct consumer const *consumer, size_t count)
* field of all zeros.
*/
if (valid_transfer_start(consumer, count, &upfr) ||
- (count != sizeof(upfr)))
+ (count != sizeof(upfr))) {
/*
* Instead of a block start message we received either
* a transfer start message or a chunk. We must have
* gotten out of sync with the host.
*/
+ resp_value = UPGRADE_GEN_ERROR;
+ QUEUE_ADD_UNITS(&upgrade_to_usb, &resp_value, 1);
return;
+ }
/* Let's allocate a large enough buffer. */
block_size = be32toh(upfr.block_size) -
offsetof(struct update_frame_header, cmd);
if (shared_mem_acquire(block_size, (char **)&block_buffer)
!= EC_SUCCESS) {
- /* TODO:(vbendeb) report out of memory here. */
CPRINTS("FW update: error: failed to alloc %d bytes.",
block_size);
+ resp_value = UPGRADE_MALLOC_ERROR;
+ QUEUE_ADD_UNITS(&upgrade_to_usb, &resp_value, 1);
return;
}