summaryrefslogtreecommitdiffstats
path: root/src/tpm2/NVDynamic.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/tpm2/NVDynamic.c')
-rw-r--r--src/tpm2/NVDynamic.c1679
1 files changed, 1679 insertions, 0 deletions
diff --git a/src/tpm2/NVDynamic.c b/src/tpm2/NVDynamic.c
new file mode 100644
index 0000000..c7c6a3b
--- /dev/null
+++ b/src/tpm2/NVDynamic.c
@@ -0,0 +1,1679 @@
+/********************************************************************************/
+/* */
+/* Dynamic space for user defined NV */
+/* Written by Ken Goldman */
+/* IBM Thomas J. Watson Research Center */
+/* $Id: NVDynamic.c 1658 2021-01-22 23:14:01Z kgoldman $ */
+/* */
+/* Licenses and Notices */
+/* */
+/* 1. Copyright Licenses: */
+/* */
+/* - Trusted Computing Group (TCG) grants to the user of the source code in */
+/* this specification (the "Source Code") a worldwide, irrevocable, */
+/* nonexclusive, royalty free, copyright license to reproduce, create */
+/* derivative works, distribute, display and perform the Source Code and */
+/* derivative works thereof, and to grant others the rights granted herein. */
+/* */
+/* - The TCG grants to the user of the other parts of the specification */
+/* (other than the Source Code) the rights to reproduce, distribute, */
+/* display, and perform the specification solely for the purpose of */
+/* developing products based on such documents. */
+/* */
+/* 2. Source Code Distribution Conditions: */
+/* */
+/* - Redistributions of Source Code must retain the above copyright licenses, */
+/* this list of conditions and the following disclaimers. */
+/* */
+/* - Redistributions in binary form must reproduce the above copyright */
+/* licenses, this list of conditions and the following disclaimers in the */
+/* documentation and/or other materials provided with the distribution. */
+/* */
+/* 3. Disclaimers: */
+/* */
+/* - THE COPYRIGHT LICENSES SET FORTH ABOVE DO NOT REPRESENT ANY FORM OF */
+/* LICENSE OR WAIVER, EXPRESS OR IMPLIED, BY ESTOPPEL OR OTHERWISE, WITH */
+/* RESPECT TO PATENT RIGHTS HELD BY TCG MEMBERS (OR OTHER THIRD PARTIES) */
+/* THAT MAY BE NECESSARY TO IMPLEMENT THIS SPECIFICATION OR OTHERWISE. */
+/* Contact TCG Administration (admin@trustedcomputinggroup.org) for */
+/* information on specification licensing rights available through TCG */
+/* membership agreements. */
+/* */
+/* - THIS SPECIFICATION IS PROVIDED "AS IS" WITH NO EXPRESS OR IMPLIED */
+/* WARRANTIES WHATSOEVER, INCLUDING ANY WARRANTY OF MERCHANTABILITY OR */
+/* FITNESS FOR A PARTICULAR PURPOSE, ACCURACY, COMPLETENESS, OR */
+/* NONINFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS, OR ANY WARRANTY */
+/* OTHERWISE ARISING OUT OF ANY PROPOSAL, SPECIFICATION OR SAMPLE. */
+/* */
+/* - Without limitation, TCG and its members and licensors disclaim all */
+/* liability, including liability for infringement of any proprietary */
+/* rights, relating to use of information in this specification and to the */
+/* implementation of this specification, and TCG disclaims all liability for */
+/* cost of procurement of substitute goods or services, lost profits, loss */
+/* of use, loss of data or any incidental, consequential, direct, indirect, */
+/* or special damages, whether under contract, tort, warranty or otherwise, */
+/* arising in any way out of use or reliance upon this specification or any */
+/* information herein. */
+/* */
+/* (c) Copyright IBM Corp. and others, 2016 - 2021 */
+/* */
+/********************************************************************************/
+
+/* 8.4 NVDynamic.c */
+/* 8.4.2 Includes, Defines and Data Definitions */
+#define NV_C
+#include "Tpm.h"
+
+/* 8.4.3 Local Functions */
+/* 8.4.3.1 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 is 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;
+}
+/* 8.4.3.2 NvNextByType() */
+/* This function returns a reference to the next NV entry of the desired type */
+/* Return Values Meaning */
+/* 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;
+}
+/* 8.4.3.3 NvNextIndex() */
+/* This function returns the reference to the next NV Index entry. A value of 0 indicates the end
+ of the list. */
+/* Return Values Meaning */
+/* 0 end of list */
+/* != 0 the next */
+#define NvNextIndex(handle, iter) \
+ NvNextByType(handle, iter, TPM_HT_NV_INDEX)
+/* 8.4.3.4 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)
+/* 8.4.3.5 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;
+}
+/* 8.4.3.6 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();
+}
+/* 8.4.3.7 NvTestSpace() */
+/* This function will test if there is enough space to add a new entity. */
+/* Return Values 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
+ 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));
+}
+/* 8.4.3.8 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);
+}
+/* 8.4.3.9 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;
+}
+/* 8.4.3.10 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;
+}
+/* 8.4.4 RAM-based NV Index Data Access Functions */
+/* 8.4.4.1 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()) */
+/* 8.4.4.2 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
+ memcpy(&header, currentAddr, sizeof(NV_RAM_HEADER)); // libtpms: do not use MemoryCopy to avoid gcc warning
+ // 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;
+}
+/* 8.4.4.2 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;
+}
+/* 8.4.4.3 NvRamTestSpaceIndex() */
+/* This function indicates if there is enough RAM space to add a data for a new NV Index. */
+/* Return Values Meaning */
+/* TRUE space available */
+/* FALSE 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;
+}
+/* 8.4.4.4 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;
+}
+/* 8.4.4.5 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);
+}
+/* 8.4.4.6 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;
+}
+/* 8.4.4.7 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;
+}
+/* 8.4.4.9 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;
+}
+/* 8.4.4.9 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;
+}
+/* 8.4.4.10 NvFindEvict() */
+/* This function will return the NV offset of an evict object */
+/* Return Values Meaning */
+/* 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;
+}
+/* 8.4.4.11 NvIndexIsDefined() */
+/* See if an index is already defined */
+BOOL
+NvIndexIsDefined(
+ TPM_HANDLE nvHandle // IN: Index to look for
+ )
+{
+ return (NvFindHandle(nvHandle) != 0);
+}
+/* 8.4.4.12 NvConditionallyWrite() */
+/* Function to check if the data to be written has changed and write it if it has */
+/* Error Returns Meaning */
+/* 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;
+}
+/* 8.4.4.13 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;
+}
+/* 8.4.4.14 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;
+}
+/* 8.4.4.15 NvWriteNvIndexAttributes() */
+/* This function is used to write just the attributes of an index to NV. */
+/* Error Returns Meaning */
+/* 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);
+}
+/* 8.4.4.16 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;
+}
+/* 8.4.5 Externally Accessible Functions */
+/* 8.4.5.1 NvIsPlatformPersistentHandle() */
+/* This function indicates if a handle references a persistent object in the range belonging to the
+ platform. */
+/* Return Values Meaning */
+/* TRUE handle references a platform persistent object */
+/* FALSE handle does not reference platform persistent object */
+BOOL
+NvIsPlatformPersistentHandle(
+ TPM_HANDLE handle // IN: handle
+ )
+{
+ return (handle >= PLATFORM_PERSISTENT && handle <= PERSISTENT_LAST);
+}
+/* 8.4.5.2 NvIsOwnerPersistentHandle() */
+/* This function indicates if a handle references a persistent object in the range belonging to the
+ owner. */
+/* Return Values 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);
+}
+/* 8.4.5.3 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 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;
+}
+/* 8.4.5.4 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
+ )
+{
+ 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;
+}
+/* 8.4.5.5 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;
+}
+/* 8.4.5.6 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: size of NV data
+ 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;
+}
+/* 8.4.5.7 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
+}
+/* 8.4.5.7 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));
+}
+/* 8.4.5.8 NvWriteIndexAttributes() */
+/* This function is used to write just the attributes of an index. */
+/* Error Returns Meaning */
+/* 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;
+}
+/* 8.4.5.9 NvWriteIndexAuth() */
+/* This function is used to write the authValue of an index. It is used by TPM2_NV_ChangeAuth() */
+/* Error Returns Meaning */
+/* 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;
+ }
+}
+/* 8.4.5.10 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;
+}
+/* 8.4.5.11 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. */
+/* Error Returns Meaning */
+/* 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;
+}
+/* 8.4.5.12 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);
+}
+/* 8.4.5.13 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;
+}
+/* 8.4.5.14 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);
+}
+/* 8.4.5.15 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
+ 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;
+}
+/* 8.4.5.16 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
+ )
+{
+ 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;
+}
+/* 8.4.5.17 NvDeleteIndex() */
+/* This function is used to delete an NV Index. */
+/* Error Returns Meaning */
+/* 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;
+}
+/* 8.4.5.18 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;
+}
+/* 8.4.5.19 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. */
+/* Error Returns Meaning */
+/* 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;
+}
+/* 8.4.5.20 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(). */
+/* Error Returns Meaning */
+/* 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;
+}
+/* 8.4.5.21 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;
+}
+/* 8.4.5.22 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 Values 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 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;
+}
+/* 8.4.5.23 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 Values 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: 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;
+}
+/* 8.4.5.24 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;
+}
+/* 8.4.5.25 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;
+}
+/* 8.4.5.26 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;
+}
+/* 8.4.5.27 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;
+}
+/* 8.4.5.28 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;
+}
+/* 8.4.5.29 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;
+}
+/* 8.4.5.30 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 = (RAM_ORDERLY_END - NvRamGetEnd()); /* kgold - removed cast */
+ // 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;
+}
+/* 8.4.5.31 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;
+}
+/* 8.4.6 NV Max Counter */
+/* 8.4.6.1 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 ensures 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). */
+/* 8.4.6.2 NvReadMaxCount() */
+/* This function returns the max NV counter value. */
+UINT64
+NvReadMaxCount(
+ void
+ )
+{
+ return s_maxCounter;
+}
+/* 8.4.6.3 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;
+}
+/* 8.4.6.4 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;
+}
+/* 8.4.6.5 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;
+}