Index: firmware/lib/rollback_index.c |
diff --git a/firmware/lib/rollback_index.c b/firmware/lib/rollback_index.c |
index cb86e89e8d51cf7952a3adacc24acd03e40241d7..b5d404a4352c79c3cb818746f74c889066973405 100644 |
--- a/firmware/lib/rollback_index.c |
+++ b/firmware/lib/rollback_index.c |
@@ -24,10 +24,32 @@ uint16_t g_kernel_version = 0; |
} \ |
} while (0) |
+static uint32_t TPMClearAndReenable() { |
+ RETURN_ON_FAILURE(TlclForceClear()); |
+ RETURN_ON_FAILURE(TlclSetEnable()); |
+ RETURN_ON_FAILURE(TlclSetDeactivated(0)); |
+ return TPM_SUCCESS; |
+} |
+ |
+/* Like TlclWrite(), but checks for write errors due to hitting the 64-write |
+ * limit and clears the TPM when that happens. This can only happen when the |
+ * TPM is unowned, so it is OK to clear it (and we really have no choice). |
+ * This is not expected to happen frequently, but it could happen. |
+ */ |
+static uint32_t SafeWrite(uint32_t index, uint8_t* data, uint32_t length) { |
+ uint32_t result = TlclWrite(index, data, length); |
+ if (result == TPM_E_MAXNVWRITES) { |
+ RETURN_ON_FAILURE(TPMClearAndReenable()); |
+ return TlclWrite(index, data, length); |
+ } else { |
+ return result; |
+ } |
+} |
+ |
static uint32_t InitializeKernelVersionsSpaces(void) { |
RETURN_ON_FAILURE(TlclDefineSpace(KERNEL_VERSIONS_NV_INDEX, |
TPM_NV_PER_PPWRITE, KERNEL_SPACE_SIZE)); |
- RETURN_ON_FAILURE(TlclWrite(KERNEL_VERSIONS_NV_INDEX, KERNEL_SPACE_INIT_DATA, |
+ RETURN_ON_FAILURE(SafeWrite(KERNEL_VERSIONS_NV_INDEX, KERNEL_SPACE_INIT_DATA, |
KERNEL_SPACE_SIZE)); |
return TPM_SUCCESS; |
} |
@@ -65,7 +87,7 @@ static uint32_t InitializeSpaces(void) { |
RETURN_ON_FAILURE(TlclDefineSpace(FIRMWARE_VERSIONS_NV_INDEX, |
firmware_perm, sizeof(uint32_t))); |
- RETURN_ON_FAILURE(TlclWrite(FIRMWARE_VERSIONS_NV_INDEX, |
+ RETURN_ON_FAILURE(SafeWrite(FIRMWARE_VERSIONS_NV_INDEX, |
(uint8_t*) &zero, sizeof(uint32_t))); |
RETURN_ON_FAILURE(InitializeKernelVersionsSpaces()); |
@@ -76,15 +98,15 @@ static uint32_t InitializeSpaces(void) { |
*/ |
RETURN_ON_FAILURE(TlclDefineSpace(KERNEL_VERSIONS_BACKUP_NV_INDEX, |
firmware_perm, sizeof(uint32_t))); |
- RETURN_ON_FAILURE(TlclWrite(KERNEL_VERSIONS_BACKUP_NV_INDEX, |
+ RETURN_ON_FAILURE(SafeWrite(KERNEL_VERSIONS_BACKUP_NV_INDEX, |
(uint8_t*) &zero, sizeof(uint32_t))); |
RETURN_ON_FAILURE(TlclDefineSpace(KERNEL_MUST_USE_BACKUP_NV_INDEX, |
firmware_perm, sizeof(uint32_t))); |
- RETURN_ON_FAILURE(TlclWrite(KERNEL_MUST_USE_BACKUP_NV_INDEX, |
+ RETURN_ON_FAILURE(SafeWrite(KERNEL_MUST_USE_BACKUP_NV_INDEX, |
(uint8_t*) &zero, sizeof(uint32_t))); |
RETURN_ON_FAILURE(TlclDefineSpace(DEVELOPER_MODE_NV_INDEX, |
firmware_perm, sizeof(uint32_t))); |
- RETURN_ON_FAILURE(TlclWrite(DEVELOPER_MODE_NV_INDEX, |
+ RETURN_ON_FAILURE(SafeWrite(DEVELOPER_MODE_NV_INDEX, |
(uint8_t*) &zero, sizeof(uint32_t))); |
/* The space TPM_IS_INITIALIZED_NV_INDEX is used to indicate that the TPM |
@@ -102,7 +124,7 @@ static uint32_t SetDistrustKernelSpaceAtNextBoot(uint32_t distrust) { |
RETURN_ON_FAILURE(TlclRead(KERNEL_MUST_USE_BACKUP_NV_INDEX, |
(uint8_t*) &must_use_backup, sizeof(uint32_t))); |
if (must_use_backup != distrust) { |
- RETURN_ON_FAILURE(TlclWrite(KERNEL_MUST_USE_BACKUP_NV_INDEX, |
+ RETURN_ON_FAILURE(SafeWrite(KERNEL_MUST_USE_BACKUP_NV_INDEX, |
(uint8_t*) &distrust, sizeof(uint32_t))); |
} |
return TPM_SUCCESS; |
@@ -173,14 +195,13 @@ uint32_t RecoverKernelSpace(void) { |
RETURN_ON_FAILURE(TlclRead(KERNEL_VERSIONS_BACKUP_NV_INDEX, |
(uint8_t*) &backup_combined_versions, |
sizeof(uint32_t))); |
- RETURN_ON_FAILURE(TlclWrite(KERNEL_VERSIONS_NV_INDEX, |
+ RETURN_ON_FAILURE(SafeWrite(KERNEL_VERSIONS_NV_INDEX, |
(uint8_t*) &backup_combined_versions, |
sizeof(uint32_t))); |
if (must_use_backup) { |
uint32_t zero = 0; |
- RETURN_ON_FAILURE(TlclWrite(KERNEL_MUST_USE_BACKUP_NV_INDEX, |
+ RETURN_ON_FAILURE(SafeWrite(KERNEL_MUST_USE_BACKUP_NV_INDEX, |
(uint8_t*) &zero, 0)); |
- |
} |
return TPM_SUCCESS; |
} |
@@ -198,7 +219,7 @@ static uint32_t BackupKernelSpace(void) { |
/* This cannot happen. We're screwed. */ |
return TPM_E_INTERNAL_INCONSISTENCY; |
} |
- RETURN_ON_FAILURE(TlclWrite(KERNEL_VERSIONS_BACKUP_NV_INDEX, |
+ RETURN_ON_FAILURE(SafeWrite(KERNEL_VERSIONS_BACKUP_NV_INDEX, |
(uint8_t*) &kernel_versions, sizeof(uint32_t))); |
return TPM_SUCCESS; |
} |
@@ -208,23 +229,16 @@ static uint32_t BackupKernelSpace(void) { |
*/ |
static uint32_t CheckDeveloperModeTransition(uint32_t current_developer) { |
uint32_t past_developer; |
- int must_clear; |
RETURN_ON_FAILURE(TlclRead(DEVELOPER_MODE_NV_INDEX, |
(uint8_t*) &past_developer, |
sizeof(past_developer))); |
- must_clear = current_developer != past_developer; |
- if (must_clear) { |
- RETURN_ON_FAILURE(TlclForceClear()); |
- } |
if (past_developer != current_developer) { |
- /* (Unauthorized) writes to the TPM succeed even when the TPM is disabled |
- * and deactivated. |
- */ |
- RETURN_ON_FAILURE(TlclWrite(DEVELOPER_MODE_NV_INDEX, |
+ RETURN_ON_FAILURE(TPMClearAndReenable()); |
+ RETURN_ON_FAILURE(SafeWrite(DEVELOPER_MODE_NV_INDEX, |
(uint8_t*) ¤t_developer, |
sizeof(current_developer))); |
} |
- return must_clear ? TPM_E_MUST_REBOOT : TPM_SUCCESS; |
+ return TPM_SUCCESS; |
} |
static uint32_t SetupTPM_(int mode, int developer_flag) { |
@@ -295,11 +309,7 @@ uint32_t SetupTPM(int mode, int developer_flag) { |
case RO_RECOVERY_MODE: |
case RO_NORMAL_MODE: { |
uint32_t result = SetupTPM_(mode, developer_flag); |
- if (result == TPM_E_MAXNVWRITES) { |
- /* ForceClears and reboots */ |
- RETURN_ON_FAILURE(TlclForceClear()); |
- return TPM_E_MUST_REBOOT; |
- } else if (mode == RO_NORMAL_MODE) { |
+ if (mode == RO_NORMAL_MODE) { |
return result; |
} else { |
/* In recovery mode we want to keep going even if there are errors. */ |
@@ -307,8 +317,6 @@ uint32_t SetupTPM(int mode, int developer_flag) { |
} |
} |
case RW_NORMAL_MODE: |
- /* There are no TPM writes here, so no need to check for write limit errors. |
- */ |
RETURN_ON_FAILURE(GetTPMRollbackIndices(KERNEL_VERSIONS)); |
default: |
return TPM_E_INTERNAL_INCONSISTENCY; |
@@ -340,13 +348,13 @@ uint32_t WriteStoredVersions(int type, uint16_t key_version, uint16_t version) { |
uint32_t combined_version = (key_version << 16) & version; |
switch (type) { |
case FIRMWARE_VERSIONS: |
- RETURN_ON_FAILURE(TlclWrite(FIRMWARE_VERSIONS_NV_INDEX, |
+ RETURN_ON_FAILURE(SafeWrite(FIRMWARE_VERSIONS_NV_INDEX, |
(uint8_t*) &combined_version, |
sizeof(uint32_t))); |
break; |
case KERNEL_VERSIONS: |
- RETURN_ON_FAILURE(TlclWrite(KERNEL_VERSIONS_NV_INDEX, |
+ RETURN_ON_FAILURE(SafeWrite(KERNEL_VERSIONS_NV_INDEX, |
(uint8_t*) &combined_version, |
sizeof(uint32_t))); |
} |