diff options
Diffstat (limited to 'chip/g/upgrade_fw.c')
-rw-r--r-- | chip/g/upgrade_fw.c | 210 |
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; } |