diff options
Diffstat (limited to 'board/cr50/tpm2/upgrade.c')
-rw-r--r-- | board/cr50/tpm2/upgrade.c | 186 |
1 files changed, 186 insertions, 0 deletions
diff --git a/board/cr50/tpm2/upgrade.c b/board/cr50/tpm2/upgrade.c new file mode 100644 index 0000000000..634c7fa9e4 --- /dev/null +++ b/board/cr50/tpm2/upgrade.c @@ -0,0 +1,186 @@ +/* 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 "dcrypto/dcrypto.h" +#include "extension.h" +#include "flash.h" +#include "hooks.h" +#include "include/compile_time_macros.h" +#include "sha1.h" +#include "uart.h" + +#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, +}; + +/* + * The payload of the upgrade 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 upgrade_command { + uint32_t block_digest; + uint32_t block_base; + uint8_t block_body[0]; +} __packed; + +/* + * 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. + */ +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) +{ + 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; + break; + } +} + +/* 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)) + return 1; + + return 0; +} + +static void fw_upgrade_command_handler(void *body, + size_t cmd_size, + size_t *response_size) +{ + struct upgrade_command *cmd_body = body; + uint8_t *rv = body; + uint8_t sha1_digest[SHA1_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); + + body_size = cmd_size - offsetof(struct upgrade_command, block_body); + if (body_size < 0) { + CPRINTF("%s:%d\n", __func__, __LINE__); + *rv = UPGRADE_GEN_ERROR; + return; + } + + if (!cmd_body->block_base && !body_size) { + /* + * This is the first message of the upgrade process, let's + * determine the valid upgrade section and erase its contents. + */ + 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; + } + /* + * Successful erase means that we need to return the base + * address of the section to be programmed with the upgrade. + */ + *(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_upgrade_chunk(block_offset, body_size)) { + *rv = 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); + return; + } + + /* Check if the block was received properly. */ + DCRYPTO_SHA1_hash((uint8_t *)&cmd_body->block_base, + body_size + sizeof(cmd_body->block_base), + sha1_digest); + if (memcmp(sha1_digest, &cmd_body->block_digest, + sizeof(cmd_body->block_digest))) { + *rv = 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, + block_offset); + return; + } + + CPRINTF("%s: programming at offset 0x%x\n", __func__, block_offset); + if (flash_physical_write(block_offset, body_size, + cmd_body->block_body) != EC_SUCCESS) { + *rv = UPGRADE_WRITE_FAILURE; + CPRINTF("%s:%d upgrade write error\n", + __func__, __LINE__); + return; + } + + /* Werify that data was written properly. */ + if (memcmp(cmd_body->block_body, (void *) + (block_offset + CONFIG_PROGRAM_MEMORY_BASE), + body_size)) { + *rv = UPGRADE_VERIFY_ERROR; + CPRINTF("%s:%d upgrade verification error\n", + __func__, __LINE__); + return; + } + + *rv = UPGRADE_SUCCESS; +} + +DECLARE_EXTENSION_COMMAND(EXTENSION_FW_UPGRADE, fw_upgrade_command_handler); |