/** @file
  Provide functions to provide tcg storage core spec related functions.

Copyright (c) 2016, Intel Corporation. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent

**/

#include <Library/TcgStorageCoreLib.h>

#include <Library/BaseLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/DebugLib.h>

typedef struct {
  UINT16                                  FeatureCode;
  TCG_LEVEL0_FEATURE_DESCRIPTOR_HEADER    *Feature;
  UINTN                                   FeatureSize;
} TCG_FIND_FEATURE_CTX;

/**
  Returns a human-readable string representing a method status return code.

  @param[in]  MethodStatus   Method status to translate to a string


  @retval   return the string info.
**/
CHAR8 *
EFIAPI
TcgMethodStatusString (
  UINT8  MethodStatus
  )
{
  switch (MethodStatus) {
    #define C(status)  case TCG_METHOD_STATUS_CODE_ ## status: return #status
    C (SUCCESS);
    C (NOT_AUTHORIZED);
    C (OBSOLETE);
    C (SP_BUSY);
    C (SP_FAILED);
    C (SP_DISABLED);
    C (SP_FROZEN);
    C (NO_SESSIONS_AVAILABLE);
    C (UNIQUENESS_CONFLICT);
    C (INSUFFICIENT_SPACE);
    C (INSUFFICIENT_ROWS);
    C (INVALID_PARAMETER);
    C (OBSOLETE2);
    C (OBSOLETE3);
    C (TPER_MALFUNCTION);
    C (TRANSACTION_FAILURE);
    C (RESPONSE_OVERFLOW);
    C (AUTHORITY_LOCKED_OUT);
    C (FAIL);
    #undef C
  }

  return "unknown";
}

/**
  adds call token and method Header (invoking id, and method id).

  @param    CreateStruct             The input create structure.
  @param    InvokingId               Invoking id.
  @param    MethodId                 Method id.

**/
TCG_RESULT
EFIAPI
TcgStartMethodCall (
  TCG_CREATE_STRUCT  *CreateStruct,
  TCG_UID            InvokingId,
  TCG_UID            MethodId
  )
{
  NULL_CHECK (CreateStruct);

  if ((CreateStruct->ComPacket == NULL) ||
      (CreateStruct->CurPacket == NULL) ||
      (CreateStruct->CurSubPacket == NULL)
      )
  {
    DEBUG ((DEBUG_INFO, "unexpected state: ComPacket=%p CurPacket=%p CurSubPacket=%p\n", CreateStruct->ComPacket, CreateStruct->CurPacket, CreateStruct->CurSubPacket));
    return (TcgResultFailureInvalidAction);
  }

  ERROR_CHECK (TcgAddCall (CreateStruct));
  ERROR_CHECK (TcgAddTcgUid (CreateStruct, InvokingId));
  ERROR_CHECK (TcgAddTcgUid (CreateStruct, MethodId));

  return TcgResultSuccess;
}

/**
  Adds START LIST token.

  @param    CreateStruct        The input create structure.

**/
TCG_RESULT
EFIAPI
TcgStartParameters (
  TCG_CREATE_STRUCT  *CreateStruct
  )
{
  NULL_CHECK (CreateStruct);

  if ((CreateStruct->ComPacket == NULL) ||
      (CreateStruct->CurPacket == NULL) ||
      (CreateStruct->CurSubPacket == NULL)
      )
  {
    DEBUG ((DEBUG_INFO, "unexpected state: ComPacket=%p CurPacket=%p CurSubPacket=%p\n", CreateStruct->ComPacket, CreateStruct->CurPacket, CreateStruct->CurSubPacket));
    return (TcgResultFailureInvalidAction);
  }

  return TcgAddStartList (CreateStruct);
}

/**
  Adds END LIST token.

  @param    CreateStruct        The input create structure.

**/
TCG_RESULT
EFIAPI
TcgEndParameters (
  TCG_CREATE_STRUCT  *CreateStruct
  )
{
  NULL_CHECK (CreateStruct);

  if ((CreateStruct->ComPacket == NULL) ||
      (CreateStruct->CurPacket == NULL) ||
      (CreateStruct->CurSubPacket == NULL)
      )
  {
    DEBUG ((DEBUG_INFO, "unexpected state: ComPacket=%p CurPacket=%p CurSubPacket=%p\n", CreateStruct->ComPacket, CreateStruct->CurPacket, CreateStruct->CurSubPacket));
    return (TcgResultFailureInvalidAction);
  }

  return TcgAddEndList (CreateStruct);
}

