summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--firmware/2lib/2misc.c44
-rw-r--r--firmware/2lib/include/2api.h20
-rw-r--r--tests/vb2_misc_tests.c67
3 files changed, 119 insertions, 12 deletions
diff --git a/firmware/2lib/2misc.c b/firmware/2lib/2misc.c
index c14aab55..92a3452e 100644
--- a/firmware/2lib/2misc.c
+++ b/firmware/2lib/2misc.c
@@ -82,17 +82,31 @@ vb2_error_t vb2_read_gbb_header(struct vb2_context *ctx,
return VB2_SUCCESS;
}
-test_mockable
-void vb2api_fail(struct vb2_context *ctx, uint8_t reason, uint8_t subcode)
+static void fail_impl(struct vb2_context *ctx,
+ uint8_t reason, uint8_t subcode, bool previous_boot)
{
struct vb2_shared_data *sd = vb2_get_sd(ctx);
+ uint32_t last_fw_slot, last_fw_result, fw_slot;
/* If NV data hasn't been initialized, initialize it now */
if (!(sd->status & VB2_SD_STATUS_NV_INIT))
vb2_nv_init(ctx);
+ /*
+ * Donot overwrite any existing failure with a new failure reported
+ * through vb2api_previous_boot_fail(). Existing failure might have
+ * been set through vb2api_fail() in the previous boot and the new
+ * failure can stand.
+ */
+ if (previous_boot &&
+ vb2_nv_get(ctx, VB2_NV_FW_RESULT) == VB2_FW_RESULT_FAILURE)
+ return;
+
/* See if we were far enough in the boot process to choose a slot */
- if (sd->status & VB2_SD_STATUS_CHOSE_SLOT) {
+ if (previous_boot || (sd->status & VB2_SD_STATUS_CHOSE_SLOT)) {
+ last_fw_slot = vb2_nv_get(ctx, VB2_NV_FW_PREV_TRIED);
+ last_fw_result = vb2_nv_get(ctx, VB2_NV_FW_PREV_RESULT);
+ fw_slot = vb2_nv_get(ctx, VB2_NV_FW_TRIED);
/* Boot failed */
vb2_nv_set(ctx, VB2_NV_FW_RESULT, VB2_FW_RESULT_FAILURE);
@@ -105,14 +119,14 @@ void vb2api_fail(struct vb2_context *ctx, uint8_t reason, uint8_t subcode)
* between slots, which may help if one or both slots is
* flaky.
*/
- vb2_nv_set(ctx, VB2_NV_TRY_NEXT, 1 - sd->fw_slot);
+ vb2_nv_set(ctx, VB2_NV_TRY_NEXT, 1 - fw_slot);
/*
* If we didn't try the other slot last boot, or we tried it
* and it didn't fail, try it next boot.
*/
- if (sd->last_fw_slot != 1 - sd->fw_slot ||
- sd->last_fw_result != VB2_FW_RESULT_FAILURE)
+ if (last_fw_slot != 1 - fw_slot ||
+ last_fw_result != VB2_FW_RESULT_FAILURE)
return;
}
@@ -132,6 +146,24 @@ void vb2api_fail(struct vb2_context *ctx, uint8_t reason, uint8_t subcode)
}
}
+test_mockable
+void vb2api_fail(struct vb2_context *ctx, uint8_t reason, uint8_t subcode)
+{
+ fail_impl(ctx, reason, subcode, false);
+}
+
+test_mockable
+void vb2api_previous_boot_fail(struct vb2_context *ctx,
+ uint8_t reason, uint8_t subcode)
+{
+ struct vb2_shared_data *sd = vb2_get_sd(ctx);
+
+ VB2_ASSERT(!(sd->status & VB2_SD_STATUS_NV_INIT) &&
+ !(sd->status & VB2_SD_STATUS_CHOSE_SLOT));
+
+ fail_impl(ctx, reason, subcode, true);
+}
+
void vb2_check_recovery(struct vb2_context *ctx)
{
struct vb2_shared_data *sd = vb2_get_sd(ctx);
diff --git a/firmware/2lib/include/2api.h b/firmware/2lib/include/2api.h
index 643bc986..dd8af7b3 100644
--- a/firmware/2lib/include/2api.h
+++ b/firmware/2lib/include/2api.h
@@ -404,6 +404,26 @@ vb2_error_t vb2api_secdata_fwmp_check(struct vb2_context *ctx, uint8_t *size);
void vb2api_fail(struct vb2_context *ctx, uint8_t reason, uint8_t subcode);
/**
+ * Report firmware failure from previous boot to vboot.
+ *
+ * This function can only be called before vb2api_fw_phase1 (nvdata is
+ * initialized). Otherwise an assert is raised. This function is required to be
+ * called in the following environment:
+ * - Context has to be initialized using vb2api_init
+ * - NV data has to be read into context
+ * - Secdata may or may not have been read
+ * - vb2api_fw_phase1 must not have been called.
+ *
+ * If the other slot is not known bad then try the other firmware slot.
+ * If both the slots are known bad, then request recovery.
+ *
+ * @param reason Recovery reason
+ * @param subcode Recovery subcode
+ */
+void vb2api_previous_boot_fail(struct vb2_context *ctx,
+ uint8_t reason, uint8_t subcode);
+
+/**
* Entry point for setting up a context that can only load and verify a kernel.
*
* The only allowed usage is to call vb2api_init, then this entry point,
diff --git a/tests/vb2_misc_tests.c b/tests/vb2_misc_tests.c
index 0365f008..b73c0478 100644
--- a/tests/vb2_misc_tests.c
+++ b/tests/vb2_misc_tests.c
@@ -390,10 +390,10 @@ static void fail_tests(void)
reset_common_data();
vb2_nv_set(ctx, VB2_NV_TRY_COUNT, 3);
vb2_nv_set(ctx, VB2_NV_FW_RESULT, VB2_FW_RESULT_UNKNOWN);
+ vb2_nv_set(ctx, VB2_NV_FW_TRIED, 0);
+ vb2_nv_set(ctx, VB2_NV_FW_PREV_TRIED, 1);
+ vb2_nv_set(ctx, VB2_NV_FW_PREV_RESULT, VB2_FW_RESULT_UNKNOWN);
sd->status |= VB2_SD_STATUS_CHOSE_SLOT;
- sd->fw_slot = 0;
- sd->last_fw_slot = 1;
- sd->last_fw_result = VB2_FW_RESULT_UNKNOWN;
vb2api_fail(ctx, 5, 6);
TEST_EQ(vb2_nv_get(ctx, VB2_NV_RECOVERY_REQUEST), 0, "vb2_failover");
TEST_EQ(vb2_nv_get(ctx, VB2_NV_FW_RESULT),
@@ -405,10 +405,10 @@ static void fail_tests(void)
/* Fail with other slot already failing triggers recovery */
reset_common_data();
+ vb2_nv_set(ctx, VB2_NV_FW_PREV_TRIED, 0);
+ vb2_nv_set(ctx, VB2_NV_FW_PREV_RESULT, VB2_FW_RESULT_FAILURE);
+ vb2_nv_set(ctx, VB2_NV_FW_TRIED, 1);
sd->status |= VB2_SD_STATUS_CHOSE_SLOT;
- sd->fw_slot = 1;
- sd->last_fw_slot = 0;
- sd->last_fw_result = VB2_FW_RESULT_FAILURE;
vb2api_fail(ctx, 7, 8);
TEST_EQ(vb2_nv_get(ctx, VB2_NV_RECOVERY_REQUEST), 7,
"vb2api_fail both slots bad");
@@ -418,6 +418,60 @@ static void fail_tests(void)
"vb2api_fail try other slot");
}
+static void previous_boot_fail_tests(void)
+{
+ /* Previous boot fail (before even NV init) */
+ /* Fail with other slot good doesn't trigger recovery */
+ reset_common_data();
+ vb2_nv_set(ctx, VB2_NV_FW_TRIED, VB2_FW_SLOT_A);
+ vb2_nv_set(ctx, VB2_NV_TRY_COUNT, 3);
+ vb2_nv_set(ctx, VB2_NV_FW_RESULT, VB2_FW_RESULT_UNKNOWN);
+ sd->status &= ~VB2_SD_STATUS_NV_INIT;
+ vb2api_previous_boot_fail(ctx, 1, 2);
+ TEST_NEQ(sd->status & VB2_SD_STATUS_NV_INIT,
+ 0, "vb2api_previous_boot_fail inits NV");
+ TEST_EQ(vb2_nv_get(ctx, VB2_NV_RECOVERY_REQUEST),
+ 0, "vb2_previous_boot_fail over");
+ TEST_EQ(vb2_nv_get(ctx, VB2_NV_FW_RESULT),
+ VB2_FW_RESULT_FAILURE, "vb2api_previous_boot_fail result");
+ TEST_EQ(vb2_nv_get(ctx, VB2_NV_TRY_COUNT),
+ 0, "vb2api_previous_boot_fail try count");
+ TEST_EQ(vb2_nv_get(ctx, VB2_NV_TRY_NEXT),
+ 1, "vb2api_previous_boot_fail FW tried");
+
+ /* Fail with other slot already failing triggers recovery */
+ reset_common_data();
+ vb2_nv_set(ctx, VB2_NV_FW_PREV_TRIED, VB2_FW_SLOT_A);
+ vb2_nv_set(ctx, VB2_NV_FW_PREV_RESULT, VB2_FW_RESULT_FAILURE);
+ vb2_nv_set(ctx, VB2_NV_FW_TRIED, VB2_FW_SLOT_B);
+ vb2_nv_set(ctx, VB2_NV_TRY_COUNT, 3);
+ vb2_nv_set(ctx, VB2_NV_FW_RESULT, VB2_FW_RESULT_UNKNOWN);
+ sd->status &= ~VB2_SD_STATUS_NV_INIT;
+ vb2api_previous_boot_fail(ctx, 3, 4);
+ TEST_EQ(vb2_nv_get(ctx, VB2_NV_RECOVERY_REQUEST),
+ 3, "vb2api_previous_boot_fail both slots bad");
+ TEST_EQ(vb2_nv_get(ctx, VB2_NV_FW_RESULT),
+ VB2_FW_RESULT_FAILURE, "vb2api_previous_boot_fail result");
+ TEST_EQ(vb2_nv_get(ctx, VB2_NV_TRY_COUNT),
+ 0, "vb2api_previous_boot_fail try count");
+ TEST_EQ(vb2_nv_get(ctx, VB2_NV_TRY_NEXT),
+ 0, "vb2api_previous_boot_fail FW tried");
+
+ /* Repeated fail doesn't overwrite the error code */
+ reset_common_data();
+ vb2_nv_set(ctx, VB2_NV_TRY_NEXT, VB2_FW_SLOT_A);
+ vb2_nv_set(ctx, VB2_NV_TRY_COUNT, 3);
+ vb2_nv_set(ctx, VB2_NV_FW_RESULT, VB2_FW_RESULT_FAILURE);
+ sd->status &= ~VB2_SD_STATUS_NV_INIT;
+ vb2api_previous_boot_fail(ctx, 5, 6);
+ TEST_EQ(vb2_nv_get(ctx, VB2_NV_FW_RESULT),
+ VB2_FW_RESULT_FAILURE, "vb2api_previous_boot_fail result");
+ TEST_EQ(vb2_nv_get(ctx, VB2_NV_TRY_COUNT),
+ 3, "vb2api_previous_boot_fail try count");
+ TEST_EQ(vb2_nv_get(ctx, VB2_NV_TRY_NEXT),
+ VB2_FW_SLOT_A, "vb2api_previous_boot_fail try next");
+}
+
static void recovery_tests(void)
{
/* No recovery */
@@ -1085,6 +1139,7 @@ int main(int argc, char* argv[])
misc_tests();
gbb_tests();
fail_tests();
+ previous_boot_fail_tests();
recovery_tests();
dev_switch_tests();
enable_dev_tests();