diff options
-rw-r--r-- | Makefile | 4 | ||||
-rw-r--r-- | firmware/2lib/2kernel.c | 193 | ||||
-rw-r--r-- | firmware/2lib/include/2kernel.h | 21 | ||||
-rw-r--r-- | firmware/lib/include/vboot_kernel.h | 5 | ||||
-rw-r--r-- | firmware/lib/vboot_api_kernel.c | 84 | ||||
-rw-r--r-- | firmware/lib20/api_kernel.c | 97 | ||||
-rw-r--r-- | tests/vb20_api_kernel_tests.c | 115 | ||||
-rw-r--r-- | tests/vb2_kernel_tests.c | 270 | ||||
-rw-r--r-- | tests/vboot_legacy_clamshell_tests.c | 17 | ||||
-rw-r--r-- | tests/vboot_legacy_menu_tests.c | 3 |
10 files changed, 501 insertions, 308 deletions
@@ -357,6 +357,7 @@ FWLIB_SRCS = \ firmware/2lib/2ec_sync.c \ firmware/2lib/2gbb.c \ firmware/2lib/2hmac.c \ + firmware/2lib/2kernel.c \ firmware/2lib/2misc.c \ firmware/2lib/2nvstorage.c \ firmware/2lib/2rsa.c \ @@ -479,6 +480,7 @@ HOSTLIB_SRCS = \ firmware/2lib/2context.c \ firmware/2lib/2crc8.c \ firmware/2lib/2hmac.c \ + firmware/2lib/2kernel.c \ firmware/2lib/2nvstorage.c \ firmware/2lib/2rsa.c \ firmware/2lib/2sha1.c \ @@ -695,6 +697,7 @@ TEST2X_NAMES = \ tests/vb2_ec_sync_tests \ tests/vb2_gbb_tests \ tests/vb2_host_key_tests \ + tests/vb2_kernel_tests \ tests/vb2_misc_tests \ tests/vb2_nvstorage_tests \ tests/vb2_rsa_utility_tests \ @@ -1204,6 +1207,7 @@ run2tests: install_for_test ${RUNTEST} ${BUILD_RUN}/tests/vb2_ec_sync_tests ${RUNTEST} ${BUILD_RUN}/tests/vb2_gbb_tests ${RUNTEST} ${BUILD_RUN}/tests/vb2_host_key_tests + ${RUNTEST} ${BUILD_RUN}/tests/vb2_kernel_tests ${RUNTEST} ${BUILD_RUN}/tests/vb2_misc_tests ${RUNTEST} ${BUILD_RUN}/tests/vb2_nvstorage_tests ${RUNTEST} ${BUILD_RUN}/tests/vb2_rsa_utility_tests diff --git a/firmware/2lib/2kernel.c b/firmware/2lib/2kernel.c new file mode 100644 index 00000000..f49b6dea --- /dev/null +++ b/firmware/2lib/2kernel.c @@ -0,0 +1,193 @@ +/* Copyright 2020 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. + * + * Kernel selection, loading, verification, and booting. + */ + +#include "2common.h" +#include "2kernel.h" +#include "2misc.h" +#include "2nvstorage.h" +#include "2rsa.h" +#include "2secdata.h" +#include "vb2_common.h" +#include "vboot_kernel.h" + +/** + * Reset any NVRAM requests. + * + * @param ctx Vboot context + * @return 1 if a reboot is required, 0 otherwise. + */ +static int vb2_reset_nv_requests(struct vb2_context *ctx) +{ + int need_reboot = 0; + + if (vb2_nv_get(ctx, VB2_NV_DISPLAY_REQUEST)) { + VB2_DEBUG("Unset display request (undo display init)\n"); + vb2_nv_set(ctx, VB2_NV_DISPLAY_REQUEST, 0); + need_reboot = 1; + } + + if (vb2_nv_get(ctx, VB2_NV_DIAG_REQUEST)) { + VB2_DEBUG("Unset diagnostic request (undo display init)\n"); + vb2_nv_set(ctx, VB2_NV_DIAG_REQUEST, 0); + need_reboot = 1; + } + + return need_reboot; +} + +vb2_error_t vb2_normal_boot(struct vb2_context *ctx) +{ + struct vb2_shared_data *sd = vb2_get_sd(ctx); + uint32_t max_rollforward = vb2_nv_get(ctx, + VB2_NV_KERNEL_MAX_ROLLFORWARD); + + /* Boot from fixed disk only */ + VB2_DEBUG("Entering\n"); + + if (vb2_reset_nv_requests(ctx)) { + VB2_DEBUG("Normal mode: reboot to reset NVRAM requests\n"); + return VBERROR_REBOOT_REQUIRED; + } + + vb2_error_t rv = VbTryLoadKernel(ctx, VB_DISK_FLAG_FIXED); + + VB2_DEBUG("Checking if TPM kernel version needs advancing\n"); + + /* + * Special case for when we're trying a slot with new firmware. + * Firmware updates also usually change the kernel key, which means + * that the new firmware can only boot a new kernel, and the old + * firmware in the previous slot can only boot the previous kernel. + * + * Don't roll-forward the kernel version, because we don't yet know if + * the new kernel will successfully boot. + */ + if (vb2_nv_get(ctx, VB2_NV_FW_RESULT) == VB2_FW_RESULT_TRYING) { + VB2_DEBUG("Trying new FW; skip kernel version roll-forward.\n"); + return rv; + } + + /* + * Limit kernel version rollforward if needed. Can't limit kernel + * version to less than the version currently in the TPM. That is, + * we're limiting rollforward, not allowing rollback. + */ + if (max_rollforward < sd->kernel_version_secdata) + max_rollforward = sd->kernel_version_secdata; + + if (sd->kernel_version > max_rollforward) { + VB2_DEBUG("Limiting TPM kernel version roll-forward " + "to %#x < %#x\n", + max_rollforward, sd->kernel_version); + + sd->kernel_version = max_rollforward; + } + + if (sd->kernel_version > sd->kernel_version_secdata) { + vb2_secdata_kernel_set(ctx, VB2_SECDATA_KERNEL_VERSIONS, + sd->kernel_version); + } + + return rv; +} + +int vb2api_is_developer_signed(struct vb2_context *ctx) +{ + struct vb2_shared_data *sd = vb2_get_sd(ctx); + + if (!sd->kernel_key_offset || !sd->kernel_key_size) { + VB2_REC_OR_DIE(ctx, "Cannot call this before kernel_phase1!\n"); + return 0; + } + + struct vb2_public_key key; + if (vb2_unpack_key(&key, vb2_member_of(sd, sd->kernel_key_offset))) + return 0; + + /* This is a debugging aid, not a security-relevant feature. There's no + reason to hardcode the whole key or waste time computing a hash. Just + spot check the starting bytes of the pseudorandom part of the key. */ + uint32_t devkey_n0inv = ctx->flags & VB2_CONTEXT_RECOVERY_MODE ? + 0x18cebcf5 : /* recovery_key.vbpubk @0x24 */ + 0xe0cd87d9; /* kernel_subkey.vbpubk @0x24 */ + + if (key.n0inv == devkey_n0inv) + return 1; + + return 0; +} + +vb2_error_t vb2api_kernel_phase1(struct vb2_context *ctx) +{ + struct vb2_shared_data *sd = vb2_get_sd(ctx); + struct vb2_workbuf wb; + struct vb2_packed_key *packed_key; + vb2_error_t rv; + + vb2_workbuf_from_ctx(ctx, &wb); + + /* + * Init secdata_kernel and secdata_fwmp spaces. No need to init + * secdata_firmware, since it was already read during firmware + * verification. Ignore errors in recovery mode. + */ + rv = vb2_secdata_kernel_init(ctx); + if (rv && !(ctx->flags & VB2_CONTEXT_RECOVERY_MODE)) { + VB2_DEBUG("TPM: init secdata_kernel returned %#x\n", rv); + vb2api_fail(ctx, VB2_RECOVERY_SECDATA_KERNEL_INIT, rv); + return rv; + } + rv = vb2_secdata_fwmp_init(ctx); + if (rv && !(ctx->flags & VB2_CONTEXT_RECOVERY_MODE)) { + VB2_DEBUG("TPM: init secdata_fwmp returned %#x\n", rv); + vb2api_fail(ctx, VB2_RECOVERY_SECDATA_FWMP_INIT, rv); + return rv; + } + + /* Read kernel version from secdata. */ + sd->kernel_version_secdata = + vb2_secdata_kernel_get(ctx, VB2_SECDATA_KERNEL_VERSIONS); + sd->kernel_version = sd->kernel_version_secdata; + + /* Find the key to use to verify the kernel keyblock */ + if ((ctx->flags & VB2_CONTEXT_RECOVERY_MODE)) { + /* Load recovery key from GBB. */ + rv = vb2_gbb_read_recovery_key(ctx, &packed_key, NULL, &wb); + if (rv) { + if (vb2_allow_recovery(ctx)) + VB2_DIE("GBB read recovery key failed.\n"); + else + /* + * If we're headed for the BROKEN screen, + * we won't need the recovery key. Just + * short-circuit with success. + */ + return VB2_SUCCESS; + } + } else { + /* Kernel subkey from firmware preamble */ + struct vb2_fw_preamble *pre; + + /* Make sure we have a firmware preamble loaded */ + if (!sd->preamble_size) + return VB2_ERROR_API_KPHASE1_PREAMBLE; + + pre = (struct vb2_fw_preamble *) + vb2_member_of(sd, sd->preamble_offset); + packed_key = &pre->kernel_subkey; + } + + sd->kernel_key_offset = vb2_offset_of(sd, packed_key); + sd->kernel_key_size = packed_key->key_offset + packed_key->key_size; + + vb2_set_workbuf_used(ctx, vb2_offset_of(sd, wb.buf)); + + if (vb2api_is_developer_signed(ctx)) + VB2_DEBUG("This is developer-signed firmware.\n"); + + return VB2_SUCCESS; +} diff --git a/firmware/2lib/include/2kernel.h b/firmware/2lib/include/2kernel.h new file mode 100644 index 00000000..bed1a01f --- /dev/null +++ b/firmware/2lib/include/2kernel.h @@ -0,0 +1,21 @@ +/* Copyright 2020 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. + * + * Kernel selection, loading, verification, and booting. + */ + +#ifndef VBOOT_REFERENCE_2KERNEL_H_ +#define VBOOT_REFERENCE_2KERNEL_H_ + +#include "2common.h" + +/** + * Handle a normal boot. + * + * @param ctx Vboot context. + * @return VB2_SUCCESS, or error code on error. + */ +vb2_error_t vb2_normal_boot(struct vb2_context *ctx); + +#endif /* VBOOT_REFERENCE_2KERNEL_H_ */ diff --git a/firmware/lib/include/vboot_kernel.h b/firmware/lib/include/vboot_kernel.h index eb15e5e2..81e523d9 100644 --- a/firmware/lib/include/vboot_kernel.h +++ b/firmware/lib/include/vboot_kernel.h @@ -127,11 +127,6 @@ vb2_error_t VbTryLoadKernel(struct vb2_context *ctx, uint32_t get_info_flags); int VbUserConfirms(struct vb2_context *ctx, uint32_t confirm_flags); /** - * Handle a normal boot. - */ -vb2_error_t VbBootNormal(struct vb2_context *ctx); - -/** * Handle a developer-mode boot using legacy clamshell UI. */ vb2_error_t VbBootDeveloperLegacyClamshell(struct vb2_context *ctx); diff --git a/firmware/lib/vboot_api_kernel.c b/firmware/lib/vboot_api_kernel.c index 42dded12..82b2b033 100644 --- a/firmware/lib/vboot_api_kernel.c +++ b/firmware/lib/vboot_api_kernel.c @@ -7,6 +7,7 @@ #include "2api.h" #include "2common.h" +#include "2kernel.h" #include "2misc.h" #include "2nvstorage.h" #include "2rsa.h" @@ -141,87 +142,6 @@ vb2_error_t VbTryLoadKernel(struct vb2_context *ctx, uint32_t get_info_flags) return rv; } -/** - * Reset any NVRAM requests. - * - * @param ctx Vboot context - * @return 1 if a reboot is required, 0 otherwise. - */ -static int vb2_reset_nv_requests(struct vb2_context *ctx) -{ - int need_reboot = 0; - - if (vb2_nv_get(ctx, VB2_NV_DISPLAY_REQUEST)) { - VB2_DEBUG("Unset display request (undo display init)\n"); - vb2_nv_set(ctx, VB2_NV_DISPLAY_REQUEST, 0); - need_reboot = 1; - } - - if (vb2_nv_get(ctx, VB2_NV_DIAG_REQUEST)) { - VB2_DEBUG("Unset diagnostic request (undo display init)\n"); - vb2_nv_set(ctx, VB2_NV_DIAG_REQUEST, 0); - need_reboot = 1; - } - - return need_reboot; -} - -vb2_error_t VbBootNormal(struct vb2_context *ctx) -{ - struct vb2_shared_data *sd = vb2_get_sd(ctx); - uint32_t max_rollforward = vb2_nv_get(ctx, - VB2_NV_KERNEL_MAX_ROLLFORWARD); - - /* Boot from fixed disk only */ - VB2_DEBUG("Entering\n"); - - if (vb2_reset_nv_requests(ctx)) { - VB2_DEBUG("Normal mode: reboot to reset NVRAM requests\n"); - return VBERROR_REBOOT_REQUIRED; - } - - vb2_error_t rv = VbTryLoadKernel(ctx, VB_DISK_FLAG_FIXED); - - VB2_DEBUG("Checking if TPM kernel version needs advancing\n"); - - /* - * Special case for when we're trying a slot with new firmware. - * Firmware updates also usually change the kernel key, which means - * that the new firmware can only boot a new kernel, and the old - * firmware in the previous slot can only boot the previous kernel. - * - * Don't roll-forward the kernel version, because we don't yet know if - * the new kernel will successfully boot. - */ - if (vb2_nv_get(ctx, VB2_NV_FW_RESULT) == VB2_FW_RESULT_TRYING) { - VB2_DEBUG("Trying new FW; skip kernel version roll-forward.\n"); - return rv; - } - - /* - * Limit kernel version rollforward if needed. Can't limit kernel - * version to less than the version currently in the TPM. That is, - * we're limiting rollforward, not allowing rollback. - */ - if (max_rollforward < sd->kernel_version_secdata) - max_rollforward = sd->kernel_version_secdata; - - if (sd->kernel_version > max_rollforward) { - VB2_DEBUG("Limiting TPM kernel version roll-forward " - "to %#x < %#x\n", - max_rollforward, sd->kernel_version); - - sd->kernel_version = max_rollforward; - } - - if (sd->kernel_version > sd->kernel_version_secdata) { - vb2_secdata_kernel_set(ctx, VB2_SECDATA_KERNEL_VERSIONS, - sd->kernel_version); - } - - return rv; -} - static vb2_error_t vb2_kernel_init_kparams(struct vb2_context *ctx, VbSelectAndLoadKernelParams *kparams) { @@ -350,7 +270,7 @@ vb2_error_t VbSelectAndLoadKernel(struct vb2_context *ctx, rv = VbBootDeveloperLegacyClamshell(ctx); } else { /* Normal boot */ - rv = VbBootNormal(ctx); + rv = vb2_normal_boot(ctx); } if (VB2_SUCCESS == rv && (ctx->flags & VB2_CONTEXT_NO_BOOT)) { diff --git a/firmware/lib20/api_kernel.c b/firmware/lib20/api_kernel.c index 8b146093..36456f61 100644 --- a/firmware/lib20/api_kernel.c +++ b/firmware/lib20/api_kernel.c @@ -16,103 +16,6 @@ #include "vb2_common.h" #include "vboot_struct.h" -int vb2api_is_developer_signed(struct vb2_context *ctx) -{ - struct vb2_shared_data *sd = vb2_get_sd(ctx); - - if (!sd->kernel_key_offset || !sd->kernel_key_size) { - VB2_REC_OR_DIE(ctx, "Cannot call this before kernel_phase1!\n"); - return 0; - } - - struct vb2_public_key key; - if (vb2_unpack_key(&key, vb2_member_of(sd, sd->kernel_key_offset))) - return 0; - - /* This is a debugging aid, not a security-relevant feature. There's no - reason to hardcode the whole key or waste time computing a hash. Just - spot check the starting bytes of the pseudorandom part of the key. */ - uint32_t devkey_n0inv = ctx->flags & VB2_CONTEXT_RECOVERY_MODE ? - 0x18cebcf5 : /* recovery_key.vbpubk @0x24 */ - 0xe0cd87d9; /* kernel_subkey.vbpubk @0x24 */ - - if (key.n0inv == devkey_n0inv) - return 1; - - return 0; -} - -vb2_error_t vb2api_kernel_phase1(struct vb2_context *ctx) -{ - struct vb2_shared_data *sd = vb2_get_sd(ctx); - struct vb2_workbuf wb; - struct vb2_packed_key *packed_key; - vb2_error_t rv; - - vb2_workbuf_from_ctx(ctx, &wb); - - /* - * Init secdata_kernel and secdata_fwmp spaces. No need to init - * secdata_firmware, since it was already read during firmware - * verification. Ignore errors in recovery mode. - */ - rv = vb2_secdata_kernel_init(ctx); - if (rv && !(ctx->flags & VB2_CONTEXT_RECOVERY_MODE)) { - VB2_DEBUG("TPM: init secdata_kernel returned %#x\n", rv); - vb2api_fail(ctx, VB2_RECOVERY_SECDATA_KERNEL_INIT, rv); - return rv; - } - rv = vb2_secdata_fwmp_init(ctx); - if (rv && !(ctx->flags & VB2_CONTEXT_RECOVERY_MODE)) { - VB2_DEBUG("TPM: init secdata_fwmp returned %#x\n", rv); - vb2api_fail(ctx, VB2_RECOVERY_SECDATA_FWMP_INIT, rv); - return rv; - } - - /* Read kernel version from secdata. */ - sd->kernel_version_secdata = - vb2_secdata_kernel_get(ctx, VB2_SECDATA_KERNEL_VERSIONS); - sd->kernel_version = sd->kernel_version_secdata; - - /* Find the key to use to verify the kernel keyblock */ - if ((ctx->flags & VB2_CONTEXT_RECOVERY_MODE)) { - /* Load recovery key from GBB. */ - rv = vb2_gbb_read_recovery_key(ctx, &packed_key, NULL, &wb); - if (rv) { - if (vb2_allow_recovery(ctx)) - VB2_DIE("GBB read recovery key failed.\n"); - else - /* - * If we're headed for the BROKEN screen, - * we won't need the recovery key. Just - * short-circuit with success. - */ - return VB2_SUCCESS; - } - } else { - /* Kernel subkey from firmware preamble */ - struct vb2_fw_preamble *pre; - - /* Make sure we have a firmware preamble loaded */ - if (!sd->preamble_size) - return VB2_ERROR_API_KPHASE1_PREAMBLE; - - pre = (struct vb2_fw_preamble *) - vb2_member_of(sd, sd->preamble_offset); - packed_key = &pre->kernel_subkey; - } - - sd->kernel_key_offset = vb2_offset_of(sd, packed_key); - sd->kernel_key_size = packed_key->key_offset + packed_key->key_size; - - vb2_set_workbuf_used(ctx, vb2_offset_of(sd, wb.buf)); - - if (vb2api_is_developer_signed(ctx)) - VB2_DEBUG("This is developer-signed firmware.\n"); - - return VB2_SUCCESS; -} - vb2_error_t vb2api_load_kernel_vblock(struct vb2_context *ctx) { vb2_error_t rv; diff --git a/tests/vb20_api_kernel_tests.c b/tests/vb20_api_kernel_tests.c index dad612f0..aa7686b0 100644 --- a/tests/vb20_api_kernel_tests.c +++ b/tests/vb20_api_kernel_tests.c @@ -223,120 +223,6 @@ vb2_error_t vb2_verify_digest(const struct vb2_public_key *key, /* Tests */ -static void phase1_tests(void) -{ - struct vb2_packed_key *k; - uint32_t wb_used_before; - - /* Test successful call */ - reset_common_data(FOR_PHASE1); - TEST_SUCC(vb2api_kernel_phase1(ctx), "phase1 good"); - /* Make sure normal key was loaded */ - TEST_EQ(sd->kernel_key_offset, sd->preamble_offset + - offsetof(struct vb2_fw_preamble, kernel_subkey), - " workbuf key offset"); - k = vb2_member_of(sd, sd->kernel_key_offset); - TEST_EQ(sd->kernel_key_size, k->key_offset + k->key_size, - " workbuf key size"); - TEST_EQ(sd->workbuf_used, - vb2_wb_round_up(sd->kernel_key_offset + - sd->kernel_key_size), - " workbuf used"); - TEST_EQ(k->algorithm, 7, " key algorithm"); - TEST_EQ(k->key_size, sizeof(fw_kernel_key_data), " key_size"); - TEST_EQ(memcmp((uint8_t *)k + k->key_offset, fw_kernel_key_data, - k->key_size), 0, " key data"); - TEST_EQ(sd->kernel_version_secdata, 0x20002, - " secdata_kernel version"); - - /* Test successful call in recovery mode */ - reset_common_data(FOR_PHASE1); - ctx->flags |= VB2_CONTEXT_RECOVERY_MODE; - /* No preamble needed in recovery mode */ - sd->workbuf_used = sd->preamble_offset; - sd->preamble_offset = sd->preamble_size = 0; - wb_used_before = sd->workbuf_used; - TEST_SUCC(vb2api_kernel_phase1(ctx), "phase1 rec good"); - /* Make sure recovery key was loaded */ - TEST_EQ(sd->kernel_key_offset, wb_used_before, - " workbuf key offset"); - k = vb2_member_of(sd, sd->kernel_key_offset); - TEST_EQ(sd->kernel_key_size, k->key_offset + k->key_size, - " workbuf key size"); - TEST_EQ(sd->workbuf_used, - vb2_wb_round_up(sd->kernel_key_offset + - sd->kernel_key_size), - " workbuf used"); - TEST_EQ(k->algorithm, 11, " key algorithm"); - TEST_EQ(k->key_size, sizeof(mock_gbb.recovery_key_data), " key_size"); - TEST_EQ(memcmp((uint8_t *)k + k->key_offset, - mock_gbb.recovery_key_data, k->key_size), 0, - " key data"); - TEST_EQ(sd->kernel_version_secdata, 0x20002, - " secdata_kernel version"); - - /* Bad secdata_kernel causes failure in normal mode only */ - reset_common_data(FOR_PHASE1); - ctx->secdata_kernel[2] ^= 0x33; /* 3rd byte is CRC */ - TEST_EQ(vb2api_kernel_phase1(ctx), VB2_ERROR_SECDATA_KERNEL_CRC, - "phase1 bad secdata_kernel"); - TEST_EQ(vb2_nv_get(ctx, VB2_NV_RECOVERY_REQUEST), - VB2_RECOVERY_SECDATA_KERNEL_INIT, " recovery reason"); - - reset_common_data(FOR_PHASE1); - ctx->secdata_kernel[0] ^= 0x33; - ctx->flags |= VB2_CONTEXT_RECOVERY_MODE; - TEST_SUCC(vb2api_kernel_phase1(ctx), "phase1 bad secdata_kernel rec"); - TEST_EQ(sd->kernel_version_secdata, 0, " secdata_kernel version"); - TEST_EQ(vb2_nv_get(ctx, VB2_NV_RECOVERY_REQUEST), - VB2_RECOVERY_NOT_REQUESTED, " no recovery"); - - /* Bad secdata_fwmp causes failure in normal mode only */ - reset_common_data(FOR_PHASE1); - mock_secdata_fwmp_check_retval = VB2_ERROR_SECDATA_FWMP_CRC; - TEST_EQ(vb2api_kernel_phase1(ctx), mock_secdata_fwmp_check_retval, - "phase1 bad secdata_fwmp"); - TEST_EQ(vb2_nv_get(ctx, VB2_NV_RECOVERY_REQUEST), - VB2_RECOVERY_SECDATA_FWMP_INIT, " recovery reason"); - - reset_common_data(FOR_PHASE1); - mock_secdata_fwmp_check_retval = VB2_ERROR_SECDATA_FWMP_CRC; - ctx->flags |= VB2_CONTEXT_RECOVERY_MODE; - TEST_SUCC(vb2api_kernel_phase1(ctx), "phase1 bad secdata_fwmp rec"); - TEST_EQ(vb2_nv_get(ctx, VB2_NV_RECOVERY_REQUEST), - VB2_RECOVERY_NOT_REQUESTED, " no recovery"); - - /* Failures while reading recovery key */ - reset_common_data(FOR_PHASE1); - ctx->flags |= VB2_CONTEXT_RECOVERY_MODE; - mock_gbb.h.recovery_key_size = sd->workbuf_size - 1; - mock_gbb.recovery_key.key_size = - mock_gbb.h.recovery_key_size - sizeof(mock_gbb.recovery_key); - TEST_EQ(vb2api_kernel_phase1(ctx), VB2_SUCCESS, - "phase1 rec workbuf key"); - TEST_EQ(sd->kernel_key_offset, 0, " workbuf key offset"); - TEST_EQ(sd->kernel_key_size, 0, " workbuf key size"); - mock_gbb.h.flags |= VB2_GBB_FLAG_FORCE_MANUAL_RECOVERY; - TEST_ABORT(vb2api_kernel_phase1(ctx), " fatal for manual recovery"); - - reset_common_data(FOR_PHASE1); - ctx->flags |= VB2_CONTEXT_RECOVERY_MODE; - mock_read_res_fail_on_call = 1; - TEST_EQ(vb2api_kernel_phase1(ctx), VB2_SUCCESS, - "phase1 rec gbb read key"); - TEST_EQ(sd->kernel_key_offset, 0, " workbuf key offset"); - TEST_EQ(sd->kernel_key_size, 0, " workbuf key size"); - mock_gbb.h.flags |= VB2_GBB_FLAG_FORCE_MANUAL_RECOVERY; - mock_read_res_fail_on_call = 1; - TEST_ABORT(vb2api_kernel_phase1(ctx), " fatal for manual recovery"); - - /* Failures while parsing subkey from firmware preamble */ - reset_common_data(FOR_PHASE1); - sd->preamble_size = 0; - TEST_EQ(vb2api_kernel_phase1(ctx), VB2_ERROR_API_KPHASE1_PREAMBLE, - "phase1 fw preamble"); -} - static void load_kernel_vblock_tests(void) { reset_common_data(FOR_PHASE1); @@ -472,7 +358,6 @@ static void phase3_tests(void) int main(int argc, char* argv[]) { - phase1_tests(); load_kernel_vblock_tests(); get_kernel_size_tests(); verify_kernel_data_tests(); diff --git a/tests/vb2_kernel_tests.c b/tests/vb2_kernel_tests.c new file mode 100644 index 00000000..bd64fb08 --- /dev/null +++ b/tests/vb2_kernel_tests.c @@ -0,0 +1,270 @@ +/* Copyright 2020 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. + * + * Tests for kernel selection, loading, verification, and booting. + */ + +#include "2api.h" +#include "2common.h" +#include "2misc.h" +#include "2nvstorage.h" +#include "2rsa.h" +#include "2secdata.h" +#include "2sysincludes.h" +#include "test_common.h" +#include "vb2_common.h" +#include "vboot_struct.h" + +/* Common context for tests */ +static uint8_t workbuf[VB2_KERNEL_WORKBUF_RECOMMENDED_SIZE] + __attribute__((aligned(VB2_WORKBUF_ALIGN))); +static struct vb2_context *ctx; +static struct vb2_shared_data *sd; +static struct vb2_fw_preamble *fwpre; +static const char fw_kernel_key_data[36] = "Test kernel key data"; + +/* Mocked function data */ + +static struct { + struct vb2_gbb_header h; + struct vb2_packed_key recovery_key; + char recovery_key_data[32]; +} mock_gbb; + +static int mock_read_res_fail_on_call; +static int mock_secdata_fwmp_check_retval; + +/* Type of test to reset for */ +enum reset_type { + FOR_PHASE1, +}; + +static void reset_common_data(enum reset_type t) +{ + struct vb2_packed_key *k; + + memset(workbuf, 0xaa, sizeof(workbuf)); + + TEST_SUCC(vb2api_init(workbuf, sizeof(workbuf), &ctx), + "vb2api_init failed"); + + sd = vb2_get_sd(ctx); + vb2_nv_init(ctx); + + vb2api_secdata_kernel_create(ctx); + vb2_secdata_kernel_init(ctx); + vb2_secdata_kernel_set(ctx, VB2_SECDATA_KERNEL_VERSIONS, 0x20002); + + mock_read_res_fail_on_call = 0; + mock_secdata_fwmp_check_retval = VB2_SUCCESS; + + /* Recovery key in mock GBB */ + memset(&mock_gbb, 0, sizeof(mock_gbb)); + mock_gbb.recovery_key.algorithm = 11; + mock_gbb.recovery_key.key_offset = + vb2_offset_of(&mock_gbb.recovery_key, + &mock_gbb.recovery_key_data); + mock_gbb.recovery_key.key_size = sizeof(mock_gbb.recovery_key_data); + strcpy(mock_gbb.recovery_key_data, "The recovery key"); + mock_gbb.h.recovery_key_offset = + vb2_offset_of(&mock_gbb, &mock_gbb.recovery_key); + mock_gbb.h.recovery_key_size = + mock_gbb.recovery_key.key_offset + + mock_gbb.recovery_key.key_size; + + + if (t == FOR_PHASE1) { + uint8_t *kdata; + + /* Create mock firmware preamble in the context */ + sd->preamble_offset = sd->workbuf_used; + fwpre = (struct vb2_fw_preamble *) + vb2_member_of(sd, sd->preamble_offset); + k = &fwpre->kernel_subkey; + kdata = (uint8_t *)fwpre + sizeof(*fwpre); + memcpy(kdata, fw_kernel_key_data, sizeof(fw_kernel_key_data)); + k->algorithm = 7; + k->key_offset = vb2_offset_of(k, kdata); + k->key_size = sizeof(fw_kernel_key_data); + sd->preamble_size = sizeof(*fwpre) + k->key_size; + vb2_set_workbuf_used(ctx, + sd->preamble_offset + sd->preamble_size); + + /* Needed to check that secdata_kernel initialization is + performed by phase1 function. */ + sd->status &= ~VB2_SD_STATUS_SECDATA_KERNEL_INIT; + + } +}; + +/* Mocked functions */ + +vb2_error_t vb2api_secdata_fwmp_check(struct vb2_context *c, uint8_t *size) +{ + return mock_secdata_fwmp_check_retval; +} + +struct vb2_gbb_header *vb2_get_gbb(struct vb2_context *c) +{ + return &mock_gbb.h; +} + +vb2_error_t vb2ex_read_resource(struct vb2_context *c, + enum vb2_resource_index index, uint32_t offset, + void *buf, uint32_t size) +{ + uint8_t *rptr; + uint32_t rsize; + + if (--mock_read_res_fail_on_call == 0) + return VB2_ERROR_MOCK; + + switch(index) { + case VB2_RES_GBB: + rptr = (uint8_t *)&mock_gbb; + rsize = sizeof(mock_gbb); + break; + default: + return VB2_ERROR_EX_READ_RESOURCE_INDEX; + } + + if (offset > rsize || offset + size > rsize) + return VB2_ERROR_EX_READ_RESOURCE_SIZE; + + memcpy(buf, rptr + offset, size); + return VB2_SUCCESS; +} + +/* Tests */ + +static void phase1_tests(void) +{ + struct vb2_packed_key *k; + uint32_t wb_used_before; + + /* Test successful call */ + reset_common_data(FOR_PHASE1); + TEST_SUCC(vb2api_kernel_phase1(ctx), "phase1 good"); + /* Make sure normal key was loaded */ + TEST_EQ(sd->kernel_key_offset, sd->preamble_offset + + offsetof(struct vb2_fw_preamble, kernel_subkey), + " workbuf key offset"); + k = vb2_member_of(sd, sd->kernel_key_offset); + TEST_EQ(sd->kernel_key_size, k->key_offset + k->key_size, + " workbuf key size"); + TEST_EQ(sd->workbuf_used, + vb2_wb_round_up(sd->kernel_key_offset + + sd->kernel_key_size), + " workbuf used"); + TEST_EQ(k->algorithm, 7, " key algorithm"); + TEST_EQ(k->key_size, sizeof(fw_kernel_key_data), " key_size"); + TEST_EQ(memcmp((uint8_t *)k + k->key_offset, fw_kernel_key_data, + k->key_size), 0, " key data"); + TEST_EQ(sd->kernel_version_secdata, 0x20002, + " secdata_kernel version"); + + /* Test successful call in recovery mode */ + reset_common_data(FOR_PHASE1); + ctx->flags |= VB2_CONTEXT_RECOVERY_MODE; + /* No preamble needed in recovery mode */ + sd->workbuf_used = sd->preamble_offset; + sd->preamble_offset = sd->preamble_size = 0; + wb_used_before = sd->workbuf_used; + TEST_SUCC(vb2api_kernel_phase1(ctx), "phase1 rec good"); + /* Make sure recovery key was loaded */ + TEST_EQ(sd->kernel_key_offset, wb_used_before, + " workbuf key offset"); + k = vb2_member_of(sd, sd->kernel_key_offset); + TEST_EQ(sd->kernel_key_size, k->key_offset + k->key_size, + " workbuf key size"); + TEST_EQ(sd->workbuf_used, + vb2_wb_round_up(sd->kernel_key_offset + + sd->kernel_key_size), + " workbuf used"); + TEST_EQ(k->algorithm, 11, " key algorithm"); + TEST_EQ(k->key_size, sizeof(mock_gbb.recovery_key_data), " key_size"); + TEST_EQ(memcmp((uint8_t *)k + k->key_offset, + mock_gbb.recovery_key_data, k->key_size), 0, + " key data"); + TEST_EQ(sd->kernel_version_secdata, 0x20002, + " secdata_kernel version"); + + /* Bad secdata_kernel causes failure in normal mode only */ + reset_common_data(FOR_PHASE1); + ctx->secdata_kernel[2] ^= 0x33; /* 3rd byte is CRC */ + TEST_EQ(vb2api_kernel_phase1(ctx), VB2_ERROR_SECDATA_KERNEL_CRC, + "phase1 bad secdata_kernel"); + TEST_EQ(vb2_nv_get(ctx, VB2_NV_RECOVERY_REQUEST), + VB2_RECOVERY_SECDATA_KERNEL_INIT, " recovery reason"); + + reset_common_data(FOR_PHASE1); + ctx->secdata_kernel[0] ^= 0x33; + ctx->flags |= VB2_CONTEXT_RECOVERY_MODE; + TEST_SUCC(vb2api_kernel_phase1(ctx), "phase1 bad secdata_kernel rec"); + TEST_EQ(sd->kernel_version_secdata, 0, " secdata_kernel version"); + TEST_EQ(vb2_nv_get(ctx, VB2_NV_RECOVERY_REQUEST), + VB2_RECOVERY_NOT_REQUESTED, " no recovery"); + + /* Bad secdata_fwmp causes failure in normal mode only */ + reset_common_data(FOR_PHASE1); + mock_secdata_fwmp_check_retval = VB2_ERROR_SECDATA_FWMP_CRC; + TEST_EQ(vb2api_kernel_phase1(ctx), mock_secdata_fwmp_check_retval, + "phase1 bad secdata_fwmp"); + TEST_EQ(vb2_nv_get(ctx, VB2_NV_RECOVERY_REQUEST), + VB2_RECOVERY_SECDATA_FWMP_INIT, " recovery reason"); + + reset_common_data(FOR_PHASE1); + mock_secdata_fwmp_check_retval = VB2_ERROR_SECDATA_FWMP_CRC; + ctx->flags |= VB2_CONTEXT_RECOVERY_MODE; + TEST_SUCC(vb2api_kernel_phase1(ctx), "phase1 bad secdata_fwmp rec"); + TEST_EQ(vb2_nv_get(ctx, VB2_NV_RECOVERY_REQUEST), + VB2_RECOVERY_NOT_REQUESTED, " no recovery"); + + /* Failures while reading recovery key */ + reset_common_data(FOR_PHASE1); + ctx->flags |= VB2_CONTEXT_RECOVERY_MODE; + mock_gbb.h.recovery_key_size = sd->workbuf_size - 1; + mock_gbb.recovery_key.key_size = + mock_gbb.h.recovery_key_size - sizeof(mock_gbb.recovery_key); + TEST_EQ(vb2api_kernel_phase1(ctx), VB2_SUCCESS, + "phase1 rec workbuf key"); + TEST_EQ(sd->kernel_key_offset, 0, " workbuf key offset"); + TEST_EQ(sd->kernel_key_size, 0, " workbuf key size"); + mock_gbb.h.flags |= VB2_GBB_FLAG_FORCE_MANUAL_RECOVERY; + TEST_ABORT(vb2api_kernel_phase1(ctx), " fatal for manual recovery"); + + reset_common_data(FOR_PHASE1); + ctx->flags |= VB2_CONTEXT_RECOVERY_MODE; + mock_read_res_fail_on_call = 1; + TEST_EQ(vb2api_kernel_phase1(ctx), VB2_SUCCESS, + "phase1 rec gbb read key"); + TEST_EQ(sd->kernel_key_offset, 0, " workbuf key offset"); + TEST_EQ(sd->kernel_key_size, 0, " workbuf key size"); + mock_gbb.h.flags |= VB2_GBB_FLAG_FORCE_MANUAL_RECOVERY; + mock_read_res_fail_on_call = 1; + TEST_ABORT(vb2api_kernel_phase1(ctx), " fatal for manual recovery"); + + /* Failures while parsing subkey from firmware preamble */ + reset_common_data(FOR_PHASE1); + sd->preamble_size = 0; + TEST_EQ(vb2api_kernel_phase1(ctx), VB2_ERROR_API_KPHASE1_PREAMBLE, + "phase1 fw preamble"); +} + +static void normal_boot_tests(void) +{ + /* + * TODO: vb2_normal_boot() tests go here. Relocate from + * vboot_legacy_clamshell_tests.c, and remove comment in + * vboot_legacy_menu_tests.c. + */ +} + +int main(int argc, char* argv[]) +{ + phase1_tests(); + normal_boot_tests(); + + return gTestSuccess ? 0 : 255; +} diff --git a/tests/vboot_legacy_clamshell_tests.c b/tests/vboot_legacy_clamshell_tests.c index 9c7e0c71..4f0849d3 100644 --- a/tests/vboot_legacy_clamshell_tests.c +++ b/tests/vboot_legacy_clamshell_tests.c @@ -6,6 +6,7 @@ */ #include "2common.h" +#include "2kernel.h" #include "2misc.h" #include "2nvstorage.h" #include "2secdata.h" @@ -484,25 +485,25 @@ static void VbBootTest(void) ResetMocks(); vbtlk_expect_fixed = 1; vbtlk_retval = VB2_SUCCESS; - TEST_EQ(VbBootNormal(ctx), VB2_SUCCESS, - "VbBootNormal() returns VB2_SUCCESS"); + TEST_EQ(vb2_normal_boot(ctx), VB2_SUCCESS, + "vb2_normal_boot() returns VB2_SUCCESS"); ResetMocks(); vbtlk_expect_fixed = 1; - TEST_EQ(VbBootNormal(ctx), VB2_ERROR_MOCK, - "VbBootNormal() returns VB2_ERROR_MOCK"); + TEST_EQ(vb2_normal_boot(ctx), VB2_ERROR_MOCK, + "vb2_normal_boot() returns VB2_ERROR_MOCK"); ResetMocks(); vb2_nv_set(ctx, VB2_NV_DISPLAY_REQUEST, 1); - TEST_EQ(VbBootNormal(ctx), VBERROR_REBOOT_REQUIRED, - "VbBootNormal() reboot to reset NVRAM display request"); + TEST_EQ(vb2_normal_boot(ctx), VBERROR_REBOOT_REQUIRED, + "vb2_normal_boot() reboot to reset NVRAM display request"); TEST_EQ(vb2_nv_get(ctx, VB2_NV_DISPLAY_REQUEST), 0, " display request reset"); ResetMocks(); vb2_nv_set(ctx, VB2_NV_DIAG_REQUEST, 1); - TEST_EQ(VbBootNormal(ctx), VBERROR_REBOOT_REQUIRED, - "VbBootNormal() reboot to reset NVRAM diag request"); + TEST_EQ(vb2_normal_boot(ctx), VBERROR_REBOOT_REQUIRED, + "vb2_normal_boot() reboot to reset NVRAM diag request"); TEST_EQ(vb2_nv_get(ctx, VB2_NV_DIAG_REQUEST), 0, " diag request reset"); } diff --git a/tests/vboot_legacy_menu_tests.c b/tests/vboot_legacy_menu_tests.c index a9af7ddd..57dcb4a6 100644 --- a/tests/vboot_legacy_menu_tests.c +++ b/tests/vboot_legacy_menu_tests.c @@ -235,7 +235,8 @@ vb2_error_t vb2_enable_developer_mode(struct vb2_context *c) /* Tests */ /* - * VbBootNormal tests: Please see VbBootTest in vboot_legacy_clamshell_tests.c + * vb2_normal_boot tests: Please see VbBootTest in + * vboot_legacy_clamshell_tests.c. */ static void VbBootDevTest(void) |