/**
  Adds END Data token and method list.

  @param    CreateStruct        The input create structure.

**/
TCG_RESULT
EFIAPI
TcgEndMethodCall (
  TCG_CREATE_STRUCT  *CreateStruct
  )
{
  NULL_CHECK (CreateStruct);

  if ((CreateStruct->ComPacket == NULL) ||
      (CreateStruct->CurPacket == NULL) ||
      (CreateStruct->CurSubPacket == NULL)
      )
  {
    DEBUG ((DEBUG_INFO, "unexpected state: ComPacket=%p CurPacket=%p CurSubPacket=%p\n", CreateStruct->ComPacket, CreateStruct->CurPacket, CreateStruct->CurSubPacket));
    return (TcgResultFailureInvalidAction);
  }

  ERROR_CHECK (TcgAddEndOfData (CreateStruct));

  ERROR_CHECK (TcgAddStartList (CreateStruct));
  ERROR_CHECK (TcgAddUINT8 (CreateStruct, 0x00));   // expected to complete properly
  ERROR_CHECK (TcgAddUINT8 (CreateStruct, 0x00));   // reserved
  ERROR_CHECK (TcgAddUINT8 (CreateStruct, 0x00));   // reserved
  ERROR_CHECK (TcgAddEndList (CreateStruct));

  return TcgResultSuccess;
}

/**
  Retrieves the comID and Extended comID of the ComPacket in the Tcg response.
  It is intended to be used to confirm the received Tcg response is intended for user that received it.

  @param [in]        ParseStruct        Structure used to parse received TCG response.
  @param [in/out]    ComId              comID retrieved from received ComPacket.
  @param [in/out]    ComIdExtension     Extended comID retrieved from received ComPacket

**/
TCG_RESULT
EFIAPI
TcgGetComIds (
  const TCG_PARSE_STRUCT  *ParseStruct,
  UINT16                  *ComId,
  UINT16                  *ComIdExtension
  )
{
  NULL_CHECK (ParseStruct);
  NULL_CHECK (ComId);
  NULL_CHECK (ComIdExtension);

  if (ParseStruct->ComPacket == NULL) {
    DEBUG ((DEBUG_INFO, "unexpected state: ComPacket=%p\n", ParseStruct->ComPacket));
    return TcgResultFailureInvalidAction;
  }

  *ComId          = SwapBytes16 (ParseStruct->ComPacket->ComIDBE);
  *ComIdExtension = SwapBytes16 (ParseStruct->ComPacket->ComIDExtensionBE);

  return TcgResultSuccess;
}

/**
  Checks if the ComIDs of the response match the expected values.

  @param[in]   ParseStruct               Structure used to parse received TCG response
  @param[in]   ExpectedComId             Expected comID
  @param[in]   ExpectedComIdExtension    Expected extended comID

**/
TCG_RESULT
EFIAPI
TcgCheckComIds (
  const TCG_PARSE_STRUCT  *ParseStruct,
  UINT16                  ExpectedComId,
  UINT16                  ExpectedComIdExtension
  )
{
  UINT16  ParseComId;
  UINT16  ParseComIdExtension;

  ERROR_CHECK (TcgGetComIds (ParseStruct, &ParseComId, &ParseComIdExtension));
  if ((ParseComId != ExpectedComId) || (ParseComIdExtension != ExpectedComIdExtension)) {
    DEBUG ((DEBUG_INFO, "Com ID: Actual 0x%02X Expected 0x%02X\n", ParseComId, ExpectedComId));
    DEBUG ((DEBUG_INFO, "Extended Com ID: 0x%02X Expected 0x%02X\n", ParseComIdExtension, ExpectedComIdExtension));
    return TcgResultFailure;
  }

  return TcgResultSuccess;
}

