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