summaryrefslogtreecommitdiff
path: root/tests/vboot_kernel2_tests.c
diff options
context:
space:
mode:
Diffstat (limited to 'tests/vboot_kernel2_tests.c')
-rw-r--r--tests/vboot_kernel2_tests.c434
1 files changed, 434 insertions, 0 deletions
diff --git a/tests/vboot_kernel2_tests.c b/tests/vboot_kernel2_tests.c
new file mode 100644
index 00000000..5424e86c
--- /dev/null
+++ b/tests/vboot_kernel2_tests.c
@@ -0,0 +1,434 @@
+/* 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 "load_kernel_fw.h"
+#include "test_common.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 int mock_tpm_set_mode_calls;
+
+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;
+
+ 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;
+
+ mock_tpm_set_mode_calls = 0;
+}
+
+/* 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;
+}
+
+vb2_error_t vb2ex_tpm_set_mode(enum vb2_tpm_mode mode_val)
+{
+ mock_tpm_set_mode_calls++;
+ 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()");
+}
+
+/* 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),
+ "{valid kernel}");
+ TEST_EQ(mock_tpm_set_mode_calls, 1,
+ " TPM disabled");
+
+ reset_common_data();
+ disk_info.bytes_per_lba = KBUF_SIZE;
+ disk_info.lba_count = 1;
+ TEST_EQ(LoadMiniOsKernel(ctx, &lkp, &disk_info),
+ VB2_ERROR_LK_NO_KERNEL_FOUND, "{no kernel}");
+ TEST_EQ(mock_tpm_set_mode_calls, 0,
+ " TPM not disabled");
+
+ 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),
+ "{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),
+ "{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),
+ VB2_ERROR_LK_NO_KERNEL_FOUND,
+ "{invalid kernel, invalid kernel}");
+ TEST_EQ(mock_tpm_set_mode_calls, 0,
+ " TPM not disabled");
+
+ 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),
+ "{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),
+ "{valid kernel, valid kernel} minios_priority=1");
+ TEST_EQ(cur_kernel->sector, 1, " select second kernel");
+
+ 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),
+ 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),
+ 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),
+ "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),
+ "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),
+ "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),
+ "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),
+ "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),
+ "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),
+ 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),
+ 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),
+ 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),
+ "newer kernel version");
+}
+
+int main(void)
+{
+ load_minios_kernel_tests();
+
+ return gTestSuccess ? 0 : 255;
+}