/********************************************************************************/
/*										*/
/*			     				*/
/*			     Written by Ken Goldman				*/
/*		       IBM Thomas J. Watson Research Center			*/
/*            $Id: CommandCodeAttributes.c 489 2016-02-02 19:58:19Z 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, 2012-2015				*/
/*										*/
/********************************************************************************/

/* rev 122 */

// 9.4	CommandCodeAttributes.c
// 9.4.1	Introduction
// This file contains the functions for testing various command properties.
// 9.4.2	Includes and Defines

#include    "Tpm.h"
#include    "InternalRoutines.h"

#define V_BIT   (1 << 29)

typedef UINT16          ATTRIBUTE_TYPE;

// The following file is produced from the command tables in part 3 of the specification. It defines
// the attributes for each of the commands.

// NOTE: This file is currently produced by an automated process. Files produced from Part 2 or Part
// 3 tables through automated processes are not included in the specification so that their is no
// ambiguity about the table containing the information being the normative definition.

#include    "CommandAttributeData.c"

// 9.4.3	Command Attribute Functions

// 9.4.3.1	NextImplementedIndex()

// This function is used when the lists are not compressed. In a compressed list, only the
// implemented commands are present. So, a search might find a value but that value may not be
// implemented. This function checks to see if the input commandIndex points to an impemented
// command and, if not, it searches upwards until it finds one. When the list is compressed, this
// function gets defined as a no-op.

#ifndef COMPRESSED_LISTS
static COMMAND_INDEX
NextImplementedIndex(
		     COMMAND_INDEX       commandIndex
		     )
{
    for(;commandIndex < COMMAND_COUNT; commandIndex++)
	{
	    if(s_commandAttributes[commandIndex] & IS_IMPLEMENTED)
		return commandIndex;
	}
    return UNIMPLEMENTED_COMMAND_INDEX;
}
#else
#define NextImplementedIndex(x) (x)
#endif

// 9.4.3.2	GetClosestCommandIndex()

// This function returns the command index for the command with a value that is equal to or greater
// than the input value

