summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoel Kitching <kitching@google.com>2021-04-28 16:50:21 +0800
committerCommit Bot <commit-bot@chromium.org>2021-09-24 12:25:24 +0000
commit13f601fbd4c1b128f333391e4552082594f0ff25 (patch)
tree4ae29d731f370835f790e8a571cc13d9c5e3025b
parent8aded7005e2830f54cf53b329946cbb1f11548f2 (diff)
downloadvboot-13f601fbd4c1b128f333391e4552082594f0ff25.tar.gz
vboot: boot from miniOS recovery kernels on diskstabilize-14249.B
Add VbTryLoadMiniOsKernel() to vboot API, which boots from a miniOS recovery kernel located on internal disk. In this boot path, an attempt is made to verify and boot this kernel. Recovery proceeds from within the miniOS kernel by downloading a recovery image over the network. No USB disk is used in the process. For more information, see go/nbr-firmware. BUG=b:188121855, b:186682292 TEST=make clean && make runtests BRANCH=none Change-Id: Ic4d1fe5642a2bf71c51c78fd7830ad2b6e9eebeb Signed-off-by: Yu-Ping Wu <yupingso@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/vboot_reference/+/2856364 Reviewed-by: Julius Werner <jwerner@chromium.org>
-rw-r--r--Makefile2
-rw-r--r--firmware/2lib/include/2return_codes.h6
-rw-r--r--firmware/include/vboot_api.h15
-rw-r--r--firmware/lib/include/load_kernel_fw.h16
-rw-r--r--firmware/lib/vboot_api_kernel.c30
-rw-r--r--firmware/lib/vboot_kernel.c177
-rw-r--r--tests/vboot_api_kernel_tests.c105
-rw-r--r--tests/vboot_kernel2_tests.c434
-rw-r--r--tests/vboot_kernel_tests.c72
9 files changed, 824 insertions, 33 deletions
diff --git a/Makefile b/Makefile
index 70ef346e..82d6a44a 100644
--- a/Makefile
+++ b/Makefile
@@ -710,6 +710,7 @@ TEST_NAMES = \
tests/vboot_api_kernel4_tests \
tests/vboot_api_kernel_tests \
tests/vboot_kernel_tests \
+ tests/vboot_kernel2_tests \
tests/verify_kernel
ifeq (${MOCK_TPM}${TPM2_MODE},)
@@ -1267,6 +1268,7 @@ endif
${RUNTEST} ${BUILD_RUN}/tests/vboot_api_kernel4_tests
${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
diff --git a/firmware/2lib/include/2return_codes.h b/firmware/2lib/include/2return_codes.h
index 33cb7623..0dc7c441 100644
--- a/firmware/2lib/include/2return_codes.h
+++ b/firmware/2lib/include/2return_codes.h
@@ -617,6 +617,12 @@ enum vb2_return_code {
/* Escape from NO_BOOT mode is detected */
VB2_ERROR_ESCAPE_NO_BOOT = 0x10080034,
+ /*
+ * Keyblock flags don't match current mode in
+ * vb2_load_kernel_keyblock().
+ */
+ VB2_ERROR_KERNEL_KEYBLOCK_MINIOS_FLAG = 0x10080035,
+
/**********************************************************************
* API-level errors
*/
diff --git a/firmware/include/vboot_api.h b/firmware/include/vboot_api.h
index b7b0ce54..f19ee747 100644
--- a/firmware/include/vboot_api.h
+++ b/firmware/include/vboot_api.h
@@ -89,6 +89,21 @@ vb2_error_t VbSelectAndLoadKernel(struct vb2_context *ctx,
*/
vb2_error_t VbTryLoadKernel(struct vb2_context *ctx, uint32_t disk_flags);
+/**
+ * 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
+ * @return VB2_SUCCESS or the most specific VB2_ERROR_LK error.
+ */
+vb2_error_t VbTryLoadMiniOsKernel(struct vb2_context *ctx);
+
/*****************************************************************************/
/* Disk access (previously in boot_device.h) */
diff --git a/firmware/lib/include/load_kernel_fw.h b/firmware/lib/include/load_kernel_fw.h
index 96c8f864..af454703 100644
--- a/firmware/lib/include/load_kernel_fw.h
+++ b/firmware/lib/include/load_kernel_fw.h
@@ -12,10 +12,11 @@
#include "vboot_api.h"
/**
- * Attempt to load the kernel from the current device.
+ * 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.
*/
@@ -23,4 +24,17 @@ 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
+ *
+ * Returns VB2_SUCCESS if successful. If unsuccessful, returns an error code.
+ */
+vb2_error_t LoadMiniOsKernel(struct vb2_context *ctx,
+ VbSelectAndLoadKernelParams *params,
+ VbDiskInfo *disk_info);
+
#endif /* VBOOT_REFERENCE_LOAD_KERNEL_FW_H_ */
diff --git a/firmware/lib/vboot_api_kernel.c b/firmware/lib/vboot_api_kernel.c
index b79b26fd..2abd57eb 100644
--- a/firmware/lib/vboot_api_kernel.c
+++ b/firmware/lib/vboot_api_kernel.c
@@ -61,13 +61,14 @@ static int is_valid_disk(VbDiskInfo *info, uint32_t disk_flags)
((info->flags & VB_DISK_FLAG_SELECT_MASK) - 1)) == 0;
}
-test_mockable
-vb2_error_t VbTryLoadKernel(struct vb2_context *ctx, uint32_t disk_flags)
+static vb2_error_t VbTryLoadKernelImpl(struct vb2_context *ctx,
+ uint32_t disk_flags, int minios)
{
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;
/* TODO: Should have been set by VbSelectAndLoadKernel. Remove when
this global is no longer needed. */
@@ -91,11 +92,16 @@ vb2_error_t VbTryLoadKernel(struct vb2_context *ctx, uint32_t disk_flags)
disk_info[i].flags);
continue;
}
-
kparams_ptr->disk_handle = disk_info[i].handle;
- vb2_error_t new_rv = LoadKernel(ctx, kparams_ptr,
- &disk_info[i]);
- VB2_DEBUG("LoadKernel() = %#x\n", new_rv);
+
+ if (minios) {
+ new_rv = LoadMiniOsKernel(ctx, kparams_ptr,
+ &disk_info[i]);
+ VB2_DEBUG("LoadMiniOsKernel() = %#x\n", new_rv);
+ } else {
+ new_rv = LoadKernel(ctx, kparams_ptr, &disk_info[i]);
+ VB2_DEBUG("LoadKernel() = %#x\n", new_rv);
+ }
/* Stop now if we found a kernel. */
if (VB2_SUCCESS == new_rv) {
@@ -133,6 +139,18 @@ vb2_error_t VbTryLoadKernel(struct vb2_context *ctx, uint32_t disk_flags)
return rv;
}
+test_mockable
+vb2_error_t VbTryLoadKernel(struct vb2_context *ctx, uint32_t disk_flags)
+{
+ return VbTryLoadKernelImpl(ctx, disk_flags, 0);
+}
+
+test_mockable
+vb2_error_t VbTryLoadMiniOsKernel(struct vb2_context *ctx)
+{
+ return VbTryLoadKernelImpl(ctx, VB_DISK_FLAG_FIXED, 1);
+}
+
vb2_error_t VbSelectAndLoadKernel(struct vb2_context *ctx,
VbSelectAndLoadKernelParams *kparams)
{
diff --git a/firmware/lib/vboot_kernel.c b/firmware/lib/vboot_kernel.c
index d4cca14e..34a8a426 100644
--- a/firmware/lib/vboot_kernel.c
+++ b/firmware/lib/vboot_kernel.c
@@ -20,6 +20,7 @@
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 */
@@ -173,15 +174,17 @@ static vb2_error_t vb2_verify_kernel_dev_key_hash(
/**
* 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,
- struct vb2_workbuf *wb)
+ uint32_t lpflags, struct vb2_workbuf *wb)
{
struct vb2_shared_data *sd = vb2_get_sd(ctx);
@@ -250,6 +253,15 @@ static vb2_error_t vb2_verify_kernel_vblock(
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. */
enum vb2_boot_mode boot_mode = get_boot_mode(ctx);
@@ -309,6 +321,29 @@ static vb2_error_t vb2_verify_kernel_vblock(
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.
@@ -361,9 +396,8 @@ static vb2_error_t vb2_load_partition(
}
read_ms += vb2ex_mtime() - start_ts;
- if (vb2_verify_kernel_vblock(ctx, kbuf, KBUF_SIZE, &wb)) {
+ 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;
@@ -455,6 +489,143 @@ static vb2_error_t vb2_load_partition(
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)
+{
+ vb2_error_t rv;
+ int end_region_first = vb2_nv_get(ctx, VB2_NV_MINIOS_PRIORITY);
+
+ 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)
+ return rv;
+
+ rv = vb2ex_tpm_set_mode(VB2_TPM_MODE_DISABLED);
+ if (rv)
+ VB2_DEBUG("Failed to disable TPM\n");
+
+ return rv;
+}
+
vb2_error_t LoadKernel(struct vb2_context *ctx,
VbSelectAndLoadKernelParams *params,
VbDiskInfo *disk_info)
diff --git a/tests/vboot_api_kernel_tests.c b/tests/vboot_api_kernel_tests.c
index ea2f97c9..51f0ddb1 100644
--- a/tests/vboot_api_kernel_tests.c
+++ b/tests/vboot_api_kernel_tests.c
@@ -50,7 +50,7 @@ typedef struct {
static const char pickme[] = "correct choice";
#define DONT_CARE ((const char *)42)
-test_case_t test[] = {
+test_case_t normal_tests[] = {
{
.name = "first drive (removable)",
.ctx_flags = 0,
@@ -388,12 +388,52 @@ test_case_t test[] = {
},
};
+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;
@@ -407,7 +447,7 @@ static struct VbSelectAndLoadKernelParams kparams;
/**
* Reset mock data (for use before each test)
*/
-static void ResetMocks(int i)
+static void ResetMocks(test_case_t *test_case)
{
TEST_SUCC(vb2api_init(workbuf, sizeof(workbuf), &ctx),
"vb2api_init failed");
@@ -418,13 +458,15 @@ static void ResetMocks(int i)
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 + i;
+ t = test_case;
}
static int is_nonzero(const void *vptr, size_t count)
@@ -497,9 +539,9 @@ vb2_error_t VbExDiskFreeInfo(VbDiskInfo *infos,
return VB2_SUCCESS;
}
-vb2_error_t LoadKernel(struct vb2_context *c,
- VbSelectAndLoadKernelParams *params,
- VbDiskInfo *disk_info)
+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__,
@@ -511,6 +553,22 @@ vb2_error_t LoadKernel(struct vb2_context *c,
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)
+{
+ 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)
@@ -527,11 +585,11 @@ void vb2_nv_set(struct vb2_context *c,
static void VbTryLoadKernelTest(void)
{
int i;
- int num_tests = sizeof(test) / sizeof(test[0]);
+ int num_tests = ARRAY_SIZE(normal_tests);
for (i = 0; i < num_tests; i++) {
- printf("Test case: %s ...\n", test[i].name);
- ResetMocks(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),
t->expected_return_val, " return value");
@@ -545,11 +603,40 @@ static void VbTryLoadKernelTest(void)
}
TEST_EQ(got_external_mismatch, 0, " external GPT errors");
}
+ 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),
+ 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(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
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;
+}
diff --git a/tests/vboot_kernel_tests.c b/tests/vboot_kernel_tests.c
index 944be0aa..14e7e1f2 100644
--- a/tests/vboot_kernel_tests.c
+++ b/tests/vboot_kernel_tests.c
@@ -363,50 +363,94 @@ static void LoadKernelTest(void)
TestLoadKernel(VB2_ERROR_LK_INVALID_KERNEL_FOUND,
"Fail key block dev sig fwmp");
- /* Check keyblock flag mismatches */
+ /* Check keyblock flags */
ResetMocks();
- kbh.keyblock_flags =
- VB2_KEYBLOCK_FLAG_RECOVERY_0 | VB2_KEYBLOCK_FLAG_DEVELOPER_1;
+ 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;
+ 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();
ctx->flags |= VB2_CONTEXT_RECOVERY_MODE;
- kbh.keyblock_flags =
- VB2_KEYBLOCK_FLAG_RECOVERY_1 | VB2_KEYBLOCK_FLAG_DEVELOPER_1;
+ 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();
+ ctx->flags |= VB2_CONTEXT_RECOVERY_MODE;
+ 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();
ctx->flags |= VB2_CONTEXT_RECOVERY_MODE | VB2_CONTEXT_DEVELOPER_MODE;
- kbh.keyblock_flags =
- VB2_KEYBLOCK_FLAG_RECOVERY_1 | VB2_KEYBLOCK_FLAG_DEVELOPER_0;
+ 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");
- /* Check keyblock flag mismatches (dev mode + signed kernel required) */
+ ResetMocks();
+ ctx->flags |= VB2_CONTEXT_RECOVERY_MODE | 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();
ctx->flags |= VB2_CONTEXT_DEVELOPER_MODE;
vb2_nv_set(ctx, VB2_NV_DEV_BOOT_SIGNED_ONLY, 1);
- kbh.keyblock_flags =
- VB2_KEYBLOCK_FLAG_RECOVERY_1 | VB2_KEYBLOCK_FLAG_DEVELOPER_0;
+ 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();
ctx->flags |= VB2_CONTEXT_DEVELOPER_MODE;
fwmp->flags |= VB2_SECDATA_FWMP_DEV_ENABLE_OFFICIAL_ONLY;
- kbh.keyblock_flags =
- VB2_KEYBLOCK_FLAG_RECOVERY_1 | VB2_KEYBLOCK_FLAG_DEVELOPER_0;
+ 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();
+ ctx->flags |= VB2_CONTEXT_DEVELOPER_MODE;
+ 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();
+ ctx->flags |= VB2_CONTEXT_DEVELOPER_MODE;
+ 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;