diff options
author | Vadim Bendebury <vbendeb@chromium.org> | 2015-05-20 10:32:25 -0700 |
---|---|---|
committer | Vadim Bendebury <vbendeb@chromium.org> | 2015-05-20 22:32:05 -0700 |
commit | 5679752bf24c21135884e987c4077e2f71848971 (patch) | |
tree | 3e680dd91a7af84c45ea1170ee88225bd4ad32c8 /NV.c | |
download | tpm2-5679752bf24c21135884e987c4077e2f71848971.tar.gz |
Initial commit to seed TPM2.0 source code directory
LICENSE file text copied from TCG library specification. README
describes the procedure used to extract source code from parts 3 and 4
of the specification.
The python scripts and part{34}.txt files will be removed in the
following commits.
Change-Id: Ie281e6e988481831f33483053455e8aff8f3f75f
Signed-off-by: Vadim Bendebury <vbendeb@chromium.org>
Diffstat (limited to 'NV.c')
-rw-r--r-- | NV.c | 1894 |
1 files changed, 1894 insertions, 0 deletions
@@ -0,0 +1,1894 @@ +// 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 + ) +{ + 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; + currHandle = * (TPM_HANDLE *) &s_ramIndex[currAddr + sizeof(UINT32)]; + // Found a match + if(currHandle == handle) + // data buffer follows the handle and size field + break; + currSize = * (UINT32 *) &s_ramIndex[currAddr]; + 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 *) &s_ramIndex[s_ramIndexSize] = size + sizeof(TPMI_RH_NV_INDEX); + * (TPMI_RH_NV_INDEX *) &s_ramIndex[s_ramIndexSize + sizeof(UINT32)] = handle; + 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 + size = * (UINT32 *) &s_ramIndex[nodeOffset]; + // 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 + NvReadReserved(NV_FIRMWARE_V1, &gp.firmwareV1); + NvReadReserved(NV_FIRMWARE_V2, &gp.firmwareV2); + 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; +} +// +// +// 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(); + } + 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; + 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 + if(*((UINT32 *)&attributes) != *((UINT32 *)&nvIndex.publicArea.attributes)) + { + 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) + { + if(gc.shEnable == FALSE) + 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; +} +// +// +// 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 + // Read evict object + _plat__NvMemoryRead(entityAddr + sizeof(TPM_HANDLE), + sizeof(OBJECT), + object); + // 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; + HASH_STATE hashState; + // Get NV public info + NvGetIndexInfo(handle, &nvIndex); + // Marshal public area + buffer = marshalBuffer; + dataSize = TPMS_NV_PUBLIC_Marshal(&nvIndex.publicArea, &buffer, NULL); + // 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 + * (TPM_HANDLE *) nvBuffer = publicArea->nvIndex; + // 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; +} +// +// +// 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)]; + OBJECT *nvObject; // a pointer to the OBJECT data in + // nvBuffer + UINT16 entrySize; // size of entry + // 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))); + // An evict needs 4 bytes of handle + sizeof OBJECT + entrySize = sizeof(TPM_HANDLE) + sizeof(OBJECT); + // Check if we have enough space to add the evict object + // An evict object needs 8 bytes in index table + sizeof OBJECT + // In this implementation, the only resource limitation is the available NV + // space. Other implementation may have other limitation on evict object + // handle space + if(!NvTestSpace(entrySize, FALSE)) return TPM_RC_NV_SPACE; + // Allocate a new evict handle + if(!NvIsUndefinedEvictHandle(evictHandle)) + return TPM_RC_NV_DEFINED; + // Copy evict object to nvBuffer + // Copy handle + * (TPM_HANDLE *) nvBuffer = evictHandle; + // Copy OBJECT + nvObject = (OBJECT *) (nvBuffer + sizeof(TPM_HANDLE)); + *nvObject = *object; + // Set evict attribute and handle + nvObject->attributes.evict = SET; + nvObject->evictHandle = evictHandle; + // 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; +} |