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 commandCode = commandHeader.CommandCode;
// Lookup command
CommandInfo command = Tpm2.CommandInfoFromCommandCode(commandCode);
if (command == null)
{
throw new Exception("Unrecognized command");
}
if (commandCode == TpmCc.ContextLoad || commandCode == TpmCc.ContextSave)
{
//throw new Exception("ContextLoad and ContextSave not supported in this build");
#if !NETFX_CORE
Console.Error.WriteLine("ContextLoad and ContextSave not supported in this build");
#endif
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 (neededObjects == null || neededSessions == null)
{
// This means that 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.
bool loadOk = LoadEntities(neededEntities);
if (!loadOk)
{
throw new Exception("Failed to make space for objects or sessions at to execute command");
}
// 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);
// create the translated command from the various components we have been manipulating
byte[] commandBuf = CommandProcessor.CreateCommand(commandHeader.CommandCode, inHandles, inSessions, commandParmsNoHandles);
Debug.Assert(commandBuf.Length == inBuf.Length);
byte[] responseBuf;
// Todo: Virtualize 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.
//
do
{
TpmDevice.DispatchCommand(active, commandBuf, out responseBuf);
TpmRc res = GetResultCode(responseBuf);
if (res == TpmRc.Success)
{
break;
}
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;
}
bool slotMade = MakeSpace(slotType, neededEntities);
if (!slotMade)
{
throw new Exception("Failed to make an object slot in the TPM");
}
} while (true);
// Parse the response from the TPM
// TODO: Make this use the new methods in Tpm2
// ReSharper disable once UnusedVariable
var mOut = new Marshaller(responseBuf);
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);
// If we have 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);
byte[] translatedResponse = CommandProcessor.CreateResponse(resultCode, responseHandles, responseSessions, responseParmsNoHandles);
outBuf = translatedResponse;
Debug.Assert(outBuf.Length == responseBuf.Length);
} // lock(this)
}