COMMAND_INDEX
GetClosestCommandIndex(
		       TPM_CC           commandCode    // IN: the command code to start at
		       )
{
    BOOL                vendor = (commandCode & V_BIT) != 0;
    COMMAND_INDEX       searchIndex = (COMMAND_INDEX)commandCode;
    
    //    int     s_ccAttrSize = sizeof(s_ccAttr)/sizeof(TPMA_CC);
    //    int     s_commandAttributesSize
    //                  = sizeof(s_commandAttributes)/sizeof(COMMAND_ATTRIBUTES);
    //    pAssert(      s_ccAttrSize
    //              == s_commandAttributesSize && s_ccAttrSize == COMMAND_COUNT);
    // if any bit other than the V-bit or a valid command index value is set, then
    // indicate that the command is not implemented.
    if((commandCode & ~V_BIT) != searchIndex)
	return UNIMPLEMENTED_COMMAND_INDEX;
    
    // if there is at least one vendor command, the last entry in the array will
    // have the v bit set. If the input commandCode is larger than the last
    // vendor-command, then it is out of range.
    if(vendor)
	{
#if VENDOR_COMMAND_ARRAY_SIZE > 0
	    COMMAND_INDEX       commandIndex;
	    COMMAND_INDEX       min;
	    COMMAND_INDEX       max;
	    int                 diff;
#if LIBRARY_COMMAND_ARRAY_SIZE == COMMAND_COUNT
#error "Constants are not consistent."
#endif
	    // Check to see if the value is equal to or below the minumum
	    // entry.
	    // Note: Put this check first so that the typical case of only one vendor-
	    // specific command doesn't waste any more time.
	    if(s_ccAttr[LIBRARY_COMMAND_ARRAY_SIZE].commandIndex >= searchIndex)
		{
		    // the vendor array is always assumed to be packed so there is
		    // no need to check to see if the command is implemented
		    return LIBRARY_COMMAND_ARRAY_SIZE;
		}
	    // See if this is out of range on the top
	    if(s_ccAttr[COMMAND_COUNT - 1].commandIndex < searchIndex)
		{
		    return UNIMPLEMENTED_COMMAND_INDEX;
		}
	    commandIndex = UNIMPLEMENTED_COMMAND_INDEX; // Needs initialization to keep
	    // compiler happy
	    min = LIBRARY_COMMAND_ARRAY_SIZE;       // first vendor command
	    max = COMMAND_COUNT - 1;                // last vendor command
	    diff = 1;                               // needs initialization to keep
	    // compiler happy
	    while(min <= max)
		{
		    commandIndex = (min+max+1) / 2;
		    diff = s_ccAttr[commandIndex].commandIndex - searchIndex;
		    if(diff == 0)
			return commandIndex;
		    if(diff > 0)
			max = commandIndex - 1;
		    else
			min = commandIndex + 1;
		}
	    // didn't find and exact match. commandIndex will be pointing at the last
	    // item tested. If diff is positive, then the last item tested was
	    // larger index of the command code so it is the smallest value
	    // larger than the requested value.
	    if(diff > 0)
		return commandIndex;
	    
	    // if diff is negative, then the value tested was smaller than
	    // the commandCode index and the next higher value is the correct one.
	    // Note: this will necessarily be in range because of the earlier check
	    // that the index was within range.
	    return commandIndex + 1;
	    
#else
	    // If there are no vendor commands so anything with the vendor bit set is out
	    // of range
	    return UNIMPLEMENTED_COMMAND_INDEX;
#endif
	}
    // Get here if the V-Bit was not set in 'commandCode'
    
    if(s_ccAttr[LIBRARY_COMMAND_ARRAY_SIZE - 1].commandIndex < searchIndex)
	{
	    // requested index is out of the range to the top
#if VENDOR_COMMAND_ARRAY_SIZE > 0
	    // If there are vendor commands, then the first vendor command
	    // is the next value greater than the commandCode
	    // NOTE: we got here if the starting index did not have the V bit but we
	    // reached the end of the array of library commands (non-vendor). Since
	    // there is at least one vendor command, and vendor commands are always
	    // in a compressed list that starts after the library list, the next
	    // index value contains a valid vendor command.
	    return LIBRARY_COMMAND_ARRAY_SIZE;
#else
	    // if there are no vendor commands, then this is out of range
	    return UNIMPLEMENTED_COMMAND_INDEX;
#endif
	}
    // If the request is lower than any value in the array, then return
    // the lowest value (needs to be an index for an implemented command
    if(s_ccAttr[0].commandIndex >= searchIndex)
	{
	    return NextImplementedIndex(0);
	}
    else
	{
#ifdef COMPRESSED_LISTS
	    COMMAND_INDEX       commandIndex = UNIMPLEMENTED_COMMAND_INDEX;
	    COMMAND_INDEX       min = 0;
	    COMMAND_INDEX       max = LIBRARY_COMMAND_ARRAY_SIZE - 1;
	    int                 diff = 1;
#if LIBRARY_COMMAND_ARRAY_SIZE == 0
#error  "Something is terribly wrong"
#endif
	    // The s_ccAttr array contains an extra entry at the end (a zero value).
	    // Don't count this as an array entry. This means that max should start
	    // out pointing to the last valid entry in the array which is - 2
	    //            int t = sizeof(s_ccAttr)/sizeof(TPMA_CC);
	    //            int t1 = LIBRARY_COMMAND_ARRAY_SIZE;
	    //            int t2 = VENDOR_COMMAND_ARRAY_SIZE;
	    pAssert(max == (sizeof(s_ccAttr)/sizeof(TPMA_CC)
			    - VENDOR_COMMAND_ARRAY_SIZE - 2));
	    while(min <= max)
		{
		    commandIndex = (min+max+1) / 2;
		    diff = s_ccAttr[commandIndex].commandIndex - searchIndex;
		    if(diff == 0)
			return commandIndex;
		    if(diff > 0)
			max = commandIndex - 1;
		    else
			min = commandIndex + 1;
		}
	    // didn't find and exact match. commandIndex will be pointing at the
	    // last item tested. If diff is positive, then the last item tested was
	    // larger index of the command code so it is the smallest value
	    // larger than the requested value.
	    if(diff > 0)
		return commandIndex;
	    
	    // if diff is negative, then the value tested was smaller than
	    // the commandCode index and the next higher value is the correct one.
	    // Note: this will necessarily be in range because of the earlier check
	    // that the index was within range.
	    return commandIndex + 1;
#else
	    // The list is not compressed so offset into the array by the command
	    // code value of the first entry in the list. Then go find the first
	    // implemented command.
	    return NextImplementedIndex(searchIndex
					- (COMMAND_INDEX)s_ccAttr[0].commandIndex);
#endif
	}
}

