// This file was extracted from the TCG Published
// Trusted Platform Module Library
// Part 4: Supporting Routines
// Family "2.0"
// Level 00 Revision 01.16
// October 30, 2014
#include "InternalRoutines.h"
#include "ExecCommand_fp.h"
#include "HandleProcess_fp.h"
#include "SessionProcess_fp.h"
#include "CommandDispatcher_fp.h"
//
// Uncomment this next #include if doing static command/response buffer sizing
//
// #include "CommandResponseSizes_fp.h"
//
//
// ExecuteCommand()
//
// The function performs the following steps.
// a) Parses the command header from input buffer.
// b) Calls ParseHandleBuffer() to parse the handle area of the command.
// c) Validates that each of the handles references a loaded entity.
//
// d) Calls ParseSessionBuffer() () to:
// 1) unmarshal and parse the session area;
// 2) check the authorizations; and
// 3) when necessary, decrypt a parameter.
// e) Calls CommandDispatcher() to:
// 1) unmarshal the command parameters from the command buffer;
// 2) call the routine that performs the command actions; and
// 3) marshal the responses into the response buffer.
// f) If any error occurs in any of the steps above create the error response and return.
// g) Calls BuildResponseSession() to:
// 1) when necessary, encrypt a parameter
// 2) build the response authorization sessions
// 3) update the audit sessions and nonces
// h) Assembles handle, parameter and session buffers for response and return.
//
LIB_EXPORT void
ExecuteCommand(
unsigned int requestSize, // IN: command buffer size
unsigned char *request, // IN: command buffer
unsigned int *responseSize, // OUT: response buffer size
unsigned char **response // OUT: response buffer
)
{
// Command local variables
TPM_ST tag; // these first three variables are the
UINT32 commandSize;
TPM_CC commandCode = 0;
BYTE *parmBufferStart; // pointer to the first byte of an
// optional parameter buffer
UINT32 parmBufferSize = 0;// number of bytes in parameter area
UINT32 handleNum = 0; // number of handles unmarshaled into
// the handles array
TPM_HANDLE handles[MAX_HANDLE_NUM];// array to hold handles in the
// command. Only handles in the handle
// area are stored here, not handles
// passed as parameters.
// Response local variables
TPM_RC result; // return code for the command
TPM_ST resTag; // tag for the response
UINT32 resHandleSize = 0; // size of the handle area in the
// response. This is needed so that the
// handle area can be skipped when
// generating the rpHash.
UINT32 resParmSize = 0; // the size of the response parameters
// These values go in the rpHash.
UINT32 resAuthSize = 0; // size of authorization area in the
//
// response
INT32 size; // remaining data to be unmarshaled
// or remaining space in the marshaling
// buffer
BYTE *buffer; // pointer into the buffer being used
// for marshaling or unmarshaling
INT32 bufferSize; // size of buffer being used for
// marshaling or unmarshaling
UINT32 i; // local temp
// This next function call is used in development to size the command and response
// buffers. The values printed are the sizes of the internal structures and
// not the sizes of the canonical forms of the command response structures. Also,
// the sizes do not include the tag, commandCode, requestSize, or the authorization
// fields.
//CommandResponseSizes();
// Set flags for NV access state. This should happen before any other
// operation that may require a NV write. Note, that this needs to be done
// even when in failure mode. Otherwise, g_updateNV would stay SET while in
// Failure mode and the NB would be written on each call.
g_updateNV = FALSE;
g_clearOrderly = FALSE;
// As of Sept 25, 2013, the failure mode handling has been incorporated in the
// reference code. This implementation requires that the system support
// setjmp/longjmp. This code is put here because of the complexity being
// added to the platform and simulator code to deal with all the variations
// of errors.
if(g_inFailureMode)
{
// Do failure mode processing
TpmFailureMode (requestSize, request, responseSize, response);
return;
}
#ifndef EMBEDDED_MODE
if(setjmp(g_jumpBuffer) != 0)
{
// Get here if we got a longjump putting us into failure mode
g_inFailureMode = TRUE;
result = TPM_RC_FAILURE;
goto Fail;
}
#endif // EMBEDDED_MODE ^^^ not defined
// Assume that everything is going to work.
result = TPM_RC_SUCCESS;
// Query platform to get the NV state. The result state is saved internally
// and will be reported by NvIsAvailable(). The reference code requires that
// accessibility of NV does not change during the execution of a command.
// Specifically, if NV is available when the command execution starts and then
// is not available later when it is necessary to write to NV, then the TPM
// will go into failure mode.
NvCheckState();
// Due to the limitations of the simulation, TPM clock must be explicitly
// synchronized with the system clock whenever a command is received.
// This function call is not necessary in a hardware TPM. However, taking
// a snapshot of the hardware timer at the beginning of the command allows
// the time value to be consistent for the duration of the command execution.
TimeUpdateToCurrent();
// Any command through this function will unceremoniously end the
// _TPM_Hash_Data/_TPM_Hash_End sequence.
if(g_DRTMHandle != TPM_RH_UNASSIGNED)
ObjectTerminateEvent();
// Get command buffer size and command buffer.
size = requestSize;
buffer = request;
// Parse command header: tag, commandSize and commandCode.
// First parse the tag. The unmarshaling routine will validate
// that it is either TPM_ST_SESSIONS or TPM_ST_NO_SESSIONS.
result = TPMI_ST_COMMAND_TAG_Unmarshal(&tag, &buffer, &size);
if(result != TPM_RC_SUCCESS)
goto Cleanup;
// Unmarshal the commandSize indicator.
result = UINT32_Unmarshal(&commandSize, &buffer, &size);
if(result != TPM_RC_SUCCESS)
goto Cleanup;
// On a TPM that receives bytes on a port, the number of bytes that were
// received on that port is requestSize it must be identical to commandSize.
// In addition, commandSize must not be larger than MAX_COMMAND_SIZE allowed
// by the implementation. The check against MAX_COMMAND_SIZE may be redundant
// as the input processing (the function that receives the command bytes and
// places them in the input buffer) would likely have the input truncated when
// it reaches MAX_COMMAND_SIZE, and requestSize would not equal commandSize.
if(commandSize != requestSize || commandSize > MAX_COMMAND_SIZE)
{
result = TPM_RC_COMMAND_SIZE;
goto Cleanup;
}
// Unmarshal the command code.
result = TPM_CC_Unmarshal(&commandCode, &buffer, &size);
if(result != TPM_RC_SUCCESS)
goto Cleanup;
// Check to see if the command is implemented.
if(!CommandIsImplemented(commandCode))
{
result = TPM_RC_COMMAND_CODE;
goto Cleanup;
}
#if FIELD_UPGRADE_IMPLEMENTED == YES
// If the TPM is in FUM, then the only allowed command is
// TPM_CC_FieldUpgradeData.
if(IsFieldUgradeMode() && (commandCode != TPM_CC_FieldUpgradeData))
{
result = TPM_RC_UPGRADE;
goto Cleanup;
}
else
#endif
// Excepting FUM, the TPM only accepts TPM2_Startup() after
// _TPM_Init. After getting a TPM2_Startup(), TPM2_Startup()
// is no longer allowed.
if(( !TPMIsStarted() && commandCode != TPM_CC_Startup)
|| (TPMIsStarted() && commandCode == TPM_CC_Startup))
{
result = TPM_RC_INITIALIZE;
goto Cleanup;
}
// Start regular command process.
// Parse Handle buffer.
result = ParseHandleBuffer(commandCode, &buffer, &size, handles, &handleNum);
if(result != TPM_RC_SUCCESS)
goto Cleanup;
// Number of handles retrieved from handle area should be less than
// MAX_HANDLE_NUM.
pAssert(handleNum <= MAX_HANDLE_NUM);
// All handles in the handle area are required to reference TPM-resident
// entities.
for(i = 0; i < handleNum; i++)
{
result = EntityGetLoadStatus(&handles[i], commandCode);
if(result != TPM_RC_SUCCESS)
{
if(result == TPM_RC_REFERENCE_H0)
result = result + i;
else
result = RcSafeAddToResult(result, TPM_RC_H + g_rcIndex[i]);
goto Cleanup;
}
}
// Authorization session handling for the command.
if(tag == TPM_ST_SESSIONS)
{
BYTE *sessionBufferStart;// address of the session area first byte
// in the input buffer
UINT32 authorizationSize; // number of bytes in the session area
// Find out session buffer size.
result = UINT32_Unmarshal(&authorizationSize, &buffer, &size);
if(result != TPM_RC_SUCCESS)
goto Cleanup;
// Perform sanity check on the unmarshaled value. If it is smaller than
// the smallest possible session or larger than the remaining size of
// the command, then it is an error. NOTE: This check could pass but the
// session size could still be wrong. That will be determined after the
// sessions are unmarshaled.
if( authorizationSize < 9
|| authorizationSize > (UINT32) size)
{
result = TPM_RC_SIZE;
goto Cleanup;
}
// The sessions, if any, follows authorizationSize.
sessionBufferStart = buffer;
// The parameters follow the session area.
parmBufferStart = sessionBufferStart + authorizationSize;
// Any data left over after removing the authorization sessions is
// parameter data. If the command does not have parameters, then an
// error will be returned if the remaining size is not zero. This is
// checked later.
parmBufferSize = size - authorizationSize;
// The actions of ParseSessionBuffer() are described in the introduction.
result = ParseSessionBuffer(commandCode,
handleNum,
handles,
sessionBufferStart,
authorizationSize,
parmBufferStart,
parmBufferSize);
if(result != TPM_RC_SUCCESS)
goto Cleanup;
}
else
{
// Whatever remains in the input buffer is used for the parameters of the
// command.
parmBufferStart = buffer;
parmBufferSize = size;
// The command has no authorization sessions.
// If the command requires authorizations, then CheckAuthNoSession() will
// return an error.
result = CheckAuthNoSession(commandCode, handleNum, handles,
parmBufferStart, parmBufferSize);
if(result != TPM_RC_SUCCESS)
goto Cleanup;
}
// CommandDispatcher returns a response handle buffer and a response parameter
// buffer if it succeeds. It will also set the parameterSize field in the
// buffer if the tag is TPM_RC_SESSIONS.
result = CommandDispatcher(tag,
commandCode,
(INT32 *) &parmBufferSize,
parmBufferStart,
handles,
&resHandleSize,
&resParmSize);
if(result != TPM_RC_SUCCESS)
goto Cleanup;
// Build the session area at the end of the parameter area.
BuildResponseSession(tag,
commandCode,
resHandleSize,
resParmSize,
&resAuthSize);
Cleanup:
// This implementation loads an "evict" object to a transient object slot in
// RAM whenever an "evict" object handle is used in a command so that the
// access to any object is the same. These temporary objects need to be
// cleared from RAM whether the command succeeds or fails.
ObjectCleanupEvict();
#ifndef EMBEDDED_MODE
Fail:
#endif // EMBEDDED_MODE ^^^ not defined
// The response will contain at least a response header.
*responseSize = sizeof(TPM_ST) + sizeof(UINT32) + sizeof(TPM_RC);
// If the command completed successfully, then build the rest of the response.
if(result == TPM_RC_SUCCESS)
{
// Outgoing tag will be the same as the incoming tag.
resTag = tag;
// The overall response will include the handles, parameters,
// and authorizations.
*responseSize += resHandleSize + resParmSize + resAuthSize;
// Adding parameter size field.
if(tag == TPM_ST_SESSIONS)
*responseSize += sizeof(UINT32);
if( g_clearOrderly == TRUE
&& gp.orderlyState != SHUTDOWN_NONE)
{
gp.orderlyState = SHUTDOWN_NONE;
NvWriteReserved(NV_ORDERLY, &gp.orderlyState);
g_updateNV = TRUE;
}
}
else
{
// The command failed.
// If this was a failure due to a bad command tag, then need to return
// a TPM 1.2 compatible response
if(result == TPM_RC_BAD_TAG)
resTag = TPM_ST_RSP_COMMAND;
else
// return 2.0 compatible response
resTag = TPM_ST_NO_SESSIONS;
}
// Try to commit all the writes to NV if any NV write happened during this
// command execution. This check should be made for both succeeded and failed
// commands, because a failed one may trigger a NV write in DA logic as well.
// This is the only place in the command execution path that may call the NV
// commit. If the NV commit fails, the TPM should be put in failure mode.
if(g_updateNV && !g_inFailureMode)
{
g_updateNV = FALSE;
if(!NvCommit())
FAIL(FATAL_ERROR_INTERNAL);
}
// Marshal the response header.
buffer = MemoryGetResponseBuffer(commandCode);
bufferSize = 10;
TPM_ST_Marshal(&resTag, &buffer, &bufferSize);
UINT32_Marshal((UINT32 *)responseSize, &buffer, &bufferSize);
pAssert(*responseSize <= MAX_RESPONSE_SIZE);
TPM_RC_Marshal(&result, &buffer, &bufferSize);
*response = MemoryGetResponseBuffer(commandCode);
// Clear unused bit in response buffer.
MemorySet(*response + *responseSize, 0, MAX_RESPONSE_SIZE - *responseSize);
return;
}