| /* 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 "extension.h" |
| #include "flash.h" |
| #include "hooks.h" |
| #include "include/compile_time_macros.h" |
| #include "uart.h" |
| #include "update_fw.h" |
| #include "util.h" |
| |
| #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, |
| }; |
| |
| /* |
| * The payload of the update 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 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; |
| uint32_t run_time_offs = (uint32_t) set_valid_section - |
| CONFIG_PROGRAM_MEMORY_BASE; |
| valid_section = rw_sections; |
| |
| for (i = 0; i < num_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; |
| } |
| if (i == num_rw_sections) { |
| CPRINTF("%s:%d No valid section found!\n", __func__, __LINE__); |
| return EC_ERROR_INVAL; |
| } |
| 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) |
| { |
| if (valid_section && |
| (block_offset >= valid_section->sect_base_offset) && |
| ((block_offset + body_size) <= valid_section->sect_top_offset)) |
| return 1; |
| |
| return 0; |
| } |
| |
| void fw_update_command_handler(void *body, |
| size_t cmd_size, |
| size_t *response_size) |
| { |
| struct update_command *cmd_body = body; |
| uint8_t *rv = body; |
| 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 update_command, block_body); |
| if (body_size < 0) { |
| CPRINTF("%s:%d\n", __func__, __LINE__); |
| *rv = UPDATE_GEN_ERROR; |
| return; |
| } |
| |
| if (!cmd_body->block_base && !body_size) { |
| int ret; |
| /* |
| * This is the first message of the update process, let's |
| * determine the valid update section and erase its contents. |
| */ |
| 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; |
| } |
| |
| /* |
| * 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); |
| 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); |
| 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); |
| return; |
| } |
| |
| /* Werify that data was written properly. */ |
| if (memcmp(cmd_body->block_body, (void *) |
| (block_offset + CONFIG_PROGRAM_MEMORY_BASE), |
| body_size)) { |
| *rv = UPDATE_VERIFY_ERROR; |
| CPRINTF("%s:%d update verification error\n", |
| __func__, __LINE__); |
| return; |
| } |
| |
| *rv = UPDATE_SUCCESS; |
| } |