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