in TSS.NET/TSS.Net/SlotManager.cs [135:325]
internal void DispatchCommand(TbsContext caller, CommandModifier active, byte[] inBuf, out byte[] outBuf)
{
lock (this)
{
CommandNumber++;
// ReSharper disable once CompareOfFloatsByEqualityOperator
if (StateSaveProbability != 0.0)
{
// S3 debug support
DebugStateSave();
LastStateSaveCommandNumber = CommandNumber;
}
CommandHeader commandHeader;
TpmHandle[] inHandles;
SessionIn[] inSessions;
byte[] commandParmsNoHandles;
bool legalCommand = CommandProcessor.CrackCommand(inBuf,
out commandHeader, out inHandles, out inSessions, out commandParmsNoHandles);
if (!legalCommand)
{
// Is a diagnostics command. Pass through to TPM (a real RM would refuse).
TpmDevice.DispatchCommand(active, inBuf, out outBuf);
return;
}
TpmCc cc = commandHeader.CommandCode;
// Lookup command
CommandInfo command = Tpm2.CommandInfoFromCommandCode(cc);
if (command == null)
{
throw new Exception("Unrecognized command");
}
if (cc == TpmCc.ContextLoad || cc == TpmCc.ContextSave)
{
Debug.WriteLine("ContextLoad and ContextSave are not supported in this build");
outBuf = Marshaller.GetTpmRepresentation(new Object[] {
TpmSt.NoSessions,
(uint)10,
TpmRc.NotUsed });
}
// Look up referenced objects and sessions
ObjectContext[] neededObjects = GetReferencedObjects(caller, inHandles);
ObjectContext[] neededSessions = GetSessions(caller, inSessions);
ObjectContext[] neededEntities =
neededObjects != null
? neededSessions != null
? neededObjects.Concat(neededSessions).ToArray()
: neededObjects
: neededSessions;
#if false
// Tpm2Tester may intentionally use invalid handles, therefore it always
// work in the passthru mode (all correctness checks by TSS infra suppressed)
if (!Tpm2._TssBehavior.Passthrough &&
(neededObjects == null || neededSessions == null))
#endif
if (neededObjects == null || neededSessions == null)
{
// One or more of the handles was not registered for the context
byte[] ret = FormatError(TpmRc.Handle);
outBuf = ret;
return;
}
// Load referenced objects and sessions (free slots if needed)
// It's important to load all object and session handles in a single call
// to LoadEntities(), as for some commands (e.g. GetSessionAuditDigest)
// the objects array may contain session handles. In this case the session
// handles loaded by the invocation of LoadEntities for neededObjects
// may be evicted again during the subsequent call for neededSessions.
var expectedResponses = Tpm._GetExpectedResponses();
if (!LoadEntities(neededEntities))
{
throw new Exception("Failed to make space for objects or sessions");
}
else
{
// At this point everything referenced should be loaded, and
// there will be a free slot if needed so we can translate
// the input handles to the underlying handles
ReplaceHandlesIn(inHandles, inSessions, neededObjects, neededSessions);
}
// Re-create the command using translated object and session handles
byte[] commandBuf = CommandProcessor.CreateCommand(commandHeader.CommandCode,
inHandles, inSessions, commandParmsNoHandles);
if (!Tpm2._TssBehavior.Passthrough)
Debug.Assert(commandBuf.Length == inBuf.Length);
byte[] responseBuf;
// TODO: Virtualize TPM2_GetCapability() for handle enumeration.
//
// Execute command on underlying TPM device.
// If we get an ObjectMemory or SessionMemory error we try to make more space and try again
// Note: If the TPM device throws an error above we let it propagate out. There should be no side
// effects on TPM state that the TBS cares about.
//
ulong firstCtxSeqNum = 0;
while (true)
{
Tpm._ExpectResponses(expectedResponses);
TpmDevice.DispatchCommand(active, commandBuf, out responseBuf);
TpmRc res = GetResultCode(responseBuf);
if (res == TpmRc.Success ||
expectedResponses != null && expectedResponses.Contains(res))
{
break;
}
if (res == TpmRc.ContextGap)
{
ulong seqNum = ShortenSessionContextGap(firstCtxSeqNum);
if (seqNum == 0)
break; // Failed to handle CONTEXT_GAP error
if (firstCtxSeqNum == 0)
firstCtxSeqNum = seqNum;
//if (firstCtxSeqNum != 0)
// Console.WriteLine("DispatchCommand: CONTEXT_GAP handled");
continue;
}
var slotType = SlotType.NoSlot;
if (res == TpmRc.ObjectHandles || res == TpmRc.ObjectMemory)
{
slotType = SlotType.ObjectSlot;
}
else if (res == TpmRc.SessionHandles || res == TpmRc.SessionMemory)
{
slotType = SlotType.SessionSlot;
}
else
{
// Command failure not related to resources
break;
}
if (!MakeSpace(slotType, neededEntities))
{
// Failed to make an object slot in the TPM
responseBuf = TpmErrorHelpers.BuildErrorResponseBuffer(TpmRc.Memory);
break;
}
}
// Parse the response from the TPM
TpmSt responseTag;
uint responseParamSize;
TpmRc resultCode;
TpmHandle[] responseHandles;
SessionOut[] responseSessions;
byte[] responseParmsNoHandles, responseParmsWithHandles;
CommandProcessor.SplitResponse(responseBuf,
command.HandleCountOut,
out responseTag,
out responseParamSize,
out resultCode,
out responseHandles,
out responseSessions,
out responseParmsNoHandles,
out responseParmsWithHandles);
// In case of an error there is no impact on the loaded sessions, but
// we update the LRU values because the user will likely try again.
if (resultCode != TpmRc.Success)
{
outBuf = responseBuf;
UpdateLastUseCount(new[] {neededObjects, neededSessions});
return;
}
// Update TBS database with any newly created TPM objects
ProcessUpdatedTpmState(caller, command, responseHandles, neededObjects);
// And if there were any newly created objects use the new DB entries
// to translate the handles
ReplaceHandlesOut(responseHandles);
outBuf = CommandProcessor.CreateResponse(resultCode, responseHandles,
responseSessions, responseParmsNoHandles);
Debug.Assert(outBuf.Length == responseBuf.Length);
} // lock(this)
}