/**
 Returns the method status of the current subpacket.  Does not affect the current position
 in the ComPacket.  In other words, it can be called whenever you have a valid SubPacket.

 @param [in/out]  ParseStruct       Structure used to parse received TCG response
 @param [in/out]  MethodStatus      Method status retrieved of the current SubPacket

**/
TCG_RESULT
EFIAPI
TcgGetMethodStatus (
  const TCG_PARSE_STRUCT  *ParseStruct,
  UINT8                   *MethodStatus
  )
{
  TCG_PARSE_STRUCT  TmpParseStruct;
  TCG_TOKEN         TcgToken;
  UINT8             Reserved1, Reserved2;

  NULL_CHECK (ParseStruct);
  NULL_CHECK (MethodStatus);

  if ((ParseStruct->ComPacket == NULL) ||
      (ParseStruct->CurPacket == NULL) ||
      (ParseStruct->CurSubPacket == NULL)
      )
  {
    DEBUG ((DEBUG_INFO, "unexpected state: ComPacket=%p CurPacket=%p CurSubPacket=%p\n", ParseStruct->ComPacket, ParseStruct->CurPacket, ParseStruct->CurSubPacket));
    return TcgResultFailureInvalidAction;
  }

  // duplicate ParseStruct, then don't need to "reset" location cur ptr
  CopyMem (&TmpParseStruct, ParseStruct, sizeof (TCG_PARSE_STRUCT));

  // method status list exists after the end method call in the subpacket
  // skip tokens until ENDDATA is found
  do {
    ERROR_CHECK (TcgGetNextToken (&TmpParseStruct, &TcgToken));
  } while (TcgToken.Type != TcgTokenTypeEndOfData);

  // only reach here if enddata is found
  // at this point, the curptr is pointing at method status list beginning
  ERROR_CHECK (TcgGetNextStartList (&TmpParseStruct));
  ERROR_CHECK (TcgGetNextUINT8 (&TmpParseStruct, MethodStatus));
  ERROR_CHECK (TcgGetNextUINT8 (&TmpParseStruct, &Reserved1));
  ERROR_CHECK (TcgGetNextUINT8 (&TmpParseStruct, &Reserved2));
  ERROR_CHECK (TcgGetNextEndList (&TmpParseStruct));

  if (Reserved1 != 0) {
    DEBUG ((DEBUG_INFO, "Method status reserved1 = 0x%02X (expected 0)\n", Reserved1));
    return TcgResultFailure;
  }

  if (Reserved2 != 0) {
    DEBUG ((DEBUG_INFO, "Method status reserved2 = 0x%02X (expected 0)\n", Reserved1));
    return TcgResultFailure;
  }

  return TcgResultSuccess;
}

/**
  Return the toke type string info.

  @param    Type       Input the type info.

  @retval   Return the string for this type.

**/
CHAR8 *
EFIAPI
TcgTokenTypeString (
  TCG_TOKEN_TYPE  Type
  )
{
  switch (Type) {
    case TcgTokenTypeReserved: return "Reserved";
    case TcgTokenTypeTinyAtom: return "Tiny Atom";
    case TcgTokenTypeShortAtom: return "Short Atom";
    case TcgTokenTypeMediumAtom: return "Medium Atom";
    case TcgTokenTypeLongAtom: return "Long Atom";
    case TcgTokenTypeStartList: return "Start List";
    case TcgTokenTypeEndList: return "End List";
    case TcgTokenTypeStartName: return "Start Name";
    case TcgTokenTypeEndName: return "End Name";
    case TcgTokenTypeCall: return "Call";
    case TcgTokenTypeEndOfData: return "End of Data";
    case TcgTokenTypeEndOfSession: return "End of Session";
    case TcgTokenTypeStartTransaction: return "Start Transaction";
    case TcgTokenTypeEndTransaction: return "End Transaction";
    case TcgTokenTypeEmptyAtom: return "Empty atom";
  }

  return "Unknown";
}

