diff options
author | Daisuke Nojiri <dnojiri@chromium.org> | 2016-05-04 14:55:57 -0700 |
---|---|---|
committer | chrome-bot <chrome-bot@chromium.org> | 2016-05-07 03:32:47 -0700 |
commit | 85dbb3442011e6d5b69da54c46f11d59c9172784 (patch) | |
tree | a722d6a20b82ee22eec8b1ce295f116d281404a8 | |
parent | 8917b808ce4b4cda9b8e52c7ff8bb04e7836479b (diff) | |
download | vboot-85dbb3442011e6d5b69da54c46f11d59c9172784.tar.gz |
bdb: Add vba_bdb_init
vba_bdb_init initializes the vboot context and decides what to do next
based on the vboot register content. Possible actions are:
1. proceed to verify the current slot
2. reset to try the other slot
3. reset to recovery mode
bdb_sprw_test demonstrates these actions.
BUG=chrome-os-partner:51907
BRANCH=tot
TEST=make runtests
Change-Id: If72cdd575d09b9162a871f088064ca853b7fd74d
Signed-off-by: Daisuke Nojiri <dnojiri@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/342604
Reviewed-by: Randall Spangler <rspangler@chromium.org>
-rw-r--r-- | Makefile | 9 | ||||
-rw-r--r-- | firmware/bdb/bdb.h | 4 | ||||
-rw-r--r-- | firmware/bdb/bdb_api.h | 68 | ||||
-rw-r--r-- | firmware/bdb/misc.c | 124 | ||||
-rw-r--r-- | firmware/bdb/stub.c | 24 | ||||
-rw-r--r-- | tests/bdb_sprw_test.c | 273 |
6 files changed, 500 insertions, 2 deletions
@@ -374,7 +374,9 @@ FWLIB21_SRCS = \ BDBLIB_SRCS = \ firmware/bdb/bdb.c \ - firmware/bdb/rsa.c + firmware/bdb/misc.c \ + firmware/bdb/rsa.c \ + firmware/bdb/stub.c # Support real TPM unless BIOS sets MOCK_TPM ifeq (${MOCK_TPM},) @@ -781,7 +783,8 @@ TEST21_NAMES = \ tests/vb21_host_sig_tests TESTBDB_NAMES = \ - tests/bdb_test + tests/bdb_test \ + tests/bdb_sprw_test TEST_NAMES += ${TEST2X_NAMES} ${TEST20_NAMES} ${TEST21_NAMES} ${TESTBDB_NAMES} @@ -1259,6 +1262,7 @@ ${BUILD}/tests/vb20_common2_tests: LDLIBS += ${CRYPTO_LIBS} ${BUILD}/tests/vb20_common3_tests: LDLIBS += ${CRYPTO_LIBS} ${BUILD}/tests/verify_kernel: LDLIBS += ${CRYPTO_LIBS} ${BUILD}/tests/bdb_test: LDLIBS += ${CRYPTO_LIBS} +${BUILD}/tests/bdb_sprw_test: LDLIBS += ${CRYPTO_LIBS} ${TEST21_BINS}: LDLIBS += ${CRYPTO_LIBS} @@ -1464,6 +1468,7 @@ run2tests: test_setup .PHONY: runbdbtests runbdbtests: test_setup ${RUNTEST} ${BUILD_RUN}/tests/bdb_test ${TEST_KEYS} + ${RUNTEST} ${BUILD_RUN}/tests/bdb_sprw_test ${TEST_KEYS} .PHONY: runfutiltests runfutiltests: test_setup diff --git a/firmware/bdb/bdb.h b/firmware/bdb/bdb.h index 91834913..30ecc17c 100644 --- a/firmware/bdb/bdb.h +++ b/firmware/bdb/bdb.h @@ -68,6 +68,10 @@ enum bdb_return_code { /* Other errors in bdb_verify() */ BDB_ERROR_DIGEST, /* Error calculating digest */ BDB_ERROR_VERIFY_SIG, /* Error verifying signature */ + + /* Errors in vba_bdb_init */ + BDB_ERROR_TRY_OTHER_SLOT, + BDB_ERROR_RECOVERY_REQUEST, }; /*****************************************************************************/ diff --git a/firmware/bdb/bdb_api.h b/firmware/bdb/bdb_api.h new file mode 100644 index 00000000..53823fae --- /dev/null +++ b/firmware/bdb/bdb_api.h @@ -0,0 +1,68 @@ +/* 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. + */ + +#ifndef VBOOT_REFERENCE_FIRMWARE_BDB_BDB_API_H +#define VBOOT_REFERENCE_FIRMWARE_BDB_BDB_API_H + +#include <stdint.h> +#include "vboot_register.h" + +struct vba_context { + /* Indicate which slot is being tried: 0 - primary, 1 - secondary */ + uint8_t slot; +}; + +/** + * Initialize vboot process + * + * @param ctx + * @return enum bdb_return_code + */ +int vba_bdb_init(struct vba_context *ctx); + +/** + * Finalize vboot process + * + * @param ctx + * @return enum bdb_return_code + */ +int vba_bdb_finalize(struct vba_context *ctx); + +/** + * Log failed boot attempt and reset the chip + * + * @param ctx + */ +void vba_bdb_fail(struct vba_context *ctx); + +/** + * Get vboot register value + * + * Implemented by each chip + * + * @param type Type of register to get + * @return Register value + */ +uint32_t vbe_get_vboot_register(enum vboot_register type); + +/** + * Set vboot register value + * + * Implemented by each chip + * + * @param type Type of register to set + * @param val Value to set + */ +void vbe_set_vboot_register(enum vboot_register type, uint32_t val); + +/** + * Reset the SoC + * + * Implemented by each chip. This is different from reboot (a.k.a. board reset, + * cold reset). + */ +void vbe_reset(void); + +#endif diff --git a/firmware/bdb/misc.c b/firmware/bdb/misc.c new file mode 100644 index 00000000..fd3e0c9b --- /dev/null +++ b/firmware/bdb/misc.c @@ -0,0 +1,124 @@ +/* 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 <stdint.h> +#include "bdb.h" +#include "bdb_api.h" +#include "vboot_register.h" + +static int did_current_slot_fail(struct vba_context *ctx) +{ + uint32_t val = vbe_get_vboot_register(VBOOT_REGISTER_PERSIST); + + if (ctx->slot) + return val & VBOOT_REGISTER_FAILED_RW_SECONDARY; + else + return val & VBOOT_REGISTER_FAILED_RW_PRIMARY; +} + +static int did_other_slot_fail(struct vba_context *ctx) +{ + uint32_t val = vbe_get_vboot_register(VBOOT_REGISTER_PERSIST); + + if (ctx->slot) + return val & VBOOT_REGISTER_FAILED_RW_PRIMARY; + else + return val & VBOOT_REGISTER_FAILED_RW_SECONDARY; +} + +static void set_try_other_slot(struct vba_context *ctx) +{ + uint32_t val = vbe_get_vboot_register(VBOOT_REGISTER_PERSIST); + + if (ctx->slot) + val &= ~VBOOT_REGISTER_TRY_SECONDARY_BDB; + else + val |= VBOOT_REGISTER_TRY_SECONDARY_BDB; + + vbe_set_vboot_register(VBOOT_REGISTER_PERSIST, val); +} + +static void set_recovery_request(struct vba_context *ctx) +{ + uint32_t val = vbe_get_vboot_register(VBOOT_REGISTER_PERSIST); + + val |= VBOOT_REGISTER_RECOVERY_REQUEST; + + vbe_set_vboot_register(VBOOT_REGISTER_PERSIST, val); +} + +static void get_current_slot(struct vba_context *ctx) +{ + /* Assume SP-RO selects slot this way */ + ctx->slot = (vbe_get_vboot_register(VBOOT_REGISTER_PERSIST) + & VBOOT_REGISTER_TRY_SECONDARY_BDB) ? 1 : 0; +} + +static void set_current_slot_failed(struct vba_context *ctx) +{ + uint32_t val = vbe_get_vboot_register(VBOOT_REGISTER_PERSIST); + + if (ctx->slot) + val |= VBOOT_REGISTER_FAILED_RW_SECONDARY; + else + val |= VBOOT_REGISTER_FAILED_RW_PRIMARY; + + vbe_set_vboot_register(VBOOT_REGISTER_PERSIST, val); +} + +static void unset_current_slot_failed(struct vba_context *ctx) +{ + uint32_t val = vbe_get_vboot_register(VBOOT_REGISTER_PERSIST); + + if (ctx->slot) + val &= ~VBOOT_REGISTER_FAILED_RW_SECONDARY; + else + val &= ~VBOOT_REGISTER_FAILED_RW_PRIMARY; + + vbe_set_vboot_register(VBOOT_REGISTER_PERSIST, val); +} + +int vba_bdb_init(struct vba_context *ctx) +{ + /* Get current slot */ + get_current_slot(ctx); + + /* Check current slot failed or not at the last boot */ + if (!did_current_slot_fail(ctx)) { + /* If not, we try this slot. Prepare for any accidents */ + set_current_slot_failed(ctx); + return BDB_SUCCESS; + } + + /* Check other slot failed or not at the previous boot */ + if (!did_other_slot_fail(ctx)) { + /* If not, we try the other slot after reboot. */ + set_try_other_slot(ctx); + return BDB_ERROR_TRY_OTHER_SLOT; + } else { + /* Otherwise, both slots are bad. Reboot to recovery */ + set_recovery_request(ctx); + return BDB_ERROR_RECOVERY_REQUEST; + } +} + +int vba_bdb_finalize(struct vba_context *ctx) +{ + /* Mark the current slot good */ + unset_current_slot_failed(ctx); + + /* Disable NVM bus */ + + return BDB_SUCCESS; +} + +void vba_bdb_fail(struct vba_context *ctx) +{ + /* We can do some logging here if we want */ + + /* Unconditionally reboot. FailedRW flag is already set. + * At the next boot, bdb_init will decide what to do. */ + vbe_reset(); +} diff --git a/firmware/bdb/stub.c b/firmware/bdb/stub.c new file mode 100644 index 00000000..87efbc33 --- /dev/null +++ b/firmware/bdb/stub.c @@ -0,0 +1,24 @@ +/* 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 "bdb_api.h" + +__attribute__((weak)) +uint32_t vbe_get_vboot_register(enum vboot_register type) +{ + return 0; +} + +__attribute__((weak)) +void vbe_set_vboot_register(enum vboot_register type, uint32_t val) +{ + return; +} + +__attribute__((weak)) +void vbe_reset(void) +{ + return; +} diff --git a/tests/bdb_sprw_test.c b/tests/bdb_sprw_test.c new file mode 100644 index 00000000..995707da --- /dev/null +++ b/tests/bdb_sprw_test.c @@ -0,0 +1,273 @@ +/* Copyright 2015 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. + * + * Unit tests + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "2sha.h" +#include "bdb.h" +#include "bdb_api.h" +#include "bdb_struct.h" +#include "host.h" +#include "test_common.h" +#include "vboot_register.h" + +#define TEST_EQ_S(result, expect) TEST_EQ(result, expect, #result "==" #expect) + +static struct bdb_header *bdb, *bdb0, *bdb1; +static uint32_t vboot_register; +static uint32_t vboot_register_persist; +static char slot_selected; +static uint8_t aprw_digest[BDB_SHA256_DIGEST_SIZE]; +static uint8_t reset_count; + +static struct bdb_header *create_bdb(const char *key_dir, + struct bdb_hash *hash, int num_hashes) +{ + struct bdb_header *b; + uint8_t oem_area_0[32] = "Some OEM area."; + uint8_t oem_area_1[64] = "Some other OEM area."; + char filename[1024]; + + struct bdb_create_params p = { + .bdb_load_address = 0x11223344, + .oem_area_0 = oem_area_0, + .oem_area_0_size = sizeof(oem_area_0), + .oem_area_1 = oem_area_1, + .oem_area_1_size = sizeof(oem_area_1), + .header_sig_description = "The header sig", + .data_sig_description = "The data sig", + .data_description = "Test BDB data", + .data_version = 3, + .hash = hash, + .num_hashes = num_hashes, + }; + + uint8_t bdbkey_digest[BDB_SHA256_DIGEST_SIZE]; + + /* Load keys */ + sprintf(filename, "%s/bdbkey.keyb", key_dir); + p.bdbkey = bdb_create_key(filename, 100, "BDB key"); + sprintf(filename, "%s/datakey.keyb", key_dir); + p.datakey = bdb_create_key(filename, 200, "datakey"); + sprintf(filename, "%s/bdbkey.pem", key_dir); + p.private_bdbkey = read_pem(filename); + sprintf(filename, "%s/datakey.pem", key_dir); + p.private_datakey = read_pem(filename); + if (!p.bdbkey || !p.datakey || !p.private_bdbkey || !p.private_datakey) { + fprintf(stderr, "Unable to load test keys\n"); + exit(2); + } + + vb2_digest_buffer((uint8_t *)p.bdbkey, p.bdbkey->struct_size, + VB2_HASH_SHA256, + bdbkey_digest, BDB_SHA256_DIGEST_SIZE); + + b = bdb_create(&p); + if (!b) { + fprintf(stderr, "Unable to create test BDB\n"); + exit(2); + } + + /* Free keys and buffers */ + free(p.bdbkey); + free(p.datakey); + RSA_free(p.private_bdbkey); + RSA_free(p.private_datakey); + + return b; +} + +static void calculate_aprw_digest(const struct bdb_hash *hash, uint8_t *digest) +{ + /* Locate AP-RW */ + /* Calculate digest as loading AP-RW */ + memcpy(digest, aprw_digest, sizeof(aprw_digest)); +} + +static void verstage_main(void) +{ + struct vba_context ctx; + const struct bdb_hash *hash; + uint8_t digest[BDB_SHA256_DIGEST_SIZE]; + int rv; + + rv = vba_bdb_init(&ctx); + if (rv) { + fprintf(stderr, "Initializing context failed for (%d)\n", rv); + vba_bdb_fail(&ctx); + /* This return is needed for unit test. vba_bdb_fail calls + * vbe_reset, which calls verstage_main. If verstage_main + * successfully returns, we return here as well. */ + return; + } + fprintf(stderr, "Initialized context. Trying slot %c\n", + ctx.slot ? 'B' : 'A'); + + /* 1. Locate BDB */ + + /* 2. Get bdb_hash structure for AP-RW */ + hash = bdb_get_hash(bdb, BDB_DATA_AP_RW); + fprintf(stderr, "Got hash of AP-RW\n"); + + /* 3. Load & calculate digest of AP-RW */ + calculate_aprw_digest(hash, digest); + fprintf(stderr, "Calculated digest\n"); + + /* 4. Compare digests */ + if (memcmp(hash->digest, digest, BDB_SHA256_DIGEST_SIZE)) { + fprintf(stderr, "Digests do not match\n"); + vba_bdb_fail(&ctx); + /* This return is needed for unit test. vba_bdb_fail calls + * vbe_reset, which calls verstage_main. If verstage_main + * successfully returns, we return here as well. */ + return; + } + + /* 5. Record selected slot. This depends on the firmware */ + slot_selected = ctx.slot ? 'B' : 'A'; + fprintf(stderr, "Selected AP-RW in slot %c\n", slot_selected); + + /* X. This should be done upon AP-RW's request after everything is + * successful. We do it here for the unit test. */ + vba_bdb_finalize(&ctx); +} + +uint32_t vbe_get_vboot_register(enum vboot_register type) +{ + switch (type) { + case VBOOT_REGISTER: + return vboot_register; + case VBOOT_REGISTER_PERSIST: + return vboot_register_persist; + default: + fprintf(stderr, "Invalid vboot register type (%d)\n", type); + exit(2); + } +} + +void vbe_set_vboot_register(enum vboot_register type, uint32_t val) +{ + switch (type) { + case VBOOT_REGISTER: + vboot_register = val; + break; + case VBOOT_REGISTER_PERSIST: + vboot_register_persist = val; + break; + default: + fprintf(stderr, "Invalid vboot register type (%d)\n", type); + exit(2); + } +} + +void vbe_reset(void) +{ + uint32_t val = vbe_get_vboot_register(VBOOT_REGISTER_PERSIST); + + fprintf(stderr, "Booting ...\n"); + + if (++reset_count > 5) { + fprintf(stderr, "Reset counter exceeded maximum value\n"); + exit(2); + } + + /* Emulate warm reset */ + vboot_register = 0; + if (val & VBOOT_REGISTER_RECOVERY_REQUEST) { + fprintf(stderr, "Recovery requested\n"); + return; + } + /* Selected by SP-RO */ + bdb = (val & VBOOT_REGISTER_TRY_SECONDARY_BDB) ? bdb1 : bdb0; + verstage_main(); +} + +static void test_verify_aprw(const char *key_dir) +{ + struct bdb_hash hash0 = { + .offset = 0x28000, + .size = 0x20000, + .partition = 1, + .type = BDB_DATA_AP_RW, + .load_address = 0x200000, + .digest = {0x11, 0x11, 0x11, 0x11}, + }; + struct bdb_hash hash1 = { + .offset = 0x28000, + .size = 0x20000, + .partition = 1, + .type = BDB_DATA_AP_RW, + .load_address = 0x200000, + .digest = {0x22, 0x22, 0x22, 0x22}, + }; + + bdb0 = create_bdb(key_dir, &hash0, 1); + bdb1 = create_bdb(key_dir, &hash1, 1); + memset(aprw_digest, 0, BDB_SHA256_DIGEST_SIZE); + + /* (slotA, slotB) = (good, bad) */ + reset_count = 0; + vboot_register_persist = 0; + slot_selected = 'X'; + memcpy(aprw_digest, hash0.digest, 4); + vbe_reset(); + TEST_EQ_S(reset_count, 1); + TEST_EQ_S(slot_selected, 'A'); + TEST_FALSE(vboot_register_persist & VBOOT_REGISTER_FAILED_RW_PRIMARY, + "VBOOT_REGISTER_FAILED_RW_PRIMARY==false"); + TEST_FALSE(vboot_register_persist & VBOOT_REGISTER_FAILED_RW_SECONDARY, + "VBOOT_REGISTER_FAILED_RW_SECONDARY==false"); + + /* (slotA, slotB) = (bad, good) */ + reset_count = 0; + vboot_register_persist = 0; + slot_selected = 'X'; + memcpy(aprw_digest, hash1.digest, 4); + vbe_reset(); + TEST_EQ_S(reset_count, 3); + TEST_EQ_S(slot_selected, 'B'); + TEST_TRUE(vboot_register_persist & VBOOT_REGISTER_FAILED_RW_PRIMARY, + "VBOOT_REGISTER_FAILED_RW_PRIMARY==true"); + TEST_FALSE(vboot_register_persist & VBOOT_REGISTER_FAILED_RW_SECONDARY, + "VBOOT_REGISTER_FAILED_RW_SECONDARY==false"); + + /* (slotA, slotB) = (bad, bad) */ + reset_count = 0; + vboot_register_persist = 0; + slot_selected = 'X'; + memset(aprw_digest, 0, BDB_SHA256_DIGEST_SIZE); + vbe_reset(); + TEST_EQ_S(reset_count, 5); + TEST_EQ_S(slot_selected, 'X'); + TEST_TRUE(vboot_register_persist & VBOOT_REGISTER_FAILED_RW_PRIMARY, + "VBOOT_REGISTER_FAILED_RW_PRIMARY==true"); + TEST_TRUE(vboot_register_persist & VBOOT_REGISTER_FAILED_RW_SECONDARY, + "VBOOT_REGISTER_FAILED_RW_SECONDARY==true"); + TEST_TRUE(vboot_register_persist & VBOOT_REGISTER_RECOVERY_REQUEST, + "Recovery request"); + + /* Clean up */ + free(bdb0); + free(bdb1); +} + +/*****************************************************************************/ + +int main(int argc, char *argv[]) +{ + if (argc != 2) { + fprintf(stderr, "Usage: %s <keys_dir>", argv[0]); + return -1; + } + printf("Running BDB SP-RW tests...\n"); + + test_verify_aprw(argv[1]); + + return gTestSuccess ? 0 : 255; +} |