diff options
-rw-r--r-- | firmware/2lib/2misc.c | 5 | ||||
-rw-r--r-- | firmware/2lib/include/2api.h | 3 | ||||
-rw-r--r-- | firmware/include/vboot_api.h | 5 | ||||
-rw-r--r-- | firmware/include/vboot_struct.h | 2 | ||||
-rw-r--r-- | firmware/lib/vboot_api_init.c | 2 | ||||
-rw-r--r-- | firmware/lib/vboot_firmware.c | 3 | ||||
-rw-r--r-- | firmware/lib/vboot_kernel.c | 8 | ||||
-rw-r--r-- | tests/vb2_misc_tests.c | 24 | ||||
-rw-r--r-- | tests/vboot_firmware_tests.c | 33 |
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(); |