diff options
Diffstat (limited to 'common/vboot_hash.c')
-rw-r--r-- | common/vboot_hash.c | 498 |
1 files changed, 0 insertions, 498 deletions
diff --git a/common/vboot_hash.c b/common/vboot_hash.c deleted file mode 100644 index 33172e7c74..0000000000 --- a/common/vboot_hash.c +++ /dev/null @@ -1,498 +0,0 @@ -/* Copyright 2012 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. - */ - -/* Verified boot hash computing module for Chrome EC */ - -#include "clock.h" -#include "common.h" -#include "console.h" -#include "flash.h" -#include "hooks.h" -#include "host_command.h" -#include "sha256.h" -#include "shared_mem.h" -#include "stdbool.h" -#include "stdint.h" -#include "system.h" -#include "task.h" -#include "timer.h" -#include "util.h" -#include "watchdog.h" - -/* Console output macros */ -#define CPUTS(outstr) cputs(CC_VBOOT, outstr) -#define CPRINTS(format, args...) cprints(CC_VBOOT, format, ## args) - -struct vboot_hash_tag { - uint8_t hash[SHA256_DIGEST_SIZE]; - uint32_t offset; - uint32_t size; -}; - -#define CHUNK_SIZE 1024 /* Bytes to hash per deferred call */ -#define WORK_INTERVAL_US 100 /* Delay between deferred calls */ - -/* Check that CHUNK_SIZE fits in shared memory. */ -SHARED_MEM_CHECK_SIZE(CHUNK_SIZE); - -static uint32_t data_offset; -static uint32_t data_size; -static uint32_t curr_pos; -static const uint8_t *hash; /* Hash, or NULL if not valid */ -static int want_abort; -static int in_progress; -#define VBOOT_HASH_DEFERRED true -#define VBOOT_HASH_BLOCKING false - -static struct sha256_ctx ctx; - -int vboot_hash_in_progress(void) -{ - return in_progress; -} - -/** - * Abort hash currently in progress, and invalidate any completed hash. - */ -void vboot_hash_abort(void) -{ - if (in_progress) { - want_abort = 1; - } else { - CPRINTS("hash abort"); - want_abort = 0; - data_size = 0; - hash = NULL; -#ifdef CONFIG_SHA256_HW_ACCELERATE - SHA256_abort(&ctx); -#endif - } -} - -static void vboot_hash_next_chunk(void); -DECLARE_DEFERRED(vboot_hash_next_chunk); - -#ifndef CONFIG_MAPPED_STORAGE - -static int read_and_hash_chunk(int offset, int size) -{ - char *buf; - int rv; - - if (size == 0) - return EC_SUCCESS; - - rv = shared_mem_acquire(size, &buf); - if (rv == EC_ERROR_BUSY) { - /* Couldn't update hash right now; try again later */ - hook_call_deferred(&vboot_hash_next_chunk_data, - WORK_INTERVAL_US); - return rv; - } else if (rv != EC_SUCCESS) { - vboot_hash_abort(); - return rv; - } - - rv = crec_flash_read(offset, size, buf); - if (rv == EC_SUCCESS) - SHA256_update(&ctx, (const uint8_t *)buf, size); - else - vboot_hash_abort(); - - shared_mem_release(buf); - return rv; -} - -#endif - -#ifdef CONFIG_CONSOLE_VERBOSE -#define SHA256_PRINT_SIZE SHA256_DIGEST_SIZE -#else -#define SHA256_PRINT_SIZE 4 -#endif - -static void hash_next_chunk(size_t size) -{ -#ifdef CONFIG_MAPPED_STORAGE - crec_flash_lock_mapped_storage(1); - SHA256_update(&ctx, (const uint8_t *) - ((uintptr_t)CONFIG_MAPPED_STORAGE_BASE + - data_offset + curr_pos), size); - crec_flash_lock_mapped_storage(0); -#else - if (read_and_hash_chunk(data_offset + curr_pos, size) != EC_SUCCESS) - return; -#endif -} - -static void vboot_hash_all_chunks(void) -{ - do { - size_t size = MIN(CHUNK_SIZE, data_size - curr_pos); - hash_next_chunk(size); - curr_pos += size; - } while (curr_pos < data_size); - - hash = SHA256_final(&ctx); - CPRINTS("hash done %ph", HEX_BUF(hash, SHA256_PRINT_SIZE)); - in_progress = 0; - clock_enable_module(MODULE_FAST_CPU, 0); - - return; -} - -/** - * Do next chunk of hashing work, if any. - */ -static void vboot_hash_next_chunk(void) -{ - int size; - - /* Handle abort */ - if (want_abort) { - in_progress = 0; - clock_enable_module(MODULE_FAST_CPU, 0); - vboot_hash_abort(); - return; - } - - /* Compute the next chunk of hash */ - size = MIN(CHUNK_SIZE, data_size - curr_pos); - hash_next_chunk(size); - - curr_pos += size; - if (curr_pos >= data_size) { - /* Store the final hash */ - hash = SHA256_final(&ctx); - CPRINTS("hash done %ph", HEX_BUF(hash, SHA256_PRINT_SIZE)); - - in_progress = 0; - - clock_enable_module(MODULE_FAST_CPU, 0); - - /* Handle receiving abort during finalize */ - if (want_abort) - vboot_hash_abort(); - - return; - } - - /* If we're still here, more work to do; come back later */ - hook_call_deferred(&vboot_hash_next_chunk_data, WORK_INTERVAL_US); -} - -/** - * - * If nonce_size is non-zero, prefixes the <nonce> onto the data to be hashed. - * Returns non-zero if error. - */ -/** - * Start computing a hash of <size> bytes of data at flash offset <offset>. - * - * @param offset start address of data on flash to compute hash for. - * @param size size of data to compute hash for. - * @param nonce nonce to differentiate hash. - * @param nonce_size size of nonce. - * @param deferred True to hash progressively through deferred calls. - * False to hash with a blocking single call. - * @return ec_error_list. - */ -static int vboot_hash_start(uint32_t offset, uint32_t size, - const uint8_t *nonce, int nonce_size, bool deferred) -{ - /* Fail if hash computation is already in progress */ - if (in_progress) - return EC_ERROR_BUSY; - - /* - * Make sure request fits inside flash. That is, you can't use this - * command to peek at other memory. - */ - if (offset > CONFIG_FLASH_SIZE_BYTES || - size > CONFIG_FLASH_SIZE_BYTES || - offset + size > CONFIG_FLASH_SIZE_BYTES || nonce_size < 0) { - return EC_ERROR_INVAL; - } - - clock_enable_module(MODULE_FAST_CPU, 1); - /* Save new hash request */ - data_offset = offset; - data_size = size; - curr_pos = 0; - hash = NULL; - want_abort = 0; - in_progress = 1; - - /* Restart the hash computation */ - CPRINTS("hash start 0x%08x 0x%08x", offset, size); - SHA256_init(&ctx); - if (nonce_size) - SHA256_update(&ctx, nonce, nonce_size); - - if (deferred) - hook_call_deferred(&vboot_hash_next_chunk_data, 0); - else - vboot_hash_all_chunks(); - - return EC_SUCCESS; -} - -int vboot_hash_invalidate(int offset, int size) -{ - /* Don't invalidate if passed an invalid region */ - if (offset < 0 || size <= 0 || offset + size < 0) - return 0; - - /* Don't invalidate if hash is already invalid */ - if (!hash) - return 0; - - /* - * Always invalidate zero-size hash. No overlap if passed region is off - * either end of hashed region. - */ - if (data_size > 0 && - (offset + size <= data_offset || offset >= data_offset + data_size)) - return 0; - - /* Invalidate the hash */ - CPRINTS("hash invalidated 0x%08x 0x%08x", offset, size); - vboot_hash_abort(); - return 1; -} - -/*****************************************************************************/ -/* Hooks */ - -/** - * Returns the size of a RW copy to be hashed as expected by Softsync. - */ -static uint32_t get_rw_size(void) -{ -#ifdef CONFIG_VBOOT_EFS /* Only needed for EFS, which signs and verifies - * entire RW, thus not needed for EFS2, which - * verifies only the used image size. */ - return CONFIG_RW_SIZE; -#else - return system_get_image_used(EC_IMAGE_RW); -#endif -} - -static void vboot_hash_init(void) -{ -#ifdef CONFIG_HOSTCMD_EVENTS - /* - * Don't auto-start hash computation if we've asked the host to enter - * recovery mode since we probably won't need the hash. Although - * the host is capable of clearing this host event, the host is - * likely not even up and running yet in the case of cold boot, due to - * the power sequencing task not having run yet. - */ - if (!(host_get_events() & - EC_HOST_EVENT_MASK(EC_HOST_EVENT_KEYBOARD_RECOVERY))) -#endif - { - /* Start computing the hash of RW firmware */ - vboot_hash_start(flash_get_rw_offset(system_get_active_copy()), - get_rw_size(), NULL, 0, VBOOT_HASH_DEFERRED); - } -} -DECLARE_HOOK(HOOK_INIT, vboot_hash_init, HOOK_PRIO_INIT_VBOOT_HASH); - -int vboot_get_rw_hash(const uint8_t **dst) -{ - int rv = vboot_hash_start(flash_get_rw_offset(system_get_active_copy()), - get_rw_size(), NULL, 0, VBOOT_HASH_BLOCKING); - *dst = hash; - return rv; -} - -/** - * Returns the offset of RO or RW image if the either region is specifically - * requested otherwise return the current hash offset. - */ -static int get_offset(int offset) -{ - if (offset == EC_VBOOT_HASH_OFFSET_RO) - return CONFIG_EC_PROTECTED_STORAGE_OFF + CONFIG_RO_STORAGE_OFF; - if (offset == EC_VBOOT_HASH_OFFSET_ACTIVE) - return flash_get_rw_offset(system_get_active_copy()); - if (offset == EC_VBOOT_HASH_OFFSET_UPDATE) - return flash_get_rw_offset(system_get_update_copy()); - return offset; -} - -/****************************************************************************/ -/* Console commands */ -#ifdef CONFIG_CMD_HASH -static int command_hash(int argc, char **argv) -{ - uint32_t offset = CONFIG_EC_WRITABLE_STORAGE_OFF + - CONFIG_RW_STORAGE_OFF; - uint32_t size = CONFIG_RW_SIZE; - char *e; - - if (argc == 1) { - ccprintf("Offset: 0x%08x\n", data_offset); - ccprintf("Size: 0x%08x (%d)\n", data_size, data_size); - ccprintf("Digest: "); - if (want_abort) - ccprintf("(aborting)\n"); - else if (in_progress) - ccprintf("(in progress)\n"); - else if (hash) - ccprintf("%ph\n", HEX_BUF(hash, SHA256_DIGEST_SIZE)); - else - ccprintf("(invalid)\n"); - - return EC_SUCCESS; - } - - if (argc == 2) { - if (!strcasecmp(argv[1], "abort")) { - vboot_hash_abort(); - return EC_SUCCESS; - } else if (!strcasecmp(argv[1], "rw")) { - return vboot_hash_start( - get_offset(EC_VBOOT_HASH_OFFSET_ACTIVE), - get_rw_size(), - NULL, 0, VBOOT_HASH_DEFERRED); - } else if (!strcasecmp(argv[1], "ro")) { - return vboot_hash_start( - CONFIG_EC_PROTECTED_STORAGE_OFF + - CONFIG_RO_STORAGE_OFF, - system_get_image_used(EC_IMAGE_RO), - NULL, 0, VBOOT_HASH_DEFERRED); - } - return EC_ERROR_PARAM2; - } - - if (argc >= 3) { - offset = strtoi(argv[1], &e, 0); - if (*e) - return EC_ERROR_PARAM1; - - size = strtoi(argv[2], &e, 0); - if (*e) - return EC_ERROR_PARAM2; - } - - if (argc == 4) { - int nonce = strtoi(argv[3], &e, 0); - if (*e) - return EC_ERROR_PARAM3; - - return vboot_hash_start(offset, size, - (const uint8_t *)&nonce, - sizeof(nonce), VBOOT_HASH_DEFERRED); - } else - return vboot_hash_start(offset, size, - NULL, 0, VBOOT_HASH_DEFERRED); -} -DECLARE_CONSOLE_COMMAND(hash, command_hash, - "[abort | ro | rw] | [<offset> <size> [<nonce>]]", - "Request hash recomputation"); -#endif /* CONFIG_CMD_HASH */ -/****************************************************************************/ -/* Host commands */ - -/* Fill in the response with the current hash status */ -static void fill_response(struct ec_response_vboot_hash *r, - int request_offset) -{ - if (in_progress) - r->status = EC_VBOOT_HASH_STATUS_BUSY; - else if (get_offset(request_offset) == data_offset && hash && - !want_abort) { - r->status = EC_VBOOT_HASH_STATUS_DONE; - r->hash_type = EC_VBOOT_HASH_TYPE_SHA256; - r->digest_size = SHA256_DIGEST_SIZE; - r->reserved0 = 0; - r->offset = data_offset; - r->size = data_size; - ASSERT(SHA256_DIGEST_SIZE < sizeof(r->hash_digest)); - memcpy(r->hash_digest, hash, SHA256_DIGEST_SIZE); - } else - r->status = EC_VBOOT_HASH_STATUS_NONE; -} - -/** - * Start computing a hash, with validity checking on params. - * - * @return EC_RES_SUCCESS if success, or other result code on error. - */ -static int host_start_hash(const struct ec_params_vboot_hash *p) -{ - int offset = p->offset; - int size = p->size; - int rv; - - /* Validity-check input params */ - if (p->hash_type != EC_VBOOT_HASH_TYPE_SHA256) - return EC_RES_INVALID_PARAM; - if (p->nonce_size > sizeof(p->nonce_data)) - return EC_RES_INVALID_PARAM; - - /* Handle special offset values */ - if (offset == EC_VBOOT_HASH_OFFSET_RO) - size = system_get_image_used(EC_IMAGE_RO); - else if ((offset == EC_VBOOT_HASH_OFFSET_ACTIVE) || - (offset == EC_VBOOT_HASH_OFFSET_UPDATE)) - size = get_rw_size(); - offset = get_offset(offset); - rv = vboot_hash_start(offset, size, p->nonce_data, p->nonce_size, - VBOOT_HASH_DEFERRED); - - if (rv == EC_SUCCESS) - return EC_RES_SUCCESS; - else if (rv == EC_ERROR_INVAL) - return EC_RES_INVALID_PARAM; - else - return EC_RES_ERROR; -} - -static enum ec_status -host_command_vboot_hash(struct host_cmd_handler_args *args) -{ - const struct ec_params_vboot_hash *p = args->params; - struct ec_response_vboot_hash *r = args->response; - int rv; - - switch (p->cmd) { - case EC_VBOOT_HASH_GET: - if (p->offset || p->size) - fill_response(r, p->offset); - else - fill_response(r, data_offset); - - args->response_size = sizeof(*r); - return EC_RES_SUCCESS; - - case EC_VBOOT_HASH_ABORT: - vboot_hash_abort(); - return EC_RES_SUCCESS; - - case EC_VBOOT_HASH_START: - case EC_VBOOT_HASH_RECALC: - rv = host_start_hash(p); - if (rv != EC_RES_SUCCESS) - return rv; - - /* Wait for hash to finish if command is RECALC */ - if (p->cmd == EC_VBOOT_HASH_RECALC) - while (in_progress) - usleep(1000); - - fill_response(r, p->offset); - args->response_size = sizeof(*r); - return EC_RES_SUCCESS; - - default: - return EC_RES_INVALID_PARAM; - } -} -DECLARE_HOST_COMMAND(EC_CMD_VBOOT_HASH, - host_command_vboot_hash, - EC_VER_MASK(0)); |