/** @file
  Implementation file for the HECI2 SMM Message Library

 @copyright
  INTEL CONFIDENTIAL
  Copyright 2016 - 2018 Intel Corporation.

  The source code contained or described herein and all documents related to the
  source code ("Material") are owned by Intel Corporation or its suppliers or
  licensors. Title to the Material remains with Intel Corporation or its suppliers
  and licensors. The Material may contain trade secrets and proprietary and
  confidential information of Intel Corporation and its suppliers and licensors,
  and is protected by worldwide copyright and trade secret laws and treaty
  provisions. No part of the Material may be used, copied, reproduced, modified,
  published, uploaded, posted, transmitted, distributed, or disclosed in any way
  without Intel's prior express written permission.

  No license under any patent, copyright, trade secret or other intellectual
  property right is granted to or conferred upon you by disclosure or delivery
  of the Materials, either expressly, by implication, inducement, estoppel or
  otherwise. Any license under such intellectual property rights must be
  express and approved by Intel in writing.

  Unless otherwise agreed by Intel in writing, you may not remove or alter
  this notice or any other notice embedded in Materials by Intel or
  Intel's suppliers or licensors in any way.

  This file contains an 'Intel Peripheral Driver' and is uniquely identified as
  "Intel Reference Module" and is licensed for Intel CPUs and chipsets under
  the terms of your license agreement with Intel or your vendor. This file may
  be modified by the user, subject to additional terms of the license agreement.

@par Specification Reference

**/

#include <Uefi.h>

#include <SeCState.h>
#include <CoreBiosMsg.h>
#include <HeciRegs.h>
#include <SeCAccess.h>
#include <SmmHeci2MsgLib.h>

#include <Library/BaseLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/DebugLib.h>
#include <Library/Heci2MsgLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/SmmServicesTableLib.h>
#include <Private/Library/HeciInitLib.h>  // For HeciGetSecMode()

#include <Protocol/CseVariableStorageSupport.h>
#include <Protocol/TrustedChannel.h>
#include <Protocol/VariableStorageSupportProtocol.h>

STATIC UINT8  Heci2DataBuffer[MAX (HECI2_BIOS_MAX_WRITE_MSG_SIZE, HECI2_BIOS_MAX_READ_MSG_SIZE)];

STATIC EDKII_VARIABLE_STORAGE_SUPPORT_PROTOCOL    *mVariableStorageSupportProtocol    = NULL;
STATIC EFI_SMM_PERIODIC_TIMER_DISPATCH2_PROTOCOL  *mSmmPeriodicTimerDispatch2Protocol = NULL;
STATIC UINT64                                     *mSmiTickPeriodTable                = NULL;
STATIC UINT64                                     mSmiTickPeriod                      = 0;

STATIC CSE_VARIABLE_STORAGE_SUPPORT_PROTOCOL      *mCseVariableStorageSupportProtocol;
STATIC HECI_TRUSTED_CHANNEL_PROTOCOL              *mHeciTrustedChannelProtocol;
STATIC HECI2_COMMAND_QUEUE_CONTEXT                mHeci2CommandQueueContext;
STATIC HECI2_NVM_FILE_CONTEXT                     mHeci2FileContext;
STATIC HECI2_PERIODIC_SMI_CONTEXT                 mHeci2PeriodicSmiContext;

extern EFI_CIRCULAR_BUFFER                        mCseCommandQueue;

/**
  Get the HECI2 read buffer size.

  @param       None

  @retval      UINTN   The HECI2 read buffer size in bytes.
**/
UINTN
EFIAPI
HeciGetHeci2ReadBufferSize (
  VOID
  )
{
  return MAX_HECI2_READ_DATA_SIZE;
}

/**
  Get the HECI2 write buffer size.

  @param       None

  @retval      UINTN   The HECI2 write buffer size in bytes.
**/
UINTN
EFIAPI
HeciGetHeci2WriteBufferSize (
  VOID
  )
{
  return MAX_HECI2_WRITE_DATA_SIZE;
}

/**
  Get NVM file's size through HECI2.

  @param[in]  FileName       The file name.
  @param[out] FileSize       The file's size.
  @param[in]  Heci2Protocol  A pointer to a HECI2 protocol instance.

  @return     EFI_SUCCESS    Get NVM file size success.
  @return     Others         Get NVM file size failed.
**/
EFI_STATUS
EFIAPI
Heci2GetNVMFileSize (
  IN  CONST  UINT8              *FileName,
  OUT        UINTN              *FileSize,
  IN  CONST  EFI_HECI_PROTOCOL  *Heci2Protocol
  )
{
  EFI_STATUS                                      Status;
  UINT32                                          HeciSendLength;
  UINT32                                          HeciRecvLength;
  HECI2_TRUSTED_CHANNEL_BIOS_NVMSIZE_REQ         *GetFileSizeMessage;
  UINT32                                          SeCMode;
  UINTN                                           CurrentWriteDataSize = 0;
  HECI2_TRUSTED_CHANNEL_BIOS_NVMSIZE_RESP         *Res;

  DEBUG ((EFI_D_INFO, "BIOS Start Send HECI Message: Heci2GetNVMFileSize\n"));

  Status = HeciGetSeCMode (HECI1_DEVICE, &SeCMode);
  if (EFI_ERROR (Status) || (SeCMode != SEC_MODE_NORMAL)) {
    return EFI_UNSUPPORTED;
  }
  DEBUG ((EFI_D_INFO, "GetSeCMode successful\n"));

  SetMem (Heci2DataBuffer, sizeof (Heci2DataBuffer), 0);

  GetFileSizeMessage = (HECI2_TRUSTED_CHANNEL_BIOS_NVMSIZE_REQ *) Heci2DataBuffer;
  GetFileSizeMessage->TrustedChannelHeader.CommandId = HECI2_FILE_SIZE_CMD_ID;
  AsciiStrCpyS ((CHAR8 *) GetFileSizeMessage->FileName, (sizeof (GetFileSizeMessage->FileName) / sizeof (CHAR8)), FileName);

  //
  // Fill in the HMAC signature and update the monotonic counter
  //
  Status = mHeciTrustedChannelProtocol->UpdateTrustedHeader (
                                          Heci2DataBuffer,
                                          sizeof (HECI2_TRUSTED_CHANNEL_BIOS_NVMSIZE_REQ),
                                          (UINT32) CurrentWriteDataSize
                                          );

  DEBUG ((
     EFI_D_INFO,
    "HECI2 GetNVMfilesize data size = 0x%x\n",
    sizeof (HECI2_TRUSTED_CHANNEL_BIOS_NVMSIZE_REQ) + CurrentWriteDataSize
    ));

  HeciSendLength = (UINT32) (sizeof (HECI2_TRUSTED_CHANNEL_BIOS_NVMSIZE_REQ) + CurrentWriteDataSize);
  HeciRecvLength = sizeof (HECI2_TRUSTED_CHANNEL_BIOS_NVMSIZE_RESP);

  Status = Heci2Protocol->SendwACK(
                  HECI2_DEVICE,
                  (UINT32 *)Heci2DataBuffer,
                  HeciSendLength,
                  &HeciRecvLength,
                  BIOS_FIXED_HOST_ADDR,
                  HECI2_BIOS_MCA_FIXED_ADDR
                  );
  Res = (HECI2_TRUSTED_CHANNEL_BIOS_NVMSIZE_RESP *) Heci2DataBuffer;

  DEBUG ((EFI_D_INFO, "CommandId        =%x\n", Res->TrustedChannelHeader.CommandId));
  DEBUG ((EFI_D_INFO, "RequestResponse  =%x\n", Res->TrustedChannelHeader.RequestResponse));
  DEBUG ((EFI_D_INFO, "Status           =%x\n", Res->Status));
  DEBUG ((EFI_D_INFO, "DataSize         =%x\n", Res->DataSize));
  *FileSize = Res->DataSize;

  return Status;
}