// 9.4.3.3	CommandCodeToComandIndex()
// This function returns the index in the various attributes arrays of the command.
// Return Value	Meaning
// UNIMPLEMNED_COMMAND_INDEX	command is not implemented
// other	index of the command

COMMAND_INDEX
CommandCodeToCommandIndex(
			  TPM_CC           commandCode    // IN: the command code to look up
			  )
{
    COMMAND_INDEX       searchIndex = (COMMAND_INDEX)commandCode;
    BOOL                vendor = (commandCode & V_BIT) != 0;
    COMMAND_INDEX       commandIndex;
#if !defined COMPRESSED_LISTS
    if(!vendor)
	{
	    commandIndex = searchIndex - (COMMAND_INDEX)s_ccAttr[0].commandIndex;
	    // Check for out of range or unimplemented.
	    // Note, since a COMMAND_INDEX is unsigned, if searchIndex is smaller than
	    // the lowest value of command, it will become a 'negative' number making
	    // it look like a large unsigned numbere, this will cause it to fail
	    // the unsigned check below.
	    if(     commandIndex >= LIBRARY_COMMAND_ARRAY_SIZE
		    ||  (s_commandAttributes[commandIndex] & IS_IMPLEMENTED) == 0)
		return UNIMPLEMENTED_COMMAND_INDEX;
	    return commandIndex;
	}
    
#endif
    // Need this code for any vendor code lookup or for compressed lists
    commandIndex = GetClosestCommandIndex(commandCode);
    
    // Look at the returned value from get closest. If it isn't the one that was
    // requested, then the command is not implemented.
    if(commandIndex != UNIMPLEMENTED_COMMAND_INDEX)
	{
	    if(     s_ccAttr[commandIndex].commandIndex != searchIndex
		    ||  ((s_ccAttr[commandIndex].V == SET) && !vendor)
		    ||  ((s_ccAttr[commandIndex].V == CLEAR) && vendor)
		    )
		commandIndex = UNIMPLEMENTED_COMMAND_INDEX;
	}
    return commandIndex;
}

// 9.4.3.4	GetNextCommandIndex()
// This function returns the index of the next implemented command.
// Return Value	Meaning
// UNIMPLEMENTED_COMMAND_INDEX	no more implemented commands
// other	the index of the next implmented command

COMMAND_INDEX
GetNextCommandIndex(
		    COMMAND_INDEX    commandIndex   // IN: the starting index
		    )
{
    while(++commandIndex < COMMAND_COUNT)
	{
#if !defined COMPRESSED_LISTS
	    if(s_commandAttributes[commandIndex] & IS_IMPLEMENTED)
#endif
		return commandIndex;
	}
    return UNIMPLEMENTED_COMMAND_INDEX;
}

// 9.4.3.5	GetCommandCode()
// This function returns the commandCode associated with the command index

#ifndef USE_COMMAND_ATTRIBUTE_MACROS //%
TPM_CC
GetCommandCode(
	       COMMAND_INDEX    commandIndex   // IN: the command index
	       )
{
    TPM_CC              commandCode = s_ccAttr[commandIndex].commandIndex;
    if(s_ccAttr[commandIndex].V)
	commandCode += V_BIT;
    return commandCode;
}
#else       //%
//% #define GetCommandCode(commandIndex)
//%    ((TPM_CC)(s_ccAttr[commandIndex]commandIndex)
//%        + ((s_ccAttr[commandIndex].V) ? V_BIT : 0))
#endif  //%

