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>
This commit is contained in:
Patrick Georgi
2015-02-10 14:58:28 +01:00
committed by ChromeOS Commit Bot
parent f274360326
commit ebf886b5fd
9 changed files with 79 additions and 6 deletions

View File

@@ -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 */

View File

@@ -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),
};
/*

View File

@@ -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

View File

@@ -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

View File

@@ -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)

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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);

View File

@@ -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();