/**
  Populate the CSE variable storage command queue with HMAC CSE NVM read commands.

  @param[in]     FileName              The file name.
  @param[in]     Offset                The offset of data.
  @param[in]     DataSize              The data size in bytes.
  @param[in,out] CommandQueueBuffer    A pointer to a buffer of EFI_CIRCULAR_BUFFER that holds the command queue

  @return        EFI_SUCCESS           The command queue was populated successfully.
  @return        Others                The command queue could not be populated successfully.
**/
EFI_STATUS
EFIAPI
PopulateCseReadFileCommandQueue (
  IN     CONST  UINT8                *FileName,
  IN            UINT32               Offset,
  IN            UINTN                DataSize,
  IN OUT        EFI_CIRCULAR_BUFFER  *CommandQueueBuffer
  )
{
  EFI_STATUS    Status;
  UINT32        CurrentReadDataSize;
  INTN          RemainingDataSize;
  UINTN         TotalDataSize;

  HECI2_TRUSTED_CHANNEL_BIOS_READ_REQ  *ReadFileMessage;

  DEBUG ((EFI_D_INFO, "Adding HECI2 read commands to the command queue.\n"));
  DEBUG ((EFI_D_INFO, "Total data size = 0x%x.\n", DataSize));

  RemainingDataSize   = DataSize;
  TotalDataSize       = 0;

  do {
    if (RemainingDataSize > MAX_HECI2_READ_DATA_SIZE) {
      CurrentReadDataSize = MAX_HECI2_READ_DATA_SIZE;
    } else {
      CurrentReadDataSize = (UINT32) RemainingDataSize;
    }

    ZeroMem (Heci2DataBuffer, sizeof (Heci2DataBuffer));

    ReadFileMessage = (HECI2_TRUSTED_CHANNEL_BIOS_READ_REQ *) Heci2DataBuffer;

    ReadFileMessage->Offset   = Offset;
    ReadFileMessage->DataSize = (UINT16) CurrentReadDataSize;

    ReadFileMessage->TrustedChannelHeader.CommandId  = HECI2_READ_DATA_CMD_ID;

    //
    // Copy the name of the NVM file to read
    //
    ASSERT (AsciiStrLen (FileName) <= sizeof (ReadFileMessage->FileName));
    ASSERT (sizeof (Heci2DataBuffer) > sizeof (HECI2_TRUSTED_CHANNEL_BIOS_READ_REQ));
    AsciiStrCpyS ((CHAR8 *) ReadFileMessage->FileName, sizeof (ReadFileMessage->FileName), (CONST CHAR8 *) FileName);

    //
    // Fill in the HMAC signature and update the monotonic counter
    //
    Status = mHeciTrustedChannelProtocol->UpdateTrustedHeader (
                                            Heci2DataBuffer,
                                            sizeof (HECI2_TRUSTED_CHANNEL_BIOS_READ_REQ),
                                            0
                                            );
    if (EFI_ERROR (Status)) {
      return Status;
    }

    DEBUG ((
      EFI_D_INFO,
      "HECI2 read data header size is [ %x  + %x ] = %x\n",
      sizeof (HECI2_TRUSTED_CHANNEL_BIOS_READ_RESP),
      CurrentReadDataSize,
      sizeof (HECI2_TRUSTED_CHANNEL_BIOS_READ_RESP) + CurrentReadDataSize
      ));

    //
    // Add the message chunk to the command queue
    // The messages in the command queue should be DWORD aligned
    //
    Status = CircularBufferEnqueueAligned (
               CommandQueueBuffer,
               (UINT8 *) Heci2DataBuffer,
               sizeof (HECI2_TRUSTED_CHANNEL_BIOS_READ_REQ),
               sizeof (UINT32)
               );
    if (EFI_ERROR (Status)) {
      return Status;
    }

    Offset            += CurrentReadDataSize;
    RemainingDataSize -= CurrentReadDataSize;
  } while (RemainingDataSize > 0);

  return Status;
}

