summaryrefslogtreecommitdiff
path: root/common/rollback.c
diff options
context:
space:
mode:
Diffstat (limited to 'common/rollback.c')
-rw-r--r--common/rollback.c520
1 files changed, 0 insertions, 520 deletions
diff --git a/common/rollback.c b/common/rollback.c
deleted file mode 100644
index 984058c49a..0000000000
--- a/common/rollback.c
+++ /dev/null
@@ -1,520 +0,0 @@
-/* Copyright 2017 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.
- */
-
-/* Rollback protection logic. */
-
-#include "common.h"
-#include "console.h"
-#ifdef CONFIG_LIBCRYPTOC
-#include "cryptoc/util.h"
-#endif
-#include "flash.h"
-#include "hooks.h"
-#include "host_command.h"
-#ifdef CONFIG_MPU
-#include "mpu.h"
-#endif
-#include "rollback.h"
-#include "rollback_private.h"
-#include "sha256.h"
-#include "system.h"
-#include "task.h"
-#include "trng.h"
-#include "util.h"
-
-/* Console output macros */
-#define CPRINTS(format, args...) cprints(CC_SYSTEM, format, ## args)
-
-/* Number of rollback regions */
-#define ROLLBACK_REGIONS 2
-
-static int get_rollback_offset(int region)
-{
-#ifdef CONFIG_FLASH_MULTIPLE_REGION
- int rv;
- int rollback_start_bank = crec_flash_bank_index(CONFIG_ROLLBACK_OFF);
-
- rv = crec_flash_bank_start_offset(rollback_start_bank + region);
- ASSERT(rv >= 0);
- return rv;
-#else
- return CONFIG_ROLLBACK_OFF + region * CONFIG_FLASH_ERASE_SIZE;
-#endif
-}
-
-#ifdef SECTION_IS_RO
-static int get_rollback_erase_size_bytes(int region)
-{
- int erase_size;
-
-#ifndef CONFIG_FLASH_MULTIPLE_REGION
- erase_size = CONFIG_FLASH_ERASE_SIZE;
-#else
- int rollback_start_bank = crec_flash_bank_index(CONFIG_ROLLBACK_OFF);
-
- erase_size = crec_flash_bank_erase_size(rollback_start_bank + region);
-#endif
- ASSERT(erase_size > 0);
- ASSERT(ROLLBACK_REGIONS * erase_size <= CONFIG_ROLLBACK_SIZE);
- ASSERT(sizeof(struct rollback_data) <= erase_size);
- return erase_size;
-}
-#endif
-
-/*
- * When MPU is available, read rollback with interrupts disabled, to minimize
- * time protection is left open.
- */
-static void lock_rollback(void)
-{
-#ifdef CONFIG_ROLLBACK_MPU_PROTECT
- mpu_lock_rollback(1);
- interrupt_enable();
-#endif
-}
-
-static void unlock_rollback(void)
-{
-#ifdef CONFIG_ROLLBACK_MPU_PROTECT
- interrupt_disable();
- mpu_lock_rollback(0);
-#endif
-}
-
-static void clear_rollback(struct rollback_data *data)
-{
-#ifdef CONFIG_ROLLBACK_SECRET_SIZE
- always_memset(data->secret, 0, sizeof(data->secret));
-#endif
-}
-
-int read_rollback(int region, struct rollback_data *data)
-{
- int offset;
- int ret = EC_SUCCESS;
-
- offset = get_rollback_offset(region);
-
- unlock_rollback();
- if (crec_flash_read(offset, sizeof(*data), (char *)data))
- ret = EC_ERROR_UNKNOWN;
- lock_rollback();
-
- return ret;
-}
-
-/*
- * Get the most recent rollback information.
- *
- * @data: Returns most recent rollback data block. The data is filled
- * with zeros if no valid rollback block is present
- *
- * Return most recent region index on success (>= 0, or 0 if no rollback
- * region is valid), negative value on error.
- */
-static int get_latest_rollback(struct rollback_data *data)
-{
- int ret = -1;
- int region;
- int min_region = -1;
- int max_id = -1;
- struct rollback_data tmp_data;
-
- for (region = 0; region < ROLLBACK_REGIONS; region++) {
- if (read_rollback(region, &tmp_data))
- goto failed;
-
- /* Check if not initialized or invalid cookie. */
- if (tmp_data.cookie != CROS_EC_ROLLBACK_COOKIE)
- continue;
-
- if (tmp_data.id > max_id) {
- min_region = region;
- max_id = tmp_data.id;
- }
- }
-
- if (min_region >= 0) {
- if (read_rollback(min_region, data))
- goto failed;
- } else {
- min_region = 0;
- clear_rollback(data);
- }
- ret = min_region;
-
-failed:
- clear_rollback(&tmp_data);
- return ret;
-}
-
-int32_t rollback_get_minimum_version(void)
-{
- struct rollback_data data;
- int32_t ret = -1;
-
- if (get_latest_rollback(&data) < 0)
- goto failed;
- ret = data.rollback_min_version;
-
-failed:
- clear_rollback(&data);
- return ret;
-}
-
-#ifdef CONFIG_ROLLBACK_SECRET_SIZE
-test_mockable int rollback_get_secret(uint8_t *secret)
-{
- int ret = EC_ERROR_UNKNOWN;
- struct rollback_data data;
-
- if (get_latest_rollback(&data) < 0)
- goto failed;
-
- /* Check that secret is not full of 0x00 or 0xff */
- if (bytes_are_trivial(data.secret, sizeof(data.secret)))
- goto failed;
-
- memcpy(secret, data.secret, sizeof(data.secret));
- ret = EC_SUCCESS;
-failed:
- clear_rollback(&data);
- return ret;
-}
-#endif
-
-#ifdef CONFIG_ROLLBACK_UPDATE
-
-#ifdef CONFIG_ROLLBACK_SECRET_SIZE
-static int add_entropy(uint8_t *dst, const uint8_t *src,
- const uint8_t *add, unsigned int add_len)
-{
- int ret = 0;
-#ifdef CONFIG_SHA256
-BUILD_ASSERT(SHA256_DIGEST_SIZE == CONFIG_ROLLBACK_SECRET_SIZE);
- struct sha256_ctx ctx;
- uint8_t *hash;
-#ifdef CONFIG_ROLLBACK_SECRET_LOCAL_ENTROPY_SIZE
- uint8_t extra;
- int i;
-#endif
-
- SHA256_init(&ctx);
- SHA256_update(&ctx, src, CONFIG_ROLLBACK_SECRET_SIZE);
- SHA256_update(&ctx, add, add_len);
-#ifdef CONFIG_ROLLBACK_SECRET_LOCAL_ENTROPY_SIZE
- /* Add some locally produced entropy */
- for (i = 0; i < CONFIG_ROLLBACK_SECRET_LOCAL_ENTROPY_SIZE; i++) {
- if (!board_get_entropy(&extra, 1))
- goto failed;
- SHA256_update(&ctx, &extra, 1);
- }
-#endif
- hash = SHA256_final(&ctx);
-
- memcpy(dst, hash, CONFIG_ROLLBACK_SECRET_SIZE);
- ret = 1;
-
-#ifdef CONFIG_ROLLBACK_SECRET_LOCAL_ENTROPY_SIZE
-failed:
-#endif
- always_memset(&ctx, 0, sizeof(ctx));
-#else
-#error "Adding entropy to secret in rollback region requires SHA256."
-#endif
- return ret;
-}
-#endif /* CONFIG_ROLLBACK_SECRET_SIZE */
-
-/**
- * Update rollback block.
- *
- * @param next_min_version Minimum version to update in rollback block. Can
- * be a negative value if entropy is provided (in
- * that case the current minimum version is kept).
- * @param entropy Entropy to be added to rollback block secret
- * (can be NULL, in that case no entropy is added).
- * @param len entropy length
- *
- * @return EC_SUCCESS on success, EC_ERROR_* on error.
- */
-static int rollback_update(int32_t next_min_version,
- const uint8_t *entropy, unsigned int length)
-{
- /*
- * When doing flash_write operation, the data needs to be in blocks
- * of CONFIG_FLASH_WRITE_SIZE, pad rollback_data as required.
- */
- uint8_t block[CONFIG_FLASH_WRITE_SIZE *
- DIV_ROUND_UP(sizeof(struct rollback_data),
- CONFIG_FLASH_WRITE_SIZE)];
- struct rollback_data *data = (struct rollback_data *)block;
- BUILD_ASSERT(sizeof(block) >= sizeof(*data));
- int erase_size, offset, region, ret;
-
- if (crec_flash_get_protect() & EC_FLASH_PROTECT_ROLLBACK_NOW) {
- ret = EC_ERROR_ACCESS_DENIED;
- goto out;
- }
-
- /* Initialize the rest of the block. */
- memset(&block[sizeof(*data)], 0xff, sizeof(block)-sizeof(*data));
-
- region = get_latest_rollback(data);
-
- if (region < 0) {
- ret = EC_ERROR_UNKNOWN;
- goto out;
- }
-
-#ifdef CONFIG_ROLLBACK_SECRET_SIZE
- if (entropy) {
- /* Do not accept to decrease the value. */
- if (next_min_version < data->rollback_min_version)
- next_min_version = data->rollback_min_version;
- } else
-#endif
- {
- /* Do not accept to decrease the value. */
- if (next_min_version < data->rollback_min_version) {
- ret = EC_ERROR_INVAL;
- goto out;
- }
-
- /* No need to update if version is already correct. */
- if (next_min_version == data->rollback_min_version) {
- ret = EC_SUCCESS;
- goto out;
- }
- }
-
- /* Use the other region. */
- region = (region + 1) % ROLLBACK_REGIONS;
-
- offset = get_rollback_offset(region);
-
- data->id = data->id + 1;
- data->rollback_min_version = next_min_version;
-#ifdef CONFIG_ROLLBACK_SECRET_SIZE
- /*
- * If we are provided with some entropy, add it to secret. Otherwise,
- * data.secret is left untouched and written back to the other region.
- */
- if (entropy) {
- if (!add_entropy(data->secret, data->secret, entropy, length)) {
- ret = EC_ERROR_UNCHANGED;
- goto out;
- }
- }
-#endif
- data->cookie = CROS_EC_ROLLBACK_COOKIE;
-
- erase_size = get_rollback_erase_size_bytes(region);
-
- if (erase_size < 0) {
- ret = EC_ERROR_UNKNOWN;
- goto out;
- }
-
- /* Offset should never be part of active image. */
- if (system_unsafe_to_overwrite(offset, erase_size)) {
- ret = EC_ERROR_UNKNOWN;
- goto out;
- }
-
- unlock_rollback();
- if (crec_flash_erase(offset, erase_size)) {
- ret = EC_ERROR_UNKNOWN;
- lock_rollback();
- goto out;
- }
-
- ret = crec_flash_write(offset, sizeof(block), block);
- lock_rollback();
-
-out:
- clear_rollback(data);
- return ret;
-}
-
-int rollback_update_version(int32_t next_min_version)
-{
- return rollback_update(next_min_version, NULL, 0);
-}
-
-int rollback_add_entropy(const uint8_t *data, unsigned int len)
-{
- return rollback_update(-1, data, len);
-}
-
-static int command_rollback_update(int argc, char **argv)
-{
- int32_t min_version;
- char *e;
-
- if (argc < 2)
- return EC_ERROR_PARAM_COUNT;
-
- min_version = strtoi(argv[1], &e, 0);
-
- if (*e || min_version < 0)
- return EC_ERROR_PARAM1;
-
- return rollback_update_version(min_version);
-}
-DECLARE_CONSOLE_COMMAND(rollbackupdate, command_rollback_update,
- "min_version",
- "Update rollback info");
-
-#ifdef CONFIG_ROLLBACK_SECRET_SIZE
-static int command_rollback_add_entropy(int argc, char **argv)
-{
- int len;
-
- if (argc < 2)
- return EC_ERROR_PARAM_COUNT;
-
- len = strlen(argv[1]);
-
- return rollback_add_entropy(argv[1], len);
-}
-DECLARE_CONSOLE_COMMAND(rollbackaddent, command_rollback_add_entropy,
- "data",
- "Add entropy to rollback block");
-
-#ifdef CONFIG_RNG
-static int add_entropy_action;
-static int add_entropy_rv = EC_RES_UNAVAILABLE;
-
-static void add_entropy_deferred(void)
-{
- uint8_t rand[CONFIG_ROLLBACK_SECRET_SIZE];
- int repeat = 1;
-
- /*
- * If asked to reset the old secret, just add entropy multiple times,
- * which will ping-pong between the blocks.
- */
- if (add_entropy_action == ADD_ENTROPY_RESET_ASYNC)
- repeat = ROLLBACK_REGIONS;
-
- init_trng();
- do {
- rand_bytes(rand, sizeof(rand));
- if (rollback_add_entropy(rand, sizeof(rand)) != EC_SUCCESS) {
- add_entropy_rv = EC_RES_ERROR;
- goto out;
- }
- } while (--repeat);
-
- add_entropy_rv = EC_RES_SUCCESS;
-out:
- exit_trng();
-}
-DECLARE_DEFERRED(add_entropy_deferred);
-
-static enum ec_status
-hc_rollback_add_entropy(struct host_cmd_handler_args *args)
-{
- const struct ec_params_rollback_add_entropy *p = args->params;
-
- switch (p->action) {
- case ADD_ENTROPY_ASYNC:
- case ADD_ENTROPY_RESET_ASYNC:
- if (add_entropy_rv == EC_RES_BUSY)
- return EC_RES_BUSY;
-
- add_entropy_action = p->action;
- add_entropy_rv = EC_RES_BUSY;
- hook_call_deferred(&add_entropy_deferred_data, 0);
-
- return EC_RES_SUCCESS;
-
- case ADD_ENTROPY_GET_RESULT:
- return add_entropy_rv;
- }
-
- return EC_RES_INVALID_PARAM;
-}
-DECLARE_HOST_COMMAND(EC_CMD_ADD_ENTROPY,
- hc_rollback_add_entropy,
- EC_VER_MASK(0));
-#endif /* CONFIG_RNG */
-#endif /* CONFIG_ROLLBACK_SECRET_SIZE */
-#endif /* CONFIG_ROLLBACK_UPDATE */
-
-static int command_rollback_info(int argc, char **argv)
-{
- int ret = EC_ERROR_UNKNOWN;
- int region, min_region;
- int32_t rw_rollback_version;
- struct rollback_data data;
-
- min_region = get_latest_rollback(&data);
-
- if (min_region < 0)
- goto failed;
-
- rw_rollback_version = system_get_rollback_version(EC_IMAGE_RW);
-
- ccprintf("rollback minimum version: %d\n", data.rollback_min_version);
- ccprintf("RW rollback version: %d\n", rw_rollback_version);
-
- for (region = 0; region < ROLLBACK_REGIONS; region++) {
- ret = read_rollback(region, &data);
- if (ret)
- goto failed;
-
- ccprintf("rollback %d: %08x %08x %08x",
- region, data.id, data.rollback_min_version,
- data.cookie);
-#ifdef CONFIG_ROLLBACK_SECRET_SIZE
- if (!system_is_locked()) {
- /* If system is unlocked, show some of the secret. */
- ccprintf(" [%02x..%02x]", data.secret[0],
- data.secret[CONFIG_ROLLBACK_SECRET_SIZE-1]);
- }
-#endif
- if (min_region == region)
- ccprintf(" *");
- ccprintf("\n");
- }
- ret = EC_SUCCESS;
-
-failed:
- clear_rollback(&data);
- return ret;
-}
-DECLARE_SAFE_CONSOLE_COMMAND(rollbackinfo, command_rollback_info,
- NULL,
- "Print rollback info");
-
-static enum ec_status
-host_command_rollback_info(struct host_cmd_handler_args *args)
-{
- int ret = EC_RES_UNAVAILABLE;
- struct ec_response_rollback_info *r = args->response;
- int min_region;
- struct rollback_data data;
-
- min_region = get_latest_rollback(&data);
-
- if (min_region < 0)
- goto failed;
-
- r->id = data.id;
- r->rollback_min_version = data.rollback_min_version;
- r->rw_rollback_version = system_get_rollback_version(EC_IMAGE_RW);
-
- args->response_size = sizeof(*r);
- ret = EC_RES_SUCCESS;
-
-failed:
- clear_rollback(&data);
- return ret;
-}
-DECLARE_HOST_COMMAND(EC_CMD_ROLLBACK_INFO,
- host_command_rollback_info,
- EC_VER_MASK(0));