summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--firmware/2lib/2misc.c5
-rw-r--r--firmware/2lib/include/2api.h3
-rw-r--r--firmware/include/vboot_api.h5
-rw-r--r--firmware/include/vboot_struct.h2
-rw-r--r--firmware/lib/vboot_api_init.c2
-rw-r--r--firmware/lib/vboot_firmware.c3
-rw-r--r--firmware/lib/vboot_kernel.c8
-rw-r--r--tests/vb2_misc_tests.c24
-rw-r--r--tests/vboot_firmware_tests.c33
9 files changed, 79 insertions, 6 deletions
diff --git a/firmware/2lib/2misc.c b/firmware/2lib/2misc.c
index 31884732..360365d8 100644
--- a/firmware/2lib/2misc.c
+++ b/firmware/2lib/2misc.c
@@ -360,8 +360,9 @@ int vb2_select_fw_slot(struct vb2_context *ctx)
/* Still trying this firmware */
vb2_nv_set(ctx, VB2_NV_FW_RESULT, VB2_FW_RESULT_TRYING);
- /* Decrement non-zero try count */
- vb2_nv_set(ctx, VB2_NV_TRY_COUNT, tries - 1);
+ /* Decrement non-zero try count, unless told not to */
+ if (!(ctx->flags & VB2_CONTEXT_NOFAIL_BOOT))
+ vb2_nv_set(ctx, VB2_NV_TRY_COUNT, tries - 1);
}
/* Store the slot we're trying */
diff --git a/firmware/2lib/include/2api.h b/firmware/2lib/include/2api.h
index 1341528b..3fbd7edf 100644
--- a/firmware/2lib/include/2api.h
+++ b/firmware/2lib/include/2api.h
@@ -100,6 +100,9 @@ enum vb2_context_flags {
* back to its underlying storage, then may clear this flag.
*/
VB2_CONTEXT_SECDATAK_CHANGED = (1 << 11),
+
+ /* Boot optimistically: don't touch failure counters */
+ VB2_CONTEXT_NOFAIL_BOOT = (1 << 12),
};
/*
diff --git a/firmware/include/vboot_api.h b/firmware/include/vboot_api.h
index e66a2fba..397d8840 100644
--- a/firmware/include/vboot_api.h
+++ b/firmware/include/vboot_api.h
@@ -229,9 +229,12 @@ typedef struct VbCommonParams {
#define VB_INIT_FLAG_VIRTUAL_REC_SWITCH 0x00001000
/* Set when we are calling VbInit() before loading Option ROMs */
#define VB_INIT_FLAG_BEFORE_OPROM_LOAD 0x00002000
-
/* Allow USB boot on transition to dev */
#define VB_INIT_FLAG_ALLOW_USB_BOOT 0x00004000
+/* Set when we can't reliably identify boot failures. This prevents
+ * the boot-try counters from decrementing.
+ */
+#define VB_INIT_FLAG_NOFAIL_BOOT 0x00008000
/*
* Output flags for VbInitParams.out_flags. Used to indicate potential boot
diff --git a/firmware/include/vboot_struct.h b/firmware/include/vboot_struct.h
index 09abf138..3a3f534e 100644
--- a/firmware/include/vboot_struct.h
+++ b/firmware/include/vboot_struct.h
@@ -322,6 +322,8 @@ typedef struct VbKernelPreambleHeader {
#define VBSD_OPROM_MATTERS 0x00010000
/* Firmware has loaded the VGA Option ROM */
#define VBSD_OPROM_LOADED 0x00020000
+/* Don't try for boot failures */
+#define VBSD_NOFAIL_BOOT 0x00040000
/*
* Supported flags by header version. It's ok to add new flags while keeping
diff --git a/firmware/lib/vboot_api_init.c b/firmware/lib/vboot_api_init.c
index 4371042d..10b435c2 100644
--- a/firmware/lib/vboot_api_init.c
+++ b/firmware/lib/vboot_api_init.c
@@ -76,6 +76,8 @@ VbError_t VbInit(VbCommonParams *cparams, VbInitParams *iparams)
shared->flags |= VBSD_BOOT_S3_RESUME;
if (iparams->flags & VB_INIT_FLAG_RO_NORMAL_SUPPORT)
shared->flags |= VBSD_BOOT_RO_NORMAL_SUPPORT;
+ if (iparams->flags & VB_INIT_FLAG_NOFAIL_BOOT)
+ shared->flags |= VBSD_NOFAIL_BOOT;
if (iparams->flags & VB_INIT_FLAG_EC_SOFTWARE_SYNC)
shared->flags |= VBSD_EC_SOFTWARE_SYNC;
if (iparams->flags & VB_INIT_FLAG_EC_SLOW_UPDATE)
diff --git a/firmware/lib/vboot_firmware.c b/firmware/lib/vboot_firmware.c
index e6a31fa9..7e6010bb 100644
--- a/firmware/lib/vboot_firmware.c
+++ b/firmware/lib/vboot_firmware.c
@@ -78,7 +78,8 @@ int LoadFirmware(VbCommonParams *cparams, VbSelectFirmwareParams *fparams,
/* Read try-b count and decrement if necessary */
VbNvGet(vnc, VBNV_TRY_B_COUNT, &try_b_count);
if (0 != try_b_count) {
- VbNvSet(vnc, VBNV_TRY_B_COUNT, try_b_count - 1);
+ if (!(shared->flags & VBSD_NOFAIL_BOOT))
+ VbNvSet(vnc, VBNV_TRY_B_COUNT, try_b_count - 1);
shared->flags |= VBSD_FWB_TRIED;
}
diff --git a/firmware/lib/vboot_kernel.c b/firmware/lib/vboot_kernel.c
index 86cf6ef4..c6d4e27a 100644
--- a/firmware/lib/vboot_kernel.c
+++ b/firmware/lib/vboot_kernel.c
@@ -441,8 +441,12 @@ VbError_t LoadKernel(LoadKernelParams *params, VbCommonParams *cparams)
if (VbKernelHasFlags(preamble) == VBOOT_SUCCESS)
params->flags = preamble->flags;
- /* Update GPT to note this is the kernel we're trying */
- GptUpdateKernelEntry(&gpt, GPT_UPDATE_ENTRY_TRY);
+ /* 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 (!(shared->flags & VBSD_NOFAIL_BOOT))
+ GptUpdateKernelEntry(&gpt, GPT_UPDATE_ENTRY_TRY);
/*
* If we're in recovery mode or we're about to boot a
diff --git a/tests/vb2_misc_tests.c b/tests/vb2_misc_tests.c
index 4d804509..e41a16d9 100644
--- a/tests/vb2_misc_tests.c
+++ b/tests/vb2_misc_tests.c
@@ -446,6 +446,17 @@ static void select_slot_tests(void)
TEST_EQ(sd->fw_slot, 1, "selected B");
TEST_NEQ(cc.flags & VB2_CONTEXT_FW_SLOT_B, 0, "ctx says choose B");
+ /* Slot A ran out of tries, even with nofail active */
+ reset_common_data();
+ cc.flags |= VB2_CONTEXT_NOFAIL_BOOT;
+ vb2_nv_set(&cc, VB2_NV_FW_RESULT, VB2_FW_RESULT_TRYING);
+ TEST_SUCC(vb2_select_fw_slot(&cc), "select slot A out of tries");
+ TEST_EQ(vb2_nv_get(&cc, VB2_NV_TRY_NEXT), 1, "try B next");
+ TEST_NEQ(sd->status & VB2_SD_STATUS_CHOSE_SLOT, 0, "chose slot");
+ TEST_EQ(vb2_nv_get(&cc, VB2_NV_FW_TRIED), 1, "tried B");
+ TEST_EQ(sd->fw_slot, 1, "selected B");
+ TEST_NEQ(cc.flags & VB2_CONTEXT_FW_SLOT_B, 0, "ctx says choose B");
+
/* Slot A used up a try */
reset_common_data();
vb2_nv_set(&cc, VB2_NV_TRY_COUNT, 3);
@@ -458,6 +469,19 @@ static void select_slot_tests(void)
TEST_EQ(cc.flags & VB2_CONTEXT_FW_SLOT_B, 0, "didn't choose B");
TEST_EQ(vb2_nv_get(&cc, VB2_NV_TRY_COUNT), 2, "tries decremented");
+ /* Slot A failed, but nofail active */
+ reset_common_data();
+ cc.flags |= VB2_CONTEXT_NOFAIL_BOOT;
+ vb2_nv_set(&cc, VB2_NV_TRY_COUNT, 3);
+ TEST_SUCC(vb2_select_fw_slot(&cc), "try slot A");
+ TEST_EQ(vb2_nv_get(&cc, VB2_NV_FW_RESULT),
+ VB2_FW_RESULT_TRYING, "result trying");
+ TEST_NEQ(sd->status & VB2_SD_STATUS_CHOSE_SLOT, 0, "chose slot");
+ TEST_EQ(vb2_nv_get(&cc, VB2_NV_FW_TRIED), 0, "tried A");
+ TEST_EQ(sd->fw_slot, 0, "selected A");
+ TEST_EQ(cc.flags & VB2_CONTEXT_FW_SLOT_B, 0, "didn't choose B");
+ TEST_EQ(vb2_nv_get(&cc, VB2_NV_TRY_COUNT), 3, "tries not decremented");
+
/* Tried/result get copied to the previous fields */
reset_common_data();
vb2_nv_set(&cc, VB2_NV_FW_TRIED, 0);
diff --git a/tests/vboot_firmware_tests.c b/tests/vboot_firmware_tests.c
index 651197cf..f83d970f 100644
--- a/tests/vboot_firmware_tests.c
+++ b/tests/vboot_firmware_tests.c
@@ -371,6 +371,39 @@ static void LoadFirmwareTest(void) {
VbNvGet(&vnc, VBNV_TRY_B_COUNT, &u);
TEST_EQ(u, 4, "Used up a try");
+ /* There's a optimistic boot mode that doesn't consume tries.
+ * The behaviour should differ only in that the try count doesn't change. */
+ /* Optimistic boot case 1: count == 0: Go for A */
+ ResetMocks();
+ mpreamble[0].flags = VB_FIRMWARE_PREAMBLE_USE_RO_NORMAL;
+ mpreamble[1].flags = VB_FIRMWARE_PREAMBLE_USE_RO_NORMAL;
+ shared->flags |= VBSD_BOOT_RO_NORMAL_SUPPORT;
+ shared->flags |= VBSD_NOFAIL_BOOT;
+ VbNvSet(&vnc, VBNV_TRY_B_COUNT, 0);
+ TestLoadFirmware(VBERROR_SUCCESS, 0, "Give up on B");
+ TEST_EQ(shared->check_fw_a_result, VBSD_LF_CHECK_VALID, "RO normal A valid");
+ TEST_EQ(shared->check_fw_b_result, 0, "RO normal B not checked");
+ TEST_EQ(shared->firmware_index, 0, "Boot A");
+ TEST_EQ(shared->flags & VBSD_FWB_TRIED, 0, "Didn't try firmware B");
+ TEST_EQ(shared->kernel_subkey.algorithm, 7, "Copy kernel subkey");
+ VbNvGet(&vnc, VBNV_TRY_B_COUNT, &u);
+ TEST_EQ(u, 0, "try count still zero");
+ /* Optimistic boot case 2: count > 0: count unchanged, use B */
+ ResetMocks();
+ mpreamble[0].flags = VB_FIRMWARE_PREAMBLE_USE_RO_NORMAL;
+ mpreamble[1].flags = VB_FIRMWARE_PREAMBLE_USE_RO_NORMAL;
+ shared->flags |= VBSD_BOOT_RO_NORMAL_SUPPORT;
+ shared->flags |= VBSD_NOFAIL_BOOT;
+ VbNvSet(&vnc, VBNV_TRY_B_COUNT, 5);
+ TestLoadFirmware(VBERROR_SUCCESS, 0, "Check B then A");
+ TEST_EQ(shared->check_fw_a_result, 0, "RO normal A not checked ");
+ TEST_EQ(shared->check_fw_b_result, VBSD_LF_CHECK_VALID, "RO normal B valid");
+ TEST_EQ(shared->firmware_index, 1, "Boot B");
+ TEST_NEQ(shared->flags & VBSD_FWB_TRIED, 0, "Tried firmware B");
+ TEST_EQ(shared->kernel_subkey.algorithm, 8, "Copy kernel subkey");
+ VbNvGet(&vnc, VBNV_TRY_B_COUNT, &u);
+ TEST_EQ(u, 5, "Not used up a try");
+
/* If both A and B are valid and grater version than TPM, A is
* selected and B preamble (but not body) is verified. */
ResetMocks();