summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPatrick Georgi <pgeorgi@google.com>2015-02-10 14:58:28 +0100
committerChromeOS Commit Bot <chromeos-commit-bot@chromium.org>2015-05-28 16:30:17 +0000
commitebf886b5fd3dd75a00205879f3847198ecf83bdd (patch)
tree79ca3669907a75aefbc84d1a9d3ac659e741f5e1
parentf27436032668e332b4ed7cc168bbcc2a939ae7b9 (diff)
downloadvboot-ebf886b5fd3dd75a00205879f3847198ecf83bdd.tar.gz
Provide a way to disable counting failed boots
When the lid is closed and external power is applied the system may boot and shut down faster than required for the OS to determine that things were alright. In timed charging setups this led to systems ending up to consider the current version broken because it "failed" repeatedly. Remain generic about the reason for not counting boots since there may be more situations in which we want to handle the situation optimistically. BRANCH=none BUG=chromium:446945 TEST=none Change-Id: Iea350e3c98d5c00156da682e52c90a882ba017c0 Signed-off-by: Patrick Georgi <pgeorgi@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/249150 Reviewed-by: Randall Spangler <rspangler@chromium.org>
-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();