summaryrefslogtreecommitdiff
path: root/chip/g/upgrade_fw.c
diff options
context:
space:
mode:
Diffstat (limited to 'chip/g/upgrade_fw.c')
-rw-r--r--chip/g/upgrade_fw.c210
1 files changed, 128 insertions, 82 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;
}