/**

  Adds Start Session call to the data structure.  This creates the entire ComPacket structure and
  returns the size of the entire compacket in the size parameter.

  @param [in/out]    CreateStruct               Structure used to add the start session call
  @param [in/out]    Size                       Describes the size of the entire ComPacket (header and payload). Filled out by function.
  @param [in]        ComId                      ComID for the ComPacket
  @param [in]        ComIdExtension             Extended ComID for the ComPacket
  @param [in]        HostSessionId              Host Session ID
  @param [in]        SpId                       Security Provider to start session with
  @param [in]        Write                      Write option for start session.  TRUE = start session requests write access
  @param [in]        HostChallengeLength        Length of the host challenge.  Length should be 0 if hostChallenge is NULL
  @param [in]        HostChallenge              Host challenge for Host Signing Authority.  If NULL, then no Host Challenge shall be sent.
  @param [in]        HostSigningAuthority       Host Signing Authority used for start session.  If NULL, then no Host Signing Authority shall be sent.

**/
TCG_RESULT
EFIAPI
TcgCreateStartSession (
  TCG_CREATE_STRUCT  *CreateStruct,
  UINT32             *Size,
  UINT16             ComId,
  UINT16             ComIdExtension,
  UINT32             HostSessionId,
  TCG_UID            SpId,
  BOOLEAN            Write,
  UINT32             HostChallengeLength,
  const VOID         *HostChallenge,
  TCG_UID            HostSigningAuthority
  )
{
  ERROR_CHECK (TcgStartComPacket (CreateStruct, ComId, ComIdExtension));
  ERROR_CHECK (TcgStartPacket (CreateStruct, 0x0, 0x0, 0x0, 0x0, 0x0));
  ERROR_CHECK (TcgStartSubPacket (CreateStruct, 0x0));
  ERROR_CHECK (TcgStartMethodCall (CreateStruct, TCG_UID_SMUID, TCG_UID_SM_START_SESSION));
  ERROR_CHECK (TcgStartParameters (CreateStruct));
  ERROR_CHECK (TcgAddUINT32 (CreateStruct, HostSessionId));
  ERROR_CHECK (TcgAddTcgUid (CreateStruct, SpId));
  ERROR_CHECK (TcgAddBOOLEAN (CreateStruct, Write));

  // optional parameters
  if ((HostChallenge != NULL) && (HostChallengeLength != 0)) {
    ERROR_CHECK (TcgAddStartName (CreateStruct));
    ERROR_CHECK (TcgAddUINT8 (CreateStruct, 0x00));   // TODO Create Enum for Method Optional Parameters?
    ERROR_CHECK (TcgAddByteSequence (CreateStruct, HostChallenge, HostChallengeLength, FALSE));
    ERROR_CHECK (TcgAddEndName (CreateStruct));
  }

  // optional parameters
  if (HostSigningAuthority != 0) {
    ERROR_CHECK (TcgAddStartName (CreateStruct));
    ERROR_CHECK (TcgAddUINT8 (CreateStruct, 0x03));   // TODO Create Enum for Method Optional Parameters?
    ERROR_CHECK (TcgAddTcgUid (CreateStruct, HostSigningAuthority));
    ERROR_CHECK (TcgAddEndName (CreateStruct));
  }

  ERROR_CHECK (TcgEndParameters (CreateStruct));
  ERROR_CHECK (TcgEndMethodCall (CreateStruct));
  ERROR_CHECK (TcgEndSubPacket (CreateStruct));
  ERROR_CHECK (TcgEndPacket (CreateStruct));
  ERROR_CHECK (TcgEndComPacket (CreateStruct, Size));

  return TcgResultSuccess;
}

/**
 Parses the Sync Session response contained in the parseStruct to retrieve Tper session ID.  If the Sync Session response
 parameters do not match the comID, extended ComID and host session ID then a failure is returned.

 @param[in/out]   ParseStruct          Structure used to parse received TCG response, contains Sync Session response.
 @param[in]       ComId                Expected ComID that is compared to actual ComID of response
 @param[in]       ComIdExtension       Expected Extended ComID that is compared to actual Extended ComID of response
 @param[in]       HostSessionId        Expected Host Session ID that is compared to actual  Host Session ID of response
 @param[in/out]   TperSessionId        Tper Session ID retrieved from the Sync Session response.

**/
TCG_RESULT
EFIAPI
TcgParseSyncSession (
  const TCG_PARSE_STRUCT  *ParseStruct,
  UINT16                  ComId,
  UINT16                  ComIdExtension,
  UINT32                  HostSessionId,
  UINT32                  *TperSessionId
  )
{
  UINT8             MethodStatus;
  TCG_PARSE_STRUCT  TmpParseStruct;
  UINT16            ParseComId;
  UINT16            ParseExtComId;
  TCG_UID           InvokingUID;
  TCG_UID           MethodUID;
  UINT32            RecvHostSessionId;

  NULL_CHECK (ParseStruct);
  NULL_CHECK (TperSessionId);

  CopyMem (&TmpParseStruct, ParseStruct, sizeof (TCG_PARSE_STRUCT));

  // verify method status is good
  ERROR_CHECK (TcgGetMethodStatus (&TmpParseStruct, &MethodStatus));
  METHOD_STATUS_ERROR_CHECK (MethodStatus, TcgResultFailure);

  // verify comids
  ERROR_CHECK (TcgGetComIds (&TmpParseStruct, &ParseComId, &ParseExtComId));

  if ((ComId != ParseComId) || (ComIdExtension != ParseExtComId)) {
    DEBUG ((DEBUG_INFO, "unmatched comid (exp: 0x%X recv: 0x%X) or comid extension (exp: 0x%X recv: 0x%X)\n", ComId, ParseComId, ComIdExtension, ParseExtComId));
    return TcgResultFailure;
  }

  ERROR_CHECK (TcgGetNextCall (&TmpParseStruct));
  ERROR_CHECK (TcgGetNextTcgUid (&TmpParseStruct, &InvokingUID));
  ERROR_CHECK (TcgGetNextTcgUid (&TmpParseStruct, &MethodUID));
  ERROR_CHECK (TcgGetNextStartList (&TmpParseStruct));
  ERROR_CHECK (TcgGetNextUINT32 (&TmpParseStruct, &RecvHostSessionId));
  ERROR_CHECK (TcgGetNextUINT32 (&TmpParseStruct, TperSessionId));
  ERROR_CHECK (TcgGetNextEndList (&TmpParseStruct));
  ERROR_CHECK (TcgGetNextEndOfData (&TmpParseStruct));

  if (InvokingUID != TCG_UID_SMUID) {
    DEBUG ((DEBUG_INFO, "Invoking UID did not match UID_SMUID\n"));
    return TcgResultFailure;
  }

  if (MethodUID != TCG_UID_SM_SYNC_SESSION) {
    DEBUG ((DEBUG_INFO, "Method UID did not match UID_SM_SYNC_SESSION\n"));
    return TcgResultFailure;
  }

  if (HostSessionId != RecvHostSessionId) {
    DEBUG ((DEBUG_INFO, "unmatched HostSessionId (exp: 0x%X recv: 0x%X)\n", HostSessionId, RecvHostSessionId));
    return TcgResultFailure;
  }

  return TcgResultSuccess;
}

