/** @file
  This file defines the Variable Storage Command Queue module.

 @copyright
  INTEL CONFIDENTIAL
  Copyright 2016 - 2017 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 "CseVariableStorageCommandQueueRuntimeDxe.h"

#include <MkhiMsgs.h>

#include <Library/BaseMemoryLib.h>
#include <Library/MemoryAllocationLib.h>

EFI_HECI_PROTOCOL                 *mHeciProtocol              = NULL;
EFI_SMM_COMMUNICATION_PROTOCOL    *mSmmCommunicationProtocol  = NULL;

UINT8         *mCseVariableStorageBuffer                      = NULL; ///< Host buffer reserved for dispatch data communication with SMM
STATIC UINT8  *mCseVariableStorageBufferPhysical              = NULL; ///< Maintains a pointer to the physical buffer address

UINT8         *mCseVariableStorageResponseVerificationBuffer         = NULL; ///< Host buffer reserved for response verification with SMM
STATIC UINT8  *mCseVariableStorageResponseVerificationBufferPhysical = NULL; ///< Maintains a pointer to the physical buffer address

UINT8         *mCseVariableStorageDebugBuffer                             = NULL; ///< Host buffer reserved for debug communication with SMM
STATIC UINT8  *mCseVariableStorageDebugBufferPhysical                     = NULL; ///< Maintains a pointer to the physical buffer address

UINT8         *mCseVariableStorageNoPayloadCommunicateBuffer              = NULL; ///< Header for messages with no payload
STATIC UINT8  *mCseVariableStorageNoPayloadCommunicateBufferPhysical      = NULL; ///< Maintains a pointer to the physical buffer address

STATIC UINTN  mCseVariableStoragePayloadSize;                        ///< Size allocated for the dispatch data payload in SMM
STATIC UINTN  mCseVariableStorageBufferSize;                         ///< Size allocated for the dispatch data communicate buffer
STATIC UINTN  mCseVariableStorageResponseVerificationBufferSize;     ///< Size allocated for the response verification communicate buffer
STATIC UINTN  mCseVariableStorageDebugBufferSize;                    ///< Size allocated for the Debug communicate buffer
STATIC UINTN  mCseVariableStorageNoPayloadCommunicateBufferSize;     ///< Size allocated for the no payload communicate buffer

STATIC BOOLEAN mCseVariableStorageAfterEndOfService  = FALSE;        ///< Indicates the CSE End of Services event has occurred

/**
  Initialize the communicate buffer using DataSize and Function.

  The communicate size is: SMM_COMMUNICATE_HEADER_SIZE + SMM_CSE_VARIABLE_STORAGE_COMMUNICATE_HEADER_SIZE + DataSize.

  @param[out]    DataPtr                     Points to the data in the communicate buffer.
  @param[in,out] CommBuffer                  A pointer to the communicate buffer.
  @param[in]     DataSize                    The data size to send to SMM.
  @param[in]     Function                    The function number used to initialize the communicate header.

  @retval        EFI_SUCCESS                 The communicate buffer was initialized successfully.
  @retval        EFI_BUFFER_TOO_SMALL        The communicate buffer is too small for the given data size.
  @retval        EFI_INVALID_PARAMETER       The DataSize given is too large or CommBuffer is NULL.

**/
EFI_STATUS
EFIAPI
InitCommunicateBuffer (
  OUT            VOID                        **DataPtr OPTIONAL,
  IN  OUT        UINT8                       *CommBuffer,
  IN             UINTN                       CommBufferSize,
  IN             UINTN                       DataSize,
  IN             UINTN                       Function
  )
{
  EFI_SMM_COMMUNICATE_HEADER                  *SmmCommunicateHeader;
  SMM_CSE_VARIABLE_STORAGE_COMMUNICATE_HEADER *SmmCseVariableStorageFunctionHeader;

  if (CommBuffer == NULL) {
    return EFI_INVALID_PARAMETER;
  }
  if (SMM_CSE_VARIABLE_STORAGE_GET_TOTAL_COMMUNICATE_BUFFER_SIZE (DataSize) > CommBufferSize) {
    return EFI_BUFFER_TOO_SMALL;
  }

  SmmCommunicateHeader = (EFI_SMM_COMMUNICATE_HEADER *) CommBuffer;
  CopyGuid (&SmmCommunicateHeader->HeaderGuid, &gCseVariableStorageProtocolInstanceGuid);
  SmmCommunicateHeader->MessageLength = DataSize + SMM_CSE_VARIABLE_STORAGE_COMMUNICATE_HEADER_SIZE;

  SmmCseVariableStorageFunctionHeader = (SMM_CSE_VARIABLE_STORAGE_COMMUNICATE_HEADER *) SmmCommunicateHeader->Data;
  SmmCseVariableStorageFunctionHeader->Function     = Function;
  SmmCseVariableStorageFunctionHeader->ReturnStatus = EFI_SUCCESS;
  if (DataPtr != NULL) {
    *DataPtr = SmmCseVariableStorageFunctionHeader->Data;
  }

  return EFI_SUCCESS;
}

