| /* 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 "rollback.h" |
| #include "rwsig.h" |
| #include "sha256.h" |
| #include "system.h" |
| #include "uart.h" |
| #include "update_fw.h" |
| #include "util.h" |
| #include "vb21_struct.h" |
| |
| #if defined(CONFIG_TOUCHPAD_VIRTUAL_OFF) && defined(CONFIG_TOUCHPAD_HASH_FW) |
| #define CONFIG_TOUCHPAD_FW_CHUNKS \ |
| (CONFIG_TOUCHPAD_VIRTUAL_SIZE / CONFIG_UPDATE_PDU_SIZE) |
| |
| #include "touchpad_fw_hash.h" |
| |
| BUILD_ASSERT(sizeof(touchpad_fw_hashes) == |
| (CONFIG_TOUCHPAD_FW_CHUNKS * SHA256_DIGEST_SIZE)); |
| BUILD_ASSERT(sizeof(touchpad_fw_hashes[0]) == SHA256_DIGEST_SIZE); |
| |
| BUILD_ASSERT(sizeof(touchpad_fw_full_hash) == SHA256_DIGEST_SIZE); |
| #endif |
| |
| #define CPRINTF(format, args...) cprintf(CC_USB, format, ## args) |
| |
| /* Section to be updated (i.e. not the current section). */ |
| struct { |
| uint32_t base_offset; |
| uint32_t top_offset; |
| } update_section; |
| |
| #ifdef CONFIG_TOUCHPAD_VIRTUAL_OFF |
| /* |
| * Check if a block is within touchpad FW virtual address region, and |
| * is therefore meant to be flashed to the touchpad. |
| */ |
| static int is_touchpad_block(uint32_t block_offset, size_t body_size) |
| { |
| return (block_offset >= CONFIG_TOUCHPAD_VIRTUAL_OFF) && |
| (block_offset + body_size) <= |
| (CONFIG_TOUCHPAD_VIRTUAL_OFF + |
| CONFIG_TOUCHPAD_VIRTUAL_SIZE); |
| } |
| #endif |
| |
| /* |
| * 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 (update_section.base_offset != update_section.top_offset && |
| (block_offset >= update_section.base_offset) && |
| ((block_offset + body_size) <= update_section.top_offset)) { |
| |
| base = update_section.base_offset; |
| size = update_section.top_offset - |
| update_section.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; |
| } |
| |
| #ifdef CONFIG_TOUCHPAD_VIRTUAL_OFF |
| if (is_touchpad_block(block_offset, body_size)) |
| return UPDATE_SUCCESS; |
| #endif |
| |
| CPRINTF("%s:%d %x, %d section base %x top %x\n", |
| __func__, __LINE__, |
| block_offset, body_size, |
| update_section.base_offset, |
| update_section.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) |
| { |
| #if defined(CONFIG_TOUCHPAD_VIRTUAL_OFF) && defined(CONFIG_TOUCHPAD_HASH_FW) |
| if (is_touchpad_block(block_offset, body_size)) { |
| struct sha256_ctx ctx; |
| uint8_t *tmp; |
| uint32_t fw_offset = block_offset - CONFIG_TOUCHPAD_VIRTUAL_OFF; |
| unsigned int chunk = fw_offset / CONFIG_UPDATE_PDU_SIZE; |
| int good = 0; |
| |
| if (chunk >= CONFIG_TOUCHPAD_FW_CHUNKS || |
| (fw_offset % CONFIG_UPDATE_PDU_SIZE) != 0) { |
| CPRINTF("%s: TP invalid offset %08x\n", |
| __func__, fw_offset); |
| return 0; |
| } |
| |
| SHA256_init(&ctx); |
| SHA256_update(&ctx, update_data, body_size); |
| tmp = SHA256_final(&ctx); |
| |
| good = !memcmp(tmp, touchpad_fw_hashes[chunk], |
| SHA256_DIGEST_SIZE); |
| |
| CPRINTF("%s: TP %08x %02x..%02x (%s)\n", __func__, |
| fw_offset, tmp[0], tmp[31], good ? "GOOD" : "BAD"); |
| |
| return good; |
| } |
| #endif |
| 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. |
| */ |
| void fw_update_start(struct first_response_pdu *rpdu) |
| { |
| const char *version; |
| #ifdef CONFIG_RWSIG_TYPE_RWSIG |
| const struct vb21_packed_key *vb21_key; |
| #endif |
| |
| rpdu->header_type = htobe16(UPDATE_HEADER_TYPE_COMMON); |
| |
| /* Determine the valid update section. */ |
| switch (system_get_image_copy()) { |
| case SYSTEM_IMAGE_RO: |
| /* RO running, so update RW */ |
| update_section.base_offset = CONFIG_RW_MEM_OFF; |
| update_section.top_offset = CONFIG_RW_MEM_OFF + CONFIG_RW_SIZE; |
| version = system_get_version(SYSTEM_IMAGE_RW); |
| break; |
| case SYSTEM_IMAGE_RW: |
| /* RW running, so update RO */ |
| update_section.base_offset = CONFIG_RO_MEM_OFF; |
| update_section.top_offset = CONFIG_RO_MEM_OFF + CONFIG_RO_SIZE; |
| version = system_get_version(SYSTEM_IMAGE_RO); |
| break; |
| default: |
| CPRINTF("%s:%d\n", __func__, __LINE__); |
| rpdu->return_value = htobe32(UPDATE_GEN_ERROR); |
| return; |
| } |
| |
| rpdu->common.maximum_pdu_size = htobe32(CONFIG_UPDATE_PDU_SIZE); |
| rpdu->common.flash_protection = htobe32(flash_get_protect()); |
| rpdu->common.offset = htobe32(update_section.base_offset); |
| if (version) |
| memcpy(rpdu->common.version, version, |
| sizeof(rpdu->common.version)); |
| |
| #ifdef CONFIG_ROLLBACK |
| rpdu->common.min_rollback = htobe32(rollback_get_minimum_version()); |
| #else |
| rpdu->common.min_rollback = htobe32(-1); |
| #endif |
| |
| #ifdef CONFIG_RWSIG_TYPE_RWSIG |
| vb21_key = (const struct vb21_packed_key *)CONFIG_RO_PUBKEY_ADDR; |
| rpdu->common.key_version = htobe32(vb21_key->key_version); |
| #endif |
| |
| #ifdef HAS_TASK_RWSIG |
| /* Do not allow the update to start if RWSIG is still running. */ |
| if (rwsig_get_status() == RWSIG_IN_PROGRESS) { |
| CPRINTF("RWSIG in progress\n"); |
| rpdu->return_value = htobe32(UPDATE_RWSIG_BUSY); |
| } |
| #endif |
| } |
| |
| void fw_update_command_handler(void *body, |
| size_t cmd_size, |
| size_t *response_size) |
| { |
| struct update_command *cmd_body = body; |
| void *update_data; |
| uint8_t *error_code = body; /* Cache the address for code clarity. */ |
| size_t body_size; |
| uint32_t block_offset; |
| |
| *response_size = 1; /* One byte response unless this is a start PDU. */ |
| |
| if (cmd_size < sizeof(struct update_command)) { |
| CPRINTF("%s:%d\n", __func__, __LINE__); |
| *error_code = UPDATE_GEN_ERROR; |
| return; |
| } |
| body_size = cmd_size - sizeof(struct update_command); |
| |
| if (!cmd_body->block_base && !body_size) { |
| struct first_response_pdu *rpdu = body; |
| |
| /* |
| * 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. |
| */ |
| |
| /* First, prepare the response structure. */ |
| memset(rpdu, 0, sizeof(*rpdu)); |
| /* |
| * TODO(b/36375666): The response size can be shorter depending |
| * on which board-specific type of response we provide. This |
| * may send trailing 0 bytes, which should be harmless. |
| */ |
| *response_size = sizeof(*rpdu); |
| rpdu->protocol_version = htobe16(UPDATE_PROTOCOL_VERSION); |
| |
| /* 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. */ |
| *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; |
| } |
| |
| /* |
| * TODO(b/36375666): chip/g code has some cr50-specific stuff right |
| * here, which should probably be merged into contents_allowed... |
| */ |
| |
| #ifdef CONFIG_TOUCHPAD_VIRTUAL_OFF |
| if (is_touchpad_block(block_offset, body_size)) { |
| if (touchpad_update_write( |
| block_offset - CONFIG_TOUCHPAD_VIRTUAL_OFF, |
| body_size, update_data) != EC_SUCCESS) { |
| *error_code = UPDATE_WRITE_FAILURE; |
| CPRINTF("%s:%d update write error\n", |
| __func__, __LINE__); |
| return; |
| } |
| |
| new_chunk_written(block_offset); |
| |
| *error_code = UPDATE_SUCCESS; |
| return; |
| } |
| #endif |
| |
| 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; |
| } |
| |
| new_chunk_written(block_offset); |
| |
| /* Verify that data was written properly. */ |
| if (memcmp(update_data, (void *) |
| (block_offset + CONFIG_PROGRAM_MEMORY_BASE), |
| body_size)) { |
| *error_code = UPDATE_VERIFY_ERROR; |
| CPRINTF("%s:%d update verification error\n", |
| __func__, __LINE__); |
| return; |
| } |
| |
| *error_code = UPDATE_SUCCESS; |
| } |
| |
| /* TODO(b/36375666): This need to be overridden for chip/g. */ |
| void fw_update_complete(void) |
| { |
| } |