/**

  Creates ComPacket with EndSession.
  This assumes a start session has already been opened.

  @param  [in/out]    CreateStruct        Structure used to add Endsession
  @param  [in/out]    Size                Describes the size of the entire ComPacket (header and payload). Filled out by function.
  @param  [in]        ComId               ComID for the ComPacket
  @param  [in]        ComIdExtension      Extended ComID for the ComPacket
  @param  [in]        HostSessionId         Host Session ID for the Packet
  @param  [in]        TpSessionId         Tper Session ID for the Packet

**/
TCG_RESULT
EFIAPI
TcgCreateEndSession (
  TCG_CREATE_STRUCT  *CreateStruct,
  UINT32             *Size,
  UINT16             ComId,
  UINT16             ComIdExtension,
  UINT32             HostSessionId,
  UINT32             TpSessionId
  )
{
  ERROR_CHECK (TcgStartComPacket (CreateStruct, ComId, ComIdExtension));
  ERROR_CHECK (TcgStartPacket (CreateStruct, TpSessionId, HostSessionId, 0x0, 0x0, 0x0));
  ERROR_CHECK (TcgStartSubPacket (CreateStruct, 0x0));
  ERROR_CHECK (TcgAddEndOfSession (CreateStruct));
  ERROR_CHECK (TcgEndSubPacket (CreateStruct));
  ERROR_CHECK (TcgEndPacket (CreateStruct));
  ERROR_CHECK (TcgEndComPacket (CreateStruct, Size));

  return TcgResultSuccess;
}

/**
  Set start method.

  @param     CreateStruct   Input create structure.
  @param     Row            Input the row info.
  @param     ColumnNumber   the column info.

**/
TCG_RESULT
EFIAPI
TcgStartMethodSet (
  TCG_CREATE_STRUCT  *CreateStruct,
  TCG_UID            Row,
  UINT32             ColumnNumber
  )
{
  ERROR_CHECK (TcgStartMethodCall (CreateStruct, Row, TCG_UID_METHOD_SET));
  ERROR_CHECK (TcgStartParameters (CreateStruct));
  ERROR_CHECK (TcgAddStartName (CreateStruct));
  ERROR_CHECK (TcgAddUINT8 (CreateStruct, 0x01)); // "Values"
  ERROR_CHECK (TcgAddStartList (CreateStruct));
  ERROR_CHECK (TcgAddStartName (CreateStruct));
  ERROR_CHECK (TcgAddUINT32 (CreateStruct, ColumnNumber));
  return TcgResultSuccess;
}

/**
  Set end method.

  @param     CreateStruct  Input create structure.

**/
TCG_RESULT
EFIAPI
TcgEndMethodSet (
  TCG_CREATE_STRUCT  *CreateStruct
  )
{
  ERROR_CHECK (TcgAddEndName (CreateStruct));
  ERROR_CHECK (TcgAddEndList (CreateStruct));
  ERROR_CHECK (TcgAddEndName (CreateStruct));
  ERROR_CHECK (TcgEndParameters (CreateStruct));
  ERROR_CHECK (TcgEndMethodCall (CreateStruct));
  return TcgResultSuccess;
}