/**
  Send the data in the communicate buffer to SMM.

  @param[in,out] CommBuffer                  A pointer to the SMM communicate buffer.
  @param[in,out] CommBufferPhysical          A pointer to the physical SMM communicate buffer.
  @param[in]     DataSize                    This size of the function header and data.

  @retval        EFI_SUCCESS                 The SMI handler returned EFI_SUCCESS.
  @retval        Others                      A failure was returned from the SMI handler.

**/
EFI_STATUS
EFIAPI
SendCommunicateBuffer (
  IN OUT         UINT8                       *CommBuffer,
  IN OUT         UINT8                       *CommBufferPhysical,
  IN             UINTN                       DataSize
  )
{
  EFI_STATUS                                   Status;
  UINTN                                        CommSize;
  EFI_SMM_COMMUNICATE_HEADER                   *SmmCommunicateHeader;
  SMM_CSE_VARIABLE_STORAGE_COMMUNICATE_HEADER  *SmmCseVariableStorageFunctionHeader;

  CommSize = SMM_CSE_VARIABLE_STORAGE_GET_TOTAL_COMMUNICATE_BUFFER_SIZE (DataSize);
  Status = mSmmCommunicationProtocol->Communicate (mSmmCommunicationProtocol, CommBufferPhysical, &CommSize);
  //ASSERT_EFI_ERROR (Status);

  SmmCommunicateHeader                = (EFI_SMM_COMMUNICATE_HEADER *) CommBuffer;
  SmmCseVariableStorageFunctionHeader = (SMM_CSE_VARIABLE_STORAGE_COMMUNICATE_HEADER *) SmmCommunicateHeader->Data;

  return SmmCseVariableStorageFunctionHeader->ReturnStatus;
}

