2068 lines
72 KiB
C
2068 lines
72 KiB
C
// 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;
|
||
}
|