/**
  Creates ComPacket with a Method call that sets the PIN column for the row specified.
  This assumes a start session has already been opened with the desired SP.

  @param [in/out]   CreateStruct           Structure used to add method call.
  @param [in/out]   Size                   Describes the size of the entire ComPacket (header and payload). Filled out by function.
  @param [in]       ComId                  ComID for the ComPacket
  @param [in]       ComIdExtension         Extended ComID for the ComPacket
  @param [in]       TperSession            Tper Session ID for the Packet
  @param [in]       HostSession            Host Session ID for the Packet
  @param [in]       SidRow                 UID of row of current SP to set PIN column
  @param [in]       Password               value of PIN to set
  @param [in]       PasswordSize           Size of PIN

**/
TCG_RESULT
EFIAPI
TcgCreateSetCPin (
  TCG_CREATE_STRUCT  *CreateStruct,
  UINT32             *Size,
  UINT16             ComId,
  UINT16             ComIdExtension,
  UINT32             TperSession,
  UINT32             HostSession,
  TCG_UID            SidRow,
  const VOID         *Password,
  UINT32             PasswordSize
  )
{
  // set new SID Password
  ERROR_CHECK (TcgStartComPacket (CreateStruct, ComId, ComIdExtension));
  ERROR_CHECK (TcgStartPacket (CreateStruct, TperSession, HostSession, 0x0, 0x0, 0x0));
  ERROR_CHECK (TcgStartSubPacket (CreateStruct, 0x0));
  ERROR_CHECK (TcgStartMethodSet (CreateStruct, SidRow, 0x03)); // "PIN"
  ERROR_CHECK (TcgAddByteSequence (CreateStruct, Password, PasswordSize, FALSE));
  ERROR_CHECK (TcgEndMethodSet (CreateStruct));
  ERROR_CHECK (TcgEndSubPacket (CreateStruct));
  ERROR_CHECK (TcgEndPacket (CreateStruct));
  ERROR_CHECK (TcgEndComPacket (CreateStruct, Size));
  return TcgResultSuccess;
}

/**
 Creates ComPacket with a Method call that sets the "Enabled" column for the row specified using the value specified.
 This assumes a start session has already been opened with the desired SP.

 @param [in/out]  CreateStruct          Structure used to add method call
 @param [in/out]  Size                  Describes the size of the entire ComPacket (header and payload). Filled out by function.
 @param [in]      ComId                 ComID for the ComPacket
 @param [in]      ComIdExtension        Extended ComID for the ComPacket
 @param [in]      TperSession           Tper Session ID for the Packet
 @param [in]      HostSession           Host Session ID for the Packet
 @param [in]      AuthorityUid          Authority UID to modify the "Enabled" column for
 @param [in]      Enabled               Value to set the "Enabled" column to

**/
TCG_RESULT
EFIAPI
TcgSetAuthorityEnabled (
  TCG_CREATE_STRUCT  *CreateStruct,
  UINT32             *Size,
  UINT16             ComId,
  UINT16             ComIdExtension,
  UINT32             TperSession,
  UINT32             HostSession,
  TCG_UID            AuthorityUid,
  BOOLEAN            Enabled
  )
{
  ERROR_CHECK (TcgStartComPacket (CreateStruct, ComId, ComIdExtension));
  ERROR_CHECK (TcgStartPacket (CreateStruct, TperSession, HostSession, 0x0, 0x0, 0x0));
  ERROR_CHECK (TcgStartSubPacket (CreateStruct, 0x0));
  ERROR_CHECK (TcgStartMethodSet (CreateStruct, AuthorityUid, 0x05)); // "Enabled"
  ERROR_CHECK (TcgAddBOOLEAN (CreateStruct, Enabled));
  ERROR_CHECK (TcgEndMethodSet (CreateStruct));
  ERROR_CHECK (TcgEndSubPacket (CreateStruct));
  ERROR_CHECK (TcgEndPacket (CreateStruct));
  ERROR_CHECK (TcgEndComPacket (CreateStruct, Size));
  return TcgResultSuccess;
}

