// This file was extracted from the TCG Published // Trusted Platform Module Library // Part 4: Supporting Routines // Family "2.0" // Level 00 Revision 01.16 // October 30, 2014 #define NV_C #include "InternalRoutines.h" #include "Platform.h" // // NV Index/evict object iterator value // typedef UINT32 NV_ITER; // type of a NV iterator #define NV_ITER_INIT 0xFFFFFFFF // initial value to start an // iterator // // // NV Utility Functions // // NvCheckState() // // Function to check the NV state by accessing the platform-specific function to get the NV state. The result // state is registered in s_NvIsAvailable that will be reported by NvIsAvailable(). // This function is called at the beginning of ExecuteCommand() before any potential call to NvIsAvailable(). // void NvCheckState(void) { int func_return; func_return = _plat__IsNvAvailable(); if(func_return == 0) { s_NvStatus = TPM_RC_SUCCESS; } else if(func_return == 1) { s_NvStatus = TPM_RC_NV_UNAVAILABLE; } else { s_NvStatus = TPM_RC_NV_RATE; } return; } // // // NvIsAvailable() // // This function returns the NV availability parameter. // // Error Returns Meaning // // TPM_RC_SUCCESS NV is available // TPM_RC_NV_RATE NV is unavailable because of rate limit // TPM_RC_NV_UNAVAILABLE NV is inaccessible // TPM_RC NvIsAvailable( void ) { // Make sure that NV state is still good if (s_NvStatus == TPM_RC_SUCCESS) NvCheckState(); return s_NvStatus; } // // // NvCommit // // This is a wrapper for the platform function to commit pending NV writes. // BOOL NvCommit( void ) { BOOL success = (_plat__NvCommit() == 0); return success; } // // // NvReadMaxCount() // // This function returns the max NV counter value. // static UINT64 NvReadMaxCount( void ) { UINT64 countValue; _plat__NvMemoryRead(s_maxCountAddr, sizeof(UINT64), &countValue); return countValue; } // // // NvWriteMaxCount() // // This function updates the max counter value to NV memory. // static void NvWriteMaxCount( UINT64 maxCount ) { _plat__NvMemoryWrite(s_maxCountAddr, sizeof(UINT64), &maxCount); return; } // // // NV Index and Persistent Object Access Functions // // Introduction // // These functions are used to access an NV Index and persistent object memory. In this implementation, // the memory is simulated with RAM. The data in dynamic area is organized as a linked list, starting from // address s_evictNvStart. The first 4 bytes of a node in this link list is the offset of next node, followed by // the data entry. A 0-valued offset value indicates the end of the list. If the data entry area of the last node // happens to reach the end of the dynamic area without space left for an additional 4 byte end marker, the // end address, s_evictNvEnd, should serve as the mark of list end // // NvNext() // // This function provides a method to traverse every data entry in NV dynamic area. // To begin with, parameter iter should be initialized to NV_ITER_INIT indicating the first element. Every // time this function is called, the value in iter would be adjusted pointing to the next element in traversal. If // there is no next element, iter value would be 0. This function returns the address of the 'data entry' // pointed by the iter. If there is no more element in the set, a 0 value is returned indicating the end of // traversal. // static UINT32 NvNext( NV_ITER *iter ) { NV_ITER currentIter; // If iterator is at the beginning of list if(*iter == NV_ITER_INIT) { // Initialize iterator *iter = s_evictNvStart; } // If iterator reaches the end of NV space, or iterator indicates list end if(*iter + sizeof(UINT32) > s_evictNvEnd || *iter == 0) return 0; // Save the current iter offset currentIter = *iter; // Adjust iter pointer pointing to next entity // Read pointer value _plat__NvMemoryRead(*iter, sizeof(UINT32), iter); if(*iter == 0) return 0; return currentIter + sizeof(UINT32); // entity stores after the pointer } // // // NvGetEnd() // // Function to find the end of the NV dynamic data list // static UINT32 NvGetEnd( void ) { NV_ITER iter = NV_ITER_INIT; UINT32 endAddr = s_evictNvStart; UINT32 currentAddr; while((currentAddr = NvNext(&iter)) != 0) endAddr = currentAddr; if(endAddr != s_evictNvStart) { // Read offset endAddr -= sizeof(UINT32); _plat__NvMemoryRead(endAddr, sizeof(UINT32), &endAddr); } return endAddr; } // // // NvGetFreeByte // // This function returns the number of free octets in NV space. // static UINT32 NvGetFreeByte( void ) { return s_evictNvEnd - NvGetEnd(); } // // NvGetEvictObjectSize // // This function returns the size of an evict object in NV space // static UINT32 NvGetEvictObjectSize( void ) { return sizeof(TPM_HANDLE) + sizeof(OBJECT) + sizeof(UINT32); } // // // NvGetCounterSize // // This function returns the size of a counter index in NV space. // static UINT32 NvGetCounterSize( void ) { // It takes an offset field, a handle and the sizeof(NV_INDEX) and // sizeof(UINT64) for counter data return sizeof(TPM_HANDLE) + sizeof(NV_INDEX) + sizeof(UINT64) + sizeof(UINT32); } // // // NvTestSpace() // // This function will test if there is enough space to add a new entity. // // Return Value Meaning // // TRUE space available // FALSE no enough space // static BOOL NvTestSpace( UINT32 size, // IN: size of the entity to be added BOOL isIndex // IN: TRUE if the entity is an index ) { UINT32 remainByte = NvGetFreeByte(); // For NV Index, need to make sure that we do not allocate and Index if this // would mean that the TPM cannot allocate the minimum number of evict // objects. if(isIndex) { // Get the number of persistent objects allocated UINT32 persistentNum = NvCapGetPersistentNumber(); // If we have not allocated the requisite number of evict objects, then we // need to reserve space for them. // NOTE: some of this is not written as simply as it might seem because // the values are all unsigned and subtracting needs to be done carefully // so that an underflow doesn't cause problems. if(persistentNum < MIN_EVICT_OBJECTS) { UINT32 needed = (MIN_EVICT_OBJECTS - persistentNum) * NvGetEvictObjectSize(); if(needed > remainByte) remainByte = 0; else remainByte -= needed; } // if the requisite number of evict objects have been allocated then // no need to reserve additional space } // This checks for the size of the value being added plus the index value. // NOTE: This does not check to see if the end marker can be placed in // memory because the end marker will not be written if it will not fit. return (size + sizeof(UINT32) <= remainByte); } // // // NvAdd() // // This function adds a new entity to NV. // This function requires that there is enough space to add a new entity (i.e., that NvTestSpace() has been // called and the available space is at least as large as the required space). // static void NvAdd( UINT32 totalSize, // IN: total size needed for this entity For // evict object, totalSize is the same as // bufferSize. For NV Index, totalSize is // bufferSize plus index data size UINT32 bufferSize, // IN: size of initial buffer BYTE *entity // IN: initial buffer ) { UINT32 endAddr; UINT32 nextAddr; UINT32 listEnd = 0; // Get the end of data list endAddr = NvGetEnd(); // Calculate the value of next pointer, which is the size of a pointer + // the entity data size nextAddr = endAddr + sizeof(UINT32) + totalSize; // Write next pointer _plat__NvMemoryWrite(endAddr, sizeof(UINT32), &nextAddr); // Write entity data _plat__NvMemoryWrite(endAddr + sizeof(UINT32), bufferSize, entity); // Write the end of list if it is not going to exceed the NV space if(nextAddr + sizeof(UINT32) <= s_evictNvEnd) _plat__NvMemoryWrite(nextAddr, sizeof(UINT32), &listEnd); // Set the flag so that NV changes are committed before the command completes. g_updateNV = TRUE; } // // // NvDelete() // // This function is used to delete an NV Index or persistent object from NV memory. // static void NvDelete( UINT32 entityAddr // IN: address of entity to be deleted ) { UINT32 next; UINT32 entrySize; UINT32 entryAddr = entityAddr - sizeof(UINT32); UINT32 listEnd = 0; // Get the offset of the next entry. _plat__NvMemoryRead(entryAddr, sizeof(UINT32), &next); // The size of this entry is the difference between the current entry and the // next entry. entrySize = next - entryAddr; // Move each entry after the current one to fill the freed space. // Stop when we have reached the end of all the indexes. There are two // ways to detect the end of the list. The first is to notice that there // is no room for anything else because we are at the end of NV. The other // indication is that we find an end marker. // The loop condition checks for the end of NV. while(next + sizeof(UINT32) <= s_evictNvEnd) { UINT32 size, oldAddr, newAddr; // Now check for the end marker _plat__NvMemoryRead(next, sizeof(UINT32), &oldAddr); if(oldAddr == 0) break; size = oldAddr - next; // Move entry _plat__NvMemoryMove(next, next - entrySize, size); // Update forward link newAddr = oldAddr - entrySize; _plat__NvMemoryWrite(next - entrySize, sizeof(UINT32), &newAddr); next = oldAddr; } // Mark the end of list _plat__NvMemoryWrite(next - entrySize, sizeof(UINT32), &listEnd); // Set the flag so that NV changes are committed before the command completes. g_updateNV = TRUE; } // // // RAM-based NV Index Data Access Functions // // Introduction // // The data layout in ram buffer is {size of(NV_handle() + data), NV_handle(), data} for each NV Index data // stored in RAM. // NV storage is updated when a NV Index is added or deleted. We do NOT updated NV storage when the // data is updated/ // // NvTestRAMSpace() // // This function indicates if there is enough RAM space to add a data for a new NV Index. // // // // // Return Value Meaning // // TRUE space available // FALSE no enough space // static BOOL NvTestRAMSpace( UINT32 size // IN: size of the data to be added to RAM ) { BOOL success = ( s_ramIndexSize + size + sizeof(TPM_HANDLE) + sizeof(UINT32) <= RAM_INDEX_SPACE); return success; } // // // NvGetRamIndexOffset // // This function returns the offset of NV data in the RAM buffer // This function requires that NV Index is in RAM. That is, the index must be known to exist. // static UINT32 NvGetRAMIndexOffset( TPMI_RH_NV_INDEX handle // IN: NV handle ) { UINT32 currAddr = 0; while(currAddr < s_ramIndexSize) { TPMI_RH_NV_INDEX currHandle; UINT32 currSize; memcpy(&currHandle, &s_ramIndex[currAddr + sizeof(UINT32)], sizeof(currHandle)); // Found a match if(currHandle == handle) // data buffer follows the handle and size field break; memcpy(&currSize, &s_ramIndex[currAddr], sizeof(currSize)); currAddr += sizeof(UINT32) + currSize; } // We assume the index data is existing in RAM space pAssert(currAddr < s_ramIndexSize); return currAddr + sizeof(TPMI_RH_NV_INDEX) + sizeof(UINT32); } // // // NvAddRAM() // // This function adds a new data area to RAM. // This function requires that enough free RAM space is available to add the new data. // static void NvAddRAM( TPMI_RH_NV_INDEX handle, // IN: NV handle UINT32 size // IN: size of data ) { // Add data space at the end of reserved RAM buffer UINT32 value = size + sizeof(TPMI_RH_NV_INDEX); memcpy(&s_ramIndex[s_ramIndexSize], &value, sizeof(s_ramIndex[s_ramIndexSize])); memcpy(&s_ramIndex[s_ramIndexSize + sizeof(UINT32)], &handle, sizeof(s_ramIndex[s_ramIndexSize + sizeof(UINT32)])); s_ramIndexSize += sizeof(UINT32) + sizeof(TPMI_RH_NV_INDEX) + size; pAssert(s_ramIndexSize <= RAM_INDEX_SPACE); // Update NV version of s_ramIndexSize _plat__NvMemoryWrite(s_ramIndexSizeAddr, sizeof(UINT32), &s_ramIndexSize); // Write reserved RAM space to NV to reflect the newly added NV Index _plat__NvMemoryWrite(s_ramIndexAddr, RAM_INDEX_SPACE, s_ramIndex); return; } // // // NvDeleteRAM() // // This function is used to delete a RAM-backed NV Index data area. // This function assumes the data of NV Index exists in RAM // static void NvDeleteRAM( TPMI_RH_NV_INDEX handle // IN: NV handle ) { UINT32 nodeOffset; UINT32 nextNode; UINT32 size; nodeOffset = NvGetRAMIndexOffset(handle); // Move the pointer back to get the size field of this node nodeOffset -= sizeof(UINT32) + sizeof(TPMI_RH_NV_INDEX); // Get node size memcpy(&size, &s_ramIndex[nodeOffset], sizeof(size)); // Get the offset of next node nextNode = nodeOffset + sizeof(UINT32) + size; // Move data MemoryMove(s_ramIndex + nodeOffset, s_ramIndex + nextNode, s_ramIndexSize - nextNode, s_ramIndexSize - nextNode); // Update RAM size s_ramIndexSize -= size + sizeof(UINT32); // Update NV version of s_ramIndexSize _plat__NvMemoryWrite(s_ramIndexSizeAddr, sizeof(UINT32), &s_ramIndexSize); // Write reserved RAM space to NV to reflect the newly delete NV Index _plat__NvMemoryWrite(s_ramIndexAddr, RAM_INDEX_SPACE, s_ramIndex); return; } // // // // Utility Functions // // NvInitStatic() // // This function initializes the static variables used in the NV subsystem. // static void NvInitStatic( void ) { UINT16 i; UINT32 reservedAddr; s_reservedSize[NV_DISABLE_CLEAR] = sizeof(gp.disableClear); s_reservedSize[NV_OWNER_ALG] = sizeof(gp.ownerAlg); s_reservedSize[NV_ENDORSEMENT_ALG] = sizeof(gp.endorsementAlg); s_reservedSize[NV_LOCKOUT_ALG] = sizeof(gp.lockoutAlg); s_reservedSize[NV_OWNER_POLICY] = sizeof(gp.ownerPolicy); s_reservedSize[NV_ENDORSEMENT_POLICY] = sizeof(gp.endorsementPolicy); s_reservedSize[NV_LOCKOUT_POLICY] = sizeof(gp.lockoutPolicy); s_reservedSize[NV_OWNER_AUTH] = sizeof(gp.ownerAuth); s_reservedSize[NV_ENDORSEMENT_AUTH] = sizeof(gp.endorsementAuth); s_reservedSize[NV_LOCKOUT_AUTH] = sizeof(gp.lockoutAuth); s_reservedSize[NV_EP_SEED] = sizeof(gp.EPSeed); s_reservedSize[NV_SP_SEED] = sizeof(gp.SPSeed); s_reservedSize[NV_PP_SEED] = sizeof(gp.PPSeed); s_reservedSize[NV_PH_PROOF] = sizeof(gp.phProof); s_reservedSize[NV_SH_PROOF] = sizeof(gp.shProof); s_reservedSize[NV_EH_PROOF] = sizeof(gp.ehProof); s_reservedSize[NV_TOTAL_RESET_COUNT] = sizeof(gp.totalResetCount); s_reservedSize[NV_RESET_COUNT] = sizeof(gp.resetCount); s_reservedSize[NV_PCR_POLICIES] = sizeof(gp.pcrPolicies); s_reservedSize[NV_PCR_ALLOCATED] = sizeof(gp.pcrAllocated); s_reservedSize[NV_PP_LIST] = sizeof(gp.ppList); s_reservedSize[NV_FAILED_TRIES] = sizeof(gp.failedTries); s_reservedSize[NV_MAX_TRIES] = sizeof(gp.maxTries); s_reservedSize[NV_RECOVERY_TIME] = sizeof(gp.recoveryTime); s_reservedSize[NV_LOCKOUT_RECOVERY] = sizeof(gp.lockoutRecovery); s_reservedSize[NV_LOCKOUT_AUTH_ENABLED] = sizeof(gp.lockOutAuthEnabled); s_reservedSize[NV_ORDERLY] = sizeof(gp.orderlyState); s_reservedSize[NV_AUDIT_COMMANDS] = sizeof(gp.auditComands); s_reservedSize[NV_AUDIT_HASH_ALG] = sizeof(gp.auditHashAlg); s_reservedSize[NV_AUDIT_COUNTER] = sizeof(gp.auditCounter); s_reservedSize[NV_ALGORITHM_SET] = sizeof(gp.algorithmSet); s_reservedSize[NV_FIRMWARE_V1] = sizeof(gp.firmwareV1); s_reservedSize[NV_FIRMWARE_V2] = sizeof(gp.firmwareV2); s_reservedSize[NV_ORDERLY_DATA] = sizeof(go); s_reservedSize[NV_STATE_CLEAR] = sizeof(gc); s_reservedSize[NV_STATE_RESET] = sizeof(gr); // Initialize reserved data address. In this implementation, reserved data // is stored at the start of NV memory reservedAddr = 0; for(i = 0; i < NV_RESERVE_LAST; i++) { s_reservedAddr[i] = reservedAddr; reservedAddr += s_reservedSize[i]; } // Initialize auxiliary variable space for index/evict implementation. // Auxiliary variables are stored after reserved data area // RAM index copy starts at the beginning s_ramIndexSizeAddr = reservedAddr; s_ramIndexAddr = s_ramIndexSizeAddr + sizeof(UINT32); // Maximum counter value s_maxCountAddr = s_ramIndexAddr + RAM_INDEX_SPACE; // dynamic memory start s_evictNvStart = s_maxCountAddr + sizeof(UINT64); // dynamic memory ends at the end of NV memory s_evictNvEnd = NV_MEMORY_SIZE; return; } // // // NvInit() // // This function initializes the NV system at pre-install time. // This function should only be called in a manufacturing environment or in a simulation. // The layout of NV memory space is an implementation choice. // void NvInit( void ) { UINT32 nullPointer = 0; UINT64 zeroCounter = 0; // Initialize static variables NvInitStatic(); // Initialize RAM index space as unused _plat__NvMemoryWrite(s_ramIndexSizeAddr, sizeof(UINT32), &nullPointer); // Initialize max counter value to 0 _plat__NvMemoryWrite(s_maxCountAddr, sizeof(UINT64), &zeroCounter); // Initialize the next offset of the first entry in evict/index list to 0 _plat__NvMemoryWrite(s_evictNvStart, sizeof(TPM_HANDLE), &nullPointer); return; } // // // NvReadReserved() // // This function is used to move reserved data from NV memory to RAM. // void NvReadReserved( NV_RESERVE type, // IN: type of reserved data void *buffer // OUT: buffer receives the data. ) { // Input type should be valid pAssert(type >= 0 && type < NV_RESERVE_LAST); _plat__NvMemoryRead(s_reservedAddr[type], s_reservedSize[type], buffer); return; } // // // NvWriteReserved() // // This function is used to post a reserved data for writing to NV memory. Before the TPM completes the // operation, the value will be written. // void NvWriteReserved( NV_RESERVE type, // IN: type of reserved data void *buffer // IN: data buffer ) { // Input type should be valid pAssert(type >= 0 && type < NV_RESERVE_LAST); _plat__NvMemoryWrite(s_reservedAddr[type], s_reservedSize[type], buffer); // Set the flag that a NV write happens g_updateNV = TRUE; return; } // // // NvReadPersistent() // // This function reads persistent data to the RAM copy of the gp structure. // void NvReadPersistent( void ) { // Hierarchy persistent data NvReadReserved(NV_DISABLE_CLEAR, &gp.disableClear); NvReadReserved(NV_OWNER_ALG, &gp.ownerAlg); NvReadReserved(NV_ENDORSEMENT_ALG, &gp.endorsementAlg); NvReadReserved(NV_LOCKOUT_ALG, &gp.lockoutAlg); NvReadReserved(NV_OWNER_POLICY, &gp.ownerPolicy); NvReadReserved(NV_ENDORSEMENT_POLICY, &gp.endorsementPolicy); NvReadReserved(NV_LOCKOUT_POLICY, &gp.lockoutPolicy); NvReadReserved(NV_OWNER_AUTH, &gp.ownerAuth); NvReadReserved(NV_ENDORSEMENT_AUTH, &gp.endorsementAuth); NvReadReserved(NV_LOCKOUT_AUTH, &gp.lockoutAuth); NvReadReserved(NV_EP_SEED, &gp.EPSeed); NvReadReserved(NV_SP_SEED, &gp.SPSeed); NvReadReserved(NV_PP_SEED, &gp.PPSeed); NvReadReserved(NV_PH_PROOF, &gp.phProof); NvReadReserved(NV_SH_PROOF, &gp.shProof); NvReadReserved(NV_EH_PROOF, &gp.ehProof); // Time persistent data NvReadReserved(NV_TOTAL_RESET_COUNT, &gp.totalResetCount); NvReadReserved(NV_RESET_COUNT, &gp.resetCount); // PCR persistent data NvReadReserved(NV_PCR_POLICIES, &gp.pcrPolicies); NvReadReserved(NV_PCR_ALLOCATED, &gp.pcrAllocated); // Physical Presence persistent data NvReadReserved(NV_PP_LIST, &gp.ppList); // Dictionary attack values persistent data NvReadReserved(NV_FAILED_TRIES, &gp.failedTries); NvReadReserved(NV_MAX_TRIES, &gp.maxTries); NvReadReserved(NV_RECOVERY_TIME, &gp.recoveryTime); // NvReadReserved(NV_LOCKOUT_RECOVERY, &gp.lockoutRecovery); NvReadReserved(NV_LOCKOUT_AUTH_ENABLED, &gp.lockOutAuthEnabled); // Orderly State persistent data NvReadReserved(NV_ORDERLY, &gp.orderlyState); // Command audit values persistent data NvReadReserved(NV_AUDIT_COMMANDS, &gp.auditComands); NvReadReserved(NV_AUDIT_HASH_ALG, &gp.auditHashAlg); NvReadReserved(NV_AUDIT_COUNTER, &gp.auditCounter); // Algorithm selection persistent data NvReadReserved(NV_ALGORITHM_SET, &gp.algorithmSet); // Firmware version persistent data #ifdef EMBEDDED_MODE _plat__GetFwVersion(&gp.firmwareV1, &gp.firmwareV2); #else NvReadReserved(NV_FIRMWARE_V1, &gp.firmwareV1); NvReadReserved(NV_FIRMWARE_V2, &gp.firmwareV2); #endif return; } // // // NvIsPlatformPersistentHandle() // // This function indicates if a handle references a persistent object in the range belonging to the platform. // // Return Value Meaning // // TRUE handle references a platform persistent object // FALSE handle does not reference platform persistent object and may // reference an owner persistent object either // BOOL NvIsPlatformPersistentHandle( TPM_HANDLE handle // IN: handle ) { return (handle >= PLATFORM_PERSISTENT && handle <= PERSISTENT_LAST); } // // // NvIsOwnerPersistentHandle() // // This function indicates if a handle references a persistent object in the range belonging to the owner. // // Return Value Meaning // // TRUE handle is owner persistent handle // FALSE handle is not owner persistent handle and may not be a persistent // handle at all // BOOL NvIsOwnerPersistentHandle( TPM_HANDLE handle // IN: handle ) { return (handle >= PERSISTENT_FIRST && handle < PLATFORM_PERSISTENT); } // // // NvNextIndex() // // This function returns the offset in NV of the next NV Index entry. A value of 0 indicates the end of the list. // Family "2.0" TCG Published Page 131 // Level 00 Revision 01.16 Copyright © TCG 2006-2014 October 30, 2014 // Trusted Platform Module Library Part 4: Supporting Routines // static UINT32 NvNextIndex( NV_ITER *iter ) { UINT32 addr; TPM_HANDLE handle; while((addr = NvNext(iter)) != 0) { // Read handle _plat__NvMemoryRead(addr, sizeof(TPM_HANDLE), &handle); if(HandleGetType(handle) == TPM_HT_NV_INDEX) return addr; } pAssert(addr == 0); return addr; } // // // NvNextEvict() // // This function returns the offset in NV of the next evict object entry. A value of 0 indicates the end of the // list. // static UINT32 NvNextEvict( NV_ITER *iter ) { UINT32 addr; TPM_HANDLE handle; while((addr = NvNext(iter)) != 0) { // Read handle _plat__NvMemoryRead(addr, sizeof(TPM_HANDLE), &handle); if(HandleGetType(handle) == TPM_HT_PERSISTENT) return addr; } pAssert(addr == 0); return addr; } // // // NvFindHandle() // // this function returns the offset in NV memory of the entity associated with the input handle. A value of // zero indicates that handle does not exist reference an existing persistent object or defined NV Index. // static UINT32 NvFindHandle( TPM_HANDLE handle ) { UINT32 addr; NV_ITER iter = NV_ITER_INIT; while((addr = NvNext(&iter)) != 0) { TPM_HANDLE entityHandle; // Read handle // _plat__NvMemoryRead(addr, sizeof(TPM_HANDLE), &entityHandle); if(entityHandle == handle) return addr; } pAssert(addr == 0); return addr; } // // NvCheckAndMigrateIfNeeded() // // Supported only in EMBEDDED_MODE. // // Check if the NVRAM storage format changed, and if so - reinitialize the // NVRAM. No content migration yet, hopefully it will come one day. // // Note that the NV_FIRMWARE_V1 and NV_FIRMWARE_V2 values not used to store // TPM versoion when in embedded mode are used for NVRAM format version // instead. // // static void NvCheckAndMigrateIfNeeded(void) { #ifdef EMBEDDED_MODE UINT32 nv_vers1; UINT32 nv_vers2; NvReadReserved(NV_FIRMWARE_V1, &nv_vers1); NvReadReserved(NV_FIRMWARE_V2, &nv_vers2); if ((nv_vers1 == ~nv_vers2) && (nv_vers1 == NV_FORMAT_VERSION)) return; // All is well. // This will reinitialize NVRAM to empty. Migration code will come here // later. NvInit(); nv_vers1 = NV_FORMAT_VERSION; nv_vers2 = ~NV_FORMAT_VERSION; NvWriteReserved(NV_FIRMWARE_V1, &nv_vers1); NvWriteReserved(NV_FIRMWARE_V2, &nv_vers2); NvCommit(); #endif } // // // NvPowerOn() // // This function is called at _TPM_Init() to initialize the NV environment. // // Return Value Meaning // // TRUE all NV was initialized // FALSE the NV containing saved state had an error and // TPM2_Startup(CLEAR) is required // BOOL NvPowerOn( void ) { int nvError = 0; // If power was lost, need to re-establish the RAM data that is loaded from // NV and initialize the static variables if(_plat__WasPowerLost(TRUE)) { if((nvError = _plat__NVEnable(0)) < 0) FAIL(FATAL_ERROR_NV_UNRECOVERABLE); NvInitStatic(); NvCheckAndMigrateIfNeeded(); } return nvError == 0; } // // // NvStateSave() // // This function is used to cause the memory containing the RAM backed NV Indices to be written to NV. // void NvStateSave( void ) { // Write RAM backed NV Index info to NV // No need to save s_ramIndexSize because we save it to NV whenever it is // updated. _plat__NvMemoryWrite(s_ramIndexAddr, RAM_INDEX_SPACE, s_ramIndex); // Set the flag so that an NV write happens before the command completes. g_updateNV = TRUE; return; } // // // // NvEntityStartup() // // This function is called at TPM_Startup(). If the startup completes a TPM Resume cycle, no action is // taken. If the startup is a TPM Reset or a TPM Restart, then this function will: // a) clear read/write lock; // b) reset NV Index data that has TPMA_NV_CLEAR_STCLEAR SET; and // c) set the lower bits in orderly counters to 1 for a non-orderly startup // It is a prerequisite that NV be available for writing before this function is called. // void NvEntityStartup( STARTUP_TYPE type // IN: start up type ) { NV_ITER iter = NV_ITER_INIT; UINT32 currentAddr; // offset points to the current entity // Restore RAM index data _plat__NvMemoryRead(s_ramIndexSizeAddr, sizeof(UINT32), &s_ramIndexSize); _plat__NvMemoryRead(s_ramIndexAddr, RAM_INDEX_SPACE, s_ramIndex); // If recovering from state save, do nothing if(type == SU_RESUME) return; // Iterate all the NV Index to clear the locks while((currentAddr = NvNextIndex(&iter)) != 0) { NV_INDEX nvIndex; UINT32 indexAddr; // NV address points to index info TPMA_NV attributes; UINT32 attributesValue; UINT32 publicAreaAttributesValue; indexAddr = currentAddr + sizeof(TPM_HANDLE); // Read NV Index info structure _plat__NvMemoryRead(indexAddr, sizeof(NV_INDEX), &nvIndex); attributes = nvIndex.publicArea.attributes; // Clear read/write lock if(attributes.TPMA_NV_READLOCKED == SET) attributes.TPMA_NV_READLOCKED = CLEAR; if( attributes.TPMA_NV_WRITELOCKED == SET && ( attributes.TPMA_NV_WRITTEN == CLEAR || attributes.TPMA_NV_WRITEDEFINE == CLEAR ) ) attributes.TPMA_NV_WRITELOCKED = CLEAR; // Reset NV data for TPMA_NV_CLEAR_STCLEAR if(attributes.TPMA_NV_CLEAR_STCLEAR == SET) { attributes.TPMA_NV_WRITTEN = CLEAR; attributes.TPMA_NV_WRITELOCKED = CLEAR; } // Reset NV data for orderly values that are not counters // NOTE: The function has already exited on a TPM Resume, so the only // things being processed are TPM Restart and TPM Reset if( type == SU_RESET && attributes.TPMA_NV_ORDERLY == SET && attributes.TPMA_NV_COUNTER == CLEAR ) attributes.TPMA_NV_WRITTEN = CLEAR; // Write NV Index info back if it has changed memcpy(&attributesValue, &attributes, sizeof(attributesValue)); memcpy(&publicAreaAttributesValue, &nvIndex.publicArea.attributes, sizeof(publicAreaAttributesValue)); if(attributesValue != publicAreaAttributesValue) { nvIndex.publicArea.attributes = attributes; _plat__NvMemoryWrite(indexAddr, sizeof(NV_INDEX), &nvIndex); // Set the flag that a NV write happens g_updateNV = TRUE; } // Set the lower bits in an orderly counter to 1 for a non-orderly startup if( g_prevOrderlyState == SHUTDOWN_NONE && attributes.TPMA_NV_WRITTEN == SET) { if( attributes.TPMA_NV_ORDERLY == SET && attributes.TPMA_NV_COUNTER == SET) { TPMI_RH_NV_INDEX nvHandle; UINT64 counter; // Read NV handle _plat__NvMemoryRead(currentAddr, sizeof(TPM_HANDLE), &nvHandle); // Read the counter value saved to NV upon the last roll over. // Do not use RAM backed storage for this once. nvIndex.publicArea.attributes.TPMA_NV_ORDERLY = CLEAR; NvGetIntIndexData(nvHandle, &nvIndex, &counter); nvIndex.publicArea.attributes.TPMA_NV_ORDERLY = SET; // Set the lower bits of counter to 1's counter |= MAX_ORDERLY_COUNT; // Write back to RAM NvWriteIndexData(nvHandle, &nvIndex, 0, sizeof(counter), &counter); // No write to NV because an orderly shutdown will update the // counters. } } } return; } // // // NV Access Functions // // Introduction // // This set of functions provide accessing NV Index and persistent objects based using a handle for // reference to the entity. // // NvIsUndefinedIndex() // // This function is used to verify that an NV Index is not defined. This is only used by // TPM2_NV_DefineSpace(). // // // // // Return Value Meaning // // TRUE the handle points to an existing NV Index // FALSE the handle points to a non-existent Index // BOOL NvIsUndefinedIndex( TPMI_RH_NV_INDEX handle // IN: handle ) { UINT32 entityAddr; // offset points to the entity pAssert(HandleGetType(handle) == TPM_HT_NV_INDEX); // Find the address of index entityAddr = NvFindHandle(handle); // If handle is not found, return TPM_RC_SUCCESS if(entityAddr == 0) return TPM_RC_SUCCESS; // NV Index is defined return TPM_RC_NV_DEFINED; } // // // NvIndexIsAccessible() // // This function validates that a handle references a defined NV Index and that the Index is currently // accessible. // // Error Returns Meaning // // TPM_RC_HANDLE the handle points to an undefined NV Index If shEnable is CLEAR, // this would include an index created using ownerAuth. If phEnableNV // is CLEAR, this would include and index created using platform auth // TPM_RC_NV_READLOCKED Index is present but locked for reading and command does not write // to the index // TPM_RC_NV_WRITELOCKED Index is present but locked for writing and command writes to the // index // TPM_RC NvIndexIsAccessible( TPMI_RH_NV_INDEX handle, // IN: handle TPM_CC commandCode // IN: the command ) { UINT32 entityAddr; // offset points to the entity NV_INDEX nvIndex; // pAssert(HandleGetType(handle) == TPM_HT_NV_INDEX); // Find the address of index entityAddr = NvFindHandle(handle); // If handle is not found, return TPM_RC_HANDLE if(entityAddr == 0) return TPM_RC_HANDLE; // Read NV Index info structure _plat__NvMemoryRead(entityAddr + sizeof(TPM_HANDLE), sizeof(NV_INDEX), &nvIndex); if(gc.shEnable == FALSE || gc.phEnableNV == FALSE) { // if shEnable is CLEAR, an ownerCreate NV Index should not be // indicated as present if(nvIndex.publicArea.attributes.TPMA_NV_PLATFORMCREATE == CLEAR) { /* * FWMP is a Chrome OS specific object saved at address 0x100a, it * needs to be available for reading even before TPM2_Startup * command is issued. */ UINT32 isFwmpRead = (handle == 0x100100a) && IsReadOperation(commandCode); if((gc.shEnable == FALSE) && !isFwmpRead) return TPM_RC_HANDLE; } // if phEnableNV is CLEAR, a platform created Index should not // be visible else if(gc.phEnableNV == FALSE) return TPM_RC_HANDLE; } // If the Index is write locked and this is an NV Write operation... if( nvIndex.publicArea.attributes.TPMA_NV_WRITELOCKED && IsWriteOperation(commandCode)) { // then return a locked indication unless the command is TPM2_NV_WriteLock if(commandCode != TPM_CC_NV_WriteLock) return TPM_RC_NV_LOCKED; return TPM_RC_SUCCESS; } // If the Index is read locked and this is an NV Read operation... if( nvIndex.publicArea.attributes.TPMA_NV_READLOCKED && IsReadOperation(commandCode)) { // then return a locked indication unless the command is TPM2_NV_ReadLock if(commandCode != TPM_CC_NV_ReadLock) return TPM_RC_NV_LOCKED; return TPM_RC_SUCCESS; } // NV Index is accessible return TPM_RC_SUCCESS; } // // // NvIsUndefinedEvictHandle() // // This function indicates if a handle does not reference an existing persistent object. This function requires // that the handle be in the proper range for persistent objects. // // Return Value Meaning // // TRUE handle does not reference an existing persistent object // FALSE handle does reference an existing persistent object // static BOOL NvIsUndefinedEvictHandle( TPM_HANDLE handle // IN: handle ) { UINT32 entityAddr; // offset points to the entity pAssert(HandleGetType(handle) == TPM_HT_PERSISTENT); // Find the address of evict object entityAddr = NvFindHandle(handle); // If handle is not found, return TRUE if(entityAddr == 0) return TRUE; else return FALSE; } // // // NvUnmarshalObject() // // This function accepts a buffer containing a marshaled OBJECT // structure, a pointer to the area where the input data should be // unmarshaled, and a pointer to the size of the output area. // // No error checking is performed, unmarshaled data is guaranteed not to // spill over the allocated space. // static TPM_RC NvUnmarshalObject(OBJECT *o, BYTE **buf, INT32 *size) { TPM_RC result; // There is no generated function to unmarshal the attributes field, do it // by hand. MemoryCopy(&o->attributes, *buf, sizeof(o->attributes), *size); *buf += sizeof(o->attributes); *size -= sizeof(o->attributes); result = TPMT_PUBLIC_Unmarshal(&o->publicArea, buf, size); if (result != TPM_RC_SUCCESS) return result; result = TPMT_SENSITIVE_Unmarshal(&o->sensitive, buf, size); if (result != TPM_RC_SUCCESS) return result; #ifdef TPM_ALG_RSA result = TPM2B_PUBLIC_KEY_RSA_Unmarshal(&o->privateExponent, buf, size); if (result != TPM_RC_SUCCESS) return result; #endif result = TPM2B_NAME_Unmarshal(&o->qualifiedName, buf, size); if (result != TPM_RC_SUCCESS) return result; result = TPMI_DH_OBJECT_Unmarshal(&o->evictHandle, buf, size, TRUE); if (result != TPM_RC_SUCCESS) return result; return TPM2B_NAME_Unmarshal(&o->name, buf, size); } // // // NvGetEvictObject() // // This function is used to dereference an evict object handle and get a pointer to the object. // // Error Returns Meaning // // TPM_RC_HANDLE the handle does not point to an existing persistent object // TPM_RC NvGetEvictObject( TPM_HANDLE handle, // IN: handle OBJECT *object // OUT: object data ) { UINT32 entityAddr; // offset points to the entity TPM_RC result = TPM_RC_SUCCESS; pAssert(HandleGetType(handle) == TPM_HT_PERSISTENT); // Find the address of evict object entityAddr = NvFindHandle(handle); // If handle is not found, return an error if(entityAddr == 0) { result = TPM_RC_HANDLE; } else { UINT32 storedSize; UINT32 nextEntryAddr; // Let's calculate the size of object as stored in NVMEM. _plat__NvMemoryRead(entityAddr - sizeof(UINT32), sizeof(UINT32), &nextEntryAddr); storedSize = nextEntryAddr - entityAddr; if (storedSize == (sizeof(TPM_HANDLE) + sizeof(OBJECT))) { // Read evict object stored unmarshaled. _plat__NvMemoryRead(entityAddr + sizeof(TPM_HANDLE), sizeof(OBJECT), object); } else { // Must be stored marshaled, let's unmarshal it. BYTE marshaled[sizeof(OBJECT)]; INT32 max_size = sizeof(marshaled); BYTE *marshaledPtr = marshaled; _plat__NvMemoryRead(entityAddr + sizeof(TPM_HANDLE), storedSize, marshaled); result = NvUnmarshalObject(object, &marshaledPtr, &max_size); } } // whether there is an error or not, make sure that the evict // status of the object is set so that the slot will get freed on exit object->attributes.evict = SET; return result; } // // // NvGetIndexInfo() // // This function is used to retrieve the contents of an NV Index. // An implementation is allowed to save the NV Index in a vendor-defined format. If the format is different // from the default used by the reference code, then this function would be changed to reformat the data into // the default format. // A prerequisite to calling this function is that the handle must be known to reference a defined NV Index. // void NvGetIndexInfo( TPMI_RH_NV_INDEX handle, // IN: handle NV_INDEX *nvIndex // OUT: NV index structure ) { UINT32 entityAddr; // offset points to the entity pAssert(HandleGetType(handle) == TPM_HT_NV_INDEX); // Find the address of NV index entityAddr = NvFindHandle(handle); pAssert(entityAddr != 0); // This implementation uses the default format so just // read the data in _plat__NvMemoryRead(entityAddr + sizeof(TPM_HANDLE), sizeof(NV_INDEX), nvIndex); return; } // // // NvInitialCounter() // // This function returns the value to be used when a counter index is initialized. It will scan the NV counters // and find the highest value in any active counter. It will use that value as the starting point. If there are no // active counters, it will use the value of the previous largest counter. // UINT64 NvInitialCounter( void ) { UINT64 maxCount; NV_ITER iter = NV_ITER_INIT; UINT32 currentAddr; // Read the maxCount value maxCount = NvReadMaxCount(); // Iterate all existing counters while((currentAddr = NvNextIndex(&iter)) != 0) { TPMI_RH_NV_INDEX nvHandle; NV_INDEX nvIndex; // Read NV handle _plat__NvMemoryRead(currentAddr, sizeof(TPM_HANDLE), &nvHandle); // Get NV Index NvGetIndexInfo(nvHandle, &nvIndex); if( nvIndex.publicArea.attributes.TPMA_NV_COUNTER == SET && nvIndex.publicArea.attributes.TPMA_NV_WRITTEN == SET) { UINT64 countValue; // Read counter value NvGetIntIndexData(nvHandle, &nvIndex, &countValue); if(countValue > maxCount) maxCount = countValue; } } // Initialize the new counter value to be maxCount + 1 // A counter is only initialized the first time it is written. The // way to write a counter is with TPM2_NV_INCREMENT(). Since the // "initial" value of a defined counter is the largest count value that // may have existed in this index previously, then the first use would // add one to that value. return maxCount; } // // // NvGetIndexData() // // This function is used to access the data in an NV Index. The data is returned as a byte sequence. Since // counter values are kept in native format, they are converted to canonical form before being returned. // Family "2.0" TCG Published Page 139 // Level 00 Revision 01.16 Copyright © TCG 2006-2014 October 30, 2014 // Trusted Platform Module Library Part 4: Supporting Routines // // // This function requires that the NV Index be defined, and that the required data is within the data range. It // also requires that TPMA_NV_WRITTEN of the Index is SET. // void NvGetIndexData( TPMI_RH_NV_INDEX handle, // IN: handle NV_INDEX *nvIndex, // IN: RAM image of index header UINT32 offset, // IN: offset of NV data UINT16 size, // IN: size of NV data void *data // OUT: data buffer ) { pAssert(nvIndex->publicArea.attributes.TPMA_NV_WRITTEN == SET); if( nvIndex->publicArea.attributes.TPMA_NV_BITS == SET || nvIndex->publicArea.attributes.TPMA_NV_COUNTER == SET) { // Read bit or counter data in canonical form UINT64 dataInInt; NvGetIntIndexData(handle, nvIndex, &dataInInt); UINT64_TO_BYTE_ARRAY(dataInInt, (BYTE *)data); } else { if(nvIndex->publicArea.attributes.TPMA_NV_ORDERLY == SET) { UINT32 ramAddr; // Get data from RAM buffer ramAddr = NvGetRAMIndexOffset(handle); MemoryCopy(data, s_ramIndex + ramAddr + offset, size, size); } else { UINT32 entityAddr; entityAddr = NvFindHandle(handle); // Get data from NV // Skip NV Index info, read data buffer entityAddr += sizeof(TPM_HANDLE) + sizeof(NV_INDEX) + offset; // Read the data _plat__NvMemoryRead(entityAddr, size, data); } } return; } // // // NvGetIntIndexData() // // Get data in integer format of a bit or counter NV Index. // This function requires that the NV Index is defined and that the NV Index previously has been written. // void NvGetIntIndexData( TPMI_RH_NV_INDEX handle, // IN: handle NV_INDEX *nvIndex, // IN: RAM image of NV Index header UINT64 *data // IN: UINT64 pointer for counter or bit ) { // Validate that index has been written and is the right type pAssert( nvIndex->publicArea.attributes.TPMA_NV_WRITTEN == SET && ( nvIndex->publicArea.attributes.TPMA_NV_BITS == SET || nvIndex->publicArea.attributes.TPMA_NV_COUNTER == SET ) ); // bit and counter value is store in native format for TPM CPU. So we directly // copy the contents of NV to output data buffer if(nvIndex->publicArea.attributes.TPMA_NV_ORDERLY == SET) { UINT32 ramAddr; // Get data from RAM buffer ramAddr = NvGetRAMIndexOffset(handle); MemoryCopy(data, s_ramIndex + ramAddr, sizeof(*data), sizeof(*data)); } else { UINT32 entityAddr; entityAddr = NvFindHandle(handle); // Get data from NV // Skip NV Index info, read data buffer _plat__NvMemoryRead( entityAddr + sizeof(TPM_HANDLE) + sizeof(NV_INDEX), sizeof(UINT64), data); } return; } // // // NvWriteIndexInfo() // // This function is called to queue the write of NV Index data to persistent memory. // This function requires that NV Index is defined. // // Error Returns Meaning // // TPM_RC_NV_RATE NV is rate limiting so retry // TPM_RC_NV_UNAVAILABLE NV is not available // TPM_RC NvWriteIndexInfo( TPMI_RH_NV_INDEX handle, // IN: handle NV_INDEX *nvIndex // IN: NV Index info to be written ) { UINT32 entryAddr; TPM_RC result; // Get the starting offset for the index in the RAM image of NV entryAddr = NvFindHandle(handle); pAssert(entryAddr != 0); // Step over the link value entryAddr = entryAddr + sizeof(TPM_HANDLE); // If the index data is actually changed, then a write to NV is required if(_plat__NvIsDifferent(entryAddr, sizeof(NV_INDEX),nvIndex)) { // Make sure that NV is available result = NvIsAvailable(); if(result != TPM_RC_SUCCESS) return result; _plat__NvMemoryWrite(entryAddr, sizeof(NV_INDEX), nvIndex); g_updateNV = TRUE; } return TPM_RC_SUCCESS; } // // // NvWriteIndexData() // // This function is used to write NV index data. // This function requires that the NV Index is defined, and the data is within the defined data range for the // index. // // Error Returns Meaning // // TPM_RC_NV_RATE NV is rate limiting so retry // TPM_RC_NV_UNAVAILABLE NV is not available // TPM_RC NvWriteIndexData( TPMI_RH_NV_INDEX handle, // IN: handle NV_INDEX *nvIndex, // IN: RAM copy of NV Index UINT32 offset, // IN: offset of NV data UINT32 size, // IN: size of NV data void *data // OUT: data buffer ) { TPM_RC result; // Validate that write falls within range of the index pAssert(nvIndex->publicArea.dataSize >= offset + size); // Update TPMA_NV_WRITTEN bit if necessary if(nvIndex->publicArea.attributes.TPMA_NV_WRITTEN == CLEAR) { nvIndex->publicArea.attributes.TPMA_NV_WRITTEN = SET; result = NvWriteIndexInfo(handle, nvIndex); if(result != TPM_RC_SUCCESS) return result; } // Check to see if process for an orderly index is required. if(nvIndex->publicArea.attributes.TPMA_NV_ORDERLY == SET) { UINT32 ramAddr; // Write data to RAM buffer ramAddr = NvGetRAMIndexOffset(handle); MemoryCopy(s_ramIndex + ramAddr + offset, data, size, sizeof(s_ramIndex) - ramAddr - offset); // NV update does not happen for orderly index. Have // to clear orderlyState to reflect that we have changed the // NV and an orderly shutdown is required. Only going to do this if we // are not processing a counter that has just rolled over if(g_updateNV == FALSE) g_clearOrderly = TRUE; } // Need to process this part if the Index isn't orderly or if it is // an orderly counter that just rolled over. if(g_updateNV || nvIndex->publicArea.attributes.TPMA_NV_ORDERLY == CLEAR) { // Processing for an index with TPMA_NV_ORDERLY CLEAR UINT32 entryAddr = NvFindHandle(handle); pAssert(entryAddr != 0); // // Offset into the index to the first byte of the data to be written entryAddr += sizeof(TPM_HANDLE) + sizeof(NV_INDEX) + offset; // If the data is actually changed, then a write to NV is required if(_plat__NvIsDifferent(entryAddr, size, data)) { // Make sure that NV is available result = NvIsAvailable(); if(result != TPM_RC_SUCCESS) return result; _plat__NvMemoryWrite(entryAddr, size, data); g_updateNV = TRUE; } } return TPM_RC_SUCCESS; } // // // NvGetName() // // This function is used to compute the Name of an NV Index. // The name buffer receives the bytes of the Name and the return value is the number of octets in the // Name. // This function requires that the NV Index is defined. // UINT16 NvGetName( TPMI_RH_NV_INDEX handle, // IN: handle of the index NAME *name // OUT: name of the index ) { UINT16 dataSize, digestSize; NV_INDEX nvIndex; BYTE marshalBuffer[sizeof(TPMS_NV_PUBLIC)]; BYTE *buffer; INT32 bufferSize; HASH_STATE hashState; // Get NV public info NvGetIndexInfo(handle, &nvIndex); // Marshal public area buffer = marshalBuffer; bufferSize = sizeof(TPMS_NV_PUBLIC); dataSize = TPMS_NV_PUBLIC_Marshal(&nvIndex.publicArea, &buffer, &bufferSize); // hash public area digestSize = CryptStartHash(nvIndex.publicArea.nameAlg, &hashState); CryptUpdateDigest(&hashState, dataSize, marshalBuffer); // Complete digest leaving room for the nameAlg CryptCompleteHash(&hashState, digestSize, &((BYTE *)name)[2]); // Include the nameAlg UINT16_TO_BYTE_ARRAY(nvIndex.publicArea.nameAlg, (BYTE *)name); return digestSize + 2; } // // // NvDefineIndex() // // This function is used to assign NV memory to an NV Index. // // // // Error Returns Meaning // // TPM_RC_NV_SPACE insufficient NV space // TPM_RC NvDefineIndex( TPMS_NV_PUBLIC *publicArea, // IN: A template for an area to create. TPM2B_AUTH *authValue // IN: The initial authorization value ) { // The buffer to be written to NV memory BYTE nvBuffer[sizeof(TPM_HANDLE) + sizeof(NV_INDEX)]; NV_INDEX *nvIndex; // a pointer to the NV_INDEX data in // nvBuffer UINT16 entrySize; // size of entry entrySize = sizeof(TPM_HANDLE) + sizeof(NV_INDEX) + publicArea->dataSize; // Check if we have enough space to create the NV Index // In this implementation, the only resource limitation is the available NV // space. Other implementation may have other limitation on counter or on // NV slot if(!NvTestSpace(entrySize, TRUE)) return TPM_RC_NV_SPACE; // if the index to be defined is RAM backed, check RAM space availability // as well if(publicArea->attributes.TPMA_NV_ORDERLY == SET && !NvTestRAMSpace(publicArea->dataSize)) return TPM_RC_NV_SPACE; // Copy input value to nvBuffer // Copy handle memcpy(nvBuffer, &publicArea->nvIndex, sizeof(TPM_HANDLE)); // Copy NV_INDEX nvIndex = (NV_INDEX *) (nvBuffer + sizeof(TPM_HANDLE)); nvIndex->publicArea = *publicArea; nvIndex->authValue = *authValue; // Add index to NV memory NvAdd(entrySize, sizeof(TPM_HANDLE) + sizeof(NV_INDEX), nvBuffer); // If the data of NV Index is RAM backed, add the data area in RAM as well if(publicArea->attributes.TPMA_NV_ORDERLY == SET) NvAddRAM(publicArea->nvIndex, publicArea->dataSize); return TPM_RC_SUCCESS; } // // // NvMarshalObject() // // This function marshals the passed in OBJECT structure into a buffer. A // pointer to pointer to the buffer and a pointer to the size of the // buffer are passed in for this function to update as appropriate. // // On top of marshaling the object, this function also modifies one of // the object's properties and sets the evictHandle field of the // marshaled object to the requested value. // // Returns // // Marshaled size of the object. // static UINT16 NvMarshalObject(OBJECT *o, TPMI_DH_OBJECT evictHandle, BYTE **buf, INT32 *size) { UINT16 marshaledSize; OBJECT_ATTRIBUTES stored_attributes; stored_attributes = o->attributes; stored_attributes.evict = SET; marshaledSize = sizeof(stored_attributes); MemoryCopy(*buf, &stored_attributes, marshaledSize, *size); *buf += marshaledSize; *size -= marshaledSize; marshaledSize += TPMT_PUBLIC_Marshal(&o->publicArea, buf, size); marshaledSize += TPMT_SENSITIVE_Marshal(&o->sensitive, buf, size); #ifdef TPM_ALG_RSA marshaledSize += TPM2B_PUBLIC_KEY_RSA_Marshal(&o->privateExponent, buf, size); #endif marshaledSize += TPM2B_NAME_Marshal(&o->qualifiedName, buf, size); // Use the supplied handle instead of the object contents. marshaledSize += TPMI_DH_OBJECT_Marshal(&evictHandle, buf, size); marshaledSize += TPM2B_NAME_Marshal(&o->name, buf, size); return marshaledSize; } // // // NvAddEvictObject() // // This function is used to assign NV memory to a persistent object. // // Error Returns Meaning // // TPM_RC_NV_HANDLE the requested handle is already in use // TPM_RC_NV_SPACE insufficient NV space // TPM_RC NvAddEvictObject( TPMI_DH_OBJECT evictHandle, // IN: new evict handle // OBJECT *object // IN: object to be added ) { // The buffer to be written to NV memory BYTE nvBuffer[sizeof(TPM_HANDLE) + sizeof(OBJECT)]; UINT16 entrySize; // size of entry BYTE *marshalSpace; INT32 marshalRoom; // evict handle type should match the object hierarchy pAssert( ( NvIsPlatformPersistentHandle(evictHandle) && object->attributes.ppsHierarchy == SET) || ( NvIsOwnerPersistentHandle(evictHandle) && ( object->attributes.spsHierarchy == SET || object->attributes.epsHierarchy == SET))); // Do not attemp storing a duplicate handle. if(!NvIsUndefinedEvictHandle(evictHandle)) return TPM_RC_NV_DEFINED; // Copy handle entrySize = sizeof(TPM_HANDLE); memcpy(nvBuffer, &evictHandle, entrySize); // Let's serialize the object before storing it in NVMEM marshalSpace = nvBuffer + entrySize; marshalRoom = sizeof(nvBuffer) - entrySize; entrySize += NvMarshalObject(object, evictHandle, &marshalSpace, &marshalRoom); // Check if we have enough space to add this evict object if(!NvTestSpace(entrySize, FALSE)) return TPM_RC_NV_SPACE; // Add evict to NV memory NvAdd(entrySize, entrySize, nvBuffer); return TPM_RC_SUCCESS; } // // // NvDeleteEntity() // // This function will delete a NV Index or an evict object. // This function requires that the index/evict object has been defined. // void NvDeleteEntity( TPM_HANDLE handle // IN: handle of entity to be deleted ) { UINT32 entityAddr; // pointer to entity entityAddr = NvFindHandle(handle); pAssert(entityAddr != 0); if(HandleGetType(handle) == TPM_HT_NV_INDEX) { NV_INDEX nvIndex; // Read the NV Index info _plat__NvMemoryRead(entityAddr + sizeof(TPM_HANDLE), sizeof(NV_INDEX), &nvIndex); // If the entity to be deleted is a counter with the maximum counter // value, record it in NV memory if(nvIndex.publicArea.attributes.TPMA_NV_COUNTER == SET && nvIndex.publicArea.attributes.TPMA_NV_WRITTEN == SET) { UINT64 countValue; UINT64 maxCount; NvGetIntIndexData(handle, &nvIndex, &countValue); maxCount = NvReadMaxCount(); if(countValue > maxCount) NvWriteMaxCount(countValue); } // If the NV Index is RAM back, delete the RAM data as well if(nvIndex.publicArea.attributes.TPMA_NV_ORDERLY == SET) NvDeleteRAM(handle); } NvDelete(entityAddr); return; } // // // NvFlushHierarchy() // // This function will delete persistent objects belonging to the indicated If the storage hierarchy is selected, // the function will also delete any NV Index define using ownerAuth. // void NvFlushHierarchy( TPMI_RH_HIERARCHY hierarchy // IN: hierarchy to be flushed. ) { NV_ITER iter = NV_ITER_INIT; UINT32 currentAddr; while((currentAddr = NvNext(&iter)) != 0) { TPM_HANDLE entityHandle; // Read handle information. _plat__NvMemoryRead(currentAddr, sizeof(TPM_HANDLE), &entityHandle); if(HandleGetType(entityHandle) == TPM_HT_NV_INDEX) { // Handle NV Index NV_INDEX nvIndex; // If flush endorsement or platform hierarchy, no NV Index would be // flushed if(hierarchy == TPM_RH_ENDORSEMENT || hierarchy == TPM_RH_PLATFORM) continue; _plat__NvMemoryRead(currentAddr + sizeof(TPM_HANDLE), sizeof(NV_INDEX), &nvIndex); // For storage hierarchy, flush OwnerCreated index if( nvIndex.publicArea.attributes.TPMA_NV_PLATFORMCREATE == CLEAR) { // Delete the NV Index NvDelete(currentAddr); // Re-iterate from beginning after a delete iter = NV_ITER_INIT; // If the NV Index is RAM back, delete the RAM data as well if(nvIndex.publicArea.attributes.TPMA_NV_ORDERLY == SET) NvDeleteRAM(entityHandle); } } else if(HandleGetType(entityHandle) == TPM_HT_PERSISTENT) { OBJECT object; // Get evict object NvGetEvictObject(entityHandle, &object); // If the evict object belongs to the hierarchy to be flushed if( ( hierarchy == TPM_RH_PLATFORM && object.attributes.ppsHierarchy == SET) || ( hierarchy == TPM_RH_OWNER && object.attributes.spsHierarchy == SET) || ( hierarchy == TPM_RH_ENDORSEMENT && object.attributes.epsHierarchy == SET) ) { // Delete the evict object NvDelete(currentAddr); // Re-iterate from beginning after a delete iter = NV_ITER_INIT; } } else { pAssert(FALSE); } } return; } // // // NvSetGlobalLock() // // This function is used to SET the TPMA_NV_WRITELOCKED attribute for all NV Indices that have // TPMA_NV_GLOBALLOCK SET. This function is use by TPM2_NV_GlobalWriteLock(). // void NvSetGlobalLock( void ) { NV_ITER iter = NV_ITER_INIT; UINT32 currentAddr; // Check all Indices while((currentAddr = NvNextIndex(&iter)) != 0) { NV_INDEX nvIndex; // Read the index data _plat__NvMemoryRead(currentAddr + sizeof(TPM_HANDLE), sizeof(NV_INDEX), &nvIndex); // See if it should be locked if(nvIndex.publicArea.attributes.TPMA_NV_GLOBALLOCK == SET) { // if so, lock it nvIndex.publicArea.attributes.TPMA_NV_WRITELOCKED = SET; _plat__NvMemoryWrite(currentAddr + sizeof(TPM_HANDLE), sizeof(NV_INDEX), &nvIndex); // Set the flag that a NV write happens g_updateNV = TRUE; } } return; } // // // InsertSort() // // Sort a handle into handle list in ascending order. The total handle number in the list should not exceed // MAX_CAP_HANDLES // static void InsertSort( TPML_HANDLE *handleList, // IN/OUT: sorted handle list UINT32 count, // IN: maximum count in the handle list TPM_HANDLE entityHandle // IN: handle to be inserted ) { UINT32 i, j; UINT32 originalCount; // For a corner case that the maximum count is 0, do nothing if(count == 0) return; // For empty list, add the handle at the beginning and return if(handleList->count == 0) { handleList->handle[0] = entityHandle; handleList->count++; return; } // Check if the maximum of the list has been reached originalCount = handleList->count; if(originalCount < count) handleList->count++; // Insert the handle to the list for(i = 0; i < originalCount; i++) { if(handleList->handle[i] > entityHandle) { for(j = handleList->count - 1; j > i; j--) { handleList->handle[j] = handleList->handle[j-1]; } break; } } // If a slot was found, insert the handle in this position if(i < originalCount || handleList->count > originalCount) handleList->handle[i] = entityHandle; return; } // // // NvCapGetPersistent() // // This function is used to get a list of handles of the persistent objects, starting at handle. // Handle must be in valid persistent object handle range, but does not have to reference an existing // persistent object. // // Return Value Meaning // // YES if there are more handles available // NO all the available handles has been returned // TPMI_YES_NO NvCapGetPersistent( TPMI_DH_OBJECT handle, // IN: start handle UINT32 count, // IN: maximum number of returned handle TPML_HANDLE *handleList // OUT: list of handle ) { TPMI_YES_NO more = NO; NV_ITER iter = NV_ITER_INIT; UINT32 currentAddr; pAssert(HandleGetType(handle) == TPM_HT_PERSISTENT); // Initialize output handle list handleList->count = 0; // The maximum count of handles we may return is MAX_CAP_HANDLES if(count > MAX_CAP_HANDLES) count = MAX_CAP_HANDLES; while((currentAddr = NvNextEvict(&iter)) != 0) { TPM_HANDLE entityHandle; // Read handle information. _plat__NvMemoryRead(currentAddr, sizeof(TPM_HANDLE), &entityHandle); // Ignore persistent handles that have values less than the input handle if(entityHandle < handle) continue; // if the handles in the list have reached the requested count, and there // are still handles need to be inserted, indicate that there are more. if(handleList->count == count) more = YES; // A handle with a value larger than start handle is a candidate // for return. Insert sort it to the return list. Insert sort algorithm // is chosen here for simplicity based on the assumption that the total // number of NV Indices is small. For an implementation that may allow // large number of NV Indices, a more efficient sorting algorithm may be // used here. InsertSort(handleList, count, entityHandle); // } return more; } // // // NvCapGetIndex() // // This function returns a list of handles of NV Indices, starting from handle. Handle must be in the range of // NV Indices, but does not have to reference an existing NV Index. // // Return Value Meaning // // YES if there are more handles to report // NO all the available handles has been reported // TPMI_YES_NO NvCapGetIndex( TPMI_DH_OBJECT handle, // IN: start handle UINT32 count, // IN: maximum number of returned handle TPML_HANDLE *handleList // OUT: list of handle ) { TPMI_YES_NO more = NO; NV_ITER iter = NV_ITER_INIT; UINT32 currentAddr; pAssert(HandleGetType(handle) == TPM_HT_NV_INDEX); // Initialize output handle list handleList->count = 0; // The maximum count of handles we may return is MAX_CAP_HANDLES if(count > MAX_CAP_HANDLES) count = MAX_CAP_HANDLES; while((currentAddr = NvNextIndex(&iter)) != 0) { TPM_HANDLE entityHandle; // Read handle information. _plat__NvMemoryRead(currentAddr, sizeof(TPM_HANDLE), &entityHandle); // Ignore index handles that have values less than the 'handle' if(entityHandle < handle) continue; // if the count of handles in the list has reached the requested count, // and there are still handles to report, set more. if(handleList->count == count) more = YES; // A handle with a value larger than start handle is a candidate // for return. Insert sort it to the return list. Insert sort algorithm // is chosen here for simplicity based on the assumption that the total // number of NV Indices is small. For an implementation that may allow // large number of NV Indices, a more efficient sorting algorithm may be // used here. InsertSort(handleList, count, entityHandle); } return more; } // // // // NvCapGetIndexNumber() // // This function returns the count of NV Indexes currently defined. // UINT32 NvCapGetIndexNumber( void ) { UINT32 num = 0; NV_ITER iter = NV_ITER_INIT; while(NvNextIndex(&iter) != 0) num++; return num; } // // // NvCapGetPersistentNumber() // // Function returns the count of persistent objects currently in NV memory. // UINT32 NvCapGetPersistentNumber( void ) { UINT32 num = 0; NV_ITER iter = NV_ITER_INIT; while(NvNextEvict(&iter) != 0) num++; return num; } // // // NvCapGetPersistentAvail() // // This function returns an estimate of the number of additional persistent objects that could be loaded into // NV memory. // UINT32 NvCapGetPersistentAvail( void ) { UINT32 availSpace; UINT32 objectSpace; // Compute the available space in NV storage availSpace = NvGetFreeByte(); // Get the space needed to add a persistent object to NV storage objectSpace = NvGetEvictObjectSize(); return availSpace / objectSpace; } // // // NvCapGetCounterNumber() // // Get the number of defined NV Indexes that have NV TPMA_NV_COUNTER attribute SET. // // UINT32 NvCapGetCounterNumber( void ) { NV_ITER iter = NV_ITER_INIT; UINT32 currentAddr; UINT32 num = 0; while((currentAddr = NvNextIndex(&iter)) != 0) { NV_INDEX nvIndex; // Get NV Index info _plat__NvMemoryRead(currentAddr + sizeof(TPM_HANDLE), sizeof(NV_INDEX), &nvIndex); if(nvIndex.publicArea.attributes.TPMA_NV_COUNTER == SET) num++; } return num; } // // // NvCapGetCounterAvail() // // This function returns an estimate of the number of additional counter type NV Indices that can be defined. // UINT32 NvCapGetCounterAvail( void ) { UINT32 availNVSpace; UINT32 availRAMSpace; UINT32 counterNVSpace; UINT32 counterRAMSpace; UINT32 persistentNum = NvCapGetPersistentNumber(); // Get the available space in NV storage availNVSpace = NvGetFreeByte(); if (persistentNum < MIN_EVICT_OBJECTS) { // Some space have to be reserved for evict object. Adjust availNVSpace. UINT32 reserved = (MIN_EVICT_OBJECTS - persistentNum) * NvGetEvictObjectSize(); if (reserved > availNVSpace) availNVSpace = 0; else availNVSpace -= reserved; } // Get the space needed to add a counter index to NV storage counterNVSpace = NvGetCounterSize(); // Compute the available space in RAM availRAMSpace = RAM_INDEX_SPACE - s_ramIndexSize; // Compute the space needed to add a counter index to RAM storage // It takes an size field, a handle and sizeof(UINT64) for counter data counterRAMSpace = sizeof(UINT32) + sizeof(TPM_HANDLE) + sizeof(UINT64); // Return the min of counter number in NV and in RAM if(availNVSpace / counterNVSpace > availRAMSpace / counterRAMSpace) return availRAMSpace / counterRAMSpace; else return availNVSpace / counterNVSpace; }