Index: firmware/lib/vboot_firmware.c |
diff --git a/firmware/lib/vboot_firmware.c b/firmware/lib/vboot_firmware.c |
index 97494333442e243204063e6db0c05a960ab4edfd..e8c8a86fa902d4cb5f52954a46d2eed9ebb6a2b0 100644 |
--- a/firmware/lib/vboot_firmware.c |
+++ b/firmware/lib/vboot_firmware.c |
@@ -10,6 +10,7 @@ |
#include "rollback_index.h" |
#include "utility.h" |
#include "vboot_common.h" |
+#include "vboot_nvstorage.h" |
/* Static variables for UpdateFirmwareBodyHash(). It's less than |
* optimal to have static variables in a library, but in UEFI the |
@@ -31,32 +32,47 @@ void UpdateFirmwareBodyHash(LoadFirmwareParams* params, |
} |
+int LoadFirmwareSetup(void) { |
+ /* TODO: start initializing the TPM */ |
+ return LOAD_FIRMWARE_SUCCESS; |
+} |
+ |
+ |
int LoadFirmware(LoadFirmwareParams* params) { |
VbPublicKey* root_key = (VbPublicKey*)params->firmware_root_key_blob; |
VbLoadFirmwareInternal* lfi; |
+ VbNvContext* vnc = params->nv_context; |
+ uint32_t try_b_count; |
uint32_t tpm_version = 0; |
uint64_t lowest_version = 0xFFFFFFFF; |
uint32_t status; |
int good_index = -1; |
int is_dev; |
int index; |
+ int i; |
+ |
+ int retval = LOAD_FIRMWARE_RECOVERY; |
+ int recovery = VBNV_RECOVERY_RO_UNSPECIFIED; |
/* Clear output params in case we fail */ |
params->firmware_index = 0; |
VBDEBUG(("LoadFirmware started...\n")); |
+ /* Setup NV storage */ |
+ VbNvSetup(vnc); |
+ |
if (params->kernel_sign_key_size < sizeof(VbPublicKey)) { |
VBDEBUG(("Kernel sign key buffer too small\n")); |
- return LOAD_FIRMWARE_RECOVERY; |
+ goto LoadFirmwareExit; |
} |
/* Must have a root key */ |
if (!root_key) { |
VBDEBUG(("No root key\n")); |
- return LOAD_FIRMWARE_RECOVERY; |
+ goto LoadFirmwareExit; |
} |
/* Parse flags */ |
@@ -68,11 +84,20 @@ int LoadFirmware(LoadFirmwareParams* params) { |
if (0 != status) { |
VBDEBUG(("Unable to setup TPM and read stored versions.\n")); |
VBPERFEND("VB_TPMI"); |
- return (status == TPM_E_MUST_REBOOT ? |
- LOAD_FIRMWARE_REBOOT : LOAD_FIRMWARE_RECOVERY_TPM); |
+ if (status == TPM_E_MUST_REBOOT) |
+ retval = LOAD_FIRMWARE_REBOOT; |
+ else |
+ recovery = VBNV_RECOVERY_RO_TPM_ERROR; |
+ goto LoadFirmwareExit; |
} |
VBPERFEND("VB_TPMI"); |
+ /* Read try-b count and decrement if necessary */ |
+ VbNvGet(vnc, VBNV_TRY_B_COUNT, &try_b_count); |
+ if (0 != try_b_count) |
+ VbNvSet(vnc, VBNV_TRY_B_COUNT, try_b_count - 1); |
+ VbNvSet(vnc, VBNV_TRIED_FIRMWARE_B, try_b_count ? 1 : 0); |
+ |
/* Allocate our internal data */ |
lfi = (VbLoadFirmwareInternal*)Malloc(sizeof(VbLoadFirmwareInternal)); |
if (!lfi) |
@@ -81,7 +106,7 @@ int LoadFirmware(LoadFirmwareParams* params) { |
params->load_firmware_internal = (uint8_t*)lfi; |
/* Loop over indices */ |
- for (index = 0; index < 2; index++) { |
+ for (i = 0; i < 2; i++) { |
VbKeyBlockHeader* key_block; |
uint64_t vblock_size; |
VbFirmwarePreambleHeader* preamble; |
@@ -90,6 +115,9 @@ int LoadFirmware(LoadFirmwareParams* params) { |
uint64_t combined_version; |
uint8_t* body_digest; |
+ /* If try B count is non-zero try firmware B first */ |
+ index = (try_b_count ? i : 1 - i); |
+ |
/* Verify the key block */ |
VBPERFSTART("VB_VKB"); |
if (0 == index) { |
@@ -248,8 +276,11 @@ int LoadFirmware(LoadFirmwareParams* params) { |
VBPERFEND("VB_TPMU"); |
if (0 != status) { |
VBDEBUG(("Unable to write stored versions.\n")); |
- return (status == TPM_E_MUST_REBOOT ? |
- LOAD_FIRMWARE_REBOOT : LOAD_FIRMWARE_RECOVERY_TPM); |
+ if (status == TPM_E_MUST_REBOOT) |
+ retval = LOAD_FIRMWARE_REBOOT; |
+ else |
+ recovery = VBNV_RECOVERY_RO_TPM_ERROR; |
+ goto LoadFirmwareExit; |
} |
} |
@@ -259,18 +290,29 @@ int LoadFirmware(LoadFirmwareParams* params) { |
VBPERFEND("VB_TPML"); |
if (0 != status) { |
VBDEBUG(("Unable to lock firmware versions.\n")); |
- return (status == TPM_E_MUST_REBOOT ? |
- LOAD_FIRMWARE_REBOOT : LOAD_FIRMWARE_RECOVERY_TPM); |
+ if (status == TPM_E_MUST_REBOOT) |
+ retval = LOAD_FIRMWARE_REBOOT; |
+ else |
+ recovery = VBNV_RECOVERY_RO_TPM_ERROR; |
+ goto LoadFirmwareExit; |
} |
/* Success */ |
VBDEBUG(("Will boot firmware index %d\n", (int)params->firmware_index)); |
- return LOAD_FIRMWARE_SUCCESS; |
+ retval = LOAD_FIRMWARE_SUCCESS; |
+ } else { |
+ /* No good firmware, so go to recovery mode. */ |
+ VBDEBUG(("Alas, no good firmware.\n")); |
+ recovery = VBNV_RECOVERY_RO_INVALID_RW; |
} |
- /* If we're still here, no good firmware, so go to recovery mode. */ |
- VBDEBUG(("Alas, no good firmware.\n")); |
- return LOAD_FIRMWARE_RECOVERY; |
+LoadFirmwareExit: |
+ /* Store recovery request, if any, then tear down non-volatile storage */ |
+ VbNvSet(vnc, VBNV_RECOVERY_REQUEST, LOAD_FIRMWARE_RECOVERY == retval ? |
+ recovery : VBNV_RECOVERY_NOT_REQUESTED); |
+ VbNvTeardown(vnc); |
+ |
+ return retval; |
} |
@@ -278,10 +320,11 @@ int S3Resume(void) { |
/* Resume the TPM */ |
uint32_t status = RollbackS3Resume(); |
+ /* If we can't resume, just do a full reboot. No need to go to recovery |
+ * mode here, since if the TPM is really broken we'll catch it on the |
+ * next boot. */ |
if (status == TPM_SUCCESS) |
return LOAD_FIRMWARE_SUCCESS; |
- else if (status == TPM_E_MUST_REBOOT) |
- return LOAD_FIRMWARE_REBOOT; |
else |
- return LOAD_FIRMWARE_RECOVERY_TPM; |
+ return LOAD_FIRMWARE_REBOOT; |
} |