From 18cb85b52d689b4cfbd49c6384ae2088b78af1e0 Mon Sep 17 00:00:00 2001 From: Hsin-Te Yuan Date: Tue, 12 Jul 2022 10:52:13 +0800 Subject: 2load_kernel.c: Expose load kernel as vb2_api Move VbTryLoadKernel() and VbTryLoadMiniOsKernel() to depthcharge. In addition, expose LoadKernel() and LoadMiniOsKernel() as vb2api_load_kernel() and vb2api_load_minos_kernel(), respectively. Since we remove VbTryLoadKernel() in vb2api_normal_boot(), rename vb2api_normal_boot() to check_kernel_version() and call it in vb2api_kernel_finalize(). Also, rename VbSelectAndLoadKernelParams, VbDiskInfo, and VbExDiskHandle_t as vb2_kernel_params, vb2_disk_info, and vb2ex_disk_handle_t, respectively. BUG=b:172339016 BRANCH=none TEST=make runtests TEST=FW_NAME=tomato emerge-cherry depthcharge Cq-Depend: chromium:3758201 Signed-off-by: Hsin-Te Yuan Change-Id: Iaf182ca81797fee24c6104dac9d4770ff75c7fb2 Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/vboot_reference/+/3755923 Reviewed-by: Yu-Ping Wu Reviewed-by: Julius Werner --- Makefile | 15 +- firmware/2lib/2kernel.c | 102 ++-- firmware/2lib/2load_kernel.c | 762 ++++++++++++++++++++++++++++++ firmware/2lib/2recovery_reasons.c | 2 +- firmware/2lib/2secdata_kernel.c | 1 + firmware/2lib/include/2api.h | 150 +++++- firmware/2lib/include/2constants.h | 8 + firmware/2lib/include/2recovery_reasons.h | 2 +- firmware/2lib/include/2return_codes.h | 8 +- firmware/include/gpt_misc.h | 9 +- firmware/include/vboot_api.h | 164 +------ firmware/lib/gpt_misc.c | 4 +- firmware/lib/include/load_kernel_fw.h | 41 -- firmware/lib/include/vboot_struct.h | 21 +- firmware/lib/vboot_api_kernel.c | 123 ----- firmware/lib/vboot_kernel.c | 758 ----------------------------- firmware/stub/vboot_api_stub_disk.c | 10 +- firmware/stub/vboot_api_stub_stream.c | 4 +- host/lib/crossystem.c | 3 +- tests/cgpt_fuzzer.c | 2 +- tests/gpt_misc_tests.c | 10 +- tests/vb2_auxfw_sync_tests.c | 1 - tests/vb2_ec_sync_tests.c | 1 - tests/vb2_kernel2_tests.c | 146 ------ tests/vb2_kernel_tests.c | 98 +++- tests/vb2_load_kernel2_tests.c | 467 ++++++++++++++++++ tests/vb2_load_kernel_tests.c | 624 ++++++++++++++++++++++++ tests/vboot_api_kernel_tests.c | 644 ------------------------- tests/vboot_kernel2_tests.c | 443 ----------------- tests/vboot_kernel_tests.c | 621 ------------------------ tests/verify_kernel.c | 25 +- utility/load_kernel_test.c | 17 +- 32 files changed, 2192 insertions(+), 3094 deletions(-) create mode 100644 firmware/2lib/2load_kernel.c delete mode 100644 firmware/lib/include/load_kernel_fw.h delete mode 100644 firmware/lib/vboot_api_kernel.c delete mode 100644 firmware/lib/vboot_kernel.c delete mode 100644 tests/vb2_kernel2_tests.c create mode 100644 tests/vb2_load_kernel2_tests.c create mode 100644 tests/vb2_load_kernel_tests.c delete mode 100644 tests/vboot_api_kernel_tests.c delete mode 100644 tests/vboot_kernel2_tests.c delete mode 100644 tests/vboot_kernel_tests.c diff --git a/Makefile b/Makefile index e1569516..df906a2f 100644 --- a/Makefile +++ b/Makefile @@ -376,6 +376,7 @@ FWLIB_SRCS = \ firmware/2lib/2gbb.c \ firmware/2lib/2hmac.c \ firmware/2lib/2kernel.c \ + firmware/2lib/2load_kernel.c \ firmware/2lib/2misc.c \ firmware/2lib/2nvstorage.c \ firmware/2lib/2packed_key.c \ @@ -395,8 +396,6 @@ FWLIB_SRCS = \ firmware/lib/cgptlib/cgptlib_internal.c \ firmware/lib/cgptlib/crc32.c \ firmware/lib/gpt_misc.c \ - firmware/lib/vboot_api_kernel.c \ - firmware/lib/vboot_kernel.c \ firmware/lib20/api_kernel.c \ firmware/lib20/kernel.c @@ -714,9 +713,6 @@ TEST_NAMES = \ tests/gpt_misc_tests \ tests/sha_benchmark \ tests/subprocess_tests \ - tests/vboot_api_kernel_tests \ - tests/vboot_kernel_tests \ - tests/vboot_kernel2_tests \ tests/verify_kernel ifeq ($(filter-out 0,${MOCK_TPM})$(filter-out 0,${TPM2_MODE}),) @@ -747,7 +743,8 @@ TEST2X_NAMES = \ tests/vb2_host_key_tests \ tests/vb2_host_nvdata_flashrom_tests \ tests/vb2_kernel_tests \ - tests/vb2_kernel2_tests \ + tests/vb2_load_kernel_tests \ + tests/vb2_load_kernel2_tests \ tests/vb2_misc_tests \ tests/vb2_misc2_tests \ tests/vb2_nvstorage_tests \ @@ -1286,9 +1283,6 @@ ifeq ($(filter-out 0,${MOCK_TPM})$(filter-out 0,${TPM2_MODE}),) # tlcl_tests only works when MOCK_TPM is disabled ${RUNTEST} ${BUILD_RUN}/tests/tlcl_tests endif - ${RUNTEST} ${BUILD_RUN}/tests/vboot_api_kernel_tests - ${RUNTEST} ${BUILD_RUN}/tests/vboot_kernel_tests - ${RUNTEST} ${BUILD_RUN}/tests/vboot_kernel2_tests .PHONY: run2tests run2tests: install_for_test @@ -1302,8 +1296,9 @@ run2tests: install_for_test ${RUNTEST} ${BUILD_RUN}/tests/vb2_firmware_tests ${RUNTEST} ${BUILD_RUN}/tests/vb2_gbb_tests ${RUNTEST} ${BUILD_RUN}/tests/vb2_host_key_tests + ${RUNTEST} ${BUILD_RUN}/tests/vb2_load_kernel_tests + ${RUNTEST} ${BUILD_RUN}/tests/vb2_load_kernel2_tests ${RUNTEST} ${BUILD_RUN}/tests/vb2_kernel_tests - ${RUNTEST} ${BUILD_RUN}/tests/vb2_kernel2_tests ${RUNTEST} ${BUILD_RUN}/tests/vb2_misc_tests ${RUNTEST} ${BUILD_RUN}/tests/vb2_misc2_tests ${RUNTEST} ${BUILD_RUN}/tests/vb2_nvstorage_tests diff --git a/firmware/2lib/2kernel.c b/firmware/2lib/2kernel.c index 41764ef1..ff89e142 100644 --- a/firmware/2lib/2kernel.c +++ b/firmware/2lib/2kernel.c @@ -5,64 +5,12 @@ * 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 "vboot_api.h" - -vb2_error_t vb2api_normal_boot(struct vb2_context *ctx, - VbSelectAndLoadKernelParams *kparams) -{ - 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"); - - vb2_error_t rv = VbTryLoadKernel(ctx, VB_DISK_FLAG_FIXED, kparams); - - 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) { @@ -263,6 +211,51 @@ vb2_error_t vb2api_kernel_phase2(struct vb2_context *ctx) return VB2_SUCCESS; } +static void update_kernel_version(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); + + 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; + } + + /* + * 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); + } +} + vb2_error_t vb2api_kernel_finalize(struct vb2_context *ctx) { vb2_gbb_flags_t gbb_flags = vb2api_gbb_get_flags(ctx); @@ -279,5 +272,8 @@ vb2_error_t vb2api_kernel_finalize(struct vb2_context *ctx) return VB2_ERROR_ESCAPE_NO_BOOT; } + if (ctx->boot_mode == VB2_BOOT_MODE_NORMAL) + update_kernel_version(ctx); + return VB2_SUCCESS; } diff --git a/firmware/2lib/2load_kernel.c b/firmware/2lib/2load_kernel.c new file mode 100644 index 00000000..65e40536 --- /dev/null +++ b/firmware/2lib/2load_kernel.c @@ -0,0 +1,762 @@ +/* Copyright (c) 2013 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. + * + * Functions for loading a kernel from disk. + * (Firmware portion) + */ + +#include "2api.h" +#include "2common.h" +#include "2misc.h" +#include "2nvstorage.h" +#include "2packed_key.h" +#include "2secdata.h" +#include "2sysincludes.h" +#include "cgptlib.h" +#include "cgptlib_internal.h" +#include "gpt_misc.h" +#include "vboot_api.h" + +enum vb2_load_partition_flags { + VB2_LOAD_PARTITION_FLAG_VBLOCK_ONLY = (1 << 0), + VB2_LOAD_PARTITION_FLAG_MINIOS = (1 << 1), +}; + +#define KBUF_SIZE 65536 /* Bytes to read at start of kernel partition */ + +/* Minimum context work buffer size needed for vb2_load_partition() */ +#define VB2_LOAD_PARTITION_WORKBUF_BYTES \ + (VB2_VERIFY_KERNEL_PREAMBLE_WORKBUF_BYTES + KBUF_SIZE) + +#define LOWEST_TPM_VERSION 0xffffffff + +/** + * Check if a valid keyblock is required. + * + * @param ctx Vboot context + * @return 1 if valid keyblock required (officially signed kernel); + * 0 if valid hash is enough (self-signed kernel). + */ +static int need_valid_keyblock(struct vb2_context *ctx) +{ + /* Normal and recovery modes always require official OS */ + if (ctx->boot_mode != VB2_BOOT_MODE_DEVELOPER) + return 1; + + /* FWMP can require developer mode to use signed kernels */ + if (vb2_secdata_fwmp_get_flag( + ctx, VB2_SECDATA_FWMP_DEV_ENABLE_OFFICIAL_ONLY)) + return 1; + + /* Developers may require signed kernels */ + if (vb2_nv_get(ctx, VB2_NV_DEV_BOOT_SIGNED_ONLY)) + return 1; + + return 0; +} + +/** + * Return a pointer to the keyblock inside a vblock. + * + * Must only be called during or after vb2_verify_kernel_vblock(). + * + * @param kbuf Buffer containing vblock + * @return The keyblock pointer. + */ +static struct vb2_keyblock *get_keyblock(uint8_t *kbuf) +{ + return (struct vb2_keyblock *)kbuf; +} + +/** + * Return a pointer to the kernel preamble inside a vblock. + * + * Must only be called during or after vb2_verify_kernel_vblock(). + * + * @param kbuf Buffer containing vblock + * @return The kernel preamble pointer. + */ +static struct vb2_kernel_preamble *get_preamble(uint8_t *kbuf) +{ + return (struct vb2_kernel_preamble *) + (kbuf + get_keyblock(kbuf)->keyblock_size); +} + +/** + * Return the offset of the kernel body from the start of the vblock. + * + * Must only be called during or after vb2_verify_kernel_vblock(). + * + * @param kbuf Buffer containing vblock + * @return The offset of the kernel body from the vblock start, in bytes. + */ +static uint32_t get_body_offset(uint8_t *kbuf) +{ + return (get_keyblock(kbuf)->keyblock_size + + get_preamble(kbuf)->preamble_size); +} + +/** + * Verify developer mode key hash. + * + * @param ctx Vboot context + * @param keyblock Keyblock to verify + * @return VB2_SUCCESS, or non-zero error code. + */ +static vb2_error_t vb2_verify_kernel_dev_key_hash( + struct vb2_context *ctx, struct vb2_keyblock *keyblock) +{ + struct vb2_packed_key *key = &keyblock->data_key; + uint8_t *buf = ((uint8_t *)key) + key->key_offset; + uint32_t buflen = key->key_size; + uint8_t digest[VB2_SHA256_DIGEST_SIZE]; + + VB2_DEBUG("Checking developer key hash.\n"); + VB2_TRY(vb2_digest_buffer(buf, buflen, VB2_HASH_SHA256, digest, + sizeof(digest))); + + uint8_t *fwmp_dev_key_hash = + vb2_secdata_fwmp_get_dev_key_hash(ctx); + if (fwmp_dev_key_hash == NULL) { + VB2_DEBUG("Couldn't retrieve developer key hash.\n"); + return VB2_ERROR_KERNEL_KEYBLOCK_DEV_KEY_HASH; + } + + if (vb2_safe_memcmp(digest, fwmp_dev_key_hash, + VB2_SHA256_DIGEST_SIZE)) { + int i; + + VB2_DEBUG("Wrong developer key hash.\n"); + VB2_DEBUG("Want: "); + for (i = 0; i < VB2_SHA256_DIGEST_SIZE; i++) + VB2_DEBUG_RAW("%02x ", fwmp_dev_key_hash[i]); + VB2_DEBUG_RAW("\n"); + VB2_DEBUG("Got: "); + for (i = 0; i < VB2_SHA256_DIGEST_SIZE; i++) + VB2_DEBUG_RAW("%02x ", digest[i]); + VB2_DEBUG_RAW("\n"); + + return VB2_ERROR_KERNEL_KEYBLOCK_DEV_KEY_HASH; + } + + return VB2_SUCCESS; +} + +/** + * Verify a kernel vblock. + * + * @param ctx Vboot context + * @param kbuf Buffer containing the vblock + * @param kbuf_size Size of the buffer in bytes + * @param lpflags Flags (one or more of vb2_load_partition_flags) + * @param wb Work buffer. Must be at least + * VB2_VERIFY_KERNEL_PREAMBLE_WORKBUF_BYTES bytes. + * @return VB2_SUCCESS, or non-zero error code. + */ +static vb2_error_t vb2_verify_kernel_vblock( + struct vb2_context *ctx, uint8_t *kbuf, uint32_t kbuf_size, + uint32_t lpflags, struct vb2_workbuf *wb) +{ + struct vb2_shared_data *sd = vb2_get_sd(ctx); + + uint8_t *key_data; + uint32_t key_size; + struct vb2_public_key kernel_key; + + int need_keyblock_valid = need_valid_keyblock(ctx); + int keyblock_valid = 1; /* Assume valid */ + + vb2_error_t rv; + + /* Locate key to verify kernel. This will either be a recovery key, or + a kernel subkey passed from firmware verification. */ + key_data = vb2_member_of(sd, sd->kernel_key_offset); + key_size = sd->kernel_key_size; + VB2_TRY(vb2_unpack_key_buffer(&kernel_key, key_data, key_size)); + + if (vb2_hwcrypto_allowed(ctx)) + kernel_key.allow_hwcrypto = 1; + + /* + * Clear any previous keyblock-valid flag (for example, from a previous + * kernel where the keyblock was signed but the preamble failed + * verification). + */ + sd->flags &= ~VB2_SD_FLAG_KERNEL_SIGNED; + + /* Verify the keyblock. */ + struct vb2_keyblock *keyblock = get_keyblock(kbuf); + rv = vb2_verify_keyblock(keyblock, kbuf_size, &kernel_key, wb); + if (rv) { + VB2_DEBUG("Verifying keyblock signature failed.\n"); + keyblock_valid = 0; + + /* Check if we must have an officially signed kernel */ + if (need_keyblock_valid) { + VB2_DEBUG("Self-signed kernels not enabled.\n"); + return rv; + } + + /* Otherwise, allow the kernel if the keyblock hash is valid */ + rv = vb2_verify_keyblock_hash(keyblock, kbuf_size, wb); + if (rv) { + VB2_DEBUG("Verifying keyblock hash failed.\n"); + return rv; + } + } + + /* Check the keyblock flags against boot flags. */ + if (!(keyblock->keyblock_flags & + ((ctx->flags & VB2_CONTEXT_DEVELOPER_MODE) ? + VB2_KEYBLOCK_FLAG_DEVELOPER_1 : + VB2_KEYBLOCK_FLAG_DEVELOPER_0))) { + VB2_DEBUG("Keyblock developer flag mismatch.\n"); + keyblock_valid = 0; + if (need_keyblock_valid) + return VB2_ERROR_KERNEL_KEYBLOCK_DEV_FLAG; + } + if (!(keyblock->keyblock_flags & + ((ctx->flags & VB2_CONTEXT_RECOVERY_MODE) ? + VB2_KEYBLOCK_FLAG_RECOVERY_1 : + VB2_KEYBLOCK_FLAG_RECOVERY_0))) { + VB2_DEBUG("Keyblock recovery flag mismatch.\n"); + keyblock_valid = 0; + if (need_keyblock_valid) + return VB2_ERROR_KERNEL_KEYBLOCK_REC_FLAG; + } + if (!(keyblock->keyblock_flags & + ((lpflags & VB2_LOAD_PARTITION_FLAG_MINIOS) ? + VB2_KEYBLOCK_FLAG_MINIOS_1 : + VB2_KEYBLOCK_FLAG_MINIOS_0))) { + VB2_DEBUG("Keyblock miniOS flag mismatch.\n"); + keyblock_valid = 0; + if (need_keyblock_valid) + return VB2_ERROR_KERNEL_KEYBLOCK_MINIOS_FLAG; + } + + /* Check for rollback of key version except in recovery mode. */ + uint32_t key_version = keyblock->data_key.key_version; + if (ctx->boot_mode != VB2_BOOT_MODE_MANUAL_RECOVERY) { + if (key_version < (sd->kernel_version_secdata >> 16)) { + keyblock_valid = 0; + if (need_keyblock_valid) { + VB2_DEBUG("Key version too old.\n"); + return VB2_ERROR_KERNEL_KEYBLOCK_VERSION_ROLLBACK; + } + } + if (key_version > VB2_MAX_KEY_VERSION) { + /* + * Key version is stored in 16 bits in the TPM, so key + * versions greater than 0xFFFF can't be stored + * properly. + */ + VB2_DEBUG("Key version > 0xFFFF.\n"); + keyblock_valid = 0; + if (need_keyblock_valid) + return VB2_ERROR_KERNEL_KEYBLOCK_VERSION_RANGE; + } + } + + /* If in developer mode and using key hash, check it. */ + if (ctx->boot_mode == VB2_BOOT_MODE_DEVELOPER && + vb2_secdata_fwmp_get_flag(ctx, VB2_SECDATA_FWMP_DEV_USE_KEY_HASH)) { + VB2_TRY(vb2_verify_kernel_dev_key_hash(ctx, keyblock)); + } + + /* + * At this point, we've checked everything. The kernel keyblock is at + * least self-consistent, and has either a valid signature or a valid + * hash. Track if it had a valid signature (that is, would we have + * been willing to boot it even if developer mode was off). + */ + if (keyblock_valid) + sd->flags |= VB2_SD_FLAG_KERNEL_SIGNED; + + /* Get key for preamble verification from the keyblock. */ + struct vb2_public_key data_key; + rv = vb2_unpack_key(&data_key, &keyblock->data_key); + if (rv) { + VB2_DEBUG("Unable to unpack kernel data key\n"); + return rv; + } + + /* Verify the preamble, which follows the keyblock */ + struct vb2_kernel_preamble *preamble = get_preamble(kbuf); + rv = vb2_verify_kernel_preamble(preamble, + kbuf_size - keyblock->keyblock_size, + &data_key, + wb); + if (rv) { + VB2_DEBUG("Preamble verification failed.\n"); + return rv; + } + + /* Rollback check for miniOS */ + if (need_keyblock_valid && (lpflags & VB2_LOAD_PARTITION_FLAG_MINIOS)) { + if (preamble->kernel_version < + (sd->kernel_version_secdata >> 24)) { + keyblock_valid = 0; + if (need_keyblock_valid) { + VB2_DEBUG("miniOS kernel version too old.\n"); + return VB2_ERROR_KERNEL_PREAMBLE_VERSION_ROLLBACK; + } + } + if (preamble->kernel_version > 0xff) { + /* + * Key version is stored in the top 8 bits of 16 bits + * in the TPM, so key versions greater than 0xFF can't + * be stored properly. + */ + VB2_DEBUG("Key version > 0xFF.\n"); + keyblock_valid = 0; + if (need_keyblock_valid) + return VB2_ERROR_KERNEL_PREAMBLE_VERSION_RANGE; + } + } + + /* + * Kernel preamble version is the lower 16 bits of the composite + * kernel version. + */ + if (preamble->kernel_version > VB2_MAX_PREAMBLE_VERSION) + return VB2_ERROR_KERNEL_PREAMBLE_VERSION_RANGE; + + /* Combine with the key version. */ + sd->kernel_version = key_version << 16 | preamble->kernel_version; + + /* If not in recovery mode, check for rollback of the kernel version. */ + if (need_keyblock_valid && + ctx->boot_mode != VB2_BOOT_MODE_MANUAL_RECOVERY && + sd->kernel_version < sd->kernel_version_secdata) { + VB2_DEBUG("Kernel version too low.\n"); + return VB2_ERROR_KERNEL_PREAMBLE_VERSION_ROLLBACK; + } + + VB2_DEBUG("Kernel preamble is good.\n"); + return VB2_SUCCESS; +} + +/** + * Load and verify a partition from the stream. + * + * @param ctx Vboot context + * @param params Load-kernel parameters + * @param stream Stream to load kernel from + * @param lpflags Flags (one or more of vb2_load_partition_flags) + * @return VB2_SUCCESS, or non-zero error code. + */ +static vb2_error_t vb2_load_partition( + struct vb2_context *ctx, struct vb2_kernel_params *params, + VbExStream_t stream, uint32_t lpflags) +{ + uint32_t read_ms = 0, start_ts; + struct vb2_workbuf wb; + + vb2_workbuf_from_ctx(ctx, &wb); + + /* Allocate kernel header buffer in workbuf */ + uint8_t *kbuf = vb2_workbuf_alloc(&wb, KBUF_SIZE); + if (!kbuf) + return VB2_ERROR_LOAD_PARTITION_WORKBUF; + + start_ts = vb2ex_mtime(); + if (VbExStreamRead(stream, KBUF_SIZE, kbuf)) { + VB2_DEBUG("Unable to read start of partition.\n"); + return VB2_ERROR_LOAD_PARTITION_READ_VBLOCK; + } + read_ms += vb2ex_mtime() - start_ts; + + if (vb2_verify_kernel_vblock(ctx, kbuf, KBUF_SIZE, lpflags, &wb)) + return VB2_ERROR_LOAD_PARTITION_VERIFY_VBLOCK; + + if (lpflags & VB2_LOAD_PARTITION_FLAG_VBLOCK_ONLY) + return VB2_SUCCESS; + + struct vb2_keyblock *keyblock = get_keyblock(kbuf); + struct vb2_kernel_preamble *preamble = get_preamble(kbuf); + + /* + * Make sure the kernel starts at or before what we already read into + * kbuf. + * + * We could deal with a larger offset by reading and discarding the + * data in between the vblock and the kernel data. + */ + uint32_t body_offset = get_body_offset(kbuf); + if (body_offset > KBUF_SIZE) { + VB2_DEBUG("Kernel body offset is %u > 64KB.\n", body_offset); + return VB2_ERROR_LOAD_PARTITION_BODY_OFFSET; + } + + uint8_t *kernbuf = params->kernel_buffer; + uint32_t kernbuf_size = params->kernel_buffer_size; + if (!kernbuf) { + /* Get kernel load address and size from the header. */ + kernbuf = (uint8_t *)((long)preamble->body_load_address); + kernbuf_size = preamble->body_signature.data_size; + } else if (preamble->body_signature.data_size > kernbuf_size) { + VB2_DEBUG("Kernel body doesn't fit in memory.\n"); + return VB2_ERROR_LOAD_PARTITION_BODY_SIZE; + } + + uint32_t body_toread = preamble->body_signature.data_size; + uint8_t *body_readptr = kernbuf; + + /* + * If we've already read part of the kernel, copy that to the beginning + * of the kernel buffer. + */ + uint32_t body_copied = KBUF_SIZE - body_offset; + if (body_copied > body_toread) + body_copied = body_toread; /* Don't over-copy tiny kernel */ + memcpy(body_readptr, kbuf + body_offset, body_copied); + body_toread -= body_copied; + body_readptr += body_copied; + + /* Read the kernel data */ + start_ts = vb2ex_mtime(); + if (body_toread && VbExStreamRead(stream, body_toread, body_readptr)) { + VB2_DEBUG("Unable to read kernel data.\n"); + return VB2_ERROR_LOAD_PARTITION_READ_BODY; + } + read_ms += vb2ex_mtime() - start_ts; + if (read_ms == 0) /* Avoid division by 0 in speed calculation */ + read_ms = 1; + VB2_DEBUG("read %u KB in %u ms at %u KB/s.\n", + (body_toread + KBUF_SIZE) / 1024, read_ms, + (uint32_t)(((body_toread + KBUF_SIZE) * VB2_MSEC_PER_SEC) / + (read_ms * 1024))); + + /* Get key for preamble/data verification from the keyblock. */ + struct vb2_public_key data_key; + if (vb2_unpack_key(&data_key, &keyblock->data_key)) { + VB2_DEBUG("Unable to unpack kernel data key\n"); + return VB2_ERROR_LOAD_PARTITION_DATA_KEY; + } + + if (vb2_hwcrypto_allowed(ctx)) + data_key.allow_hwcrypto = 1; + + /* Verify kernel data */ + if (vb2_verify_data(kernbuf, kernbuf_size, &preamble->body_signature, + &data_key, &wb)) { + VB2_DEBUG("Kernel data verification failed.\n"); + return VB2_ERROR_LOAD_PARTITION_VERIFY_BODY; + } + + /* If we're still here, the kernel is valid */ + VB2_DEBUG("Partition is good.\n"); + + /* Save kernel data back to parameters */ + params->bootloader_address = preamble->bootloader_address; + params->bootloader_size = preamble->bootloader_size; + params->flags = vb2_kernel_get_flags(preamble); + if (!params->kernel_buffer) { + params->kernel_buffer = kernbuf; + params->kernel_buffer_size = kernbuf_size; + } + + return VB2_SUCCESS; +} + +static vb2_error_t try_minios_kernel(struct vb2_context *ctx, + struct vb2_kernel_params *params, + struct vb2_disk_info *disk_info, + uint64_t sector) { + VbExStream_t stream; + uint64_t sectors_left = disk_info->lba_count - sector; + const uint32_t lpflags = VB2_LOAD_PARTITION_FLAG_MINIOS; + vb2_error_t rv = VB2_ERROR_LK_NO_KERNEL_FOUND; + + /* Re-open stream at correct offset to pass to vb2_load_partition. */ + if (VbExStreamOpen(disk_info->handle, sector, sectors_left, + &stream)) { + VB2_DEBUG("Unable to open disk handle.\n"); + return rv; + } + + rv = vb2_load_partition(ctx, params, stream, lpflags); + VB2_DEBUG("vb2_load_partition returned: %d\n", rv); + + VbExStreamClose(stream); + + if (rv) + return VB2_ERROR_LK_NO_KERNEL_FOUND; + return rv; +} + +static vb2_error_t try_minios_sectors(struct vb2_context *ctx, + struct vb2_kernel_params *params, + struct vb2_disk_info *disk_info, + uint64_t start, uint64_t count) +{ + const uint32_t buf_size = count * disk_info->bytes_per_lba; + char *buf; + VbExStream_t stream; + uint64_t isector; + vb2_error_t rv = VB2_ERROR_LK_NO_KERNEL_FOUND; + + buf = malloc(buf_size); + if (buf == NULL) { + VB2_DEBUG("Unable to allocate disk read buffer.\n"); + return rv; + } + + if (VbExStreamOpen(disk_info->handle, start, count, &stream)) { + VB2_DEBUG("Unable to open disk handle.\n"); + free(buf); + return rv; + } + if (VbExStreamRead(stream, buf_size, buf)) { + VB2_DEBUG("Unable to read disk.\n"); + free(buf); + VbExStreamClose(stream); + return rv; + } + VbExStreamClose(stream); + + for (isector = 0; isector < count; isector++) { + if (memcmp(buf + isector * disk_info->bytes_per_lba, + VB2_KEYBLOCK_MAGIC, VB2_KEYBLOCK_MAGIC_SIZE)) + continue; + VB2_DEBUG("Match on sector %" PRIu64 " / %" PRIu64 "\n", + start + isector, + disk_info->lba_count - 1); + rv = try_minios_kernel(ctx, params, disk_info, start + isector); + if (rv == VB2_SUCCESS) + break; + } + + free(buf); + return rv; +} + +static vb2_error_t try_minios_sector_region(struct vb2_context *ctx, + struct vb2_kernel_params *params, + struct vb2_disk_info *disk_info, + int end_region) +{ + const uint64_t disk_count_half = (disk_info->lba_count + 1) / 2; + const uint64_t check_count_256 = 256 * 1024 + * 1024 / disk_info->bytes_per_lba; // 256 MB + const uint64_t batch_count_1 = 1024 + * 1024 / disk_info->bytes_per_lba; // 1 MB + const uint64_t check_count = VB2_MIN(disk_count_half, check_count_256); + const uint64_t batch_count = VB2_MIN(disk_count_half, batch_count_1); + uint64_t sector; + uint64_t start; + uint64_t end; + const char *region_name; + vb2_error_t rv = VB2_ERROR_LK_NO_KERNEL_FOUND; + + if (!end_region) { + start = 0; + end = check_count; + region_name = "start"; + } else { + start = disk_info->lba_count - check_count; + end = disk_info->lba_count; + region_name = "end"; + } + + VB2_DEBUG("Checking %s of disk for kernels...\n", region_name); + for (sector = start; sector < end; sector += batch_count) { + rv = try_minios_sectors(ctx, params, disk_info, sector, + batch_count); + if (rv == VB2_SUCCESS) + return rv; + } + + return rv; +} + +/* + * Search for kernels by sector, rather than by partition. Only sectors near + * the start and end of disks are considered, and the kernel must start exactly + * at the first byte of the sector. + */ +vb2_error_t vb2api_load_minios_kernel(struct vb2_context *ctx, + struct vb2_kernel_params *params, + struct vb2_disk_info *disk_info, + uint32_t minios_flags) +{ + vb2_error_t rv; + int end_region_first = vb2_nv_get(ctx, VB2_NV_MINIOS_PRIORITY); + + if (minios_flags & VB2_MINIOS_FLAG_NON_ACTIVE) + rv = VB2_ERROR_UNKNOWN; /* Ignore active partition */ + else + rv = try_minios_sector_region(ctx, params, disk_info, + end_region_first); + + if (rv) + rv = try_minios_sector_region(ctx, params, disk_info, + !end_region_first); + + if (rv == VB2_SUCCESS) + params->disk_handle = disk_info->handle; + + return rv; +} + +vb2_error_t vb2api_load_kernel(struct vb2_context *ctx, + struct vb2_kernel_params *params, + struct vb2_disk_info *disk_info) +{ + struct vb2_shared_data *sd = vb2_get_sd(ctx); + int found_partitions = 0; + uint32_t lowest_version = LOWEST_TPM_VERSION; + vb2_error_t rv; + + /* Clear output params */ + params->partition_number = 0; + + /* Read GPT data */ + GptData gpt; + gpt.sector_bytes = (uint32_t)disk_info->bytes_per_lba; + gpt.streaming_drive_sectors = disk_info->streaming_lba_count + ?: disk_info->lba_count; + gpt.gpt_drive_sectors = disk_info->lba_count; + gpt.flags = disk_info->flags & VB2_DISK_FLAG_EXTERNAL_GPT + ? GPT_FLAG_EXTERNAL : 0; + if (AllocAndReadGptData(disk_info->handle, &gpt)) { + VB2_DEBUG("Unable to read GPT data\n"); + goto gpt_done; + } + + /* Initialize GPT library */ + if (GptInit(&gpt)) { + VB2_DEBUG("Error parsing GPT\n"); + goto gpt_done; + } + + /* Loop over candidate kernel partitions */ + uint64_t part_start, part_size; + while (GptNextKernelEntry(&gpt, &part_start, &part_size) == + GPT_SUCCESS) { + + VB2_DEBUG("Found kernel entry at %" + PRIu64 " size %" PRIu64 "\n", + part_start, part_size); + + /* Found at least one kernel partition. */ + found_partitions++; + + /* Set up the stream */ + VbExStream_t stream = NULL; + if (VbExStreamOpen(disk_info->handle, + part_start, part_size, &stream)) { + VB2_DEBUG("Partition error getting stream.\n"); + VB2_DEBUG("Marking kernel as invalid.\n"); + GptUpdateKernelEntry(&gpt, GPT_UPDATE_ENTRY_BAD); + continue; + } + + uint32_t lpflags = 0; + if (params->partition_number > 0) { + /* + * If we already have a good kernel, we only needed to + * look at the vblock versions to check for rollback. + */ + lpflags |= VB2_LOAD_PARTITION_FLAG_VBLOCK_ONLY; + } + + rv = vb2_load_partition(ctx, params, stream, lpflags); + VbExStreamClose(stream); + + if (rv) { + VB2_DEBUG("Marking kernel as invalid (err=%x).\n", rv); + GptUpdateKernelEntry(&gpt, GPT_UPDATE_ENTRY_BAD); + continue; + } + + int keyblock_valid = sd->flags & VB2_SD_FLAG_KERNEL_SIGNED; + /* Track lowest version from a valid header. */ + if (keyblock_valid && lowest_version > sd->kernel_version) { + lowest_version = sd->kernel_version; + } + VB2_DEBUG("Keyblock valid: %d\n", keyblock_valid); + VB2_DEBUG("Combined version: %u\n", sd->kernel_version); + + /* + * If we're only looking at headers, we're done with this + * partition. + */ + if (lpflags & VB2_LOAD_PARTITION_FLAG_VBLOCK_ONLY) + continue; + + /* + * Otherwise, we found a partition we like. + * + * TODO: GPT partitions start at 1, but cgptlib starts them at + * 0. Adjust here, until cgptlib is fixed. + */ + params->partition_number = gpt.current_kernel + 1; + + /* + * TODO: GetCurrentKernelUniqueGuid() should take a destination + * size, or the dest should be a struct, so we know it's big + * enough. + */ + GetCurrentKernelUniqueGuid(&gpt, ¶ms->partition_guid); + + /* Update GPT to note this is the kernel we're trying. + * But not when we assume that the boot process may + * not complete for valid reasons (eg. early shutdown). + */ + if (!(ctx->flags & VB2_CONTEXT_NOFAIL_BOOT)) + GptUpdateKernelEntry(&gpt, GPT_UPDATE_ENTRY_TRY); + + /* + * If we're in recovery mode or we're about to boot a + * non-officially-signed kernel, there's no rollback + * protection, so we can stop at the first valid kernel. + */ + if (ctx->boot_mode == VB2_BOOT_MODE_MANUAL_RECOVERY || + !keyblock_valid) { + VB2_DEBUG("In recovery mode or dev-signed kernel\n"); + break; + } + + /* + * Otherwise, we do care about the key index in the TPM. If + * the good partition's key version is the same as the tpm, + * then the TPM doesn't need updating; we can stop now. + * Otherwise, we'll check all the other headers to see if they + * contain a newer key. + */ + if (sd->kernel_version == sd->kernel_version_secdata) { + VB2_DEBUG("Same kernel version\n"); + break; + } + } /* while(GptNextKernelEntry) */ + + gpt_done: + /* Write and free GPT data */ + WriteAndFreeGptData(disk_info->handle, &gpt); + + /* Handle finding a good partition */ + if (params->partition_number > 0) { + VB2_DEBUG("Good partition %d\n", params->partition_number); + /* + * Validity check - only store a new TPM version if we found + * one. If lowest_version is still at its initial value, we + * didn't find one; for example, we're in developer mode and + * just didn't look. + */ + if (lowest_version != LOWEST_TPM_VERSION && + lowest_version > sd->kernel_version_secdata) + sd->kernel_version = lowest_version; + + /* Success! */ + rv = VB2_SUCCESS; + params->disk_handle = disk_info->handle; + } else if (found_partitions > 0) { + rv = VB2_ERROR_LK_INVALID_KERNEL_FOUND; + } else { + rv = VB2_ERROR_LK_NO_KERNEL_FOUND; + } + + return rv; +} diff --git a/firmware/2lib/2recovery_reasons.c b/firmware/2lib/2recovery_reasons.c index 8c43dcef..73fd8121 100644 --- a/firmware/2lib/2recovery_reasons.c +++ b/firmware/2lib/2recovery_reasons.c @@ -113,7 +113,7 @@ const char *vb2_get_recovery_reason_string(uint8_t code) /* 0x46 */ case VB2_RECOVERY_RW_SHARED_DATA: return "Shared data error in rewritable firmware"; /* 0x47 */ case VB2_RECOVERY_DEPRECATED_RW_TEST_LK: - return "Test error from LoadKernel()"; + return "Test error from vb2api_load_kernel()"; /* 0x48 */ case VB2_RECOVERY_DEPRECATED_RW_NO_DISK: return "No bootable storage device in system"; /* 0x49 */ case VB2_RECOVERY_TPM_E_FAIL: diff --git a/firmware/2lib/2secdata_kernel.c b/firmware/2lib/2secdata_kernel.c index 9973cd46..754552c0 100644 --- a/firmware/2lib/2secdata_kernel.c +++ b/firmware/2lib/2secdata_kernel.c @@ -180,6 +180,7 @@ vb2_error_t vb2_secdata_kernel_init(struct vb2_context *ctx) return VB2_SUCCESS; } +test_mockable uint32_t vb2_secdata_kernel_get(struct vb2_context *ctx, enum vb2_secdata_kernel_param param) { diff --git a/firmware/2lib/include/2api.h b/firmware/2lib/include/2api.h index 904b7105..23af95d8 100644 --- a/firmware/2lib/include/2api.h +++ b/firmware/2lib/include/2api.h @@ -29,7 +29,6 @@ #include "2return_codes.h" #include "2rsa.h" #include "2secdata_struct.h" -#include "vboot_api.h" #define _VB2_TRY_IMPL(expr, ctx, recovery_reason, ...) do { \ vb2_error_t _vb2_try_rv = (expr); \ @@ -779,24 +778,155 @@ vb2_error_t vb2api_kernel_phase1(struct vb2_context *ctx); vb2_error_t vb2api_kernel_phase2(struct vb2_context *ctx); /** - * Handle a normal boot. + * Finalize for kernel verification stage. * - * @param ctx Vboot context. - * @param kparams Params specific to loading the kernel + * Handle NO_BOOT flag. Also, check and roll forward kernel version. + * + * @param ctx Vboot context * @return VB2_SUCCESS, or error code on error. */ -vb2_error_t vb2api_normal_boot(struct vb2_context *ctx, - VbSelectAndLoadKernelParams *kparams); +vb2_error_t vb2api_kernel_finalize(struct vb2_context *ctx); + +struct vb2_kernel_params { + /* Inputs to vb2api_load_kernel(). */ + /* Destination buffer for kernel (normally at 0x100000 on x86). */ + void *kernel_buffer; + /* Size of kernel buffer in bytes. */ + uint32_t kernel_buffer_size; + + /* + * Outputs from vb2api_load_kernel(); valid only if it returns success. + */ + /* Handle of disk containing loaded kernel. */ + vb2ex_disk_handle_t disk_handle; + /* Partition number on disk to boot (1...M). */ + uint32_t partition_number; + /* Address of bootloader image in RAM. */ + uint64_t bootloader_address; + /* Size of bootloader image in bytes. */ + uint32_t bootloader_size; + /* UniquePartitionGuid for boot partition. */ + uint8_t partition_guid[16]; + /* Flags set by signer. */ + uint32_t flags; +}; + +/*****************************************************************************/ +/* Disk access */ + +/* Flags for vb2_disk_info */ + +/* + * Disk selection in the lower 16 bits (where the disk lives), and disk + * attributes in the higher 16 bits (extra information about the disk + * needed to access it correctly). + */ +#define VB2_DISK_FLAG_SELECT_MASK 0xffff +#define VB2_DISK_FLAG_ATTRIBUTE_MASK (0xffff << 16) + +/* Disk is removable. Example removable disks: SD cards, USB keys. */ +#define VB2_DISK_FLAG_REMOVABLE (1 << 0) +/* + * Disk is fixed. If this flag is present, disk is internal to the system and + * not removable. Example fixed disks: internal SATA SSD, eMMC. + */ +#define VB2_DISK_FLAG_FIXED (1 << 1) +/* + * Note that VB2_DISK_FLAG_REMOVABLE and VB2_DISK_FLAG_FIXED are + * mutually-exclusive for a single disk. VbExDiskGetInfo() may specify both + * flags to request disks of both types in a single call. + * + * At some point we could specify additional flags, but we don't currently + * have a way to make use of these: + * + * USB Device is known to be attached to USB. Note that the SD + * card reader inside x86 systems is attached to USB so this + * isn't super useful. + * SD Device is known to be a SD card. Note that external card + * readers might not return this information, so also of + * questionable use. + * READ_ONLY Device is known to be read-only. Could be used by recovery + * when processing read-only recovery image. + */ + +/* + * Disks are used in two ways: + * - As a random-access device to read and write the GPT + * - As a streaming device to read the kernel + * These are implemented differently on raw NAND vs eMMC/SATA/USB + * - On eMMC/SATA/USB, both of these refer to the same underlying + * storage, so they have the same size and LBA size. In this case, + * the GPT should not point to the same address as itself. + * - On raw NAND, the GPT is held on a portion of the SPI flash. + * Random access GPT operations refer to the SPI and streaming + * operations refer to NAND. The GPT may therefore point into + * the same offsets as itself. + * These types are distinguished by the following flag and vb2_disk_info + * has separate fields to describe the random-access ("GPT") and + * streaming aspects of the disk. If a disk is random-access (i.e. + * not raw NAND) then these fields are equal. + */ +#define VB2_DISK_FLAG_EXTERNAL_GPT (1 << 16) + +/* Information on a single disk. */ +struct vb2_disk_info { + /* Disk handle. */ + vb2ex_disk_handle_t handle; + /* Size of a random-access LBA sector in bytes. */ + uint64_t bytes_per_lba; + /* Number of random-access LBA sectors on the device. + * If streaming_lba_count is 0, this stands in for the size of the + * randomly accessed portion as well as the streaming portion. + * Otherwise, this is only the randomly-accessed portion. */ + uint64_t lba_count; + /* Number of streaming sectors on the device. */ + uint64_t streaming_lba_count; + /* Flags (see VB2_DISK_FLAG_* constants). */ + uint32_t flags; + /* + * Optional name string, for use in debugging. May be empty or null if + * not available. + */ + const char *name; +}; /** - * Finalize for kernel verification stage. + * Attempt to load kernel from the specified device. On success, the output + * fields of params will be filled. The caller should set the input fields of + * params. * - * Handle NO_BOOT flag. * * @param ctx Vboot context - * @return VB2_SUCCESS, or error code on error. + * @param params Params specific to loading the kernel + * @param disk_info Disk from which to read kernel + * + * @return VB2_SUCCESS, or non-zero error code. */ -vb2_error_t vb2api_kernel_finalize(struct vb2_context *ctx); +vb2_error_t vb2api_load_kernel(struct vb2_context *ctx, + struct vb2_kernel_params *params, + struct vb2_disk_info *disk_info); + +/* miniOS flags */ + +/* Boot from non-active miniOS partition only. */ +#define VB2_MINIOS_FLAG_NON_ACTIVE (1 << 0) + +/** + * Attempt to load miniOS kernel from the specified device. On success, the + * output fields of params will be filled. The caller should set the input + * fields of params. + * + * @param ctx Vboot context + * @param params Params specific to loading the kernel + * @param disk_info Disk from which to read kernel + * @param minios_flags Flags for miniOS + * + * @return VB2_SUCCESS, or non-zero error code. + */ +vb2_error_t vb2api_load_minios_kernel(struct vb2_context *ctx, + struct vb2_kernel_params *params, + struct vb2_disk_info *disk_info, + uint32_t minios_flags); /** * Load the verified boot block (vblock) for a kernel. diff --git a/firmware/2lib/include/2constants.h b/firmware/2lib/include/2constants.h index 96dc9995..c1e6bb91 100644 --- a/firmware/2lib/include/2constants.h +++ b/firmware/2lib/include/2constants.h @@ -80,6 +80,14 @@ #ifndef __ASSEMBLER__ #include typedef uint32_t vb2_gbb_flags_t; +/* + * We use disk handles rather than indices. Using indices causes problems if + * a disk is removed/inserted in the middle of processing. + * + * TODO(b/181739551): move this to 2api.h when the VbExDisk* functions are + * removed from vboot_api.h. + */ +typedef void *vb2ex_disk_handle_t; #endif /* Size of legacy VbSharedDataHeader struct. Defined here to avoid including diff --git a/firmware/2lib/include/2recovery_reasons.h b/firmware/2lib/include/2recovery_reasons.h index 30dbd2e3..918e3327 100644 --- a/firmware/2lib/include/2recovery_reasons.h +++ b/firmware/2lib/include/2recovery_reasons.h @@ -205,7 +205,7 @@ enum vb2_nv_recovery { /* Shared data error in rewritable firmware */ VB2_RECOVERY_RW_SHARED_DATA = 0x46, - /* Test error from LoadKernel() (deprecated) */ + /* Test error from vb2api_load_kernel() (deprecated) */ VB2_RECOVERY_DEPRECATED_RW_TEST_LK = 0x47, /* No bootable disk found (deprecated, see 0x5a) */ diff --git a/firmware/2lib/include/2return_codes.h b/firmware/2lib/include/2return_codes.h index ccb50aa4..a7b79307 100644 --- a/firmware/2lib/include/2return_codes.h +++ b/firmware/2lib/include/2return_codes.h @@ -767,19 +767,19 @@ enum vb2_return_code { VB2_ERROR_DEPRECATED_EX_DIAG_TEST_INIT_FAILED, /********************************************************************** - * LoadKernel errors + * Kernel loading errors * * Should be ordered by specificity -- lower number means more specific. */ VB2_ERROR_LK = 0x100b0000, - /* Only an invalid kernel was found in LoadKernel() */ + /* Only an invalid kernel was found in vb2api_load_kernel() */ VB2_ERROR_LK_INVALID_KERNEL_FOUND = 0x100b1000, - /* No kernel partitions were found in LoadKernel() */ + /* No kernel partitions were found in vb2api_load_kernel() */ VB2_ERROR_LK_NO_KERNEL_FOUND = 0x100b2000, - /* No working block devices were found in VbTryLoadKernel() */ + /* No working block devices were found */ VB2_ERROR_LK_NO_DISK_FOUND = 0x100b3000, /********************************************************************** diff --git a/firmware/include/gpt_misc.h b/firmware/include/gpt_misc.h index aa46094f..e01e0dcf 100644 --- a/firmware/include/gpt_misc.h +++ b/firmware/include/gpt_misc.h @@ -76,9 +76,8 @@ enum { * depthcharge does not have logic to properly setup stored_on_device and * gpt_drive_sectors, but it does do a memset(gpt, 0, sizeof(GptData)). And so, * GPT_STORED_ON_DEVICE should be 0 to make stored_on_device compatible with - * present behavior. At the same time, in vboot_kernel:LoadKernel(), and - * cgpt_common:GptLoad(), we need to have simple shims to set gpt_drive_sectors - * to drive_sectors. + * present behavior. At the same time, in vb2api_load_kernel() and GptLoad(), + * we need to have simple shims to set gpt_drive_sectors to drive_sectors. * * TODO(namnguyen): Remove those shims when the firmware can set these fields. */ @@ -157,12 +156,12 @@ GptEntry *GptFindNthEntry(GptData *gpt, const Guid *guid, unsigned int n); * * Returns 0 if successful, 1 if error. */ -int AllocAndReadGptData(VbExDiskHandle_t disk_handle, GptData *gptdata); +int AllocAndReadGptData(vb2ex_disk_handle_t disk_handle, GptData *gptdata); /** * Write any changes for the GPT data back to the drive, then free the buffers. */ -int WriteAndFreeGptData(VbExDiskHandle_t disk_handle, GptData *gptdata); +int WriteAndFreeGptData(vb2ex_disk_handle_t disk_handle, GptData *gptdata); /** * Return 1 if the entry is unused, 0 if it is used. diff --git a/firmware/include/vboot_api.h b/firmware/include/vboot_api.h index 4dba0a06..4059accb 100644 --- a/firmware/include/vboot_api.h +++ b/firmware/include/vboot_api.h @@ -24,6 +24,7 @@ #include #include +#include "../2lib/include/2constants.h" #include "../2lib/include/2return_codes.h" #include "gpt.h" @@ -32,165 +33,18 @@ extern "C" { #endif /* __cplusplus */ struct vb2_context; +struct vb2_disk_info; typedef struct VbSharedDataHeader VbSharedDataHeader; - -/*****************************************************************************/ -/* Main entry points from firmware into vboot_reference */ - -/* - * We use disk handles rather than indices. Using indices causes problems if - * a disk is removed/inserted in the middle of processing. - */ -typedef void *VbExDiskHandle_t; - -typedef struct VbSelectAndLoadKernelParams { - /* Inputs to VbTryLoadKernel() */ - /* Destination buffer for kernel (normally at 0x100000 on x86) */ - void *kernel_buffer; - /* Size of kernel buffer in bytes */ - uint32_t kernel_buffer_size; - - /* - * Outputs from VbTryLoadKernel(); valid only if it returns success. - */ - /* Handle of disk containing loaded kernel */ - VbExDiskHandle_t disk_handle; - /* Partition number on disk to boot (1...M) */ - uint32_t partition_number; - /* Address of bootloader image in RAM */ - uint64_t bootloader_address; - /* Size of bootloader image in bytes */ - uint32_t bootloader_size; - /* UniquePartitionGuid for boot partition */ - uint8_t partition_guid[16]; - /* Flags set by signer */ - uint32_t flags; -} VbSelectAndLoadKernelParams; - -/** - * Attempt loading a kernel from the specified type(s) of disks. - * - * If successful, sets kparams.disk_handle to the disk for the kernel and - * returns VB2_SUCCESS. - * - * @param ctx Vboot context - * @param disk_flags Flags to pass to VbExDiskGetInfo() - * @param kparams Params specific to loading the kernel - * @return VB2_SUCCESS or the most specific VB2_ERROR_LK error. - */ -vb2_error_t VbTryLoadKernel(struct vb2_context *ctx, uint32_t disk_flags, - VbSelectAndLoadKernelParams *kparams); - -/* miniOS flags */ - -/* Boot from non-active miniOS partition only */ -#define VB_MINIOS_FLAG_NON_ACTIVE (1 << 0) - -/** - * Attempt loading a miniOS kernel from internal disk. - * - * Scans sectors at the start and end of the disk, and looks for miniOS kernels - * starting at the beginning of the sector. Attempts loading any miniOS - * kernels found. - * - * If successful, sets lkp.disk_handle to the disk for the kernel and returns - * VB2_SUCCESS. - * - * @param ctx Vboot context - * @param minios_flags Flags for miniOS - * @param kparams Params specific to loading the kernel - * @return VB2_SUCCESS or the most specific VB2_ERROR_LK error. - */ -vb2_error_t VbTryLoadMiniOsKernel(struct vb2_context *ctx, - uint32_t minios_flags, - VbSelectAndLoadKernelParams *kparams); - /*****************************************************************************/ /* Disk access (previously in boot_device.h) */ -/* Flags for VbDisk APIs */ - -/* - * Disk selection in the lower 16 bits (where the disk lives), and disk - * attributes in the higher 16 bits (extra information about the disk - * needed to access it correctly). - */ -#define VB_DISK_FLAG_SELECT_MASK 0xffff -#define VB_DISK_FLAG_ATTRIBUTE_MASK (0xffff << 16) - -/* Disk is removable. Example removable disks: SD cards, USB keys. */ -#define VB_DISK_FLAG_REMOVABLE (1 << 0) -/* - * Disk is fixed. If this flag is present, disk is internal to the system and - * not removable. Example fixed disks: internal SATA SSD, eMMC. - */ -#define VB_DISK_FLAG_FIXED (1 << 1) -/* - * Note that VB_DISK_FLAG_REMOVABLE and VB_DISK_FLAG_FIXED are - * mutually-exclusive for a single disk. VbExDiskGetInfo() may specify both - * flags to request disks of both types in a single call. - * - * At some point we could specify additional flags, but we don't currently - * have a way to make use of these: - * - * USB Device is known to be attached to USB. Note that the SD - * card reader inside x86 systems is attached to USB so this - * isn't super useful. - * SD Device is known to be a SD card. Note that external card - * readers might not return this information, so also of - * questionable use. - * READ_ONLY Device is known to be read-only. Could be used by recovery - * when processing read-only recovery image. - */ - -/* - * Disks are used in two ways: - * - As a random-access device to read and write the GPT - * - As a streaming device to read the kernel - * These are implemented differently on raw NAND vs eMMC/SATA/USB - * - On eMMC/SATA/USB, both of these refer to the same underlying - * storage, so they have the same size and LBA size. In this case, - * the GPT should not point to the same address as itself. - * - On raw NAND, the GPT is held on a portion of the SPI flash. - * Random access GPT operations refer to the SPI and streaming - * operations refer to NAND. The GPT may therefore point into - * the same offsets as itself. - * These types are distinguished by the following flag and VbDiskInfo - * has separate fields to describe the random-access ("GPT") and - * streaming aspects of the disk. If a disk is random-access (i.e. - * not raw NAND) then these fields are equal. - */ -#define VB_DISK_FLAG_EXTERNAL_GPT (1 << 16) - -/* Information on a single disk */ -typedef struct VbDiskInfo { - /* Disk handle */ - VbExDiskHandle_t handle; - /* Size of a random-access LBA sector in bytes */ - uint64_t bytes_per_lba; - /* Number of random-access LBA sectors on the device. - * If streaming_lba_count is 0, this stands in for the size of the - * randomly accessed portion as well as the streaming portion. - * Otherwise, this is only the randomly-accessed portion. */ - uint64_t lba_count; - /* Number of streaming sectors on the device */ - uint64_t streaming_lba_count; - /* Flags (see VB_DISK_FLAG_* constants) */ - uint32_t flags; - /* - * Optional name string, for use in debugging. May be empty or null if - * not available. - */ - const char *name; -} VbDiskInfo; - /** * Store information into [info] for all disks (storage devices) attached to * the system which match all of the disk_flags. * * On output, count indicates how many disks are present, and [infos_ptr] - * points to a [count]-sized array of VbDiskInfo structs with the information + * points to a [count]-sized array of vb2_disk_info structs with the information * on those disks; this pointer must be freed by calling VbExDiskFreeInfo(). * If count=0, infos_ptr may point to NULL. If [infos_ptr] points to NULL * because count=0 or error, it is not necessary to call VbExDiskFreeInfo(). @@ -201,7 +55,7 @@ typedef struct VbDiskInfo { * The firmware must not alter or free the list pointed to by [infos_ptr] until * VbExDiskFreeInfo() is called. */ -vb2_error_t VbExDiskGetInfo(VbDiskInfo **infos_ptr, uint32_t *count, +vb2_error_t VbExDiskGetInfo(struct vb2_disk_info **infos_ptr, uint32_t *count, uint32_t disk_flags); /** @@ -210,8 +64,8 @@ vb2_error_t VbExDiskGetInfo(VbDiskInfo **infos_ptr, uint32_t *count, * that handle remains valid after this call; all other handles from the info * list need not remain valid after this call. */ -vb2_error_t VbExDiskFreeInfo(VbDiskInfo *infos, - VbExDiskHandle_t preserve_handle); +vb2_error_t VbExDiskFreeInfo(struct vb2_disk_info *infos, + vb2ex_disk_handle_t preserve_handle); /** * Read lba_count LBA sectors, starting at sector lba_start, from the disk, @@ -224,7 +78,7 @@ vb2_error_t VbExDiskFreeInfo(VbDiskInfo *infos, * which as been removed), the function must return error but must not * crash. */ -vb2_error_t VbExDiskRead(VbExDiskHandle_t handle, uint64_t lba_start, +vb2_error_t VbExDiskRead(vb2ex_disk_handle_t handle, uint64_t lba_start, uint64_t lba_count, void *buffer); /** @@ -238,7 +92,7 @@ vb2_error_t VbExDiskRead(VbExDiskHandle_t handle, uint64_t lba_start, * which as been removed), the function must return error but must not * crash. */ -vb2_error_t VbExDiskWrite(VbExDiskHandle_t handle, uint64_t lba_start, +vb2_error_t VbExDiskWrite(vb2ex_disk_handle_t handle, uint64_t lba_start, uint64_t lba_count, const void *buffer); /* Streaming read interface */ @@ -258,7 +112,7 @@ typedef void *VbExStream_t; * device. It is not used to access the GPT. The size of the content addressed * is within streaming_lba_count. */ -vb2_error_t VbExStreamOpen(VbExDiskHandle_t handle, uint64_t lba_start, +vb2_error_t VbExStreamOpen(vb2ex_disk_handle_t handle, uint64_t lba_start, uint64_t lba_count, VbExStream_t *stream_ptr); /** diff --git a/firmware/lib/gpt_misc.c b/firmware/lib/gpt_misc.c index 641ef37b..b42d4660 100644 --- a/firmware/lib/gpt_misc.c +++ b/firmware/lib/gpt_misc.c @@ -19,7 +19,7 @@ * * Returns 0 if successful, 1 if error. */ -int AllocAndReadGptData(VbExDiskHandle_t disk_handle, GptData *gptdata) +int AllocAndReadGptData(vb2ex_disk_handle_t disk_handle, GptData *gptdata) { int primary_valid = 0, secondary_valid = 0; @@ -126,7 +126,7 @@ int AllocAndReadGptData(VbExDiskHandle_t disk_handle, GptData *gptdata) * * Returns 0 if successful, 1 if error. */ -int WriteAndFreeGptData(VbExDiskHandle_t disk_handle, GptData *gptdata) +int WriteAndFreeGptData(vb2ex_disk_handle_t disk_handle, GptData *gptdata) { int skip_primary = 0; GptHeader *header; diff --git a/firmware/lib/include/load_kernel_fw.h b/firmware/lib/include/load_kernel_fw.h deleted file mode 100644 index 76d25560..00000000 --- a/firmware/lib/include/load_kernel_fw.h +++ /dev/null @@ -1,41 +0,0 @@ -/* Copyright (c) 2013 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. - * - * High-level firmware API for loading and verifying kernel. - * (Firmware Portion) - */ - -#ifndef VBOOT_REFERENCE_LOAD_KERNEL_FW_H_ -#define VBOOT_REFERENCE_LOAD_KERNEL_FW_H_ - -#include "vboot_api.h" - -/** - * Attempt to load kernel from the specified device. - * - * @param ctx Vboot context - * @param params Params specific to loading the kernel - * @param disk_info Disk from which to read kernel - * - * Returns VB2_SUCCESS if successful. If unsuccessful, returns an error code. - */ -vb2_error_t LoadKernel(struct vb2_context *ctx, - VbSelectAndLoadKernelParams *params, - VbDiskInfo *disk_info); - -/** - * Attempt to load miniOS kernel from the specified device. - * - * @param ctx Vboot context - * @param params Params specific to loading the kernel - * @param disk_info Disk from which to read kernel - * @param minios_flags Flags for miniOS - * - * Returns VB2_SUCCESS if successful. If unsuccessful, returns an error code. - */ -vb2_error_t LoadMiniOsKernel(struct vb2_context *ctx, - VbSelectAndLoadKernelParams *params, - VbDiskInfo *disk_info, uint32_t minios_flags); - -#endif /* VBOOT_REFERENCE_LOAD_KERNEL_FW_H_ */ diff --git a/firmware/lib/include/vboot_struct.h b/firmware/lib/include/vboot_struct.h index edcb2b45..ea4a9ee9 100644 --- a/firmware/lib/include/vboot_struct.h +++ b/firmware/lib/include/vboot_struct.h @@ -35,9 +35,9 @@ extern "C" { Deprecated as part of chromium:1010389. */ #define VBSD_FWB_TRIED 0x00000001 /* - * LoadKernel() verified the good kernel keyblock using the kernel subkey from - * the firmware. If this flag is not present, it just used the hash of the - * kernel keyblock. + * vb2api_load_kernel() verified the good kernel keyblock using the kernel + * subkey from the firmware. If this flag is not present, it just used the + * hash of the kernel keyblock. */ #define VBSD_KERNEL_KEY_VERIFIED 0x00000002 /* Developer switch was enabled at boot time */ @@ -111,21 +111,8 @@ typedef struct VbSharedDataHeader { /* Firmware lowest version found */ uint32_t fw_version_lowest; - /* Debugging information from LoadKernel() */ - /* Number of times LoadKernel() called */ - uint32_t lk_call_count; /* Reserved for padding */ - uint8_t reserved3[896]; - - /* - * Offset and size of supplemental kernel data. Reserve space for - * these fields now, so that future LoadKernel() versions can store - * information there without needing to shift down whatever data the - * original LoadFirmware() might have put immediately following its - * VbSharedDataHeader. - */ - uint64_t kernel_supplemental_offset; - uint64_t kernel_supplemental_size; + uint8_t reserved3[916]; /* * Fields added in version 2. Before accessing, make sure that diff --git a/firmware/lib/vboot_api_kernel.c b/firmware/lib/vboot_api_kernel.c deleted file mode 100644 index 0fdc2d33..00000000 --- a/firmware/lib/vboot_api_kernel.c +++ /dev/null @@ -1,123 +0,0 @@ -/* Copyright (c) 2013 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. - * - * High-level firmware wrapper API - entry points for kernel selection - */ - -#include "2api.h" -#include "2common.h" -#include "2misc.h" -#include "2nvstorage.h" -#include "2rsa.h" -#include "2secdata.h" -#include "2sysincludes.h" -#include "load_kernel_fw.h" -#include "vboot_api.h" -#include "vboot_struct.h" - -static int is_valid_disk(VbDiskInfo *info, uint32_t disk_flags) -{ - return info->bytes_per_lba >= 512 && - (info->bytes_per_lba & (info->bytes_per_lba - 1)) == 0 && - info->lba_count >= 16 && - (info->flags & disk_flags & VB_DISK_FLAG_SELECT_MASK) && - ((info->flags & VB_DISK_FLAG_SELECT_MASK) & - ((info->flags & VB_DISK_FLAG_SELECT_MASK) - 1)) == 0; -} - -static vb2_error_t VbTryLoadKernelImpl(struct vb2_context *ctx, - uint32_t disk_flags, int minios, - uint32_t minios_flags, - VbSelectAndLoadKernelParams *kparams) -{ - vb2_error_t rv = VB2_ERROR_LK_NO_DISK_FOUND; - VbDiskInfo* disk_info = NULL; - uint32_t disk_count = 0; - uint32_t i; - vb2_error_t new_rv; - - VB2_ASSERT(kparams); - kparams->disk_handle = NULL; - - /* Find disks */ - if (VB2_SUCCESS != VbExDiskGetInfo(&disk_info, &disk_count, disk_flags)) - disk_count = 0; - - /* Loop over disks */ - for (i = 0; i < disk_count; i++) { - VB2_DEBUG("trying disk %d\n", (int)i); - - if (!is_valid_disk(&disk_info[i], disk_flags)) { - VB2_DEBUG(" skipping: bytes_per_lba=%" PRIu64 - " lba_count=%" PRIu64 " flags=%#x\n", - disk_info[i].bytes_per_lba, - disk_info[i].lba_count, - disk_info[i].flags); - continue; - } - kparams->disk_handle = disk_info[i].handle; - - if (minios) { - new_rv = LoadMiniOsKernel(ctx, kparams, - &disk_info[i], minios_flags); - VB2_DEBUG("LoadMiniOsKernel() = %#x\n", new_rv); - } else { - new_rv = LoadKernel(ctx, kparams, &disk_info[i]); - VB2_DEBUG("LoadKernel() = %#x\n", new_rv); - } - - /* Stop now if we found a kernel. */ - if (VB2_SUCCESS == new_rv) { - VbExDiskFreeInfo(disk_info, disk_info[i].handle); - return VB2_SUCCESS; - } - - /* Don't update error if we already have a more specific one. */ - if (VB2_ERROR_LK_INVALID_KERNEL_FOUND != rv) - rv = new_rv; - } - - /* If we drop out of the loop, we didn't find any usable kernel. */ - if (!(ctx->flags & VB2_CONTEXT_RECOVERY_MODE) && - !(ctx->flags & VB2_CONTEXT_DEVELOPER_MODE)) { - switch (rv) { - case VB2_ERROR_LK_INVALID_KERNEL_FOUND: - vb2api_fail(ctx, VB2_RECOVERY_RW_INVALID_OS, rv); - break; - case VB2_ERROR_LK_NO_KERNEL_FOUND: - vb2api_fail(ctx, VB2_RECOVERY_RW_NO_KERNEL, rv); - break; - case VB2_ERROR_LK_NO_DISK_FOUND: - vb2api_fail(ctx, VB2_RECOVERY_RW_NO_DISK, rv); - break; - default: - vb2api_fail(ctx, VB2_RECOVERY_LK_UNSPECIFIED, rv); - break; - } - } - - /* If we didn't find any good kernels, don't return a disk handle. */ - VbExDiskFreeInfo(disk_info, NULL); - - return rv; -} - -test_mockable -vb2_error_t VbTryLoadKernel(struct vb2_context *ctx, uint32_t disk_flags, - VbSelectAndLoadKernelParams *kparams) -{ - ctx->flags &= ~VB2_CONTEXT_DISABLE_TPM; - return VbTryLoadKernelImpl(ctx, disk_flags, 0, 0, kparams); -} - -test_mockable -vb2_error_t VbTryLoadMiniOsKernel(struct vb2_context *ctx, - uint32_t minios_flags, - VbSelectAndLoadKernelParams *kparams) -{ - VB2_TRY(VbTryLoadKernelImpl(ctx, VB_DISK_FLAG_FIXED, 1, minios_flags, - kparams)); - ctx->flags |= VB2_CONTEXT_DISABLE_TPM; - return VB2_SUCCESS; -} diff --git a/firmware/lib/vboot_kernel.c b/firmware/lib/vboot_kernel.c deleted file mode 100644 index 44bcf295..00000000 --- a/firmware/lib/vboot_kernel.c +++ /dev/null @@ -1,758 +0,0 @@ -/* Copyright (c) 2013 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. - * - * Functions for loading a kernel from disk. - * (Firmware portion) - */ - -#include "2api.h" -#include "2common.h" -#include "2misc.h" -#include "2nvstorage.h" -#include "2packed_key.h" -#include "2secdata.h" -#include "2sysincludes.h" -#include "cgptlib.h" -#include "cgptlib_internal.h" -#include "gpt_misc.h" -#include "load_kernel_fw.h" -#include "vboot_api.h" - -enum vb2_load_partition_flags { - VB2_LOAD_PARTITION_FLAG_VBLOCK_ONLY = (1 << 0), - VB2_LOAD_PARTITION_FLAG_MINIOS = (1 << 1), -}; - -#define KBUF_SIZE 65536 /* Bytes to read at start of kernel partition */ - -/* Minimum context work buffer size needed for vb2_load_partition() */ -#define VB2_LOAD_PARTITION_WORKBUF_BYTES \ - (VB2_VERIFY_KERNEL_PREAMBLE_WORKBUF_BYTES + KBUF_SIZE) - -#define LOWEST_TPM_VERSION 0xffffffff - -/** - * Check if a valid keyblock is required. - * - * @param ctx Vboot context - * @return 1 if valid keyblock required (officially signed kernel); - * 0 if valid hash is enough (self-signed kernel). - */ -static int need_valid_keyblock(struct vb2_context *ctx) -{ - /* Normal and recovery modes always require official OS */ - if (ctx->boot_mode != VB2_BOOT_MODE_DEVELOPER) - return 1; - - /* FWMP can require developer mode to use signed kernels */ - if (vb2_secdata_fwmp_get_flag( - ctx, VB2_SECDATA_FWMP_DEV_ENABLE_OFFICIAL_ONLY)) - return 1; - - /* Developers may require signed kernels */ - if (vb2_nv_get(ctx, VB2_NV_DEV_BOOT_SIGNED_ONLY)) - return 1; - - return 0; -} - -/** - * Return a pointer to the keyblock inside a vblock. - * - * Must only be called during or after vb2_verify_kernel_vblock(). - * - * @param kbuf Buffer containing vblock - * @return The keyblock pointer. - */ -static struct vb2_keyblock *get_keyblock(uint8_t *kbuf) -{ - return (struct vb2_keyblock *)kbuf; -} - -/** - * Return a pointer to the kernel preamble inside a vblock. - * - * Must only be called during or after vb2_verify_kernel_vblock(). - * - * @param kbuf Buffer containing vblock - * @return The kernel preamble pointer. - */ -static struct vb2_kernel_preamble *get_preamble(uint8_t *kbuf) -{ - return (struct vb2_kernel_preamble *) - (kbuf + get_keyblock(kbuf)->keyblock_size); -} - -/** - * Return the offset of the kernel body from the start of the vblock. - * - * Must only be called during or after vb2_verify_kernel_vblock(). - * - * @param kbuf Buffer containing vblock - * @return The offset of the kernel body from the vblock start, in bytes. - */ -static uint32_t get_body_offset(uint8_t *kbuf) -{ - return (get_keyblock(kbuf)->keyblock_size + - get_preamble(kbuf)->preamble_size); -} - -/** - * Verify developer mode key hash. - * - * @param ctx Vboot context - * @param keyblock Keyblock to verify - * @return VB2_SUCCESS, or non-zero error code. - */ -static vb2_error_t vb2_verify_kernel_dev_key_hash( - struct vb2_context *ctx, struct vb2_keyblock *keyblock) -{ - struct vb2_packed_key *key = &keyblock->data_key; - uint8_t *buf = ((uint8_t *)key) + key->key_offset; - uint32_t buflen = key->key_size; - uint8_t digest[VB2_SHA256_DIGEST_SIZE]; - - VB2_DEBUG("Checking developer key hash.\n"); - VB2_TRY(vb2_digest_buffer(buf, buflen, VB2_HASH_SHA256, digest, - sizeof(digest))); - - uint8_t *fwmp_dev_key_hash = - vb2_secdata_fwmp_get_dev_key_hash(ctx); - if (fwmp_dev_key_hash == NULL) { - VB2_DEBUG("Couldn't retrieve developer key hash.\n"); - return VB2_ERROR_KERNEL_KEYBLOCK_DEV_KEY_HASH; - } - - if (vb2_safe_memcmp(digest, fwmp_dev_key_hash, - VB2_SHA256_DIGEST_SIZE)) { - int i; - - VB2_DEBUG("Wrong developer key hash.\n"); - VB2_DEBUG("Want: "); - for (i = 0; i < VB2_SHA256_DIGEST_SIZE; i++) - VB2_DEBUG_RAW("%02x ", fwmp_dev_key_hash[i]); - VB2_DEBUG_RAW("\n"); - VB2_DEBUG("Got: "); - for (i = 0; i < VB2_SHA256_DIGEST_SIZE; i++) - VB2_DEBUG_RAW("%02x ", digest[i]); - VB2_DEBUG_RAW("\n"); - - return VB2_ERROR_KERNEL_KEYBLOCK_DEV_KEY_HASH; - } - - return VB2_SUCCESS; -} - -/** - * Verify a kernel vblock. - * - * @param ctx Vboot context - * @param kbuf Buffer containing the vblock - * @param kbuf_size Size of the buffer in bytes - * @param lpflags Flags (one or more of vb2_load_partition_flags) - * @param wb Work buffer. Must be at least - * VB2_VERIFY_KERNEL_PREAMBLE_WORKBUF_BYTES bytes. - * @return VB2_SUCCESS, or non-zero error code. - */ -static vb2_error_t vb2_verify_kernel_vblock( - struct vb2_context *ctx, uint8_t *kbuf, uint32_t kbuf_size, - uint32_t lpflags, struct vb2_workbuf *wb) -{ - struct vb2_shared_data *sd = vb2_get_sd(ctx); - - uint8_t *key_data; - uint32_t key_size; - struct vb2_public_key kernel_key; - - int need_keyblock_valid = need_valid_keyblock(ctx); - int keyblock_valid = 1; /* Assume valid */ - - vb2_error_t rv; - - /* Locate key to verify kernel. This will either be a recovery key, or - a kernel subkey passed from firmware verification. */ - key_data = vb2_member_of(sd, sd->kernel_key_offset); - key_size = sd->kernel_key_size; - VB2_TRY(vb2_unpack_key_buffer(&kernel_key, key_data, key_size)); - - if (vb2_hwcrypto_allowed(ctx)) - kernel_key.allow_hwcrypto = 1; - - /* - * Clear any previous keyblock-valid flag (for example, from a previous - * kernel where the keyblock was signed but the preamble failed - * verification). - */ - sd->flags &= ~VB2_SD_FLAG_KERNEL_SIGNED; - - /* Verify the keyblock. */ - struct vb2_keyblock *keyblock = get_keyblock(kbuf); - rv = vb2_verify_keyblock(keyblock, kbuf_size, &kernel_key, wb); - if (rv) { - VB2_DEBUG("Verifying keyblock signature failed.\n"); - keyblock_valid = 0; - - /* Check if we must have an officially signed kernel */ - if (need_keyblock_valid) { - VB2_DEBUG("Self-signed kernels not enabled.\n"); - return rv; - } - - /* Otherwise, allow the kernel if the keyblock hash is valid */ - rv = vb2_verify_keyblock_hash(keyblock, kbuf_size, wb); - if (rv) { - VB2_DEBUG("Verifying keyblock hash failed.\n"); - return rv; - } - } - - /* Check the keyblock flags against boot flags. */ - if (!(keyblock->keyblock_flags & - ((ctx->flags & VB2_CONTEXT_DEVELOPER_MODE) ? - VB2_KEYBLOCK_FLAG_DEVELOPER_1 : - VB2_KEYBLOCK_FLAG_DEVELOPER_0))) { - VB2_DEBUG("Keyblock developer flag mismatch.\n"); - keyblock_valid = 0; - if (need_keyblock_valid) - return VB2_ERROR_KERNEL_KEYBLOCK_DEV_FLAG; - } - if (!(keyblock->keyblock_flags & - ((ctx->flags & VB2_CONTEXT_RECOVERY_MODE) ? - VB2_KEYBLOCK_FLAG_RECOVERY_1 : - VB2_KEYBLOCK_FLAG_RECOVERY_0))) { - VB2_DEBUG("Keyblock recovery flag mismatch.\n"); - keyblock_valid = 0; - if (need_keyblock_valid) - return VB2_ERROR_KERNEL_KEYBLOCK_REC_FLAG; - } - if (!(keyblock->keyblock_flags & - ((lpflags & VB2_LOAD_PARTITION_FLAG_MINIOS) ? - VB2_KEYBLOCK_FLAG_MINIOS_1 : - VB2_KEYBLOCK_FLAG_MINIOS_0))) { - VB2_DEBUG("Keyblock miniOS flag mismatch.\n"); - keyblock_valid = 0; - if (need_keyblock_valid) - return VB2_ERROR_KERNEL_KEYBLOCK_MINIOS_FLAG; - } - - /* Check for rollback of key version except in recovery mode. */ - uint32_t key_version = keyblock->data_key.key_version; - if (ctx->boot_mode != VB2_BOOT_MODE_MANUAL_RECOVERY) { - if (key_version < (sd->kernel_version_secdata >> 16)) { - keyblock_valid = 0; - if (need_keyblock_valid) { - VB2_DEBUG("Key version too old.\n"); - return VB2_ERROR_KERNEL_KEYBLOCK_VERSION_ROLLBACK; - } - } - if (key_version > VB2_MAX_KEY_VERSION) { - /* - * Key version is stored in 16 bits in the TPM, so key - * versions greater than 0xFFFF can't be stored - * properly. - */ - VB2_DEBUG("Key version > 0xFFFF.\n"); - keyblock_valid = 0; - if (need_keyblock_valid) - return VB2_ERROR_KERNEL_KEYBLOCK_VERSION_RANGE; - } - } - - /* If in developer mode and using key hash, check it. */ - if (ctx->boot_mode == VB2_BOOT_MODE_DEVELOPER && - vb2_secdata_fwmp_get_flag(ctx, VB2_SECDATA_FWMP_DEV_USE_KEY_HASH)) { - VB2_TRY(vb2_verify_kernel_dev_key_hash(ctx, keyblock)); - } - - /* - * At this point, we've checked everything. The kernel keyblock is at - * least self-consistent, and has either a valid signature or a valid - * hash. Track if it had a valid signature (that is, would we have - * been willing to boot it even if developer mode was off). - */ - if (keyblock_valid) - sd->flags |= VB2_SD_FLAG_KERNEL_SIGNED; - - /* Get key for preamble verification from the keyblock. */ - struct vb2_public_key data_key; - rv = vb2_unpack_key(&data_key, &keyblock->data_key); - if (rv) { - VB2_DEBUG("Unable to unpack kernel data key\n"); - return rv; - } - - /* Verify the preamble, which follows the keyblock */ - struct vb2_kernel_preamble *preamble = get_preamble(kbuf); - rv = vb2_verify_kernel_preamble(preamble, - kbuf_size - keyblock->keyblock_size, - &data_key, - wb); - if (rv) { - VB2_DEBUG("Preamble verification failed.\n"); - return rv; - } - - /* Rollback check for miniOS */ - if (need_keyblock_valid && (lpflags & VB2_LOAD_PARTITION_FLAG_MINIOS)) { - if (preamble->kernel_version < - (sd->kernel_version_secdata >> 24)) { - keyblock_valid = 0; - if (need_keyblock_valid) { - VB2_DEBUG("miniOS kernel version too old.\n"); - return VB2_ERROR_KERNEL_PREAMBLE_VERSION_ROLLBACK; - } - } - if (preamble->kernel_version > 0xff) { - /* - * Key version is stored in the top 8 bits of 16 bits - * in the TPM, so key versions greater than 0xFF can't - * be stored properly. - */ - VB2_DEBUG("Key version > 0xFF.\n"); - keyblock_valid = 0; - if (need_keyblock_valid) - return VB2_ERROR_KERNEL_PREAMBLE_VERSION_RANGE; - } - } - - /* - * Kernel preamble version is the lower 16 bits of the composite - * kernel version. - */ - if (preamble->kernel_version > VB2_MAX_PREAMBLE_VERSION) - return VB2_ERROR_KERNEL_PREAMBLE_VERSION_RANGE; - - /* Combine with the key version. */ - sd->kernel_version = key_version << 16 | preamble->kernel_version; - - /* If not in recovery mode, check for rollback of the kernel version. */ - if (need_keyblock_valid && - ctx->boot_mode != VB2_BOOT_MODE_MANUAL_RECOVERY && - sd->kernel_version < sd->kernel_version_secdata) { - VB2_DEBUG("Kernel version too low.\n"); - return VB2_ERROR_KERNEL_PREAMBLE_VERSION_ROLLBACK; - } - - VB2_DEBUG("Kernel preamble is good.\n"); - return VB2_SUCCESS; -} - -/** - * Load and verify a partition from the stream. - * - * @param ctx Vboot context - * @param params Load-kernel parameters - * @param stream Stream to load kernel from - * @param lpflags Flags (one or more of vb2_load_partition_flags) - * @return VB2_SUCCESS, or non-zero error code. - */ -static vb2_error_t vb2_load_partition( - struct vb2_context *ctx, VbSelectAndLoadKernelParams *params, - VbExStream_t stream, uint32_t lpflags) -{ - uint32_t read_ms = 0, start_ts; - struct vb2_workbuf wb; - - vb2_workbuf_from_ctx(ctx, &wb); - - /* Allocate kernel header buffer in workbuf */ - uint8_t *kbuf = vb2_workbuf_alloc(&wb, KBUF_SIZE); - if (!kbuf) - return VB2_ERROR_LOAD_PARTITION_WORKBUF; - - start_ts = vb2ex_mtime(); - if (VbExStreamRead(stream, KBUF_SIZE, kbuf)) { - VB2_DEBUG("Unable to read start of partition.\n"); - return VB2_ERROR_LOAD_PARTITION_READ_VBLOCK; - } - read_ms += vb2ex_mtime() - start_ts; - - if (vb2_verify_kernel_vblock(ctx, kbuf, KBUF_SIZE, lpflags, &wb)) - return VB2_ERROR_LOAD_PARTITION_VERIFY_VBLOCK; - - if (lpflags & VB2_LOAD_PARTITION_FLAG_VBLOCK_ONLY) - return VB2_SUCCESS; - - struct vb2_keyblock *keyblock = get_keyblock(kbuf); - struct vb2_kernel_preamble *preamble = get_preamble(kbuf); - - /* - * Make sure the kernel starts at or before what we already read into - * kbuf. - * - * We could deal with a larger offset by reading and discarding the - * data in between the vblock and the kernel data. - */ - uint32_t body_offset = get_body_offset(kbuf); - if (body_offset > KBUF_SIZE) { - VB2_DEBUG("Kernel body offset is %u > 64KB.\n", body_offset); - return VB2_ERROR_LOAD_PARTITION_BODY_OFFSET; - } - - uint8_t *kernbuf = params->kernel_buffer; - uint32_t kernbuf_size = params->kernel_buffer_size; - if (!kernbuf) { - /* Get kernel load address and size from the header. */ - kernbuf = (uint8_t *)((long)preamble->body_load_address); - kernbuf_size = preamble->body_signature.data_size; - } else if (preamble->body_signature.data_size > kernbuf_size) { - VB2_DEBUG("Kernel body doesn't fit in memory.\n"); - return VB2_ERROR_LOAD_PARTITION_BODY_SIZE; - } - - uint32_t body_toread = preamble->body_signature.data_size; - uint8_t *body_readptr = kernbuf; - - /* - * If we've already read part of the kernel, copy that to the beginning - * of the kernel buffer. - */ - uint32_t body_copied = KBUF_SIZE - body_offset; - if (body_copied > body_toread) - body_copied = body_toread; /* Don't over-copy tiny kernel */ - memcpy(body_readptr, kbuf + body_offset, body_copied); - body_toread -= body_copied; - body_readptr += body_copied; - - /* Read the kernel data */ - start_ts = vb2ex_mtime(); - if (body_toread && VbExStreamRead(stream, body_toread, body_readptr)) { - VB2_DEBUG("Unable to read kernel data.\n"); - return VB2_ERROR_LOAD_PARTITION_READ_BODY; - } - read_ms += vb2ex_mtime() - start_ts; - if (read_ms == 0) /* Avoid division by 0 in speed calculation */ - read_ms = 1; - VB2_DEBUG("read %u KB in %u ms at %u KB/s.\n", - (body_toread + KBUF_SIZE) / 1024, read_ms, - (uint32_t)(((body_toread + KBUF_SIZE) * VB2_MSEC_PER_SEC) / - (read_ms * 1024))); - - /* Get key for preamble/data verification from the keyblock. */ - struct vb2_public_key data_key; - if (vb2_unpack_key(&data_key, &keyblock->data_key)) { - VB2_DEBUG("Unable to unpack kernel data key\n"); - return VB2_ERROR_LOAD_PARTITION_DATA_KEY; - } - - if (vb2_hwcrypto_allowed(ctx)) - data_key.allow_hwcrypto = 1; - - /* Verify kernel data */ - if (vb2_verify_data(kernbuf, kernbuf_size, &preamble->body_signature, - &data_key, &wb)) { - VB2_DEBUG("Kernel data verification failed.\n"); - return VB2_ERROR_LOAD_PARTITION_VERIFY_BODY; - } - - /* If we're still here, the kernel is valid */ - VB2_DEBUG("Partition is good.\n"); - - /* Save kernel data back to parameters */ - params->bootloader_address = preamble->bootloader_address; - params->bootloader_size = preamble->bootloader_size; - params->flags = vb2_kernel_get_flags(preamble); - if (!params->kernel_buffer) { - params->kernel_buffer = kernbuf; - params->kernel_buffer_size = kernbuf_size; - } - - return VB2_SUCCESS; -} - -static vb2_error_t try_minios_kernel(struct vb2_context *ctx, - VbSelectAndLoadKernelParams *params, - VbDiskInfo *disk_info, - uint64_t sector) { - VbExStream_t stream; - uint64_t sectors_left = disk_info->lba_count - sector; - const uint32_t lpflags = VB2_LOAD_PARTITION_FLAG_MINIOS; - vb2_error_t rv = VB2_ERROR_LK_NO_KERNEL_FOUND; - - /* Re-open stream at correct offset to pass to vb2_load_partition. */ - if (VbExStreamOpen(params->disk_handle, sector, sectors_left, - &stream)) { - VB2_DEBUG("Unable to open disk handle.\n"); - return rv; - } - - rv = vb2_load_partition(ctx, params, stream, lpflags); - VB2_DEBUG("vb2_load_partition returned: %d\n", rv); - - VbExStreamClose(stream); - - if (rv) - return VB2_ERROR_LK_NO_KERNEL_FOUND; - return rv; -} - -static vb2_error_t try_minios_sectors(struct vb2_context *ctx, - VbSelectAndLoadKernelParams *params, - VbDiskInfo *disk_info, - uint64_t start, uint64_t count) -{ - const uint32_t buf_size = count * disk_info->bytes_per_lba; - char *buf; - VbExStream_t stream; - uint64_t isector; - vb2_error_t rv = VB2_ERROR_LK_NO_KERNEL_FOUND; - - buf = malloc(buf_size); - if (buf == NULL) { - VB2_DEBUG("Unable to allocate disk read buffer.\n"); - return rv; - } - - if (VbExStreamOpen(params->disk_handle, start, count, &stream)) { - VB2_DEBUG("Unable to open disk handle.\n"); - free(buf); - return rv; - } - if (VbExStreamRead(stream, buf_size, buf)) { - VB2_DEBUG("Unable to read disk.\n"); - free(buf); - VbExStreamClose(stream); - return rv; - } - VbExStreamClose(stream); - - for (isector = 0; isector < count; isector++) { - if (memcmp(buf + isector * disk_info->bytes_per_lba, - VB2_KEYBLOCK_MAGIC, VB2_KEYBLOCK_MAGIC_SIZE)) - continue; - VB2_DEBUG("Match on sector %" PRIu64 " / %" PRIu64 "\n", - start + isector, - disk_info->lba_count - 1); - rv = try_minios_kernel(ctx, params, disk_info, start + isector); - if (rv == VB2_SUCCESS) - break; - } - - free(buf); - return rv; -} - -static vb2_error_t try_minios_sector_region(struct vb2_context *ctx, - VbSelectAndLoadKernelParams *params, - VbDiskInfo *disk_info, - int end_region) -{ - const uint64_t disk_count_half = (disk_info->lba_count + 1) / 2; - const uint64_t check_count_256 = 256 * 1024 - * 1024 / disk_info->bytes_per_lba; // 256 MB - const uint64_t batch_count_1 = 1024 - * 1024 / disk_info->bytes_per_lba; // 1 MB - const uint64_t check_count = VB2_MIN(disk_count_half, check_count_256); - const uint64_t batch_count = VB2_MIN(disk_count_half, batch_count_1); - uint64_t sector; - uint64_t start; - uint64_t end; - const char *region_name; - vb2_error_t rv = VB2_ERROR_LK_NO_KERNEL_FOUND; - - if (!end_region) { - start = 0; - end = check_count; - region_name = "start"; - } else { - start = disk_info->lba_count - check_count; - end = disk_info->lba_count; - region_name = "end"; - } - - VB2_DEBUG("Checking %s of disk for kernels...\n", region_name); - for (sector = start; sector < end; sector += batch_count) { - rv = try_minios_sectors(ctx, params, disk_info, sector, - batch_count); - if (rv == VB2_SUCCESS) - return rv; - } - - return rv; -} - -/* - * Search for kernels by sector, rather than by partition. Only sectors near - * the start and end of disks are considered, and the kernel must start exactly - * at the first byte of the sector. - */ -vb2_error_t LoadMiniOsKernel(struct vb2_context *ctx, - VbSelectAndLoadKernelParams *params, - VbDiskInfo *disk_info, uint32_t minios_flags) -{ - vb2_error_t rv; - int end_region_first = vb2_nv_get(ctx, VB2_NV_MINIOS_PRIORITY); - - if (minios_flags & VB_MINIOS_FLAG_NON_ACTIVE) - rv = VB2_ERROR_UNKNOWN; /* Ignore active partition */ - else - rv = try_minios_sector_region(ctx, params, disk_info, - end_region_first); - - if (rv) - rv = try_minios_sector_region(ctx, params, disk_info, - !end_region_first); - - return rv; -} - -vb2_error_t LoadKernel(struct vb2_context *ctx, - VbSelectAndLoadKernelParams *params, - VbDiskInfo *disk_info) -{ - struct vb2_shared_data *sd = vb2_get_sd(ctx); - int found_partitions = 0; - uint32_t lowest_version = LOWEST_TPM_VERSION; - vb2_error_t rv; - - /* Clear output params */ - params->partition_number = 0; - - /* Read GPT data */ - GptData gpt; - gpt.sector_bytes = (uint32_t)disk_info->bytes_per_lba; - gpt.streaming_drive_sectors = disk_info->streaming_lba_count - ?: disk_info->lba_count; - gpt.gpt_drive_sectors = disk_info->lba_count; - gpt.flags = disk_info->flags & VB_DISK_FLAG_EXTERNAL_GPT - ? GPT_FLAG_EXTERNAL : 0; - if (AllocAndReadGptData(disk_info->handle, &gpt)) { - VB2_DEBUG("Unable to read GPT data\n"); - goto gpt_done; - } - - /* Initialize GPT library */ - if (GptInit(&gpt)) { - VB2_DEBUG("Error parsing GPT\n"); - goto gpt_done; - } - - /* Loop over candidate kernel partitions */ - uint64_t part_start, part_size; - while (GptNextKernelEntry(&gpt, &part_start, &part_size) == - GPT_SUCCESS) { - - VB2_DEBUG("Found kernel entry at %" - PRIu64 " size %" PRIu64 "\n", - part_start, part_size); - - /* Found at least one kernel partition. */ - found_partitions++; - - /* Set up the stream */ - VbExStream_t stream = NULL; - if (VbExStreamOpen(disk_info->handle, - part_start, part_size, &stream)) { - VB2_DEBUG("Partition error getting stream.\n"); - VB2_DEBUG("Marking kernel as invalid.\n"); - GptUpdateKernelEntry(&gpt, GPT_UPDATE_ENTRY_BAD); - continue; - } - - uint32_t lpflags = 0; - if (params->partition_number > 0) { - /* - * If we already have a good kernel, we only needed to - * look at the vblock versions to check for rollback. - */ - lpflags |= VB2_LOAD_PARTITION_FLAG_VBLOCK_ONLY; - } - - rv = vb2_load_partition(ctx, params, stream, lpflags); - VbExStreamClose(stream); - - if (rv) { - VB2_DEBUG("Marking kernel as invalid (err=%x).\n", rv); - GptUpdateKernelEntry(&gpt, GPT_UPDATE_ENTRY_BAD); - continue; - } - - int keyblock_valid = sd->flags & VB2_SD_FLAG_KERNEL_SIGNED; - /* Track lowest version from a valid header. */ - if (keyblock_valid && lowest_version > sd->kernel_version) { - lowest_version = sd->kernel_version; - } - VB2_DEBUG("Keyblock valid: %d\n", keyblock_valid); - VB2_DEBUG("Combined version: %u\n", sd->kernel_version); - - /* - * If we're only looking at headers, we're done with this - * partition. - */ - if (lpflags & VB2_LOAD_PARTITION_FLAG_VBLOCK_ONLY) - continue; - - /* - * Otherwise, we found a partition we like. - * - * TODO: GPT partitions start at 1, but cgptlib starts them at - * 0. Adjust here, until cgptlib is fixed. - */ - params->partition_number = gpt.current_kernel + 1; - - /* - * TODO: GetCurrentKernelUniqueGuid() should take a destination - * size, or the dest should be a struct, so we know it's big - * enough. - */ - GetCurrentKernelUniqueGuid(&gpt, ¶ms->partition_guid); - - /* Update GPT to note this is the kernel we're trying. - * But not when we assume that the boot process may - * not complete for valid reasons (eg. early shutdown). - */ - if (!(ctx->flags & VB2_CONTEXT_NOFAIL_BOOT)) - GptUpdateKernelEntry(&gpt, GPT_UPDATE_ENTRY_TRY); - - /* - * If we're in recovery mode or we're about to boot a - * non-officially-signed kernel, there's no rollback - * protection, so we can stop at the first valid kernel. - */ - if (ctx->boot_mode == VB2_BOOT_MODE_MANUAL_RECOVERY || - !keyblock_valid) { - VB2_DEBUG("In recovery mode or dev-signed kernel\n"); - break; - } - - /* - * Otherwise, we do care about the key index in the TPM. If - * the good partition's key version is the same as the tpm, - * then the TPM doesn't need updating; we can stop now. - * Otherwise, we'll check all the other headers to see if they - * contain a newer key. - */ - if (sd->kernel_version == sd->kernel_version_secdata) { - VB2_DEBUG("Same kernel version\n"); - break; - } - } /* while(GptNextKernelEntry) */ - - gpt_done: - /* Write and free GPT data */ - WriteAndFreeGptData(disk_info->handle, &gpt); - - /* Handle finding a good partition */ - if (params->partition_number > 0) { - VB2_DEBUG("Good partition %d\n", params->partition_number); - /* - * Validity check - only store a new TPM version if we found - * one. If lowest_version is still at its initial value, we - * didn't find one; for example, we're in developer mode and - * just didn't look. - */ - if (lowest_version != LOWEST_TPM_VERSION && - lowest_version > sd->kernel_version_secdata) - sd->kernel_version = lowest_version; - - /* Success! */ - rv = VB2_SUCCESS; - } else if (found_partitions > 0) { - rv = VB2_ERROR_LK_INVALID_KERNEL_FOUND; - } else { - rv = VB2_ERROR_LK_NO_KERNEL_FOUND; - } - - return rv; -} diff --git a/firmware/stub/vboot_api_stub_disk.c b/firmware/stub/vboot_api_stub_disk.c index 3244dc65..dd24c21d 100644 --- a/firmware/stub/vboot_api_stub_disk.c +++ b/firmware/stub/vboot_api_stub_disk.c @@ -17,7 +17,7 @@ __attribute__((weak)) -vb2_error_t VbExDiskGetInfo(VbDiskInfo** infos_ptr, uint32_t* count, +vb2_error_t VbExDiskGetInfo(struct vb2_disk_info **infos_ptr, uint32_t *count, uint32_t disk_flags) { *infos_ptr = NULL; @@ -27,15 +27,15 @@ vb2_error_t VbExDiskGetInfo(VbDiskInfo** infos_ptr, uint32_t* count, __attribute__((weak)) -vb2_error_t VbExDiskFreeInfo(VbDiskInfo* infos_ptr, - VbExDiskHandle_t preserve_handle) +vb2_error_t VbExDiskFreeInfo(struct vb2_disk_info *infos_ptr, + vb2ex_disk_handle_t preserve_handle) { return VB2_SUCCESS; } __attribute__((weak)) -vb2_error_t VbExDiskRead(VbExDiskHandle_t handle, uint64_t lba_start, +vb2_error_t VbExDiskRead(vb2ex_disk_handle_t handle, uint64_t lba_start, uint64_t lba_count, void* buffer) { return VB2_SUCCESS; @@ -43,7 +43,7 @@ vb2_error_t VbExDiskRead(VbExDiskHandle_t handle, uint64_t lba_start, __attribute__((weak)) -vb2_error_t VbExDiskWrite(VbExDiskHandle_t handle, uint64_t lba_start, +vb2_error_t VbExDiskWrite(vb2ex_disk_handle_t handle, uint64_t lba_start, uint64_t lba_count, const void* buffer) { return VB2_SUCCESS; diff --git a/firmware/stub/vboot_api_stub_stream.c b/firmware/stub/vboot_api_stub_stream.c index 68e7ec7d..1a6a65cd 100644 --- a/firmware/stub/vboot_api_stub_stream.c +++ b/firmware/stub/vboot_api_stub_stream.c @@ -16,7 +16,7 @@ /* Internal struct to simulate a stream for sector-based disks */ struct disk_stream { /* Disk handle */ - VbExDiskHandle_t handle; + vb2ex_disk_handle_t handle; /* Next sector to read */ uint64_t sector; @@ -26,7 +26,7 @@ struct disk_stream { }; __attribute__((weak)) -vb2_error_t VbExStreamOpen(VbExDiskHandle_t handle, uint64_t lba_start, +vb2_error_t VbExStreamOpen(vb2ex_disk_handle_t handle, uint64_t lba_start, uint64_t lba_count, VbExStream_t *stream) { struct disk_stream *s; diff --git a/host/lib/crossystem.c b/host/lib/crossystem.c index a5427c2d..6d553357 100644 --- a/host/lib/crossystem.c +++ b/host/lib/crossystem.c @@ -38,7 +38,8 @@ typedef enum VdatStringField { VDAT_STRING_DEPRECATED_TIMERS = 0, /* Timer values */ VDAT_STRING_LOAD_FIRMWARE_DEBUG, /* LoadFirmware() debug info */ - VDAT_STRING_DEPRECATED_LOAD_KERNEL_DEBUG, /* LoadKernel() debug info */ + VDAT_STRING_DEPRECATED_LOAD_KERNEL_DEBUG, /* vb2api_load_kernel() + debug info */ VDAT_STRING_MAINFW_ACT /* Active main firmware */ } VdatStringField; diff --git a/tests/cgpt_fuzzer.c b/tests/cgpt_fuzzer.c index d440ee82..6762bc38 100644 --- a/tests/cgpt_fuzzer.c +++ b/tests/cgpt_fuzzer.c @@ -25,7 +25,7 @@ struct GptDataParams { static struct MockDisk mock_disk; -vb2_error_t VbExDiskRead(VbExDiskHandle_t h, uint64_t lba_start, +vb2_error_t VbExDiskRead(vb2ex_disk_handle_t h, uint64_t lba_start, uint64_t lba_count, void *buffer) { size_t lba_size = mock_disk.size >> mock_disk.sector_shift; diff --git a/tests/gpt_misc_tests.c b/tests/gpt_misc_tests.c index 30632991..e7ffccf7 100644 --- a/tests/gpt_misc_tests.c +++ b/tests/gpt_misc_tests.c @@ -29,7 +29,7 @@ static char call_log[4096]; static int disk_read_to_fail; static int disk_write_to_fail; -static VbExDiskHandle_t handle; +static vb2ex_disk_handle_t handle; static uint8_t mock_disk[MOCK_SECTOR_SIZE * MOCK_SECTOR_COUNT]; static GptHeader *mock_gpt_primary = (GptHeader*)&mock_disk[MOCK_SECTOR_SIZE * 1]; @@ -91,7 +91,7 @@ static void ResetMocks(void) /* Mocks */ -vb2_error_t VbExDiskRead(VbExDiskHandle_t h, uint64_t lba_start, +vb2_error_t VbExDiskRead(vb2ex_disk_handle_t h, uint64_t lba_start, uint64_t lba_count, void *buffer) { LOGCALL("VbExDiskRead(h, %d, %d)\n", (int)lba_start, (int)lba_count); @@ -105,7 +105,7 @@ vb2_error_t VbExDiskRead(VbExDiskHandle_t h, uint64_t lba_start, return VB2_SUCCESS; } -vb2_error_t VbExDiskWrite(VbExDiskHandle_t h, uint64_t lba_start, +vb2_error_t VbExDiskWrite(vb2ex_disk_handle_t h, uint64_t lba_start, uint64_t lba_count, const void *buffer) { LOGCALL("VbExDiskWrite(h, %d, %d)\n", (int)lba_start, (int)lba_count); @@ -207,7 +207,7 @@ static void ReadWriteGptTest(void) * Invalidate primary GPT header and check that it is * repaired by GptRepair(). * - * This would normally be called by LoadKernel()->GptInit() + * This would normally be called by vb2api_load_kernel()->GptInit() * but this callback is mocked in these tests. */ ResetMocks(); @@ -232,7 +232,7 @@ static void ReadWriteGptTest(void) * Invalidate secondary GPT header and check that it can be * repaired by GptRepair(). * - * This would normally be called by LoadKernel()->GptInit() + * This would normally be called by vb2api_load_kernel()->GptInit() * but this callback is mocked in these tests. */ ResetMocks(); diff --git a/tests/vb2_auxfw_sync_tests.c b/tests/vb2_auxfw_sync_tests.c index bb236be8..b9c869ab 100644 --- a/tests/vb2_auxfw_sync_tests.c +++ b/tests/vb2_auxfw_sync_tests.c @@ -15,7 +15,6 @@ #include "2sysincludes.h" #include "common/tests.h" #include "host_common.h" -#include "load_kernel_fw.h" #include "vboot_struct.h" /* Mock data */ diff --git a/tests/vb2_ec_sync_tests.c b/tests/vb2_ec_sync_tests.c index 4afc5ef0..f317561a 100644 --- a/tests/vb2_ec_sync_tests.c +++ b/tests/vb2_ec_sync_tests.c @@ -12,7 +12,6 @@ #include "2sysincludes.h" #include "common/tests.h" #include "host_common.h" -#include "load_kernel_fw.h" #include "vboot_struct.h" /* Mock data */ diff --git a/tests/vb2_kernel2_tests.c b/tests/vb2_kernel2_tests.c deleted file mode 100644 index 04defe50..00000000 --- a/tests/vb2_kernel2_tests.c +++ /dev/null @@ -1,146 +0,0 @@ -/* Copyright 2022 The ChromiumOS Authors. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - * - * Tests for vb2api_normal_boot. - */ - -#include "2api.h" -#include "2common.h" -#include "2misc.h" -#include "2nvstorage.h" -#include "2secdata.h" -#include "2sysincludes.h" -#include "common/boot_mode.h" -#include "common/tests.h" -#include "host_common.h" -#include "load_kernel_fw.h" -#include "tlcl.h" -#include "tss_constants.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 uint32_t kernel_version; -static uint32_t new_version; -static VbSelectAndLoadKernelParams kparams; - -/* Mocked function data */ -static struct vb2_gbb_header mock_gbb; -static int mock_vbtlk_expect_fixed; -static int mock_vbtlk_expect_removable; -static vb2_error_t mock_vbtlk_retval; - -static void reset_common_data(void) -{ - memset(&kparams, 0, sizeof(kparams)); - - memset(&mock_gbb, 0, sizeof(mock_gbb)); - mock_gbb.major_version = VB2_GBB_MAJOR_VER; - mock_gbb.minor_version = VB2_GBB_MINOR_VER; - mock_gbb.flags = 0; - - mock_vbtlk_expect_fixed = 1; - mock_vbtlk_expect_removable = 0; - mock_vbtlk_retval = VB2_SUCCESS; - - TEST_SUCC(vb2api_init(workbuf, sizeof(workbuf), &ctx), - "vb2api_init failed"); - - SET_BOOT_MODE(ctx, VB2_BOOT_MODE_NORMAL); - - sd = vb2_get_sd(ctx); - - vb2_nv_init(ctx); - vb2_nv_set(ctx, VB2_NV_KERNEL_MAX_ROLLFORWARD, 0xffffffff); - - kernel_version = new_version = 0x10002; - - sd->kernel_version_secdata = kernel_version; - sd->kernel_version = kernel_version; -} - -/* Mock functions */ - -struct vb2_gbb_header *vb2_get_gbb(struct vb2_context *c) -{ - return &mock_gbb; -} - -void vb2_secdata_kernel_set(struct vb2_context *c, - enum vb2_secdata_kernel_param param, - uint32_t value) -{ - kernel_version = value; -} - -vb2_error_t VbTryLoadKernel(struct vb2_context *c, uint32_t disk_flags, - VbSelectAndLoadKernelParams *kpa) -{ - /* - * TODO: Currently we don't have a good way of testing for an ordered - * sequence of VB_DISK_FLAG_FIXED and then VB_DISK_FLAG_REMOVABLE. If - * both are set, then just assume success. - */ - if (mock_vbtlk_expect_fixed && mock_vbtlk_expect_removable) - return mock_vbtlk_retval; - - TEST_EQ(!!mock_vbtlk_expect_fixed, - !!(disk_flags & VB_DISK_FLAG_FIXED), - " unexpected fixed disk call"); - TEST_EQ(!!mock_vbtlk_expect_removable, - !!(disk_flags & VB_DISK_FLAG_REMOVABLE), - " unexpected removable disk call"); - - sd->kernel_version = new_version; - - return mock_vbtlk_retval; -} - -/* Tests */ - -static void normal_boot_tests(void) -{ - reset_common_data(); - TEST_SUCC(vb2api_normal_boot(ctx, &kparams), - "vb2api_normal_boot() returns VB2_SUCCESS"); - - reset_common_data(); - mock_vbtlk_retval = VB2_ERROR_MOCK; - TEST_EQ(vb2api_normal_boot(ctx, &kparams), VB2_ERROR_MOCK, - "vb2api_normal_boot() returns VB2_ERROR_MOCK"); - - reset_common_data(); - new_version = 0x20003; - TEST_SUCC(vb2api_normal_boot(ctx, &kparams), "Roll forward"); - TEST_EQ(kernel_version, 0x20003, " version"); - - reset_common_data(); - vb2_nv_set(ctx, VB2_NV_FW_RESULT, VB2_FW_RESULT_TRYING); - new_version = 0x20003; - TEST_SUCC(vb2api_normal_boot(ctx, &kparams), - "Don't roll forward kernel when trying new FW"); - TEST_EQ(kernel_version, 0x10002, " version"); - - reset_common_data(); - vb2_nv_set(ctx, VB2_NV_KERNEL_MAX_ROLLFORWARD, 0x30005); - new_version = 0x40006; - TEST_SUCC(vb2api_normal_boot(ctx, &kparams), "Limit max roll forward"); - TEST_EQ(kernel_version, 0x30005, " version"); - - reset_common_data(); - vb2_nv_set(ctx, VB2_NV_KERNEL_MAX_ROLLFORWARD, 0x10001); - new_version = 0x40006; - TEST_SUCC(vb2api_normal_boot(ctx, &kparams), - "Max roll forward can't rollback"); - TEST_EQ(kernel_version, 0x10002, " version"); -} - -int main(void) -{ - normal_boot_tests(); - return gTestSuccess ? 0 : 255; -} diff --git a/tests/vb2_kernel_tests.c b/tests/vb2_kernel_tests.c index ca945e95..6d32eb2d 100644 --- a/tests/vb2_kernel_tests.c +++ b/tests/vb2_kernel_tests.c @@ -14,8 +14,6 @@ #include "2sysincludes.h" #include "common/boot_mode.h" #include "common/tests.h" -#include "vboot_struct.h" -#include "vboot_api.h" /* Common context for tests */ static uint8_t workbuf[VB2_KERNEL_WORKBUF_RECOMMENDED_SIZE] @@ -24,7 +22,7 @@ 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"; -static VbSelectAndLoadKernelParams kparams; +static struct vb2_kernel_params kparams; /* Mocked function data */ @@ -40,6 +38,9 @@ static int mock_commit_data_called; static int mock_ec_sync_called; static int mock_ec_sync_retval; static int mock_battery_cutoff_called; +static int mock_kernel_flag; +static int mock_kernel_flag_set; +static int mock_kernel_version; /* Type of test to reset for */ enum reset_type { @@ -62,16 +63,15 @@ static void reset_common_data(enum reset_type t) 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; mock_commit_data_called = 0; mock_ec_sync_called = 0; mock_ec_sync_retval = VB2_SUCCESS; mock_battery_cutoff_called = 0; + mock_kernel_flag = 0; + mock_kernel_flag_set = 0; + mock_kernel_version = 0x10002; /* Recovery key in mock GBB */ memset(&mock_gbb, 0, sizeof(mock_gbb)); @@ -86,6 +86,8 @@ static void reset_common_data(enum reset_type t) mock_gbb.h.recovery_key_size = mock_gbb.recovery_key.key_offset + mock_gbb.recovery_key.key_size; + mock_gbb.h.major_version = VB2_GBB_MAJOR_VER; + mock_gbb.h.minor_version = VB2_GBB_MINOR_VER; if (t == FOR_PHASE1) { uint8_t *kdata; @@ -103,6 +105,11 @@ static void reset_common_data(enum reset_type t) sd->preamble_size = sizeof(*fwpre) + k->key_size; vb2_set_workbuf_used(ctx, sd->preamble_offset + sd->preamble_size); + } else if (t == FOR_FINALIZE) { + SET_BOOT_MODE(ctx, VB2_BOOT_MODE_NORMAL); + vb2_nv_set(ctx, VB2_NV_KERNEL_MAX_ROLLFORWARD, 0xffffffff); + sd->kernel_version_secdata = mock_kernel_version; + sd->kernel_version = mock_kernel_version; } }; @@ -178,6 +185,37 @@ vb2_error_t vb2ex_commit_data(struct vb2_context *c) return VB2_SUCCESS; } +void vb2_secdata_kernel_set(struct vb2_context *c, + enum vb2_secdata_kernel_param param, + uint32_t value) +{ + switch (param) { + case VB2_SECDATA_KERNEL_FLAGS: + mock_kernel_flag = value; + mock_kernel_flag_set = 1; + break; + case VB2_SECDATA_KERNEL_VERSIONS: + mock_kernel_version = value; + break; + default: + vb2ex_abort(); + } +} + +uint32_t vb2_secdata_kernel_get(struct vb2_context *c, + enum vb2_secdata_kernel_param param) +{ + switch (param) { + case VB2_SECDATA_KERNEL_FLAGS: + return mock_kernel_flag; + case VB2_SECDATA_KERNEL_VERSIONS: + return mock_kernel_version; + default: + vb2ex_abort(); + } + return 0; +} + /* Tests */ static void phase1_tests(void) @@ -204,7 +242,7 @@ static void phase1_tests(void) 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, + TEST_EQ(sd->kernel_version_secdata, 0x10002, " secdata_kernel version"); /* Test successful call in recovery mode */ @@ -230,7 +268,7 @@ static void phase1_tests(void) 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, + TEST_EQ(sd->kernel_version_secdata, 0x10002, " secdata_kernel version"); /* Test flags for experimental features in non-recovery path */ @@ -249,22 +287,10 @@ static void phase1_tests(void) /* * Test flags are unchanged for experimental features in recovery path */ - - /* Set 8 bits to 0 */ reset_common_data(FOR_PHASE1); SET_BOOT_MODE(ctx, VB2_BOOT_MODE_BROKEN_SCREEN, 123); - vb2_secdata_kernel_set(ctx, VB2_SECDATA_KERNEL_FLAGS, 0); TEST_SUCC(vb2api_kernel_phase1(ctx), "phase1 rec good"); - TEST_EQ(vb2_secdata_kernel_get(ctx, VB2_SECDATA_KERNEL_FLAGS), 0, - "VB2_SECDATA_KERNEL_FLAGS remains unchanged in recovery path"); - - /* Set 8 bits to 1 */ - reset_common_data(FOR_PHASE1); - SET_BOOT_MODE(ctx, VB2_BOOT_MODE_BROKEN_SCREEN, 123); - vb2_secdata_kernel_set(ctx, VB2_SECDATA_KERNEL_FLAGS, UINT8_MAX); - TEST_SUCC(vb2api_kernel_phase1(ctx), "phase1 rec good"); - TEST_EQ(vb2_secdata_kernel_get(ctx, VB2_SECDATA_KERNEL_FLAGS), - UINT8_MAX, + TEST_EQ(mock_kernel_flag_set, 0, "VB2_SECDATA_KERNEL_FLAGS remains unchanged in recovery path"); /* Bad secdata_fwmp causes failure in normal mode only */ @@ -404,6 +430,34 @@ static void phase2_tests(void) static void finalize_tests(void) { + /* Kernel version roll forward */ + reset_common_data(FOR_FINALIZE); + sd->kernel_version = 0x20003; + TEST_EQ(vb2api_kernel_finalize(ctx), VB2_SUCCESS, + "Kernel version roll forward"); + TEST_EQ(mock_kernel_version, 0x20003, " kernel version"); + + reset_common_data(FOR_FINALIZE); + vb2_nv_set(ctx, VB2_NV_FW_RESULT, VB2_FW_RESULT_TRYING); + sd->kernel_version = 0x20003; + TEST_EQ(vb2api_kernel_finalize(ctx), VB2_SUCCESS, + "Don't roll forward kernel when trying new FW"); + TEST_EQ(mock_kernel_version, 0x10002, " kernel version"); + + reset_common_data(FOR_FINALIZE); + vb2_nv_set(ctx, VB2_NV_KERNEL_MAX_ROLLFORWARD, 0x30005); + sd->kernel_version = 0x40006; + TEST_EQ(vb2api_kernel_finalize(ctx), VB2_SUCCESS, + "Limit max roll forward"); + TEST_EQ(mock_kernel_version, 0x30005, " kernel version"); + + reset_common_data(FOR_FINALIZE); + vb2_nv_set(ctx, VB2_NV_KERNEL_MAX_ROLLFORWARD, 0x10001); + sd->kernel_version = 0x40006; + TEST_EQ(vb2api_kernel_finalize(ctx), VB2_SUCCESS, + "Max roll forward can't rollback"); + TEST_EQ(mock_kernel_version, 0x10002, " kernel version"); + /* NO_BOOT with EC sync support */ reset_common_data(FOR_FINALIZE); ctx->flags |= VB2_CONTEXT_NO_BOOT; diff --git a/tests/vb2_load_kernel2_tests.c b/tests/vb2_load_kernel2_tests.c new file mode 100644 index 00000000..dfb06fbf --- /dev/null +++ b/tests/vb2_load_kernel2_tests.c @@ -0,0 +1,467 @@ +/* Copyright 2021 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 miniOS kernel selection, loading, verification, and booting. + */ + +#include "2api.h" +#include "2common.h" +#include "2misc.h" +#include "2nvstorage.h" +#include "2secdata.h" +#include "common/boot_mode.h" +#include "common/tests.h" +#include "vboot_api.h" + +#define MAX_MOCK_KERNELS 10 +#define KBUF_SIZE 65536 + +/* Internal struct to simulate a stream for sector-based disks */ +struct disk_stream { + /* Disk handle */ + vb2ex_disk_handle_t handle; + + /* Next sector to read */ + uint64_t sector; + + /* Number of sectors left */ + uint64_t sectors_left; +}; + +/* Represent a "kernel" located on the disk */ +struct mock_kernel { + /* Sector where the kernel begins */ + uint64_t sector; + + /* Return value from vb2_load_partition */ + vb2_error_t rv; + + /* Number of times the sector was read */ + int read_count; +}; + +/* Mock data */ +static struct vb2_context *ctx; +static struct vb2_shared_data *sd; +static struct vb2_workbuf wb; +static uint8_t workbuf[VB2_KERNEL_WORKBUF_RECOMMENDED_SIZE] + __attribute__((aligned(VB2_WORKBUF_ALIGN))); + +static struct vb2_kernel_params lkp; +static struct vb2_disk_info disk_info; +static struct vb2_keyblock kbh; +static struct vb2_kernel_preamble kph; +static uint8_t kernel_buffer[80000]; + +static struct mock_kernel kernels[MAX_MOCK_KERNELS]; +static int kernel_count; +static struct mock_kernel *cur_kernel; + +static void add_mock_kernel(uint64_t sector, vb2_error_t rv) +{ + if (kernel_count >= ARRAY_SIZE(kernels)) { + TEST_TRUE(0, " kernel_count ran out of entries!"); + return; + } + + kernels[kernel_count].sector = sector; + kernels[kernel_count].rv = rv; + kernel_count++; +} + +static void reset_common_data(void) +{ + TEST_SUCC(vb2api_init(workbuf, sizeof(workbuf), &ctx), + "vb2api_init failed"); + vb2_workbuf_from_ctx(ctx, &wb); + vb2_nv_init(ctx); + vb2api_secdata_kernel_create(ctx); + vb2_secdata_kernel_init(ctx); + ctx->flags = VB2_CONTEXT_RECOVERY_MODE; + + SET_BOOT_MODE(ctx, VB2_BOOT_MODE_MANUAL_RECOVERY, + VB2_RECOVERY_RO_MANUAL); + + sd = vb2_get_sd(ctx); + sd->kernel_version_secdata = 0xabcdef | (1 << 24); + + memset(&lkp, 0, sizeof(lkp)); + lkp.kernel_buffer = kernel_buffer; + lkp.kernel_buffer_size = sizeof(kernel_buffer); + + memset(&disk_info, 0, sizeof(disk_info)); + disk_info.bytes_per_lba = 512; + disk_info.lba_count = 1024; + disk_info.handle = (vb2ex_disk_handle_t)1; + + memset(&kbh, 0, sizeof(kbh)); + kbh.data_key.key_version = 2; + kbh.keyblock_flags = VB2_KEYBLOCK_FLAG_DEVELOPER_0 + | VB2_KEYBLOCK_FLAG_DEVELOPER_1 + | VB2_KEYBLOCK_FLAG_RECOVERY_1 + | VB2_KEYBLOCK_FLAG_MINIOS_1; + kbh.keyblock_size = sizeof(kbh); + + memset(&kph, 0, sizeof(kph)); + kph.kernel_version = 1; + kph.preamble_size = 4096 - kbh.keyblock_size; + kph.body_signature.data_size = 0; + kph.bootloader_address = 0xbeadd008; + kph.bootloader_size = 0x1234; + + + memset(&kernels, 0, sizeof(kernels)); + kernel_count = 0; + cur_kernel = NULL; +} + +/* Mocks */ + +vb2_error_t VbExStreamOpen(vb2ex_disk_handle_t handle, uint64_t lba_start, + uint64_t lba_count, VbExStream_t *stream) +{ + struct disk_stream *s; + uint64_t i; + + if (!handle) { + *stream = NULL; + return VB2_ERROR_UNKNOWN; + } + + if (lba_start + lba_count > disk_info.lba_count) + return VB2_ERROR_UNKNOWN; + + s = malloc(sizeof(*s)); + s->handle = handle; + s->sector = lba_start; + s->sectors_left = lba_count; + + *stream = (void *)s; + + for (i = 0; i < kernel_count; i++) { + if (kernels[i].sector == lba_start) + cur_kernel = &kernels[i]; + } + + return VB2_SUCCESS; +} + +vb2_error_t VbExStreamRead(VbExStream_t stream, uint32_t bytes, void *buffer) +{ + struct disk_stream *s = (struct disk_stream *)stream; + uint64_t sectors; + uint64_t i; + + if (!s) + return VB2_ERROR_UNKNOWN; + + /* For now, require reads to be a multiple of the LBA size */ + if (bytes % disk_info.bytes_per_lba) + return VB2_ERROR_UNKNOWN; + + /* Fail on overflow */ + sectors = bytes / disk_info.bytes_per_lba; + if (sectors > s->sectors_left) + return VB2_ERROR_UNKNOWN; + + memset(buffer, 0, bytes); + for (i = 0; i < kernel_count; i++) { + if (kernels[i].sector >= s->sector && + kernels[i].sector < s->sector + sectors) { + VB2_DEBUG("Simulating kernel %" PRIu64 " match\n", i); + uint64_t buf_offset = (kernels[i].sector - s->sector) + * disk_info.bytes_per_lba; + memcpy(buffer + buf_offset, VB2_KEYBLOCK_MAGIC, + VB2_KEYBLOCK_MAGIC_SIZE); + kernels[i].read_count++; + TEST_TRUE(kernels[i].read_count <= 2, + " Max read count exceeded"); + } + } + + s->sector += sectors; + s->sectors_left -= sectors; + + return VB2_SUCCESS; +} + +void VbExStreamClose(VbExStream_t stream) +{ + free(stream); +} + +vb2_error_t vb2_unpack_key_buffer(struct vb2_public_key *key, + const uint8_t *buf, uint32_t size) +{ + return cur_kernel->rv; +} + +vb2_error_t vb2_verify_keyblock(struct vb2_keyblock *block, uint32_t size, + const struct vb2_public_key *key, + const struct vb2_workbuf *w) +{ + /* Use this as an opportunity to override the keyblock */ + memcpy((void *)block, &kbh, sizeof(kbh)); + + return cur_kernel->rv; +} + +vb2_error_t vb2_verify_keyblock_hash(const struct vb2_keyblock *block, + uint32_t size, + const struct vb2_workbuf *w) +{ + /* Use this as an opportunity to override the keyblock */ + memcpy((void *)block, &kbh, sizeof(kbh)); + + return cur_kernel->rv; +} + +vb2_error_t vb2_verify_kernel_preamble(struct vb2_kernel_preamble *preamble, + uint32_t size, const struct vb2_public_key *key, + const struct vb2_workbuf *w) +{ + /* Use this as an opportunity to override the preamble */ + memcpy((void *)preamble, &kph, sizeof(kph)); + + return cur_kernel->rv; +} + +vb2_error_t vb2_verify_data(const uint8_t *data, uint32_t size, + struct vb2_signature *sig, + const struct vb2_public_key *key, + const struct vb2_workbuf *w) +{ + return cur_kernel->rv; +} + +vb2_error_t vb2_digest_buffer(const uint8_t *buf, uint32_t size, + enum vb2_hash_algorithm hash_alg, uint8_t *digest, + uint32_t digest_size) +{ + return cur_kernel->rv; +} + +/* Make sure nothing tested here ever calls this directly. */ +void vb2api_fail(struct vb2_context *c, uint8_t reason, uint8_t subcode) +{ + TEST_TRUE(0, " called vb2api_fail()"); +} + +/* Tests */ + +static void load_minios_kernel_tests(void) +{ + reset_common_data(); + disk_info.bytes_per_lba = KBUF_SIZE; + disk_info.lba_count = 1; + add_mock_kernel(0, VB2_SUCCESS); + TEST_SUCC(vb2api_load_minios_kernel(ctx, &lkp, &disk_info, 0), + "{valid kernel}"); + TEST_PTR_EQ(lkp.disk_handle, disk_info.handle, + " fill disk_handle when success"); + + reset_common_data(); + disk_info.bytes_per_lba = KBUF_SIZE; + disk_info.lba_count = 1; + TEST_EQ(vb2api_load_minios_kernel(ctx, &lkp, &disk_info, 0), + VB2_ERROR_LK_NO_KERNEL_FOUND, "{no kernel}"); + + reset_common_data(); + disk_info.bytes_per_lba = KBUF_SIZE; + disk_info.lba_count = 2; + add_mock_kernel(1, VB2_SUCCESS); + TEST_SUCC(vb2api_load_minios_kernel(ctx, &lkp, &disk_info, 0), + "{no kernel, valid kernel}"); + TEST_EQ(cur_kernel->sector, 1, " select kernel"); + TEST_PTR_EQ(lkp.disk_handle, disk_info.handle, + " fill disk_handle when success"); + + reset_common_data(); + disk_info.bytes_per_lba = KBUF_SIZE; + disk_info.lba_count = 2; + add_mock_kernel(0, VB2_ERROR_MOCK); + add_mock_kernel(1, VB2_SUCCESS); + TEST_SUCC(vb2api_load_minios_kernel(ctx, &lkp, &disk_info, 0), + "{invalid kernel, valid kernel}"); + TEST_EQ(cur_kernel->sector, 1, " select second kernel"); + TEST_PTR_EQ(lkp.disk_handle, disk_info.handle, + " fill disk_handle when success"); + + reset_common_data(); + disk_info.bytes_per_lba = KBUF_SIZE; + disk_info.lba_count = 2; + add_mock_kernel(0, VB2_ERROR_MOCK); + add_mock_kernel(1, VB2_ERROR_MOCK); + TEST_EQ(vb2api_load_minios_kernel(ctx, &lkp, &disk_info, 0), + VB2_ERROR_LK_NO_KERNEL_FOUND, + "{invalid kernel, invalid kernel}"); + + reset_common_data(); + disk_info.bytes_per_lba = KBUF_SIZE; + disk_info.lba_count = 2; + add_mock_kernel(0, VB2_SUCCESS); + add_mock_kernel(1, VB2_SUCCESS); + TEST_SUCC(vb2api_load_minios_kernel(ctx, &lkp, &disk_info, 0), + "{valid kernel, valid kernel} minios_priority=0"); + TEST_EQ(cur_kernel->sector, 0, " select first kernel"); + TEST_PTR_EQ(lkp.disk_handle, disk_info.handle, + " fill disk_handle when success"); + + reset_common_data(); + disk_info.bytes_per_lba = KBUF_SIZE; + disk_info.lba_count = 2; + add_mock_kernel(0, VB2_SUCCESS); + add_mock_kernel(1, VB2_SUCCESS); + vb2_nv_set(ctx, VB2_NV_MINIOS_PRIORITY, 1); + TEST_SUCC(vb2api_load_minios_kernel(ctx, &lkp, &disk_info, 0), + "{valid kernel, valid kernel} minios_priority=1"); + TEST_EQ(cur_kernel->sector, 1, " select second kernel"); + TEST_PTR_EQ(lkp.disk_handle, disk_info.handle, + " fill disk_handle when success"); + + reset_common_data(); + disk_info.bytes_per_lba = KBUF_SIZE; + disk_info.lba_count = 2; + add_mock_kernel(0, VB2_SUCCESS); + add_mock_kernel(1, VB2_SUCCESS); + TEST_SUCC(vb2api_load_minios_kernel(ctx, &lkp, &disk_info, + VB2_MINIOS_FLAG_NON_ACTIVE), + "{valid kernel, valid kernel} minios_priority=0 non-active"); + TEST_EQ(cur_kernel->sector, 1, " select second kernel"); + TEST_PTR_EQ(lkp.disk_handle, disk_info.handle, + " fill disk_handle when success"); + + reset_common_data(); + disk_info.bytes_per_lba = KBUF_SIZE; + disk_info.lba_count = 2; + add_mock_kernel(0, VB2_ERROR_MOCK); + add_mock_kernel(1, VB2_SUCCESS); + vb2_nv_set(ctx, VB2_NV_MINIOS_PRIORITY, 1); + TEST_EQ(vb2api_load_minios_kernel(ctx, &lkp, &disk_info, + VB2_MINIOS_FLAG_NON_ACTIVE), + VB2_ERROR_LK_NO_KERNEL_FOUND, + "{invalid kernel, valid kernel} minios_priority=1 non-active"); + + reset_common_data(); + disk_info.bytes_per_lba = VB2_KEYBLOCK_MAGIC_SIZE; + disk_info.lba_count = 4; + add_mock_kernel(1, VB2_SUCCESS); + TEST_EQ(vb2api_load_minios_kernel(ctx, &lkp, &disk_info, 0), + VB2_ERROR_LK_NO_KERNEL_FOUND, + "valid kernel header near start of disk (disk too small)"); + + reset_common_data(); + disk_info.bytes_per_lba = VB2_KEYBLOCK_MAGIC_SIZE; + disk_info.lba_count = 1000; + add_mock_kernel(999, VB2_SUCCESS); + TEST_EQ(vb2api_load_minios_kernel(ctx, &lkp, &disk_info, 0), + VB2_ERROR_LK_NO_KERNEL_FOUND, + "valid kernel header near end of disk"); + + reset_common_data(); + disk_info.bytes_per_lba = 1024; + disk_info.lba_count = 128; + add_mock_kernel(63, VB2_SUCCESS); + TEST_SUCC(vb2api_load_minios_kernel(ctx, &lkp, &disk_info, 0), + "start/end overlap assuming >128 MB search range (start)"); + TEST_PTR_EQ(lkp.disk_handle, disk_info.handle, + " fill disk_handle when success"); + + reset_common_data(); + disk_info.bytes_per_lba = 1024; + disk_info.lba_count = 128; + add_mock_kernel(64, VB2_SUCCESS); + TEST_SUCC(vb2api_load_minios_kernel(ctx, &lkp, &disk_info, 0), + "start/end overlap assuming >128 MB search range (end)"); + TEST_PTR_EQ(lkp.disk_handle, disk_info.handle, + " fill disk_handle when success"); + + reset_common_data(); + disk_info.bytes_per_lba = 128; + disk_info.lba_count = 1024; + add_mock_kernel(3, VB2_SUCCESS); + TEST_SUCC(vb2api_load_minios_kernel(ctx, &lkp, &disk_info, 0), + "kernel at last sector in batch assuming 512 KB batches"); + TEST_PTR_EQ(lkp.disk_handle, disk_info.handle, + " fill disk_handle when success"); + + reset_common_data(); + disk_info.bytes_per_lba = 256; + disk_info.lba_count = 1024; + add_mock_kernel(3, VB2_SUCCESS); + TEST_SUCC(vb2api_load_minios_kernel(ctx, &lkp, &disk_info, 0), + "kernel at last sector in batch assuming 1 MB batches"); + TEST_PTR_EQ(lkp.disk_handle, disk_info.handle, + " fill disk_handle when success"); + + reset_common_data(); + disk_info.bytes_per_lba = 512; + disk_info.lba_count = 1024; + add_mock_kernel(3, VB2_SUCCESS); + TEST_SUCC(vb2api_load_minios_kernel(ctx, &lkp, &disk_info, 0), + "kernel at last sector in batch assuming 2 MB batches"); + TEST_PTR_EQ(lkp.disk_handle, disk_info.handle, + " fill disk_handle when success"); + + reset_common_data(); + kbh.keyblock_flags = VB2_KEYBLOCK_FLAG_DEVELOPER_0 + | VB2_KEYBLOCK_FLAG_RECOVERY_1 + | VB2_KEYBLOCK_FLAG_MINIOS_1; + disk_info.bytes_per_lba = KBUF_SIZE; + disk_info.lba_count = 2; + add_mock_kernel(0, VB2_SUCCESS); + TEST_SUCC(vb2api_load_minios_kernel(ctx, &lkp, &disk_info, 0), + "kernel with minios keyblock flag"); + TEST_PTR_EQ(lkp.disk_handle, disk_info.handle, + " fill disk_handle when success"); + + reset_common_data(); + kbh.keyblock_flags = VB2_KEYBLOCK_FLAG_DEVELOPER_0 + | VB2_KEYBLOCK_FLAG_RECOVERY_1 + | VB2_KEYBLOCK_FLAG_MINIOS_0; + disk_info.bytes_per_lba = KBUF_SIZE; + disk_info.lba_count = 2; + add_mock_kernel(0, VB2_SUCCESS); + TEST_EQ(vb2api_load_minios_kernel(ctx, &lkp, &disk_info, 0), + VB2_ERROR_LK_NO_KERNEL_FOUND, + "kernel with !minios keyblock flag"); + + reset_common_data(); + disk_info.bytes_per_lba = KBUF_SIZE; + disk_info.lba_count = 2; + add_mock_kernel(0, VB2_SUCCESS); + sd->kernel_version_secdata = 5 << 24; + kph.kernel_version = 4; + TEST_EQ(vb2api_load_minios_kernel(ctx, &lkp, &disk_info, 0), + VB2_ERROR_LK_NO_KERNEL_FOUND, + "kernel version too old"); + + reset_common_data(); + disk_info.bytes_per_lba = KBUF_SIZE; + disk_info.lba_count = 2; + add_mock_kernel(0, VB2_SUCCESS); + sd->kernel_version_secdata = 5 << 24; + kph.kernel_version = 0x100; + TEST_EQ(vb2api_load_minios_kernel(ctx, &lkp, &disk_info, 0), + VB2_ERROR_LK_NO_KERNEL_FOUND, + "kernel version greater than 0xff"); + + reset_common_data(); + disk_info.bytes_per_lba = KBUF_SIZE; + disk_info.lba_count = 2; + add_mock_kernel(0, VB2_SUCCESS); + sd->kernel_version_secdata = 5 << 24; + kph.kernel_version = 6; + TEST_SUCC(vb2api_load_minios_kernel(ctx, &lkp, &disk_info, 0), + "newer kernel version"); + TEST_PTR_EQ(lkp.disk_handle, disk_info.handle, + " fill disk_handle when success"); +} + +int main(void) +{ + load_minios_kernel_tests(); + + return gTestSuccess ? 0 : 255; +} diff --git a/tests/vb2_load_kernel_tests.c b/tests/vb2_load_kernel_tests.c new file mode 100644 index 00000000..bb6f3487 --- /dev/null +++ b/tests/vb2_load_kernel_tests.c @@ -0,0 +1,624 @@ +/* Copyright (c) 2013 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 vboot_kernel.c + */ + +#include "2api.h" +#include "2common.h" +#include "2misc.h" +#include "2nvstorage.h" +#include "2secdata.h" +#include "2secdata_struct.h" +#include "cgptlib.h" +#include "cgptlib_internal.h" +#include "common/boot_mode.h" +#include "common/tests.h" +#include "gpt.h" +#include "vboot_api.h" + +/* Mock kernel partition */ +struct mock_part { + uint32_t start; + uint32_t size; +}; + +/* Partition list; ends with a 0-size partition. */ +#define MOCK_PART_COUNT 8 +static struct mock_part mock_parts[MOCK_PART_COUNT]; +static int mock_part_next; + +/* Mock data */ +static uint8_t kernel_buffer[80000]; +static int disk_read_to_fail; +static int gpt_init_fail; +static int keyblock_verify_fail; /* 0=ok, 1=sig, 2=hash */ +static int preamble_verify_fail; +static int verify_data_fail; +static int unpack_key_fail; +static int gpt_flag_external; + +static struct vb2_gbb_header gbb; +static struct vb2_kernel_params lkp; +static struct vb2_disk_info disk_info; +static struct vb2_keyblock kbh; +static struct vb2_kernel_preamble kph; +static struct vb2_secdata_fwmp *fwmp; +static uint8_t mock_digest[VB2_SHA256_DIGEST_SIZE] = {12, 34, 56, 78}; +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_packed_key mock_key; + +/** + * Reset mock data (for use before each test) + */ +static void ResetMocks(void) +{ + disk_read_to_fail = -1; + + gpt_init_fail = 0; + keyblock_verify_fail = 0; + preamble_verify_fail = 0; + verify_data_fail = 0; + unpack_key_fail = 0; + + gpt_flag_external = 0; + + memset(&gbb, 0, sizeof(gbb)); + gbb.major_version = VB2_GBB_MAJOR_VER; + gbb.minor_version = VB2_GBB_MINOR_VER; + gbb.flags = 0; + + memset(&lkp, 0, sizeof(lkp)); + lkp.kernel_buffer = kernel_buffer; + lkp.kernel_buffer_size = sizeof(kernel_buffer); + + memset(&disk_info, 0, sizeof(disk_info)); + disk_info.bytes_per_lba = 512; + disk_info.streaming_lba_count = 1024; + disk_info.lba_count = 1024; + disk_info.handle = (vb2ex_disk_handle_t)1; + + memset(&kbh, 0, sizeof(kbh)); + kbh.data_key.key_version = 2; + kbh.keyblock_flags = -1; + kbh.keyblock_size = sizeof(kbh); + + memset(&kph, 0, sizeof(kph)); + kph.kernel_version = 1; + kph.preamble_size = 4096 - kbh.keyblock_size; + kph.body_signature.data_size = 70144; + kph.bootloader_address = 0xbeadd008; + kph.bootloader_size = 0x1234; + + memset(mock_parts, 0, sizeof(mock_parts)); + mock_parts[0].start = 100; + mock_parts[0].size = 150; /* 75 KB */ + mock_part_next = 0; + + memset(&mock_key, 0, sizeof(mock_key)); + + TEST_SUCC(vb2api_init(workbuf, sizeof(workbuf), &ctx), + "vb2api_init failed"); + vb2_nv_init(ctx); + + sd = vb2_get_sd(ctx); + sd->kernel_version_secdata = 0x20001; + + /* CRC will be invalid after here, but nobody's checking */ + sd->status |= VB2_SD_STATUS_SECDATA_FWMP_INIT; + fwmp = (struct vb2_secdata_fwmp *)ctx->secdata_fwmp; + memcpy(&fwmp->dev_key_hash, mock_digest, sizeof(fwmp->dev_key_hash)); + + SET_BOOT_MODE(ctx, VB2_BOOT_MODE_NORMAL); + + // TODO: more workbuf fields - flags, secdata_firmware + + vb2api_secdata_kernel_create(ctx); + vb2_secdata_kernel_init(ctx); + vb2_secdata_kernel_set(ctx, VB2_SECDATA_KERNEL_FLAGS, + VB2_SECDATA_KERNEL_FLAG_HWCRYPTO_ALLOWED); +} + +/* Mocks */ +struct vb2_gbb_header *vb2_get_gbb(struct vb2_context *c) +{ + return &gbb; +} + +vb2_error_t vb2ex_read_resource(struct vb2_context *c, + enum vb2_resource_index index, uint32_t offset, + void *buf, uint32_t size) +{ + memset(buf, 0, size); + return VB2_SUCCESS; +} + +vb2_error_t vb2_gbb_read_root_key(struct vb2_context *c, + struct vb2_packed_key **keyp, uint32_t *size, + struct vb2_workbuf *wb) +{ + *keyp = &mock_key; + return VB2_SUCCESS; +} + +vb2_error_t vb2_gbb_read_recovery_key(struct vb2_context *c, + struct vb2_packed_key **keyp, + uint32_t *size, struct vb2_workbuf *wb) +{ + *keyp = &mock_key; + return VB2_SUCCESS; +} + +vb2_error_t VbExDiskRead(vb2ex_disk_handle_t h, uint64_t lba_start, + uint64_t lba_count, void *buffer) +{ + if ((int)lba_start == disk_read_to_fail) + return VB2_ERROR_MOCK; + + return VB2_SUCCESS; +} + +int AllocAndReadGptData(vb2ex_disk_handle_t disk_handle, GptData *gptdata) +{ + return GPT_SUCCESS; +} + +int GptInit(GptData *gpt) +{ + return gpt_init_fail; +} + +int GptNextKernelEntry(GptData *gpt, uint64_t *start_sector, uint64_t *size) +{ + struct mock_part *p = mock_parts + mock_part_next; + + if (!p->size) + return GPT_ERROR_NO_VALID_KERNEL; + + if (gpt->flags & GPT_FLAG_EXTERNAL) + gpt_flag_external++; + + gpt->current_kernel = mock_part_next; + *start_sector = p->start; + *size = p->size; + mock_part_next++; + return GPT_SUCCESS; +} + +int GptUpdateKernelEntry(GptData *gpt, uint32_t update_type) +{ + return GPT_SUCCESS; +} + +int WriteAndFreeGptData(vb2ex_disk_handle_t disk_handle, GptData *gptdata) +{ + return GPT_SUCCESS; +} + +void GetCurrentKernelUniqueGuid(GptData *gpt, void *dest) +{ + static char fake_guid[] = "FakeGuid"; + + memcpy(dest, fake_guid, sizeof(fake_guid)); +} + +vb2_error_t vb2_unpack_key_buffer(struct vb2_public_key *key, + const uint8_t *buf, uint32_t size) +{ + if (--unpack_key_fail == 0) + return VB2_ERROR_MOCK; + + return VB2_SUCCESS; +} + +vb2_error_t vb2_verify_keyblock(struct vb2_keyblock *block, uint32_t size, + const struct vb2_public_key *key, + const struct vb2_workbuf *wb) +{ + if (keyblock_verify_fail >= 1) + return VB2_ERROR_MOCK; + + /* Use this as an opportunity to override the keyblock */ + memcpy((void *)block, &kbh, sizeof(kbh)); + return VB2_SUCCESS; +} + +vb2_error_t vb2_verify_keyblock_hash(const struct vb2_keyblock *block, + uint32_t size, + const struct vb2_workbuf *wb) +{ + if (keyblock_verify_fail >= 2) + return VB2_ERROR_MOCK; + + /* Use this as an opportunity to override the keyblock */ + memcpy((void *)block, &kbh, sizeof(kbh)); + return VB2_SUCCESS; +} + +vb2_error_t vb2_verify_kernel_preamble(struct vb2_kernel_preamble *preamble, + uint32_t size, const struct vb2_public_key *key, + const struct vb2_workbuf *wb) +{ + if (preamble_verify_fail) + return VB2_ERROR_MOCK; + + /* Use this as an opportunity to override the preamble */ + memcpy((void *)preamble, &kph, sizeof(kph)); + return VB2_SUCCESS; +} + +vb2_error_t vb2_verify_data(const uint8_t *data, uint32_t size, + struct vb2_signature *sig, + const struct vb2_public_key *key, + const struct vb2_workbuf *wb) +{ + if (verify_data_fail) + return VB2_ERROR_MOCK; + + return VB2_SUCCESS; +} + +vb2_error_t vb2_digest_buffer(const uint8_t *buf, uint32_t size, + enum vb2_hash_algorithm hash_alg, uint8_t *digest, + uint32_t digest_size) +{ + memcpy(digest, mock_digest, sizeof(mock_digest)); + return VB2_SUCCESS; +} + +/* Make sure nothing tested here ever calls this directly. */ +void vb2api_fail(struct vb2_context *c, uint8_t reason, uint8_t subcode) +{ + TEST_TRUE(0, " called vb2api_fail()"); +} + +static void test_load_kernel(vb2_error_t expect_retval, const char *test_name) +{ + TEST_EQ(vb2api_load_kernel(ctx, &lkp, &disk_info), expect_retval, + test_name); + if (expect_retval == VB2_SUCCESS) + TEST_PTR_EQ(lkp.disk_handle, disk_info.handle, + " fill disk_handle when success"); +} + +/** + * Trivial invalid calls to vb2api_load_kernel() + */ +static void invalid_params_tests(void) +{ + ResetMocks(); + gpt_init_fail = 1; + test_load_kernel(VB2_ERROR_LK_NO_KERNEL_FOUND, "Bad GPT"); + + /* This causes the stream open call to fail */ + ResetMocks(); + disk_info.handle = NULL; + test_load_kernel(VB2_ERROR_LK_INVALID_KERNEL_FOUND, "Bad disk handle"); +} + +static void load_kernel_tests(void) +{ + ResetMocks(); + test_load_kernel(VB2_SUCCESS, "First kernel good"); + TEST_EQ(lkp.partition_number, 1, " part num"); + TEST_EQ(lkp.bootloader_address, 0xbeadd008, " bootloader addr"); + TEST_EQ(lkp.bootloader_size, 0x1234, " bootloader size"); + TEST_STR_EQ((char *)lkp.partition_guid, "FakeGuid", " guid"); + TEST_EQ(gpt_flag_external, 0, "GPT was internal"); + TEST_NEQ(sd->flags & VB2_SD_FLAG_KERNEL_SIGNED, 0, " use signature"); + + ResetMocks(); + mock_parts[1].start = 300; + mock_parts[1].size = 150; + test_load_kernel(VB2_SUCCESS, "Two good kernels"); + TEST_EQ(lkp.partition_number, 1, " part num"); + TEST_EQ(mock_part_next, 1, " didn't read second one"); + + /* Fail if no kernels found */ + ResetMocks(); + mock_parts[0].size = 0; + test_load_kernel(VB2_ERROR_LK_NO_KERNEL_FOUND, "No kernels"); + + /* Skip kernels which are too small */ + ResetMocks(); + mock_parts[0].size = 10; + test_load_kernel(VB2_ERROR_LK_INVALID_KERNEL_FOUND, "Too small"); + + ResetMocks(); + disk_read_to_fail = 100; + test_load_kernel(VB2_ERROR_LK_INVALID_KERNEL_FOUND, + "Fail reading kernel start"); + + ResetMocks(); + keyblock_verify_fail = 1; + test_load_kernel(VB2_ERROR_LK_INVALID_KERNEL_FOUND, + "Fail key block sig"); + + /* In dev mode, fail if hash is bad too */ + ResetMocks(); + SET_BOOT_MODE(ctx, VB2_BOOT_MODE_DEVELOPER); + keyblock_verify_fail = 2; + test_load_kernel(VB2_ERROR_LK_INVALID_KERNEL_FOUND, + "Fail key block dev hash"); + + /* But just bad sig is ok */ + ResetMocks(); + SET_BOOT_MODE(ctx, VB2_BOOT_MODE_DEVELOPER); + keyblock_verify_fail = 1; + test_load_kernel(VB2_SUCCESS, "Succeed keyblock dev sig"); + TEST_EQ(sd->flags & VB2_SD_FLAG_KERNEL_SIGNED, 0, " use hash"); + + /* In dev mode and requiring signed kernel, fail if sig is bad */ + ResetMocks(); + SET_BOOT_MODE(ctx, VB2_BOOT_MODE_DEVELOPER); + vb2_nv_set(ctx, VB2_NV_DEV_BOOT_SIGNED_ONLY, 1); + keyblock_verify_fail = 1; + test_load_kernel(VB2_ERROR_LK_INVALID_KERNEL_FOUND, + "Fail key block dev sig"); + + ResetMocks(); + SET_BOOT_MODE(ctx, VB2_BOOT_MODE_DEVELOPER); + fwmp->flags |= VB2_SECDATA_FWMP_DEV_ENABLE_OFFICIAL_ONLY; + keyblock_verify_fail = 1; + test_load_kernel(VB2_ERROR_LK_INVALID_KERNEL_FOUND, + "Fail key block dev sig fwmp"); + + /* Check keyblock flags */ + ResetMocks(); + kbh.keyblock_flags = VB2_KEYBLOCK_FLAG_RECOVERY_0 + | VB2_KEYBLOCK_FLAG_DEVELOPER_1 + | VB2_KEYBLOCK_FLAG_MINIOS_0; + test_load_kernel(VB2_ERROR_LK_INVALID_KERNEL_FOUND, + "Keyblock dev flag mismatch"); + + ResetMocks(); + kbh.keyblock_flags = VB2_KEYBLOCK_FLAG_RECOVERY_1 + | VB2_KEYBLOCK_FLAG_DEVELOPER_0 + | VB2_KEYBLOCK_FLAG_MINIOS_0; + test_load_kernel(VB2_ERROR_LK_INVALID_KERNEL_FOUND, + "Keyblock rec flag mismatch"); + + ResetMocks(); + kbh.keyblock_flags = VB2_KEYBLOCK_FLAG_RECOVERY_0 + | VB2_KEYBLOCK_FLAG_DEVELOPER_0 + | VB2_KEYBLOCK_FLAG_MINIOS_1; + test_load_kernel(VB2_ERROR_LK_INVALID_KERNEL_FOUND, + "Keyblock minios flag mismatch"); + + ResetMocks(); + SET_BOOT_MODE(ctx, VB2_BOOT_MODE_MANUAL_RECOVERY, + VB2_RECOVERY_RO_MANUAL); + kbh.keyblock_flags = VB2_KEYBLOCK_FLAG_RECOVERY_1 + | VB2_KEYBLOCK_FLAG_DEVELOPER_1 + | VB2_KEYBLOCK_FLAG_MINIOS_0; + test_load_kernel(VB2_ERROR_LK_INVALID_KERNEL_FOUND, + "Keyblock recdev flag mismatch"); + + ResetMocks(); + SET_BOOT_MODE(ctx, VB2_BOOT_MODE_MANUAL_RECOVERY, + VB2_RECOVERY_RO_MANUAL); + kbh.keyblock_flags = VB2_KEYBLOCK_FLAG_RECOVERY_1 + | VB2_KEYBLOCK_FLAG_DEVELOPER_0 + | VB2_KEYBLOCK_FLAG_MINIOS_0; + test_load_kernel(VB2_SUCCESS, "Keyblock rec flag okay"); + + ResetMocks(); + SET_BOOT_MODE(ctx, VB2_BOOT_MODE_MANUAL_RECOVERY, + VB2_RECOVERY_RO_MANUAL); + ctx->flags |= VB2_CONTEXT_DEVELOPER_MODE; + kbh.keyblock_flags = VB2_KEYBLOCK_FLAG_RECOVERY_1 + | VB2_KEYBLOCK_FLAG_DEVELOPER_0 + | VB2_KEYBLOCK_FLAG_MINIOS_0; + test_load_kernel(VB2_ERROR_LK_INVALID_KERNEL_FOUND, + "Keyblock rec!dev flag mismatch"); + + ResetMocks(); + SET_BOOT_MODE(ctx, VB2_BOOT_MODE_MANUAL_RECOVERY, + VB2_RECOVERY_RO_MANUAL); + ctx->flags |= VB2_CONTEXT_DEVELOPER_MODE; + kbh.keyblock_flags = VB2_KEYBLOCK_FLAG_RECOVERY_1 + | VB2_KEYBLOCK_FLAG_DEVELOPER_1 + | VB2_KEYBLOCK_FLAG_MINIOS_0; + test_load_kernel(VB2_SUCCESS, "Keyblock recdev flag okay"); + + /* Check keyblock flags (dev mode + signed kernel required) */ + ResetMocks(); + SET_BOOT_MODE(ctx, VB2_BOOT_MODE_DEVELOPER); + vb2_nv_set(ctx, VB2_NV_DEV_BOOT_SIGNED_ONLY, 1); + kbh.keyblock_flags = VB2_KEYBLOCK_FLAG_RECOVERY_1 + | VB2_KEYBLOCK_FLAG_DEVELOPER_0 + | VB2_KEYBLOCK_FLAG_MINIOS_0; + test_load_kernel(VB2_ERROR_LK_INVALID_KERNEL_FOUND, + "Keyblock dev flag mismatch (signed kernel required)"); + + ResetMocks(); + SET_BOOT_MODE(ctx, VB2_BOOT_MODE_DEVELOPER); + fwmp->flags |= VB2_SECDATA_FWMP_DEV_ENABLE_OFFICIAL_ONLY; + kbh.keyblock_flags = VB2_KEYBLOCK_FLAG_RECOVERY_1 + | VB2_KEYBLOCK_FLAG_DEVELOPER_0 + | VB2_KEYBLOCK_FLAG_MINIOS_0; + test_load_kernel(VB2_ERROR_LK_INVALID_KERNEL_FOUND, + "Keyblock dev flag mismatch (signed kernel required)"); + + ResetMocks(); + SET_BOOT_MODE(ctx, VB2_BOOT_MODE_DEVELOPER); + fwmp->flags |= VB2_SECDATA_FWMP_DEV_ENABLE_OFFICIAL_ONLY; + kbh.keyblock_flags = VB2_KEYBLOCK_FLAG_RECOVERY_0 + | VB2_KEYBLOCK_FLAG_DEVELOPER_0 + | VB2_KEYBLOCK_FLAG_MINIOS_1; + test_load_kernel(VB2_ERROR_LK_INVALID_KERNEL_FOUND, + "Keyblock dev flag mismatch (signed kernel required)"); + + ResetMocks(); + SET_BOOT_MODE(ctx, VB2_BOOT_MODE_DEVELOPER); + vb2_nv_set(ctx, VB2_NV_DEV_BOOT_SIGNED_ONLY, 1); + kbh.keyblock_flags = VB2_KEYBLOCK_FLAG_RECOVERY_0 + | VB2_KEYBLOCK_FLAG_DEVELOPER_1 + | VB2_KEYBLOCK_FLAG_MINIOS_0; + test_load_kernel(VB2_SUCCESS, + "Keyblock dev flag okay (signed kernel required)"); + + /* Check kernel key version */ + ResetMocks(); + kbh.data_key.key_version = 1; + test_load_kernel(VB2_ERROR_LK_INVALID_KERNEL_FOUND, + "Keyblock kernel key rollback"); + + ResetMocks(); + kbh.data_key.key_version = 0x10000; + test_load_kernel(VB2_ERROR_LK_INVALID_KERNEL_FOUND, + "Keyblock kernel key version too big"); + + ResetMocks(); + kbh.data_key.key_version = 3; + test_load_kernel(VB2_SUCCESS, "Keyblock version roll forward"); + TEST_EQ(sd->kernel_version, 0x30001, " SD version"); + + ResetMocks(); + kbh.data_key.key_version = 3; + mock_parts[1].start = 300; + mock_parts[1].size = 150; + test_load_kernel(VB2_SUCCESS, "Two kernels roll forward"); + TEST_EQ(mock_part_next, 2, " read both"); + TEST_EQ(sd->kernel_version, 0x30001, " SD version"); + + ResetMocks(); + kbh.data_key.key_version = 1; + SET_BOOT_MODE(ctx, VB2_BOOT_MODE_DEVELOPER); + test_load_kernel(VB2_SUCCESS, "Key version ignored in dev mode"); + + ResetMocks(); + kbh.data_key.key_version = 1; + SET_BOOT_MODE(ctx, VB2_BOOT_MODE_MANUAL_RECOVERY, + VB2_RECOVERY_RO_MANUAL); + test_load_kernel(VB2_SUCCESS, "Key version ignored in rec mode"); + + ResetMocks(); + unpack_key_fail = 2; + test_load_kernel(VB2_ERROR_LK_INVALID_KERNEL_FOUND, "Bad data key"); + + ResetMocks(); + preamble_verify_fail = 1; + test_load_kernel(VB2_ERROR_LK_INVALID_KERNEL_FOUND, "Bad preamble"); + + ResetMocks(); + kph.kernel_version = 0; + test_load_kernel(VB2_ERROR_LK_INVALID_KERNEL_FOUND, + "Kernel version rollback"); + + ResetMocks(); + kph.kernel_version = 0; + SET_BOOT_MODE(ctx, VB2_BOOT_MODE_DEVELOPER); + test_load_kernel(VB2_SUCCESS, "Kernel version ignored in dev mode"); + + ResetMocks(); + kph.kernel_version = 0; + SET_BOOT_MODE(ctx, VB2_BOOT_MODE_MANUAL_RECOVERY, + VB2_RECOVERY_RO_MANUAL); + test_load_kernel(VB2_SUCCESS, "Kernel version ignored in rec mode"); + + /* Check kernel version (dev mode + signed kernel required) */ + ResetMocks(); + kbh.data_key.key_version = 0; + SET_BOOT_MODE(ctx, VB2_BOOT_MODE_DEVELOPER); + vb2_nv_set(ctx, VB2_NV_DEV_BOOT_SIGNED_ONLY, 1); + test_load_kernel(VB2_ERROR_LK_INVALID_KERNEL_FOUND, + "Keyblock key version checked in dev mode " + "(signed kernel required)"); + + ResetMocks(); + kbh.data_key.key_version = 0; + SET_BOOT_MODE(ctx, VB2_BOOT_MODE_DEVELOPER); + fwmp->flags |= VB2_SECDATA_FWMP_DEV_ENABLE_OFFICIAL_ONLY; + test_load_kernel(VB2_ERROR_LK_INVALID_KERNEL_FOUND, + "Keyblock key version checked in dev mode " + "(signed kernel required)"); + + /* Check developer key hash - bad */ + ResetMocks(); + SET_BOOT_MODE(ctx, VB2_BOOT_MODE_DEVELOPER); + fwmp->flags |= VB2_SECDATA_FWMP_DEV_USE_KEY_HASH; + fwmp->dev_key_hash[0]++; + test_load_kernel(VB2_ERROR_LK_INVALID_KERNEL_FOUND, + "Fail keyblock dev fwmp hash"); + + /* Check developer key hash - bad (recovery mode) */ + ResetMocks(); + SET_BOOT_MODE(ctx, VB2_BOOT_MODE_MANUAL_RECOVERY, + VB2_RECOVERY_RO_MANUAL); + fwmp->flags |= VB2_SECDATA_FWMP_DEV_USE_KEY_HASH; + fwmp->dev_key_hash[0]++; + test_load_kernel(VB2_SUCCESS, + "Bad keyblock dev fwmp hash ignored in rec mode"); + + /* Check developer key hash - good */ + ResetMocks(); + SET_BOOT_MODE(ctx, VB2_BOOT_MODE_DEVELOPER); + fwmp->flags |= VB2_SECDATA_FWMP_DEV_USE_KEY_HASH; + test_load_kernel(VB2_SUCCESS, "Good keyblock dev fwmp hash"); + + ResetMocks(); + kph.preamble_size |= 0x07; + test_load_kernel(VB2_ERROR_LK_INVALID_KERNEL_FOUND, + "Kernel body offset"); + + ResetMocks(); + kph.preamble_size += 65536; + test_load_kernel(VB2_ERROR_LK_INVALID_KERNEL_FOUND, + "Kernel body offset huge"); + + /* Check getting kernel load address from header */ + ResetMocks(); + kph.body_load_address = (size_t)kernel_buffer; + lkp.kernel_buffer = NULL; + test_load_kernel(VB2_SUCCESS, "Get load address from preamble"); + TEST_PTR_EQ(lkp.kernel_buffer, kernel_buffer, " address"); + /* Size is rounded up to nearest sector */ + TEST_EQ(lkp.kernel_buffer_size, 70144, " size"); + + ResetMocks(); + lkp.kernel_buffer_size = 8192; + test_load_kernel(VB2_ERROR_LK_INVALID_KERNEL_FOUND, + "Kernel too big for buffer"); + + ResetMocks(); + mock_parts[0].size = 130; + test_load_kernel(VB2_ERROR_LK_INVALID_KERNEL_FOUND, + "Kernel too big for partition"); + + ResetMocks(); + kph.body_signature.data_size = 8192; + test_load_kernel(VB2_SUCCESS, "Kernel tiny"); + + ResetMocks(); + disk_read_to_fail = 228; + test_load_kernel(VB2_ERROR_LK_INVALID_KERNEL_FOUND, + "Fail reading kernel data"); + + ResetMocks(); + verify_data_fail = 1; + test_load_kernel(VB2_ERROR_LK_INVALID_KERNEL_FOUND, "Bad data"); + + /* Check that EXTERNAL_GPT flag makes it down */ + ResetMocks(); + disk_info.flags |= VB2_DISK_FLAG_EXTERNAL_GPT; + test_load_kernel(VB2_SUCCESS, "Succeed external GPT"); + TEST_EQ(gpt_flag_external, 1, "GPT was external"); + + /* Check recovery from unreadble primary GPT */ + ResetMocks(); + disk_read_to_fail = 1; + test_load_kernel(VB2_SUCCESS, "Can't read disk"); +} + +int main(void) +{ + invalid_params_tests(); + load_kernel_tests(); + + return gTestSuccess ? 0 : 255; +} diff --git a/tests/vboot_api_kernel_tests.c b/tests/vboot_api_kernel_tests.c deleted file mode 100644 index 5c1d0182..00000000 --- a/tests/vboot_api_kernel_tests.c +++ /dev/null @@ -1,644 +0,0 @@ -/* Copyright (c) 2013 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 VbTryLoadKernel() - */ - -#include "2common.h" -#include "2misc.h" -#include "2nvstorage.h" -#include "2sysincludes.h" -#include "common/tests.h" -#include "load_kernel_fw.h" -#include "vboot_api.h" - -#define MAX_TEST_DISKS 10 -#define DEFAULT_COUNT -1 - -typedef struct { - uint64_t bytes_per_lba; - uint64_t lba_count; - uint32_t flags; - const char *diskname; -} disk_desc_t; - -typedef struct { - const char *name; - - /* inputs for test case */ - uint32_t ctx_flags; - uint32_t want_flags; - vb2_error_t diskgetinfo_return_val; - disk_desc_t disks_to_provide[MAX_TEST_DISKS]; - int disk_count_to_return; - vb2_error_t loadkernel_return_val[MAX_TEST_DISKS]; - uint8_t external_expected[MAX_TEST_DISKS]; - - /* outputs from test */ - uint32_t expected_recovery_request_val; - const char *expected_to_find_disk; - const char *expected_to_load_disk; - uint32_t expected_return_val; - -} test_case_t; - -/****************************************************************************/ -/* Test cases */ - -static const char pickme[] = "correct choice"; -#define DONT_CARE ((const char *)42) - -test_case_t normal_tests[] = { - { - .name = "first drive (removable)", - .ctx_flags = 0, - .want_flags = VB_DISK_FLAG_REMOVABLE | VB_DISK_FLAG_FIXED, - .disks_to_provide = { - {4096, 100, VB_DISK_FLAG_REMOVABLE, pickme}, - {4096, 100, VB_DISK_FLAG_FIXED, "holygrail"}, - }, - .disk_count_to_return = DEFAULT_COUNT, - .diskgetinfo_return_val = VB2_SUCCESS, - .loadkernel_return_val = {0}, - .external_expected = {0}, - - .expected_recovery_request_val = VB2_RECOVERY_NOT_REQUESTED, - .expected_to_find_disk = pickme, - .expected_to_load_disk = pickme, - .expected_return_val = VB2_SUCCESS - }, - { - .name = "first drive (fixed)", - .ctx_flags = 0, - .want_flags = VB_DISK_FLAG_REMOVABLE | VB_DISK_FLAG_FIXED, - .disks_to_provide = { - {4096, 100, VB_DISK_FLAG_FIXED, pickme}, - {4096, 100, VB_DISK_FLAG_REMOVABLE, "holygrail"}, - }, - .disk_count_to_return = DEFAULT_COUNT, - .diskgetinfo_return_val = VB2_SUCCESS, - .loadkernel_return_val = {0}, - .external_expected = {0}, - - .expected_recovery_request_val = VB2_RECOVERY_NOT_REQUESTED, - .expected_to_find_disk = pickme, - .expected_to_load_disk = pickme, - .expected_return_val = VB2_SUCCESS - }, - { - .name = "first removable drive", - .ctx_flags = 0, - .want_flags = VB_DISK_FLAG_REMOVABLE, - .disks_to_provide = { - /* too small */ - {512, 10, VB_DISK_FLAG_REMOVABLE, 0}, - /* wrong LBA */ - {511, 100, VB_DISK_FLAG_REMOVABLE, 0}, - /* not a power of 2 */ - {2047, 100, VB_DISK_FLAG_REMOVABLE, 0}, - /* wrong type */ - {512, 100, VB_DISK_FLAG_FIXED, 0}, - /* wrong flags */ - {512, 100, 0, 0}, - /* still wrong flags */ - {512, 100, -1, 0}, - {4096, 100, VB_DISK_FLAG_REMOVABLE, pickme}, - /* already got one */ - {512, 100, VB_DISK_FLAG_REMOVABLE, "holygrail"}, - }, - .disk_count_to_return = DEFAULT_COUNT, - .diskgetinfo_return_val = VB2_SUCCESS, - .loadkernel_return_val = {0}, - .external_expected = {0}, - - .expected_recovery_request_val = VB2_RECOVERY_NOT_REQUESTED, - .expected_to_find_disk = pickme, - .expected_to_load_disk = pickme, - .expected_return_val = VB2_SUCCESS - }, - { - .name = "first removable drive (skip external GPT)", - .ctx_flags = 0, - .want_flags = VB_DISK_FLAG_REMOVABLE, - .disks_to_provide = { - /* too small */ - {512, 10, VB_DISK_FLAG_REMOVABLE, 0}, - /* wrong LBA */ - {511, 100, VB_DISK_FLAG_REMOVABLE, 0}, - /* not a power of 2 */ - {2047, 100, VB_DISK_FLAG_REMOVABLE, 0}, - /* wrong type */ - {512, 100, VB_DISK_FLAG_FIXED, 0}, - /* wrong flags */ - {512, 100, 0, 0}, - /* still wrong flags */ - {512, 100, -1, 0}, - {512, 100, - VB_DISK_FLAG_REMOVABLE | VB_DISK_FLAG_EXTERNAL_GPT, - pickme}, - /* already got one */ - {512, 100, VB_DISK_FLAG_REMOVABLE, "holygrail"}, - }, - .disk_count_to_return = DEFAULT_COUNT, - .diskgetinfo_return_val = VB2_SUCCESS, - .loadkernel_return_val = {0, 0}, - .external_expected = {1, 0}, - - .expected_recovery_request_val = VB2_RECOVERY_NOT_REQUESTED, - .expected_to_find_disk = pickme, - .expected_to_load_disk = pickme, - .expected_return_val = VB2_SUCCESS - }, - { - .name = "second removable drive", - .ctx_flags = 0, - .want_flags = VB_DISK_FLAG_REMOVABLE, - .disks_to_provide = { - /* wrong flags */ - {512, 100, 0, 0}, - {512, 100, VB_DISK_FLAG_REMOVABLE, "not yet"}, - {512, 100, VB_DISK_FLAG_REMOVABLE, pickme}, - }, - .disk_count_to_return = DEFAULT_COUNT, - .diskgetinfo_return_val = VB2_SUCCESS, - .loadkernel_return_val = {VB2_ERROR_LK_INVALID_KERNEL_FOUND, 0}, - - .expected_recovery_request_val = VB2_RECOVERY_NOT_REQUESTED, - .expected_to_find_disk = pickme, - .expected_to_load_disk = pickme, - .expected_return_val = VB2_SUCCESS - }, - { - .name = "first fixed drive", - .ctx_flags = 0, - .want_flags = VB_DISK_FLAG_FIXED, - .disks_to_provide = { - /* too small */ - {512, 10, VB_DISK_FLAG_FIXED, 0}, - /* wrong LBA */ - {511, 100, VB_DISK_FLAG_FIXED, 0}, - /* not a power of 2 */ - {2047, 100, VB_DISK_FLAG_REMOVABLE, 0}, - /* wrong type */ - {512, 100, VB_DISK_FLAG_REMOVABLE, 0}, - /* wrong flags */ - {512, 100, 0, 0}, - /* still wrong flags */ - {512, 100, -1, 0}, - /* flags */ - {512, 100, VB_DISK_FLAG_REMOVABLE|VB_DISK_FLAG_FIXED, - 0}, - {512, 100, VB_DISK_FLAG_FIXED, pickme}, - /* already got one */ - {512, 100, VB_DISK_FLAG_FIXED, "holygrail"}, - }, - .disk_count_to_return = DEFAULT_COUNT, - .diskgetinfo_return_val = VB2_SUCCESS, - .loadkernel_return_val = {0}, - - .expected_recovery_request_val = VB2_RECOVERY_NOT_REQUESTED, - .expected_to_find_disk = pickme, - .expected_to_load_disk = pickme, - .expected_return_val = VB2_SUCCESS - }, - { - .name = "no drives at all", - .ctx_flags = 0, - .want_flags = VB_DISK_FLAG_FIXED, - .disks_to_provide = {}, - .disk_count_to_return = DEFAULT_COUNT, - .diskgetinfo_return_val = VB2_SUCCESS, - - .expected_recovery_request_val = VB2_RECOVERY_RW_NO_DISK, - .expected_to_find_disk = 0, - .expected_to_load_disk = 0, - .expected_return_val = VB2_ERROR_LK_NO_DISK_FOUND - }, - { - .name = "VbExDiskGetInfo() error", - .ctx_flags = 0, - .want_flags = VB_DISK_FLAG_FIXED, - .disks_to_provide = { - {512, 10, VB_DISK_FLAG_REMOVABLE, 0}, - {512, 100, VB_DISK_FLAG_FIXED, 0}, - }, - .disk_count_to_return = DEFAULT_COUNT, - .diskgetinfo_return_val = VB2_ERROR_UNKNOWN, - - .expected_recovery_request_val = VB2_RECOVERY_RW_NO_DISK, - .expected_to_find_disk = 0, - .expected_to_load_disk = 0, - .expected_return_val = VB2_ERROR_LK_NO_DISK_FOUND, - }, - { - .name = "invalid kernel", - .ctx_flags = 0, - .want_flags = VB_DISK_FLAG_FIXED, - .disks_to_provide = { - /* too small */ - {512, 10, VB_DISK_FLAG_FIXED, 0}, - /* wrong LBA */ - {511, 100, VB_DISK_FLAG_FIXED, 0}, - /* not a power of 2 */ - {2047, 100, VB_DISK_FLAG_FIXED, 0}, - /* wrong type */ - {512, 100, VB_DISK_FLAG_REMOVABLE, 0}, - /* wrong flags */ - {512, 100, 0, 0}, - /* still wrong flags */ - {512, 100, -1, 0}, - /* doesn't load */ - {512, 100, VB_DISK_FLAG_FIXED, "corrupted kernel"}, - /* doesn't load */ - {512, 100, VB_DISK_FLAG_FIXED, "stateful partition"}, - }, - .disk_count_to_return = DEFAULT_COUNT, - .diskgetinfo_return_val = VB2_SUCCESS, - .loadkernel_return_val = {VB2_ERROR_LK_INVALID_KERNEL_FOUND, - VB2_ERROR_LK_NO_KERNEL_FOUND}, - - .expected_recovery_request_val = VB2_RECOVERY_RW_INVALID_OS, - .expected_to_find_disk = DONT_CARE, - .expected_to_load_disk = 0, - .expected_return_val = VB2_ERROR_LK_INVALID_KERNEL_FOUND, - }, - { - .name = "invalid kernel, order flipped", - .ctx_flags = 0, - .want_flags = VB_DISK_FLAG_FIXED, - .disks_to_provide = { - {512, 1000, VB_DISK_FLAG_FIXED, "stateful partition"}, - {512, 1000, VB_DISK_FLAG_FIXED, "corrupted kernel"}, - }, - .disk_count_to_return = DEFAULT_COUNT, - .diskgetinfo_return_val = VB2_SUCCESS, - .loadkernel_return_val = {VB2_ERROR_LK_NO_KERNEL_FOUND, - VB2_ERROR_LK_INVALID_KERNEL_FOUND}, - - .expected_recovery_request_val = VB2_RECOVERY_RW_INVALID_OS, - .expected_to_find_disk = DONT_CARE, - .expected_to_load_disk = 0, - .expected_return_val = VB2_ERROR_LK_INVALID_KERNEL_FOUND, - }, - { - .name = "no Chrome OS partitions", - .ctx_flags = 0, - .want_flags = VB_DISK_FLAG_FIXED, - .disks_to_provide = { - {512, 100, VB_DISK_FLAG_FIXED, "stateful partition"}, - {512, 1000, VB_DISK_FLAG_FIXED, "Chrubuntu"}, - }, - .disk_count_to_return = DEFAULT_COUNT, - .diskgetinfo_return_val = VB2_SUCCESS, - .loadkernel_return_val = {VB2_ERROR_LK_NO_KERNEL_FOUND, - VB2_ERROR_LK_NO_KERNEL_FOUND}, - - .expected_recovery_request_val = VB2_RECOVERY_RW_NO_KERNEL, - .expected_to_find_disk = DONT_CARE, - .expected_to_load_disk = 0, - .expected_return_val = VB2_ERROR_LK_NO_KERNEL_FOUND, - }, - { - .name = "invalid kernel (removable)", - .ctx_flags = 0, - .want_flags = VB_DISK_FLAG_REMOVABLE, - .disks_to_provide = { - {512, 100, VB_DISK_FLAG_REMOVABLE, "corrupted"}, - {512, 100, VB_DISK_FLAG_REMOVABLE, "data"}, - }, - .disk_count_to_return = DEFAULT_COUNT, - .diskgetinfo_return_val = VB2_SUCCESS, - .loadkernel_return_val = {VB2_ERROR_LK_INVALID_KERNEL_FOUND, - VB2_ERROR_LK_NO_KERNEL_FOUND}, - - .expected_recovery_request_val = VB2_RECOVERY_RW_INVALID_OS, - .expected_to_find_disk = DONT_CARE, - .expected_to_load_disk = 0, - .expected_return_val = VB2_ERROR_LK_INVALID_KERNEL_FOUND, - }, - { - .name = "invalid kernel (removable, rec mode)", - .ctx_flags = VB2_CONTEXT_RECOVERY_MODE, - .want_flags = VB_DISK_FLAG_REMOVABLE, - .disks_to_provide = { - {512, 100, VB_DISK_FLAG_REMOVABLE, "corrupted"}, - {512, 100, VB_DISK_FLAG_REMOVABLE, "data"}, - }, - .disk_count_to_return = DEFAULT_COUNT, - .diskgetinfo_return_val = VB2_SUCCESS, - .loadkernel_return_val = {VB2_ERROR_LK_INVALID_KERNEL_FOUND, - VB2_ERROR_LK_NO_KERNEL_FOUND}, - - .expected_recovery_request_val = VB2_RECOVERY_NOT_REQUESTED, - .expected_to_find_disk = DONT_CARE, - .expected_to_load_disk = 0, - .expected_return_val = VB2_ERROR_LK_INVALID_KERNEL_FOUND, - }, - { - .name = "invalid kernel (removable, dev mode)", - .ctx_flags = VB2_CONTEXT_DEVELOPER_MODE, - .want_flags = VB_DISK_FLAG_REMOVABLE, - .disks_to_provide = { - {512, 100, VB_DISK_FLAG_REMOVABLE, "corrupted"}, - {512, 100, VB_DISK_FLAG_REMOVABLE, "data"}, - }, - .disk_count_to_return = DEFAULT_COUNT, - .diskgetinfo_return_val = VB2_SUCCESS, - .loadkernel_return_val = {VB2_ERROR_LK_INVALID_KERNEL_FOUND, - VB2_ERROR_LK_NO_KERNEL_FOUND}, - - .expected_recovery_request_val = VB2_RECOVERY_NOT_REQUESTED, - .expected_to_find_disk = DONT_CARE, - .expected_to_load_disk = 0, - .expected_return_val = VB2_ERROR_LK_INVALID_KERNEL_FOUND, - }, - { - .name = "no kernel (removable)", - .ctx_flags = 0, - .want_flags = VB_DISK_FLAG_REMOVABLE, - .disks_to_provide = { - {512, 100, VB_DISK_FLAG_REMOVABLE, "data"}, - }, - .disk_count_to_return = DEFAULT_COUNT, - .diskgetinfo_return_val = VB2_SUCCESS, - .loadkernel_return_val = {VB2_ERROR_LK_NO_KERNEL_FOUND}, - - .expected_recovery_request_val = VB2_RECOVERY_RW_NO_KERNEL, - .expected_to_find_disk = DONT_CARE, - .expected_to_load_disk = 0, - .expected_return_val = VB2_ERROR_LK_NO_KERNEL_FOUND, - }, - { - .name = "no kernel (removable, rec mode)", - .ctx_flags = VB2_CONTEXT_RECOVERY_MODE, - .want_flags = VB_DISK_FLAG_REMOVABLE, - .disks_to_provide = { - {512, 100, VB_DISK_FLAG_REMOVABLE, "data"}, - }, - .disk_count_to_return = DEFAULT_COUNT, - .diskgetinfo_return_val = VB2_SUCCESS, - .loadkernel_return_val = {VB2_ERROR_LK_NO_KERNEL_FOUND}, - - .expected_recovery_request_val = VB2_RECOVERY_NOT_REQUESTED, - .expected_to_find_disk = DONT_CARE, - .expected_to_load_disk = 0, - .expected_return_val = VB2_ERROR_LK_NO_KERNEL_FOUND, - }, -}; - -test_case_t minios_tests[] = { - { - .name = "pick first fixed drive", - .ctx_flags = 0, - .disks_to_provide = { - {4096, 100, VB_DISK_FLAG_REMOVABLE, 0}, - {4096, 100, VB_DISK_FLAG_FIXED, pickme}, - {4096, 100, VB_DISK_FLAG_FIXED, "holygrail"}, - }, - .disk_count_to_return = DEFAULT_COUNT, - .diskgetinfo_return_val = VB2_SUCCESS, - .loadkernel_return_val = {0}, - .external_expected = {0}, - - .expected_recovery_request_val = VB2_RECOVERY_NOT_REQUESTED, - .expected_to_find_disk = pickme, - .expected_to_load_disk = pickme, - .expected_return_val = VB2_SUCCESS - }, - { - .name = "skip failed fixed drive", - .ctx_flags = 0, - .disks_to_provide = { - {4096, 100, VB_DISK_FLAG_FIXED, "holygrail"}, - {4096, 100, VB_DISK_FLAG_FIXED, pickme}, - }, - .disk_count_to_return = DEFAULT_COUNT, - .diskgetinfo_return_val = VB2_SUCCESS, - .loadkernel_return_val = {VB2_ERROR_LK_INVALID_KERNEL_FOUND, 0}, - .external_expected = {0}, - - .expected_recovery_request_val = VB2_RECOVERY_NOT_REQUESTED, - .expected_to_find_disk = pickme, - .expected_to_load_disk = pickme, - .expected_return_val = VB2_SUCCESS - }, -}; - -/****************************************************************************/ - -/* Mock data */ -static VbDiskInfo mock_disks[MAX_TEST_DISKS]; -static test_case_t *t; -static int load_kernel_calls; -static int lk_normal_calls; -static int lk_minios_calls; -static uint32_t got_recovery_request_val; -static const char *got_find_disk; -static const char *got_load_disk; -static uint32_t got_return_val; -static uint32_t got_external_mismatch; -static uint8_t workbuf[VB2_KERNEL_WORKBUF_RECOMMENDED_SIZE] - __attribute__((aligned(VB2_WORKBUF_ALIGN))); -static struct vb2_context *ctx; -static struct VbSelectAndLoadKernelParams kparams; - -/** - * Reset mock data (for use before each test) - */ -static void ResetMocks(test_case_t *test_case) -{ - TEST_SUCC(vb2api_init(workbuf, sizeof(workbuf), &ctx), - "vb2api_init failed"); - - memset(&kparams, 0, sizeof(kparams)); - - - memset(&mock_disks, 0, sizeof(mock_disks)); - load_kernel_calls = 0; - lk_normal_calls = 0; - lk_minios_calls = 0; - - got_recovery_request_val = VB2_RECOVERY_NOT_REQUESTED; - got_find_disk = 0; - got_load_disk = 0; - got_return_val = 0xdeadbeef; - - t = test_case; -} - -static int is_nonzero(const void *vptr, size_t count) -{ - const char *p = (const char *)vptr; - while (count--) - if (*p++) - return 1; - - return 0; -} - -/****************************************************************************/ -/* Mocked verification functions */ - -vb2_error_t VbExDiskGetInfo(VbDiskInfo **infos_ptr, uint32_t *count, - uint32_t disk_flags) -{ - int i; - int num_disks = 0; - - VB2_DEBUG("My %s\n", __FUNCTION__); - - *infos_ptr = mock_disks; - - for(i = 0; i < MAX_TEST_DISKS; i++) { - if (is_nonzero(&t->disks_to_provide[i], - sizeof(t->disks_to_provide[i]))) { - mock_disks[num_disks].bytes_per_lba = - t->disks_to_provide[i].bytes_per_lba; - mock_disks[num_disks].lba_count = - mock_disks[num_disks].streaming_lba_count = - t->disks_to_provide[i].lba_count; - mock_disks[num_disks].flags = - t->disks_to_provide[i].flags; - mock_disks[num_disks].handle = (VbExDiskHandle_t) - t->disks_to_provide[i].diskname; - VB2_DEBUG(" mock_disk[%d] %" PRIu64 " %" PRIu64 - " %#x %s\n", i, - mock_disks[num_disks].bytes_per_lba, - mock_disks[num_disks].lba_count, - mock_disks[num_disks].flags, - (mock_disks[num_disks].handle - ? (char *)mock_disks[num_disks].handle - : "0")); - num_disks++; - } else { - mock_disks[num_disks].handle = - (VbExDiskHandle_t)"INVALID"; - } - } - - if (t->disk_count_to_return >= 0) - *count = t->disk_count_to_return; - else - *count = num_disks; - - VB2_DEBUG(" *count=%" PRIu32 "\n", *count); - VB2_DEBUG(" return %#x\n", t->diskgetinfo_return_val); - - return t->diskgetinfo_return_val; -} - -vb2_error_t VbExDiskFreeInfo(VbDiskInfo *infos, - VbExDiskHandle_t preserve_handle) -{ - got_load_disk = (const char *)preserve_handle; - VB2_DEBUG("%s(): got_load_disk = %s\n", __FUNCTION__, - got_load_disk ? got_load_disk : "0"); - return VB2_SUCCESS; -} - -static vb2_error_t LoadKernelImpl(struct vb2_context *c, - VbSelectAndLoadKernelParams *params, - VbDiskInfo *disk_info) -{ - got_find_disk = (const char *)params->disk_handle; - VB2_DEBUG("%s(%d): got_find_disk = %s\n", __FUNCTION__, - load_kernel_calls, - got_find_disk ? got_find_disk : "0"); - if (t->external_expected[load_kernel_calls] != - !!(disk_info->flags & VB_DISK_FLAG_EXTERNAL_GPT)) - got_external_mismatch++; - return t->loadkernel_return_val[load_kernel_calls++]; -} - -vb2_error_t LoadKernel(struct vb2_context *c, - VbSelectAndLoadKernelParams *params, - VbDiskInfo *disk_info) -{ - lk_normal_calls++; - return LoadKernelImpl(c, params, disk_info); -} - -vb2_error_t LoadMiniOsKernel(struct vb2_context *c, - VbSelectAndLoadKernelParams *params, - VbDiskInfo *disk_info, uint32_t minios_flags) -{ - lk_minios_calls++; - return LoadKernelImpl(c, params, disk_info); -} - -void vb2_nv_set(struct vb2_context *c, - enum vb2_nv_param param, - uint32_t value) -{ - if (param != VB2_NV_RECOVERY_REQUEST) - return; - VB2_DEBUG("%s(): got_recovery_request_val = %d (%#x)\n", __FUNCTION__, - value, value); - got_recovery_request_val = value; -} - -/****************************************************************************/ - -static void VbTryLoadKernelTest(void) -{ - int i; - int num_tests = ARRAY_SIZE(normal_tests); - - for (i = 0; i < num_tests; i++) { - printf("Test case: %s ...\n", normal_tests[i].name); - ResetMocks(&normal_tests[i]); - ctx->flags = t->ctx_flags; - TEST_EQ(VbTryLoadKernel(ctx, t->want_flags, &kparams), - t->expected_return_val, " return value"); - TEST_EQ(got_recovery_request_val, - t->expected_recovery_request_val, " recovery_request"); - if (t->expected_to_find_disk != DONT_CARE) { - TEST_PTR_EQ(got_find_disk, t->expected_to_find_disk, - " find disk"); - TEST_PTR_EQ(got_load_disk, t->expected_to_load_disk, - " load disk"); - } - TEST_EQ(got_external_mismatch, 0, " external GPT errors"); - TEST_EQ(!!(ctx->flags & VB2_CONTEXT_DISABLE_TPM), 0, - " ctx flag disable_tpm"); - } - TEST_EQ(lk_normal_calls, load_kernel_calls, " LoadKernel called"); - TEST_EQ(lk_minios_calls, 0, " LoadMiniOsKernel not called"); -} - -static void VbTryLoadMiniOsKernelTest(void) -{ - int i; - int num_tests = ARRAY_SIZE(minios_tests); - - for (i = 0; i < num_tests; i++) { - printf("Test case: %s ...\n", minios_tests[i].name); - ResetMocks(&minios_tests[i]); - ctx->flags = t->ctx_flags; - TEST_EQ(VbTryLoadMiniOsKernel(ctx, 0, &kparams), - t->expected_return_val, " return value"); - TEST_EQ(got_recovery_request_val, - t->expected_recovery_request_val, " recovery_request"); - if (t->expected_to_find_disk != DONT_CARE) { - TEST_PTR_EQ(got_find_disk, t->expected_to_find_disk, - " find disk"); - TEST_PTR_EQ(got_load_disk, t->expected_to_load_disk, - " load disk"); - } - TEST_EQ(got_external_mismatch, 0, " external GPT errors"); - TEST_EQ(!!(ctx->flags & VB2_CONTEXT_DISABLE_TPM), 1, - " ctx flag disable_tpm"); - } - TEST_EQ(lk_normal_calls, 0, " LoadKernel not called"); - TEST_EQ(lk_minios_calls, load_kernel_calls, - " LoadMiniOsKernel called"); -} - -int main(void) -{ - VbTryLoadKernelTest(); - VbTryLoadMiniOsKernelTest(); - - return gTestSuccess ? 0 : 255; -} diff --git a/tests/vboot_kernel2_tests.c b/tests/vboot_kernel2_tests.c deleted file mode 100644 index 1f2f7a85..00000000 --- a/tests/vboot_kernel2_tests.c +++ /dev/null @@ -1,443 +0,0 @@ -/* Copyright 2021 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 miniOS kernel selection, loading, verification, and booting. - */ - -#include "2api.h" -#include "2common.h" -#include "2misc.h" -#include "2nvstorage.h" -#include "2secdata.h" -#include "common/boot_mode.h" -#include "common/tests.h" -#include "load_kernel_fw.h" -#include "vboot_api.h" - -#define MAX_MOCK_KERNELS 10 -#define KBUF_SIZE 65536 - -/* Internal struct to simulate a stream for sector-based disks */ -struct disk_stream { - /* Disk handle */ - VbExDiskHandle_t handle; - - /* Next sector to read */ - uint64_t sector; - - /* Number of sectors left */ - uint64_t sectors_left; -}; - -/* Represent a "kernel" located on the disk */ -struct mock_kernel { - /* Sector where the kernel begins */ - uint64_t sector; - - /* Return value from vb2_load_partition */ - vb2_error_t rv; - - /* Number of times the sector was read */ - int read_count; -}; - -/* Mock data */ -static struct vb2_context *ctx; -static struct vb2_shared_data *sd; -static struct vb2_workbuf wb; -static uint8_t workbuf[VB2_KERNEL_WORKBUF_RECOMMENDED_SIZE] - __attribute__((aligned(VB2_WORKBUF_ALIGN))); - -static VbSelectAndLoadKernelParams lkp; -static VbDiskInfo disk_info; -static struct vb2_keyblock kbh; -static struct vb2_kernel_preamble kph; -static uint8_t kernel_buffer[80000]; - -static struct mock_kernel kernels[MAX_MOCK_KERNELS]; -static int kernel_count; -static struct mock_kernel *cur_kernel; - -static void add_mock_kernel(uint64_t sector, vb2_error_t rv) -{ - if (kernel_count >= ARRAY_SIZE(kernels)) { - TEST_TRUE(0, " kernel_count ran out of entries!"); - return; - } - - kernels[kernel_count].sector = sector; - kernels[kernel_count].rv = rv; - kernel_count++; -} - -static void reset_common_data(void) -{ - TEST_SUCC(vb2api_init(workbuf, sizeof(workbuf), &ctx), - "vb2api_init failed"); - vb2_workbuf_from_ctx(ctx, &wb); - vb2_nv_init(ctx); - vb2api_secdata_kernel_create(ctx); - vb2_secdata_kernel_init(ctx); - ctx->flags = VB2_CONTEXT_RECOVERY_MODE; - - SET_BOOT_MODE(ctx, VB2_BOOT_MODE_MANUAL_RECOVERY, - VB2_RECOVERY_RO_MANUAL); - - sd = vb2_get_sd(ctx); - sd->kernel_version_secdata = 0xabcdef | (1 << 24); - - memset(&lkp, 0, sizeof(lkp)); - lkp.kernel_buffer = kernel_buffer; - lkp.kernel_buffer_size = sizeof(kernel_buffer); - lkp.disk_handle = (VbExDiskHandle_t)1; - - memset(&disk_info, 0, sizeof(disk_info)); - disk_info.bytes_per_lba = 512; - disk_info.lba_count = 1024; - disk_info.handle = lkp.disk_handle; - - memset(&kbh, 0, sizeof(kbh)); - kbh.data_key.key_version = 2; - kbh.keyblock_flags = VB2_KEYBLOCK_FLAG_DEVELOPER_0 - | VB2_KEYBLOCK_FLAG_DEVELOPER_1 - | VB2_KEYBLOCK_FLAG_RECOVERY_1 - | VB2_KEYBLOCK_FLAG_MINIOS_1; - kbh.keyblock_size = sizeof(kbh); - - memset(&kph, 0, sizeof(kph)); - kph.kernel_version = 1; - kph.preamble_size = 4096 - kbh.keyblock_size; - kph.body_signature.data_size = 0; - kph.bootloader_address = 0xbeadd008; - kph.bootloader_size = 0x1234; - - - memset(&kernels, 0, sizeof(kernels)); - kernel_count = 0; - cur_kernel = NULL; -} - -/* Mocks */ - -vb2_error_t VbExStreamOpen(VbExDiskHandle_t handle, uint64_t lba_start, - uint64_t lba_count, VbExStream_t *stream) -{ - struct disk_stream *s; - uint64_t i; - - if (!handle) { - *stream = NULL; - return VB2_ERROR_UNKNOWN; - } - - if (lba_start + lba_count > disk_info.lba_count) - return VB2_ERROR_UNKNOWN; - - s = malloc(sizeof(*s)); - s->handle = handle; - s->sector = lba_start; - s->sectors_left = lba_count; - - *stream = (void *)s; - - for (i = 0; i < kernel_count; i++) { - if (kernels[i].sector == lba_start) - cur_kernel = &kernels[i]; - } - - return VB2_SUCCESS; -} - -vb2_error_t VbExStreamRead(VbExStream_t stream, uint32_t bytes, void *buffer) -{ - struct disk_stream *s = (struct disk_stream *)stream; - uint64_t sectors; - uint64_t i; - - if (!s) - return VB2_ERROR_UNKNOWN; - - /* For now, require reads to be a multiple of the LBA size */ - if (bytes % disk_info.bytes_per_lba) - return VB2_ERROR_UNKNOWN; - - /* Fail on overflow */ - sectors = bytes / disk_info.bytes_per_lba; - if (sectors > s->sectors_left) - return VB2_ERROR_UNKNOWN; - - memset(buffer, 0, bytes); - for (i = 0; i < kernel_count; i++) { - if (kernels[i].sector >= s->sector && - kernels[i].sector < s->sector + sectors) { - VB2_DEBUG("Simulating kernel %" PRIu64 " match\n", i); - uint64_t buf_offset = (kernels[i].sector - s->sector) - * disk_info.bytes_per_lba; - memcpy(buffer + buf_offset, VB2_KEYBLOCK_MAGIC, - VB2_KEYBLOCK_MAGIC_SIZE); - kernels[i].read_count++; - TEST_TRUE(kernels[i].read_count <= 2, - " Max read count exceeded"); - } - } - - s->sector += sectors; - s->sectors_left -= sectors; - - return VB2_SUCCESS; -} - -void VbExStreamClose(VbExStream_t stream) -{ - free(stream); -} - -vb2_error_t vb2_unpack_key_buffer(struct vb2_public_key *key, - const uint8_t *buf, uint32_t size) -{ - return cur_kernel->rv; -} - -vb2_error_t vb2_verify_keyblock(struct vb2_keyblock *block, uint32_t size, - const struct vb2_public_key *key, - const struct vb2_workbuf *w) -{ - /* Use this as an opportunity to override the keyblock */ - memcpy((void *)block, &kbh, sizeof(kbh)); - - return cur_kernel->rv; -} - -vb2_error_t vb2_verify_keyblock_hash(const struct vb2_keyblock *block, - uint32_t size, - const struct vb2_workbuf *w) -{ - /* Use this as an opportunity to override the keyblock */ - memcpy((void *)block, &kbh, sizeof(kbh)); - - return cur_kernel->rv; -} - -vb2_error_t vb2_verify_kernel_preamble(struct vb2_kernel_preamble *preamble, - uint32_t size, const struct vb2_public_key *key, - const struct vb2_workbuf *w) -{ - /* Use this as an opportunity to override the preamble */ - memcpy((void *)preamble, &kph, sizeof(kph)); - - return cur_kernel->rv; -} - -vb2_error_t vb2_verify_data(const uint8_t *data, uint32_t size, - struct vb2_signature *sig, - const struct vb2_public_key *key, - const struct vb2_workbuf *w) -{ - return cur_kernel->rv; -} - -vb2_error_t vb2_digest_buffer(const uint8_t *buf, uint32_t size, - enum vb2_hash_algorithm hash_alg, uint8_t *digest, - uint32_t digest_size) -{ - return cur_kernel->rv; -} - -/* Make sure nothing tested here ever calls this directly. */ -void vb2api_fail(struct vb2_context *c, uint8_t reason, uint8_t subcode) -{ - TEST_TRUE(0, " called vb2api_fail()"); -} - -/* Tests */ - -static void load_minios_kernel_tests(void) -{ - reset_common_data(); - disk_info.bytes_per_lba = KBUF_SIZE; - disk_info.lba_count = 1; - add_mock_kernel(0, VB2_SUCCESS); - TEST_SUCC(LoadMiniOsKernel(ctx, &lkp, &disk_info, 0), - "{valid kernel}"); - - reset_common_data(); - disk_info.bytes_per_lba = KBUF_SIZE; - disk_info.lba_count = 1; - TEST_EQ(LoadMiniOsKernel(ctx, &lkp, &disk_info, 0), - VB2_ERROR_LK_NO_KERNEL_FOUND, "{no kernel}"); - - reset_common_data(); - disk_info.bytes_per_lba = KBUF_SIZE; - disk_info.lba_count = 2; - add_mock_kernel(1, VB2_SUCCESS); - TEST_SUCC(LoadMiniOsKernel(ctx, &lkp, &disk_info, 0), - "{no kernel, valid kernel}"); - TEST_EQ(cur_kernel->sector, 1, " select kernel"); - - reset_common_data(); - disk_info.bytes_per_lba = KBUF_SIZE; - disk_info.lba_count = 2; - add_mock_kernel(0, VB2_ERROR_MOCK); - add_mock_kernel(1, VB2_SUCCESS); - TEST_SUCC(LoadMiniOsKernel(ctx, &lkp, &disk_info, 0), - "{invalid kernel, valid kernel}"); - TEST_EQ(cur_kernel->sector, 1, " select second kernel"); - - reset_common_data(); - disk_info.bytes_per_lba = KBUF_SIZE; - disk_info.lba_count = 2; - add_mock_kernel(0, VB2_ERROR_MOCK); - add_mock_kernel(1, VB2_ERROR_MOCK); - TEST_EQ(LoadMiniOsKernel(ctx, &lkp, &disk_info, 0), - VB2_ERROR_LK_NO_KERNEL_FOUND, - "{invalid kernel, invalid kernel}"); - - reset_common_data(); - disk_info.bytes_per_lba = KBUF_SIZE; - disk_info.lba_count = 2; - add_mock_kernel(0, VB2_SUCCESS); - add_mock_kernel(1, VB2_SUCCESS); - TEST_SUCC(LoadMiniOsKernel(ctx, &lkp, &disk_info, 0), - "{valid kernel, valid kernel} minios_priority=0"); - TEST_EQ(cur_kernel->sector, 0, " select first kernel"); - - reset_common_data(); - disk_info.bytes_per_lba = KBUF_SIZE; - disk_info.lba_count = 2; - add_mock_kernel(0, VB2_SUCCESS); - add_mock_kernel(1, VB2_SUCCESS); - vb2_nv_set(ctx, VB2_NV_MINIOS_PRIORITY, 1); - TEST_SUCC(LoadMiniOsKernel(ctx, &lkp, &disk_info, 0), - "{valid kernel, valid kernel} minios_priority=1"); - TEST_EQ(cur_kernel->sector, 1, " select second kernel"); - - reset_common_data(); - disk_info.bytes_per_lba = KBUF_SIZE; - disk_info.lba_count = 2; - add_mock_kernel(0, VB2_SUCCESS); - add_mock_kernel(1, VB2_SUCCESS); - TEST_SUCC(LoadMiniOsKernel(ctx, &lkp, &disk_info, - VB_MINIOS_FLAG_NON_ACTIVE), - "{valid kernel, valid kernel} minios_priority=0 non-active"); - TEST_EQ(cur_kernel->sector, 1, " select second kernel"); - - reset_common_data(); - disk_info.bytes_per_lba = KBUF_SIZE; - disk_info.lba_count = 2; - add_mock_kernel(0, VB2_ERROR_MOCK); - add_mock_kernel(1, VB2_SUCCESS); - vb2_nv_set(ctx, VB2_NV_MINIOS_PRIORITY, 1); - TEST_EQ(LoadMiniOsKernel(ctx, &lkp, &disk_info, - VB_MINIOS_FLAG_NON_ACTIVE), - VB2_ERROR_LK_NO_KERNEL_FOUND, - "{invalid kernel, valid kernel} minios_priority=1 non-active"); - - reset_common_data(); - disk_info.bytes_per_lba = VB2_KEYBLOCK_MAGIC_SIZE; - disk_info.lba_count = 4; - add_mock_kernel(1, VB2_SUCCESS); - TEST_EQ(LoadMiniOsKernel(ctx, &lkp, &disk_info, 0), - VB2_ERROR_LK_NO_KERNEL_FOUND, - "valid kernel header near start of disk (disk too small)"); - - reset_common_data(); - disk_info.bytes_per_lba = VB2_KEYBLOCK_MAGIC_SIZE; - disk_info.lba_count = 1000; - add_mock_kernel(999, VB2_SUCCESS); - TEST_EQ(LoadMiniOsKernel(ctx, &lkp, &disk_info, 0), - VB2_ERROR_LK_NO_KERNEL_FOUND, - "valid kernel header near end of disk"); - - reset_common_data(); - disk_info.bytes_per_lba = 1024; - disk_info.lba_count = 128; - add_mock_kernel(63, VB2_SUCCESS); - TEST_SUCC(LoadMiniOsKernel(ctx, &lkp, &disk_info, 0), - "start/end overlap assuming >128 MB search range (start)"); - - reset_common_data(); - disk_info.bytes_per_lba = 1024; - disk_info.lba_count = 128; - add_mock_kernel(64, VB2_SUCCESS); - TEST_SUCC(LoadMiniOsKernel(ctx, &lkp, &disk_info, 0), - "start/end overlap assuming >128 MB search range (end)"); - - reset_common_data(); - disk_info.bytes_per_lba = 128; - disk_info.lba_count = 1024; - add_mock_kernel(3, VB2_SUCCESS); - TEST_SUCC(LoadMiniOsKernel(ctx, &lkp, &disk_info, 0), - "kernel at last sector in batch assuming 512 KB batches"); - - reset_common_data(); - disk_info.bytes_per_lba = 256; - disk_info.lba_count = 1024; - add_mock_kernel(3, VB2_SUCCESS); - TEST_SUCC(LoadMiniOsKernel(ctx, &lkp, &disk_info, 0), - "kernel at last sector in batch assuming 1 MB batches"); - - reset_common_data(); - disk_info.bytes_per_lba = 512; - disk_info.lba_count = 1024; - add_mock_kernel(3, VB2_SUCCESS); - TEST_SUCC(LoadMiniOsKernel(ctx, &lkp, &disk_info, 0), - "kernel at last sector in batch assuming 2 MB batches"); - - reset_common_data(); - kbh.keyblock_flags = VB2_KEYBLOCK_FLAG_DEVELOPER_0 - | VB2_KEYBLOCK_FLAG_RECOVERY_1 - | VB2_KEYBLOCK_FLAG_MINIOS_1; - disk_info.bytes_per_lba = KBUF_SIZE; - disk_info.lba_count = 2; - add_mock_kernel(0, VB2_SUCCESS); - TEST_SUCC(LoadMiniOsKernel(ctx, &lkp, &disk_info, 0), - "kernel with minios keyblock flag"); - - reset_common_data(); - kbh.keyblock_flags = VB2_KEYBLOCK_FLAG_DEVELOPER_0 - | VB2_KEYBLOCK_FLAG_RECOVERY_1 - | VB2_KEYBLOCK_FLAG_MINIOS_0; - disk_info.bytes_per_lba = KBUF_SIZE; - disk_info.lba_count = 2; - add_mock_kernel(0, VB2_SUCCESS); - TEST_EQ(LoadMiniOsKernel(ctx, &lkp, &disk_info, 0), - VB2_ERROR_LK_NO_KERNEL_FOUND, - "kernel with !minios keyblock flag"); - - reset_common_data(); - disk_info.bytes_per_lba = KBUF_SIZE; - disk_info.lba_count = 2; - add_mock_kernel(0, VB2_SUCCESS); - sd->kernel_version_secdata = 5 << 24; - kph.kernel_version = 4; - TEST_EQ(LoadMiniOsKernel(ctx, &lkp, &disk_info, 0), - VB2_ERROR_LK_NO_KERNEL_FOUND, - "kernel version too old"); - - reset_common_data(); - disk_info.bytes_per_lba = KBUF_SIZE; - disk_info.lba_count = 2; - add_mock_kernel(0, VB2_SUCCESS); - sd->kernel_version_secdata = 5 << 24; - kph.kernel_version = 0x100; - TEST_EQ(LoadMiniOsKernel(ctx, &lkp, &disk_info, 0), - VB2_ERROR_LK_NO_KERNEL_FOUND, - "kernel version greater than 0xff"); - - reset_common_data(); - disk_info.bytes_per_lba = KBUF_SIZE; - disk_info.lba_count = 2; - add_mock_kernel(0, VB2_SUCCESS); - sd->kernel_version_secdata = 5 << 24; - kph.kernel_version = 6; - TEST_SUCC(LoadMiniOsKernel(ctx, &lkp, &disk_info, 0), - "newer kernel version"); -} - -int main(void) -{ - load_minios_kernel_tests(); - - return gTestSuccess ? 0 : 255; -} diff --git a/tests/vboot_kernel_tests.c b/tests/vboot_kernel_tests.c deleted file mode 100644 index e4162fbf..00000000 --- a/tests/vboot_kernel_tests.c +++ /dev/null @@ -1,621 +0,0 @@ -/* Copyright (c) 2013 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 vboot_kernel.c - */ - -#include "2api.h" -#include "2common.h" -#include "2misc.h" -#include "2nvstorage.h" -#include "2secdata.h" -#include "2secdata_struct.h" -#include "cgptlib.h" -#include "cgptlib_internal.h" -#include "common/boot_mode.h" -#include "common/tests.h" -#include "gpt.h" -#include "load_kernel_fw.h" -#include "vboot_api.h" - -/* Mock kernel partition */ -struct mock_part { - uint32_t start; - uint32_t size; -}; - -/* Partition list; ends with a 0-size partition. */ -#define MOCK_PART_COUNT 8 -static struct mock_part mock_parts[MOCK_PART_COUNT]; -static int mock_part_next; - -/* Mock data */ -static uint8_t kernel_buffer[80000]; -static int disk_read_to_fail; -static int gpt_init_fail; -static int keyblock_verify_fail; /* 0=ok, 1=sig, 2=hash */ -static int preamble_verify_fail; -static int verify_data_fail; -static int unpack_key_fail; -static int gpt_flag_external; - -static struct vb2_gbb_header gbb; -static VbSelectAndLoadKernelParams lkp; -static VbDiskInfo disk_info; -static struct vb2_keyblock kbh; -static struct vb2_kernel_preamble kph; -static struct vb2_secdata_fwmp *fwmp; -static uint8_t mock_digest[VB2_SHA256_DIGEST_SIZE] = {12, 34, 56, 78}; -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_packed_key mock_key; - -/** - * Reset mock data (for use before each test) - */ -static void ResetMocks(void) -{ - disk_read_to_fail = -1; - - gpt_init_fail = 0; - keyblock_verify_fail = 0; - preamble_verify_fail = 0; - verify_data_fail = 0; - unpack_key_fail = 0; - - gpt_flag_external = 0; - - memset(&gbb, 0, sizeof(gbb)); - gbb.major_version = VB2_GBB_MAJOR_VER; - gbb.minor_version = VB2_GBB_MINOR_VER; - gbb.flags = 0; - - memset(&lkp, 0, sizeof(lkp)); - lkp.kernel_buffer = kernel_buffer; - lkp.kernel_buffer_size = sizeof(kernel_buffer); - lkp.disk_handle = (VbExDiskHandle_t)1; - - memset(&disk_info, 0, sizeof(disk_info)); - disk_info.bytes_per_lba = 512; - disk_info.streaming_lba_count = 1024; - disk_info.lba_count = 1024; - disk_info.handle = lkp.disk_handle; - - memset(&kbh, 0, sizeof(kbh)); - kbh.data_key.key_version = 2; - kbh.keyblock_flags = -1; - kbh.keyblock_size = sizeof(kbh); - - memset(&kph, 0, sizeof(kph)); - kph.kernel_version = 1; - kph.preamble_size = 4096 - kbh.keyblock_size; - kph.body_signature.data_size = 70144; - kph.bootloader_address = 0xbeadd008; - kph.bootloader_size = 0x1234; - - memset(mock_parts, 0, sizeof(mock_parts)); - mock_parts[0].start = 100; - mock_parts[0].size = 150; /* 75 KB */ - mock_part_next = 0; - - memset(&mock_key, 0, sizeof(mock_key)); - - TEST_SUCC(vb2api_init(workbuf, sizeof(workbuf), &ctx), - "vb2api_init failed"); - vb2_nv_init(ctx); - - sd = vb2_get_sd(ctx); - sd->kernel_version_secdata = 0x20001; - - /* CRC will be invalid after here, but nobody's checking */ - sd->status |= VB2_SD_STATUS_SECDATA_FWMP_INIT; - fwmp = (struct vb2_secdata_fwmp *)ctx->secdata_fwmp; - memcpy(&fwmp->dev_key_hash, mock_digest, sizeof(fwmp->dev_key_hash)); - - SET_BOOT_MODE(ctx, VB2_BOOT_MODE_NORMAL); - - // TODO: more workbuf fields - flags, secdata_firmware - - vb2api_secdata_kernel_create(ctx); - vb2_secdata_kernel_init(ctx); - vb2_secdata_kernel_set(ctx, VB2_SECDATA_KERNEL_FLAGS, - VB2_SECDATA_KERNEL_FLAG_HWCRYPTO_ALLOWED); -} - -/* Mocks */ -struct vb2_gbb_header *vb2_get_gbb(struct vb2_context *c) -{ - return &gbb; -} - -vb2_error_t vb2ex_read_resource(struct vb2_context *c, - enum vb2_resource_index index, uint32_t offset, - void *buf, uint32_t size) -{ - memset(buf, 0, size); - return VB2_SUCCESS; -} - -vb2_error_t vb2_gbb_read_root_key(struct vb2_context *c, - struct vb2_packed_key **keyp, uint32_t *size, - struct vb2_workbuf *wb) -{ - *keyp = &mock_key; - return VB2_SUCCESS; -} - -vb2_error_t vb2_gbb_read_recovery_key(struct vb2_context *c, - struct vb2_packed_key **keyp, - uint32_t *size, struct vb2_workbuf *wb) -{ - *keyp = &mock_key; - return VB2_SUCCESS; -} - -vb2_error_t VbExDiskRead(VbExDiskHandle_t h, uint64_t lba_start, - uint64_t lba_count, void *buffer) -{ - if ((int)lba_start == disk_read_to_fail) - return VB2_ERROR_MOCK; - - return VB2_SUCCESS; -} - -int AllocAndReadGptData(VbExDiskHandle_t disk_handle, GptData *gptdata) -{ - return GPT_SUCCESS; -} - -int GptInit(GptData *gpt) -{ - return gpt_init_fail; -} - -int GptNextKernelEntry(GptData *gpt, uint64_t *start_sector, uint64_t *size) -{ - struct mock_part *p = mock_parts + mock_part_next; - - if (!p->size) - return GPT_ERROR_NO_VALID_KERNEL; - - if (gpt->flags & GPT_FLAG_EXTERNAL) - gpt_flag_external++; - - gpt->current_kernel = mock_part_next; - *start_sector = p->start; - *size = p->size; - mock_part_next++; - return GPT_SUCCESS; -} - -int GptUpdateKernelEntry(GptData *gpt, uint32_t update_type) -{ - return GPT_SUCCESS; -} - -int WriteAndFreeGptData(VbExDiskHandle_t disk_handle, GptData *gptdata) -{ - return GPT_SUCCESS; -} - -void GetCurrentKernelUniqueGuid(GptData *gpt, void *dest) -{ - static char fake_guid[] = "FakeGuid"; - - memcpy(dest, fake_guid, sizeof(fake_guid)); -} - -vb2_error_t vb2_unpack_key_buffer(struct vb2_public_key *key, - const uint8_t *buf, uint32_t size) -{ - if (--unpack_key_fail == 0) - return VB2_ERROR_MOCK; - - return VB2_SUCCESS; -} - -vb2_error_t vb2_verify_keyblock(struct vb2_keyblock *block, uint32_t size, - const struct vb2_public_key *key, - const struct vb2_workbuf *wb) -{ - if (keyblock_verify_fail >= 1) - return VB2_ERROR_MOCK; - - /* Use this as an opportunity to override the keyblock */ - memcpy((void *)block, &kbh, sizeof(kbh)); - return VB2_SUCCESS; -} - -vb2_error_t vb2_verify_keyblock_hash(const struct vb2_keyblock *block, - uint32_t size, - const struct vb2_workbuf *wb) -{ - if (keyblock_verify_fail >= 2) - return VB2_ERROR_MOCK; - - /* Use this as an opportunity to override the keyblock */ - memcpy((void *)block, &kbh, sizeof(kbh)); - return VB2_SUCCESS; -} - -vb2_error_t vb2_verify_kernel_preamble(struct vb2_kernel_preamble *preamble, - uint32_t size, const struct vb2_public_key *key, - const struct vb2_workbuf *wb) -{ - if (preamble_verify_fail) - return VB2_ERROR_MOCK; - - /* Use this as an opportunity to override the preamble */ - memcpy((void *)preamble, &kph, sizeof(kph)); - return VB2_SUCCESS; -} - -vb2_error_t vb2_verify_data(const uint8_t *data, uint32_t size, - struct vb2_signature *sig, - const struct vb2_public_key *key, - const struct vb2_workbuf *wb) -{ - if (verify_data_fail) - return VB2_ERROR_MOCK; - - return VB2_SUCCESS; -} - -vb2_error_t vb2_digest_buffer(const uint8_t *buf, uint32_t size, - enum vb2_hash_algorithm hash_alg, uint8_t *digest, - uint32_t digest_size) -{ - memcpy(digest, mock_digest, sizeof(mock_digest)); - return VB2_SUCCESS; -} - -/* Make sure nothing tested here ever calls this directly. */ -void vb2api_fail(struct vb2_context *c, uint8_t reason, uint8_t subcode) -{ - TEST_TRUE(0, " called vb2api_fail()"); -} - -static void TestLoadKernel(int expect_retval, const char *test_name) -{ - TEST_EQ(LoadKernel(ctx, &lkp, &disk_info), expect_retval, test_name); -} - -/** - * Trivial invalid calls to LoadKernel() - */ -static void InvalidParamsTest(void) -{ - ResetMocks(); - gpt_init_fail = 1; - TestLoadKernel(VB2_ERROR_LK_NO_KERNEL_FOUND, "Bad GPT"); - - /* This causes the stream open call to fail */ - ResetMocks(); - lkp.disk_handle = NULL; - disk_info.handle = NULL; - TestLoadKernel(VB2_ERROR_LK_INVALID_KERNEL_FOUND, "Bad disk handle"); -} - -static void LoadKernelTest(void) -{ - ResetMocks(); - TestLoadKernel(0, "First kernel good"); - TEST_EQ(lkp.partition_number, 1, " part num"); - TEST_EQ(lkp.bootloader_address, 0xbeadd008, " bootloader addr"); - TEST_EQ(lkp.bootloader_size, 0x1234, " bootloader size"); - TEST_STR_EQ((char *)lkp.partition_guid, "FakeGuid", " guid"); - TEST_EQ(gpt_flag_external, 0, "GPT was internal"); - TEST_NEQ(sd->flags & VB2_SD_FLAG_KERNEL_SIGNED, 0, " use signature"); - - ResetMocks(); - mock_parts[1].start = 300; - mock_parts[1].size = 150; - TestLoadKernel(0, "Two good kernels"); - TEST_EQ(lkp.partition_number, 1, " part num"); - TEST_EQ(mock_part_next, 1, " didn't read second one"); - - /* Fail if no kernels found */ - ResetMocks(); - mock_parts[0].size = 0; - TestLoadKernel(VB2_ERROR_LK_NO_KERNEL_FOUND, "No kernels"); - - /* Skip kernels which are too small */ - ResetMocks(); - mock_parts[0].size = 10; - TestLoadKernel(VB2_ERROR_LK_INVALID_KERNEL_FOUND, "Too small"); - - ResetMocks(); - disk_read_to_fail = 100; - TestLoadKernel(VB2_ERROR_LK_INVALID_KERNEL_FOUND, - "Fail reading kernel start"); - - ResetMocks(); - keyblock_verify_fail = 1; - TestLoadKernel(VB2_ERROR_LK_INVALID_KERNEL_FOUND, - "Fail key block sig"); - - /* In dev mode, fail if hash is bad too */ - ResetMocks(); - SET_BOOT_MODE(ctx, VB2_BOOT_MODE_DEVELOPER); - keyblock_verify_fail = 2; - TestLoadKernel(VB2_ERROR_LK_INVALID_KERNEL_FOUND, - "Fail key block dev hash"); - - /* But just bad sig is ok */ - ResetMocks(); - SET_BOOT_MODE(ctx, VB2_BOOT_MODE_DEVELOPER); - keyblock_verify_fail = 1; - TestLoadKernel(0, "Succeed keyblock dev sig"); - TEST_EQ(sd->flags & VB2_SD_FLAG_KERNEL_SIGNED, 0, " use hash"); - - /* In dev mode and requiring signed kernel, fail if sig is bad */ - ResetMocks(); - SET_BOOT_MODE(ctx, VB2_BOOT_MODE_DEVELOPER); - vb2_nv_set(ctx, VB2_NV_DEV_BOOT_SIGNED_ONLY, 1); - keyblock_verify_fail = 1; - TestLoadKernel(VB2_ERROR_LK_INVALID_KERNEL_FOUND, - "Fail key block dev sig"); - - ResetMocks(); - SET_BOOT_MODE(ctx, VB2_BOOT_MODE_DEVELOPER); - fwmp->flags |= VB2_SECDATA_FWMP_DEV_ENABLE_OFFICIAL_ONLY; - keyblock_verify_fail = 1; - TestLoadKernel(VB2_ERROR_LK_INVALID_KERNEL_FOUND, - "Fail key block dev sig fwmp"); - - /* Check keyblock flags */ - ResetMocks(); - kbh.keyblock_flags = VB2_KEYBLOCK_FLAG_RECOVERY_0 - | VB2_KEYBLOCK_FLAG_DEVELOPER_1 - | VB2_KEYBLOCK_FLAG_MINIOS_0; - TestLoadKernel(VB2_ERROR_LK_INVALID_KERNEL_FOUND, - "Keyblock dev flag mismatch"); - - ResetMocks(); - kbh.keyblock_flags = VB2_KEYBLOCK_FLAG_RECOVERY_1 - | VB2_KEYBLOCK_FLAG_DEVELOPER_0 - | VB2_KEYBLOCK_FLAG_MINIOS_0; - TestLoadKernel(VB2_ERROR_LK_INVALID_KERNEL_FOUND, - "Keyblock rec flag mismatch"); - - ResetMocks(); - kbh.keyblock_flags = VB2_KEYBLOCK_FLAG_RECOVERY_0 - | VB2_KEYBLOCK_FLAG_DEVELOPER_0 - | VB2_KEYBLOCK_FLAG_MINIOS_1; - TestLoadKernel(VB2_ERROR_LK_INVALID_KERNEL_FOUND, - "Keyblock minios flag mismatch"); - - ResetMocks(); - SET_BOOT_MODE(ctx, VB2_BOOT_MODE_MANUAL_RECOVERY, - VB2_RECOVERY_RO_MANUAL); - kbh.keyblock_flags = VB2_KEYBLOCK_FLAG_RECOVERY_1 - | VB2_KEYBLOCK_FLAG_DEVELOPER_1 - | VB2_KEYBLOCK_FLAG_MINIOS_0; - TestLoadKernel(VB2_ERROR_LK_INVALID_KERNEL_FOUND, - "Keyblock recdev flag mismatch"); - - ResetMocks(); - SET_BOOT_MODE(ctx, VB2_BOOT_MODE_MANUAL_RECOVERY, - VB2_RECOVERY_RO_MANUAL); - kbh.keyblock_flags = VB2_KEYBLOCK_FLAG_RECOVERY_1 - | VB2_KEYBLOCK_FLAG_DEVELOPER_0 - | VB2_KEYBLOCK_FLAG_MINIOS_0; - TestLoadKernel(0, "Keyblock rec flag okay"); - - ResetMocks(); - SET_BOOT_MODE(ctx, VB2_BOOT_MODE_MANUAL_RECOVERY, - VB2_RECOVERY_RO_MANUAL); - ctx->flags |= VB2_CONTEXT_DEVELOPER_MODE; - kbh.keyblock_flags = VB2_KEYBLOCK_FLAG_RECOVERY_1 - | VB2_KEYBLOCK_FLAG_DEVELOPER_0 - | VB2_KEYBLOCK_FLAG_MINIOS_0; - TestLoadKernel(VB2_ERROR_LK_INVALID_KERNEL_FOUND, - "Keyblock rec!dev flag mismatch"); - - ResetMocks(); - SET_BOOT_MODE(ctx, VB2_BOOT_MODE_MANUAL_RECOVERY, - VB2_RECOVERY_RO_MANUAL); - ctx->flags |= VB2_CONTEXT_DEVELOPER_MODE; - kbh.keyblock_flags = VB2_KEYBLOCK_FLAG_RECOVERY_1 - | VB2_KEYBLOCK_FLAG_DEVELOPER_1 - | VB2_KEYBLOCK_FLAG_MINIOS_0; - TestLoadKernel(0, "Keyblock recdev flag okay"); - - /* Check keyblock flags (dev mode + signed kernel required) */ - ResetMocks(); - SET_BOOT_MODE(ctx, VB2_BOOT_MODE_DEVELOPER); - vb2_nv_set(ctx, VB2_NV_DEV_BOOT_SIGNED_ONLY, 1); - kbh.keyblock_flags = VB2_KEYBLOCK_FLAG_RECOVERY_1 - | VB2_KEYBLOCK_FLAG_DEVELOPER_0 - | VB2_KEYBLOCK_FLAG_MINIOS_0; - TestLoadKernel(VB2_ERROR_LK_INVALID_KERNEL_FOUND, - "Keyblock dev flag mismatch (signed kernel required)"); - - ResetMocks(); - SET_BOOT_MODE(ctx, VB2_BOOT_MODE_DEVELOPER); - fwmp->flags |= VB2_SECDATA_FWMP_DEV_ENABLE_OFFICIAL_ONLY; - kbh.keyblock_flags = VB2_KEYBLOCK_FLAG_RECOVERY_1 - | VB2_KEYBLOCK_FLAG_DEVELOPER_0 - | VB2_KEYBLOCK_FLAG_MINIOS_0; - TestLoadKernel(VB2_ERROR_LK_INVALID_KERNEL_FOUND, - "Keyblock dev flag mismatch (signed kernel required)"); - - ResetMocks(); - SET_BOOT_MODE(ctx, VB2_BOOT_MODE_DEVELOPER); - fwmp->flags |= VB2_SECDATA_FWMP_DEV_ENABLE_OFFICIAL_ONLY; - kbh.keyblock_flags = VB2_KEYBLOCK_FLAG_RECOVERY_0 - | VB2_KEYBLOCK_FLAG_DEVELOPER_0 - | VB2_KEYBLOCK_FLAG_MINIOS_1; - TestLoadKernel(VB2_ERROR_LK_INVALID_KERNEL_FOUND, - "Keyblock dev flag mismatch (signed kernel required)"); - - ResetMocks(); - SET_BOOT_MODE(ctx, VB2_BOOT_MODE_DEVELOPER); - vb2_nv_set(ctx, VB2_NV_DEV_BOOT_SIGNED_ONLY, 1); - kbh.keyblock_flags = VB2_KEYBLOCK_FLAG_RECOVERY_0 - | VB2_KEYBLOCK_FLAG_DEVELOPER_1 - | VB2_KEYBLOCK_FLAG_MINIOS_0; - TestLoadKernel(0, "Keyblock dev flag okay (signed kernel required)"); - - /* Check kernel key version */ - ResetMocks(); - kbh.data_key.key_version = 1; - TestLoadKernel(VB2_ERROR_LK_INVALID_KERNEL_FOUND, - "Keyblock kernel key rollback"); - - ResetMocks(); - kbh.data_key.key_version = 0x10000; - TestLoadKernel(VB2_ERROR_LK_INVALID_KERNEL_FOUND, - "Keyblock kernel key version too big"); - - ResetMocks(); - kbh.data_key.key_version = 3; - TestLoadKernel(0, "Keyblock version roll forward"); - TEST_EQ(sd->kernel_version, 0x30001, " SD version"); - - ResetMocks(); - kbh.data_key.key_version = 3; - mock_parts[1].start = 300; - mock_parts[1].size = 150; - TestLoadKernel(0, "Two kernels roll forward"); - TEST_EQ(mock_part_next, 2, " read both"); - TEST_EQ(sd->kernel_version, 0x30001, " SD version"); - - ResetMocks(); - kbh.data_key.key_version = 1; - SET_BOOT_MODE(ctx, VB2_BOOT_MODE_DEVELOPER); - TestLoadKernel(0, "Key version ignored in dev mode"); - - ResetMocks(); - kbh.data_key.key_version = 1; - SET_BOOT_MODE(ctx, VB2_BOOT_MODE_MANUAL_RECOVERY, - VB2_RECOVERY_RO_MANUAL); - TestLoadKernel(0, "Key version ignored in rec mode"); - - ResetMocks(); - unpack_key_fail = 2; - TestLoadKernel(VB2_ERROR_LK_INVALID_KERNEL_FOUND, "Bad data key"); - - ResetMocks(); - preamble_verify_fail = 1; - TestLoadKernel(VB2_ERROR_LK_INVALID_KERNEL_FOUND, "Bad preamble"); - - ResetMocks(); - kph.kernel_version = 0; - TestLoadKernel(VB2_ERROR_LK_INVALID_KERNEL_FOUND, - "Kernel version rollback"); - - ResetMocks(); - kph.kernel_version = 0; - SET_BOOT_MODE(ctx, VB2_BOOT_MODE_DEVELOPER); - TestLoadKernel(0, "Kernel version ignored in dev mode"); - - ResetMocks(); - kph.kernel_version = 0; - SET_BOOT_MODE(ctx, VB2_BOOT_MODE_MANUAL_RECOVERY, - VB2_RECOVERY_RO_MANUAL); - TestLoadKernel(0, "Kernel version ignored in rec mode"); - - /* Check kernel version (dev mode + signed kernel required) */ - ResetMocks(); - kbh.data_key.key_version = 0; - SET_BOOT_MODE(ctx, VB2_BOOT_MODE_DEVELOPER); - vb2_nv_set(ctx, VB2_NV_DEV_BOOT_SIGNED_ONLY, 1); - TestLoadKernel(VB2_ERROR_LK_INVALID_KERNEL_FOUND, - "Keyblock key version checked in dev mode " - "(signed kernel required)"); - - ResetMocks(); - kbh.data_key.key_version = 0; - SET_BOOT_MODE(ctx, VB2_BOOT_MODE_DEVELOPER); - fwmp->flags |= VB2_SECDATA_FWMP_DEV_ENABLE_OFFICIAL_ONLY; - TestLoadKernel(VB2_ERROR_LK_INVALID_KERNEL_FOUND, - "Keyblock key version checked in dev mode " - "(signed kernel required)"); - - /* Check developer key hash - bad */ - ResetMocks(); - SET_BOOT_MODE(ctx, VB2_BOOT_MODE_DEVELOPER); - fwmp->flags |= VB2_SECDATA_FWMP_DEV_USE_KEY_HASH; - fwmp->dev_key_hash[0]++; - TestLoadKernel(VB2_ERROR_LK_INVALID_KERNEL_FOUND, - "Fail keyblock dev fwmp hash"); - - /* Check developer key hash - bad (recovery mode) */ - ResetMocks(); - SET_BOOT_MODE(ctx, VB2_BOOT_MODE_MANUAL_RECOVERY, - VB2_RECOVERY_RO_MANUAL); - fwmp->flags |= VB2_SECDATA_FWMP_DEV_USE_KEY_HASH; - fwmp->dev_key_hash[0]++; - TestLoadKernel(0, "Bad keyblock dev fwmp hash ignored in rec mode"); - - /* Check developer key hash - good */ - ResetMocks(); - SET_BOOT_MODE(ctx, VB2_BOOT_MODE_DEVELOPER); - fwmp->flags |= VB2_SECDATA_FWMP_DEV_USE_KEY_HASH; - TestLoadKernel(0, "Good keyblock dev fwmp hash"); - - ResetMocks(); - kph.preamble_size |= 0x07; - TestLoadKernel(VB2_ERROR_LK_INVALID_KERNEL_FOUND, - "Kernel body offset"); - - ResetMocks(); - kph.preamble_size += 65536; - TestLoadKernel(VB2_ERROR_LK_INVALID_KERNEL_FOUND, - "Kernel body offset huge"); - - /* Check getting kernel load address from header */ - ResetMocks(); - kph.body_load_address = (size_t)kernel_buffer; - lkp.kernel_buffer = NULL; - TestLoadKernel(0, "Get load address from preamble"); - TEST_PTR_EQ(lkp.kernel_buffer, kernel_buffer, " address"); - /* Size is rounded up to nearest sector */ - TEST_EQ(lkp.kernel_buffer_size, 70144, " size"); - - ResetMocks(); - lkp.kernel_buffer_size = 8192; - TestLoadKernel(VB2_ERROR_LK_INVALID_KERNEL_FOUND, - "Kernel too big for buffer"); - - ResetMocks(); - mock_parts[0].size = 130; - TestLoadKernel(VB2_ERROR_LK_INVALID_KERNEL_FOUND, - "Kernel too big for partition"); - - ResetMocks(); - kph.body_signature.data_size = 8192; - TestLoadKernel(0, "Kernel tiny"); - - ResetMocks(); - disk_read_to_fail = 228; - TestLoadKernel(VB2_ERROR_LK_INVALID_KERNEL_FOUND, - "Fail reading kernel data"); - - ResetMocks(); - verify_data_fail = 1; - TestLoadKernel(VB2_ERROR_LK_INVALID_KERNEL_FOUND, "Bad data"); - - /* Check that EXTERNAL_GPT flag makes it down */ - ResetMocks(); - disk_info.flags |= VB_DISK_FLAG_EXTERNAL_GPT; - TestLoadKernel(0, "Succeed external GPT"); - TEST_EQ(gpt_flag_external, 1, "GPT was external"); - - /* Check recovery from unreadble primary GPT */ - ResetMocks(); - disk_read_to_fail = 1; - TestLoadKernel(0, "Can't read disk"); -} - -int main(void) -{ - InvalidParamsTest(); - LoadKernelTest(); - - return gTestSuccess ? 0 : 255; -} diff --git a/tests/verify_kernel.c b/tests/verify_kernel.c index a79f33a0..8af47918 100644 --- a/tests/verify_kernel.c +++ b/tests/verify_kernel.c @@ -14,7 +14,6 @@ #include "host_common.h" #include "util_misc.h" #include "vboot_api.h" -#include "load_kernel_fw.h" static uint8_t workbuf[VB2_KERNEL_WORKBUF_RECOMMENDED_SIZE] __attribute__((aligned(VB2_WORKBUF_ALIGN))); @@ -23,13 +22,13 @@ static struct vb2_shared_data *sd; static uint8_t *diskbuf; -static VbSelectAndLoadKernelParams params; -static VbDiskInfo disk_info; +static struct vb2_kernel_params params; +static struct vb2_disk_info disk_info; -vb2_error_t VbExDiskRead(VbExDiskHandle_t handle, uint64_t lba_start, +vb2_error_t VbExDiskRead(vb2ex_disk_handle_t handle, uint64_t lba_start, uint64_t lba_count, void *buffer) { - if (handle != (VbExDiskHandle_t)1) + if (handle != (vb2ex_disk_handle_t)1) return VB2_ERROR_UNKNOWN; if (lba_start >= disk_info.streaming_lba_count) return VB2_ERROR_UNKNOWN; @@ -40,10 +39,10 @@ vb2_error_t VbExDiskRead(VbExDiskHandle_t handle, uint64_t lba_start, return VB2_SUCCESS; } -vb2_error_t VbExDiskWrite(VbExDiskHandle_t handle, uint64_t lba_start, +vb2_error_t VbExDiskWrite(vb2ex_disk_handle_t handle, uint64_t lba_start, uint64_t lba_count, const void *buffer) { - if (handle != (VbExDiskHandle_t)1) + if (handle != (vb2ex_disk_handle_t)1) return VB2_ERROR_UNKNOWN; if (lba_start >= disk_info.streaming_lba_count) return VB2_ERROR_UNKNOWN; @@ -87,8 +86,7 @@ int main(int argc, char *argv[]) } /* Set up params */ - params.disk_handle = (VbExDiskHandle_t)1; - disk_info.handle = (VbExDiskHandle_t)1; + disk_info.handle = (vb2ex_disk_handle_t)1; disk_info.bytes_per_lba = 512; disk_info.streaming_lba_count = disk_bytes / 512; disk_info.lba_count = disk_info.streaming_lba_count; @@ -123,8 +121,8 @@ int main(int argc, char *argv[]) } /* - * LoadKernel() cares only about VBNV_DEV_BOOT_SIGNED_ONLY, and only in - * dev mode. So just use defaults for nv storage. + * vb2api_load_kernel() cares only about VBNV_DEV_BOOT_SIGNED_ONLY, and + * only in dev mode. So just use defaults for nv storage. */ vb2_nv_init(ctx); /* We need to init kernel secdata for @@ -134,9 +132,10 @@ int main(int argc, char *argv[]) vb2_secdata_kernel_init(ctx); /* Try loading kernel */ - rv = LoadKernel(ctx, ¶ms, &disk_info); + rv = vb2api_load_kernel(ctx, ¶ms, &disk_info); if (rv != VB2_SUCCESS) { - fprintf(stderr, "LoadKernel() failed with code %d\n", rv); + fprintf(stderr, "vb2api_load_kernel() failed with code %d\n", + rv); return 1; } diff --git a/utility/load_kernel_test.c b/utility/load_kernel_test.c index 405fced6..565161eb 100644 --- a/utility/load_kernel_test.c +++ b/utility/load_kernel_test.c @@ -15,7 +15,6 @@ #include "2misc.h" #include "2sysincludes.h" #include "host_common.h" -#include "load_kernel_fw.h" #define LBA_BYTES 512 #define KERNEL_BUFFER_SIZE 0xA00000 @@ -25,13 +24,13 @@ static struct vb2_context *ctx; static struct vb2_shared_data *sd; /* Global variables for stub functions */ -static VbSelectAndLoadKernelParams lkp; -static VbDiskInfo disk_info; +static struct vb2_kernel_params lkp; +static struct vb2_disk_info disk_info; static FILE *image_file = NULL; /* Boot device stub implementations to read from the image file */ -vb2_error_t VbExDiskRead(VbExDiskHandle_t handle, uint64_t lba_start, +vb2_error_t VbExDiskRead(vb2ex_disk_handle_t handle, uint64_t lba_start, uint64_t lba_count, void *buffer) { printf("Read(%" PRIu64 ", %" PRIu64 ")\n", lba_start, lba_count); @@ -56,7 +55,7 @@ vb2_error_t VbExDiskRead(VbExDiskHandle_t handle, uint64_t lba_start, } -vb2_error_t VbExDiskWrite(VbExDiskHandle_t handle, uint64_t lba_start, +vb2_error_t VbExDiskWrite(vb2ex_disk_handle_t handle, uint64_t lba_start, uint64_t lba_count, const void *buffer) { printf("Write(%" PRIu64 ", %" PRIu64 ")\n", lba_start, lba_count); @@ -99,7 +98,7 @@ int main(int argc, char* argv[]) int errorcnt = 0; char *e = 0; - memset(&lkp, 0, sizeof(VbSelectAndLoadKernelParams)); + memset(&lkp, 0, sizeof(lkp)); disk_info.bytes_per_lba = LBA_BYTES; int boot_flags = BOOT_FLAG_RECOVERY; @@ -243,9 +242,9 @@ int main(int argc, char* argv[]) if (boot_flags & BOOT_FLAG_DEVELOPER) ctx->flags |= VB2_CONTEXT_DEVELOPER_MODE; - /* Call LoadKernel() */ - rv = LoadKernel(ctx, &lkp, &disk_info); - printf("LoadKernel() returned %d\n", rv); + /* Call vb2api_load_kernel() */ + rv = vb2api_load_kernel(ctx, &lkp, &disk_info); + printf("vb2api_load_kernel() returned %d\n", rv); if (VB2_SUCCESS == rv) { printf("Partition number: %u\n", lkp.partition_number); -- cgit v1.2.1