/**
  Sends a debug message to SMM.

  @param[in] DebugMessageString          A pointer to a debug message string.
  @param[in] DebugMessageData            A pointer to a data buffer that should be printed.
  @param[in] DebugMessageDataLength      The length in bytes of the data buffer.

  @retval    EFI_SUCCESS                 The debug message was communicated successfully.
  @retval    Others                      A failure occurred sending the debug message.

**/
STATIC
EFI_STATUS
EFIAPI
RuntimeDebugMessage (
  IN    CHAR8     *DebugMessageString,
  IN    UINT8     *DebugMessageData,      OPTIONAL
  IN    UINTN     DebugMessageDataLength  OPTIONAL
  )
{
  EFI_STATUS    Status = EFI_SUCCESS;

  DEBUG_CODE_BEGIN();

  SMM_CSE_VARIABLE_STORAGE_COMMUNICATE_DEBUG_MSG  *SmmDebugMsg = NULL;
  UINTN       PayloadSize;

  if (DebugMessageString == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  PayloadSize = sizeof (SMM_CSE_VARIABLE_STORAGE_COMMUNICATE_DEBUG_MSG);

  Status = InitCommunicateBuffer (
             (VOID ** ) &SmmDebugMsg,
             mCseVariableStorageDebugBuffer,
             mCseVariableStorageDebugBufferSize,
             PayloadSize,
             SMM_CSE_VARIABLE_STORAGE_FUNCTION_DEBUG_MESSAGE
             );
  if (EFI_ERROR (Status)) {
    return Status;
  }

  if (SmmDebugMsg == NULL) {
    return EFI_OUT_OF_RESOURCES;
  }
  CopyMem ((VOID *) &SmmDebugMsg->DebugMsg[0], (VOID *) DebugMessageString, AsciiStrSize (DebugMessageString));
  SmmDebugMsg->Length = (UINT8) AsciiStrSize (DebugMessageString);

  if (DebugMessageData != NULL) {
    CopyMem (
      (VOID *) &SmmDebugMsg->Data[0],
      (VOID *) DebugMessageData,
      MIN (sizeof (SmmDebugMsg->Data), DebugMessageDataLength)
      );
    SmmDebugMsg->Datalength = (UINT8) MIN (sizeof (SmmDebugMsg->Data), DebugMessageDataLength);
  } else {
    SmmDebugMsg->Datalength = 0;
  }
  Status = SendCommunicateBuffer (
             mCseVariableStorageDebugBuffer,
             mCseVariableStorageDebugBufferPhysical,
             PayloadSize
             );

  DEBUG_CODE_END();

  return Status;
}

/**
  Sets the CSE proxy state in SMM.

  @param[in]     ProxyState                  The state of the CSE Proxy Driver

  @retval        EFI_SUCCESS                 The proxy state update was successful.
  @retval        Others                      The proxy state update failed.
**/
EFI_STATUS
EFIAPI
CseVariableStorageSetProxyState (
  IN  HECI_PROXY_STATE    ProxyState
  )
{
  EFI_STATUS                                                      Status;
  UINTN                                                           CommSize;
  UINT8                                                           *CommBuffer                           = NULL;
  EFI_SMM_COMMUNICATE_HEADER                                      *SmmCommunicateHeader                 = NULL;
  SMM_CSE_VARIABLE_STORAGE_COMMUNICATE_SET_PROXYSTATE_MSG_HEADER  *SetProxyStateMsgHeader               = NULL;
  SMM_CSE_VARIABLE_STORAGE_COMMUNICATE_HEADER                     *SmmCseVariableStorageFunctionHeader  = NULL;

  CommSize = SMM_CSE_VARIABLE_STORAGE_GET_TOTAL_COMMUNICATE_BUFFER_SIZE (
               sizeof (SMM_CSE_VARIABLE_STORAGE_COMMUNICATE_SET_PROXYSTATE_MSG_HEADER)
               );
  //
  // Reuse the verification buffer
  //
  //ASSERT (mCseVariableStorageResponseVerificationBufferSize >= CommSize);
  if (mCseVariableStorageResponseVerificationBuffer == NULL) {
    return EFI_OUT_OF_RESOURCES;
  }
  ZeroMem (mCseVariableStorageResponseVerificationBuffer, mCseVariableStorageResponseVerificationBufferSize);
  CommBuffer = mCseVariableStorageResponseVerificationBuffer;
  SmmCommunicateHeader = (EFI_SMM_COMMUNICATE_HEADER *) CommBuffer;

  Status = InitCommunicateBuffer (
             (VOID **) &SetProxyStateMsgHeader,
             CommBuffer,
             CommSize,
             sizeof (SMM_CSE_VARIABLE_STORAGE_COMMUNICATE_SET_PROXYSTATE_MSG_HEADER),
             SMM_CSE_VARIABLE_STORAGE_FUNCTION_SET_PROXY_STATE
             );
  if (EFI_ERROR (Status)) {
    return Status;
  } else if (SetProxyStateMsgHeader == NULL) {
    return EFI_OUT_OF_RESOURCES;
  }
  SetProxyStateMsgHeader->ProxyState = ProxyState;

  //
  // Send the communicate data to SMM
  //
  Status = mSmmCommunicationProtocol->Communicate (
                                        mSmmCommunicationProtocol,
                                        mCseVariableStorageResponseVerificationBufferPhysical,
                                        &CommSize
                                        );
  //ASSERT_EFI_ERROR (Status);
  if (EFI_ERROR (Status)) {
    return Status;
  }

  SmmCseVariableStorageFunctionHeader = (SMM_CSE_VARIABLE_STORAGE_COMMUNICATE_HEADER *) SmmCommunicateHeader->Data;
  Status = SmmCseVariableStorageFunctionHeader->ReturnStatus;

  return Status;
}

/**
  Gets the currently known status of the CSE Proxy Driver from SMM as well as
  the HMAC SHA256 GetProxyState HECI message

  @param[out]    GetProxyStateHeciMessage    A pointer to a GetProxyState HECI message
  @param[out]    CurrentProxyState           The currently known state of the CSE Proxy Driver

  @retval        EFI_SUCCESS                 Successfully acquired a GetProxyState message header.
  @retval        Others                      An error occurred attempting to get the GetProxyState message header.

**/
EFI_STATUS
EFIAPI
CseVariableStorageGetProxyStateFromSmm (
  OUT  HECI2_TRUSTED_CHANNEL_BIOS_GETPROXYSTATE_MSG  *GetProxyStateHeciMessage,
  OUT  HECI_PROXY_STATE                              *CurrentProxyState
  )
{
  EFI_STATUS        Status;
  UINTN             CommSize;

  EFI_SMM_COMMUNICATE_HEADER                                     *SmmCommunicateHeader                        = NULL;
  SMM_CSE_VARIABLE_STORAGE_COMMUNICATE_GET_PROXYSTATE_MSG_HEADER *SmmCseVariableStorageGetProxyStateMsgHeader = NULL;
  SMM_CSE_VARIABLE_STORAGE_COMMUNICATE_HEADER                    *SmmCseVariableStorageFunctionHeader         = NULL;
  UINT8                                                          *CommBuffer                                  = NULL;

  RuntimeDebugMessage ("Start of CseVariableStorageGetProxyStateFromSmm()\n", NULL, 0);

  if (GetProxyStateHeciMessage == NULL || CurrentProxyState == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  CommSize = SMM_CSE_VARIABLE_STORAGE_GET_TOTAL_COMMUNICATE_BUFFER_SIZE (
               sizeof (SMM_CSE_VARIABLE_STORAGE_COMMUNICATE_GET_PROXYSTATE_MSG_HEADER)
               );

  //
  // Reuse the verification buffer
  //
  //ASSERT (mCseVariableStorageResponseVerificationBufferSize >= CommSize);
  if (mCseVariableStorageResponseVerificationBuffer == NULL) {
    return EFI_OUT_OF_RESOURCES;
  }
  ZeroMem (mCseVariableStorageResponseVerificationBuffer, mCseVariableStorageResponseVerificationBufferSize);
  CommBuffer = mCseVariableStorageResponseVerificationBuffer;
  SmmCommunicateHeader = (EFI_SMM_COMMUNICATE_HEADER *) CommBuffer;

  //
  // Initialize the communicate buffer
  //
  Status = InitCommunicateBuffer (
             (VOID **) &SmmCseVariableStorageGetProxyStateMsgHeader,
             CommBuffer,
             CommSize,
             sizeof (SMM_CSE_VARIABLE_STORAGE_COMMUNICATE_GET_PROXYSTATE_MSG_HEADER),
             SMM_CSE_VARIABLE_STORAGE_FUNCTION_GET_PROXY_STATE
             );
  if (EFI_ERROR (Status)) {
    return Status;
  } else if (SmmCseVariableStorageGetProxyStateMsgHeader == NULL) {
    return EFI_OUT_OF_RESOURCES;
  }

  //
  // Send the communicate data to SMM
  //
  Status = mSmmCommunicationProtocol->Communicate (
                                        mSmmCommunicationProtocol,
                                        mCseVariableStorageResponseVerificationBufferPhysical,
                                        &CommSize
                                        );
  //ASSERT_EFI_ERROR (Status);
  if (EFI_ERROR (Status)) {
    return Status;
  }

  SmmCseVariableStorageFunctionHeader = (SMM_CSE_VARIABLE_STORAGE_COMMUNICATE_HEADER *) SmmCommunicateHeader->Data;
  Status = SmmCseVariableStorageFunctionHeader->ReturnStatus;
  if (EFI_ERROR (Status)) {
    return Status;
  }

  ZeroMem (GetProxyStateHeciMessage, sizeof (HECI2_TRUSTED_CHANNEL_BIOS_GETPROXYSTATE_MSG));
  CopyMem (
    (VOID *) GetProxyStateHeciMessage,
    (VOID *) &SmmCseVariableStorageGetProxyStateMsgHeader->MessageHeader,
    sizeof (HECI2_TRUSTED_CHANNEL_BIOS_HEADER)
    );
  *CurrentProxyState = SmmCseVariableStorageGetProxyStateMsgHeader->ProxyState;

  return EFI_SUCCESS;
}

/**
  Requests the CSE OS proxy driver state.

  @param[out]    IsProxyPathAvailable       Indicates if the CSE OS proxy driver is available.

  @retval        EFI_SUCCESS                An error did not occur attempting to get proxy state.
  @retval        Others                     An error occurred attempting to get proxy state.

**/
EFI_STATUS
EFIAPI
CseVariableStorageGetProxyState (
  OUT BOOLEAN                               *IsProxyPathAvailable
  )
{
  EFI_STATUS                                   Status;
  HECI2_TRUSTED_CHANNEL_BIOS_GETPROXYSTATE_MSG GetProxyStateHeciMessage;
  UINT32                                       GetProxyStateMessageResponseSize;
  HECI_PROXY_STATE                             CurrentProxyState;

  RuntimeDebugMessage ("Start of CseVariableStorageGetProxyState()\n", NULL, 0);

  Status = CseVariableStorageGetProxyStateFromSmm (
             &GetProxyStateHeciMessage,
             &CurrentProxyState
             );
  if (EFI_ERROR (Status)) {
    return Status;
  } else if (CurrentProxyState != HeciProxyStateUnknown) {
    if (CurrentProxyState == HeciProxyStatePresent) {
      *IsProxyPathAvailable = TRUE;
    } else {
      *IsProxyPathAvailable = FALSE;
    }
    return EFI_SUCCESS;
  }
  GetProxyStateMessageResponseSize = sizeof (HECI2_TRUSTED_CHANNEL_BIOS_GETPROXYSTATE_MSG);

  //
  // Attempt to get the proxy state with the HECI2 Runtime DXE protocol
  //
  Status = mHeciProtocol->SendwACK (
                            HECI2_DEVICE,
                            (UINT32 *) &GetProxyStateHeciMessage,
                            sizeof (HECI2_TRUSTED_CHANNEL_BIOS_HEADER),
                            &GetProxyStateMessageResponseSize,
                            BIOS_FIXED_HOST_ADDR,
                            HECI2_BIOS_MCA_FIXED_ADDR
                            );
  if (Status == EFI_TIMEOUT) {
    *IsProxyPathAvailable = FALSE;
    Status = CseVariableStorageSetProxyState (HeciProxyStateNotPresent);
    //ASSERT_EFI_ERROR (Status);
    return Status;
  } else if (EFI_ERROR (Status)) {
    //ASSERT_EFI_ERROR (Status);
    return Status;
  }

  *IsProxyPathAvailable = (BOOLEAN) (GetProxyStateHeciMessage.Status == 0x0);
  if (*IsProxyPathAvailable) {
    Status = CseVariableStorageSetProxyState (HeciProxyStatePresent);
    //ASSERT_EFI_ERROR (Status);
  } else {
    Status = CseVariableStorageSetProxyState (HeciProxyStateNotPresent);
    //ASSERT_EFI_ERROR (Status);
  }
  RuntimeDebugMessage (
    "Sent HECI RT Proxy State message return value:\n",
    (UINT8 *) &GetProxyStateHeciMessage.Status,
    sizeof (UINT8)
    );

  return Status;
}

/**
  Notification function for the CSE End of Services event.

  @param[in]  Event        Event whose notification function is being invoked.
  @param[in]  Context      Pointer to the notification function's context.

**/
VOID
EFIAPI
CseVariableStorageEndOfServicesEvent (
  IN EFI_EVENT             Event,
  IN VOID                  *Context
  )
{
  //
  // The CSE OS proxy driver should not be loaded prior to Exit Boot Services
  //
  //ASSERT (!mCseVariableStorageAfterEndOfService);

  mCseVariableStorageAfterEndOfService = TRUE;
}

/**
  This code gets the CSE variable storage payload size allocated in SMRAM.

  This function should only be called once to determine the maximum payload size.

  @param[out]    VariablePayloadSize         Output pointer to the payload size.

  @retval        EFI_SUCCESS                 Successfully got the payload size.
  @retval        Others                      An error occurred getting the payload size.

**/
EFI_STATUS
EFIAPI
GetCseVariableStoragePayloadSize (
  OUT            UINTN                       *CseVariableStoragePayloadSize
  )
{
  EFI_STATUS  Status;
  UINTN       CommSize;

  EFI_SMM_COMMUNICATE_HEADER                            *SmmCommunicateHeader                = NULL;
  SMM_CSE_VARIABLE_STORAGE_COMMUNICATE_GET_PAYLOAD_SIZE *SmmCseVariableStorageGetPayloadSize = NULL;
  SMM_CSE_VARIABLE_STORAGE_COMMUNICATE_HEADER           *SmmCseVariableStorageFunctionHeader = NULL;
  UINT8                                                 *CommBuffer                          = NULL;

  CommSize = SMM_CSE_VARIABLE_STORAGE_GET_TOTAL_COMMUNICATE_BUFFER_SIZE (
               sizeof (SMM_CSE_VARIABLE_STORAGE_COMMUNICATE_GET_PAYLOAD_SIZE)
               );

  CommBuffer = AllocateZeroPool (CommSize);
  if (CommBuffer == NULL) {
    Status = EFI_OUT_OF_RESOURCES;
    goto Done;
  }

  SmmCommunicateHeader = (EFI_SMM_COMMUNICATE_HEADER *) CommBuffer;

  //
  // Initialize the communicate buffer
  //
  Status = InitCommunicateBuffer (
             (VOID **) &SmmCseVariableStorageGetPayloadSize,
             CommBuffer,
             CommSize,
             sizeof (SMM_CSE_VARIABLE_STORAGE_COMMUNICATE_GET_PAYLOAD_SIZE),
             SMM_CSE_VARIABLE_STORAGE_FUNCTION_GET_PAYLOAD_SIZE
             );
  if (EFI_ERROR (Status)) {
    goto Done;
  } else if (SmmCseVariableStorageGetPayloadSize == NULL) {
    Status = EFI_OUT_OF_RESOURCES;
    goto Done;
  }

  //
  // Send the communicate data to SMM
  //
  Status = mSmmCommunicationProtocol->Communicate (mSmmCommunicationProtocol, CommBuffer, &CommSize);
  //ASSERT_EFI_ERROR (Status);

  SmmCseVariableStorageFunctionHeader = (SMM_CSE_VARIABLE_STORAGE_COMMUNICATE_HEADER *) SmmCommunicateHeader->Data;
  Status = SmmCseVariableStorageFunctionHeader->ReturnStatus;
  if (EFI_ERROR (Status)) {
    goto Done;
  }

  //
  // Return the payload size to the caller
  //
  *CseVariableStoragePayloadSize = SmmCseVariableStorageGetPayloadSize->PayloadSize;

Done:
  if (CommBuffer != NULL) {
    FreePool (CommBuffer);
  }

  return Status;
}

/**
  Initializes module buffers used in this module.

  @retval        EFI_SUCCESS                 The buffers were initialized successfully.
  @retval        Others                      The buffers could not be initialized.

**/
EFI_STATUS
EFIAPI
InitializeModuleBuffers (
  VOID
  )
{
  EFI_STATUS  Status;

  Status = GetCseVariableStoragePayloadSize (&mCseVariableStoragePayloadSize);
  if (EFI_ERROR (Status)) {
    //ASSERT_EFI_ERROR (Status);
    return Status;
  }

  //
  // Allocate buffer used for dispatch data communication
  //   BufferSize = EFI SMM Header Size + CSE Variable Storage SMM Header Size + SMRAM Buffer Size
  //   Multiple commands can be returned in the command queue.
  //
  mCseVariableStorageBufferSize = SMM_CSE_VARIABLE_STORAGE_GET_TOTAL_COMMUNICATE_BUFFER_SIZE (
                                    mCseVariableStoragePayloadSize
                                    );

  mCseVariableStorageBuffer = AllocateRuntimeZeroPool (mCseVariableStorageBufferSize);
  if (mCseVariableStorageBuffer == NULL) {
    //ASSERT (mCseVariableStorageBuffer != NULL);
    return EFI_OUT_OF_RESOURCES;
  }
  mCseVariableStorageBufferPhysical = mCseVariableStorageBuffer;

  //
  // Allocate buffer used for response verification communication
  //   BufferSize = EFI_SMM_HEADER_SIZE + CSE Variable Storage SMM Header Size + Maximum Command Size (1 command)
  //   Single commands will be sent to SMM for verification
  //
  mCseVariableStorageResponseVerificationBufferSize = SMM_CSE_VARIABLE_STORAGE_GET_TOTAL_COMMUNICATE_BUFFER_SIZE (
                                                        CMD_QUEUE_SINGLE_CMD_BUFFER_SIZE
                                                        );
  mCseVariableStorageResponseVerificationBuffer = AllocateRuntimeZeroPool (
                                                    mCseVariableStorageResponseVerificationBufferSize
                                                    );
  if (mCseVariableStorageResponseVerificationBuffer == NULL) {
    //ASSERT (mCseVariableStorageResponseVerificationBuffer != NULL);
    return EFI_OUT_OF_RESOURCES;
  }
  mCseVariableStorageResponseVerificationBufferPhysical = mCseVariableStorageResponseVerificationBuffer;

  //
  // Allocate buffer used to send communication with no payload
  //
  mCseVariableStorageNoPayloadCommunicateBufferSize = SMM_CSE_VARIABLE_STORAGE_GET_TOTAL_COMMUNICATE_BUFFER_SIZE (0);
  mCseVariableStorageNoPayloadCommunicateBuffer = AllocateRuntimeZeroPool (
                                                    mCseVariableStorageNoPayloadCommunicateBufferSize
                                                    );
  if (mCseVariableStorageNoPayloadCommunicateBuffer == NULL) {
    //ASSERT (mCseVariableStorageNoPayloadCommunicateBuffer != NULL);
    return EFI_OUT_OF_RESOURCES;
  }
  mCseVariableStorageNoPayloadCommunicateBufferPhysical = mCseVariableStorageNoPayloadCommunicateBuffer;

  //
  // Allocate buffer used for debug communication to SMM
  //
  mCseVariableStorageDebugBufferSize = SMM_CSE_VARIABLE_STORAGE_GET_TOTAL_COMMUNICATE_BUFFER_SIZE (
                                        sizeof (SMM_CSE_VARIABLE_STORAGE_COMMUNICATE_DEBUG_MSG)
                                        );

  mCseVariableStorageDebugBuffer = AllocateRuntimeZeroPool (mCseVariableStorageDebugBufferSize);
  if (mCseVariableStorageDebugBuffer == NULL) {
    //ASSERT (mCseVariableStorageDebugBuffer != NULL);
    return EFI_OUT_OF_RESOURCES;
  }
  mCseVariableStorageDebugBufferPhysical = mCseVariableStorageDebugBuffer;

  DEBUG ((EFI_D_INFO, "CSE Variable Storage Command Queue Buffer Sizes:\n"));
  DEBUG ((EFI_D_INFO, "  Data Payload Size: %d bytes.\n", mCseVariableStoragePayloadSize));
  DEBUG ((EFI_D_INFO, "  Wrapper Buffer Size: %d bytes.\n", mCseVariableStorageBufferSize));
  DEBUG ((EFI_D_INFO, "  Wrapper Verification Buffer Size: %d bytes.\n", mCseVariableStorageResponseVerificationBufferSize));

  return EFI_SUCCESS;
}

/**
  Verifies an HMAC command response from CSE in SMM.

  Note: IsLastCommand is only valid in a GetVariable() transaction. It is assumed there will only be a single
        GetVariable() request in the command queue at a time. SetVariable() does require IsLastComamnd to be
        valid as it does not update the non-volatile cache in the core variable driver on completion.

  @param[in]     IsLastCommand               Indicates if the is the final command in the current dispatch sequence.
  @param[in]     CommandResponseHeaderSize   The size in bytes of the current command response header.
  @param[in]     CommandResponseTotalSize     The size in bytes of the data being verified.
  @param[in]     CommandResponseData         A pointer to the HECI reponse data to be verified.

  @retval        EFI_SUCCESS                 The message was successfully verified.
  @retval        EFI_BUFFER_TOO_SMALL        The verification buffer is too small for the response data size.
  @retval        EFI_SECURITY_VIOLATION      The message failed verification.
  @retval        Others                      An error occurred verifying the message.

**/
EFI_STATUS
EFIAPI
VerifyHmacCommandResponse (
  IN             BOOLEAN                    IsLastCommand,
  IN             UINTN                      CommandResponseHeaderSize,
  IN             UINTN                      CommandResponseTotalSize,
  IN             UINT8                      *CommandResponseData
  )
{
  EFI_STATUS  Status;
  UINTN       CommunicateDataSize;
  SMM_CSE_VARIABLE_STORAGE_COMMUNICATE_VERIFY_RESPONSE *SmmVerifyResponseHeader = NULL;

  if (CommandResponseData == NULL) {
    return EFI_INVALID_PARAMETER;
  } else if (CommandResponseTotalSize == 0) {
    return EFI_SUCCESS;
  }

  CommunicateDataSize = OFFSET_OF (SMM_CSE_VARIABLE_STORAGE_COMMUNICATE_VERIFY_RESPONSE, Data) +
                          CommandResponseTotalSize;

  RuntimeDebugMessage (
    "Preparing to send VerifyHmac SMI. CommandResponseHeaderSize:\n",
    (UINT8 *) &CommandResponseHeaderSize,
    sizeof (UINTN)
    );
  RuntimeDebugMessage (
    "Preparing to send VerifyHmac SMI. CommandResponseTotalSize:\n",
    (UINT8 *) &CommandResponseTotalSize,
    sizeof (UINTN)
    );
  RuntimeDebugMessage (
    "Preparing to send VerifyHmac SMI. CommunicateDataSize:\n",
    (UINT8 *) &CommunicateDataSize,
    sizeof (UINTN)
    );

  if (
    mCseVariableStorageResponseVerificationBufferSize <
    SMM_CSE_VARIABLE_STORAGE_GET_TOTAL_COMMUNICATE_BUFFER_SIZE (CommunicateDataSize)
    ) {
      //
      // The verification data size has exceeded the size of a single command
      //
      //ASSERT (FALSE);
      return EFI_OUT_OF_RESOURCES;
  }


  Status = InitCommunicateBuffer (
             (VOID **) &SmmVerifyResponseHeader,
             mCseVariableStorageResponseVerificationBuffer,
             mCseVariableStorageResponseVerificationBufferSize,
             CommunicateDataSize,
             SMM_CSE_VARIABLE_STORAGE_FUNCTION_VERIFY_RESPONSE
             );
  if (EFI_ERROR (Status)) {
    return Status;
  }
  if (SmmVerifyResponseHeader == NULL) {
    //ASSERT (SmmVerifyResponseHeader != NULL);
    return EFI_OUT_OF_RESOURCES;
  }

  SmmVerifyResponseHeader->IsLastCommand = IsLastCommand;
  SmmVerifyResponseHeader->TrustedChannelHeaderSize = CommandResponseHeaderSize;

  RuntimeDebugMessage (
    "Response header sent for verification:\n",
    (UINT8 *) &SmmVerifyResponseHeader->Data[0],
    CommandResponseTotalSize
    );

  Status = SendCommunicateBuffer (
             mCseVariableStorageResponseVerificationBuffer,
             mCseVariableStorageResponseVerificationBufferPhysical,
             CommunicateDataSize
             );

  return Status;
}

/**
  Resets the command queue to an empty state.

  @retval        EFI_SUCCESS                 The command queue was reset successfully
  @retval        Others                      The command queue could not be reset.
**/
EFI_STATUS
EFIAPI
ResetSmmCommandQueue (
  VOID
  )
{
  EFI_STATUS  Status;

  ZeroMem (mCseVariableStorageNoPayloadCommunicateBuffer, mCseVariableStorageNoPayloadCommunicateBufferSize);
  Status = InitCommunicateBuffer (
             NULL,
             mCseVariableStorageNoPayloadCommunicateBuffer,
             mCseVariableStorageNoPayloadCommunicateBufferSize,
             0,
             SMM_CSE_VARIABLE_STORAGE_FUNCTION_RESET_COMMAND_QUEUE
             );
  if (EFI_ERROR (Status)) {
    return Status;
  }

  Status = SendCommunicateBuffer (
             mCseVariableStorageNoPayloadCommunicateBuffer,
             mCseVariableStorageNoPayloadCommunicateBufferPhysical,
             0
             );

  return Status;
}

/**
  Dispatches all the commands in the CSE variable storage command queue.

  This function will not return until all commands are dispatched.

  @param[in,out] CommandQueueBuffer          A pointer to a buffer of HECI2 trusted channel commands (the command queue).
  @param[in]     CommandQueueCount           The total number of commands in the command queue.

  @retval        EFI_SUCCESS                 The command queue was dispatched successfully.
  @retval        EFI_INVALID_PARAMETER       The command queue buffer pointer is null or the internal buffer is null.
  @retval        EFI_DEVICE_ERROR            The HECI command was not recognized.
  @retval        EFI_ABORTED                 An internal error occurred in the command queue and dispatch was aborted.
  @retval        EFI_SECURITY_VIOLATION      The command response data failed HMAC verification.
  @retval        Others                      The command queue could not be dispatched successfully.

**/
EFI_STATUS
EFIAPI
PerformCommandQueueDispatch (
  IN OUT      HECI2_TRUSTED_CHANNEL_BIOS_HEADER  *CommandQueueBuffer,
  IN          UINTN                              TotalCommandCount
  )
{
  EFI_STATUS  Status;
  UINT32      CurrentCommandDataSize           = 0;  // The data contents size (excluding headers)
  UINTN       CurrentTotalCommandRequestSize   = 0;  // Total size of request command (header + data content size)
  UINTN       CurrentTotalCommandResponseSize  = 0;  // Total size of response command (header + data content size)
  UINTN       CurrentCommandResponseHeaderSize = 0;  // Size of the current command response header
  UINTN       CurrentCommandAdvancementIndex   = 0;  // The calculated location of the next command header
  UINTN       CurrentCommandIndex              = 0;  // The index of the current command being dispatched

  HECI2_TRUSTED_CHANNEL_BIOS_HEADER  *CurrentHeci2TrustedChannelRequestHeader  = NULL;
  UINT8                              *CurrentHeci2TrustedChannelResponseHeader = NULL;

  RuntimeDebugMessage ("Performing command queue dispatch. Total command count:\n", (UINT8 *) &TotalCommandCount, sizeof (UINTN));

  if (CommandQueueBuffer == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  CurrentHeci2TrustedChannelRequestHeader  = CommandQueueBuffer;
  CurrentHeci2TrustedChannelResponseHeader =  mCseVariableStorageResponseVerificationBuffer +
                                               SMM_CSE_VARIABLE_STORAGE_GET_TOTAL_COMMUNICATE_BUFFER_SIZE (
                                                 OFFSET_OF (SMM_CSE_VARIABLE_STORAGE_COMMUNICATE_VERIFY_RESPONSE, Data)
                                                 );

  //
  //  Dispatch all commands in the command queue
  //  The commands might represent several variable requests.
  //
  for (CurrentCommandIndex = 0; CurrentCommandIndex < TotalCommandCount; CurrentCommandIndex++) {
    RuntimeDebugMessage ("Dispatching command chunk number:\n", (UINT8 *) &CurrentCommandIndex, sizeof (UINTN));

    ZeroMem (mCseVariableStorageResponseVerificationBuffer, mCseVariableStorageResponseVerificationBufferSize);
    if (CurrentHeci2TrustedChannelRequestHeader == NULL) {
      //
      // Command queue header is invalid
      //
      //ASSERT (CurrentHeci2TrustedChannelRequestHeader != NULL);
      RuntimeDebugMessage ("CurrentHeci2TrustedChannelRequestHeader is NULL cannot continue dispatch.\n", NULL, 0);
      return EFI_ABORTED;
    }

    switch (CurrentHeci2TrustedChannelRequestHeader->CommandId) {
      case HECI2_READ_DATA_CMD_ID:
        RuntimeDebugMessage ("Current command is: read\n", NULL, 0);
        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:
        RuntimeDebugMessage ("Current command is: write\n", NULL, 0);
        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
        //ASSERT (FALSE);
        return EFI_DEVICE_ERROR;
    }

    RuntimeDebugMessage ("CurrentCommandDataSize:\n", (UINT8 *) &CurrentCommandDataSize, sizeof (UINT32));
    RuntimeDebugMessage ("CurrentTotalCommandRequestSize:\n", (UINT8 *) &CurrentTotalCommandRequestSize, sizeof (UINTN));
    RuntimeDebugMessage ("CurrentCommandResponseHeaderSize:\n", (UINT8 *) &CurrentCommandResponseHeaderSize, sizeof (UINTN));
    RuntimeDebugMessage ("CurrentTotalCommandResponseSize:\n", (UINT8 *) &CurrentTotalCommandResponseSize, sizeof (UINTN));
    RuntimeDebugMessage (
      "Current HECI2 Trusted Request Header for Dispatch:\n",
      (UINT8 *) CurrentHeci2TrustedChannelRequestHeader,
      CurrentTotalCommandRequestSize
      );

    CopyMem (
      (VOID *) CurrentHeci2TrustedChannelResponseHeader,
      (VOID *) CurrentHeci2TrustedChannelRequestHeader,
      CurrentTotalCommandRequestSize
      );

    Status = mHeciProtocol->SendwACK (
               HECI2_DEVICE,
               (UINT32 *) CurrentHeci2TrustedChannelResponseHeader,
               (UINT32) CurrentTotalCommandRequestSize,
               (UINT32 *) &CurrentTotalCommandResponseSize,
               BIOS_FIXED_HOST_ADDR,
               HECI2_BIOS_MCA_FIXED_ADDR
               );
    //
    // The SMM command queue must be flushed after the first HECI message
    // so it is not tried again with the same monotonic counter value.
    //
    if (EFI_ERROR (ResetSmmCommandQueue ())) {
      RuntimeDebugMessage ("An error occurred resetting the SMM command queue.\n", NULL, 0);
      return EFI_OUT_OF_RESOURCES;
    }

    RuntimeDebugMessage ("HECI2 Response Header:\n", (UINT8 *) CurrentHeci2TrustedChannelResponseHeader, CurrentCommandResponseHeaderSize);

    if (((HECI2_TRUSTED_CHANNEL_BIOS_READ_RESP *) CurrentHeci2TrustedChannelResponseHeader)->Status != BIOS_HECI2_STATUS_OK) {
      RuntimeDebugMessage ("An error occurred sending HECI command.\n", NULL, 0);
      return EFI_DEVICE_ERROR;
    }

    RuntimeDebugMessage ("Sending command response for verification.\n", NULL, 0);

    Status = VerifyHmacCommandResponse (
               (BOOLEAN) (CurrentCommandIndex == (TotalCommandCount - 1)),
               CurrentCommandResponseHeaderSize,
               CurrentTotalCommandResponseSize,
               CurrentHeci2TrustedChannelResponseHeader
               );
    if (EFI_ERROR (Status)) {
      //ASSERT_EFI_ERROR (Status);
      return Status;
    }

    //
    // Move to the next command (all commands in the queue are requests)
    // Command headers must be DWORD aligned
    //
    CurrentCommandAdvancementIndex = (UINTN) (VOID *) CurrentHeci2TrustedChannelRequestHeader;
    CurrentCommandAdvancementIndex += CurrentTotalCommandRequestSize;
    CurrentCommandAdvancementIndex += TRUSTED_CHANNEL_GET_PAD_SIZE (CurrentTotalCommandRequestSize);
    CurrentHeci2TrustedChannelRequestHeader = (HECI2_TRUSTED_CHANNEL_BIOS_HEADER *) CurrentCommandAdvancementIndex;
  }

  return EFI_SUCCESS;
}

/**
  Gets the command queue from CSE Variable Storage SMM.

  @param[out]    CommandQueueBuffer  A pointer to a buffer of HECI2 trusted channel commands (the command queue).
  @param[in]     CommandQueueCount   The total number of commands in the command queue.

  @retval        EFI_SUCCESS         The command queue was retrieved successfully.
  @retval        Others              The command queue could not be retrieved successfully.

**/
EFI_STATUS
EFIAPI
GetCommandQueue (
  OUT        HECI2_TRUSTED_CHANNEL_BIOS_HEADER  **CommandQueueBuffer,
  OUT        UINTN                              *CommandQueueCount
  )
{
  EFI_STATUS  Status;
  SMM_CSE_VARIABLE_STORAGE_COMMUNICATE_GET_COMMAND_QUEUE *SmmGetCommandQueueHeader = NULL;

  RuntimeDebugMessage ("Start of GetCommandQueue() RT DXE\n", NULL, 0);

  ZeroMem (mCseVariableStorageBuffer, mCseVariableStorageBufferSize);

  Status = InitCommunicateBuffer (
             (VOID **) &SmmGetCommandQueueHeader,
             mCseVariableStorageBuffer,
             mCseVariableStorageBufferSize,
             mCseVariableStoragePayloadSize,
             SMM_CSE_VARIABLE_STORAGE_FUNCTION_GET_COMMAND_QUEUE
             );
  if (EFI_ERROR (Status)) {
    return Status;
  }
  if (SmmGetCommandQueueHeader == NULL) {
    //ASSERT (SmmGetCommandQueueHeader != NULL);
    return EFI_OUT_OF_RESOURCES;
  }

  RuntimeDebugMessage ("Triggering GetCommandQueue SMI\n", NULL, 0);

  Status = SendCommunicateBuffer (
             mCseVariableStorageBuffer,
             mCseVariableStorageBufferPhysical,
             mCseVariableStoragePayloadSize
             );
  if (!EFI_ERROR (Status)) {
    *CommandQueueBuffer = &SmmGetCommandQueueHeader->CommandQueueDataBuffer[0];
    *CommandQueueCount  = SmmGetCommandQueueHeader->CommandQueueCount;
  }

  RuntimeDebugMessage ("End of GetCommandQueue() RT DXE\n", NULL, 0);

  return Status;
}

/**
  Retrieves a protocol instance-specific GUID.

  Returns a unique GUID per VARIABLE_STORAGE_IO_COMPLETION_PROTOCOL instance.

  @param[in]     This                        A pointer to this protocol instance.
  @param[out]    InstanceGuid                A pointer to an EFI_GUID that is this protocol instance's GUID.

  @retval        EFI_SUCCESS                 The protocol instance GUID was returned successfully.
  @retval        EFI_INVALID_PARAMETER       The InstanceGuid parameter provided was NULL.

**/
EFI_STATUS
EFIAPI
CseVariableStorageIoCompletionGetId (
  IN             EDKII_VARIABLE_STORAGE_IO_COMPLETION_PROTOCOL  *This,
  OUT            EFI_GUID                                       *InstanceGuid
  )
{
  if (InstanceGuid == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  CopyMem ((VOID *) InstanceGuid, (VOID *) &gCseVariableStorageProtocolInstanceGuid, sizeof (EFI_GUID));

  return EFI_SUCCESS;
}

/**
  Returns when the variable storage IO request is finished (the transaction is complete).

  @param[in]     This                        A pointer to this protocol instance.
  @param[in]     SetVariableIoCompletion     TRUE if Complete() is being called to perform I/O for SetVariable
                                             FALSE if Complete() is being called to perform I/O for GetVariable


  @retval        EFI_SUCCESS                 The command status was retrieved successfully.
  @retval        EFI_NOT_FOUND               The CSE OS proxy driver is not available.
  @retval        Others                      An error occurred retrieving the command status.

**/
EFI_STATUS
EFIAPI
CseVariableStorageIoCompletionComplete (
  IN        EDKII_VARIABLE_STORAGE_IO_COMPLETION_PROTOCOL   *This,
  IN        BOOLEAN                                         SetVariableIoCompletion
  )
{
  EFI_STATUS                                   Status;

  //BOOLEAN                                      IsProxyPathReady       = FALSE;
  HECI2_TRUSTED_CHANNEL_BIOS_HEADER            *CommandQueueBuffer    = NULL;
  UINTN                                        TotalCommandQueueCount = 0;

  RuntimeDebugMessage ("Start of IoCompletionComplete()\n", NULL, 0);

  //
  // Ensure the proxy path is available
  //

 #if 0
  if (mCseVariableStorageAfterEndOfService) {
    Status = CseVariableStorageGetProxyState (&IsProxyPathReady);
    if (EFI_ERROR (Status) || !IsProxyPathReady) {
      RuntimeDebugMessage ("Proxy is not ready cannot send HECI command queue message.\n", NULL, 0);
      if (SetVariableIoCompletion) {
        return EFI_WARN_OS_DRIVER_NOT_PRESENT;
      } else {
        return EFI_NOT_FOUND;
      }
    }
  }
#endif

  RuntimeDebugMessage ("Getting command queue from SMM...\n", NULL, 0);
  //
  // Get the command queue
  //
  Status = GetCommandQueue (&CommandQueueBuffer, &TotalCommandQueueCount);
  if (EFI_ERROR (Status)) {
    return Status;
  }

  //
  // Dispatch the command queue
  //
  Status = PerformCommandQueueDispatch (CommandQueueBuffer, TotalCommandQueueCount);

  RuntimeDebugMessage ("End of IoCompletionComplete()\n", NULL, 0);

  return Status;
}