summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--firmware/2lib/2api.c21
-rw-r--r--firmware/2lib/2nvstorage.c7
-rw-r--r--firmware/2lib/include/2api.h16
-rw-r--r--firmware/2lib/include/2nvstorage.h2
-rw-r--r--firmware/2lib/include/2nvstorage_fields.h3
-rw-r--r--firmware/2lib/include/2return_codes.h3
-rw-r--r--firmware/include/vboot_nvstorage.h2
-rw-r--r--firmware/lib/vboot_nvstorage.c12
-rw-r--r--host/lib/crossystem.c2
-rw-r--r--tests/vb2_api_tests.c81
-rw-r--r--tests/vb2_nvstorage_tests.c1
-rw-r--r--tests/vboot_nvstorage_test.c1
-rw-r--r--utility/crossystem.c1
13 files changed, 148 insertions, 4 deletions
diff --git a/firmware/2lib/2api.c b/firmware/2lib/2api.c
index 70cb8a30..a7b41d2b 100644
--- a/firmware/2lib/2api.c
+++ b/firmware/2lib/2api.c
@@ -44,6 +44,27 @@ int vb2api_fw_phase1(struct vb2_context *ctx)
/* Initialize NV context */
vb2_nv_init(ctx);
+ /*
+ * Handle caller-requested reboot due to secdata. Do this before we
+ * even look at secdata. If we fail because of a reboot loop we'll be
+ * the first failure so will get to set the recovery reason.
+ */
+ if (!(ctx->flags & VB2_CONTEXT_SECDATA_WANTS_REBOOT)) {
+ /* No reboot requested */
+ vb2_nv_set(ctx, VB2_NV_TPM_REQUESTED_REBOOT, 0);
+ } else if (vb2_nv_get(ctx, VB2_NV_TPM_REQUESTED_REBOOT)) {
+ /*
+ * Reboot requested... again. Fool me once, shame on you.
+ * Fool me twice, shame on me. Fail into recovery to avoid
+ * a reboot loop.
+ */
+ vb2_fail(ctx, VB2_RECOVERY_RO_TPM_REBOOT, 0);
+ } else {
+ /* Reboot requested for the first time */
+ vb2_nv_set(ctx, VB2_NV_TPM_REQUESTED_REBOOT, 1);
+ return VB2_ERROR_API_PHASE1_SECDATA_REBOOT;
+ }
+
/* Initialize secure data */
rv = vb2_secdata_init(ctx);
if (rv)
diff --git a/firmware/2lib/2nvstorage.c b/firmware/2lib/2nvstorage.c
index 66a2202f..55f1d0dd 100644
--- a/firmware/2lib/2nvstorage.c
+++ b/firmware/2lib/2nvstorage.c
@@ -154,6 +154,9 @@ uint32_t vb2_nv_get(struct vb2_context *ctx, enum vb2_nv_param param)
case VB2_NV_CLEAR_TPM_OWNER_DONE:
return GETBIT(VB2_NV_OFFS_TPM, VB2_NV_TPM_CLEAR_OWNER_DONE);
+ case VB2_NV_TPM_REQUESTED_REBOOT:
+ return GETBIT(VB2_NV_OFFS_TPM, VB2_NV_TPM_REBOOTED);
+
case VB2_NV_REQ_WIPEOUT:
return GETBIT(VB2_NV_OFFS_HEADER , VB2_NV_HEADER_WIPEOUT);
@@ -309,6 +312,10 @@ void vb2_nv_set(struct vb2_context *ctx,
SETBIT(VB2_NV_OFFS_TPM, VB2_NV_TPM_CLEAR_OWNER_DONE);
break;
+ case VB2_NV_TPM_REQUESTED_REBOOT:
+ SETBIT(VB2_NV_OFFS_TPM, VB2_NV_TPM_REBOOTED);
+ break;
+
case VB2_NV_REQ_WIPEOUT:
SETBIT(VB2_NV_OFFS_HEADER , VB2_NV_HEADER_WIPEOUT);
break;
diff --git a/firmware/2lib/include/2api.h b/firmware/2lib/include/2api.h
index 95af636d..56d18d69 100644
--- a/firmware/2lib/include/2api.h
+++ b/firmware/2lib/include/2api.h
@@ -120,6 +120,15 @@ enum vb2_context_flags {
/* Boot optimistically: don't touch failure counters */
VB2_CONTEXT_NOFAIL_BOOT = (1 << 12),
+
+ /*
+ * Secdata is not ready this boot, but should be ready next boot. It
+ * would like to reboot. The decision whether to reboot or not must be
+ * deferred until vboot, because rebooting all the time before then
+ * could cause a device with malfunctioning secdata to get stuck in an
+ * unrecoverable crash loop.
+ */
+ VB2_CONTEXT_SECDATA_WANTS_REBOOT = (1 << 13),
};
/*
@@ -411,8 +420,11 @@ void vb2api_fail(struct vb2_context *ctx, uint8_t reason, uint8_t subcode);
/**
* Firmware selection, phase 1.
*
- * On error, the calling firmware should jump directly to recovery-mode
- * firmware without rebooting.
+ * If the returned error is VB2_ERROR_API_PHASE1_RECOVERY, the calling firmware
+ * should jump directly to recovery-mode firmware without rebooting.
+ *
+ * For other errors, the calling firmware should check for updates to secdata
+ * and/or nvdata, then reboot.
*
* @param ctx Vboot context
* @return VB2_SUCCESS, or error code on error.
diff --git a/firmware/2lib/include/2nvstorage.h b/firmware/2lib/include/2nvstorage.h
index 0a533e3e..bfe9a3a3 100644
--- a/firmware/2lib/include/2nvstorage.h
+++ b/firmware/2lib/include/2nvstorage.h
@@ -71,6 +71,8 @@ enum vb2_nv_param {
VB2_NV_CLEAR_TPM_OWNER_REQUEST,
/* Flag that TPM owner was cleared on request. */
VB2_NV_CLEAR_TPM_OWNER_DONE,
+ /* TPM requested a reboot already. */
+ VB2_NV_TPM_REQUESTED_REBOOT,
/* More details on recovery reason */
VB2_NV_RECOVERY_SUBCODE,
/* Request that NVRAM be backed up at next boot if possible. */
diff --git a/firmware/2lib/include/2nvstorage_fields.h b/firmware/2lib/include/2nvstorage_fields.h
index 45d9a7c5..8ae21f87 100644
--- a/firmware/2lib/include/2nvstorage_fields.h
+++ b/firmware/2lib/include/2nvstorage_fields.h
@@ -63,9 +63,10 @@ enum vb2_nv_offset {
#define VB2_NV_DEV_FLAG_LEGACY 0x04
#define VB2_NV_DEV_FLAG_FASTBOOT_FULL_CAP 0x08
-/* Fields in VB2_NV_OFFS_TPM (unused = 0xfc) */
+/* Fields in VB2_NV_OFFS_TPM (unused = 0xf8) */
#define VB2_NV_TPM_CLEAR_OWNER_REQUEST 0x01
#define VB2_NV_TPM_CLEAR_OWNER_DONE 0x02
+#define VB2_NV_TPM_REBOOTED 0x04
/* Fields in VB2_NV_OFFS_MISC (unused = 0xfc) */
#define VB2_NV_MISC_UNLOCK_FASTBOOT 0x01
diff --git a/firmware/2lib/include/2return_codes.h b/firmware/2lib/include/2return_codes.h
index 40a4c284..4201b693 100644
--- a/firmware/2lib/include/2return_codes.h
+++ b/firmware/2lib/include/2return_codes.h
@@ -520,6 +520,9 @@ enum vb2_return_code {
/* Bad data key in vb2api_verify_kernel_data() */
VB2_ERROR_API_VERIFY_KDATA_KEY,
+ /* Phase one passing through secdata's request to reboot */
+ VB2_ERROR_API_PHASE1_SECDATA_REBOOT,
+
/**********************************************************************
* Errors which may be generated by implementations of vb2ex functions.
* Implementation may also return its own specific errors, which should
diff --git a/firmware/include/vboot_nvstorage.h b/firmware/include/vboot_nvstorage.h
index 4447ef7f..d3070d93 100644
--- a/firmware/include/vboot_nvstorage.h
+++ b/firmware/include/vboot_nvstorage.h
@@ -92,6 +92,8 @@ typedef enum VbNvParam {
VBNV_CLEAR_TPM_OWNER_REQUEST,
/* Flag that TPM owner was cleared on request. */
VBNV_CLEAR_TPM_OWNER_DONE,
+ /* TPM requested a reboot */
+ VBNV_TPM_REQUESTED_REBOOT,
/* More details on recovery reason */
VBNV_RECOVERY_SUBCODE,
/* Request that NVRAM be backed up at next boot if possible. */
diff --git a/firmware/lib/vboot_nvstorage.c b/firmware/lib/vboot_nvstorage.c
index 3b2a725b..6a21bfe8 100644
--- a/firmware/lib/vboot_nvstorage.c
+++ b/firmware/lib/vboot_nvstorage.c
@@ -46,6 +46,7 @@
#define TPM_FLAGS_OFFSET 5
#define TPM_CLEAR_OWNER_REQUEST 0x01
#define TPM_CLEAR_OWNER_DONE 0x02
+#define TPM_REBOOTED 0x04
#define RECOVERY_SUBCODE_OFFSET 6
@@ -177,6 +178,10 @@ int VbNvGet(VbNvContext *context, VbNvParam param, uint32_t *dest)
*dest = (raw[TPM_FLAGS_OFFSET] & TPM_CLEAR_OWNER_DONE ? 1 : 0);
return 0;
+ case VBNV_TPM_REQUESTED_REBOOT:
+ *dest = (raw[TPM_FLAGS_OFFSET] & TPM_REBOOTED ? 1 : 0);
+ return 0;
+
case VBNV_BACKUP_NVRAM_REQUEST:
*dest = (raw[BOOT_OFFSET] & BOOT_BACKUP_NVRAM ? 1 : 0);
return 0;
@@ -347,6 +352,13 @@ int VbNvSet(VbNvContext *context, VbNvParam param, uint32_t value)
raw[TPM_FLAGS_OFFSET] &= ~TPM_CLEAR_OWNER_DONE;
break;
+ case VBNV_TPM_REQUESTED_REBOOT:
+ if (value)
+ raw[TPM_FLAGS_OFFSET] |= TPM_REBOOTED;
+ else
+ raw[TPM_FLAGS_OFFSET] &= ~TPM_REBOOTED;
+ break;
+
case VBNV_BACKUP_NVRAM_REQUEST:
if (value)
raw[BOOT_OFFSET] |= BOOT_BACKUP_NVRAM;
diff --git a/host/lib/crossystem.c b/host/lib/crossystem.c
index 268ecdf1..3080344d 100644
--- a/host/lib/crossystem.c
+++ b/host/lib/crossystem.c
@@ -467,6 +467,8 @@ int VbGetSystemPropertyInt(const char* name) {
value = VbGetNvStorage(VBNV_CLEAR_TPM_OWNER_REQUEST);
} else if (!strcasecmp(name,"clear_tpm_owner_done")) {
value = VbGetNvStorage(VBNV_CLEAR_TPM_OWNER_DONE);
+ } else if (!strcasecmp(name,"tpm_rebooted")) {
+ value = VbGetNvStorage(VBNV_TPM_REQUESTED_REBOOT);
} else if (!strcasecmp(name,"fwb_tries")) {
value = VbGetNvStorage(VBNV_TRY_B_COUNT);
} else if (!strcasecmp(name,"fw_vboot2")) {
diff --git a/tests/vb2_api_tests.c b/tests/vb2_api_tests.c
index 2978c3fd..adc536b6 100644
--- a/tests/vb2_api_tests.c
+++ b/tests/vb2_api_tests.c
@@ -131,7 +131,6 @@ static void phase1_tests(void)
TEST_NEQ(cc.flags & VB2_CONTEXT_RECOVERY_MODE, 0, " recovery flag");
TEST_NEQ(cc.flags & VB2_CONTEXT_CLEAR_RAM, 0, " clear ram flag");
-
reset_common_data(FOR_MISC);
retval_vb2_check_dev_switch = VB2_ERROR_MOCK;
TEST_EQ(vb2api_fw_phase1(&cc), VB2_ERROR_API_PHASE1_RECOVERY,
@@ -149,6 +148,86 @@ static void phase1_tests(void)
" recovery reason");
TEST_NEQ(cc.flags & VB2_CONTEXT_RECOVERY_MODE, 0, " recovery flag");
TEST_NEQ(cc.flags & VB2_CONTEXT_CLEAR_RAM, 0, " clear ram flag");
+
+ /* Test secdata-requested reboot */
+ reset_common_data(FOR_MISC);
+ cc.flags |= VB2_CONTEXT_SECDATA_WANTS_REBOOT;
+ TEST_EQ(vb2api_fw_phase1(&cc), VB2_ERROR_API_PHASE1_SECDATA_REBOOT,
+ "phase1 secdata reboot normal");
+ TEST_EQ(sd->recovery_reason, 0, " recovery reason");
+ TEST_EQ(vb2_nv_get(&cc, VB2_NV_TPM_REQUESTED_REBOOT),
+ 1, " tpm reboot request");
+ TEST_EQ(vb2_nv_get(&cc, VB2_NV_RECOVERY_REQUEST),
+ 0, " recovery request");
+
+ reset_common_data(FOR_MISC);
+ vb2_nv_set(&cc, VB2_NV_TPM_REQUESTED_REBOOT, 1);
+ TEST_SUCC(vb2api_fw_phase1(&cc), "phase1 secdata reboot back normal");
+ TEST_EQ(sd->recovery_reason, 0, " recovery reason");
+ TEST_EQ(vb2_nv_get(&cc, VB2_NV_TPM_REQUESTED_REBOOT),
+ 0, " tpm reboot request");
+ TEST_EQ(vb2_nv_get(&cc, VB2_NV_RECOVERY_REQUEST),
+ 0, " recovery request");
+
+ reset_common_data(FOR_MISC);
+ cc.flags |= VB2_CONTEXT_SECDATA_WANTS_REBOOT;
+ memset(cc.secdata, 0, sizeof(cc.secdata));
+ TEST_EQ(vb2api_fw_phase1(&cc), VB2_ERROR_API_PHASE1_SECDATA_REBOOT,
+ "phase1 secdata reboot normal, secdata blank");
+ TEST_EQ(sd->recovery_reason, 0, " recovery reason");
+ TEST_EQ(vb2_nv_get(&cc, VB2_NV_TPM_REQUESTED_REBOOT),
+ 1, " tpm reboot request");
+ TEST_EQ(vb2_nv_get(&cc, VB2_NV_RECOVERY_REQUEST),
+ 0, " recovery request");
+
+ reset_common_data(FOR_MISC);
+ cc.flags |= VB2_CONTEXT_SECDATA_WANTS_REBOOT;
+ vb2_nv_set(&cc, VB2_NV_TPM_REQUESTED_REBOOT, 1);
+ TEST_EQ(vb2api_fw_phase1(&cc), VB2_ERROR_API_PHASE1_RECOVERY,
+ "phase1 secdata reboot normal again");
+ TEST_EQ(sd->recovery_reason, VB2_RECOVERY_RO_TPM_REBOOT,
+ " recovery reason");
+ TEST_EQ(vb2_nv_get(&cc, VB2_NV_TPM_REQUESTED_REBOOT),
+ 1, " tpm reboot request");
+ TEST_EQ(vb2_nv_get(&cc, VB2_NV_RECOVERY_REQUEST),
+ 0, " recovery request");
+
+ reset_common_data(FOR_MISC);
+ cc.flags |= VB2_CONTEXT_SECDATA_WANTS_REBOOT;
+ vb2_nv_set(&cc, VB2_NV_RECOVERY_REQUEST, VB2_RECOVERY_RO_UNSPECIFIED);
+ TEST_EQ(vb2api_fw_phase1(&cc), VB2_ERROR_API_PHASE1_SECDATA_REBOOT,
+ "phase1 secdata reboot recovery");
+ /* Recovery reason isn't set this boot because we're rebooting first */
+ TEST_EQ(sd->recovery_reason, 0, " recovery reason not set THIS boot");
+ TEST_EQ(vb2_nv_get(&cc, VB2_NV_TPM_REQUESTED_REBOOT),
+ 1, " tpm reboot request");
+ TEST_EQ(vb2_nv_get(&cc, VB2_NV_RECOVERY_REQUEST),
+ VB2_RECOVERY_RO_UNSPECIFIED, " recovery request not cleared");
+
+ reset_common_data(FOR_MISC);
+ vb2_nv_set(&cc, VB2_NV_TPM_REQUESTED_REBOOT, 1);
+ vb2_nv_set(&cc, VB2_NV_RECOVERY_REQUEST, VB2_RECOVERY_RO_UNSPECIFIED);
+ TEST_EQ(vb2api_fw_phase1(&cc), VB2_ERROR_API_PHASE1_RECOVERY,
+ "phase1 secdata reboot back recovery");
+ TEST_EQ(sd->recovery_reason, VB2_RECOVERY_RO_UNSPECIFIED,
+ " recovery reason");
+ TEST_EQ(vb2_nv_get(&cc, VB2_NV_TPM_REQUESTED_REBOOT),
+ 0, " tpm reboot request");
+ TEST_EQ(vb2_nv_get(&cc, VB2_NV_RECOVERY_REQUEST), 0,
+ " recovery request cleared");
+
+ reset_common_data(FOR_MISC);
+ cc.flags |= VB2_CONTEXT_SECDATA_WANTS_REBOOT;
+ vb2_nv_set(&cc, VB2_NV_TPM_REQUESTED_REBOOT, 1);
+ vb2_nv_set(&cc, VB2_NV_RECOVERY_REQUEST, VB2_RECOVERY_RO_UNSPECIFIED);
+ TEST_EQ(vb2api_fw_phase1(&cc), VB2_ERROR_API_PHASE1_RECOVERY,
+ "phase1 secdata reboot recovery again");
+ TEST_EQ(sd->recovery_reason, VB2_RECOVERY_RO_UNSPECIFIED,
+ " recovery reason");
+ TEST_EQ(vb2_nv_get(&cc, VB2_NV_TPM_REQUESTED_REBOOT),
+ 1, " tpm reboot request");
+ TEST_EQ(vb2_nv_get(&cc, VB2_NV_RECOVERY_REQUEST), 0,
+ " recovery request cleared");
}
static void phase2_tests(void)
diff --git a/tests/vb2_nvstorage_tests.c b/tests/vb2_nvstorage_tests.c
index 02c8acc8..0bf58b61 100644
--- a/tests/vb2_nvstorage_tests.c
+++ b/tests/vb2_nvstorage_tests.c
@@ -47,6 +47,7 @@ static struct nv_field nvfields[] = {
{VB2_NV_DISABLE_DEV_REQUEST, 0, 1, 0, "disable dev request"},
{VB2_NV_CLEAR_TPM_OWNER_REQUEST, 0, 1, 0, "clear tpm owner request"},
{VB2_NV_CLEAR_TPM_OWNER_DONE, 0, 1, 0, "clear tpm owner done"},
+ {VB2_NV_TPM_REQUESTED_REBOOT, 0, 1, 0, "tpm requested reboot"},
{VB2_NV_OPROM_NEEDED, 0, 1, 0, "oprom needed"},
{VB2_NV_BACKUP_NVRAM_REQUEST, 0, 1, 0, "backup nvram request"},
{VB2_NV_FASTBOOT_UNLOCK_IN_FW, 0, 1, 0, "fastboot unlock in fw"},
diff --git a/tests/vboot_nvstorage_test.c b/tests/vboot_nvstorage_test.c
index bbded635..a82a7429 100644
--- a/tests/vboot_nvstorage_test.c
+++ b/tests/vboot_nvstorage_test.c
@@ -37,6 +37,7 @@ static VbNvField nvfields[] = {
{VBNV_DISABLE_DEV_REQUEST, 0, 1, 0, "disable dev request"},
{VBNV_CLEAR_TPM_OWNER_REQUEST, 0, 1, 0, "clear tpm owner request"},
{VBNV_CLEAR_TPM_OWNER_DONE, 0, 1, 0, "clear tpm owner done"},
+ {VBNV_TPM_REQUESTED_REBOOT, 0, 1, 0, "tpm requested reboot"},
{VBNV_OPROM_NEEDED, 0, 1, 0, "oprom needed"},
{VBNV_FW_TRY_COUNT, 0, 8, 15, "try count"},
{VBNV_FW_TRY_NEXT, 0, 1, 0, "try next"},
diff --git a/utility/crossystem.c b/utility/crossystem.c
index 606240e5..2c017aff 100644
--- a/utility/crossystem.c
+++ b/utility/crossystem.c
@@ -87,6 +87,7 @@ const Param sys_param_list[] = {
{"tpm_attack", CAN_WRITE, "TPM was interrupted since this flag was cleared"},
{"tpm_fwver", 0, "Firmware version stored in TPM", "0x%08x"},
{"tpm_kernver", 0, "Kernel version stored in TPM", "0x%08x"},
+ {"tpm_rebooted", 0, "TPM requesting repeated reboot (vboot2)"},
{"tried_fwb", 0, "Tried firmware B before A this boot"},
{"vdat_flags", 0, "Flags from VbSharedData", "0x%08x"},
{"vdat_lfdebug", IS_STRING|NO_PRINT_ALL,