summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRandall Spangler <rspangler@chromium.org>2014-09-23 12:20:31 -0700
committerchrome-internal-fetch <chrome-internal-fetch@google.com>2014-10-04 00:59:48 +0000
commit782300d093a2fbf2ca24e446fb6d65f9f28e56a6 (patch)
tree07e711928061b4b1c19845ca6bb17f4992ae5487
parent80872dbffcb2e0079bfe095d770ccc6c8bd5fd7a (diff)
downloadvboot-782300d093a2fbf2ca24e446fb6d65f9f28e56a6.tar.gz
vboot2: Add previously tried slot and result to NV storage
This gives recovery mode information on two boots back instead of one, which may be handy for debugging. It also allows determining whether a failure of the current boot should try the other slot or go to recovery, using only information stored in NV storage. Added crossystem support for printing the fields, and unit tests. BUG=chrome-os-partner:32585 BRANCH=none TEST=make runtests; VBOOT2=1 make runtests Change-Id: Ia9f4186210d30217b902db7c513ae4ab8851f8f4 Signed-off-by: Randall Spangler <rspangler@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/221230 Reviewed-by: Daisuke Nojiri <dnojiri@chromium.org>
-rw-r--r--firmware/2lib/2misc.c4
-rw-r--r--firmware/2lib/2nvstorage.c28
-rw-r--r--firmware/2lib/include/2nvstorage.h6
-rw-r--r--firmware/include/vboot_nvstorage.h5
-rw-r--r--firmware/lib/vboot_nvstorage.c28
-rw-r--r--host/lib/crossystem.c8
-rw-r--r--tests/vb2_misc_tests.c17
-rw-r--r--tests/vb2_nvstorage_tests.c2
-rw-r--r--tests/vboot_nvstorage_test.c2
9 files changed, 96 insertions, 4 deletions
diff --git a/firmware/2lib/2misc.c b/firmware/2lib/2misc.c
index 94ad0c3b..bcf5b362 100644
--- a/firmware/2lib/2misc.c
+++ b/firmware/2lib/2misc.c
@@ -316,6 +316,10 @@ int vb2_select_fw_slot(struct vb2_context *ctx)
sd->last_fw_slot = vb2_nv_get(ctx, VB2_NV_FW_TRIED);
sd->last_fw_result = vb2_nv_get(ctx, VB2_NV_FW_RESULT);
+ /* Save to the previous result fields in NV storage */
+ vb2_nv_set(ctx, VB2_NV_FW_PREV_TRIED, sd->last_fw_slot);
+ vb2_nv_set(ctx, VB2_NV_FW_PREV_RESULT, sd->last_fw_result);
+
/* Clear result, since we don't know what will happen this boot */
vb2_nv_set(ctx, VB2_NV_FW_RESULT, VB2_FW_RESULT_UNKNOWN);
diff --git a/firmware/2lib/2nvstorage.c b/firmware/2lib/2nvstorage.c
index 02904741..5c7b9800 100644
--- a/firmware/2lib/2nvstorage.c
+++ b/firmware/2lib/2nvstorage.c
@@ -31,7 +31,7 @@ enum vb2_nv_offset {
VB2_NV_OFFS_TPM = 5,
VB2_NV_OFFS_RECOVERY_SUBCODE = 6,
VB2_NV_OFFS_BOOT2 = 7,
- /* Offsets 7-10 are currently unused */
+ /* Offsets 8-10 are currently unused */
VB2_NV_OFFS_KERNEL = 11, /* 11-14; field is 32 bits */
/* CRC must be last field */
VB2_NV_OFFS_CRC = 15
@@ -50,10 +50,13 @@ enum vb2_nv_offset {
#define VB2_NV_BOOT_DISABLE_DEV 0x40
#define VB2_NV_BOOT_DEBUG_RESET 0x80
-/* Fields in VB2_NV_OFFS_BOOT2 (unused = 0xf0) */
+/* Fields in VB2_NV_OFFS_BOOT2 (unused = 0x80) */
#define VB2_NV_BOOT2_RESULT_MASK 0x03
#define VB2_NV_BOOT2_TRIED 0x04
#define VB2_NV_BOOT2_TRY_NEXT 0x08
+#define VB2_NV_BOOT2_PREV_RESULT_MASK 0x30
+#define VB2_NV_BOOT2_PREV_RESULT_SHIFT 4 /* Number of bits to shift result */
+#define VB2_NV_BOOT2_PREV_TRIED 0x40
/* Fields in VB2_NV_OFFS_DEV (unused = 0xf8) */
#define VB2_NV_DEV_FLAG_USB 0x01
@@ -156,6 +159,13 @@ uint32_t vb2_nv_get(struct vb2_context *ctx, enum vb2_nv_param param)
case VB2_NV_FW_RESULT:
return p[VB2_NV_OFFS_BOOT2] & VB2_NV_BOOT2_RESULT_MASK;
+ case VB2_NV_FW_PREV_TRIED:
+ return GETBIT(VB2_NV_OFFS_BOOT2, VB2_NV_BOOT2_PREV_TRIED);
+
+ case VB2_NV_FW_PREV_RESULT:
+ return (p[VB2_NV_OFFS_BOOT2] & VB2_NV_BOOT2_PREV_RESULT_MASK)
+ >> VB2_NV_BOOT2_PREV_RESULT_SHIFT;
+
case VB2_NV_RECOVERY_REQUEST:
return p[VB2_NV_OFFS_RECOVERY];
@@ -262,6 +272,20 @@ void vb2_nv_set(struct vb2_context *ctx,
p[VB2_NV_OFFS_BOOT2] |= (uint8_t)value;
break;
+ case VB2_NV_FW_PREV_TRIED:
+ SETBIT(VB2_NV_OFFS_BOOT2, VB2_NV_BOOT2_PREV_TRIED);
+ break;
+
+ case VB2_NV_FW_PREV_RESULT:
+ /* Map out of range values to unknown */
+ if (value > VB2_NV_BOOT2_RESULT_MASK)
+ value = VB2_FW_RESULT_UNKNOWN;
+
+ p[VB2_NV_OFFS_BOOT2] &= ~VB2_NV_BOOT2_PREV_RESULT_MASK;
+ p[VB2_NV_OFFS_BOOT2] |=
+ (uint8_t)(value << VB2_NV_BOOT2_PREV_RESULT_SHIFT);
+ break;
+
case VB2_NV_RECOVERY_REQUEST:
/*
* Map values outside the valid range to the legacy reason,
diff --git a/firmware/2lib/include/2nvstorage.h b/firmware/2lib/include/2nvstorage.h
index 11a77966..3bda9d78 100644
--- a/firmware/2lib/include/2nvstorage.h
+++ b/firmware/2lib/include/2nvstorage.h
@@ -74,9 +74,13 @@ enum vb2_nv_param {
VB2_NV_FW_TRIED,
/* Result of trying that firmware (see vb2_fw_result) */
VB2_NV_FW_RESULT,
+ /* Firmware slot tried previous boot (0=A, 1=B) */
+ VB2_NV_FW_PREV_TRIED,
+ /* Result of trying that firmware (see vb2_fw_result) */
+ VB2_NV_FW_PREV_RESULT,
};
-/* Result of trying the firmware in VB2_NV_FW_TRIED */
+/* Firmware result codes for VB2_NV_FW_RESULT and VB2_NV_FW_PREV_RESULT */
enum vb2_fw_result {
/* Unknown */
VB2_FW_RESULT_UNKNOWN = 0,
diff --git a/firmware/include/vboot_nvstorage.h b/firmware/include/vboot_nvstorage.h
index 0a9a4841..f71a55ab 100644
--- a/firmware/include/vboot_nvstorage.h
+++ b/firmware/include/vboot_nvstorage.h
@@ -98,7 +98,10 @@ typedef enum VbNvParam {
VBNV_FW_TRIED,
/* Vboot2: Result of trying that firmware (see vb2_fw_result) */
VBNV_FW_RESULT,
-
+ /* Firmware slot tried previous boot (0=A, 1=B) */
+ VBNV_FW_PREV_TRIED,
+ /* Result of trying that firmware (see vb2_fw_result) */
+ VBNV_FW_PREV_RESULT,
} VbNvParam;
diff --git a/firmware/lib/vboot_nvstorage.c b/firmware/lib/vboot_nvstorage.c
index 5476e85b..a0721d7c 100644
--- a/firmware/lib/vboot_nvstorage.c
+++ b/firmware/lib/vboot_nvstorage.c
@@ -51,6 +51,9 @@
#define BOOT2_RESULT_MASK 0x03
#define BOOT2_TRIED 0x04
#define BOOT2_TRY_NEXT 0x08
+#define BOOT2_PREV_RESULT_MASK 0x30
+#define BOOT2_PREV_RESULT_SHIFT 4 /* Number of bits to shift result */
+#define BOOT2_PREV_TRIED 0x40
#define KERNEL_FIELD_OFFSET 11
#define CRC_OFFSET 15
@@ -179,6 +182,15 @@ int VbNvGet(VbNvContext *context, VbNvParam param, uint32_t *dest)
*dest = raw[BOOT2_OFFSET] & BOOT2_RESULT_MASK;
return 0;
+ case VBNV_FW_PREV_TRIED:
+ *dest = (raw[BOOT2_OFFSET] & BOOT2_PREV_TRIED ? 1 : 0);
+ return 0;
+
+ case VBNV_FW_PREV_RESULT:
+ *dest = (raw[BOOT2_OFFSET] & BOOT2_PREV_RESULT_MASK)
+ >> BOOT2_PREV_RESULT_SHIFT;
+ return 0;
+
default:
return 1;
}
@@ -333,6 +345,22 @@ int VbNvSet(VbNvContext *context, VbNvParam param, uint32_t value)
raw[BOOT2_OFFSET] |= (uint8_t)value;
break;
+ case VBNV_FW_PREV_TRIED:
+ if (value)
+ raw[BOOT2_OFFSET] |= BOOT2_PREV_TRIED;
+ else
+ raw[BOOT2_OFFSET] &= ~BOOT2_PREV_TRIED;
+ break;
+
+ case VBNV_FW_PREV_RESULT:
+ /* Map out of range values to unknown */
+ if (value > BOOT2_RESULT_MASK)
+ value = VBNV_FW_RESULT_UNKNOWN;
+
+ raw[BOOT2_OFFSET] &= ~BOOT2_PREV_RESULT_MASK;
+ raw[BOOT2_OFFSET] |= (uint8_t)value << BOOT2_PREV_RESULT_SHIFT;
+ break;
+
default:
return 1;
}
diff --git a/host/lib/crossystem.c b/host/lib/crossystem.c
index d6555c67..b28fd877 100644
--- a/host/lib/crossystem.c
+++ b/host/lib/crossystem.c
@@ -560,6 +560,14 @@ const char* VbGetSystemPropertyString(const char* name, char* dest,
return fw_results[v];
else
return "unknown";
+ } else if (!strcasecmp(name, "fw_prev_tried")) {
+ return VbGetNvStorage(VBNV_FW_PREV_TRIED) ? "B" : "A";
+ } else if (!strcasecmp(name, "fw_prev_result")) {
+ int v = VbGetNvStorage(VBNV_FW_PREV_RESULT);
+ if (v < ARRAY_SIZE(fw_results))
+ return fw_results[v];
+ else
+ return "unknown";
}
return NULL;
diff --git a/tests/vb2_misc_tests.c b/tests/vb2_misc_tests.c
index 72c284c6..6155f23e 100644
--- a/tests/vb2_misc_tests.c
+++ b/tests/vb2_misc_tests.c
@@ -450,6 +450,23 @@ static void select_slot_tests(void)
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), 2, "tries decremented");
+
+ /* Tried/result get copied to the previous fields */
+ reset_common_data();
+ vb2_nv_set(&cc, VB2_NV_FW_TRIED, 0);
+ vb2_nv_set(&cc, VB2_NV_FW_RESULT, VB2_FW_RESULT_SUCCESS);
+ vb2_select_fw_slot(&cc);
+ TEST_EQ(vb2_nv_get(&cc, VB2_NV_FW_PREV_TRIED), 0, "prev A");
+ TEST_EQ(vb2_nv_get(&cc, VB2_NV_FW_PREV_RESULT), VB2_FW_RESULT_SUCCESS,
+ "prev success");
+
+ reset_common_data();
+ vb2_nv_set(&cc, VB2_NV_FW_TRIED, 1);
+ vb2_nv_set(&cc, VB2_NV_FW_RESULT, VB2_FW_RESULT_FAILURE);
+ vb2_select_fw_slot(&cc);
+ TEST_EQ(vb2_nv_get(&cc, VB2_NV_FW_PREV_TRIED), 1, "prev B");
+ TEST_EQ(vb2_nv_get(&cc, VB2_NV_FW_PREV_RESULT), VB2_FW_RESULT_FAILURE,
+ "prev failure");
}
int main(int argc, char* argv[])
diff --git a/tests/vb2_nvstorage_tests.c b/tests/vb2_nvstorage_tests.c
index b648ca95..46547f8f 100644
--- a/tests/vb2_nvstorage_tests.c
+++ b/tests/vb2_nvstorage_tests.c
@@ -34,6 +34,8 @@ static struct nv_field nvfields[] = {
{VB2_NV_TRY_COUNT, 0, 6, 15, "try B count"},
{VB2_NV_FW_TRIED, 0, 1, 0, "firmware tried"},
{VB2_NV_FW_RESULT, 0, 1, 2, "firmware result"},
+ {VB2_NV_FW_PREV_TRIED, 0, 1, 0, "firmware prev tried"},
+ {VB2_NV_FW_PREV_RESULT, 0, 1, 3, "firmware prev result"},
{VB2_NV_RECOVERY_REQUEST, 0, 0x42, 0xED, "recovery request"},
{VB2_NV_RECOVERY_SUBCODE, 0, 0x56, 0xAC, "recovery subcode"},
{VB2_NV_LOCALIZATION_INDEX, 0, 0x69, 0xB0, "localization index"},
diff --git a/tests/vboot_nvstorage_test.c b/tests/vboot_nvstorage_test.c
index 2d096182..ec857382 100644
--- a/tests/vboot_nvstorage_test.c
+++ b/tests/vboot_nvstorage_test.c
@@ -41,6 +41,8 @@ static VbNvField nvfields[] = {
{VBNV_FW_TRY_NEXT, 0, 1, 0, "try next"},
{VBNV_FW_TRIED, 0, 1, 0, "firmware tried"},
{VBNV_FW_RESULT, VBNV_FW_RESULT_UNKNOWN, 1, 2, "firmware result"},
+ {VBNV_FW_PREV_TRIED, 0, 1, 0, "firmware prev tried"},
+ {VBNV_FW_PREV_RESULT, VBNV_FW_RESULT_UNKNOWN, 1, 3, "firmware prev result"},
{0, 0, 0, 0, NULL}
};