summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--common/update_fw.c253
-rw-r--r--common/usb_update.c271
-rw-r--r--include/update_fw.h145
3 files changed, 435 insertions, 234 deletions
diff --git a/common/update_fw.c b/common/update_fw.c
index de374ec4dd..585977f774 100644
--- a/common/update_fw.c
+++ b/common/update_fw.c
@@ -15,35 +15,13 @@
#define CPRINTF(format, args...) cprintf(CC_USB, format, ## args)
-/* Various update extension command return values. */
-enum return_value {
- UPDATE_SUCCESS = 0,
- UPDATE_BAD_ADDR = 1,
- UPDATE_ERASE_FAILURE = 2,
- UPDATE_DATA_ERROR = 3,
- UPDATE_WRITE_FAILURE = 4,
- UPDATE_VERIFY_ERROR = 5,
- UPDATE_GEN_ERROR = 6,
-};
+const struct section_descriptor *valid_section;
/*
- * The payload of the update command. (Integer values in network byte order).
+ * Pick the section where updates can go to based on current code address.
*
- * 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'.
+ * TODO(b/36375666): Each board/chip should be able to re-define this.
*/
-struct update_command {
- uint32_t block_digest;
- uint32_t block_base;
- uint8_t block_body[0];
-} __packed;
-
-
-const struct section_descriptor *valid_section;
-
-/* Pick the section where updates can go to based on current code address. */
static int set_valid_section(void)
{
int i;
@@ -65,103 +43,210 @@ static int set_valid_section(void)
return EC_SUCCESS;
}
-/* Verify that the passed in block fits into the valid area. */
-static int valid_update_chunk(uint32_t block_offset, size_t body_size)
+/*
+ * Verify that the passed in block fits into the valid area. If it does, and
+ * is destined to the base address of the area - erase the area contents.
+ *
+ * Return success, or indication of an erase failure or chunk not fitting into
+ * valid area.
+ *
+ * TODO(b/36375666): Each board/chip should be able to re-define this.
+ */
+static uint8_t check_update_chunk(uint32_t block_offset, size_t body_size)
{
+ uint32_t base;
+ uint32_t size;
+
+ /* Is this an RW chunk? */
if (valid_section &&
(block_offset >= valid_section->sect_base_offset) &&
- ((block_offset + body_size) <= valid_section->sect_top_offset))
- return 1;
+ ((block_offset + body_size) <= valid_section->sect_top_offset)) {
+ base = valid_section->sect_base_offset;
+ size = valid_section->sect_top_offset -
+ valid_section->sect_base_offset;
+ /*
+ * If this is the first chunk for this section, it needs to
+ * be erased.
+ */
+ if (block_offset == base) {
+ if (flash_physical_erase(base, size) != EC_SUCCESS) {
+ CPRINTF("%s:%d erase failure of 0x%x..+0x%x\n",
+ __func__, __LINE__, base, size);
+ return UPDATE_ERASE_FAILURE;
+ }
+ }
+
+ return UPDATE_SUCCESS;
+ }
+
+ CPRINTF("%s:%d %x, %d section base %x top %x\n",
+ __func__, __LINE__,
+ block_offset, body_size,
+ valid_section->sect_base_offset,
+ valid_section->sect_top_offset);
+
+ return UPDATE_BAD_ADDR;
+
+}
+
+/* TODO(b/36375666): These need to be overridden for chip/g. */
+int update_pdu_valid(struct update_command *cmd_body, size_t cmd_size)
+{
+ return 1;
+}
+
+static int chunk_came_too_soon(uint32_t block_offset)
+{
return 0;
}
+static void new_chunk_written(uint32_t block_offset)
+{
+}
+
+static int contents_allowed(uint32_t block_offset,
+ size_t body_size, void *update_data)
+{
+ return 1;
+}
+
+/*
+ * Setup internal state (e.g. valid sections, and fill first response).
+ *
+ * Assumes rpdu is already prefilled with 0, and that version has already
+ * been set. May set a return_value != 0 on error.
+ *
+ * TODO(b/36375666): Each board/chip should be able to re-define this.
+ */
+void fw_update_start(struct first_response_pdu *rpdu)
+{
+ /* Determine the valid update section. */
+ set_valid_section();
+
+ /*
+ * If there have been any problems when determining the valid
+ * section offsets/sizes - return an error code.
+ */
+ if (!valid_section) {
+ CPRINTF("%s:%d\n", __func__, __LINE__);
+ rpdu->return_value = htobe32(UPDATE_GEN_ERROR);
+ return;
+ }
+
+ /*
+ * TODO(b/36375666): We reuse the same structure as cr50 updater, but
+ * there isn't a whole lot that can be shared... We should probably
+ * switch to a board-specific response packet (at least common vs
+ * cr50-specific).
+ */
+ rpdu->backup_ro_offset = htobe32(valid_section->sect_base_offset);
+ rpdu->backup_rw_offset = 0x0;
+
+ /* RO header information. */
+ rpdu->shv[0].minor = 0;
+ rpdu->shv[0].major = 0;
+ rpdu->shv[0].epoch = 0;
+ rpdu->keyid[0] = 0;
+
+ /* RW header information. */
+ rpdu->shv[1].minor = 0;
+ rpdu->shv[1].major = 0;
+ rpdu->shv[1].epoch = 0;
+
+ rpdu->keyid[1] = 0;
+}
+
void fw_update_command_handler(void *body,
- size_t cmd_size,
- size_t *response_size)
+ size_t cmd_size,
+ size_t *response_size)
{
struct update_command *cmd_body = body;
- uint8_t *rv = body;
+ void *update_data;
+ uint8_t *error_code = body; /* Cache the address for code clarity. */
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. */
- body_size = cmd_size - offsetof(struct update_command, block_body);
- if (body_size < 0) {
+ if (cmd_size < sizeof(struct update_command)) {
CPRINTF("%s:%d\n", __func__, __LINE__);
- *rv = UPDATE_GEN_ERROR;
+ *error_code = UPDATE_GEN_ERROR;
return;
}
+ body_size = cmd_size - sizeof(struct update_command);
if (!cmd_body->block_base && !body_size) {
- int ret;
+ struct first_response_pdu *rpdu = body;
+
/*
- * This is the first message of the update process, let's
- * determine the valid update 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.
*/
- ret = set_valid_section();
- if (ret) {
- CPRINTF("%s:%d no valid section\n", __func__, __LINE__);
- return;
- }
- 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 = UPDATE_ERASE_FAILURE;
- return;
- }
+ /* First, prepare the response structure. */
+ memset(rpdu, 0, sizeof(*rpdu));
+ *response_size = sizeof(*rpdu);
+ rpdu->protocol_version = htobe32(UPDATE_PROTOCOL_VERSION);
- /*
- * Successful erase means that we need to return the base
- * address of the section to be programmed with the update.
- */
- *(uint32_t *)body = htobe32(valid_section->sect_base_offset +
- CONFIG_PROGRAM_MEMORY_BASE);
- *response_size = sizeof(uint32_t);
+ /* Setup internal state (e.g. valid sections, and fill rpdu) */
+ fw_update_start(rpdu);
+ return;
+ }
+
+ block_offset = be32toh(cmd_body->block_base);
+
+ if (!update_pdu_valid(cmd_body, cmd_size)) {
+ *error_code = UPDATE_DATA_ERROR;
+ return;
+ }
+
+ update_data = cmd_body + 1;
+ if (!contents_allowed(block_offset, body_size, update_data)) {
+ *error_code = UPDATE_ROLLBACK_ERROR;
return;
}
/* Check if the block will fit into the valid area. */
- block_offset = be32toh(cmd_body->block_base) -
- CONFIG_PROGRAM_MEMORY_BASE;
- if (!valid_update_chunk(block_offset, body_size)) {
- *rv = UPDATE_BAD_ADDR;
- CPRINTF("%s:%d Write out of range %x ..+%d (Window %x - %x)\n",
- __func__, __LINE__,
- block_offset, body_size,
- valid_section->sect_base_offset,
- valid_section->sect_top_offset);
+ *error_code = check_update_chunk(block_offset, body_size);
+ if (*error_code)
+ return;
+
+ if (chunk_came_too_soon(block_offset)) {
+ *error_code = UPDATE_RATE_LIMIT_ERROR;
return;
}
- if (flash_physical_write(block_offset, body_size,
- cmd_body->block_body) != EC_SUCCESS) {
- *rv = UPDATE_WRITE_FAILURE;
- CPRINTF("%s:%d update write error @0x%x:%x\n",
- __func__, __LINE__, block_offset, body_size);
+ /*
+ * TODO(b/36375666): chip/g code has some cr50-specific stuff right
+ * here, which should probably be merged into contents_allowed...
+ */
+
+ CPRINTF("update: 0x%x\n", block_offset + CONFIG_PROGRAM_MEMORY_BASE);
+ if (flash_physical_write(block_offset, body_size, update_data)
+ != EC_SUCCESS) {
+ *error_code = UPDATE_WRITE_FAILURE;
+ CPRINTF("%s:%d update write error\n", __func__, __LINE__);
return;
}
- /* Werify that data was written properly. */
- if (memcmp(cmd_body->block_body, (void *)
+ new_chunk_written(block_offset);
+
+ /* Verify that data was written properly. */
+ if (memcmp(update_data, (void *)
(block_offset + CONFIG_PROGRAM_MEMORY_BASE),
body_size)) {
- *rv = UPDATE_VERIFY_ERROR;
+ *error_code = UPDATE_VERIFY_ERROR;
CPRINTF("%s:%d update verification error\n",
__func__, __LINE__);
return;
}
- *rv = UPDATE_SUCCESS;
+ *error_code = UPDATE_SUCCESS;
+}
+
+/* TODO(b/36375666): This need to be overridden for chip/g. */
+void fw_update_complete(void)
+{
}
diff --git a/common/usb_update.c b/common/usb_update.c
index 382677835f..6a85f19424 100644
--- a/common/usb_update.c
+++ b/common/usb_update.c
@@ -7,13 +7,11 @@
#include "common.h"
#include "console.h"
#include "consumer.h"
-#include "include/compile_time_macros.h"
+#include "extension.h"
#include "queue_policies.h"
#include "shared_mem.h"
#include "system.h"
#include "update_fw.h"
-#include "usb_api.h"
-#include "usb_hw.h"
#include "usb-stream.h"
#include "util.h"
@@ -30,12 +28,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.
*/
@@ -68,26 +67,6 @@ enum rx_state {
rx_inside_block, /* Assembling a block to pass to the programmer. */
rx_outside_block, /* Waiting for the next block to start or for the
reset command. */
- rx_awaiting_reset /* Waiting for reset confirmation. */
-};
-
-/* This is the format of the header the programmer expects. */
-struct update_command {
- uint32_t block_digest; /* first 4 bytes of sha1 of the rest of the
- block. */
- uint32_t block_base; /* Offset of this block into the flash SPI. */
-};
-
-/* This is the format of the header the host uses. */
-struct update_pdu_header {
- uint32_t block_size; /* Total size of the block, including this
- field. */
- union {
- struct update_command cmd;
- uint32_t resp; /* The programmer puts response to the same
- buffer where the command was. */
- };
- /* The actual payload goes here. */
};
enum rx_state rx_state_ = rx_idle;
@@ -96,12 +75,12 @@ static uint32_t block_size;
static uint32_t block_index;
/*
- * Verify that the contens of the USB rx queue is a valid transfer start
- * message from host, and if so - save its contents in the passed in
- * update_pdu_header structure.
+ * Fetches a transfer start frame from the queue. This can be either an update
+ * start frame (block_size = 0, all of cmd = 0), or the beginning of a frame
+ * (block_size > 0, valid block_base in cmd).
*/
-static int valid_transfer_start(struct consumer const *consumer, size_t count,
- struct update_pdu_header *pupdu)
+static int fetch_transfer_start(struct consumer const *consumer, size_t count,
+ struct update_frame_header *pupfr)
{
int i;
@@ -109,40 +88,61 @@ static int valid_transfer_start(struct consumer const *consumer, size_t count,
* Let's just make sure we drain the queue no matter what the contents
* are. This way they won't be in the way during next callback, even
* if these contents are not what's expected.
+ *
+ * Note: If count > sizeof(*pupfr), pupfr will be corrupted. This is
+ * ok as we will immediately fail after this.
*/
i = count;
while (i > 0) {
- QUEUE_REMOVE_UNITS(consumer->queue, pupdu,
- MIN(i, sizeof(*pupdu)));
- i -= sizeof(*pupdu);
+ QUEUE_REMOVE_UNITS(consumer->queue, pupfr,
+ MIN(i, sizeof(*pupfr)));
+ i -= sizeof(*pupfr);
}
- if (count != sizeof(struct update_pdu_header)) {
+ if (count != sizeof(struct update_frame_header)) {
CPRINTS("FW update: wrong first block, size %d", count);
return 0;
}
- /* In the first block the payload (updu.cmd) must be all zeros. */
- for (i = 0; i < sizeof(pupdu->cmd); i++)
- if (((uint8_t *)&pupdu->cmd)[i])
- return 0;
return 1;
}
+static int try_vendor_command(struct consumer const *consumer, size_t count)
+{
+ /* TODO(b/35587171): Vendor commands not implemented (yet). */
+ return 0;
+}
+
/*
* When was last time a USB callback was called, in microseconds, free running
* timer.
*/
static uint64_t prev_activity_timestamp;
-#define UPDATE_PROTOCOL_VERSION 2
+/*
+ * A flag indicating that at least one valid PDU containing flash update block
+ * has been received in the current transfer session.
+ */
+static uint8_t data_was_transferred;
+
+/* Reply with an error to remote side, reset state. */
+static void send_error_reset(uint8_t resp_value)
+{
+ QUEUE_ADD_UNITS(&update_to_usb, &resp_value, 1);
+ rx_state_ = rx_idle;
+ data_was_transferred = 0;
+ if (block_buffer) {
+ shared_mem_release(block_buffer);
+ block_buffer = NULL;
+ }
+}
/* Called to deal with data from the host */
static void update_out_handler(struct consumer const *consumer, size_t count)
{
- struct update_pdu_header updu;
+ 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? */
@@ -165,67 +165,59 @@ static void update_out_handler(struct consumer const *consumer, size_t count)
if (rx_state_ == rx_idle) {
/*
- * When responding to the very first packet of the update
- * sequence, the original implementation was responding with a
- * four byte value, just as to any other block of the transfer
- * sequence.
- *
- * It became clear that there is a need to be able to enhance
- * the update protocol, while stayng backwards compatible. To
- * achieve that we respond to the very first packet with an 8
- * byte value, the first 4 bytes the same as before, the
- * second 4 bytes - the protocol version number.
+ * The payload must be an update initiating PDU.
*
- * This way if on the host side receiving of a four byte value
- * in response to the first packet is an indication of the
- * 'legacy' protocol, version 0. Receiving of an 8 byte
- * response would communicate the protocol version in the
- * second 4 bytes.
+ * 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.
*/
- struct {
- uint32_t value;
- uint32_t version;
- } startup_resp;
+ union {
+ struct update_frame_header upfr;
+ struct {
+ uint32_t unused;
+ struct first_response_pdu startup_resp;
+ };
+ } u;
+
+ /* Check is this is a channeled TPM extension command. */
+ if (try_vendor_command(consumer, count))
+ return;
- if (!valid_transfer_start(consumer, count, &updu))
+ /*
+ * An update start PDU is a command without any payload, with
+ * digest = 0, and base = 0.
+ */
+ if (!fetch_transfer_start(consumer, count, &u.upfr) ||
+ be32toh(u.upfr.block_size) !=
+ sizeof(struct update_frame_header) ||
+ u.upfr.cmd.block_digest != 0 ||
+ u.upfr.cmd.block_base != 0) {
+ /*
+ * Something is wrong, this payload is not a valid
+ * update start PDU. Let'w indicate this by returning
+ * a single byte error code.
+ */
+ CPRINTS("FW update: invalid start.");
+ send_error_reset(UPDATE_GEN_ERROR);
return;
+ }
CPRINTS("FW update: starting...");
-
- fw_update_command_handler(&updu.cmd, count -
- offsetof(struct update_pdu_header,
+ fw_update_command_handler(&u.upfr.cmd, count -
+ offsetof(struct update_frame_header,
cmd),
&resp_size);
- if (resp_size == 4) {
- /* Already in network order. */
- startup_resp.value = updu.resp;
- rx_state_ = rx_outside_block;
- } else {
- /* This must be a single byte error code. */
- startup_resp.value = htobe32(*((uint8_t *)&updu.resp));
+ if (!u.startup_resp.return_value) {
+ rx_state_ = rx_outside_block; /* We're in business. */
+ data_was_transferred = 0; /* No data received yet. */
}
- startup_resp.version = htobe32(UPDATE_PROTOCOL_VERSION);
-
/* Let the host know what updater had to say. */
- QUEUE_ADD_UNITS(&update_to_usb, &startup_resp,
- sizeof(startup_resp));
+ QUEUE_ADD_UNITS(&update_to_usb, &u.startup_resp, resp_size);
return;
}
- if (rx_state_ == rx_awaiting_reset) {
- /*
- * Any USB data received in this state triggers reset, no
- * response required.
- */
- CPRINTS("reboot hard");
- cflush();
- system_reset(SYSTEM_RESET_HARD);
- while (1)
- ;
- }
-
if (rx_state_ == rx_outside_block) {
/*
* Expecting to receive the beginning of the block or the
@@ -239,40 +231,49 @@ static void update_out_handler(struct consumer const *consumer, size_t count)
command = be32toh(command);
if (command == UPDATE_DONE) {
CPRINTS("FW update: done");
+
+ if (data_was_transferred) {
+ fw_update_complete();
+ data_was_transferred = 0;
+ }
+
resp_value = 0;
- QUEUE_ADD_UNITS(&update_to_usb, &resp_value,
- sizeof(resp_value));
- rx_state_ = rx_awaiting_reset;
+ QUEUE_ADD_UNITS(&update_to_usb,
+ &resp_value, 1);
+ rx_state_ = rx_idle;
return;
- } else {
- CPRINTS("Unexpected packet command 0x%x",
- command);
}
}
/*
* At this point we expect a block start message. It is
- * sizeof(updu) bytes in size, but is not the transfer start
- * message, which also is of that size AND has the command
- * field of all zeros.
+ * sizeof(upfr) bytes in size.
*/
- if (valid_transfer_start(consumer, count, &updu) ||
- (count != sizeof(updu)))
- /*
- * 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.
- */
+ if (!fetch_transfer_start(consumer, count, &upfr)) {
+ CPRINTS("Invalid block start.");
+ send_error_reset(UPDATE_GEN_ERROR);
return;
+ }
/* Let's allocate a large enough buffer. */
- block_size = be32toh(updu.block_size) -
- offsetof(struct update_pdu_header, cmd);
+ block_size = be32toh(upfr.block_size) -
+ offsetof(struct update_frame_header, cmd);
+
+ /*
+ * Only update start PDU is allowed to have a size 0 payload.
+ */
+ if (block_size <= sizeof(struct update_command) ||
+ block_size > (UPDATE_PDU_SIZE +
+ sizeof(struct update_command))) {
+ CPRINTS("Invalid block size (%d).", block_size);
+ send_error_reset(UPDATE_GEN_ERROR);
+ return;
+ }
+
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);
+ != EC_SUCCESS) {
+ CPRINTS("Alloc error (%d).", block_size);
+ send_error_reset(UPDATE_MALLOC_ERROR);
return;
}
@@ -280,9 +281,9 @@ static void update_out_handler(struct consumer const *consumer, size_t count)
* Copy the rest of the message into the block buffer to pass
* to the updater.
*/
- block_index = sizeof(updu) -
- offsetof(struct update_pdu_header, cmd);
- memcpy(block_buffer, &updu.cmd, block_index);
+ block_index = sizeof(upfr) -
+ offsetof(struct update_frame_header, cmd);
+ memcpy(block_buffer, &upfr.cmd, block_index);
block_size -= block_index;
rx_state_ = rx_inside_block;
return;
@@ -294,39 +295,14 @@ static void update_out_handler(struct consumer const *consumer, size_t count)
block_size -= count;
if (block_size) {
- if (count == sizeof(updu)) {
+ if (count <= sizeof(upfr)) {
/*
* A block header size instead of chunk size message
- * has been received. There must have been some packet
- * loss and the host is restarting this block.
- *
- * Let's copy its contents into the header structure.
- */
- memcpy(&updu, block_buffer + block_index - count,
- count);
-
-
- /* And re-allocate a large enough buffer. */
- shared_mem_release(block_buffer);
- block_size = be32toh(updu.block_size) -
- offsetof(struct update_pdu_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);
- return;
- }
-
- /*
- * Copy the rest of the message into the block buffer
- * to pass to the updater.
+ * has been received, let's abort the transfer.
*/
- block_index = sizeof(updu) -
- offsetof(struct update_pdu_header, cmd);
- memcpy(block_buffer, &updu.cmd, block_index);
- block_size -= block_index;
+ CPRINTS("Unexpected header");
+ send_error_reset(UPDATE_GEN_ERROR);
+ return;
}
return; /* More to come. */
}
@@ -337,6 +313,11 @@ static void update_out_handler(struct consumer const *consumer, size_t count)
*/
fw_update_command_handler(block_buffer, block_index, &resp_size);
+ /*
+ * There was at least an attempt to program the flash, set the
+ * flag.
+ */
+ data_was_transferred = 1;
resp_value = block_buffer[0];
QUEUE_ADD_UNITS(&update_to_usb, &resp_value, sizeof(resp_value));
rx_state_ = rx_outside_block;
diff --git a/include/update_fw.h b/include/update_fw.h
index f1b76e1033..8f7148f6ed 100644
--- a/include/update_fw.h
+++ b/include/update_fw.h
@@ -8,9 +8,149 @@
#include <stddef.h>
+
+/*
+ * This file contains structures used to facilitate EC firmware updates
+ * over USB (and over TPM for cr50).
+ *
+ * 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 PDUs by prepending a header including the flash
+ * offset where the block is destined and its digest.
+ *
+ * The EC device responds to each PDU with a confirmation which is 1 byte
+ * response. Zero value means success, non zero value is the error code
+ * reported by EC.
+ *
+ * To establish the connection, the host sends a different PDU, which
+ * contains no data and is destined to offset 0. Receiving such a PDU
+ * signals the EC that the host intends to transfer a new image.
+ *
+ * The connection establishment response is described by the
+ * first_response_pdu structure below.
+ */
+
+#define UPDATE_PROTOCOL_VERSION 6
+
+/*
+ * This is the format of the update PDU header.
+ *
+ * block digest: the first four bytes of the sha1 digest of the rest of the
+ * structure (can be 0 on boards where digest is ignored).
+ * block_base: offset of this PDU into the flash SPI.
+ */
+struct update_command {
+ uint32_t block_digest;
+ uint32_t block_base;
+ /* The actual payload goes here. */
+} __packed;
+
+/*
+ * This is the frame format the host uses when sending update PDUs over USB.
+ *
+ * The PDUs are up to 1K bytes in size, they are fragmented into USB chunks of
+ * 64 bytes each and reassembled on the receive side before being passed to
+ * the flash update function.
+ *
+ * The flash update function receives the unframed PDU body (starting at the
+ * cmd field below), and puts its reply into the same buffer the PDU was in.
+ */
+struct update_frame_header {
+ uint32_t block_size; /* Total frame size, including this field. */
+ struct update_command cmd;
+};
+
+/*
+ * A convenience structure which allows to group together various revision
+ * fields of the header created by the signer.
+ *
+ * These fields are compared when deciding if versions of two images are the
+ * same or when deciding which one of the available images to run.
+ */
+struct signed_header_version {
+ uint32_t minor;
+ uint32_t major;
+ uint32_t epoch;
+};
+
+/*
+ * Response to the connection establishment request.
+ *
+ * When responding to the very first packet of the update sequence, the
+ * original USB update implementation was responding with a four byte value,
+ * just as to any other block of the transfer sequence.
+ *
+ * It became clear that there is a need to be able to enhance the update
+ * protocol, while staying backwards compatible.
+ *
+ * All newer protocol versions (starting with version 2) respond to the very
+ * first packet with an 8 byte or larger response, where the first 4 bytes are
+ * a version specific data, and the second 4 bytes - the protocol version
+ * number.
+ *
+ * This way the host receiving of a four byte value in response to the first
+ * packet is considered an indication of the target running the 'legacy'
+ * protocol, version 1. Receiving of an 8 byte or longer response would
+ * communicates the protocol version in the second 4 bytes.
+ */
+struct first_response_pdu {
+ uint32_t return_value;
+
+ /* The below fields are present in versions 2 and up. */
+ uint32_t protocol_version;
+
+ /* The below fields are present in versions 3 and up. */
+ uint32_t backup_ro_offset;
+ uint32_t backup_rw_offset;
+
+ /* The below fields are present in versions 4 and up. */
+ /* Versions of the currently active RO and RW sections. */
+ struct signed_header_version shv[2];
+
+ /* The below fields are present in versions 5 and up */
+ /* keyids of the currently active RO and RW sections. */
+ uint32_t keyid[2];
+};
+
/* TODO: Handle this in update_fw.c, not usb_update.c */
#define UPDATE_DONE 0xB007AB1E
+void fw_update_command_handler(void *body,
+ size_t cmd_size,
+ size_t *response_size);
+
+/* Used to tell fw update the update ran successfully and is finished */
+void fw_update_complete(void);
+
+/* Verify integrity of the PDU received. */
+int update_pdu_valid(struct update_command *cmd_body, size_t cmd_size);
+
+/* Various update command return values. */
+enum {
+ UPDATE_SUCCESS = 0,
+ UPDATE_BAD_ADDR = 1,
+ UPDATE_ERASE_FAILURE = 2,
+ UPDATE_DATA_ERROR = 3,
+ UPDATE_WRITE_FAILURE = 4,
+ UPDATE_VERIFY_ERROR = 5,
+ UPDATE_GEN_ERROR = 6,
+ UPDATE_MALLOC_ERROR = 7,
+ UPDATE_ROLLBACK_ERROR = 8,
+ UPDATE_RATE_LIMIT_ERROR = 9,
+};
+
+/*
+ * This is the size of the update frame payload, unless this is the last chunk
+ * of the image.
+ *
+ * TODO(b/36375666): Some boards may not be able to allocate that much memory.
+ */
+#define UPDATE_PDU_SIZE 1024
+
+/* TODO(b/36375666): The structures below might not belong in this file. */
+
/*
* This array defines possible sections available for the firmare update.
* The section which does not map the current execting code is picked as the
@@ -28,9 +168,4 @@ struct section_descriptor {
extern const struct section_descriptor * const rw_sections;
extern const int num_rw_sections;
-
-void fw_update_command_handler(void *body,
- size_t cmd_size,
- size_t *response_size);
-
#endif /* ! __CROS_EC_UPDATE_FW_H */