/********************************************************************************/
/*										*/
/*		Dynamic space for user defined NV      				*/
/*			     Written by Ken Goldman				*/
/*		       IBM Thomas J. Watson Research Center			*/
/*										*/
/*  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 - 2023				*/
/*										*/
/********************************************************************************/

//** 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"
#include "Marshal.h"
#include "tpm_library_intern.h"				// libtpms added
#include "BackwardsCompatibilityObject.h"		// libtpms added

//** 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.
    MUST_BE(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
    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;
}

//*** 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;
}

// Convert an OBJECT into a buffer to store in NVRAM	// libtpms added begin
UINT32 NvObjectToBuffer(OBJECT* object, BYTE* buffer, UINT32 size)
{
    // always use ANY_OBJECT_Marshal if StateFormatLevel >=2 (v0.10)
    BOOL marshalAnyObject = g_RuntimeProfile.stateFormatLevel >= 2;

    pAssert(size >= MAX_MARSHALLED_OBJECT_SIZE);

    // RSA 3072 objects and older are stored as RSA3072_OBJECT
    switch(object->publicArea.type) {
    case TPM_ALG_RSA:
        if (object->publicArea.parameters.rsaDetail.keyBits > 3072)
            marshalAnyObject = true;
        break;
    case TPM_ALG_ECC:
        break;
    case TPM_ALG_KEYEDHASH:
        break;
    case TPM_ALG_SYMCIPHER:
        break;
    default:
        // must never happen
        TPMLIB_LogTPM2Error("%s : Unhandled object type: %d\n",
                            __func__, object->publicArea.type);
        FAIL(FATAL_ERROR_INTERNAL);
    }

    if (marshalAnyObject) {
        return ANY_OBJECT_Marshal(object, &buffer, (INT32*)&size, &g_RuntimeProfile);
    }
    return OBJECT_To_Buffer_As_RSA3072_OBJECT(object, buffer, size);
}

// Convert a buffer to an OBJECT; the size of the buffer must have
// exactly the size consumed by ANY_OBJECT_Unmarshal, if the OBJECT
// can be unmarshaled from the buffer.
static void NvObjectFromBuffer(OBJECT* object, BYTE* buf, UINT32 buf_size)
{
    TPM_RC rc;
    BYTE*  buffer = buf;
    INT32  size = buf_size;

    /* Try to unmarshal it as an ANY_OBJECT; if this works, an OBJECT will
     * be passed back, otherwise fall back to copying from memory directly
     * as an RSA3072_OBJECT and have it converted to current OBJECT.
     */
    rc = ANY_OBJECT_Unmarshal(object, &buffer, &size, false);
    if (!rc) {
        pAssert(size == 0);
    } else {
        /* It could not be unmarshalled, it must be a plain RSA3072_OBJECT */
        rc = RSA3072_OBJECT_Buffer_To_OBJECT(object, buf, buf_size);
        pAssert(rc == TPM_RC_SUCCESS);
    }
}							// libtpms added end

//*** 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
		  )
{
#if 0							// libtpms changed begin
    NvRead(object, (ref + sizeof(TPM_HANDLE)), sizeof(OBJECT));
#endif
    UINT32 entrysize;
    BYTE   buffer[MAX_MARSHALLED_OBJECT_SIZE];

    /* read size of object in NVRAM; this includes the NV_ENTRY_HEADER */
    NvRead(&entrysize, ref - sizeof(UINT32), sizeof(entrysize));
    entrysize -= sizeof(NV_ENTRY_HEADER);

    /* read the flat object into a buffer */
    pAssert(entrysize <= sizeof(buffer));
    NvRead(buffer, ref + sizeof(TPM_HANDLE), entrysize);

    NvObjectFromBuffer(object, buffer, entrysize);

    return;						// libtpms changed end
}

//*** 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
    int isDifferent = _plat__NvGetChangedStatus(entryAddr, size, data);
    if(isDifferent == NV_INVALID_LOCATION)
	{
	    // invalid request, we should be in failure mode by now.
	    return TPM_RC_FAILURE;
	}
    else if(isDifferent == NV_HAS_CHANGED)
	{
	    // Write the data if NV is available
	    if(g_NvStatus == TPM_RC_SUCCESS)
		{
		    NvWrite(entryAddr, size, data);
		}
	    return g_NvStatus;
	}
    else if(isDifferent == NV_IS_SAME)
	{
	    return TPM_RC_SUCCESS;
	}
    // the platform gave us an invalid response.
    FAIL_RC(FATAL_ERROR_PLATFORM);
}

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

//*** 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;
}

static TPM_RC						// libtpms added begin
NvWriteObject(OBJECT* object)
{
    UINT32 sizeOfObject;
    BYTE   buffer[MAX_MARSHALLED_OBJECT_SIZE];

    sizeOfObject = NvObjectToBuffer(object, buffer, sizeof(buffer));

    if(!NvTestSpace(sizeOfObject + sizeof(TPM_HANDLE), FALSE, FALSE))
	return TPM_RC_NV_SPACE;

    // Now put this in NV
    return NvAdd(sizeOfObject, sizeOfObject, object->evictHandle, buffer);
}							// libtpms added end

//*** 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;
#if 0							// libtpms added
    //
    // 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;
#endif							// libtpms added

    // Set evict attribute and handle
    object->attributes.evict = SET;
    object->evictHandle      = evictHandle;

    // Now put this in NV
    result = NvWriteObject(object);			// libtpms changed

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

//*** NvCapGetOnePersistent()
// This function returns whether a given persistent handle exists.
//
// 'Handle' must be in valid persistent object handle range.
BOOL NvCapGetOnePersistent(TPMI_DH_OBJECT handle)  // IN: handle
{
    NV_REF     iter = NV_REF_INIT;
    NV_REF     currentAddr;
    TPM_HANDLE entityHandle;

    pAssert(HandleGetType(handle) == TPM_HT_PERSISTENT);

    while((currentAddr = NvNextEvict(&entityHandle, &iter)) != 0)
	{
	    if(entityHandle == handle)
		{
		    return TRUE;
		}
	}
    return FALSE;
}

//*** 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;
}

//*** NvCapGetOneIndex()
// This function whether an NV index exists.
BOOL NvCapGetOneIndex(TPMI_DH_OBJECT handle)  // IN: handle
{
    NV_REF     iter = NV_REF_INIT;
    NV_REF     currentAddr;
    TPM_HANDLE nvHandle;

    pAssert(HandleGetType(handle) == TPM_HT_NV_INDEX);

    while((currentAddr = NvNextIndex(&nvHandle, &iter)) != 0)
	{
	    if(nvHandle == handle)
		{
		    return TRUE;
		}
	}
    return FALSE;
}

//*** 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 = (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;
}

//*** 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;
}
