diff --git a/TAs/optee_ta/fTPM/fTPM.c b/TAs/optee_ta/fTPM/fTPM.c index ec2806c..379b31a 100644 --- a/TAs/optee_ta/fTPM/fTPM.c +++ b/TAs/optee_ta/fTPM/fTPM.c @@ -119,12 +119,14 @@ TEE_Result TA_CreateEntryPoint(void) 0x00, 0x00, 0x01, 0x44, 0x00, 0x01 }; uint32_t respLen; uint8_t *respBuf; + uint32_t result; + TEE_Result teeResult; // If we've been here before, don't init again. if (fTPMInitialized) { // We may have had TA_DestroyEntryPoint called but we didn't // actually get torn down. Re-NVEnable, just in case. - if (_plat__NVEnable(NULL) == 0) { + if (_plat__NVEnable(NULL) != 0) { TEE_Panic(TEE_ERROR_BAD_STATE); } return TEE_SUCCESS; @@ -134,8 +136,26 @@ TEE_Result TA_CreateEntryPoint(void) _admin__NvInitState(); // If we fail to open fTPM storage we cannot continue. - if (_plat__NVEnable(NULL) == 0) { - TEE_Panic(TEE_ERROR_BAD_STATE); + DMSG("fTPM attempting to enable NV storage"); + result = _plat__NVEnable(NULL); + if (result != 0) { + EMSG("fTPM failed to load NV storage"); + if (result > 0) { + EMSG("Possibly a recoverable failure..."); // This is a possibly recoverable error, try to fix it and re-enable. + teeResult = _plat__NvRecoverStorage(); + if (teeResult == TEE_ERROR_STORAGE_NOT_AVAILABLE || + teeResult == TEE_SUCCESS) + { + EMSG("Possible storage recovery completed (0x%x)", teeResult); + result = _plat__NVEnable(NULL); + } + } + + if (result != 0) + { + EMSG("fTPM unrecoverable failure when enabling NV storage (0x%x)", result); + TEE_Panic(TEE_ERROR_BAD_STATE); + } } // This only occurs when there is no previous NV state, i.e., on first @@ -145,30 +165,28 @@ TEE_Result TA_CreateEntryPoint(void) TPM_Manufacture(1); } + DMSG("fTPM Power on"); // "Power-On" the platform _plat__Signal_PowerOn(); + DMSG("fTPM Init"); // Internal init for reference implementation _TPM_Init(); // Startup with state if (g_chipFlags.fields.TpmStatePresent) { - + DMSG("fTPM Found existing state"); // Re-use request buffer for response (ignored) respBuf = startupState; respLen = STARTUP_SIZE; - ExecuteCommand(STARTUP_SIZE, startupState, &respLen, &respBuf); + _plat__RunCommand(STARTUP_SIZE, startupState, &respLen, &respBuf); if (fTPMResponseCode(respLen, respBuf) == TPM_RC_SUCCESS) { + DMSG("fTPM starup success"); goto Exit; } -#ifdef fTPMDebug DMSG("Fall through to startup clear\n"); -#endif - - //DMSG("Start self test"); - //CryptSelfTest(1); goto Clear; } @@ -179,7 +197,7 @@ TEE_Result TA_CreateEntryPoint(void) respLen = STARTUP_SIZE; // Fall back to a Startup Clear - ExecuteCommand(STARTUP_SIZE, startupClear, &respLen, &respBuf); + _plat__RunCommand(STARTUP_SIZE, startupClear, &respLen, &respBuf); Exit: // Init is complete, indicate so in fTPM admin state. @@ -189,7 +207,18 @@ TEE_Result TA_CreateEntryPoint(void) // Initialization complete fTPMInitialized = true; - return TEE_SUCCESS; +#ifdef fTPMDebug + DMSG("Start forced self test"); + CryptSelfTest(1); +#endif + + if (g_inFailureMode) { + EMSG("Startup failed"); + return TEE_ERROR_BAD_STATE; + } else { + EMSG("Startup succeeded"); + return TEE_SUCCESS; + } } @@ -303,7 +332,7 @@ static TEE_Result fTPM_Submit_Command(uint32_t param_types, // Check if this is a PPI Command if (!_admin__PPICommand(cmdLen, cmdBuf, &respLen, &respBuf)) { // If not, pass through to TPM - ExecuteCommand(cmdLen, cmdBuf, &respLen, &respBuf); + _plat__RunCommand(cmdLen, cmdBuf, &respLen, &respBuf); } // Unfortunately, this cannot be done until after we have our response in // hand. We will, however, make an effort to return at least a portion of @@ -319,7 +348,12 @@ static TEE_Result fTPM_Submit_Command(uint32_t param_types, DMSG("Success, RS: 0x%x\n", respLen); #endif - return TEE_SUCCESS; + if (g_inFailureMode) { + EMSG("fTPM in failure mode!"); + return TEE_ERROR_BAD_STATE; + } else { + return TEE_SUCCESS; + } } // diff --git a/TAs/optee_ta/fTPM/include/fTPM.h b/TAs/optee_ta/fTPM/include/fTPM.h index 1bf5d10..c612620 100644 --- a/TAs/optee_ta/fTPM/include/fTPM.h +++ b/TAs/optee_ta/fTPM/include/fTPM.h @@ -36,6 +36,8 @@ #ifndef FTPM_TA_H #define FTPM_TA_H +#include + // // This UUID is generated with uuidgen // @@ -92,6 +94,9 @@ extern void _plat__Signal_PowerOn(void); extern void _plat__NVDisable(void); extern void _admin__SaveChipFlags(void); +extern TEE_Result _plat__NvRecoverStorage(void); + + // // External types/data supporting TPM initialization // @@ -105,4 +110,6 @@ typedef union { } TPM_CHIP_STATE; extern TPM_CHIP_STATE g_chipFlags; +extern bool g_inFailureMode; + #endif /* FTPM_TA_H */ \ No newline at end of file diff --git a/TAs/optee_ta/fTPM/platform/NVMem.c b/TAs/optee_ta/fTPM/platform/NVMem.c index 90346fa..f76f546 100644 --- a/TAs/optee_ta/fTPM/platform/NVMem.c +++ b/TAs/optee_ta/fTPM/platform/NVMem.c @@ -95,8 +95,12 @@ for (int i = 0; i < NV_BLOCK_COUNT; i++) \ // // NV state // +// Contents of NV need to be cleared/initialized static BOOL s_NVChipFileNeedsManufacture = FALSE; +// NV objects have been successfully opened static BOOL s_NVInitialized = FALSE; +// Error state of the NV storage objects +static TEE_Result s_NVStorageState = TEE_ERROR_STORAGE_NOT_AVAILABLE; // // Firmware revision @@ -109,12 +113,156 @@ static const UINT32 firmwareV2 = FIRMWARE_V2; // static UINT64 s_chipRevision = 0; +TEE_Result +WipeMemory( + VOID +) +/*++ + + Routine Description: + + Clears all persistent objects + + Arguments: + + None + + Returns: + + TEE_SUCCESS if the memory was successfully wiped. + + +--*/ +{ + TEE_Result status; + TEE_ObjectInfo objInfo; + TEE_ObjectHandle objHandle; + TEE_ObjectEnumHandle enumHandle = TEE_HANDLE_NULL; + UINT64 objID; + UINT32 objIDLen; + + EMSG("Wiping Authvars memory!"); + + status = TEE_AllocatePersistentObjectEnumerator(&enumHandle); + if (status != TEE_SUCCESS) + { + EMSG("Failed to create enumerator: 0x%x", status); + goto Cleanup; + } + + do + { + // It is possible deleting objects will break the current enumerator, restart it each time. + DMSG("Starting enumerator"); + status = TEE_StartPersistentObjectEnumerator(enumHandle, TEE_STORAGE_PRIVATE); + if (status == TEE_ERROR_ITEM_NOT_FOUND) + { + break; + } + if (status != TEE_SUCCESS) + { + EMSG("Authvars storage wipe failed 0x%x", status); + goto Cleanup; + } + + status = TEE_GetNextPersistentObject(enumHandle, + &objInfo, + &objID, + &objIDLen); + if (status != TEE_SUCCESS) + { + EMSG("Authvars storage wipe failed to get next object"); + goto Cleanup; + } + + status = TEE_OpenPersistentObject(TEE_STORAGE_PRIVATE, + &objID, + objIDLen, + TA_STORAGE_FLAGS, + &objHandle); + + if (status != TEE_SUCCESS) + { + EMSG("Authvars storage wipe failed to open object for deletion"); + goto Cleanup; + } + + DMSG("Deleting object"); + TEE_CloseAndDeletePersistentObject1(objHandle); + objHandle = TEE_HANDLE_NULL; + + //TEE_ResetPersistentObjectEnumerator(enumHandle); + + } while( status == TEE_SUCCESS ); + + if (status == TEE_ERROR_ITEM_NOT_FOUND) + { + // This is the expected result, once the enumerator is done. + status = TEE_SUCCESS; + } + +Cleanup: + if(IS_VALID(enumHandle)) + { + TEE_FreePersistentObjectEnumerator(enumHandle); + } + + if (status != TEE_SUCCESS) + { + EMSG("Failed to reset Authvars storage 0x%x", status); + return status; + } + + return status; +} + +TEE_Result +_plat__NvRecoverStorage( +) +/*++ + + Routine Description: + + Decides which options should be attempted to recover memory. + + Arguments: + + None + + Returns: + + TEE_SUCCESS if recovery was performed successfully. + TEE_ERROR_STORAGE_NOT_AVAILABLE if storage has been deleted. + TEE_ERROR_BAD_STATE if recover was not succesfull. + +--*/ +{ +#ifdef FTPM_DEBUG + EMSG("Authvars has detected its storage is invalid, run OP-TEE with CFG_RPMB_FAT_RESET=y to clear."); +#endif +#ifdef FTPM_UPGRADE_MEMORY + if (s_NVStorageState = TEE_ERROR_NOT_SUPPORTED) { + EMSG("Version upgrade not supported"); + TEE_Panic(TEE_ERROR_NOT_SUPPORTED); + } +#else +#endif +#ifdef FTPM_WIPE_MEMORY_ON_CORRUPTION + if (WipeMemory() == TEE_SUCCESS) + { + EMSG("Returning 0x%x", TEE_ERROR_STORAGE_NOT_AVAILABLE); + return TEE_ERROR_STORAGE_NOT_AVAILABLE; + } +#endif + EMSG("fTPM: Failed to recover storage."); + return TEE_ERROR_BAD_STATE; +} + VOID _plat__NvInitFromStorage() { DMSG("_plat__NvInitFromStorage()"); UINT32 i; - BOOL initialized; UINT32 objID; UINT32 bytesRead; TEE_Result Result; @@ -126,14 +274,10 @@ _plat__NvInitFromStorage() // // If the NV file is successfully read from the storage then - // initialized must be set. We are setting initialized to true - // here but if an error is encountered reading the NV file it will - // be reset. + // s_NVInitialized must be set. We will set s_NVInitialized to true + // once reading the NV file has been completed without error. // - initialized = TRUE; - - // Collect storage objects and init NV. for (i = 0; i < NV_BLOCK_COUNT; i++) { @@ -154,7 +298,8 @@ _plat__NvInitFromStorage() // There was an error, fail the init, NVEnable can retry. if (Result != TEE_ERROR_ITEM_NOT_FOUND) { - DMSG("fTPM: Failed to open fTPM storage object %d", i); + DMSG("fTPM: Failed to open fTPM storage object %d (0x%x)", i, Result); + s_NVStorageState = TEE_ERROR_BAD_STATE; goto Error; } @@ -173,6 +318,7 @@ _plat__NvInitFromStorage() // There was an error, fail the init, NVEnable can retry. if (Result != TEE_SUCCESS) { DMSG("Failed to create fTPM storage object"); + s_NVStorageState = TEE_ERROR_STORAGE_NOT_AVAILABLE; goto Error; } @@ -182,9 +328,6 @@ _plat__NvInitFromStorage() // To ensure NV is consistent, force a write back of all NV blocks NV_DIRTY_ALL(s_blockMap); - // Need to re-initialize - initialized = FALSE; - IMSG("Created fTPM storage object, i: 0x%x, s: 0x%x, id: 0x%x, h:0x%x\n", i, NV_BLOCK_SIZE, objID, s_NVStore[i]); } @@ -198,8 +341,15 @@ _plat__NvInitFromStorage() &bytesRead); // Give up on failed or incomplete reads. - if ((Result != TEE_SUCCESS) || (bytesRead != NV_BLOCK_SIZE)) { + if (Result != TEE_SUCCESS) { DMSG("Failed to read fTPM storage object"); + s_NVStorageState = TEE_ERROR_STORAGE_NOT_AVAILABLE; + goto Error; + } + + if (bytesRead != NV_BLOCK_SIZE) { + DMSG("fTPM storage objects are the wrong size"); + s_NVStorageState = TEE_ERROR_NOT_SUPPORTED; goto Error; } @@ -210,41 +360,27 @@ _plat__NvInitFromStorage() // Storage objects are open and valid, next validate revision. Note that a // change to s_chipRevision width also requires a change to NvMemoryLayout.h. + // Reaching here means that we recognize the storage, but may still need to + // reset its contents. s_chipRevision = ((((UINT64)firmwareV2) << 32) | (firmwareV1)); if ((s_chipRevision != *(UINT64*)&(s_NV[NV_CHIP_REVISION_OFFSET]))) { - - // Failure to validate revision, re-init (only the TPM's NV memory) - memset(s_NV, 0, (NV_TPM_STORAGE_SIZE)); - - // Going to manufacture, ensure zero flags - g_chipFlags.flags = 0; - - // Save flags - _admin__SaveChipFlags(); - - // Dirty the block map, we're going to re-init. - _plat__MarkDirtyBlocks(0, (NV_TPM_STORAGE_SIZE)); - - // Init with proper revision - s_chipRevision = ((((UINT64)firmwareV2) << 32) | (firmwareV1)); - *(UINT64*)&(s_NV[NV_CHIP_REVISION_OFFSET]) = s_chipRevision; - - DMSG("Failed to validate revision. Did we just (re)-init?"); + // Failure to validate revision, this may be recoverable. If not we will + // wipe the NV storage and re-manufacture. + s_NVStorageState = TEE_ERROR_NOT_SUPPORTED; // Force (re)manufacture. s_NVChipFileNeedsManufacture = TRUE; - - // Need to re-initialize - initialized = FALSE; - - return; } - s_NVInitialized = initialized; + // The NV storage has been loaded, however may still require manufacturing + s_NVInitialized = TRUE; + + s_NVStorageState = TEE_SUCCESS; return; Error: + EMSG("Aborting NvInit due to error"); s_NVInitialized = FALSE; for (i = 0; i < NV_BLOCK_COUNT; i++) { if (IS_VALID(s_NVStore[i])) { @@ -388,7 +524,7 @@ _plat__NVEnable( _plat__NvInitFromStorage(); // Were we successful? - if (!s_NVInitialized) { + if (s_NVChipFileNeedsManufacture == TRUE || s_NVInitialized == FALSE) { // Arriving here means one of two things: Either there existed no // NV state before we came along and we just (re)initialized our // storage. Or there is an error condition preventing us from @@ -401,10 +537,10 @@ _plat__NVEnable( // should we decide not to just TEE_Panic, we can continue // execution after (re)manufacture. Later an attempt at re-init // can be made by calling _plat__NvInitFromStorage again. - retVal = 0; + retVal = 1; } else { - retVal = 1; + retVal = 0; } // Going to manufacture, zero flags @@ -413,9 +549,6 @@ _plat__NVEnable( // Save flags _admin__SaveChipFlags(); - // Now we're done - s_NVInitialized = TRUE; - return retVal; } else { @@ -429,7 +562,7 @@ _plat__NVEnable( _admin__RestoreChipFlags(); // Success - retVal = 1; + retVal = 0; } return retVal; diff --git a/TAs/optee_ta/fTPM/platform/RunCommand.c b/TAs/optee_ta/fTPM/platform/RunCommand.c index 32d169f..aadb11f 100644 --- a/TAs/optee_ta/fTPM/platform/RunCommand.c +++ b/TAs/optee_ta/fTPM/platform/RunCommand.c @@ -94,5 +94,6 @@ _plat__Fail( #if FAIL_TRACE EMSG("%s:%d", s_failFunction, s_failLine); #endif + longjmp(&(s_jumpBuffer[0]), 1); TEE_Panic(TEE_ERROR_BAD_STATE); } \ No newline at end of file diff --git a/TAs/optee_ta/fTPM/sub.mk b/TAs/optee_ta/fTPM/sub.mk index 15ecf3d..f67daa7 100644 --- a/TAs/optee_ta/fTPM/sub.mk +++ b/TAs/optee_ta/fTPM/sub.mk @@ -37,6 +37,17 @@ CPPFLAGS += -mno-unaligned-access CPPFLAGS += -DfTPMARM32=1 endif +# +# Configure memory upgrade/recovery options +# +ifeq ($(CFG_TA_WIPE_ON_ERROR),y) +$(info wipe on) +CPPFLAGS += -DFTPM_WIPE_MEMORY_ON_CORRUPTION +endif +ifeq ($(CFG_TA_ENABLE_UPGRADE),y) +CPPFLAGS += -DFTPM_UPGRADE_MEMORY +endif + ifeq ($(CFG_TA_DEBUG),y) CPPFLAGS += -DDBG=1 CPPFLAGS += -O0