vboot/vboot_kernel: change recovery behaviour on kernel failure

On load kernel failure, only call vb2api_fail when in normal
mode.

Previously, the behaviour here was to only call when attempting
to load a kernel from a fixed disk.  This maps to (1) normal
mode, and (2) developer mode when booting from an internal disk.

Excluding (2) creates a more consistent experience in developer
mode, and also prepares for a world where recovery kernels might
exist on disk.

This CL is part of a series to merge vboot1 and vboot2.0
kernel verification code; see b/181739551.

BUG=b:181739551, b:188121855
TEST=make clean && make runtests
BRANCH=none

Signed-off-by: Joel Kitching <kitching@google.com>
Change-Id: Ic2c55a073b036be98f4ce9b2e0c7fb3209de74c8
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/vboot_reference/+/2917623
Tested-by: Joel Kitching <kitching@chromium.org>
Commit-Queue: Joel Kitching <kitching@chromium.org>
Reviewed-by: Julius Werner <jwerner@chromium.org>
diff --git a/firmware/lib/vboot_api_kernel.c b/firmware/lib/vboot_api_kernel.c
index b28cc44..93de040 100644
--- a/firmware/lib/vboot_api_kernel.c
+++ b/firmware/lib/vboot_api_kernel.c
@@ -106,7 +106,8 @@
 	}
 
 	/* If we drop out of the loop, we didn't find any usable kernel. */
-	if (disk_flags & VB_DISK_FLAG_FIXED) {
+	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);
diff --git a/tests/vboot_api_kernel_tests.c b/tests/vboot_api_kernel_tests.c
index 83b6d6c..9294fd7 100644
--- a/tests/vboot_api_kernel_tests.c
+++ b/tests/vboot_api_kernel_tests.c
@@ -29,6 +29,7 @@
 	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];
@@ -53,6 +54,7 @@
 test_case_t test[] = {
 	{
 		.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},
@@ -70,6 +72,7 @@
 	},
 	{
 		.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},
@@ -87,6 +90,7 @@
 	},
 	{
 		.name = "first removable drive",
+		.ctx_flags = 0,
 		.want_flags = VB_DISK_FLAG_REMOVABLE,
 		.disks_to_provide = {
 			/* too small */
@@ -117,6 +121,7 @@
 	},
 	{
 		.name = "first removable drive (skip external GPT)",
+		.ctx_flags = 0,
 		.want_flags = VB_DISK_FLAG_REMOVABLE,
 		.disks_to_provide = {
 			/* too small */
@@ -149,6 +154,7 @@
 	},
 	{
 		.name = "second removable drive",
+		.ctx_flags = 0,
 		.want_flags = VB_DISK_FLAG_REMOVABLE,
 		.disks_to_provide = {
 			/* wrong flags */
@@ -167,6 +173,7 @@
 	},
 	{
 		.name = "first fixed drive",
+		.ctx_flags = 0,
 		.want_flags = VB_DISK_FLAG_FIXED,
 		.disks_to_provide = {
 			/* too small */
@@ -199,6 +206,7 @@
 	},
 	{
 		.name = "no drives at all",
+		.ctx_flags = 0,
 		.want_flags = VB_DISK_FLAG_FIXED,
 		.disks_to_provide = {},
 		.disk_count_to_return = DEFAULT_COUNT,
@@ -211,6 +219,7 @@
 	},
 	{
 		.name = "VbExDiskGetInfo() error",
+		.ctx_flags = 0,
 		.want_flags = VB_DISK_FLAG_FIXED,
 		.disks_to_provide = {
 			{512,  10, VB_DISK_FLAG_REMOVABLE, 0},
@@ -226,6 +235,7 @@
 	},
 	{
 		.name = "invalid kernel",
+		.ctx_flags = 0,
 		.want_flags = VB_DISK_FLAG_FIXED,
 		.disks_to_provide = {
 			/* too small */
@@ -257,6 +267,7 @@
 	},
 	{
 		.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"},
@@ -274,6 +285,7 @@
 	},
 	{
 		.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"},
@@ -291,6 +303,43 @@
 	},
 	{
 		.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"},
@@ -308,6 +357,23 @@
 	},
 	{
 		.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"},
@@ -464,7 +530,8 @@
 	for (i = 0; i < num_tests; i++) {
 		printf("Test case: %s ...\n", test[i].name);
 		ResetMocks(i);
-		TEST_EQ(VbTryLoadKernel(ctx, test[i].want_flags),
+		ctx->flags = t->ctx_flags;
+		TEST_EQ(VbTryLoadKernel(ctx, t->want_flags),
 			t->expected_return_val, "  return value");
 		TEST_EQ(got_recovery_request_val,
 			t->expected_recovery_request_val, "  recovery_request");