summaryrefslogtreecommitdiff
path: root/firmware
diff options
context:
space:
mode:
authorKarthikeyan Ramasubramanian <kramasub@google.com>2022-11-29 13:20:45 -0700
committerChromeos LUCI <chromeos-scoped@luci-project-accounts.iam.gserviceaccount.com>2022-12-09 19:12:36 +0000
commitd19765a9507e33c697bfd18038f860e62bd99161 (patch)
treee227ef9cb5b711fe7472c36d98f725c4ec45d4c6 /firmware
parent05b81fcc18c905613ef56e09ca100ef79a6ee5d2 (diff)
downloadvboot-d19765a9507e33c697bfd18038f860e62bd99161.tar.gz
firmware/2lib: Introduce API to report previous boot failure
Currently when failures are reported before a slot is selected, vboot directly requests for recovery. Add a new API to report previous boot failure before a slot is selected. This will allow coreboot verstage to report any failures that happened in the previous boot such that verified boot can select the appropriate FW slot instead of booting into recovery mode directly. BUG=b:242825052 BRANCH=None TEST=Build Skyrim BIOS image. Run the unit test built for this API. Boot to OS in Skyrim. Corrupt certain sections in flashmap and report boot failures and ensured that vboot selected the appropriate FW slot. Change-Id: I3b1fe8e28fc754919cd4067eeed5029e7dbae7a4 Signed-off-by: Karthikeyan Ramasubramanian <kramasub@google.com> Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/vboot_reference/+/4064425 Reviewed-by: Julius Werner <jwerner@chromium.org>
Diffstat (limited to 'firmware')
-rw-r--r--firmware/2lib/2misc.c44
-rw-r--r--firmware/2lib/include/2api.h20
2 files changed, 58 insertions, 6 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,