diff options
author | Hsin-Te Yuan <yuanhsinte@google.com> | 2022-07-12 10:52:13 +0800 |
---|---|---|
committer | Chromeos LUCI <chromeos-scoped@luci-project-accounts.iam.gserviceaccount.com> | 2022-07-29 09:47:12 +0000 |
commit | 18cb85b52d689b4cfbd49c6384ae2088b78af1e0 (patch) | |
tree | 2c7e7aca1e50a81e05aa7c302db29554f0a82c70 /tests/vb2_load_kernel2_tests.c | |
parent | 81b8d3faabad2f16bfd08efa6920bdaaeb637ce8 (diff) | |
download | vboot-18cb85b52d689b4cfbd49c6384ae2088b78af1e0.tar.gz |
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 <yuanhsinte@google.com>
Change-Id: Iaf182ca81797fee24c6104dac9d4770ff75c7fb2
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/vboot_reference/+/3755923
Reviewed-by: Yu-Ping Wu <yupingso@chromium.org>
Reviewed-by: Julius Werner <jwerner@chromium.org>
Diffstat (limited to 'tests/vb2_load_kernel2_tests.c')
-rw-r--r-- | tests/vb2_load_kernel2_tests.c | 467 |
1 files changed, 467 insertions, 0 deletions
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; +} |