/**
  Create set ace.

  @param     CreateStruct      Input create structure.
  @param     Size              size info.
  @param     ComId             ComId info.
  @param     ComIdExtension    ComId extension info.
  @param     TperSession       Tper session data.
  @param     HostSession       Host session data.
  @param     AceRow            Ace row info.
  @param     Authority1        Authority 1 info.
  @param     LogicalOperator   Logical operator info.
  @param     Authority2        Authority 2 info.

  @retval    Return the action result.

**/
TCG_RESULT
EFIAPI
TcgCreateSetAce (
  TCG_CREATE_STRUCT  *CreateStruct,
  UINT32             *Size,
  UINT16             ComId,
  UINT16             ComIdExtension,
  UINT32             TperSession,
  UINT32             HostSession,
  TCG_UID            AceRow,
  TCG_UID            Authority1,
  BOOLEAN            LogicalOperator,
  TCG_UID            Authority2
  )
{
  UINT8  HalfUidAuthorityObjectRef[4];
  UINT8  HalfUidBooleanAce[4];

  HalfUidAuthorityObjectRef[0] = 0x0;
  HalfUidAuthorityObjectRef[1] = 0x0;
  HalfUidAuthorityObjectRef[2] = 0xC;
  HalfUidAuthorityObjectRef[3] = 0x5;

  HalfUidBooleanAce[0] = 0x0;
  HalfUidBooleanAce[1] = 0x0;
  HalfUidBooleanAce[2] = 0x4;
  HalfUidBooleanAce[3] = 0xE;

  ERROR_CHECK (TcgStartComPacket (CreateStruct, ComId, ComIdExtension));
  ERROR_CHECK (TcgStartPacket (CreateStruct, TperSession, HostSession, 0x0, 0x0, 0x0));
  ERROR_CHECK (TcgStartSubPacket (CreateStruct, 0x0));
  ERROR_CHECK (TcgStartMethodSet (CreateStruct, AceRow, 0x03));     // "BooleanExpr"
  ERROR_CHECK (TcgAddStartList (CreateStruct));
  ERROR_CHECK (TcgAddStartName (CreateStruct));
  ERROR_CHECK (TcgAddByteSequence (CreateStruct, HalfUidAuthorityObjectRef, sizeof (HalfUidAuthorityObjectRef), FALSE));
  ERROR_CHECK (TcgAddTcgUid (CreateStruct, Authority1));
  ERROR_CHECK (TcgAddEndName (CreateStruct));
  ERROR_CHECK (TcgAddStartName (CreateStruct));
  ERROR_CHECK (TcgAddByteSequence (CreateStruct, HalfUidAuthorityObjectRef, sizeof (HalfUidAuthorityObjectRef), FALSE));
  ERROR_CHECK (TcgAddTcgUid (CreateStruct, Authority2));
  ERROR_CHECK (TcgAddEndName (CreateStruct));

  ERROR_CHECK (TcgAddStartName (CreateStruct));
  ERROR_CHECK (TcgAddByteSequence (CreateStruct, HalfUidBooleanAce, sizeof (HalfUidBooleanAce), FALSE));
  ERROR_CHECK (TcgAddBOOLEAN (CreateStruct, LogicalOperator));
  ERROR_CHECK (TcgAddEndName (CreateStruct));
  ERROR_CHECK (TcgAddEndList (CreateStruct));
  ERROR_CHECK (TcgEndMethodSet (CreateStruct));
  ERROR_CHECK (TcgEndSubPacket (CreateStruct));
  ERROR_CHECK (TcgEndPacket (CreateStruct));
  ERROR_CHECK (TcgEndComPacket (CreateStruct, Size));
  return TcgResultSuccess;
}

/**
  Enum level 0 discovery.

  @param     DiscoveryHeader   Discovery header.
  @param     Callback          Callback function.
  @param     Context           The context for the function.

  @retval    return true if the callback return TRUE, else return FALSE.

**/
BOOLEAN
EFIAPI
TcgEnumLevel0Discovery (
  const TCG_LEVEL0_DISCOVERY_HEADER  *DiscoveryHeader,
  TCG_LEVEL0_ENUM_CALLBACK           Callback,
  VOID                               *Context
  )
{
  UINT32                                BytesLeft;
  const UINT8                           *DiscoveryBufferPtr;
  UINT32                                FeatLength;
  TCG_LEVEL0_FEATURE_DESCRIPTOR_HEADER  *Feat;

  //
  // Total bytes including descriptors but not including the Length field
  //
  BytesLeft = SwapBytes32 (DiscoveryHeader->LengthBE);

  //
  // If discovery Header is not valid, exit
  //
  if (BytesLeft == 0) {
    return FALSE;
  }

  //
  // Subtract the Length of the Header, except the Length field, which is not included
  //
  BytesLeft -= (sizeof (TCG_LEVEL0_DISCOVERY_HEADER) - sizeof (DiscoveryHeader->LengthBE));

  //
  // Move ptr to first descriptor
  //
  DiscoveryBufferPtr = (const UINT8 *)DiscoveryHeader + sizeof (TCG_LEVEL0_DISCOVERY_HEADER);

  while (BytesLeft > sizeof (TCG_LEVEL0_FEATURE_DESCRIPTOR_HEADER)) {
    //
    // Pointer to beginning of descriptor (including common Header)
    //
    Feat = (TCG_LEVEL0_FEATURE_DESCRIPTOR_HEADER *)DiscoveryBufferPtr;

    FeatLength = Feat->Length + sizeof (TCG_LEVEL0_FEATURE_DESCRIPTOR_HEADER);

    //
    // Not enough bytes left for Feature descriptor
    //
    if (BytesLeft < FeatLength) {
      break;
    }

    //
    // Report the Feature to the callback
    //
    if (Callback (DiscoveryHeader, Feat, FeatLength, Context)) {
      return TRUE;
    }

    //
    // Descriptor Length only describes Data after common Header
    //
    BytesLeft          -= FeatLength;
    DiscoveryBufferPtr += FeatLength;
  }

  return FALSE;
}