/**
  Populate the CSE variable storage command queue with HMAC CSE NVM write commands.

  @param[in]     FileName              The file name.
  @param[in]     Offset                The offset of data in the file.
  @param[in]     Data                  The data buffer.
  @param[in]     DataSize              The data size in bytes.
  @param[in]     Truncate              Indicates if the file should be truncated.
  @param[in,out] CommandQueueBuffer    A pointer to a buffer of EFI_CIRCULAR_BUFFER that holds the command queue

  @return        EFI_SUCCESS           The command queue was populated successfully.
  @return        Others                The command queue could not be populated successfully.
**/
EFI_STATUS
EFIAPI
PopulateCseWriteFileCommandQueue (
  IN      CONST CHAR8                *FileName,
  IN            UINT32               Offset,
  IN      CONST UINT8                *Data,
  IN            UINTN                DataSize,
  IN            BOOLEAN              Truncate,
  IN OUT        EFI_CIRCULAR_BUFFER  *CommandQueueBuffer
  )
{
  EFI_STATUS    Status;
  UINT32        CurrentWriteDataSize;
  INTN          RemainingDataSize;
  UINTN         TotalDataSize;

  HECI2_TRUSTED_CHANNEL_BIOS_WRITE_REQ  *WriteFileMessage = NULL;
  CONST UINT8                           *DataPtr          = Data;

  if (FileName == NULL || Data == NULL || CommandQueueBuffer == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  DEBUG ((EFI_D_INFO, "Adding HECI2 write commands to the command queue.\n"));
  DEBUG ((EFI_D_INFO, "Total data size = 0x%x.\n", DataSize));
  DEBUG ((EFI_D_INFO, "    Truncate bit: %a.\n", (Truncate) ? "Set" : "Not set"));

  RemainingDataSize   = DataSize;
  TotalDataSize       = 0;

  do {
    if (RemainingDataSize > MAX_HECI2_WRITE_DATA_SIZE) {
      CurrentWriteDataSize = MAX_HECI2_WRITE_DATA_SIZE;
    } else {
      CurrentWriteDataSize = (UINT32) RemainingDataSize;
    }

    ZeroMem (Heci2DataBuffer, sizeof (Heci2DataBuffer));

    WriteFileMessage = (HECI2_TRUSTED_CHANNEL_BIOS_WRITE_REQ *) Heci2DataBuffer;

    WriteFileMessage->Offset   = Offset;
    WriteFileMessage->DataSize = (UINT16) CurrentWriteDataSize;
    WriteFileMessage->Truncate = (Truncate) ? 1 : 0;
    Truncate = FALSE;

    WriteFileMessage->TrustedChannelHeader.CommandId  = HECI2_WRITE_DATA_CMD_ID;

    //
    // Copy the name of the NVM file to write
    //
    ASSERT (AsciiStrLen (FileName) <= sizeof (WriteFileMessage->FileName));
    ASSERT (sizeof (Heci2DataBuffer) > sizeof (HECI2_TRUSTED_CHANNEL_BIOS_WRITE_REQ));
    AsciiStrCpyS ((CHAR8 *) WriteFileMessage->FileName, sizeof (WriteFileMessage->FileName), (CONST CHAR8 *) FileName);

    CopyMem ((UINT8*) Heci2DataBuffer + sizeof (HECI2_TRUSTED_CHANNEL_BIOS_WRITE_REQ), DataPtr, CurrentWriteDataSize);

    //
    // Fill in the HMAC signature and update the monotonic counter
    //
    Status = mHeciTrustedChannelProtocol->UpdateTrustedHeader (
                                            Heci2DataBuffer,
                                            sizeof (HECI2_TRUSTED_CHANNEL_BIOS_WRITE_REQ),
                                            CurrentWriteDataSize
                                            );
    if (EFI_ERROR (Status)) {
      return Status;
    }

    DEBUG ((
      EFI_D_INFO,
      "HECI2 write data header size is [ %x  + %x ] = %x\n",
      sizeof (HECI2_TRUSTED_CHANNEL_BIOS_WRITE_REQ),
      CurrentWriteDataSize,
      sizeof (HECI2_TRUSTED_CHANNEL_BIOS_WRITE_REQ) + CurrentWriteDataSize
      ));

    //
    // Add the message chunk to the command queue
    //
    Status = CircularBufferEnqueueAligned (
               CommandQueueBuffer,
               (UINT8 *) Heci2DataBuffer,
               sizeof (HECI2_TRUSTED_CHANNEL_BIOS_WRITE_REQ) + CurrentWriteDataSize,
               sizeof (UINT32)
               );
    if (EFI_ERROR (Status)) {
      return Status;
    }

    DataPtr           += CurrentWriteDataSize;
    Offset            += CurrentWriteDataSize;
    RemainingDataSize -= CurrentWriteDataSize;
  } while (RemainingDataSize > 0);

  return Status;
}

/**
  Lock Directory message through HECI2.

  TODO: This needs to be updated to use HECI2_TRUSTED_CHANNEL

  @param[in] DirName       The Directory name.
  @param[in] Heci2Protocol The HECI protocol to send the message to HECI2 device.

  @return EFI_SUCCESS   Send EOP message success.
  @return Others              Send EOP message failed.
**/
EFI_STATUS
EFIAPI
Heci2LockDirectory (
  IN UINT8               *DirName,
  IN EFI_HECI_PROTOCOL   *Heci2Protocol
  )
{
  EFI_STATUS            Status;
  UINT32                HeciSendLength;
  UINT32                HeciRecvLength;
  HECI2_BIOS_MESSAGE    *LockDirMessage;
  UINT32                SeCMode;
  UINT32                DataBuffer[0x40];

  DEBUG((EFI_D_INFO, "BIOS Start Send HECI Message: Heci2LockDirectory\n"));
  Status = HeciGetSeCMode(HECI2_DEVICE, &SeCMode);
  if (EFI_ERROR (Status) || ((SeCMode != SEC_MODE_NORMAL) && (SEC_MODE_RECOVER != SeCMode))) {
    return EFI_UNSUPPORTED;
  }
  DEBUG ((EFI_D_INFO, "GetSeCMode successful\n"));

  SetMem(DataBuffer, sizeof(DataBuffer), 0);

  LockDirMessage = (HECI2_BIOS_MESSAGE*)DataBuffer;
  LockDirMessage->header.cmd_id = HECI2_LOCK_DIR_CMD_ID;
  AsciiStrCpyS ((CHAR8 *)LockDirMessage->Body.lockDirReq.DirName, MAX_DIR_NAME, (CONST CHAR8 *)DirName);

  DEBUG ((EFI_D_INFO, "HECI2_BIOS_MESSAGE size is %x\n", sizeof(HECI2_BIOS_MESSAGE)));
  HeciSendLength              = sizeof(HECI2_BIOS_MESSAGE);
  HeciRecvLength              = sizeof(DataBuffer);

  Status = Heci2Protocol->SendwACK (
          HECI2_DEVICE,
          DataBuffer,
          HeciSendLength,
          &HeciRecvLength,
          BIOS_FIXED_HOST_ADDR,
          HECI2_BIOS_MCA_FIXED_ADDR
          );

  DEBUG ((EFI_D_INFO, "CommandId        =%x\n", LockDirMessage->header.cmd_id));
  DEBUG ((EFI_D_INFO, "RequestResponse  =%x\n", LockDirMessage->header.req_resp));
  DEBUG ((EFI_D_INFO, "Status           =%x\n", LockDirMessage->Body.readResp.Status));

  return Status;
}

/**
  Returns a signed HECI Trusted Channel Get Proxy State message header.

  @param[out] MessageRequestHeader  A pointer the buffer that contains the Get Proxy State header.

  @return     EFI_SUCCESS      Constructed and signed the Get Proxy State message header successfully.
  @return     Others           Failed to construct and sign the Get Proxy State message header.
**/
EFI_STATUS
EFIAPI
Heci2GetProxyStateMessageHeader (
  OUT  HECI2_TRUSTED_CHANNEL_BIOS_HEADER  *TrustedChannelHeader
  )
{
  EFI_STATUS                   Status;

  DEBUG ((EFI_D_INFO, "Constructing a HECI2 Get Proxy State Trusted Channel message header.\n"));

  ZeroMem (TrustedChannelHeader, sizeof (HECI2_TRUSTED_CHANNEL_BIOS_HEADER));

  TrustedChannelHeader->CommandId = HECI2_GET_PROXY_STATE_CMD_ID;
  //
  // Fill in the HMAC signature and update the monotonic counter
  //
  Status = mHeciTrustedChannelProtocol->UpdateTrustedHeader (
                                          (UINT8 *) TrustedChannelHeader,
                                          sizeof (HECI2_TRUSTED_CHANNEL_BIOS_HEADER),
                                          0
                                          );

  return Status;
}

/**
  Sends a HECI2 write file send command.

  @param[out]  CurrentWriteDataSize  The size in bytes of the data writen from the data buffer.

  @return      EFI_SUCCESS           The HECI2 write file command completed successfully.
  @return      Others                An error occurred sending the HECI2 write file command.
**/
EFI_STATUS
EFIAPI
Heci2WriteNvmFileSend (
  OUT    UINTN    *CurrentWriteDataSize
  )
{
  EFI_STATUS                            Status;
  UINT32                                HeciSendLength;
  HECI2_TRUSTED_CHANNEL_BIOS_WRITE_REQ  *WriteFileMessage = NULL;

  if (mHeci2FileContext.RemainingDataSize > MAX_HECI2_WRITE_DATA_SIZE) {
    *CurrentWriteDataSize = MAX_HECI2_WRITE_DATA_SIZE;
  } else {
    *CurrentWriteDataSize = mHeci2FileContext.RemainingDataSize;
  }

  ZeroMem (Heci2DataBuffer, sizeof (Heci2DataBuffer));

  WriteFileMessage = (HECI2_TRUSTED_CHANNEL_BIOS_WRITE_REQ *) Heci2DataBuffer;

  WriteFileMessage->Offset           = mHeci2FileContext.Offset;
  WriteFileMessage->DataSize         = (UINT16) *CurrentWriteDataSize;
  WriteFileMessage->Truncate         = (mHeci2FileContext.Truncate) ? 1 : 0;
  mHeci2FileContext.Truncate = FALSE;

  WriteFileMessage->TrustedChannelHeader.CommandId = HECI2_WRITE_DATA_CMD_ID;

  //
  // Copy the name of the NVM file to write
  //
  ASSERT (AsciiStrLen (mHeci2FileContext.FileName) <= sizeof (WriteFileMessage->FileName));
  ASSERT (sizeof (Heci2DataBuffer) > sizeof (HECI2_TRUSTED_CHANNEL_BIOS_WRITE_REQ));
  AsciiStrCpyS (
    (CHAR8 *) WriteFileMessage->FileName,
    sizeof (WriteFileMessage->FileName),
    mHeci2FileContext.FileName
    );

  CopyMem (
    (UINT8 *) Heci2DataBuffer + sizeof (HECI2_TRUSTED_CHANNEL_BIOS_WRITE_REQ),
    mHeci2FileContext.Data,
    *CurrentWriteDataSize
    );

  //
  // Fill in the HMAC signature and update the monotonic counter
  //
  Status = mHeciTrustedChannelProtocol->UpdateTrustedHeader (
                                          Heci2DataBuffer,
                                          sizeof (HECI2_TRUSTED_CHANNEL_BIOS_WRITE_REQ),
                                          (UINT32) *CurrentWriteDataSize
                                          );
  if (EFI_ERROR (Status)) {
    return Status;
  }

  DEBUG ((
    EFI_D_INFO,
    "HECI2 write data size = 0x%x.\n",
    sizeof (HECI2_TRUSTED_CHANNEL_BIOS_WRITE_REQ) + *CurrentWriteDataSize
    ));

  HeciSendLength = (UINT32) (sizeof (HECI2_TRUSTED_CHANNEL_BIOS_WRITE_REQ) + *CurrentWriteDataSize);

  Status = mHeci2FileContext.Heci2Protocol->SendMsg (
                                              HECI2_DEVICE,
                                              (UINT32 *) Heci2DataBuffer,
                                              HeciSendLength,
                                              BIOS_FIXED_HOST_ADDR,
                                              HECI2_BIOS_MCA_FIXED_ADDR
                                              );
  return Status;
}

/**
  Sends a HECI2 receive command.

  @param[in]      Blocking                Indicates whether the receive command should block or not.
  @param[out]     DataBuffer              Pointer to a data buffer the hold the HECI response data.
  @param[in,out]  ReceiveLength           The size in bytes of the NVM file response.

  @return      EFI_SUCCESS             The HECI2 command completed successfully.
  @return      EFI_INVALID_PARAMETER   The DataBuffer or ReceiveLength argument pointers are invalid.
  @return      EFI_SECURITY_VIOLATION  The response data verification failed.
  @return      Others                  An error occurred sending the HECI2 command.
**/
EFI_STATUS
EFIAPI
Heci2NvmFileReceive (
  IN      UINT32  Blocking,
  OUT     UINT8   *DataBuffer,
  IN OUT  UINT32  *ReceiveLength
  )
{
  EFI_STATUS                            Status;
  BOOLEAN                               IsVerificationSuccessful = FALSE;
  HECI2_TRUSTED_CHANNEL_BIOS_WRITE_REQ  *NvmFileMessage;

  if (DataBuffer == NULL || ReceiveLength == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  Status = mHeci2FileContext.Heci2Protocol->ReadMsg (
                                              HECI2_DEVICE,
                                              Blocking,
                                              (UINT32 *) DataBuffer,
                                              ReceiveLength
                                              );
  if (EFI_ERROR (Status)) {
    return Status;
  }
  NvmFileMessage = (HECI2_TRUSTED_CHANNEL_BIOS_WRITE_REQ *) DataBuffer;

  //
  // Verify the response message:
  //  1. Verify the monotonic counter matches that of the request (in digest calculation)
  //  2. The signature is the HMAC-SHA2 of the response message including
  //     the monotonic counter but not including the signature itself.
  //
  Status = mHeciTrustedChannelProtocol->VerifyTrustedHeader (
                                          DataBuffer,
                                          *ReceiveLength,
                                          &IsVerificationSuccessful
                                          );
  if (EFI_ERROR (Status)) {
    return Status;
  } else if (!IsVerificationSuccessful) {
    DEBUG ((DEBUG_ERROR, "HMAC SHA256 signature verification failed!\n"));
    return EFI_SECURITY_VIOLATION;
  }

  DEBUG ((EFI_D_INFO, "HECI2 CommandId       =%x\n", NvmFileMessage->TrustedChannelHeader.CommandId));
  DEBUG ((EFI_D_INFO, "HECI2 RequestResponse =%x\n", NvmFileMessage->TrustedChannelHeader.RequestResponse));

  return Status;
}

/**
  Gets a tick period to use for HECI NVM periodic SMI handlers.

  @retval    EFI_SUCCESS           The periodic timer interval was found successfully.
  @retval    EFI_NOT_FOUND         A valid periodic timer interval could not be found.
  @retval    EFI_NOT_READY         An instance of the EFI_SMM_PERIODIC_TIMER_DISPATCH2_PROTOCOL was not found.
  @retval    EFI_OUT_OF_RESOURCES  Insufficient resources to allocate a buffer for a periodic timer interval table.
**/
EFI_STATUS
EFIAPI
FindPeriodicTimerInterval (
  VOID
  )
{
  EFI_STATUS  Status;
  UINTN       Count               = 0;
  UINT64      *SmiTickInterval    = NULL;

  if (mSmmPeriodicTimerDispatch2Protocol == NULL) {
    return EFI_NOT_READY;
  }

  //
  // Count the number of periodic SMI tick intervals that the SMM Periodic Timer
  // Dipatch 2 Protocol supports.
  //
  do {
    Status = mSmmPeriodicTimerDispatch2Protocol->GetNextShorterInterval (
                                                   mSmmPeriodicTimerDispatch2Protocol,
                                                   &SmiTickInterval
                                                   );
    ASSERT_EFI_ERROR (Status);
    Count++;
  } while (SmiTickInterval != NULL);

  //
  // Allocate a buffer for the table of supported periodic SMI tick periods.
  //
  mSmiTickPeriodTable = AllocateZeroPool (Count * sizeof (UINT64));
  if (mSmiTickPeriodTable == NULL) {
    return EFI_OUT_OF_RESOURCES;
  }

  //
  // Fill in the table of supported periodic SMI tick periods.
  //
  SmiTickInterval = NULL;
  Count = 0;
  do {
    mSmiTickPeriodTable[Count] = 0;
    Status = mSmmPeriodicTimerDispatch2Protocol->GetNextShorterInterval (
                                                   mSmmPeriodicTimerDispatch2Protocol,
                                                   &SmiTickInterval
                                                   );
    ASSERT_EFI_ERROR (Status);
    if (SmiTickInterval != NULL) {
      mSmiTickPeriodTable[Count] = *SmiTickInterval;
    }
    Count++;
  } while (SmiTickInterval != NULL);

  //
  // Finds the next period less than 128ms
  // Note: The table is sorted largest to smallest and terminated by zero
  // Note: The table values are in 100ns units
  //
  for (Count = 0; mSmiTickPeriodTable[Count] > 0; Count++) {
    if (mSmiTickPeriodTable[Count] < 1280000) {
      mSmiTickPeriod = mSmiTickPeriodTable[Count];
      return EFI_SUCCESS;
    }
  }

  //
  // Should never reach here
  //
  DEBUG ((DEBUG_ERROR, "A valid periodic tick period was not found.\n"));
  ASSERT (FALSE);
  CpuDeadLoop ();
  return EFI_NOT_FOUND;
}

/**
  Sends the next message in the SMM HECI2 command queue..

  @param[in]    CurrentHeci2TrustedChannelRequestHeader  A pointer to the current HECI2 trusted channel request header.
  @param[out]   CurrentTotalCommandRequestSize           The total size in bytes of the current request command.
  @param[out]   CurrentTotalCommandResponseSize          The total size in bytes of the current response command.

  @retval       EFI_STATUS               The HECI2 command was sent successfully.
  @retval       EFI_INVALID_PARAMETER    The CurrentHeci2TrustedChannelRequestHeader is NULL.
  @retval       EFI_DEVICE_ERROR         The command was not recognized, the command queue may be corrupted.
  @retval       Others                   An error occurred sending the HECI2 command.
**/
EFI_STATUS
EFIAPI
Heci2CommandQueueSend (
  IN  CONST HECI2_TRUSTED_CHANNEL_BIOS_HEADER  *CurrentHeci2TrustedChannelRequestHeader,
  OUT       UINTN                              *CurrentTotalCommandRequestSize,
  OUT       UINTN                              *CurrentTotalCommandResponseSize
  )
{
  UINT32    CurrentCommandDataSize           = 0;  // The data contents size (excluding headers)
  UINTN     CurrentCommandResponseHeaderSize = 0;  // Size of the current command response header

  ZeroMem (
      mHeci2CommandQueueContext.CommandQueueVerificationBuffer,
      mHeci2CommandQueueContext.CommandQueueVerificationBufferSize
      );
  if (CurrentHeci2TrustedChannelRequestHeader == NULL) {
    DEBUG ((DEBUG_ERROR, "CurrentHeci2TrustedChannelRequestHeader is NULL, cannot continue dispatch.\n"));
    return EFI_INVALID_PARAMETER;
  }

  switch (CurrentHeci2TrustedChannelRequestHeader->CommandId) {
    case HECI2_READ_DATA_CMD_ID:
      DEBUG ((DEBUG_INFO, "Current command is: read\n"));
      CurrentCommandDataSize = ((HECI2_TRUSTED_CHANNEL_BIOS_READ_REQ *) CurrentHeci2TrustedChannelRequestHeader)->DataSize;
      *CurrentTotalCommandRequestSize  = sizeof (HECI2_TRUSTED_CHANNEL_BIOS_READ_REQ);
      CurrentCommandResponseHeaderSize = sizeof (HECI2_TRUSTED_CHANNEL_BIOS_READ_RESP);
      *CurrentTotalCommandResponseSize = CurrentCommandResponseHeaderSize + CurrentCommandDataSize;

      break;
    case HECI2_WRITE_DATA_CMD_ID:
      DEBUG ((DEBUG_INFO, "Current command is: write\n"));
      CurrentCommandDataSize = ((HECI2_TRUSTED_CHANNEL_BIOS_WRITE_REQ *) CurrentHeci2TrustedChannelRequestHeader)->DataSize;
      *CurrentTotalCommandRequestSize  = sizeof (HECI2_TRUSTED_CHANNEL_BIOS_WRITE_REQ) + CurrentCommandDataSize;
      CurrentCommandResponseHeaderSize = sizeof (HECI2_TRUSTED_CHANNEL_BIOS_WRITE_RESP);
      *CurrentTotalCommandResponseSize = CurrentCommandResponseHeaderSize;

      break;
    default:
      // Invalid HECI2 command in the command queue
      return EFI_DEVICE_ERROR;
  }

  DEBUG ((DEBUG_INFO, "CurrentCommandDataSize: %d\n", CurrentCommandDataSize));
  DEBUG ((DEBUG_INFO, "CurrentTotalCommandRequestSize: %d\n", *CurrentTotalCommandRequestSize));
  DEBUG ((DEBUG_INFO, "CurrentCommandResponseHeaderSize: %d\n", CurrentCommandResponseHeaderSize));
  DEBUG ((DEBUG_INFO, "CurrentTotalCommandResponseSize: %d\n", *CurrentTotalCommandResponseSize));

  return mHeci2FileContext.Heci2Protocol->SendMsg (
                                            HECI2_DEVICE,
                                            (UINT32 *) CurrentHeci2TrustedChannelRequestHeader,
                                            (UINT32) *CurrentTotalCommandRequestSize,
                                            BIOS_FIXED_HOST_ADDR,
                                            HECI2_BIOS_MCA_FIXED_ADDR
                                            );
}

/**
  An SMI handler for the HECI2 command queue operations.

  @param[in]     DispatchHandle  The unique handle assigned to this handler by SmiHandlerRegister().
  @param[in]     Context         Points to an optional handler context which was specified when the
                                 handler was registered.
  @param[in,out] CommBuffer      A pointer to a collection of data in memory that will
                                 be conveyed from a non-SMM environment into an SMM environment.
  @param[in,out] CommBufferSize  The size of the CommBuffer.

  @retval EFI_SUCCESS                         The interrupt was handled and quiesced. No other handlers
                                              should still be called.
  @retval EFI_WARN_INTERRUPT_SOURCE_QUIESCED  The interrupt has been quiesced but other handlers should
                                              still be called.
  @retval EFI_WARN_INTERRUPT_SOURCE_PENDING   The interrupt is still pending and other handlers should still
                                              be called.
  @retval EFI_INTERRUPT_PENDING               The interrupt could not be quiesced.
**/
EFI_STATUS
EFIAPI
Heci2CommandQueueDispatchPeriodicSmiHandler (
  IN       EFI_HANDLE  DispatchHandle,
  IN CONST VOID        *Context         OPTIONAL,
  IN OUT   VOID        *CommBuffer      OPTIONAL,
  IN OUT   UINTN       *CommBufferSize  OPTIONAL
  )
{
  EFI_STATUS       Status;

  STATIC UINTN     CurrentTotalCommandRequestSize   = 0;  // Total size of request command (header + data content size)
  STATIC UINTN     CurrentTotalCommandResponseSize  = 0;  // Total size of response command (header + data content size)
  STATIC UINTN     CurrentCommandAdvancementIndex   = 0;  // The calculated location of the next command header
  STATIC UINTN     CurrentCommandIndex              = 0;  // The index of the current command being dispatched
  STATIC UINT32    CurrentPeriodicSmiEntryCount     = 0;  // Number of entries in the current periodic SMI sequence

  STATIC HECI2_TRUSTED_CHANNEL_BIOS_HEADER  *CurrentHeci2TrustedChannelRequestHeader  = NULL;

  Status = EFI_SUCCESS;

  DEBUG ((DEBUG_INFO, "Heci2CommandQueueDispatchPeriodicSmiHandler Entry - Count = %d.\n", CurrentPeriodicSmiEntryCount));
  if (CurrentPeriodicSmiEntryCount == 0) {
    DEBUG ((DEBUG_INFO, "Beginning a new SMM HECI2 Command Queue sequence.\n"));
    DEBUG ((DEBUG_INFO, "Total Command Count    = %d\n", (UINTN) mHeci2CommandQueueContext.CommandQueueCount));
    DEBUG ((DEBUG_INFO, "HECI2 Protocol Pointer = 0x%x\n", (UINTN) mHeci2FileContext.Heci2Protocol));

    if (
      mHeci2CommandQueueContext.CommandQueueBuffer == NULL
      || (mHeci2CommandQueueContext.CommandQueueVerificationBuffer == NULL)
      ) {
      Status = EFI_INVALID_PARAMETER;
      DEBUG ((DEBUG_ERROR, "Command queue context is NULL. Cannot continue execution.\n"));
      goto Done;
    }

    CurrentHeci2TrustedChannelRequestHeader = (HECI2_TRUSTED_CHANNEL_BIOS_HEADER  *) mHeci2CommandQueueContext.CommandQueueBuffer;
  }
  CurrentPeriodicSmiEntryCount++;

  if (CurrentCommandIndex < mHeci2CommandQueueContext.CommandQueueCount) {
    if (!mHeci2PeriodicSmiContext.WaitingForHeciReceive) {
      DEBUG ((DEBUG_INFO, "Dispatching command chunk number %d\n", CurrentCommandIndex));

      Status = Heci2CommandQueueSend (
                 CurrentHeci2TrustedChannelRequestHeader,
                 &CurrentTotalCommandRequestSize,
                 &CurrentTotalCommandResponseSize
                 );
      if (EFI_ERROR (Status)) {
        DEBUG ((
          DEBUG_ERROR,
          "An error occurred sending the HECI command queue message, aborting. Status = %r.\n",
          Status));
        goto Done;
      }
      mHeci2PeriodicSmiContext.WaitingForHeciReceive = TRUE;
      return EFI_SUCCESS;
    } else {
      Status = Heci2NvmFileReceive (
                 NON_BLOCKING,
                 mHeci2CommandQueueContext.CommandQueueVerificationBuffer,
                 (UINT32 *) &CurrentTotalCommandResponseSize
                 );
      if (Status == EFI_TIMEOUT) {
        // TODO: Give up after n number of CurrentPeriodicSmiEntryCount attempts?

        //
        // EFI_TIMEOUT is expected while CSE FW processes the last HECI command.
        //
        return EFI_SUCCESS;
      } else if (EFI_ERROR (Status)) {
        DEBUG ((
          DEBUG_ERROR,
          "An error occurred in HeciReceive, aborting current HECI command queue sequence. Status = %r.\n",
          Status
          ));
        goto Done;
      }

      if (CurrentHeci2TrustedChannelRequestHeader->CommandId == HECI2_READ_DATA_CMD_ID) {
        Status =  mCseVariableStorageSupportProtocol->ReadMessageComplete (
                    (BOOLEAN) (CurrentCommandIndex == (mHeci2CommandQueueContext.CommandQueueCount - 1)),
                    (CONST HECI2_TRUSTED_CHANNEL_BIOS_READ_RESP *) CurrentHeci2TrustedChannelRequestHeader
                    );
      } else if (CurrentHeci2TrustedChannelRequestHeader->CommandId == HECI2_WRITE_DATA_CMD_ID) {
        Status = mCseVariableStorageSupportProtocol->WriteMessageComplete ((BOOLEAN) (CurrentCommandIndex == (mHeci2CommandQueueContext.CommandQueueCount - 1)));
      }
      if (EFI_ERROR (Status)) {
        DEBUG ((
          DEBUG_ERROR,
          "An error occurred processing message completion. Status = %r.\n",
          Status
          ));
        goto Done;
      }
      mHeci2PeriodicSmiContext.WaitingForHeciReceive = FALSE;

      CurrentCommandAdvancementIndex = (UINTN) (VOID *) CurrentHeci2TrustedChannelRequestHeader;
      CurrentCommandAdvancementIndex += CurrentTotalCommandRequestSize;
      CurrentCommandAdvancementIndex += TRUSTED_CHANNEL_GET_PAD_SIZE (CurrentTotalCommandRequestSize);
      CurrentHeci2TrustedChannelRequestHeader = (HECI2_TRUSTED_CHANNEL_BIOS_HEADER *) CurrentCommandAdvancementIndex;
      CurrentCommandIndex++;
    }

    if ((BOOLEAN) (CurrentCommandIndex < mHeci2CommandQueueContext.CommandQueueCount)) {
      DEBUG ((
        DEBUG_INFO,
        "Completed command %d. %d commands remaining.\n",
        CurrentCommandIndex,
        (mHeci2CommandQueueContext.CommandQueueCount - CurrentCommandIndex)
        ));
      DEBUG ((
        DEBUG_INFO,
        "Exiting current periodic SMI. Current entry count = %d\n",
        CurrentPeriodicSmiEntryCount
        ));
      return EFI_SUCCESS;
    }
  }

Done:
  if (EFI_ERROR (Status)) {
    ASSERT_EFI_ERROR (Status);
  }

  Status = mSmmPeriodicTimerDispatch2Protocol->UnRegister (
                                                 mSmmPeriodicTimerDispatch2Protocol,
                                                 DispatchHandle
                                                 );
  DEBUG ((DEBUG_INFO, "Heci2CommandQueueDispatchPeriodicSmiHandler Complete - Unregister Status = %r.\n", Status));
  ASSERT_EFI_ERROR (Status);

  //
  // Clean up global state for the next periodic SMI sequence
  //
  CircularBufferReset (&mCseCommandQueue);
  ZeroMem (mHeci2CommandQueueContext.CommandQueueVerificationBuffer, mHeci2CommandQueueContext.CommandQueueVerificationBufferSize);

  CurrentTotalCommandRequestSize          = 0;
  CurrentTotalCommandResponseSize         = 0;
  CurrentCommandAdvancementIndex          = 0;
  CurrentCommandIndex                     = 0;
  CurrentPeriodicSmiEntryCount            = 0;
  CurrentHeci2TrustedChannelRequestHeader = NULL;

  if (mVariableStorageSupportProtocol != NULL) {
    mVariableStorageSupportProtocol->NotifySmmIoComplete (&gCseVariableStorageProtocolInstanceGuid, Status);
  } else {
    DEBUG ((DEBUG_ERROR, "Failed to locate EDKII storage support protocol. Cannot notify the variable driver.\n"));
    ASSERT (FALSE);
  }

  return EFI_SUCCESS;
}

/**
  Start dispatch of commands in the given HECI2 NVM file command queue.

  @param[in]      CommandQueue                        A pointer to the command queue buffer.
  @param[in]      CommandQueueSize                    The command queue buffer size in bytes.
  @param[in]      CommandQueueVerificationBuffer      A pointer to the command queue verification buffer.
  @param[in]      CommandQueueVerificationBufferSize  The command queue verification buffer size in bytes.
  @param[in]      CommandQueueCount                   The number of items in the command queue.

  @retval         EFI_SUCCESS                         Command queue disptach was enabled successfully
  @retval         Others                              An error occurred starting command queue dispatch
**/
EFI_STATUS
EFIAPI
StartHeci2SmmCommandQueueDispatch (
  IN    UINT8     *CommandQueue,
  IN    UINTN     CommandQueueSize,
  IN    UINT8     *CommandQueueVerificationBuffer,
  IN    UINTN     CommandQueueVerificationBufferSize,
  IN    UINTN     CommandQueueCount
  )
{
  EFI_STATUS      Status;
  EFI_HANDLE      PeriodicSmiDispatchHandle;

  if (CommandQueue == NULL || CommandQueueVerificationBuffer == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  if (mSmiTickPeriod == 0) {
    DEBUG ((DEBUG_ERROR, "The periodic SMI tick period is invalid. Cannot register the HECI2 periodic SMI.\n"));
    return EFI_NOT_READY;
  }

  if (mCseVariableStorageSupportProtocol == NULL) {
    Status = gSmst->SmmLocateProtocol (
                      &gCseVariableStorageSupportProtocolGuid,
                      NULL,
                      (VOID **) &mCseVariableStorageSupportProtocol
                      );
    if (EFI_ERROR (Status)) {
      ASSERT_EFI_ERROR (Status);
      return Status;
    }
  }

  mHeci2CommandQueueContext.CommandQueueBuffer                 = CommandQueue;
  mHeci2CommandQueueContext.CommandQueueVerificationBuffer     = CommandQueueVerificationBuffer;
  mHeci2CommandQueueContext.CommandQueueBufferSize             = CommandQueueSize;
  mHeci2CommandQueueContext.CommandQueueVerificationBufferSize = CommandQueueVerificationBufferSize;
  mHeci2CommandQueueContext.CommandQueueCount                  = CommandQueueCount;
  mHeci2PeriodicSmiContext.WaitingForHeciReceive               = FALSE;

  DEBUG ((
    DEBUG_INFO,
    "    Starting HECI SMM CMD Queue Dispatch.\n    Periodic tick period = %dms.\n",
    (mSmiTickPeriod / 10000)
    ));

  mHeci2PeriodicSmiContext.RegisterContext.Period          = mSmiTickPeriod * 4;
  mHeci2PeriodicSmiContext.RegisterContext.SmiTickInterval = mSmiTickPeriod;

  return mSmmPeriodicTimerDispatch2Protocol->Register (
                                               mSmmPeriodicTimerDispatch2Protocol,
                                               Heci2CommandQueueDispatchPeriodicSmiHandler,
                                               &mHeci2PeriodicSmiContext.RegisterContext,
                                               &PeriodicSmiDispatchHandle
                                               );
}

/**
  Write data to NVM file through HECI2.

  @param[in] FileName          The file name.
  @param[in] Offset            The offset of data.
  @param[in] Data              The data content.
  @param[in] DataSize          Data's size.
  @param[in] Truncate          Truncate the file.
  @param[in] Heci2Protocol     A pointer to a valid instance of the EFI_HECI_PROTOCOL

  @return EFI_SUCCESS   Write NVM file success.
  @return Others        Write NVM file failed.
**/
EFI_STATUS
EFIAPI
Heci2WriteNVMFile (
  IN CONST CHAR8              *FileName,
  IN       UINT32             Offset,
  IN CONST UINT8              *Data,
  IN       UINTN              DataSize,
  IN       BOOLEAN            Truncate,
  IN CONST EFI_HECI_PROTOCOL  *Heci2Protocol
  )
{
  EFI_STATUS                  Status;
  UINT32                      SeCMode;
  UINT32                      ReceiveLength;
  UINTN                       CurrentWriteDataSize;

  if (FileName == NULL || Data == NULL || Heci2Protocol == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  DEBUG ((EFI_D_INFO, "BIOS Start Send HECI Message: Heci2WriteNVMFile\n"));
  DEBUG ((EFI_D_INFO, "    Truncate bit: %a.\n", (Truncate) ? "Set" : "Not set"));

  Status = HeciGetSeCMode (HECI1_DEVICE, &SeCMode);
  if (EFI_ERROR (Status) || (SeCMode != SEC_MODE_NORMAL)) {
    return EFI_UNSUPPORTED;
  }
  DEBUG ((EFI_D_INFO, "GetSeCMode successful\n"));

  mHeci2FileContext.FileName          = FileName;
  mHeci2FileContext.Offset            = Offset;
  mHeci2FileContext.Data              = Data;
  mHeci2FileContext.DataSize          = DataSize;
  mHeci2FileContext.Truncate          = Truncate;
  mHeci2FileContext.Heci2Protocol     = Heci2Protocol;
  mHeci2FileContext.RemainingDataSize = DataSize;
  ReceiveLength                       = sizeof (HECI2_TRUSTED_CHANNEL_BIOS_WRITE_RESP);

  do {
    Status = Heci2WriteNvmFileSend (&CurrentWriteDataSize);
    if (EFI_ERROR (Status)) {
      return Status;
    }

    Status = Heci2NvmFileReceive (BLOCKING, &Heci2DataBuffer[0], &ReceiveLength);
    if (EFI_ERROR (Status)) {
      return Status;
    }

    mHeci2FileContext.Data              += CurrentWriteDataSize;
    mHeci2FileContext.Offset            += (UINT32) CurrentWriteDataSize;
    mHeci2FileContext.RemainingDataSize -= CurrentWriteDataSize;
  } while (mHeci2FileContext.RemainingDataSize > 0);

  return Status;
}

/**
  Read NVM file data through HECI2.

  @param[in]     FileName       The file name.
  @param[in]     Offset         The offset of data.
  @param[out]    Data           The data buffer.
  @param[in,out] DataSize       Data's size.
  @param[in]     Heci2Protocol  Pointer to a valid instance of the EFI_HECI_PROTOCOL

  @return        EFI_SUCCESS    Read NVM file success.
  @return        Others         Read NVM file failed.
**/
EFI_STATUS
EFIAPI
Heci2ReadNVMFile (
  IN     CONST  UINT8               *FileName,
  IN            UINT32              Offset,
  OUT           UINT8               *Data,
  IN OUT        UINTN               *DataSize,
  IN     CONST  EFI_HECI_PROTOCOL   *Heci2Protocol
  )
{
  EFI_STATUS                            Status;
  UINT32                                HeciSendLength;
  UINT32                                HeciRecvLength;
  HECI2_TRUSTED_CHANNEL_BIOS_READ_REQ   *ReadFileMessage;
  HECI2_TRUSTED_CHANNEL_BIOS_READ_RESP  *ReadFileResponse;
  UINT32                                SeCMode;
  UINT32                                CurrentReadDataSize;
  INTN                                  RemainingDataSize;
  UINTN                                 TotalDataSize;

  BOOLEAN                               IsVerificationSuccessful = FALSE;

  DEBUG ((EFI_D_INFO, "BIOS Start Send HECI Message: Heci2ReadNVMFile\n"));
  DEBUG ((EFI_D_INFO, "Size of HECI2_READ_DATA_SIZE = 0x%x.\n", *DataSize));

  Status = HeciGetSeCMode (HECI1_DEVICE, &SeCMode);
  if (EFI_ERROR (Status) || (SeCMode != SEC_MODE_NORMAL)) {
    return EFI_UNSUPPORTED;
  }
  DEBUG ((EFI_D_INFO, "GetSeCMode successful\n"));

  RemainingDataSize = *DataSize;
  TotalDataSize     = 0;

  do {
    if (RemainingDataSize > MAX_HECI2_READ_DATA_SIZE) {
      CurrentReadDataSize = MAX_HECI2_READ_DATA_SIZE;
    } else {
      CurrentReadDataSize = (UINT32) RemainingDataSize;
    }

    ZeroMem (Heci2DataBuffer, sizeof (Heci2DataBuffer));

    ReadFileMessage = (HECI2_TRUSTED_CHANNEL_BIOS_READ_REQ *) Heci2DataBuffer;

    ReadFileMessage->Offset   = Offset;
    ReadFileMessage->DataSize = (UINT16) CurrentReadDataSize;

    ReadFileMessage->TrustedChannelHeader.CommandId  = HECI2_READ_DATA_CMD_ID;

    //
    // Copy the name of the NVM file to read
    //
    ASSERT (AsciiStrLen (FileName) <= sizeof (ReadFileMessage->FileName));
    ASSERT (sizeof (Heci2DataBuffer) > sizeof (HECI2_TRUSTED_CHANNEL_BIOS_READ_REQ));
    AsciiStrCpyS ((CHAR8 *) ReadFileMessage->FileName, sizeof (ReadFileMessage->FileName), (CONST CHAR8 *) FileName);

    //
    // Fill in the HMAC signature and update the monotonic counter
    //
    Status = mHeciTrustedChannelProtocol->UpdateTrustedHeader (
                                            Heci2DataBuffer,
                                            sizeof (HECI2_TRUSTED_CHANNEL_BIOS_READ_REQ),
                                            0
                                            );
    if (EFI_ERROR (Status)) {
      return Status;
    }

    DEBUG ((
      EFI_D_INFO,
      "HECI2 read data header size is[ %x  + %x ] = %x\n",
      sizeof (HECI2_TRUSTED_CHANNEL_BIOS_READ_RESP),
      CurrentReadDataSize,
      sizeof (HECI2_TRUSTED_CHANNEL_BIOS_READ_RESP) + CurrentReadDataSize
      ));

    HeciSendLength = sizeof (HECI2_TRUSTED_CHANNEL_BIOS_READ_REQ);
    HeciRecvLength = sizeof (HECI2_TRUSTED_CHANNEL_BIOS_READ_RESP) + CurrentReadDataSize;

    Status = Heci2Protocol->SendwACK (
               HECI2_DEVICE,
               (UINT32 *) Heci2DataBuffer,
               HeciSendLength,
               &HeciRecvLength,
               BIOS_FIXED_HOST_ADDR,
               HECI2_BIOS_MCA_FIXED_ADDR
               );
    if (EFI_ERROR (Status)) {
      break;
    }

    ReadFileResponse = (HECI2_TRUSTED_CHANNEL_BIOS_READ_RESP *) Heci2DataBuffer;
    //
    // Verify the response message:
    //  1. Verify the monotonic counter matches that of the request (in digest calculation)
    //  2. The signature is the HMAC-SHA2 of the response message including
    //     the monotonic counter but not including the signature itself.
    //
    Status = mHeciTrustedChannelProtocol->VerifyTrustedHeader (
                                            Heci2DataBuffer,
                                            sizeof (HECI2_TRUSTED_CHANNEL_BIOS_READ_RESP) + ReadFileResponse->DataSize,
                                            &IsVerificationSuccessful
                                            );
    if (EFI_ERROR (Status)) {
      break;
    } else if (!IsVerificationSuccessful) {
      DEBUG ((EFI_D_WARN, "HMAC SHA256 signature verification failed in Heci2ReadNVMFile!\n"));
      return EFI_SECURITY_VIOLATION;
    }


    DEBUG ((EFI_D_INFO, "HECI2Read CommandId        =%x\n", ReadFileMessage->TrustedChannelHeader.CommandId));
    DEBUG ((EFI_D_INFO, "HECI2Read RequestResponse  =%x\n", ReadFileMessage->TrustedChannelHeader.RequestResponse));
    DEBUG ((EFI_D_INFO, "HECI2Read Status           =%x\n", ReadFileResponse->Status));
    DEBUG ((EFI_D_INFO, "HECI2Read DataSize         =%x\n", ReadFileResponse->DataSize));

    CopyMem (
      Data,
      (VOID *) (((UINTN) Heci2DataBuffer) + sizeof (HECI2_TRUSTED_CHANNEL_BIOS_READ_RESP)),
      ReadFileResponse->DataSize
      );

    Data   += CurrentReadDataSize;
    Offset += CurrentReadDataSize;

    TotalDataSize     += ReadFileResponse->DataSize;
    RemainingDataSize -= CurrentReadDataSize;

  } while (RemainingDataSize > 0);

  *DataSize = TotalDataSize;
  DEBUG ((EFI_D_INFO, " Heci2ReadNVMFile - Total DataSize = %x\n", *DataSize));

  return Status;
}

/**
  EDKII Variable Storage Support Protocol notification callback.

  @param   Protocol      Points to the protocol's unique identifier
  @param   Interface     Points to the interface instance
  @param   Handle        The handle on which the interface was installed

  @retval  EFI_SUCCESS   The callback function ran successfully
  @retval  EFI_NOT_FOUND The EDKII_VARIABLE_STORAGE_SUPPORT_PROTOCOL could not be found
**/
EFI_STATUS
EFIAPI
VariableStorageSupportProtocolNotify (
  IN CONST EFI_GUID      *Protocol,
  IN VOID                *Interface,
  IN EFI_HANDLE          Handle
  )
{
  EFI_STATUS         Status;

  Status = gSmst->SmmLocateProtocol (
                    &gEdkiiVariableStorageSupportProtocolGuid,
                    NULL,
                    (VOID **) &mVariableStorageSupportProtocol
                    );
  ASSERT_EFI_ERROR (Status);
  if (mVariableStorageSupportProtocol == NULL) {
    return EFI_NOT_FOUND;
  }

  return Status;
}

/**
  The constructor initializes the library.

  @param  ImageHandle   The firmware allocated handle for the EFI image.
  @param  SystemTable   A pointer to the EFI System Table.

  @retval EFI_SUCCESS   The constructor always returns EFI_SUCCESS.

**/
EFI_STATUS
EFIAPI
SmmHeci2MsgLibConstructor (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  EFI_STATUS  Status;
  VOID        *EdkiiVariableStorageSupportProtocolRegistration;

  Status = gSmst->SmmLocateProtocol (
                    &gEfiHeciTrustedChannelSmmProtocolGuid,
                    NULL,
                    (VOID **) &mHeciTrustedChannelProtocol
                    );
  ASSERT_EFI_ERROR (Status);

  Status = gSmst->SmmLocateProtocol (
                    &gEfiSmmPeriodicTimerDispatch2ProtocolGuid,
                    NULL,
                    (VOID **) &mSmmPeriodicTimerDispatch2Protocol
                    );
  ASSERT_EFI_ERROR (Status);

  Status = gSmst->SmmRegisterProtocolNotify (
                    &gEdkiiVariableStorageSupportProtocolGuid,
                    VariableStorageSupportProtocolNotify,
                    &EdkiiVariableStorageSupportProtocolRegistration
                    );
  ASSERT_EFI_ERROR (Status);

  Status = FindPeriodicTimerInterval ();
  ASSERT_EFI_ERROR (Status);

  return Status;
}