// 9.4.3.6	CommandAuthRole()
// This function returns the authorization role required of a handle.
// Return Value	Meaning
// AUTH_NONE	no authorization is required
// AUTH_USER	user role authorization is required
// AUTH_ADMIN	admin role authorization is required
// AUTH_DUP	duplication role authorization is required

AUTH_ROLE
CommandAuthRole(
		COMMAND_INDEX    commandIndex,  // IN: command index
		UINT32           handleIndex    // IN: handle index (zero based)
		)
{
    if(0 == handleIndex )
	{
	    // Any auth role set?
	    COMMAND_ATTRIBUTES  properties = s_commandAttributes[commandIndex];
	    
	    if(properties & HANDLE_1_USER)
		return AUTH_USER;
	    if(properties & HANDLE_1_ADMIN)
		return AUTH_ADMIN;
	    if(properties & HANDLE_1_DUP)
		return AUTH_DUP;
	}
    else if (1 == handleIndex)
	{
	    if(s_commandAttributes[commandIndex] & HANDLE_2_USER)
		return AUTH_USER;
	}
    return AUTH_NONE;
}

// 9.4.3.7	EncryptSize()
// This function returns the size of the decrypt size field. This function returns 0 if encryption is not allowed
// Return Value	Meaning
// 0	encryption not allowed
// 2	size field is two bytes
// 4	size field is four bytes

#ifndef USE_COMMAND_ATTRIBUTE_MACROS //%
int
EncryptSize(
	    COMMAND_INDEX    commandIndex   // IN: command index
	    )
{
    COMMAND_ATTRIBUTES  ca = s_commandAttributes[commandIndex];
    if(ca & ENCRYPT_2)
	return 2;
    if(ca & ENCRYPT_4)
	return 4;
    return 0;
}
#else   //%
	//% #define EncrptSize(commandIndex)
//%    ((s_commandAttriutes[commandIndex] & ENCRYPT_2) ? 2 : 0)
#endif     //%

// 9.4.3.8	DecryptSize()
// This function returns the size of the decrypt size field. This function returns 0 if decryption is not allowed
// Return Value	Meaning
// 0	encryption not allowed
// 2	size field is two bytes
// 4	size field is four bytes

#ifndef USE_COMMAND_ATTRIBUTE_MACROS   //%
int
DecryptSize(
	    COMMAND_INDEX    commandIndex   // IN: command index
	    )
{
    COMMAND_ATTRIBUTES      ca = s_commandAttributes[commandIndex];
    
    if(ca & DECRYPT_2)
	return 2;
    if(ca & DECRYPT_4)
	return 4;
    return 0;
}
#else   //%
	//%#define DecryptSize(commandIndex)
//%    ((s_commandAttriutes[commandIndex] & DECRYPT_2) ? 2 : 0)
#endif   //%

// 9.4.3.9	IsSessionAllowed()
// This function indicates if the command is allowed to have sessions.
// This function must not be called if the command is not known to be implemented.
// Return Value	Meaning
// TRUE	session is allowed with this command
// FALSE	session is not allowed with this command

#ifndef USE_COMMAND_ATTRIBUTE_MACROS   //%
BOOL
IsSessionAllowed(
		 COMMAND_INDEX    commandIndex   // IN: the command to be checked
		 )
{
    return ((s_commandAttributes[commandIndex] & NO_SESSIONS) == 0);
}
#else   //%
	//% #define IsSessionAllowed(commandIndex)
//%    ((s_commAttributes[commandIndex] & NO_SESSIONS) == 0)
#endif   //%

// 9.4.3.10	IsHandleInResponse()

#ifndef USE_COMMAND_ATTRIBUTE_MACROS   //%
BOOL
IsHandleInResponse(
		   COMMAND_INDEX    commandIndex
		   )
{
    return ((s_commandAttributes[commandIndex] & R_HANDLE) != 0);
}
#else   //%
	//% #define IsHandleInResponse(commandIndex)
