diff options
-rw-r--r-- | firmware/2lib/2api.c | 21 | ||||
-rw-r--r-- | firmware/2lib/2nvstorage.c | 7 | ||||
-rw-r--r-- | firmware/2lib/include/2api.h | 16 | ||||
-rw-r--r-- | firmware/2lib/include/2nvstorage.h | 2 | ||||
-rw-r--r-- | firmware/2lib/include/2nvstorage_fields.h | 3 | ||||
-rw-r--r-- | firmware/2lib/include/2return_codes.h | 3 | ||||
-rw-r--r-- | firmware/include/vboot_nvstorage.h | 2 | ||||
-rw-r--r-- | firmware/lib/vboot_nvstorage.c | 12 | ||||
-rw-r--r-- | host/lib/crossystem.c | 2 | ||||
-rw-r--r-- | tests/vb2_api_tests.c | 81 | ||||
-rw-r--r-- | tests/vb2_nvstorage_tests.c | 1 | ||||
-rw-r--r-- | tests/vboot_nvstorage_test.c | 1 | ||||
-rw-r--r-- | utility/crossystem.c | 1 |
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, |