/**
  The callback function for Get Feature function.

  @param     DiscoveryHeader   Input discovery header.
  @param     Feature           Input Feature.
  @param     FeatureSize       Input Feature size.
  @param     Context           The context.

**/
BOOLEAN
EFIAPI
TcgFindFeatureCallback (
  const TCG_LEVEL0_DISCOVERY_HEADER     *DiscoveryHeader,
  TCG_LEVEL0_FEATURE_DESCRIPTOR_HEADER  *Feature,
  UINTN                                 FeatureSize,
  VOID                                  *Context
  )
{
  TCG_FIND_FEATURE_CTX  *FindCtx;

  FindCtx = (TCG_FIND_FEATURE_CTX *)Context;
  if ( SwapBytes16 (Feature->FeatureCode_BE) == FindCtx->FeatureCode ) {
    FindCtx->Feature     = Feature;
    FindCtx->FeatureSize = FeatureSize;
    return TRUE; // done enumerating features
  }

  return FALSE; // continue enumerating
}

/**
  Get Feature code from the header.

  @param     DiscoveryHeader    The discovery header.
  @param     FeatureCode        return the Feature code.
  @param     FeatureSize        return the Feature size.

  @retval    return the Feature code data.
**/
TCG_LEVEL0_FEATURE_DESCRIPTOR_HEADER *
EFIAPI
TcgGetFeature (
  const TCG_LEVEL0_DISCOVERY_HEADER  *DiscoveryHeader,
  UINT16                             FeatureCode,
  UINTN                              *FeatureSize
  )
{
  TCG_FIND_FEATURE_CTX  FindCtx;

  FindCtx.FeatureCode = FeatureCode;
  FindCtx.Feature     = NULL;
  FindCtx.FeatureSize = 0;

  TcgEnumLevel0Discovery (DiscoveryHeader, TcgFindFeatureCallback, &FindCtx);
  if (FeatureSize != NULL) {
    *FeatureSize = FindCtx.FeatureSize;
  }

  return FindCtx.Feature;
}

/**
  Determines if the protocol provided is part of the provided supported protocol list.

  @param[in]  ProtocolList     Supported protocol list to investigate
  @param[in]  Protocol         Protocol value to determine if supported

  @return TRUE = protocol is supported, FALSE = protocol is not supported
**/
BOOLEAN
EFIAPI
TcgIsProtocolSupported (
  const TCG_SUPPORTED_SECURITY_PROTOCOLS  *ProtocolList,
  UINT16                                  Protocol
  )
{
  UINT16  Index;
  UINT16  ListLength;

  ListLength = SwapBytes16 (ProtocolList->ListLength_BE);

  if (ListLength > sizeof (ProtocolList->List)) {
    DEBUG ((DEBUG_INFO, "WARNING: list Length is larger than max allowed Value; truncating\n"));
    ListLength = sizeof (ProtocolList->List);
  }

  for (Index = 0; Index < ListLength; Index++) {
    if (ProtocolList->List[Index] == Protocol) {
      return TRUE;
    }
  }

  return FALSE;
}

/**
  Check whether lock or not.

  @param     Discovery

  @retval    TRUE if lock, FALSE if not lock.
**/
BOOLEAN
EFIAPI
TcgIsLocked (
  const TCG_LEVEL0_DISCOVERY_HEADER  *Discovery
  )
{
  UINTN                           Size;
  TCG_LOCKING_FEATURE_DESCRIPTOR  *LockDescriptor;

  Size           = 0;
  LockDescriptor = (TCG_LOCKING_FEATURE_DESCRIPTOR *)TcgGetFeature (Discovery, TCG_FEATURE_LOCKING, &Size);

  if ((LockDescriptor != NULL) && (Size >= sizeof (*LockDescriptor))) {
    DEBUG ((DEBUG_INFO, "locked: %d\n", LockDescriptor->Locked));
    return LockDescriptor->Locked;
  }

  //
  // Descriptor was not found
  //
  return FALSE;
}