//%    ((s_commandAttributes[commandIndex] & R_HANDLE) != 0)
#endif  //%

// 9.4.3.11	IsWriteOperation()
// Checks to see if an operation will write to NV memory

BOOL
IsWriteOperation(
		 COMMAND_INDEX    commandIndex   // IN: Command to check
		 )
{
#ifdef  WRITE_LOCK
    return ((s_commandAttributes[commandIndex] & WRITE_LOCK) != 0);
#else
    if(!s_ccAttr[commandIndex].V)
	{
	    switch (s_ccAttr[commandIndex].commandIndex)
		{
		  case TPM_CC_NV_Write:
		  case TPM_CC_NV_Increment:
		  case TPM_CC_NV_SetBits:
		  case TPM_CC_NV_Extend:
		    // Nv write lock counts as a write operation for authorization purposes.
		    // We check to see if the NV is write locked before we do the
		    // authorization. If it is locked, we fail the command early.
		  case TPM_CC_NV_WriteLock:
		    return TRUE;
		  default:
		    break;
		}
	}
    return FALSE;
#endif
}

// 9.4.3.12	IsReadOperation()
// Checks to see if an operation will write to NV memory

BOOL
IsReadOperation(
		COMMAND_INDEX    commandIndex   // IN: Command to check
		)
{
#ifdef  READ_LOCK
    return ((s_commandAttributes[commandIndex] & READ_LOCK) != 0);
#else
    
    if(!s_ccAttr[commandIndex].V)
	{
	    switch (s_ccAttr[commandIndex].commandIndex)
		{
		  case TPM_CC_NV_Read:
		  case TPM_CC_PolicyNV:
		  case TPM_CC_NV_Certify:
		    // Nv read lock counts as a read operation for authorization purposes.
		    // We check to see if the NV is read locked before we do the
		    // authorization. If it is locked, we fail the command early.
		  case TPM_CC_NV_ReadLock:
		    return TRUE;
		  default:
		    break;
		}
	}
    return FALSE;
#endif
}

// 9.4.3.13	CommandCapGetCCList()
// This function returns a list of implemented commands and command attributes starting from the command in commandCode.
// Return Value	Meaning
// YES	more command attributes are available
// NO	no more command attributes are available

TPMI_YES_NO
CommandCapGetCCList(
		    TPM_CC           commandCode,   // IN: start command code
		    UINT32           count,         // IN: maximum count for number of entries in
		    //     'commandList'
		    TPML_CCA        *commandList    // OUT: list of TPMA_CC
		    )
{
    TPMI_YES_NO      more = NO;
    COMMAND_INDEX    commandIndex;
    
    // initialize output handle list count
    commandList->count = 0;
    
    for(commandIndex = GetClosestCommandIndex(commandCode);
	commandIndex != UNIMPLEMENTED_COMMAND_INDEX;
	commandIndex = GetNextCommandIndex(commandIndex))
	{
#ifndef COMPRESSED_LISTS
	    // this check isn't needed for compressed lists.
	    if(!(s_commandAttributes[commandIndex] & IS_IMPLEMENTED))
		continue;
#endif
	    if(commandList->count < count)
		{
		    // If the list is not full, add the attributes for this command.
		    commandList->commandAttributes[commandList->count]
			= s_ccAttr[commandIndex];
		    commandList->count++;
		}
	    else
		{
		    // If the list is full but there are more commands to report,
		    // indicate this and return.
		    more = YES;
		    break;
		}
	}
    return more;
}

// 9.4.3.14	IsVendorCommand()
// Function indicates if a command index references a vendor command.
// Return Value	Meaning
// TRUE	command is a vendor command
// FALSE	command is not a vendor command

#ifndef USE_COMMAND_ATTRIBUTE_MACROS   //%
BOOL
IsVendorCommand(
		COMMAND_INDEX    commandIndex   // IN: command index to check
		)
{
    return (s_ccAttr[commandIndex].V == SET);
}
#else   //%
	//% #define IsVendorCommand(commandIndex)       ((s_ccAttr[commandIndex].V == SET)
#endif   //%
