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>
This commit is contained in:
Randall Spangler
2014-09-23 12:20:31 -07:00
committed by chrome-internal-fetch
parent 80872dbffc
commit 782300d093
9 changed files with 96 additions and 4 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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"},

View File

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