/* Microsoft Reference Implementation for TPM 2.0 | |
* | |
* The copyright in this software is being made available under the BSD License, | |
* included below. This software may be subject to other third party and | |
* contributor rights, including patent rights, and no such rights are granted | |
* under this license. | |
* | |
* Copyright (c) Microsoft Corporation | |
* | |
* All rights reserved. | |
* | |
* BSD License | |
* | |
* Redistribution and use in source and binary forms, with or without modification, | |
* are permitted provided that the following conditions are met: | |
* | |
* Redistributions of source code must retain the above copyright notice, this list | |
* of conditions and the following disclaimer. | |
* | |
* Redistributions in binary form must reproduce the above copyright notice, this | |
* list of conditions and the following disclaimer in the documentation and/or | |
* other materials provided with the distribution. | |
* | |
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ""AS IS"" | |
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR | |
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON | |
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
*/ | |
//** Introduction | |
// The NV memory is divided into two areas: dynamic space for user defined NV | |
// indexes and evict objects, and reserved space for TPM persistent and state save | |
// data. | |
// | |
// The entries in dynamic space are a linked list of entries. Each entry has, as its | |
// first field, a size. If the size field is zero, it marks the end of the | |
// list. | |
// | |
// An Index allocation will contain an NV_INDEX structure. If the Index does not | |
// have the orderly attribute, the NV_INDEX is followed immediately by the NV data. | |
// | |
// An evict object entry contains a handle followed by an OBJECT structure. This | |
// results in both the Index and Evict Object having an identifying handle as the | |
// first field following the size field. | |
// | |
// When an Index has the orderly attribute, the data is kept in RAM. This RAM is | |
// saved to backing store in NV memory on any orderly shutdown. The entries in | |
// orderly memory are also a linked list using a size field as the first entry. | |
// | |
// The attributes of an orderly index are maintained in RAM memory in order to | |
// reduce the number of NV writes needed for orderly data. When an orderly index | |
// is created, an entry is made in the dynamic NV memory space that holds the Index | |
// authorizations (authPolicy and authValue) and the size of the data. This entry is | |
// only modified if the authValue of the index is changed. The more volatile data | |
// of the index is kept in RAM. When an orderly Index is created or deleted, the | |
// RAM data is copied to NV backing store so that the image in the backing store | |
// matches the layout of RAM. In normal operation. The RAM data is also copied on | |
// any orderly shutdown. In normal operation, the only other reason for writing | |
// to the backing store for RAM is when a counter is first written (TPMA_NV_WRITTEN | |
// changes from CLEAR to SET) or when a counter ""rolls over"". | |
// | |
// Static space contains items that are individually modifiable. The values are in | |
// the 'gp' PERSISTENT_DATA structure in RAM and mapped to locations in NV. | |
// | |
//** Includes, Defines and Data Definitions | |
#define NV_C | |
#include "Tpm.h" | |
//** Local Functions | |
//*** 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_REF_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 are no more elements in the set, a 0 value is returned | |
// indicating the end of traversal. | |
// | |
static NV_REF | |
NvNext( | |
NV_REF *iter, // IN/OUT: the list iterator | |
TPM_HANDLE *handle // OUT: the handle of the next item. | |
) | |
{ | |
NV_REF currentAddr; | |
NV_ENTRY_HEADER header; | |
// | |
// If iterator is at the beginning of list | |
if(*iter == NV_REF_INIT) | |
{ | |
// Initialize iterator | |
*iter = NV_USER_DYNAMIC; | |
} | |
// Step over the size field and point to the handle | |
currentAddr = *iter + sizeof(UINT32); | |
// read the header of the next entry | |
NvRead(&header, *iter, sizeof(NV_ENTRY_HEADER)); | |
// if the size field is zero, then we have hit the end of the list | |
if(header.size == 0) | |
// leave the *iter pointing at the end of the list | |
return 0; | |
// advance the header by the size of the entry | |
*iter += header.size; | |
if(handle != NULL) | |
*handle = header.handle; | |
return currentAddr; | |
} | |
//*** NvNextByType() | |
// This function returns a reference to the next NV entry of the desired type | |
// Return Type: NV_REF | |
// 0 end of list | |
// != 0 the next entry of the indicated type | |
static NV_REF | |
NvNextByType( | |
TPM_HANDLE *handle, // OUT: the handle of the found type or 0 | |
NV_REF *iter, // IN: the iterator | |
TPM_HT type // IN: the handle type to look for | |
) | |
{ | |
NV_REF addr; | |
TPM_HANDLE nvHandle = 0; | |
// | |
while((addr = NvNext(iter, &nvHandle)) != 0) | |
{ | |
// addr: the address of the location containing the handle of the value | |
// iter: the next location. | |
if(HandleGetType(nvHandle) == type) | |
break; | |
} | |
if(handle != NULL) | |
*handle = nvHandle; | |
return addr; | |
} | |
//*** NvNextIndex() | |
// This function returns the reference to the next NV Index entry. A value | |
// of 0 indicates the end of the list. | |
// Return Type: NV_REF | |
// 0 end of list | |
// != 0 the next reference | |
#define NvNextIndex(handle, iter) \ | |
NvNextByType(handle, iter, TPM_HT_NV_INDEX) | |
//*** NvNextEvict() | |
// This function returns the offset in NV of the next evict object entry. A value | |
// of 0 indicates the end of the list. | |
#define NvNextEvict(handle, iter) \ | |
NvNextByType(handle, iter, TPM_HT_PERSISTENT) | |
//*** NvGetEnd() | |
// Function to find the end of the NV dynamic data list | |
static NV_REF | |
NvGetEnd( | |
void | |
) | |
{ | |
NV_REF iter = NV_REF_INIT; | |
NV_REF currentAddr; | |
// | |
// Scan until the next address is 0 | |
while((currentAddr = NvNext(&iter, NULL)) != 0); | |
return iter; | |
} | |
//*** NvGetFreeBytes | |
// This function returns the number of free octets in NV space. | |
static UINT32 | |
NvGetFreeBytes( | |
void | |
) | |
{ | |
// This does not have an overflow issue because NvGetEnd() cannot return a value | |
// that is larger than s_evictNvEnd. This is because there is always a 'stop' | |
// word in the NV memory that terminates the search for the end before the | |
// value can go past s_evictNvEnd. | |
return s_evictNvEnd - NvGetEnd(); | |
} | |
//*** NvTestSpace() | |
// This function will test if there is enough space to add a new entity. | |
// Return Type: BOOL | |
// TRUE(1) space available | |
// FALSE(0) 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 | |
BOOL isCounter // IN: TRUE if the index is a counter | |
) | |
{ | |
UINT32 remainBytes = NvGetFreeBytes(); | |
UINT32 reserved = sizeof(UINT32) // size of the forward pointer | |
+ sizeof(NV_LIST_TERMINATOR); | |
// | |
// Do a compile time sanity check on the setting for NV_MEMORY_SIZE | |
#if NV_MEMORY_SIZE < 1024 | |
#error "NV_MEMORY_SIZE probably isn't large enough" | |
#endif | |
// For NV Index, need to make sure that we do not allocate an 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) | |
reserved += (MIN_EVICT_OBJECTS - persistentNum) * NV_EVICT_OBJECT_SIZE; | |
} | |
// If this is not an index or is not a counter, reserve space for the | |
// required number of counter indexes | |
if(!isIndex || !isCounter) | |
{ | |
// Get the number of counters | |
UINT32 counterNum = NvCapGetCounterNumber(); | |
// If the required number of counters have not been allocated, reserved | |
// space for the extra needed counters | |
if(counterNum < MIN_COUNTER_INDICES) | |
reserved += (MIN_COUNTER_INDICES - counterNum) * NV_INDEX_COUNTER_SIZE; | |
} | |
// Check that the requested allocation will fit after making sure that there | |
// will be no chance of overflow | |
return ((reserved < remainBytes) | |
&& (size <= remainBytes) | |
&& (size + reserved <= remainBytes)); | |
} | |
//*** NvWriteNvListEnd() | |
// Function to write the list terminator. | |
NV_REF | |
NvWriteNvListEnd( | |
NV_REF end | |
) | |
{ | |
// Marker is initialized with zeros | |
BYTE listEndMarker[sizeof(NV_LIST_TERMINATOR)] = {0}; | |
UINT64 maxCount = NvReadMaxCount(); | |
// | |
// This is a constant check that can be resolved at compile time. | |
cAssert(sizeof(UINT64) <= sizeof(NV_LIST_TERMINATOR) - sizeof(UINT32)); | |
// Copy the maxCount value to the marker buffer | |
MemoryCopy(&listEndMarker[sizeof(UINT32)], &maxCount, sizeof(UINT64)); | |
pAssert(end + sizeof(NV_LIST_TERMINATOR) <= s_evictNvEnd); | |
// Write it to memory | |
NvWrite(end, sizeof(NV_LIST_TERMINATOR), &listEndMarker); | |
return end + sizeof(NV_LIST_TERMINATOR); | |
} | |
//*** 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). | |
// | |
// The 'totalSize' will be the size of 'entity'. If a handle is added, this | |
// function will increase the size accordingly. | |
static TPM_RC | |
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 | |
TPM_HANDLE handle, // IN: optional handle | |
BYTE *entity // IN: initial buffer | |
) | |
{ | |
NV_REF newAddr; // IN: where the new entity will start | |
NV_REF nextAddr; | |
// | |
RETURN_IF_NV_IS_NOT_AVAILABLE; | |
// Get the end of data list | |
newAddr = NvGetEnd(); | |
// Step over the forward pointer | |
nextAddr = newAddr + sizeof(UINT32); | |
// Optionally write the handle. For indexes, the handle is TPM_RH_UNASSIGNED | |
// so that the handle in the nvIndex is used instead of writing this value | |
if(handle != TPM_RH_UNASSIGNED) | |
{ | |
NvWrite((UINT32)nextAddr, sizeof(TPM_HANDLE), &handle); | |
nextAddr += sizeof(TPM_HANDLE); | |
} | |
// Write entity data | |
NvWrite((UINT32)nextAddr, bufferSize, entity); | |
// Advance the pointer by the amount of the total | |
nextAddr += totalSize; | |
// Finish by writing the link value | |
// Write the next offset (relative addressing) | |
totalSize = nextAddr - newAddr; | |
// Write link value | |
NvWrite((UINT32)newAddr, sizeof(UINT32), &totalSize); | |
// Write the list terminator | |
NvWriteNvListEnd(nextAddr); | |
return TPM_RC_SUCCESS; | |
} | |
//*** NvDelete() | |
// This function is used to delete an NV Index or persistent object from NV memory. | |
static TPM_RC | |
NvDelete( | |
NV_REF entityRef // IN: reference to entity to be deleted | |
) | |
{ | |
UINT32 entrySize; | |
// adjust entityAddr to back up and point to the forward pointer | |
NV_REF entryRef = entityRef - sizeof(UINT32); | |
NV_REF endRef = NvGetEnd(); | |
NV_REF nextAddr; // address of the next entry | |
// | |
RETURN_IF_NV_IS_NOT_AVAILABLE; | |
// Get the offset of the next entry. That is, back up and point to the size | |
// field of the entry | |
NvRead(&entrySize, entryRef, sizeof(UINT32)); | |
// The next entry after the one being deleted is at a relative offset | |
// from the current entry | |
nextAddr = entryRef + entrySize; | |
// If this is not the last entry, move everything up | |
if(nextAddr < endRef) | |
{ | |
pAssert(nextAddr > entryRef); | |
_plat__NvMemoryMove(nextAddr, | |
entryRef, | |
(endRef - nextAddr)); | |
} | |
// The end of the used space is now moved up by the amount of space we just | |
// reclaimed | |
endRef -= entrySize; | |
// Write the end marker, and make the new end equal to the first byte after | |
// the just added end value. This will automatically update the NV value for | |
// maxCounter. | |
// NOTE: This is the call that sets flag to cause NV to be updated | |
endRef = NvWriteNvListEnd(endRef); | |
// Clear the reclaimed memory | |
_plat__NvMemoryClear(endRef, entrySize); | |
return TPM_RC_SUCCESS; | |
} | |
//************************************************ | |
//** RAM-based NV Index Data Access Functions | |
//************************************************ | |
//*** Introduction | |
// The data layout in ram buffer is {size of(NV_handle + attributes + data | |
// NV_handle, attributes, data} | |
// for each NV Index data stored in RAM. | |
// | |
// NV storage associated with orderly data is updated when a NV Index is added | |
// but NOT when the data or attributes are changed. Orderly data is only updated | |
// to NV on an orderly shutdown (TPM2_Shutdown()) | |
//*** NvRamNext() | |
// This function is used to iterate trough the list of Ram Index values. *iter needs | |
// to be initialized by calling | |
static NV_RAM_REF | |
NvRamNext( | |
NV_RAM_REF *iter, // IN/OUT: the list iterator | |
TPM_HANDLE *handle // OUT: the handle of the next item. | |
) | |
{ | |
NV_RAM_REF currentAddr; | |
NV_RAM_HEADER header; | |
// | |
// If iterator is at the beginning of list | |
if(*iter == NV_RAM_REF_INIT) | |
{ | |
// Initialize iterator | |
*iter = &s_indexOrderlyRam[0]; | |
} | |
// if we are going to return what the iter is currently pointing to... | |
currentAddr = *iter; | |
// If iterator reaches the end of NV space, then don't advance and return | |
// that we are at the end of the list. The end of the list occurs when | |
// we don't have space for a size and a handle | |
if(currentAddr + sizeof(NV_RAM_HEADER) > RAM_ORDERLY_END) | |
return NULL; | |
// read the header of the next entry | |
MemoryCopy(&header, currentAddr, sizeof(NV_RAM_HEADER)); | |
// if the size field is zero, then we have hit the end of the list | |
if(header.size == 0) | |
// leave the *iter pointing at the end of the list | |
return NULL; | |
// advance the header by the size of the entry | |
*iter = currentAddr + header.size; | |
// pAssert(*iter <= RAM_ORDERLY_END); | |
if(handle != NULL) | |
*handle = header.handle; | |
return currentAddr; | |
} | |
//*** NvRamGetEnd() | |
// This routine performs the same function as NvGetEnd() but for the RAM data. | |
static NV_RAM_REF | |
NvRamGetEnd( | |
void | |
) | |
{ | |
NV_RAM_REF iter = NV_RAM_REF_INIT; | |
NV_RAM_REF currentAddr; | |
// | |
// Scan until the next address is 0 | |
while((currentAddr = NvRamNext(&iter, NULL)) != 0); | |
return iter; | |
} | |
//*** NvRamTestSpaceIndex() | |
// This function indicates if there is enough RAM space to add a data for a | |
// new NV Index. | |
// Return Type: BOOL | |
// TRUE(1) space available | |
// FALSE(0) no enough space | |
static BOOL | |
NvRamTestSpaceIndex( | |
UINT32 size // IN: size of the data to be added to RAM | |
) | |
{ | |
UINT32 remaining = (UINT32)(RAM_ORDERLY_END - NvRamGetEnd()); | |
UINT32 needed = sizeof(NV_RAM_HEADER) + size; | |
// | |
// NvRamGetEnd points to the next available byte. | |
return remaining >= needed; | |
} | |
//*** NvRamGetIndex() | |
// 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 NV_RAM_REF | |
NvRamGetIndex( | |
TPMI_RH_NV_INDEX handle // IN: NV handle | |
) | |
{ | |
NV_RAM_REF iter = NV_RAM_REF_INIT; | |
NV_RAM_REF currentAddr; | |
TPM_HANDLE foundHandle; | |
// | |
while((currentAddr = NvRamNext(&iter, &foundHandle)) != 0) | |
{ | |
if(handle == foundHandle) | |
break; | |
} | |
return currentAddr; | |
} | |
//*** NvUpdateIndexOrderlyData() | |
// This function is used to cause an update of the orderly data to the NV backing | |
// store. | |
void | |
NvUpdateIndexOrderlyData( | |
void | |
) | |
{ | |
// Write reserved RAM space to NV | |
NvWrite(NV_INDEX_RAM_DATA, sizeof(s_indexOrderlyRam), s_indexOrderlyRam); | |
} | |
//*** 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. | |
// | |
// This function should be called after the NV Index space has been updated | |
// and the index removed. This insures that NV is available so that checking | |
// for NV availability is not required during this function. | |
static void | |
NvAddRAM( | |
TPMS_NV_PUBLIC *index // IN: the index descriptor | |
) | |
{ | |
NV_RAM_HEADER header; | |
NV_RAM_REF end = NvRamGetEnd(); | |
// | |
header.size = sizeof(NV_RAM_HEADER) + index->dataSize; | |
header.handle = index->nvIndex; | |
MemoryCopy(&header.attributes, &index->attributes, sizeof(TPMA_NV)); | |
pAssert(ORDERLY_RAM_ADDRESS_OK(end, header.size)); | |
// Copy the header to the memory | |
MemoryCopy(end, &header, sizeof(NV_RAM_HEADER)); | |
// Clear the data area (just in case) | |
MemorySet(end + sizeof(NV_RAM_HEADER), 0, index->dataSize); | |
// Step over this new entry | |
end += header.size; | |
// If the end marker will fit, add it | |
if(end + sizeof(UINT32) < RAM_ORDERLY_END) | |
MemorySet(end, 0, sizeof(UINT32)); | |
// Write reserved RAM space to NV to reflect the newly added NV Index | |
SET_NV_UPDATE(UT_ORDERLY); | |
return; | |
} | |
//*** NvDeleteRAM() | |
// This function is used to delete a RAM-backed NV Index data area. | |
// The space used by the entry are overwritten by the contents of the | |
// Index data that comes after (the data is moved up to fill the hole left | |
// by removing this index. The reclaimed space is cleared to zeros. | |
// This function assumes the data of NV Index exists in RAM. | |
// | |
// This function should be called after the NV Index space has been updated | |
// and the index removed. This insures that NV is available so that checking | |
// for NV availability is not required during this function. | |
static void | |
NvDeleteRAM( | |
TPMI_RH_NV_INDEX handle // IN: NV handle | |
) | |
{ | |
NV_RAM_REF nodeAddress; | |
NV_RAM_REF nextNode; | |
UINT32 size; | |
NV_RAM_REF lastUsed = NvRamGetEnd(); | |
// | |
nodeAddress = NvRamGetIndex(handle); | |
pAssert(nodeAddress != 0); | |
// Get node size | |
MemoryCopy(&size, nodeAddress, sizeof(size)); | |
// Get the offset of next node | |
nextNode = nodeAddress + size; | |
// Copy the data | |
MemoryCopy(nodeAddress, nextNode, (int)(lastUsed - nextNode)); | |
// Clear out the reclaimed space | |
MemorySet(lastUsed - size, 0, size); | |
// Write reserved RAM space to NV to reflect the newly delete NV Index | |
SET_NV_UPDATE(UT_ORDERLY); | |
return; | |
} | |
//*** NvReadIndex() | |
// This function is used to read the NV Index NV_INDEX. This is used so that the | |
// index information can be compressed and only this function would be needed | |
// to decompress it. Mostly, compression would only be able to save the space | |
// needed by the policy. | |
void | |
NvReadNvIndexInfo( | |
NV_REF ref, // IN: points to NV where index is located | |
NV_INDEX *nvIndex // OUT: place to receive index data | |
) | |
{ | |
pAssert(nvIndex != NULL); | |
NvRead(nvIndex, ref, sizeof(NV_INDEX)); | |
return; | |
} | |
//*** NvReadObject() | |
// This function is used to read a persistent object. This is used so that the | |
// object information can be compressed and only this function would be needed | |
// to uncompress it. | |
void | |
NvReadObject( | |
NV_REF ref, // IN: points to NV where index is located | |
OBJECT *object // OUT: place to receive the object data | |
) | |
{ | |
NvRead(object, (ref + sizeof(TPM_HANDLE)), sizeof(OBJECT)); | |
return; | |
} | |
//*** NvFindEvict() | |
// This function will return the NV offset of an evict object | |
// Return Type: UINT32 | |
// 0 evict object not found | |
// != 0 offset of evict object | |
static NV_REF | |
NvFindEvict( | |
TPM_HANDLE nvHandle, | |
OBJECT *object | |
) | |
{ | |
NV_REF found = NvFindHandle(nvHandle); | |
// | |
// If we found the handle and the request included an object pointer, fill it in | |
if(found != 0 && object != NULL) | |
NvReadObject(found, object); | |
return found; | |
} | |
//*** NvIndexIsDefined() | |
// See if an index is already defined | |
BOOL | |
NvIndexIsDefined( | |
TPM_HANDLE nvHandle // IN: Index to look for | |
) | |
{ | |
return (NvFindHandle(nvHandle) != 0); | |
} | |
//*** NvConditionallyWrite() | |
// Function to check if the data to be written has changed | |
// and write it if it has | |
// Return Type: TPM_RC | |
// TPM_RC_NV_RATE NV is unavailable because of rate limit | |
// TPM_RC_NV_UNAVAILABLE NV is inaccessible | |
static TPM_RC | |
NvConditionallyWrite( | |
NV_REF entryAddr, // IN: stating address | |
UINT32 size, // IN: size of the data to write | |
void *data // IN: the data to write | |
) | |
{ | |
// If the index data is actually changed, then a write to NV is required | |
if(_plat__NvIsDifferent(entryAddr, size, data)) | |
{ | |
// Write the data if NV is available | |
if(g_NvStatus == TPM_RC_SUCCESS) | |
{ | |
NvWrite(entryAddr, size, data); | |
} | |
return g_NvStatus; | |
} | |
return TPM_RC_SUCCESS; | |
} | |
//*** NvReadNvIndexAttributes() | |
// This function returns the attributes of an NV Index. | |
static TPMA_NV | |
NvReadNvIndexAttributes( | |
NV_REF locator // IN: reference to an NV index | |
) | |
{ | |
TPMA_NV attributes; | |
// | |
NvRead(&attributes, | |
locator + offsetof(NV_INDEX, publicArea.attributes), | |
sizeof(TPMA_NV)); | |
return attributes; | |
} | |
//*** NvReadRamIndexAttributes() | |
// This function returns the attributes from the RAM header structure. This function | |
// is used to deal with the fact that the header structure is only byte aligned. | |
static TPMA_NV | |
NvReadRamIndexAttributes( | |
NV_RAM_REF ref // IN: pointer to a NV_RAM_HEADER | |
) | |
{ | |
TPMA_NV attributes; | |
// | |
MemoryCopy(&attributes, ref + offsetof(NV_RAM_HEADER, attributes), | |
sizeof(TPMA_NV)); | |
return attributes; | |
} | |
//*** NvWriteNvIndexAttributes() | |
// This function is used to write just the attributes of an index to NV. | |
// Return type: TPM_RC | |
// TPM_RC_NV_RATE NV is rate limiting so retry | |
// TPM_RC_NV_UNAVAILABLE NV is not available | |
static TPM_RC | |
NvWriteNvIndexAttributes( | |
NV_REF locator, // IN: location of the index | |
TPMA_NV attributes // IN: attributes to write | |
) | |
{ | |
return NvConditionallyWrite( | |
locator + offsetof(NV_INDEX, publicArea.attributes), | |
sizeof(TPMA_NV), | |
&attributes); | |
} | |
//*** NvWriteRamIndexAttributes() | |
// This function is used to write the index attributes into an unaligned structure | |
static void | |
NvWriteRamIndexAttributes( | |
NV_RAM_REF ref, // IN: address of the header | |
TPMA_NV attributes // IN: the attributes to write | |
) | |
{ | |
MemoryCopy(ref + offsetof(NV_RAM_HEADER, attributes), &attributes, | |
sizeof(TPMA_NV)); | |
return; | |
} | |
//************************************************ | |
//** Externally Accessible Functions | |
//************************************************ | |
//*** NvIsPlatformPersistentHandle() | |
// This function indicates if a handle references a persistent object in the | |
// range belonging to the platform. | |
// Return Type: BOOL | |
// TRUE(1) handle references a platform persistent object | |
// FALSE(0) handle does not reference platform persistent object | |
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 Type: BOOL | |
// TRUE(1) handle is owner persistent handle | |
// FALSE(0) 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); | |
} | |
//*** NvIndexIsAccessible() | |
// | |
// This function validates that a handle references a defined NV Index and | |
// that the Index is currently accessible. | |
// Return Type: TPM_RC | |
// 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 | |
// platformAuth | |
// 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 | |
) | |
{ | |
NV_INDEX *nvIndex = NvGetIndexInfo(handle, NULL); | |
// | |
if(nvIndex == NULL) | |
// If index is not found, return TPM_RC_HANDLE | |
return TPM_RC_HANDLE; | |
if(gc.shEnable == FALSE || gc.phEnableNV == FALSE) | |
{ | |
// if shEnable is CLEAR, an ownerCreate NV Index should not be | |
// indicated as present | |
if(!IS_ATTRIBUTE(nvIndex->publicArea.attributes, TPMA_NV, PLATFORMCREATE)) | |
{ | |
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 0 // Writelock test for debug | |
// If the Index is write locked and this is an NV Write operation... | |
if(IS_ATTRIBUTE(nvIndex->publicArea.attributes, TPMA_NV, WRITELOCKED) | |
&& IsWriteOperation(commandIndex)) | |
{ | |
// then return a locked indication unless the command is TPM2_NV_WriteLock | |
if(GetCommandCode(commandIndex) != TPM_CC_NV_WriteLock) | |
return TPM_RC_NV_LOCKED; | |
return TPM_RC_SUCCESS; | |
} | |
#endif | |
#if 0 // Readlock Test for debug | |
// If the Index is read locked and this is an NV Read operation... | |
if(IS_ATTRIBUTE(nvIndex->publicArea.attributes, TPMA_NV, READLOCKED) | |
&& IsReadOperation(commandIndex)) | |
{ | |
// then return a locked indication unless the command is TPM2_NV_ReadLock | |
if(GetCommandCode(commandIndex) != TPM_CC_NV_ReadLock) | |
return TPM_RC_NV_LOCKED; | |
} | |
#endif | |
// NV Index is accessible | |
return TPM_RC_SUCCESS; | |
} | |
//*** NvGetEvictObject() | |
// This function is used to dereference an evict object handle and get a pointer | |
// to the object. | |
// Return Type: TPM_RC | |
// 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 | |
) | |
{ | |
NV_REF entityAddr; // offset points to the entity | |
// | |
// Find the address of evict object and copy to object | |
entityAddr = NvFindEvict(handle, object); | |
// whether there is an error or not, make sure that the evict | |
// status of the object is set so that the slot will get freed on exit | |
// Must do this after NvFindEvict loads the object | |
object->attributes.evict = SET; | |
// If handle is not found, return an error | |
if(entityAddr == 0) | |
return TPM_RC_HANDLE; | |
return TPM_RC_SUCCESS; | |
} | |
//*** NvIndexCacheInit() | |
// Function to initialize the Index cache | |
void | |
NvIndexCacheInit( | |
void | |
) | |
{ | |
s_cachedNvRef = NV_REF_INIT; | |
s_cachedNvRamRef = NV_RAM_REF_INIT; | |
s_cachedNvIndex.publicArea.nvIndex = TPM_RH_UNASSIGNED; | |
return; | |
} | |
//*** NvGetIndexData() | |
// This function is used to access the data in an NV Index. The data is returned | |
// as a byte sequence. | |
// | |
// 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( | |
NV_INDEX *nvIndex, // IN: the in RAM index descriptor | |
NV_REF locator, // IN: where the data is located | |
UINT32 offset, // IN: offset of NV data | |
UINT16 size, // IN: number of octets of NV data to read | |
void *data // OUT: data buffer | |
) | |
{ | |
TPMA_NV nvAttributes; | |
// | |
pAssert(nvIndex != NULL); | |
nvAttributes = nvIndex->publicArea.attributes; | |
pAssert(IS_ATTRIBUTE(nvAttributes, TPMA_NV, WRITTEN)); | |
if(IS_ATTRIBUTE(nvAttributes, TPMA_NV, ORDERLY)) | |
{ | |
// Get data from RAM buffer | |
NV_RAM_REF ramAddr = NvRamGetIndex(nvIndex->publicArea.nvIndex); | |
pAssert(ramAddr != 0 && (size <= | |
((NV_RAM_HEADER *)ramAddr)->size - sizeof(NV_RAM_HEADER) - offset)); | |
MemoryCopy(data, ramAddr + sizeof(NV_RAM_HEADER) + offset, size); | |
} | |
else | |
{ | |
// Validate that read falls within range of the index | |
pAssert(offset <= nvIndex->publicArea.dataSize | |
&& size <= (nvIndex->publicArea.dataSize - offset)); | |
NvRead(data, locator + sizeof(NV_INDEX) + offset, size); | |
} | |
return; | |
} | |
//*** NvHashIndexData() | |
// This function adds Index data to a hash. It does this in parts to avoid large stack | |
// buffers. | |
void | |
NvHashIndexData( | |
HASH_STATE *hashState, // IN: Initialized hash state | |
NV_INDEX *nvIndex, // IN: Index | |
NV_REF locator, // IN: where the data is located | |
UINT32 offset, // IN: starting offset | |
UINT16 size // IN: amount to hash | |
) | |
{ | |
#define BUFFER_SIZE 64 | |
BYTE buffer[BUFFER_SIZE]; | |
if (offset > nvIndex->publicArea.dataSize) | |
return; | |
// Make sure that we don't try to read off the end. | |
if ((offset + size) > nvIndex->publicArea.dataSize) | |
size = nvIndex->publicArea.dataSize - (UINT16)offset; | |
#if BUFFER_SIZE >= MAX_NV_INDEX_SIZE | |
NvGetIndexData(nvIndex, locator, offset, size, buffer); | |
CryptDigestUpdate(hashState, size, buffer); | |
#else | |
{ | |
INT16 i; | |
UINT16 readSize; | |
// | |
for (i = size; i > 0; offset += readSize, i -= readSize) | |
{ | |
readSize = (i < BUFFER_SIZE) ? i : BUFFER_SIZE; | |
NvGetIndexData(nvIndex, locator, offset, readSize, buffer); | |
CryptDigestUpdate(hashState, readSize, buffer); | |
} | |
} | |
#endif // BUFFER_SIZE >= MAX_NV_INDEX_SIZE | |
#undef BUFFER_SIZE | |
} | |
//*** NvGetUINT64Data() | |
// 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. | |
UINT64 | |
NvGetUINT64Data( | |
NV_INDEX *nvIndex, // IN: the in RAM index descriptor | |
NV_REF locator // IN: where index exists in NV | |
) | |
{ | |
UINT64 intVal; | |
// | |
// Read the value and convert it to internal format | |
NvGetIndexData(nvIndex, locator, 0, 8, &intVal); | |
return BYTE_ARRAY_TO_UINT64(((BYTE *)&intVal)); | |
} | |
//*** NvWriteIndexAttributes() | |
// This function is used to write just the attributes of an index. | |
// Return type: TPM_RC | |
// TPM_RC_NV_RATE NV is rate limiting so retry | |
// TPM_RC_NV_UNAVAILABLE NV is not available | |
TPM_RC | |
NvWriteIndexAttributes( | |
TPM_HANDLE handle, | |
NV_REF locator, // IN: location of the index | |
TPMA_NV attributes // IN: attributes to write | |
) | |
{ | |
TPM_RC result; | |
// | |
if(IS_ATTRIBUTE(attributes, TPMA_NV, ORDERLY)) | |
{ | |
NV_RAM_REF ram = NvRamGetIndex(handle); | |
NvWriteRamIndexAttributes(ram, attributes); | |
result = TPM_RC_SUCCESS; | |
} | |
else | |
{ | |
result = NvWriteNvIndexAttributes(locator, attributes); | |
} | |
return result; | |
} | |
//*** NvWriteIndexAuth() | |
// This function is used to write the authValue of an index. It is used by | |
// TPM2_NV_ChangeAuth() | |
// Return type: TPM_RC | |
// TPM_RC_NV_RATE NV is rate limiting so retry | |
// TPM_RC_NV_UNAVAILABLE NV is not available | |
TPM_RC | |
NvWriteIndexAuth( | |
NV_REF locator, // IN: location of the index | |
TPM2B_AUTH *authValue // IN: the authValue to write | |
) | |
{ | |
TPM_RC result; | |
// | |
// If the locator is pointing to the cached index value... | |
if(locator == s_cachedNvRef) | |
{ | |
// copy the authValue to the cached index so it will be there if we | |
// look for it. This is a safety thing. | |
MemoryCopy2B(&s_cachedNvIndex.authValue.b, &authValue->b, | |
sizeof(s_cachedNvIndex.authValue.t.buffer)); | |
} | |
result = NvConditionallyWrite( | |
locator + offsetof(NV_INDEX, authValue), | |
sizeof(UINT16) + authValue->t.size, | |
authValue); | |
return result; | |
} | |
//*** NvGetIndexInfo() | |
// This function loads the nvIndex Info into the NV cache and returns a pointer | |
// to the NV_INDEX. If the returned value is zero, the index was not found. | |
// The 'locator' parameter, if not NULL, will be set to the offset in NV of the | |
// Index (the location of the handle of the Index). | |
// | |
// This function will set the index cache. If the index is orderly, the attributes | |
// from RAM are substituted for the attributes in the cached index | |
NV_INDEX * | |
NvGetIndexInfo( | |
TPM_HANDLE nvHandle, // IN: the index handle | |
NV_REF *locator // OUT: location of the index | |
) | |
{ | |
if(s_cachedNvIndex.publicArea.nvIndex != nvHandle) | |
{ | |
s_cachedNvIndex.publicArea.nvIndex = TPM_RH_UNASSIGNED; | |
s_cachedNvRamRef = 0; | |
s_cachedNvRef = NvFindHandle(nvHandle); | |
if(s_cachedNvRef == 0) | |
return NULL; | |
NvReadNvIndexInfo(s_cachedNvRef, &s_cachedNvIndex); | |
if(IS_ATTRIBUTE(s_cachedNvIndex.publicArea.attributes, TPMA_NV, ORDERLY)) | |
{ | |
s_cachedNvRamRef = NvRamGetIndex(nvHandle); | |
s_cachedNvIndex.publicArea.attributes = | |
NvReadRamIndexAttributes(s_cachedNvRamRef); | |
} | |
} | |
if(locator != NULL) | |
*locator = s_cachedNvRef; | |
return &s_cachedNvIndex; | |
} | |
//*** NvWriteIndexData() | |
// This function is used to write NV index data. It is intended to be used to | |
// update the data associated with the default index. | |
// | |
// This function requires that the NV Index is defined, and the data is | |
// within the defined data range for the index. | |
// | |
// Index data is only written due to a command that modifies the data in a single | |
// index. There is no case where changes are made to multiple indexes data at the | |
// same time. Multiple attributes may be change but not multiple index data. This | |
// is important because we will normally be handling the index for which we have | |
// the cached pointer values. | |
// Return type: TPM_RC | |
// TPM_RC_NV_RATE NV is rate limiting so retry | |
// TPM_RC_NV_UNAVAILABLE NV is not available | |
TPM_RC | |
NvWriteIndexData( | |
NV_INDEX *nvIndex, // IN: the description of the index | |
UINT32 offset, // IN: offset of NV data | |
UINT32 size, // IN: size of NV data | |
void *data // IN: data buffer | |
) | |
{ | |
TPM_RC result = TPM_RC_SUCCESS; | |
// | |
pAssert(nvIndex != NULL); | |
// Make sure that this is dealing with the 'default' index. | |
// Note: it is tempting to change the calling sequence so that the 'default' is | |
// presumed. | |
pAssert(nvIndex->publicArea.nvIndex == s_cachedNvIndex.publicArea.nvIndex); | |
// Validate that write falls within range of the index | |
pAssert(offset <= nvIndex->publicArea.dataSize | |
&& size <= (nvIndex->publicArea.dataSize - offset)); | |
// Update TPMA_NV_WRITTEN bit if necessary | |
if(!IS_ATTRIBUTE(nvIndex->publicArea.attributes, TPMA_NV, WRITTEN)) | |
{ | |
// Update the in memory version of the attributes | |
SET_ATTRIBUTE(nvIndex->publicArea.attributes, TPMA_NV, WRITTEN); | |
// If this is not orderly, then update the NV version of | |
// the attributes | |
if(!IS_ATTRIBUTE(nvIndex->publicArea.attributes, TPMA_NV, ORDERLY)) | |
{ | |
result = NvWriteNvIndexAttributes(s_cachedNvRef, | |
nvIndex->publicArea.attributes); | |
if(result != TPM_RC_SUCCESS) | |
return result; | |
// If this is a partial write of an ordinary index, clear the whole | |
// index. | |
if(IsNvOrdinaryIndex(nvIndex->publicArea.attributes) | |
&& (nvIndex->publicArea.dataSize > size)) | |
_plat__NvMemoryClear(s_cachedNvRef + sizeof(NV_INDEX), | |
nvIndex->publicArea.dataSize); | |
} | |
else | |
{ | |
// This is orderly so update the RAM version | |
MemoryCopy(s_cachedNvRamRef + offsetof(NV_RAM_HEADER, attributes), | |
&nvIndex->publicArea.attributes, sizeof(TPMA_NV)); | |
// If setting WRITTEN for an orderly counter, make sure that the | |
// state saved version of the counter is saved | |
if(IsNvCounterIndex(nvIndex->publicArea.attributes)) | |
SET_NV_UPDATE(UT_ORDERLY); | |
// If setting the written attribute on an ordinary index, make sure that | |
// the data is all cleared out in case there is a partial write. This | |
// is only necessary for ordinary indexes because all of the other types | |
// are always written in total. | |
else if(IsNvOrdinaryIndex(nvIndex->publicArea.attributes)) | |
MemorySet(s_cachedNvRamRef + sizeof(NV_RAM_HEADER), | |
0, nvIndex->publicArea.dataSize); | |
} | |
} | |
// If this is orderly data, write it to RAM | |
if(IS_ATTRIBUTE(nvIndex->publicArea.attributes, TPMA_NV, ORDERLY)) | |
{ | |
// Note: if this is the first write to a counter, the code above will queue | |
// the write to NV of the RAM data in order to update TPMA_NV_WRITTEN. In | |
// process of doing that write, it will also write the initial counter value | |
// Update RAM | |
MemoryCopy(s_cachedNvRamRef + sizeof(NV_RAM_HEADER) + offset, data, size); | |
// And indicate that the TPM is no longer orderly | |
g_clearOrderly = TRUE; | |
} | |
else | |
{ | |
// Offset into the index to the first byte of the data to be written to NV | |
result = NvConditionallyWrite(s_cachedNvRef + sizeof(NV_INDEX) + offset, | |
size, data); | |
} | |
return result; | |
} | |
//*** NvWriteUINT64Data() | |
// This function to write back a UINT64 value. The various UINT64 values (bits, | |
// counters, and PINs) are kept in canonical format but manipulate in native | |
// format. This takes a native format value converts it and saves it back as | |
// in canonical format. | |
// | |
// This function will return the value from NV or RAM depending on the type of the | |
// index (orderly or not) | |
// | |
TPM_RC | |
NvWriteUINT64Data( | |
NV_INDEX *nvIndex, // IN: the description of the index | |
UINT64 intValue // IN: the value to write | |
) | |
{ | |
BYTE bytes[8]; | |
UINT64_TO_BYTE_ARRAY(intValue, bytes); | |
// | |
return NvWriteIndexData(nvIndex, 0, 8, &bytes); | |
} | |
//*** NvGetIndexName() | |
// This function computes the Name of an 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. | |
TPM2B_NAME * | |
NvGetIndexName( | |
NV_INDEX *nvIndex, // IN: the index over which the name is to be | |
// computed | |
TPM2B_NAME *name // OUT: name of the index | |
) | |
{ | |
UINT16 dataSize, digestSize; | |
BYTE marshalBuffer[sizeof(TPMS_NV_PUBLIC)]; | |
BYTE *buffer; | |
HASH_STATE hashState; | |
// | |
// Marshal public area | |
buffer = marshalBuffer; | |
dataSize = TPMS_NV_PUBLIC_Marshal(&nvIndex->publicArea, &buffer, NULL); | |
// hash public area | |
digestSize = CryptHashStart(&hashState, nvIndex->publicArea.nameAlg); | |
CryptDigestUpdate(&hashState, dataSize, marshalBuffer); | |
// Complete digest leaving room for the nameAlg | |
CryptHashEnd(&hashState, digestSize, &name->b.buffer[2]); | |
// Include the nameAlg | |
UINT16_TO_BYTE_ARRAY(nvIndex->publicArea.nameAlg, name->b.buffer); | |
name->t.size = digestSize + 2; | |
return name; | |
} | |
//*** NvGetNameByIndexHandle() | |
// This function is used to compute the Name of an NV Index referenced by handle. | |
// | |
// 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. | |
TPM2B_NAME * | |
NvGetNameByIndexHandle( | |
TPMI_RH_NV_INDEX handle, // IN: handle of the index | |
TPM2B_NAME *name // OUT: name of the index | |
) | |
{ | |
NV_INDEX *nvIndex = NvGetIndexInfo(handle, NULL); | |
// | |
return NvGetIndexName(nvIndex, name); | |
} | |
//*** NvDefineIndex() | |
// This function is used to assign NV memory to an NV Index. | |
// | |
// Return Type: TPM_RC | |
// 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 | |
NV_INDEX nvIndex; // the index data | |
UINT16 entrySize; // size of entry | |
TPM_RC result; | |
// | |
entrySize = sizeof(NV_INDEX); | |
// only allocate data space for indexes that are going to be written to NV. | |
// Orderly indexes don't need space. | |
if(!IS_ATTRIBUTE(publicArea->attributes, TPMA_NV, ORDERLY)) | |
entrySize += 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 (and possibly RAM space.) Other implementation may have other | |
// limitation on counter or on NV slots | |
if(!NvTestSpace(entrySize, TRUE, IsNvCounterIndex(publicArea->attributes))) | |
return TPM_RC_NV_SPACE; | |
// if the index to be defined is RAM backed, check RAM space availability | |
// as well | |
if(IS_ATTRIBUTE(publicArea->attributes, TPMA_NV, ORDERLY) | |
&& !NvRamTestSpaceIndex(publicArea->dataSize)) | |
return TPM_RC_NV_SPACE; | |
// Copy input value to nvBuffer | |
nvIndex.publicArea = *publicArea; | |
// Copy the authValue | |
nvIndex.authValue = *authValue; | |
// Add index to NV memory | |
result = NvAdd(entrySize, sizeof(NV_INDEX), TPM_RH_UNASSIGNED, | |
(BYTE *)&nvIndex); | |
if(result == TPM_RC_SUCCESS) | |
{ | |
// If the data of NV Index is RAM backed, add the data area in RAM as well | |
if(IS_ATTRIBUTE(publicArea->attributes, TPMA_NV, ORDERLY)) | |
NvAddRAM(publicArea); | |
} | |
return result; | |
} | |
//*** NvAddEvictObject() | |
// This function is used to assign NV memory to a persistent object. | |
// Return Type: TPM_RC | |
// 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 | |
) | |
{ | |
TPM_HANDLE temp = object->evictHandle; | |
TPM_RC result; | |
// | |
// Check if we have enough space to add the evict object | |
// An evict object needs 8 bytes in index table + sizeof OBJECT | |
// In this implementation, the only resource limitation is the available NV | |
// space. Other implementation may have other limitation on evict object | |
// handle space | |
if(!NvTestSpace(sizeof(OBJECT) + sizeof(TPM_HANDLE), FALSE, FALSE)) | |
return TPM_RC_NV_SPACE; | |
// Set evict attribute and handle | |
object->attributes.evict = SET; | |
object->evictHandle = evictHandle; | |
// Now put this in NV | |
result = NvAdd(sizeof(OBJECT), sizeof(OBJECT), evictHandle, (BYTE *)object); | |
// Put things back the way they were | |
object->attributes.evict = CLEAR; | |
object->evictHandle = temp; | |
return result; | |
} | |
//*** NvDeleteIndex() | |
// This function is used to delete an NV Index. | |
// Return Type: TPM_RC | |
// TPM_RC_NV_UNAVAILABLE NV is not accessible | |
// TPM_RC_NV_RATE NV is rate limiting | |
TPM_RC | |
NvDeleteIndex( | |
NV_INDEX *nvIndex, // IN: an in RAM index descriptor | |
NV_REF entityAddr // IN: location in NV | |
) | |
{ | |
TPM_RC result; | |
// | |
if(nvIndex != NULL) | |
{ | |
// Whenever a counter is deleted, make sure that the MaxCounter value is | |
// updated to reflect the value | |
if(IsNvCounterIndex(nvIndex->publicArea.attributes) | |
&& IS_ATTRIBUTE(nvIndex->publicArea.attributes, TPMA_NV, WRITTEN)) | |
NvUpdateMaxCount(NvGetUINT64Data(nvIndex, entityAddr)); | |
result = NvDelete(entityAddr); | |
if(result != TPM_RC_SUCCESS) | |
return result; | |
// If the NV Index is RAM backed, delete the RAM data as well | |
if(IS_ATTRIBUTE(nvIndex->publicArea.attributes, TPMA_NV, ORDERLY)) | |
NvDeleteRAM(nvIndex->publicArea.nvIndex); | |
NvIndexCacheInit(); | |
} | |
return TPM_RC_SUCCESS; | |
} | |
//*** NvDeleteEvict() | |
// This function will delete a NV evict object. | |
// Will return success if object deleted or if it does not exist | |
TPM_RC | |
NvDeleteEvict( | |
TPM_HANDLE handle // IN: handle of entity to be deleted | |
) | |
{ | |
NV_REF entityAddr = NvFindEvict(handle, NULL); // pointer to entity | |
TPM_RC result = TPM_RC_SUCCESS; | |
// | |
if(entityAddr != 0) | |
result = NvDelete(entityAddr); | |
return result; | |
} | |
//*** NvFlushHierarchy() | |
// This function will delete persistent objects belonging to the indicated hierarchy. | |
// If the storage hierarchy is selected, the function will also delete any | |
// NV Index defined using ownerAuth. | |
// Return Type: TPM_RC | |
// TPM_RC_NV_RATE NV is unavailable because of rate limit | |
// TPM_RC_NV_UNAVAILABLE NV is inaccessible | |
TPM_RC | |
NvFlushHierarchy( | |
TPMI_RH_HIERARCHY hierarchy // IN: hierarchy to be flushed. | |
) | |
{ | |
NV_REF iter = NV_REF_INIT; | |
NV_REF currentAddr; | |
TPM_HANDLE entityHandle; | |
TPM_RC result = TPM_RC_SUCCESS; | |
// | |
while((currentAddr = NvNext(&iter, &entityHandle)) != 0) | |
{ | |
if(HandleGetType(entityHandle) == TPM_HT_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; | |
// Get the index information | |
NvReadNvIndexInfo(currentAddr, &nvIndex); | |
// For storage hierarchy, flush OwnerCreated index | |
if(!IS_ATTRIBUTE(nvIndex.publicArea.attributes, TPMA_NV, | |
PLATFORMCREATE)) | |
{ | |
// Delete the index (including RAM for orderly) | |
result = NvDeleteIndex(&nvIndex, currentAddr); | |
if(result != TPM_RC_SUCCESS) | |
break; | |
// Re-iterate from beginning after a delete | |
iter = NV_REF_INIT; | |
} | |
} | |
else if(HandleGetType(entityHandle) == TPM_HT_PERSISTENT) | |
{ | |
OBJECT_ATTRIBUTES attributes; | |
// | |
NvRead(&attributes, | |
(UINT32)(currentAddr | |
+ sizeof(TPM_HANDLE) | |
+ offsetof(OBJECT, attributes)), | |
sizeof(OBJECT_ATTRIBUTES)); | |
// If the evict object belongs to the hierarchy to be flushed... | |
if((hierarchy == TPM_RH_PLATFORM && attributes.ppsHierarchy == SET) | |
|| (hierarchy == TPM_RH_OWNER && attributes.spsHierarchy == SET) | |
|| (hierarchy == TPM_RH_ENDORSEMENT | |
&& attributes.epsHierarchy == SET)) | |
{ | |
// ...then delete the evict object | |
result = NvDelete(currentAddr); | |
if(result != TPM_RC_SUCCESS) | |
break; | |
// Re-iterate from beginning after a delete | |
iter = NV_REF_INIT; | |
} | |
} | |
else | |
{ | |
FAIL(FATAL_ERROR_INTERNAL); | |
} | |
} | |
return result; | |
} | |
//*** NvSetGlobalLock() | |
// This function is used to SET the TPMA_NV_WRITELOCKED attribute for all | |
// NV indexes that have TPMA_NV_GLOBALLOCK SET. This function is use by | |
// TPM2_NV_GlobalWriteLock(). | |
// Return Type: TPM_RC | |
// TPM_RC_NV_RATE NV is unavailable because of rate limit | |
// TPM_RC_NV_UNAVAILABLE NV is inaccessible | |
TPM_RC | |
NvSetGlobalLock( | |
void | |
) | |
{ | |
NV_REF iter = NV_REF_INIT; | |
NV_RAM_REF ramIter = NV_RAM_REF_INIT; | |
NV_REF currentAddr; | |
NV_RAM_REF currentRamAddr; | |
TPM_RC result = TPM_RC_SUCCESS; | |
// | |
// Check all normal indexes | |
while((currentAddr = NvNextIndex(NULL, &iter)) != 0) | |
{ | |
TPMA_NV attributes = NvReadNvIndexAttributes(currentAddr); | |
// | |
// See if it should be locked | |
if(!IS_ATTRIBUTE(attributes, TPMA_NV, ORDERLY) | |
&& IS_ATTRIBUTE(attributes, TPMA_NV, GLOBALLOCK)) | |
{ | |
SET_ATTRIBUTE(attributes, TPMA_NV, WRITELOCKED); | |
result = NvWriteNvIndexAttributes(currentAddr, attributes); | |
if(result != TPM_RC_SUCCESS) | |
return result; | |
} | |
} | |
// Now search all the orderly attributes | |
while((currentRamAddr = NvRamNext(&ramIter, NULL)) != 0) | |
{ | |
// See if it should be locked | |
TPMA_NV attributes = NvReadRamIndexAttributes(currentRamAddr); | |
if(IS_ATTRIBUTE(attributes, TPMA_NV, GLOBALLOCK)) | |
{ | |
SET_ATTRIBUTE(attributes, TPMA_NV, WRITELOCKED); | |
NvWriteRamIndexAttributes(currentRamAddr, attributes); | |
} | |
} | |
return result; | |
} | |
//***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 Type: TPMI_YES_NO | |
// 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 handles | |
TPML_HANDLE *handleList // OUT: list of handle | |
) | |
{ | |
TPMI_YES_NO more = NO; | |
NV_REF iter = NV_REF_INIT; | |
NV_REF currentAddr; | |
TPM_HANDLE entityHandle; | |
// | |
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(&entityHandle, &iter)) != 0) | |
{ | |
// 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 indexes is small. For an implementation that may allow | |
// large number of NV indexes, 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 indexes, starting from 'handle'. | |
// 'Handle' must be in the range of NV indexes, but does not have to reference | |
// an existing NV Index. | |
// Return Type: TPMI_YES_NO | |
// 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: max number of returned handles | |
TPML_HANDLE *handleList // OUT: list of handle | |
) | |
{ | |
TPMI_YES_NO more = NO; | |
NV_REF iter = NV_REF_INIT; | |
NV_REF currentAddr; | |
TPM_HANDLE nvHandle; | |
// | |
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(&nvHandle, &iter)) != 0) | |
{ | |
// Ignore index handles that have values less than the 'handle' | |
if(nvHandle < 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 indexes is small. For an implementation that may allow | |
// large number of NV indexes, a more efficient sorting algorithm may be | |
// used here. | |
InsertSort(handleList, count, nvHandle); | |
} | |
return more; | |
} | |
//*** NvCapGetIndexNumber() | |
// This function returns the count of NV Indexes currently defined. | |
UINT32 | |
NvCapGetIndexNumber( | |
void | |
) | |
{ | |
UINT32 num = 0; | |
NV_REF iter = NV_REF_INIT; | |
// | |
while(NvNextIndex(NULL, &iter) != 0) | |
num++; | |
return num; | |
} | |
//*** NvCapGetPersistentNumber() | |
// Function returns the count of persistent objects currently in NV memory. | |
UINT32 | |
NvCapGetPersistentNumber( | |
void | |
) | |
{ | |
UINT32 num = 0; | |
NV_REF iter = NV_REF_INIT; | |
TPM_HANDLE handle; | |
// | |
while(NvNextEvict(&handle, &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 availNVSpace; | |
UINT32 counterNum = NvCapGetCounterNumber(); | |
UINT32 reserved = sizeof(NV_LIST_TERMINATOR); | |
// | |
// Get the available space in NV storage | |
availNVSpace = NvGetFreeBytes(); | |
if(counterNum < MIN_COUNTER_INDICES) | |
{ | |
// Some space has to be reserved for counter objects. | |
reserved += (MIN_COUNTER_INDICES - counterNum) * NV_INDEX_COUNTER_SIZE; | |
if(reserved > availNVSpace) | |
availNVSpace = 0; | |
else | |
availNVSpace -= reserved; | |
} | |
return availNVSpace / NV_EVICT_OBJECT_SIZE; | |
} | |
//*** NvCapGetCounterNumber() | |
// Get the number of defined NV Indexes that are counter indexes. | |
UINT32 | |
NvCapGetCounterNumber( | |
void | |
) | |
{ | |
NV_REF iter = NV_REF_INIT; | |
NV_REF currentAddr; | |
UINT32 num = 0; | |
// | |
while((currentAddr = NvNextIndex(NULL, &iter)) != 0) | |
{ | |
TPMA_NV attributes = NvReadNvIndexAttributes(currentAddr); | |
if(IsNvCounterIndex(attributes)) | |
num++; | |
} | |
return num; | |
} | |
//*** NvSetStartupAttributes() | |
// Local function to set the attributes of an Index at TPM Reset and TPM Restart. | |
static TPMA_NV | |
NvSetStartupAttributes( | |
TPMA_NV attributes, // IN: attributes to change | |
STARTUP_TYPE type // IN: start up type | |
) | |
{ | |
// Clear read lock | |
CLEAR_ATTRIBUTE(attributes, TPMA_NV, READLOCKED); | |
// Will change a non counter index to the unwritten state if: | |
// a) TPMA_NV_CLEAR_STCLEAR is SET | |
// b) orderly and TPM Reset | |
if(!IsNvCounterIndex(attributes)) | |
{ | |
if(IS_ATTRIBUTE(attributes, TPMA_NV, CLEAR_STCLEAR) | |
|| (IS_ATTRIBUTE(attributes, TPMA_NV, ORDERLY) | |
&& (type == SU_RESET))) | |
CLEAR_ATTRIBUTE(attributes, TPMA_NV, WRITTEN); | |
} | |
// Unlock any index that is not written or that does not have | |
// TPMA_NV_WRITEDEFINE SET. | |
if(!IS_ATTRIBUTE(attributes, TPMA_NV, WRITTEN) | |
|| !IS_ATTRIBUTE(attributes, TPMA_NV, WRITEDEFINE)) | |
CLEAR_ATTRIBUTE(attributes, TPMA_NV, WRITELOCKED); | |
return attributes; | |
} | |
//*** 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. | |
BOOL | |
NvEntityStartup( | |
STARTUP_TYPE type // IN: start up type | |
) | |
{ | |
NV_REF iter = NV_REF_INIT; | |
NV_RAM_REF ramIter = NV_RAM_REF_INIT; | |
NV_REF currentAddr; // offset points to the current entity | |
NV_RAM_REF currentRamAddr; | |
TPM_HANDLE nvHandle; | |
TPMA_NV attributes; | |
// | |
// Restore RAM index data | |
NvRead(s_indexOrderlyRam, NV_INDEX_RAM_DATA, sizeof(s_indexOrderlyRam)); | |
// Initialize the max NV counter value | |
NvSetMaxCount(NvGetMaxCount()); | |
// If recovering from state save, do nothing else | |
if(type == SU_RESUME) | |
return TRUE; | |
// Iterate all the NV Index to clear the locks | |
while((currentAddr = NvNextIndex(&nvHandle, &iter)) != 0) | |
{ | |
attributes = NvReadNvIndexAttributes(currentAddr); | |
// If this is an orderly index, defer processing until loop below | |
if(IS_ATTRIBUTE(attributes, TPMA_NV, ORDERLY)) | |
continue; | |
// Set the attributes appropriate for this startup type | |
attributes = NvSetStartupAttributes(attributes, type); | |
NvWriteNvIndexAttributes(currentAddr, attributes); | |
} | |
// Iterate all the orderly indexes to clear the locks and initialize counters | |
while((currentRamAddr = NvRamNext(&ramIter, NULL)) != 0) | |
{ | |
attributes = NvReadRamIndexAttributes(currentRamAddr); | |
attributes = NvSetStartupAttributes(attributes, type); | |
// update attributes in RAM | |
NvWriteRamIndexAttributes(currentRamAddr, attributes); | |
// Set the lower bits in an orderly counter to 1 for a non-orderly startup | |
if(IsNvCounterIndex(attributes) | |
&& (g_prevOrderlyState == SU_NONE_VALUE)) | |
{ | |
UINT64 counter; | |
// | |
// Read the counter value last saved to NV. | |
counter = BYTE_ARRAY_TO_UINT64(currentRamAddr + sizeof(NV_RAM_HEADER)); | |
// Set the lower bits of counter to 1's | |
counter |= MAX_ORDERLY_COUNT; | |
// Write back to RAM | |
// NOTE: Do not want to force a write to NV here. The counter value will | |
// stay in RAM until the next shutdown or rollover. | |
UINT64_TO_BYTE_ARRAY(counter, currentRamAddr + sizeof(NV_RAM_HEADER)); | |
} | |
} | |
return TRUE; | |
} | |
//*** NvCapGetCounterAvail() | |
// This function returns an estimate of the number of additional counter type | |
// NV indexes that can be defined. | |
UINT32 | |
NvCapGetCounterAvail( | |
void | |
) | |
{ | |
UINT32 availNVSpace; | |
UINT32 availRAMSpace; | |
UINT32 persistentNum = NvCapGetPersistentNumber(); | |
UINT32 reserved = sizeof(NV_LIST_TERMINATOR); | |
// | |
// Get the available space in NV storage | |
availNVSpace = NvGetFreeBytes(); | |
if(persistentNum < MIN_EVICT_OBJECTS) | |
{ | |
// Some space has to be reserved for evict object. Adjust availNVSpace. | |
reserved += (MIN_EVICT_OBJECTS - persistentNum) * NV_EVICT_OBJECT_SIZE; | |
if(reserved > availNVSpace) | |
availNVSpace = 0; | |
else | |
availNVSpace -= reserved; | |
} | |
// Compute the available space in RAM | |
availRAMSpace = (int)(RAM_ORDERLY_END - NvRamGetEnd()); | |
// Return the min of counter number in NV and in RAM | |
if(availNVSpace / NV_INDEX_COUNTER_SIZE | |
> availRAMSpace / NV_RAM_INDEX_COUNTER_SIZE) | |
return availRAMSpace / NV_RAM_INDEX_COUNTER_SIZE; | |
else | |
return availNVSpace / NV_INDEX_COUNTER_SIZE; | |
} | |
//*** 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. | |
NV_REF | |
NvFindHandle( | |
TPM_HANDLE handle | |
) | |
{ | |
NV_REF addr; | |
NV_REF iter = NV_REF_INIT; | |
TPM_HANDLE nextHandle; | |
// | |
while((addr = NvNext(&iter, &nextHandle)) != 0) | |
{ | |
if(nextHandle == handle) | |
break; | |
} | |
return addr; | |
} | |
//** NV Max Counter | |
//*** Introduction | |
// The TPM keeps track of the highest value of a deleted counter index. When an | |
// index is deleted, this value is updated if the deleted counter index is greater | |
// than the previous value. When a new index is created and first incremented, it | |
// will get a value that is at least one greater than any other index than any | |
// previously deleted index. This insures that it is not possible to roll back an | |
// index. | |
// | |
// The highest counter value is kept in NV in a special end-of-list marker. This | |
// marker is only updated when an index is deleted. Otherwise it just moves. | |
// | |
// When the TPM starts up, it searches NV for the end of list marker and initializes | |
// an in memory value (s_maxCounter). | |
//*** NvReadMaxCount() | |
// This function returns the max NV counter value. | |
// | |
UINT64 | |
NvReadMaxCount( | |
void | |
) | |
{ | |
return s_maxCounter; | |
} | |
//*** NvUpdateMaxCount() | |
// This function updates the max counter value to NV memory. This is just staging | |
// for the actual write that will occur when the NV index memory is modified. | |
// | |
void | |
NvUpdateMaxCount( | |
UINT64 count | |
) | |
{ | |
if(count > s_maxCounter) | |
s_maxCounter = count; | |
} | |
//*** NvSetMaxCount() | |
// This function is used at NV initialization time to set the initial value of | |
// the maximum counter. | |
void | |
NvSetMaxCount( | |
UINT64 value | |
) | |
{ | |
s_maxCounter = value; | |
} | |
//*** NvGetMaxCount() | |
// Function to get the NV max counter value from the end-of-list marker | |
UINT64 | |
NvGetMaxCount( | |
void | |
) | |
{ | |
NV_REF iter = NV_REF_INIT; | |
NV_REF currentAddr; | |
UINT64 maxCount; | |
// | |
// Find the end of list marker and initialize the NV Max Counter value. | |
while((currentAddr = NvNext(&iter, NULL )) != 0); | |
// 'iter' should be pointing at the end of list marker so read in the current | |
// value of the s_maxCounter. | |
NvRead(&maxCount, iter + sizeof(UINT32), sizeof(maxCount)); | |
return maxCount; | |
} |